Skip to content

Commit 6c63c6a

Browse files
asomersTulsiJain
authored andcommitted
zpool: Add zpool status -vv error ranges
Print the byte error ranges with 'zpool status -vv'. This works with the normal zpool status formatting flags (-p, -j, --json-int). In addition: - Move range_tree/btree to common userspace/kernel code. - Modify ZFS_IOC_OBJ_TO_STATS ioctl to optionally return "extended" object stats. - Let zinject corrupt zvol data. - Add test case. This commit takes code from these PRs: #17502 #9781 #8902 Signed-off-by: Tony Hutter <hutter2@llnl.gov> Co-authored-by:: Alan Somers <asomers@gmail.com> Co-authored-by: TulsiJain <tulsi.jain@delphix.com>
1 parent f93506d commit 6c63c6a

File tree

33 files changed

+914
-142
lines changed

33 files changed

+914
-142
lines changed

cmd/Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ zhack_SOURCES = \
3535
zhack_LDADD = \
3636
libzpool.la \
3737
libzfs_core.la \
38-
libnvpair.la
39-
38+
libnvpair.la \
39+
librange_tree.la
4040

4141
ztest_CFLAGS = $(AM_CFLAGS) $(KERNEL_CFLAGS)
4242
ztest_CPPFLAGS = $(AM_CPPFLAGS) $(LIBZPOOL_CPPFLAGS)

cmd/zdb/Makefile.am

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ zdb_LDADD = \
1313
libzdb.la \
1414
libzpool.la \
1515
libzfs_core.la \
16-
libnvpair.la
16+
libnvpair.la \
17+
librange_tree.la \
18+
libbtree.la
1719

1820
zdb_LDADD += $(LIBCRYPTO_LIBS)

cmd/zinject/translate.c

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <sys/dmu_objset.h>
4444
#include <sys/dnode.h>
4545
#include <sys/vdev_impl.h>
46+
#include <sys/zvol.h>
4647

4748
#include <sys/mkdev.h>
4849

@@ -75,11 +76,25 @@ compress_slashes(const char *src, char *dest)
7576
*dest = '\0';
7677
}
7778

79+
static boolean_t
80+
path_is_zvol(const char *inpath)
81+
{
82+
char buf[MAXPATHLEN];
83+
const char *devname = "/dev/" ZVOL_DEV_NAME;
84+
85+
/* Resolve symlinks to /dev/zd* device */
86+
if (realpath(inpath, buf) != NULL)
87+
if (strncmp(buf, devname, strlen(devname)) == 0)
88+
return (B_TRUE);
89+
90+
return (B_FALSE);
91+
}
92+
7893
/*
79-
* Given a full path to a file, translate into a dataset name and a relative
80-
* path within the dataset. 'dataset' must be at least MAXNAMELEN characters,
81-
* and 'relpath' must be at least MAXPATHLEN characters. We also pass a stat64
82-
* buffer, which we need later to get the object ID.
94+
* Given a full path to a file or zvol device, translate into a dataset name and
95+
* a relative path within the dataset. 'dataset' must be at least MAXNAMELEN
96+
* characters, and 'relpath' must be at least MAXPATHLEN characters. We also
97+
* pass a stat64 buffer, which we need later to get the object ID.
8398
*/
8499
static int
85100
parse_pathname(const char *inpath, char *dataset, char *relpath,
@@ -98,6 +113,56 @@ parse_pathname(const char *inpath, char *dataset, char *relpath,
98113
return (-1);
99114
}
100115

116+
/* special case: inject errors into zvol */
117+
if (path_is_zvol(inpath)) {
118+
int fd;
119+
char *slash;
120+
int rc;
121+
122+
if ((fd = open(inpath, O_RDONLY|O_CLOEXEC)) == -1) {
123+
return (-1);
124+
}
125+
126+
if (fstat64(fd, statbuf) != 0) {
127+
close(fd);
128+
return (-1);
129+
}
130+
131+
/*
132+
* HACK: the zvol's inode will not contain its object number.
133+
* However, it has long been the case that the zvol data is
134+
* object number 1 (ZVOL_OBJ):
135+
*
136+
* Object lvl iblk dblk dsize lsize %full type
137+
* 0 6 128K 16K 11K 16K 6.25 DMU dnode
138+
* 1 2 128K 16K 20.1M 20M 100.00 zvol object
139+
* 2 1 128K 512 0 512 100.00 zvol prop
140+
*
141+
* So we hardcode that in the statbuf inode field as workaround.
142+
*/
143+
statbuf->st_ino = ZVOL_OBJ;
144+
145+
rc = ioctl(fd, BLKZNAME, fullpath);
146+
close(fd);
147+
if (rc != 0)
148+
return (-1);
149+
150+
(void) strlcpy(dataset, fullpath, MAXNAMELEN);
151+
152+
/*
153+
* fullpath contains string like 'tank/zvol'. Strip off the
154+
* 'tank' and 'zvol' parts.
155+
*/
156+
slash = strchr(fullpath, '/');
157+
if (slash == NULL) {
158+
(void) fprintf(stderr, "invalid volume name: '%s'\n",
159+
fullpath);
160+
return (-1);
161+
};
162+
(void) strcpy(relpath, slash + 1);
163+
return (0);
164+
}
165+
101166
if (getextmntent(fullpath, &mp, statbuf) != 0) {
102167
(void) fprintf(stderr, "cannot find mountpoint for '%s'\n",
103168
fullpath);

cmd/zpool/zpool_main.c

Lines changed: 140 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ get_usage(zpool_help_t idx)
521521
return (gettext("\ttrim [-dw] [-r <rate>] [-c | -s] "
522522
"<-a | <pool> [<device> ...]>\n"));
523523
case HELP_STATUS:
524-
return (gettext("\tstatus [-DdegiLPpstvx] "
524+
return (gettext("\tstatus [-DdegiLPpstx] [-v|-vv] "
525525
"[-c script1[,script2,...]] ...\n"
526526
"\t [-j|--json [--json-flat-vdevs] [--json-int] "
527527
"[--json-pool-key-guid]] ...\n"
@@ -1050,6 +1050,9 @@ nice_num_str_nvlist(nvlist_t *item, const char *key, uint64_t value,
10501050
case ZFS_NICENUM_BYTES:
10511051
zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_BYTES);
10521052
break;
1053+
case ZFS_NICENUM_RAW:
1054+
zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_RAW);
1055+
break;
10531056
case ZFS_NICENUM_TIME:
10541057
zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_TIME);
10551058
break;
@@ -2590,7 +2593,7 @@ typedef struct status_cbdata {
25902593
int cb_name_flags;
25912594
int cb_namewidth;
25922595
boolean_t cb_allpools;
2593-
boolean_t cb_verbose;
2596+
int cb_verbosity;
25942597
boolean_t cb_literal;
25952598
boolean_t cb_explain;
25962599
boolean_t cb_first;
@@ -3322,7 +3325,7 @@ print_class_vdevs(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
33223325
nvlist_t **child;
33233326
boolean_t printed = B_FALSE;
33243327

3325-
assert(zhp != NULL || !cb->cb_verbose);
3328+
assert(zhp != NULL || cb->cb_verbosity == 0);
33263329

33273330
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
33283331
&children) != 0)
@@ -9516,7 +9519,7 @@ class_vdevs_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
95169519
if (!cb->cb_flat_vdevs)
95179520
class_obj = fnvlist_alloc();
95189521

9519-
assert(zhp != NULL || !cb->cb_verbose);
9522+
assert(zhp != NULL || cb->cb_verbosity == 0);
95209523

95219524
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
95229525
&children) != 0)
@@ -9620,57 +9623,96 @@ spares_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
96209623
}
96219624
}
96229625

9626+
/*
9627+
* Take a uint64 nvpair named 'name' from nverrlist, nicenum-ify it, and
9628+
* put it back in 'nverrlist', possibly as a string, with the same 'name'.
9629+
*/
9630+
static void
9631+
convert_nvlist_uint64_to_nicenum(status_cbdata_t *cb, nvlist_t *parent,
9632+
const char *name, enum zfs_nicenum_format format)
9633+
{
9634+
uint64_t val;
9635+
nvpair_t *nvp;
9636+
9637+
if (nvlist_lookup_nvpair(parent, name, &nvp) != 0)
9638+
return; /* nothing by that name, ignore */
9639+
9640+
val = fnvpair_value_uint64(nvp);
9641+
nvlist_remove_nvpair(parent, nvp);
9642+
nice_num_str_nvlist(parent, name, val,
9643+
cb->cb_literal, cb->cb_json_as_int, format);
9644+
}
9645+
96239646
static void
96249647
errors_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *item)
96259648
{
9626-
uint64_t nerr;
9627-
nvlist_t *config = zpool_get_config(zhp, NULL);
9628-
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
9629-
&nerr) == 0) {
9630-
nice_num_str_nvlist(item, ZPOOL_CONFIG_ERRCOUNT, nerr,
9631-
cb->cb_literal, cb->cb_json_as_int, ZFS_NICENUM_1024);
9632-
if (nerr != 0 && cb->cb_verbose) {
9633-
nvlist_t *nverrlist = NULL;
9634-
if (zpool_get_errlog(zhp, &nverrlist) == 0) {
9635-
int i = 0;
9636-
int count = 0;
9637-
size_t len = MAXPATHLEN * 2;
9638-
nvpair_t *elem = NULL;
9639-
9640-
for (nvpair_t *pair =
9641-
nvlist_next_nvpair(nverrlist, NULL);
9642-
pair != NULL;
9643-
pair = nvlist_next_nvpair(nverrlist, pair))
9644-
count++;
9645-
char **errl = (char **)malloc(
9646-
count * sizeof (char *));
9647-
9648-
while ((elem = nvlist_next_nvpair(nverrlist,
9649-
elem)) != NULL) {
9650-
nvlist_t *nv;
9651-
uint64_t dsobj, obj;
9652-
9653-
verify(nvpair_value_nvlist(elem,
9654-
&nv) == 0);
9655-
verify(nvlist_lookup_uint64(nv,
9656-
ZPOOL_ERR_DATASET, &dsobj) == 0);
9657-
verify(nvlist_lookup_uint64(nv,
9658-
ZPOOL_ERR_OBJECT, &obj) == 0);
9659-
errl[i] = safe_malloc(len);
9660-
zpool_obj_to_path(zhp, dsobj, obj,
9661-
errl[i++], len);
9662-
}
9663-
nvlist_free(nverrlist);
9664-
fnvlist_add_string_array(item, "errlist",
9665-
(const char **)errl, count);
9666-
for (int i = 0; i < count; ++i)
9667-
free(errl[i]);
9668-
free(errl);
9669-
} else
9670-
fnvlist_add_string(item, "errlist",
9671-
strerror(errno));
9649+
int verbosity = cb->cb_verbosity;
9650+
nvlist_t *nverrlist = NULL, *json;
9651+
nvpair_t *elem;
9652+
char *pathname;
9653+
size_t len = MAXPATHLEN * 2;
9654+
nvlist_t **ranges;
9655+
uint_t count;
9656+
9657+
if (zpool_get_errlog(zhp, &nverrlist) != 0)
9658+
return;
9659+
9660+
pathname = safe_malloc(len);
9661+
json = fnvlist_alloc();
9662+
9663+
elem = NULL;
9664+
while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) {
9665+
nvlist_t *nv;
9666+
uint64_t dsobj, obj;
9667+
9668+
verify(nvpair_value_nvlist(elem, &nv) == 0);
9669+
9670+
dsobj = fnvlist_lookup_uint64(nv, ZPOOL_ERR_DATASET);
9671+
obj = fnvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT);
9672+
9673+
zpool_obj_to_path(zhp, dsobj, obj, pathname, len);
9674+
9675+
/*
9676+
* Each JSON entry is a different file/zvol. If user has
9677+
* verbosity = 1, then just make a simple object containing
9678+
* the name.
9679+
*/
9680+
if (verbosity <= 1) {
9681+
nvlist_t *nameonly;
9682+
nameonly = fnvlist_alloc();
9683+
fnvlist_add_string(nameonly, ZPOOL_ERR_NAME, pathname);
9684+
fnvlist_add_nvlist(json, pathname, nameonly);
9685+
nvlist_free(nameonly);
9686+
continue;
9687+
}
9688+
9689+
fnvlist_add_string(nv, ZPOOL_ERR_NAME, pathname);
9690+
9691+
/* nicenum-ify our nvlist */
9692+
convert_nvlist_uint64_to_nicenum(cb, nv, ZPOOL_ERR_OBJECT,
9693+
ZFS_NICENUM_RAW);
9694+
convert_nvlist_uint64_to_nicenum(cb, nv, ZPOOL_ERR_DATASET,
9695+
ZFS_NICENUM_RAW);
9696+
convert_nvlist_uint64_to_nicenum(cb, nv, ZPOOL_ERR_BLOCK_SIZE,
9697+
ZFS_NICENUM_1024);
9698+
9699+
if (nvlist_lookup_nvlist_array(nv, ZPOOL_ERR_RANGES, &ranges,
9700+
&count) == 0) {
9701+
for (uint_t i = 0; i < count; i++) {
9702+
convert_nvlist_uint64_to_nicenum(cb, ranges[i],
9703+
ZPOOL_ERR_START_BYTE, ZFS_NICENUM_1024);
9704+
convert_nvlist_uint64_to_nicenum(cb, ranges[i],
9705+
ZPOOL_ERR_END_BYTE, ZFS_NICENUM_1024);
9706+
}
96729707
}
9708+
9709+
fnvlist_add_nvlist(json, pathname, nv);
96739710
}
9711+
9712+
/* Place our error list in a top level "errors" JSON object. */
9713+
fnvlist_add_nvlist(item, ZPOOL_ERR_JSON, json);
9714+
free(pathname);
9715+
nvlist_free(nverrlist);
96749716
}
96759717

96769718
static void
@@ -10342,12 +10384,14 @@ print_checkpoint_status(pool_checkpoint_stat_t *pcs)
1034210384
}
1034310385

1034410386
static void
10345-
print_error_log(zpool_handle_t *zhp)
10387+
print_error_log(zpool_handle_t *zhp, int verbosity, boolean_t literal)
1034610388
{
1034710389
nvlist_t *nverrlist = NULL;
1034810390
nvpair_t *elem;
1034910391
char *pathname;
1035010392
size_t len = MAXPATHLEN * 2;
10393+
boolean_t started = B_FALSE;
10394+
char last_pathname[MAXPATHLEN] = "";
1035110395

1035210396
if (zpool_get_errlog(zhp, &nverrlist) != 0)
1035310397
return;
@@ -10367,8 +10411,48 @@ print_error_log(zpool_handle_t *zhp)
1036710411
verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT,
1036810412
&obj) == 0);
1036910413
zpool_obj_to_path(zhp, dsobj, obj, pathname, len);
10370-
(void) printf("%7s %s\n", "", pathname);
10414+
if (last_pathname[0] == '\0' ||
10415+
strncmp(pathname, last_pathname, len) != 0) {
10416+
strlcpy(last_pathname, pathname,
10417+
sizeof (last_pathname));
10418+
if (started)
10419+
(void) printf("\n");
10420+
else
10421+
started = B_TRUE;
10422+
(void) printf("%7s %s ", "", pathname);
10423+
} else if (verbosity > 1) {
10424+
(void) printf(",");
10425+
}
10426+
if (verbosity > 1) {
10427+
nvlist_t **arr;
10428+
uint_t count;
10429+
if (nvlist_lookup_nvlist_array(nv, ZPOOL_ERR_RANGES,
10430+
&arr, &count) != 0) {
10431+
(void) printf("(no ranges)");
10432+
continue;
10433+
}
10434+
10435+
for (uint_t i = 0; i < count; i++) {
10436+
uint64_t start;
10437+
uint64_t end;
10438+
start = fnvlist_lookup_uint64(arr[i],
10439+
ZPOOL_ERR_START_BYTE);
10440+
end = fnvlist_lookup_uint64(arr[i],
10441+
ZPOOL_ERR_END_BYTE);
10442+
if (literal) {
10443+
(void) printf("%lu-%lu", start, end);
10444+
} else {
10445+
char s1[32], s2[32];
10446+
zfs_nicenum(start, s1, sizeof (s1));
10447+
zfs_nicenum(end, s2, sizeof (s2));
10448+
(void) printf("%s-%s", s1, s2);
10449+
}
10450+
if (i != count - 1)
10451+
(void) printf(",");
10452+
}
10453+
}
1037110454
}
10455+
(void) printf("\n");
1037210456
free(pathname);
1037310457
nvlist_free(nverrlist);
1037410458
}
@@ -11065,14 +11149,15 @@ status_callback(zpool_handle_t *zhp, void *data)
1106511149
if (nerr == 0) {
1106611150
(void) printf(gettext(
1106711151
"errors: No known data errors\n"));
11068-
} else if (!cbp->cb_verbose) {
11152+
} else if (cbp->cb_verbosity == 0) {
1106911153
color_start(ANSI_RED);
1107011154
(void) printf(gettext("errors: %llu data "
1107111155
"errors, use '-v' for a list\n"),
1107211156
(u_longlong_t)nerr);
1107311157
color_end();
1107411158
} else {
11075-
print_error_log(zhp);
11159+
print_error_log(zhp, cbp->cb_verbosity,
11160+
cbp->cb_literal);
1107611161
}
1107711162
}
1107811163

@@ -11199,7 +11284,7 @@ zpool_do_status(int argc, char **argv)
1119911284
get_timestamp_arg(*optarg);
1120011285
break;
1120111286
case 'v':
11202-
cb.cb_verbose = B_TRUE;
11287+
cb.cb_verbosity++;
1120311288
break;
1120411289
case 'j':
1120511290
cb.cb_json = B_TRUE;

0 commit comments

Comments
 (0)