1247580Smm/*
2247580Smm * CDDL HEADER START
3247580Smm *
4247580Smm * The contents of this file are subject to the terms of the
5247580Smm * Common Development and Distribution License (the "License").
6247580Smm * You may not use this file except in compliance with the License.
7247580Smm *
8247580Smm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9247580Smm * or http://www.opensolaris.org/os/licensing.
10247580Smm * See the License for the specific language governing permissions
11247580Smm * and limitations under the License.
12247580Smm *
13247580Smm * When distributing Covered Code, include this CDDL HEADER in each
14247580Smm * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15247580Smm * If applicable, add the following below this CDDL HEADER, with the
16247580Smm * fields enclosed by brackets "[]" replaced with your own identifying
17247580Smm * information: Portions Copyright [yyyy] [name of copyright owner]
18247580Smm *
19247580Smm * CDDL HEADER END
20247580Smm */
21247580Smm/*
22247580Smm * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23288572Smav * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
24251646Sdelphij * Copyright (c) 2013 Steven Hartland. All rights reserved.
25265744Sdelphij * Copyright (c) 2013 by Joyent, Inc. All rights reserved.
26297112Smav * Copyright (c) 2014 Integros [integros.com]
27247580Smm */
28247580Smm
29247580Smm#include <sys/zfs_context.h>
30247580Smm#include <sys/dsl_userhold.h>
31247580Smm#include <sys/dsl_dataset.h>
32247580Smm#include <sys/dsl_synctask.h>
33247580Smm#include <sys/dmu_tx.h>
34247580Smm#include <sys/dsl_pool.h>
35247580Smm#include <sys/dsl_dir.h>
36247580Smm#include <sys/dmu_traverse.h>
37247580Smm#include <sys/dsl_scan.h>
38247580Smm#include <sys/dmu_objset.h>
39247580Smm#include <sys/zap.h>
40247580Smm#include <sys/zfeature.h>
41247580Smm#include <sys/zfs_ioctl.h>
42247580Smm#include <sys/dsl_deleg.h>
43263390Sdelphij#include <sys/dmu_impl.h>
44247580Smm
45247580Smmtypedef struct dmu_snapshots_destroy_arg {
46247580Smm	nvlist_t *dsda_snaps;
47247580Smm	nvlist_t *dsda_successful_snaps;
48247580Smm	boolean_t dsda_defer;
49247580Smm	nvlist_t *dsda_errlist;
50247580Smm} dmu_snapshots_destroy_arg_t;
51247580Smm
52253820Sdelphijint
53247580Smmdsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)
54247580Smm{
55288549Smav	if (!ds->ds_is_snapshot)
56249195Smm		return (SET_ERROR(EINVAL));
57247580Smm
58247580Smm	if (dsl_dataset_long_held(ds))
59249195Smm		return (SET_ERROR(EBUSY));
60247580Smm
61247580Smm	/*
62247580Smm	 * Only allow deferred destroy on pools that support it.
63247580Smm	 * NOTE: deferred destroy is only supported on snapshots.
64247580Smm	 */
65247580Smm	if (defer) {
66247580Smm		if (spa_version(ds->ds_dir->dd_pool->dp_spa) <
67247580Smm		    SPA_VERSION_USERREFS)
68249195Smm			return (SET_ERROR(ENOTSUP));
69247580Smm		return (0);
70247580Smm	}
71247580Smm
72247580Smm	/*
73247580Smm	 * If this snapshot has an elevated user reference count,
74247580Smm	 * we can't destroy it yet.
75247580Smm	 */
76247580Smm	if (ds->ds_userrefs > 0)
77249195Smm		return (SET_ERROR(EBUSY));
78247580Smm
79247580Smm	/*
80247580Smm	 * Can't delete a branch point.
81247580Smm	 */
82277585Sdelphij	if (dsl_dataset_phys(ds)->ds_num_children > 1)
83249195Smm		return (SET_ERROR(EEXIST));
84247580Smm
85247580Smm	return (0);
86247580Smm}
87247580Smm
88247580Smmstatic int
89247580Smmdsl_destroy_snapshot_check(void *arg, dmu_tx_t *tx)
90247580Smm{
91247580Smm	dmu_snapshots_destroy_arg_t *dsda = arg;
92247580Smm	dsl_pool_t *dp = dmu_tx_pool(tx);
93247580Smm	nvpair_t *pair;
94247580Smm	int error = 0;
95247580Smm
96247580Smm	if (!dmu_tx_is_syncing(tx))
97247580Smm		return (0);
98247580Smm
99247580Smm	for (pair = nvlist_next_nvpair(dsda->dsda_snaps, NULL);
100247580Smm	    pair != NULL; pair = nvlist_next_nvpair(dsda->dsda_snaps, pair)) {
101247580Smm		dsl_dataset_t *ds;
102247580Smm
103247580Smm		error = dsl_dataset_hold(dp, nvpair_name(pair),
104247580Smm		    FTAG, &ds);
105247580Smm
106247580Smm		/*
107247580Smm		 * If the snapshot does not exist, silently ignore it
108247580Smm		 * (it's "already destroyed").
109247580Smm		 */
110247580Smm		if (error == ENOENT)
111247580Smm			continue;
112247580Smm
113247580Smm		if (error == 0) {
114247580Smm			error = dsl_destroy_snapshot_check_impl(ds,
115247580Smm			    dsda->dsda_defer);
116247580Smm			dsl_dataset_rele(ds, FTAG);
117247580Smm		}
118247580Smm
119247580Smm		if (error == 0) {
120247580Smm			fnvlist_add_boolean(dsda->dsda_successful_snaps,
121247580Smm			    nvpair_name(pair));
122247580Smm		} else {
123247580Smm			fnvlist_add_int32(dsda->dsda_errlist,
124247580Smm			    nvpair_name(pair), error);
125247580Smm		}
126247580Smm	}
127247580Smm
128247580Smm	pair = nvlist_next_nvpair(dsda->dsda_errlist, NULL);
129247580Smm	if (pair != NULL)
130247580Smm		return (fnvpair_value_int32(pair));
131251646Sdelphij
132247580Smm	return (0);
133247580Smm}
134247580Smm
135247580Smmstruct process_old_arg {
136247580Smm	dsl_dataset_t *ds;
137247580Smm	dsl_dataset_t *ds_prev;
138247580Smm	boolean_t after_branch_point;
139247580Smm	zio_t *pio;
140247580Smm	uint64_t used, comp, uncomp;
141247580Smm};
142247580Smm
143247580Smmstatic int
144247580Smmprocess_old_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
145247580Smm{
146247580Smm	struct process_old_arg *poa = arg;
147247580Smm	dsl_pool_t *dp = poa->ds->ds_dir->dd_pool;
148247580Smm
149263397Sdelphij	ASSERT(!BP_IS_HOLE(bp));
150263397Sdelphij
151277585Sdelphij	if (bp->blk_birth <= dsl_dataset_phys(poa->ds)->ds_prev_snap_txg) {
152247580Smm		dsl_deadlist_insert(&poa->ds->ds_deadlist, bp, tx);
153247580Smm		if (poa->ds_prev && !poa->after_branch_point &&
154247580Smm		    bp->blk_birth >
155277585Sdelphij		    dsl_dataset_phys(poa->ds_prev)->ds_prev_snap_txg) {
156277585Sdelphij			dsl_dataset_phys(poa->ds_prev)->ds_unique_bytes +=
157247580Smm			    bp_get_dsize_sync(dp->dp_spa, bp);
158247580Smm		}
159247580Smm	} else {
160247580Smm		poa->used += bp_get_dsize_sync(dp->dp_spa, bp);
161247580Smm		poa->comp += BP_GET_PSIZE(bp);
162247580Smm		poa->uncomp += BP_GET_UCSIZE(bp);
163247580Smm		dsl_free_sync(poa->pio, dp, tx->tx_txg, bp);
164247580Smm	}
165247580Smm	return (0);
166247580Smm}
167247580Smm
168247580Smmstatic void
169247580Smmprocess_old_deadlist(dsl_dataset_t *ds, dsl_dataset_t *ds_prev,
170247580Smm    dsl_dataset_t *ds_next, boolean_t after_branch_point, dmu_tx_t *tx)
171247580Smm{
172247580Smm	struct process_old_arg poa = { 0 };
173247580Smm	dsl_pool_t *dp = ds->ds_dir->dd_pool;
174247580Smm	objset_t *mos = dp->dp_meta_objset;
175247580Smm	uint64_t deadlist_obj;
176247580Smm
177247580Smm	ASSERT(ds->ds_deadlist.dl_oldfmt);
178247580Smm	ASSERT(ds_next->ds_deadlist.dl_oldfmt);
179247580Smm
180247580Smm	poa.ds = ds;
181247580Smm	poa.ds_prev = ds_prev;
182247580Smm	poa.after_branch_point = after_branch_point;
183247580Smm	poa.pio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
184247580Smm	VERIFY0(bpobj_iterate(&ds_next->ds_deadlist.dl_bpobj,
185247580Smm	    process_old_cb, &poa, tx));
186247580Smm	VERIFY0(zio_wait(poa.pio));
187277585Sdelphij	ASSERT3U(poa.used, ==, dsl_dataset_phys(ds)->ds_unique_bytes);
188247580Smm
189247580Smm	/* change snapused */
190247580Smm	dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP,
191247580Smm	    -poa.used, -poa.comp, -poa.uncomp, tx);
192247580Smm
193247580Smm	/* swap next's deadlist to our deadlist */
194247580Smm	dsl_deadlist_close(&ds->ds_deadlist);
195247580Smm	dsl_deadlist_close(&ds_next->ds_deadlist);
196277585Sdelphij	deadlist_obj = dsl_dataset_phys(ds)->ds_deadlist_obj;
197277585Sdelphij	dsl_dataset_phys(ds)->ds_deadlist_obj =
198277585Sdelphij	    dsl_dataset_phys(ds_next)->ds_deadlist_obj;
199277585Sdelphij	dsl_dataset_phys(ds_next)->ds_deadlist_obj = deadlist_obj;
200277585Sdelphij	dsl_deadlist_open(&ds->ds_deadlist, mos,
201277585Sdelphij	    dsl_dataset_phys(ds)->ds_deadlist_obj);
202247580Smm	dsl_deadlist_open(&ds_next->ds_deadlist, mos,
203277585Sdelphij	    dsl_dataset_phys(ds_next)->ds_deadlist_obj);
204247580Smm}
205247580Smm
206247580Smmstatic void
207247580Smmdsl_dataset_remove_clones_key(dsl_dataset_t *ds, uint64_t mintxg, dmu_tx_t *tx)
208247580Smm{
209247580Smm	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
210247580Smm	zap_cursor_t zc;
211247580Smm	zap_attribute_t za;
212247580Smm
213247580Smm	/*
214247580Smm	 * If it is the old version, dd_clones doesn't exist so we can't
215247580Smm	 * find the clones, but dsl_deadlist_remove_key() is a no-op so it
216247580Smm	 * doesn't matter.
217247580Smm	 */
218277585Sdelphij	if (dsl_dir_phys(ds->ds_dir)->dd_clones == 0)
219247580Smm		return;
220247580Smm
221277585Sdelphij	for (zap_cursor_init(&zc, mos, dsl_dir_phys(ds->ds_dir)->dd_clones);
222247580Smm	    zap_cursor_retrieve(&zc, &za) == 0;
223247580Smm	    zap_cursor_advance(&zc)) {
224247580Smm		dsl_dataset_t *clone;
225247580Smm
226247580Smm		VERIFY0(dsl_dataset_hold_obj(ds->ds_dir->dd_pool,
227247580Smm		    za.za_first_integer, FTAG, &clone));
228247580Smm		if (clone->ds_dir->dd_origin_txg > mintxg) {
229247580Smm			dsl_deadlist_remove_key(&clone->ds_deadlist,
230247580Smm			    mintxg, tx);
231247580Smm			dsl_dataset_remove_clones_key(clone, mintxg, tx);
232247580Smm		}
233247580Smm		dsl_dataset_rele(clone, FTAG);
234247580Smm	}
235247580Smm	zap_cursor_fini(&zc);
236247580Smm}
237247580Smm
238247580Smmvoid
239247580Smmdsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
240247580Smm{
241247580Smm	int err;
242247580Smm	int after_branch_point = FALSE;
243247580Smm	dsl_pool_t *dp = ds->ds_dir->dd_pool;
244247580Smm	objset_t *mos = dp->dp_meta_objset;
245247580Smm	dsl_dataset_t *ds_prev = NULL;
246247580Smm	uint64_t obj;
247247580Smm
248247580Smm	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
249308083Smav	rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
250277585Sdelphij	ASSERT3U(dsl_dataset_phys(ds)->ds_bp.blk_birth, <=, tx->tx_txg);
251308083Smav	rrw_exit(&ds->ds_bp_rwlock, FTAG);
252247580Smm	ASSERT(refcount_is_zero(&ds->ds_longholds));
253247580Smm
254247580Smm	if (defer &&
255277585Sdelphij	    (ds->ds_userrefs > 0 ||
256277585Sdelphij	    dsl_dataset_phys(ds)->ds_num_children > 1)) {
257247580Smm		ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS);
258247580Smm		dmu_buf_will_dirty(ds->ds_dbuf, tx);
259277585Sdelphij		dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_DEFER_DESTROY;
260247580Smm		spa_history_log_internal_ds(ds, "defer_destroy", tx, "");
261247580Smm		return;
262247580Smm	}
263247580Smm
264277585Sdelphij	ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);
265247580Smm
266247580Smm	/* We need to log before removing it from the namespace. */
267247580Smm	spa_history_log_internal_ds(ds, "destroy", tx, "");
268247580Smm
269247580Smm	dsl_scan_ds_destroyed(ds, tx);
270247580Smm
271247580Smm	obj = ds->ds_object;
272247580Smm
273288572Smav	for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
274288572Smav		if (ds->ds_feature_inuse[f]) {
275288572Smav			dsl_dataset_deactivate_feature(obj, f, tx);
276288572Smav			ds->ds_feature_inuse[f] = B_FALSE;
277288572Smav		}
278276081Sdelphij	}
279277585Sdelphij	if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
280247580Smm		ASSERT3P(ds->ds_prev, ==, NULL);
281247580Smm		VERIFY0(dsl_dataset_hold_obj(dp,
282277585Sdelphij		    dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &ds_prev));
283247580Smm		after_branch_point =
284277585Sdelphij		    (dsl_dataset_phys(ds_prev)->ds_next_snap_obj != obj);
285247580Smm
286247580Smm		dmu_buf_will_dirty(ds_prev->ds_dbuf, tx);
287247580Smm		if (after_branch_point &&
288277585Sdelphij		    dsl_dataset_phys(ds_prev)->ds_next_clones_obj != 0) {
289247580Smm			dsl_dataset_remove_from_next_clones(ds_prev, obj, tx);
290277585Sdelphij			if (dsl_dataset_phys(ds)->ds_next_snap_obj != 0) {
291247580Smm				VERIFY0(zap_add_int(mos,
292277585Sdelphij				    dsl_dataset_phys(ds_prev)->
293277585Sdelphij				    ds_next_clones_obj,
294277585Sdelphij				    dsl_dataset_phys(ds)->ds_next_snap_obj,
295277585Sdelphij				    tx));
296247580Smm			}
297247580Smm		}
298247580Smm		if (!after_branch_point) {
299277585Sdelphij			dsl_dataset_phys(ds_prev)->ds_next_snap_obj =
300277585Sdelphij			    dsl_dataset_phys(ds)->ds_next_snap_obj;
301247580Smm		}
302247580Smm	}
303247580Smm
304247580Smm	dsl_dataset_t *ds_next;
305247580Smm	uint64_t old_unique;
306247580Smm	uint64_t used = 0, comp = 0, uncomp = 0;
307247580Smm
308247580Smm	VERIFY0(dsl_dataset_hold_obj(dp,
309277585Sdelphij	    dsl_dataset_phys(ds)->ds_next_snap_obj, FTAG, &ds_next));
310277585Sdelphij	ASSERT3U(dsl_dataset_phys(ds_next)->ds_prev_snap_obj, ==, obj);
311247580Smm
312277585Sdelphij	old_unique = dsl_dataset_phys(ds_next)->ds_unique_bytes;
313247580Smm
314247580Smm	dmu_buf_will_dirty(ds_next->ds_dbuf, tx);
315277585Sdelphij	dsl_dataset_phys(ds_next)->ds_prev_snap_obj =
316277585Sdelphij	    dsl_dataset_phys(ds)->ds_prev_snap_obj;
317277585Sdelphij	dsl_dataset_phys(ds_next)->ds_prev_snap_txg =
318277585Sdelphij	    dsl_dataset_phys(ds)->ds_prev_snap_txg;
319277585Sdelphij	ASSERT3U(dsl_dataset_phys(ds)->ds_prev_snap_txg, ==,
320277585Sdelphij	    ds_prev ? dsl_dataset_phys(ds_prev)->ds_creation_txg : 0);
321247580Smm
322247580Smm	if (ds_next->ds_deadlist.dl_oldfmt) {
323247580Smm		process_old_deadlist(ds, ds_prev, ds_next,
324247580Smm		    after_branch_point, tx);
325247580Smm	} else {
326247580Smm		/* Adjust prev's unique space. */
327247580Smm		if (ds_prev && !after_branch_point) {
328247580Smm			dsl_deadlist_space_range(&ds_next->ds_deadlist,
329277585Sdelphij			    dsl_dataset_phys(ds_prev)->ds_prev_snap_txg,
330277585Sdelphij			    dsl_dataset_phys(ds)->ds_prev_snap_txg,
331247580Smm			    &used, &comp, &uncomp);
332277585Sdelphij			dsl_dataset_phys(ds_prev)->ds_unique_bytes += used;
333247580Smm		}
334247580Smm
335247580Smm		/* Adjust snapused. */
336247580Smm		dsl_deadlist_space_range(&ds_next->ds_deadlist,
337277585Sdelphij		    dsl_dataset_phys(ds)->ds_prev_snap_txg, UINT64_MAX,
338247580Smm		    &used, &comp, &uncomp);
339247580Smm		dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP,
340247580Smm		    -used, -comp, -uncomp, tx);
341247580Smm
342247580Smm		/* Move blocks to be freed to pool's free list. */
343247580Smm		dsl_deadlist_move_bpobj(&ds_next->ds_deadlist,
344277585Sdelphij		    &dp->dp_free_bpobj, dsl_dataset_phys(ds)->ds_prev_snap_txg,
345247580Smm		    tx);
346247580Smm		dsl_dir_diduse_space(tx->tx_pool->dp_free_dir,
347247580Smm		    DD_USED_HEAD, used, comp, uncomp, tx);
348247580Smm
349247580Smm		/* Merge our deadlist into next's and free it. */
350247580Smm		dsl_deadlist_merge(&ds_next->ds_deadlist,
351277585Sdelphij		    dsl_dataset_phys(ds)->ds_deadlist_obj, tx);
352247580Smm	}
353247580Smm	dsl_deadlist_close(&ds->ds_deadlist);
354277585Sdelphij	dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx);
355247580Smm	dmu_buf_will_dirty(ds->ds_dbuf, tx);
356277585Sdelphij	dsl_dataset_phys(ds)->ds_deadlist_obj = 0;
357247580Smm
358247580Smm	/* Collapse range in clone heads */
359247580Smm	dsl_dataset_remove_clones_key(ds,
360277585Sdelphij	    dsl_dataset_phys(ds)->ds_creation_txg, tx);
361247580Smm
362288549Smav	if (ds_next->ds_is_snapshot) {
363247580Smm		dsl_dataset_t *ds_nextnext;
364247580Smm
365247580Smm		/*
366247580Smm		 * Update next's unique to include blocks which
367247580Smm		 * were previously shared by only this snapshot
368247580Smm		 * and it.  Those blocks will be born after the
369247580Smm		 * prev snap and before this snap, and will have
370247580Smm		 * died after the next snap and before the one
371247580Smm		 * after that (ie. be on the snap after next's
372247580Smm		 * deadlist).
373247580Smm		 */
374247580Smm		VERIFY0(dsl_dataset_hold_obj(dp,
375277585Sdelphij		    dsl_dataset_phys(ds_next)->ds_next_snap_obj,
376277585Sdelphij		    FTAG, &ds_nextnext));
377247580Smm		dsl_deadlist_space_range(&ds_nextnext->ds_deadlist,
378277585Sdelphij		    dsl_dataset_phys(ds)->ds_prev_snap_txg,
379277585Sdelphij		    dsl_dataset_phys(ds)->ds_creation_txg,
380247580Smm		    &used, &comp, &uncomp);
381277585Sdelphij		dsl_dataset_phys(ds_next)->ds_unique_bytes += used;
382247580Smm		dsl_dataset_rele(ds_nextnext, FTAG);
383247580Smm		ASSERT3P(ds_next->ds_prev, ==, NULL);
384247580Smm
385247580Smm		/* Collapse range in this head. */
386247580Smm		dsl_dataset_t *hds;
387247580Smm		VERIFY0(dsl_dataset_hold_obj(dp,
388277585Sdelphij		    dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &hds));
389247580Smm		dsl_deadlist_remove_key(&hds->ds_deadlist,
390277585Sdelphij		    dsl_dataset_phys(ds)->ds_creation_txg, tx);
391247580Smm		dsl_dataset_rele(hds, FTAG);
392247580Smm
393247580Smm	} else {
394247580Smm		ASSERT3P(ds_next->ds_prev, ==, ds);
395247580Smm		dsl_dataset_rele(ds_next->ds_prev, ds_next);
396247580Smm		ds_next->ds_prev = NULL;
397247580Smm		if (ds_prev) {
398247580Smm			VERIFY0(dsl_dataset_hold_obj(dp,
399277585Sdelphij			    dsl_dataset_phys(ds)->ds_prev_snap_obj,
400247580Smm			    ds_next, &ds_next->ds_prev));
401247580Smm		}
402247580Smm
403247580Smm		dsl_dataset_recalc_head_uniq(ds_next);
404247580Smm
405247580Smm		/*
406247580Smm		 * Reduce the amount of our unconsumed refreservation
407247580Smm		 * being charged to our parent by the amount of
408247580Smm		 * new unique data we have gained.
409247580Smm		 */
410247580Smm		if (old_unique < ds_next->ds_reserved) {
411247580Smm			int64_t mrsdelta;
412247580Smm			uint64_t new_unique =
413277585Sdelphij			    dsl_dataset_phys(ds_next)->ds_unique_bytes;
414247580Smm
415247580Smm			ASSERT(old_unique <= new_unique);
416247580Smm			mrsdelta = MIN(new_unique - old_unique,
417247580Smm			    ds_next->ds_reserved - old_unique);
418247580Smm			dsl_dir_diduse_space(ds->ds_dir,
419247580Smm			    DD_USED_REFRSRV, -mrsdelta, 0, 0, tx);
420247580Smm		}
421247580Smm	}
422247580Smm	dsl_dataset_rele(ds_next, FTAG);
423247580Smm
424247580Smm	/*
425247580Smm	 * This must be done after the dsl_traverse(), because it will
426247580Smm	 * re-open the objset.
427247580Smm	 */
428247580Smm	if (ds->ds_objset) {
429247580Smm		dmu_objset_evict(ds->ds_objset);
430247580Smm		ds->ds_objset = NULL;
431247580Smm	}
432247580Smm
433247580Smm	/* remove from snapshot namespace */
434247580Smm	dsl_dataset_t *ds_head;
435277585Sdelphij	ASSERT(dsl_dataset_phys(ds)->ds_snapnames_zapobj == 0);
436247580Smm	VERIFY0(dsl_dataset_hold_obj(dp,
437277585Sdelphij	    dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &ds_head));
438247580Smm	VERIFY0(dsl_dataset_get_snapname(ds));
439247580Smm#ifdef ZFS_DEBUG
440247580Smm	{
441247580Smm		uint64_t val;
442247580Smm
443247580Smm		err = dsl_dataset_snap_lookup(ds_head,
444247580Smm		    ds->ds_snapname, &val);
445247580Smm		ASSERT0(err);
446247580Smm		ASSERT3U(val, ==, obj);
447247580Smm	}
448247580Smm#endif
449265744Sdelphij	VERIFY0(dsl_dataset_snap_remove(ds_head, ds->ds_snapname, tx, B_TRUE));
450247580Smm	dsl_dataset_rele(ds_head, FTAG);
451247580Smm
452247580Smm	if (ds_prev != NULL)
453247580Smm		dsl_dataset_rele(ds_prev, FTAG);
454247580Smm
455247580Smm	spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
456247580Smm
457277585Sdelphij	if (dsl_dataset_phys(ds)->ds_next_clones_obj != 0) {
458247580Smm		uint64_t count;
459247580Smm		ASSERT0(zap_count(mos,
460277585Sdelphij		    dsl_dataset_phys(ds)->ds_next_clones_obj, &count) &&
461277585Sdelphij		    count == 0);
462247580Smm		VERIFY0(dmu_object_free(mos,
463277585Sdelphij		    dsl_dataset_phys(ds)->ds_next_clones_obj, tx));
464247580Smm	}
465277585Sdelphij	if (dsl_dataset_phys(ds)->ds_props_obj != 0)
466277585Sdelphij		VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_props_obj,
467277585Sdelphij		    tx));
468277585Sdelphij	if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0)
469277585Sdelphij		VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
470277585Sdelphij		    tx));
471247580Smm	dsl_dir_rele(ds->ds_dir, ds);
472247580Smm	ds->ds_dir = NULL;
473263390Sdelphij	dmu_object_free_zapified(mos, obj, tx);
474247580Smm}
475247580Smm
476247580Smmstatic void
477247580Smmdsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx)
478247580Smm{
479247580Smm	dmu_snapshots_destroy_arg_t *dsda = arg;
480247580Smm	dsl_pool_t *dp = dmu_tx_pool(tx);
481247580Smm	nvpair_t *pair;
482247580Smm
483247580Smm	for (pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, NULL);
484247580Smm	    pair != NULL;
485247580Smm	    pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, pair)) {
486247580Smm		dsl_dataset_t *ds;
487247580Smm
488247580Smm		VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
489247580Smm
490247580Smm		dsl_destroy_snapshot_sync_impl(ds, dsda->dsda_defer, tx);
491247580Smm		dsl_dataset_rele(ds, FTAG);
492247580Smm	}
493247580Smm}
494247580Smm
495247580Smm/*
496247580Smm * The semantics of this function are described in the comment above
497247580Smm * lzc_destroy_snaps().  To summarize:
498247580Smm *
499247580Smm * The snapshots must all be in the same pool.
500247580Smm *
501247580Smm * Snapshots that don't exist will be silently ignored (considered to be
502247580Smm * "already deleted").
503247580Smm *
504247580Smm * On success, all snaps will be destroyed and this will return 0.
505247580Smm * On failure, no snaps will be destroyed, the errlist will be filled in,
506247580Smm * and this will return an errno.
507247580Smm */
508247580Smmint
509247580Smmdsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer,
510247580Smm    nvlist_t *errlist)
511247580Smm{
512247580Smm	dmu_snapshots_destroy_arg_t dsda;
513247580Smm	int error;
514247580Smm	nvpair_t *pair;
515247580Smm
516247580Smm	pair = nvlist_next_nvpair(snaps, NULL);
517247580Smm	if (pair == NULL)
518247580Smm		return (0);
519247580Smm
520247580Smm	dsda.dsda_snaps = snaps;
521247580Smm	dsda.dsda_successful_snaps = fnvlist_alloc();
522247580Smm	dsda.dsda_defer = defer;
523247580Smm	dsda.dsda_errlist = errlist;
524247580Smm
525247580Smm	error = dsl_sync_task(nvpair_name(pair),
526247580Smm	    dsl_destroy_snapshot_check, dsl_destroy_snapshot_sync,
527269006Sdelphij	    &dsda, 0, ZFS_SPACE_CHECK_NONE);
528247580Smm	fnvlist_free(dsda.dsda_successful_snaps);
529247580Smm
530247580Smm	return (error);
531247580Smm}
532247580Smm
533247580Smmint
534247580Smmdsl_destroy_snapshot(const char *name, boolean_t defer)
535247580Smm{
536247580Smm	int error;
537247580Smm	nvlist_t *nvl = fnvlist_alloc();
538247580Smm	nvlist_t *errlist = fnvlist_alloc();
539247580Smm
540247580Smm	fnvlist_add_boolean(nvl, name);
541247580Smm	error = dsl_destroy_snapshots_nvl(nvl, defer, errlist);
542247580Smm	fnvlist_free(errlist);
543247580Smm	fnvlist_free(nvl);
544247580Smm	return (error);
545247580Smm}
546247580Smm
547247580Smmstruct killarg {
548247580Smm	dsl_dataset_t *ds;
549247580Smm	dmu_tx_t *tx;
550247580Smm};
551247580Smm
552247580Smm/* ARGSUSED */
553247580Smmstatic int
554247580Smmkill_blkptr(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
555268657Sdelphij    const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg)
556247580Smm{
557247580Smm	struct killarg *ka = arg;
558247580Smm	dmu_tx_t *tx = ka->tx;
559247580Smm
560288571Smav	if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp))
561247580Smm		return (0);
562247580Smm
563247580Smm	if (zb->zb_level == ZB_ZIL_LEVEL) {
564247580Smm		ASSERT(zilog != NULL);
565247580Smm		/*
566247580Smm		 * It's a block in the intent log.  It has no
567247580Smm		 * accounting, so just free it.
568247580Smm		 */
569247580Smm		dsl_free(ka->tx->tx_pool, ka->tx->tx_txg, bp);
570247580Smm	} else {
571247580Smm		ASSERT(zilog == NULL);
572277585Sdelphij		ASSERT3U(bp->blk_birth, >,
573277585Sdelphij		    dsl_dataset_phys(ka->ds)->ds_prev_snap_txg);
574247580Smm		(void) dsl_dataset_block_kill(ka->ds, bp, tx, B_FALSE);
575247580Smm	}
576247580Smm
577247580Smm	return (0);
578247580Smm}
579247580Smm
580247580Smmstatic void
581247580Smmold_synchronous_dataset_destroy(dsl_dataset_t *ds, dmu_tx_t *tx)
582247580Smm{
583247580Smm	struct killarg ka;
584247580Smm
585247580Smm	/*
586247580Smm	 * Free everything that we point to (that's born after
587247580Smm	 * the previous snapshot, if we are a clone)
588247580Smm	 *
589247580Smm	 * NB: this should be very quick, because we already
590247580Smm	 * freed all the objects in open context.
591247580Smm	 */
592247580Smm	ka.ds = ds;
593247580Smm	ka.tx = tx;
594247580Smm	VERIFY0(traverse_dataset(ds,
595277585Sdelphij	    dsl_dataset_phys(ds)->ds_prev_snap_txg, TRAVERSE_POST,
596247580Smm	    kill_blkptr, &ka));
597277585Sdelphij	ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||
598277585Sdelphij	    dsl_dataset_phys(ds)->ds_unique_bytes == 0);
599247580Smm}
600247580Smm
601247580Smmtypedef struct dsl_destroy_head_arg {
602247580Smm	const char *ddha_name;
603247580Smm} dsl_destroy_head_arg_t;
604247580Smm
605247580Smmint
606247580Smmdsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds)
607247580Smm{
608247580Smm	int error;
609247580Smm	uint64_t count;
610247580Smm	objset_t *mos;
611247580Smm
612288549Smav	ASSERT(!ds->ds_is_snapshot);
613288549Smav	if (ds->ds_is_snapshot)
614249195Smm		return (SET_ERROR(EINVAL));
615247580Smm
616247580Smm	if (refcount_count(&ds->ds_longholds) != expected_holds)
617249195Smm		return (SET_ERROR(EBUSY));
618247580Smm
619247580Smm	mos = ds->ds_dir->dd_pool->dp_meta_objset;
620247580Smm
621247580Smm	/*
622247580Smm	 * Can't delete a head dataset if there are snapshots of it.
623247580Smm	 * (Except if the only snapshots are from the branch we cloned
624247580Smm	 * from.)
625247580Smm	 */
626247580Smm	if (ds->ds_prev != NULL &&
627277585Sdelphij	    dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj == ds->ds_object)
628249195Smm		return (SET_ERROR(EBUSY));
629247580Smm
630247580Smm	/*
631247580Smm	 * Can't delete if there are children of this fs.
632247580Smm	 */
633247580Smm	error = zap_count(mos,
634277585Sdelphij	    dsl_dir_phys(ds->ds_dir)->dd_child_dir_zapobj, &count);
635247580Smm	if (error != 0)
636247580Smm		return (error);
637247580Smm	if (count != 0)
638249195Smm		return (SET_ERROR(EEXIST));
639247580Smm
640247580Smm	if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev) &&
641277585Sdelphij	    dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 &&
642247580Smm	    ds->ds_prev->ds_userrefs == 0) {
643247580Smm		/* We need to remove the origin snapshot as well. */
644247580Smm		if (!refcount_is_zero(&ds->ds_prev->ds_longholds))
645249195Smm			return (SET_ERROR(EBUSY));
646247580Smm	}
647247580Smm	return (0);
648247580Smm}
649247580Smm
650247580Smmstatic int
651247580Smmdsl_destroy_head_check(void *arg, dmu_tx_t *tx)
652247580Smm{
653247580Smm	dsl_destroy_head_arg_t *ddha = arg;
654247580Smm	dsl_pool_t *dp = dmu_tx_pool(tx);
655247580Smm	dsl_dataset_t *ds;
656247580Smm	int error;
657247580Smm
658247580Smm	error = dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds);
659247580Smm	if (error != 0)
660247580Smm		return (error);
661247580Smm
662247580Smm	error = dsl_destroy_head_check_impl(ds, 0);
663247580Smm	dsl_dataset_rele(ds, FTAG);
664247580Smm	return (error);
665247580Smm}
666247580Smm
667247580Smmstatic void
668247580Smmdsl_dir_destroy_sync(uint64_t ddobj, dmu_tx_t *tx)
669247580Smm{
670247580Smm	dsl_dir_t *dd;
671247580Smm	dsl_pool_t *dp = dmu_tx_pool(tx);
672247580Smm	objset_t *mos = dp->dp_meta_objset;
673247580Smm	dd_used_t t;
674247580Smm
675247580Smm	ASSERT(RRW_WRITE_HELD(&dmu_tx_pool(tx)->dp_config_rwlock));
676247580Smm
677247580Smm	VERIFY0(dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd));
678247580Smm
679277585Sdelphij	ASSERT0(dsl_dir_phys(dd)->dd_head_dataset_obj);
680247580Smm
681247580Smm	/*
682265744Sdelphij	 * Decrement the filesystem count for all parent filesystems.
683265744Sdelphij	 *
684265744Sdelphij	 * When we receive an incremental stream into a filesystem that already
685265744Sdelphij	 * exists, a temporary clone is created.  We never count this temporary
686265744Sdelphij	 * clone, whose name begins with a '%'.
687265744Sdelphij	 */
688265744Sdelphij	if (dd->dd_myname[0] != '%' && dd->dd_parent != NULL)
689265744Sdelphij		dsl_fs_ss_count_adjust(dd->dd_parent, -1,
690265744Sdelphij		    DD_FIELD_FILESYSTEM_COUNT, tx);
691265744Sdelphij
692265744Sdelphij	/*
693247580Smm	 * Remove our reservation. The impl() routine avoids setting the
694247580Smm	 * actual property, which would require the (already destroyed) ds.
695247580Smm	 */
696247580Smm	dsl_dir_set_reservation_sync_impl(dd, 0, tx);
697247580Smm
698277585Sdelphij	ASSERT0(dsl_dir_phys(dd)->dd_used_bytes);
699277585Sdelphij	ASSERT0(dsl_dir_phys(dd)->dd_reserved);
700247580Smm	for (t = 0; t < DD_USED_NUM; t++)
701277585Sdelphij		ASSERT0(dsl_dir_phys(dd)->dd_used_breakdown[t]);
702247580Smm
703277585Sdelphij	VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_child_dir_zapobj, tx));
704277585Sdelphij	VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_props_zapobj, tx));
705277585Sdelphij	VERIFY0(dsl_deleg_destroy(mos, dsl_dir_phys(dd)->dd_deleg_zapobj, tx));
706247580Smm	VERIFY0(zap_remove(mos,
707277585Sdelphij	    dsl_dir_phys(dd->dd_parent)->dd_child_dir_zapobj,
708277585Sdelphij	    dd->dd_myname, tx));
709247580Smm
710247580Smm	dsl_dir_rele(dd, FTAG);
711263390Sdelphij	dmu_object_free_zapified(mos, ddobj, tx);
712247580Smm}
713247580Smm
714247580Smmvoid
715247580Smmdsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
716247580Smm{
717247580Smm	dsl_pool_t *dp = dmu_tx_pool(tx);
718247580Smm	objset_t *mos = dp->dp_meta_objset;
719247580Smm	uint64_t obj, ddobj, prevobj = 0;
720247580Smm	boolean_t rmorigin;
721247580Smm
722277585Sdelphij	ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);
723247580Smm	ASSERT(ds->ds_prev == NULL ||
724277585Sdelphij	    dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj != ds->ds_object);
725308083Smav	rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
726277585Sdelphij	ASSERT3U(dsl_dataset_phys(ds)->ds_bp.blk_birth, <=, tx->tx_txg);
727308083Smav	rrw_exit(&ds->ds_bp_rwlock, FTAG);
728247580Smm	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
729247580Smm
730247580Smm	/* We need to log before removing it from the namespace. */
731247580Smm	spa_history_log_internal_ds(ds, "destroy", tx, "");
732247580Smm
733247580Smm	rmorigin = (dsl_dir_is_clone(ds->ds_dir) &&
734247580Smm	    DS_IS_DEFER_DESTROY(ds->ds_prev) &&
735277585Sdelphij	    dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 &&
736247580Smm	    ds->ds_prev->ds_userrefs == 0);
737247580Smm
738268649Sdelphij	/* Remove our reservation. */
739247580Smm	if (ds->ds_reserved != 0) {
740247580Smm		dsl_dataset_set_refreservation_sync_impl(ds,
741247580Smm		    (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED),
742247580Smm		    0, tx);
743247580Smm		ASSERT0(ds->ds_reserved);
744247580Smm	}
745247580Smm
746288572Smav	obj = ds->ds_object;
747276081Sdelphij
748288572Smav	for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
749288572Smav		if (ds->ds_feature_inuse[f]) {
750288572Smav			dsl_dataset_deactivate_feature(obj, f, tx);
751288572Smav			ds->ds_feature_inuse[f] = B_FALSE;
752288572Smav		}
753288572Smav	}
754288572Smav
755247580Smm	dsl_scan_ds_destroyed(ds, tx);
756247580Smm
757277585Sdelphij	if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
758247580Smm		/* This is a clone */
759247580Smm		ASSERT(ds->ds_prev != NULL);
760277585Sdelphij		ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj, !=,
761277585Sdelphij		    obj);
762277585Sdelphij		ASSERT0(dsl_dataset_phys(ds)->ds_next_snap_obj);
763247580Smm
764247580Smm		dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
765277585Sdelphij		if (dsl_dataset_phys(ds->ds_prev)->ds_next_clones_obj != 0) {
766247580Smm			dsl_dataset_remove_from_next_clones(ds->ds_prev,
767247580Smm			    obj, tx);
768247580Smm		}
769247580Smm
770277585Sdelphij		ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_num_children, >, 1);
771277585Sdelphij		dsl_dataset_phys(ds->ds_prev)->ds_num_children--;
772247580Smm	}
773247580Smm
774247580Smm	/*
775247580Smm	 * Destroy the deadlist.  Unless it's a clone, the
776247580Smm	 * deadlist should be empty.  (If it's a clone, it's
777247580Smm	 * safe to ignore the deadlist contents.)
778247580Smm	 */
779247580Smm	dsl_deadlist_close(&ds->ds_deadlist);
780277585Sdelphij	dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx);
781247580Smm	dmu_buf_will_dirty(ds->ds_dbuf, tx);
782277585Sdelphij	dsl_dataset_phys(ds)->ds_deadlist_obj = 0;
783247580Smm
784263390Sdelphij	objset_t *os;
785247580Smm	VERIFY0(dmu_objset_from_ds(ds, &os));
786247580Smm
787263390Sdelphij	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY)) {
788247580Smm		old_synchronous_dataset_destroy(ds, tx);
789247580Smm	} else {
790247580Smm		/*
791247580Smm		 * Move the bptree into the pool's list of trees to
792247580Smm		 * clean up and update space accounting information.
793247580Smm		 */
794247580Smm		uint64_t used, comp, uncomp;
795247580Smm
796247580Smm		zil_destroy_sync(dmu_objset_zil(os), tx);
797247580Smm
798263390Sdelphij		if (!spa_feature_is_active(dp->dp_spa,
799263390Sdelphij		    SPA_FEATURE_ASYNC_DESTROY)) {
800249858Smm			dsl_scan_t *scn = dp->dp_scan;
801263390Sdelphij			spa_feature_incr(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY,
802263390Sdelphij			    tx);
803247580Smm			dp->dp_bptree_obj = bptree_alloc(mos, tx);
804247580Smm			VERIFY0(zap_add(mos,
805247580Smm			    DMU_POOL_DIRECTORY_OBJECT,
806247580Smm			    DMU_POOL_BPTREE_OBJ, sizeof (uint64_t), 1,
807247580Smm			    &dp->dp_bptree_obj, tx));
808249858Smm			ASSERT(!scn->scn_async_destroying);
809249858Smm			scn->scn_async_destroying = B_TRUE;
810247580Smm		}
811247580Smm
812277585Sdelphij		used = dsl_dir_phys(ds->ds_dir)->dd_used_bytes;
813277585Sdelphij		comp = dsl_dir_phys(ds->ds_dir)->dd_compressed_bytes;
814277585Sdelphij		uncomp = dsl_dir_phys(ds->ds_dir)->dd_uncompressed_bytes;
815247580Smm
816247580Smm		ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||
817277585Sdelphij		    dsl_dataset_phys(ds)->ds_unique_bytes == used);
818247580Smm
819308083Smav		rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
820247580Smm		bptree_add(mos, dp->dp_bptree_obj,
821277585Sdelphij		    &dsl_dataset_phys(ds)->ds_bp,
822277585Sdelphij		    dsl_dataset_phys(ds)->ds_prev_snap_txg,
823247580Smm		    used, comp, uncomp, tx);
824308083Smav		rrw_exit(&ds->ds_bp_rwlock, FTAG);
825247580Smm		dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD,
826247580Smm		    -used, -comp, -uncomp, tx);
827247580Smm		dsl_dir_diduse_space(dp->dp_free_dir, DD_USED_HEAD,
828247580Smm		    used, comp, uncomp, tx);
829247580Smm	}
830247580Smm
831247580Smm	if (ds->ds_prev != NULL) {
832247580Smm		if (spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) {
833247580Smm			VERIFY0(zap_remove_int(mos,
834277585Sdelphij			    dsl_dir_phys(ds->ds_prev->ds_dir)->dd_clones,
835247580Smm			    ds->ds_object, tx));
836247580Smm		}
837247580Smm		prevobj = ds->ds_prev->ds_object;
838247580Smm		dsl_dataset_rele(ds->ds_prev, ds);
839247580Smm		ds->ds_prev = NULL;
840247580Smm	}
841247580Smm
842247580Smm	/*
843247580Smm	 * This must be done after the dsl_traverse(), because it will
844247580Smm	 * re-open the objset.
845247580Smm	 */
846247580Smm	if (ds->ds_objset) {
847247580Smm		dmu_objset_evict(ds->ds_objset);
848247580Smm		ds->ds_objset = NULL;
849247580Smm	}
850247580Smm
851247580Smm	/* Erase the link in the dir */
852247580Smm	dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
853277585Sdelphij	dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj = 0;
854247580Smm	ddobj = ds->ds_dir->dd_object;
855277585Sdelphij	ASSERT(dsl_dataset_phys(ds)->ds_snapnames_zapobj != 0);
856277585Sdelphij	VERIFY0(zap_destroy(mos,
857277585Sdelphij	    dsl_dataset_phys(ds)->ds_snapnames_zapobj, tx));
858247580Smm
859263407Sdelphij	if (ds->ds_bookmarks != 0) {
860277585Sdelphij		VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
861263407Sdelphij		spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
862263407Sdelphij	}
863263407Sdelphij
864247580Smm	spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
865247580Smm
866277585Sdelphij	ASSERT0(dsl_dataset_phys(ds)->ds_next_clones_obj);
867277585Sdelphij	ASSERT0(dsl_dataset_phys(ds)->ds_props_obj);
868277585Sdelphij	ASSERT0(dsl_dataset_phys(ds)->ds_userrefs_obj);
869247580Smm	dsl_dir_rele(ds->ds_dir, ds);
870247580Smm	ds->ds_dir = NULL;
871263390Sdelphij	dmu_object_free_zapified(mos, obj, tx);
872247580Smm
873247580Smm	dsl_dir_destroy_sync(ddobj, tx);
874247580Smm
875247580Smm	if (rmorigin) {
876247580Smm		dsl_dataset_t *prev;
877247580Smm		VERIFY0(dsl_dataset_hold_obj(dp, prevobj, FTAG, &prev));
878247580Smm		dsl_destroy_snapshot_sync_impl(prev, B_FALSE, tx);
879247580Smm		dsl_dataset_rele(prev, FTAG);
880247580Smm	}
881247580Smm}
882247580Smm
883247580Smmstatic void
884247580Smmdsl_destroy_head_sync(void *arg, dmu_tx_t *tx)
885247580Smm{
886247580Smm	dsl_destroy_head_arg_t *ddha = arg;
887247580Smm	dsl_pool_t *dp = dmu_tx_pool(tx);
888247580Smm	dsl_dataset_t *ds;
889247580Smm
890247580Smm	VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds));
891247580Smm	dsl_destroy_head_sync_impl(ds, tx);
892247580Smm	dsl_dataset_rele(ds, FTAG);
893247580Smm}
894247580Smm
895247580Smmstatic void
896247580Smmdsl_destroy_head_begin_sync(void *arg, dmu_tx_t *tx)
897247580Smm{
898247580Smm	dsl_destroy_head_arg_t *ddha = arg;
899247580Smm	dsl_pool_t *dp = dmu_tx_pool(tx);
900247580Smm	dsl_dataset_t *ds;
901247580Smm
902247580Smm	VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds));
903247580Smm
904247580Smm	/* Mark it as inconsistent on-disk, in case we crash */
905247580Smm	dmu_buf_will_dirty(ds->ds_dbuf, tx);
906277585Sdelphij	dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT;
907247580Smm
908247580Smm	spa_history_log_internal_ds(ds, "destroy begin", tx, "");
909247580Smm	dsl_dataset_rele(ds, FTAG);
910247580Smm}
911247580Smm
912247580Smmint
913247580Smmdsl_destroy_head(const char *name)
914247580Smm{
915247580Smm	dsl_destroy_head_arg_t ddha;
916247580Smm	int error;
917247580Smm	spa_t *spa;
918247580Smm	boolean_t isenabled;
919247580Smm
920247580Smm#ifdef _KERNEL
921247580Smm	zfs_destroy_unmount_origin(name);
922247580Smm#endif
923247580Smm
924247580Smm	error = spa_open(name, &spa, FTAG);
925247580Smm	if (error != 0)
926247580Smm		return (error);
927263390Sdelphij	isenabled = spa_feature_is_enabled(spa, SPA_FEATURE_ASYNC_DESTROY);
928247580Smm	spa_close(spa, FTAG);
929247580Smm
930247580Smm	ddha.ddha_name = name;
931247580Smm
932247580Smm	if (!isenabled) {
933247580Smm		objset_t *os;
934247580Smm
935247580Smm		error = dsl_sync_task(name, dsl_destroy_head_check,
936269006Sdelphij		    dsl_destroy_head_begin_sync, &ddha,
937269006Sdelphij		    0, ZFS_SPACE_CHECK_NONE);
938247580Smm		if (error != 0)
939247580Smm			return (error);
940247580Smm
941247580Smm		/*
942247580Smm		 * Head deletion is processed in one txg on old pools;
943247580Smm		 * remove the objects from open context so that the txg sync
944247580Smm		 * is not too long.
945247580Smm		 */
946247580Smm		error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, FTAG, &os);
947247580Smm		if (error == 0) {
948247580Smm			uint64_t prev_snap_txg =
949277585Sdelphij			    dsl_dataset_phys(dmu_objset_ds(os))->
950277585Sdelphij			    ds_prev_snap_txg;
951247580Smm			for (uint64_t obj = 0; error == 0;
952247580Smm			    error = dmu_object_next(os, &obj, FALSE,
953247580Smm			    prev_snap_txg))
954254753Sdelphij				(void) dmu_free_long_object(os, obj);
955247580Smm			/* sync out all frees */
956247580Smm			txg_wait_synced(dmu_objset_pool(os), 0);
957247580Smm			dmu_objset_disown(os, FTAG);
958247580Smm		}
959247580Smm	}
960247580Smm
961247580Smm	return (dsl_sync_task(name, dsl_destroy_head_check,
962269006Sdelphij	    dsl_destroy_head_sync, &ddha, 0, ZFS_SPACE_CHECK_NONE));
963247580Smm}
964247580Smm
965247580Smm/*
966247580Smm * Note, this function is used as the callback for dmu_objset_find().  We
967247580Smm * always return 0 so that we will continue to find and process
968247580Smm * inconsistent datasets, even if we encounter an error trying to
969247580Smm * process one of them.
970247580Smm */
971247580Smm/* ARGSUSED */
972247580Smmint
973247580Smmdsl_destroy_inconsistent(const char *dsname, void *arg)
974247580Smm{
975247580Smm	objset_t *os;
976247580Smm
977247580Smm	if (dmu_objset_hold(dsname, FTAG, &os) == 0) {
978290756Smav		boolean_t need_destroy = DS_IS_INCONSISTENT(dmu_objset_ds(os));
979290756Smav
980290756Smav		/*
981290756Smav		 * If the dataset is inconsistent because a resumable receive
982290756Smav		 * has failed, then do not destroy it.
983290756Smav		 */
984290756Smav		if (dsl_dataset_has_resume_receive_state(dmu_objset_ds(os)))
985290756Smav			need_destroy = B_FALSE;
986290756Smav
987247580Smm		dmu_objset_rele(os, FTAG);
988290756Smav		if (need_destroy)
989247580Smm			(void) dsl_destroy_head(dsname);
990247580Smm	}
991247580Smm	return (0);
992247580Smm}
993