Message ID | fe9738eb5db055e06eafb178bf6aea41c48b2890.1621526221.git.dsterba@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Support resize and device delete cancel ops | expand |
On 5/21/21 8:06 AM, David Sterba wrote: > To support optional cancelation of some operations, add helper that will > wrap all the combinations. In normal mode it's same as > btrfs_exclop_start, in cancelation mode it checks if it's already > running and request cancelation and waits until completion. > > The error codes can be returned to to user space and semantics is not > changed, adding ECANCELED. This should be evaluated as an error and that > the operation has not completed and the operation should be restarted > or the filesystem status reviewed. > > Signed-off-by: David Sterba <dsterba@suse.com> > --- > fs/btrfs/ioctl.c | 42 ++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 42 insertions(+) > > diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c > index cacd6ee17d8e..c75ccadf23dc 100644 > --- a/fs/btrfs/ioctl.c > +++ b/fs/btrfs/ioctl.c > @@ -1600,6 +1600,48 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, > return ret; > } > > +/* > + * Try to start exclusive operation @type or cancel it if it's running. > + * > + * Return: > + * 0 - normal mode, newly claimed op started > + * >0 - normal mode, something else is running, > + * return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS to user space > + * ECANCELED - cancel mode, successful cancel > + * ENOTCONN - cancel mode, operation not running anymore > + */ > +static int exclop_start_or_cancel_reloc(struct btrfs_fs_info *fs_info, > + enum btrfs_exclusive_operation type, bool cancel) > +{ > + if (!cancel) { > + /* Start normal op */ > + if (!btrfs_exclop_start(fs_info, type)) > + return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; > + /* Exclusive operation is now claimed */ > + return 0; > + } > + > + /* Cancel running op */ > + if (btrfs_exclop_start_try_lock(fs_info, type)) { > + /* > + * This blocks any exclop finish from setting it to NONE, so we > + * request cancelation. Either it runs and we will wait for it, > + * or it has finished and no waiting will happen. > + */ > + atomic_inc(&fs_info->reloc_cancel_req); > + btrfs_exclop_start_unlock(fs_info); > + > + if (test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)) > + wait_on_bit(&fs_info->flags, BTRFS_FS_RELOC_RUNNING, > + TASK_INTERRUPTIBLE); Do we want to capture the return value here, in case the user hit's ctrl+c we can return -EINTR instead so we don't think it succeeded? Thanks, Josef
On Fri, May 21, 2021 at 09:29:16AM -0400, Josef Bacik wrote: > On 5/21/21 8:06 AM, David Sterba wrote: > > + /* > > + * This blocks any exclop finish from setting it to NONE, so we > > + * request cancelation. Either it runs and we will wait for it, > > + * or it has finished and no waiting will happen. > > + */ > > + atomic_inc(&fs_info->reloc_cancel_req); > > + btrfs_exclop_start_unlock(fs_info); > > + > > + if (test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)) > > + wait_on_bit(&fs_info->flags, BTRFS_FS_RELOC_RUNNING, > > + TASK_INTERRUPTIBLE); > > Do we want to capture the return value here, in case the user hit's ctrl+c we > can return -EINTR instead so we don't think it succeeded? Thanks, The cancel request will stay, so only the waiting part won't happen. I'm not sure if this is worth to distinguish the two states, eg. allow progs to print a different message. Maybe a waiting and non-waiting cancel modes would be also useful. As the interface is string-based we can also add 'status' that would say if it's running or not. This should cover the usecases, but would be a bit more complicated in the state transitions.
On Fri, May 21, 2021 at 06:45:51PM +0200, David Sterba wrote: > On Fri, May 21, 2021 at 09:29:16AM -0400, Josef Bacik wrote: > > On 5/21/21 8:06 AM, David Sterba wrote: > > > + /* > > > + * This blocks any exclop finish from setting it to NONE, so we > > > + * request cancelation. Either it runs and we will wait for it, > > > + * or it has finished and no waiting will happen. > > > + */ > > > + atomic_inc(&fs_info->reloc_cancel_req); > > > + btrfs_exclop_start_unlock(fs_info); > > > + > > > + if (test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)) > > > + wait_on_bit(&fs_info->flags, BTRFS_FS_RELOC_RUNNING, > > > + TASK_INTERRUPTIBLE); > > > > Do we want to capture the return value here, in case the user hit's ctrl+c we > > can return -EINTR instead so we don't think it succeeded? Thanks, > > The cancel request will stay, so only the waiting part won't happen. I'm > not sure if this is worth to distinguish the two states, eg. allow progs > to print a different message. So as the cancelling would happen I hope it's ok to return the ECANCELED error in all cases. Other operations like balance or scrub aren't interruptible and wait until the condition is satisified, but there's a different pattern regarding the cancel request so it has to be that way there. This could be unified but right now I don't see the need for that. > Maybe a waiting and non-waiting cancel modes would be also useful. As > the interface is string-based we can also add 'status' that would say if > it's running or not. This should cover the usecases, but would be > a bit more complicated in the state transitions. I've asked on IRC what's the expected behaviour of cancel command regarding waiting/not waiting. Seems that 'wait until it's finished' is preferred and it's consistent with what scrub and balance (cancel) do.
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index cacd6ee17d8e..c75ccadf23dc 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1600,6 +1600,48 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, return ret; } +/* + * Try to start exclusive operation @type or cancel it if it's running. + * + * Return: + * 0 - normal mode, newly claimed op started + * >0 - normal mode, something else is running, + * return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS to user space + * ECANCELED - cancel mode, successful cancel + * ENOTCONN - cancel mode, operation not running anymore + */ +static int exclop_start_or_cancel_reloc(struct btrfs_fs_info *fs_info, + enum btrfs_exclusive_operation type, bool cancel) +{ + if (!cancel) { + /* Start normal op */ + if (!btrfs_exclop_start(fs_info, type)) + return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; + /* Exclusive operation is now claimed */ + return 0; + } + + /* Cancel running op */ + if (btrfs_exclop_start_try_lock(fs_info, type)) { + /* + * This blocks any exclop finish from setting it to NONE, so we + * request cancelation. Either it runs and we will wait for it, + * or it has finished and no waiting will happen. + */ + atomic_inc(&fs_info->reloc_cancel_req); + btrfs_exclop_start_unlock(fs_info); + + if (test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)) + wait_on_bit(&fs_info->flags, BTRFS_FS_RELOC_RUNNING, + TASK_INTERRUPTIBLE); + + return -ECANCELED; + } + + /* Something else is running or none */ + return -ENOTCONN; +} + static noinline int btrfs_ioctl_resize(struct file *file, void __user *arg) {
To support optional cancelation of some operations, add helper that will wrap all the combinations. In normal mode it's same as btrfs_exclop_start, in cancelation mode it checks if it's already running and request cancelation and waits until completion. The error codes can be returned to to user space and semantics is not changed, adding ECANCELED. This should be evaluated as an error and that the operation has not completed and the operation should be restarted or the filesystem status reviewed. Signed-off-by: David Sterba <dsterba@suse.com> --- fs/btrfs/ioctl.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)