xref: /titanic_51/usr/src/uts/common/fs/zfs/dsl_userhold.c (revision 40a5c998e5462ed47916e2edfcc5016073cf8851)
13b2aab18SMatthew Ahrens /*
23b2aab18SMatthew Ahrens  * CDDL HEADER START
33b2aab18SMatthew Ahrens  *
43b2aab18SMatthew Ahrens  * The contents of this file are subject to the terms of the
53b2aab18SMatthew Ahrens  * Common Development and Distribution License (the "License").
63b2aab18SMatthew Ahrens  * You may not use this file except in compliance with the License.
73b2aab18SMatthew Ahrens  *
83b2aab18SMatthew Ahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93b2aab18SMatthew Ahrens  * or http://www.opensolaris.org/os/licensing.
103b2aab18SMatthew Ahrens  * See the License for the specific language governing permissions
113b2aab18SMatthew Ahrens  * and limitations under the License.
123b2aab18SMatthew Ahrens  *
133b2aab18SMatthew Ahrens  * When distributing Covered Code, include this CDDL HEADER in each
143b2aab18SMatthew Ahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153b2aab18SMatthew Ahrens  * If applicable, add the following below this CDDL HEADER, with the
163b2aab18SMatthew Ahrens  * fields enclosed by brackets "[]" replaced with your own identifying
173b2aab18SMatthew Ahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
183b2aab18SMatthew Ahrens  *
193b2aab18SMatthew Ahrens  * CDDL HEADER END
203b2aab18SMatthew Ahrens  */
213b2aab18SMatthew Ahrens /*
223b2aab18SMatthew Ahrens  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23*40a5c998SMatthew Ahrens  * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
24a7a845e4SSteven Hartland  * Copyright (c) 2013 Steven Hartland. All rights reserved.
253b2aab18SMatthew Ahrens  */
263b2aab18SMatthew Ahrens 
273b2aab18SMatthew Ahrens #include <sys/zfs_context.h>
283b2aab18SMatthew Ahrens #include <sys/dsl_userhold.h>
293b2aab18SMatthew Ahrens #include <sys/dsl_dataset.h>
303b2aab18SMatthew Ahrens #include <sys/dsl_destroy.h>
313b2aab18SMatthew Ahrens #include <sys/dsl_synctask.h>
323b2aab18SMatthew Ahrens #include <sys/dmu_tx.h>
333b2aab18SMatthew Ahrens #include <sys/zfs_onexit.h>
343b2aab18SMatthew Ahrens #include <sys/dsl_pool.h>
353b2aab18SMatthew Ahrens #include <sys/dsl_dir.h>
363b2aab18SMatthew Ahrens #include <sys/zfs_ioctl.h>
373b2aab18SMatthew Ahrens #include <sys/zap.h>
383b2aab18SMatthew Ahrens 
393b2aab18SMatthew Ahrens typedef struct dsl_dataset_user_hold_arg {
403b2aab18SMatthew Ahrens 	nvlist_t *dduha_holds;
41a7a845e4SSteven Hartland 	nvlist_t *dduha_chkholds;
423b2aab18SMatthew Ahrens 	nvlist_t *dduha_errlist;
433b2aab18SMatthew Ahrens 	minor_t dduha_minor;
443b2aab18SMatthew Ahrens } dsl_dataset_user_hold_arg_t;
453b2aab18SMatthew Ahrens 
463b2aab18SMatthew Ahrens /*
473b2aab18SMatthew Ahrens  * If you add new checks here, you may need to add additional checks to the
483b2aab18SMatthew Ahrens  * "temporary" case in snapshot_check() in dmu_objset.c.
493b2aab18SMatthew Ahrens  */
503b2aab18SMatthew Ahrens int
513b2aab18SMatthew Ahrens dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag,
523b2aab18SMatthew Ahrens     boolean_t temphold, dmu_tx_t *tx)
533b2aab18SMatthew Ahrens {
543b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
553b2aab18SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
563b2aab18SMatthew Ahrens 	int error = 0;
573b2aab18SMatthew Ahrens 
58a7a845e4SSteven Hartland 	ASSERT(dsl_pool_config_held(dp));
59a7a845e4SSteven Hartland 
603b2aab18SMatthew Ahrens 	if (strlen(htag) > MAXNAMELEN)
61a7a845e4SSteven Hartland 		return (SET_ERROR(E2BIG));
623b2aab18SMatthew Ahrens 	/* Tempholds have a more restricted length */
633b2aab18SMatthew Ahrens 	if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
64a7a845e4SSteven Hartland 		return (SET_ERROR(E2BIG));
653b2aab18SMatthew Ahrens 
663b2aab18SMatthew Ahrens 	/* tags must be unique (if ds already exists) */
67c1379625SJustin T. Gibbs 	if (ds != NULL && dsl_dataset_phys(ds)->ds_userrefs_obj != 0) {
683b2aab18SMatthew Ahrens 		uint64_t value;
69a7a845e4SSteven Hartland 
70c1379625SJustin T. Gibbs 		error = zap_lookup(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
713b2aab18SMatthew Ahrens 		    htag, 8, 1, &value);
723b2aab18SMatthew Ahrens 		if (error == 0)
73be6fd75aSMatthew Ahrens 			error = SET_ERROR(EEXIST);
743b2aab18SMatthew Ahrens 		else if (error == ENOENT)
753b2aab18SMatthew Ahrens 			error = 0;
763b2aab18SMatthew Ahrens 	}
773b2aab18SMatthew Ahrens 
783b2aab18SMatthew Ahrens 	return (error);
793b2aab18SMatthew Ahrens }
803b2aab18SMatthew Ahrens 
813b2aab18SMatthew Ahrens static int
823b2aab18SMatthew Ahrens dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx)
833b2aab18SMatthew Ahrens {
843b2aab18SMatthew Ahrens 	dsl_dataset_user_hold_arg_t *dduha = arg;
853b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
863b2aab18SMatthew Ahrens 
873b2aab18SMatthew Ahrens 	if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
88be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENOTSUP));
893b2aab18SMatthew Ahrens 
90a7a845e4SSteven Hartland 	if (!dmu_tx_is_syncing(tx))
91a7a845e4SSteven Hartland 		return (0);
92a7a845e4SSteven Hartland 
93a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL);
94a7a845e4SSteven Hartland 	    pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
953b2aab18SMatthew Ahrens 		dsl_dataset_t *ds;
96a7a845e4SSteven Hartland 		int error = 0;
97a7a845e4SSteven Hartland 		char *htag, *name;
983b2aab18SMatthew Ahrens 
993b2aab18SMatthew Ahrens 		/* must be a snapshot */
100a7a845e4SSteven Hartland 		name = nvpair_name(pair);
101a7a845e4SSteven Hartland 		if (strchr(name, '@') == NULL)
102be6fd75aSMatthew Ahrens 			error = SET_ERROR(EINVAL);
1033b2aab18SMatthew Ahrens 
1043b2aab18SMatthew Ahrens 		if (error == 0)
1053b2aab18SMatthew Ahrens 			error = nvpair_value_string(pair, &htag);
106a7a845e4SSteven Hartland 
107a7a845e4SSteven Hartland 		if (error == 0)
108a7a845e4SSteven Hartland 			error = dsl_dataset_hold(dp, name, FTAG, &ds);
109a7a845e4SSteven Hartland 
1103b2aab18SMatthew Ahrens 		if (error == 0) {
1113b2aab18SMatthew Ahrens 			error = dsl_dataset_user_hold_check_one(ds, htag,
1123b2aab18SMatthew Ahrens 			    dduha->dduha_minor != 0, tx);
1133b2aab18SMatthew Ahrens 			dsl_dataset_rele(ds, FTAG);
1143b2aab18SMatthew Ahrens 		}
1153b2aab18SMatthew Ahrens 
116a7a845e4SSteven Hartland 		if (error == 0) {
117a7a845e4SSteven Hartland 			fnvlist_add_string(dduha->dduha_chkholds, name, htag);
118a7a845e4SSteven Hartland 		} else {
119a7a845e4SSteven Hartland 			/*
120a7a845e4SSteven Hartland 			 * We register ENOENT errors so they can be correctly
121a7a845e4SSteven Hartland 			 * reported if needed, such as when all holds fail.
122a7a845e4SSteven Hartland 			 */
123a7a845e4SSteven Hartland 			fnvlist_add_int32(dduha->dduha_errlist, name, error);
124a7a845e4SSteven Hartland 			if (error != ENOENT)
125a7a845e4SSteven Hartland 				return (error);
1263b2aab18SMatthew Ahrens 		}
1273b2aab18SMatthew Ahrens 	}
1283b2aab18SMatthew Ahrens 
129a7a845e4SSteven Hartland 	return (0);
130a7a845e4SSteven Hartland }
131a7a845e4SSteven Hartland 
132a7a845e4SSteven Hartland 
133a7a845e4SSteven Hartland static void
134a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds,
135a7a845e4SSteven Hartland     const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx)
1363b2aab18SMatthew Ahrens {
1373b2aab18SMatthew Ahrens 	dsl_pool_t *dp = ds->ds_dir->dd_pool;
1383b2aab18SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
1393b2aab18SMatthew Ahrens 	uint64_t zapobj;
1403b2aab18SMatthew Ahrens 
141a7a845e4SSteven Hartland 	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
142a7a845e4SSteven Hartland 
143c1379625SJustin T. Gibbs 	if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
1443b2aab18SMatthew Ahrens 		/*
1453b2aab18SMatthew Ahrens 		 * This is the first user hold for this dataset.  Create
1463b2aab18SMatthew Ahrens 		 * the userrefs zap object.
1473b2aab18SMatthew Ahrens 		 */
1483b2aab18SMatthew Ahrens 		dmu_buf_will_dirty(ds->ds_dbuf, tx);
149c1379625SJustin T. Gibbs 		zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj =
1503b2aab18SMatthew Ahrens 		    zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx);
1513b2aab18SMatthew Ahrens 	} else {
152c1379625SJustin T. Gibbs 		zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
1533b2aab18SMatthew Ahrens 	}
1543b2aab18SMatthew Ahrens 	ds->ds_userrefs++;
1553b2aab18SMatthew Ahrens 
1563b2aab18SMatthew Ahrens 	VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx));
1573b2aab18SMatthew Ahrens 
1583b2aab18SMatthew Ahrens 	if (minor != 0) {
159a7a845e4SSteven Hartland 		char name[MAXNAMELEN];
160a7a845e4SSteven Hartland 		nvlist_t *tags;
161a7a845e4SSteven Hartland 
1623b2aab18SMatthew Ahrens 		VERIFY0(dsl_pool_user_hold(dp, ds->ds_object,
1633b2aab18SMatthew Ahrens 		    htag, now, tx));
164a7a845e4SSteven Hartland 		(void) snprintf(name, sizeof (name), "%llx",
165a7a845e4SSteven Hartland 		    (u_longlong_t)ds->ds_object);
166a7a845e4SSteven Hartland 
167a7a845e4SSteven Hartland 		if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) {
168a7a845e4SSteven Hartland 			tags = fnvlist_alloc();
169a7a845e4SSteven Hartland 			fnvlist_add_boolean(tags, htag);
170a7a845e4SSteven Hartland 			fnvlist_add_nvlist(tmpholds, name, tags);
171a7a845e4SSteven Hartland 			fnvlist_free(tags);
172a7a845e4SSteven Hartland 		} else {
173a7a845e4SSteven Hartland 			fnvlist_add_boolean(tags, htag);
174a7a845e4SSteven Hartland 		}
1753b2aab18SMatthew Ahrens 	}
1763b2aab18SMatthew Ahrens 
1773b2aab18SMatthew Ahrens 	spa_history_log_internal_ds(ds, "hold", tx,
1783b2aab18SMatthew Ahrens 	    "tag=%s temp=%d refs=%llu",
1793b2aab18SMatthew Ahrens 	    htag, minor != 0, ds->ds_userrefs);
1803b2aab18SMatthew Ahrens }
1813b2aab18SMatthew Ahrens 
1823b2aab18SMatthew Ahrens typedef struct zfs_hold_cleanup_arg {
183*40a5c998SMatthew Ahrens 	char zhca_spaname[ZFS_MAX_DATASET_NAME_LEN];
1843b2aab18SMatthew Ahrens 	uint64_t zhca_spa_load_guid;
185a7a845e4SSteven Hartland 	nvlist_t *zhca_holds;
1863b2aab18SMatthew Ahrens } zfs_hold_cleanup_arg_t;
1873b2aab18SMatthew Ahrens 
1883b2aab18SMatthew Ahrens static void
1893b2aab18SMatthew Ahrens dsl_dataset_user_release_onexit(void *arg)
1903b2aab18SMatthew Ahrens {
1913b2aab18SMatthew Ahrens 	zfs_hold_cleanup_arg_t *ca = arg;
1923b2aab18SMatthew Ahrens 	spa_t *spa;
1933b2aab18SMatthew Ahrens 	int error;
1943b2aab18SMatthew Ahrens 
1953b2aab18SMatthew Ahrens 	error = spa_open(ca->zhca_spaname, &spa, FTAG);
1963b2aab18SMatthew Ahrens 	if (error != 0) {
197a7a845e4SSteven Hartland 		zfs_dbgmsg("couldn't release holds on pool=%s "
1983b2aab18SMatthew Ahrens 		    "because pool is no longer loaded",
199a7a845e4SSteven Hartland 		    ca->zhca_spaname);
2003b2aab18SMatthew Ahrens 		return;
2013b2aab18SMatthew Ahrens 	}
2023b2aab18SMatthew Ahrens 	if (spa_load_guid(spa) != ca->zhca_spa_load_guid) {
203a7a845e4SSteven Hartland 		zfs_dbgmsg("couldn't release holds on pool=%s "
2043b2aab18SMatthew Ahrens 		    "because pool is no longer loaded (guid doesn't match)",
205a7a845e4SSteven Hartland 		    ca->zhca_spaname);
2063b2aab18SMatthew Ahrens 		spa_close(spa, FTAG);
2073b2aab18SMatthew Ahrens 		return;
2083b2aab18SMatthew Ahrens 	}
2093b2aab18SMatthew Ahrens 
210a7a845e4SSteven Hartland 	(void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds);
211a7a845e4SSteven Hartland 	fnvlist_free(ca->zhca_holds);
2123b2aab18SMatthew Ahrens 	kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
2133b2aab18SMatthew Ahrens 	spa_close(spa, FTAG);
2143b2aab18SMatthew Ahrens }
2153b2aab18SMatthew Ahrens 
216a7a845e4SSteven Hartland static void
217a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor)
2183b2aab18SMatthew Ahrens {
219a7a845e4SSteven Hartland 	zfs_hold_cleanup_arg_t *ca;
220a7a845e4SSteven Hartland 
221a7a845e4SSteven Hartland 	if (minor == 0 || nvlist_empty(holds)) {
222a7a845e4SSteven Hartland 		fnvlist_free(holds);
223a7a845e4SSteven Hartland 		return;
224a7a845e4SSteven Hartland 	}
225a7a845e4SSteven Hartland 
226a7a845e4SSteven Hartland 	ASSERT(spa != NULL);
227a7a845e4SSteven Hartland 	ca = kmem_alloc(sizeof (*ca), KM_SLEEP);
228a7a845e4SSteven Hartland 
2293b2aab18SMatthew Ahrens 	(void) strlcpy(ca->zhca_spaname, spa_name(spa),
2303b2aab18SMatthew Ahrens 	    sizeof (ca->zhca_spaname));
2313b2aab18SMatthew Ahrens 	ca->zhca_spa_load_guid = spa_load_guid(spa);
232a7a845e4SSteven Hartland 	ca->zhca_holds = holds;
2333b2aab18SMatthew Ahrens 	VERIFY0(zfs_onexit_add_cb(minor,
2343b2aab18SMatthew Ahrens 	    dsl_dataset_user_release_onexit, ca, NULL));
2353b2aab18SMatthew Ahrens }
2363b2aab18SMatthew Ahrens 
237a7a845e4SSteven Hartland void
238a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag,
239a7a845e4SSteven Hartland     minor_t minor, uint64_t now, dmu_tx_t *tx)
240a7a845e4SSteven Hartland {
241a7a845e4SSteven Hartland 	nvlist_t *tmpholds;
242a7a845e4SSteven Hartland 
243a7a845e4SSteven Hartland 	if (minor != 0)
244a7a845e4SSteven Hartland 		tmpholds = fnvlist_alloc();
245a7a845e4SSteven Hartland 	else
246a7a845e4SSteven Hartland 		tmpholds = NULL;
247a7a845e4SSteven Hartland 	dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx);
248a7a845e4SSteven Hartland 	dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor);
249a7a845e4SSteven Hartland }
250a7a845e4SSteven Hartland 
251a7a845e4SSteven Hartland static void
252a7a845e4SSteven Hartland dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx)
253a7a845e4SSteven Hartland {
254a7a845e4SSteven Hartland 	dsl_dataset_user_hold_arg_t *dduha = arg;
255a7a845e4SSteven Hartland 	dsl_pool_t *dp = dmu_tx_pool(tx);
256a7a845e4SSteven Hartland 	nvlist_t *tmpholds;
257a7a845e4SSteven Hartland 	uint64_t now = gethrestime_sec();
258a7a845e4SSteven Hartland 
259a7a845e4SSteven Hartland 	if (dduha->dduha_minor != 0)
260a7a845e4SSteven Hartland 		tmpholds = fnvlist_alloc();
261a7a845e4SSteven Hartland 	else
262a7a845e4SSteven Hartland 		tmpholds = NULL;
263a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL);
264a7a845e4SSteven Hartland 	    pair != NULL;
265a7a845e4SSteven Hartland 	    pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) {
266a7a845e4SSteven Hartland 		dsl_dataset_t *ds;
267a7a845e4SSteven Hartland 
268a7a845e4SSteven Hartland 		VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
269a7a845e4SSteven Hartland 		dsl_dataset_user_hold_sync_one_impl(tmpholds, ds,
270a7a845e4SSteven Hartland 		    fnvpair_value_string(pair), dduha->dduha_minor, now, tx);
271a7a845e4SSteven Hartland 		dsl_dataset_rele(ds, FTAG);
272a7a845e4SSteven Hartland 	}
273a7a845e4SSteven Hartland 	dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor);
274a7a845e4SSteven Hartland }
275a7a845e4SSteven Hartland 
276a7a845e4SSteven Hartland /*
277a7a845e4SSteven Hartland  * The full semantics of this function are described in the comment above
278a7a845e4SSteven Hartland  * lzc_hold().
279a7a845e4SSteven Hartland  *
280a7a845e4SSteven Hartland  * To summarize:
281a7a845e4SSteven Hartland  * holds is nvl of snapname -> holdname
282a7a845e4SSteven Hartland  * errlist will be filled in with snapname -> error
283a7a845e4SSteven Hartland  *
284a7a845e4SSteven Hartland  * The snaphosts must all be in the same pool.
285a7a845e4SSteven Hartland  *
286a7a845e4SSteven Hartland  * Holds for snapshots that don't exist will be skipped.
287a7a845e4SSteven Hartland  *
288a7a845e4SSteven Hartland  * If none of the snapshots for requested holds exist then ENOENT will be
289a7a845e4SSteven Hartland  * returned.
290a7a845e4SSteven Hartland  *
291a7a845e4SSteven Hartland  * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned
292a7a845e4SSteven Hartland  * up when the process exits.
293a7a845e4SSteven Hartland  *
294a7a845e4SSteven Hartland  * On success all the holds, for snapshots that existed, will be created and 0
295a7a845e4SSteven Hartland  * will be returned.
296a7a845e4SSteven Hartland  *
297a7a845e4SSteven Hartland  * On failure no holds will be created, the errlist will be filled in,
298a7a845e4SSteven Hartland  * and an errno will returned.
299a7a845e4SSteven Hartland  *
300a7a845e4SSteven Hartland  * In all cases the errlist will contain entries for holds where the snapshot
301a7a845e4SSteven Hartland  * didn't exist.
302a7a845e4SSteven Hartland  */
303a7a845e4SSteven Hartland int
304a7a845e4SSteven Hartland dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist)
305a7a845e4SSteven Hartland {
306a7a845e4SSteven Hartland 	dsl_dataset_user_hold_arg_t dduha;
307a7a845e4SSteven Hartland 	nvpair_t *pair;
308a7a845e4SSteven Hartland 	int ret;
309a7a845e4SSteven Hartland 
310a7a845e4SSteven Hartland 	pair = nvlist_next_nvpair(holds, NULL);
311a7a845e4SSteven Hartland 	if (pair == NULL)
312a7a845e4SSteven Hartland 		return (0);
313a7a845e4SSteven Hartland 
314a7a845e4SSteven Hartland 	dduha.dduha_holds = holds;
315a7a845e4SSteven Hartland 	dduha.dduha_chkholds = fnvlist_alloc();
316a7a845e4SSteven Hartland 	dduha.dduha_errlist = errlist;
317a7a845e4SSteven Hartland 	dduha.dduha_minor = cleanup_minor;
318a7a845e4SSteven Hartland 
319a7a845e4SSteven Hartland 	ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
3207d46dc6cSMatthew Ahrens 	    dsl_dataset_user_hold_sync, &dduha,
3217d46dc6cSMatthew Ahrens 	    fnvlist_num_pairs(holds), ZFS_SPACE_CHECK_RESERVED);
322a7a845e4SSteven Hartland 	fnvlist_free(dduha.dduha_chkholds);
323a7a845e4SSteven Hartland 
324a7a845e4SSteven Hartland 	return (ret);
325a7a845e4SSteven Hartland }
326a7a845e4SSteven Hartland 
327a7a845e4SSteven Hartland typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag,
328a7a845e4SSteven Hartland     dsl_dataset_t **dsp);
329a7a845e4SSteven Hartland 
330a7a845e4SSteven Hartland typedef struct dsl_dataset_user_release_arg {
331a7a845e4SSteven Hartland 	dsl_holdfunc_t *ddura_holdfunc;
332a7a845e4SSteven Hartland 	nvlist_t *ddura_holds;
333a7a845e4SSteven Hartland 	nvlist_t *ddura_todelete;
334a7a845e4SSteven Hartland 	nvlist_t *ddura_errlist;
335a7a845e4SSteven Hartland 	nvlist_t *ddura_chkholds;
336a7a845e4SSteven Hartland } dsl_dataset_user_release_arg_t;
337a7a845e4SSteven Hartland 
338a7a845e4SSteven Hartland /* Place a dataset hold on the snapshot identified by passed dsobj string */
339a7a845e4SSteven Hartland static int
340a7a845e4SSteven Hartland dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag,
341a7a845e4SSteven Hartland     dsl_dataset_t **dsp)
342a7a845e4SSteven Hartland {
343a7a845e4SSteven Hartland 	return (dsl_dataset_hold_obj(dp, strtonum(dsobj, NULL), tag, dsp));
344a7a845e4SSteven Hartland }
345a7a845e4SSteven Hartland 
346a7a845e4SSteven Hartland static int
347a7a845e4SSteven Hartland dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura,
348a7a845e4SSteven Hartland     dsl_dataset_t *ds, nvlist_t *holds, const char *snapname)
349a7a845e4SSteven Hartland {
350a7a845e4SSteven Hartland 	uint64_t zapobj;
351a7a845e4SSteven Hartland 	nvlist_t *holds_found;
352a7a845e4SSteven Hartland 	objset_t *mos;
353a7a845e4SSteven Hartland 	int numholds;
354a7a845e4SSteven Hartland 
355bc9014e6SJustin Gibbs 	if (!ds->ds_is_snapshot)
356a7a845e4SSteven Hartland 		return (SET_ERROR(EINVAL));
357a7a845e4SSteven Hartland 
358a7a845e4SSteven Hartland 	if (nvlist_empty(holds))
359a7a845e4SSteven Hartland 		return (0);
360a7a845e4SSteven Hartland 
361a7a845e4SSteven Hartland 	numholds = 0;
362a7a845e4SSteven Hartland 	mos = ds->ds_dir->dd_pool->dp_meta_objset;
363c1379625SJustin T. Gibbs 	zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
364a7a845e4SSteven Hartland 	holds_found = fnvlist_alloc();
365a7a845e4SSteven Hartland 
366a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
367a7a845e4SSteven Hartland 	    pair = nvlist_next_nvpair(holds, pair)) {
368a7a845e4SSteven Hartland 		uint64_t tmp;
369a7a845e4SSteven Hartland 		int error;
370a7a845e4SSteven Hartland 		const char *holdname = nvpair_name(pair);
371a7a845e4SSteven Hartland 
372a7a845e4SSteven Hartland 		if (zapobj != 0)
373a7a845e4SSteven Hartland 			error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp);
374a7a845e4SSteven Hartland 		else
375a7a845e4SSteven Hartland 			error = SET_ERROR(ENOENT);
376a7a845e4SSteven Hartland 
377a7a845e4SSteven Hartland 		/*
378a7a845e4SSteven Hartland 		 * Non-existent holds are put on the errlist, but don't
379a7a845e4SSteven Hartland 		 * cause an overall failure.
380a7a845e4SSteven Hartland 		 */
381a7a845e4SSteven Hartland 		if (error == ENOENT) {
382a7a845e4SSteven Hartland 			if (ddura->ddura_errlist != NULL) {
383a7a845e4SSteven Hartland 				char *errtag = kmem_asprintf("%s#%s",
384a7a845e4SSteven Hartland 				    snapname, holdname);
385a7a845e4SSteven Hartland 				fnvlist_add_int32(ddura->ddura_errlist, errtag,
386a7a845e4SSteven Hartland 				    ENOENT);
387a7a845e4SSteven Hartland 				strfree(errtag);
388a7a845e4SSteven Hartland 			}
389a7a845e4SSteven Hartland 			continue;
390a7a845e4SSteven Hartland 		}
391a7a845e4SSteven Hartland 
392a7a845e4SSteven Hartland 		if (error != 0) {
393a7a845e4SSteven Hartland 			fnvlist_free(holds_found);
394a7a845e4SSteven Hartland 			return (error);
395a7a845e4SSteven Hartland 		}
396a7a845e4SSteven Hartland 
397a7a845e4SSteven Hartland 		fnvlist_add_boolean(holds_found, holdname);
398a7a845e4SSteven Hartland 		numholds++;
399a7a845e4SSteven Hartland 	}
400a7a845e4SSteven Hartland 
401c1379625SJustin T. Gibbs 	if (DS_IS_DEFER_DESTROY(ds) &&
402c1379625SJustin T. Gibbs 	    dsl_dataset_phys(ds)->ds_num_children == 1 &&
403a7a845e4SSteven Hartland 	    ds->ds_userrefs == numholds) {
404a7a845e4SSteven Hartland 		/* we need to destroy the snapshot as well */
405a7a845e4SSteven Hartland 		if (dsl_dataset_long_held(ds)) {
406a7a845e4SSteven Hartland 			fnvlist_free(holds_found);
407a7a845e4SSteven Hartland 			return (SET_ERROR(EBUSY));
408a7a845e4SSteven Hartland 		}
409a7a845e4SSteven Hartland 		fnvlist_add_boolean(ddura->ddura_todelete, snapname);
410a7a845e4SSteven Hartland 	}
411a7a845e4SSteven Hartland 
412a7a845e4SSteven Hartland 	if (numholds != 0) {
413a7a845e4SSteven Hartland 		fnvlist_add_nvlist(ddura->ddura_chkholds, snapname,
414a7a845e4SSteven Hartland 		    holds_found);
415a7a845e4SSteven Hartland 	}
416a7a845e4SSteven Hartland 	fnvlist_free(holds_found);
417a7a845e4SSteven Hartland 
418a7a845e4SSteven Hartland 	return (0);
419a7a845e4SSteven Hartland }
420a7a845e4SSteven Hartland 
421a7a845e4SSteven Hartland static int
422a7a845e4SSteven Hartland dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx)
423a7a845e4SSteven Hartland {
424a7a845e4SSteven Hartland 	dsl_dataset_user_release_arg_t *ddura;
425a7a845e4SSteven Hartland 	dsl_holdfunc_t *holdfunc;
426a7a845e4SSteven Hartland 	dsl_pool_t *dp;
427a7a845e4SSteven Hartland 
428a7a845e4SSteven Hartland 	if (!dmu_tx_is_syncing(tx))
429a7a845e4SSteven Hartland 		return (0);
430a7a845e4SSteven Hartland 
431a7a845e4SSteven Hartland 	dp = dmu_tx_pool(tx);
432a7a845e4SSteven Hartland 
433a7a845e4SSteven Hartland 	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
434a7a845e4SSteven Hartland 
435a7a845e4SSteven Hartland 	ddura = arg;
436a7a845e4SSteven Hartland 	holdfunc = ddura->ddura_holdfunc;
437a7a845e4SSteven Hartland 
438a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_holds, NULL);
439a7a845e4SSteven Hartland 	    pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
440a7a845e4SSteven Hartland 		int error;
441a7a845e4SSteven Hartland 		dsl_dataset_t *ds;
442a7a845e4SSteven Hartland 		nvlist_t *holds;
443a7a845e4SSteven Hartland 		const char *snapname = nvpair_name(pair);
444a7a845e4SSteven Hartland 
445a7a845e4SSteven Hartland 		error = nvpair_value_nvlist(pair, &holds);
446a7a845e4SSteven Hartland 		if (error != 0)
447a7a845e4SSteven Hartland 			error = (SET_ERROR(EINVAL));
448a7a845e4SSteven Hartland 		else
449a7a845e4SSteven Hartland 			error = holdfunc(dp, snapname, FTAG, &ds);
450a7a845e4SSteven Hartland 		if (error == 0) {
451a7a845e4SSteven Hartland 			error = dsl_dataset_user_release_check_one(ddura, ds,
452a7a845e4SSteven Hartland 			    holds, snapname);
453a7a845e4SSteven Hartland 			dsl_dataset_rele(ds, FTAG);
454a7a845e4SSteven Hartland 		}
455a7a845e4SSteven Hartland 		if (error != 0) {
456a7a845e4SSteven Hartland 			if (ddura->ddura_errlist != NULL) {
457a7a845e4SSteven Hartland 				fnvlist_add_int32(ddura->ddura_errlist,
458a7a845e4SSteven Hartland 				    snapname, error);
459a7a845e4SSteven Hartland 			}
460a7a845e4SSteven Hartland 			/*
461a7a845e4SSteven Hartland 			 * Non-existent snapshots are put on the errlist,
462a7a845e4SSteven Hartland 			 * but don't cause an overall failure.
463a7a845e4SSteven Hartland 			 */
464a7a845e4SSteven Hartland 			if (error != ENOENT)
465a7a845e4SSteven Hartland 				return (error);
466a7a845e4SSteven Hartland 		}
467a7a845e4SSteven Hartland 	}
468a7a845e4SSteven Hartland 
469a7a845e4SSteven Hartland 	return (0);
470a7a845e4SSteven Hartland }
471a7a845e4SSteven Hartland 
472a7a845e4SSteven Hartland static void
473a7a845e4SSteven Hartland dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds,
474a7a845e4SSteven Hartland     dmu_tx_t *tx)
475a7a845e4SSteven Hartland {
476a7a845e4SSteven Hartland 	dsl_pool_t *dp = ds->ds_dir->dd_pool;
477a7a845e4SSteven Hartland 	objset_t *mos = dp->dp_meta_objset;
478a7a845e4SSteven Hartland 
479a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
480a7a845e4SSteven Hartland 	    pair = nvlist_next_nvpair(holds, pair)) {
481a7a845e4SSteven Hartland 		int error;
482a7a845e4SSteven Hartland 		const char *holdname = nvpair_name(pair);
483a7a845e4SSteven Hartland 
484a7a845e4SSteven Hartland 		/* Remove temporary hold if one exists. */
485a7a845e4SSteven Hartland 		error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx);
486a7a845e4SSteven Hartland 		VERIFY(error == 0 || error == ENOENT);
487a7a845e4SSteven Hartland 
488c1379625SJustin T. Gibbs 		VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
489c1379625SJustin T. Gibbs 		    holdname, tx));
490a7a845e4SSteven Hartland 		ds->ds_userrefs--;
491a7a845e4SSteven Hartland 
492a7a845e4SSteven Hartland 		spa_history_log_internal_ds(ds, "release", tx,
493a7a845e4SSteven Hartland 		    "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs);
494a7a845e4SSteven Hartland 	}
495a7a845e4SSteven Hartland }
496a7a845e4SSteven Hartland 
497a7a845e4SSteven Hartland static void
498a7a845e4SSteven Hartland dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx)
499a7a845e4SSteven Hartland {
500a7a845e4SSteven Hartland 	dsl_dataset_user_release_arg_t *ddura = arg;
501a7a845e4SSteven Hartland 	dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc;
502a7a845e4SSteven Hartland 	dsl_pool_t *dp = dmu_tx_pool(tx);
503a7a845e4SSteven Hartland 
504a7a845e4SSteven Hartland 	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
505a7a845e4SSteven Hartland 
506a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL);
507a7a845e4SSteven Hartland 	    pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds,
508a7a845e4SSteven Hartland 	    pair)) {
509a7a845e4SSteven Hartland 		dsl_dataset_t *ds;
510a7a845e4SSteven Hartland 		const char *name = nvpair_name(pair);
511a7a845e4SSteven Hartland 
512a7a845e4SSteven Hartland 		VERIFY0(holdfunc(dp, name, FTAG, &ds));
513a7a845e4SSteven Hartland 
514a7a845e4SSteven Hartland 		dsl_dataset_user_release_sync_one(ds,
515a7a845e4SSteven Hartland 		    fnvpair_value_nvlist(pair), tx);
516a7a845e4SSteven Hartland 		if (nvlist_exists(ddura->ddura_todelete, name)) {
517a7a845e4SSteven Hartland 			ASSERT(ds->ds_userrefs == 0 &&
518c1379625SJustin T. Gibbs 			    dsl_dataset_phys(ds)->ds_num_children == 1 &&
519a7a845e4SSteven Hartland 			    DS_IS_DEFER_DESTROY(ds));
520a7a845e4SSteven Hartland 			dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
521a7a845e4SSteven Hartland 		}
522a7a845e4SSteven Hartland 		dsl_dataset_rele(ds, FTAG);
523a7a845e4SSteven Hartland 	}
524a7a845e4SSteven Hartland }
525a7a845e4SSteven Hartland 
526a7a845e4SSteven Hartland /*
527a7a845e4SSteven Hartland  * The full semantics of this function are described in the comment above
528a7a845e4SSteven Hartland  * lzc_release().
529a7a845e4SSteven Hartland  *
530a7a845e4SSteven Hartland  * To summarize:
531a7a845e4SSteven Hartland  * Releases holds specified in the nvl holds.
532a7a845e4SSteven Hartland  *
533a7a845e4SSteven Hartland  * holds is nvl of snapname -> { holdname, ... }
534a7a845e4SSteven Hartland  * errlist will be filled in with snapname -> error
535a7a845e4SSteven Hartland  *
536a7a845e4SSteven Hartland  * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots,
537a7a845e4SSteven Hartland  * otherwise they should be the names of shapshots.
538a7a845e4SSteven Hartland  *
539a7a845e4SSteven Hartland  * As a release may cause snapshots to be destroyed this trys to ensure they
540a7a845e4SSteven Hartland  * aren't mounted.
541a7a845e4SSteven Hartland  *
542a7a845e4SSteven Hartland  * The release of non-existent holds are skipped.
543a7a845e4SSteven Hartland  *
544a7a845e4SSteven Hartland  * At least one hold must have been released for the this function to succeed
545a7a845e4SSteven Hartland  * and return 0.
546a7a845e4SSteven Hartland  */
547a7a845e4SSteven Hartland static int
548a7a845e4SSteven Hartland dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist,
549a7a845e4SSteven Hartland     dsl_pool_t *tmpdp)
550a7a845e4SSteven Hartland {
551a7a845e4SSteven Hartland 	dsl_dataset_user_release_arg_t ddura;
552a7a845e4SSteven Hartland 	nvpair_t *pair;
553a7a845e4SSteven Hartland 	char *pool;
554a7a845e4SSteven Hartland 	int error;
555a7a845e4SSteven Hartland 
556a7a845e4SSteven Hartland 	pair = nvlist_next_nvpair(holds, NULL);
557a7a845e4SSteven Hartland 	if (pair == NULL)
558a7a845e4SSteven Hartland 		return (0);
559a7a845e4SSteven Hartland 
560a7a845e4SSteven Hartland 	/*
561a7a845e4SSteven Hartland 	 * The release may cause snapshots to be destroyed; make sure they
562a7a845e4SSteven Hartland 	 * are not mounted.
563a7a845e4SSteven Hartland 	 */
564a7a845e4SSteven Hartland 	if (tmpdp != NULL) {
565a7a845e4SSteven Hartland 		/* Temporary holds are specified by dsobj string. */
566a7a845e4SSteven Hartland 		ddura.ddura_holdfunc = dsl_dataset_hold_obj_string;
567a7a845e4SSteven Hartland 		pool = spa_name(tmpdp->dp_spa);
568a7a845e4SSteven Hartland #ifdef _KERNEL
569a7a845e4SSteven Hartland 		for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
570a7a845e4SSteven Hartland 		    pair = nvlist_next_nvpair(holds, pair)) {
571a7a845e4SSteven Hartland 			dsl_dataset_t *ds;
572a7a845e4SSteven Hartland 
573c50d56f6SSteven Hartland 			dsl_pool_config_enter(tmpdp, FTAG);
574a7a845e4SSteven Hartland 			error = dsl_dataset_hold_obj_string(tmpdp,
575a7a845e4SSteven Hartland 			    nvpair_name(pair), FTAG, &ds);
576a7a845e4SSteven Hartland 			if (error == 0) {
577*40a5c998SMatthew Ahrens 				char name[ZFS_MAX_DATASET_NAME_LEN];
578a7a845e4SSteven Hartland 				dsl_dataset_name(ds, name);
579c50d56f6SSteven Hartland 				dsl_pool_config_exit(tmpdp, FTAG);
580a7a845e4SSteven Hartland 				dsl_dataset_rele(ds, FTAG);
581a7a845e4SSteven Hartland 				(void) zfs_unmount_snap(name);
582c50d56f6SSteven Hartland 			} else {
583a7a845e4SSteven Hartland 				dsl_pool_config_exit(tmpdp, FTAG);
584c50d56f6SSteven Hartland 			}
585c50d56f6SSteven Hartland 		}
586a7a845e4SSteven Hartland #endif
587a7a845e4SSteven Hartland 	} else {
588a7a845e4SSteven Hartland 		/* Non-temporary holds are specified by name. */
589a7a845e4SSteven Hartland 		ddura.ddura_holdfunc = dsl_dataset_hold;
590a7a845e4SSteven Hartland 		pool = nvpair_name(pair);
591a7a845e4SSteven Hartland #ifdef _KERNEL
592a7a845e4SSteven Hartland 		for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
593a7a845e4SSteven Hartland 		    pair = nvlist_next_nvpair(holds, pair)) {
594a7a845e4SSteven Hartland 			(void) zfs_unmount_snap(nvpair_name(pair));
595a7a845e4SSteven Hartland 		}
596a7a845e4SSteven Hartland #endif
597a7a845e4SSteven Hartland 	}
598a7a845e4SSteven Hartland 
599a7a845e4SSteven Hartland 	ddura.ddura_holds = holds;
600a7a845e4SSteven Hartland 	ddura.ddura_errlist = errlist;
601a7a845e4SSteven Hartland 	ddura.ddura_todelete = fnvlist_alloc();
602a7a845e4SSteven Hartland 	ddura.ddura_chkholds = fnvlist_alloc();
603a7a845e4SSteven Hartland 
604a7a845e4SSteven Hartland 	error = dsl_sync_task(pool, dsl_dataset_user_release_check,
6057d46dc6cSMatthew Ahrens 	    dsl_dataset_user_release_sync, &ddura, 0, ZFS_SPACE_CHECK_NONE);
606a7a845e4SSteven Hartland 	fnvlist_free(ddura.ddura_todelete);
607a7a845e4SSteven Hartland 	fnvlist_free(ddura.ddura_chkholds);
608a7a845e4SSteven Hartland 
609a7a845e4SSteven Hartland 	return (error);
610a7a845e4SSteven Hartland }
611a7a845e4SSteven Hartland 
612a7a845e4SSteven Hartland /*
613a7a845e4SSteven Hartland  * holds is nvl of snapname -> { holdname, ... }
614a7a845e4SSteven Hartland  * errlist will be filled in with snapname -> error
615a7a845e4SSteven Hartland  */
616a7a845e4SSteven Hartland int
617a7a845e4SSteven Hartland dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist)
618a7a845e4SSteven Hartland {
619a7a845e4SSteven Hartland 	return (dsl_dataset_user_release_impl(holds, errlist, NULL));
620a7a845e4SSteven Hartland }
621a7a845e4SSteven Hartland 
622a7a845e4SSteven Hartland /*
623a7a845e4SSteven Hartland  * holds is nvl of snapdsobj -> { holdname, ... }
624a7a845e4SSteven Hartland  */
625a7a845e4SSteven Hartland void
626a7a845e4SSteven Hartland dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds)
627a7a845e4SSteven Hartland {
628a7a845e4SSteven Hartland 	ASSERT(dp != NULL);
629a7a845e4SSteven Hartland 	(void) dsl_dataset_user_release_impl(holds, NULL, dp);
630a7a845e4SSteven Hartland }
631a7a845e4SSteven Hartland 
6323b2aab18SMatthew Ahrens int
6333b2aab18SMatthew Ahrens dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl)
6343b2aab18SMatthew Ahrens {
6353b2aab18SMatthew Ahrens 	dsl_pool_t *dp;
6363b2aab18SMatthew Ahrens 	dsl_dataset_t *ds;
6373b2aab18SMatthew Ahrens 	int err;
6383b2aab18SMatthew Ahrens 
6393b2aab18SMatthew Ahrens 	err = dsl_pool_hold(dsname, FTAG, &dp);
6403b2aab18SMatthew Ahrens 	if (err != 0)
6413b2aab18SMatthew Ahrens 		return (err);
6423b2aab18SMatthew Ahrens 	err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
6433b2aab18SMatthew Ahrens 	if (err != 0) {
6443b2aab18SMatthew Ahrens 		dsl_pool_rele(dp, FTAG);
6453b2aab18SMatthew Ahrens 		return (err);
6463b2aab18SMatthew Ahrens 	}
6473b2aab18SMatthew Ahrens 
648c1379625SJustin T. Gibbs 	if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) {
6493b2aab18SMatthew Ahrens 		zap_attribute_t *za;
6503b2aab18SMatthew Ahrens 		zap_cursor_t zc;
6513b2aab18SMatthew Ahrens 
6523b2aab18SMatthew Ahrens 		za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
6533b2aab18SMatthew Ahrens 		for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
654c1379625SJustin T. Gibbs 		    dsl_dataset_phys(ds)->ds_userrefs_obj);
6553b2aab18SMatthew Ahrens 		    zap_cursor_retrieve(&zc, za) == 0;
6563b2aab18SMatthew Ahrens 		    zap_cursor_advance(&zc)) {
6573b2aab18SMatthew Ahrens 			fnvlist_add_uint64(nvl, za->za_name,
6583b2aab18SMatthew Ahrens 			    za->za_first_integer);
6593b2aab18SMatthew Ahrens 		}
6603b2aab18SMatthew Ahrens 		zap_cursor_fini(&zc);
6613b2aab18SMatthew Ahrens 		kmem_free(za, sizeof (zap_attribute_t));
6623b2aab18SMatthew Ahrens 	}
6633b2aab18SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
6643b2aab18SMatthew Ahrens 	dsl_pool_rele(dp, FTAG);
6653b2aab18SMatthew Ahrens 	return (0);
6663b2aab18SMatthew Ahrens }
667