aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-06-09 14:43:54 -0400
committerJens Axboe <jaxboe@fusionio.com>2011-06-09 14:43:54 -0400
commita9dce2a3b4f0686dd66cb44d4826a59508bce969 (patch)
tree3a5ed54316884a86102e99a4442a13d2f0c81d57 /block
parentab4bd22d3cce6977dc039664cc2d052e3147d662 (diff)
block: don't use non-syncing event blocking in disk_check_events()
This patch is part of fix for triggering of WARN_ON_ONCE() in disk_clear_events() reported in bug#34662. https://bugzilla.kernel.org/show_bug.cgi?id=34662 disk_clear_events() blocks events, schedules and flushes the event work. It expects the work to have started execution on schedule and finished on return from flush. WARN_ON_ONCE() triggers if the event work hasn't executed as expected. This problem happens because __disk_block_events() fails to guarantee that the event work item is not in flight on return from the function in race-free manner. The problem is two-fold and this patch addresses one of them. When __disk_block_events() is called with @sync == %false, it bumps event block count, calls cancel_delayed_work() and return. This makes it impossible to guarantee that event polling is not in flight on return from syncing __disk_block_events() - if the first blocker was non-syncing, polling could still be in progress and later syncing ones would assume that the first blocker already canceled it. Making __disk_block_events() cancel_sync regardless of block count isn't feasible either as it may race with forced event checking in disk_clear_events(). As disk_check_events() is the only user of non-syncing __disk_block_events(), updating it to directly cancel and schedule event work is the easiest way to solve the issue. Note that there's another bug in __disk_block_events() and this patch doesn't fix the issue completely. Later patch will fix the other bug. Signed-off-by: Tejun Heo <tj@kernel.org> Tested-by: Sitsofe Wheeler <sitsofe@yahoo.com> Reported-by: Sitsofe Wheeler <sitsofe@yahoo.com> Reported-by: Borislav Petkov <bp@alien8.de> Reported-by: Meelis Roos <mroos@linux.ee> Reported-by: Linus Torvalds <torvalds@linux-foundation.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Jens Axboe <axboe@kernel.dk> Cc: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'block')
-rw-r--r--block/genhd.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/block/genhd.c b/block/genhd.c
index 95822ae25cf..3f093307764 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1508,10 +1508,18 @@ void disk_unblock_events(struct gendisk *disk)
1508 */ 1508 */
1509void disk_check_events(struct gendisk *disk) 1509void disk_check_events(struct gendisk *disk)
1510{ 1510{
1511 if (disk->ev) { 1511 struct disk_events *ev = disk->ev;
1512 __disk_block_events(disk, false); 1512 unsigned long flags;
1513 __disk_unblock_events(disk, true); 1513
1514 if (!ev)
1515 return;
1516
1517 spin_lock_irqsave(&ev->lock, flags);
1518 if (!ev->block) {
1519 cancel_delayed_work(&ev->dwork);
1520 queue_delayed_work(system_nrt_wq, &ev->dwork, 0);
1514 } 1521 }
1522 spin_unlock_irqrestore(&ev->lock, flags);
1515} 1523}
1516EXPORT_SYMBOL_GPL(disk_check_events); 1524EXPORT_SYMBOL_GPL(disk_check_events);
1517 1525