--- /dev/null
+From 62d3c5439c534b0e6c653fc63e6d8c67be3a57b1 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 2 Mar 2012 10:51:00 +0100
+Subject: Block: use a freezable workqueue for disk-event polling
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 62d3c5439c534b0e6c653fc63e6d8c67be3a57b1 upstream.
+
+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>
+Acked-by: Tejun Heo <tj@kernel.org>
+Acked-by: Rafael J. Wysocki <rjw@sisk.pl>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ block/genhd.c | 10 +++++-----
+ include/linux/workqueue.h | 4 ++++
+ kernel/workqueue.c | 7 ++++++-
+ 3 files changed, 15 insertions(+), 6 deletions(-)
+
+--- a/block/genhd.c
++++ b/block/genhd.c
+@@ -1479,9 +1479,9 @@ static void __disk_unblock_events(struct
+ intv = disk_events_poll_jiffies(disk);
+ set_timer_slack(&ev->dwork.timer, intv / 4);
+ 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)
+- queue_delayed_work(system_nrt_wq, &ev->dwork, intv);
++ queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv);
+ out_unlock:
+ spin_unlock_irqrestore(&ev->lock, flags);
+ }
+@@ -1525,7 +1525,7 @@ void disk_flush_events(struct gendisk *d
+ ev->clearing |= mask;
+ if (!ev->block) {
+ 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);
+ }
+@@ -1562,7 +1562,7 @@ unsigned int disk_clear_events(struct ge
+
+ /* uncondtionally schedule event check and wait for it to finish */
+ 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);
+ __disk_unblock_events(disk, false);
+
+@@ -1599,7 +1599,7 @@ static void disk_events_workfn(struct wo
+
+ intv = disk_events_poll_jiffies(disk);
+ 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);
+
+--- a/include/linux/workqueue.h
++++ b/include/linux/workqueue.h
+@@ -289,12 +289,16 @@ enum {
+ *
+ * system_freezable_wq is equivalent to system_wq except that it's
+ * 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_long_wq;
+ extern struct workqueue_struct *system_nrt_wq;
+ extern struct workqueue_struct *system_unbound_wq;
+ extern struct workqueue_struct *system_freezable_wq;
++extern struct workqueue_struct *system_nrt_freezable_wq;
+
+ extern struct workqueue_struct *
+ __alloc_workqueue_key(const char *name, unsigned int flags, int max_active,
+--- a/kernel/workqueue.c
++++ b/kernel/workqueue.c
+@@ -253,11 +253,13 @@ struct workqueue_struct *system_long_wq
+ struct workqueue_struct *system_nrt_wq __read_mostly;
+ struct workqueue_struct *system_unbound_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_long_wq);
+ EXPORT_SYMBOL_GPL(system_nrt_wq);
+ EXPORT_SYMBOL_GPL(system_unbound_wq);
+ EXPORT_SYMBOL_GPL(system_freezable_wq);
++EXPORT_SYMBOL_GPL(system_nrt_freezable_wq);
+
+ #define CREATE_TRACE_POINTS
+ #include <trace/events/workqueue.h>
+@@ -3821,8 +3823,11 @@ static int __init init_workqueues(void)
+ WQ_UNBOUND_MAX_ACTIVE);
+ system_freezable_wq = alloc_workqueue("events_freezable",
+ 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 ||
+- !system_unbound_wq || !system_freezable_wq);
++ !system_unbound_wq || !system_freezable_wq ||
++ !system_nrt_freezable_wq);
+ return 0;
+ }
+ early_initcall(init_workqueues);