提交 62d3c543 编写于 作者: A Alan Stern 提交者: Jens Axboe

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: NAlan Stern <stern@rowland.harvard.edu>
CC: <stable@kernel.org>
Acked-by: NTejun Heo <tj@kernel.org>
Acked-by: NRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: NJens Axboe <axboe@kernel.dk>
上级 cecd353a
...@@ -1478,9 +1478,9 @@ static void __disk_unblock_events(struct gendisk *disk, bool check_now) ...@@ -1478,9 +1478,9 @@ static void __disk_unblock_events(struct gendisk *disk, bool check_now)
intv = disk_events_poll_jiffies(disk); intv = disk_events_poll_jiffies(disk);
set_timer_slack(&ev->dwork.timer, intv / 4); set_timer_slack(&ev->dwork.timer, intv / 4);
if (check_now) if (check_now)
queue_delayed_work(system_nrt_wq, &ev->dwork, 0); queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
else if (intv) else if (intv)
queue_delayed_work(system_nrt_wq, &ev->dwork, intv); queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv);
out_unlock: out_unlock:
spin_unlock_irqrestore(&ev->lock, flags); spin_unlock_irqrestore(&ev->lock, flags);
} }
...@@ -1524,7 +1524,7 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask) ...@@ -1524,7 +1524,7 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask)
ev->clearing |= mask; ev->clearing |= mask;
if (!ev->block) { if (!ev->block) {
cancel_delayed_work(&ev->dwork); cancel_delayed_work(&ev->dwork);
queue_delayed_work(system_nrt_wq, &ev->dwork, 0); queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
} }
spin_unlock_irq(&ev->lock); spin_unlock_irq(&ev->lock);
} }
...@@ -1561,7 +1561,7 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) ...@@ -1561,7 +1561,7 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask)
/* uncondtionally schedule event check and wait for it to finish */ /* uncondtionally schedule event check and wait for it to finish */
disk_block_events(disk); disk_block_events(disk);
queue_delayed_work(system_nrt_wq, &ev->dwork, 0); queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
flush_delayed_work(&ev->dwork); flush_delayed_work(&ev->dwork);
__disk_unblock_events(disk, false); __disk_unblock_events(disk, false);
...@@ -1598,7 +1598,7 @@ static void disk_events_workfn(struct work_struct *work) ...@@ -1598,7 +1598,7 @@ static void disk_events_workfn(struct work_struct *work)
intv = disk_events_poll_jiffies(disk); intv = disk_events_poll_jiffies(disk);
if (!ev->block && intv) if (!ev->block && intv)
queue_delayed_work(system_nrt_wq, &ev->dwork, intv); queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv);
spin_unlock_irq(&ev->lock); spin_unlock_irq(&ev->lock);
......
...@@ -289,12 +289,16 @@ enum { ...@@ -289,12 +289,16 @@ enum {
* *
* system_freezable_wq is equivalent to system_wq except that it's * system_freezable_wq is equivalent to system_wq except that it's
* freezable. * freezable.
*
* system_nrt_freezable_wq is equivalent to system_nrt_wq except that
* it's freezable.
*/ */
extern struct workqueue_struct *system_wq; extern struct workqueue_struct *system_wq;
extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_long_wq;
extern struct workqueue_struct *system_nrt_wq; extern struct workqueue_struct *system_nrt_wq;
extern struct workqueue_struct *system_unbound_wq; extern struct workqueue_struct *system_unbound_wq;
extern struct workqueue_struct *system_freezable_wq; extern struct workqueue_struct *system_freezable_wq;
extern struct workqueue_struct *system_nrt_freezable_wq;
extern struct workqueue_struct * extern struct workqueue_struct *
__alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
......
...@@ -253,11 +253,13 @@ struct workqueue_struct *system_long_wq __read_mostly; ...@@ -253,11 +253,13 @@ struct workqueue_struct *system_long_wq __read_mostly;
struct workqueue_struct *system_nrt_wq __read_mostly; struct workqueue_struct *system_nrt_wq __read_mostly;
struct workqueue_struct *system_unbound_wq __read_mostly; struct workqueue_struct *system_unbound_wq __read_mostly;
struct workqueue_struct *system_freezable_wq __read_mostly; struct workqueue_struct *system_freezable_wq __read_mostly;
struct workqueue_struct *system_nrt_freezable_wq __read_mostly;
EXPORT_SYMBOL_GPL(system_wq); EXPORT_SYMBOL_GPL(system_wq);
EXPORT_SYMBOL_GPL(system_long_wq); EXPORT_SYMBOL_GPL(system_long_wq);
EXPORT_SYMBOL_GPL(system_nrt_wq); EXPORT_SYMBOL_GPL(system_nrt_wq);
EXPORT_SYMBOL_GPL(system_unbound_wq); EXPORT_SYMBOL_GPL(system_unbound_wq);
EXPORT_SYMBOL_GPL(system_freezable_wq); EXPORT_SYMBOL_GPL(system_freezable_wq);
EXPORT_SYMBOL_GPL(system_nrt_freezable_wq);
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <trace/events/workqueue.h> #include <trace/events/workqueue.h>
...@@ -3833,8 +3835,11 @@ static int __init init_workqueues(void) ...@@ -3833,8 +3835,11 @@ static int __init init_workqueues(void)
WQ_UNBOUND_MAX_ACTIVE); WQ_UNBOUND_MAX_ACTIVE);
system_freezable_wq = alloc_workqueue("events_freezable", system_freezable_wq = alloc_workqueue("events_freezable",
WQ_FREEZABLE, 0); WQ_FREEZABLE, 0);
system_nrt_freezable_wq = alloc_workqueue("events_nrt_freezable",
WQ_NON_REENTRANT | WQ_FREEZABLE, 0);
BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq || BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq ||
!system_unbound_wq || !system_freezable_wq); !system_unbound_wq || !system_freezable_wq ||
!system_nrt_freezable_wq);
return 0; return 0;
} }
early_initcall(init_workqueues); early_initcall(init_workqueues);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册