After a CIS update -- or the finalization of the resource database --, proceed with the re-scanning or re-querying of PCMCIA cards only in a separate thread to avoid deadlocks.
Signed-off-by: Dominik Brodowski <li...@dominikbrodowski.net> --- drivers/pcmcia/cistpl.c | 13 ++---- drivers/pcmcia/cs_internal.h | 1 - drivers/pcmcia/ds.c | 90 ++++++++++++++++++++++++++--------------- drivers/pcmcia/socket_sysfs.c | 17 +++----- include/pcmcia/ss.h | 9 ++++- 5 files changed, 77 insertions(+), 53 deletions(-) diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 14de287..79db80f 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1670,15 +1670,12 @@ static ssize_t pccard_store_cis(struct kobject *kobj, if (error) return -EIO; - mutex_lock(&s->skt_mutex); - if ((s->callback) && (s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_CARDBUS)) { - if (try_module_get(s->callback->owner)) { - s->callback->requery(s, 1); - module_put(s->callback->owner); - } + mutex_lock(&s->ops_mutex); + if (!s->pcmcia_state.requery_pending) { + schedule_work(&s->requery); + s->pcmcia_state.requery_pending = 1; } - mutex_unlock(&s->skt_mutex); + mutex_unlock(&s->ops_mutex); return count; } diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 9625bf2..c07041e 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -116,7 +116,6 @@ struct pcmcia_callback{ struct module *owner; int (*event) (struct pcmcia_socket *s, event_t event, int priority); - void (*requery) (struct pcmcia_socket *s, int new_cis); int (*validate) (struct pcmcia_socket *s, unsigned int *i); int (*suspend) (struct pcmcia_socket *s); int (*resume) (struct pcmcia_socket *s); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 253d9ac..d61f3f1 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -432,16 +432,20 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_MANFID, &manf_id)) { + mutex_lock(&p_dev->socket->ops_mutex); p_dev->manf_id = manf_id.manf; p_dev->card_id = manf_id.card; p_dev->has_manf_id = 1; p_dev->has_card_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_FUNCID, &func_id)) { + mutex_lock(&p_dev->socket->ops_mutex); p_dev->func_id = func_id.func; p_dev->has_func_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } else { /* rule of thumb: cards with no FUNCID, but with * common memory device geometry information, are @@ -458,14 +462,17 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) dev_dbg(&p_dev->dev, "mem device geometry probably means " "FUNCID_MEMORY\n"); + mutex_lock(&p_dev->socket->ops_mutex); p_dev->func_id = CISTPL_FUNCID_MEMORY; p_dev->has_func_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } kfree(devgeo); } if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1, vers1)) { + mutex_lock(&p_dev->socket->ops_mutex); for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { char *tmp; unsigned int length; @@ -484,6 +491,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) p_dev->prod_id[i] = strncpy(p_dev->prod_id[i], tmp, length); } + mutex_unlock(&p_dev->socket->ops_mutex); } kfree(vers1); @@ -667,44 +675,57 @@ static int pcmcia_requery(struct device *dev, void * _data) return 0; } -static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis) +static void pcmcia_delayed_requery(struct work_struct *work) { - int no_devices = 0; - int ret = 0; + int old_funcs; - /* must be called with skt_mutex held */ - dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock); + struct pcmcia_socket *s = + container_of(work, struct pcmcia_socket, requery); - mutex_lock(&skt->ops_mutex); - if (list_empty(&skt->devices_list)) - no_devices = 1; - mutex_unlock(&skt->ops_mutex); + mutex_lock(&s->skt_mutex); - /* If this is because of a CIS override, start over */ - if (new_cis && !no_devices) - pcmcia_card_remove(skt, NULL); - - /* if no devices were added for this socket yet because of - * missing resource information or other trouble, we need to - * do this now. */ - if (no_devices || new_cis) { - ret = pcmcia_card_add(skt); - if (ret) - return; - } + /* does this cis override add or remove functions? */ + old_funcs = s->functions; /* some device information might have changed because of a CIS * update or because we can finally read it correctly... so * determine it again, overwriting old values if necessary. */ bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery); - /* we re-scan all devices, not just the ones connected to this - * socket. This does not matter, though. */ - ret = bus_rescan_devices(&pcmcia_bus_type); - if (ret) - printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n"); + if (old_funcs == 0) + pcmcia_card_add(s); + else { + cistpl_longlink_mfc_t mfc; + int no_funcs; + int ret; + + if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, + &mfc)) + no_funcs = mfc.nfn; + else + no_funcs = 1; + s->functions = no_funcs; + + if (old_funcs > no_funcs) { + pcmcia_card_remove(s, NULL); + pcmcia_card_add(s); + } else if (no_funcs > old_funcs) + pcmcia_device_add(s, 1); + + /* we re-scan all devices, not just the ones connected to this + * socket. This does not matter, though. */ + ret = bus_rescan_devices(&pcmcia_bus_type); + if (ret) + dev_warn(&s->dev, "rescanning the bus failed\n"); + } + mutex_lock(&s->ops_mutex); + s->pcmcia_state.requery_pending = 0; + mutex_unlock(&s->ops_mutex); + + mutex_unlock(&s->skt_mutex); } + #ifdef CONFIG_PCMCIA_LOAD_CIS /** @@ -1081,20 +1102,18 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - int ret; if (!count) return -EINVAL; mutex_lock(&p_dev->socket->ops_mutex); p_dev->allow_func_id_match = 1; + if (!p_dev->socket->pcmcia_state.requery_pending) { + schedule_work(&p_dev->socket->requery); + p_dev->socket->pcmcia_state.requery_pending = 1; + } mutex_unlock(&p_dev->socket->ops_mutex); - ret = bus_rescan_devices(&pcmcia_bus_type); - if (ret) - printk(KERN_INFO "pcmcia: bus_rescan_devices failed after " - "allowing func_id matches\n"); - return count; } @@ -1355,7 +1374,6 @@ EXPORT_SYMBOL(pcmcia_dev_present); static struct pcmcia_callback pcmcia_bus_callback = { .owner = THIS_MODULE, .event = ds_event, - .requery = pcmcia_bus_rescan, .validate = pccard_validate_cis, .suspend = pcmcia_bus_suspend, .resume = pcmcia_bus_resume, @@ -1392,6 +1410,7 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, #endif INIT_LIST_HEAD(&socket->devices_list); INIT_WORK(&socket->device_add, pcmcia_delayed_add_device); + INIT_WORK(&socket->requery, pcmcia_delayed_requery); memset(&socket->pcmcia_state, 0, sizeof(u8)); socket->device_count = 0; @@ -1402,6 +1421,10 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, return ret; } + mutex_lock(&socket->ops_mutex); + socket->pcmcia_state.requery_allowed = 1; + mutex_unlock(&socket->ops_mutex); + return 0; } @@ -1415,6 +1438,7 @@ static void pcmcia_bus_remove_socket(struct device *dev, mutex_lock(&socket->ops_mutex); socket->pcmcia_state.dead = 1; + socket->pcmcia_state.requery_allowed = 0; mutex_unlock(&socket->ops_mutex); pccard_register_pcmcia(socket, NULL); diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index e8826df..aadf83d 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -194,21 +194,18 @@ static ssize_t pccard_store_resource(struct device *dev, if (!count) return -EINVAL; +#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) mutex_lock(&s->ops_mutex); if (!s->resource_setup_done) s->resource_setup_done = 1; - mutex_unlock(&s->ops_mutex); - mutex_lock(&s->skt_mutex); - if ((s->callback) && - (s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_CARDBUS)) { - if (try_module_get(s->callback->owner)) { - s->callback->requery(s, 0); - module_put(s->callback->owner); - } + if ((s->pcmcia_state.requery_allowed) && + (!s->pcmcia_state.requery_pending)) { + schedule_work(&s->requery); + s->pcmcia_state.requery_pending = 1; } - mutex_unlock(&s->skt_mutex); + mutex_unlock(&s->ops_mutex); +#endif return count; } diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index cfaccc2..e197adb 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -231,16 +231,23 @@ struct pcmcia_socket { u8 dead:1; /* a multifunction-device add event is pending */ u8 device_add_pending:1; + /* a requery event is pending */ + u8 requery_pending:1; + /* a requery event is pending */ + u8 requery_allowed:1; /* the pending event adds a mfc (1) or pfc (0) */ u8 mfc_pfc:1; - u8 reserved:3; + u8 reserved:1; } pcmcia_state; /* for adding further pseudo-multifunction devices */ struct work_struct device_add; + /* for re-querying devices */ + struct work_struct requery; + #ifdef CONFIG_PCMCIA_IOCTL struct user_info_t *user; wait_queue_head_t queue; -- 1.6.3.3 _______________________________________________ Linux PCMCIA reimplementation list http://lists.infradead.org/mailman/listinfo/linux-pcmcia