aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxim Levitsky <maximlevitsky@gmail.com>2010-08-10 21:01:41 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-11 11:59:03 -0400
commit4c2ef25fe0b847d2ae818f74758ddb0be1c27d8e (patch)
tree76dd136c6248c7bfffbf6663951e9e6b38238f20
parent7310ece86ad7da027f85a37a0638164118a5d12f (diff)
mmc: fix all hangs related to mmc/sd card insert/removal during suspend/resume
If you don't use CONFIG_MMC_UNSAFE_RESUME, as soon as you attempt to suspend, the card will be removed, therefore this patch doesn't change the behavior of this option. However the removal will be done by pm notifier, which runs while userspace is still not frozen and thus can freely use del_gendisk, without the risk of deadlock which would happen otherwise. Card detect workqueue is now disabled while userspace is frozen, Therefore if you do use CONFIG_MMC_UNSAFE_RESUME, and remove the card during suspend, the removal will be detected as soon as userspace is unfrozen, again at the moment it is safe to call del_gendisk. Tested with and without CONFIG_MMC_UNSAFE_RESUME with suspend and hibernate. [akpm@linux-foundation.org: clean up function prototype] [akpm@linux-foundation.org: fix CONFIG_PM-n linkage, small cleanups] [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com> Cc: David Brownell <david-b@pacbell.net> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: <linux-mmc@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/mmc/core/core.c83
-rw-r--r--drivers/mmc/core/host.c4
-rw-r--r--include/linux/mmc/host.h3
3 files changed, 64 insertions, 26 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index b69ce91b11e1..83240faa1dc8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1057,6 +1057,17 @@ void mmc_rescan(struct work_struct *work)
1057 container_of(work, struct mmc_host, detect.work); 1057 container_of(work, struct mmc_host, detect.work);
1058 u32 ocr; 1058 u32 ocr;
1059 int err; 1059 int err;
1060 unsigned long flags;
1061
1062 spin_lock_irqsave(&host->lock, flags);
1063
1064 if (host->rescan_disable) {
1065 spin_unlock_irqrestore(&host->lock, flags);
1066 return;
1067 }
1068
1069 spin_unlock_irqrestore(&host->lock, flags);
1070
1060 1071
1061 mmc_bus_get(host); 1072 mmc_bus_get(host);
1062 1073
@@ -1274,19 +1285,6 @@ int mmc_suspend_host(struct mmc_host *host)
1274 if (host->bus_ops && !host->bus_dead) { 1285 if (host->bus_ops && !host->bus_dead) {
1275 if (host->bus_ops->suspend) 1286 if (host->bus_ops->suspend)
1276 err = host->bus_ops->suspend(host); 1287 err = host->bus_ops->suspend(host);
1277 if (err == -ENOSYS || !host->bus_ops->resume) {
1278 /*
1279 * We simply "remove" the card in this case.
1280 * It will be redetected on resume.
1281 */
1282 if (host->bus_ops->remove)
1283 host->bus_ops->remove(host);
1284 mmc_claim_host(host);
1285 mmc_detach_bus(host);
1286 mmc_release_host(host);
1287 host->pm_flags = 0;
1288 err = 0;
1289 }
1290 } 1288 }
1291 mmc_bus_put(host); 1289 mmc_bus_put(host);
1292 1290
@@ -1318,28 +1316,61 @@ int mmc_resume_host(struct mmc_host *host)
1318 printk(KERN_WARNING "%s: error %d during resume " 1316 printk(KERN_WARNING "%s: error %d during resume "
1319 "(card was removed?)\n", 1317 "(card was removed?)\n",
1320 mmc_hostname(host), err); 1318 mmc_hostname(host), err);
1321 if (host->bus_ops->remove)
1322 host->bus_ops->remove(host);
1323 mmc_claim_host(host);
1324 mmc_detach_bus(host);
1325 mmc_release_host(host);
1326 /* no need to bother upper layers */
1327 err = 0; 1319 err = 0;
1328 } 1320 }
1329 } 1321 }
1330 mmc_bus_put(host); 1322 mmc_bus_put(host);
1331 1323
1332 /*
1333 * We add a slight delay here so that resume can progress
1334 * in parallel.
1335 */
1336 mmc_detect_change(host, 1);
1337
1338 return err; 1324 return err;
1339} 1325}
1340
1341EXPORT_SYMBOL(mmc_resume_host); 1326EXPORT_SYMBOL(mmc_resume_host);
1342 1327
1328/* Do the card removal on suspend if card is assumed removeable
1329 * Do that in pm notifier while userspace isn't yet frozen, so we will be able
1330 to sync the card.
1331*/
1332int mmc_pm_notify(struct notifier_block *notify_block,
1333 unsigned long mode, void *unused)
1334{
1335 struct mmc_host *host = container_of(
1336 notify_block, struct mmc_host, pm_notify);
1337 unsigned long flags;
1338
1339
1340 switch (mode) {
1341 case PM_HIBERNATION_PREPARE:
1342 case PM_SUSPEND_PREPARE:
1343
1344 spin_lock_irqsave(&host->lock, flags);
1345 host->rescan_disable = 1;
1346 spin_unlock_irqrestore(&host->lock, flags);
1347 cancel_delayed_work_sync(&host->detect);
1348
1349 if (!host->bus_ops || host->bus_ops->suspend)
1350 break;
1351
1352 mmc_claim_host(host);
1353
1354 if (host->bus_ops->remove)
1355 host->bus_ops->remove(host);
1356
1357 mmc_detach_bus(host);
1358 mmc_release_host(host);
1359 host->pm_flags = 0;
1360 break;
1361
1362 case PM_POST_SUSPEND:
1363 case PM_POST_HIBERNATION:
1364
1365 spin_lock_irqsave(&host->lock, flags);
1366 host->rescan_disable = 0;
1367 spin_unlock_irqrestore(&host->lock, flags);
1368 mmc_detect_change(host, 0);
1369
1370 }
1371
1372 return 0;
1373}
1343#endif 1374#endif
1344 1375
1345static int __init mmc_init(void) 1376static int __init mmc_init(void)
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 47353909e345..0efe631e50ca 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -17,6 +17,7 @@
17#include <linux/pagemap.h> 17#include <linux/pagemap.h>
18#include <linux/leds.h> 18#include <linux/leds.h>
19#include <linux/slab.h> 19#include <linux/slab.h>
20#include <linux/suspend.h>
20 21
21#include <linux/mmc/host.h> 22#include <linux/mmc/host.h>
22 23
@@ -85,6 +86,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
85 init_waitqueue_head(&host->wq); 86 init_waitqueue_head(&host->wq);
86 INIT_DELAYED_WORK(&host->detect, mmc_rescan); 87 INIT_DELAYED_WORK(&host->detect, mmc_rescan);
87 INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); 88 INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
89 host->pm_notify.notifier_call = mmc_pm_notify;
88 90
89 /* 91 /*
90 * By default, hosts do not support SGIO or large requests. 92 * By default, hosts do not support SGIO or large requests.
@@ -133,6 +135,7 @@ int mmc_add_host(struct mmc_host *host)
133#endif 135#endif
134 136
135 mmc_start_host(host); 137 mmc_start_host(host);
138 register_pm_notifier(&host->pm_notify);
136 139
137 return 0; 140 return 0;
138} 141}
@@ -149,6 +152,7 @@ EXPORT_SYMBOL(mmc_add_host);
149 */ 152 */
150void mmc_remove_host(struct mmc_host *host) 153void mmc_remove_host(struct mmc_host *host)
151{ 154{
155 unregister_pm_notifier(&host->pm_notify);
152 mmc_stop_host(host); 156 mmc_stop_host(host);
153 157
154#ifdef CONFIG_DEBUG_FS 158#ifdef CONFIG_DEBUG_FS
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f65913c9f5a4..513ff0376b09 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -124,6 +124,7 @@ struct mmc_host {
124 unsigned int f_min; 124 unsigned int f_min;
125 unsigned int f_max; 125 unsigned int f_max;
126 u32 ocr_avail; 126 u32 ocr_avail;
127 struct notifier_block pm_notify;
127 128
128#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ 129#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
129#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ 130#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
@@ -183,6 +184,7 @@ struct mmc_host {
183 184
184 /* Only used with MMC_CAP_DISABLE */ 185 /* Only used with MMC_CAP_DISABLE */
185 int enabled; /* host is enabled */ 186 int enabled; /* host is enabled */
187 int rescan_disable; /* disable card detection */
186 int nesting_cnt; /* "enable" nesting count */ 188 int nesting_cnt; /* "enable" nesting count */
187 int en_dis_recurs; /* detect recursion */ 189 int en_dis_recurs; /* detect recursion */
188 unsigned int disable_delay; /* disable delay in msecs */ 190 unsigned int disable_delay; /* disable delay in msecs */
@@ -257,6 +259,7 @@ int mmc_card_can_sleep(struct mmc_host *host);
257int mmc_host_enable(struct mmc_host *host); 259int mmc_host_enable(struct mmc_host *host);
258int mmc_host_disable(struct mmc_host *host); 260int mmc_host_disable(struct mmc_host *host);
259int mmc_host_lazy_disable(struct mmc_host *host); 261int mmc_host_lazy_disable(struct mmc_host *host);
262int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
260 263
261static inline void mmc_set_disable_delay(struct mmc_host *host, 264static inline void mmc_set_disable_delay(struct mmc_host *host,
262 unsigned int disable_delay) 265 unsigned int disable_delay)