online resizing of devices/filesystems (2.6)
Quoted 15 lines · Joe Thornber
Hi,
Should genhd.h:set_capacity() find and update the
i_sizefield of the inode for the device ?The BLKGETSIZE and BLKGETSIZE64 ioctls report the size in the devices inode:
case BLKGETSIZE: if ((bdev->bd_inode->i_size >> 9) > ~0UL) return -EFBIG; return put_ulong(arg, bdev->bd_inode->i_size >> 9); case BLKGETSIZE64: return put_u64(arg, bdev->bd_inode->i_size);
Quoted 3 lines
Currently people have to close and reopen the device in order for a size change to take effect. This is a problem if people want to do online resizing of a filesystem (supported by xfs and resier).
Has anyone had any thoughts about this issue?
To recap, in drivers/md/dm.c:__bind(), there is a call to set_capacity(), which sets the device size in the gendisk entry. But if the device is already open (e.g., mounted, or in use by another device (loop, raid, other device-mapper)), then the bdev->bd_inode->i_size field for that gendisk entry also needs to be updated to reflect this new size to the VFS.
My initial thoughts were to add something like this to the __bind() function mentioned above:
bdev = bdget_disk(md->disk, 0);
if (bdev) {
bd_set_size(bdev, size << 9);
bdput(bdev);
}Of course, bd_set_size() is static to fs/block_dev.c, but it does seem to do exactly what we need in this situation. The only user of bd_set_size() is do_open(), which does quite a bit of locking before making modifications to the block_device entry.
Does anyone have any advice as to whether this is the correct approach, or what additional locking is necessary? Or is there a different approach that I'm overlooking?
Thanks!
Signature · 3 lines
Kevin Corry [email protected] http://evms.sourceforge.net/
Quoted 17 lines · Kevin Corry
Quoted 15 lines · Joe Thornber
Hi,
Should genhd.h:set_capacity() find and update the
i_sizefield of the inode for the device ?The BLKGETSIZE and BLKGETSIZE64 ioctls report the size in the devices inode:
case BLKGETSIZE: if ((bdev->bd_inode->i_size >> 9) > ~0UL) return -EFBIG; return put_ulong(arg, bdev->bd_inode->i_size >> 9); case BLKGETSIZE64: return put_u64(arg, bdev->bd_inode->i_size);
Quoted 5 lines
Quoted 3 lines
Currently people have to close and reopen the device in order for a size change to take effect. This is a problem if people want to do online resizing of a filesystem (supported by xfs and resier).
Has anyone had any thoughts about this issue?
Resizing a blockdev while someone has a filesystem mounted on it is a bit rude, but I guess that as long as it is being expanded, not much can go wrong.
bd_set_size() isn't quite what you want because it fiddles with the blocksize as well.
So one approach would be to do what NBD does, and just write i_size directly.
You could create a little helper library function which takes i_sem and then writes i_size. But the VFS tends to avoid taking i_sem on blockdevs because it doesn't expect i_size to change ;)
Quoted 3 lines · Andrew Morton
Resizing a blockdev while someone has a filesystem mounted on it is a bit rude, but I guess that as long as it is being expanded, not much can go wrong.
There's no technical reason why you couldn't shrink as well. But right now we don't have any filesystems that support online shrink, so the tools prevent that scenario. Online expand is quite common, though.
Quoted 2 lines
bd_set_size()isn't quite what you want because it fiddles with the blocksize as well.
Ok.
Quoted 2 lines
So one approach would be to do what NBD does, and just write
i_sizedirectly.
We had considered that originally. It just seemed like too big of a hack. :) Plus I wasn't sure if there were locking issues with changing fields in the inode.
Quoted 3 lines
You could create a little helper library function which takes
i_semand then writesi_size. But the VFS tends to avoid takingi_semon blockdevs because it doesn't expecti_sizeto change ;)
So...should I take i_sem or not? :) Perhaps calling i_size_write() in include/linux/fs.h would be preferrable, since it seems to be doing different things for SMP and preempt.
Thanks for the feedback!
Signature · 3 lines
Kevin Corry [email protected] http://evms.sourceforge.net/
Quoted 12 lines · Kevin Corry
Quoted 2 lines · Andrew Morton
So one approach would be to do what NBD does, and just write
i_sizedirectly.We had considered that originally. It just seemed like too big of a hack. :) Plus I wasn't sure if there were locking issues with changing fields in the inode.
Quoted 3 lines
You could create a little helper library function which takes
i_semand then writesi_size. But the VFS tends to avoid takingi_semon blockdevs because it doesn't expecti_sizeto change ;)
Quoted 5 lines
So...should I take
i_semor not? :) Perhaps callingi_size_write()ininclude/linux/fs.hwould be preferrable, since it seems to be doing different things for SMP and preempt.Thanks for the feedback!
Here's a patch that takes bdev->bd_inode->i_sem, and then calls i_size_write() to update the inode size. My initial tests have been successful in resizing mounted reiser and xfs filesystems on 2.6.0-test7.
Signature · 40 lines
Kevin Corry [email protected] http://evms.sourceforge.net/ When setting the size of a Device-Mapper device in the gendisk entry, also try to set the size of the corresponding block_device entry's inode. This is necessary to allow online device/filesystem resizing to work correctly. --- a/drivers/md/dm.c 9 Oct 2003 16:47:44 -0000 +++ b/drivers/md/dm.c 17 Oct 2003 21:06:01 -0000 @@ -726,6 +726,20 @@ up_write(&md->lock); } +static void __set_size(struct gendisk *disk, sector_t size) +{ + struct block_device *bdev; + + set_capacity(disk, size); + bdev = bdget_disk(disk, 0); + if (bdev) { + down(&bdev->bd_inode->i_sem); + i_size_write(bdev->bd_inode, size << SECTOR_SHIFT); + up(&bdev->bd_inode->i_sem); + bdput(bdev); + } +} + static int __bind(struct mapped_device *md, struct dm_table *t) { request_queue_t *q = md->queue; @@ -733,7 +747,7 @@ md->map = t; size = dm_table_get_size(t); - set_capacity(md->disk, size); + __set_size(md->disk, size); if (size == 0) return 0;