]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/dsl_destroy.c
OpenZFS 7614, 9064 - zfs device evacuation/removal
[mirror_zfs.git] / module / zfs / dsl_destroy.c
index dc49fd558c3caa7a5143005d4a6292105e1640a7..e11508c90d0f260551eeee4740160a3f24671ac8 100644 (file)
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
  * Copyright (c) 2013 Steven Hartland. All rights reserved.
+ * Copyright (c) 2013 by Joyent, Inc. All rights reserved.
+ * Copyright (c) 2016 Actifio, Inc. All rights reserved.
  */
 
 #include <sys/zfs_context.h>
 #include <sys/dsl_userhold.h>
 #include <sys/dsl_dataset.h>
 #include <sys/dsl_synctask.h>
+#include <sys/dsl_destroy.h>
 #include <sys/dmu_tx.h>
 #include <sys/dsl_pool.h>
 #include <sys/dsl_dir.h>
 #include <sys/zfs_ioctl.h>
 #include <sys/dsl_deleg.h>
 #include <sys/dmu_impl.h>
-
-typedef struct dmu_snapshots_destroy_arg {
-       nvlist_t *dsda_snaps;
-       nvlist_t *dsda_successful_snaps;
-       boolean_t dsda_defer;
-       nvlist_t *dsda_errlist;
-} dmu_snapshots_destroy_arg_t;
+#include <sys/zvol.h>
+#include <sys/zcp.h>
 
 int
 dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)
 {
-       if (!dsl_dataset_is_snapshot(ds))
+       if (!ds->ds_is_snapshot)
                return (SET_ERROR(EINVAL));
 
        if (dsl_dataset_long_held(ds))
@@ -77,57 +75,39 @@ dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)
        /*
         * Can't delete a branch point.
         */
-       if (ds->ds_phys->ds_num_children > 1)
+       if (dsl_dataset_phys(ds)->ds_num_children > 1)
                return (SET_ERROR(EEXIST));
 
        return (0);
 }
 
-static int
+int
 dsl_destroy_snapshot_check(void *arg, dmu_tx_t *tx)
 {
-       dmu_snapshots_destroy_arg_t *dsda = arg;
+       dsl_destroy_snapshot_arg_t *ddsa = arg;
+       const char *dsname = ddsa->ddsa_name;
+       boolean_t defer = ddsa->ddsa_defer;
+
        dsl_pool_t *dp = dmu_tx_pool(tx);
-       nvpair_t *pair;
        int error = 0;
+       dsl_dataset_t *ds;
 
-       if (!dmu_tx_is_syncing(tx))
-               return (0);
-
-       for (pair = nvlist_next_nvpair(dsda->dsda_snaps, NULL);
-           pair != NULL; pair = nvlist_next_nvpair(dsda->dsda_snaps, pair)) {
-               dsl_dataset_t *ds;
-
-               error = dsl_dataset_hold(dp, nvpair_name(pair),
-                   FTAG, &ds);
-
-               /*
-                * If the snapshot does not exist, silently ignore it
-                * (it's "already destroyed").
-                */
-               if (error == ENOENT)
-                       continue;
+       error = dsl_dataset_hold(dp, dsname, FTAG, &ds);
 
-               if (error == 0) {
-                       error = dsl_destroy_snapshot_check_impl(ds,
-                           dsda->dsda_defer);
-                       dsl_dataset_rele(ds, FTAG);
-               }
+       /*
+        * If the snapshot does not exist, silently ignore it, and
+        * dsl_destroy_snapshot_sync() will be a no-op
+        * (it's "already destroyed").
+        */
+       if (error == ENOENT)
+               return (0);
 
-               if (error == 0) {
-                       fnvlist_add_boolean(dsda->dsda_successful_snaps,
-                           nvpair_name(pair));
-               } else {
-                       fnvlist_add_int32(dsda->dsda_errlist,
-                           nvpair_name(pair), error);
-               }
+       if (error == 0) {
+               error = dsl_destroy_snapshot_check_impl(ds, defer);
+               dsl_dataset_rele(ds, FTAG);
        }
 
-       pair = nvlist_next_nvpair(dsda->dsda_errlist, NULL);
-       if (pair != NULL)
-               return (fnvpair_value_int32(pair));
-
-       return (0);
+       return (error);
 }
 
 struct process_old_arg {
@@ -146,12 +126,12 @@ process_old_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
 
        ASSERT(!BP_IS_HOLE(bp));
 
-       if (bp->blk_birth <= poa->ds->ds_phys->ds_prev_snap_txg) {
+       if (bp->blk_birth <= dsl_dataset_phys(poa->ds)->ds_prev_snap_txg) {
                dsl_deadlist_insert(&poa->ds->ds_deadlist, bp, tx);
                if (poa->ds_prev && !poa->after_branch_point &&
                    bp->blk_birth >
-                   poa->ds_prev->ds_phys->ds_prev_snap_txg) {
-                       poa->ds_prev->ds_phys->ds_unique_bytes +=
+                   dsl_dataset_phys(poa->ds_prev)->ds_prev_snap_txg) {
+                       dsl_dataset_phys(poa->ds_prev)->ds_unique_bytes +=
                            bp_get_dsize_sync(dp->dp_spa, bp);
                }
        } else {
@@ -182,7 +162,7 @@ process_old_deadlist(dsl_dataset_t *ds, dsl_dataset_t *ds_prev,
        VERIFY0(bpobj_iterate(&ds_next->ds_deadlist.dl_bpobj,
            process_old_cb, &poa, tx));
        VERIFY0(zio_wait(poa.pio));
-       ASSERT3U(poa.used, ==, ds->ds_phys->ds_unique_bytes);
+       ASSERT3U(poa.used, ==, dsl_dataset_phys(ds)->ds_unique_bytes);
 
        /* change snapused */
        dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP,
@@ -191,12 +171,14 @@ process_old_deadlist(dsl_dataset_t *ds, dsl_dataset_t *ds_prev,
        /* swap next's deadlist to our deadlist */
        dsl_deadlist_close(&ds->ds_deadlist);
        dsl_deadlist_close(&ds_next->ds_deadlist);
-       deadlist_obj = ds->ds_phys->ds_deadlist_obj;
-       ds->ds_phys->ds_deadlist_obj = ds_next->ds_phys->ds_deadlist_obj;
-       ds_next->ds_phys->ds_deadlist_obj = deadlist_obj;
-       dsl_deadlist_open(&ds->ds_deadlist, mos, ds->ds_phys->ds_deadlist_obj);
+       deadlist_obj = dsl_dataset_phys(ds)->ds_deadlist_obj;
+       dsl_dataset_phys(ds)->ds_deadlist_obj =
+           dsl_dataset_phys(ds_next)->ds_deadlist_obj;
+       dsl_dataset_phys(ds_next)->ds_deadlist_obj = deadlist_obj;
+       dsl_deadlist_open(&ds->ds_deadlist, mos,
+           dsl_dataset_phys(ds)->ds_deadlist_obj);
        dsl_deadlist_open(&ds_next->ds_deadlist, mos,
-           ds_next->ds_phys->ds_deadlist_obj);
+           dsl_dataset_phys(ds_next)->ds_deadlist_obj);
 }
 
 static void
@@ -211,13 +193,13 @@ dsl_dataset_remove_clones_key(dsl_dataset_t *ds, uint64_t mintxg, dmu_tx_t *tx)
         * find the clones, but dsl_deadlist_remove_key() is a no-op so it
         * doesn't matter.
         */
-       if (ds->ds_dir->dd_phys->dd_clones == 0)
+       if (dsl_dir_phys(ds->ds_dir)->dd_clones == 0)
                return;
 
-       zc = kmem_alloc(sizeof (zap_cursor_t), KM_PUSHPAGE);
-       za = kmem_alloc(sizeof (zap_attribute_t), KM_PUSHPAGE);
+       zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP);
+       za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
 
-       for (zap_cursor_init(zc, mos, ds->ds_dir->dd_phys->dd_clones);
+       for (zap_cursor_init(zc, mos, dsl_dir_phys(ds->ds_dir)->dd_clones);
            zap_cursor_retrieve(zc, za) == 0;
            zap_cursor_advance(zc)) {
                dsl_dataset_t *clone;
@@ -227,6 +209,10 @@ dsl_dataset_remove_clones_key(dsl_dataset_t *ds, uint64_t mintxg, dmu_tx_t *tx)
                if (clone->ds_dir->dd_origin_txg > mintxg) {
                        dsl_deadlist_remove_key(&clone->ds_deadlist,
                            mintxg, tx);
+                       if (dsl_dataset_remap_deadlist_exists(clone)) {
+                               dsl_deadlist_remove_key(
+                                   &clone->ds_remap_deadlist, mintxg, tx);
+                       }
                        dsl_dataset_remove_clones_key(clone, mintxg, tx);
                }
                dsl_dataset_rele(clone, FTAG);
@@ -237,34 +223,65 @@ dsl_dataset_remove_clones_key(dsl_dataset_t *ds, uint64_t mintxg, dmu_tx_t *tx)
        kmem_free(zc, sizeof (zap_cursor_t));
 }
 
+static void
+dsl_destroy_snapshot_handle_remaps(dsl_dataset_t *ds, dsl_dataset_t *ds_next,
+    dmu_tx_t *tx)
+{
+       dsl_pool_t *dp = ds->ds_dir->dd_pool;
+
+       /* Move blocks to be obsoleted to pool's obsolete list. */
+       if (dsl_dataset_remap_deadlist_exists(ds_next)) {
+               if (!bpobj_is_open(&dp->dp_obsolete_bpobj))
+                       dsl_pool_create_obsolete_bpobj(dp, tx);
+
+               dsl_deadlist_move_bpobj(&ds_next->ds_remap_deadlist,
+                   &dp->dp_obsolete_bpobj,
+                   dsl_dataset_phys(ds)->ds_prev_snap_txg, tx);
+       }
+
+       /* Merge our deadlist into next's and free it. */
+       if (dsl_dataset_remap_deadlist_exists(ds)) {
+               uint64_t remap_deadlist_object =
+                   dsl_dataset_get_remap_deadlist_object(ds);
+               ASSERT(remap_deadlist_object != 0);
+
+               mutex_enter(&ds_next->ds_remap_deadlist_lock);
+               if (!dsl_dataset_remap_deadlist_exists(ds_next))
+                       dsl_dataset_create_remap_deadlist(ds_next, tx);
+               mutex_exit(&ds_next->ds_remap_deadlist_lock);
+
+               dsl_deadlist_merge(&ds_next->ds_remap_deadlist,
+                   remap_deadlist_object, tx);
+               dsl_dataset_destroy_remap_deadlist(ds, tx);
+       }
+}
+
 void
 dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
 {
-#ifdef ZFS_DEBUG
-       int err;
-#endif
        int after_branch_point = FALSE;
        dsl_pool_t *dp = ds->ds_dir->dd_pool;
        objset_t *mos = dp->dp_meta_objset;
        dsl_dataset_t *ds_prev = NULL;
-       uint64_t obj, old_unique, used = 0, comp = 0, uncomp = 0;
-       dsl_dataset_t *ds_next, *ds_head, *hds;
-
+       uint64_t obj;
 
        ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
-       ASSERT3U(ds->ds_phys->ds_bp.blk_birth, <=, tx->tx_txg);
+       rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
+       ASSERT3U(dsl_dataset_phys(ds)->ds_bp.blk_birth, <=, tx->tx_txg);
+       rrw_exit(&ds->ds_bp_rwlock, FTAG);
        ASSERT(refcount_is_zero(&ds->ds_longholds));
 
        if (defer &&
-           (ds->ds_userrefs > 0 || ds->ds_phys->ds_num_children > 1)) {
+           (ds->ds_userrefs > 0 ||
+           dsl_dataset_phys(ds)->ds_num_children > 1)) {
                ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS);
                dmu_buf_will_dirty(ds->ds_dbuf, tx);
-               ds->ds_phys->ds_flags |= DS_FLAG_DEFER_DESTROY;
+               dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_DEFER_DESTROY;
                spa_history_log_internal_ds(ds, "defer_destroy", tx, "");
                return;
        }
 
-       ASSERT3U(ds->ds_phys->ds_num_children, <=, 1);
+       ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);
 
        /* We need to log before removing it from the namespace. */
        spa_history_log_internal_ds(ds, "destroy", tx, "");
@@ -273,42 +290,54 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
 
        obj = ds->ds_object;
 
-       if (ds->ds_phys->ds_prev_snap_obj != 0) {
+       for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
+               if (ds->ds_feature_inuse[f]) {
+                       dsl_dataset_deactivate_feature(obj, f, tx);
+                       ds->ds_feature_inuse[f] = B_FALSE;
+               }
+       }
+       if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
                ASSERT3P(ds->ds_prev, ==, NULL);
                VERIFY0(dsl_dataset_hold_obj(dp,
-                   ds->ds_phys->ds_prev_snap_obj, FTAG, &ds_prev));
+                   dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &ds_prev));
                after_branch_point =
-                   (ds_prev->ds_phys->ds_next_snap_obj != obj);
+                   (dsl_dataset_phys(ds_prev)->ds_next_snap_obj != obj);
 
                dmu_buf_will_dirty(ds_prev->ds_dbuf, tx);
                if (after_branch_point &&
-                   ds_prev->ds_phys->ds_next_clones_obj != 0) {
+                   dsl_dataset_phys(ds_prev)->ds_next_clones_obj != 0) {
                        dsl_dataset_remove_from_next_clones(ds_prev, obj, tx);
-                       if (ds->ds_phys->ds_next_snap_obj != 0) {
+                       if (dsl_dataset_phys(ds)->ds_next_snap_obj != 0) {
                                VERIFY0(zap_add_int(mos,
-                                   ds_prev->ds_phys->ds_next_clones_obj,
-                                   ds->ds_phys->ds_next_snap_obj, tx));
+                                   dsl_dataset_phys(ds_prev)->
+                                   ds_next_clones_obj,
+                                   dsl_dataset_phys(ds)->ds_next_snap_obj,
+                                   tx));
                        }
                }
                if (!after_branch_point) {
-                       ds_prev->ds_phys->ds_next_snap_obj =
-                           ds->ds_phys->ds_next_snap_obj;
+                       dsl_dataset_phys(ds_prev)->ds_next_snap_obj =
+                           dsl_dataset_phys(ds)->ds_next_snap_obj;
                }
        }
 
+       dsl_dataset_t *ds_next;
+       uint64_t old_unique;
+       uint64_t used = 0, comp = 0, uncomp = 0;
+
        VERIFY0(dsl_dataset_hold_obj(dp,
-           ds->ds_phys->ds_next_snap_obj, FTAG, &ds_next));
-       ASSERT3U(ds_next->ds_phys->ds_prev_snap_obj, ==, obj);
+           dsl_dataset_phys(ds)->ds_next_snap_obj, FTAG, &ds_next));
+       ASSERT3U(dsl_dataset_phys(ds_next)->ds_prev_snap_obj, ==, obj);
 
-       old_unique = ds_next->ds_phys->ds_unique_bytes;
+       old_unique = dsl_dataset_phys(ds_next)->ds_unique_bytes;
 
        dmu_buf_will_dirty(ds_next->ds_dbuf, tx);
-       ds_next->ds_phys->ds_prev_snap_obj =
-           ds->ds_phys->ds_prev_snap_obj;
-       ds_next->ds_phys->ds_prev_snap_txg =
-           ds->ds_phys->ds_prev_snap_txg;
-       ASSERT3U(ds->ds_phys->ds_prev_snap_txg, ==,
-           ds_prev ? ds_prev->ds_phys->ds_creation_txg : 0);
+       dsl_dataset_phys(ds_next)->ds_prev_snap_obj =
+           dsl_dataset_phys(ds)->ds_prev_snap_obj;
+       dsl_dataset_phys(ds_next)->ds_prev_snap_txg =
+           dsl_dataset_phys(ds)->ds_prev_snap_txg;
+       ASSERT3U(dsl_dataset_phys(ds)->ds_prev_snap_txg, ==,
+           ds_prev ? dsl_dataset_phys(ds_prev)->ds_creation_txg : 0);
 
        if (ds_next->ds_deadlist.dl_oldfmt) {
                process_old_deadlist(ds, ds_prev, ds_next,
@@ -317,40 +346,43 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
                /* Adjust prev's unique space. */
                if (ds_prev && !after_branch_point) {
                        dsl_deadlist_space_range(&ds_next->ds_deadlist,
-                           ds_prev->ds_phys->ds_prev_snap_txg,
-                           ds->ds_phys->ds_prev_snap_txg,
+                           dsl_dataset_phys(ds_prev)->ds_prev_snap_txg,
+                           dsl_dataset_phys(ds)->ds_prev_snap_txg,
                            &used, &comp, &uncomp);
-                       ds_prev->ds_phys->ds_unique_bytes += used;
+                       dsl_dataset_phys(ds_prev)->ds_unique_bytes += used;
                }
 
                /* Adjust snapused. */
                dsl_deadlist_space_range(&ds_next->ds_deadlist,
-                   ds->ds_phys->ds_prev_snap_txg, UINT64_MAX,
+                   dsl_dataset_phys(ds)->ds_prev_snap_txg, UINT64_MAX,
                    &used, &comp, &uncomp);
                dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP,
                    -used, -comp, -uncomp, tx);
 
                /* Move blocks to be freed to pool's free list. */
                dsl_deadlist_move_bpobj(&ds_next->ds_deadlist,
-                   &dp->dp_free_bpobj, ds->ds_phys->ds_prev_snap_txg,
+                   &dp->dp_free_bpobj, dsl_dataset_phys(ds)->ds_prev_snap_txg,
                    tx);
                dsl_dir_diduse_space(tx->tx_pool->dp_free_dir,
                    DD_USED_HEAD, used, comp, uncomp, tx);
 
                /* Merge our deadlist into next's and free it. */
                dsl_deadlist_merge(&ds_next->ds_deadlist,
-                   ds->ds_phys->ds_deadlist_obj, tx);
+                   dsl_dataset_phys(ds)->ds_deadlist_obj, tx);
        }
+
        dsl_deadlist_close(&ds->ds_deadlist);
-       dsl_deadlist_free(mos, ds->ds_phys->ds_deadlist_obj, tx);
+       dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx);
        dmu_buf_will_dirty(ds->ds_dbuf, tx);
-       ds->ds_phys->ds_deadlist_obj = 0;
+       dsl_dataset_phys(ds)->ds_deadlist_obj = 0;
+
+       dsl_destroy_snapshot_handle_remaps(ds, ds_next, tx);
 
        /* Collapse range in clone heads */
        dsl_dataset_remove_clones_key(ds,
-           ds->ds_phys->ds_creation_txg, tx);
+           dsl_dataset_phys(ds)->ds_creation_txg, tx);
 
-       if (dsl_dataset_is_snapshot(ds_next)) {
+       if (ds_next->ds_is_snapshot) {
                dsl_dataset_t *ds_nextnext;
 
                /*
@@ -363,20 +395,26 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
                 * deadlist).
                 */
                VERIFY0(dsl_dataset_hold_obj(dp,
-                   ds_next->ds_phys->ds_next_snap_obj, FTAG, &ds_nextnext));
+                   dsl_dataset_phys(ds_next)->ds_next_snap_obj,
+                   FTAG, &ds_nextnext));
                dsl_deadlist_space_range(&ds_nextnext->ds_deadlist,
-                   ds->ds_phys->ds_prev_snap_txg,
-                   ds->ds_phys->ds_creation_txg,
+                   dsl_dataset_phys(ds)->ds_prev_snap_txg,
+                   dsl_dataset_phys(ds)->ds_creation_txg,
                    &used, &comp, &uncomp);
-               ds_next->ds_phys->ds_unique_bytes += used;
+               dsl_dataset_phys(ds_next)->ds_unique_bytes += used;
                dsl_dataset_rele(ds_nextnext, FTAG);
                ASSERT3P(ds_next->ds_prev, ==, NULL);
 
                /* Collapse range in this head. */
+               dsl_dataset_t *hds;
                VERIFY0(dsl_dataset_hold_obj(dp,
-                   ds->ds_dir->dd_phys->dd_head_dataset_obj, FTAG, &hds));
+                   dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &hds));
                dsl_deadlist_remove_key(&hds->ds_deadlist,
-                   ds->ds_phys->ds_creation_txg, tx);
+                   dsl_dataset_phys(ds)->ds_creation_txg, tx);
+               if (dsl_dataset_remap_deadlist_exists(hds)) {
+                       dsl_deadlist_remove_key(&hds->ds_remap_deadlist,
+                           dsl_dataset_phys(ds)->ds_creation_txg, tx);
+               }
                dsl_dataset_rele(hds, FTAG);
 
        } else {
@@ -385,7 +423,7 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
                ds_next->ds_prev = NULL;
                if (ds_prev) {
                        VERIFY0(dsl_dataset_hold_obj(dp,
-                           ds->ds_phys->ds_prev_snap_obj,
+                           dsl_dataset_phys(ds)->ds_prev_snap_obj,
                            ds_next, &ds_next->ds_prev));
                }
 
@@ -399,7 +437,7 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
                if (old_unique < ds_next->ds_reserved) {
                        int64_t mrsdelta;
                        uint64_t new_unique =
-                           ds_next->ds_phys->ds_unique_bytes;
+                           dsl_dataset_phys(ds_next)->ds_unique_bytes;
 
                        ASSERT(old_unique <= new_unique);
                        mrsdelta = MIN(new_unique - old_unique,
@@ -420,13 +458,15 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
        }
 
        /* remove from snapshot namespace */
-       ASSERT(ds->ds_phys->ds_snapnames_zapobj == 0);
+       dsl_dataset_t *ds_head;
+       ASSERT(dsl_dataset_phys(ds)->ds_snapnames_zapobj == 0);
        VERIFY0(dsl_dataset_hold_obj(dp,
-           ds->ds_dir->dd_phys->dd_head_dataset_obj, FTAG, &ds_head));
+           dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &ds_head));
        VERIFY0(dsl_dataset_get_snapname(ds));
 #ifdef ZFS_DEBUG
        {
                uint64_t val;
+               int err;
 
                err = dsl_dataset_snap_lookup(ds_head,
                    ds->ds_snapname, &val);
@@ -434,7 +474,7 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
                ASSERT3U(val, ==, obj);
        }
 #endif
-       VERIFY0(dsl_dataset_snap_remove(ds_head, ds->ds_snapname, tx));
+       VERIFY0(dsl_dataset_snap_remove(ds_head, ds->ds_snapname, tx, B_TRUE));
        dsl_dataset_rele(ds_head, FTAG);
 
        if (ds_prev != NULL)
@@ -442,39 +482,42 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
 
        spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
 
-       if (ds->ds_phys->ds_next_clones_obj != 0) {
+       if (dsl_dataset_phys(ds)->ds_next_clones_obj != 0) {
                ASSERTV(uint64_t count);
                ASSERT0(zap_count(mos,
-                   ds->ds_phys->ds_next_clones_obj, &count) && count == 0);
+                   dsl_dataset_phys(ds)->ds_next_clones_obj, &count) &&
+                   count == 0);
                VERIFY0(dmu_object_free(mos,
-                   ds->ds_phys->ds_next_clones_obj, tx));
+                   dsl_dataset_phys(ds)->ds_next_clones_obj, tx));
        }
-       if (ds->ds_phys->ds_props_obj != 0)
-               VERIFY0(zap_destroy(mos, ds->ds_phys->ds_props_obj, tx));
-       if (ds->ds_phys->ds_userrefs_obj != 0)
-               VERIFY0(zap_destroy(mos, ds->ds_phys->ds_userrefs_obj, tx));
+       if (dsl_dataset_phys(ds)->ds_props_obj != 0)
+               VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_props_obj,
+                   tx));
+       if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0)
+               VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
+                   tx));
        dsl_dir_rele(ds->ds_dir, ds);
        ds->ds_dir = NULL;
        dmu_object_free_zapified(mos, obj, tx);
 }
 
-static void
+void
 dsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx)
 {
-       dmu_snapshots_destroy_arg_t *dsda = arg;
-       dsl_pool_t *dp = dmu_tx_pool(tx);
-       nvpair_t *pair;
-
-       for (pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, NULL);
-           pair != NULL;
-           pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, pair)) {
-               dsl_dataset_t *ds;
+       dsl_destroy_snapshot_arg_t *ddsa = arg;
+       const char *dsname = ddsa->ddsa_name;
+       boolean_t defer = ddsa->ddsa_defer;
 
-               VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
+       dsl_pool_t *dp = dmu_tx_pool(tx);
+       dsl_dataset_t *ds;
 
-               dsl_destroy_snapshot_sync_impl(ds, dsda->dsda_defer, tx);
-               dsl_dataset_rele(ds, FTAG);
-       }
+       int error = dsl_dataset_hold(dp, dsname, FTAG, &ds);
+       if (error == ENOENT)
+               return;
+       ASSERT0(error);
+       dsl_destroy_snapshot_sync_impl(ds, defer, tx);
+       zvol_remove_minors(dp->dp_spa, dsname, B_TRUE);
+       dsl_dataset_rele(ds, FTAG);
 }
 
 /*
@@ -494,37 +537,101 @@ int
 dsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer,
     nvlist_t *errlist)
 {
-       dmu_snapshots_destroy_arg_t dsda;
-       int error;
-       nvpair_t *pair;
-
-       pair = nvlist_next_nvpair(snaps, NULL);
-       if (pair == NULL)
+       if (nvlist_next_nvpair(snaps, NULL) == NULL)
                return (0);
 
-       dsda.dsda_snaps = snaps;
-       VERIFY0(nvlist_alloc(&dsda.dsda_successful_snaps,
-           NV_UNIQUE_NAME, KM_PUSHPAGE));
-       dsda.dsda_defer = defer;
-       dsda.dsda_errlist = errlist;
+       /*
+        * lzc_destroy_snaps() is documented to take an nvlist whose
+        * values "don't matter".  We need to convert that nvlist to
+        * one that we know can be converted to LUA. We also don't
+        * care about any duplicate entries because the nvlist will
+        * be converted to a LUA table which should take care of this.
+        */
+       nvlist_t *snaps_normalized;
+       VERIFY0(nvlist_alloc(&snaps_normalized, 0, KM_SLEEP));
+       for (nvpair_t *pair = nvlist_next_nvpair(snaps, NULL);
+           pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) {
+               fnvlist_add_boolean_value(snaps_normalized,
+                   nvpair_name(pair), B_TRUE);
+       }
 
-       error = dsl_sync_task(nvpair_name(pair),
-           dsl_destroy_snapshot_check, dsl_destroy_snapshot_sync,
-           &dsda, 0);
-       fnvlist_free(dsda.dsda_successful_snaps);
+       nvlist_t *arg;
+       VERIFY0(nvlist_alloc(&arg, 0, KM_SLEEP));
+       fnvlist_add_nvlist(arg, "snaps", snaps_normalized);
+       fnvlist_free(snaps_normalized);
+       fnvlist_add_boolean_value(arg, "defer", defer);
+
+       nvlist_t *wrapper;
+       VERIFY0(nvlist_alloc(&wrapper, 0, KM_SLEEP));
+       fnvlist_add_nvlist(wrapper, ZCP_ARG_ARGLIST, arg);
+       fnvlist_free(arg);
+
+       const char *program =
+           "arg = ...\n"
+           "snaps = arg['snaps']\n"
+           "defer = arg['defer']\n"
+           "errors = { }\n"
+           "has_errors = false\n"
+           "for snap, v in pairs(snaps) do\n"
+           "    errno = zfs.check.destroy{snap, defer=defer}\n"
+           "    zfs.debug('snap: ' .. snap .. ' errno: ' .. errno)\n"
+           "    if errno == ENOENT then\n"
+           "        snaps[snap] = nil\n"
+           "    elseif errno ~= 0 then\n"
+           "        errors[snap] = errno\n"
+           "        has_errors = true\n"
+           "    end\n"
+           "end\n"
+           "if has_errors then\n"
+           "    return errors\n"
+           "end\n"
+           "for snap, v in pairs(snaps) do\n"
+           "    errno = zfs.sync.destroy{snap, defer=defer}\n"
+           "    assert(errno == 0)\n"
+           "end\n"
+           "return { }\n";
+
+       nvlist_t *result = fnvlist_alloc();
+       int error = zcp_eval(nvpair_name(nvlist_next_nvpair(snaps, NULL)),
+           program,
+           B_TRUE,
+           0,
+           zfs_lua_max_memlimit,
+           nvlist_next_nvpair(wrapper, NULL), result);
+       if (error != 0) {
+               char *errorstr = NULL;
+               (void) nvlist_lookup_string(result, ZCP_RET_ERROR, &errorstr);
+               if (errorstr != NULL) {
+                       zfs_dbgmsg(errorstr);
+               }
+               return (error);
+       }
+       fnvlist_free(wrapper);
 
-       return (error);
+       /*
+        * lzc_destroy_snaps() is documented to fill the errlist with
+        * int32 values, so we need to covert the int64 values that are
+        * returned from LUA.
+        */
+       int rv = 0;
+       nvlist_t *errlist_raw = fnvlist_lookup_nvlist(result, ZCP_RET_RETURN);
+       for (nvpair_t *pair = nvlist_next_nvpair(errlist_raw, NULL);
+           pair != NULL; pair = nvlist_next_nvpair(errlist_raw, pair)) {
+               int32_t val = (int32_t)fnvpair_value_int64(pair);
+               if (rv == 0)
+                       rv = val;
+               fnvlist_add_int32(errlist, nvpair_name(pair), val);
+       }
+       fnvlist_free(result);
+       return (rv);
 }
 
 int
 dsl_destroy_snapshot(const char *name, boolean_t defer)
 {
        int error;
-       nvlist_t *nvl;
-       nvlist_t *errlist;
-
-       VERIFY0(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_PUSHPAGE));
-       VERIFY0(nvlist_alloc(&errlist, NV_UNIQUE_NAME, KM_PUSHPAGE));
+       nvlist_t *nvl = fnvlist_alloc();
+       nvlist_t *errlist = fnvlist_alloc();
 
        fnvlist_add_boolean(nvl, name);
        error = dsl_destroy_snapshots_nvl(nvl, defer, errlist);
@@ -541,12 +648,12 @@ struct killarg {
 /* ARGSUSED */
 static int
 kill_blkptr(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
-    const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg)
+    const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg)
 {
        struct killarg *ka = arg;
        dmu_tx_t *tx = ka->tx;
 
-       if (BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp))
+       if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp))
                return (0);
 
        if (zb->zb_level == ZB_ZIL_LEVEL) {
@@ -558,7 +665,8 @@ kill_blkptr(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
                dsl_free(ka->tx->tx_pool, ka->tx->tx_txg, bp);
        } else {
                ASSERT(zilog == NULL);
-               ASSERT3U(bp->blk_birth, >, ka->ds->ds_phys->ds_prev_snap_txg);
+               ASSERT3U(bp->blk_birth, >,
+                   dsl_dataset_phys(ka->ds)->ds_prev_snap_txg);
                (void) dsl_dataset_block_kill(ka->ds, bp, tx, B_FALSE);
        }
 
@@ -580,15 +688,12 @@ old_synchronous_dataset_destroy(dsl_dataset_t *ds, dmu_tx_t *tx)
        ka.ds = ds;
        ka.tx = tx;
        VERIFY0(traverse_dataset(ds,
-           ds->ds_phys->ds_prev_snap_txg, TRAVERSE_POST,
-           kill_blkptr, &ka));
-       ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) || ds->ds_phys->ds_unique_bytes == 0);
+           dsl_dataset_phys(ds)->ds_prev_snap_txg, TRAVERSE_POST |
+           TRAVERSE_NO_DECRYPT, kill_blkptr, &ka));
+       ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||
+           dsl_dataset_phys(ds)->ds_unique_bytes == 0);
 }
 
-typedef struct dsl_destroy_head_arg {
-       const char *ddha_name;
-} dsl_destroy_head_arg_t;
-
 int
 dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds)
 {
@@ -596,8 +701,8 @@ dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds)
        uint64_t count;
        objset_t *mos;
 
-       ASSERT(!dsl_dataset_is_snapshot(ds));
-       if (dsl_dataset_is_snapshot(ds))
+       ASSERT(!ds->ds_is_snapshot);
+       if (ds->ds_is_snapshot)
                return (SET_ERROR(EINVAL));
 
        if (refcount_count(&ds->ds_longholds) != expected_holds)
@@ -611,21 +716,21 @@ dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds)
         * from.)
         */
        if (ds->ds_prev != NULL &&
-           ds->ds_prev->ds_phys->ds_next_snap_obj == ds->ds_object)
+           dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj == ds->ds_object)
                return (SET_ERROR(EBUSY));
 
        /*
         * Can't delete if there are children of this fs.
         */
        error = zap_count(mos,
-           ds->ds_dir->dd_phys->dd_child_dir_zapobj, &count);
+           dsl_dir_phys(ds->ds_dir)->dd_child_dir_zapobj, &count);
        if (error != 0)
                return (error);
        if (count != 0)
                return (SET_ERROR(EEXIST));
 
        if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev) &&
-           ds->ds_prev->ds_phys->ds_num_children == 2 &&
+           dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 &&
            ds->ds_prev->ds_userrefs == 0) {
                /* We need to remove the origin snapshot as well. */
                if (!refcount_is_zero(&ds->ds_prev->ds_longholds))
@@ -634,7 +739,7 @@ dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds)
        return (0);
 }
 
-static int
+int
 dsl_destroy_head_check(void *arg, dmu_tx_t *tx)
 {
        dsl_destroy_head_arg_t *ddha = arg;
@@ -663,7 +768,18 @@ dsl_dir_destroy_sync(uint64_t ddobj, dmu_tx_t *tx)
 
        VERIFY0(dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd));
 
-       ASSERT0(dd->dd_phys->dd_head_dataset_obj);
+       ASSERT0(dsl_dir_phys(dd)->dd_head_dataset_obj);
+
+       /*
+        * Decrement the filesystem count for all parent filesystems.
+        *
+        * When we receive an incremental stream into a filesystem that already
+        * exists, a temporary clone is created.  We never count this temporary
+        * clone, whose name begins with a '%'.
+        */
+       if (dd->dd_myname[0] != '%' && dd->dd_parent != NULL)
+               dsl_fs_ss_count_adjust(dd->dd_parent, -1,
+                   DD_FIELD_FILESYSTEM_COUNT, tx);
 
        /*
         * Remove our reservation. The impl() routine avoids setting the
@@ -671,16 +787,22 @@ dsl_dir_destroy_sync(uint64_t ddobj, dmu_tx_t *tx)
         */
        dsl_dir_set_reservation_sync_impl(dd, 0, tx);
 
-       ASSERT0(dd->dd_phys->dd_used_bytes);
-       ASSERT0(dd->dd_phys->dd_reserved);
+       ASSERT0(dsl_dir_phys(dd)->dd_used_bytes);
+       ASSERT0(dsl_dir_phys(dd)->dd_reserved);
        for (t = 0; t < DD_USED_NUM; t++)
-               ASSERT0(dd->dd_phys->dd_used_breakdown[t]);
+               ASSERT0(dsl_dir_phys(dd)->dd_used_breakdown[t]);
+
+       if (dd->dd_crypto_obj != 0) {
+               dsl_crypto_key_destroy_sync(dd->dd_crypto_obj, tx);
+               (void) spa_keystore_unload_wkey_impl(dp->dp_spa, dd->dd_object);
+       }
 
-       VERIFY0(zap_destroy(mos, dd->dd_phys->dd_child_dir_zapobj, tx));
-       VERIFY0(zap_destroy(mos, dd->dd_phys->dd_props_zapobj, tx));
-       VERIFY0(dsl_deleg_destroy(mos, dd->dd_phys->dd_deleg_zapobj, tx));
+       VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_child_dir_zapobj, tx));
+       VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_props_zapobj, tx));
+       VERIFY0(dsl_deleg_destroy(mos, dsl_dir_phys(dd)->dd_deleg_zapobj, tx));
        VERIFY0(zap_remove(mos,
-           dd->dd_parent->dd_phys->dd_child_dir_zapobj, dd->dd_myname, tx));
+           dsl_dir_phys(dd->dd_parent)->dd_child_dir_zapobj,
+           dd->dd_myname, tx));
 
        dsl_dir_rele(dd, FTAG);
        dmu_object_free_zapified(mos, ddobj, tx);
@@ -693,12 +815,13 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
        objset_t *mos = dp->dp_meta_objset;
        uint64_t obj, ddobj, prevobj = 0;
        boolean_t rmorigin;
-       objset_t *os;
 
-       ASSERT3U(ds->ds_phys->ds_num_children, <=, 1);
+       ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);
        ASSERT(ds->ds_prev == NULL ||
-           ds->ds_prev->ds_phys->ds_next_snap_obj != ds->ds_object);
-       ASSERT3U(ds->ds_phys->ds_bp.blk_birth, <=, tx->tx_txg);
+           dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj != ds->ds_object);
+       rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
+       ASSERT3U(dsl_dataset_phys(ds)->ds_bp.blk_birth, <=, tx->tx_txg);
+       rrw_exit(&ds->ds_bp_rwlock, FTAG);
        ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
 
        /* We need to log before removing it from the namespace. */
@@ -706,7 +829,7 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
 
        rmorigin = (dsl_dir_is_clone(ds->ds_dir) &&
            DS_IS_DEFER_DESTROY(ds->ds_prev) &&
-           ds->ds_prev->ds_phys->ds_num_children == 2 &&
+           dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 &&
            ds->ds_prev->ds_userrefs == 0);
 
        /* Remove our reservation. */
@@ -717,36 +840,49 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
                ASSERT0(ds->ds_reserved);
        }
 
-       dsl_scan_ds_destroyed(ds, tx);
-
        obj = ds->ds_object;
 
-       if (ds->ds_phys->ds_prev_snap_obj != 0) {
+       for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
+               if (ds->ds_feature_inuse[f]) {
+                       dsl_dataset_deactivate_feature(obj, f, tx);
+                       ds->ds_feature_inuse[f] = B_FALSE;
+               }
+       }
+
+       dsl_scan_ds_destroyed(ds, tx);
+
+       if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
                /* This is a clone */
                ASSERT(ds->ds_prev != NULL);
-               ASSERT3U(ds->ds_prev->ds_phys->ds_next_snap_obj, !=, obj);
-               ASSERT0(ds->ds_phys->ds_next_snap_obj);
+               ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj, !=,
+                   obj);
+               ASSERT0(dsl_dataset_phys(ds)->ds_next_snap_obj);
 
                dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
-               if (ds->ds_prev->ds_phys->ds_next_clones_obj != 0) {
+               if (dsl_dataset_phys(ds->ds_prev)->ds_next_clones_obj != 0) {
                        dsl_dataset_remove_from_next_clones(ds->ds_prev,
                            obj, tx);
                }
 
-               ASSERT3U(ds->ds_prev->ds_phys->ds_num_children, >, 1);
-               ds->ds_prev->ds_phys->ds_num_children--;
+               ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_num_children, >, 1);
+               dsl_dataset_phys(ds->ds_prev)->ds_num_children--;
        }
 
        /*
         * Destroy the deadlist.  Unless it's a clone, the
-        * deadlist should be empty.  (If it's a clone, it's
-        * safe to ignore the deadlist contents.)
+        * deadlist should be empty since the dataset has no snapshots.
+        * (If it's a clone, it's safe to ignore the deadlist contents
+        * since they are still referenced by the origin snapshot.)
         */
        dsl_deadlist_close(&ds->ds_deadlist);
-       dsl_deadlist_free(mos, ds->ds_phys->ds_deadlist_obj, tx);
+       dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx);
        dmu_buf_will_dirty(ds->ds_dbuf, tx);
-       ds->ds_phys->ds_deadlist_obj = 0;
+       dsl_dataset_phys(ds)->ds_deadlist_obj = 0;
 
+       if (dsl_dataset_remap_deadlist_exists(ds))
+               dsl_dataset_destroy_remap_deadlist(ds, tx);
+
+       objset_t *os;
        VERIFY0(dmu_objset_from_ds(ds, &os));
 
        if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY)) {
@@ -774,16 +910,19 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
                        scn->scn_async_destroying = B_TRUE;
                }
 
-               used = ds->ds_dir->dd_phys->dd_used_bytes;
-               comp = ds->ds_dir->dd_phys->dd_compressed_bytes;
-               uncomp = ds->ds_dir->dd_phys->dd_uncompressed_bytes;
+               used = dsl_dir_phys(ds->ds_dir)->dd_used_bytes;
+               comp = dsl_dir_phys(ds->ds_dir)->dd_compressed_bytes;
+               uncomp = dsl_dir_phys(ds->ds_dir)->dd_uncompressed_bytes;
 
                ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||
-                   ds->ds_phys->ds_unique_bytes == used);
+                   dsl_dataset_phys(ds)->ds_unique_bytes == used);
 
+               rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
                bptree_add(mos, dp->dp_bptree_obj,
-                   &ds->ds_phys->ds_bp, ds->ds_phys->ds_prev_snap_txg,
+                   &dsl_dataset_phys(ds)->ds_bp,
+                   dsl_dataset_phys(ds)->ds_prev_snap_txg,
                    used, comp, uncomp, tx);
+               rrw_exit(&ds->ds_bp_rwlock, FTAG);
                dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD,
                    -used, -comp, -uncomp, tx);
                dsl_dir_diduse_space(dp->dp_free_dir, DD_USED_HEAD,
@@ -793,7 +932,7 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
        if (ds->ds_prev != NULL) {
                if (spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) {
                        VERIFY0(zap_remove_int(mos,
-                           ds->ds_prev->ds_dir->dd_phys->dd_clones,
+                           dsl_dir_phys(ds->ds_prev->ds_dir)->dd_clones,
                            ds->ds_object, tx));
                }
                prevobj = ds->ds_prev->ds_object;
@@ -812,22 +951,22 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
 
        /* Erase the link in the dir */
        dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
-       ds->ds_dir->dd_phys->dd_head_dataset_obj = 0;
+       dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj = 0;
        ddobj = ds->ds_dir->dd_object;
-       ASSERT(ds->ds_phys->ds_snapnames_zapobj != 0);
-       VERIFY0(zap_destroy(mos, ds->ds_phys->ds_snapnames_zapobj, tx));
+       ASSERT(dsl_dataset_phys(ds)->ds_snapnames_zapobj != 0);
+       VERIFY0(zap_destroy(mos,
+           dsl_dataset_phys(ds)->ds_snapnames_zapobj, tx));
 
        if (ds->ds_bookmarks != 0) {
-               VERIFY0(zap_destroy(mos,
-                   ds->ds_bookmarks, tx));
+               VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
                spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
        }
 
        spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
 
-       ASSERT0(ds->ds_phys->ds_next_clones_obj);
-       ASSERT0(ds->ds_phys->ds_props_obj);
-       ASSERT0(ds->ds_phys->ds_userrefs_obj);
+       ASSERT0(dsl_dataset_phys(ds)->ds_next_clones_obj);
+       ASSERT0(dsl_dataset_phys(ds)->ds_props_obj);
+       ASSERT0(dsl_dataset_phys(ds)->ds_userrefs_obj);
        dsl_dir_rele(ds->ds_dir, ds);
        ds->ds_dir = NULL;
        dmu_object_free_zapified(mos, obj, tx);
@@ -842,7 +981,7 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
        }
 }
 
-static void
+void
 dsl_destroy_head_sync(void *arg, dmu_tx_t *tx)
 {
        dsl_destroy_head_arg_t *ddha = arg;
@@ -851,6 +990,7 @@ dsl_destroy_head_sync(void *arg, dmu_tx_t *tx)
 
        VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds));
        dsl_destroy_head_sync_impl(ds, tx);
+       zvol_remove_minors(dp->dp_spa, ddha->ddha_name, B_TRUE);
        dsl_dataset_rele(ds, FTAG);
 }
 
@@ -865,7 +1005,7 @@ dsl_destroy_head_begin_sync(void *arg, dmu_tx_t *tx)
 
        /* Mark it as inconsistent on-disk, in case we crash */
        dmu_buf_will_dirty(ds->ds_dbuf, tx);
-       ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
+       dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT;
 
        spa_history_log_internal_ds(ds, "destroy begin", tx, "");
        dsl_dataset_rele(ds, FTAG);
@@ -895,7 +1035,8 @@ dsl_destroy_head(const char *name)
                objset_t *os;
 
                error = dsl_sync_task(name, dsl_destroy_head_check,
-                   dsl_destroy_head_begin_sync, &ddha, 0);
+                   dsl_destroy_head_begin_sync, &ddha,
+                   0, ZFS_SPACE_CHECK_NONE);
                if (error != 0)
                        return (error);
 
@@ -904,23 +1045,24 @@ dsl_destroy_head(const char *name)
                 * remove the objects from open context so that the txg sync
                 * is not too long.
                 */
-               error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, FTAG, &os);
+               error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, B_FALSE,
+                   FTAG, &os);
                if (error == 0) {
-                       uint64_t obj;
                        uint64_t prev_snap_txg =
-                           dmu_objset_ds(os)->ds_phys->ds_prev_snap_txg;
-                       for (obj = 0; error == 0;
+                           dsl_dataset_phys(dmu_objset_ds(os))->
+                           ds_prev_snap_txg;
+                       for (uint64_t obj = 0; error == 0;
                            error = dmu_object_next(os, &obj, FALSE,
                            prev_snap_txg))
                                (void) dmu_free_long_object(os, obj);
                        /* sync out all frees */
                        txg_wait_synced(dmu_objset_pool(os), 0);
-                       dmu_objset_disown(os, FTAG);
+                       dmu_objset_disown(os, B_FALSE, FTAG);
                }
        }
 
        return (dsl_sync_task(name, dsl_destroy_head_check,
-           dsl_destroy_head_sync, &ddha, 0));
+           dsl_destroy_head_sync, &ddha, 0, ZFS_SPACE_CHECK_NONE));
 }
 
 /*
@@ -936,9 +1078,17 @@ dsl_destroy_inconsistent(const char *dsname, void *arg)
        objset_t *os;
 
        if (dmu_objset_hold(dsname, FTAG, &os) == 0) {
-               boolean_t inconsistent = DS_IS_INCONSISTENT(dmu_objset_ds(os));
+               boolean_t need_destroy = DS_IS_INCONSISTENT(dmu_objset_ds(os));
+
+               /*
+                * If the dataset is inconsistent because a resumable receive
+                * has failed, then do not destroy it.
+                */
+               if (dsl_dataset_has_resume_receive_state(dmu_objset_ds(os)))
+                       need_destroy = B_FALSE;
+
                dmu_objset_rele(os, FTAG);
-               if (inconsistent)
+               if (need_destroy)
                        (void) dsl_destroy_head(dsname);
        }
        return (0);