Skip to content

Commit cfd3d77

Browse files
committed
btrfs-progs: device stats: add support for offline mode
Add new option --offline that reads device stats from the device (or a file image) directly, ie. not using the GET_DEV_STATS ioctl. For that the argument must be a block device or a file. $ btrfs device stats /tmp/img1 ERROR: not a directory: /tmp/img1 ERROR: to read device stats from a file image use --offline option The output is the same as for online (mounted) case, with the exception of missing devices that may not show full path in the output. Then the device name is "devid:N" where N is the device id: The following output is from 4 file backed images img[1234] that are added to a filesystem as loop devices and then removed. The files exist but the device scan cannot see them: $ btrfs device stats --offline /tmp/img1 [/tmp/img1].write_io_errs 0 [/tmp/img1].read_io_errs 0 [/tmp/img1].flush_io_errs 0 [/tmp/img1].corruption_errs 0 [/tmp/img1].generation_errs 0 [devid:2].write_io_errs 0 [devid:2].read_io_errs 0 [devid:2].flush_io_errs 0 [devid:2].corruption_errs 0 [devid:2].generation_errs 0 [devid:3].write_io_errs 0 [devid:3].read_io_errs 0 [devid:3].flush_io_errs 0 [devid:3].corruption_errs 0 [devid:3].generation_errs 0 [devid:4].write_io_errs 0 [devid:4].read_io_errs 0 [devid:4].flush_io_errs 0 [devid:4].corruption_errs 0 [devid:4].generation_errs 0 Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 395d7bf commit cfd3d77

File tree

2 files changed

+193
-22
lines changed

2 files changed

+193
-22
lines changed

Documentation/btrfs-device.rst

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,36 @@ scan [options] [<device> [<device>...]]
124124
Unregister a given device or all stale devices if no path is given, the device
125125
must be unmounted otherwise it's an error.
126126

127-
stats [options] <path>|<device>
127+
stats [options] <path>|<device>|<file>
128128
Read and print the device IO error statistics for all devices of the given
129-
filesystem identified by *path* or for a single *device*. The filesystem must
130-
be mounted. See section :ref:`DEVICE STATS<man-device-device-stats>`
131-
for more information about the reported statistics and the meaning.
129+
filesystem identified by *path* or for a single *device* or *file* (the
130+
offline mode, unmounted filesystem).
131+
132+
When the filesystem is mounted the *path* is expected to be a directory
133+
and the stats are read using an ioctl and can be reset or checked. Same
134+
for a block *device* of a mounted filesystem.
135+
136+
For an offline mode the path must be a file image or a block device,
137+
the option *--offline* is mandatory. As it's read-only access the
138+
option *--reset* does not work.
139+
140+
See section :ref:`DEVICE STATS<man-device-device-stats>` for more
141+
information about the reported statistics and the meaning.
132142

133143
``Options``
134144

135145
-z|--reset
136-
Print the stats and reset the values to zero afterwards.
146+
Print the stats and reset the values to zero afterwards. Does
147+
not work in offline mode.
137148

138149
-c|--check
139150
Check if the stats are all zeros and return 0 if it is so. Set bit 6 of the
140151
return code if any of the statistics is no-zero. The error values is 65 if
141152
reading stats from at least one device failed, otherwise it's 64.
142153

154+
--offline
155+
Read the stats from the file image or directly from block device.
156+
143157
-T
144158
Print stats in a tabular form, devices as rows and stats as columns
145159

cmds/device.c

Lines changed: 174 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <getopt.h>
2626
#include <dirent.h>
2727
#include <stdbool.h>
28+
#include "kernel-lib/list_sort.h"
2829
#include "kernel-shared/zoned.h"
2930
#include "common/string-table.h"
3031
#include "common/utils.h"
@@ -585,10 +586,13 @@ static const char * const cmd_device_stats_usage[] = {
585586
"btrfs device stats [options] <path>|<device>",
586587
"Show device IO error statistics",
587588
"Show device IO error statistics for all devices of the given filesystem",
588-
"identified by PATH or DEVICE. The filesystem must be mounted.",
589+
"identified by PATH or DEVICE. On a mounted filesystem the stats are read",
590+
"using ioctls, for direct read from file images or block devices use the",
591+
"option --offline.",
589592
"",
590593
OPTLINE("-c|--check", "return non-zero if any stat counter is not zero"),
591594
OPTLINE("-z|--reset", "show current stats and reset values to zero"),
595+
OPTLINE("--offline", "read stats from file or device directly (not from mounted filesystem)"),
592596
OPTLINE("-T", "show current stats in tabular format"),
593597
HELPINFO_INSERT_GLOBALS,
594598
HELPINFO_INSERT_FORMAT,
@@ -701,28 +705,132 @@ static int print_device_stat_tabular(struct string_table *table, int row,
701705
return err;
702706
}
703707

708+
/*
709+
* Offline version of GET_DEV_STATS ioctl.
710+
*/
711+
static int get_device_stats_offline(struct btrfs_fs_info *fs_info, u64 devid,
712+
struct btrfs_ioctl_get_dev_stats *stats_args)
713+
{
714+
int ret = 0;
715+
struct btrfs_path path = { 0 };
716+
struct btrfs_key key;
717+
struct btrfs_dev_stats_item *stats_item;
718+
719+
key.objectid = BTRFS_DEV_STATS_OBJECTID;
720+
key.type = BTRFS_PERSISTENT_ITEM_KEY;
721+
key.offset = devid;
722+
ret = btrfs_search_slot(NULL, fs_info->dev_root, &key, &path, 0, 0);
723+
if (ret < 0)
724+
goto out;
725+
if (ret > 0) {
726+
pr_verbose(LOG_DEBUG, "no device stats found for devid %llu\n", devid);
727+
ret = 0;
728+
goto out;
729+
}
730+
731+
memset(stats_args->values, 0, stats_args->nr_items * sizeof(u64));
732+
stats_item = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dev_stats_item);
733+
734+
stats_args->values[BTRFS_DEV_STAT_WRITE_ERRS] =
735+
btrfs_dev_stats_value(path.nodes[0], stats_item, BTRFS_DEV_STAT_WRITE_ERRS);
736+
stats_args->values[BTRFS_DEV_STAT_READ_ERRS] =
737+
btrfs_dev_stats_value(path.nodes[0], stats_item, BTRFS_DEV_STAT_READ_ERRS);
738+
stats_args->values[BTRFS_DEV_STAT_FLUSH_ERRS] =
739+
btrfs_dev_stats_value(path.nodes[0], stats_item, BTRFS_DEV_STAT_FLUSH_ERRS);
740+
stats_args->values[BTRFS_DEV_STAT_CORRUPTION_ERRS] =
741+
btrfs_dev_stats_value(path.nodes[0], stats_item, BTRFS_DEV_STAT_CORRUPTION_ERRS);
742+
stats_args->values[BTRFS_DEV_STAT_GENERATION_ERRS] =
743+
btrfs_dev_stats_value(path.nodes[0], stats_item, BTRFS_DEV_STAT_GENERATION_ERRS);
744+
745+
out:
746+
btrfs_release_path(&path);
747+
return ret;
748+
}
749+
750+
/*
751+
* Offline version of get_fs_info() with limitations:
752+
*
753+
* - no seeding device support
754+
* - fi_args filled only partially (num_devices)
755+
*/
756+
static int get_fs_info_offline(struct btrfs_fs_info *fs_info,
757+
struct btrfs_ioctl_fs_info_args *fi_args,
758+
struct btrfs_ioctl_dev_info_args **di_ret)
759+
{
760+
int i;
761+
struct btrfs_ioctl_dev_info_args *di_args;
762+
struct btrfs_fs_devices *cur_fs;
763+
struct btrfs_device *device;
764+
struct list_head *fs_uuids;
765+
766+
fs_uuids = btrfs_scanned_uuids();
767+
/* Count devices from fs_devices in case some of them are missing */
768+
fi_args->num_devices = 0;
769+
list_for_each_entry(cur_fs, fs_uuids, fs_list) {
770+
if (memcmp(fs_info->fs_devices->fsid, cur_fs->fsid, BTRFS_FSID_SIZE) != 0)
771+
continue;
772+
list_for_each_entry(device, &cur_fs->devices, dev_list) {
773+
fi_args->num_devices++;
774+
}
775+
}
776+
777+
di_args = malloc(fi_args->num_devices * sizeof(*di_args));
778+
if (!di_args)
779+
return -ENOMEM;
780+
781+
i = 0;
782+
list_sort(NULL, &fs_info->fs_devices->devices, cmp_device_id);
783+
list_for_each_entry(cur_fs, fs_uuids, fs_list) {
784+
if (memcmp(fs_info->fs_devices->fsid, cur_fs->fsid, BTRFS_FSID_SIZE) != 0)
785+
continue;
786+
787+
list_for_each_entry(device, &cur_fs->devices, dev_list) {
788+
di_args[i].devid = device->devid;
789+
memcpy(di_args[i].uuid, device->uuid, BTRFS_FSID_SIZE);
790+
di_args[i].bytes_used = device->bytes_used;
791+
di_args[i].total_bytes = device->total_bytes;
792+
memcpy(di_args[i].fsid, cur_fs->fsid, BTRFS_FSID_SIZE);
793+
/*
794+
* File devices without loop device or a missing device
795+
* don't have a path.
796+
*/
797+
if (device->name)
798+
strncpy_null((char *)di_args[i].path, device->name,
799+
BTRFS_DEVICE_PATH_NAME_MAX + 1);
800+
i++;
801+
}
802+
}
803+
804+
*di_ret = di_args;
805+
return 0;
806+
}
807+
704808
static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
705809
{
706810
char *dev_path;
707811
struct btrfs_ioctl_fs_info_args fi_args;
708812
struct btrfs_ioctl_dev_info_args *di_args = NULL;
813+
struct btrfs_fs_info *fs_info = NULL;
709814
struct string_table *table = NULL;
710815
int ret;
711-
int fdmnt;
816+
int fdmnt = -1;
712817
int i;
713818
int err = 0;
714819
bool check = false;
715820
bool free_table = false;
716821
bool tabular = false;
822+
bool opt_offline = false;
717823
__u64 flags = 0;
718824
struct format_ctx fctx;
719825

720826
optind = 0;
721827
while (1) {
722828
int c;
829+
enum { GETOPT_VAL_OFFLINE = GETOPT_VAL_FIRST };
723830
static const struct option long_options[] = {
724831
{"check", no_argument, NULL, 'c'},
725832
{"reset", no_argument, NULL, 'z'},
833+
{"offline", no_argument, NULL, GETOPT_VAL_OFFLINE},
726834
{NULL, 0, NULL, 0}
727835
};
728836

@@ -740,6 +848,9 @@ static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
740848
case 'T':
741849
tabular = true;
742850
break;
851+
case GETOPT_VAL_OFFLINE:
852+
opt_offline = true;
853+
break;
743854
default:
744855
usage_unknown_option(cmd, argv);
745856
}
@@ -748,19 +859,48 @@ static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
748859
if (check_argc_exact(argc - optind, 1))
749860
return 1;
750861

862+
if (opt_offline && (flags & BTRFS_DEV_STATS_RESET)) {
863+
error("--offline and --reset cannot be used together");
864+
return 1;
865+
}
866+
751867
dev_path = argv[optind];
752868

753-
fdmnt = btrfs_open_mnt(dev_path);
754-
if (fdmnt < 0)
755-
return 1;
869+
if (!opt_offline) {
870+
fdmnt = btrfs_open_mnt(dev_path);
871+
if (fdmnt < 0) {
872+
if (fdmnt == -ENOTDIR && !opt_offline)
873+
error("to read device stats from a file image use --offline option");
874+
return 1;
875+
}
756876

757-
ret = get_fs_info(dev_path, &fi_args, &di_args);
758-
if (ret) {
759-
errno = -ret;
760-
error("getting device info for %s failed: %m", dev_path);
761-
err = 1;
762-
goto out;
877+
ret = get_fs_info(dev_path, &fi_args, &di_args);
878+
if (ret) {
879+
errno = -ret;
880+
error("getting device info for %s failed: %m", dev_path);
881+
err = 1;
882+
goto out;
883+
}
884+
} else {
885+
struct open_ctree_args oca = { 0 };
886+
887+
oca.filename = dev_path;
888+
fs_info = open_ctree_fs_info(&oca);
889+
if (!fs_info) {
890+
error("cannot open filesystem on %s", dev_path);
891+
err = 1;
892+
goto out;
893+
}
894+
895+
ret = get_fs_info_offline(fs_info, &fi_args, &di_args);
896+
if (ret < 0) {
897+
errno = -ret;
898+
error("getting device info for %s failed: %m", dev_path);
899+
err = 1;
900+
goto out;
901+
}
763902
}
903+
764904
if (!fi_args.num_devices) {
765905
error("no devices found");
766906
err = 1;
@@ -802,13 +942,28 @@ static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
802942

803943
args.devid = di_args[i].devid;
804944
args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
805-
args.flags = flags;
945+
if (!opt_offline) {
946+
args.flags = flags;
806947

807-
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
808-
error("device stats ioctl failed on %s: %m",
809-
path);
810-
err |= 1;
811-
goto out;
948+
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
949+
error("device stats ioctl failed on %s: %m",
950+
path);
951+
err |= 1;
952+
goto out;
953+
}
954+
} else {
955+
if (args.flags != 0) {
956+
error("no flags supported");
957+
err = -EINVAL;
958+
goto out;
959+
}
960+
ret = get_device_stats_offline(fs_info, di_args[i].devid, &args);
961+
if (ret < 0) {
962+
error("cannot read offline stats for devid %llu",
963+
di_args[i].devid);
964+
err = ret;
965+
goto out;
966+
}
812967
}
813968

814969
if (tabular)
@@ -836,6 +991,8 @@ static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
836991
out:
837992
free(di_args);
838993
close(fdmnt);
994+
if (fs_info)
995+
close_ctree_fs_info(fs_info);
839996
if (free_table)
840997
table_free(table);
841998

0 commit comments

Comments
 (0)