From fbf211459af2122ad132a141ac3bb64e14468df9 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 7 Dec 2023 19:45:12 +0100 Subject: [PATCH] btrfs-progs: srcub: new subcommand limit Add new command to read the scrub limits set via the sysfs file (no root access needed). Example output: $ btrfs scrub limit /mnt UUID: 57a05502-9e81-4b21-ad9d-0fc31863ed11 Id Limit Path -- ----- -------------- 1 - /dev/nvme0n1p1 2 - /dev/nvme0n1p2 3 - /dev/nvme0n1p3 4 - /dev/nvme2n1p4 5 - /dev/nvme0n1p5 6 - /dev/nvme0n1p6 Signed-off-by: David Sterba --- Documentation/btrfs-scrub.rst | 23 +++++++ cmds/scrub.c | 114 ++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/Documentation/btrfs-scrub.rst b/Documentation/btrfs-scrub.rst index 712efa76..6535c5ce 100644 --- a/Documentation/btrfs-scrub.rst +++ b/Documentation/btrfs-scrub.rst @@ -23,6 +23,29 @@ cancel | The progress is saved in the status file so :command:`btrfs scrub resume` can continue from the last position. +limit [options] + Show scrub limits set on devices of the given filesystem. + + ``Options`` + + --raw + print all numbers raw values in bytes without the *B* suffix + --human-readable + print human friendly numbers, base 1024, this is the default + --iec + select the 1024 base for the following options, according to + the IEC standard + --si + select the 1000 base for the following options, according to the SI standard + --kbytes + show sizes in KiB, or kB with --si + --mbytes + show sizes in MiB, or MB with --si + --gbytes + show sizes in GiB, or GB with --si + --tbytes + show sizes in TiB, or TB with --si + resume [-BdqrR] | Resume a cancelled or interrupted scrub on the filesystem identified by *path* or on a given *device*. The starting point is read from the diff --git a/cmds/scrub.c b/cmds/scrub.c index 7c4da4ef..2dff766b 100644 --- a/cmds/scrub.c +++ b/cmds/scrub.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,11 @@ #include "common/utils.h" #include "common/open-utils.h" #include "common/units.h" +#include "common/device-utils.h" #include "common/sysfs-utils.h" +#include "common/string-table.h" +#include "common/string-utils.h" +#include "common/parse-utils.h" #include "common/help.h" #include "cmds/commands.h" @@ -1950,6 +1955,114 @@ out: } 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.", + "", + HELPINFO_UNITS_LONG, + NULL +}; + +static int cmd_scrub_limit(const struct cmd_struct *cmd, int argc, char **argv) +{ + struct btrfs_ioctl_fs_info_args fi_args = { 0 }; + char fsid[BTRFS_UUID_UNPARSED_SIZE]; + struct string_table *table = NULL; + int ret; + int fd = -1; + DIR *dirstream = NULL; + int cols, idx; + + unit_mode = get_unit_mode_from_arg(&argc, argv, 0); + + optind = 0; + while (1) { + int c; + static const struct option long_options[] = { + { NULL, 0, NULL, 0 } + }; + + c = getopt_long(argc, argv, "", long_options, NULL); + if (c < 0) + break; + + switch (c) { + default: + usage_unknown_option(cmd, argv); + } + } + if (check_argc_exact(argc - optind, 1)) + return 1; + + fd = open_file_or_dir(argv[optind], &dirstream); + if (fd < 0) + return 1; + + ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args); + if (ret < 0) { + error("failed to read filesystem info: %m"); + ret = 1; + goto out; + } + if (fi_args.num_devices == 0) { + error("no devices found"); + ret = 1; + goto out; + } + uuid_unparse(fi_args.fsid, fsid); + pr_verbose(LOG_DEFAULT, "UUID: %s\n", fsid); + + cols = 3; + table = table_create(cols, 2 + fi_args.num_devices); + if (!table) { + error_msg(ERROR_MSG_MEMORY, NULL); + ret = 1; + goto out; + } + table->spacing = STRING_TABLE_SPACING_2; + idx = 0; + table_printf(table, idx++, 0, ">Id"); + table_printf(table, idx++, 0, ">Limit"); + table_printf(table, idx++, 0, ">Path"); + for (int i = 0; i < cols; i++) + table_printf(table, i, 1, "*-"); + + for (u64 devid = 1, i = 0; devid <= fi_args.max_id; devid++) { + u64 limit; + struct btrfs_ioctl_dev_info_args di_args = { 0 }; + + ret = device_get_info(fd, devid, &di_args); + if (ret == -ENODEV) { + continue; + } else if (ret < 0) { + errno = -ret; + error("cannot read devid %llu info: %m", devid); + goto out; + } + + limit = read_scrub_device_limit(fd, di_args.devid); + idx = 0; + table_printf(table, idx++, 2 + i, ">%llu", di_args.devid); + if (limit > 0) { + table_printf(table, idx++, 2 + i, ">%s", + pretty_size_mode(limit, unit_mode)); + } else { + table_printf(table, idx++, 2 + i, ">%s", "-"); + } + table_printf(table, idx++, 2 + i, "<%s", di_args.path); + i++; + } + table_dump(table); + +out: + if (table) + table_free(table); + close_file_or_dir(fd, dirstream); + + return !!ret; +} +static DEFINE_SIMPLE_COMMAND(scrub_limit, "limit"); + static const char scrub_cmd_group_info[] = "verify checksums of data and metadata"; @@ -1959,6 +2072,7 @@ static const struct cmd_group scrub_cmd_group = { &cmd_struct_scrub_cancel, &cmd_struct_scrub_resume, &cmd_struct_scrub_status, + &cmd_struct_scrub_limit, NULL } };