diff options
author | Tejun Heo <tj@kernel.org> | 2011-06-09 14:43:54 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2011-06-09 14:43:54 -0400 |
commit | a9dce2a3b4f0686dd66cb44d4826a59508bce969 (patch) | |
tree | 3a5ed54316884a86102e99a4442a13d2f0c81d57 /block/genhd.c | |
parent | ab4bd22d3cce6977dc039664cc2d052e3147d662 (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/genhd.c')
-rw-r--r-- | block/genhd.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/block/genhd.c b/block/genhd.c index 95822ae25cfe..3f0933077642 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -1508,10 +1508,18 @@ void disk_unblock_events(struct gendisk *disk) | |||
1508 | */ | 1508 | */ |
1509 | void disk_check_events(struct gendisk *disk) | 1509 | void 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 | } |
1516 | EXPORT_SYMBOL_GPL(disk_check_events); | 1524 | EXPORT_SYMBOL_GPL(disk_check_events); |
1517 | 1525 | ||