-
Notifications
You must be signed in to change notification settings - Fork 1.9k
zpool: Add zpool status -vv error ranges #17864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -75,11 +75,24 @@ compress_slashes(const char *src, char *dest) | |||||
| *dest = '\0'; | ||||||
| } | ||||||
|
|
||||||
| static boolean_t | ||||||
| path_is_zvol(const char *inpath) | ||||||
| { | ||||||
| char buf[MAXPATHLEN]; | ||||||
|
|
||||||
| /* Resolve symlinks to /dev/zd* device */ | ||||||
| if (realpath(inpath, buf) != NULL) | ||||||
| if (strncmp(buf, "/dev/zd", 7) == 0) | ||||||
| return (B_TRUE); | ||||||
|
|
||||||
| return (B_FALSE); | ||||||
| } | ||||||
|
|
||||||
| /* | ||||||
| * Given a full path to a file, translate into a dataset name and a relative | ||||||
| * path within the dataset. 'dataset' must be at least MAXNAMELEN characters, | ||||||
| * and 'relpath' must be at least MAXPATHLEN characters. We also pass a stat64 | ||||||
| * buffer, which we need later to get the object ID. | ||||||
| * Given a full path to a file or zvol device, translate into a dataset name and | ||||||
| * a relative path within the dataset. 'dataset' must be at least MAXNAMELEN | ||||||
| * characters, and 'relpath' must be at least MAXPATHLEN characters. We also | ||||||
| * pass a stat64 buffer, which we need later to get the object ID. | ||||||
| */ | ||||||
| static int | ||||||
| parse_pathname(const char *inpath, char *dataset, char *relpath, | ||||||
|
|
@@ -98,6 +111,47 @@ parse_pathname(const char *inpath, char *dataset, char *relpath, | |||||
| return (-1); | ||||||
| } | ||||||
|
|
||||||
| /* special case: inject errors into zvol */ | ||||||
| if (path_is_zvol(inpath)) { | ||||||
| int fd; | ||||||
| char *slash; | ||||||
| int rc; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: missing newline after the declarations. |
||||||
| if ((fd = open(inpath, O_RDONLY|O_CLOEXEC)) == -1 || | ||||||
| fstat64(fd, statbuf) != 0) { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||||||
| return (-1); | ||||||
| } | ||||||
|
|
||||||
| /* | ||||||
| * HACK: the zvol's inode will not contain its object number. | ||||||
| * However, it has long been the case that the zvol data is | ||||||
| * object number 1: | ||||||
| * | ||||||
| * Object lvl iblk dblk dsize lsize %full type | ||||||
| * 0 6 128K 16K 11K 16K 6.25 DMU dnode | ||||||
| * 1 2 128K 16K 20.1M 20M 100.00 zvol object | ||||||
| * 2 1 128K 512 0 512 100.00 zvol prop | ||||||
| * | ||||||
| * So we hardcode that in the statbuf inode field as workaround. | ||||||
| */ | ||||||
| statbuf->st_ino = 1; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| rc = ioctl(fd, BLKZNAME, fullpath); | ||||||
| close(fd); | ||||||
| if (rc != 0) | ||||||
| return (-1); | ||||||
|
|
||||||
| (void) strcpy(dataset, fullpath); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| /* | ||||||
| * fullpath contains string like 'tank/zvol'. Strip off the | ||||||
| * 'tank' and 'zvol' parts. | ||||||
| */ | ||||||
| slash = strchr(fullpath, '/'); | ||||||
| *slash = '\0'; | ||||||
| (void) strcpy(relpath, slash + 1); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should check for NULL here to handle a somehow malformed full dataset name. How about this (untested). /*
* fullpath contains string like 'tank/zvol'. Strip off the
* leading 'tank' pool name component.
*/
slash = strchr(fullpath, '/');
if (slash == NULL) {
(void) fprintf(stderr, "invalid volume name: '%s'\n",
fullpath);
return (-1);
}
(void) strlcpy(relpath, slash + 1, MAXPATHLEN);
return (0); |
||||||
| return (0); | ||||||
| } | ||||||
|
|
||||||
| if (getextmntent(fullpath, &mp, statbuf) != 0) { | ||||||
| (void) fprintf(stderr, "cannot find mountpoint for '%s'\n", | ||||||
| fullpath); | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1050,6 +1050,9 @@ nice_num_str_nvlist(nvlist_t *item, const char *key, uint64_t value, | |||||
| case ZFS_NICENUM_BYTES: | ||||||
| zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_BYTES); | ||||||
| break; | ||||||
| case ZFS_NICENUM_RAW: | ||||||
| zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_RAW); | ||||||
| break; | ||||||
| case ZFS_NICENUM_TIME: | ||||||
| zfs_nicenum_format(value, buf, 256, ZFS_NICENUM_TIME); | ||||||
| break; | ||||||
|
|
@@ -2590,7 +2593,7 @@ typedef struct status_cbdata { | |||||
| int cb_name_flags; | ||||||
| int cb_namewidth; | ||||||
| boolean_t cb_allpools; | ||||||
| boolean_t cb_verbose; | ||||||
| int cb_verbosity; | ||||||
| boolean_t cb_literal; | ||||||
| boolean_t cb_explain; | ||||||
| boolean_t cb_first; | ||||||
|
|
@@ -3322,7 +3325,7 @@ print_class_vdevs(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv, | |||||
| nvlist_t **child; | ||||||
| boolean_t printed = B_FALSE; | ||||||
|
|
||||||
| assert(zhp != NULL || !cb->cb_verbose); | ||||||
| assert(zhp != NULL || cb->cb_verbosity == 0); | ||||||
|
|
||||||
| if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, | ||||||
| &children) != 0) | ||||||
|
|
@@ -9516,7 +9519,7 @@ class_vdevs_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv, | |||||
| if (!cb->cb_flat_vdevs) | ||||||
| class_obj = fnvlist_alloc(); | ||||||
|
|
||||||
| assert(zhp != NULL || !cb->cb_verbose); | ||||||
| assert(zhp != NULL || cb->cb_verbosity == 0); | ||||||
|
|
||||||
| if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, | ||||||
| &children) != 0) | ||||||
|
|
@@ -9620,57 +9623,96 @@ spares_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv, | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /* | ||||||
| * Take a uint64 nvpair named 'name' from nverrlist, nicenum-ify it, and | ||||||
| * put it back in 'nverrlist', possibly as a string, with the same 'name'. | ||||||
| */ | ||||||
| static void | ||||||
| convert_nvlist_uint64_to_nicenum(status_cbdata_t *cb, nvlist_t *parent, | ||||||
| const char *name, enum zfs_nicenum_format format) | ||||||
| { | ||||||
| uint64_t val; | ||||||
| nvpair_t *nvp; | ||||||
|
|
||||||
| if (nvlist_lookup_nvpair(parent, name, &nvp) != 0) | ||||||
| return; /* nothing by that name, ignore */ | ||||||
|
|
||||||
| val = fnvpair_value_uint64(nvp); | ||||||
| nvlist_remove_nvpair(parent, nvp); | ||||||
| nice_num_str_nvlist(parent, name, val, | ||||||
| cb->cb_literal, cb->cb_json_as_int, format); | ||||||
| } | ||||||
|
|
||||||
| static void | ||||||
| errors_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *item) | ||||||
| { | ||||||
| uint64_t nerr; | ||||||
| nvlist_t *config = zpool_get_config(zhp, NULL); | ||||||
| if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT, | ||||||
| &nerr) == 0) { | ||||||
| nice_num_str_nvlist(item, ZPOOL_CONFIG_ERRCOUNT, nerr, | ||||||
| cb->cb_literal, cb->cb_json_as_int, ZFS_NICENUM_1024); | ||||||
| if (nerr != 0 && cb->cb_verbose) { | ||||||
| nvlist_t *nverrlist = NULL; | ||||||
| if (zpool_get_errlog(zhp, &nverrlist) == 0) { | ||||||
| int i = 0; | ||||||
| int count = 0; | ||||||
| size_t len = MAXPATHLEN * 2; | ||||||
| nvpair_t *elem = NULL; | ||||||
|
|
||||||
| for (nvpair_t *pair = | ||||||
| nvlist_next_nvpair(nverrlist, NULL); | ||||||
| pair != NULL; | ||||||
| pair = nvlist_next_nvpair(nverrlist, pair)) | ||||||
| count++; | ||||||
| char **errl = (char **)malloc( | ||||||
| count * sizeof (char *)); | ||||||
|
|
||||||
| while ((elem = nvlist_next_nvpair(nverrlist, | ||||||
| elem)) != NULL) { | ||||||
| nvlist_t *nv; | ||||||
| uint64_t dsobj, obj; | ||||||
|
|
||||||
| verify(nvpair_value_nvlist(elem, | ||||||
| &nv) == 0); | ||||||
| verify(nvlist_lookup_uint64(nv, | ||||||
| ZPOOL_ERR_DATASET, &dsobj) == 0); | ||||||
| verify(nvlist_lookup_uint64(nv, | ||||||
| ZPOOL_ERR_OBJECT, &obj) == 0); | ||||||
| errl[i] = safe_malloc(len); | ||||||
| zpool_obj_to_path(zhp, dsobj, obj, | ||||||
| errl[i++], len); | ||||||
| } | ||||||
| nvlist_free(nverrlist); | ||||||
| fnvlist_add_string_array(item, "errlist", | ||||||
| (const char **)errl, count); | ||||||
| for (int i = 0; i < count; ++i) | ||||||
| free(errl[i]); | ||||||
| free(errl); | ||||||
| } else | ||||||
| fnvlist_add_string(item, "errlist", | ||||||
| strerror(errno)); | ||||||
| int verbosity = cb->cb_verbosity; | ||||||
| nvlist_t *nverrlist = NULL, *json; | ||||||
| nvpair_t *elem; | ||||||
| char *pathname; | ||||||
| size_t len = MAXPATHLEN * 2; | ||||||
| nvlist_t **ranges; | ||||||
| uint_t count; | ||||||
|
|
||||||
| if (zpool_get_errlog(zhp, &nverrlist) != 0) | ||||||
| return; | ||||||
|
|
||||||
| pathname = safe_malloc(len); | ||||||
| json = fnvlist_alloc(); | ||||||
|
|
||||||
| elem = NULL; | ||||||
| while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) { | ||||||
| nvlist_t *nv; | ||||||
| uint64_t dsobj, obj; | ||||||
|
|
||||||
| verify(nvpair_value_nvlist(elem, &nv) == 0); | ||||||
|
|
||||||
| dsobj = fnvlist_lookup_uint64(nv, ZPOOL_ERR_DATASET); | ||||||
| obj = fnvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT); | ||||||
|
|
||||||
| zpool_obj_to_path(zhp, dsobj, obj, pathname, len); | ||||||
|
|
||||||
| /* | ||||||
| * Each JSON entry is a different file/zvol. If user has | ||||||
| * verbosity = 1, then just make a simple object containing | ||||||
| * the name. | ||||||
| */ | ||||||
| if (verbosity <= 1) { | ||||||
| nvlist_t *nameonly; | ||||||
| nameonly = fnvlist_alloc(); | ||||||
| fnvlist_add_string(nameonly, ZPOOL_ERR_NAME, pathname); | ||||||
| fnvlist_add_nvlist(json, pathname, nameonly); | ||||||
| nvlist_free(nameonly); | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| fnvlist_add_string(nv, ZPOOL_ERR_NAME, pathname); | ||||||
|
|
||||||
| /* nicenum-ify our nvlist */ | ||||||
| convert_nvlist_uint64_to_nicenum(cb, nv, ZPOOL_ERR_OBJECT, | ||||||
| ZFS_NICENUM_RAW); | ||||||
| convert_nvlist_uint64_to_nicenum(cb, nv, ZPOOL_ERR_DATASET, | ||||||
| ZFS_NICENUM_RAW); | ||||||
| convert_nvlist_uint64_to_nicenum(cb, nv, ZPOOL_ERR_BLOCK_SIZE, | ||||||
| ZFS_NICENUM_1024); | ||||||
|
|
||||||
| if (nvlist_lookup_nvlist_array(nv, ZPOOL_ERR_RANGES, &ranges, | ||||||
| &count) == 0) { | ||||||
| for (uint_t i = 0; i < count; i++) { | ||||||
| convert_nvlist_uint64_to_nicenum(cb, ranges[i], | ||||||
| ZPOOL_ERR_START_BYTE, ZFS_NICENUM_1024); | ||||||
| convert_nvlist_uint64_to_nicenum(cb, ranges[i], | ||||||
| ZPOOL_ERR_END_BYTE, ZFS_NICENUM_1024); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| fnvlist_add_nvlist(json, pathname, nv); | ||||||
| } | ||||||
|
|
||||||
| /* Place our error list in a top level "errors" JSON object. */ | ||||||
| fnvlist_add_nvlist(item, ZPOOL_ERR_JSON, json); | ||||||
| free(pathname); | ||||||
| nvlist_free(nverrlist); | ||||||
| } | ||||||
|
|
||||||
| static void | ||||||
|
|
@@ -10341,13 +10383,15 @@ print_checkpoint_status(pool_checkpoint_stat_t *pcs) | |||||
| space_buf); | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: stray extra newline |
||||||
| static void | ||||||
| print_error_log(zpool_handle_t *zhp) | ||||||
| print_error_log(zpool_handle_t *zhp, int verbosity, boolean_t literal) | ||||||
| { | ||||||
| nvlist_t *nverrlist = NULL; | ||||||
| nvpair_t *elem; | ||||||
| char *pathname; | ||||||
| char *pathname, *last_pathname = NULL; | ||||||
| size_t len = MAXPATHLEN * 2; | ||||||
| boolean_t started = B_FALSE; | ||||||
|
|
||||||
| if (zpool_get_errlog(zhp, &nverrlist) != 0) | ||||||
| return; | ||||||
|
|
@@ -10367,9 +10411,49 @@ print_error_log(zpool_handle_t *zhp) | |||||
| verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT, | ||||||
| &obj) == 0); | ||||||
| zpool_obj_to_path(zhp, dsobj, obj, pathname, len); | ||||||
| (void) printf("%7s %s\n", "", pathname); | ||||||
| if (last_pathname == NULL || | ||||||
| 0 != strncmp(pathname, last_pathname, len)) { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Purely for consistency we've tried to avoid the |
||||||
| last_pathname = strdup(pathname); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be leaked when the pathname changes. |
||||||
| if (started) | ||||||
| (void) printf("\n"); | ||||||
| else | ||||||
| started = B_TRUE; | ||||||
| (void) printf("%7s %s ", "", pathname); | ||||||
| } else if (verbosity > 1) { | ||||||
| (void) printf(","); | ||||||
| } | ||||||
| if (verbosity > 1) { | ||||||
| nvlist_t **arr; | ||||||
| uint_t count; | ||||||
| if (nvlist_lookup_nvlist_array(nv, ZPOOL_ERR_RANGES, | ||||||
| &arr, &count) != 0) { | ||||||
| printf("(no ranges)"); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| for (uint_t i = 0; i < count; i++) { | ||||||
| uint64_t start; | ||||||
| uint64_t end; | ||||||
| start = fnvlist_lookup_uint64(arr[i], | ||||||
| ZPOOL_ERR_START_BYTE); | ||||||
| end = fnvlist_lookup_uint64(arr[i], | ||||||
| ZPOOL_ERR_END_BYTE); | ||||||
| if (literal) { | ||||||
| (void) printf("%lu-%lu", start, end); | ||||||
| } else { | ||||||
| char s1[32], s2[32]; | ||||||
| zfs_nicenum(start, s1, sizeof (s1)); | ||||||
| zfs_nicenum(end, s2, sizeof (s2)); | ||||||
| (void) printf("%s-%s", s1, s2); | ||||||
| } | ||||||
| if (i != count - 1) | ||||||
| printf(","); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
| } | ||||||
| } | ||||||
| (void) printf("\n"); | ||||||
| free(pathname); | ||||||
| free(last_pathname); | ||||||
| nvlist_free(nverrlist); | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -11065,14 +11149,15 @@ status_callback(zpool_handle_t *zhp, void *data) | |||||
| if (nerr == 0) { | ||||||
| (void) printf(gettext( | ||||||
| "errors: No known data errors\n")); | ||||||
| } else if (!cbp->cb_verbose) { | ||||||
| } else if (0 == cbp->cb_verbosity) { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| color_start(ANSI_RED); | ||||||
| (void) printf(gettext("errors: %llu data " | ||||||
| "errors, use '-v' for a list\n"), | ||||||
| (u_longlong_t)nerr); | ||||||
| color_end(); | ||||||
| } else { | ||||||
| print_error_log(zhp); | ||||||
| print_error_log(zhp, cbp->cb_verbosity, | ||||||
| cbp->cb_literal); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -11199,7 +11284,7 @@ zpool_do_status(int argc, char **argv) | |||||
| get_timestamp_arg(*optarg); | ||||||
| break; | ||||||
| case 'v': | ||||||
| cb.cb_verbose = B_TRUE; | ||||||
| cb.cb_verbosity++; | ||||||
| break; | ||||||
| case 'j': | ||||||
| cb.cb_json = B_TRUE; | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.