xref: /onnv-gate/usr/src/uts/common/fs/zfs/dsl_prop.c (revision 789:b348f31ed315)
1*789Sahrens /*
2*789Sahrens  * CDDL HEADER START
3*789Sahrens  *
4*789Sahrens  * The contents of this file are subject to the terms of the
5*789Sahrens  * Common Development and Distribution License, Version 1.0 only
6*789Sahrens  * (the "License").  You may not use this file except in compliance
7*789Sahrens  * with the License.
8*789Sahrens  *
9*789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*789Sahrens  * or http://www.opensolaris.org/os/licensing.
11*789Sahrens  * See the License for the specific language governing permissions
12*789Sahrens  * and limitations under the License.
13*789Sahrens  *
14*789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15*789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17*789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18*789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19*789Sahrens  *
20*789Sahrens  * CDDL HEADER END
21*789Sahrens  */
22*789Sahrens /*
23*789Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*789Sahrens  * Use is subject to license terms.
25*789Sahrens  */
26*789Sahrens 
27*789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*789Sahrens 
29*789Sahrens #include <sys/dmu.h>
30*789Sahrens #include <sys/dmu_tx.h>
31*789Sahrens #include <sys/dsl_dataset.h>
32*789Sahrens #include <sys/dsl_dir.h>
33*789Sahrens #include <sys/dsl_prop.h>
34*789Sahrens #include <sys/spa.h>
35*789Sahrens #include <sys/zio_checksum.h> /* for the default checksum value */
36*789Sahrens #include <sys/zap.h>
37*789Sahrens #include <sys/fs/zfs.h>
38*789Sahrens 
39*789Sahrens #include "zfs_prop.h"
40*789Sahrens 
41*789Sahrens static int
42*789Sahrens dodefault(const char *propname, int intsz, int numint, void *buf)
43*789Sahrens {
44*789Sahrens 	zfs_prop_t prop;
45*789Sahrens 
46*789Sahrens 	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL ||
47*789Sahrens 	    zfs_prop_readonly(prop))
48*789Sahrens 		return (ENOENT);
49*789Sahrens 
50*789Sahrens 	if (zfs_prop_get_type(prop) == prop_type_string) {
51*789Sahrens 		if (intsz != 1)
52*789Sahrens 			return (EOVERFLOW);
53*789Sahrens 		zfs_prop_default_string(prop, buf, numint);
54*789Sahrens 	} else {
55*789Sahrens 		if (intsz != 8 || numint < 1)
56*789Sahrens 			return (EOVERFLOW);
57*789Sahrens 
58*789Sahrens 		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
59*789Sahrens 	}
60*789Sahrens 
61*789Sahrens 	return (0);
62*789Sahrens }
63*789Sahrens 
64*789Sahrens static int
65*789Sahrens dsl_prop_get_impl(dsl_pool_t *dp, uint64_t ddobj, const char *propname,
66*789Sahrens     int intsz, int numint, void *buf, char *setpoint)
67*789Sahrens {
68*789Sahrens 	int err = 0;
69*789Sahrens 	objset_t *mos = dp->dp_meta_objset;
70*789Sahrens 
71*789Sahrens 	if (setpoint)
72*789Sahrens 		setpoint[0] = '\0';
73*789Sahrens 
74*789Sahrens 	ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock));
75*789Sahrens 
76*789Sahrens 	while (ddobj != 0) {
77*789Sahrens 		dsl_dir_t *dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG);
78*789Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
79*789Sahrens 		    propname, intsz, numint, buf);
80*789Sahrens 		if (err != ENOENT) {
81*789Sahrens 			if (setpoint)
82*789Sahrens 				dsl_dir_name(dd, setpoint);
83*789Sahrens 			dsl_dir_close(dd, FTAG);
84*789Sahrens 			break;
85*789Sahrens 		}
86*789Sahrens 		ASSERT3U(err, ==, ENOENT);
87*789Sahrens 		ddobj = dd->dd_phys->dd_parent_obj;
88*789Sahrens 		dsl_dir_close(dd, FTAG);
89*789Sahrens 	}
90*789Sahrens 	if (err == ENOENT)
91*789Sahrens 		err = dodefault(propname, intsz, numint, buf);
92*789Sahrens 
93*789Sahrens 	return (err);
94*789Sahrens }
95*789Sahrens 
96*789Sahrens /*
97*789Sahrens  * Register interest in the named property.  We'll call the callback
98*789Sahrens  * once to notify it of the current property value, and again each time
99*789Sahrens  * the property changes, until this callback is unregistered.
100*789Sahrens  *
101*789Sahrens  * Return 0 on success, errno if the prop is not an integer value.
102*789Sahrens  */
103*789Sahrens int
104*789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname,
105*789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
106*789Sahrens {
107*789Sahrens 	dsl_dir_t *dd;
108*789Sahrens 	uint64_t value;
109*789Sahrens 	dsl_prop_cb_record_t *cbr;
110*789Sahrens 	int err;
111*789Sahrens 
112*789Sahrens 	dd = ds->ds_dir;
113*789Sahrens 
114*789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
115*789Sahrens 
116*789Sahrens 	err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, propname,
117*789Sahrens 	    8, 1, &value, NULL);
118*789Sahrens 	if (err == ENOENT) {
119*789Sahrens 		err = 0;
120*789Sahrens 		value = DSL_PROP_VALUE_UNDEFINED;
121*789Sahrens 	}
122*789Sahrens 	if (err != 0) {
123*789Sahrens 		rw_exit(&dd->dd_pool->dp_config_rwlock);
124*789Sahrens 		return (err);
125*789Sahrens 	}
126*789Sahrens 
127*789Sahrens 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
128*789Sahrens 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
129*789Sahrens 	(void) strcpy((char *)cbr->cbr_propname, propname);
130*789Sahrens 	cbr->cbr_func = callback;
131*789Sahrens 	cbr->cbr_arg = cbarg;
132*789Sahrens 	mutex_enter(&dd->dd_lock);
133*789Sahrens 	list_insert_head(&dd->dd_prop_cbs, cbr);
134*789Sahrens 	mutex_exit(&dd->dd_lock);
135*789Sahrens 
136*789Sahrens 	cbr->cbr_func(cbr->cbr_arg, value);
137*789Sahrens 
138*789Sahrens 	(void) dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, cbr);
139*789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
140*789Sahrens 	/* Leave dataset open until this callback is unregistered */
141*789Sahrens 	return (0);
142*789Sahrens }
143*789Sahrens 
144*789Sahrens int
145*789Sahrens dsl_prop_get_ds(dsl_dir_t *dd, const char *propname,
146*789Sahrens     int intsz, int numints, void *buf, char *setpoint)
147*789Sahrens {
148*789Sahrens 	int err;
149*789Sahrens 
150*789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
151*789Sahrens 	err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object,
152*789Sahrens 	    propname, intsz, numints, buf, setpoint);
153*789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
154*789Sahrens 
155*789Sahrens 	return (err);
156*789Sahrens }
157*789Sahrens 
158*789Sahrens int
159*789Sahrens dsl_prop_get(const char *ddname, const char *propname,
160*789Sahrens     int intsz, int numints, void *buf, char *setpoint)
161*789Sahrens {
162*789Sahrens 	dsl_dir_t *dd;
163*789Sahrens 	const char *tail;
164*789Sahrens 	int err;
165*789Sahrens 
166*789Sahrens 	dd = dsl_dir_open(ddname, FTAG, &tail);
167*789Sahrens 	if (dd == NULL)
168*789Sahrens 		return (ENOENT);
169*789Sahrens 	if (tail && tail[0] != '@') {
170*789Sahrens 		dsl_dir_close(dd, FTAG);
171*789Sahrens 		return (ENOENT);
172*789Sahrens 	}
173*789Sahrens 
174*789Sahrens 	err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint);
175*789Sahrens 
176*789Sahrens 	dsl_dir_close(dd, FTAG);
177*789Sahrens 	return (err);
178*789Sahrens }
179*789Sahrens 
180*789Sahrens /*
181*789Sahrens  * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if
182*789Sahrens  * valuelen not big enough.
183*789Sahrens  */
184*789Sahrens int
185*789Sahrens dsl_prop_get_string(const char *ddname, const char *propname,
186*789Sahrens     char *value, int valuelen, char *setpoint)
187*789Sahrens {
188*789Sahrens 	return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint));
189*789Sahrens }
190*789Sahrens 
191*789Sahrens /*
192*789Sahrens  * Get the current property value.  It may have changed by the time this
193*789Sahrens  * function returns, so it is NOT safe to follow up with
194*789Sahrens  * dsl_prop_register() and assume that the value has not changed in
195*789Sahrens  * between.
196*789Sahrens  *
197*789Sahrens  * Return 0 on success, ENOENT if ddname is invalid.
198*789Sahrens  */
199*789Sahrens int
200*789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname,
201*789Sahrens     uint64_t *valuep, char *setpoint)
202*789Sahrens {
203*789Sahrens 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
204*789Sahrens }
205*789Sahrens 
206*789Sahrens int
207*789Sahrens dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname,
208*789Sahrens     uint64_t *valuep, char *setpoint)
209*789Sahrens {
210*789Sahrens 	return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint));
211*789Sahrens }
212*789Sahrens 
213*789Sahrens /*
214*789Sahrens  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
215*789Sahrens  * invalid, ENOMSG if no matching callback registered.
216*789Sahrens  */
217*789Sahrens int
218*789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
219*789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
220*789Sahrens {
221*789Sahrens 	dsl_dir_t *dd;
222*789Sahrens 	dsl_prop_cb_record_t *cbr;
223*789Sahrens 
224*789Sahrens 	dd = ds->ds_dir;
225*789Sahrens 
226*789Sahrens 	mutex_enter(&dd->dd_lock);
227*789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
228*789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
229*789Sahrens 		if (strcmp(cbr->cbr_propname, propname) == 0 &&
230*789Sahrens 		    cbr->cbr_func == callback &&
231*789Sahrens 		    cbr->cbr_arg == cbarg)
232*789Sahrens 			break;
233*789Sahrens 	}
234*789Sahrens 
235*789Sahrens 	if (cbr == NULL) {
236*789Sahrens 		mutex_exit(&dd->dd_lock);
237*789Sahrens 		return (ENOMSG);
238*789Sahrens 	}
239*789Sahrens 
240*789Sahrens 	list_remove(&dd->dd_prop_cbs, cbr);
241*789Sahrens 	mutex_exit(&dd->dd_lock);
242*789Sahrens 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
243*789Sahrens 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
244*789Sahrens 
245*789Sahrens 	/* Clean up from dsl_prop_register */
246*789Sahrens 	dsl_dir_close(dd, cbr);
247*789Sahrens 	return (0);
248*789Sahrens }
249*789Sahrens 
250*789Sahrens static void
251*789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
252*789Sahrens     const char *propname, uint64_t value, int first)
253*789Sahrens {
254*789Sahrens 	dsl_dir_t *dd;
255*789Sahrens 	dsl_prop_cb_record_t *cbr;
256*789Sahrens 	objset_t *mos = dp->dp_meta_objset;
257*789Sahrens 	int err;
258*789Sahrens 
259*789Sahrens 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
260*789Sahrens 	dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG);
261*789Sahrens 
262*789Sahrens 	if (!first) {
263*789Sahrens 		/*
264*789Sahrens 		 * If the prop is set here, then this change is not
265*789Sahrens 		 * being inherited here or below; stop the recursion.
266*789Sahrens 		 */
267*789Sahrens 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
268*789Sahrens 		    8, 1, &value);
269*789Sahrens 		if (err == 0) {
270*789Sahrens 			dsl_dir_close(dd, FTAG);
271*789Sahrens 			return;
272*789Sahrens 		}
273*789Sahrens 		ASSERT3U(err, ==, ENOENT);
274*789Sahrens 	}
275*789Sahrens 
276*789Sahrens 	mutex_enter(&dd->dd_lock);
277*789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
278*789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
279*789Sahrens 		if (strcmp(cbr->cbr_propname, propname) == 0) {
280*789Sahrens 			cbr->cbr_func(cbr->cbr_arg, value);
281*789Sahrens 		}
282*789Sahrens 	}
283*789Sahrens 	mutex_exit(&dd->dd_lock);
284*789Sahrens 
285*789Sahrens 	if (dd->dd_phys->dd_child_dir_zapobj) {
286*789Sahrens 		zap_cursor_t zc;
287*789Sahrens 		zap_attribute_t za;
288*789Sahrens 
289*789Sahrens 		for (zap_cursor_init(&zc, mos,
290*789Sahrens 		    dd->dd_phys->dd_child_dir_zapobj);
291*789Sahrens 		    zap_cursor_retrieve(&zc, &za) == 0;
292*789Sahrens 		    zap_cursor_advance(&zc)) {
293*789Sahrens 			/* XXX recursion could blow stack; esp. za! */
294*789Sahrens 			dsl_prop_changed_notify(dp, za.za_first_integer,
295*789Sahrens 			    propname, value, FALSE);
296*789Sahrens 		}
297*789Sahrens 	}
298*789Sahrens 	dsl_dir_close(dd, FTAG);
299*789Sahrens }
300*789Sahrens 
301*789Sahrens struct prop_set_arg {
302*789Sahrens 	const char *name;
303*789Sahrens 	int intsz;
304*789Sahrens 	int numints;
305*789Sahrens 	const void *buf;
306*789Sahrens };
307*789Sahrens 
308*789Sahrens static int
309*789Sahrens dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx)
310*789Sahrens {
311*789Sahrens 	struct prop_set_arg *psa = arg;
312*789Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
313*789Sahrens 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
314*789Sahrens 	uint64_t intval;
315*789Sahrens 	int err, isint;
316*789Sahrens 
317*789Sahrens 	rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER);
318*789Sahrens 
319*789Sahrens 	isint = (dodefault(psa->name, 8, 1, &intval) == 0);
320*789Sahrens 
321*789Sahrens 	if (psa->numints == 0) {
322*789Sahrens 		err = zap_remove(mos, zapobj, psa->name, tx);
323*789Sahrens 		if (err == ENOENT) /* that's fine. */
324*789Sahrens 			err = 0;
325*789Sahrens 		if (err == 0 && isint) {
326*789Sahrens 			err = dsl_prop_get_impl(dd->dd_pool,
327*789Sahrens 			    dd->dd_phys->dd_parent_obj, psa->name,
328*789Sahrens 			    8, 1, &intval, NULL);
329*789Sahrens 		}
330*789Sahrens 	} else {
331*789Sahrens 		err = zap_update(mos, zapobj, psa->name,
332*789Sahrens 		    psa->intsz, psa->numints, psa->buf, tx);
333*789Sahrens 		if (isint)
334*789Sahrens 			intval = *(uint64_t *)psa->buf;
335*789Sahrens 	}
336*789Sahrens 
337*789Sahrens 	if (err == 0 && isint) {
338*789Sahrens 		dsl_prop_changed_notify(dd->dd_pool,
339*789Sahrens 		    dd->dd_object, psa->name, intval, TRUE);
340*789Sahrens 	}
341*789Sahrens 	rw_exit(&dd->dd_pool->dp_config_rwlock);
342*789Sahrens 
343*789Sahrens 	return (err);
344*789Sahrens }
345*789Sahrens 
346*789Sahrens int
347*789Sahrens dsl_prop_set(const char *ddname, const char *propname,
348*789Sahrens     int intsz, int numints, const void *buf)
349*789Sahrens {
350*789Sahrens 	dsl_dir_t *dd;
351*789Sahrens 	int err;
352*789Sahrens 	struct prop_set_arg psa;
353*789Sahrens 
354*789Sahrens 	dd = dsl_dir_open(ddname, FTAG, NULL);
355*789Sahrens 	if (dd == NULL)
356*789Sahrens 		return (ENOENT);
357*789Sahrens 
358*789Sahrens 	psa.name = propname;
359*789Sahrens 	psa.intsz = intsz;
360*789Sahrens 	psa.numints = numints;
361*789Sahrens 	psa.buf = buf;
362*789Sahrens 	err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 0);
363*789Sahrens 
364*789Sahrens 	dsl_dir_close(dd, FTAG);
365*789Sahrens 
366*789Sahrens 	return (err);
367*789Sahrens }
368