Print this page
Optimize creation and removal of temporary "user holds" placed on
snapshots by a zfs send, by ensuring all the required holds and
releases are done in a single dsl_sync_task.
Creation now collates the required holds during a dry run and
then uses a single lzc_hold call via zfs_hold_apply instead of
processing each snapshot in turn.
Defered (on exit) cleanup by the kernel is also now done in
dsl_sync_task by reusing dsl_dataset_user_release.
On a test with 11 volumes in a tree each with 8 snapshots on a
single HDD zpool this reduces the time required to perform a full
send from 20 seconds to under 0.8 seconds.
For reference eliminating the hold entirely reduces this 0.15
seconds.
While I'm here:-
* Remove some unused structures
* Fix nvlist_t leak in zfs_release_one
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/fs/zfs/dsl_userhold.c
+++ new/usr/src/uts/common/fs/zfs/dsl_userhold.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 23 * Copyright (c) 2013 by Delphix. All rights reserved.
24 24 */
25 25
26 26 #include <sys/zfs_context.h>
27 27 #include <sys/dsl_userhold.h>
28 28 #include <sys/dsl_dataset.h>
29 29 #include <sys/dsl_destroy.h>
30 30 #include <sys/dsl_synctask.h>
31 31 #include <sys/dmu_tx.h>
32 32 #include <sys/zfs_onexit.h>
33 33 #include <sys/dsl_pool.h>
34 34 #include <sys/dsl_dir.h>
35 35 #include <sys/zfs_ioctl.h>
36 36 #include <sys/zap.h>
37 37
38 38 typedef struct dsl_dataset_user_hold_arg {
39 39 nvlist_t *dduha_holds;
40 40 nvlist_t *dduha_errlist;
41 41 minor_t dduha_minor;
42 42 } dsl_dataset_user_hold_arg_t;
43 43
44 44 /*
45 45 * If you add new checks here, you may need to add additional checks to the
46 46 * "temporary" case in snapshot_check() in dmu_objset.c.
47 47 */
48 48 int
49 49 dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag,
50 50 boolean_t temphold, dmu_tx_t *tx)
51 51 {
52 52 dsl_pool_t *dp = dmu_tx_pool(tx);
53 53 objset_t *mos = dp->dp_meta_objset;
54 54 int error = 0;
55 55
56 56 if (strlen(htag) > MAXNAMELEN)
57 57 return (E2BIG);
58 58 /* Tempholds have a more restricted length */
59 59 if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
60 60 return (E2BIG);
61 61
62 62 /* tags must be unique (if ds already exists) */
63 63 if (ds != NULL) {
64 64 mutex_enter(&ds->ds_lock);
65 65 if (ds->ds_phys->ds_userrefs_obj != 0) {
66 66 uint64_t value;
67 67 error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj,
68 68 htag, 8, 1, &value);
69 69 if (error == 0)
70 70 error = SET_ERROR(EEXIST);
71 71 else if (error == ENOENT)
72 72 error = 0;
73 73 }
74 74 mutex_exit(&ds->ds_lock);
75 75 }
76 76
77 77 return (error);
78 78 }
79 79
80 80 static int
81 81 dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx)
82 82 {
83 83 dsl_dataset_user_hold_arg_t *dduha = arg;
84 84 dsl_pool_t *dp = dmu_tx_pool(tx);
85 85 nvpair_t *pair;
86 86 int rv = 0;
87 87
88 88 if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
89 89 return (SET_ERROR(ENOTSUP));
90 90
91 91 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL;
92 92 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
93 93 int error = 0;
94 94 dsl_dataset_t *ds;
95 95 char *htag;
96 96
97 97 /* must be a snapshot */
98 98 if (strchr(nvpair_name(pair), '@') == NULL)
99 99 error = SET_ERROR(EINVAL);
100 100
101 101 if (error == 0)
102 102 error = nvpair_value_string(pair, &htag);
103 103 if (error == 0) {
104 104 error = dsl_dataset_hold(dp,
105 105 nvpair_name(pair), FTAG, &ds);
106 106 }
107 107 if (error == 0) {
108 108 error = dsl_dataset_user_hold_check_one(ds, htag,
109 109 dduha->dduha_minor != 0, tx);
110 110 dsl_dataset_rele(ds, FTAG);
111 111 }
112 112
113 113 if (error != 0) {
114 114 rv = error;
115 115 fnvlist_add_int32(dduha->dduha_errlist,
116 116 nvpair_name(pair), error);
117 117 }
118 118 }
119 119 return (rv);
120 120 }
121 121
122 122 void
123 123 dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag,
124 124 minor_t minor, uint64_t now, dmu_tx_t *tx)
125 125 {
126 126 dsl_pool_t *dp = ds->ds_dir->dd_pool;
127 127 objset_t *mos = dp->dp_meta_objset;
128 128 uint64_t zapobj;
129 129
130 130 mutex_enter(&ds->ds_lock);
131 131 if (ds->ds_phys->ds_userrefs_obj == 0) {
132 132 /*
133 133 * This is the first user hold for this dataset. Create
134 134 * the userrefs zap object.
135 135 */
136 136 dmu_buf_will_dirty(ds->ds_dbuf, tx);
137 137 zapobj = ds->ds_phys->ds_userrefs_obj =
138 138 zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx);
139 139 } else {
↓ open down ↓ |
139 lines elided |
↑ open up ↑ |
140 140 zapobj = ds->ds_phys->ds_userrefs_obj;
141 141 }
142 142 ds->ds_userrefs++;
143 143 mutex_exit(&ds->ds_lock);
144 144
145 145 VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx));
146 146
147 147 if (minor != 0) {
148 148 VERIFY0(dsl_pool_user_hold(dp, ds->ds_object,
149 149 htag, now, tx));
150 - dsl_register_onexit_hold_cleanup(ds, htag, minor);
151 150 }
152 151
153 152 spa_history_log_internal_ds(ds, "hold", tx,
154 153 "tag=%s temp=%d refs=%llu",
155 154 htag, minor != 0, ds->ds_userrefs);
156 155 }
157 156
158 157 static void
159 158 dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx)
160 159 {
161 160 dsl_dataset_user_hold_arg_t *dduha = arg;
162 161 dsl_pool_t *dp = dmu_tx_pool(tx);
163 162 nvpair_t *pair;
164 163 uint64_t now = gethrestime_sec();
165 164
166 165 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL;
167 166 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
168 167 dsl_dataset_t *ds;
168 +
169 169 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
170 170 dsl_dataset_user_hold_sync_one(ds, fnvpair_value_string(pair),
171 171 dduha->dduha_minor, now, tx);
172 172 dsl_dataset_rele(ds, FTAG);
173 173 }
174 174 }
175 175
176 176 /*
177 177 * holds is nvl of snapname -> holdname
178 178 * errlist will be filled in with snapname -> error
179 179 * if cleanup_minor is not 0, the holds will be temporary, cleaned up
180 180 * when the process exits.
181 181 *
182 182 * if any fails, all will fail.
183 183 */
184 184 int
185 185 dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist)
186 186 {
187 187 dsl_dataset_user_hold_arg_t dduha;
188 188 nvpair_t *pair;
189 + int ret;
189 190
190 191 pair = nvlist_next_nvpair(holds, NULL);
191 192 if (pair == NULL)
192 193 return (0);
193 194
194 195 dduha.dduha_holds = holds;
195 196 dduha.dduha_errlist = errlist;
196 197 dduha.dduha_minor = cleanup_minor;
197 198
198 - return (dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
199 - dsl_dataset_user_hold_sync, &dduha, fnvlist_num_pairs(holds)));
199 + ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
200 + dsl_dataset_user_hold_sync, &dduha, fnvlist_num_pairs(holds));
201 + if (ret == 0)
202 + dsl_register_onexit_hold_cleanup(holds, cleanup_minor);
203 +
204 + return (ret);
200 205 }
201 206
202 207 typedef struct dsl_dataset_user_release_arg {
203 208 nvlist_t *ddura_holds;
204 209 nvlist_t *ddura_todelete;
205 210 nvlist_t *ddura_errlist;
206 211 } dsl_dataset_user_release_arg_t;
207 212
208 213 static int
209 214 dsl_dataset_user_release_check_one(dsl_dataset_t *ds,
210 215 nvlist_t *holds, boolean_t *todelete)
211 216 {
212 217 uint64_t zapobj;
213 218 nvpair_t *pair;
214 219 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
215 220 int error;
216 221 int numholds = 0;
217 222
218 223 *todelete = B_FALSE;
219 224
220 225 if (!dsl_dataset_is_snapshot(ds))
221 226 return (SET_ERROR(EINVAL));
222 227
223 228 zapobj = ds->ds_phys->ds_userrefs_obj;
224 229 if (zapobj == 0)
225 230 return (SET_ERROR(ESRCH));
226 231
227 232 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
228 233 pair = nvlist_next_nvpair(holds, pair)) {
229 234 /* Make sure the hold exists */
230 235 uint64_t tmp;
231 236 error = zap_lookup(mos, zapobj, nvpair_name(pair), 8, 1, &tmp);
232 237 if (error == ENOENT)
233 238 error = SET_ERROR(ESRCH);
234 239 if (error != 0)
235 240 return (error);
236 241 numholds++;
237 242 }
238 243
239 244 if (DS_IS_DEFER_DESTROY(ds) && ds->ds_phys->ds_num_children == 1 &&
240 245 ds->ds_userrefs == numholds) {
241 246 /* we need to destroy the snapshot as well */
242 247
243 248 if (dsl_dataset_long_held(ds))
244 249 return (SET_ERROR(EBUSY));
245 250 *todelete = B_TRUE;
246 251 }
247 252 return (0);
248 253 }
249 254
250 255 static int
251 256 dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx)
252 257 {
253 258 dsl_dataset_user_release_arg_t *ddura = arg;
254 259 dsl_pool_t *dp = dmu_tx_pool(tx);
255 260 nvpair_t *pair;
256 261 int rv = 0;
257 262
258 263 if (!dmu_tx_is_syncing(tx))
259 264 return (0);
260 265
261 266 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL;
262 267 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
263 268 const char *name = nvpair_name(pair);
264 269 int error;
265 270 dsl_dataset_t *ds;
266 271 nvlist_t *holds;
267 272
268 273 error = nvpair_value_nvlist(pair, &holds);
269 274 if (error != 0)
270 275 return (SET_ERROR(EINVAL));
271 276
272 277 error = dsl_dataset_hold(dp, name, FTAG, &ds);
273 278 if (error == 0) {
274 279 boolean_t deleteme;
275 280 error = dsl_dataset_user_release_check_one(ds,
276 281 holds, &deleteme);
277 282 if (error == 0 && deleteme) {
278 283 fnvlist_add_boolean(ddura->ddura_todelete,
279 284 name);
280 285 }
281 286 dsl_dataset_rele(ds, FTAG);
282 287 }
283 288 if (error != 0) {
284 289 if (ddura->ddura_errlist != NULL) {
285 290 fnvlist_add_int32(ddura->ddura_errlist,
286 291 name, error);
287 292 }
288 293 rv = error;
289 294 }
290 295 }
291 296 return (rv);
292 297 }
293 298
294 299 static void
295 300 dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds,
296 301 dmu_tx_t *tx)
297 302 {
298 303 dsl_pool_t *dp = ds->ds_dir->dd_pool;
299 304 objset_t *mos = dp->dp_meta_objset;
300 305 uint64_t zapobj;
301 306 int error;
302 307 nvpair_t *pair;
303 308
304 309 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
305 310 pair = nvlist_next_nvpair(holds, pair)) {
306 311 ds->ds_userrefs--;
307 312 error = dsl_pool_user_release(dp, ds->ds_object,
308 313 nvpair_name(pair), tx);
309 314 VERIFY(error == 0 || error == ENOENT);
310 315 zapobj = ds->ds_phys->ds_userrefs_obj;
311 316 VERIFY0(zap_remove(mos, zapobj, nvpair_name(pair), tx));
312 317
313 318 spa_history_log_internal_ds(ds, "release", tx,
314 319 "tag=%s refs=%lld", nvpair_name(pair),
315 320 (longlong_t)ds->ds_userrefs);
316 321 }
317 322 }
318 323
319 324 static void
320 325 dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx)
321 326 {
322 327 dsl_dataset_user_release_arg_t *ddura = arg;
323 328 dsl_pool_t *dp = dmu_tx_pool(tx);
324 329 nvpair_t *pair;
325 330
326 331 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL;
327 332 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
328 333 dsl_dataset_t *ds;
329 334
330 335 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
331 336 dsl_dataset_user_release_sync_one(ds,
332 337 fnvpair_value_nvlist(pair), tx);
333 338 if (nvlist_exists(ddura->ddura_todelete,
334 339 nvpair_name(pair))) {
335 340 ASSERT(ds->ds_userrefs == 0 &&
336 341 ds->ds_phys->ds_num_children == 1 &&
337 342 DS_IS_DEFER_DESTROY(ds));
338 343 dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
339 344 }
340 345 dsl_dataset_rele(ds, FTAG);
341 346 }
342 347 }
343 348
↓ open down ↓ |
134 lines elided |
↑ open up ↑ |
344 349 /*
345 350 * holds is nvl of snapname -> { holdname, ... }
346 351 * errlist will be filled in with snapname -> error
347 352 *
348 353 * if any fails, all will fail.
349 354 */
350 355 int
351 356 dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist)
352 357 {
353 358 dsl_dataset_user_release_arg_t ddura;
354 - nvpair_t *pair;
359 + nvpair_t *pair, *pair2;
355 360 int error;
356 361
357 362 pair = nvlist_next_nvpair(holds, NULL);
358 363 if (pair == NULL)
359 364 return (0);
360 365
366 +#ifdef _KERNEL
367 + /*
368 + * The release may cause the snapshot to be destroyed; make sure it
369 + * is not mounted.
370 + */
371 + for (pair2 = pair; pair2 != NULL;
372 + pair2 = nvlist_next_nvpair(holds, pair2)) {
373 + zfs_unmount_snap(nvpair_name(pair2));
374 + }
375 +#endif
376 +
361 377 ddura.ddura_holds = holds;
362 378 ddura.ddura_errlist = errlist;
363 379 ddura.ddura_todelete = fnvlist_alloc();
364 380
365 381 error = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_release_check,
366 382 dsl_dataset_user_release_sync, &ddura, fnvlist_num_pairs(holds));
367 383 fnvlist_free(ddura.ddura_todelete);
368 384 return (error);
369 385 }
370 386
371 -typedef struct dsl_dataset_user_release_tmp_arg {
372 - uint64_t ddurta_dsobj;
373 - nvlist_t *ddurta_holds;
374 - boolean_t ddurta_deleteme;
375 -} dsl_dataset_user_release_tmp_arg_t;
376 -
377 -static int
378 -dsl_dataset_user_release_tmp_check(void *arg, dmu_tx_t *tx)
379 -{
380 - dsl_dataset_user_release_tmp_arg_t *ddurta = arg;
381 - dsl_pool_t *dp = dmu_tx_pool(tx);
382 - dsl_dataset_t *ds;
383 - int error;
384 -
385 - if (!dmu_tx_is_syncing(tx))
386 - return (0);
387 -
388 - error = dsl_dataset_hold_obj(dp, ddurta->ddurta_dsobj, FTAG, &ds);
389 - if (error)
390 - return (error);
391 -
392 - error = dsl_dataset_user_release_check_one(ds,
393 - ddurta->ddurta_holds, &ddurta->ddurta_deleteme);
394 - dsl_dataset_rele(ds, FTAG);
395 - return (error);
396 -}
397 -
398 387 static void
399 -dsl_dataset_user_release_tmp_sync(void *arg, dmu_tx_t *tx)
388 +dsl_dataset_user_release_onexit(void *arg)
400 389 {
401 - dsl_dataset_user_release_tmp_arg_t *ddurta = arg;
402 - dsl_pool_t *dp = dmu_tx_pool(tx);
403 - dsl_dataset_t *ds;
390 + nvlist_t *holds = arg;
404 391
405 - VERIFY0(dsl_dataset_hold_obj(dp, ddurta->ddurta_dsobj, FTAG, &ds));
406 - dsl_dataset_user_release_sync_one(ds, ddurta->ddurta_holds, tx);
407 - if (ddurta->ddurta_deleteme) {
408 - ASSERT(ds->ds_userrefs == 0 &&
409 - ds->ds_phys->ds_num_children == 1 &&
410 - DS_IS_DEFER_DESTROY(ds));
411 - dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
412 - }
413 - dsl_dataset_rele(ds, FTAG);
392 + (void) dsl_dataset_user_release(holds, NULL);
393 + fnvlist_free(holds);
414 394 }
415 395
416 -/*
417 - * Called at spa_load time to release a stale temporary user hold.
418 - * Also called by the onexit code.
419 - */
420 396 void
421 -dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, const char *htag)
397 +dsl_register_onexit_hold_cleanup(nvlist_t *holds, minor_t minor)
422 398 {
423 - dsl_dataset_user_release_tmp_arg_t ddurta;
424 - dsl_dataset_t *ds;
425 - int error;
426 -
427 -#ifdef _KERNEL
428 - /* Make sure it is not mounted. */
429 - dsl_pool_config_enter(dp, FTAG);
430 - error = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
431 - if (error == 0) {
432 - char name[MAXNAMELEN];
433 - dsl_dataset_name(ds, name);
434 - dsl_dataset_rele(ds, FTAG);
435 - dsl_pool_config_exit(dp, FTAG);
436 - zfs_unmount_snap(name);
437 - } else {
438 - dsl_pool_config_exit(dp, FTAG);
439 - }
440 -#endif
441 -
442 - ddurta.ddurta_dsobj = dsobj;
443 - ddurta.ddurta_holds = fnvlist_alloc();
444 - fnvlist_add_boolean(ddurta.ddurta_holds, htag);
445 -
446 - (void) dsl_sync_task(spa_name(dp->dp_spa),
447 - dsl_dataset_user_release_tmp_check,
448 - dsl_dataset_user_release_tmp_sync, &ddurta, 1);
449 - fnvlist_free(ddurta.ddurta_holds);
450 -}
451 -
452 -typedef struct zfs_hold_cleanup_arg {
453 - char zhca_spaname[MAXNAMELEN];
454 - uint64_t zhca_spa_load_guid;
455 - uint64_t zhca_dsobj;
456 - char zhca_htag[MAXNAMELEN];
457 -} zfs_hold_cleanup_arg_t;
458 -
459 -static void
460 -dsl_dataset_user_release_onexit(void *arg)
461 -{
462 - zfs_hold_cleanup_arg_t *ca = arg;
463 - spa_t *spa;
464 - int error;
399 + nvlist_t *ca;
400 + nvpair_t *pair;
401 + char *htag;
465 402
466 - error = spa_open(ca->zhca_spaname, &spa, FTAG);
467 - if (error != 0) {
468 - zfs_dbgmsg("couldn't release hold on pool=%s ds=%llu tag=%s "
469 - "because pool is no longer loaded",
470 - ca->zhca_spaname, ca->zhca_dsobj, ca->zhca_htag);
471 - return;
472 - }
473 - if (spa_load_guid(spa) != ca->zhca_spa_load_guid) {
474 - zfs_dbgmsg("couldn't release hold on pool=%s ds=%llu tag=%s "
475 - "because pool is no longer loaded (guid doesn't match)",
476 - ca->zhca_spaname, ca->zhca_dsobj, ca->zhca_htag);
477 - spa_close(spa, FTAG);
478 - return;
479 - }
480 -
481 - dsl_dataset_user_release_tmp(spa_get_dsl(spa),
482 - ca->zhca_dsobj, ca->zhca_htag);
483 - kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
484 - spa_close(spa, FTAG);
485 -}
403 + ca = fnvlist_alloc();
404 + /*
405 + * Convert from hold format: nvl of snapname -> holdname
406 + * to release format: nvl of snapname -> { holdname, ... }
407 + */
408 + for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
409 + pair = nvlist_next_nvpair(holds, pair)) {
410 + if (nvpair_value_string(pair, &htag) == 0) {
411 + nvlist_t *tags;
486 412
487 -void
488 -dsl_register_onexit_hold_cleanup(dsl_dataset_t *ds, const char *htag,
489 - minor_t minor)
490 -{
491 - zfs_hold_cleanup_arg_t *ca = kmem_alloc(sizeof (*ca), KM_SLEEP);
492 - spa_t *spa = dsl_dataset_get_spa(ds);
493 - (void) strlcpy(ca->zhca_spaname, spa_name(spa),
494 - sizeof (ca->zhca_spaname));
495 - ca->zhca_spa_load_guid = spa_load_guid(spa);
496 - ca->zhca_dsobj = ds->ds_object;
497 - (void) strlcpy(ca->zhca_htag, htag, sizeof (ca->zhca_htag));
413 + tags = fnvlist_alloc();
414 + fnvlist_add_boolean(tags, htag);
415 + fnvlist_add_nvlist(ca, nvpair_name(pair), tags);
416 + fnvlist_free(tags);
417 + }
418 + }
498 419 VERIFY0(zfs_onexit_add_cb(minor,
499 420 dsl_dataset_user_release_onexit, ca, NULL));
500 421 }
501 422
502 423 int
503 424 dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl)
504 425 {
505 426 dsl_pool_t *dp;
506 427 dsl_dataset_t *ds;
507 428 int err;
508 429
509 430 err = dsl_pool_hold(dsname, FTAG, &dp);
510 431 if (err != 0)
511 432 return (err);
512 433 err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
513 434 if (err != 0) {
514 435 dsl_pool_rele(dp, FTAG);
515 436 return (err);
516 437 }
517 438
518 439 if (ds->ds_phys->ds_userrefs_obj != 0) {
519 440 zap_attribute_t *za;
520 441 zap_cursor_t zc;
521 442
522 443 za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
523 444 for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
524 445 ds->ds_phys->ds_userrefs_obj);
525 446 zap_cursor_retrieve(&zc, za) == 0;
526 447 zap_cursor_advance(&zc)) {
527 448 fnvlist_add_uint64(nvl, za->za_name,
528 449 za->za_first_integer);
529 450 }
530 451 zap_cursor_fini(&zc);
531 452 kmem_free(za, sizeof (zap_attribute_t));
532 453 }
533 454 dsl_dataset_rele(ds, FTAG);
534 455 dsl_pool_rele(dp, FTAG);
535 456 return (0);
536 457 }
↓ open down ↓ |
29 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX