Skip to content

Commit 325ce73

Browse files
tonyhutterTulsiJain
andcommitted
zpool status -vv prints error ranges
Add a '-vv' option to zpool status to print all error'd out blocks ranges: $ zpool status -vv ... NAME STATE READ WRITE CKSUM testpool ONLINE 0 0 0 loop0 ONLINE 0 0 20 errors: Permanent errors have been detected in the following files: /var/tmp/testdir/10m_file: found 9 corrupted 128K blocks [0x0-0x1ffff] (128K) [0x100000-0x1fffff] (1M) /var/tmp/testdir/1m_file: found 1 corrupted 128K block [0x0-0x1ffff] (128K) This is a modification of @TulsiJain's #8902 patch, with updates added to print out the different ranges of errors, and to bring the code up to date with master. Signed-off-by: Tony Hutter <[email protected]> Co-authored-by: TulsiJain <[email protected]>
1 parent 54aefa6 commit 325ce73

File tree

11 files changed

+257
-32
lines changed

11 files changed

+257
-32
lines changed

cmd/zpool/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ endif
1818
zpool_LDADD = \
1919
$(top_builddir)/lib/libnvpair/libnvpair.la \
2020
$(top_builddir)/lib/libuutil/libuutil.la \
21-
$(top_builddir)/lib/libzfs/libzfs.la
21+
$(top_builddir)/lib/libzfs/libzfs.la \
22+
$(top_builddir)/lib/libzpool/libzpool.la
2223

2324
zpool_LDADD += -lm $(LIBBLKID)
2425

cmd/zpool/zpool_main.c

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include <sys/zfs_ioctl.h>
6565
#include <sys/mount.h>
6666
#include <sys/sysmacros.h>
67+
#include <sys/range_tree.h>
6768

6869
#include <math.h>
6970

@@ -1852,8 +1853,8 @@ typedef struct status_cbdata {
18521853
int cb_count;
18531854
int cb_name_flags;
18541855
int cb_namewidth;
1856+
unsigned int cb_verbose;
18551857
boolean_t cb_allpools;
1856-
boolean_t cb_verbose;
18571858
boolean_t cb_literal;
18581859
boolean_t cb_explain;
18591860
boolean_t cb_first;
@@ -7400,7 +7401,18 @@ print_checkpoint_status(pool_checkpoint_stat_t *pcs)
74007401
}
74017402

74027403
static void
7403-
print_error_log(zpool_handle_t *zhp)
7404+
print_error_log_range_tree_cb(void *arg, uint64_t start, uint64_t size)
7405+
{
7406+
char str[32];
7407+
7408+
zfs_nicenum(size, str, sizeof (str));
7409+
7410+
printf("%11s[0x%llx-0x%llx] (%s)\n", "", (u_longlong_t)start,
7411+
(u_longlong_t)(start + size - 1), str);
7412+
}
7413+
7414+
static void
7415+
print_error_log(zpool_handle_t *zhp, unsigned int verbose)
74047416
{
74057417
nvlist_t *nverrlist = NULL;
74067418
nvpair_t *elem;
@@ -7410,23 +7422,81 @@ print_error_log(zpool_handle_t *zhp)
74107422
if (zpool_get_errlog(zhp, &nverrlist) != 0)
74117423
return;
74127424

7413-
(void) printf("errors: Permanent errors have been "
7414-
"detected in the following files:\n\n");
7425+
printf_color(ANSI_RED, "errors: Permanent errors have been "
7426+
"detected in the following files:");
7427+
printf("\n\n");
74157428

74167429
pathname = safe_malloc(len);
74177430
elem = NULL;
74187431
while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) {
74197432
nvlist_t *nv;
7420-
uint64_t dsobj, obj;
7433+
uint64_t dsobj, obj, data_block_size, indirect_block_size;
7434+
uint64_t *block_ids;
7435+
int64_t *indrt_levels;
7436+
unsigned int error_count;
7437+
int rc = 0;
74217438

74227439
verify(nvpair_value_nvlist(elem, &nv) == 0);
74237440
verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_DATASET,
74247441
&dsobj) == 0);
74257442
verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT,
74267443
&obj) == 0);
7444+
verify(nvlist_lookup_int64_array(nv, ZPOOL_ERR_LEVEL,
7445+
&indrt_levels, &error_count) == 0);
7446+
verify(nvlist_lookup_uint64_array(nv, ZPOOL_ERR_BLOCKID,
7447+
&block_ids, &error_count) == 0);
7448+
74277449
zpool_obj_to_path(zhp, dsobj, obj, pathname, len);
7428-
(void) printf("%7s %s\n", "", pathname);
7450+
7451+
if (error_count > 0) {
7452+
rc = zpool_get_block_size(zhp, dsobj, obj,
7453+
&data_block_size, &indirect_block_size);
7454+
}
7455+
if (rc == 0) {
7456+
char str[32];
7457+
zfs_nicenum(data_block_size, str, sizeof (str));
7458+
7459+
(void) printf("%7s %s: found %u corrupted %s %s\n",
7460+
"", pathname, error_count, str,
7461+
error_count == 1 ? "block" : "blocks");
7462+
7463+
if (verbose > 1) {
7464+
range_tree_t *range_tree;
7465+
zfs_btree_init();
7466+
range_tree = range_tree_create(NULL,
7467+
RANGE_SEG64, NULL, 0, 0);
7468+
if (!range_tree)
7469+
goto fail;
7470+
7471+
/* Add all our blocks to the range tree */
7472+
for (int i = 0; i < error_count; i++) {
7473+
uint8_t blkptr_size_shift = 0;
7474+
uint8_t indirect_block_shift = 0;
7475+
uint64_t offset_blks = block_ids[i] <<
7476+
((indirect_block_shift -
7477+
blkptr_size_shift) *
7478+
indrt_levels[i]);
7479+
7480+
range_tree_add(range_tree,
7481+
offset_blks * data_block_size,
7482+
data_block_size);
7483+
}
7484+
7485+
/* Print out our ranges */
7486+
range_tree_walk(range_tree,
7487+
print_error_log_range_tree_cb, NULL);
7488+
7489+
printf("\n");
7490+
range_tree_vacate(range_tree, NULL, NULL);
7491+
range_tree_destroy(range_tree);
7492+
zfs_btree_fini();
7493+
}
7494+
} else {
7495+
(void) printf("%7s %s %s\n", "", pathname, " can not "
7496+
"determine error offset");
7497+
}
74297498
}
7499+
fail:
74307500
free(pathname);
74317501
nvlist_free(nverrlist);
74327502
}
@@ -7975,7 +8045,7 @@ status_callback(zpool_handle_t *zhp, void *data)
79758045
"errors, use '-v' for a list\n"),
79768046
(u_longlong_t)nerr);
79778047
else
7978-
print_error_log(zhp);
8048+
print_error_log(zhp, cbp->cb_verbose);
79798049
}
79808050

79818051
if (cbp->cb_dedup_stats)
@@ -8063,7 +8133,7 @@ zpool_do_status(int argc, char **argv)
80638133
cb.cb_print_slow_ios = B_TRUE;
80648134
break;
80658135
case 'v':
8066-
cb.cb_verbose = B_TRUE;
8136+
cb.cb_verbose++;
80678137
break;
80688138
case 'x':
80698139
cb.cb_explain = B_TRUE;

include/libzfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,8 @@ extern int zpool_events_clear(libzfs_handle_t *, int *);
440440
extern int zpool_events_seek(libzfs_handle_t *, uint64_t, int);
441441
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
442442
size_t len);
443+
extern int zpool_get_block_size(zpool_handle_t *, uint64_t, uint64_t,
444+
uint64_t *, uint64_t *);
443445
extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *);
444446
extern int zpool_get_physpath(zpool_handle_t *, char *, size_t);
445447
extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,

include/sys/fs/zfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,8 @@ typedef enum {
13591359
#define ZPOOL_ERR_LIST "error list"
13601360
#define ZPOOL_ERR_DATASET "dataset"
13611361
#define ZPOOL_ERR_OBJECT "object"
1362+
#define ZPOOL_ERR_LEVEL "level"
1363+
#define ZPOOL_ERR_BLOCKID "block id"
13621364

13631365
#define HIS_MAX_RECORD_LEN (MAXPATHLEN + MAXPATHLEN + 1)
13641366

include/sys/zfs_stat.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ typedef struct zfs_stat {
4444
uint64_t zs_mode;
4545
uint64_t zs_links;
4646
uint64_t zs_ctime[2];
47+
uint64_t zs_data_block_size;
48+
uint64_t zs_indirect_block_size;
4749
} zfs_stat_t;
4850

4951
extern int zfs_obj_to_stats(objset_t *osp, uint64_t obj, zfs_stat_t *sb,

lib/libzfs/libzfs_pool.c

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3956,6 +3956,60 @@ zbookmark_mem_compare(const void *a, const void *b)
39563956
return (memcmp(a, b, sizeof (zbookmark_phys_t)));
39573957
}
39583958

3959+
/*
3960+
* Given a sorted array of zbookmark_phys_t's, process one object groups worth
3961+
* (object group = objset + object), add it to the nvlist, and return
3962+
* the number of zbookmark_phys_ts processed. 'nv' is assumed to be already
3963+
* allocated. 'count' is the number of items in the zb[] array.
3964+
*/
3965+
static uint64_t
3966+
zpool_get_errlog_process_obj_group(zpool_handle_t *zhp, zbookmark_phys_t *zb,
3967+
uint64_t count, nvlist_t *nv)
3968+
{
3969+
uint64_t error_count;
3970+
uint64_t *block_ids = NULL;
3971+
int64_t *indrt_levels = NULL;
3972+
uint64_t i;
3973+
3974+
if (count == 0)
3975+
return (0);
3976+
3977+
/* First see how many zbookmarks are of the same object group */
3978+
for (i = 0; i < count; i++) {
3979+
if (i > 0 && !(zb[i - 1].zb_objset == zb[i].zb_objset &&
3980+
zb[i - 1].zb_object == zb[i].zb_object)) {
3981+
/* We've hit a new object group */
3982+
break;
3983+
}
3984+
}
3985+
3986+
error_count = i;
3987+
3988+
block_ids = zfs_alloc(zhp->zpool_hdl, error_count *
3989+
sizeof (*block_ids));
3990+
indrt_levels = zfs_alloc(zhp->zpool_hdl, error_count *
3991+
sizeof (*indrt_levels));
3992+
3993+
/* Write our object group's objset and object */
3994+
VERIFY0(nvlist_add_uint64(nv, ZPOOL_ERR_DATASET, zb[0].zb_objset));
3995+
VERIFY0(nvlist_add_uint64(nv, ZPOOL_ERR_OBJECT, zb[0].zb_object));
3996+
3997+
/* Write all the error'd blocks for this group */
3998+
for (i = 0; i < error_count; i++) {
3999+
block_ids[i] = zb[i].zb_blkid;
4000+
indrt_levels[i] = zb[i].zb_level;
4001+
}
4002+
VERIFY0(nvlist_add_uint64_array(nv, ZPOOL_ERR_BLOCKID, block_ids,
4003+
error_count));
4004+
VERIFY0(nvlist_add_int64_array(nv, ZPOOL_ERR_LEVEL, indrt_levels,
4005+
error_count));
4006+
4007+
free(indrt_levels);
4008+
free(block_ids);
4009+
4010+
return (error_count);
4011+
}
4012+
39594013
/*
39604014
* Retrieve the persistent error log, uniquify the members, and return to the
39614015
* caller.
@@ -3968,6 +4022,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
39684022
uint64_t count;
39694023
zbookmark_phys_t *zb = NULL;
39704024
int i;
4025+
nvlist_t *nv;
39714026

39724027
/*
39734028
* Retrieve the raw error list from the kernel. If the number of errors
@@ -4018,34 +4073,26 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
40184073

40194074
verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0);
40204075

4076+
40214077
/*
4022-
* Fill in the nverrlistp with nvlist's of dataset and object numbers.
4078+
* zb[count] is an array of zbookmarks which point to error'd out
4079+
* blocks. We logically group these into objset + object, which
4080+
* we'll call an "object group", which is usually a file (but
4081+
* can be something else.
4082+
*
4083+
* The 'i = i' in this for() loop is to get rid of a cstyle warning:
4084+
* "comma or semicolon followed by non-blank"
40234085
*/
4024-
for (i = 0; i < count; i++) {
4025-
nvlist_t *nv;
4026-
4027-
/* ignoring zb_blkid and zb_level for now */
4028-
if (i > 0 && zb[i-1].zb_objset == zb[i].zb_objset &&
4029-
zb[i-1].zb_object == zb[i].zb_object)
4030-
continue;
4031-
4086+
for (i = 0; i < count; i = i) {
40324087
if (nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) != 0)
40334088
goto nomem;
4034-
if (nvlist_add_uint64(nv, ZPOOL_ERR_DATASET,
4035-
zb[i].zb_objset) != 0) {
4036-
nvlist_free(nv);
4037-
goto nomem;
4038-
}
4039-
if (nvlist_add_uint64(nv, ZPOOL_ERR_OBJECT,
4040-
zb[i].zb_object) != 0) {
4041-
nvlist_free(nv);
4042-
goto nomem;
4043-
}
4089+
4090+
i += zpool_get_errlog_process_obj_group(zhp, &zb[i], count - i,
4091+
nv);
40444092
if (nvlist_add_nvlist(*nverrlistp, "ejk", nv) != 0) {
40454093
nvlist_free(nv);
40464094
goto nomem;
40474095
}
4048-
nvlist_free(nv);
40494096
}
40504097

40514098
free((void *)(uintptr_t)zc.zc_nvlist_dst);
@@ -4390,6 +4437,36 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
43904437
free(mntpnt);
43914438
}
43924439

4440+
/*
4441+
* Given an dataset object number, return data block and indirect block size.
4442+
*/
4443+
int
4444+
zpool_get_block_size(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
4445+
uint64_t *data_blk_size, uint64_t *indrt_blk_size)
4446+
{
4447+
zfs_cmd_t zc = {"\0"};
4448+
char dsname[ZFS_MAX_DATASET_NAME_LEN];
4449+
/* get the dataset's name */
4450+
zc.zc_obj = dsobj;
4451+
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
4452+
int error = ioctl(zhp->zpool_hdl->libzfs_fd,
4453+
ZFS_IOC_DSOBJ_TO_DSNAME, &zc);
4454+
if (error) {
4455+
return (error);
4456+
}
4457+
(void) strlcpy(dsname, zc.zc_value, sizeof (dsname));
4458+
4459+
/* get data block and indirect block size */
4460+
(void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
4461+
zc.zc_obj = obj;
4462+
error = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_STATS, &zc);
4463+
if (error == 0) {
4464+
*data_blk_size = zc.zc_stat.zs_data_block_size;
4465+
*indrt_blk_size = zc.zc_stat.zs_indirect_block_size;
4466+
}
4467+
return (error);
4468+
}
4469+
43934470
/*
43944471
* Wait while the specified activity is in progress in the pool.
43954472
*/

man/man8/zpool-status.8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ See
121121
.Xr date 1 .
122122
.It Fl v
123123
Displays verbose data error information, printing out a complete list of all
124-
data errors since the last complete pool scrub.
124+
data errors since the last complete pool scrub. Passing this flag twice ('-vv')
125+
will print out the byte ranges for the errors within the files.
125126
.It Fl x
126127
Only display status for pools that are exhibiting errors or are otherwise
127128
unavailable.

module/os/linux/zfs/zfs_znode.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2087,6 +2087,11 @@ zfs_obj_to_stats_impl(sa_handle_t *hdl, sa_attr_type_t *sa_table,
20872087
sa_bulk_attr_t bulk[4];
20882088
int count = 0;
20892089

2090+
dmu_object_info_t doi;
2091+
sa_object_info(hdl, &doi);
2092+
sb->zs_data_block_size = doi.doi_data_block_size;
2093+
sb->zs_indirect_block_size = doi.doi_metadata_block_size;
2094+
20902095
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL,
20912096
&sb->zs_mode, sizeof (sb->zs_mode));
20922097
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL,

tests/runfiles/common.run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ tests = ['zpool_split_cliargs', 'zpool_split_devices',
443443
tags = ['functional', 'cli_root', 'zpool_split']
444444

445445
[tests/functional/cli_root/zpool_status]
446-
tests = ['zpool_status_001_pos', 'zpool_status_002_pos']
446+
tests = ['zpool_status_001_pos', 'zpool_status_002_pos', 'zpool_status_-v']
447447
tags = ['functional', 'cli_root', 'zpool_status']
448448

449449
[tests/functional/cli_root/zpool_sync]

tests/zfs-tests/tests/functional/cli_root/zpool_status/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ dist_pkgdata_SCRIPTS = \
33
setup.ksh \
44
cleanup.ksh \
55
zpool_status_001_pos.ksh \
6-
zpool_status_002_pos.ksh
6+
zpool_status_002_pos.ksh \
7+
zpool_status_-v.ksh

0 commit comments

Comments
 (0)