aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2023-12-08 23:17:17 +0100
committerDavid Sterba <dsterba@suse.com>2023-12-09 01:57:27 +0100
commit30d1a2d39028cdab66a6ae1ad3a45dba3e6863b1 (patch)
tree1b480795ffa10ac7fc268ba17153e0cd20f33259
parent87dba20daf84e6f6b0cbc77bc2f231660918be11 (diff)
downloadbtrfs-progs-30d1a2d39028cdab66a6ae1ad3a45dba3e6863b1.tar.gz
btrfs-progs: scrub limit: allow to set the limit
Add new options to set the per-device limit (requires root privileges as it writes to the sysfs files). Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--Documentation/btrfs-scrub.rst8
-rw-r--r--cmds/scrub.c65
-rwxr-xr-xtests/cli-tests/024-scrub-limit/test.sh22
3 files changed, 91 insertions, 4 deletions
diff --git a/Documentation/btrfs-scrub.rst b/Documentation/btrfs-scrub.rst
index c9d260da..0cd78705 100644
--- a/Documentation/btrfs-scrub.rst
+++ b/Documentation/btrfs-scrub.rst
@@ -26,10 +26,16 @@ cancel <path>|<device>
.. _man-scrub-limit:
limit [options] <path>
- Show scrub limits set on devices of the given filesystem.
+ Show or set scrub limits on devices of the given filesystem.
``Options``
+ -d|--devid DEVID
+ select the device by DEVID to apply the limit
+ -l|--limit SIZE
+ set the limit of the device to SIZE (size units with suffix),
+ or 0 to reset to *unlimited*
+
--raw
print all numbers raw values in bytes without the *B* suffix
--human-readable
diff --git a/cmds/scrub.c b/cmds/scrub.c
index 9bbbdb2d..785a30e7 100644
--- a/cmds/scrub.c
+++ b/cmds/scrub.c
@@ -50,6 +50,7 @@
#include "common/open-utils.h"
#include "common/units.h"
#include "common/device-utils.h"
+#include "common/parse-utils.h"
#include "common/sysfs-utils.h"
#include "common/string-table.h"
#include "common/string-utils.h"
@@ -1212,6 +1213,17 @@ static u64 read_scrub_device_limit(int fd, u64 devid)
return limit;
}
+static u64 write_scrub_device_limit(int fd, u64 devid, u64 limit)
+{
+ char path[PATH_MAX] = { 0 };
+ int ret;
+
+ /* /sys/fs/btrfs/FSID/devinfo/1/scrub_speed_max */
+ snprintf(path, sizeof(path), "devinfo/%llu/scrub_speed_max", devid);
+ ret = sysfs_write_fsid_file_u64(fd, path, limit);
+ return ret;
+}
+
static int scrub_start(const struct cmd_struct *cmd, int argc, char **argv,
bool resume)
{
@@ -1970,8 +1982,10 @@ static DEFINE_SIMPLE_COMMAND(scrub_status, "status");
static const char * const cmd_scrub_limit_usage[] = {
"btrfs scrub limit [options] <path>",
- "Show scrub limits set on devices of the given filesystem.",
+ "Show or set scrub limits on devices of the given filesystem.",
"",
+ OPTLINE("-d|--devid DEVID", "select the device by DEVID to apply the limit"),
+ OPTLINE("-l|--limit SIZE", "set the limit of the device to SIZE (size units with suffix), or 0 to reset to unlimited"),
HELPINFO_UNITS_LONG,
NULL
};
@@ -1985,6 +1999,10 @@ static int cmd_scrub_limit(const struct cmd_struct *cmd, int argc, char **argv)
int fd = -1;
DIR *dirstream = NULL;
int cols, idx;
+ u64 opt_devid = 0;
+ bool devid_set = false;
+ u64 opt_limit = 0;
+ bool limit_set = false;
unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
@@ -1992,14 +2010,24 @@ static int cmd_scrub_limit(const struct cmd_struct *cmd, int argc, char **argv)
while (1) {
int c;
static const struct option long_options[] = {
+ { "devid", required_argument, NULL, 'd' },
+ { "limit", required_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 }
};
- c = getopt_long(argc, argv, "", long_options, NULL);
+ c = getopt_long(argc, argv, "d:l:", long_options, NULL);
if (c < 0)
break;
switch (c) {
+ case 'd':
+ opt_devid = arg_strtou64(optarg);
+ devid_set = true;
+ break;
+ case 'l':
+ opt_limit = parse_size_from_string(optarg);
+ limit_set = true;
+ break;
default:
usage_unknown_option(cmd, argv);
}
@@ -2007,6 +2035,11 @@ static int cmd_scrub_limit(const struct cmd_struct *cmd, int argc, char **argv)
if (check_argc_exact(argc - optind, 1))
return 1;
+ if ((devid_set && !limit_set) || (!devid_set && limit_set)) {
+ error("--devid and --limit must be set together");
+ return 1;
+ }
+
fd = open_file_or_dir(argv[optind], &dirstream);
if (fd < 0)
return 1;
@@ -2025,6 +2058,34 @@ static int cmd_scrub_limit(const struct cmd_struct *cmd, int argc, char **argv)
uuid_unparse(fi_args.fsid, fsid);
pr_verbose(LOG_DEFAULT, "UUID: %s\n", fsid);
+ if (devid_set) {
+ /* Set one device only. */
+ struct btrfs_ioctl_dev_info_args di_args = { 0 };
+ u64 limit;
+
+ ret = device_get_info(fd, opt_devid, &di_args);
+ if (ret == -ENODEV) {
+ error("device with devid %llu not found", opt_devid);
+ ret = 1;
+ goto out;
+ }
+ limit = read_scrub_device_limit(fd, opt_devid);
+ pr_verbose(LOG_DEFAULT, "Set scrub limit of devid %llu from %s%s to %s%s\n",
+ opt_devid,
+ limit > 0 ? pretty_size_mode(limit, unit_mode) : "unlimited",
+ limit > 0 ? "/s" : "",
+ opt_limit > 0 ? pretty_size_mode(opt_limit, unit_mode) : "unlimited",
+ opt_limit > 0 ? "/s" : "");
+ ret = write_scrub_device_limit(fd, opt_devid, opt_limit);
+ if (ret < 0) {
+ errno = -ret;
+ error("cannot write to the sysfs file: %m");
+ ret = 1;
+ }
+ ret = 0;
+ goto out;
+ }
+
cols = 3;
table = table_create(cols, 2 + fi_args.num_devices);
if (!table) {
diff --git a/tests/cli-tests/024-scrub-limit/test.sh b/tests/cli-tests/024-scrub-limit/test.sh
index a360f15c..f6285130 100755
--- a/tests/cli-tests/024-scrub-limit/test.sh
+++ b/tests/cli-tests/024-scrub-limit/test.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Read scrub limits on a filesystem
+# Read and set scrub limits on a filesystem
source "$TEST_TOP/common" || exit
@@ -7,15 +7,19 @@ setup_root_helper
setup_loopdevs 4
prepare_loopdevs
TEST_DEV=${loopdevs[1]}
+support=true
fsid="13411a59-ccea-4296-a6f8-1446ccf8c9be"
sysfs="/sys/fs/btrfs/13411a59-ccea-4296-a6f8-1446ccf8c9be"
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f --uuid "$fsid" -d raid1 -m raid1 "${loopdevs[@]}"
run_check_mount_test_dev
+
+# Set the limits directly
for i in "$sysfs"/devinfo/*/scrub_speed_max; do
if ! [ -f "$i" ]; then
_log "sysfs file scrub_speed_max not available, skip setting limits"
+ support=false
break;
fi
run_check cat "$i"
@@ -23,6 +27,22 @@ for i in "$sysfs"/devinfo/*/scrub_speed_max; do
done
# This works even if scrub_speed_max files don't exist, this is equivalent to unlimited
run_check "$TOP/btrfs" scrub limit "$TEST_MNT"
+
+# The rest of the test would fail
+if ! $support; then
+ run_check_umount_test_dev
+ cleanup_loopdevs
+fi
+
+# Set the limits by command
+here=`pwd`
+cd "$sysfs/devinfo"
+for i in *; do
+ run_check $SUDO_HELPER "$TOP/btrfs" scrub limit -d "$i" -l 20m "$TEST_MNT"
+done
+cd "$here"
+run_check "$TOP/btrfs" scrub limit "$TEST_MNT"
+
run_check_umount_test_dev
cleanup_loopdevs