Skip to content

Commit

Permalink
zio_decompress_data always ASSERTs successful decompression
Browse files Browse the repository at this point in the history
This interferes with zdb_read_block trying all the decompression
algorithms when the 'd' flag is specified, as some are
expected to fail.  Also control the output when guessing
algorithms, try the more common compression types first, allow
specifying lsize/psize, and fix an uninitialized variable.

Reviewed-by: Ryan Moeller <ryan@ixsystems.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Zuchowski <pzuchowski@datto.com>
Closes openzfs#9612
Closes openzfs#9630
  • Loading branch information
PaulZ-98 authored and tonyhutter committed Jan 22, 2020
1 parent d2233a0 commit 4d658bd
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 59 deletions.
129 changes: 77 additions & 52 deletions cmd/zdb/zdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -2409,7 +2409,7 @@ static const char *objset_types[DMU_OST_NUMTYPES] = {
static void
dump_dir(objset_t *os)
{
dmu_objset_stats_t dds;
dmu_objset_stats_t dds = { 0 };
uint64_t object, object_count;
uint64_t refdbytes, usedobjs, scratch;
char numbuf[32];
Expand Down Expand Up @@ -5447,9 +5447,9 @@ dump_zpool(spa_t *spa)
#define ZDB_FLAG_BSWAP 0x0004
#define ZDB_FLAG_GBH 0x0008
#define ZDB_FLAG_INDIRECT 0x0010
#define ZDB_FLAG_PHYS 0x0020
#define ZDB_FLAG_RAW 0x0040
#define ZDB_FLAG_PRINT_BLKPTR 0x0080
#define ZDB_FLAG_RAW 0x0020
#define ZDB_FLAG_PRINT_BLKPTR 0x0040
#define ZDB_FLAG_VERBOSE 0x0080

static int flagbits[256];

Expand Down Expand Up @@ -5580,11 +5580,30 @@ zdb_vdev_lookup(vdev_t *vdev, const char *path)
return (NULL);
}

static boolean_t
zdb_parse_block_sizes(char *sizes, uint64_t *lsize, uint64_t *psize)
{
char *s0, *s1;

if (sizes == NULL)
return (B_FALSE);

s0 = strtok(sizes, "/");
if (s0 == NULL)
return (B_FALSE);
s1 = strtok(NULL, "/");
*lsize = strtoull(s0, NULL, 16);
*psize = s1 ? strtoull(s1, NULL, 16) : *lsize;
return (*lsize >= *psize && *psize > 0);
}

#define ZIO_COMPRESS_MASK(alg) (1ULL << (ZIO_COMPRESS_##alg))

/*
* Read a block from a pool and print it out. The syntax of the
* block descriptor is:
*
* pool:vdev_specifier:offset:size[:flags]
* pool:vdev_specifier:offset:[lsize/]psize[:flags]
*
* pool - The name of the pool you wish to read from
* vdev_specifier - Which vdev (see comment for zdb_vdev_lookup)
Expand All @@ -5597,8 +5616,8 @@ zdb_vdev_lookup(vdev_t *vdev, const char *path)
* e: Byteswap data before dumping
* g: Display data as a gang block header
* i: Display as an indirect block
* p: Do I/O to physical offset
* r: Dump raw data to stdout
* v: Verbose
*
*/
static void
Expand All @@ -5607,13 +5626,12 @@ zdb_read_block(char *thing, spa_t *spa)
blkptr_t blk, *bp = &blk;
dva_t *dva = bp->blk_dva;
int flags = 0;
uint64_t offset = 0, size = 0, psize = 0, lsize = 0, blkptr_offset = 0;
uint64_t offset = 0, psize = 0, lsize = 0, blkptr_offset = 0;
zio_t *zio;
vdev_t *vd;
abd_t *pabd;
void *lbuf, *buf;
const char *s, *vdev;
char *p, *dup, *flagstr;
char *s, *p, *dup, *vdev, *flagstr, *sizes;
int i, error;
boolean_t borrowed = B_FALSE;

Expand All @@ -5622,18 +5640,14 @@ zdb_read_block(char *thing, spa_t *spa)
vdev = s ? s : "";
s = strtok(NULL, ":");
offset = strtoull(s ? s : "", NULL, 16);
sizes = strtok(NULL, ":");
s = strtok(NULL, ":");
size = strtoull(s ? s : "", NULL, 16);
s = strtok(NULL, ":");
if (s)
flagstr = strdup(s);
else
flagstr = strdup("");
flagstr = strdup(s ? s : "");

s = NULL;
if (size == 0)
s = "size must not be zero";
if (!IS_P2ALIGNED(size, DEV_BSIZE))
if (!zdb_parse_block_sizes(sizes, &lsize, &psize))
s = "invalid size(s)";
if (!IS_P2ALIGNED(psize, DEV_BSIZE) || !IS_P2ALIGNED(lsize, DEV_BSIZE))
s = "size must be a multiple of sector size";
if (!IS_P2ALIGNED(offset, DEV_BSIZE))
s = "offset must be a multiple of sector size";
Expand Down Expand Up @@ -5689,9 +5703,6 @@ zdb_read_block(char *thing, spa_t *spa)
vd->vdev_ops->vdev_op_type);
}

psize = size;
lsize = size;

pabd = abd_alloc_for_io(SPA_MAXBLOCKSIZE, B_FALSE);
lbuf = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);

Expand Down Expand Up @@ -5748,30 +5759,41 @@ zdb_read_block(char *thing, spa_t *spa)
* We don't know how the data was compressed, so just try
* every decompress function at every inflated blocksize.
*/
enum zio_compress c;
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;

/*
* XXX - On the one hand, with SPA_MAXBLOCKSIZE at 16MB,
* this could take a while and we should let the user know
* 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. What to do?
* info gets old after a while. User can specify 'v' flag
* to see the progression.
*/
for (lsize = psize + SPA_MINBLOCKSIZE;
lsize <= SPA_MAXBLOCKSIZE; lsize += SPA_MINBLOCKSIZE) {
for (c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++) {
/*
* ZLE can easily decompress non zle stream.
* So have an option to disable it.
*/
if (c == ZIO_COMPRESS_ZLE &&
getenv("ZDB_NO_ZLE"))
continue;

(void) fprintf(stderr,
"Trying %05llx -> %05llx (%s)\n",
(u_longlong_t)psize, (u_longlong_t)lsize,
zio_compress_table[c].ci_name);
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
Expand All @@ -5780,42 +5802,45 @@ zdb_read_block(char *thing, spa_t *spa)
*/
VERIFY0(random_get_pseudo_bytes(lbuf2, lsize));

if (zio_decompress_data(c, pabd,
if (zio_decompress_data(*cfuncp, pabd,
lbuf, psize, lsize) == 0 &&
zio_decompress_data(c, pabd,
zio_decompress_data(*cfuncp, pabd,
lbuf2, psize, lsize) == 0 &&
bcmp(lbuf, lbuf2, lsize) == 0)
break;
}
if (c != ZIO_COMPRESS_FUNCTIONS)
if (*cfuncp != 0)
break;
}
umem_free(lbuf2, SPA_MAXBLOCKSIZE);

if (lsize > SPA_MAXBLOCKSIZE) {
if (lsize > maxlsize) {
(void) printf("Decompress of %s failed\n", thing);
goto out;
}
buf = lbuf;
size = lsize;
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 {
size = psize;
buf = abd_borrow_buf_copy(pabd, size);
buf = abd_borrow_buf_copy(pabd, lsize);
borrowed = B_TRUE;
}

if (flags & ZDB_FLAG_PRINT_BLKPTR)
zdb_print_blkptr((blkptr_t *)(void *)
((uintptr_t)buf + (uintptr_t)blkptr_offset), flags);
else if (flags & ZDB_FLAG_RAW)
zdb_dump_block_raw(buf, size, flags);
zdb_dump_block_raw(buf, lsize, flags);
else if (flags & ZDB_FLAG_INDIRECT)
zdb_dump_indirect((blkptr_t *)buf, size / sizeof (blkptr_t),
zdb_dump_indirect((blkptr_t *)buf, lsize / sizeof (blkptr_t),
flags);
else if (flags & ZDB_FLAG_GBH)
zdb_dump_gbh(buf, flags);
else
zdb_dump_block(thing, buf, size, flags);
zdb_dump_block(thing, buf, lsize, flags);

/*
* If :c was specified, iterate through the checksum table to
Expand Down Expand Up @@ -5879,7 +5904,7 @@ zdb_read_block(char *thing, spa_t *spa)
}

if (borrowed)
abd_return_buf_copy(pabd, buf, size);
abd_return_buf_copy(pabd, buf, lsize);

out:
abd_free(pabd);
Expand Down Expand Up @@ -6294,8 +6319,8 @@ main(int argc, char **argv)
flagbits['e'] = ZDB_FLAG_BSWAP;
flagbits['g'] = ZDB_FLAG_GBH;
flagbits['i'] = ZDB_FLAG_INDIRECT;
flagbits['p'] = ZDB_FLAG_PHYS;
flagbits['r'] = ZDB_FLAG_RAW;
flagbits['v'] = ZDB_FLAG_VERBOSE;

for (int i = 0; i < argc; i++)
zdb_read_block(argv[i], spa);
Expand Down
10 changes: 6 additions & 4 deletions man/man8/zdb.8
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
.Op Fl A
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
.Op Fl U Ar cache
.Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
.Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar [<lsize>/]<psize> Ns Op : Ns Ar flags
.Nm
.Fl S
.Op Fl AP
Expand Down Expand Up @@ -227,7 +227,7 @@ This option can be combined with
.Fl v
for increasing verbosity.
.It Xo
.Fl R Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
.Fl R Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar [<lsize>/]<psize> Ns Op : Ns Ar flags
.Xc
Read and display a block from the specified device.
By default the block is displayed as a hex dump, but see the description of the
Expand All @@ -240,8 +240,8 @@ The block is specified in terms of a colon-separated tuple
.Ar offset
.Pq the offset within the vdev
.Ar size
.Pq the size of the block to read
and, optionally,
.Pq the physical size, or logical size / physical size
of the block to read and, optionally,
.Ar flags
.Pq a set of flags, described below .
.Pp
Expand All @@ -262,6 +262,8 @@ Dump gang block header
Dump indirect block
.It Sy r
Dump raw uninterpreted block data
.It Sy v
Verbose output for guessing compression algorithm
.El
.It Fl s
Report statistics on
Expand Down
1 change: 0 additions & 1 deletion module/zfs/zio_compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ zio_decompress_data(enum zio_compress c, abd_t *src, void *dst,
* the checksum. However, for extra protection (e.g. against bitflips
* in non-ECC RAM), we handle this error (and test it).
*/
ASSERT0(ret);
if (zio_decompress_fail_fraction != 0 &&
spa_get_random(zio_decompress_fail_fraction) == 0)
ret = SET_ERROR(EINVAL);
Expand Down
2 changes: 1 addition & 1 deletion tests/runfiles/linux.run
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ tags = ['functional', 'clean_mirror']

[tests/functional/cli_root/zdb]
tests = ['zdb_001_neg', 'zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos',
'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum']
'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress']
pre =
post =
tags = ['functional', 'cli_root', 'zdb']
Expand Down
3 changes: 2 additions & 1 deletion tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ dist_pkgdata_SCRIPTS = \
zdb_004_pos.ksh \
zdb_005_pos.ksh \
zdb_006_pos.ksh \
zdb_checksum.ksh
zdb_checksum.ksh \
zdb_decompress.ksh
Loading

0 comments on commit 4d658bd

Please sign in to comment.