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>
This commit is contained in:
parent
960cc0f4fe
commit
592b09a42f
1 changed files with 16 additions and 0 deletions
|
@ -610,6 +610,21 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi)
|
||||||
kthread_stop(wb->task);
|
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)
|
void bdi_unregister(struct backing_dev_info *bdi)
|
||||||
{
|
{
|
||||||
if (bdi->dev) {
|
if (bdi->dev) {
|
||||||
|
@ -682,6 +697,7 @@ void bdi_destroy(struct backing_dev_info *bdi)
|
||||||
spin_unlock(&inode_lock);
|
spin_unlock(&inode_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdi_prune_sb(bdi);
|
||||||
bdi_unregister(bdi);
|
bdi_unregister(bdi);
|
||||||
|
|
||||||
for (i = 0; i < NR_BDI_STAT_ITEMS; i++)
|
for (i = 0; i < NR_BDI_STAT_ITEMS; i++)
|
||||||
|
|
Loading…
Reference in a new issue