Fix zdb -R with 'b' flag

zdb -R :b fails due to the indirect block being compressed,
and the 'b' and 'd' flag not working in tandem when specified.
Fix the flag parsing code and create a zfs test for zdb -R
block display.  Also fix the zio flags where the dotted notation
for the vdev portion of DVA (i.e. 0.0:offset:length) fails.

Reviewed-by: Ryan Moeller <ryan@iXsystems.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Zuchowski <pzuchowski@datto.com>
Closes #9640
Closes #9729
This commit is contained in:
Paul Zuchowski 2020-02-10 18:00:05 -04:00 committed by GitHub
parent dda702fd16
commit bc67cba7c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 376 additions and 117 deletions

View File

@ -6410,6 +6410,18 @@ dump_zpool(spa_t *spa)
} }
} }
#define ZDB_FLAG_CHECKSUM 0x0001
#define ZDB_FLAG_DECOMPRESS 0x0002
#define ZDB_FLAG_BSWAP 0x0004
#define ZDB_FLAG_GBH 0x0008
#define ZDB_FLAG_INDIRECT 0x0010
#define ZDB_FLAG_RAW 0x0020
#define ZDB_FLAG_PRINT_BLKPTR 0x0040
#define ZDB_FLAG_VERBOSE 0x0080
static int flagbits[256];
static char flagbitstr[16];
static void static void
zdb_print_blkptr(blkptr_t *bp, int flags) zdb_print_blkptr(blkptr_t *bp, int flags)
{ {
@ -6576,6 +6588,83 @@ zdb_parse_block_sizes(char *sizes, uint64_t *lsize, uint64_t *psize)
#define ZIO_COMPRESS_MASK(alg) (1ULL << (ZIO_COMPRESS_##alg)) #define ZIO_COMPRESS_MASK(alg) (1ULL << (ZIO_COMPRESS_##alg))
static boolean_t
zdb_decompress_block(abd_t *pabd, void *buf, void *lbuf, uint64_t lsize,
uint64_t psize, int flags)
{
boolean_t exceeded = B_FALSE;
/*
* We don't know how the data was compressed, so just try
* every decompress function at every inflated blocksize.
*/
void *lbuf2 = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
int cfuncs[ZIO_COMPRESS_FUNCTIONS] = { 0 };
int *cfuncp = cfuncs;
uint64_t maxlsize = SPA_MAXBLOCKSIZE;
uint64_t mask = ZIO_COMPRESS_MASK(ON) | ZIO_COMPRESS_MASK(OFF) |
ZIO_COMPRESS_MASK(INHERIT) | ZIO_COMPRESS_MASK(EMPTY) |
(getenv("ZDB_NO_ZLE") ? ZIO_COMPRESS_MASK(ZLE) : 0);
*cfuncp++ = ZIO_COMPRESS_LZ4;
*cfuncp++ = ZIO_COMPRESS_LZJB;
mask |= ZIO_COMPRESS_MASK(LZ4) | ZIO_COMPRESS_MASK(LZJB);
for (int c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++)
if (((1ULL << c) & mask) == 0)
*cfuncp++ = c;
/*
* On the one hand, with SPA_MAXBLOCKSIZE at 16MB, this
* could take a while and we should let the user know
* we are not stuck. On the other hand, printing progress
* info gets old after a while. User can specify 'v' flag
* to see the progression.
*/
if (lsize == psize)
lsize += SPA_MINBLOCKSIZE;
else
maxlsize = lsize;
for (; lsize <= maxlsize; lsize += SPA_MINBLOCKSIZE) {
for (cfuncp = cfuncs; *cfuncp; cfuncp++) {
if (flags & ZDB_FLAG_VERBOSE) {
(void) fprintf(stderr,
"Trying %05llx -> %05llx (%s)\n",
(u_longlong_t)psize,
(u_longlong_t)lsize,
zio_compress_table[*cfuncp].\
ci_name);
}
/*
* We randomize lbuf2, and decompress to both
* lbuf and lbuf2. This way, we will know if
* decompression fill exactly to lsize.
*/
VERIFY0(random_get_pseudo_bytes(lbuf2, lsize));
if (zio_decompress_data(*cfuncp, pabd,
lbuf, psize, lsize) == 0 &&
zio_decompress_data(*cfuncp, pabd,
lbuf2, psize, lsize) == 0 &&
bcmp(lbuf, lbuf2, lsize) == 0)
break;
}
if (*cfuncp != 0)
break;
}
umem_free(lbuf2, SPA_MAXBLOCKSIZE);
if (lsize > maxlsize) {
exceeded = B_TRUE;
}
buf = lbuf;
if (*cfuncp == ZIO_COMPRESS_ZLE) {
printf("\nZLE decompression was selected. If you "
"suspect the results are wrong,\ntry avoiding ZLE "
"by setting and exporting ZDB_NO_ZLE=\"true\"\n");
}
return (exceeded);
}
/* /*
* Read a block from a pool and print it out. The syntax of the * Read a block from a pool and print it out. The syntax of the
* block descriptor is: * block descriptor is:
@ -6610,7 +6699,7 @@ zdb_read_block(char *thing, spa_t *spa)
void *lbuf, *buf; void *lbuf, *buf;
char *s, *p, *dup, *vdev, *flagstr, *sizes; char *s, *p, *dup, *vdev, *flagstr, *sizes;
int i, error; int i, error;
boolean_t borrowed = B_FALSE; boolean_t borrowed = B_FALSE, found = B_FALSE;
dup = strdup(thing); dup = strdup(thing);
s = strtok(dup, ":"); s = strtok(dup, ":");
@ -6630,41 +6719,57 @@ zdb_read_block(char *thing, spa_t *spa)
s = "offset must be a multiple of sector size"; s = "offset must be a multiple of sector size";
if (s) { if (s) {
(void) printf("Invalid block specifier: %s - %s\n", thing, s); (void) printf("Invalid block specifier: %s - %s\n", thing, s);
free(flagstr); goto done;
free(dup);
return;
} }
for (s = strtok(flagstr, ":"); s; s = strtok(NULL, ":")) { for (s = strtok(flagstr, ":"); s; s = strtok(NULL, ":")) {
for (i = 0; flagstr[i]; i++) { for (i = 0; i < strlen(flagstr); i++) {
int bit = flagbits[(uchar_t)flagstr[i]]; int bit = flagbits[(uchar_t)flagstr[i]];
if (bit == 0) { if (bit == 0) {
(void) printf("***Invalid flag: %c\n", (void) printf("***Ignoring flag: %c\n",
flagstr[i]); (uchar_t)flagstr[i]);
continue; continue;
} }
found = B_TRUE;
flags |= bit; flags |= bit;
/* If it's not something with an argument, keep going */
if ((bit & (ZDB_FLAG_CHECKSUM |
ZDB_FLAG_PRINT_BLKPTR)) == 0)
continue;
p = &flagstr[i + 1]; p = &flagstr[i + 1];
if (bit == ZDB_FLAG_PRINT_BLKPTR) {
blkptr_offset = strtoull(p, &p, 16);
i = p - &flagstr[i + 1];
}
if (*p != ':' && *p != '\0') { if (*p != ':' && *p != '\0') {
(void) printf("***Invalid flag arg: '%s'\n", s); int j = 0, nextbit = flagbits[(uchar_t)*p];
free(flagstr); char *end, offstr[8] = { 0 };
free(dup); if ((bit == ZDB_FLAG_PRINT_BLKPTR) &&
return; (nextbit == 0)) {
/* look ahead to isolate the offset */
while (nextbit == 0 &&
strchr(flagbitstr, *p) == NULL) {
offstr[j] = *p;
j++;
if (i + j > strlen(flagstr))
break;
p++;
nextbit = flagbits[(uchar_t)*p];
}
blkptr_offset = strtoull(offstr, &end,
16);
i += j;
} else if (nextbit == 0) {
(void) printf("***Ignoring flag arg:"
" '%c'\n", (uchar_t)*p);
}
} }
} }
} }
free(flagstr); if (blkptr_offset % sizeof (blkptr_t)) {
printf("Block pointer offset 0x%llx "
"must be divisible by 0x%x\n",
(longlong_t)blkptr_offset, (int)sizeof (blkptr_t));
goto done;
}
if (found == B_FALSE && strlen(flagstr) > 0) {
printf("Invalid flag arg: '%s'\n", flagstr);
goto done;
}
vd = zdb_vdev_lookup(spa->spa_root_vdev, vdev); vd = zdb_vdev_lookup(spa->spa_root_vdev, vdev);
if (vd == NULL) { if (vd == NULL) {
@ -6717,10 +6822,9 @@ zdb_read_block(char *thing, spa_t *spa)
*/ */
zio_nowait(zio_vdev_child_io(zio, bp, vd, offset, pabd, zio_nowait(zio_vdev_child_io(zio, bp, vd, offset, pabd,
psize, ZIO_TYPE_READ, ZIO_PRIORITY_SYNC_READ, psize, ZIO_TYPE_READ, ZIO_PRIORITY_SYNC_READ,
ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_QUEUE | ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_PROPAGATE |
ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY | ZIO_FLAG_DONT_RETRY | ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW |
ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW | ZIO_FLAG_OPTIONAL, ZIO_FLAG_OPTIONAL, NULL, NULL));
NULL, NULL));
} }
error = zio_wait(zio); error = zio_wait(zio);
@ -6731,80 +6835,43 @@ zdb_read_block(char *thing, spa_t *spa)
goto out; goto out;
} }
uint64_t orig_lsize = lsize;
buf = lbuf;
if (flags & ZDB_FLAG_DECOMPRESS) { if (flags & ZDB_FLAG_DECOMPRESS) {
/* boolean_t failed = zdb_decompress_block(pabd, buf, lbuf,
* We don't know how the data was compressed, so just try lsize, psize, flags);
* every decompress function at every inflated blocksize. if (failed) {
*/
void *lbuf2 = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
int cfuncs[ZIO_COMPRESS_FUNCTIONS] = { 0 };
int *cfuncp = cfuncs;
uint64_t maxlsize = SPA_MAXBLOCKSIZE;
uint64_t mask = ZIO_COMPRESS_MASK(ON) | ZIO_COMPRESS_MASK(OFF) |
ZIO_COMPRESS_MASK(INHERIT) | ZIO_COMPRESS_MASK(EMPTY) |
(getenv("ZDB_NO_ZLE") ? ZIO_COMPRESS_MASK(ZLE) : 0);
*cfuncp++ = ZIO_COMPRESS_LZ4;
*cfuncp++ = ZIO_COMPRESS_LZJB;
mask |= ZIO_COMPRESS_MASK(LZ4) | ZIO_COMPRESS_MASK(LZJB);
for (int c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++)
if (((1ULL << c) & mask) == 0)
*cfuncp++ = c;
/*
* On the one hand, with SPA_MAXBLOCKSIZE at 16MB, this
* could take a while and we should let the user know
* we are not stuck. On the other hand, printing progress
* info gets old after a while. User can specify 'v' flag
* to see the progression.
*/
if (lsize == psize)
lsize += SPA_MINBLOCKSIZE;
else
maxlsize = lsize;
for (; lsize <= maxlsize; lsize += SPA_MINBLOCKSIZE) {
for (cfuncp = cfuncs; *cfuncp; cfuncp++) {
if (flags & ZDB_FLAG_VERBOSE) {
(void) fprintf(stderr,
"Trying %05llx -> %05llx (%s)\n",
(u_longlong_t)psize,
(u_longlong_t)lsize,
zio_compress_table[*cfuncp].\
ci_name);
}
/*
* We randomize lbuf2, and decompress to both
* lbuf and lbuf2. This way, we will know if
* decompression fill exactly to lsize.
*/
VERIFY0(random_get_pseudo_bytes(lbuf2, lsize));
if (zio_decompress_data(*cfuncp, pabd,
lbuf, psize, lsize) == 0 &&
zio_decompress_data(*cfuncp, pabd,
lbuf2, psize, lsize) == 0 &&
bcmp(lbuf, lbuf2, lsize) == 0)
break;
}
if (*cfuncp != 0)
break;
}
umem_free(lbuf2, SPA_MAXBLOCKSIZE);
if (lsize > maxlsize) {
(void) printf("Decompress of %s failed\n", thing); (void) printf("Decompress of %s failed\n", thing);
goto out; goto out;
} }
buf = lbuf;
if (*cfuncp == ZIO_COMPRESS_ZLE) {
printf("\nZLE decompression was selected. If you "
"suspect the results are wrong,\ntry avoiding ZLE "
"by setting and exporting ZDB_NO_ZLE=\"true\"\n");
}
} else { } else {
buf = abd_borrow_buf_copy(pabd, lsize); buf = abd_borrow_buf_copy(pabd, lsize);
borrowed = B_TRUE; borrowed = B_TRUE;
} }
/*
* Try to detect invalid block pointer. If invalid, try
* decompressing.
*/
if ((flags & ZDB_FLAG_PRINT_BLKPTR || flags & ZDB_FLAG_INDIRECT) &&
!(flags & ZDB_FLAG_DECOMPRESS)) {
const blkptr_t *b = (const blkptr_t *)(void *)
((uintptr_t)buf + (uintptr_t)blkptr_offset);
if (zfs_blkptr_verify(spa, b, B_FALSE, BLK_VERIFY_ONLY) ==
B_FALSE) {
abd_return_buf_copy(pabd, buf, lsize);
borrowed = B_FALSE;
buf = lbuf;
boolean_t failed = zdb_decompress_block(pabd, buf,
lbuf, lsize, psize, flags);
b = (const blkptr_t *)(void *)
((uintptr_t)buf + (uintptr_t)blkptr_offset);
if (failed || zfs_blkptr_verify(spa, b, B_FALSE,
BLK_VERIFY_LOG) == B_FALSE) {
printf("invalid block pointer at this DVA\n");
goto out;
}
}
}
if (flags & ZDB_FLAG_PRINT_BLKPTR) if (flags & ZDB_FLAG_PRINT_BLKPTR)
zdb_print_blkptr((blkptr_t *)(void *) zdb_print_blkptr((blkptr_t *)(void *)
@ -6812,8 +6879,8 @@ zdb_read_block(char *thing, spa_t *spa)
else if (flags & ZDB_FLAG_RAW) else if (flags & ZDB_FLAG_RAW)
zdb_dump_block_raw(buf, lsize, flags); zdb_dump_block_raw(buf, lsize, flags);
else if (flags & ZDB_FLAG_INDIRECT) else if (flags & ZDB_FLAG_INDIRECT)
zdb_dump_indirect((blkptr_t *)buf, lsize / sizeof (blkptr_t), zdb_dump_indirect((blkptr_t *)buf,
flags); orig_lsize / sizeof (blkptr_t), flags);
else if (flags & ZDB_FLAG_GBH) else if (flags & ZDB_FLAG_GBH)
zdb_dump_gbh(buf, flags); zdb_dump_gbh(buf, flags);
else else
@ -6886,6 +6953,8 @@ zdb_read_block(char *thing, spa_t *spa)
out: out:
abd_free(pabd); abd_free(pabd);
umem_free(lbuf, SPA_MAXBLOCKSIZE); umem_free(lbuf, SPA_MAXBLOCKSIZE);
done:
free(flagstr);
free(dup); free(dup);
} }

View File

@ -516,6 +516,12 @@ struct zio {
taskq_ent_t io_tqent; taskq_ent_t io_tqent;
}; };
enum blk_verify_flag {
BLK_VERIFY_ONLY,
BLK_VERIFY_LOG,
BLK_VERIFY_HALT
};
extern int zio_bookmark_compare(const void *, const void *); extern int zio_bookmark_compare(const void *, const void *);
extern zio_t *zio_null(zio_t *pio, spa_t *spa, vdev_t *vd, extern zio_t *zio_null(zio_t *pio, spa_t *spa, vdev_t *vd,
@ -626,6 +632,9 @@ extern void zio_suspend(spa_t *spa, zio_t *zio, zio_suspend_reason_t);
extern int zio_resume(spa_t *spa); extern int zio_resume(spa_t *spa);
extern void zio_resume_wait(spa_t *spa); extern void zio_resume_wait(spa_t *spa);
extern boolean_t zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp,
boolean_t config_held, enum blk_verify_flag blk_verify);
/* /*
* Initial setup and teardown. * Initial setup and teardown.
*/ */

View File

@ -286,7 +286,7 @@ of the block to read and, optionally,
.Pp .Pp
.Bl -tag -compact -width "b offset" .Bl -tag -compact -width "b offset"
.It Sy b Ar offset .It Sy b Ar offset
Print block pointer Print block pointer at hex offset
.It Sy c .It Sy c
Calculate and display checksums Calculate and display checksums
.It Sy d .It Sy d

View File

@ -890,35 +890,82 @@ zio_root(spa_t *spa, zio_done_func_t *done, void *private, enum zio_flag flags)
return (zio_null(NULL, spa, NULL, done, private, flags)); return (zio_null(NULL, spa, NULL, done, private, flags));
} }
static void static int
zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held) zfs_blkptr_verify_log(spa_t *spa, const blkptr_t *bp,
enum blk_verify_flag blk_verify, const char *fmt, ...)
{ {
va_list adx;
char buf[256];
va_start(adx, fmt);
(void) vsnprintf(buf, sizeof (buf), fmt, adx);
va_end(adx);
switch (blk_verify) {
case BLK_VERIFY_HALT:
zfs_panic_recover("%s: %s", spa_name(spa), buf);
break;
case BLK_VERIFY_LOG:
zfs_dbgmsg("%s: %s", spa_name(spa), buf);
break;
case BLK_VERIFY_ONLY:
break;
}
return (1);
}
/*
* Verify the block pointer fields contain reasonable values. This means
* it only contains known object types, checksum/compression identifiers,
* block sizes within the maximum allowed limits, valid DVAs, etc.
*
* If everything checks out B_TRUE is returned. The zfs_blkptr_verify
* argument controls the behavior when an invalid field is detected.
*
* Modes for zfs_blkptr_verify:
* 1) BLK_VERIFY_ONLY (evaluate the block)
* 2) BLK_VERIFY_LOG (evaluate the block and log problems)
* 3) BLK_VERIFY_HALT (call zfs_panic_recover on error)
*/
boolean_t
zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held,
enum blk_verify_flag blk_verify)
{
int errors = 0;
if (!DMU_OT_IS_VALID(BP_GET_TYPE(bp))) { if (!DMU_OT_IS_VALID(BP_GET_TYPE(bp))) {
zfs_panic_recover("blkptr at %p has invalid TYPE %llu", errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"blkptr at %p has invalid TYPE %llu",
bp, (longlong_t)BP_GET_TYPE(bp)); bp, (longlong_t)BP_GET_TYPE(bp));
} }
if (BP_GET_CHECKSUM(bp) >= ZIO_CHECKSUM_FUNCTIONS || if (BP_GET_CHECKSUM(bp) >= ZIO_CHECKSUM_FUNCTIONS ||
BP_GET_CHECKSUM(bp) <= ZIO_CHECKSUM_ON) { BP_GET_CHECKSUM(bp) <= ZIO_CHECKSUM_ON) {
zfs_panic_recover("blkptr at %p has invalid CHECKSUM %llu", errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"blkptr at %p has invalid CHECKSUM %llu",
bp, (longlong_t)BP_GET_CHECKSUM(bp)); bp, (longlong_t)BP_GET_CHECKSUM(bp));
} }
if (BP_GET_COMPRESS(bp) >= ZIO_COMPRESS_FUNCTIONS || if (BP_GET_COMPRESS(bp) >= ZIO_COMPRESS_FUNCTIONS ||
BP_GET_COMPRESS(bp) <= ZIO_COMPRESS_ON) { BP_GET_COMPRESS(bp) <= ZIO_COMPRESS_ON) {
zfs_panic_recover("blkptr at %p has invalid COMPRESS %llu", errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"blkptr at %p has invalid COMPRESS %llu",
bp, (longlong_t)BP_GET_COMPRESS(bp)); bp, (longlong_t)BP_GET_COMPRESS(bp));
} }
if (BP_GET_LSIZE(bp) > SPA_MAXBLOCKSIZE) { if (BP_GET_LSIZE(bp) > SPA_MAXBLOCKSIZE) {
zfs_panic_recover("blkptr at %p has invalid LSIZE %llu", errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"blkptr at %p has invalid LSIZE %llu",
bp, (longlong_t)BP_GET_LSIZE(bp)); bp, (longlong_t)BP_GET_LSIZE(bp));
} }
if (BP_GET_PSIZE(bp) > SPA_MAXBLOCKSIZE) { if (BP_GET_PSIZE(bp) > SPA_MAXBLOCKSIZE) {
zfs_panic_recover("blkptr at %p has invalid PSIZE %llu", errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"blkptr at %p has invalid PSIZE %llu",
bp, (longlong_t)BP_GET_PSIZE(bp)); bp, (longlong_t)BP_GET_PSIZE(bp));
} }
if (BP_IS_EMBEDDED(bp)) { if (BP_IS_EMBEDDED(bp)) {
if (BPE_GET_ETYPE(bp) >= NUM_BP_EMBEDDED_TYPES) { if (BPE_GET_ETYPE(bp) >= NUM_BP_EMBEDDED_TYPES) {
zfs_panic_recover("blkptr at %p has invalid ETYPE %llu", errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"blkptr at %p has invalid ETYPE %llu",
bp, (longlong_t)BPE_GET_ETYPE(bp)); bp, (longlong_t)BPE_GET_ETYPE(bp));
} }
} }
@ -928,7 +975,7 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held)
* will be done once the zio is executed in vdev_mirror_map_alloc. * will be done once the zio is executed in vdev_mirror_map_alloc.
*/ */
if (!spa->spa_trust_config) if (!spa->spa_trust_config)
return; return (B_TRUE);
if (!config_held) if (!config_held)
spa_config_enter(spa, SCL_VDEV, bp, RW_READER); spa_config_enter(spa, SCL_VDEV, bp, RW_READER);
@ -946,21 +993,21 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held)
uint64_t vdevid = DVA_GET_VDEV(&bp->blk_dva[i]); uint64_t vdevid = DVA_GET_VDEV(&bp->blk_dva[i]);
if (vdevid >= spa->spa_root_vdev->vdev_children) { if (vdevid >= spa->spa_root_vdev->vdev_children) {
zfs_panic_recover("blkptr at %p DVA %u has invalid " errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"VDEV %llu", "blkptr at %p DVA %u has invalid VDEV %llu",
bp, i, (longlong_t)vdevid); bp, i, (longlong_t)vdevid);
continue; continue;
} }
vdev_t *vd = spa->spa_root_vdev->vdev_child[vdevid]; vdev_t *vd = spa->spa_root_vdev->vdev_child[vdevid];
if (vd == NULL) { if (vd == NULL) {
zfs_panic_recover("blkptr at %p DVA %u has invalid " errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"VDEV %llu", "blkptr at %p DVA %u has invalid VDEV %llu",
bp, i, (longlong_t)vdevid); bp, i, (longlong_t)vdevid);
continue; continue;
} }
if (vd->vdev_ops == &vdev_hole_ops) { if (vd->vdev_ops == &vdev_hole_ops) {
zfs_panic_recover("blkptr at %p DVA %u has hole " errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"VDEV %llu", "blkptr at %p DVA %u has hole VDEV %llu",
bp, i, (longlong_t)vdevid); bp, i, (longlong_t)vdevid);
continue; continue;
} }
@ -977,13 +1024,15 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held)
if (BP_IS_GANG(bp)) if (BP_IS_GANG(bp))
asize = vdev_psize_to_asize(vd, SPA_GANGBLOCKSIZE); asize = vdev_psize_to_asize(vd, SPA_GANGBLOCKSIZE);
if (offset + asize > vd->vdev_asize) { if (offset + asize > vd->vdev_asize) {
zfs_panic_recover("blkptr at %p DVA %u has invalid " errors += zfs_blkptr_verify_log(spa, bp, blk_verify,
"OFFSET %llu", "blkptr at %p DVA %u has invalid OFFSET %llu",
bp, i, (longlong_t)offset); bp, i, (longlong_t)offset);
} }
} }
if (!config_held) if (!config_held)
spa_config_exit(spa, SCL_VDEV, bp); spa_config_exit(spa, SCL_VDEV, bp);
return (errors == 0);
} }
boolean_t boolean_t
@ -1023,7 +1072,8 @@ zio_read(zio_t *pio, spa_t *spa, const blkptr_t *bp,
{ {
zio_t *zio; zio_t *zio;
zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER); (void) zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER,
BLK_VERIFY_HALT);
zio = zio_create(pio, spa, BP_PHYSICAL_BIRTH(bp), bp, zio = zio_create(pio, spa, BP_PHYSICAL_BIRTH(bp), bp,
data, size, size, done, private, data, size, size, done, private,
@ -1116,7 +1166,7 @@ void
zio_free(spa_t *spa, uint64_t txg, const blkptr_t *bp) zio_free(spa_t *spa, uint64_t txg, const blkptr_t *bp)
{ {
zfs_blkptr_verify(spa, bp, B_FALSE); (void) zfs_blkptr_verify(spa, bp, B_FALSE, BLK_VERIFY_HALT);
/* /*
* The check for EMBEDDED is a performance optimization. We * The check for EMBEDDED is a performance optimization. We
@ -1186,7 +1236,8 @@ zio_claim(zio_t *pio, spa_t *spa, uint64_t txg, const blkptr_t *bp,
{ {
zio_t *zio; zio_t *zio;
zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER); (void) zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER,
BLK_VERIFY_HALT);
if (BP_IS_EMBEDDED(bp)) if (BP_IS_EMBEDDED(bp))
return (zio_null(pio, spa, NULL, NULL, NULL, 0)); return (zio_null(pio, spa, NULL, NULL, NULL, 0));

View File

@ -101,7 +101,8 @@ tags = ['functional', 'clean_mirror']
[tests/functional/cli_root/zdb] [tests/functional/cli_root/zdb]
tests = ['zdb_001_neg', 'zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos', tests = ['zdb_001_neg', 'zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos',
'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress', 'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress',
'zdb_object_range_neg', 'zdb_object_range_pos', 'zdb_objset_id'] 'zdb_object_range_neg', 'zdb_object_range_pos', 'zdb_display_block',
'zdb_objset_id']
pre = pre =
post = post =
tags = ['functional', 'cli_root', 'zdb'] tags = ['functional', 'cli_root', 'zdb']

View File

@ -8,6 +8,7 @@ dist_pkgdata_SCRIPTS = \
zdb_006_pos.ksh \ zdb_006_pos.ksh \
zdb_checksum.ksh \ zdb_checksum.ksh \
zdb_decompress.ksh \ zdb_decompress.ksh \
zdb_objset_id.ksh \
zdb_object_range_neg.ksh \ zdb_object_range_neg.ksh \
zdb_object_range_pos.ksh zdb_object_range_pos.ksh \
zdb_display_block.ksh \
zdb_objset_id.ksh

View File

@ -0,0 +1,128 @@
#!/bin/ksh
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2019 by Datto, Inc. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
#
# Description:
# zdb -R pool <DVA>:b will display the block
#
# Strategy:
# 1. Create a pool, set compression to lzjb
# 2. Write some identifiable data to a file
# 3. Run zdb -ddddddbbbbbb against the file
# 4. Record the DVA of the first L1 block;
# record the first L0 block display; and
# record the 2nd L0 block display.
# 5. Run zdb -R with :bd displays first L0
# 6. Run zdb -R with :b80d displays 2nd L0
# 7. Run zdb -R with :db80 displays 2nd L0
# 8. Run zdb -R with :id flag displays indirect block
# (similar to zdb -ddddddbbbbbb output)
# 9. Run zdb -R with :id flag and .0 vdev
#
function cleanup
{
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
}
log_assert "Verify zdb -R :b flag (block display) works as expected"
log_onexit cleanup
init_data=$TESTDIR/file1
write_count=256
blksize=4096
# only read 256 128 byte block pointers in L1 (:i flag)
# 256 x 128 = 32k / 0x8000
l1_read_size="8000"
verify_runnable "global"
verify_disk_count "$DISKS" 2
default_mirror_setup_noexit $DISKS
log_must zfs set recordsize=$blksize $TESTPOOL/$TESTFS
log_must zfs set compression=lzjb $TESTPOOL/$TESTFS
file_write -d R -o create -w -f $init_data -b $blksize -c $write_count
sync_pool $TESTPOOL true
# get object number of file
listing=$(ls -i $init_data)
set -A array $listing
obj=${array[0]}
log_note "file $init_data has object number $obj"
output=$(zdb -ddddddbbbbbb $TESTPOOL/$TESTFS $obj 2> /dev/null \
|grep -m 1 "L1 DVA" |head -n1)
dva=$(sed -Ene 's/^.+DVA\[0\]=<([^>]+)>.*/\1/p' <<< "$output")
log_note "first L1 block $init_data has a DVA of $dva"
output=$(zdb -ddddddbbbbbb $TESTPOOL/$TESTFS $obj 2> /dev/null \
|grep -m 1 "L0 DVA" |head -n1)
blk_out0=${output##*>}
blk_out0=${blk_out0##+([[:space:]])}
output=$(zdb -ddddddbbbbbb $TESTPOOL/$TESTFS $obj 2> /dev/null \
|grep -m 1 "1000 L0 DVA" |head -n1)
blk_out1=${output##*>}
blk_out1=${blk_out1##+([[:space:]])}
output=$(export ZDB_NO_ZLE=\"true\"; zdb -R $TESTPOOL $dva:bd\
2> /dev/null)
output=${output##*>}
output=${output##+([[:space:]])}
if [ "$output" != "$blk_out0" ]; then
log_fail "zdb -R :bd (block 0 display/decompress) failed"
fi
output=$(export ZDB_NO_ZLE=\"true\"; zdb -R $TESTPOOL $dva:db80\
2> /dev/null)
output=${output##*>}
output=${output##+([[:space:]])}
if [ "$output" != "$blk_out1" ]; then
log_fail "zdb -R :db80 (block 1 display/decompress) failed"
fi
output=$(export ZDB_NO_ZLE=\"true\"; zdb -R $TESTPOOL $dva:b80d\
2> /dev/null)
output=${output##*>}
output=${output##+([[:space:]])}
if [ "$output" != "$blk_out1" ]; then
log_fail "zdb -R :b80d (block 1 display/decompress) failed"
fi
vdev=$(echo "$dva" |awk '{split($0,array,":")} END{print array[1]}')
offset=$(echo "$dva" |awk '{split($0,array,":")} END{print array[2]}')
output=$(export ZDB_NO_ZLE=\"true\";\
zdb -R $TESTPOOL $vdev:$offset:$l1_read_size:id 2> /dev/null)
block_cnt=$(echo "$output" | grep 'L0' | wc -l)
if [ "$block_cnt" != "$write_count" ]; then
log_fail "zdb -R :id (indirect block display) failed"
fi
# read from specific half of mirror
vdev="$vdev.0"
log_note "Reading from DVA $vdev:$offset:$l1_read_size"
output=$(export ZDB_NO_ZLE=\"true\";\
zdb -R $TESTPOOL $vdev:$offset:$l1_read_size:id 2> /dev/null)
block_cnt=$(echo "$output" | grep 'L0' | wc -l)
if [ "$block_cnt" != "$write_count" ]; then
log_fail "zdb -R 0.0:offset:length:id (indirect block display) failed"
fi
log_pass "zdb -R :b flag (block display) works as expected"