]> granicus.if.org Git - zfs/commitdiff
zdb -vvvvv on ztest pool dies with "out of memory"
authorPaul Dagnelie <pcd@delphix.com>
Tue, 25 Jun 2019 19:50:38 +0000 (12:50 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 25 Jun 2019 19:50:37 +0000 (12:50 -0700)
ztest creates some extremely large files as part of its
operation. When zdb tries to dump a large enough file, it
can run out of memory or spend an extremely long time
attempting to print millions or billions of uint64_ts.

We cap the amount of data from a uint64 object that we
are willing to read and print.

Reviewed-by: Don Brady <don.brady@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
External-issue: DLPX-53814
Closes #8947

cmd/zdb/zdb.c

index 969c8f1b794c3f8f45c046e5894ea0dcb53a95f0..e0ea0728091804ffca2aa257fd10b5d4ae6c7de6 100644 (file)
@@ -424,23 +424,35 @@ static void
 dump_uint64(objset_t *os, uint64_t object, void *data, size_t size)
 {
        uint64_t *arr;
-
+       uint64_t oursize;
        if (dump_opt['d'] < 6)
                return;
+
        if (data == NULL) {
                dmu_object_info_t doi;
 
                VERIFY0(dmu_object_info(os, object, &doi));
                size = doi.doi_max_offset;
-               arr = kmem_alloc(size, KM_SLEEP);
+               /*
+                * We cap the size at 1 mebibyte here to prevent
+                * allocation failures and nigh-infinite printing if the
+                * object is extremely large.
+                */
+               oursize = MIN(size, 1 << 20);
+               arr = kmem_alloc(oursize, KM_SLEEP);
 
-               int err = dmu_read(os, object, 0, size, arr, 0);
+               int err = dmu_read(os, object, 0, oursize, arr, 0);
                if (err != 0) {
                        (void) printf("got error %u from dmu_read\n", err);
-                       kmem_free(arr, size);
+                       kmem_free(arr, oursize);
                        return;
                }
        } else {
+               /*
+                * Even though the allocation is already done in this code path,
+                * we still cap the size to prevent excessive printing.
+                */
+               oursize = MIN(size, 1 << 20);
                arr = data;
        }
 
@@ -450,16 +462,18 @@ dump_uint64(objset_t *os, uint64_t object, void *data, size_t size)
        }
 
        (void) printf("\t\t[%0llx", (u_longlong_t)arr[0]);
-       for (size_t i = 1; i * sizeof (uint64_t) < size; i++) {
+       for (size_t i = 1; i * sizeof (uint64_t) < oursize; i++) {
                if (i % 4 != 0)
                        (void) printf(", %0llx", (u_longlong_t)arr[i]);
                else
                        (void) printf(",\n\t\t%0llx", (u_longlong_t)arr[i]);
        }
+       if (oursize != size)
+               (void) printf(", ... ");
        (void) printf("]\n");
 
        if (data == NULL)
-               kmem_free(arr, size);
+               kmem_free(arr, oursize);
 }
 
 /*ARGSUSED*/