Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 65 additions & 9 deletions cmd/zpool/zpool_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7349,16 +7349,72 @@ print_error_log(zpool_handle_t *zhp)
pathname = safe_malloc(len);
elem = NULL;
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);
uint64_t data_block_size, indirect_block_size;
unsigned int error_count_blkids, error_count_levels;
uint64_t *block_ids;
int64_t *indirect_levels;

nvlist_t *object_nv = fnvpair_value_nvlist(elem);
uint64_t dsobj = fnvlist_lookup_uint64(object_nv,
ZPOOL_ERR_DATASET);
uint64_t obj = fnvlist_lookup_uint64(object_nv,
ZPOOL_ERR_OBJECT);
zpool_obj_to_path(zhp, dsobj, obj, pathname, len);
(void) printf("%7s %s\n", "", pathname);

if (zpool_get_block_size(zhp, dsobj, obj, &data_block_size,
&indirect_block_size) == 0 && nvlist_lookup_uint64_array(
object_nv, ZPOOL_ERR_BLOCKID, &block_ids,
&error_count_blkids) == 0 && nvlist_lookup_int64_array(
object_nv, ZPOOL_ERR_LEVEL, &indirect_levels,
&error_count_levels) == 0) {

ASSERT(error_count_blkids == error_count_levels);

uint64_t blkptr_size = (uint64_t)sizeof (blkptr_t);
uint8_t blkptr_size_shift = 0;
uint8_t indirect_block_shift = 0;
uint64_t min_offset_blk = UINT64_MAX;
uint64_t max_offset_blk = 0;
while (indirect_block_size > 1) {
indirect_block_size = indirect_block_size >> 1;
indirect_block_shift++;
}

while (blkptr_size > 1) {
blkptr_size = blkptr_size >> 1;
blkptr_size_shift++;
}
/*
* Iterate through the error blockids and find minimum
* and maximum offset.
*/
for (int i = 0; i < error_count_blkids; i++) {
uint64_t start_offset = block_ids[i] <<
((indirect_block_shift -
blkptr_size_shift) * indirect_levels[i]);
uint64_t end_offset = (block_ids[i] + 1) <<
((indirect_block_shift -
blkptr_size_shift) * indirect_levels[i]);
min_offset_blk =
MIN(min_offset_blk, start_offset);
max_offset_blk =
MAX(max_offset_blk, end_offset);
}
uint64_t min_offset_byte =
data_block_size * min_offset_blk;
uint64_t max_offset_byte =
data_block_size * max_offset_blk;
char size_buf[16];
nicenum(data_block_size, size_buf, sizeof (size_buf));
(void) printf("%7s %s: errors in %u blocks "
"(size %s), between offset %#llx and %#llx "
"bytes\n", "", pathname, error_count_blkids,
size_buf, (u_longlong_t)min_offset_byte,
(u_longlong_t)max_offset_byte);
} else {
(void) printf("%7s %s %s\n", "", pathname, " (can not "
"determine error offset)");
}
}
free(pathname);
nvlist_free(nverrlist);
Expand Down
2 changes: 2 additions & 0 deletions include/libzfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,8 @@ extern int zpool_events_clear(libzfs_handle_t *, int *);
extern int zpool_events_seek(libzfs_handle_t *, uint64_t, int);
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
size_t len);
extern int zpool_get_block_size(zpool_handle_t *, uint64_t, uint64_t,
uint64_t *, uint64_t *);
extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *);
extern int zpool_get_physpath(zpool_handle_t *, char *, size_t);
extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
Expand Down
3 changes: 3 additions & 0 deletions include/sys/fs/zfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ typedef struct zpool_load_policy {
#define ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH "vdev_enc_sysfs_path"

#define ZPOOL_CONFIG_WHOLE_DISK "whole_disk"
#define ZPOOL_CONFIG_ERRLIST "error_list"
#define ZPOOL_CONFIG_ERRCOUNT "error_count"
#define ZPOOL_CONFIG_NOT_PRESENT "not_present"
#define ZPOOL_CONFIG_SPARES "spares"
Expand Down Expand Up @@ -1359,6 +1360,8 @@ typedef enum {
#define ZPOOL_ERR_LIST "error list"
#define ZPOOL_ERR_DATASET "dataset"
#define ZPOOL_ERR_OBJECT "object"
#define ZPOOL_ERR_LEVEL "level"
#define ZPOOL_ERR_BLOCKID "block id"

#define HIS_MAX_RECORD_LEN (MAXPATHLEN + MAXPATHLEN + 1)

Expand Down
2 changes: 2 additions & 0 deletions include/sys/zfs_stat.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ typedef struct zfs_stat {
uint64_t zs_mode;
uint64_t zs_links;
uint64_t zs_ctime[2];
uint64_t zs_data_block_size;
uint64_t zs_indirect_block_size;
} zfs_stat_t;

extern int zfs_obj_to_stats(objset_t *osp, uint64_t obj, zfs_stat_t *sb,
Expand Down
137 changes: 114 additions & 23 deletions lib/libzfs/libzfs_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -3956,9 +3956,33 @@ zbookmark_mem_compare(const void *a, const void *b)
return (memcmp(a, b, sizeof (zbookmark_phys_t)));
}

/*
* Either adds both the arrays in the nvlist or none.
*/
static void
add_error_block_ids_and_levels(nvlist_t *object_nv, uint64_t *object_block_ids,
int64_t *object_indirect_levels, uint64_t object_errors)
{
int err = nvlist_add_uint64_array(object_nv, ZPOOL_ERR_BLOCKID,
object_block_ids, object_errors);

if (err != 0)
return;

err = nvlist_add_int64_array(object_nv, ZPOOL_ERR_LEVEL,
object_indirect_levels, object_errors);

if (err != 0) {
nvlist_remove(object_nv, ZPOOL_ERR_BLOCKID,
DATA_TYPE_UINT64_ARRAY);
}
}

/*
* Retrieve the persistent error log, uniquify the members, and return to the
* caller.
* caller. Return nvlist_t is a list of corrupted objects. Each entity in
* nvlist_t contains four elements: dataset number, object number, an array of
* corrupted blockids and another array of corresponding level.
*/
int
zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
Expand All @@ -3967,7 +3991,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
libzfs_handle_t *hdl = zhp->zpool_hdl;
uint64_t count;
zbookmark_phys_t *zb = NULL;
int i;
uint64_t i;

/*
* Retrieve the raw error list from the kernel. If the number of errors
Expand Down Expand Up @@ -4019,39 +4043,79 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0);

/*
* Fill in the nverrlistp with nvlist's of dataset and object numbers.
* Fill in the nverrlistp with nvlist's of dataset number, object number
* level position, and block id.
*/
nvlist_t *object_nv;
boolean_t mem_alloc_failed = B_FALSE;

uint64_t *object_block_ids = zfs_alloc(zhp->zpool_hdl,
count * sizeof (uint64_t));
int64_t *object_indirect_levels = zfs_alloc(zhp->zpool_hdl,
count * sizeof (int64_t));
if (object_block_ids == NULL || object_indirect_levels == NULL) {
mem_alloc_failed = B_TRUE;
}
uint64_t object_errors = 0;
for (i = 0; i < count; i++) {
nvlist_t *nv;
if (i == 0 || zb[i - 1].zb_objset != zb[i].zb_objset ||
zb[i - 1].zb_object != zb[i].zb_object) {
if (i != 0) {
/* If no memory is available do not add this. */
if (!mem_alloc_failed) {
add_error_block_ids_and_levels(
object_nv,
object_block_ids,
object_indirect_levels,
object_errors);
}

/* ignoring zb_blkid and zb_level for now */
if (i > 0 && zb[i-1].zb_objset == zb[i].zb_objset &&
zb[i-1].zb_object == zb[i].zb_object)
continue;
if (nvlist_add_nvlist(*nverrlistp,
ZPOOL_CONFIG_ERRLIST, object_nv) != 0)
goto nomem;
nvlist_free(object_nv);
object_errors = 0;
}

if (nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) != 0)
goto nomem;
if (nvlist_add_uint64(nv, ZPOOL_ERR_DATASET,
zb[i].zb_objset) != 0) {
nvlist_free(nv);
goto nomem;
if (nvlist_alloc(&object_nv, NV_UNIQUE_NAME,
KM_SLEEP) != 0)
goto nomem;
if (nvlist_add_uint64(object_nv, ZPOOL_ERR_DATASET,
zb[i].zb_objset) != 0)
goto nomem;
if (nvlist_add_uint64(object_nv, ZPOOL_ERR_OBJECT,
zb[i].zb_object) != 0)
goto nomem;
}
if (nvlist_add_uint64(nv, ZPOOL_ERR_OBJECT,
zb[i].zb_object) != 0) {
nvlist_free(nv);
goto nomem;
object_errors++;
if (mem_alloc_failed)
continue;
object_block_ids[object_errors] = zb[i].zb_blkid;
object_indirect_levels[object_errors] = zb[i].zb_level;
}
if (object_errors > 0) {
/* If no memory is available do not add this. */
if (!mem_alloc_failed) {
add_error_block_ids_and_levels(object_nv,
object_block_ids, object_indirect_levels,
object_errors);
}
if (nvlist_add_nvlist(*nverrlistp, "ejk", nv) != 0) {
nvlist_free(nv);

if (nvlist_add_nvlist(*nverrlistp, ZPOOL_CONFIG_ERRLIST,
object_nv) != 0)
goto nomem;
}
nvlist_free(nv);
nvlist_free(object_nv);
object_errors = 0;
}

free(object_block_ids);
free(object_indirect_levels);
free((void *)(uintptr_t)zc.zc_nvlist_dst);
return (0);

nomem:
nvlist_free(object_nv);
free(object_block_ids);
free(object_indirect_levels);
free((void *)(uintptr_t)zc.zc_nvlist_dst);
return (no_memory(zhp->zpool_hdl));
}
Expand Down Expand Up @@ -4390,6 +4454,33 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
free(mntpnt);
}

/*
* Given an dataset object number, return data block and indirect block size.
*/
int
zpool_get_block_size(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
uint64_t *data_blk_size, uint64_t *indrt_blk_size)
{
zfs_cmd_t zc = {"\0"};
/* get the dataset's name */
zc.zc_obj = dsobj;
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
int error = ioctl(zhp->zpool_hdl->libzfs_fd,
ZFS_IOC_DSOBJ_TO_DSNAME, &zc);
if (error != 0) {
return (error);
}
/* get data block and indirect block size */
(void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name));
zc.zc_obj = obj;
error = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_STATS, &zc);
if (error == 0) {
*data_blk_size = zc.zc_stat.zs_data_block_size;
*indrt_blk_size = zc.zc_stat.zs_indirect_block_size;
}
return (error);
}

/*
* Wait while the specified activity is in progress in the pool.
*/
Expand Down
5 changes: 5 additions & 0 deletions module/os/linux/zfs/zfs_znode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2087,6 +2087,11 @@ zfs_obj_to_stats_impl(sa_handle_t *hdl, sa_attr_type_t *sa_table,
sa_bulk_attr_t bulk[4];
int count = 0;

dmu_object_info_t doi;
sa_object_info(hdl, &doi);
sb->zs_data_block_size = doi.doi_data_block_size;
sb->zs_indirect_block_size = doi.doi_metadata_block_size;

SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL,
&sb->zs_mode, sizeof (sb->zs_mode));
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL,
Expand Down
2 changes: 1 addition & 1 deletion tests/runfiles/common.run
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ tests = ['zpool_split_cliargs', 'zpool_split_devices',
tags = ['functional', 'cli_root', 'zpool_split']

[tests/functional/cli_root/zpool_status]
tests = ['zpool_status_001_pos', 'zpool_status_002_pos']
tests = ['zpool_status_001_pos', 'zpool_status_002_pos', 'zpool_status_-v']
tags = ['functional', 'cli_root', 'zpool_status']

[tests/functional/cli_root/zpool_sync]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ dist_pkgdata_SCRIPTS = \
setup.ksh \
cleanup.ksh \
zpool_status_001_pos.ksh \
zpool_status_002_pos.ksh
zpool_status_002_pos.ksh \
zpool_status_-v.ksh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Use is subject to license terms.
#

#
# Copyright (c) 2019 by Delphix. All rights reserved.
#

. $STF_SUITE/include/libtest.shlib

#
# DESCRIPTION:
# Verify correct output with 'zpool status -v' after corrupting a file
#
# STRATEGY:
# 1. Create a file
# 2. zinject checksum errors
# 3. Read the file
# 4. Verify we see "file corrupted" output in 'zpool status -v'
#

verify_runnable "both"


log_assert "Verify correct 'zpool status -v' output with a corrupted file"

log_must mkfile 10m $TESTDIR/10m_file
log_must mkfile 1m $TESTDIR/1m_file

log_must zpool export $TESTPOOL
log_must zpool import $TESTPOOL

log_must zinject -t data -e checksum -f 100 $TESTDIR/10m_file
log_must zinject -t data -e checksum -f 100 $TESTDIR/1m_file

# Try to read the entire file. This should stop after the first 128k block
# of each file errors out.
cat $TESTDIR/*file || true

# Try to read the 2nd megabyte of 10m_file
dd if=$TESTDIR/10m_file bs=1M skip=1 count=1 || true
dd if=$TESTDIR/1m_file bs=128K count=1 || true

log_must zinject -c all

# Look to see that both our files report errors
log_must eval "zpool status -v | grep '10m_file: errors'"
log_must eval "zpool status -v | grep '1m_file: errors'"

log_pass "'zpool status -v' output is correct"