Hi. Thanks for the helpful pointers. Neil Brown <neilb@xxxxxxx> writes: > I'd put this on my todo list if it wasn't so long already :-) > (Well, I've put it there anyway, but no promises). I'll have a stab at implementing this if you like: it's only fair as I'm the one who wants the feature! > It would require changing the 'size' as known by md of each device > first. i.e. /sys/block/mdX/md/dev-*/size. > However this currently cannot be changed while the array is active. > > I cannot immediately see a good reason why not. It may be as simple > as removing > > if (my_mddev->pers) > return -EBUSY; > > from the start of rdev_size_store, but I wouldn't recommend > experimenting with that on a production system.... Looking through the kernel md.c driver, I can't see anywhere that will obviously break if rdev->size increases on a running array; it's used very rarely. Given this, I've tried taking this check out in a scratch virtual machine, and it works nicely: it's sufficient to echo the new size into the sysfs file exactly as you indicate, then --grow --size=max works correctly. I added some checks that the new value of rdev->size won't allow the data to overlap the superblock, with rdev_size_store truncating the size value if it is too large for the device, and filling the device if size = 0 is passed in. The only glitch is that the available dev size doesn't get updated in the metadata, so next time the device is assembled, it fails to add the device with an invalid argument error. Calling --assemble with --update=devicesize fixes this of course. I guess that ideally rdev_size_store should write out the changed rdev->size to the metadata when it's called on a running array, in a similar manner to size_store for component_size? If the superblock is version 1.x, I'm already updating the data_size in the superblock ready to write out: if (sb->major_version == 1) { ((struct mdp_superblock_1 *) sb)->data_size = cpu_to_le64(rdev->size*2); /* TODO: write superblock out */ } However, I'm not sure what the correct call to get the superblock written out at this point would be. I tried md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, rdev->sb_page); after updating sb->data_size as above, but that doesn't seem to do the right thing. Cheers, Chris.
--- linux-2.6.24.4/drivers/md/md.c 2008-03-24 18:49:18.000000000 +0000 +++ linux-2.6.24.4-mdplay/drivers/md/md.c 2008-06-19 16:39:29.000000000 +0100 @@ -1946,8 +1946,21 @@ rdev_size_store(mdk_rdev_t *rdev, const unsigned long long size = simple_strtoull(buf, &e, 10); if (e==buf || (*e && *e != '\n')) return -EINVAL; - if (rdev->mddev->pers) - return -EBUSY; + if (rdev->mddev->pers) { + mdp_super_t *sb; + if (rdev->sb_offset > rdev->data_offset/2) { + if (!size || size > rdev->sb_offset - rdev->data_offset/2) + size = rdev->sb_offset - rdev->data_offset/2; + } else if (!size || 2*size + rdev->data_offset > rdev->bdev->bd_inode->i_size >> 9) + size = ((rdev->bdev->bd_inode->i_size >> 9) - rdev->data_offset)/2; + sb = (mdp_super_t *) page_address(rdev->sb_page); + if (sb->major_version == 1) { + ((struct mdp_superblock_1 *) sb)->data_size = cpu_to_le64(rdev->size*2); + md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, + rdev->sb_page); + /* TODO: write superblock out */ + } + } rdev->size = size; if (size < rdev->mddev->size || rdev->mddev->size == 0) rdev->mddev->size = size;