]> granicus.if.org Git - zfs/commitdiff
OpenZFS 9421, 9422 - zdb show possibly leaked objects
authorPaul Dagnelie <pcd@delphix.com>
Thu, 6 Jul 2017 17:35:20 +0000 (10:35 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 4 May 2018 17:50:24 +0000 (10:50 -0700)
9421 zdb should detect and print out the number of "leaked" objects
9422 zfs diff and zdb should explicitly mark objects that are on
     the deleted queue

It is possible for zfs to "leak" objects in such a way that they are not
freed, but are also not accessible via the POSIX interface. As the only
way to know that this is happened is to see one of them directly in a
zdb run, or by noting unaccounted space usage, zdb should be enhanced to
count these objects and return failure if some are detected.

We have access to the delete queue through the zfs_get_deleteq function;
we should call it in dump_znode to determine if the object is on the
delete queue. This is not the most efficient possible method, but it is
the simplest to implement, and should suffice for the common case where
there few objects on the delete queue.

Also zfs diff and zdb currently traverse every single dnode in a dataset
and tries to figure out the path of the object by following it's parent.
When an object is placed on the delete queue, for all practical purposes
it's already discarded, it's parent might not exist anymore, and another
object might now have the object number that belonged to the parent.
While all of the above makes sense, when trying to figure out the path
of an object that is on the delete queue, we can run into issues where
either it is impossible to determine the path because the parent is
gone, or another dnode has taken it's place and thus we are returned a
wrong path.

We should therefore avoid trying to determine the path of an object on
the delete queue and mark the object itself as being on the delete queue
to avoid confusion. To achieve this, we currently have two ideas:

1. When putting an object on the delete queue, change it's parent object
   number to a known constant that means NULL.

2. When displaying objects, first check if it is present on the delete
   queue.

Authored by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Matt Ahrens <matt@delphix.com>
Reviewed by: Pavel Zakharov <pavel.zakharov@delphix.com>
Approved by: Matt Ahrens <mahrens@delphix.com>
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
OpenZFS-issue: https://illumos.org/issues/9421
OpenZFS-issue: https://illumos.org/issues/9422
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/45ae0dd9ca
Closes #7500

cmd/zdb/zdb.c
lib/libzfs/libzfs_diff.c
module/zfs/zfs_znode.c

index 852f853559c7c37a575de590c092aa77b75b0429..5f56b10e2d01bfe21e8504fdb7759257c08200ef 100644 (file)
@@ -106,6 +106,7 @@ uint64_t *zopt_object = NULL;
 static unsigned zopt_objects = 0;
 libzfs_handle_t *g_zfs;
 uint64_t max_inflight = 1000;
+static int leaked_objects = 0;
 
 static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *);
 
@@ -1983,9 +1984,12 @@ dump_znode(objset_t *os, uint64_t object, void *data, size_t size)
 
        if (dump_opt['d'] > 4) {
                error = zfs_obj_to_path(os, object, path, sizeof (path));
-               if (error != 0) {
+               if (error == ESTALE) {
+                       (void) snprintf(path, sizeof (path), "on delete queue");
+               } else if (error != 0) {
+                       leaked_objects++;
                        (void) snprintf(path, sizeof (path),
-                           "\?\?\?<object#%llu>", (u_longlong_t)object);
+                           "path not found, possibly leaked");
                }
                (void) printf("\tpath   %s\n", path);
        }
@@ -2376,14 +2380,19 @@ dump_dir(objset_t *os)
            (double)(max_slot_used - total_slots_used)*100 /
            (double)max_slot_used);
 
+       ASSERT3U(object_count, ==, usedobjs);
+
        (void) printf("\n");
 
        if (error != ESRCH) {
                (void) fprintf(stderr, "dmu_object_next() = %d\n", error);
                abort();
        }
-
-       ASSERT3U(object_count, ==, usedobjs);
+       if (leaked_objects != 0) {
+               (void) printf("%d potentially leaked objects detected\n",
+                   leaked_objects);
+               leaked_objects = 0;
+       }
 }
 
 static void
@@ -5209,5 +5218,5 @@ main(int argc, char **argv)
        libzfs_fini(g_zfs);
        kernel_fini();
 
-       return (0);
+       return (error);
 }
index 3e000504808e6af423c7b8c930cf76a31711ced4..249334acd9552588782df821a16d4fd654403ced 100644 (file)
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2015, 2017 by Delphix. All rights reserved.
  * Copyright 2016 Joyent, Inc.
  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  */
@@ -103,7 +103,10 @@ get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj,
                return (0);
        }
 
-       if (di->zerr == EPERM) {
+       if (di->zerr == ESTALE) {
+               (void) snprintf(pn, maxlen, "(on_delete_queue)");
+               return (0);
+       } else if (di->zerr == EPERM) {
                (void) snprintf(di->errbuf, sizeof (di->errbuf),
                    dgettext(TEXT_DOMAIN,
                    "The sys_config privilege or diff delegated permission "
index 5288c9c68498819aaba001fa9ca883f53138c78a..c8a613f14f7738ef4b57df667173181a0e18e0d7 100644 (file)
@@ -2120,6 +2120,17 @@ zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl,
        *path = '\0';
        sa_hdl = hdl;
 
+       uint64_t deleteq_obj;
+       VERIFY0(zap_lookup(osp, MASTER_NODE_OBJ,
+           ZFS_UNLINKED_SET, sizeof (uint64_t), 1, &deleteq_obj));
+       error = zap_lookup_int(osp, deleteq_obj, obj);
+       if (error == 0) {
+               return (ESTALE);
+       } else if (error != ENOENT) {
+               return (error);
+       }
+       error = 0;
+
        for (;;) {
                uint64_t pobj = 0;
                char component[MAXNAMELEN + 2];