summaryrefslogtreecommitdiff
path: root/c_src/cmd_data.c
diff options
context:
space:
mode:
Diffstat (limited to 'c_src/cmd_data.c')
-rw-r--r--c_src/cmd_data.c203
1 files changed, 202 insertions, 1 deletions
diff --git a/c_src/cmd_data.c b/c_src/cmd_data.c
index 1ef689bc..9dd2c15c 100644
--- a/c_src/cmd_data.c
+++ b/c_src/cmd_data.c
@@ -1,5 +1,5 @@
-
+#include <getopt.h>
#include <stdio.h>
#include <sys/ioctl.h>
@@ -64,6 +64,207 @@ int cmd_data_rereplicate(int argc, char *argv[])
});
}
+static void data_scrub_usage(void)
+{
+ puts("bcachefs data scrub\n"
+ "Usage: bcachefs data scrub [filesystem|device]\n"
+ "\n"
+ "Check data for errors, fix from another replica if possible\n"
+ "\n"
+ "Options:\n"
+ " -m, --metadata check metadata only\n"
+ " -h, --help display this help and exit\n"
+ "Report bugs to <linux-bcachefs@vger.kernel.org>");
+ exit(EXIT_SUCCESS);
+}
+
+int cmd_data_scrub(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "metadata", no_argument, NULL, 'm' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+ struct bch_ioctl_data cmd = {
+ .op = BCH_DATA_OP_scrub,
+ .scrub.data_types = ~0,
+ };
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "hm", longopts, NULL)) != -1)
+ switch (opt) {
+ case 'm':
+ cmd.scrub.data_types = BIT(BCH_DATA_btree);
+ break;
+ case 'h':
+ data_scrub_usage();
+ break;
+ }
+ args_shift(optind);
+
+ char *path = arg_pop();
+ if (!path)
+ die("Please supply a filesystem");
+
+ if (argc)
+ die("too many arguments");
+
+ printf("Starting scrub on");
+
+ struct bchfs_handle fs = bcache_fs_open(path);
+ dev_names dev_names = bchu_fs_get_devices(fs);
+
+ struct scrub_device {
+ const char *name;
+ int progress_fd;
+ u64 done, corrected, uncorrected, total;
+ enum bch_ioctl_data_event_ret ret;
+ };
+ DARRAY(struct scrub_device) scrub_devs = {};
+
+ if (fs.dev_idx >= 0) {
+ cmd.scrub.dev = fs.dev_idx;
+ struct scrub_device d = {
+ .name = dev_idx_to_name(&dev_names, fs.dev_idx)->dev,
+ .progress_fd = xioctl(fs.ioctl_fd, BCH_IOCTL_DATA, &cmd),
+ };
+ darray_push(&scrub_devs, d);
+ } else {
+ /* Scrubbing every device */
+ darray_for_each(dev_names, dev) {
+ cmd.scrub.dev = dev->idx;
+ struct scrub_device d = {
+ .name = dev->dev,
+ .progress_fd = xioctl(fs.ioctl_fd, BCH_IOCTL_DATA, &cmd),
+ };
+ darray_push(&scrub_devs, d);
+ }
+ }
+
+ printf(" %zu devices: ", scrub_devs.nr);
+ darray_for_each(scrub_devs, dev)
+ printf(" %s", dev->name);
+ printf("\n");
+
+ struct timespec now, last;
+ bool first = true;
+
+ struct printbuf buf = PRINTBUF;
+ printbuf_tabstop_push(&buf, 16);
+ printbuf_tabstop_push(&buf, 12);
+ printbuf_tabstop_push(&buf, 12);
+ printbuf_tabstop_push(&buf, 12);
+ printbuf_tabstop_push(&buf, 12);
+ printbuf_tabstop_push(&buf, 6);
+
+ prt_printf(&buf, "device\t");
+ prt_printf(&buf, "checked\r");
+ prt_printf(&buf, "corrected\r");
+ prt_printf(&buf, "uncorrected\r");
+ prt_printf(&buf, "total\r");
+ puts(buf.buf);
+
+ while (1) {
+ bool done = true;
+
+ printbuf_reset_keep_tabstops(&buf);
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ u64 ns_since_last = 0;
+ if (!first)
+ ns_since_last = (now.tv_sec - last.tv_sec) * NSEC_PER_SEC +
+ now.tv_nsec - last.tv_nsec;
+
+ darray_for_each(scrub_devs, dev) {
+ struct bch_ioctl_data_event e;
+
+ if (dev->progress_fd >= 0 &&
+ read(dev->progress_fd, &e, sizeof(e)) != sizeof(e)) {
+ close(dev->progress_fd);
+ dev->progress_fd = -1;
+ }
+
+ u64 rate = 0;
+
+ if (dev->progress_fd >= 0) {
+ if (ns_since_last)
+ rate = ((e.p.sectors_done - dev->done) << 9)
+ * NSEC_PER_SEC
+ / ns_since_last;
+
+ dev->done = e.p.sectors_done;
+ dev->corrected = e.p.sectors_error_corrected;
+ dev->uncorrected= e.p.sectors_error_uncorrected;
+ dev->total = e.p.sectors_total;
+ }
+
+ if (dev->progress_fd >= 0 && e.ret) {
+ close(dev->progress_fd);
+ dev->progress_fd = -1;
+ dev->ret = e.ret;
+ }
+
+ if (dev->progress_fd >= 0)
+ done = false;
+
+ prt_printf(&buf, "%s\t", dev->name ?: "(offline)");
+
+ prt_human_readable_u64(&buf, dev->done << 9);
+ prt_tab_rjust(&buf);
+
+ prt_human_readable_u64(&buf, dev->corrected << 9);
+ prt_tab_rjust(&buf);
+
+ prt_human_readable_u64(&buf, dev->uncorrected << 9);
+ prt_tab_rjust(&buf);
+
+ prt_human_readable_u64(&buf, dev->total << 9);
+ prt_tab_rjust(&buf);
+
+ prt_printf(&buf, "%llu%%",
+ dev->total
+ ? dev->done * 100 / dev->total
+ : 0);
+ prt_tab_rjust(&buf);
+
+ prt_str(&buf, " ");
+
+ if (dev->progress_fd >= 0) {
+ prt_human_readable_u64(&buf, rate);
+ prt_str(&buf, "/sec");
+ } else if (dev->ret == BCH_IOCTL_DATA_EVENT_RET_device_offline) {
+ prt_str(&buf, "offline");
+ } else {
+ prt_str(&buf, "complete");
+ }
+
+ if (dev != &darray_last(scrub_devs))
+ prt_newline(&buf);
+ }
+
+ fputs(buf.buf, stdout);
+ fflush(stdout);
+
+ if (done)
+ break;
+
+ last = now;
+ first = false;
+ sleep(1);
+
+ for (unsigned i = 0; i < scrub_devs.nr; i++) {
+ if (i)
+ printf("\033[1A");
+ printf("\33[2K\r");
+ }
+ }
+
+ fputs("\n", stdout);
+ printbuf_exit(&buf);
+
+ return 0;
+}
+
static void data_job_usage(void)
{
puts("bcachefs data job\n"