btrfs-progs: add enqueue parameter for exclusive ops

The exclusive ops will not start if there's one already running. Now
that we have the sysfs export (since kernel 5.10) to check if there's
one already running, use it to allow enqueueing of the operations as a
convenience.

Supported enqueuing:

  btrfs balance start --enqueue
  btrfs filesystem resize --enqueue
  btrfs device add --enqueue
  btrfs device delete --enqueue
  btrfs replace start --enqueue

This patch implements the functionality based on Goldwyn's patch
https://lore.kernel.org/linux-btrfs/?q=20200825150338.32610-4-rgoldwyn%40suse.de
but on top of previous preparatory patches.

Note that 'filesystem resize' options could confuse getopt as the
negative size change looks like a series of short options and there's no
way to make getopt ignore the short options, so there's a custom option
parser.

Signed-off-by: Goldwyn Rodrigues <rgoldwyn@suse.de>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
David Sterba 2020-11-05 03:38:00 +01:00
parent 0c77d2cd03
commit e198c6674a
10 changed files with 132 additions and 46 deletions

View file

@ -117,6 +117,8 @@ force a reduction of metadata integrity, eg. when going from 'raid1' to 'single'
--background|--bg::::
run the balance operation asynchronously in the background, uses `fork`(2) to
start the process that calls the kernel ioctl
--enqueue::::
wait if there's another exclusive operation running, otherwise continue
-v::::
(deprecated) alias for global '-v' option

View file

@ -61,8 +61,10 @@ headers.
do not perform discard (TRIM) by default
-f|--force::::
force overwrite of existing filesystem on the given disk(s)
--enqueue::::
wait if there's another exclusive operation running, otherwise continue
*remove* <device>|<devid> [<device>|<devid>...] <path>::
*remove* [options] <device>|<devid> [<device>|<devid>...] <path>::
Remove device(s) from a filesystem identified by <path>
+
Device removal must satisfy the profile constraints, otherwise the command
@ -85,6 +87,11 @@ NOTE: In most cases, there is only one missing device in degraded mode,
otherwise mount fails. If there are two or more devices missing (e.g. possible
in RAID6), you need specify 'missing' as many times as the number of missing
devices to remove all of them.
+
`Options`
+
--enqueue::::
wait if there's another exclusive operation running, otherwise continue
*delete* <device>|<devid> [<device>|<devid>...] <path>::
Alias of remove kept for backward compatibility

View file

@ -186,7 +186,7 @@ NOTE: the maximum allowable length shall be less than 256 chars and must not con
a newline. The trailing newline is stripped automatically.
// Some wording are extracted by the resize2fs man page
*resize* [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max <path>::
*resize* [options] [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max <path>::
Resize a mounted filesystem identified by 'path'. A particular device
can be resized by specifying a 'devid'.
+
@ -220,6 +220,11 @@ take a long time if there are data in the device area that's beyond the new
end. Relocation of the data takes time.
+
See also section 'EXAMPLES'.
+
`Options`
+
--enqueue::::
wait if there's another exclusive operation running, otherwise continue
*show* [options] [<path>|<uuid>|<device>|<label>]::
Show the btrfs filesystem with some additional info about devices and space

View file

@ -18,7 +18,7 @@ SUBCOMMAND
*cancel* <mount_point>::
Cancel a running device replace operation.
*start* [-Bfr] <srcdev>|<devid> <targetdev> <path>::
*start* [options] <srcdev>|<devid> <targetdev> <path>::
Replace device of a btrfs filesystem.
+
On a live filesystem, duplicate the data to the target device which
@ -53,6 +53,8 @@ never allowed to be used as the <targetdev>.
+
-B::::
no background replace.
--enqueue::::
wait if there's another exclusive operation running, otherwise continue
*status* [-1] <mount_point>::
Print status and progress information of a running device replace operation.

View file

@ -431,22 +431,20 @@ enum {
};
static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
unsigned flags)
unsigned flags, bool enqueue)
{
int fd;
int ret;
DIR *dirstream = NULL;
int exclop;
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0)
return 1;
exclop = get_fs_exclop(fd);
if (exclop > 0 ) {
error(
"unable to start balance, another exclusive operation '%s' in progress",
get_fs_exclop_name(exclop));
ret = check_running_fs_exclop(fd, BTRFS_EXCLOP_BALANCE, enqueue);
if (ret != 0) {
if (ret < 0)
error("unable to check status of exclusive operation: %m");
close_file_or_dir(fd, dirstream);
return 1;
}
@ -509,6 +507,8 @@ static const char * const cmd_balance_start_usage[] = {
"--full-balance do not print warning and do not delay start",
"--background|--bg",
" run the balance as a background process",
"--enqueue wait if there's another exclusive operation running,",
" otherwise continue",
"-v|--verbose deprecated, alias for global -v option",
HELPINFO_INSERT_GLOBALS,
HELPINFO_INSERT_VERBOSE,
@ -524,6 +524,7 @@ static int cmd_balance_start(const struct cmd_struct *cmd,
&args.meta, NULL };
int force = 0;
int background = 0;
bool enqueue = false;
unsigned start_flags = 0;
int i;
@ -532,7 +533,8 @@ static int cmd_balance_start(const struct cmd_struct *cmd,
optind = 0;
while (1) {
enum { GETOPT_VAL_FULL_BALANCE = 256,
GETOPT_VAL_BACKGROUND = 257 };
GETOPT_VAL_BACKGROUND = 257,
GETOPT_VAL_ENQUEUE };
static const struct option longopts[] = {
{ "data", optional_argument, NULL, 'd'},
{ "metadata", optional_argument, NULL, 'm' },
@ -544,6 +546,7 @@ static int cmd_balance_start(const struct cmd_struct *cmd,
{ "background", no_argument, NULL,
GETOPT_VAL_BACKGROUND },
{ "bg", no_argument, NULL, GETOPT_VAL_BACKGROUND },
{ "enqueue", no_argument, NULL, GETOPT_VAL_ENQUEUE},
{ NULL, 0, NULL, 0 }
};
@ -585,6 +588,9 @@ static int cmd_balance_start(const struct cmd_struct *cmd,
case GETOPT_VAL_BACKGROUND:
background = 1;
break;
case GETOPT_VAL_ENQUEUE:
enqueue = true;
break;
default:
usage_unknown_option(cmd, argv);
}
@ -690,7 +696,7 @@ static int cmd_balance_start(const struct cmd_struct *cmd,
}
}
return do_balance(argv[optind], &args, start_flags);
return do_balance(argv[optind], &args, start_flags, enqueue);
}
static DEFINE_SIMPLE_COMMAND(balance_start, "start");
@ -942,7 +948,8 @@ static int cmd_balance_full(const struct cmd_struct *cmd, int argc, char **argv)
memset(&args, 0, sizeof(args));
args.flags |= BTRFS_BALANCE_TYPE_MASK;
return do_balance(argv[1], &args, BALANCE_START_NOWARN);
/* No enqueueing supported for the obsolete syntax */
return do_balance(argv[1], &args, BALANCE_START_NOWARN, false);
}
static DEFINE_COMMAND(balance_full, "--full-balance", cmd_balance_full,
NULL, NULL, CMD_HIDDEN);
@ -971,7 +978,8 @@ static int cmd_balance(const struct cmd_struct *cmd, int argc, char **argv)
memset(&args, 0, sizeof(args));
args.flags |= BTRFS_BALANCE_TYPE_MASK;
return do_balance(argv[1], &args, 0);
/* No enqueueing supported for the obsolete syntax */
return do_balance(argv[1], &args, 0, false);
}
return handle_command_group(cmd, argc, argv);

View file

@ -49,6 +49,8 @@ static const char * const cmd_device_add_usage[] = {
"",
"-K|--nodiscard do not perform whole device TRIM on devices that report such capability",
"-f|--force force overwrite existing filesystem on the disk",
"--enqueue wait if there's another exclusive operation running,",
" otherwise continue",
NULL
};
@ -61,14 +63,16 @@ static int cmd_device_add(const struct cmd_struct *cmd,
int discard = 1;
int force = 0;
int last_dev;
int exclop;
bool enqueue = false;
optind = 0;
while (1) {
int c;
enum { GETOPT_VAL_ENQUEUE = 256 };
static const struct option long_options[] = {
{ "nodiscard", optional_argument, NULL, 'K'},
{ "force", no_argument, NULL, 'f'},
{ "enqueue", no_argument, NULL, GETOPT_VAL_ENQUEUE},
{ NULL, 0, NULL, 0}
};
@ -82,6 +86,9 @@ static int cmd_device_add(const struct cmd_struct *cmd,
case 'f':
force = 1;
break;
case GETOPT_VAL_ENQUEUE:
enqueue = true;
break;
default:
usage_unknown_option(cmd, argv);
}
@ -97,11 +104,10 @@ static int cmd_device_add(const struct cmd_struct *cmd,
if (fdmnt < 0)
return 1;
exclop = get_fs_exclop(fdmnt);
if (exclop > 0) {
error(
"unable to start device add, another exclusive operation '%s' in progress",
get_fs_exclop_name(exclop));
ret = check_running_fs_exclop(fdmnt, BTRFS_EXCLOP_DEV_ADD, enqueue);
if (ret != 0) {
if (ret < 0)
error("unable to check status of exclusive operation: %m");
close_file_or_dir(fdmnt, dirstream);
return 1;
}
@ -165,9 +171,28 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
char *mntpnt;
int i, fdmnt, ret = 0;
DIR *dirstream = NULL;
int exclop;
bool enqueue = false;
clean_args_no_options(cmd, argc, argv);
optind = 0;
while (1) {
int c;
enum { GETOPT_VAL_ENQUEUE = 256 };
static const struct option long_options[] = {
{ "enqueue", no_argument, NULL, GETOPT_VAL_ENQUEUE},
{ NULL, 0, NULL, 0}
};
c = getopt_long(argc, argv, "", long_options, NULL);
if (c < 0)
break;
switch (c) {
case GETOPT_VAL_ENQUEUE:
enqueue = true;
break;
default:
usage_unknown_option(cmd, argv);
}
}
if (check_argc_min(argc - optind, 2))
return 1;
@ -178,11 +203,10 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
if (fdmnt < 0)
return 1;
exclop = get_fs_exclop(fdmnt);
if (exclop > 0 ) {
error(
"unable to start device remove, another exclusive operation '%s' in progress",
get_fs_exclop_name(exclop));
ret = check_running_fs_exclop(fdmnt, BTRFS_EXCLOP_DEV_REMOVE, enqueue);
if (ret != 0) {
if (ret < 0)
error("unable to check status of exclusive operation: %m");
close_file_or_dir(fdmnt, dirstream);
return 1;
}
@ -264,6 +288,8 @@ static const char * const cmd_device_remove_usage[] = {
"Remove a device from a filesystem",
COMMON_USAGE_REMOVE_DELETE,
"",
"--enqueue wait if there's another exclusive operation running,",
" otherwise continue",
NULL
};
@ -279,6 +305,8 @@ static const char * const cmd_device_delete_usage[] = {
"Remove a device from a filesystem (alias of \"btrfs device remove\")",
COMMON_USAGE_REMOVE_DELETE,
"",
"--enqueue wait if there's another exclusive operation running,",
" otherwise continue",
NULL
};

View file

@ -1063,11 +1063,14 @@ next:
static DEFINE_SIMPLE_COMMAND(filesystem_defrag, "defragment");
static const char * const cmd_filesystem_resize_usage[] = {
"btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
"btrfs filesystem resize [options] [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
"Resize a filesystem",
"If 'max' is passed, the filesystem will occupy all available space",
"on the device 'devid'.",
"[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.",
"",
"--enqueue wait if there's another exclusive operation running,",
" otherwise continue",
NULL
};
@ -1078,10 +1081,27 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
int fd, res, len, e;
char *amount, *path;
DIR *dirstream = NULL;
int ret;
struct stat st;
int exclop;
bool enqueue = false;
clean_args_no_options_relaxed(cmd, argc, argv);
/*
* Simplified option parser, accept only long options, the resize value
* could be negative and is recognized as short options by getopt
*/
for (optind = 1; optind < argc; optind++) {
if (strcmp(argv[optind], "--enqueue") == 0) {
enqueue = true;
} else if (strcmp(argv[optind], "--") == 0) {
/* Separator: options -- non-options */
} else if (strncmp(argv[optind], "--", 2) == 0) {
/* Emulate what getopt does on unkonwn option */
optind++;
usage_unknown_option(cmd, argv);
} else {
break;
}
}
if (check_argc_exact(argc - optind, 2))
return 1;
@ -1111,11 +1131,10 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
if (fd < 0)
return 1;
exclop = get_fs_exclop(fd);
if (exclop > 0) {
error(
"unable to start filesystem resize, nother exclusive operation '%s' in progress",
get_fs_exclop_name(exclop));
ret = check_running_fs_exclop(fd, BTRFS_EXCLOP_RESIZE, enqueue);
if (ret != 0) {
if (ret < 0)
error("unable to check status of exclusive operation: %m");
close_file_or_dir(fd, dirstream);
return 1;
}

View file

@ -28,6 +28,7 @@
#include <assert.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <getopt.h>
#include "kerncompat.h"
#include "kernel-shared/ctree.h"
@ -113,6 +114,8 @@ static const char *const cmd_replace_start_usage[] = {
" correct checksum. Devices which are currently mounted are",
" never allowed to be used as the <targetdev>",
"-B do not background",
"--enqueue wait if there's another exclusive operation running,",
" otherwise continue",
NULL
};
@ -123,7 +126,6 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
struct btrfs_ioctl_dev_replace_args status_args = {0};
int ret;
int i;
int c;
int fdmnt = -1;
int fddstdev = -1;
char *path;
@ -136,10 +138,20 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
DIR *dirstream = NULL;
u64 srcdev_size;
u64 dstdev_size;
int exclop;
bool enqueue = false;
optind = 0;
while ((c = getopt(argc, argv, "Brf")) != -1) {
while (1) {
int c;
enum { GETOPT_VAL_ENQUEUE = 256 };
static const struct option long_options[] = {
{ "enqueue", no_argument, NULL, GETOPT_VAL_ENQUEUE},
{ NULL, 0, NULL, 0}
};
c = getopt_long(argc, argv, "Brf", long_options, NULL);
if (c < 0)
break;
switch (c) {
case 'B':
do_not_background = 1;
@ -150,6 +162,9 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
case 'f':
force_using_targetdev = 1;
break;
case GETOPT_VAL_ENQUEUE:
enqueue = true;
break;
default:
usage_unknown_option(cmd, argv);
}
@ -260,11 +275,10 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
}
/* Check status before any potentially destructive operation */
exclop = get_fs_exclop(fdmnt);
if (exclop > 0) {
error(
"unable to start device replace, another exclusive operation '%s' in progress",
get_fs_exclop_name(exclop));
ret = check_running_fs_exclop(fdmnt, BTRFS_EXCLOP_DEV_REPLACE, enqueue);
if (ret != 0) {
if (ret < 0)
error("unable to check status of exclusive operation: %m");
close_file_or_dir(fdmnt, dirstream);
goto leave_with_error;
}

View file

@ -1987,7 +1987,7 @@ const char *get_fs_exclop_name(int op)
* 1 - another operation running
* <0 - there was another error
*/
int check_running_fs_exclop(int fd, const char *desc, bool enqueue)
int check_running_fs_exclop(int fd, enum exclusive_operation start, bool enqueue)
{
int sysfs_fd;
int exclop;
@ -2009,7 +2009,8 @@ int check_running_fs_exclop(int fd, const char *desc, bool enqueue)
if (!enqueue) {
error(
"unable to start %s, another exclusive operation '%s' in progress",
desc, get_fs_exclop_name(exclop));
get_fs_exclop_name(start),
get_fs_exclop_name(exclop));
ret = 1;
goto out;
}

View file

@ -88,7 +88,7 @@ int get_fs_info(const char *path, struct btrfs_ioctl_fs_info_args *fi_args,
int get_fsid(const char *path, u8 *fsid, int silent);
int get_fsid_fd(int fd, u8 *fsid);
int get_fs_exclop(int fd);
int check_running_fs_exclop(int fd, const char *desc, bool enqueue);
int check_running_fs_exclop(int fd, enum exclusive_operation start, bool enqueue);
const char *get_fs_exclop_name(int op);
int get_label(const char *btrfs_dev, char *label);