Print this page
4171 clean up spa_feature_*() interfaces
4172 implement extensible_dataset feature for use by other zpool features
Reviewed by: Max Grossman <max.grossman@delphix.com>
Reviewed by: Christopher Siden <christopher.siden@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>


  21 /*
  22  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2013 by Delphix. All rights reserved.
  24  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  25  */
  26 
  27 #include <sys/zfs_context.h>
  28 #include <sys/dsl_userhold.h>
  29 #include <sys/dsl_dataset.h>
  30 #include <sys/dsl_synctask.h>
  31 #include <sys/dmu_tx.h>
  32 #include <sys/dsl_pool.h>
  33 #include <sys/dsl_dir.h>
  34 #include <sys/dmu_traverse.h>
  35 #include <sys/dsl_scan.h>
  36 #include <sys/dmu_objset.h>
  37 #include <sys/zap.h>
  38 #include <sys/zfeature.h>
  39 #include <sys/zfs_ioctl.h>
  40 #include <sys/dsl_deleg.h>

  41 
  42 typedef struct dmu_snapshots_destroy_arg {
  43         nvlist_t *dsda_snaps;
  44         nvlist_t *dsda_successful_snaps;
  45         boolean_t dsda_defer;
  46         nvlist_t *dsda_errlist;
  47 } dmu_snapshots_destroy_arg_t;
  48 
  49 int
  50 dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)
  51 {
  52         if (!dsl_dataset_is_snapshot(ds))
  53                 return (SET_ERROR(EINVAL));
  54 
  55         if (dsl_dataset_long_held(ds))
  56                 return (SET_ERROR(EBUSY));
  57 
  58         /*
  59          * Only allow deferred destroy on pools that support it.
  60          * NOTE: deferred destroy is only supported on snapshots.


 431         dsl_dataset_rele(ds_head, FTAG);
 432 
 433         if (ds_prev != NULL)
 434                 dsl_dataset_rele(ds_prev, FTAG);
 435 
 436         spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
 437 
 438         if (ds->ds_phys->ds_next_clones_obj != 0) {
 439                 uint64_t count;
 440                 ASSERT0(zap_count(mos,
 441                     ds->ds_phys->ds_next_clones_obj, &count) && count == 0);
 442                 VERIFY0(dmu_object_free(mos,
 443                     ds->ds_phys->ds_next_clones_obj, tx));
 444         }
 445         if (ds->ds_phys->ds_props_obj != 0)
 446                 VERIFY0(zap_destroy(mos, ds->ds_phys->ds_props_obj, tx));
 447         if (ds->ds_phys->ds_userrefs_obj != 0)
 448                 VERIFY0(zap_destroy(mos, ds->ds_phys->ds_userrefs_obj, tx));
 449         dsl_dir_rele(ds->ds_dir, ds);
 450         ds->ds_dir = NULL;
 451         VERIFY0(dmu_object_free(mos, obj, tx));
 452 }
 453 
 454 static void
 455 dsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx)
 456 {
 457         dmu_snapshots_destroy_arg_t *dsda = arg;
 458         dsl_pool_t *dp = dmu_tx_pool(tx);
 459         nvpair_t *pair;
 460 
 461         for (pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, NULL);
 462             pair != NULL;
 463             pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, pair)) {
 464                 dsl_dataset_t *ds;
 465 
 466                 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
 467 
 468                 dsl_destroy_snapshot_sync_impl(ds, dsda->dsda_defer, tx);
 469                 dsl_dataset_rele(ds, FTAG);
 470         }
 471 }


 654         ASSERT0(dd->dd_phys->dd_head_dataset_obj);
 655 
 656         /*
 657          * Remove our reservation. The impl() routine avoids setting the
 658          * actual property, which would require the (already destroyed) ds.
 659          */
 660         dsl_dir_set_reservation_sync_impl(dd, 0, tx);
 661 
 662         ASSERT0(dd->dd_phys->dd_used_bytes);
 663         ASSERT0(dd->dd_phys->dd_reserved);
 664         for (t = 0; t < DD_USED_NUM; t++)
 665                 ASSERT0(dd->dd_phys->dd_used_breakdown[t]);
 666 
 667         VERIFY0(zap_destroy(mos, dd->dd_phys->dd_child_dir_zapobj, tx));
 668         VERIFY0(zap_destroy(mos, dd->dd_phys->dd_props_zapobj, tx));
 669         VERIFY0(dsl_deleg_destroy(mos, dd->dd_phys->dd_deleg_zapobj, tx));
 670         VERIFY0(zap_remove(mos,
 671             dd->dd_parent->dd_phys->dd_child_dir_zapobj, dd->dd_myname, tx));
 672 
 673         dsl_dir_rele(dd, FTAG);
 674         VERIFY0(dmu_object_free(mos, ddobj, tx));
 675 }
 676 
 677 void
 678 dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
 679 {
 680         dsl_pool_t *dp = dmu_tx_pool(tx);
 681         objset_t *mos = dp->dp_meta_objset;
 682         uint64_t obj, ddobj, prevobj = 0;
 683         boolean_t rmorigin;
 684 
 685         ASSERT3U(ds->ds_phys->ds_num_children, <=, 1);
 686         ASSERT(ds->ds_prev == NULL ||
 687             ds->ds_prev->ds_phys->ds_next_snap_obj != ds->ds_object);
 688         ASSERT3U(ds->ds_phys->ds_bp.blk_birth, <=, tx->tx_txg);
 689         ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
 690 
 691         /* We need to log before removing it from the namespace. */
 692         spa_history_log_internal_ds(ds, "destroy", tx, "");
 693 
 694         rmorigin = (dsl_dir_is_clone(ds->ds_dir) &&


 707         dsl_scan_ds_destroyed(ds, tx);
 708 
 709         obj = ds->ds_object;
 710 
 711         if (ds->ds_phys->ds_prev_snap_obj != 0) {
 712                 /* This is a clone */
 713                 ASSERT(ds->ds_prev != NULL);
 714                 ASSERT3U(ds->ds_prev->ds_phys->ds_next_snap_obj, !=, obj);
 715                 ASSERT0(ds->ds_phys->ds_next_snap_obj);
 716 
 717                 dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
 718                 if (ds->ds_prev->ds_phys->ds_next_clones_obj != 0) {
 719                         dsl_dataset_remove_from_next_clones(ds->ds_prev,
 720                             obj, tx);
 721                 }
 722 
 723                 ASSERT3U(ds->ds_prev->ds_phys->ds_num_children, >, 1);
 724                 ds->ds_prev->ds_phys->ds_num_children--;
 725         }
 726 
 727         zfeature_info_t *async_destroy =
 728             &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY];
 729         objset_t *os;
 730 
 731         /*
 732          * Destroy the deadlist.  Unless it's a clone, the
 733          * deadlist should be empty.  (If it's a clone, it's
 734          * safe to ignore the deadlist contents.)
 735          */
 736         dsl_deadlist_close(&ds->ds_deadlist);
 737         dsl_deadlist_free(mos, ds->ds_phys->ds_deadlist_obj, tx);
 738         dmu_buf_will_dirty(ds->ds_dbuf, tx);
 739         ds->ds_phys->ds_deadlist_obj = 0;
 740 

 741         VERIFY0(dmu_objset_from_ds(ds, &os));
 742 
 743         if (!spa_feature_is_enabled(dp->dp_spa, async_destroy)) {
 744                 old_synchronous_dataset_destroy(ds, tx);
 745         } else {
 746                 /*
 747                  * Move the bptree into the pool's list of trees to
 748                  * clean up and update space accounting information.
 749                  */
 750                 uint64_t used, comp, uncomp;
 751 
 752                 zil_destroy_sync(dmu_objset_zil(os), tx);
 753 
 754                 if (!spa_feature_is_active(dp->dp_spa, async_destroy)) {

 755                         dsl_scan_t *scn = dp->dp_scan;
 756 
 757                         spa_feature_incr(dp->dp_spa, async_destroy, tx);
 758                         dp->dp_bptree_obj = bptree_alloc(mos, tx);
 759                         VERIFY0(zap_add(mos,
 760                             DMU_POOL_DIRECTORY_OBJECT,
 761                             DMU_POOL_BPTREE_OBJ, sizeof (uint64_t), 1,
 762                             &dp->dp_bptree_obj, tx));
 763                         ASSERT(!scn->scn_async_destroying);
 764                         scn->scn_async_destroying = B_TRUE;
 765                 }
 766 
 767                 used = ds->ds_dir->dd_phys->dd_used_bytes;
 768                 comp = ds->ds_dir->dd_phys->dd_compressed_bytes;
 769                 uncomp = ds->ds_dir->dd_phys->dd_uncompressed_bytes;
 770 
 771                 ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||
 772                     ds->ds_phys->ds_unique_bytes == used);
 773 
 774                 bptree_add(mos, dp->dp_bptree_obj,
 775                     &ds->ds_phys->ds_bp, ds->ds_phys->ds_prev_snap_txg,
 776                     used, comp, uncomp, tx);
 777                 dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD,


 797          */
 798         if (ds->ds_objset) {
 799                 dmu_objset_evict(ds->ds_objset);
 800                 ds->ds_objset = NULL;
 801         }
 802 
 803         /* Erase the link in the dir */
 804         dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
 805         ds->ds_dir->dd_phys->dd_head_dataset_obj = 0;
 806         ddobj = ds->ds_dir->dd_object;
 807         ASSERT(ds->ds_phys->ds_snapnames_zapobj != 0);
 808         VERIFY0(zap_destroy(mos, ds->ds_phys->ds_snapnames_zapobj, tx));
 809 
 810         spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
 811 
 812         ASSERT0(ds->ds_phys->ds_next_clones_obj);
 813         ASSERT0(ds->ds_phys->ds_props_obj);
 814         ASSERT0(ds->ds_phys->ds_userrefs_obj);
 815         dsl_dir_rele(ds->ds_dir, ds);
 816         ds->ds_dir = NULL;
 817         VERIFY0(dmu_object_free(mos, obj, tx));
 818 
 819         dsl_dir_destroy_sync(ddobj, tx);
 820 
 821         if (rmorigin) {
 822                 dsl_dataset_t *prev;
 823                 VERIFY0(dsl_dataset_hold_obj(dp, prevobj, FTAG, &prev));
 824                 dsl_destroy_snapshot_sync_impl(prev, B_FALSE, tx);
 825                 dsl_dataset_rele(prev, FTAG);
 826         }
 827 }
 828 
 829 static void
 830 dsl_destroy_head_sync(void *arg, dmu_tx_t *tx)
 831 {
 832         dsl_destroy_head_arg_t *ddha = arg;
 833         dsl_pool_t *dp = dmu_tx_pool(tx);
 834         dsl_dataset_t *ds;
 835 
 836         VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds));
 837         dsl_destroy_head_sync_impl(ds, tx);


 853 
 854         spa_history_log_internal_ds(ds, "destroy begin", tx, "");
 855         dsl_dataset_rele(ds, FTAG);
 856 }
 857 
 858 int
 859 dsl_destroy_head(const char *name)
 860 {
 861         dsl_destroy_head_arg_t ddha;
 862         int error;
 863         spa_t *spa;
 864         boolean_t isenabled;
 865 
 866 #ifdef _KERNEL
 867         zfs_destroy_unmount_origin(name);
 868 #endif
 869 
 870         error = spa_open(name, &spa, FTAG);
 871         if (error != 0)
 872                 return (error);
 873         isenabled = spa_feature_is_enabled(spa,
 874             &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY]);
 875         spa_close(spa, FTAG);
 876 
 877         ddha.ddha_name = name;
 878 
 879         if (!isenabled) {
 880                 objset_t *os;
 881 
 882                 error = dsl_sync_task(name, dsl_destroy_head_check,
 883                     dsl_destroy_head_begin_sync, &ddha, 0);
 884                 if (error != 0)
 885                         return (error);
 886 
 887                 /*
 888                  * Head deletion is processed in one txg on old pools;
 889                  * remove the objects from open context so that the txg sync
 890                  * is not too long.
 891                  */
 892                 error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, FTAG, &os);
 893                 if (error == 0) {
 894                         uint64_t prev_snap_txg =




  21 /*
  22  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2013 by Delphix. All rights reserved.
  24  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  25  */
  26 
  27 #include <sys/zfs_context.h>
  28 #include <sys/dsl_userhold.h>
  29 #include <sys/dsl_dataset.h>
  30 #include <sys/dsl_synctask.h>
  31 #include <sys/dmu_tx.h>
  32 #include <sys/dsl_pool.h>
  33 #include <sys/dsl_dir.h>
  34 #include <sys/dmu_traverse.h>
  35 #include <sys/dsl_scan.h>
  36 #include <sys/dmu_objset.h>
  37 #include <sys/zap.h>
  38 #include <sys/zfeature.h>
  39 #include <sys/zfs_ioctl.h>
  40 #include <sys/dsl_deleg.h>
  41 #include <sys/dmu_impl.h>
  42 
  43 typedef struct dmu_snapshots_destroy_arg {
  44         nvlist_t *dsda_snaps;
  45         nvlist_t *dsda_successful_snaps;
  46         boolean_t dsda_defer;
  47         nvlist_t *dsda_errlist;
  48 } dmu_snapshots_destroy_arg_t;
  49 
  50 int
  51 dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)
  52 {
  53         if (!dsl_dataset_is_snapshot(ds))
  54                 return (SET_ERROR(EINVAL));
  55 
  56         if (dsl_dataset_long_held(ds))
  57                 return (SET_ERROR(EBUSY));
  58 
  59         /*
  60          * Only allow deferred destroy on pools that support it.
  61          * NOTE: deferred destroy is only supported on snapshots.


 432         dsl_dataset_rele(ds_head, FTAG);
 433 
 434         if (ds_prev != NULL)
 435                 dsl_dataset_rele(ds_prev, FTAG);
 436 
 437         spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
 438 
 439         if (ds->ds_phys->ds_next_clones_obj != 0) {
 440                 uint64_t count;
 441                 ASSERT0(zap_count(mos,
 442                     ds->ds_phys->ds_next_clones_obj, &count) && count == 0);
 443                 VERIFY0(dmu_object_free(mos,
 444                     ds->ds_phys->ds_next_clones_obj, tx));
 445         }
 446         if (ds->ds_phys->ds_props_obj != 0)
 447                 VERIFY0(zap_destroy(mos, ds->ds_phys->ds_props_obj, tx));
 448         if (ds->ds_phys->ds_userrefs_obj != 0)
 449                 VERIFY0(zap_destroy(mos, ds->ds_phys->ds_userrefs_obj, tx));
 450         dsl_dir_rele(ds->ds_dir, ds);
 451         ds->ds_dir = NULL;
 452         dmu_object_free_zapified(mos, obj, tx);
 453 }
 454 
 455 static void
 456 dsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx)
 457 {
 458         dmu_snapshots_destroy_arg_t *dsda = arg;
 459         dsl_pool_t *dp = dmu_tx_pool(tx);
 460         nvpair_t *pair;
 461 
 462         for (pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, NULL);
 463             pair != NULL;
 464             pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, pair)) {
 465                 dsl_dataset_t *ds;
 466 
 467                 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
 468 
 469                 dsl_destroy_snapshot_sync_impl(ds, dsda->dsda_defer, tx);
 470                 dsl_dataset_rele(ds, FTAG);
 471         }
 472 }


 655         ASSERT0(dd->dd_phys->dd_head_dataset_obj);
 656 
 657         /*
 658          * Remove our reservation. The impl() routine avoids setting the
 659          * actual property, which would require the (already destroyed) ds.
 660          */
 661         dsl_dir_set_reservation_sync_impl(dd, 0, tx);
 662 
 663         ASSERT0(dd->dd_phys->dd_used_bytes);
 664         ASSERT0(dd->dd_phys->dd_reserved);
 665         for (t = 0; t < DD_USED_NUM; t++)
 666                 ASSERT0(dd->dd_phys->dd_used_breakdown[t]);
 667 
 668         VERIFY0(zap_destroy(mos, dd->dd_phys->dd_child_dir_zapobj, tx));
 669         VERIFY0(zap_destroy(mos, dd->dd_phys->dd_props_zapobj, tx));
 670         VERIFY0(dsl_deleg_destroy(mos, dd->dd_phys->dd_deleg_zapobj, tx));
 671         VERIFY0(zap_remove(mos,
 672             dd->dd_parent->dd_phys->dd_child_dir_zapobj, dd->dd_myname, tx));
 673 
 674         dsl_dir_rele(dd, FTAG);
 675         dmu_object_free_zapified(mos, ddobj, tx);
 676 }
 677 
 678 void
 679 dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
 680 {
 681         dsl_pool_t *dp = dmu_tx_pool(tx);
 682         objset_t *mos = dp->dp_meta_objset;
 683         uint64_t obj, ddobj, prevobj = 0;
 684         boolean_t rmorigin;
 685 
 686         ASSERT3U(ds->ds_phys->ds_num_children, <=, 1);
 687         ASSERT(ds->ds_prev == NULL ||
 688             ds->ds_prev->ds_phys->ds_next_snap_obj != ds->ds_object);
 689         ASSERT3U(ds->ds_phys->ds_bp.blk_birth, <=, tx->tx_txg);
 690         ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
 691 
 692         /* We need to log before removing it from the namespace. */
 693         spa_history_log_internal_ds(ds, "destroy", tx, "");
 694 
 695         rmorigin = (dsl_dir_is_clone(ds->ds_dir) &&


 708         dsl_scan_ds_destroyed(ds, tx);
 709 
 710         obj = ds->ds_object;
 711 
 712         if (ds->ds_phys->ds_prev_snap_obj != 0) {
 713                 /* This is a clone */
 714                 ASSERT(ds->ds_prev != NULL);
 715                 ASSERT3U(ds->ds_prev->ds_phys->ds_next_snap_obj, !=, obj);
 716                 ASSERT0(ds->ds_phys->ds_next_snap_obj);
 717 
 718                 dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
 719                 if (ds->ds_prev->ds_phys->ds_next_clones_obj != 0) {
 720                         dsl_dataset_remove_from_next_clones(ds->ds_prev,
 721                             obj, tx);
 722                 }
 723 
 724                 ASSERT3U(ds->ds_prev->ds_phys->ds_num_children, >, 1);
 725                 ds->ds_prev->ds_phys->ds_num_children--;
 726         }
 727 




 728         /*
 729          * Destroy the deadlist.  Unless it's a clone, the
 730          * deadlist should be empty.  (If it's a clone, it's
 731          * safe to ignore the deadlist contents.)
 732          */
 733         dsl_deadlist_close(&ds->ds_deadlist);
 734         dsl_deadlist_free(mos, ds->ds_phys->ds_deadlist_obj, tx);
 735         dmu_buf_will_dirty(ds->ds_dbuf, tx);
 736         ds->ds_phys->ds_deadlist_obj = 0;
 737 
 738         objset_t *os;
 739         VERIFY0(dmu_objset_from_ds(ds, &os));
 740 
 741         if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY)) {
 742                 old_synchronous_dataset_destroy(ds, tx);
 743         } else {
 744                 /*
 745                  * Move the bptree into the pool's list of trees to
 746                  * clean up and update space accounting information.
 747                  */
 748                 uint64_t used, comp, uncomp;
 749 
 750                 zil_destroy_sync(dmu_objset_zil(os), tx);
 751 
 752                 if (!spa_feature_is_active(dp->dp_spa,
 753                     SPA_FEATURE_ASYNC_DESTROY)) {
 754                         dsl_scan_t *scn = dp->dp_scan;
 755                         spa_feature_incr(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY,
 756                             tx);
 757                         dp->dp_bptree_obj = bptree_alloc(mos, tx);
 758                         VERIFY0(zap_add(mos,
 759                             DMU_POOL_DIRECTORY_OBJECT,
 760                             DMU_POOL_BPTREE_OBJ, sizeof (uint64_t), 1,
 761                             &dp->dp_bptree_obj, tx));
 762                         ASSERT(!scn->scn_async_destroying);
 763                         scn->scn_async_destroying = B_TRUE;
 764                 }
 765 
 766                 used = ds->ds_dir->dd_phys->dd_used_bytes;
 767                 comp = ds->ds_dir->dd_phys->dd_compressed_bytes;
 768                 uncomp = ds->ds_dir->dd_phys->dd_uncompressed_bytes;
 769 
 770                 ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||
 771                     ds->ds_phys->ds_unique_bytes == used);
 772 
 773                 bptree_add(mos, dp->dp_bptree_obj,
 774                     &ds->ds_phys->ds_bp, ds->ds_phys->ds_prev_snap_txg,
 775                     used, comp, uncomp, tx);
 776                 dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD,


 796          */
 797         if (ds->ds_objset) {
 798                 dmu_objset_evict(ds->ds_objset);
 799                 ds->ds_objset = NULL;
 800         }
 801 
 802         /* Erase the link in the dir */
 803         dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
 804         ds->ds_dir->dd_phys->dd_head_dataset_obj = 0;
 805         ddobj = ds->ds_dir->dd_object;
 806         ASSERT(ds->ds_phys->ds_snapnames_zapobj != 0);
 807         VERIFY0(zap_destroy(mos, ds->ds_phys->ds_snapnames_zapobj, tx));
 808 
 809         spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
 810 
 811         ASSERT0(ds->ds_phys->ds_next_clones_obj);
 812         ASSERT0(ds->ds_phys->ds_props_obj);
 813         ASSERT0(ds->ds_phys->ds_userrefs_obj);
 814         dsl_dir_rele(ds->ds_dir, ds);
 815         ds->ds_dir = NULL;
 816         dmu_object_free_zapified(mos, obj, tx);
 817 
 818         dsl_dir_destroy_sync(ddobj, tx);
 819 
 820         if (rmorigin) {
 821                 dsl_dataset_t *prev;
 822                 VERIFY0(dsl_dataset_hold_obj(dp, prevobj, FTAG, &prev));
 823                 dsl_destroy_snapshot_sync_impl(prev, B_FALSE, tx);
 824                 dsl_dataset_rele(prev, FTAG);
 825         }
 826 }
 827 
 828 static void
 829 dsl_destroy_head_sync(void *arg, dmu_tx_t *tx)
 830 {
 831         dsl_destroy_head_arg_t *ddha = arg;
 832         dsl_pool_t *dp = dmu_tx_pool(tx);
 833         dsl_dataset_t *ds;
 834 
 835         VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds));
 836         dsl_destroy_head_sync_impl(ds, tx);


 852 
 853         spa_history_log_internal_ds(ds, "destroy begin", tx, "");
 854         dsl_dataset_rele(ds, FTAG);
 855 }
 856 
 857 int
 858 dsl_destroy_head(const char *name)
 859 {
 860         dsl_destroy_head_arg_t ddha;
 861         int error;
 862         spa_t *spa;
 863         boolean_t isenabled;
 864 
 865 #ifdef _KERNEL
 866         zfs_destroy_unmount_origin(name);
 867 #endif
 868 
 869         error = spa_open(name, &spa, FTAG);
 870         if (error != 0)
 871                 return (error);
 872         isenabled = spa_feature_is_enabled(spa, SPA_FEATURE_ASYNC_DESTROY);

 873         spa_close(spa, FTAG);
 874 
 875         ddha.ddha_name = name;
 876 
 877         if (!isenabled) {
 878                 objset_t *os;
 879 
 880                 error = dsl_sync_task(name, dsl_destroy_head_check,
 881                     dsl_destroy_head_begin_sync, &ddha, 0);
 882                 if (error != 0)
 883                         return (error);
 884 
 885                 /*
 886                  * Head deletion is processed in one txg on old pools;
 887                  * remove the objects from open context so that the txg sync
 888                  * is not too long.
 889                  */
 890                 error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, FTAG, &os);
 891                 if (error == 0) {
 892                         uint64_t prev_snap_txg =