xref: /onnv-gate/usr/src/uts/common/fs/zfs/dsl_prop.c (revision 885:d925b21dba78)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
5789Sahrens  * Common Development and Distribution License, Version 1.0 only
6789Sahrens  * (the "License").  You may not use this file except in compliance
7789Sahrens  * with the License.
8789Sahrens  *
9789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10789Sahrens  * or http://www.opensolaris.org/os/licensing.
11789Sahrens  * See the License for the specific language governing permissions
12789Sahrens  * and limitations under the License.
13789Sahrens  *
14789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19789Sahrens  *
20789Sahrens  * CDDL HEADER END
21789Sahrens  */
22789Sahrens /*
23789Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24789Sahrens  * Use is subject to license terms.
25789Sahrens  */
26789Sahrens 
27789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28789Sahrens 
29789Sahrens #include <sys/dmu.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);
53789Sahrens 		zfs_prop_default_string(prop, buf, 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
65789Sahrens dsl_prop_get_impl(dsl_pool_t *dp, uint64_t ddobj, const char *propname,
66789Sahrens     int intsz, int numint, void *buf, char *setpoint)
67789Sahrens {
68789Sahrens 	int err = 0;
69789Sahrens 	objset_t *mos = dp->dp_meta_objset;
70789Sahrens 
71789Sahrens 	if (setpoint)
72789Sahrens 		setpoint[0] = '\0';
73789Sahrens 
74789Sahrens 	ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock));
75789Sahrens 
76789Sahrens 	while (ddobj != 0) {
77789Sahrens 		dsl_dir_t *dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG);
78789Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
79789Sahrens 		    propname, intsz, numint, buf);
80789Sahrens 		if (err != ENOENT) {
81789Sahrens 			if (setpoint)
82789Sahrens 				dsl_dir_name(dd, setpoint);
83789Sahrens 			dsl_dir_close(dd, FTAG);
84789Sahrens 			break;
85789Sahrens 		}
86789Sahrens 		ASSERT3U(err, ==, ENOENT);
87789Sahrens 		ddobj = dd->dd_phys->dd_parent_obj;
88789Sahrens 		dsl_dir_close(dd, FTAG);
89789Sahrens 	}
90789Sahrens 	if (err == ENOENT)
91789Sahrens 		err = dodefault(propname, intsz, numint, buf);
92789Sahrens 
93789Sahrens 	return (err);
94789Sahrens }
95789Sahrens 
96789Sahrens /*
97789Sahrens  * Register interest in the named property.  We'll call the callback
98789Sahrens  * once to notify it of the current property value, and again each time
99789Sahrens  * the property changes, until this callback is unregistered.
100789Sahrens  *
101789Sahrens  * Return 0 on success, errno if the prop is not an integer value.
102789Sahrens  */
103789Sahrens int
104789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname,
105789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
106789Sahrens {
107789Sahrens 	dsl_dir_t *dd;
108789Sahrens 	uint64_t value;
109789Sahrens 	dsl_prop_cb_record_t *cbr;
110789Sahrens 	int err;
111789Sahrens 
112789Sahrens 	dd = ds->ds_dir;
113789Sahrens 
114789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
115789Sahrens 
116789Sahrens 	err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, propname,
117789Sahrens 	    8, 1, &value, NULL);
118789Sahrens 	if (err == ENOENT) {
119789Sahrens 		err = 0;
120789Sahrens 		value = DSL_PROP_VALUE_UNDEFINED;
121789Sahrens 	}
122789Sahrens 	if (err != 0) {
123789Sahrens 		rw_exit(&dd->dd_pool->dp_config_rwlock);
124789Sahrens 		return (err);
125789Sahrens 	}
126789Sahrens 
127789Sahrens 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
128789Sahrens 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
129789Sahrens 	(void) strcpy((char *)cbr->cbr_propname, propname);
130789Sahrens 	cbr->cbr_func = callback;
131789Sahrens 	cbr->cbr_arg = cbarg;
132789Sahrens 	mutex_enter(&dd->dd_lock);
133789Sahrens 	list_insert_head(&dd->dd_prop_cbs, cbr);
134789Sahrens 	mutex_exit(&dd->dd_lock);
135789Sahrens 
136789Sahrens 	cbr->cbr_func(cbr->cbr_arg, value);
137789Sahrens 
138789Sahrens 	(void) dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, cbr);
139789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
140789Sahrens 	/* Leave dataset open until this callback is unregistered */
141789Sahrens 	return (0);
142789Sahrens }
143789Sahrens 
144789Sahrens int
145789Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname,
146789Sahrens     int intsz, int numints, void *buf, char *setpoint)
147789Sahrens {
148789Sahrens 	int err;
149789Sahrens 
150789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
151789Sahrens 	err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object,
152789Sahrens 	    propname, intsz, numints, buf, setpoint);
153789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
154789Sahrens 
155789Sahrens 	return (err);
156789Sahrens }
157789Sahrens 
158789Sahrens int
159789Sahrens dsl_prop_get(const char *ddname, const char *propname,
160789Sahrens     int intsz, int numints, void *buf, char *setpoint)
161789Sahrens {
162789Sahrens 	dsl_dir_t *dd;
163789Sahrens 	const char *tail;
164789Sahrens 	int err;
165789Sahrens 
166789Sahrens 	dd = dsl_dir_open(ddname, FTAG, &tail);
167789Sahrens 	if (dd == NULL)
168789Sahrens 		return (ENOENT);
169789Sahrens 	if (tail && tail[0] != '@') {
170789Sahrens 		dsl_dir_close(dd, FTAG);
171789Sahrens 		return (ENOENT);
172789Sahrens 	}
173789Sahrens 
174789Sahrens 	err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint);
175789Sahrens 
176789Sahrens 	dsl_dir_close(dd, FTAG);
177789Sahrens 	return (err);
178789Sahrens }
179789Sahrens 
180789Sahrens /*
181789Sahrens  * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if
182789Sahrens  * valuelen not big enough.
183789Sahrens  */
184789Sahrens int
185789Sahrens dsl_prop_get_string(const char *ddname, const char *propname,
186789Sahrens     char *value, int valuelen, char *setpoint)
187789Sahrens {
188789Sahrens 	return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint));
189789Sahrens }
190789Sahrens 
191789Sahrens /*
192789Sahrens  * Get the current property value.  It may have changed by the time this
193789Sahrens  * function returns, so it is NOT safe to follow up with
194789Sahrens  * dsl_prop_register() and assume that the value has not changed in
195789Sahrens  * between.
196789Sahrens  *
197789Sahrens  * Return 0 on success, ENOENT if ddname is invalid.
198789Sahrens  */
199789Sahrens int
200789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname,
201789Sahrens     uint64_t *valuep, char *setpoint)
202789Sahrens {
203789Sahrens 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
204789Sahrens }
205789Sahrens 
206789Sahrens int
207789Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname,
208789Sahrens     uint64_t *valuep, char *setpoint)
209789Sahrens {
210789Sahrens 	return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint));
211789Sahrens }
212789Sahrens 
213789Sahrens /*
214789Sahrens  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
215789Sahrens  * invalid, ENOMSG if no matching callback registered.
216789Sahrens  */
217789Sahrens int
218789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
219789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
220789Sahrens {
221789Sahrens 	dsl_dir_t *dd;
222789Sahrens 	dsl_prop_cb_record_t *cbr;
223789Sahrens 
224789Sahrens 	dd = ds->ds_dir;
225789Sahrens 
226789Sahrens 	mutex_enter(&dd->dd_lock);
227789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
228789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
229789Sahrens 		if (strcmp(cbr->cbr_propname, propname) == 0 &&
230789Sahrens 		    cbr->cbr_func == callback &&
231789Sahrens 		    cbr->cbr_arg == cbarg)
232789Sahrens 			break;
233789Sahrens 	}
234789Sahrens 
235789Sahrens 	if (cbr == NULL) {
236789Sahrens 		mutex_exit(&dd->dd_lock);
237789Sahrens 		return (ENOMSG);
238789Sahrens 	}
239789Sahrens 
240789Sahrens 	list_remove(&dd->dd_prop_cbs, cbr);
241789Sahrens 	mutex_exit(&dd->dd_lock);
242789Sahrens 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
243789Sahrens 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
244789Sahrens 
245789Sahrens 	/* Clean up from dsl_prop_register */
246789Sahrens 	dsl_dir_close(dd, cbr);
247789Sahrens 	return (0);
248789Sahrens }
249789Sahrens 
250789Sahrens static void
251789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
252789Sahrens     const char *propname, uint64_t value, int first)
253789Sahrens {
254789Sahrens 	dsl_dir_t *dd;
255789Sahrens 	dsl_prop_cb_record_t *cbr;
256789Sahrens 	objset_t *mos = dp->dp_meta_objset;
257789Sahrens 	int err;
258789Sahrens 
259789Sahrens 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
260789Sahrens 	dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG);
261789Sahrens 
262789Sahrens 	if (!first) {
263789Sahrens 		/*
264789Sahrens 		 * If the prop is set here, then this change is not
265789Sahrens 		 * being inherited here or below; stop the recursion.
266789Sahrens 		 */
267789Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
268789Sahrens 		    8, 1, &value);
269789Sahrens 		if (err == 0) {
270789Sahrens 			dsl_dir_close(dd, FTAG);
271789Sahrens 			return;
272789Sahrens 		}
273789Sahrens 		ASSERT3U(err, ==, ENOENT);
274789Sahrens 	}
275789Sahrens 
276789Sahrens 	mutex_enter(&dd->dd_lock);
277789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
278789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
279789Sahrens 		if (strcmp(cbr->cbr_propname, propname) == 0) {
280789Sahrens 			cbr->cbr_func(cbr->cbr_arg, value);
281789Sahrens 		}
282789Sahrens 	}
283789Sahrens 	mutex_exit(&dd->dd_lock);
284789Sahrens 
285789Sahrens 	if (dd->dd_phys->dd_child_dir_zapobj) {
286789Sahrens 		zap_cursor_t zc;
287789Sahrens 		zap_attribute_t za;
288789Sahrens 
289789Sahrens 		for (zap_cursor_init(&zc, mos,
290789Sahrens 		    dd->dd_phys->dd_child_dir_zapobj);
291789Sahrens 		    zap_cursor_retrieve(&zc, &za) == 0;
292789Sahrens 		    zap_cursor_advance(&zc)) {
293789Sahrens 			/* XXX recursion could blow stack; esp. za! */
294789Sahrens 			dsl_prop_changed_notify(dp, za.za_first_integer,
295789Sahrens 			    propname, value, FALSE);
296789Sahrens 		}
297*885Sahrens 		zap_cursor_fini(&zc);
298789Sahrens 	}
299789Sahrens 	dsl_dir_close(dd, FTAG);
300789Sahrens }
301789Sahrens 
302789Sahrens struct prop_set_arg {
303789Sahrens 	const char *name;
304789Sahrens 	int intsz;
305789Sahrens 	int numints;
306789Sahrens 	const void *buf;
307789Sahrens };
308789Sahrens 
309789Sahrens static int
310789Sahrens dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx)
311789Sahrens {
312789Sahrens 	struct prop_set_arg *psa = arg;
313789Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
314789Sahrens 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
315789Sahrens 	uint64_t intval;
316789Sahrens 	int err, isint;
317789Sahrens 
318789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER);
319789Sahrens 
320789Sahrens 	isint = (dodefault(psa->name, 8, 1, &intval) == 0);
321789Sahrens 
322789Sahrens 	if (psa->numints == 0) {
323789Sahrens 		err = zap_remove(mos, zapobj, psa->name, tx);
324789Sahrens 		if (err == ENOENT) /* that's fine. */
325789Sahrens 			err = 0;
326789Sahrens 		if (err == 0 && isint) {
327789Sahrens 			err = dsl_prop_get_impl(dd->dd_pool,
328789Sahrens 			    dd->dd_phys->dd_parent_obj, psa->name,
329789Sahrens 			    8, 1, &intval, NULL);
330789Sahrens 		}
331789Sahrens 	} else {
332789Sahrens 		err = zap_update(mos, zapobj, psa->name,
333789Sahrens 		    psa->intsz, psa->numints, psa->buf, tx);
334789Sahrens 		if (isint)
335789Sahrens 			intval = *(uint64_t *)psa->buf;
336789Sahrens 	}
337789Sahrens 
338789Sahrens 	if (err == 0 && isint) {
339789Sahrens 		dsl_prop_changed_notify(dd->dd_pool,
340789Sahrens 		    dd->dd_object, psa->name, intval, TRUE);
341789Sahrens 	}
342789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
343789Sahrens 
344789Sahrens 	return (err);
345789Sahrens }
346789Sahrens 
347789Sahrens int
348789Sahrens dsl_prop_set(const char *ddname, const char *propname,
349789Sahrens     int intsz, int numints, const void *buf)
350789Sahrens {
351789Sahrens 	dsl_dir_t *dd;
352789Sahrens 	int err;
353789Sahrens 	struct prop_set_arg psa;
354789Sahrens 
355789Sahrens 	dd = dsl_dir_open(ddname, FTAG, NULL);
356789Sahrens 	if (dd == NULL)
357789Sahrens 		return (ENOENT);
358789Sahrens 
359789Sahrens 	psa.name = propname;
360789Sahrens 	psa.intsz = intsz;
361789Sahrens 	psa.numints = numints;
362789Sahrens 	psa.buf = buf;
363789Sahrens 	err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 0);
364789Sahrens 
365789Sahrens 	dsl_dir_close(dd, FTAG);
366789Sahrens 
367789Sahrens 	return (err);
368789Sahrens }
369