From af461fc1875b6ec18e23b5f670af36c4ed35c84e Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 19:30:53 +0100 Subject: [PATCH] pcmcia: delay re-scanning and re-querying of PCMCIA bus 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. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 10 +---- drivers/pcmcia/cs.c | 8 +++- drivers/pcmcia/cs_internal.h | 3 +- drivers/pcmcia/ds.c | 80 +++++++++++++++++++++-------------- drivers/pcmcia/socket_sysfs.c | 11 +---- 5 files changed, 59 insertions(+), 53 deletions(-) diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 14de287a8bf6..17a5da32cce1 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1670,15 +1670,7 @@ 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_unlock(&s->skt_mutex); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); return count; } diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 823ecda32216..d529e02c2d54 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -728,6 +728,11 @@ static int pccardd(void *__skt) if (!ret) socket_suspend(skt); } + if ((sysfs_events & PCMCIA_UEVENT_REQUERY) && + !(skt->state & SOCKET_CARDBUS)) { + if (!ret && skt->callback) + skt->callback->requery(skt); + } } mutex_unlock(&skt->skt_mutex); @@ -783,7 +788,8 @@ EXPORT_SYMBOL(pcmcia_parse_events); * userspace-issued insert, eject, suspend and resume commands must be * handled by pccardd to avoid any sysfs-related deadlocks. Valid events * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), - * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend). + * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend) + * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card). */ void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) { diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 127c97acf849..f95864c2191e 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -110,7 +110,7 @@ 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); + void (*requery) (struct pcmcia_socket *s); int (*validate) (struct pcmcia_socket *s, unsigned int *i); int (*suspend) (struct pcmcia_socket *s); int (*resume) (struct pcmcia_socket *s); @@ -129,6 +129,7 @@ void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events); #define PCMCIA_UEVENT_INSERT 0x0002 #define PCMCIA_UEVENT_SUSPEND 0x0004 #define PCMCIA_UEVENT_RESUME 0x0008 +#define PCMCIA_UEVENT_REQUERY 0x0010 struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 76a21638291b..5400e20c664e 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); @@ -660,7 +668,7 @@ static void pcmcia_delayed_add_device(struct work_struct *work) pcmcia_device_add(s, mfc_pfc); } -static int pcmcia_requery(struct device *dev, void * _data) +static int pcmcia_requery_callback(struct device *dev, void * _data) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); if (!p_dev->dev.driver) { @@ -671,44 +679,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_requery(struct pcmcia_socket *s) { - int no_devices = 0; - int ret = 0; + int present; - /* must be called with skt_mutex held */ - dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock); + mutex_lock(&s->ops_mutex); + present = s->pcmcia_state.present; + mutex_unlock(&s->ops_mutex); - mutex_lock(&skt->ops_mutex); - if (list_empty(&skt->devices_list)) - no_devices = 1; - mutex_unlock(&skt->ops_mutex); + if (!present) + return; - /* 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; + if (s->functions == 0) { + pcmcia_card_add(s); + return; } /* 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); + bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback); + + /* if the CIS changed, we need to check whether the number of + * functions changed. */ + if (s->fake_cis) { + int old_funcs, new_funcs; + cistpl_longlink_mfc_t mfc; + + /* does this cis override add or remove functions? */ + old_funcs = s->functions; + + if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, + &mfc)) + new_funcs = mfc.nfn; + else + new_funcs = 1; + if (old_funcs > new_funcs) { + pcmcia_card_remove(s, NULL); + pcmcia_card_add(s); + } else if (new_funcs > old_funcs) { + s->functions = new_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) - printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n"); + if (bus_rescan_devices(&pcmcia_bus_type)) + dev_warn(&s->dev, "rescanning the bus failed\n"); } + #ifdef CONFIG_PCMCIA_LOAD_CIS /** @@ -1085,7 +1106,6 @@ 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; @@ -1093,11 +1113,7 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, mutex_lock(&p_dev->socket->ops_mutex); p_dev->allow_func_id_match = 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"); + pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY); return count; } @@ -1359,7 +1375,7 @@ EXPORT_SYMBOL(pcmcia_dev_present); static struct pcmcia_callback pcmcia_bus_callback = { .owner = THIS_MODULE, .event = ds_event, - .requery = pcmcia_bus_rescan, + .requery = pcmcia_requery, .validate = pccard_validate_cis, .suspend = pcmcia_bus_suspend, .resume = pcmcia_bus_resume, diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index fba0e30183f4..08278016e58d 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -201,16 +201,7 @@ static ssize_t pccard_store_resource(struct device *dev, 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); - } - } - mutex_unlock(&s->skt_mutex); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); return count; }