The following commit has been merged in the linux branch: commit 592b09a42fc3ae6737a0f3ecf4fee42ecd0296f8 Author: Jens Axboe jens.axboe@oracle.com Date: Thu Oct 29 11:46:12 2009 +0100
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 pavel@ucw.cz Signed-off-by: Jens Axboe jens.axboe@oracle.com
diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 5a37e20..1065b71 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++)