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