xref: /illumos-gate/usr/src/uts/common/fs/zfs/dsl_dir.c (revision 013023d4ed2f6d0cf75380ec686a4aac392b4e43)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5ea8dc4b6Seschrock  * Common Development and Distribution License (the "License").
6ea8dc4b6Seschrock  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21fa9e4066Sahrens /*
223f9d6ad7SLin Ling  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23be6fd75aSMatthew Ahrens  * Copyright (c) 2013 by Delphix. All rights reserved.
24*013023d4SMartin Matuska  * Copyright (c) 2013 Martin Matuska. All rights reserved.
25fa9e4066Sahrens  */
26fa9e4066Sahrens 
27fa9e4066Sahrens #include <sys/dmu.h>
28a9799022Sck #include <sys/dmu_objset.h>
29fa9e4066Sahrens #include <sys/dmu_tx.h>
30fa9e4066Sahrens #include <sys/dsl_dataset.h>
31fa9e4066Sahrens #include <sys/dsl_dir.h>
32fa9e4066Sahrens #include <sys/dsl_prop.h>
331d452cf5Sahrens #include <sys/dsl_synctask.h>
34ecd6cf80Smarks #include <sys/dsl_deleg.h>
35fa9e4066Sahrens #include <sys/spa.h>
36b24ab676SJeff Bonwick #include <sys/metaslab.h>
37fa9e4066Sahrens #include <sys/zap.h>
38fa9e4066Sahrens #include <sys/zio.h>
39fa9e4066Sahrens #include <sys/arc.h>
40ecd6cf80Smarks #include <sys/sunddi.h>
41fa9e4066Sahrens #include "zfs_namecheck.h"
42fa9e4066Sahrens 
43a9799022Sck static uint64_t dsl_dir_space_towrite(dsl_dir_t *dd);
44fa9e4066Sahrens 
45fa9e4066Sahrens /* ARGSUSED */
46fa9e4066Sahrens static void
47fa9e4066Sahrens dsl_dir_evict(dmu_buf_t *db, void *arg)
48fa9e4066Sahrens {
49fa9e4066Sahrens 	dsl_dir_t *dd = arg;
50fa9e4066Sahrens 	dsl_pool_t *dp = dd->dd_pool;
51fa9e4066Sahrens 	int t;
52fa9e4066Sahrens 
53fa9e4066Sahrens 	for (t = 0; t < TXG_SIZE; t++) {
54fa9e4066Sahrens 		ASSERT(!txg_list_member(&dp->dp_dirty_dirs, dd, t));
55fa9e4066Sahrens 		ASSERT(dd->dd_tempreserved[t] == 0);
56fa9e4066Sahrens 		ASSERT(dd->dd_space_towrite[t] == 0);
57fa9e4066Sahrens 	}
58fa9e4066Sahrens 
59fa9e4066Sahrens 	if (dd->dd_parent)
603b2aab18SMatthew Ahrens 		dsl_dir_rele(dd->dd_parent, dd);
61fa9e4066Sahrens 
62fa9e4066Sahrens 	spa_close(dd->dd_pool->dp_spa, dd);
63fa9e4066Sahrens 
64fa9e4066Sahrens 	/*
653f9d6ad7SLin Ling 	 * The props callback list should have been cleaned up by
663f9d6ad7SLin Ling 	 * objset_evict().
67fa9e4066Sahrens 	 */
68fa9e4066Sahrens 	list_destroy(&dd->dd_prop_cbs);
695ad82045Snd 	mutex_destroy(&dd->dd_lock);
70fa9e4066Sahrens 	kmem_free(dd, sizeof (dsl_dir_t));
71fa9e4066Sahrens }
72fa9e4066Sahrens 
73ea8dc4b6Seschrock int
743b2aab18SMatthew Ahrens dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj,
75ea8dc4b6Seschrock     const char *tail, void *tag, dsl_dir_t **ddp)
76fa9e4066Sahrens {
77fa9e4066Sahrens 	dmu_buf_t *dbuf;
78fa9e4066Sahrens 	dsl_dir_t *dd;
79ea8dc4b6Seschrock 	int err;
80fa9e4066Sahrens 
813b2aab18SMatthew Ahrens 	ASSERT(dsl_pool_config_held(dp));
82fa9e4066Sahrens 
83ea8dc4b6Seschrock 	err = dmu_bonus_hold(dp->dp_meta_objset, ddobj, tag, &dbuf);
843b2aab18SMatthew Ahrens 	if (err != 0)
85ea8dc4b6Seschrock 		return (err);
86fa9e4066Sahrens 	dd = dmu_buf_get_user(dbuf);
87fa9e4066Sahrens #ifdef ZFS_DEBUG
88fa9e4066Sahrens 	{
89fa9e4066Sahrens 		dmu_object_info_t doi;
90fa9e4066Sahrens 		dmu_object_info_from_db(dbuf, &doi);
911649cd4bStabriz 		ASSERT3U(doi.doi_type, ==, DMU_OT_DSL_DIR);
9274e7dc98SMatthew Ahrens 		ASSERT3U(doi.doi_bonus_size, >=, sizeof (dsl_dir_phys_t));
93fa9e4066Sahrens 	}
94fa9e4066Sahrens #endif
95fa9e4066Sahrens 	if (dd == NULL) {
96fa9e4066Sahrens 		dsl_dir_t *winner;
97fa9e4066Sahrens 
98fa9e4066Sahrens 		dd = kmem_zalloc(sizeof (dsl_dir_t), KM_SLEEP);
99fa9e4066Sahrens 		dd->dd_object = ddobj;
100fa9e4066Sahrens 		dd->dd_dbuf = dbuf;
101fa9e4066Sahrens 		dd->dd_pool = dp;
102fa9e4066Sahrens 		dd->dd_phys = dbuf->db_data;
1035ad82045Snd 		mutex_init(&dd->dd_lock, NULL, MUTEX_DEFAULT, NULL);
104fa9e4066Sahrens 
105fa9e4066Sahrens 		list_create(&dd->dd_prop_cbs, sizeof (dsl_prop_cb_record_t),
106fa9e4066Sahrens 		    offsetof(dsl_prop_cb_record_t, cbr_node));
107fa9e4066Sahrens 
10871eb0538SChris Kirby 		dsl_dir_snap_cmtime_update(dd);
10971eb0538SChris Kirby 
110fa9e4066Sahrens 		if (dd->dd_phys->dd_parent_obj) {
1113b2aab18SMatthew Ahrens 			err = dsl_dir_hold_obj(dp, dd->dd_phys->dd_parent_obj,
112ea8dc4b6Seschrock 			    NULL, dd, &dd->dd_parent);
1133b2aab18SMatthew Ahrens 			if (err != 0)
11474e7dc98SMatthew Ahrens 				goto errout;
115fa9e4066Sahrens 			if (tail) {
116fa9e4066Sahrens #ifdef ZFS_DEBUG
117fa9e4066Sahrens 				uint64_t foundobj;
118fa9e4066Sahrens 
119fa9e4066Sahrens 				err = zap_lookup(dp->dp_meta_objset,
120e7437265Sahrens 				    dd->dd_parent->dd_phys->dd_child_dir_zapobj,
121fa9e4066Sahrens 				    tail, sizeof (foundobj), 1, &foundobj);
122ea8dc4b6Seschrock 				ASSERT(err || foundobj == ddobj);
123fa9e4066Sahrens #endif
124fa9e4066Sahrens 				(void) strcpy(dd->dd_myname, tail);
125fa9e4066Sahrens 			} else {
126fa9e4066Sahrens 				err = zap_value_search(dp->dp_meta_objset,
127e7437265Sahrens 				    dd->dd_parent->dd_phys->dd_child_dir_zapobj,
128e7437265Sahrens 				    ddobj, 0, dd->dd_myname);
129ea8dc4b6Seschrock 			}
1303b2aab18SMatthew Ahrens 			if (err != 0)
13174e7dc98SMatthew Ahrens 				goto errout;
132fa9e4066Sahrens 		} else {
133fa9e4066Sahrens 			(void) strcpy(dd->dd_myname, spa_name(dp->dp_spa));
134fa9e4066Sahrens 		}
135fa9e4066Sahrens 
1363f9d6ad7SLin Ling 		if (dsl_dir_is_clone(dd)) {
1373f9d6ad7SLin Ling 			dmu_buf_t *origin_bonus;
1383f9d6ad7SLin Ling 			dsl_dataset_phys_t *origin_phys;
1393f9d6ad7SLin Ling 
1403f9d6ad7SLin Ling 			/*
1413f9d6ad7SLin Ling 			 * We can't open the origin dataset, because
1423f9d6ad7SLin Ling 			 * that would require opening this dsl_dir.
1433f9d6ad7SLin Ling 			 * Just look at its phys directly instead.
1443f9d6ad7SLin Ling 			 */
1453f9d6ad7SLin Ling 			err = dmu_bonus_hold(dp->dp_meta_objset,
1463f9d6ad7SLin Ling 			    dd->dd_phys->dd_origin_obj, FTAG, &origin_bonus);
1473b2aab18SMatthew Ahrens 			if (err != 0)
1483f9d6ad7SLin Ling 				goto errout;
1493f9d6ad7SLin Ling 			origin_phys = origin_bonus->db_data;
1503f9d6ad7SLin Ling 			dd->dd_origin_txg =
1513f9d6ad7SLin Ling 			    origin_phys->ds_creation_txg;
1523f9d6ad7SLin Ling 			dmu_buf_rele(origin_bonus, FTAG);
1533f9d6ad7SLin Ling 		}
1543f9d6ad7SLin Ling 
155fa9e4066Sahrens 		winner = dmu_buf_set_user_ie(dbuf, dd, &dd->dd_phys,
156fa9e4066Sahrens 		    dsl_dir_evict);
157fa9e4066Sahrens 		if (winner) {
158fa9e4066Sahrens 			if (dd->dd_parent)
1593b2aab18SMatthew Ahrens 				dsl_dir_rele(dd->dd_parent, dd);
1605ad82045Snd 			mutex_destroy(&dd->dd_lock);
161fa9e4066Sahrens 			kmem_free(dd, sizeof (dsl_dir_t));
162fa9e4066Sahrens 			dd = winner;
163fa9e4066Sahrens 		} else {
164fa9e4066Sahrens 			spa_open_ref(dp->dp_spa, dd);
165fa9e4066Sahrens 		}
166fa9e4066Sahrens 	}
167fa9e4066Sahrens 
168fa9e4066Sahrens 	/*
169fa9e4066Sahrens 	 * The dsl_dir_t has both open-to-close and instantiate-to-evict
170fa9e4066Sahrens 	 * holds on the spa.  We need the open-to-close holds because
171fa9e4066Sahrens 	 * otherwise the spa_refcnt wouldn't change when we open a
172fa9e4066Sahrens 	 * dir which the spa also has open, so we could incorrectly
173fa9e4066Sahrens 	 * think it was OK to unload/export/destroy the pool.  We need
174fa9e4066Sahrens 	 * the instantiate-to-evict hold because the dsl_dir_t has a
175fa9e4066Sahrens 	 * pointer to the dd_pool, which has a pointer to the spa_t.
176fa9e4066Sahrens 	 */
177fa9e4066Sahrens 	spa_open_ref(dp->dp_spa, tag);
178fa9e4066Sahrens 	ASSERT3P(dd->dd_pool, ==, dp);
179fa9e4066Sahrens 	ASSERT3U(dd->dd_object, ==, ddobj);
180fa9e4066Sahrens 	ASSERT3P(dd->dd_dbuf, ==, dbuf);
181ea8dc4b6Seschrock 	*ddp = dd;
182ea8dc4b6Seschrock 	return (0);
18374e7dc98SMatthew Ahrens 
18474e7dc98SMatthew Ahrens errout:
18574e7dc98SMatthew Ahrens 	if (dd->dd_parent)
1863b2aab18SMatthew Ahrens 		dsl_dir_rele(dd->dd_parent, dd);
18774e7dc98SMatthew Ahrens 	mutex_destroy(&dd->dd_lock);
18874e7dc98SMatthew Ahrens 	kmem_free(dd, sizeof (dsl_dir_t));
18974e7dc98SMatthew Ahrens 	dmu_buf_rele(dbuf, tag);
19074e7dc98SMatthew Ahrens 	return (err);
191fa9e4066Sahrens }
192fa9e4066Sahrens 
193fa9e4066Sahrens void
1943b2aab18SMatthew Ahrens dsl_dir_rele(dsl_dir_t *dd, void *tag)
195fa9e4066Sahrens {
196fa9e4066Sahrens 	dprintf_dd(dd, "%s\n", "");
197fa9e4066Sahrens 	spa_close(dd->dd_pool->dp_spa, tag);
198ea8dc4b6Seschrock 	dmu_buf_rele(dd->dd_dbuf, tag);
199fa9e4066Sahrens }
200fa9e4066Sahrens 
20115f66a7fSek /* buf must be long enough (MAXNAMELEN + strlen(MOS_DIR_NAME) + 1 should do) */
202fa9e4066Sahrens void
203fa9e4066Sahrens dsl_dir_name(dsl_dir_t *dd, char *buf)
204fa9e4066Sahrens {
205fa9e4066Sahrens 	if (dd->dd_parent) {
206fa9e4066Sahrens 		dsl_dir_name(dd->dd_parent, buf);
207fa9e4066Sahrens 		(void) strcat(buf, "/");
208fa9e4066Sahrens 	} else {
209fa9e4066Sahrens 		buf[0] = '\0';
210fa9e4066Sahrens 	}
211fa9e4066Sahrens 	if (!MUTEX_HELD(&dd->dd_lock)) {
212fa9e4066Sahrens 		/*
213fa9e4066Sahrens 		 * recursive mutex so that we can use
214fa9e4066Sahrens 		 * dprintf_dd() with dd_lock held
215fa9e4066Sahrens 		 */
216fa9e4066Sahrens 		mutex_enter(&dd->dd_lock);
217fa9e4066Sahrens 		(void) strcat(buf, dd->dd_myname);
218fa9e4066Sahrens 		mutex_exit(&dd->dd_lock);
219fa9e4066Sahrens 	} else {
220fa9e4066Sahrens 		(void) strcat(buf, dd->dd_myname);
221fa9e4066Sahrens 	}
222fa9e4066Sahrens }
223fa9e4066Sahrens 
224ce636f8bSMatthew Ahrens /* Calculate name length, avoiding all the strcat calls of dsl_dir_name */
225b7661cccSmmusante int
226b7661cccSmmusante dsl_dir_namelen(dsl_dir_t *dd)
227b7661cccSmmusante {
228b7661cccSmmusante 	int result = 0;
229b7661cccSmmusante 
230b7661cccSmmusante 	if (dd->dd_parent) {
231b7661cccSmmusante 		/* parent's name + 1 for the "/" */
232b7661cccSmmusante 		result = dsl_dir_namelen(dd->dd_parent) + 1;
233b7661cccSmmusante 	}
234b7661cccSmmusante 
235b7661cccSmmusante 	if (!MUTEX_HELD(&dd->dd_lock)) {
236b7661cccSmmusante 		/* see dsl_dir_name */
237b7661cccSmmusante 		mutex_enter(&dd->dd_lock);
238b7661cccSmmusante 		result += strlen(dd->dd_myname);
239b7661cccSmmusante 		mutex_exit(&dd->dd_lock);
240b7661cccSmmusante 	} else {
241b7661cccSmmusante 		result += strlen(dd->dd_myname);
242b7661cccSmmusante 	}
243b7661cccSmmusante 
244b7661cccSmmusante 	return (result);
245b7661cccSmmusante }
246b7661cccSmmusante 
247fa9e4066Sahrens static int
248fa9e4066Sahrens getcomponent(const char *path, char *component, const char **nextp)
249fa9e4066Sahrens {
250fa9e4066Sahrens 	char *p;
2513b2aab18SMatthew Ahrens 
252ccba0801SRich Morris 	if ((path == NULL) || (path[0] == '\0'))
253be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENOENT));
254fa9e4066Sahrens 	/* This would be a good place to reserve some namespace... */
255fa9e4066Sahrens 	p = strpbrk(path, "/@");
256fa9e4066Sahrens 	if (p && (p[1] == '/' || p[1] == '@')) {
257fa9e4066Sahrens 		/* two separators in a row */
258be6fd75aSMatthew Ahrens 		return (SET_ERROR(EINVAL));
259fa9e4066Sahrens 	}
260fa9e4066Sahrens 	if (p == NULL || p == path) {
261fa9e4066Sahrens 		/*
262fa9e4066Sahrens 		 * if the first thing is an @ or /, it had better be an
263fa9e4066Sahrens 		 * @ and it had better not have any more ats or slashes,
264fa9e4066Sahrens 		 * and it had better have something after the @.
265fa9e4066Sahrens 		 */
266fa9e4066Sahrens 		if (p != NULL &&
267fa9e4066Sahrens 		    (p[0] != '@' || strpbrk(path+1, "/@") || p[1] == '\0'))
268be6fd75aSMatthew Ahrens 			return (SET_ERROR(EINVAL));
269fa9e4066Sahrens 		if (strlen(path) >= MAXNAMELEN)
270be6fd75aSMatthew Ahrens 			return (SET_ERROR(ENAMETOOLONG));
271fa9e4066Sahrens 		(void) strcpy(component, path);
272fa9e4066Sahrens 		p = NULL;
273fa9e4066Sahrens 	} else if (p[0] == '/') {
2743b2aab18SMatthew Ahrens 		if (p - path >= MAXNAMELEN)
275be6fd75aSMatthew Ahrens 			return (SET_ERROR(ENAMETOOLONG));
276fa9e4066Sahrens 		(void) strncpy(component, path, p - path);
2773b2aab18SMatthew Ahrens 		component[p - path] = '\0';
278fa9e4066Sahrens 		p++;
279fa9e4066Sahrens 	} else if (p[0] == '@') {
280fa9e4066Sahrens 		/*
281fa9e4066Sahrens 		 * if the next separator is an @, there better not be
282fa9e4066Sahrens 		 * any more slashes.
283fa9e4066Sahrens 		 */
284fa9e4066Sahrens 		if (strchr(path, '/'))
285be6fd75aSMatthew Ahrens 			return (SET_ERROR(EINVAL));
2863b2aab18SMatthew Ahrens 		if (p - path >= MAXNAMELEN)
287be6fd75aSMatthew Ahrens 			return (SET_ERROR(ENAMETOOLONG));
288fa9e4066Sahrens 		(void) strncpy(component, path, p - path);
2893b2aab18SMatthew Ahrens 		component[p - path] = '\0';
290fa9e4066Sahrens 	} else {
2913b2aab18SMatthew Ahrens 		panic("invalid p=%p", (void *)p);
292fa9e4066Sahrens 	}
293fa9e4066Sahrens 	*nextp = p;
294fa9e4066Sahrens 	return (0);
295fa9e4066Sahrens }
296fa9e4066Sahrens 
297fa9e4066Sahrens /*
2983b2aab18SMatthew Ahrens  * Return the dsl_dir_t, and possibly the last component which couldn't
2993b2aab18SMatthew Ahrens  * be found in *tail.  The name must be in the specified dsl_pool_t.  This
3003b2aab18SMatthew Ahrens  * thread must hold the dp_config_rwlock for the pool.  Returns NULL if the
3013b2aab18SMatthew Ahrens  * path is bogus, or if tail==NULL and we couldn't parse the whole name.
3023b2aab18SMatthew Ahrens  * (*tail)[0] == '@' means that the last component is a snapshot.
303fa9e4066Sahrens  */
304ea8dc4b6Seschrock int
3053b2aab18SMatthew Ahrens dsl_dir_hold(dsl_pool_t *dp, const char *name, void *tag,
306ea8dc4b6Seschrock     dsl_dir_t **ddp, const char **tailp)
307fa9e4066Sahrens {
308fa9e4066Sahrens 	char buf[MAXNAMELEN];
3093b2aab18SMatthew Ahrens 	const char *spaname, *next, *nextnext = NULL;
310fa9e4066Sahrens 	int err;
311fa9e4066Sahrens 	dsl_dir_t *dd;
312fa9e4066Sahrens 	uint64_t ddobj;
313fa9e4066Sahrens 
314fa9e4066Sahrens 	err = getcomponent(name, buf, &next);
3153b2aab18SMatthew Ahrens 	if (err != 0)
316ea8dc4b6Seschrock 		return (err);
317fa9e4066Sahrens 
3183b2aab18SMatthew Ahrens 	/* Make sure the name is in the specified pool. */
3193b2aab18SMatthew Ahrens 	spaname = spa_name(dp->dp_spa);
3203b2aab18SMatthew Ahrens 	if (strcmp(buf, spaname) != 0)
321be6fd75aSMatthew Ahrens 		return (SET_ERROR(EINVAL));
322fa9e4066Sahrens 
3233b2aab18SMatthew Ahrens 	ASSERT(dsl_pool_config_held(dp));
324fa9e4066Sahrens 
3253b2aab18SMatthew Ahrens 	err = dsl_dir_hold_obj(dp, dp->dp_root_dir_obj, NULL, tag, &dd);
3263b2aab18SMatthew Ahrens 	if (err != 0) {
327ea8dc4b6Seschrock 		return (err);
328ea8dc4b6Seschrock 	}
329ea8dc4b6Seschrock 
330fa9e4066Sahrens 	while (next != NULL) {
331fa9e4066Sahrens 		dsl_dir_t *child_ds;
332fa9e4066Sahrens 		err = getcomponent(next, buf, &nextnext);
3333b2aab18SMatthew Ahrens 		if (err != 0)
334ea8dc4b6Seschrock 			break;
335fa9e4066Sahrens 		ASSERT(next[0] != '\0');
336fa9e4066Sahrens 		if (next[0] == '@')
337fa9e4066Sahrens 			break;
338fa9e4066Sahrens 		dprintf("looking up %s in obj%lld\n",
339fa9e4066Sahrens 		    buf, dd->dd_phys->dd_child_dir_zapobj);
340fa9e4066Sahrens 
341fa9e4066Sahrens 		err = zap_lookup(dp->dp_meta_objset,
342fa9e4066Sahrens 		    dd->dd_phys->dd_child_dir_zapobj,
343fa9e4066Sahrens 		    buf, sizeof (ddobj), 1, &ddobj);
3443b2aab18SMatthew Ahrens 		if (err != 0) {
345ea8dc4b6Seschrock 			if (err == ENOENT)
346ea8dc4b6Seschrock 				err = 0;
347fa9e4066Sahrens 			break;
348fa9e4066Sahrens 		}
349fa9e4066Sahrens 
3503b2aab18SMatthew Ahrens 		err = dsl_dir_hold_obj(dp, ddobj, buf, tag, &child_ds);
3513b2aab18SMatthew Ahrens 		if (err != 0)
352ea8dc4b6Seschrock 			break;
3533b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, tag);
354fa9e4066Sahrens 		dd = child_ds;
355fa9e4066Sahrens 		next = nextnext;
356fa9e4066Sahrens 	}
357fa9e4066Sahrens 
3583b2aab18SMatthew Ahrens 	if (err != 0) {
3593b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, tag);
360ea8dc4b6Seschrock 		return (err);
361ea8dc4b6Seschrock 	}
362ea8dc4b6Seschrock 
363fa9e4066Sahrens 	/*
364fa9e4066Sahrens 	 * It's an error if there's more than one component left, or
365fa9e4066Sahrens 	 * tailp==NULL and there's any component left.
366fa9e4066Sahrens 	 */
367fa9e4066Sahrens 	if (next != NULL &&
368fa9e4066Sahrens 	    (tailp == NULL || (nextnext && nextnext[0] != '\0'))) {
369fa9e4066Sahrens 		/* bad path name */
3703b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, tag);
371fa9e4066Sahrens 		dprintf("next=%p (%s) tail=%p\n", next, next?next:"", tailp);
372be6fd75aSMatthew Ahrens 		err = SET_ERROR(ENOENT);
373fa9e4066Sahrens 	}
3743b2aab18SMatthew Ahrens 	if (tailp != NULL)
375fa9e4066Sahrens 		*tailp = next;
376ea8dc4b6Seschrock 	*ddp = dd;
377ea8dc4b6Seschrock 	return (err);
378fa9e4066Sahrens }
379fa9e4066Sahrens 
3801d452cf5Sahrens uint64_t
381088f3894Sahrens dsl_dir_create_sync(dsl_pool_t *dp, dsl_dir_t *pds, const char *name,
382088f3894Sahrens     dmu_tx_t *tx)
383fa9e4066Sahrens {
384088f3894Sahrens 	objset_t *mos = dp->dp_meta_objset;
385fa9e4066Sahrens 	uint64_t ddobj;
386cde58dbcSMatthew Ahrens 	dsl_dir_phys_t *ddphys;
387fa9e4066Sahrens 	dmu_buf_t *dbuf;
388fa9e4066Sahrens 
3891649cd4bStabriz 	ddobj = dmu_object_alloc(mos, DMU_OT_DSL_DIR, 0,
3901649cd4bStabriz 	    DMU_OT_DSL_DIR, sizeof (dsl_dir_phys_t), tx);
391088f3894Sahrens 	if (pds) {
392088f3894Sahrens 		VERIFY(0 == zap_add(mos, pds->dd_phys->dd_child_dir_zapobj,
393088f3894Sahrens 		    name, sizeof (uint64_t), 1, &ddobj, tx));
394088f3894Sahrens 	} else {
395088f3894Sahrens 		/* it's the root dir */
396088f3894Sahrens 		VERIFY(0 == zap_add(mos, DMU_POOL_DIRECTORY_OBJECT,
397088f3894Sahrens 		    DMU_POOL_ROOT_DATASET, sizeof (uint64_t), 1, &ddobj, tx));
398088f3894Sahrens 	}
399ea8dc4b6Seschrock 	VERIFY(0 == dmu_bonus_hold(mos, ddobj, FTAG, &dbuf));
400fa9e4066Sahrens 	dmu_buf_will_dirty(dbuf, tx);
401cde58dbcSMatthew Ahrens 	ddphys = dbuf->db_data;
402fa9e4066Sahrens 
403cde58dbcSMatthew Ahrens 	ddphys->dd_creation_time = gethrestime_sec();
404088f3894Sahrens 	if (pds)
405cde58dbcSMatthew Ahrens 		ddphys->dd_parent_obj = pds->dd_object;
406cde58dbcSMatthew Ahrens 	ddphys->dd_props_zapobj = zap_create(mos,
407fa9e4066Sahrens 	    DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
408cde58dbcSMatthew Ahrens 	ddphys->dd_child_dir_zapobj = zap_create(mos,
40987e5029aSahrens 	    DMU_OT_DSL_DIR_CHILD_MAP, DMU_OT_NONE, 0, tx);
41074e7dc98SMatthew Ahrens 	if (spa_version(dp->dp_spa) >= SPA_VERSION_USED_BREAKDOWN)
411cde58dbcSMatthew Ahrens 		ddphys->dd_flags |= DD_FLAG_USED_BREAKDOWN;
412ea8dc4b6Seschrock 	dmu_buf_rele(dbuf, FTAG);
413fa9e4066Sahrens 
4141d452cf5Sahrens 	return (ddobj);
415fa9e4066Sahrens }
416fa9e4066Sahrens 
417088f3894Sahrens boolean_t
418088f3894Sahrens dsl_dir_is_clone(dsl_dir_t *dd)
419fa9e4066Sahrens {
420088f3894Sahrens 	return (dd->dd_phys->dd_origin_obj &&
421088f3894Sahrens 	    (dd->dd_pool->dp_origin_snap == NULL ||
422088f3894Sahrens 	    dd->dd_phys->dd_origin_obj !=
423088f3894Sahrens 	    dd->dd_pool->dp_origin_snap->ds_object));
424fa9e4066Sahrens }
425fa9e4066Sahrens 
426fa9e4066Sahrens void
427a2eea2e1Sahrens dsl_dir_stats(dsl_dir_t *dd, nvlist_t *nv)
428fa9e4066Sahrens {
429fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
43074e7dc98SMatthew Ahrens 	dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED,
43174e7dc98SMatthew Ahrens 	    dd->dd_phys->dd_used_bytes);
432a9799022Sck 	dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_QUOTA, dd->dd_phys->dd_quota);
433a2eea2e1Sahrens 	dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_RESERVATION,
434a2eea2e1Sahrens 	    dd->dd_phys->dd_reserved);
435a2eea2e1Sahrens 	dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO,
436a2eea2e1Sahrens 	    dd->dd_phys->dd_compressed_bytes == 0 ? 100 :
437a2eea2e1Sahrens 	    (dd->dd_phys->dd_uncompressed_bytes * 100 /
438a2eea2e1Sahrens 	    dd->dd_phys->dd_compressed_bytes));
43977372cb0SMatthew Ahrens 	dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_LOGICALUSED,
44077372cb0SMatthew Ahrens 	    dd->dd_phys->dd_uncompressed_bytes);
44174e7dc98SMatthew Ahrens 	if (dd->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) {
44274e7dc98SMatthew Ahrens 		dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDSNAP,
44374e7dc98SMatthew Ahrens 		    dd->dd_phys->dd_used_breakdown[DD_USED_SNAP]);
44474e7dc98SMatthew Ahrens 		dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDDS,
44574e7dc98SMatthew Ahrens 		    dd->dd_phys->dd_used_breakdown[DD_USED_HEAD]);
44674e7dc98SMatthew Ahrens 		dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDREFRESERV,
44774e7dc98SMatthew Ahrens 		    dd->dd_phys->dd_used_breakdown[DD_USED_REFRSRV]);
44874e7dc98SMatthew Ahrens 		dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDCHILD,
44974e7dc98SMatthew Ahrens 		    dd->dd_phys->dd_used_breakdown[DD_USED_CHILD] +
45074e7dc98SMatthew Ahrens 		    dd->dd_phys->dd_used_breakdown[DD_USED_CHILD_RSRV]);
45174e7dc98SMatthew Ahrens 	}
452fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
453fa9e4066Sahrens 
454088f3894Sahrens 	if (dsl_dir_is_clone(dd)) {
455fa9e4066Sahrens 		dsl_dataset_t *ds;
456a2eea2e1Sahrens 		char buf[MAXNAMELEN];
457fa9e4066Sahrens 
4583b2aab18SMatthew Ahrens 		VERIFY0(dsl_dataset_hold_obj(dd->dd_pool,
459745cd3c5Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &ds));
460a2eea2e1Sahrens 		dsl_dataset_name(ds, buf);
461745cd3c5Smaybee 		dsl_dataset_rele(ds, FTAG);
462a2eea2e1Sahrens 		dsl_prop_nvlist_add_string(nv, ZFS_PROP_ORIGIN, buf);
463fa9e4066Sahrens 	}
464fa9e4066Sahrens }
465fa9e4066Sahrens 
466fa9e4066Sahrens void
467fa9e4066Sahrens dsl_dir_dirty(dsl_dir_t *dd, dmu_tx_t *tx)
468fa9e4066Sahrens {
469fa9e4066Sahrens 	dsl_pool_t *dp = dd->dd_pool;
470fa9e4066Sahrens 
471fa9e4066Sahrens 	ASSERT(dd->dd_phys);
472fa9e4066Sahrens 
4733b2aab18SMatthew Ahrens 	if (txg_list_add(&dp->dp_dirty_dirs, dd, tx->tx_txg)) {
474fa9e4066Sahrens 		/* up the hold count until we can be written out */
475fa9e4066Sahrens 		dmu_buf_add_ref(dd->dd_dbuf, dd);
476fa9e4066Sahrens 	}
477fa9e4066Sahrens }
478fa9e4066Sahrens 
479fa9e4066Sahrens static int64_t
480fa9e4066Sahrens parent_delta(dsl_dir_t *dd, uint64_t used, int64_t delta)
481fa9e4066Sahrens {
482fa9e4066Sahrens 	uint64_t old_accounted = MAX(used, dd->dd_phys->dd_reserved);
483fa9e4066Sahrens 	uint64_t new_accounted = MAX(used + delta, dd->dd_phys->dd_reserved);
484fa9e4066Sahrens 	return (new_accounted - old_accounted);
485fa9e4066Sahrens }
486fa9e4066Sahrens 
487fa9e4066Sahrens void
488fa9e4066Sahrens dsl_dir_sync(dsl_dir_t *dd, dmu_tx_t *tx)
489fa9e4066Sahrens {
490fa9e4066Sahrens 	ASSERT(dmu_tx_is_syncing(tx));
491fa9e4066Sahrens 
492fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
493fb09f5aaSMadhav Suresh 	ASSERT0(dd->dd_tempreserved[tx->tx_txg&TXG_MASK]);
494fa9e4066Sahrens 	dprintf_dd(dd, "txg=%llu towrite=%lluK\n", tx->tx_txg,
495fa9e4066Sahrens 	    dd->dd_space_towrite[tx->tx_txg&TXG_MASK] / 1024);
496fa9e4066Sahrens 	dd->dd_space_towrite[tx->tx_txg&TXG_MASK] = 0;
497fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
498fa9e4066Sahrens 
499fa9e4066Sahrens 	/* release the hold from dsl_dir_dirty */
500ea8dc4b6Seschrock 	dmu_buf_rele(dd->dd_dbuf, dd);
501fa9e4066Sahrens }
502fa9e4066Sahrens 
503fa9e4066Sahrens static uint64_t
504a9799022Sck dsl_dir_space_towrite(dsl_dir_t *dd)
505fa9e4066Sahrens {
506a9799022Sck 	uint64_t space = 0;
507fa9e4066Sahrens 	int i;
508fa9e4066Sahrens 
509fa9e4066Sahrens 	ASSERT(MUTEX_HELD(&dd->dd_lock));
510fa9e4066Sahrens 
511fa9e4066Sahrens 	for (i = 0; i < TXG_SIZE; i++) {
512fa9e4066Sahrens 		space += dd->dd_space_towrite[i&TXG_MASK];
513fa9e4066Sahrens 		ASSERT3U(dd->dd_space_towrite[i&TXG_MASK], >=, 0);
514fa9e4066Sahrens 	}
515fa9e4066Sahrens 	return (space);
516fa9e4066Sahrens }
517fa9e4066Sahrens 
518fa9e4066Sahrens /*
519fa9e4066Sahrens  * How much space would dd have available if ancestor had delta applied
520fa9e4066Sahrens  * to it?  If ondiskonly is set, we're only interested in what's
521fa9e4066Sahrens  * on-disk, not estimated pending changes.
522fa9e4066Sahrens  */
523a2eea2e1Sahrens uint64_t
524fa9e4066Sahrens dsl_dir_space_available(dsl_dir_t *dd,
525fa9e4066Sahrens     dsl_dir_t *ancestor, int64_t delta, int ondiskonly)
526fa9e4066Sahrens {
527fa9e4066Sahrens 	uint64_t parentspace, myspace, quota, used;
528fa9e4066Sahrens 
529fa9e4066Sahrens 	/*
530fa9e4066Sahrens 	 * If there are no restrictions otherwise, assume we have
531fa9e4066Sahrens 	 * unlimited space available.
532fa9e4066Sahrens 	 */
533fa9e4066Sahrens 	quota = UINT64_MAX;
534fa9e4066Sahrens 	parentspace = UINT64_MAX;
535fa9e4066Sahrens 
536fa9e4066Sahrens 	if (dd->dd_parent != NULL) {
537fa9e4066Sahrens 		parentspace = dsl_dir_space_available(dd->dd_parent,
538fa9e4066Sahrens 		    ancestor, delta, ondiskonly);
539fa9e4066Sahrens 	}
540fa9e4066Sahrens 
541fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
542fa9e4066Sahrens 	if (dd->dd_phys->dd_quota != 0)
543fa9e4066Sahrens 		quota = dd->dd_phys->dd_quota;
54474e7dc98SMatthew Ahrens 	used = dd->dd_phys->dd_used_bytes;
545a9799022Sck 	if (!ondiskonly)
546a9799022Sck 		used += dsl_dir_space_towrite(dd);
547fa9e4066Sahrens 
548fa9e4066Sahrens 	if (dd->dd_parent == NULL) {
54999653d4eSeschrock 		uint64_t poolsize = dsl_pool_adjustedsize(dd->dd_pool, FALSE);
550fa9e4066Sahrens 		quota = MIN(quota, poolsize);
551fa9e4066Sahrens 	}
552fa9e4066Sahrens 
553fa9e4066Sahrens 	if (dd->dd_phys->dd_reserved > used && parentspace != UINT64_MAX) {
554fa9e4066Sahrens 		/*
555fa9e4066Sahrens 		 * We have some space reserved, in addition to what our
556fa9e4066Sahrens 		 * parent gave us.
557fa9e4066Sahrens 		 */
558fa9e4066Sahrens 		parentspace += dd->dd_phys->dd_reserved - used;
559fa9e4066Sahrens 	}
560fa9e4066Sahrens 
56174e7dc98SMatthew Ahrens 	if (dd == ancestor) {
56274e7dc98SMatthew Ahrens 		ASSERT(delta <= 0);
56374e7dc98SMatthew Ahrens 		ASSERT(used >= -delta);
56474e7dc98SMatthew Ahrens 		used += delta;
56574e7dc98SMatthew Ahrens 		if (parentspace != UINT64_MAX)
56674e7dc98SMatthew Ahrens 			parentspace -= delta;
56774e7dc98SMatthew Ahrens 	}
56874e7dc98SMatthew Ahrens 
569fa9e4066Sahrens 	if (used > quota) {
570fa9e4066Sahrens 		/* over quota */
571fa9e4066Sahrens 		myspace = 0;
572fa9e4066Sahrens 	} else {
573fa9e4066Sahrens 		/*
57499653d4eSeschrock 		 * the lesser of the space provided by our parent and
57599653d4eSeschrock 		 * the space left in our quota
576fa9e4066Sahrens 		 */
577fa9e4066Sahrens 		myspace = MIN(parentspace, quota - used);
578fa9e4066Sahrens 	}
579fa9e4066Sahrens 
580fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
581fa9e4066Sahrens 
582fa9e4066Sahrens 	return (myspace);
583fa9e4066Sahrens }
584fa9e4066Sahrens 
585fa9e4066Sahrens struct tempreserve {
586fa9e4066Sahrens 	list_node_t tr_node;
5871ab7f2deSmaybee 	dsl_pool_t *tr_dp;
588fa9e4066Sahrens 	dsl_dir_t *tr_ds;
589fa9e4066Sahrens 	uint64_t tr_size;
590fa9e4066Sahrens };
591fa9e4066Sahrens 
592fa9e4066Sahrens static int
593a9799022Sck dsl_dir_tempreserve_impl(dsl_dir_t *dd, uint64_t asize, boolean_t netfree,
594a9799022Sck     boolean_t ignorequota, boolean_t checkrefquota, list_t *tr_list,
5956df6c3bcSck     dmu_tx_t *tx, boolean_t first)
596fa9e4066Sahrens {
597fa9e4066Sahrens 	uint64_t txg = tx->tx_txg;
598a9799022Sck 	uint64_t est_inflight, used_on_disk, quota, parent_rsrv;
599468c413aSTim Haley 	uint64_t deferred = 0;
600a9799022Sck 	struct tempreserve *tr;
601468c413aSTim Haley 	int retval = EDQUOT;
602fa9e4066Sahrens 	int txgidx = txg & TXG_MASK;
603fa9e4066Sahrens 	int i;
6049082849eSck 	uint64_t ref_rsrv = 0;
605fa9e4066Sahrens 
606fa9e4066Sahrens 	ASSERT3U(txg, !=, 0);
607a9799022Sck 	ASSERT3S(asize, >, 0);
608fa9e4066Sahrens 
609fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
610a9799022Sck 
611fa9e4066Sahrens 	/*
612fa9e4066Sahrens 	 * Check against the dsl_dir's quota.  We don't add in the delta
613fa9e4066Sahrens 	 * when checking for over-quota because they get one free hit.
614fa9e4066Sahrens 	 */
615a9799022Sck 	est_inflight = dsl_dir_space_towrite(dd);
616fa9e4066Sahrens 	for (i = 0; i < TXG_SIZE; i++)
617a9799022Sck 		est_inflight += dd->dd_tempreserved[i];
61874e7dc98SMatthew Ahrens 	used_on_disk = dd->dd_phys->dd_used_bytes;
619fa9e4066Sahrens 
620f4d2e9e6Smaybee 	/*
6216df6c3bcSck 	 * On the first iteration, fetch the dataset's used-on-disk and
6226df6c3bcSck 	 * refreservation values. Also, if checkrefquota is set, test if
6236df6c3bcSck 	 * allocating this space would exceed the dataset's refquota.
624f4d2e9e6Smaybee 	 */
6256df6c3bcSck 	if (first && tx->tx_objset) {
626c3fdb13aSmaybee 		int error;
627503ad85cSMatthew Ahrens 		dsl_dataset_t *ds = tx->tx_objset->os_dsl_dataset;
6289082849eSck 
629a9799022Sck 		error = dsl_dataset_check_quota(ds, checkrefquota,
6309082849eSck 		    asize, est_inflight, &used_on_disk, &ref_rsrv);
631a9799022Sck 		if (error) {
632a9799022Sck 			mutex_exit(&dd->dd_lock);
633a9799022Sck 			return (error);
634a9799022Sck 		}
635a9799022Sck 	}
636a9799022Sck 
637a9799022Sck 	/*
638a9799022Sck 	 * If this transaction will result in a net free of space,
639a9799022Sck 	 * we want to let it through.
640a9799022Sck 	 */
641a9799022Sck 	if (ignorequota || netfree || dd->dd_phys->dd_quota == 0)
642f4d2e9e6Smaybee 		quota = UINT64_MAX;
643f4d2e9e6Smaybee 	else
644fa9e4066Sahrens 		quota = dd->dd_phys->dd_quota;
645fa9e4066Sahrens 
646fa9e4066Sahrens 	/*
647468c413aSTim Haley 	 * Adjust the quota against the actual pool size at the root
648468c413aSTim Haley 	 * minus any outstanding deferred frees.
649f4d2e9e6Smaybee 	 * To ensure that it's possible to remove files from a full
650f4d2e9e6Smaybee 	 * pool without inducing transient overcommits, we throttle
651fa9e4066Sahrens 	 * netfree transactions against a quota that is slightly larger,
652fa9e4066Sahrens 	 * but still within the pool's allocation slop.  In cases where
653fa9e4066Sahrens 	 * we're very close to full, this will allow a steady trickle of
654fa9e4066Sahrens 	 * removes to get through.
655fa9e4066Sahrens 	 */
6561934e92fSmaybee 	if (dd->dd_parent == NULL) {
657b24ab676SJeff Bonwick 		spa_t *spa = dd->dd_pool->dp_spa;
658fa9e4066Sahrens 		uint64_t poolsize = dsl_pool_adjustedsize(dd->dd_pool, netfree);
659b24ab676SJeff Bonwick 		deferred = metaslab_class_get_deferred(spa_normal_class(spa));
660468c413aSTim Haley 		if (poolsize - deferred < quota) {
661468c413aSTim Haley 			quota = poolsize - deferred;
662468c413aSTim Haley 			retval = ENOSPC;
663fa9e4066Sahrens 		}
664fa9e4066Sahrens 	}
665fa9e4066Sahrens 
666fa9e4066Sahrens 	/*
667fa9e4066Sahrens 	 * If they are requesting more space, and our current estimate
668a9799022Sck 	 * is over quota, they get to try again unless the actual
669ea8dc4b6Seschrock 	 * on-disk is over quota and there are no pending changes (which
670ea8dc4b6Seschrock 	 * may free up space for us).
671fa9e4066Sahrens 	 */
672468c413aSTim Haley 	if (used_on_disk + est_inflight >= quota) {
673468c413aSTim Haley 		if (est_inflight > 0 || used_on_disk < quota ||
674468c413aSTim Haley 		    (retval == ENOSPC && used_on_disk < quota + deferred))
675468c413aSTim Haley 			retval = ERESTART;
676a9799022Sck 		dprintf_dd(dd, "failing: used=%lluK inflight = %lluK "
677fa9e4066Sahrens 		    "quota=%lluK tr=%lluK err=%d\n",
678a9799022Sck 		    used_on_disk>>10, est_inflight>>10,
679468c413aSTim Haley 		    quota>>10, asize>>10, retval);
680fa9e4066Sahrens 		mutex_exit(&dd->dd_lock);
681be6fd75aSMatthew Ahrens 		return (SET_ERROR(retval));
682fa9e4066Sahrens 	}
683fa9e4066Sahrens 
684fa9e4066Sahrens 	/* We need to up our estimated delta before dropping dd_lock */
685fa9e4066Sahrens 	dd->dd_tempreserved[txgidx] += asize;
686fa9e4066Sahrens 
6879082849eSck 	parent_rsrv = parent_delta(dd, used_on_disk + est_inflight,
6889082849eSck 	    asize - ref_rsrv);
689fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
690fa9e4066Sahrens 
6911ab7f2deSmaybee 	tr = kmem_zalloc(sizeof (struct tempreserve), KM_SLEEP);
692fa9e4066Sahrens 	tr->tr_ds = dd;
693fa9e4066Sahrens 	tr->tr_size = asize;
694fa9e4066Sahrens 	list_insert_tail(tr_list, tr);
695fa9e4066Sahrens 
696fa9e4066Sahrens 	/* see if it's OK with our parent */
6971934e92fSmaybee 	if (dd->dd_parent && parent_rsrv) {
6981934e92fSmaybee 		boolean_t ismos = (dd->dd_phys->dd_head_dataset_obj == 0);
6991934e92fSmaybee 
700fa9e4066Sahrens 		return (dsl_dir_tempreserve_impl(dd->dd_parent,
7016df6c3bcSck 		    parent_rsrv, netfree, ismos, TRUE, tr_list, tx, FALSE));
702fa9e4066Sahrens 	} else {
703fa9e4066Sahrens 		return (0);
704fa9e4066Sahrens 	}
705fa9e4066Sahrens }
706fa9e4066Sahrens 
707fa9e4066Sahrens /*
708fa9e4066Sahrens  * Reserve space in this dsl_dir, to be used in this tx's txg.
709a9799022Sck  * After the space has been dirtied (and dsl_dir_willuse_space()
710a9799022Sck  * has been called), the reservation should be canceled, using
711a9799022Sck  * dsl_dir_tempreserve_clear().
712fa9e4066Sahrens  */
713fa9e4066Sahrens int
714a9799022Sck dsl_dir_tempreserve_space(dsl_dir_t *dd, uint64_t lsize, uint64_t asize,
715a9799022Sck     uint64_t fsize, uint64_t usize, void **tr_cookiep, dmu_tx_t *tx)
716fa9e4066Sahrens {
7171ab7f2deSmaybee 	int err;
718fa9e4066Sahrens 	list_t *tr_list;
719fa9e4066Sahrens 
720a9799022Sck 	if (asize == 0) {
721a9799022Sck 		*tr_cookiep = NULL;
722a9799022Sck 		return (0);
723a9799022Sck 	}
724a9799022Sck 
725fa9e4066Sahrens 	tr_list = kmem_alloc(sizeof (list_t), KM_SLEEP);
726fa9e4066Sahrens 	list_create(tr_list, sizeof (struct tempreserve),
727fa9e4066Sahrens 	    offsetof(struct tempreserve, tr_node));
728a9799022Sck 	ASSERT3S(asize, >, 0);
729ea8dc4b6Seschrock 	ASSERT3S(fsize, >=, 0);
730fa9e4066Sahrens 
7311ab7f2deSmaybee 	err = arc_tempreserve_space(lsize, tx->tx_txg);
732fa9e4066Sahrens 	if (err == 0) {
733fa9e4066Sahrens 		struct tempreserve *tr;
734fa9e4066Sahrens 
7351ab7f2deSmaybee 		tr = kmem_zalloc(sizeof (struct tempreserve), KM_SLEEP);
7361ab7f2deSmaybee 		tr->tr_size = lsize;
7371ab7f2deSmaybee 		list_insert_tail(tr_list, tr);
7381ab7f2deSmaybee 
7391ab7f2deSmaybee 		err = dsl_pool_tempreserve_space(dd->dd_pool, asize, tx);
7401ab7f2deSmaybee 	} else {
7411ab7f2deSmaybee 		if (err == EAGAIN) {
7420689f76cSAdam Leventhal 			txg_delay(dd->dd_pool, tx->tx_txg,
7430689f76cSAdam Leventhal 			    MSEC2NSEC(10), MSEC2NSEC(10));
744be6fd75aSMatthew Ahrens 			err = SET_ERROR(ERESTART);
745fa9e4066Sahrens 		}
7461ab7f2deSmaybee 		dsl_pool_memory_pressure(dd->dd_pool);
7471ab7f2deSmaybee 	}
7481ab7f2deSmaybee 
7491ab7f2deSmaybee 	if (err == 0) {
7501ab7f2deSmaybee 		struct tempreserve *tr;
7511ab7f2deSmaybee 
7521ab7f2deSmaybee 		tr = kmem_zalloc(sizeof (struct tempreserve), KM_SLEEP);
7531ab7f2deSmaybee 		tr->tr_dp = dd->dd_pool;
7541ab7f2deSmaybee 		tr->tr_size = asize;
7551ab7f2deSmaybee 		list_insert_tail(tr_list, tr);
7561ab7f2deSmaybee 
7571ab7f2deSmaybee 		err = dsl_dir_tempreserve_impl(dd, asize, fsize >= asize,
7586df6c3bcSck 		    FALSE, asize > usize, tr_list, tx, TRUE);
759fa9e4066Sahrens 	}
760fa9e4066Sahrens 
7613b2aab18SMatthew Ahrens 	if (err != 0)
762fa9e4066Sahrens 		dsl_dir_tempreserve_clear(tr_list, tx);
763fa9e4066Sahrens 	else
764fa9e4066Sahrens 		*tr_cookiep = tr_list;
7651ab7f2deSmaybee 
766fa9e4066Sahrens 	return (err);
767fa9e4066Sahrens }
768fa9e4066Sahrens 
769fa9e4066Sahrens /*
770fa9e4066Sahrens  * Clear a temporary reservation that we previously made with
771fa9e4066Sahrens  * dsl_dir_tempreserve_space().
772fa9e4066Sahrens  */
773fa9e4066Sahrens void
774fa9e4066Sahrens dsl_dir_tempreserve_clear(void *tr_cookie, dmu_tx_t *tx)
775fa9e4066Sahrens {
776fa9e4066Sahrens 	int txgidx = tx->tx_txg & TXG_MASK;
777fa9e4066Sahrens 	list_t *tr_list = tr_cookie;
778fa9e4066Sahrens 	struct tempreserve *tr;
779fa9e4066Sahrens 
780fa9e4066Sahrens 	ASSERT3U(tx->tx_txg, !=, 0);
781fa9e4066Sahrens 
782a9799022Sck 	if (tr_cookie == NULL)
783a9799022Sck 		return;
784a9799022Sck 
785fa9e4066Sahrens 	while (tr = list_head(tr_list)) {
7861ab7f2deSmaybee 		if (tr->tr_dp) {
7871ab7f2deSmaybee 			dsl_pool_tempreserve_clear(tr->tr_dp, tr->tr_size, tx);
7881ab7f2deSmaybee 		} else if (tr->tr_ds) {
789fa9e4066Sahrens 			mutex_enter(&tr->tr_ds->dd_lock);
790fa9e4066Sahrens 			ASSERT3U(tr->tr_ds->dd_tempreserved[txgidx], >=,
791fa9e4066Sahrens 			    tr->tr_size);
792fa9e4066Sahrens 			tr->tr_ds->dd_tempreserved[txgidx] -= tr->tr_size;
793fa9e4066Sahrens 			mutex_exit(&tr->tr_ds->dd_lock);
7941ab7f2deSmaybee 		} else {
7951ab7f2deSmaybee 			arc_tempreserve_clear(tr->tr_size);
796fa9e4066Sahrens 		}
797fa9e4066Sahrens 		list_remove(tr_list, tr);
798fa9e4066Sahrens 		kmem_free(tr, sizeof (struct tempreserve));
799fa9e4066Sahrens 	}
800fa9e4066Sahrens 
801fa9e4066Sahrens 	kmem_free(tr_list, sizeof (list_t));
802fa9e4066Sahrens }
803fa9e4066Sahrens 
8041ab7f2deSmaybee static void
8051ab7f2deSmaybee dsl_dir_willuse_space_impl(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
806fa9e4066Sahrens {
807fa9e4066Sahrens 	int64_t parent_space;
808fa9e4066Sahrens 	uint64_t est_used;
809fa9e4066Sahrens 
810fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
811fa9e4066Sahrens 	if (space > 0)
812fa9e4066Sahrens 		dd->dd_space_towrite[tx->tx_txg & TXG_MASK] += space;
813fa9e4066Sahrens 
81474e7dc98SMatthew Ahrens 	est_used = dsl_dir_space_towrite(dd) + dd->dd_phys->dd_used_bytes;
815fa9e4066Sahrens 	parent_space = parent_delta(dd, est_used, space);
816fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
817fa9e4066Sahrens 
818fa9e4066Sahrens 	/* Make sure that we clean up dd_space_to* */
819fa9e4066Sahrens 	dsl_dir_dirty(dd, tx);
820fa9e4066Sahrens 
821fa9e4066Sahrens 	/* XXX this is potentially expensive and unnecessary... */
822fa9e4066Sahrens 	if (parent_space && dd->dd_parent)
8231ab7f2deSmaybee 		dsl_dir_willuse_space_impl(dd->dd_parent, parent_space, tx);
8241ab7f2deSmaybee }
8251ab7f2deSmaybee 
8261ab7f2deSmaybee /*
8271ab7f2deSmaybee  * Call in open context when we think we're going to write/free space,
8281ab7f2deSmaybee  * eg. when dirtying data.  Be conservative (ie. OK to write less than
8291ab7f2deSmaybee  * this or free more than this, but don't write more or free less).
8301ab7f2deSmaybee  */
8311ab7f2deSmaybee void
8321ab7f2deSmaybee dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
8331ab7f2deSmaybee {
8341ab7f2deSmaybee 	dsl_pool_willuse_space(dd->dd_pool, space, tx);
8351ab7f2deSmaybee 	dsl_dir_willuse_space_impl(dd, space, tx);
836fa9e4066Sahrens }
837fa9e4066Sahrens 
838fa9e4066Sahrens /* call from syncing context when we actually write/free space for this dd */
839fa9e4066Sahrens void
84074e7dc98SMatthew Ahrens dsl_dir_diduse_space(dsl_dir_t *dd, dd_used_t type,
841fa9e4066Sahrens     int64_t used, int64_t compressed, int64_t uncompressed, dmu_tx_t *tx)
842fa9e4066Sahrens {
843fa9e4066Sahrens 	int64_t accounted_delta;
84402c8f3f0SMatthew Ahrens 	boolean_t needlock = !MUTEX_HELD(&dd->dd_lock);
845fa9e4066Sahrens 
846fa9e4066Sahrens 	ASSERT(dmu_tx_is_syncing(tx));
84774e7dc98SMatthew Ahrens 	ASSERT(type < DD_USED_NUM);
848fa9e4066Sahrens 
84902c8f3f0SMatthew Ahrens 	if (needlock)
85002c8f3f0SMatthew Ahrens 		mutex_enter(&dd->dd_lock);
85174e7dc98SMatthew Ahrens 	accounted_delta = parent_delta(dd, dd->dd_phys->dd_used_bytes, used);
85274e7dc98SMatthew Ahrens 	ASSERT(used >= 0 || dd->dd_phys->dd_used_bytes >= -used);
853fa9e4066Sahrens 	ASSERT(compressed >= 0 ||
854fa9e4066Sahrens 	    dd->dd_phys->dd_compressed_bytes >= -compressed);
855fa9e4066Sahrens 	ASSERT(uncompressed >= 0 ||
856fa9e4066Sahrens 	    dd->dd_phys->dd_uncompressed_bytes >= -uncompressed);
857ce636f8bSMatthew Ahrens 	dmu_buf_will_dirty(dd->dd_dbuf, tx);
85874e7dc98SMatthew Ahrens 	dd->dd_phys->dd_used_bytes += used;
859fa9e4066Sahrens 	dd->dd_phys->dd_uncompressed_bytes += uncompressed;
860fa9e4066Sahrens 	dd->dd_phys->dd_compressed_bytes += compressed;
86174e7dc98SMatthew Ahrens 
86274e7dc98SMatthew Ahrens 	if (dd->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) {
86374e7dc98SMatthew Ahrens 		ASSERT(used > 0 ||
86474e7dc98SMatthew Ahrens 		    dd->dd_phys->dd_used_breakdown[type] >= -used);
86574e7dc98SMatthew Ahrens 		dd->dd_phys->dd_used_breakdown[type] += used;
86674e7dc98SMatthew Ahrens #ifdef DEBUG
86774e7dc98SMatthew Ahrens 		dd_used_t t;
86874e7dc98SMatthew Ahrens 		uint64_t u = 0;
86974e7dc98SMatthew Ahrens 		for (t = 0; t < DD_USED_NUM; t++)
87074e7dc98SMatthew Ahrens 			u += dd->dd_phys->dd_used_breakdown[t];
87174e7dc98SMatthew Ahrens 		ASSERT3U(u, ==, dd->dd_phys->dd_used_bytes);
87274e7dc98SMatthew Ahrens #endif
87374e7dc98SMatthew Ahrens 	}
87402c8f3f0SMatthew Ahrens 	if (needlock)
87502c8f3f0SMatthew Ahrens 		mutex_exit(&dd->dd_lock);
876fa9e4066Sahrens 
877fa9e4066Sahrens 	if (dd->dd_parent != NULL) {
87874e7dc98SMatthew Ahrens 		dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD,
879fa9e4066Sahrens 		    accounted_delta, compressed, uncompressed, tx);
88074e7dc98SMatthew Ahrens 		dsl_dir_transfer_space(dd->dd_parent,
88174e7dc98SMatthew Ahrens 		    used - accounted_delta,
88274e7dc98SMatthew Ahrens 		    DD_USED_CHILD_RSRV, DD_USED_CHILD, tx);
883fa9e4066Sahrens 	}
884fa9e4066Sahrens }
885fa9e4066Sahrens 
88674e7dc98SMatthew Ahrens void
88774e7dc98SMatthew Ahrens dsl_dir_transfer_space(dsl_dir_t *dd, int64_t delta,
88874e7dc98SMatthew Ahrens     dd_used_t oldtype, dd_used_t newtype, dmu_tx_t *tx)
88974e7dc98SMatthew Ahrens {
89002c8f3f0SMatthew Ahrens 	boolean_t needlock = !MUTEX_HELD(&dd->dd_lock);
89102c8f3f0SMatthew Ahrens 
89274e7dc98SMatthew Ahrens 	ASSERT(dmu_tx_is_syncing(tx));
89374e7dc98SMatthew Ahrens 	ASSERT(oldtype < DD_USED_NUM);
89474e7dc98SMatthew Ahrens 	ASSERT(newtype < DD_USED_NUM);
89574e7dc98SMatthew Ahrens 
89674e7dc98SMatthew Ahrens 	if (delta == 0 || !(dd->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN))
89774e7dc98SMatthew Ahrens 		return;
89874e7dc98SMatthew Ahrens 
89902c8f3f0SMatthew Ahrens 	if (needlock)
90002c8f3f0SMatthew Ahrens 		mutex_enter(&dd->dd_lock);
90174e7dc98SMatthew Ahrens 	ASSERT(delta > 0 ?
90274e7dc98SMatthew Ahrens 	    dd->dd_phys->dd_used_breakdown[oldtype] >= delta :
90374e7dc98SMatthew Ahrens 	    dd->dd_phys->dd_used_breakdown[newtype] >= -delta);
90474e7dc98SMatthew Ahrens 	ASSERT(dd->dd_phys->dd_used_bytes >= ABS(delta));
905ce636f8bSMatthew Ahrens 	dmu_buf_will_dirty(dd->dd_dbuf, tx);
90674e7dc98SMatthew Ahrens 	dd->dd_phys->dd_used_breakdown[oldtype] -= delta;
90774e7dc98SMatthew Ahrens 	dd->dd_phys->dd_used_breakdown[newtype] += delta;
90802c8f3f0SMatthew Ahrens 	if (needlock)
90902c8f3f0SMatthew Ahrens 		mutex_exit(&dd->dd_lock);
91074e7dc98SMatthew Ahrens }
91174e7dc98SMatthew Ahrens 
9123b2aab18SMatthew Ahrens typedef struct dsl_dir_set_qr_arg {
9133b2aab18SMatthew Ahrens 	const char *ddsqra_name;
9143b2aab18SMatthew Ahrens 	zprop_source_t ddsqra_source;
9153b2aab18SMatthew Ahrens 	uint64_t ddsqra_value;
9163b2aab18SMatthew Ahrens } dsl_dir_set_qr_arg_t;
9173b2aab18SMatthew Ahrens 
918fa9e4066Sahrens static int
9193b2aab18SMatthew Ahrens dsl_dir_set_quota_check(void *arg, dmu_tx_t *tx)
920fa9e4066Sahrens {
9213b2aab18SMatthew Ahrens 	dsl_dir_set_qr_arg_t *ddsqra = arg;
9223b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
9233b2aab18SMatthew Ahrens 	dsl_dataset_t *ds;
9243b2aab18SMatthew Ahrens 	int error;
9253b2aab18SMatthew Ahrens 	uint64_t towrite, newval;
926fa9e4066Sahrens 
9273b2aab18SMatthew Ahrens 	error = dsl_dataset_hold(dp, ddsqra->ddsqra_name, FTAG, &ds);
9283b2aab18SMatthew Ahrens 	if (error != 0)
9293b2aab18SMatthew Ahrens 		return (error);
9303b2aab18SMatthew Ahrens 
9313b2aab18SMatthew Ahrens 	error = dsl_prop_predict(ds->ds_dir, "quota",
9323b2aab18SMatthew Ahrens 	    ddsqra->ddsqra_source, ddsqra->ddsqra_value, &newval);
9333b2aab18SMatthew Ahrens 	if (error != 0) {
9343b2aab18SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
9353b2aab18SMatthew Ahrens 		return (error);
9363b2aab18SMatthew Ahrens 	}
93792241e0bSTom Erickson 
9383b2aab18SMatthew Ahrens 	if (newval == 0) {
9393b2aab18SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
9401d452cf5Sahrens 		return (0);
9413b2aab18SMatthew Ahrens 	}
942fa9e4066Sahrens 
9433b2aab18SMatthew Ahrens 	mutex_enter(&ds->ds_dir->dd_lock);
9441d452cf5Sahrens 	/*
9451d452cf5Sahrens 	 * If we are doing the preliminary check in open context, and
9461d452cf5Sahrens 	 * there are pending changes, then don't fail it, since the
947a9799022Sck 	 * pending changes could under-estimate the amount of space to be
9481d452cf5Sahrens 	 * freed up.
9491d452cf5Sahrens 	 */
9503b2aab18SMatthew Ahrens 	towrite = dsl_dir_space_towrite(ds->ds_dir);
9511d452cf5Sahrens 	if ((dmu_tx_is_syncing(tx) || towrite == 0) &&
9523b2aab18SMatthew Ahrens 	    (newval < ds->ds_dir->dd_phys->dd_reserved ||
9533b2aab18SMatthew Ahrens 	    newval < ds->ds_dir->dd_phys->dd_used_bytes + towrite)) {
954be6fd75aSMatthew Ahrens 		error = SET_ERROR(ENOSPC);
955fa9e4066Sahrens 	}
9563b2aab18SMatthew Ahrens 	mutex_exit(&ds->ds_dir->dd_lock);
9573b2aab18SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
9583b2aab18SMatthew Ahrens 	return (error);
959fa9e4066Sahrens }
960fa9e4066Sahrens 
9611d452cf5Sahrens static void
9623b2aab18SMatthew Ahrens dsl_dir_set_quota_sync(void *arg, dmu_tx_t *tx)
9631d452cf5Sahrens {
9643b2aab18SMatthew Ahrens 	dsl_dir_set_qr_arg_t *ddsqra = arg;
9653b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
9663b2aab18SMatthew Ahrens 	dsl_dataset_t *ds;
9673b2aab18SMatthew Ahrens 	uint64_t newval;
96892241e0bSTom Erickson 
9693b2aab18SMatthew Ahrens 	VERIFY0(dsl_dataset_hold(dp, ddsqra->ddsqra_name, FTAG, &ds));
9701d452cf5Sahrens 
971*013023d4SMartin Matuska 	if (spa_version(dp->dp_spa) >= SPA_VERSION_RECVD_PROPS) {
972*013023d4SMartin Matuska 		dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_QUOTA),
973*013023d4SMartin Matuska 		    ddsqra->ddsqra_source, sizeof (ddsqra->ddsqra_value), 1,
974*013023d4SMartin Matuska 		    &ddsqra->ddsqra_value, tx);
9751d452cf5Sahrens 
976*013023d4SMartin Matuska 		VERIFY0(dsl_prop_get_int_ds(ds,
977*013023d4SMartin Matuska 		    zfs_prop_to_name(ZFS_PROP_QUOTA), &newval));
978*013023d4SMartin Matuska 	} else {
979*013023d4SMartin Matuska 		newval = ddsqra->ddsqra_value;
980*013023d4SMartin Matuska 		spa_history_log_internal_ds(ds, "set", tx, "%s=%lld",
981*013023d4SMartin Matuska 		    zfs_prop_to_name(ZFS_PROP_QUOTA), (longlong_t)newval);
982*013023d4SMartin Matuska 	}
9833b2aab18SMatthew Ahrens 
9843b2aab18SMatthew Ahrens 	dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
9853b2aab18SMatthew Ahrens 	mutex_enter(&ds->ds_dir->dd_lock);
9863b2aab18SMatthew Ahrens 	ds->ds_dir->dd_phys->dd_quota = newval;
9873b2aab18SMatthew Ahrens 	mutex_exit(&ds->ds_dir->dd_lock);
9883b2aab18SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
9891d452cf5Sahrens }
9901d452cf5Sahrens 
991fa9e4066Sahrens int
99292241e0bSTom Erickson dsl_dir_set_quota(const char *ddname, zprop_source_t source, uint64_t quota)
993fa9e4066Sahrens {
9943b2aab18SMatthew Ahrens 	dsl_dir_set_qr_arg_t ddsqra;
995fa9e4066Sahrens 
9963b2aab18SMatthew Ahrens 	ddsqra.ddsqra_name = ddname;
9973b2aab18SMatthew Ahrens 	ddsqra.ddsqra_source = source;
9983b2aab18SMatthew Ahrens 	ddsqra.ddsqra_value = quota;
99992241e0bSTom Erickson 
10003b2aab18SMatthew Ahrens 	return (dsl_sync_task(ddname, dsl_dir_set_quota_check,
10013b2aab18SMatthew Ahrens 	    dsl_dir_set_quota_sync, &ddsqra, 0));
1002fa9e4066Sahrens }
1003fa9e4066Sahrens 
1004a9799022Sck int
10053b2aab18SMatthew Ahrens dsl_dir_set_reservation_check(void *arg, dmu_tx_t *tx)
1006fa9e4066Sahrens {
10073b2aab18SMatthew Ahrens 	dsl_dir_set_qr_arg_t *ddsqra = arg;
10083b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
10093b2aab18SMatthew Ahrens 	dsl_dataset_t *ds;
10103b2aab18SMatthew Ahrens 	dsl_dir_t *dd;
10113b2aab18SMatthew Ahrens 	uint64_t newval, used, avail;
10123b2aab18SMatthew Ahrens 	int error;
101392241e0bSTom Erickson 
10143b2aab18SMatthew Ahrens 	error = dsl_dataset_hold(dp, ddsqra->ddsqra_name, FTAG, &ds);
10153b2aab18SMatthew Ahrens 	if (error != 0)
10163b2aab18SMatthew Ahrens 		return (error);
10173b2aab18SMatthew Ahrens 	dd = ds->ds_dir;
1018fa9e4066Sahrens 
10191d452cf5Sahrens 	/*
10201d452cf5Sahrens 	 * If we are doing the preliminary check in open context, the
10211d452cf5Sahrens 	 * space estimates may be inaccurate.
10221d452cf5Sahrens 	 */
10233b2aab18SMatthew Ahrens 	if (!dmu_tx_is_syncing(tx)) {
10243b2aab18SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
10251d452cf5Sahrens 		return (0);
10263b2aab18SMatthew Ahrens 	}
10273b2aab18SMatthew Ahrens 
10283b2aab18SMatthew Ahrens 	error = dsl_prop_predict(ds->ds_dir,
10293b2aab18SMatthew Ahrens 	    zfs_prop_to_name(ZFS_PROP_RESERVATION),
10303b2aab18SMatthew Ahrens 	    ddsqra->ddsqra_source, ddsqra->ddsqra_value, &newval);
10313b2aab18SMatthew Ahrens 	if (error != 0) {
10323b2aab18SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
10333b2aab18SMatthew Ahrens 		return (error);
10343b2aab18SMatthew Ahrens 	}
10351d452cf5Sahrens 
1036fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
103774e7dc98SMatthew Ahrens 	used = dd->dd_phys->dd_used_bytes;
1038fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
1039fa9e4066Sahrens 
1040fa9e4066Sahrens 	if (dd->dd_parent) {
1041fa9e4066Sahrens 		avail = dsl_dir_space_available(dd->dd_parent,
1042fa9e4066Sahrens 		    NULL, 0, FALSE);
1043fa9e4066Sahrens 	} else {
1044fa9e4066Sahrens 		avail = dsl_pool_adjustedsize(dd->dd_pool, B_FALSE) - used;
1045fa9e4066Sahrens 	}
1046fa9e4066Sahrens 
10473b2aab18SMatthew Ahrens 	if (MAX(used, newval) > MAX(used, dd->dd_phys->dd_reserved)) {
10483b2aab18SMatthew Ahrens 		uint64_t delta = MAX(used, newval) -
1049379c004dSEric Schrock 		    MAX(used, dd->dd_phys->dd_reserved);
1050379c004dSEric Schrock 
10513b2aab18SMatthew Ahrens 		if (delta > avail ||
10523b2aab18SMatthew Ahrens 		    (dd->dd_phys->dd_quota > 0 &&
10533b2aab18SMatthew Ahrens 		    newval > dd->dd_phys->dd_quota))
1054be6fd75aSMatthew Ahrens 			error = SET_ERROR(ENOSPC);
1055379c004dSEric Schrock 	}
1056379c004dSEric Schrock 
10573b2aab18SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
10583b2aab18SMatthew Ahrens 	return (error);
10591d452cf5Sahrens }
10601d452cf5Sahrens 
10613b2aab18SMatthew Ahrens void
10624445fffbSMatthew Ahrens dsl_dir_set_reservation_sync_impl(dsl_dir_t *dd, uint64_t value, dmu_tx_t *tx)
10631d452cf5Sahrens {
10641d452cf5Sahrens 	uint64_t used;
10651d452cf5Sahrens 	int64_t delta;
10661d452cf5Sahrens 
1067a9799022Sck 	dmu_buf_will_dirty(dd->dd_dbuf, tx);
1068a9799022Sck 
10691d452cf5Sahrens 	mutex_enter(&dd->dd_lock);
107074e7dc98SMatthew Ahrens 	used = dd->dd_phys->dd_used_bytes;
10714445fffbSMatthew Ahrens 	delta = MAX(used, value) - MAX(used, dd->dd_phys->dd_reserved);
10724445fffbSMatthew Ahrens 	dd->dd_phys->dd_reserved = value;
1073fa9e4066Sahrens 
1074fa9e4066Sahrens 	if (dd->dd_parent != NULL) {
1075fa9e4066Sahrens 		/* Roll up this additional usage into our ancestors */
107674e7dc98SMatthew Ahrens 		dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD_RSRV,
107774e7dc98SMatthew Ahrens 		    delta, 0, 0, tx);
1078fa9e4066Sahrens 	}
107902c8f3f0SMatthew Ahrens 	mutex_exit(&dd->dd_lock);
10804445fffbSMatthew Ahrens }
10814445fffbSMatthew Ahrens 
10824445fffbSMatthew Ahrens 
10834445fffbSMatthew Ahrens static void
10843b2aab18SMatthew Ahrens dsl_dir_set_reservation_sync(void *arg, dmu_tx_t *tx)
10854445fffbSMatthew Ahrens {
10863b2aab18SMatthew Ahrens 	dsl_dir_set_qr_arg_t *ddsqra = arg;
10873b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
10883b2aab18SMatthew Ahrens 	dsl_dataset_t *ds;
10893b2aab18SMatthew Ahrens 	uint64_t newval;
10903b2aab18SMatthew Ahrens 
10913b2aab18SMatthew Ahrens 	VERIFY0(dsl_dataset_hold(dp, ddsqra->ddsqra_name, FTAG, &ds));
10923b2aab18SMatthew Ahrens 
1093*013023d4SMartin Matuska 	if (spa_version(dp->dp_spa) >= SPA_VERSION_RECVD_PROPS) {
1094*013023d4SMartin Matuska 		dsl_prop_set_sync_impl(ds,
1095*013023d4SMartin Matuska 		    zfs_prop_to_name(ZFS_PROP_RESERVATION),
1096*013023d4SMartin Matuska 		    ddsqra->ddsqra_source, sizeof (ddsqra->ddsqra_value), 1,
1097*013023d4SMartin Matuska 		    &ddsqra->ddsqra_value, tx);
10984445fffbSMatthew Ahrens 
1099*013023d4SMartin Matuska 		VERIFY0(dsl_prop_get_int_ds(ds,
1100*013023d4SMartin Matuska 		    zfs_prop_to_name(ZFS_PROP_RESERVATION), &newval));
1101*013023d4SMartin Matuska 	} else {
1102*013023d4SMartin Matuska 		newval = ddsqra->ddsqra_value;
1103*013023d4SMartin Matuska 		spa_history_log_internal_ds(ds, "set", tx, "%s=%lld",
1104*013023d4SMartin Matuska 		    zfs_prop_to_name(ZFS_PROP_RESERVATION),
1105*013023d4SMartin Matuska 		    (longlong_t)newval);
1106*013023d4SMartin Matuska 	}
1107ecd6cf80Smarks 
11083b2aab18SMatthew Ahrens 	dsl_dir_set_reservation_sync_impl(ds->ds_dir, newval, tx);
11093b2aab18SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
1110fa9e4066Sahrens }
1111fa9e4066Sahrens 
1112fa9e4066Sahrens int
111392241e0bSTom Erickson dsl_dir_set_reservation(const char *ddname, zprop_source_t source,
111492241e0bSTom Erickson     uint64_t reservation)
1115fa9e4066Sahrens {
11163b2aab18SMatthew Ahrens 	dsl_dir_set_qr_arg_t ddsqra;
111792241e0bSTom Erickson 
11183b2aab18SMatthew Ahrens 	ddsqra.ddsqra_name = ddname;
11193b2aab18SMatthew Ahrens 	ddsqra.ddsqra_source = source;
11203b2aab18SMatthew Ahrens 	ddsqra.ddsqra_value = reservation;
112192241e0bSTom Erickson 
11223b2aab18SMatthew Ahrens 	return (dsl_sync_task(ddname, dsl_dir_set_reservation_check,
11233b2aab18SMatthew Ahrens 	    dsl_dir_set_reservation_sync, &ddsqra, 0));
1124fa9e4066Sahrens }
1125fa9e4066Sahrens 
1126fa9e4066Sahrens static dsl_dir_t *
1127fa9e4066Sahrens closest_common_ancestor(dsl_dir_t *ds1, dsl_dir_t *ds2)
1128fa9e4066Sahrens {
1129fa9e4066Sahrens 	for (; ds1; ds1 = ds1->dd_parent) {
1130fa9e4066Sahrens 		dsl_dir_t *dd;
1131fa9e4066Sahrens 		for (dd = ds2; dd; dd = dd->dd_parent) {
1132fa9e4066Sahrens 			if (ds1 == dd)
1133fa9e4066Sahrens 				return (dd);
1134fa9e4066Sahrens 		}
1135fa9e4066Sahrens 	}
1136fa9e4066Sahrens 	return (NULL);
1137fa9e4066Sahrens }
1138fa9e4066Sahrens 
1139fa9e4066Sahrens /*
1140fa9e4066Sahrens  * If delta is applied to dd, how much of that delta would be applied to
1141fa9e4066Sahrens  * ancestor?  Syncing context only.
1142fa9e4066Sahrens  */
1143fa9e4066Sahrens static int64_t
1144fa9e4066Sahrens would_change(dsl_dir_t *dd, int64_t delta, dsl_dir_t *ancestor)
1145fa9e4066Sahrens {
1146fa9e4066Sahrens 	if (dd == ancestor)
1147fa9e4066Sahrens 		return (delta);
1148fa9e4066Sahrens 
1149fa9e4066Sahrens 	mutex_enter(&dd->dd_lock);
115074e7dc98SMatthew Ahrens 	delta = parent_delta(dd, dd->dd_phys->dd_used_bytes, delta);
1151fa9e4066Sahrens 	mutex_exit(&dd->dd_lock);
1152fa9e4066Sahrens 	return (would_change(dd->dd_parent, delta, ancestor));
1153fa9e4066Sahrens }
1154fa9e4066Sahrens 
11553b2aab18SMatthew Ahrens typedef struct dsl_dir_rename_arg {
11563b2aab18SMatthew Ahrens 	const char *ddra_oldname;
11573b2aab18SMatthew Ahrens 	const char *ddra_newname;
11583b2aab18SMatthew Ahrens } dsl_dir_rename_arg_t;
11591d452cf5Sahrens 
11603b2aab18SMatthew Ahrens /* ARGSUSED */
11611d452cf5Sahrens static int
11623b2aab18SMatthew Ahrens dsl_valid_rename(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)
1163fa9e4066Sahrens {
11643b2aab18SMatthew Ahrens 	int *deltap = arg;
11653b2aab18SMatthew Ahrens 	char namebuf[MAXNAMELEN];
1166fa9e4066Sahrens 
11673b2aab18SMatthew Ahrens 	dsl_dataset_name(ds, namebuf);
11683b2aab18SMatthew Ahrens 
11693b2aab18SMatthew Ahrens 	if (strlen(namebuf) + *deltap >= MAXNAMELEN)
1170be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENAMETOOLONG));
11713b2aab18SMatthew Ahrens 	return (0);
11723b2aab18SMatthew Ahrens }
11733b2aab18SMatthew Ahrens 
11743b2aab18SMatthew Ahrens static int
11753b2aab18SMatthew Ahrens dsl_dir_rename_check(void *arg, dmu_tx_t *tx)
11763b2aab18SMatthew Ahrens {
11773b2aab18SMatthew Ahrens 	dsl_dir_rename_arg_t *ddra = arg;
11783b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
11793b2aab18SMatthew Ahrens 	dsl_dir_t *dd, *newparent;
11803b2aab18SMatthew Ahrens 	const char *mynewname;
11813b2aab18SMatthew Ahrens 	int error;
11823b2aab18SMatthew Ahrens 	int delta = strlen(ddra->ddra_newname) - strlen(ddra->ddra_oldname);
1183fa9e4066Sahrens 
11843b2aab18SMatthew Ahrens 	/* target dir should exist */
11853b2aab18SMatthew Ahrens 	error = dsl_dir_hold(dp, ddra->ddra_oldname, FTAG, &dd, NULL);
11863b2aab18SMatthew Ahrens 	if (error != 0)
11873b2aab18SMatthew Ahrens 		return (error);
11883b2aab18SMatthew Ahrens 
11893b2aab18SMatthew Ahrens 	/* new parent should exist */
11903b2aab18SMatthew Ahrens 	error = dsl_dir_hold(dp, ddra->ddra_newname, FTAG,
11913b2aab18SMatthew Ahrens 	    &newparent, &mynewname);
11923b2aab18SMatthew Ahrens 	if (error != 0) {
11933b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, FTAG);
11943b2aab18SMatthew Ahrens 		return (error);
11953b2aab18SMatthew Ahrens 	}
11963b2aab18SMatthew Ahrens 
11973b2aab18SMatthew Ahrens 	/* can't rename to different pool */
11983b2aab18SMatthew Ahrens 	if (dd->dd_pool != newparent->dd_pool) {
11993b2aab18SMatthew Ahrens 		dsl_dir_rele(newparent, FTAG);
12003b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, FTAG);
1201be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENXIO));
12023b2aab18SMatthew Ahrens 	}
12033b2aab18SMatthew Ahrens 
12043b2aab18SMatthew Ahrens 	/* new name should not already exist */
12053b2aab18SMatthew Ahrens 	if (mynewname == NULL) {
12063b2aab18SMatthew Ahrens 		dsl_dir_rele(newparent, FTAG);
12073b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, FTAG);
1208be6fd75aSMatthew Ahrens 		return (SET_ERROR(EEXIST));
12093b2aab18SMatthew Ahrens 	}
12103b2aab18SMatthew Ahrens 
12113b2aab18SMatthew Ahrens 	/* if the name length is growing, validate child name lengths */
12123b2aab18SMatthew Ahrens 	if (delta > 0) {
12133b2aab18SMatthew Ahrens 		error = dmu_objset_find_dp(dp, dd->dd_object, dsl_valid_rename,
12143b2aab18SMatthew Ahrens 		    &delta, DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS);
12153b2aab18SMatthew Ahrens 		if (error != 0) {
12163b2aab18SMatthew Ahrens 			dsl_dir_rele(newparent, FTAG);
12173b2aab18SMatthew Ahrens 			dsl_dir_rele(dd, FTAG);
12183b2aab18SMatthew Ahrens 			return (error);
12193b2aab18SMatthew Ahrens 		}
12203b2aab18SMatthew Ahrens 	}
12211d452cf5Sahrens 
12223b2aab18SMatthew Ahrens 	if (newparent != dd->dd_parent) {
122399653d4eSeschrock 		/* is there enough space? */
122499653d4eSeschrock 		uint64_t myspace =
122574e7dc98SMatthew Ahrens 		    MAX(dd->dd_phys->dd_used_bytes, dd->dd_phys->dd_reserved);
1226fa9e4066Sahrens 
12271d452cf5Sahrens 		/* no rename into our descendant */
12283b2aab18SMatthew Ahrens 		if (closest_common_ancestor(dd, newparent) == dd) {
12293b2aab18SMatthew Ahrens 			dsl_dir_rele(newparent, FTAG);
12303b2aab18SMatthew Ahrens 			dsl_dir_rele(dd, FTAG);
1231be6fd75aSMatthew Ahrens 			return (SET_ERROR(EINVAL));
12323b2aab18SMatthew Ahrens 		}
1233fa9e4066Sahrens 
12343b2aab18SMatthew Ahrens 		error = dsl_dir_transfer_possible(dd->dd_parent,
12353b2aab18SMatthew Ahrens 		    newparent, myspace);
12363b2aab18SMatthew Ahrens 		if (error != 0) {
12373b2aab18SMatthew Ahrens 			dsl_dir_rele(newparent, FTAG);
12383b2aab18SMatthew Ahrens 			dsl_dir_rele(dd, FTAG);
12393b2aab18SMatthew Ahrens 			return (error);
12403b2aab18SMatthew Ahrens 		}
12411d452cf5Sahrens 	}
1242fa9e4066Sahrens 
12433b2aab18SMatthew Ahrens 	dsl_dir_rele(newparent, FTAG);
12443b2aab18SMatthew Ahrens 	dsl_dir_rele(dd, FTAG);
12451d452cf5Sahrens 	return (0);
12461d452cf5Sahrens }
12471d452cf5Sahrens 
12481d452cf5Sahrens static void
12493b2aab18SMatthew Ahrens dsl_dir_rename_sync(void *arg, dmu_tx_t *tx)
12501d452cf5Sahrens {
12513b2aab18SMatthew Ahrens 	dsl_dir_rename_arg_t *ddra = arg;
12523b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
12533b2aab18SMatthew Ahrens 	dsl_dir_t *dd, *newparent;
12543b2aab18SMatthew Ahrens 	const char *mynewname;
12553b2aab18SMatthew Ahrens 	int error;
12561d452cf5Sahrens 	objset_t *mos = dp->dp_meta_objset;
12571d452cf5Sahrens 
12583b2aab18SMatthew Ahrens 	VERIFY0(dsl_dir_hold(dp, ddra->ddra_oldname, FTAG, &dd, NULL));
12593b2aab18SMatthew Ahrens 	VERIFY0(dsl_dir_hold(dp, ddra->ddra_newname, FTAG, &newparent,
12603b2aab18SMatthew Ahrens 	    &mynewname));
12611d452cf5Sahrens 
12624445fffbSMatthew Ahrens 	/* Log this before we change the name. */
12634445fffbSMatthew Ahrens 	spa_history_log_internal_dd(dd, "rename", tx,
12643b2aab18SMatthew Ahrens 	    "-> %s", ddra->ddra_newname);
12654445fffbSMatthew Ahrens 
12663b2aab18SMatthew Ahrens 	if (newparent != dd->dd_parent) {
126774e7dc98SMatthew Ahrens 		dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD,
126874e7dc98SMatthew Ahrens 		    -dd->dd_phys->dd_used_bytes,
1269fa9e4066Sahrens 		    -dd->dd_phys->dd_compressed_bytes,
1270fa9e4066Sahrens 		    -dd->dd_phys->dd_uncompressed_bytes, tx);
12713b2aab18SMatthew Ahrens 		dsl_dir_diduse_space(newparent, DD_USED_CHILD,
127274e7dc98SMatthew Ahrens 		    dd->dd_phys->dd_used_bytes,
1273fa9e4066Sahrens 		    dd->dd_phys->dd_compressed_bytes,
1274fa9e4066Sahrens 		    dd->dd_phys->dd_uncompressed_bytes, tx);
127574e7dc98SMatthew Ahrens 
127674e7dc98SMatthew Ahrens 		if (dd->dd_phys->dd_reserved > dd->dd_phys->dd_used_bytes) {
127774e7dc98SMatthew Ahrens 			uint64_t unused_rsrv = dd->dd_phys->dd_reserved -
127874e7dc98SMatthew Ahrens 			    dd->dd_phys->dd_used_bytes;
127974e7dc98SMatthew Ahrens 
128074e7dc98SMatthew Ahrens 			dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD_RSRV,
128174e7dc98SMatthew Ahrens 			    -unused_rsrv, 0, 0, tx);
12823b2aab18SMatthew Ahrens 			dsl_dir_diduse_space(newparent, DD_USED_CHILD_RSRV,
128374e7dc98SMatthew Ahrens 			    unused_rsrv, 0, 0, tx);
128474e7dc98SMatthew Ahrens 		}
1285fa9e4066Sahrens 	}
1286fa9e4066Sahrens 
1287fa9e4066Sahrens 	dmu_buf_will_dirty(dd->dd_dbuf, tx);
1288fa9e4066Sahrens 
1289fa9e4066Sahrens 	/* remove from old parent zapobj */
12903b2aab18SMatthew Ahrens 	error = zap_remove(mos, dd->dd_parent->dd_phys->dd_child_dir_zapobj,
1291fa9e4066Sahrens 	    dd->dd_myname, tx);
12923b2aab18SMatthew Ahrens 	ASSERT0(error);
1293fa9e4066Sahrens 
12943b2aab18SMatthew Ahrens 	(void) strcpy(dd->dd_myname, mynewname);
12953b2aab18SMatthew Ahrens 	dsl_dir_rele(dd->dd_parent, dd);
12963b2aab18SMatthew Ahrens 	dd->dd_phys->dd_parent_obj = newparent->dd_object;
12973b2aab18SMatthew Ahrens 	VERIFY0(dsl_dir_hold_obj(dp,
12983b2aab18SMatthew Ahrens 	    newparent->dd_object, NULL, dd, &dd->dd_parent));
1299fa9e4066Sahrens 
1300fa9e4066Sahrens 	/* add to new parent zapobj */
13013b2aab18SMatthew Ahrens 	VERIFY0(zap_add(mos, newparent->dd_phys->dd_child_dir_zapobj,
13023b2aab18SMatthew Ahrens 	    dd->dd_myname, 8, 1, &dd->dd_object, tx));
13033b2aab18SMatthew Ahrens 
13043b2aab18SMatthew Ahrens 	dsl_prop_notify_all(dd);
1305ecd6cf80Smarks 
13063b2aab18SMatthew Ahrens 	dsl_dir_rele(newparent, FTAG);
13073b2aab18SMatthew Ahrens 	dsl_dir_rele(dd, FTAG);
13081d452cf5Sahrens }
1309fa9e4066Sahrens 
13101d452cf5Sahrens int
13113b2aab18SMatthew Ahrens dsl_dir_rename(const char *oldname, const char *newname)
13121d452cf5Sahrens {
13133b2aab18SMatthew Ahrens 	dsl_dir_rename_arg_t ddra;
13141d452cf5Sahrens 
13153b2aab18SMatthew Ahrens 	ddra.ddra_oldname = oldname;
13163b2aab18SMatthew Ahrens 	ddra.ddra_newname = newname;
13171d452cf5Sahrens 
13183b2aab18SMatthew Ahrens 	return (dsl_sync_task(oldname,
13193b2aab18SMatthew Ahrens 	    dsl_dir_rename_check, dsl_dir_rename_sync, &ddra, 3));
1320fa9e4066Sahrens }
132199653d4eSeschrock 
132299653d4eSeschrock int
132399653d4eSeschrock dsl_dir_transfer_possible(dsl_dir_t *sdd, dsl_dir_t *tdd, uint64_t space)
132499653d4eSeschrock {
132599653d4eSeschrock 	dsl_dir_t *ancestor;
132699653d4eSeschrock 	int64_t adelta;
132799653d4eSeschrock 	uint64_t avail;
132899653d4eSeschrock 
132999653d4eSeschrock 	ancestor = closest_common_ancestor(sdd, tdd);
133099653d4eSeschrock 	adelta = would_change(sdd, -space, ancestor);
133199653d4eSeschrock 	avail = dsl_dir_space_available(tdd, ancestor, adelta, FALSE);
133299653d4eSeschrock 	if (avail < space)
1333be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENOSPC));
133499653d4eSeschrock 
133599653d4eSeschrock 	return (0);
133699653d4eSeschrock }
133771eb0538SChris Kirby 
133871eb0538SChris Kirby timestruc_t
133971eb0538SChris Kirby dsl_dir_snap_cmtime(dsl_dir_t *dd)
134071eb0538SChris Kirby {
134171eb0538SChris Kirby 	timestruc_t t;
134271eb0538SChris Kirby 
134371eb0538SChris Kirby 	mutex_enter(&dd->dd_lock);
134471eb0538SChris Kirby 	t = dd->dd_snap_cmtime;
134571eb0538SChris Kirby 	mutex_exit(&dd->dd_lock);
134671eb0538SChris Kirby 
134771eb0538SChris Kirby 	return (t);
134871eb0538SChris Kirby }
134971eb0538SChris Kirby 
135071eb0538SChris Kirby void
135171eb0538SChris Kirby dsl_dir_snap_cmtime_update(dsl_dir_t *dd)
135271eb0538SChris Kirby {
135371eb0538SChris Kirby 	timestruc_t t;
135471eb0538SChris Kirby 
135571eb0538SChris Kirby 	gethrestime(&t);
135671eb0538SChris Kirby 	mutex_enter(&dd->dd_lock);
135771eb0538SChris Kirby 	dd->dd_snap_cmtime = t;
135871eb0538SChris Kirby 	mutex_exit(&dd->dd_lock);
135971eb0538SChris Kirby }
1360