Skip to content

Commit 4c2ef25

Browse files
maximlevitskytorvalds
authored andcommitted
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>
1 parent 7310ece commit 4c2ef25

File tree

3 files changed

+64
-26
lines changed

3 files changed

+64
-26
lines changed

drivers/mmc/core/core.c

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,17 @@ void mmc_rescan(struct work_struct *work)
10571057
container_of(work, struct mmc_host, detect.work);
10581058
u32 ocr;
10591059
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+
10601071

10611072
mmc_bus_get(host);
10621073

@@ -1274,19 +1285,6 @@ int mmc_suspend_host(struct mmc_host *host)
12741285
if (host->bus_ops && !host->bus_dead) {
12751286
if (host->bus_ops->suspend)
12761287
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-
}
12901288
}
12911289
mmc_bus_put(host);
12921290

@@ -1318,28 +1316,61 @@ int mmc_resume_host(struct mmc_host *host)
13181316
printk(KERN_WARNING "%s: error %d during resume "
13191317
"(card was removed?)\n",
13201318
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 */
13271319
err = 0;
13281320
}
13291321
}
13301322
mmc_bus_put(host);
13311323

1332-
/*
1333-
* We add a slight delay here so that resume can progress
1334-
* in parallel.
1335-
*/
1336-
mmc_detect_change(host, 1);
1337-
13381324
return err;
13391325
}
1340-
13411326
EXPORT_SYMBOL(mmc_resume_host);
13421327

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+
*/
1332+
int 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+
}
13431374
#endif
13441375

13451376
static int __init mmc_init(void)

drivers/mmc/core/host.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/pagemap.h>
1818
#include <linux/leds.h>
1919
#include <linux/slab.h>
20+
#include <linux/suspend.h>
2021

2122
#include <linux/mmc/host.h>
2223

@@ -85,6 +86,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
8586
init_waitqueue_head(&host->wq);
8687
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
8788
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
89+
host->pm_notify.notifier_call = mmc_pm_notify;
8890

8991
/*
9092
* By default, hosts do not support SGIO or large requests.
@@ -133,6 +135,7 @@ int mmc_add_host(struct mmc_host *host)
133135
#endif
134136

135137
mmc_start_host(host);
138+
register_pm_notifier(&host->pm_notify);
136139

137140
return 0;
138141
}
@@ -149,6 +152,7 @@ EXPORT_SYMBOL(mmc_add_host);
149152
*/
150153
void mmc_remove_host(struct mmc_host *host)
151154
{
155+
unregister_pm_notifier(&host->pm_notify);
152156
mmc_stop_host(host);
153157

154158
#ifdef CONFIG_DEBUG_FS

include/linux/mmc/host.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ struct mmc_host {
124124
unsigned int f_min;
125125
unsigned int f_max;
126126
u32 ocr_avail;
127+
struct notifier_block pm_notify;
127128

128129
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
129130
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
@@ -183,6 +184,7 @@ struct mmc_host {
183184

184185
/* Only used with MMC_CAP_DISABLE */
185186
int enabled; /* host is enabled */
187+
int rescan_disable; /* disable card detection */
186188
int nesting_cnt; /* "enable" nesting count */
187189
int en_dis_recurs; /* detect recursion */
188190
unsigned int disable_delay; /* disable delay in msecs */
@@ -257,6 +259,7 @@ int mmc_card_can_sleep(struct mmc_host *host);
257259
int mmc_host_enable(struct mmc_host *host);
258260
int mmc_host_disable(struct mmc_host *host);
259261
int mmc_host_lazy_disable(struct mmc_host *host);
262+
int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
260263

261264
static inline void mmc_set_disable_delay(struct mmc_host *host,
262265
unsigned int disable_delay)

0 commit comments

Comments
 (0)