mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
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>
This commit is contained in:
parent
cecd353a02
commit
62d3c5439c
3 changed files with 15 additions and 6 deletions
|
@ -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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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)
|
||||||
|
|
||||||
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 {
|
||||||
*
|
*
|
||||||
* 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;
|
||||||
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)
|
||||||
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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue