From 592b09a42fc3ae6737a0f3ecf4fee42ecd0296f8 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 29 Oct 2009 11:46:12 +0100 Subject: backing-dev: ensure that a removed bdi no longer has super_block referencing it When the bdi is being removed, we have to ensure that no super_blocks currently have that cached in sb->s_bdi. Normally this is ensured by the sb having a longer life span than the bdi, but if the device is suddenly yanked, we have to kill this reference. sb->s_bdi is pointed to freed memory at that point. This fixes a problem with sync(1) hanging when a USB stick is pulled without cleanly umounting it first. Reported-by: Pavel Machek Signed-off-by: Jens Axboe --- mm/backing-dev.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'mm/backing-dev.c') diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 5a37e2055717..1065b715ef64 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -610,6 +610,21 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) kthread_stop(wb->task); } +/* + * This bdi is going away now, make sure that no super_blocks point to it + */ +static void bdi_prune_sb(struct backing_dev_info *bdi) +{ + struct super_block *sb; + + spin_lock(&sb_lock); + list_for_each_entry(sb, &super_blocks, s_list) { + if (sb->s_bdi == bdi) + sb->s_bdi = NULL; + } + spin_unlock(&sb_lock); +} + void bdi_unregister(struct backing_dev_info *bdi) { if (bdi->dev) { @@ -682,6 +697,7 @@ void bdi_destroy(struct backing_dev_info *bdi) spin_unlock(&inode_lock); } + bdi_prune_sb(bdi); bdi_unregister(bdi); for (i = 0; i < NR_BDI_STAT_ITEMS; i++) -- cgit v1.2.3 From 8c4db3355b0fcc9ad77431f15b955efa0645b5d0 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 3 Nov 2009 20:18:44 +0100 Subject: backing-dev: bdi sb prune should be in the unregister path, not destroy Commit 592b09a42fc3ae6737a0f3ecf4fee42ecd0296f8 was different from the tested path, in that it moved the bdi super_block prune from unregister to destroy context. This doesn't fully fix the sync hang bug on unexpected device removal, as need to prune the bdi cache pointer before killing flusher thread. Tested-by: Artur Skawina Signed-off-by: Jens Axboe --- mm/backing-dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'mm/backing-dev.c') diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 1065b715ef64..11aee09dd2a6 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -628,6 +628,8 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) void bdi_unregister(struct backing_dev_info *bdi) { if (bdi->dev) { + bdi_prune_sb(bdi); + if (!bdi_cap_flush_forker(bdi)) bdi_wb_shutdown(bdi); bdi_debug_unregister(bdi); @@ -697,7 +699,6 @@ void bdi_destroy(struct backing_dev_info *bdi) spin_unlock(&inode_lock); } - bdi_prune_sb(bdi); bdi_unregister(bdi); for (i = 0; i < NR_BDI_STAT_ITEMS; i++) -- cgit v1.2.3