@@ -49,6 +49,7 @@ 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",
+ "-q|--enqueue enqueue if an exclusive operation is running",
NULL
};
@@ -62,6 +63,7 @@ static int cmd_device_add(const struct cmd_struct *cmd,
int force = 0;
int last_dev;
char exop[BTRFS_SYSFS_EXOP_SIZE];
+ bool enqueue = false;
optind = 0;
while (1) {
@@ -69,10 +71,11 @@ static int cmd_device_add(const struct cmd_struct *cmd,
static const struct option long_options[] = {
{ "nodiscard", optional_argument, NULL, 'K'},
{ "force", no_argument, NULL, 'f'},
+ { "enqueue", no_argument, NULL, 'q'},
{ NULL, 0, NULL, 0}
};
- c = getopt_long(argc, argv, "Kf", long_options, NULL);
+ c = getopt_long(argc, argv, "Kfq", long_options, NULL);
if (c < 0)
break;
switch (c) {
@@ -82,6 +85,9 @@ static int cmd_device_add(const struct cmd_struct *cmd,
case 'f':
force = 1;
break;
+ case 'q':
+ enqueue = true;
+ break;
default:
usage_unknown_option(cmd, argv);
}
@@ -98,9 +104,15 @@ static int cmd_device_add(const struct cmd_struct *cmd,
return 1;
if (get_exclusive_operation(fdmnt, exop) > 0 && strcmp(exop, "none")) {
- error("unable to add device: %s in progress", exop);
- close_file_or_dir(fdmnt, dirstream);
- return 1;
+ if (enqueue) {
+ printf("%s in progress. Waiting for %s to finish.\n",
+ exop, exop);
+ wait_for_exclusive_operation(fdmnt);
+ } else {
+ error("unable to add: %s in progress", exop);
+ close_file_or_dir(fdmnt, dirstream);
+ return 1;
+ }
}
for (i = optind; i < last_dev; i++){
@@ -163,8 +175,27 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
int i, fdmnt, ret = 0;
DIR *dirstream = NULL;
char exop[BTRFS_SYSFS_EXOP_SIZE];
+ bool enqueue = false;
- clean_args_no_options(cmd, argc, argv);
+
+ while (1) {
+ int c;
+ static const struct option long_options[] = {
+ { "enqueue", no_argument, NULL, 'q'},
+ { NULL, 0, NULL, 0}
+ };
+
+ c = getopt_long(argc, argv, "q", long_options, NULL);
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'q':
+ enqueue = true;
+ break;
+ default:
+ usage_unknown_option(cmd, argv);
+ }
+ }
if (check_argc_min(argc - optind, 2))
return 1;
@@ -176,9 +207,15 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
return 1;
if (get_exclusive_operation(fdmnt, exop) > 0 && strcmp(exop, "none")) {
- error("unable to remove device: %s in progress", exop);
- close_file_or_dir(fdmnt, dirstream);
- return 1;
+ if (enqueue) {
+ printf("%s in progress. Waiting for %s to finish.\n",
+ exop, exop);
+ wait_for_exclusive_operation(fdmnt);
+ } else {
+ error("unable to remove device: %s in progress", exop);
+ close_file_or_dir(fdmnt, dirstream);
+ return 1;
+ }
}
for(i = optind; i < argc - 1; i++) {
@@ -251,7 +288,8 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
"the device.", \
"If 'missing' is specified for <device>, the first device that is", \
"described by the filesystem metadata, but not present at the mount", \
- "time will be removed. (only in degraded mode)"
+ "time will be removed. (only in degraded mode)", \
+ "-q|--enqueue enqueue if an exclusive operation is running"
static const char * const cmd_device_remove_usage[] = {
"btrfs device remove <device>|<devid> [<device>|<devid>...] <path>",
@@ -1080,8 +1080,19 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
DIR *dirstream = NULL;
struct stat st;
char exop[BTRFS_SYSFS_EXOP_SIZE];
+ bool enqueue = false;
- clean_args_no_options_relaxed(cmd, argc, argv);
+ while(1) {
+ int c = getopt(argc - 2, argv, "q");
+ if (c < 0)
+ break;
+
+ switch(c) {
+ case 'q':
+ enqueue = true;
+ break;
+ }
+ }
if (check_argc_exact(argc - optind, 2))
return 1;
@@ -1112,9 +1123,15 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
return 1;
if (get_exclusive_operation(fd, exop) > 0 && strcmp(exop, "none")) {
- error("unable to resize: %s in progress", exop);
- close_file_or_dir(fd, dirstream);
- return 1;
+ if (enqueue) {
+ printf("%s in progress. Waiting for %s to finish.\n",
+ exop, exop);
+ wait_for_exclusive_operation(fd);
+ } else {
+ error("unable to resize: %s in progress", exop);
+ close_file_or_dir(fd, dirstream);
+ return 1;
+ }
}
printf("Resize '%s' of '%s'\n", path, amount);
@@ -58,3 +58,29 @@ int get_exclusive_operation(int mp_fd, char *val)
*s = '\0';
return strlen(val);
}
+
+int sysfs_wait(int fd, int seconds)
+{
+ fd_set fds;
+ struct timeval tv;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ tv.tv_sec = seconds;
+ tv.tv_usec = 0;
+
+ return select(fd+1, NULL, NULL, &fds, &tv);
+}
+
+void wait_for_exclusive_operation(int dirfd)
+{
+ char exop[BTRFS_SYSFS_EXOP_SIZE];
+ int fd;
+
+ fd = sysfs_open(dirfd, "exclusive_operation");
+ while ((sysfs_get_str_fd(fd, exop, BTRFS_SYSFS_EXOP_SIZE) > 0) &&
+ strncmp(exop, "none", 4))
+ sysfs_wait(fd, 1);
+ close(fd);
+}
@@ -155,5 +155,6 @@ int btrfs_warn_multiple_profiles(int fd);
#define BTRFS_SYSFS_EXOP_SIZE 16
int get_exclusive_operation(int fd, char *val);
+void wait_for_exclusive_operation(int fd);
#endif