Print this page
3740 Poor ZFS send / receive performance due to snapshot hold / release processing
Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>

@@ -4079,50 +4079,60 @@
 
 static int
 zfs_hold_one(zfs_handle_t *zhp, void *arg)
 {
         struct holdarg *ha = arg;
-        zfs_handle_t *szhp;
         char name[ZFS_MAXNAMELEN];
         int rv = 0;
 
         (void) snprintf(name, sizeof (name),
             "%s@%s", zhp->zfs_name, ha->snapname);
 
-        szhp = make_dataset_handle(zhp->zfs_hdl, name);
-        if (szhp) {
+        if (lzc_exists(name))
                 fnvlist_add_string(ha->nvl, name, ha->tag);
-                zfs_close(szhp);
-        }
 
         if (ha->recursive)
                 rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
         zfs_close(zhp);
         return (rv);
 }
 
 int
 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
-    boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
+    boolean_t recursive, int cleanup_fd)
 {
         int ret;
         struct holdarg ha;
-        nvlist_t *errors;
-        libzfs_handle_t *hdl = zhp->zfs_hdl;
-        char errbuf[1024];
-        nvpair_t *elem;
 
         ha.nvl = fnvlist_alloc();
         ha.snapname = snapname;
         ha.tag = tag;
         ha.recursive = recursive;
         (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
-        ret = lzc_hold(ha.nvl, cleanup_fd, &errors);
+        ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);
         fnvlist_free(ha.nvl);
 
-        if (ret == 0)
+        return (ret);
+}
+
+int
+zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
+{
+        int ret;
+        nvlist_t *errors;
+        libzfs_handle_t *hdl = zhp->zfs_hdl;
+        char errbuf[1024];
+        nvpair_t *elem;
+ 
+        errors = NULL;
+        ret = lzc_hold(holds, cleanup_fd, &errors);
+ 
+        if (ret == 0) {
+                /* There may be errors even in the success case. */
+                fnvlist_free(errors);
                 return (0);
+        }
 
         if (nvlist_next_nvpair(errors, NULL) == NULL) {
                 /* no hold-specific errors */
                 (void) snprintf(errbuf, sizeof (errbuf),
                     dgettext(TEXT_DOMAIN, "cannot hold"));

@@ -4160,48 +4170,35 @@
                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
                         break;
                 case EEXIST:
                         (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
                         break;
-                case ENOENT:
-                        if (enoent_ok)
-                                return (ENOENT);
-                        /* FALLTHROUGH */
                 default:
                         (void) zfs_standard_error(hdl,
                             fnvpair_value_int32(elem), errbuf);
                 }
         }
 
         fnvlist_free(errors);
         return (ret);
 }
 
-struct releasearg {
-        nvlist_t *nvl;
-        const char *snapname;
-        const char *tag;
-        boolean_t recursive;
-};
-
 static int
 zfs_release_one(zfs_handle_t *zhp, void *arg)
 {
         struct holdarg *ha = arg;
-        zfs_handle_t *szhp;
         char name[ZFS_MAXNAMELEN];
         int rv = 0;
 
         (void) snprintf(name, sizeof (name),
             "%s@%s", zhp->zfs_name, ha->snapname);
 
-        szhp = make_dataset_handle(zhp->zfs_hdl, name);
-        if (szhp) {
+        if (lzc_exists(name)) {
                 nvlist_t *holds = fnvlist_alloc();
                 fnvlist_add_boolean(holds, ha->tag);
                 fnvlist_add_nvlist(ha->nvl, name, holds);
-                zfs_close(szhp);
+                fnvlist_free(holds);
         }
 
         if (ha->recursive)
                 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
         zfs_close(zhp);

@@ -4221,15 +4218,19 @@
         ha.nvl = fnvlist_alloc();
         ha.snapname = snapname;
         ha.tag = tag;
         ha.recursive = recursive;
         (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
+        errors = NULL;
         ret = lzc_release(ha.nvl, &errors);
         fnvlist_free(ha.nvl);
 
-        if (ret == 0)
+        if (ret == 0) {
+                /* There may be errors even in the success case. */
+                fnvlist_free(errors);
                 return (0);
+        }
 
         if (nvlist_next_nvpair(errors, NULL) == NULL) {
                 /* no hold-specific errors */
                 char errbuf[1024];