Btrfs-progs: add command to get/reset device stats via ioctl
authorStefan Behrens <sbehrens@giantdisaster.de>
Tue, 15 May 2012 09:30:39 +0000 (11:30 +0200)
committerDavid Sterba <dsterba@suse.cz>
Thu, 31 Jan 2013 12:47:17 +0000 (13:47 +0100)
"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>
cmds-device.c
ctree.h
ioctl.h
man/btrfs.8.in
print-tree.c

index 75ee293..d4938f4 100644 (file)
@@ -284,12 +284,129 @@ static int cmd_ready_dev(int argc, char **argv)
        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 = {
        device_cmd_group_usage, NULL, {
                { "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
                { "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
                { "scan", cmd_scan_dev, cmd_scan_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 }
        }
 };
diff --git a/ctree.h b/ctree.h
index 1f5a795..199f028 100644 (file)
--- a/ctree.h
+++ b/ctree.h
@@ -1045,6 +1045,12 @@ struct btrfs_root {
 #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
  * data in the FS
  */
diff --git a/ioctl.h b/ioctl.h
index d32e22c..6fefb57 100644 (file)
--- a/ioctl.h
+++ b/ioctl.h
@@ -349,6 +349,39 @@ struct btrfs_ioctl_qgroup_create_args {
        __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 */
 #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
                                   struct btrfs_ioctl_vol_args)
@@ -421,7 +454,6 @@ struct btrfs_ioctl_clone_range_args {
                                        struct btrfs_ioctl_ino_path_args)
 #define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
                                     struct btrfs_ioctl_vol_args)
-
 #define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
                                struct btrfs_ioctl_received_subvol_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)
 #define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
                                        struct btrfs_ioctl_qgroup_limit_args)
+#define BTRFS_IOC_GET_DEV_STATS _IOWR(BTRFS_IOCTL_MAGIC, 52, \
+                                     struct btrfs_ioctl_get_dev_stats)
+
 #endif
index d20e332..460592e 100644 (file)
@@ -33,6 +33,8 @@ btrfs \- control a btrfs filesystem
 .PP
 \fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices|<device> [<device>...]]\fP
 .PP
+\fBbtrfs\fP \fBdevice stats\fP [-z] {\fI<path>\fP|\fI<device>\fP}
+.PP
 \fBbtrfs\fP \fBdevice add\fP\fI <device> [<device>...] <path> \fP
 .PP
 \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.
 .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
 Add device(s) to the filesystem identified by \fI<path>\fR.
 .TP
index 7c615dd..828806d 100644 (file)
@@ -455,6 +455,9 @@ static void print_key_type(u64 objectid, u8 type)
        case BTRFS_QGROUP_LIMIT_KEY:
                printf("BTRFS_QGROUP_LIMIT_KEY");
                break;
+       case BTRFS_DEV_STATS_KEY:
+               printf("DEV_STATS_ITEM");
+               break;
        default:
                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);
                        printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str);
                        break;
+               case BTRFS_DEV_STATS_KEY:
+                       printf("\t\tdevice stats\n");
+                       break;
                };
                fflush(stdout);
        }