Commit bb059a37 authored by David Sterba's avatar David Sterba

btrfs: add cancellation to resize

Accept literal string "cancel" as resize operation and interpret that
as a request to cancel the running operation. If it's running, wait
until it finishes current work and return ECANCELED.

Shrinking resize uses relocation to move the chunks away, use the
conditional exclusive operation start and cancellation helpers.
Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 17aaa434
...@@ -1659,6 +1659,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1659,6 +1659,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
char *devstr = NULL; char *devstr = NULL;
int ret = 0; int ret = 0;
int mod = 0; int mod = 0;
bool cancel;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
...@@ -1667,20 +1668,23 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1667,20 +1668,23 @@ static noinline int btrfs_ioctl_resize(struct file *file,
if (ret) if (ret)
return ret; return ret;
if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_RESIZE)) { /*
mnt_drop_write_file(file); * Read the arguments before checking exclusivity to be able to
return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; * distinguish regular resize and cancel
} */
vol_args = memdup_user(arg, sizeof(*vol_args)); vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args)) { if (IS_ERR(vol_args)) {
ret = PTR_ERR(vol_args); ret = PTR_ERR(vol_args);
goto out; goto out_drop;
} }
vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
sizestr = vol_args->name; sizestr = vol_args->name;
cancel = (strcmp("cancel", sizestr) == 0);
ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_RESIZE, cancel);
if (ret)
goto out_free;
/* Exclusive operation is now claimed */
devstr = strchr(sizestr, ':'); devstr = strchr(sizestr, ':');
if (devstr) { if (devstr) {
sizestr = devstr + 1; sizestr = devstr + 1;
...@@ -1688,10 +1692,10 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1688,10 +1692,10 @@ static noinline int btrfs_ioctl_resize(struct file *file,
devstr = vol_args->name; devstr = vol_args->name;
ret = kstrtoull(devstr, 10, &devid); ret = kstrtoull(devstr, 10, &devid);
if (ret) if (ret)
goto out_free; goto out_finish;
if (!devid) { if (!devid) {
ret = -EINVAL; ret = -EINVAL;
goto out_free; goto out_finish;
} }
btrfs_info(fs_info, "resizing devid %llu", devid); btrfs_info(fs_info, "resizing devid %llu", devid);
} }
...@@ -1701,7 +1705,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1701,7 +1705,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
btrfs_info(fs_info, "resizer unable to find device %llu", btrfs_info(fs_info, "resizer unable to find device %llu",
devid); devid);
ret = -ENODEV; ret = -ENODEV;
goto out_free; goto out_finish;
} }
if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
...@@ -1709,7 +1713,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1709,7 +1713,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
"resizer unable to apply on readonly device %llu", "resizer unable to apply on readonly device %llu",
devid); devid);
ret = -EPERM; ret = -EPERM;
goto out_free; goto out_finish;
} }
if (!strcmp(sizestr, "max")) if (!strcmp(sizestr, "max"))
...@@ -1725,13 +1729,13 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1725,13 +1729,13 @@ static noinline int btrfs_ioctl_resize(struct file *file,
new_size = memparse(sizestr, &retptr); new_size = memparse(sizestr, &retptr);
if (*retptr != '\0' || new_size == 0) { if (*retptr != '\0' || new_size == 0) {
ret = -EINVAL; ret = -EINVAL;
goto out_free; goto out_finish;
} }
} }
if (test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) { if (test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) {
ret = -EPERM; ret = -EPERM;
goto out_free; goto out_finish;
} }
old_size = btrfs_device_get_total_bytes(device); old_size = btrfs_device_get_total_bytes(device);
...@@ -1739,24 +1743,24 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1739,24 +1743,24 @@ static noinline int btrfs_ioctl_resize(struct file *file,
if (mod < 0) { if (mod < 0) {
if (new_size > old_size) { if (new_size > old_size) {
ret = -EINVAL; ret = -EINVAL;
goto out_free; goto out_finish;
} }
new_size = old_size - new_size; new_size = old_size - new_size;
} else if (mod > 0) { } else if (mod > 0) {
if (new_size > ULLONG_MAX - old_size) { if (new_size > ULLONG_MAX - old_size) {
ret = -ERANGE; ret = -ERANGE;
goto out_free; goto out_finish;
} }
new_size = old_size + new_size; new_size = old_size + new_size;
} }
if (new_size < SZ_256M) { if (new_size < SZ_256M) {
ret = -EINVAL; ret = -EINVAL;
goto out_free; goto out_finish;
} }
if (new_size > device->bdev->bd_inode->i_size) { if (new_size > device->bdev->bd_inode->i_size) {
ret = -EFBIG; ret = -EFBIG;
goto out_free; goto out_finish;
} }
new_size = round_down(new_size, fs_info->sectorsize); new_size = round_down(new_size, fs_info->sectorsize);
...@@ -1765,7 +1769,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1765,7 +1769,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
trans = btrfs_start_transaction(root, 0); trans = btrfs_start_transaction(root, 0);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
ret = PTR_ERR(trans); ret = PTR_ERR(trans);
goto out_free; goto out_finish;
} }
ret = btrfs_grow_device(trans, device, new_size); ret = btrfs_grow_device(trans, device, new_size);
btrfs_commit_transaction(trans); btrfs_commit_transaction(trans);
...@@ -1778,10 +1782,11 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1778,10 +1782,11 @@ static noinline int btrfs_ioctl_resize(struct file *file,
"resize device %s (devid %llu) from %llu to %llu", "resize device %s (devid %llu) from %llu to %llu",
rcu_str_deref(device->name), device->devid, rcu_str_deref(device->name), device->devid,
old_size, new_size); old_size, new_size);
out_finish:
btrfs_exclop_finish(fs_info);
out_free: out_free:
kfree(vol_args); kfree(vol_args);
out: out_drop:
btrfs_exclop_finish(fs_info);
mnt_drop_write_file(file); mnt_drop_write_file(file);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment