From 904e377744bfdcea276c27167fa6a609929f39dc Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 12:28:04 +0100 Subject: pcmcia: validate CIS, not CIS cache. In pccard_validate_cis(), validate the card CIS, not the CIS cache. Also, destroy the CIS cache if pccard_validate_cis fails. Furthermore, do not remove the fake CIS in destroy_cis_cache() but do so explicitely in the code paths where it makes sense. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 6d6f82b38a68..96d8d25c209d 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -407,6 +407,8 @@ static void socket_shutdown(struct pcmcia_socket *s) s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; destroy_cis_cache(s); + kfree(s->fake_cis); + s->fake_cis = NULL; #ifdef CONFIG_CARDBUS cb_free(s); #endif @@ -577,6 +579,8 @@ static int socket_late_resume(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "cis mismatch - different card\n"); socket_remove_drivers(skt); destroy_cis_cache(skt); + kfree(skt->fake_cis); + skt->fake_cis = NULL; /* * Workaround: give DS time to schedule removal. * Remove me once the 100ms delay is eliminated -- cgit v1.2.3 From 88b060d6c03fcb9e4d2018b4349954c4242a5c7f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 14:14:23 +0100 Subject: pcmcia: improve check for same card in slot after resume During a suspend/resume cycle, an user may change the card in the PCMCIA/CardBus slot. The pcmcia_core can at least look at the socket state to check whether it is the same. For PCMCIA devices, move the detection and handling of such a change to ds.c. For CardBus devices, the PCI hotplug interface doesn't offer a "rescan" facility which also _removes_ devices no longer to be found behind a bridge. Therefore, remove and re-add all devices unconditionally. CC: Jesse Barnes CC: Linus Torvalds Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 65 +++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 35 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 96d8d25c209d..8c51493d1f95 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -328,7 +328,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) { int ret; - if (s->state & SOCKET_CARDBUS) + if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL)) return 0; dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n", @@ -346,13 +346,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) return ret; } -static void socket_remove_drivers(struct pcmcia_socket *skt) -{ - dev_dbg(&skt->dev, "remove_drivers\n"); - - send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); -} - static int socket_reset(struct pcmcia_socket *skt) { int status, i; @@ -395,7 +388,7 @@ static void socket_shutdown(struct pcmcia_socket *s) dev_dbg(&s->dev, "shutdown\n"); - socket_remove_drivers(s); + send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); s->state &= SOCKET_INUSE | SOCKET_PRESENT; msleep(shutdown_delay * 10); s->state &= SOCKET_INUSE; @@ -462,7 +455,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) return -EINVAL; } skt->state |= SOCKET_CARDBUS; - } + } else + skt->state &= ~SOCKET_CARDBUS; /* * Decode the card voltage requirements, and apply power to the card. @@ -544,6 +538,8 @@ static int socket_suspend(struct pcmcia_socket *skt) if (skt->state & SOCKET_SUSPEND) return -EBUSY; + skt->suspended_state = skt->state; + send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); skt->socket = dead_socket; skt->ops->set_socket(skt, &skt->socket); @@ -566,38 +562,37 @@ static int socket_early_resume(struct pcmcia_socket *skt) static int socket_late_resume(struct pcmcia_socket *skt) { - if (!(skt->state & SOCKET_PRESENT)) { - skt->state &= ~SOCKET_SUSPEND; + skt->state &= ~SOCKET_SUSPEND; + + if (!(skt->state & SOCKET_PRESENT)) return socket_insert(skt); + + if (skt->resume_status) { + socket_shutdown(skt); + return 0; } - if (skt->resume_status == 0) { - /* - * FIXME: need a better check here for cardbus cards. - */ - if (verify_cis_cache(skt) != 0) { - dev_dbg(&skt->dev, "cis mismatch - different card\n"); - socket_remove_drivers(skt); - destroy_cis_cache(skt); - kfree(skt->fake_cis); - skt->fake_cis = NULL; - /* - * Workaround: give DS time to schedule removal. - * Remove me once the 100ms delay is eliminated - * in ds.c - */ - msleep(200); - send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); - } else { - dev_dbg(&skt->dev, "cis matches cache\n"); - send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); - } - } else { + if (skt->suspended_state != skt->state) { + dev_dbg(&skt->dev, + "suspend state 0x%x != resume state 0x%x\n", + skt->suspended_state, skt->state); + socket_shutdown(skt); + return socket_insert(skt); } - skt->state &= ~SOCKET_SUSPEND; +#ifdef CONFIG_CARDBUS + if (skt->state & SOCKET_CARDBUS) { + /* We can't be sure the CardBus card is the same + * as the one previously inserted. Therefore, remove + * and re-add... */ + cb_free(skt); + cb_alloc(skt); + return 0; + } +#endif + send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); return 0; } -- cgit v1.2.3 From 180c33ee409eb3ed605d4ad9884e4a526a7655ff Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 17:34:09 +0100 Subject: pcmcia: call CIS cleanup from ds.c As ds.c is the only real user of CIS access functions, call the cleanup functions from ds.c, too. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 8c51493d1f95..9d8b9c1f5a69 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -283,8 +283,6 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) if (socket->thread) kthread_stop(socket->thread); - release_cis_mem(socket); - /* remove from our own list */ down_write(&pcmcia_socket_list_rwsem); list_del(&socket->socket_list); @@ -399,7 +397,6 @@ static void socket_shutdown(struct pcmcia_socket *s) s->ops->set_socket(s, &s->socket); s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; - destroy_cis_cache(s); kfree(s->fake_cis); s->fake_cis = NULL; #ifdef CONFIG_CARDBUS -- cgit v1.2.3 From 593f010bc0d8f7fde2ce948cac3f77f6a3d9db2b Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 22:59:15 +0100 Subject: pcmcia: do not lock socket driver module in pcmcia_get_socket() Do not lock the socket driver module in pcmcia_get_socket(), as the PCMCIA core can handle a socket module removal: In pcmcia_unregister_socket(), we explicitely wait for the last put_device() to succeed. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 9d8b9c1f5a69..f0630a61da90 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -140,19 +140,13 @@ struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt) struct device *dev = get_device(&skt->dev); if (!dev) return NULL; - skt = dev_get_drvdata(dev); - if (!try_module_get(skt->owner)) { - put_device(&skt->dev); - return NULL; - } - return skt; + return dev_get_drvdata(dev); } EXPORT_SYMBOL(pcmcia_get_socket); void pcmcia_put_socket(struct pcmcia_socket *skt) { - module_put(skt->owner); put_device(&skt->dev); } EXPORT_SYMBOL(pcmcia_put_socket); -- cgit v1.2.3 From 3970dd8c5169505f0cc5e4c3e2fde7bdd9bbad3e Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 23:19:45 +0100 Subject: pcmcia: do not lock socket driver module on card insert Do not lock the socket driver module on card insert, as the PCMCIA core can handle a socket module removal, at least if we add a call to socket_remove() on pccardd()'s shutdown. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index f0630a61da90..137a5db2eca2 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -407,7 +407,7 @@ static void socket_shutdown(struct pcmcia_socket *s) "*** DANGER *** unable to remove socket power\n"); } - cs_socket_put(s); + s->state &= ~SOCKET_INUSE; } static int socket_setup(struct pcmcia_socket *skt, int initial_delay) @@ -496,8 +496,8 @@ static int socket_insert(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "insert\n"); - if (!cs_socket_get(skt)) - return -ENODEV; + WARN_ON(skt->state & SOCKET_INUSE); + skt->state |= SOCKET_INUSE; ret = socket_setup(skt, setup_delay); if (ret == 0) { @@ -697,6 +697,13 @@ static int pccardd(void *__skt) /* make sure we are running before we exit */ set_current_state(TASK_RUNNING); + /* shut down socket, if a device is still present */ + if (skt->state & SOCKET_PRESENT) { + mutex_lock(&skt->skt_mutex); + socket_remove(skt); + mutex_unlock(&skt->skt_mutex); + } + /* remove from the device core */ pccard_sysfs_remove_socket(&skt->dev); device_unregister(&skt->dev); -- cgit v1.2.3 From 385ee871092a524869c71a8180888aadcd6ca36d Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 11:23:58 +0100 Subject: pcmcia: remove useless indirection As release_resoure_db() used to be called only from one place, and it's a two-line function, remove it. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 137a5db2eca2..43c90f69a7a5 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -283,7 +283,8 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) up_write(&pcmcia_socket_list_rwsem); /* wait for sysfs to drop all references */ - release_resource_db(socket); + if (socket->resource_ops->exit) + socket->resource_ops->exit(socket); wait_for_completion(&socket->socket_released); } /* pcmcia_unregister_socket */ EXPORT_SYMBOL(pcmcia_unregister_socket); -- cgit v1.2.3 From 6b8e087b86c59c3941e125738d30cf38014089e0 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 12 Jan 2010 21:42:51 +0100 Subject: pcmcia: add locking to set_mem_map() Protect the pccard_operations callback "set_mem_map" by a new mutex ops_mutex. This mutex also protects the following values in struct pcmcia_socket: pccard_mem_map win[] pccard_mem_map cis_mem void __iomem *cis_virt Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 43c90f69a7a5..91aa1f284068 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -222,6 +222,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) init_completion(&socket->socket_released); init_completion(&socket->thread_done); mutex_init(&socket->skt_mutex); + mutex_init(&socket->ops_mutex); spin_lock_init(&socket->thread_lock); if (socket->resource_ops->init) { -- cgit v1.2.3 From 9e86749cff70fca505c7c1a9dc760d193f27a059 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 00:26:33 +0100 Subject: pcmcia: lock ops->set_socket As a side effect, socket_state_t socket; u_int state; u_int suspended_state; are properly protected now. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 91aa1f284068..cc0ba8aef59e 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -383,6 +383,8 @@ static void socket_shutdown(struct pcmcia_socket *s) dev_dbg(&s->dev, "shutdown\n"); send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); + + mutex_lock(&s->ops_mutex); s->state &= SOCKET_INUSE | SOCKET_PRESENT; msleep(shutdown_delay * 10); s->state &= SOCKET_INUSE; @@ -410,6 +412,7 @@ static void socket_shutdown(struct pcmcia_socket *s) } s->state &= ~SOCKET_INUSE; + mutex_unlock(&s->ops_mutex); } static int socket_setup(struct pcmcia_socket *skt, int initial_delay) @@ -498,6 +501,7 @@ static int socket_insert(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "insert\n"); + mutex_lock(&skt->ops_mutex); WARN_ON(skt->state & SOCKET_INUSE); skt->state |= SOCKET_INUSE; @@ -517,9 +521,11 @@ static int socket_insert(struct pcmcia_socket *skt) } #endif dev_dbg(&skt->dev, "insert done\n"); + mutex_unlock(&skt->ops_mutex); send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); } else { + mutex_unlock(&skt->ops_mutex); socket_shutdown(skt); } @@ -531,6 +537,7 @@ static int socket_suspend(struct pcmcia_socket *skt) if (skt->state & SOCKET_SUSPEND) return -EBUSY; + mutex_lock(&skt->ops_mutex); skt->suspended_state = skt->state; send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); @@ -539,23 +546,27 @@ static int socket_suspend(struct pcmcia_socket *skt) if (skt->ops->suspend) skt->ops->suspend(skt); skt->state |= SOCKET_SUSPEND; - + mutex_unlock(&skt->ops_mutex); return 0; } static int socket_early_resume(struct pcmcia_socket *skt) { + mutex_lock(&skt->ops_mutex); skt->socket = dead_socket; skt->ops->init(skt); skt->ops->set_socket(skt, &skt->socket); if (skt->state & SOCKET_PRESENT) skt->resume_status = socket_setup(skt, resume_delay); + mutex_unlock(&skt->ops_mutex); return 0; } static int socket_late_resume(struct pcmcia_socket *skt) { + mutex_lock(&skt->ops_mutex); skt->state &= ~SOCKET_SUSPEND; + mutex_unlock(&skt->ops_mutex); if (!(skt->state & SOCKET_PRESENT)) return socket_insert(skt); @@ -795,7 +806,10 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW); if (skt->callback) skt->callback->suspend(skt); - if (socket_reset(skt) == 0) { + mutex_lock(&skt->ops_mutex); + ret = socket_reset(skt); + mutex_unlock(&skt->ops_mutex); + if (ret == 0) { send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW); if (skt->callback) skt->callback->resume(skt); -- cgit v1.2.3 From 00ce99ff506a17882747a7d6874e3f5206a99043 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 09:14:11 +0100 Subject: pcmcia: simplify locking replace pcmcia_socket->lock and pcmcia_dev_list_lock by using the per-socket "ops_mutex", as we do neither need different locks nor a spinlock here. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index cc0ba8aef59e..13277eebe46a 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -175,8 +175,6 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops); - spin_lock_init(&socket->lock); - /* try to obtain a socket number [yes, it gets ugly if we * register more than 2^sizeof(unsigned int) pcmcia * sockets... but the socket number is deprecated -- cgit v1.2.3 From cfe5d809518eda3d5e2da87c5ccbe8647143573a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 19:31:45 +0100 Subject: pcmcia: use ops_mutex for rsrc_{mgr,nonstatic} locking Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 13277eebe46a..7ba45b0cca6b 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -224,7 +224,9 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) spin_lock_init(&socket->thread_lock); if (socket->resource_ops->init) { + mutex_lock(&socket->ops_mutex); ret = socket->resource_ops->init(socket); + mutex_unlock(&socket->ops_mutex); if (ret) goto err; } @@ -282,8 +284,11 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) up_write(&pcmcia_socket_list_rwsem); /* wait for sysfs to drop all references */ - if (socket->resource_ops->exit) + if (socket->resource_ops->exit) { + mutex_lock(&socket->ops_mutex); socket->resource_ops->exit(socket); + mutex_unlock(&socket->ops_mutex); + } wait_for_completion(&socket->socket_released); } /* pcmcia_unregister_socket */ EXPORT_SYMBOL(pcmcia_unregister_socket); -- cgit v1.2.3 From f971dbd5da4e2fbf756d07b938a9c65a9c75178b Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 18:13:31 +0100 Subject: pcmcia: use pccardd to handle eject, insert, suspend and resume requests This avoids any sysfs-related deadlock (or lockdep warning), such as reported at http://lkml.org/lkml/2010/1/17/88 . Reported-by: Ming Lei Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 177 +++++++++++++++++----------------------------------- 1 file changed, 58 insertions(+), 119 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 7ba45b0cca6b..823ecda32216 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -505,7 +505,10 @@ static int socket_insert(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "insert\n"); mutex_lock(&skt->ops_mutex); - WARN_ON(skt->state & SOCKET_INUSE); + if (skt->state & SOCKET_INUSE) { + mutex_unlock(&skt->ops_mutex); + return -EINVAL; + } skt->state |= SOCKET_INUSE; ret = socket_setup(skt, setup_delay); @@ -682,16 +685,19 @@ static int pccardd(void *__skt) for (;;) { unsigned long flags; unsigned int events; + unsigned int sysfs_events; set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&skt->thread_lock, flags); events = skt->thread_events; skt->thread_events = 0; + sysfs_events = skt->sysfs_events; + skt->sysfs_events = 0; spin_unlock_irqrestore(&skt->thread_lock, flags); + mutex_lock(&skt->skt_mutex); if (events) { - mutex_lock(&skt->skt_mutex); if (events & SS_DETECT) socket_detect_change(skt); if (events & SS_BATDEAD) @@ -700,10 +706,34 @@ static int pccardd(void *__skt) send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); if (events & SS_READY) send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); - mutex_unlock(&skt->skt_mutex); - continue; } + if (sysfs_events) { + if (sysfs_events & PCMCIA_UEVENT_EJECT) + socket_remove(skt); + if (sysfs_events & PCMCIA_UEVENT_INSERT) + socket_insert(skt); + if ((sysfs_events & PCMCIA_UEVENT_RESUME) && + !(skt->state & SOCKET_CARDBUS)) { + ret = socket_resume(skt); + if (!ret && skt->callback) + skt->callback->resume(skt); + } + if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) && + !(skt->state & SOCKET_CARDBUS)) { + if (skt->callback) + ret = skt->callback->suspend(skt); + else + ret = 0; + if (!ret) + socket_suspend(skt); + } + } + mutex_unlock(&skt->skt_mutex); + + if (events || sysfs_events) + continue; + if (kthread_should_stop()) break; @@ -745,6 +775,30 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) } /* pcmcia_parse_events */ EXPORT_SYMBOL(pcmcia_parse_events); +/** + * pcmcia_parse_uevents() - tell pccardd to issue manual commands + * @s: the PCMCIA socket we wan't to command + * @events: events to pass to pccardd + * + * 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). + */ +void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) +{ + unsigned long flags; + dev_dbg(&s->dev, "parse_uevents: events %08x\n", events); + if (s->thread) { + spin_lock_irqsave(&s->thread_lock, flags); + s->sysfs_events |= events; + spin_unlock_irqrestore(&s->thread_lock, flags); + + wake_up_process(s->thread); + } +} +EXPORT_SYMBOL(pcmcia_parse_uevents); + /* register pcmcia_callback */ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) @@ -828,121 +882,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) EXPORT_SYMBOL(pcmcia_reset_card); -/* These shut down or wake up a socket. They are sort of user - * initiated versions of the APM suspend and resume actions. - */ -int pcmcia_suspend_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "suspending socket\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - if (skt->state & SOCKET_CARDBUS) { - ret = -EPERM; - break; - } - if (skt->callback) { - ret = skt->callback->suspend(skt); - if (ret) - break; - } - ret = socket_suspend(skt); - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* suspend_card */ -EXPORT_SYMBOL(pcmcia_suspend_card); - - -int pcmcia_resume_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "waking up socket\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - if (skt->state & SOCKET_CARDBUS) { - ret = -EPERM; - break; - } - ret = socket_resume(skt); - if (!ret && skt->callback) - skt->callback->resume(skt); - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* resume_card */ -EXPORT_SYMBOL(pcmcia_resume_card); - - -/* These handle user requests to eject or insert a card. */ -int pcmcia_eject_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "user eject request\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - - ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW); - if (ret != 0) { - ret = -EINVAL; - break; - } - - socket_remove(skt); - ret = 0; - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* eject_card */ -EXPORT_SYMBOL(pcmcia_eject_card); - - -int pcmcia_insert_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "user insert request\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (skt->state & SOCKET_PRESENT) { - ret = -EBUSY; - break; - } - if (socket_insert(skt) == -ENODEV) { - ret = -ENODEV; - break; - } - ret = 0; - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* insert_card */ -EXPORT_SYMBOL(pcmcia_insert_card); - - static int pcmcia_socket_uevent(struct device *dev, struct kobj_uevent_env *env) { -- cgit v1.2.3 From af461fc1875b6ec18e23b5f670af36c4ed35c84e Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 19:30:53 +0100 Subject: 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/cs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/pcmcia/cs.c') 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) { -- cgit v1.2.3 From c3bfc96ef7366aa996fb8286a36f3333a3b4ff25 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 18 Jan 2010 08:43:39 +0100 Subject: pcmcia: avoid sysfs-related lockup for cardbus In cb_free(), we remove some sysfs files -- other sysfs files might grab ops_mutex, so we cannot hold it while removing sysfs files. This fixes http://lkml.org/lkml/2010/1/17/88 . Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index d529e02c2d54..9a49c394f04f 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -400,10 +400,19 @@ static void socket_shutdown(struct pcmcia_socket *s) s->lock_count = 0; kfree(s->fake_cis); s->fake_cis = NULL; + s->functions = 0; + + /* From here on we can be sure that only we (that is, the + * pccardd thread) accesses this socket, and all (16-bit) + * PCMCIA interactions are gone. Therefore, release + * ops_mutex so that we don't get a sysfs-related lockdep + * warning. + */ + mutex_unlock(&s->ops_mutex); + #ifdef CONFIG_CARDBUS cb_free(s); #endif - s->functions = 0; /* give socket some time to power down */ msleep(100); @@ -415,7 +424,6 @@ static void socket_shutdown(struct pcmcia_socket *s) } s->state &= ~SOCKET_INUSE; - mutex_unlock(&s->ops_mutex); } static int socket_setup(struct pcmcia_socket *skt, int initial_delay) -- cgit v1.2.3 From d801c1409ef7d45339cbe8ac9de28ade6ed4699a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 17 Feb 2010 11:02:22 +0100 Subject: pcmcia: remove useless msleep in ds.c As this is the socket thread (pccardd) starting up, we do not have anything to wait for in ds.c. Instead, wait the same amount of time in pccardd to allow userspace to catch up and - possibly - execute pcmcia-socket-startup. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pcmcia/cs.c') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 9a49c394f04f..e679e708db63 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -689,6 +689,9 @@ static int pccardd(void *__skt) complete(&skt->thread_done); + /* wait for userspace to catch up */ + msleep(250); + set_freezable(); for (;;) { unsigned long flags; -- cgit v1.2.3