aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2023-12-07 19:45:12 +0100
committerDavid Sterba <dsterba@suse.com>2023-12-09 01:17:22 +0100
commitfbf211459af2122ad132a141ac3bb64e14468df9 (patch)
tree550a3a567b697342063f66361ef1f4006a0d58c0
parentdeffef9cec2b0d8abefed46f593ad0ae908f4bc7 (diff)
downloadbtrfs-progs-fbf211459af2122ad132a141ac3bb64e14468df9.tar.gz
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 <dsterba@suse.com>
-rw-r--r--Documentation/btrfs-scrub.rst23
-rw-r--r--cmds/scrub.c114
2 files changed, 137 insertions, 0 deletions
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 <path>|<device>
The progress is saved in the status file so :command:`btrfs scrub resume` can
continue from the last position.
+limit [options] <path>
+ 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] <path>|<device>
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 <stdarg.h>
#include <limits.h>
#include <dirent.h>
+#include <getopt.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
@@ -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] <path>",
+ "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
}
};