diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2006-10-04 05:15:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-04 10:55:15 -0400 |
commit | 7104e2d5a85b4b786d6a63568beffe1e185547bb (patch) | |
tree | 1478b1076a66476a121e687dd9547d5c08d9c3ba | |
parent | 8a4da1430f7f2a16df3be9c7b5d55ba4e75b708c (diff) |
[PATCH] mmc: use own work queue
The MMC layer uses the standard work queue for doing card detection. As this
queue is shared with other crucial subsystems, the effects of a long (and
perhaps buggy) detection can cause the system to be unusable. E.g. the
keyboard stops working while the detection routine is running.
The solution is to add a specific mmc work queue to run the detection code in.
This is similar to how other subsystems handle detection (a full kernel
thread is the most common theme).
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | drivers/mmc/mmc.c | 6 | ||||
-rw-r--r-- | drivers/mmc/mmc.h | 4 | ||||
-rw-r--r-- | drivers/mmc/mmc_sysfs.c | 35 |
3 files changed, 41 insertions, 4 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5b9caa7978d3..ee8863c123e3 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c | |||
@@ -1166,9 +1166,9 @@ static void mmc_setup(struct mmc_host *host) | |||
1166 | void mmc_detect_change(struct mmc_host *host, unsigned long delay) | 1166 | void mmc_detect_change(struct mmc_host *host, unsigned long delay) |
1167 | { | 1167 | { |
1168 | if (delay) | 1168 | if (delay) |
1169 | schedule_delayed_work(&host->detect, delay); | 1169 | mmc_schedule_delayed_work(&host->detect, delay); |
1170 | else | 1170 | else |
1171 | schedule_work(&host->detect); | 1171 | mmc_schedule_work(&host->detect); |
1172 | } | 1172 | } |
1173 | 1173 | ||
1174 | EXPORT_SYMBOL(mmc_detect_change); | 1174 | EXPORT_SYMBOL(mmc_detect_change); |
@@ -1311,7 +1311,7 @@ EXPORT_SYMBOL(mmc_remove_host); | |||
1311 | */ | 1311 | */ |
1312 | void mmc_free_host(struct mmc_host *host) | 1312 | void mmc_free_host(struct mmc_host *host) |
1313 | { | 1313 | { |
1314 | flush_scheduled_work(); | 1314 | mmc_flush_scheduled_work(); |
1315 | mmc_free_host_sysfs(host); | 1315 | mmc_free_host_sysfs(host); |
1316 | } | 1316 | } |
1317 | 1317 | ||
diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h index 97bae00292fa..cd5e0ab3d84b 100644 --- a/drivers/mmc/mmc.h +++ b/drivers/mmc/mmc.h | |||
@@ -18,4 +18,8 @@ struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); | |||
18 | int mmc_add_host_sysfs(struct mmc_host *host); | 18 | int mmc_add_host_sysfs(struct mmc_host *host); |
19 | void mmc_remove_host_sysfs(struct mmc_host *host); | 19 | void mmc_remove_host_sysfs(struct mmc_host *host); |
20 | void mmc_free_host_sysfs(struct mmc_host *host); | 20 | void mmc_free_host_sysfs(struct mmc_host *host); |
21 | |||
22 | int mmc_schedule_work(struct work_struct *work); | ||
23 | int mmc_schedule_delayed_work(struct work_struct *work, unsigned long delay); | ||
24 | void mmc_flush_scheduled_work(void); | ||
21 | #endif | 25 | #endif |
diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index a2a35fd946ee..10cc9734eaa0 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/device.h> | 14 | #include <linux/device.h> |
15 | #include <linux/idr.h> | 15 | #include <linux/idr.h> |
16 | #include <linux/workqueue.h> | ||
16 | 17 | ||
17 | #include <linux/mmc/card.h> | 18 | #include <linux/mmc/card.h> |
18 | #include <linux/mmc/host.h> | 19 | #include <linux/mmc/host.h> |
@@ -317,10 +318,41 @@ void mmc_free_host_sysfs(struct mmc_host *host) | |||
317 | class_device_put(&host->class_dev); | 318 | class_device_put(&host->class_dev); |
318 | } | 319 | } |
319 | 320 | ||
321 | static struct workqueue_struct *workqueue; | ||
322 | |||
323 | /* | ||
324 | * Internal function. Schedule work in the MMC work queue. | ||
325 | */ | ||
326 | int mmc_schedule_work(struct work_struct *work) | ||
327 | { | ||
328 | return queue_work(workqueue, work); | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Internal function. Schedule delayed work in the MMC work queue. | ||
333 | */ | ||
334 | int mmc_schedule_delayed_work(struct work_struct *work, unsigned long delay) | ||
335 | { | ||
336 | return queue_delayed_work(workqueue, work, delay); | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * Internal function. Flush all scheduled work from the MMC work queue. | ||
341 | */ | ||
342 | void mmc_flush_scheduled_work(void) | ||
343 | { | ||
344 | flush_workqueue(workqueue); | ||
345 | } | ||
320 | 346 | ||
321 | static int __init mmc_init(void) | 347 | static int __init mmc_init(void) |
322 | { | 348 | { |
323 | int ret = bus_register(&mmc_bus_type); | 349 | int ret; |
350 | |||
351 | workqueue = create_singlethread_workqueue("kmmcd"); | ||
352 | if (!workqueue) | ||
353 | return -ENOMEM; | ||
354 | |||
355 | ret = bus_register(&mmc_bus_type); | ||
324 | if (ret == 0) { | 356 | if (ret == 0) { |
325 | ret = class_register(&mmc_host_class); | 357 | ret = class_register(&mmc_host_class); |
326 | if (ret) | 358 | if (ret) |
@@ -333,6 +365,7 @@ static void __exit mmc_exit(void) | |||
333 | { | 365 | { |
334 | class_unregister(&mmc_host_class); | 366 | class_unregister(&mmc_host_class); |
335 | bus_unregister(&mmc_bus_type); | 367 | bus_unregister(&mmc_bus_type); |
368 | destroy_workqueue(workqueue); | ||
336 | } | 369 | } |
337 | 370 | ||
338 | module_init(mmc_init); | 371 | module_init(mmc_init); |