aboutsummaryrefslogtreecommitdiffstats
path: root/mm/backing-dev.c
diff options
context:
space:
mode:
authorRabin Vincent <rabin@rab.in>2012-01-29 13:17:33 -0500
committerWu Fengguang <fengguang.wu@intel.com>2012-02-01 03:52:49 -0500
commit2673b4cf5d59c3ee5e0c12f6d734d38770324dc4 (patch)
treeabef3ab4517f06b629f5dc4c516c135165cf7d81 /mm/backing-dev.c
parent62aa2b537c6f5957afd98e29f96897419ed5ebab (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.c23
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 */
585static void bdi_wb_shutdown(struct backing_dev_info *bdi) 585static 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
624void bdi_unregister(struct backing_dev_info *bdi) 631void 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}
639EXPORT_SYMBOL(bdi_unregister); 652EXPORT_SYMBOL(bdi_unregister);