diff options
author | Jan Kara <jack@suse.cz> | 2014-04-03 17:46:23 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-03 19:20:49 -0400 |
commit | 5acda9d12dcf1ad0d9a5a2a7c646de3472fa7555 (patch) | |
tree | 5103d2779fbf2f1f07edb1ed51083d1303073ef9 /mm/backing-dev.c | |
parent | 6ca738d60c563d5c6cf6253ee4b8e76fa77b2b9e (diff) |
bdi: avoid oops on device removal
After commit 839a8e8660b6 ("writeback: replace custom worker pool
implementation with unbound workqueue") when device is removed while we
are writing to it we crash in bdi_writeback_workfn() ->
set_worker_desc() because bdi->dev is NULL.
This can happen because even though bdi_unregister() cancels all pending
flushing work, nothing really prevents new ones from being queued from
balance_dirty_pages() or other places.
Fix the problem by clearing BDI_registered bit in bdi_unregister() and
checking it before scheduling of any flushing work.
Fixes: 839a8e8660b6777e7fe4e80af1a048aebe2b5977
Reviewed-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jan Kara <jack@suse.cz>
Cc: Derek Basehore <dbasehore@chromium.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/backing-dev.c')
-rw-r--r-- | mm/backing-dev.c | 13 |
1 files changed, 9 insertions, 4 deletions
diff --git a/mm/backing-dev.c b/mm/backing-dev.c index fab8401fc54e..09d9591b7708 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c | |||
@@ -297,7 +297,10 @@ void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi) | |||
297 | unsigned long timeout; | 297 | unsigned long timeout; |
298 | 298 | ||
299 | timeout = msecs_to_jiffies(dirty_writeback_interval * 10); | 299 | timeout = msecs_to_jiffies(dirty_writeback_interval * 10); |
300 | queue_delayed_work(bdi_wq, &bdi->wb.dwork, timeout); | 300 | spin_lock_bh(&bdi->wb_lock); |
301 | if (test_bit(BDI_registered, &bdi->state)) | ||
302 | queue_delayed_work(bdi_wq, &bdi->wb.dwork, timeout); | ||
303 | spin_unlock_bh(&bdi->wb_lock); | ||
301 | } | 304 | } |
302 | 305 | ||
303 | /* | 306 | /* |
@@ -310,9 +313,6 @@ static void bdi_remove_from_list(struct backing_dev_info *bdi) | |||
310 | spin_unlock_bh(&bdi_lock); | 313 | spin_unlock_bh(&bdi_lock); |
311 | 314 | ||
312 | synchronize_rcu_expedited(); | 315 | synchronize_rcu_expedited(); |
313 | |||
314 | /* bdi_list is now unused, clear it to mark @bdi dying */ | ||
315 | INIT_LIST_HEAD(&bdi->bdi_list); | ||
316 | } | 316 | } |
317 | 317 | ||
318 | int bdi_register(struct backing_dev_info *bdi, struct device *parent, | 318 | int bdi_register(struct backing_dev_info *bdi, struct device *parent, |
@@ -363,6 +363,11 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) | |||
363 | */ | 363 | */ |
364 | bdi_remove_from_list(bdi); | 364 | bdi_remove_from_list(bdi); |
365 | 365 | ||
366 | /* Make sure nobody queues further work */ | ||
367 | spin_lock_bh(&bdi->wb_lock); | ||
368 | clear_bit(BDI_registered, &bdi->state); | ||
369 | spin_unlock_bh(&bdi->wb_lock); | ||
370 | |||
366 | /* | 371 | /* |
367 | * Drain work list and shutdown the delayed_work. At this point, | 372 | * Drain work list and shutdown the delayed_work. At this point, |
368 | * @bdi->bdi_list is empty telling bdi_Writeback_workfn() that @bdi | 373 | * @bdi->bdi_list is empty telling bdi_Writeback_workfn() that @bdi |