dm: noflush resizing (2/3) Use bdlookup() on noflush suspend
PATCH 1/1 dm: noflush resizing (2/3) Use bdlookup() on noflush suspend
This patch makes device-mapper to use bdlookup to allow resizing of noflush-suspended device.
With this patch, by using bdlookup() instead of bdget(), resizing will no longer wait for I_LOCK.
There is a race between bdlookup() and bdget(). However, it's benign because the only use of bdev obtained by bdlookup() is to modify i_size and bdget() will anyway see the updated disk size.
(See below)
Case 1:
CPU 0 CPU 1
------------------------------------------------------------------
set_capacity()
disk size is updated
bdlookup() -> NULL
bdget()
bdget reads updated disk size and set it to bdev inode's i_size
Case 2:
CPU 0 CPU 1
------------------------------------------------------------------
set_capacity()
disk size is updated
bdget()
bdget reads updated disk size and set it to bdev inode's i_size
bdlookup()
wait for the bdget completion (I_NEW)
and get the bdev inode with updated disk size
Case 3:
CPU 0 CPU 1
------------------------------------------------------------------
bdget()
bdget reads old disk size
set_capacity()
disk size is updated
bdlookup()
get the existing bdev inode
update the i_size
To change i_size of bdev inode, the new code uses bd_mutex instead of inode->i_mutex. According to a comment in include/linux/fs.h, mutex is necessary here only to serialize i_size_write().bd_inode->i_size is set via bd_set_size() and bd_set_size() is protected by bd_mutex.
So I think bd_mutex is the lock we should use.
--- dm.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-)
drivers/md/dm.c +22 -5
@@ -1101,11 +1101,29 @@ static void event_callback(void *contextstatic void __set_size (struct mapped_device *md,sector_t size) { +struct block_device *bdev; +set_capacity (md->disk, size); -mutex_lock (&md->suspended_bdev->bd_inode->i_mutex); -i_size_write (md->suspended_bdev->bd_inode, (loff_t )size <<SECTOR_SHIFT ); -mutex_unlock (&md->suspended_bdev->bd_inode->i_mutex); +/* +* Updated disk size should be visible to bdget() possibly +* called in parallel to bdlookup() +*/ +smp_mb (); + +if (md->suspended_bdev) + bdev = md->suspended_bdev; +else + bdev =bdlookup_disk (md->disk,0 ); + +if (bdev) { +mutex_lock (&bdev->bd_mutex); +i_size_write (bdev->bd_inode, (loff_t )size <<SECTOR_SHIFT ); +mutex_unlock (&bdev->bd_mutex); + } + +if (!md->suspended_bdev) +bdput (bdev); }static int __bind (struct mapped_device *md,struct dm_table *t)
@@ -1121,8 +1139,7 @@ static int __bind(struct mapped_device *if (size !=get_capacity (md->disk))memset (&md->geometry,0 ,sizeof (md->geometry)); -if (md->suspended_bdev) -__set_size (md, size); +__set_size (md, size);if (size ==0 )return 0 ;