diff options
author | Rabin Vincent <rabin@rab.in> | 2012-01-29 13:17:33 -0500 |
---|---|---|
committer | Wu Fengguang <fengguang.wu@intel.com> | 2012-02-01 03:52:49 -0500 |
commit | 2673b4cf5d59c3ee5e0c12f6d734d38770324dc4 (patch) | |
tree | abef3ab4517f06b629f5dc4c516c135165cf7d81 /mm/backing-dev.c | |
parent | 62aa2b537c6f5957afd98e29f96897419ed5ebab (diff) |
backing-dev: fix wakeup timer races with bdi_unregister()
While 7a401a972df8e18 ("backing-dev: ensure wakeup_timer is deleted")
addressed the problem of the bdi being freed with a queued wakeup
timer, there are other races that could happen if the wakeup timer
expires after/during bdi_unregister(), before bdi_destroy() is called.
wakeup_timer_fn() could attempt to wakeup a task which has already has
been freed, or could access a NULL bdi->dev via the wake_forker_thread
tracepoint.
Cc: <stable@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Reported-by: Chanho Min <chanho.min@lge.com>
Reviewed-by: Namjae Jeon <linkinjeon@gmail.com>
Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Diffstat (limited to 'mm/backing-dev.c')
-rw-r--r-- | mm/backing-dev.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 7ba8feae11b8..dd8e2aafb07e 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c | |||
@@ -318,7 +318,7 @@ static void wakeup_timer_fn(unsigned long data) | |||
318 | if (bdi->wb.task) { | 318 | if (bdi->wb.task) { |
319 | trace_writeback_wake_thread(bdi); | 319 | trace_writeback_wake_thread(bdi); |
320 | wake_up_process(bdi->wb.task); | 320 | wake_up_process(bdi->wb.task); |
321 | } else { | 321 | } else if (bdi->dev) { |
322 | /* | 322 | /* |
323 | * When bdi tasks are inactive for long time, they are killed. | 323 | * When bdi tasks are inactive for long time, they are killed. |
324 | * In this case we have to wake-up the forker thread which | 324 | * In this case we have to wake-up the forker thread which |
@@ -584,6 +584,8 @@ EXPORT_SYMBOL(bdi_register_dev); | |||
584 | */ | 584 | */ |
585 | static void bdi_wb_shutdown(struct backing_dev_info *bdi) | 585 | static void bdi_wb_shutdown(struct backing_dev_info *bdi) |
586 | { | 586 | { |
587 | struct task_struct *task; | ||
588 | |||
587 | if (!bdi_cap_writeback_dirty(bdi)) | 589 | if (!bdi_cap_writeback_dirty(bdi)) |
588 | return; | 590 | return; |
589 | 591 | ||
@@ -602,8 +604,13 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) | |||
602 | * Finally, kill the kernel thread. We don't need to be RCU | 604 | * Finally, kill the kernel thread. We don't need to be RCU |
603 | * safe anymore, since the bdi is gone from visibility. | 605 | * safe anymore, since the bdi is gone from visibility. |
604 | */ | 606 | */ |
605 | if (bdi->wb.task) | 607 | spin_lock_bh(&bdi->wb_lock); |
606 | kthread_stop(bdi->wb.task); | 608 | task = bdi->wb.task; |
609 | bdi->wb.task = NULL; | ||
610 | spin_unlock_bh(&bdi->wb_lock); | ||
611 | |||
612 | if (task) | ||
613 | kthread_stop(task); | ||
607 | } | 614 | } |
608 | 615 | ||
609 | /* | 616 | /* |
@@ -623,7 +630,9 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) | |||
623 | 630 | ||
624 | void bdi_unregister(struct backing_dev_info *bdi) | 631 | void bdi_unregister(struct backing_dev_info *bdi) |
625 | { | 632 | { |
626 | if (bdi->dev) { | 633 | struct device *dev = bdi->dev; |
634 | |||
635 | if (dev) { | ||
627 | bdi_set_min_ratio(bdi, 0); | 636 | bdi_set_min_ratio(bdi, 0); |
628 | trace_writeback_bdi_unregister(bdi); | 637 | trace_writeback_bdi_unregister(bdi); |
629 | bdi_prune_sb(bdi); | 638 | bdi_prune_sb(bdi); |
@@ -632,8 +641,12 @@ void bdi_unregister(struct backing_dev_info *bdi) | |||
632 | if (!bdi_cap_flush_forker(bdi)) | 641 | if (!bdi_cap_flush_forker(bdi)) |
633 | bdi_wb_shutdown(bdi); | 642 | bdi_wb_shutdown(bdi); |
634 | bdi_debug_unregister(bdi); | 643 | bdi_debug_unregister(bdi); |
635 | device_unregister(bdi->dev); | 644 | |
645 | spin_lock_bh(&bdi->wb_lock); | ||
636 | bdi->dev = NULL; | 646 | bdi->dev = NULL; |
647 | spin_unlock_bh(&bdi->wb_lock); | ||
648 | |||
649 | device_unregister(dev); | ||
637 | } | 650 | } |
638 | } | 651 | } |
639 | EXPORT_SYMBOL(bdi_unregister); | 652 | EXPORT_SYMBOL(bdi_unregister); |