]> git.proxmox.com Git - mirror_zfs.git/commitdiff
zio_decompress_data always ASSERTs successful decompression
authorPaul Zuchowski <31706010+PaulZ-98@users.noreply.github.com>
Tue, 10 Dec 2019 23:51:58 +0000 (18:51 -0500)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 10 Dec 2019 23:51:58 +0000 (15:51 -0800)
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 #9612
Closes #9630

cmd/zdb/zdb.c
man/man8/zdb.8
module/zfs/zio_compress.c
tests/runfiles/common.run
tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zdb/zdb_decompress.ksh [new file with mode: 0755]

index e4c8861ebe61acf7d1cfe4a10b519358ea212cfd..91ead1d55e55fc8c80409b36fabfe1f515d785a6 100644 (file)
@@ -2738,7 +2738,7 @@ static const char *objset_types[DMU_OST_NUMTYPES] = {
 static void
 dump_objset(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];
@@ -6196,9 +6196,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];
 
@@ -6329,11 +6329,30 @@ name:
        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)
@@ -6346,8 +6365,8 @@ name:
  *              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
@@ -6356,13 +6375,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;
 
@@ -6371,18 +6389,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";
@@ -6438,9 +6452,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);
 
@@ -6497,30 +6508,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
@@ -6529,27 +6551,30 @@ 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;
        }
 
@@ -6557,14 +6582,14 @@ zdb_read_block(char *thing, spa_t *spa)
                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
@@ -6628,7 +6653,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);
@@ -7064,8 +7089,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);
index 55689a9940692ea402ee136aeff46aa383aa915c..7c50d95d91af1a8ada3574e9ed01c2320d8e27a9 100644 (file)
@@ -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
@@ -228,7 +228,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
@@ -241,8 +241,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
@@ -263,6 +263,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
index cdaade27c679f9c022673cc4c4f12257a983306c..01c51347fec3e89f626a842577fbadb94d4382a9 100644 (file)
@@ -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);
index fcf3f8d5d78c7049fef52d285967a2486dca9c6d..89e4492ddb99699b12b1f58af683084968fe7ea3 100644 (file)
@@ -94,7 +94,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']
index 0c4de2b2558eea68a0823599684713a2c25927ca..9f143078f18f134efa234c24ddd9f765d022ab96 100644 (file)
@@ -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
diff --git a/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_decompress.ksh b/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_decompress.ksh
new file mode 100755 (executable)
index 0000000..0e468d7
--- /dev/null
@@ -0,0 +1,119 @@
+#!/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>:d will display the correct data and length
+#
+# 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, lsize, and psize of L0 block 0
+# 5. Run zdb -R with :d flag and match the data
+# 6. Run zdb -R with :dr flags and match the lsize/psize
+# 7. Run zdb -R with :dr flags and match the lsize
+# 8. Run zdb -R with :dr flags and match the psize
+#
+
+
+function cleanup
+{
+       datasetexists $TESTPOOL && destroy_pool $TESTPOOL
+}
+
+log_assert "Verify zdb -R :d flag (decompress) works as expected"
+log_onexit cleanup
+init_data=$TESTDIR/file1
+write_count=256
+blksize=4096
+pattern="_match__pattern_"
+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
+
+# 16 chars 256 times = 4k = block size
+typeset four_k=""
+for i in {1..$write_count}
+do
+       four_k=$four_k$pattern
+done
+
+# write the 4k block 256 times
+for i in {1..$write_count}
+do
+       echo $four_k >> $init_data
+done
+
+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 "L0 DVA" |head -n1)
+dva=$(grep -oP 'DVA\[0\]=<\K.*?(?=>)' <<< "$output")
+log_note "block 0 of $init_data has a DVA of $dva"
+
+# use the length reported by zdb -ddddddbbbbbb
+size_str=$(grep -oP 'size=\K.*?(?= )' <<< "$output")
+log_note "block size $size_str"
+
+vdev=$(echo "$dva" |awk '{split($0,array,":")} END{print array[1]}')
+offset=$(echo "$dva" |awk '{split($0,array,":")} END{print array[2]}')
+output=$(zdb -R $TESTPOOL $vdev:$offset:$size_str:d 2> /dev/null)
+echo $output |grep $pattern > /dev/null
+(( $? != 0 )) && log_fail "zdb -R :d failed to decompress the data properly"
+
+output=$(zdb -R $TESTPOOL $vdev:$offset:$size_str:dr 2> /dev/null)
+echo $output |grep $four_k > /dev/null
+(( $? != 0 )) && log_fail "zdb -R :dr failed to decompress the data properly"
+
+output=$(zdb -R $TESTPOOL $vdev:$offset:$size_str:dr 2> /dev/null)
+result=${#output}
+(( $result != $blksize)) && log_fail \
+"zdb -R failed to decompress the data to the length (${#output} != $size_str)"
+
+# decompress using lsize
+lsize=$(echo $size_str |awk '{split($0,array,"/")} END{print array[1]}')
+psize=$(echo $size_str |awk '{split($0,array,"/")} END{print array[2]}')
+output=$(zdb -R $TESTPOOL $vdev:$offset:$lsize:dr 2> /dev/null)
+result=${#output}
+(( $result != $blksize)) && log_fail \
+"zdb -R failed to decompress the data (length ${#output} != $blksize)"
+
+# Specifying psize will decompress successfully , but not always to full
+# lsize since zdb has to guess lsize incrementally.
+output=$(zdb -R $TESTPOOL $vdev:$offset:$psize:dr 2> /dev/null)
+result=${#output}
+# convert psize to decimal
+psize_orig=$psize
+psize=${psize%?}
+psize=$((16#$psize))
+(( $result < $psize)) && log_fail \
+"zdb -R failed to decompress the data with psize $psize_orig\
+ (length ${#output} < $psize)"
+
+log_pass "zdb -R :d flag (decompress) works as expected"