Skip to content

Commit 1c4134d

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 <[email protected]> Co-authored-by:: Alan Somers <[email protected]> Co-authored-by: TulsiJain <[email protected]>
1 parent 6015edb commit 1c4134d

File tree

33 files changed

+918
-143
lines changed

33 files changed

+918
-143
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
@@ -520,7 +520,7 @@ get_usage(zpool_help_t idx)
520520
return (gettext("\ttrim [-dw] [-r <rate>] [-c | -s] "
521521
"<-a | <pool> [<device> ...]>\n"));
522522
case HELP_STATUS:
523-
return (gettext("\tstatus [-DdegiLPpstvx] "
523+
return (gettext("\tstatus [-DdegiLPpstx] [-v|-vv] "
524524
"[-c script1[,script2,...]] ...\n"
525525
"\t [-j|--json [--json-flat-vdevs] [--json-int] "
526526
"[--json-pool-key-guid]] ...\n"
@@ -1049,6 +1049,9 @@ nice_num_str_nvlist(nvlist_t *item, const char *key, uint64_t value,
10491049
case ZFS_NICENUM_BYTES:
10501050
zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_BYTES);
10511051
break;
1052+
case ZFS_NICENUM_RAW:
1053+
zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_RAW);
1054+
break;
10521055
case ZFS_NICENUM_TIME:
10531056
zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_TIME);
10541057
break;
@@ -2589,7 +2592,7 @@ typedef struct status_cbdata {
25892592
int cb_name_flags;
25902593
int cb_namewidth;
25912594
boolean_t cb_allpools;
2592-
boolean_t cb_verbose;
2595+
int cb_verbosity;
25932596
boolean_t cb_literal;
25942597
boolean_t cb_explain;
25952598
boolean_t cb_first;
@@ -3321,7 +3324,7 @@ print_class_vdevs(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
33213324
nvlist_t **child;
33223325
boolean_t printed = B_FALSE;
33233326

3324-
assert(zhp != NULL || !cb->cb_verbose);
3327+
assert(zhp != NULL || cb->cb_verbosity == 0);
33253328

33263329
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
33273330
&children) != 0)
@@ -9522,7 +9525,7 @@ class_vdevs_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
95229525
if (!cb->cb_flat_vdevs)
95239526
class_obj = fnvlist_alloc();
95249527

9525-
assert(zhp != NULL || !cb->cb_verbose);
9528+
assert(zhp != NULL || cb->cb_verbosity == 0);
95269529

95279530
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
95289531
&children) != 0)
@@ -9626,57 +9629,96 @@ spares_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
96269629
}
96279630
}
96289631

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

96829724
static void
@@ -10348,12 +10390,14 @@ print_checkpoint_status(pool_checkpoint_stat_t *pcs)
1034810390
}
1034910391

1035010392
static void
10351-
print_error_log(zpool_handle_t *zhp)
10393+
print_error_log(zpool_handle_t *zhp, int verbosity, boolean_t literal)
1035210394
{
1035310395
nvlist_t *nverrlist = NULL;
1035410396
nvpair_t *elem;
1035510397
char *pathname;
1035610398
size_t len = MAXPATHLEN * 2;
10399+
boolean_t started = B_FALSE;
10400+
char last_pathname[MAXPATHLEN] = "";
1035710401

1035810402
if (zpool_get_errlog(zhp, &nverrlist) != 0)
1035910403
return;
@@ -10373,8 +10417,48 @@ print_error_log(zpool_handle_t *zhp)
1037310417
verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT,
1037410418
&obj) == 0);
1037510419
zpool_obj_to_path(zhp, dsobj, obj, pathname, len);
10376-
(void) printf("%7s %s\n", "", pathname);
10420+
if (last_pathname[0] == '\0' ||
10421+
strncmp(pathname, last_pathname, len) != 0) {
10422+
strlcpy(last_pathname, pathname,
10423+
sizeof (last_pathname));
10424+
if (started)
10425+
(void) printf("\n");
10426+
else
10427+
started = B_TRUE;
10428+
(void) printf("%7s %s ", "", pathname);
10429+
} else if (verbosity > 1) {
10430+
(void) printf(",");
10431+
}
10432+
if (verbosity > 1) {
10433+
nvlist_t **arr;
10434+
uint_t count;
10435+
if (nvlist_lookup_nvlist_array(nv, ZPOOL_ERR_RANGES,
10436+
&arr, &count) != 0) {
10437+
(void) printf("(no ranges)");
10438+
continue;
10439+
}
10440+
10441+
for (uint_t i = 0; i < count; i++) {
10442+
uint64_t start;
10443+
uint64_t end;
10444+
start = fnvlist_lookup_uint64(arr[i],
10445+
ZPOOL_ERR_START_BYTE);
10446+
end = fnvlist_lookup_uint64(arr[i],
10447+
ZPOOL_ERR_END_BYTE);
10448+
if (literal) {
10449+
(void) printf("%lu-%lu", start, end);
10450+
} else {
10451+
char s1[32], s2[32];
10452+
zfs_nicenum(start, s1, sizeof (s1));
10453+
zfs_nicenum(end, s2, sizeof (s2));
10454+
(void) printf("%s-%s", s1, s2);
10455+
}
10456+
if (i != count - 1)
10457+
(void) printf(",");
10458+
}
10459+
}
1037710460
}
10461+
(void) printf("\n");
1037810462
free(pathname);
1037910463
nvlist_free(nverrlist);
1038010464
}
@@ -11071,14 +11155,15 @@ status_callback(zpool_handle_t *zhp, void *data)
1107111155
if (nerr == 0) {
1107211156
(void) printf(gettext(
1107311157
"errors: No known data errors\n"));
11074-
} else if (!cbp->cb_verbose) {
11158+
} else if (cbp->cb_verbosity == 0) {
1107511159
color_start(ANSI_RED);
1107611160
(void) printf(gettext("errors: %llu data "
1107711161
"errors, use '-v' for a list\n"),
1107811162
(u_longlong_t)nerr);
1107911163
color_end();
1108011164
} else {
11081-
print_error_log(zhp);
11165+
print_error_log(zhp, cbp->cb_verbosity,
11166+
cbp->cb_literal);
1108211167
}
1108311168
}
1108411169

@@ -11205,7 +11290,7 @@ zpool_do_status(int argc, char **argv)
1120511290
get_timestamp_arg(*optarg);
1120611291
break;
1120711292
case 'v':
11208-
cb.cb_verbose = B_TRUE;
11293+
cb.cb_verbosity++;
1120911294
break;
1121011295
case 'j':
1121111296
cb.cb_json = B_TRUE;

0 commit comments

Comments
 (0)