From 30d1a2d39028cdab66a6ae1ad3a45dba3e6863b1 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 8 Dec 2023 23:17:17 +0100 Subject: [PATCH] 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 --- Documentation/btrfs-scrub.rst | 8 ++- cmds/scrub.c | 65 ++++++++++++++++++++++++- tests/cli-tests/024-scrub-limit/test.sh | 22 ++++++++- 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 | .. _man-scrub-limit: limit [options] - 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] ", - "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