diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-01-17 13:30:53 -0500 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-02-17 11:48:26 -0500 |
commit | af461fc1875b6ec18e23b5f670af36c4ed35c84e (patch) | |
tree | fa09a446c94d533c169b9e28e97c0e8c03d36912 /drivers | |
parent | f971dbd5da4e2fbf756d07b938a9c65a9c75178b (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pcmcia/cistpl.c | 10 | ||||
-rw-r--r-- | drivers/pcmcia/cs.c | 8 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 3 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 80 | ||||
-rw-r--r-- | 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, | |||
1670 | if (error) | 1670 | if (error) |
1671 | return -EIO; | 1671 | return -EIO; |
1672 | 1672 | ||
1673 | mutex_lock(&s->skt_mutex); | 1673 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); |
1674 | if ((s->callback) && (s->state & SOCKET_PRESENT) && | ||
1675 | !(s->state & SOCKET_CARDBUS)) { | ||
1676 | if (try_module_get(s->callback->owner)) { | ||
1677 | s->callback->requery(s, 1); | ||
1678 | module_put(s->callback->owner); | ||
1679 | } | ||
1680 | } | ||
1681 | mutex_unlock(&s->skt_mutex); | ||
1682 | 1674 | ||
1683 | return count; | 1675 | return count; |
1684 | } | 1676 | } |
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) | |||
728 | if (!ret) | 728 | if (!ret) |
729 | socket_suspend(skt); | 729 | socket_suspend(skt); |
730 | } | 730 | } |
731 | if ((sysfs_events & PCMCIA_UEVENT_REQUERY) && | ||
732 | !(skt->state & SOCKET_CARDBUS)) { | ||
733 | if (!ret && skt->callback) | ||
734 | skt->callback->requery(skt); | ||
735 | } | ||
731 | } | 736 | } |
732 | mutex_unlock(&skt->skt_mutex); | 737 | mutex_unlock(&skt->skt_mutex); |
733 | 738 | ||
@@ -783,7 +788,8 @@ EXPORT_SYMBOL(pcmcia_parse_events); | |||
783 | * userspace-issued insert, eject, suspend and resume commands must be | 788 | * userspace-issued insert, eject, suspend and resume commands must be |
784 | * handled by pccardd to avoid any sysfs-related deadlocks. Valid events | 789 | * handled by pccardd to avoid any sysfs-related deadlocks. Valid events |
785 | * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), | 790 | * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), |
786 | * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend). | 791 | * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend) |
792 | * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card). | ||
787 | */ | 793 | */ |
788 | void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) | 794 | void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) |
789 | { | 795 | { |
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{ | |||
110 | struct module *owner; | 110 | struct module *owner; |
111 | int (*event) (struct pcmcia_socket *s, | 111 | int (*event) (struct pcmcia_socket *s, |
112 | event_t event, int priority); | 112 | event_t event, int priority); |
113 | void (*requery) (struct pcmcia_socket *s, int new_cis); | 113 | void (*requery) (struct pcmcia_socket *s); |
114 | int (*validate) (struct pcmcia_socket *s, unsigned int *i); | 114 | int (*validate) (struct pcmcia_socket *s, unsigned int *i); |
115 | int (*suspend) (struct pcmcia_socket *s); | 115 | int (*suspend) (struct pcmcia_socket *s); |
116 | int (*resume) (struct pcmcia_socket *s); | 116 | int (*resume) (struct pcmcia_socket *s); |
@@ -129,6 +129,7 @@ void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events); | |||
129 | #define PCMCIA_UEVENT_INSERT 0x0002 | 129 | #define PCMCIA_UEVENT_INSERT 0x0002 |
130 | #define PCMCIA_UEVENT_SUSPEND 0x0004 | 130 | #define PCMCIA_UEVENT_SUSPEND 0x0004 |
131 | #define PCMCIA_UEVENT_RESUME 0x0008 | 131 | #define PCMCIA_UEVENT_RESUME 0x0008 |
132 | #define PCMCIA_UEVENT_REQUERY 0x0010 | ||
132 | 133 | ||
133 | struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); | 134 | struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); |
134 | void pcmcia_put_socket(struct pcmcia_socket *skt); | 135 | 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) | |||
432 | 432 | ||
433 | if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, | 433 | if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, |
434 | CISTPL_MANFID, &manf_id)) { | 434 | CISTPL_MANFID, &manf_id)) { |
435 | mutex_lock(&p_dev->socket->ops_mutex); | ||
435 | p_dev->manf_id = manf_id.manf; | 436 | p_dev->manf_id = manf_id.manf; |
436 | p_dev->card_id = manf_id.card; | 437 | p_dev->card_id = manf_id.card; |
437 | p_dev->has_manf_id = 1; | 438 | p_dev->has_manf_id = 1; |
438 | p_dev->has_card_id = 1; | 439 | p_dev->has_card_id = 1; |
440 | mutex_unlock(&p_dev->socket->ops_mutex); | ||
439 | } | 441 | } |
440 | 442 | ||
441 | if (!pccard_read_tuple(p_dev->socket, p_dev->func, | 443 | if (!pccard_read_tuple(p_dev->socket, p_dev->func, |
442 | CISTPL_FUNCID, &func_id)) { | 444 | CISTPL_FUNCID, &func_id)) { |
445 | mutex_lock(&p_dev->socket->ops_mutex); | ||
443 | p_dev->func_id = func_id.func; | 446 | p_dev->func_id = func_id.func; |
444 | p_dev->has_func_id = 1; | 447 | p_dev->has_func_id = 1; |
448 | mutex_unlock(&p_dev->socket->ops_mutex); | ||
445 | } else { | 449 | } else { |
446 | /* rule of thumb: cards with no FUNCID, but with | 450 | /* rule of thumb: cards with no FUNCID, but with |
447 | * common memory device geometry information, are | 451 | * common memory device geometry information, are |
@@ -458,14 +462,17 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) | |||
458 | dev_dbg(&p_dev->dev, | 462 | dev_dbg(&p_dev->dev, |
459 | "mem device geometry probably means " | 463 | "mem device geometry probably means " |
460 | "FUNCID_MEMORY\n"); | 464 | "FUNCID_MEMORY\n"); |
465 | mutex_lock(&p_dev->socket->ops_mutex); | ||
461 | p_dev->func_id = CISTPL_FUNCID_MEMORY; | 466 | p_dev->func_id = CISTPL_FUNCID_MEMORY; |
462 | p_dev->has_func_id = 1; | 467 | p_dev->has_func_id = 1; |
468 | mutex_unlock(&p_dev->socket->ops_mutex); | ||
463 | } | 469 | } |
464 | kfree(devgeo); | 470 | kfree(devgeo); |
465 | } | 471 | } |
466 | 472 | ||
467 | if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1, | 473 | if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1, |
468 | vers1)) { | 474 | vers1)) { |
475 | mutex_lock(&p_dev->socket->ops_mutex); | ||
469 | for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { | 476 | for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { |
470 | char *tmp; | 477 | char *tmp; |
471 | unsigned int length; | 478 | unsigned int length; |
@@ -484,6 +491,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) | |||
484 | p_dev->prod_id[i] = strncpy(p_dev->prod_id[i], | 491 | p_dev->prod_id[i] = strncpy(p_dev->prod_id[i], |
485 | tmp, length); | 492 | tmp, length); |
486 | } | 493 | } |
494 | mutex_unlock(&p_dev->socket->ops_mutex); | ||
487 | } | 495 | } |
488 | 496 | ||
489 | kfree(vers1); | 497 | kfree(vers1); |
@@ -660,7 +668,7 @@ static void pcmcia_delayed_add_device(struct work_struct *work) | |||
660 | pcmcia_device_add(s, mfc_pfc); | 668 | pcmcia_device_add(s, mfc_pfc); |
661 | } | 669 | } |
662 | 670 | ||
663 | static int pcmcia_requery(struct device *dev, void * _data) | 671 | static int pcmcia_requery_callback(struct device *dev, void * _data) |
664 | { | 672 | { |
665 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); | 673 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); |
666 | if (!p_dev->dev.driver) { | 674 | if (!p_dev->dev.driver) { |
@@ -671,44 +679,57 @@ static int pcmcia_requery(struct device *dev, void * _data) | |||
671 | return 0; | 679 | return 0; |
672 | } | 680 | } |
673 | 681 | ||
674 | static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis) | 682 | static void pcmcia_requery(struct pcmcia_socket *s) |
675 | { | 683 | { |
676 | int no_devices = 0; | 684 | int present; |
677 | int ret = 0; | ||
678 | |||
679 | /* must be called with skt_mutex held */ | ||
680 | dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock); | ||
681 | 685 | ||
682 | mutex_lock(&skt->ops_mutex); | 686 | mutex_lock(&s->ops_mutex); |
683 | if (list_empty(&skt->devices_list)) | 687 | present = s->pcmcia_state.present; |
684 | no_devices = 1; | 688 | mutex_unlock(&s->ops_mutex); |
685 | mutex_unlock(&skt->ops_mutex); | ||
686 | 689 | ||
687 | /* If this is because of a CIS override, start over */ | 690 | if (!present) |
688 | if (new_cis && !no_devices) | 691 | return; |
689 | pcmcia_card_remove(skt, NULL); | ||
690 | 692 | ||
691 | /* if no devices were added for this socket yet because of | 693 | if (s->functions == 0) { |
692 | * missing resource information or other trouble, we need to | 694 | pcmcia_card_add(s); |
693 | * do this now. */ | 695 | return; |
694 | if (no_devices || new_cis) { | ||
695 | ret = pcmcia_card_add(skt); | ||
696 | if (ret) | ||
697 | return; | ||
698 | } | 696 | } |
699 | 697 | ||
700 | /* some device information might have changed because of a CIS | 698 | /* some device information might have changed because of a CIS |
701 | * update or because we can finally read it correctly... so | 699 | * update or because we can finally read it correctly... so |
702 | * determine it again, overwriting old values if necessary. */ | 700 | * determine it again, overwriting old values if necessary. */ |
703 | bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery); | 701 | bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback); |
702 | |||
703 | /* if the CIS changed, we need to check whether the number of | ||
704 | * functions changed. */ | ||
705 | if (s->fake_cis) { | ||
706 | int old_funcs, new_funcs; | ||
707 | cistpl_longlink_mfc_t mfc; | ||
708 | |||
709 | /* does this cis override add or remove functions? */ | ||
710 | old_funcs = s->functions; | ||
711 | |||
712 | if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, | ||
713 | &mfc)) | ||
714 | new_funcs = mfc.nfn; | ||
715 | else | ||
716 | new_funcs = 1; | ||
717 | if (old_funcs > new_funcs) { | ||
718 | pcmcia_card_remove(s, NULL); | ||
719 | pcmcia_card_add(s); | ||
720 | } else if (new_funcs > old_funcs) { | ||
721 | s->functions = new_funcs; | ||
722 | pcmcia_device_add(s, 1); | ||
723 | } | ||
724 | } | ||
704 | 725 | ||
705 | /* we re-scan all devices, not just the ones connected to this | 726 | /* we re-scan all devices, not just the ones connected to this |
706 | * socket. This does not matter, though. */ | 727 | * socket. This does not matter, though. */ |
707 | ret = bus_rescan_devices(&pcmcia_bus_type); | 728 | if (bus_rescan_devices(&pcmcia_bus_type)) |
708 | if (ret) | 729 | dev_warn(&s->dev, "rescanning the bus failed\n"); |
709 | printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n"); | ||
710 | } | 730 | } |
711 | 731 | ||
732 | |||
712 | #ifdef CONFIG_PCMCIA_LOAD_CIS | 733 | #ifdef CONFIG_PCMCIA_LOAD_CIS |
713 | 734 | ||
714 | /** | 735 | /** |
@@ -1085,7 +1106,6 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, | |||
1085 | struct device_attribute *attr, const char *buf, size_t count) | 1106 | struct device_attribute *attr, const char *buf, size_t count) |
1086 | { | 1107 | { |
1087 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); | 1108 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); |
1088 | int ret; | ||
1089 | 1109 | ||
1090 | if (!count) | 1110 | if (!count) |
1091 | return -EINVAL; | 1111 | return -EINVAL; |
@@ -1093,11 +1113,7 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, | |||
1093 | mutex_lock(&p_dev->socket->ops_mutex); | 1113 | mutex_lock(&p_dev->socket->ops_mutex); |
1094 | p_dev->allow_func_id_match = 1; | 1114 | p_dev->allow_func_id_match = 1; |
1095 | mutex_unlock(&p_dev->socket->ops_mutex); | 1115 | mutex_unlock(&p_dev->socket->ops_mutex); |
1096 | 1116 | pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY); | |
1097 | ret = bus_rescan_devices(&pcmcia_bus_type); | ||
1098 | if (ret) | ||
1099 | printk(KERN_INFO "pcmcia: bus_rescan_devices failed after " | ||
1100 | "allowing func_id matches\n"); | ||
1101 | 1117 | ||
1102 | return count; | 1118 | return count; |
1103 | } | 1119 | } |
@@ -1359,7 +1375,7 @@ EXPORT_SYMBOL(pcmcia_dev_present); | |||
1359 | static struct pcmcia_callback pcmcia_bus_callback = { | 1375 | static struct pcmcia_callback pcmcia_bus_callback = { |
1360 | .owner = THIS_MODULE, | 1376 | .owner = THIS_MODULE, |
1361 | .event = ds_event, | 1377 | .event = ds_event, |
1362 | .requery = pcmcia_bus_rescan, | 1378 | .requery = pcmcia_requery, |
1363 | .validate = pccard_validate_cis, | 1379 | .validate = pccard_validate_cis, |
1364 | .suspend = pcmcia_bus_suspend, | 1380 | .suspend = pcmcia_bus_suspend, |
1365 | .resume = pcmcia_bus_resume, | 1381 | .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, | |||
201 | s->resource_setup_done = 1; | 201 | s->resource_setup_done = 1; |
202 | mutex_unlock(&s->ops_mutex); | 202 | mutex_unlock(&s->ops_mutex); |
203 | 203 | ||
204 | mutex_lock(&s->skt_mutex); | 204 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); |
205 | if ((s->callback) && | ||
206 | (s->state & SOCKET_PRESENT) && | ||
207 | !(s->state & SOCKET_CARDBUS)) { | ||
208 | if (try_module_get(s->callback->owner)) { | ||
209 | s->callback->requery(s, 0); | ||
210 | module_put(s->callback->owner); | ||
211 | } | ||
212 | } | ||
213 | mutex_unlock(&s->skt_mutex); | ||
214 | 205 | ||
215 | return count; | 206 | return count; |
216 | } | 207 | } |