Skip to content

Commit 45b19a7

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 fc519b2 commit 45b19a7

File tree

27 files changed

+882
-188
lines changed

27 files changed

+882
-188
lines changed

cmd/zinject/translate.c

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,24 @@ compress_slashes(const char *src, char *dest)
7575
*dest = '\0';
7676
}
7777

78+
static boolean_t
79+
path_is_zvol(const char *inpath)
80+
{
81+
char buf[MAXPATHLEN];
82+
83+
/* Resolve symlinks to /dev/zd* device */
84+
if (realpath(inpath, buf) != NULL)
85+
if (strncmp(buf, "/dev/zd", 7) == 0)
86+
return (B_TRUE);
87+
88+
return (B_FALSE);
89+
}
90+
7891
/*
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.
92+
* Given a full path to a file or zvol device, translate into a dataset name and
93+
* a relative path within the dataset. 'dataset' must be at least MAXNAMELEN
94+
* characters, and 'relpath' must be at least MAXPATHLEN characters. We also
95+
* pass a stat64 buffer, which we need later to get the object ID.
8396
*/
8497
static int
8598
parse_pathname(const char *inpath, char *dataset, char *relpath,
@@ -98,6 +111,47 @@ parse_pathname(const char *inpath, char *dataset, char *relpath,
98111
return (-1);
99112
}
100113

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

cmd/zpool/zpool_main.c

Lines changed: 140 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
96729687
}
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+
}
9707+
}
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
@@ -10341,13 +10383,15 @@ print_checkpoint_status(pool_checkpoint_stat_t *pcs)
1034110383
space_buf);
1034210384
}
1034310385

10386+
1034410387
static void
10345-
print_error_log(zpool_handle_t *zhp)
10388+
print_error_log(zpool_handle_t *zhp, int verbosity, boolean_t literal)
1034610389
{
1034710390
nvlist_t *nverrlist = NULL;
1034810391
nvpair_t *elem;
10349-
char *pathname;
10392+
char *pathname, *last_pathname = NULL;
1035010393
size_t len = MAXPATHLEN * 2;
10394+
boolean_t started = B_FALSE;
1035110395

1035210396
if (zpool_get_errlog(zhp, &nverrlist) != 0)
1035310397
return;
@@ -10367,9 +10411,49 @@ 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 == NULL ||
10415+
0 != strncmp(pathname, last_pathname, len)) {
10416+
last_pathname = strdup(pathname);
10417+
if (started)
10418+
(void) printf("\n");
10419+
else
10420+
started = B_TRUE;
10421+
(void) printf("%7s %s ", "", pathname);
10422+
} else if (verbosity > 1) {
10423+
(void) printf(",");
10424+
}
10425+
if (verbosity > 1) {
10426+
nvlist_t **arr;
10427+
uint_t count;
10428+
if (nvlist_lookup_nvlist_array(nv, ZPOOL_ERR_RANGES,
10429+
&arr, &count) != 0) {
10430+
printf("(no ranges)");
10431+
continue;
10432+
}
10433+
10434+
for (uint_t i = 0; i < count; i++) {
10435+
uint64_t start;
10436+
uint64_t end;
10437+
start = fnvlist_lookup_uint64(arr[i],
10438+
ZPOOL_ERR_START_BYTE);
10439+
end = fnvlist_lookup_uint64(arr[i],
10440+
ZPOOL_ERR_END_BYTE);
10441+
if (literal) {
10442+
(void) printf("%lu-%lu", start, end);
10443+
} else {
10444+
char s1[32], s2[32];
10445+
zfs_nicenum(start, s1, sizeof (s1));
10446+
zfs_nicenum(end, s2, sizeof (s2));
10447+
(void) printf("%s-%s", s1, s2);
10448+
}
10449+
if (i != count - 1)
10450+
printf(",");
10451+
}
10452+
}
1037110453
}
10454+
(void) printf("\n");
1037210455
free(pathname);
10456+
free(last_pathname);
1037310457
nvlist_free(nverrlist);
1037410458
}
1037510459

@@ -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 (0 == cbp->cb_verbosity) {
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;

cmd/zpool/zpool_util.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,6 @@ array64_max(uint64_t array[], unsigned int len)
115115
return (max);
116116
}
117117

118-
/*
119-
* Find highest one bit set.
120-
* Returns bit number + 1 of highest bit that is set, otherwise returns 0.
121-
*/
122-
int
123-
highbit64(uint64_t i)
124-
{
125-
if (i == 0)
126-
return (0);
127-
128-
return (NBBY * sizeof (uint64_t) - __builtin_clzll(i));
129-
}
130-
131118
/*
132119
* Find lowest one bit set.
133120
* Returns bit number + 1 of lowest bit that is set, otherwise returns 0.

cmd/zpool/zpool_util.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ void *safe_realloc(void *, size_t);
4545
void zpool_no_memory(void);
4646
uint_t num_logs(nvlist_t *nv);
4747
uint64_t array64_max(uint64_t array[], unsigned int len);
48-
int highbit64(uint64_t i);
4948
int lowbit64(uint64_t i);
5049

5150
/*

0 commit comments

Comments
 (0)