[PATCH 12/16] pcmcia: delay re-scanning and re-querying of PCMCIA bus
Dominik Brodowski
linux at dominikbrodowski.net
Mon Jan 18 03:36:32 EST 2010
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 <linux at 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
More information about the linux-pcmcia
mailing list