xref: /onnv-gate/usr/src/uts/common/fs/zfs/dsl_prop.c (revision 2082:76b439ec3ac1)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51544Seschrock  * Common Development and Distribution License (the "License").
61544Seschrock  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
221356Seschrock  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27789Sahrens 
28789Sahrens #include <sys/dmu.h>
291356Seschrock #include <sys/dmu_objset.h>
30789Sahrens #include <sys/dmu_tx.h>
31789Sahrens #include <sys/dsl_dataset.h>
32789Sahrens #include <sys/dsl_dir.h>
33789Sahrens #include <sys/dsl_prop.h>
34789Sahrens #include <sys/spa.h>
35789Sahrens #include <sys/zio_checksum.h> /* for the default checksum value */
36789Sahrens #include <sys/zap.h>
37789Sahrens #include <sys/fs/zfs.h>
38789Sahrens 
39789Sahrens #include "zfs_prop.h"
40789Sahrens 
41789Sahrens static int
42789Sahrens dodefault(const char *propname, int intsz, int numint, void *buf)
43789Sahrens {
44789Sahrens 	zfs_prop_t prop;
45789Sahrens 
46789Sahrens 	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL ||
47789Sahrens 	    zfs_prop_readonly(prop))
48789Sahrens 		return (ENOENT);
49789Sahrens 
50789Sahrens 	if (zfs_prop_get_type(prop) == prop_type_string) {
51789Sahrens 		if (intsz != 1)
52789Sahrens 			return (EOVERFLOW);
531356Seschrock 		(void) strncpy(buf, zfs_prop_default_string(prop), numint);
54789Sahrens 	} else {
55789Sahrens 		if (intsz != 8 || numint < 1)
56789Sahrens 			return (EOVERFLOW);
57789Sahrens 
58789Sahrens 		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
59789Sahrens 	}
60789Sahrens 
61789Sahrens 	return (0);
62789Sahrens }
63789Sahrens 
64789Sahrens static int
65*2082Seschrock dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
66789Sahrens     int intsz, int numint, void *buf, char *setpoint)
67789Sahrens {
68*2082Seschrock 	int err = ENOENT;
69789Sahrens 
70789Sahrens 	if (setpoint)
71789Sahrens 		setpoint[0] = '\0';
72789Sahrens 
73*2082Seschrock 	/*
74*2082Seschrock 	 * Note: dd may be NULL, therefore we shouldn't dereference it
75*2082Seschrock 	 * ouside this loop.
76*2082Seschrock 	 */
77*2082Seschrock 	for (; dd != NULL; dd = dd->dd_parent) {
78*2082Seschrock 		objset_t *mos = dd->dd_pool->dp_meta_objset;
79*2082Seschrock 		ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
80789Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
81789Sahrens 		    propname, intsz, numint, buf);
82789Sahrens 		if (err != ENOENT) {
83789Sahrens 			if (setpoint)
84789Sahrens 				dsl_dir_name(dd, setpoint);
85789Sahrens 			break;
86789Sahrens 		}
87789Sahrens 	}
88789Sahrens 	if (err == ENOENT)
89789Sahrens 		err = dodefault(propname, intsz, numint, buf);
90789Sahrens 
91789Sahrens 	return (err);
92789Sahrens }
93789Sahrens 
94789Sahrens /*
95789Sahrens  * Register interest in the named property.  We'll call the callback
96789Sahrens  * once to notify it of the current property value, and again each time
97789Sahrens  * the property changes, until this callback is unregistered.
98789Sahrens  *
99789Sahrens  * Return 0 on success, errno if the prop is not an integer value.
100789Sahrens  */
101789Sahrens int
102789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname,
103789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
104789Sahrens {
105*2082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
106789Sahrens 	uint64_t value;
107789Sahrens 	dsl_prop_cb_record_t *cbr;
108789Sahrens 	int err;
109789Sahrens 
110789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
111789Sahrens 
112*2082Seschrock 	err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL);
113789Sahrens 	if (err != 0) {
114789Sahrens 		rw_exit(&dd->dd_pool->dp_config_rwlock);
115789Sahrens 		return (err);
116789Sahrens 	}
117789Sahrens 
118789Sahrens 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
119*2082Seschrock 	cbr->cbr_ds = ds;
120789Sahrens 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
121789Sahrens 	(void) strcpy((char *)cbr->cbr_propname, propname);
122789Sahrens 	cbr->cbr_func = callback;
123789Sahrens 	cbr->cbr_arg = cbarg;
124789Sahrens 	mutex_enter(&dd->dd_lock);
125789Sahrens 	list_insert_head(&dd->dd_prop_cbs, cbr);
126789Sahrens 	mutex_exit(&dd->dd_lock);
127789Sahrens 
128789Sahrens 	cbr->cbr_func(cbr->cbr_arg, value);
129789Sahrens 
1301544Seschrock 	VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object,
1311544Seschrock 	    NULL, cbr, &dd));
132789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
133789Sahrens 	/* Leave dataset open until this callback is unregistered */
134789Sahrens 	return (0);
135789Sahrens }
136789Sahrens 
137789Sahrens int
138789Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname,
139789Sahrens     int intsz, int numints, void *buf, char *setpoint)
140789Sahrens {
141789Sahrens 	int err;
142789Sahrens 
143789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
144*2082Seschrock 	err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint);
145789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
146789Sahrens 
147789Sahrens 	return (err);
148789Sahrens }
149789Sahrens 
150789Sahrens int
151789Sahrens dsl_prop_get(const char *ddname, const char *propname,
152789Sahrens     int intsz, int numints, void *buf, char *setpoint)
153789Sahrens {
154789Sahrens 	dsl_dir_t *dd;
155789Sahrens 	const char *tail;
156789Sahrens 	int err;
157789Sahrens 
1581544Seschrock 	err = dsl_dir_open(ddname, FTAG, &dd, &tail);
1591544Seschrock 	if (err)
1601544Seschrock 		return (err);
161789Sahrens 	if (tail && tail[0] != '@') {
162789Sahrens 		dsl_dir_close(dd, FTAG);
163789Sahrens 		return (ENOENT);
164789Sahrens 	}
165789Sahrens 
166789Sahrens 	err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint);
167789Sahrens 
168789Sahrens 	dsl_dir_close(dd, FTAG);
169789Sahrens 	return (err);
170789Sahrens }
171789Sahrens 
172789Sahrens /*
173789Sahrens  * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if
174789Sahrens  * valuelen not big enough.
175789Sahrens  */
176789Sahrens int
177789Sahrens dsl_prop_get_string(const char *ddname, const char *propname,
178789Sahrens     char *value, int valuelen, char *setpoint)
179789Sahrens {
180789Sahrens 	return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint));
181789Sahrens }
182789Sahrens 
183789Sahrens /*
184789Sahrens  * Get the current property value.  It may have changed by the time this
185789Sahrens  * function returns, so it is NOT safe to follow up with
186789Sahrens  * dsl_prop_register() and assume that the value has not changed in
187789Sahrens  * between.
188789Sahrens  *
189789Sahrens  * Return 0 on success, ENOENT if ddname is invalid.
190789Sahrens  */
191789Sahrens int
192789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname,
193789Sahrens     uint64_t *valuep, char *setpoint)
194789Sahrens {
195789Sahrens 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
196789Sahrens }
197789Sahrens 
198789Sahrens int
199789Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname,
200789Sahrens     uint64_t *valuep, char *setpoint)
201789Sahrens {
202789Sahrens 	return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint));
203789Sahrens }
204789Sahrens 
205789Sahrens /*
206789Sahrens  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
207789Sahrens  * invalid, ENOMSG if no matching callback registered.
208789Sahrens  */
209789Sahrens int
210789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
211789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
212789Sahrens {
213*2082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
214789Sahrens 	dsl_prop_cb_record_t *cbr;
215789Sahrens 
216789Sahrens 	mutex_enter(&dd->dd_lock);
217789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
218789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
219*2082Seschrock 		if (cbr->cbr_ds == ds &&
220789Sahrens 		    cbr->cbr_func == callback &&
221*2082Seschrock 		    cbr->cbr_arg == cbarg &&
222*2082Seschrock 		    strcmp(cbr->cbr_propname, propname) == 0)
223789Sahrens 			break;
224789Sahrens 	}
225789Sahrens 
226789Sahrens 	if (cbr == NULL) {
227789Sahrens 		mutex_exit(&dd->dd_lock);
228789Sahrens 		return (ENOMSG);
229789Sahrens 	}
230789Sahrens 
231789Sahrens 	list_remove(&dd->dd_prop_cbs, cbr);
232789Sahrens 	mutex_exit(&dd->dd_lock);
233789Sahrens 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
234789Sahrens 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
235789Sahrens 
236789Sahrens 	/* Clean up from dsl_prop_register */
237789Sahrens 	dsl_dir_close(dd, cbr);
238789Sahrens 	return (0);
239789Sahrens }
240789Sahrens 
241*2082Seschrock /*
242*2082Seschrock  * Return the number of callbacks that are registered for this dataset.
243*2082Seschrock  */
244*2082Seschrock int
245*2082Seschrock dsl_prop_numcb(dsl_dataset_t *ds)
246*2082Seschrock {
247*2082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
248*2082Seschrock 	dsl_prop_cb_record_t *cbr;
249*2082Seschrock 	int num = 0;
250*2082Seschrock 
251*2082Seschrock 	mutex_enter(&dd->dd_lock);
252*2082Seschrock 	for (cbr = list_head(&dd->dd_prop_cbs);
253*2082Seschrock 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
254*2082Seschrock 		if (cbr->cbr_ds == ds)
255*2082Seschrock 			num++;
256*2082Seschrock 	}
257*2082Seschrock 	mutex_exit(&dd->dd_lock);
258*2082Seschrock 
259*2082Seschrock 	return (num);
260*2082Seschrock }
261*2082Seschrock 
262789Sahrens static void
263789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
264789Sahrens     const char *propname, uint64_t value, int first)
265789Sahrens {
266789Sahrens 	dsl_dir_t *dd;
267789Sahrens 	dsl_prop_cb_record_t *cbr;
268789Sahrens 	objset_t *mos = dp->dp_meta_objset;
269789Sahrens 	int err;
270789Sahrens 
271789Sahrens 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
2721544Seschrock 	err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
2731544Seschrock 	if (err)
2741544Seschrock 		return;
275789Sahrens 
276789Sahrens 	if (!first) {
277789Sahrens 		/*
278789Sahrens 		 * If the prop is set here, then this change is not
279789Sahrens 		 * being inherited here or below; stop the recursion.
280789Sahrens 		 */
281789Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
282789Sahrens 		    8, 1, &value);
283789Sahrens 		if (err == 0) {
284789Sahrens 			dsl_dir_close(dd, FTAG);
285789Sahrens 			return;
286789Sahrens 		}
287789Sahrens 		ASSERT3U(err, ==, ENOENT);
288789Sahrens 	}
289789Sahrens 
290789Sahrens 	mutex_enter(&dd->dd_lock);
291789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
292789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
293789Sahrens 		if (strcmp(cbr->cbr_propname, propname) == 0) {
294789Sahrens 			cbr->cbr_func(cbr->cbr_arg, value);
295789Sahrens 		}
296789Sahrens 	}
297789Sahrens 	mutex_exit(&dd->dd_lock);
298789Sahrens 
299789Sahrens 	if (dd->dd_phys->dd_child_dir_zapobj) {
300789Sahrens 		zap_cursor_t zc;
301789Sahrens 		zap_attribute_t za;
302789Sahrens 
303789Sahrens 		for (zap_cursor_init(&zc, mos,
304789Sahrens 		    dd->dd_phys->dd_child_dir_zapobj);
305789Sahrens 		    zap_cursor_retrieve(&zc, &za) == 0;
306789Sahrens 		    zap_cursor_advance(&zc)) {
307789Sahrens 			/* XXX recursion could blow stack; esp. za! */
308789Sahrens 			dsl_prop_changed_notify(dp, za.za_first_integer,
309789Sahrens 			    propname, value, FALSE);
310789Sahrens 		}
311885Sahrens 		zap_cursor_fini(&zc);
312789Sahrens 	}
313789Sahrens 	dsl_dir_close(dd, FTAG);
314789Sahrens }
315789Sahrens 
316789Sahrens struct prop_set_arg {
317789Sahrens 	const char *name;
318789Sahrens 	int intsz;
319789Sahrens 	int numints;
320789Sahrens 	const void *buf;
321789Sahrens };
322789Sahrens 
323789Sahrens static int
324789Sahrens dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx)
325789Sahrens {
326789Sahrens 	struct prop_set_arg *psa = arg;
327789Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
328789Sahrens 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
329789Sahrens 	uint64_t intval;
330789Sahrens 	int err, isint;
331789Sahrens 
332789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER);
333789Sahrens 
334789Sahrens 	isint = (dodefault(psa->name, 8, 1, &intval) == 0);
335789Sahrens 
336789Sahrens 	if (psa->numints == 0) {
337789Sahrens 		err = zap_remove(mos, zapobj, psa->name, tx);
338789Sahrens 		if (err == ENOENT) /* that's fine. */
339789Sahrens 			err = 0;
340789Sahrens 		if (err == 0 && isint) {
341*2082Seschrock 			err = dsl_prop_get_impl(dd->dd_parent,
342*2082Seschrock 			    psa->name, 8, 1, &intval, NULL);
343789Sahrens 		}
344789Sahrens 	} else {
345789Sahrens 		err = zap_update(mos, zapobj, psa->name,
346789Sahrens 		    psa->intsz, psa->numints, psa->buf, tx);
347789Sahrens 		if (isint)
348789Sahrens 			intval = *(uint64_t *)psa->buf;
349789Sahrens 	}
350789Sahrens 
351789Sahrens 	if (err == 0 && isint) {
352789Sahrens 		dsl_prop_changed_notify(dd->dd_pool,
353789Sahrens 		    dd->dd_object, psa->name, intval, TRUE);
354789Sahrens 	}
355789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
356789Sahrens 
357789Sahrens 	return (err);
358789Sahrens }
359789Sahrens 
360789Sahrens int
361789Sahrens dsl_prop_set(const char *ddname, const char *propname,
362789Sahrens     int intsz, int numints, const void *buf)
363789Sahrens {
364789Sahrens 	dsl_dir_t *dd;
365789Sahrens 	int err;
366789Sahrens 	struct prop_set_arg psa;
367789Sahrens 
3681544Seschrock 	err = dsl_dir_open(ddname, FTAG, &dd, NULL);
3691544Seschrock 	if (err)
3701544Seschrock 		return (err);
371789Sahrens 
372789Sahrens 	psa.name = propname;
373789Sahrens 	psa.intsz = intsz;
374789Sahrens 	psa.numints = numints;
375789Sahrens 	psa.buf = buf;
3761544Seschrock 	err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 1<<20);
377789Sahrens 
378789Sahrens 	dsl_dir_close(dd, FTAG);
379789Sahrens 
380789Sahrens 	return (err);
381789Sahrens }
3821356Seschrock 
3831356Seschrock /*
3841356Seschrock  * Iterate over all properties for this dataset and return them in an nvlist.
3851356Seschrock  */
3861356Seschrock int
3871356Seschrock dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
3881356Seschrock {
3891356Seschrock 	dsl_dataset_t *ds = os->os->os_dsl_dataset;
390*2082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
3911356Seschrock 	int err = 0;
3921356Seschrock 	dsl_pool_t *dp;
3931356Seschrock 	objset_t *mos;
3941356Seschrock 	zap_cursor_t zc;
3951356Seschrock 	zap_attribute_t za;
3961356Seschrock 	char setpoint[MAXNAMELEN];
3971356Seschrock 	char *tmp;
3981356Seschrock 	nvlist_t *prop;
3991356Seschrock 
4001356Seschrock 	if (dsl_dataset_is_snapshot(ds)) {
4011356Seschrock 		VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
4021356Seschrock 		return (0);
4031356Seschrock 	}
4041356Seschrock 
4051356Seschrock 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
4061356Seschrock 
4071356Seschrock 	dp = dd->dd_pool;
4081356Seschrock 	mos = dp->dp_meta_objset;
4091356Seschrock 
4101356Seschrock 	rw_enter(&dp->dp_config_rwlock, RW_READER);
411*2082Seschrock 	for (; dd != NULL; dd = dd->dd_parent) {
4121356Seschrock 		dsl_dir_name(dd, setpoint);
4131356Seschrock 
4141356Seschrock 		for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj);
4151356Seschrock 		    (err = zap_cursor_retrieve(&zc, &za)) == 0;
4161356Seschrock 		    zap_cursor_advance(&zc)) {
4171356Seschrock 			if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0)
4181356Seschrock 				continue;
4191356Seschrock 
4201356Seschrock 			VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME,
4211356Seschrock 			    KM_SLEEP) == 0);
4221356Seschrock 			if (za.za_integer_length == 1) {
4231356Seschrock 				/*
4241356Seschrock 				 * String property
4251356Seschrock 				 */
4261356Seschrock 				tmp = kmem_alloc(za.za_num_integers, KM_SLEEP);
4271356Seschrock 				err = zap_lookup(mos,
4281356Seschrock 				    dd->dd_phys->dd_props_zapobj,
4291356Seschrock 				    za.za_name, 1, za.za_num_integers,
4301356Seschrock 				    tmp);
4311356Seschrock 				if (err != 0) {
4321356Seschrock 					kmem_free(tmp, za.za_num_integers);
4331356Seschrock 					break;
4341356Seschrock 				}
4351356Seschrock 				VERIFY(nvlist_add_string(prop,
4361356Seschrock 				    ZFS_PROP_VALUE, tmp) == 0);
4371356Seschrock 				kmem_free(tmp, za.za_num_integers);
4381356Seschrock 			} else {
4391356Seschrock 				/*
4401356Seschrock 				 * Integer property
4411356Seschrock 				 */
4421356Seschrock 				ASSERT(za.za_integer_length == 8);
4431356Seschrock 				(void) nvlist_add_uint64(prop, ZFS_PROP_VALUE,
4441356Seschrock 				    za.za_first_integer);
4451356Seschrock 			}
4461356Seschrock 
4471356Seschrock 			VERIFY(nvlist_add_string(prop,
4481356Seschrock 			    ZFS_PROP_SOURCE, setpoint) == 0);
4491356Seschrock 			VERIFY(nvlist_add_nvlist(*nvp, za.za_name,
4501356Seschrock 			    prop) == 0);
4511356Seschrock 			nvlist_free(prop);
4521356Seschrock 		}
4531356Seschrock 		zap_cursor_fini(&zc);
4541356Seschrock 
455*2082Seschrock 		if (err != ENOENT)
4561356Seschrock 			break;
457*2082Seschrock 		err = 0;
4581356Seschrock 	}
4591356Seschrock 	rw_exit(&dp->dp_config_rwlock);
4601356Seschrock 
4611356Seschrock 	return (err);
4621356Seschrock }
463