aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-03-02 04:51:00 -0500
committerJens Axboe <axboe@kernel.dk>2012-03-02 04:51:00 -0500
commit62d3c5439c534b0e6c653fc63e6d8c67be3a57b1 (patch)
treed335d0e449ef2d61d52921e3f210cdd403bb025a
parentcecd353a02fb1405c8a72a324b26b5acf97e7411 (diff)
Block: use a freezable workqueue for disk-event polling
This patch (as1519) fixes a bug in the block layer's disk-events polling. The polling is done by a work routine queued on the system_nrt_wq workqueue. Since that workqueue isn't freezable, the polling continues even in the middle of a system sleep transition. Obviously, polling a suspended drive for media changes and such isn't a good thing to do; in the case of USB mass-storage devices it can lead to real problems requiring device resets and even re-enumeration. The patch fixes things by creating a new system-wide, non-reentrant, freezable workqueue and using it for disk-events polling. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> CC: <stable@kernel.org> Acked-by: Tejun Heo <tj@kernel.org> Acked-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--block/genhd.c10
-rw-r--r--include/linux/workqueue.h4
-rw-r--r--kernel/workqueue.c7
3 files changed, 15 insertions, 6 deletions
diff --git a/block/genhd.c b/block/genhd.c
index b26c4085590d..df9816ede75b 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1478,9 +1478,9 @@ static void __disk_unblock_events(struct gendisk *disk, bool check_now)
1478 intv = disk_events_poll_jiffies(disk); 1478 intv = disk_events_poll_jiffies(disk);
1479 set_timer_slack(&ev->dwork.timer, intv / 4); 1479 set_timer_slack(&ev->dwork.timer, intv / 4);
1480 if (check_now) 1480 if (check_now)
1481 queue_delayed_work(system_nrt_wq, &ev->dwork, 0); 1481 queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
1482 else if (intv) 1482 else if (intv)
1483 queue_delayed_work(system_nrt_wq, &ev->dwork, intv); 1483 queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv);
1484out_unlock: 1484out_unlock:
1485 spin_unlock_irqrestore(&ev->lock, flags); 1485 spin_unlock_irqrestore(&ev->lock, flags);
1486} 1486}
@@ -1524,7 +1524,7 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask)
1524 ev->clearing |= mask; 1524 ev->clearing |= mask;
1525 if (!ev->block) { 1525 if (!ev->block) {
1526 cancel_delayed_work(&ev->dwork); 1526 cancel_delayed_work(&ev->dwork);
1527 queue_delayed_work(system_nrt_wq, &ev->dwork, 0); 1527 queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
1528 } 1528 }
1529 spin_unlock_irq(&ev->lock); 1529 spin_unlock_irq(&ev->lock);
1530} 1530}
@@ -1561,7 +1561,7 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask)
1561 1561
1562 /* uncondtionally schedule event check and wait for it to finish */ 1562 /* uncondtionally schedule event check and wait for it to finish */
1563 disk_block_events(disk); 1563 disk_block_events(disk);
1564 queue_delayed_work(system_nrt_wq, &ev->dwork, 0); 1564 queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
1565 flush_delayed_work(&ev->dwork); 1565 flush_delayed_work(&ev->dwork);
1566 __disk_unblock_events(disk, false); 1566 __disk_unblock_events(disk, false);
1567 1567
@@ -1598,7 +1598,7 @@ static void disk_events_workfn(struct work_struct *work)
1598 1598
1599 intv = disk_events_poll_jiffies(disk); 1599 intv = disk_events_poll_jiffies(disk);
1600 if (!ev->block && intv) 1600 if (!ev->block && intv)
1601 queue_delayed_work(system_nrt_wq, &ev->dwork, intv); 1601 queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv);
1602 1602
1603 spin_unlock_irq(&ev->lock); 1603 spin_unlock_irq(&ev->lock);
1604 1604
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index eb8b9f15f2e0..af155450cabb 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -289,12 +289,16 @@ enum {
289 * 289 *
290 * system_freezable_wq is equivalent to system_wq except that it's 290 * system_freezable_wq is equivalent to system_wq except that it's
291 * freezable. 291 * freezable.
292 *
293 * system_nrt_freezable_wq is equivalent to system_nrt_wq except that
294 * it's freezable.
292 */ 295 */
293extern struct workqueue_struct *system_wq; 296extern struct workqueue_struct *system_wq;
294extern struct workqueue_struct *system_long_wq; 297extern struct workqueue_struct *system_long_wq;
295extern struct workqueue_struct *system_nrt_wq; 298extern struct workqueue_struct *system_nrt_wq;
296extern struct workqueue_struct *system_unbound_wq; 299extern struct workqueue_struct *system_unbound_wq;
297extern struct workqueue_struct *system_freezable_wq; 300extern struct workqueue_struct *system_freezable_wq;
301extern struct workqueue_struct *system_nrt_freezable_wq;
298 302
299extern struct workqueue_struct * 303extern struct workqueue_struct *
300__alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, 304__alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index bec7b5b53e03..f2c5638bb5ab 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -253,11 +253,13 @@ struct workqueue_struct *system_long_wq __read_mostly;
253struct workqueue_struct *system_nrt_wq __read_mostly; 253struct workqueue_struct *system_nrt_wq __read_mostly;
254struct workqueue_struct *system_unbound_wq __read_mostly; 254struct workqueue_struct *system_unbound_wq __read_mostly;
255struct workqueue_struct *system_freezable_wq __read_mostly; 255struct workqueue_struct *system_freezable_wq __read_mostly;
256struct workqueue_struct *system_nrt_freezable_wq __read_mostly;
256EXPORT_SYMBOL_GPL(system_wq); 257EXPORT_SYMBOL_GPL(system_wq);
257EXPORT_SYMBOL_GPL(system_long_wq); 258EXPORT_SYMBOL_GPL(system_long_wq);
258EXPORT_SYMBOL_GPL(system_nrt_wq); 259EXPORT_SYMBOL_GPL(system_nrt_wq);
259EXPORT_SYMBOL_GPL(system_unbound_wq); 260EXPORT_SYMBOL_GPL(system_unbound_wq);
260EXPORT_SYMBOL_GPL(system_freezable_wq); 261EXPORT_SYMBOL_GPL(system_freezable_wq);
262EXPORT_SYMBOL_GPL(system_nrt_freezable_wq);
261 263
262#define CREATE_TRACE_POINTS 264#define CREATE_TRACE_POINTS
263#include <trace/events/workqueue.h> 265#include <trace/events/workqueue.h>
@@ -3833,8 +3835,11 @@ static int __init init_workqueues(void)
3833 WQ_UNBOUND_MAX_ACTIVE); 3835 WQ_UNBOUND_MAX_ACTIVE);
3834 system_freezable_wq = alloc_workqueue("events_freezable", 3836 system_freezable_wq = alloc_workqueue("events_freezable",
3835 WQ_FREEZABLE, 0); 3837 WQ_FREEZABLE, 0);
3838 system_nrt_freezable_wq = alloc_workqueue("events_nrt_freezable",
3839 WQ_NON_REENTRANT | WQ_FREEZABLE, 0);
3836 BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq || 3840 BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq ||
3837 !system_unbound_wq || !system_freezable_wq); 3841 !system_unbound_wq || !system_freezable_wq ||
3842 !system_nrt_freezable_wq);
3838 return 0; 3843 return 0;
3839} 3844}
3840early_initcall(init_workqueues); 3845early_initcall(init_workqueues);