summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSt├ęphane Lesimple <stephane_btrfs@lesimple.fr>2018-07-04 21:20:14 +0200
committerDavid Sterba <dsterba@suse.com>2018-08-06 15:03:23 +0200
commit078e9a1cc973e042ad8fde2d84e0db62fee9c45a (patch)
treea7eb3c003d64350e03076959a00c499843f6209d
parent48a2818159792bdd1fcb98aab6203a1080d4c310 (diff)
btrfs-progs: check: enhanced progress indicator
We reuse the task_position enum and task_ctx struct of the original progress indicator, adding more values and fields for our needs. Then add hooks in all steps of the check to properly record progress. Here's how the output looks like on a 22 Tb 5-disk RAID1 FS: Opening filesystem to check... Checking filesystem on /dev/mapper/luks-ST10000VN0004-XXXXXXXX UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx [1/7] checking extents (0:20:21 elapsed, 950958 items checked) [2/7] checking root items (0:01:29 elapsed, 15121 items checked) [3/7] checking free space cache (0:00:11 elapsed, 4928 items checked) [4/7] checking fs roots (0:51:31 elapsed, 600892 items checked) [5/7] checking csums (0:14:35 elapsed, 754522 items checked) [6/7] checking root refs (0:00:00 elapsed, 232 items checked) [7/7] checking quota groups skipped (not enabled on this FS) found 5286458060800 bytes used, no error found Signed-off-by: St├ęphane Lesimple <stephane_btrfs@lesimple.fr> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--check/main.c182
-rw-r--r--check/mode-common.h20
-rw-r--r--check/mode-lowmem.c1
-rw-r--r--convert/main.c2
-rw-r--r--qgroup-verify.c8
-rw-r--r--qgroup-verify.h2
-rw-r--r--task-utils.c8
-rw-r--r--task-utils.h3
8 files changed, 161 insertions, 65 deletions
diff --git a/check/main.c b/check/main.c
index ceb6d8f5..f8abbd36 100644
--- a/check/main.c
+++ b/check/main.c
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <getopt.h>
#include <uuid/uuid.h>
+#include <time.h>
#include "ctree.h"
#include "volumes.h"
#include "repair.h"
@@ -47,20 +48,6 @@
#include "check/mode-original.h"
#include "check/mode-lowmem.h"
-enum task_position {
- TASK_EXTENTS,
- TASK_FREE_SPACE,
- TASK_FS_ROOTS,
- TASK_NOTHING, /* have to be the last element */
-};
-
-struct task_ctx {
- int progress_enabled;
- enum task_position tp;
-
- struct task_info *info;
-};
-
u64 bytes_used = 0;
u64 total_csum_bytes = 0;
u64 total_btree_bytes = 0;
@@ -72,6 +59,7 @@ u64 data_bytes_referenced = 0;
LIST_HEAD(duplicate_extents);
LIST_HEAD(delete_items);
int no_holes = 0;
+static int is_free_space_tree = 0;
int init_extent_tree = 0;
int check_data_csum = 0;
struct btrfs_fs_info *global_info;
@@ -173,28 +161,55 @@ static int compare_extent_backref(struct rb_node *node1, struct rb_node *node2)
return compare_tree_backref(node1, node2);
}
+static void print_status_check_line(void *p)
+{
+ struct task_ctx *priv = p;
+ const char *task_position_string[] = {
+ "[1/7] checking root items ",
+ "[2/7] checking extents ",
+ is_free_space_tree ?
+ "[3/7] checking free space tree " :
+ "[3/7] checking free space cache ",
+ "[4/7] checking fs roots ",
+ check_data_csum ?
+ "[5/7] checking csums against data " :
+ "[5/7] checking csums (without verifying data) ",
+ "[6/7] checking root refs ",
+ "[7/7] checking quota groups ",
+ };
+ time_t elapsed;
+ int hours;
+ int minutes;
+ int seconds;
+
+ elapsed = time(NULL) - priv->start_time;
+ hours = elapsed / 3600;
+ elapsed -= hours * 3600;
+ minutes = elapsed / 60;
+ elapsed -= minutes * 60;
+ seconds = elapsed;
+
+ printf("%s (%d:%02d:%02d elapsed", task_position_string[priv->tp],
+ hours, minutes, seconds);
+ if (priv->item_count > 0)
+ printf(", %llu items checked)\r", priv->item_count);
+ else
+ printf(")\r");
+ fflush(stdout);
+}
static void *print_status_check(void *p)
{
struct task_ctx *priv = p;
- const char work_indicator[] = { '.', 'o', 'O', 'o' };
- uint32_t count = 0;
- static char *task_position_string[] = {
- "checking extents",
- "checking free space cache",
- "checking fs roots",
- };
- task_period_start(priv->info, 1000 /* 1s */);
+ /* 1 second */
+ task_period_start(priv->info, 1000);
if (priv->tp == TASK_NOTHING)
return NULL;
while (1) {
- printf("%s [%c]\r", task_position_string[priv->tp],
- work_indicator[count % 4]);
- count++;
- fflush(stdout);
+ print_status_check_line(p);
task_period_wait(priv->info);
}
return NULL;
@@ -202,6 +217,7 @@ static void *print_status_check(void *p)
static int print_status_return(void *p)
{
+ print_status_check_line(p);
printf("\n");
fflush(stdout);
@@ -2985,6 +3001,7 @@ static int check_root_refs(struct btrfs_root *root,
loop = 0;
cache = search_cache_extent(root_cache, 0);
while (1) {
+ ctx.item_count++;
if (!cache)
break;
rec = container_of(cache, struct root_record, cache);
@@ -3306,6 +3323,7 @@ static int check_fs_root(struct btrfs_root *root,
}
while (1) {
+ ctx.item_count++;
wret = walk_down_tree(root, &path, wc, &level, &nrefs);
if (wret < 0)
ret = wret;
@@ -3383,11 +3401,6 @@ static int check_fs_roots(struct btrfs_fs_info *fs_info,
int ret;
int err = 0;
- if (ctx.progress_enabled) {
- ctx.tp = TASK_FS_ROOTS;
- task_start(ctx.info);
- }
-
/*
* Just in case we made any changes to the extent tree that weren't
* reflected into the free space cache yet.
@@ -3464,8 +3477,6 @@ out:
if (!cache_tree_empty(&wc.shared))
fprintf(stderr, "warning line %d\n", __LINE__);
- task_stop(ctx.info);
-
return err;
}
@@ -3534,8 +3545,6 @@ static int do_check_fs_roots(struct btrfs_fs_info *fs_info,
{
int ret;
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking fs roots\n");
if (check_mode == CHECK_MODE_LOWMEM)
ret = check_fs_roots_lowmem(fs_info);
else
@@ -5377,12 +5386,8 @@ static int check_space_cache(struct btrfs_root *root)
return 0;
}
- if (ctx.progress_enabled) {
- ctx.tp = TASK_FREE_SPACE;
- task_start(ctx.info);
- }
-
while (1) {
+ ctx.item_count++;
cache = btrfs_lookup_first_block_group(root->fs_info, start);
if (!cache)
break;
@@ -5431,8 +5436,6 @@ static int check_space_cache(struct btrfs_root *root)
}
}
- task_stop(ctx.info);
-
return error ? -EINVAL : 0;
}
@@ -5702,6 +5705,7 @@ static int check_csums(struct btrfs_root *root)
}
while (1) {
+ ctx.item_count++;
if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
ret = btrfs_next_leaf(root, &path);
if (ret < 0) {
@@ -8094,6 +8098,7 @@ static int deal_root_from_list(struct list_head *list,
* can maximize readahead.
*/
while (1) {
+ ctx.item_count++;
ret = run_next_block(root, bits, bits_nr, &last,
pending, seen, reada, nodes,
extent_cache, chunk_cache,
@@ -8257,11 +8262,6 @@ static int check_chunks_and_extents(struct btrfs_fs_info *fs_info)
exit(1);
}
- if (ctx.progress_enabled) {
- ctx.tp = TASK_EXTENTS;
- task_start(ctx.info);
- }
-
again:
root1 = fs_info->tree_root;
level = btrfs_header_level(root1->node);
@@ -8324,7 +8324,6 @@ again:
ret = err;
out:
- task_stop(ctx.info);
if (repair) {
free_corrupt_blocks_tree(fs_info->corrupt_blocks);
extent_io_tree_cleanup(&excluded_extents);
@@ -8366,8 +8365,6 @@ static int do_check_chunks_and_extents(struct btrfs_fs_info *fs_info)
{
int ret;
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking extents\n");
if (check_mode == CHECK_MODE_LOWMEM)
ret = check_chunks_and_extents_lowmem(fs_info);
else
@@ -9097,6 +9094,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
struct cache_extent *entry;
struct root_item_info *rii;
+ ctx.item_count++;
if (slot >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(info->extent_root, &path);
if (ret < 0) {
@@ -9635,6 +9633,8 @@ int cmd_check(int argc, char **argv)
if (repair && check_mode == CHECK_MODE_LOWMEM)
warning("low-memory mode repair support is only partial");
+ printf("Opening filesystem to check...\n");
+
radix_tree_init();
cache_tree_init(&root_cache);
@@ -9808,7 +9808,14 @@ int cmd_check(int argc, char **argv)
}
if (!init_extent_tree) {
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[1/7] checking root items\n");
+ } else {
+ ctx.tp = TASK_ROOT_ITEMS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
ret = repair_root_items(info);
+ task_stop(ctx.info);
if (ret < 0) {
err = !!ret;
error("failed to repair root items: %s", strerror(-ret));
@@ -9827,9 +9834,18 @@ int cmd_check(int argc, char **argv)
err |= ret;
goto close_out;
}
+ } else {
+ fprintf(stderr, "[1/7] checking root items... skipped\n");
}
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[2/7] checking extents\n");
+ } else {
+ ctx.tp = TASK_EXTENTS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
ret = do_check_chunks_and_extents(info);
+ task_stop(ctx.info);
err |= !!ret;
if (ret)
error(
@@ -9838,16 +9854,23 @@ int cmd_check(int argc, char **argv)
/* Only re-check super size after we checked and repaired the fs */
err |= !is_super_size_valid(info);
+ is_free_space_tree = btrfs_fs_compat_ro(info, FREE_SPACE_TREE);
+
if (!ctx.progress_enabled) {
- if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
- fprintf(stderr, "checking free space tree\n");
+ if (is_free_space_tree)
+ fprintf(stderr, "[3/7] checking free space tree\n");
else
- fprintf(stderr, "checking free space cache\n");
+ fprintf(stderr, "[3/7] checking free space cache\n");
+ } else {
+ ctx.tp = TASK_FREE_SPACE;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
}
+
ret = check_space_cache(root);
+ task_stop(ctx.info);
err |= !!ret;
if (ret) {
- if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
+ if (is_free_space_tree)
error("errors found in free space tree");
else
error("errors found in free space cache");
@@ -9861,19 +9884,34 @@ int cmd_check(int argc, char **argv)
* ignore it when this happens.
*/
no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[4/7] checking fs roots\n");
+ } else {
+ ctx.tp = TASK_FS_ROOTS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
+
ret = do_check_fs_roots(info, &root_cache);
+ task_stop(ctx.info);
err |= !!ret;
if (ret) {
error("errors found in fs roots");
goto out;
}
- if (check_data_csum)
- fprintf(stderr, "checking csums against data\n");
- else
- fprintf(stderr,
- "checking only csum items (without verifying data)\n");
+ if (!ctx.progress_enabled) {
+ if (check_data_csum)
+ fprintf(stderr, "[5/7] checking csums against data\n");
+ else
+ fprintf(stderr,
+ "[5/7] checking only csums items (without verifying data)\n");
+ } else {
+ ctx.tp = TASK_CSUMS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
+
ret = check_csums(root);
+ task_stop(ctx.info);
/*
* Data csum error is not fatal, and it may indicate more serious
* corruption, continue checking.
@@ -9882,15 +9920,25 @@ int cmd_check(int argc, char **argv)
error("errors found in csum tree");
err |= !!ret;
- fprintf(stderr, "checking root refs\n");
/* For low memory mode, check_fs_roots_v2 handles root refs */
- if (check_mode != CHECK_MODE_LOWMEM) {
+ if (check_mode != CHECK_MODE_LOWMEM) {
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[6/7] checking root refs\n");
+ } else {
+ ctx.tp = TASK_ROOT_REFS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
+
ret = check_root_refs(root, &root_cache);
+ task_stop(ctx.info);
err |= !!ret;
if (ret) {
error("errors found in root refs");
goto out;
}
+ } else {
+ fprintf(stderr,
+ "[6/7] checking root refs done with fs roots in lowmem mode, skipping\n");
}
while (repair && !list_empty(&root->fs_info->recow_ebs)) {
@@ -9920,8 +9968,15 @@ int cmd_check(int argc, char **argv)
}
if (info->quota_enabled) {
- fprintf(stderr, "checking quota groups\n");
+ qgroup_set_item_count_ptr(&ctx.item_count);
+ if (!ctx.progress_enabled) {
+ fprintf(stderr, "[7/7] checking quota groups\n");
+ } else {
+ ctx.tp = TASK_QGROUPS;
+ task_start(ctx.info, &ctx.start_time, &ctx.item_count);
+ }
ret = qgroup_verify_all(info);
+ task_stop(ctx.info);
err |= !!ret;
if (ret) {
error("failed to check quota groups");
@@ -9936,6 +9991,9 @@ int cmd_check(int argc, char **argv)
if (qgroup_report_ret && (!qgroups_repaired || ret))
err |= qgroup_report_ret;
ret = 0;
+ } else {
+ fprintf(stderr,
+ "[7/7] checking quota groups skipped (not enabled on this FS)\n");
}
if (!list_empty(&root->fs_info->recow_ebs)) {
diff --git a/check/mode-common.h b/check/mode-common.h
index a4748578..6b05f8ba 100644
--- a/check/mode-common.h
+++ b/check/mode-common.h
@@ -38,6 +38,26 @@ struct node_refs {
int full_backref[BTRFS_MAX_LEVEL];
};
+enum task_position {
+ TASK_ROOT_ITEMS,
+ TASK_EXTENTS,
+ TASK_FREE_SPACE,
+ TASK_FS_ROOTS,
+ TASK_CSUMS,
+ TASK_ROOT_REFS,
+ TASK_QGROUPS,
+ TASK_NOTHING, /* has to be the last element */
+};
+
+struct task_ctx {
+ int progress_enabled;
+ enum task_position tp;
+ time_t start_time;
+ u64 item_count;
+
+ struct task_info *info;
+};
+
extern u64 bytes_used;
extern u64 total_csum_bytes;
extern u64 total_btree_bytes;
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index 224129d8..1bce44f5 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -4804,6 +4804,7 @@ static int check_btrfs_root(struct btrfs_root *root, int check_all)
}
while (1) {
+ ctx.item_count++;
ret = walk_down_tree(root, &path, &level, &nrefs, check_all);
if (ret > 0)
diff --git a/convert/main.c b/convert/main.c
index 7077fcba..3736a149 100644
--- a/convert/main.c
+++ b/convert/main.c
@@ -1182,7 +1182,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
if (progress) {
ctx.info = task_init(print_copied_inodes, after_copied_inodes,
&ctx);
- task_start(ctx.info);
+ task_start(ctx.info, NULL, NULL);
}
ret = copy_inodes(&cctx, root, convert_flags, &ctx);
if (ret) {
diff --git a/qgroup-verify.c b/qgroup-verify.c
index e2332be2..8df90eef 100644
--- a/qgroup-verify.c
+++ b/qgroup-verify.c
@@ -34,6 +34,13 @@
#include "qgroup-verify.h"
+static u64 *qgroup_item_count;
+
+void qgroup_set_item_count_ptr(u64 *item_count_ptr)
+{
+ qgroup_item_count = item_count_ptr;
+}
+
/*#define QGROUP_VERIFY_DEBUG*/
static unsigned long tot_extents_scanned = 0;
@@ -735,6 +742,7 @@ static int travel_tree(struct btrfs_fs_info *info, struct btrfs_root *root,
*/
nr = btrfs_header_nritems(eb);
for (i = 0; i < nr; i++) {
+ (*qgroup_item_count)++;
new_bytenr = btrfs_node_blockptr(eb, i);
new_num_bytes = info->nodesize;
diff --git a/qgroup-verify.h b/qgroup-verify.h
index 14d36bbf..20e93708 100644
--- a/qgroup-verify.h
+++ b/qgroup-verify.h
@@ -30,4 +30,6 @@ int print_extent_state(struct btrfs_fs_info *info, u64 subvol);
void free_qgroup_counts(void);
+void qgroup_set_item_count_ptr(u64 *item_count_ptr);
+
#endif
diff --git a/task-utils.c b/task-utils.c
index 284cbb3b..a9bee8f4 100644
--- a/task-utils.c
+++ b/task-utils.c
@@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <time.h>
#include "task-utils.h"
@@ -37,7 +38,7 @@ struct task_info *task_init(void *(*threadfn)(void *), int (*postfn)(void *),
return info;
}
-int task_start(struct task_info *info)
+int task_start(struct task_info *info, time_t *start_time, u64 *item_count)
{
int ret;
@@ -47,6 +48,11 @@ int task_start(struct task_info *info)
if (!info->threadfn)
return -1;
+ if (start_time)
+ *start_time = time(NULL);
+ if (item_count)
+ *item_count = 0;
+
ret = pthread_create(&info->id, NULL, info->threadfn,
info->private_data);
diff --git a/task-utils.h b/task-utils.h
index 91d5a646..bbb0f1fd 100644
--- a/task-utils.h
+++ b/task-utils.h
@@ -17,6 +17,7 @@
#ifndef __TASK_UTILS_H__
#define __TASK_UTILS_H__
+#include "kerncompat.h"
#include <pthread.h>
struct periodic_info {
@@ -35,7 +36,7 @@ struct task_info {
/* task life cycle */
struct task_info *task_init(void *(*threadfn)(void *), int (*postfn)(void *),
void *thread_private);
-int task_start(struct task_info *info);
+int task_start(struct task_info *info, time_t *start_time, u64 *item_count);
void task_stop(struct task_info *info);
void task_deinit(struct task_info *info);