Print this page
Optimize creation and removal of temporary "user holds" placed on
snapshots by a zfs send, by ensuring all the required holds and
releases are done in a single dsl_sync_task.
Creation now collates the required holds during a dry run and
then uses a single lzc_hold call via zfs_hold_apply instead of
processing each snapshot in turn.
Defered (on exit) cleanup by the kernel is also now done in
dsl_sync_task by reusing dsl_dataset_user_release.
On a test with 11 volumes in a tree each with 8 snapshots on a
single HDD zpool this reduces the time required to perform a full
send from 20 seconds to under 0.8 seconds.
For reference eliminating the hold entirely reduces this 0.15
seconds.
While I'm here:-
* Remove some unused structures
* Fix nvlist_t leak in zfs_release_one

@@ -830,31 +830,50 @@
  * and release them.
  */
 void
 dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp)
 {
+        char *htag;
         zap_attribute_t za;
         zap_cursor_t zc;
         objset_t *mos = dp->dp_meta_objset;
         uint64_t zapobj = dp->dp_tmp_userrefs_obj;
+        uint64_t dsobj;
+        nvlist_t *holds, *tags;
+        dsl_dataset_t *ds;
+        char name[MAXNAMELEN];
 
         if (zapobj == 0)
                 return;
         ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS);
 
+        holds = fnvlist_alloc();
+
+        dsl_pool_config_enter(dp, FTAG);
         for (zap_cursor_init(&zc, mos, zapobj);
             zap_cursor_retrieve(&zc, &za) == 0;
             zap_cursor_advance(&zc)) {
-                char *htag;
-                uint64_t dsobj;
-
                 htag = strchr(za.za_name, '-');
                 *htag = '\0';
                 ++htag;
                 dsobj = strtonum(za.za_name, NULL);
-                dsl_dataset_user_release_tmp(dp, dsobj, htag);
+                if (dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds) == 0) {
+                        dsl_dataset_name(ds, name);
+                        if (nvlist_lookup_nvlist(holds, name, &tags) != 0) {
+                                tags = fnvlist_alloc();
+                                fnvlist_add_boolean(tags, htag);
+                                fnvlist_add_nvlist(holds, name, tags);
+                                fnvlist_free(tags);
+                        } else {
+                                fnvlist_add_boolean(tags, htag);
+                        }
+                        dsl_dataset_rele(ds, FTAG);
+                }
         }
+        dsl_pool_config_exit(dp, FTAG);
+        dsl_dataset_user_release(holds, NULL);
+        fnvlist_free(holds);
         zap_cursor_fini(&zc);
 }
 
 /*
  * Create the pool-wide zap object for storing temporary snapshot holds.