From: Greg Kroah-Hartman Date: Thu, 15 Mar 2012 18:55:31 +0000 (-0700) Subject: 3.0-stable patches X-Git-Tag: v3.0.25~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a5abbe3c7e307e2234ff1c6aa050c54836ead4fb;p=thirdparty%2Fkernel%2Fstable-queue.git 3.0-stable patches added patches: block-fix-__blkdev_get-and-add_disk-race-condition.patch block-use-a-freezable-workqueue-for-disk-event-polling.patch --- diff --git a/queue-3.0/block-fix-__blkdev_get-and-add_disk-race-condition.patch b/queue-3.0/block-fix-__blkdev_get-and-add_disk-race-condition.patch new file mode 100644 index 00000000000..bac930fa729 --- /dev/null +++ b/queue-3.0/block-fix-__blkdev_get-and-add_disk-race-condition.patch @@ -0,0 +1,161 @@ +From 9f53d2fe815b4011ff930a7b6db98385d45faa68 Mon Sep 17 00:00:00 2001 +From: Stanislaw Gruszka +Date: Fri, 2 Mar 2012 10:43:28 +0100 +Subject: block: fix __blkdev_get and add_disk race condition + +From: Stanislaw Gruszka + +commit 9f53d2fe815b4011ff930a7b6db98385d45faa68 upstream. + +The following situation might occur: + +__blkdev_get: add_disk: + + register_disk() +get_gendisk() + +disk_block_events() + disk->ev == NULL + + disk_add_events() + +__disk_unblock_events() + disk->ev != NULL + --ev->block + +Then we unblock events, when they are suppose to be blocked. This can +trigger events related block/genhd.c warnings, but also can crash in +sd_check_events() or other places. + +I'm able to reproduce crashes with the following scripts (with +connected usb dongle as sdb disk). + + +DEV=/dev/sdb +ENABLE=/sys/bus/usb/devices/1-2/bConfigurationValue + +function stop_me() +{ + for i in `jobs -p` ; do kill $i 2> /dev/null ; done + exit +} + +trap stop_me SIGHUP SIGINT SIGTERM + +for ((i = 0; i < 10; i++)) ; do + while true; do fdisk -l $DEV 2>&1 > /dev/null ; done & +done + +while true ; do +echo 1 > $ENABLE +sleep 1 +echo 0 > $ENABLE +done + + +I use the script to verify patch fixing oops in sd_revalidate_disk +http://marc.info/?l=linux-scsi&m=132935572512352&w=2 +Without Jun'ichi Nomura patch titled "Fix NULL pointer dereference in +sd_revalidate_disk" or this one, script easily crash kernel within +a few seconds. With both patches applied I do not observe crash. +Unfortunately after some time (dozen of minutes), script will hung in: + +[ 1563.906432] [] schedule_timeout_uninterruptible+0x15/0x20 +[ 1563.906437] [] msleep+0x15/0x20 +[ 1563.906443] [] blk_drain_queue+0x32/0xd0 +[ 1563.906447] [] blk_cleanup_queue+0xd0/0x170 +[ 1563.906454] [] scsi_free_queue+0x3f/0x60 +[ 1563.906459] [] __scsi_remove_device+0x6e/0xb0 +[ 1563.906463] [] scsi_forget_host+0x4f/0x60 +[ 1563.906468] [] scsi_remove_host+0x5a/0xf0 +[ 1563.906482] [] quiesce_and_remove_host+0x5b/0xa0 [usb_storage] +[ 1563.906490] [] usb_stor_disconnect+0x13/0x20 [usb_storage] + +Anyway I think this patch is some step forward. + +As drawback, I do not teardown on sysfs file create error, because I do +not know how to nullify disk->ev (since it can be used). However add_disk +error handling practically does not exist too, and things will work +without this sysfs file, except events will not be exported to user +space. + +Signed-off-by: Stanislaw Gruszka +Acked-by: Tejun Heo +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman + +--- + block/genhd.c | 32 +++++++++++++++++++------------- + 1 file changed, 19 insertions(+), 13 deletions(-) + +--- a/block/genhd.c ++++ b/block/genhd.c +@@ -36,6 +36,7 @@ static DEFINE_IDR(ext_devt_idr); + + static struct device_type disk_type; + ++static void disk_alloc_events(struct gendisk *disk); + static void disk_add_events(struct gendisk *disk); + static void disk_del_events(struct gendisk *disk); + static void disk_release_events(struct gendisk *disk); +@@ -602,6 +603,8 @@ void add_disk(struct gendisk *disk) + disk->major = MAJOR(devt); + disk->first_minor = MINOR(devt); + ++ disk_alloc_events(disk); ++ + /* Register BDI before referencing it from bdev */ + bdi = &disk->queue->backing_dev_info; + bdi_register_dev(bdi, disk_devt(disk)); +@@ -1740,9 +1743,9 @@ module_param_cb(events_dfl_poll_msecs, & + &disk_events_dfl_poll_msecs, 0644); + + /* +- * disk_{add|del|release}_events - initialize and destroy disk_events. ++ * disk_{alloc|add|del|release}_events - initialize and destroy disk_events. + */ +-static void disk_add_events(struct gendisk *disk) ++static void disk_alloc_events(struct gendisk *disk) + { + struct disk_events *ev; + +@@ -1755,16 +1758,6 @@ static void disk_add_events(struct gendi + return; + } + +- if (sysfs_create_files(&disk_to_dev(disk)->kobj, +- disk_events_attrs) < 0) { +- pr_warn("%s: failed to create sysfs files for events\n", +- disk->disk_name); +- kfree(ev); +- return; +- } +- +- disk->ev = ev; +- + INIT_LIST_HEAD(&ev->node); + ev->disk = disk; + spin_lock_init(&ev->lock); +@@ -1773,8 +1766,21 @@ static void disk_add_events(struct gendi + ev->poll_msecs = -1; + INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn); + ++ disk->ev = ev; ++} ++ ++static void disk_add_events(struct gendisk *disk) ++{ ++ if (!disk->ev) ++ return; ++ ++ /* FIXME: error handling */ ++ if (sysfs_create_files(&disk_to_dev(disk)->kobj, disk_events_attrs) < 0) ++ pr_warn("%s: failed to create sysfs files for events\n", ++ disk->disk_name); ++ + mutex_lock(&disk_events_mutex); +- list_add_tail(&ev->node, &disk_events); ++ list_add_tail(&disk->ev->node, &disk_events); + mutex_unlock(&disk_events_mutex); + + /* diff --git a/queue-3.0/block-use-a-freezable-workqueue-for-disk-event-polling.patch b/queue-3.0/block-use-a-freezable-workqueue-for-disk-event-polling.patch new file mode 100644 index 00000000000..0d163c85071 --- /dev/null +++ b/queue-3.0/block-use-a-freezable-workqueue-for-disk-event-polling.patch @@ -0,0 +1,122 @@ +From 62d3c5439c534b0e6c653fc63e6d8c67be3a57b1 Mon Sep 17 00:00:00 2001 +From: Alan Stern +Date: Fri, 2 Mar 2012 10:51:00 +0100 +Subject: Block: use a freezable workqueue for disk-event polling + +From: Alan Stern + +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 +Acked-by: Tejun Heo +Acked-by: Rafael J. Wysocki +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -1487,9 +1487,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); + } +@@ -1530,7 +1530,7 @@ void disk_check_events(struct gendisk *d + spin_lock_irqsave(&ev->lock, flags); + 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_irqrestore(&ev->lock, flags); + } +@@ -1568,7 +1568,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); + +@@ -1605,7 +1605,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 +@@ -252,11 +252,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 +@@ -3796,8 +3798,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); diff --git a/queue-3.0/series b/queue-3.0/series index c52e468bfe1..b0c6bee12d0 100644 --- a/queue-3.0/series +++ b/queue-3.0/series @@ -25,3 +25,5 @@ compat-re-add-missing-asm-compat.h-include-to-fix-compile-breakage-on-s390.patch regulator-fix-setting-selector-in-tps6524x-set_voltage-function.patch block-fix-null-pointer-dereference-in-sd_revalidate_disk.patch block-sx8-fix-pointer-math-issue-getting-fw-version.patch +block-fix-__blkdev_get-and-add_disk-race-condition.patch +block-use-a-freezable-workqueue-for-disk-event-polling.patch