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

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/zfs/dsl_pool.c
          +++ new/usr/src/uts/common/fs/zfs/dsl_pool.c
↓ open down ↓ 824 lines elided ↑ open up ↑
 825  825          return (dp->dp_vnrele_taskq);
 826  826  }
 827  827  
 828  828  /*
 829  829   * Walk through the pool-wide zap object of temporary snapshot user holds
 830  830   * and release them.
 831  831   */
 832  832  void
 833  833  dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp)
 834  834  {
      835 +        char *htag;
 835  836          zap_attribute_t za;
 836  837          zap_cursor_t zc;
 837  838          objset_t *mos = dp->dp_meta_objset;
 838  839          uint64_t zapobj = dp->dp_tmp_userrefs_obj;
      840 +        uint64_t dsobj;
      841 +        nvlist_t *holds, *tags;
      842 +        dsl_dataset_t *ds;
      843 +        char name[MAXNAMELEN];
 839  844  
 840  845          if (zapobj == 0)
 841  846                  return;
 842  847          ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS);
 843  848  
      849 +        holds = fnvlist_alloc();
      850 +
      851 +        dsl_pool_config_enter(dp, FTAG);
 844  852          for (zap_cursor_init(&zc, mos, zapobj);
 845  853              zap_cursor_retrieve(&zc, &za) == 0;
 846  854              zap_cursor_advance(&zc)) {
 847      -                char *htag;
 848      -                uint64_t dsobj;
 849      -
 850  855                  htag = strchr(za.za_name, '-');
 851  856                  *htag = '\0';
 852  857                  ++htag;
 853  858                  dsobj = strtonum(za.za_name, NULL);
 854      -                dsl_dataset_user_release_tmp(dp, dsobj, htag);
      859 +                if (dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds) == 0) {
      860 +                        dsl_dataset_name(ds, name);
      861 +                        if (nvlist_lookup_nvlist(holds, name, &tags) != 0) {
      862 +                                tags = fnvlist_alloc();
      863 +                                fnvlist_add_boolean(tags, htag);
      864 +                                fnvlist_add_nvlist(holds, name, tags);
      865 +                                fnvlist_free(tags);
      866 +                        } else {
      867 +                                fnvlist_add_boolean(tags, htag);
      868 +                        }
      869 +                        dsl_dataset_rele(ds, FTAG);
      870 +                }
 855  871          }
      872 +        dsl_pool_config_exit(dp, FTAG);
      873 +        dsl_dataset_user_release(holds, NULL);
      874 +        fnvlist_free(holds);
 856  875          zap_cursor_fini(&zc);
 857  876  }
 858  877  
 859  878  /*
 860  879   * Create the pool-wide zap object for storing temporary snapshot holds.
 861  880   */
 862  881  void
 863  882  dsl_pool_user_hold_create_obj(dsl_pool_t *dp, dmu_tx_t *tx)
 864  883  {
 865  884          objset_t *mos = dp->dp_meta_objset;
↓ open down ↓ 166 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX