forked from Mirrors/btrfs-progs
Btrfs-progs: add command to get/reset device stats via ioctl
"btrfs device stats" is used to retrieve and print the device stats. "btrfs device stats -z" is used to atomically retrieve, reset and print the stats. Signed-off-by: Stefan Behrens <sbehrens@giantdisaster.de>master
parent
7a69dc4eec
commit
5b8826ddfd
117
cmds-device.c
117
cmds-device.c
|
@ -284,12 +284,129 @@ static int cmd_ready_dev(int argc, char **argv)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char * const cmd_dev_stats_usage[] = {
|
||||||
|
"btrfs device stats [-z] <path>|<device>",
|
||||||
|
"Show current device IO stats. -z to reset stats afterwards.",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cmd_dev_stats(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
struct btrfs_ioctl_fs_info_args fi_args;
|
||||||
|
struct btrfs_ioctl_dev_info_args *di_args = NULL;
|
||||||
|
int ret;
|
||||||
|
int fdmnt;
|
||||||
|
int i;
|
||||||
|
char c;
|
||||||
|
int fdres = -1;
|
||||||
|
int err = 0;
|
||||||
|
__u64 flags = 0;
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = getopt(argc, argv, "z")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'z':
|
||||||
|
flags = BTRFS_DEV_STATS_RESET;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "ERROR: device stat args invalid.\n"
|
||||||
|
" device stat [-z] <path>|<device>\n"
|
||||||
|
" -z to reset stats after reading.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind + 1 != argc) {
|
||||||
|
fprintf(stderr, "ERROR: device stat needs path|device as single"
|
||||||
|
" argument\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = argv[optind];
|
||||||
|
|
||||||
|
fdmnt = open_file_or_dir(path);
|
||||||
|
if (fdmnt < 0) {
|
||||||
|
fprintf(stderr, "ERROR: can't access '%s'\n", path);
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "ERROR: getting dev info for devstats failed: "
|
||||||
|
"%s\n", strerror(-ret));
|
||||||
|
err = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!fi_args.num_devices) {
|
||||||
|
fprintf(stderr, "ERROR: no devices found\n");
|
||||||
|
err = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < fi_args.num_devices; i++) {
|
||||||
|
struct btrfs_ioctl_get_dev_stats args = {0};
|
||||||
|
__u8 path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
|
||||||
|
|
||||||
|
strncpy((char *)path, (char *)di_args[i].path,
|
||||||
|
BTRFS_DEVICE_PATH_NAME_MAX);
|
||||||
|
path[BTRFS_DEVICE_PATH_NAME_MAX] = '\0';
|
||||||
|
|
||||||
|
args.devid = di_args[i].devid;
|
||||||
|
args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
|
||||||
|
args.flags = flags;
|
||||||
|
|
||||||
|
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: ioctl(BTRFS_IOC_GET_DEV_STATS) on %s failed: %s\n",
|
||||||
|
path, strerror(errno));
|
||||||
|
err = 1;
|
||||||
|
} else {
|
||||||
|
if (args.nr_items >= BTRFS_DEV_STAT_WRITE_ERRS + 1)
|
||||||
|
printf("[%s].write_io_errs %llu\n",
|
||||||
|
path,
|
||||||
|
(unsigned long long) args.values[
|
||||||
|
BTRFS_DEV_STAT_WRITE_ERRS]);
|
||||||
|
if (args.nr_items >= BTRFS_DEV_STAT_READ_ERRS + 1)
|
||||||
|
printf("[%s].read_io_errs %llu\n",
|
||||||
|
path,
|
||||||
|
(unsigned long long) args.values[
|
||||||
|
BTRFS_DEV_STAT_READ_ERRS]);
|
||||||
|
if (args.nr_items >= BTRFS_DEV_STAT_FLUSH_ERRS + 1)
|
||||||
|
printf("[%s].flush_io_errs %llu\n",
|
||||||
|
path,
|
||||||
|
(unsigned long long) args.values[
|
||||||
|
BTRFS_DEV_STAT_FLUSH_ERRS]);
|
||||||
|
if (args.nr_items >= BTRFS_DEV_STAT_CORRUPTION_ERRS + 1)
|
||||||
|
printf("[%s].corruption_errs %llu\n",
|
||||||
|
path,
|
||||||
|
(unsigned long long) args.values[
|
||||||
|
BTRFS_DEV_STAT_CORRUPTION_ERRS]);
|
||||||
|
if (args.nr_items >= BTRFS_DEV_STAT_GENERATION_ERRS + 1)
|
||||||
|
printf("[%s].generation_errs %llu\n",
|
||||||
|
path,
|
||||||
|
(unsigned long long) args.values[
|
||||||
|
BTRFS_DEV_STAT_GENERATION_ERRS]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(di_args);
|
||||||
|
close(fdmnt);
|
||||||
|
if (fdres > -1)
|
||||||
|
close(fdres);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
const struct cmd_group device_cmd_group = {
|
const struct cmd_group device_cmd_group = {
|
||||||
device_cmd_group_usage, NULL, {
|
device_cmd_group_usage, NULL, {
|
||||||
{ "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
|
{ "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
|
||||||
{ "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
|
{ "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
|
||||||
{ "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 },
|
{ "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 },
|
||||||
{ "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 },
|
{ "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 },
|
||||||
|
{ "stats", cmd_dev_stats, cmd_dev_stats_usage, NULL, 0 },
|
||||||
{ 0, 0, 0, 0, 0 }
|
{ 0, 0, 0, 0, 0 }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
6
ctree.h
6
ctree.h
|
@ -1044,6 +1044,12 @@ struct btrfs_root {
|
||||||
#define BTRFS_QGROUP_LIMIT_KEY 244
|
#define BTRFS_QGROUP_LIMIT_KEY 244
|
||||||
#define BTRFS_QGROUP_RELATION_KEY 246
|
#define BTRFS_QGROUP_RELATION_KEY 246
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Persistently stores the io stats in the device tree.
|
||||||
|
* One key for all stats, (0, BTRFS_DEV_STATS_KEY, devid).
|
||||||
|
*/
|
||||||
|
#define BTRFS_DEV_STATS_KEY 249
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* string items are for debugging. They just store a short string of
|
* string items are for debugging. They just store a short string of
|
||||||
* data in the FS
|
* data in the FS
|
||||||
|
|
37
ioctl.h
37
ioctl.h
|
@ -349,6 +349,39 @@ struct btrfs_ioctl_qgroup_create_args {
|
||||||
__u64 qgroupid;
|
__u64 qgroupid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum btrfs_dev_stat_values {
|
||||||
|
/* disk I/O failure stats */
|
||||||
|
BTRFS_DEV_STAT_WRITE_ERRS, /* EIO or EREMOTEIO from lower layers */
|
||||||
|
BTRFS_DEV_STAT_READ_ERRS, /* EIO or EREMOTEIO from lower layers */
|
||||||
|
BTRFS_DEV_STAT_FLUSH_ERRS, /* EIO or EREMOTEIO from lower layers */
|
||||||
|
|
||||||
|
/* stats for indirect indications for I/O failures */
|
||||||
|
BTRFS_DEV_STAT_CORRUPTION_ERRS, /* checksum error, bytenr error or
|
||||||
|
* contents is illegal: this is an
|
||||||
|
* indication that the block was damaged
|
||||||
|
* during read or write, or written to
|
||||||
|
* wrong location or read from wrong
|
||||||
|
* location */
|
||||||
|
BTRFS_DEV_STAT_GENERATION_ERRS, /* an indication that blocks have not
|
||||||
|
* been written */
|
||||||
|
|
||||||
|
BTRFS_DEV_STAT_VALUES_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Reset statistics after reading; needs SYS_ADMIN capability */
|
||||||
|
#define BTRFS_DEV_STATS_RESET (1ULL << 0)
|
||||||
|
|
||||||
|
struct btrfs_ioctl_get_dev_stats {
|
||||||
|
__u64 devid; /* in */
|
||||||
|
__u64 nr_items; /* in/out */
|
||||||
|
__u64 flags; /* in/out */
|
||||||
|
|
||||||
|
/* out values: */
|
||||||
|
__u64 values[BTRFS_DEV_STAT_VALUES_MAX];
|
||||||
|
|
||||||
|
__u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */
|
||||||
|
};
|
||||||
|
|
||||||
/* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */
|
/* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */
|
||||||
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
|
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
|
||||||
struct btrfs_ioctl_vol_args)
|
struct btrfs_ioctl_vol_args)
|
||||||
|
@ -421,7 +454,6 @@ struct btrfs_ioctl_clone_range_args {
|
||||||
struct btrfs_ioctl_ino_path_args)
|
struct btrfs_ioctl_ino_path_args)
|
||||||
#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
|
#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
|
||||||
struct btrfs_ioctl_vol_args)
|
struct btrfs_ioctl_vol_args)
|
||||||
|
|
||||||
#define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
|
#define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
|
||||||
struct btrfs_ioctl_received_subvol_args)
|
struct btrfs_ioctl_received_subvol_args)
|
||||||
#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
|
#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
|
||||||
|
@ -434,4 +466,7 @@ struct btrfs_ioctl_clone_range_args {
|
||||||
struct btrfs_ioctl_qgroup_create_args)
|
struct btrfs_ioctl_qgroup_create_args)
|
||||||
#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
|
#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
|
||||||
struct btrfs_ioctl_qgroup_limit_args)
|
struct btrfs_ioctl_qgroup_limit_args)
|
||||||
|
#define BTRFS_IOC_GET_DEV_STATS _IOWR(BTRFS_IOCTL_MAGIC, 52, \
|
||||||
|
struct btrfs_ioctl_get_dev_stats)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -33,6 +33,8 @@ btrfs \- control a btrfs filesystem
|
||||||
.PP
|
.PP
|
||||||
\fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices|<device> [<device>...]]\fP
|
\fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices|<device> [<device>...]]\fP
|
||||||
.PP
|
.PP
|
||||||
|
\fBbtrfs\fP \fBdevice stats\fP [-z] {\fI<path>\fP|\fI<device>\fP}
|
||||||
|
.PP
|
||||||
\fBbtrfs\fP \fBdevice add\fP\fI <device> [<device>...] <path> \fP
|
\fBbtrfs\fP \fBdevice add\fP\fI <device> [<device>...] <path> \fP
|
||||||
.PP
|
.PP
|
||||||
\fBbtrfs\fP \fBdevice delete\fP\fI <device> [<device>...] <path> \fP
|
\fBbtrfs\fP \fBdevice delete\fP\fI <device> [<device>...] <path> \fP
|
||||||
|
@ -252,6 +254,18 @@ Balance the chunks of the filesystem identified by \fI<path>\fR
|
||||||
across the devices.
|
across the devices.
|
||||||
.TP
|
.TP
|
||||||
|
|
||||||
|
\fBdevice stats\fP [-z] {\fI<path>\fP|\fI<device>\fP}
|
||||||
|
Read and print the device IO stats for all devices of the filesystem
|
||||||
|
identified by \fI<path>\fR or for a single \fI<device>\fR.
|
||||||
|
|
||||||
|
.RS
|
||||||
|
\fIOptions\fR
|
||||||
|
.TP
|
||||||
|
.B -z
|
||||||
|
Reset stats to zero after reading them.
|
||||||
|
.RE
|
||||||
|
.TP
|
||||||
|
|
||||||
\fBdevice add\fR\fI <dev> [<dev>..] <path>\fR
|
\fBdevice add\fR\fI <dev> [<dev>..] <path>\fR
|
||||||
Add device(s) to the filesystem identified by \fI<path>\fR.
|
Add device(s) to the filesystem identified by \fI<path>\fR.
|
||||||
.TP
|
.TP
|
||||||
|
|
|
@ -455,6 +455,9 @@ static void print_key_type(u64 objectid, u8 type)
|
||||||
case BTRFS_QGROUP_LIMIT_KEY:
|
case BTRFS_QGROUP_LIMIT_KEY:
|
||||||
printf("BTRFS_QGROUP_LIMIT_KEY");
|
printf("BTRFS_QGROUP_LIMIT_KEY");
|
||||||
break;
|
break;
|
||||||
|
case BTRFS_DEV_STATS_KEY:
|
||||||
|
printf("DEV_STATS_ITEM");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
printf("UNKNOWN.%d", type);
|
printf("UNKNOWN.%d", type);
|
||||||
};
|
};
|
||||||
|
@ -777,6 +780,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
||||||
str = l->data + btrfs_item_ptr_offset(l, i);
|
str = l->data + btrfs_item_ptr_offset(l, i);
|
||||||
printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str);
|
printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str);
|
||||||
break;
|
break;
|
||||||
|
case BTRFS_DEV_STATS_KEY:
|
||||||
|
printf("\t\tdevice stats\n");
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue