fs: switch bdev inode bdi's correctly
bdev inodes can remain dirty even after their last close. Hence the BDI associated with the bdev->inode gets modified duringthe last close to point to the default BDI. However, the bdev inode still needs to be moved to the dirty lists of the new BDI, otherwise it will corrupt the writeback list is was left on. Add a new function bdev_inode_switch_bdi() to move all the bdi state from the old bdi to the new one safely. This is only a temporary measure until the bdev inode<->bdi lifecycle problems are sorted out. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
99a3891924
commit
a5491e0c7b
1 changed files with 21 additions and 5 deletions
|
@ -48,6 +48,21 @@ inline struct block_device *I_BDEV(struct inode *inode)
|
|||
|
||||
EXPORT_SYMBOL(I_BDEV);
|
||||
|
||||
/*
|
||||
* move the inode from it's current bdi to the a new bdi. if the inode is dirty
|
||||
* we need to move it onto the dirty list of @dst so that the inode is always
|
||||
* on the right list.
|
||||
*/
|
||||
static void bdev_inode_switch_bdi(struct inode *inode,
|
||||
struct backing_dev_info *dst)
|
||||
{
|
||||
spin_lock(&inode_lock);
|
||||
inode->i_data.backing_dev_info = dst;
|
||||
if (inode->i_state & I_DIRTY)
|
||||
list_move(&inode->i_list, &dst->wb.b_dirty);
|
||||
spin_unlock(&inode_lock);
|
||||
}
|
||||
|
||||
static sector_t max_block(struct block_device *bdev)
|
||||
{
|
||||
sector_t retval = ~((sector_t)0);
|
||||
|
@ -1390,7 +1405,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
|
|||
bdi = blk_get_backing_dev_info(bdev);
|
||||
if (bdi == NULL)
|
||||
bdi = &default_backing_dev_info;
|
||||
bdev->bd_inode->i_data.backing_dev_info = bdi;
|
||||
bdev_inode_switch_bdi(bdev->bd_inode, bdi);
|
||||
}
|
||||
if (bdev->bd_invalidated)
|
||||
rescan_partitions(disk, bdev);
|
||||
|
@ -1405,8 +1420,8 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
|
|||
if (ret)
|
||||
goto out_clear;
|
||||
bdev->bd_contains = whole;
|
||||
bdev->bd_inode->i_data.backing_dev_info =
|
||||
whole->bd_inode->i_data.backing_dev_info;
|
||||
bdev_inode_switch_bdi(bdev->bd_inode,
|
||||
whole->bd_inode->i_data.backing_dev_info);
|
||||
bdev->bd_part = disk_get_part(disk, partno);
|
||||
if (!(disk->flags & GENHD_FL_UP) ||
|
||||
!bdev->bd_part || !bdev->bd_part->nr_sects) {
|
||||
|
@ -1439,7 +1454,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
|
|||
disk_put_part(bdev->bd_part);
|
||||
bdev->bd_disk = NULL;
|
||||
bdev->bd_part = NULL;
|
||||
bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info;
|
||||
bdev_inode_switch_bdi(bdev->bd_inode, &default_backing_dev_info);
|
||||
if (bdev != bdev->bd_contains)
|
||||
__blkdev_put(bdev->bd_contains, mode, 1);
|
||||
bdev->bd_contains = NULL;
|
||||
|
@ -1533,7 +1548,8 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
|
|||
disk_put_part(bdev->bd_part);
|
||||
bdev->bd_part = NULL;
|
||||
bdev->bd_disk = NULL;
|
||||
bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info;
|
||||
bdev_inode_switch_bdi(bdev->bd_inode,
|
||||
&default_backing_dev_info);
|
||||
if (bdev != bdev->bd_contains)
|
||||
victim = bdev->bd_contains;
|
||||
bdev->bd_contains = NULL;
|
||||
|
|
Loading…
Reference in a new issue