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 <w.sang@pengutronix.de>
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
This commit is contained in:
Dominik Brodowski 2010-01-17 19:30:53 +01:00
parent f971dbd5da
commit af461fc187
5 changed files with 59 additions and 53 deletions

View File

@ -1670,15 +1670,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj,
if (error) if (error)
return -EIO; return -EIO;
mutex_lock(&s->skt_mutex); pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
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);
return count; return count;
} }

View File

@ -728,6 +728,11 @@ static int pccardd(void *__skt)
if (!ret) if (!ret)
socket_suspend(skt); 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); mutex_unlock(&skt->skt_mutex);
@ -783,7 +788,8 @@ EXPORT_SYMBOL(pcmcia_parse_events);
* userspace-issued insert, eject, suspend and resume commands must be * userspace-issued insert, eject, suspend and resume commands must be
* handled by pccardd to avoid any sysfs-related deadlocks. Valid events * handled by pccardd to avoid any sysfs-related deadlocks. Valid events
* are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), * 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) void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events)
{ {

View File

@ -110,7 +110,7 @@ struct pcmcia_callback{
struct module *owner; struct module *owner;
int (*event) (struct pcmcia_socket *s, int (*event) (struct pcmcia_socket *s,
event_t event, int priority); 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 (*validate) (struct pcmcia_socket *s, unsigned int *i);
int (*suspend) (struct pcmcia_socket *s); int (*suspend) (struct pcmcia_socket *s);
int (*resume) (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_INSERT 0x0002
#define PCMCIA_UEVENT_SUSPEND 0x0004 #define PCMCIA_UEVENT_SUSPEND 0x0004
#define PCMCIA_UEVENT_RESUME 0x0008 #define PCMCIA_UEVENT_RESUME 0x0008
#define PCMCIA_UEVENT_REQUERY 0x0010
struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
void pcmcia_put_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt);

View File

@ -432,16 +432,20 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL,
CISTPL_MANFID, &manf_id)) { CISTPL_MANFID, &manf_id)) {
mutex_lock(&p_dev->socket->ops_mutex);
p_dev->manf_id = manf_id.manf; p_dev->manf_id = manf_id.manf;
p_dev->card_id = manf_id.card; p_dev->card_id = manf_id.card;
p_dev->has_manf_id = 1; p_dev->has_manf_id = 1;
p_dev->has_card_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, if (!pccard_read_tuple(p_dev->socket, p_dev->func,
CISTPL_FUNCID, &func_id)) { CISTPL_FUNCID, &func_id)) {
mutex_lock(&p_dev->socket->ops_mutex);
p_dev->func_id = func_id.func; p_dev->func_id = func_id.func;
p_dev->has_func_id = 1; p_dev->has_func_id = 1;
mutex_unlock(&p_dev->socket->ops_mutex);
} else { } else {
/* rule of thumb: cards with no FUNCID, but with /* rule of thumb: cards with no FUNCID, but with
* common memory device geometry information, are * 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, dev_dbg(&p_dev->dev,
"mem device geometry probably means " "mem device geometry probably means "
"FUNCID_MEMORY\n"); "FUNCID_MEMORY\n");
mutex_lock(&p_dev->socket->ops_mutex);
p_dev->func_id = CISTPL_FUNCID_MEMORY; p_dev->func_id = CISTPL_FUNCID_MEMORY;
p_dev->has_func_id = 1; p_dev->has_func_id = 1;
mutex_unlock(&p_dev->socket->ops_mutex);
} }
kfree(devgeo); kfree(devgeo);
} }
if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1, if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1,
vers1)) { vers1)) {
mutex_lock(&p_dev->socket->ops_mutex);
for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) {
char *tmp; char *tmp;
unsigned int length; 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], p_dev->prod_id[i] = strncpy(p_dev->prod_id[i],
tmp, length); tmp, length);
} }
mutex_unlock(&p_dev->socket->ops_mutex);
} }
kfree(vers1); kfree(vers1);
@ -660,7 +668,7 @@ static void pcmcia_delayed_add_device(struct work_struct *work)
pcmcia_device_add(s, mfc_pfc); 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); struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
if (!p_dev->dev.driver) { if (!p_dev->dev.driver) {
@ -671,44 +679,57 @@ static int pcmcia_requery(struct device *dev, void * _data)
return 0; 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 present;
int ret = 0;
/* must be called with skt_mutex held */ mutex_lock(&s->ops_mutex);
dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock); present = s->pcmcia_state.present;
mutex_unlock(&s->ops_mutex);
mutex_lock(&skt->ops_mutex); if (!present)
if (list_empty(&skt->devices_list)) return;
no_devices = 1;
mutex_unlock(&skt->ops_mutex);
/* If this is because of a CIS override, start over */ if (s->functions == 0) {
if (new_cis && !no_devices) pcmcia_card_add(s);
pcmcia_card_remove(skt, NULL); return;
/* 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;
} }
/* some device information might have changed because of a CIS /* some device information might have changed because of a CIS
* update or because we can finally read it correctly... so * update or because we can finally read it correctly... so
* determine it again, overwriting old values if necessary. */ * 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 /* we re-scan all devices, not just the ones connected to this
* socket. This does not matter, though. */ * socket. This does not matter, though. */
ret = bus_rescan_devices(&pcmcia_bus_type); if (bus_rescan_devices(&pcmcia_bus_type))
if (ret) dev_warn(&s->dev, "rescanning the bus failed\n");
printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n");
} }
#ifdef CONFIG_PCMCIA_LOAD_CIS #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 device_attribute *attr, const char *buf, size_t count)
{ {
struct pcmcia_device *p_dev = to_pcmcia_dev(dev); struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
int ret;
if (!count) if (!count)
return -EINVAL; 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); mutex_lock(&p_dev->socket->ops_mutex);
p_dev->allow_func_id_match = 1; p_dev->allow_func_id_match = 1;
mutex_unlock(&p_dev->socket->ops_mutex); mutex_unlock(&p_dev->socket->ops_mutex);
pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY);
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; return count;
} }
@ -1359,7 +1375,7 @@ EXPORT_SYMBOL(pcmcia_dev_present);
static struct pcmcia_callback pcmcia_bus_callback = { static struct pcmcia_callback pcmcia_bus_callback = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.event = ds_event, .event = ds_event,
.requery = pcmcia_bus_rescan, .requery = pcmcia_requery,
.validate = pccard_validate_cis, .validate = pccard_validate_cis,
.suspend = pcmcia_bus_suspend, .suspend = pcmcia_bus_suspend,
.resume = pcmcia_bus_resume, .resume = pcmcia_bus_resume,

View File

@ -201,16 +201,7 @@ static ssize_t pccard_store_resource(struct device *dev,
s->resource_setup_done = 1; s->resource_setup_done = 1;
mutex_unlock(&s->ops_mutex); mutex_unlock(&s->ops_mutex);
mutex_lock(&s->skt_mutex); pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
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);
return count; return count;
} }