4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2012 by Delphix. All rights reserved.
26 #include <sys/zfs_context.h>
27 #include <sys/dsl_userhold.h>
28 #include <sys/dsl_dataset.h>
29 #include <sys/dsl_destroy.h>
30 #include <sys/dsl_synctask.h>
31 #include <sys/dmu_tx.h>
32 #include <sys/zfs_onexit.h>
33 #include <sys/dsl_pool.h>
34 #include <sys/dsl_dir.h>
35 #include <sys/zfs_ioctl.h>
38 typedef struct dsl_dataset_user_hold_arg {
39 nvlist_t *dduha_holds;
40 nvlist_t *dduha_errlist;
42 } dsl_dataset_user_hold_arg_t;
45 * If you add new checks here, you may need to add additional checks to the
46 * "temporary" case in snapshot_check() in dmu_objset.c.
49 dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag,
50 boolean_t temphold, dmu_tx_t *tx)
52 dsl_pool_t *dp = dmu_tx_pool(tx);
53 objset_t *mos = dp->dp_meta_objset;
56 if (strlen(htag) > MAXNAMELEN)
58 /* Tempholds have a more restricted length */
59 if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
62 /* tags must be unique (if ds already exists) */
64 mutex_enter(&ds->ds_lock);
65 if (ds->ds_phys->ds_userrefs_obj != 0) {
67 error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj,
71 else if (error == ENOENT)
74 mutex_exit(&ds->ds_lock);
81 dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx)
83 dsl_dataset_user_hold_arg_t *dduha = arg;
84 dsl_pool_t *dp = dmu_tx_pool(tx);
88 if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
91 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL;
92 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
97 /* must be a snapshot */
98 if (strchr(nvpair_name(pair), '@') == NULL)
102 error = nvpair_value_string(pair, &htag);
104 error = dsl_dataset_hold(dp,
105 nvpair_name(pair), FTAG, &ds);
108 error = dsl_dataset_user_hold_check_one(ds, htag,
109 dduha->dduha_minor != 0, tx);
110 dsl_dataset_rele(ds, FTAG);
115 fnvlist_add_int32(dduha->dduha_errlist,
116 nvpair_name(pair), error);
123 dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag,
124 minor_t minor, uint64_t now, dmu_tx_t *tx)
126 dsl_pool_t *dp = ds->ds_dir->dd_pool;
127 objset_t *mos = dp->dp_meta_objset;
130 mutex_enter(&ds->ds_lock);
131 if (ds->ds_phys->ds_userrefs_obj == 0) {
133 * This is the first user hold for this dataset. Create
134 * the userrefs zap object.
136 dmu_buf_will_dirty(ds->ds_dbuf, tx);
137 zapobj = ds->ds_phys->ds_userrefs_obj =
138 zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx);
140 zapobj = ds->ds_phys->ds_userrefs_obj;
143 mutex_exit(&ds->ds_lock);
145 VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx));
148 VERIFY0(dsl_pool_user_hold(dp, ds->ds_object,
150 dsl_register_onexit_hold_cleanup(ds, htag, minor);
153 spa_history_log_internal_ds(ds, "hold", tx,
154 "tag=%s temp=%d refs=%llu",
155 htag, minor != 0, ds->ds_userrefs);
159 dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx)
161 dsl_dataset_user_hold_arg_t *dduha = arg;
162 dsl_pool_t *dp = dmu_tx_pool(tx);
164 uint64_t now = gethrestime_sec();
166 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL;
167 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
169 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
170 dsl_dataset_user_hold_sync_one(ds, fnvpair_value_string(pair),
171 dduha->dduha_minor, now, tx);
172 dsl_dataset_rele(ds, FTAG);
177 * holds is nvl of snapname -> holdname
178 * errlist will be filled in with snapname -> error
179 * if cleanup_minor is not 0, the holds will be temporary, cleaned up
180 * when the process exits.
182 * if any fails, all will fail.
185 dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist)
187 dsl_dataset_user_hold_arg_t dduha;
190 pair = nvlist_next_nvpair(holds, NULL);
194 dduha.dduha_holds = holds;
195 dduha.dduha_errlist = errlist;
196 dduha.dduha_minor = cleanup_minor;
198 return (dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
199 dsl_dataset_user_hold_sync, &dduha, fnvlist_num_pairs(holds)));
202 typedef struct dsl_dataset_user_release_arg {
203 nvlist_t *ddura_holds;
204 nvlist_t *ddura_todelete;
205 nvlist_t *ddura_errlist;
206 } dsl_dataset_user_release_arg_t;
209 dsl_dataset_user_release_check_one(dsl_dataset_t *ds,
210 nvlist_t *holds, boolean_t *todelete)
214 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
220 if (!dsl_dataset_is_snapshot(ds))
223 zapobj = ds->ds_phys->ds_userrefs_obj;
227 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
228 pair = nvlist_next_nvpair(holds, pair)) {
229 /* Make sure the hold exists */
231 error = zap_lookup(mos, zapobj, nvpair_name(pair), 8, 1, &tmp);
239 if (DS_IS_DEFER_DESTROY(ds) && ds->ds_phys->ds_num_children == 1 &&
240 ds->ds_userrefs == numholds) {
241 /* we need to destroy the snapshot as well */
243 if (dsl_dataset_long_held(ds))
251 dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx)
253 dsl_dataset_user_release_arg_t *ddura = arg;
254 dsl_pool_t *dp = dmu_tx_pool(tx);
258 if (!dmu_tx_is_syncing(tx))
261 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL;
262 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
263 const char *name = nvpair_name(pair);
268 error = nvpair_value_nvlist(pair, &holds);
272 error = dsl_dataset_hold(dp, name, FTAG, &ds);
275 error = dsl_dataset_user_release_check_one(ds,
277 if (error == 0 && deleteme) {
278 fnvlist_add_boolean(ddura->ddura_todelete,
281 dsl_dataset_rele(ds, FTAG);
284 if (ddura->ddura_errlist != NULL) {
285 fnvlist_add_int32(ddura->ddura_errlist,
295 dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds,
298 dsl_pool_t *dp = ds->ds_dir->dd_pool;
299 objset_t *mos = dp->dp_meta_objset;
304 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
305 pair = nvlist_next_nvpair(holds, pair)) {
307 error = dsl_pool_user_release(dp, ds->ds_object,
308 nvpair_name(pair), tx);
309 VERIFY(error == 0 || error == ENOENT);
310 zapobj = ds->ds_phys->ds_userrefs_obj;
311 VERIFY0(zap_remove(mos, zapobj, nvpair_name(pair), tx));
313 spa_history_log_internal_ds(ds, "release", tx,
314 "tag=%s refs=%lld", nvpair_name(pair),
315 (longlong_t)ds->ds_userrefs);
320 dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx)
322 dsl_dataset_user_release_arg_t *ddura = arg;
323 dsl_pool_t *dp = dmu_tx_pool(tx);
326 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL;
327 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
330 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
331 dsl_dataset_user_release_sync_one(ds,
332 fnvpair_value_nvlist(pair), tx);
333 if (nvlist_exists(ddura->ddura_todelete,
334 nvpair_name(pair))) {
335 ASSERT(ds->ds_userrefs == 0 &&
336 ds->ds_phys->ds_num_children == 1 &&
337 DS_IS_DEFER_DESTROY(ds));
338 dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
340 dsl_dataset_rele(ds, FTAG);
345 * holds is nvl of snapname -> { holdname, ... }
346 * errlist will be filled in with snapname -> error
348 * if any fails, all will fail.
351 dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist)
353 dsl_dataset_user_release_arg_t ddura;
357 pair = nvlist_next_nvpair(holds, NULL);
361 ddura.ddura_holds = holds;
362 ddura.ddura_errlist = errlist;
363 ddura.ddura_todelete = fnvlist_alloc();
365 error = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_release_check,
366 dsl_dataset_user_release_sync, &ddura, fnvlist_num_pairs(holds));
367 fnvlist_free(ddura.ddura_todelete);
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;
378 dsl_dataset_user_release_tmp_check(void *arg, dmu_tx_t *tx)
380 dsl_dataset_user_release_tmp_arg_t *ddurta = arg;
381 dsl_pool_t *dp = dmu_tx_pool(tx);
385 if (!dmu_tx_is_syncing(tx))
388 error = dsl_dataset_hold_obj(dp, ddurta->ddurta_dsobj, FTAG, &ds);
392 error = dsl_dataset_user_release_check_one(ds,
393 ddurta->ddurta_holds, &ddurta->ddurta_deleteme);
394 dsl_dataset_rele(ds, FTAG);
399 dsl_dataset_user_release_tmp_sync(void *arg, dmu_tx_t *tx)
401 dsl_dataset_user_release_tmp_arg_t *ddurta = arg;
402 dsl_pool_t *dp = dmu_tx_pool(tx);
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);
413 dsl_dataset_rele(ds, FTAG);
417 * Called at spa_load time to release a stale temporary user hold.
418 * Also called by the onexit code.
421 dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, const char *htag)
423 dsl_dataset_user_release_tmp_arg_t ddurta;
429 /* Make sure it is not mounted. */
430 dsl_pool_config_enter(dp, FTAG);
431 error = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
433 char name[MAXNAMELEN];
434 dsl_dataset_name(ds, name);
435 dsl_dataset_rele(ds, FTAG);
436 dsl_pool_config_exit(dp, FTAG);
437 zfs_unmount_snap(name);
439 dsl_pool_config_exit(dp, FTAG);
443 ddurta.ddurta_dsobj = dsobj;
444 ddurta.ddurta_holds = fnvlist_alloc();
445 fnvlist_add_boolean(ddurta.ddurta_holds, htag);
447 (void) dsl_sync_task(spa_name(dp->dp_spa),
448 dsl_dataset_user_release_tmp_check,
449 dsl_dataset_user_release_tmp_sync, &ddurta, 1);
450 fnvlist_free(ddurta.ddurta_holds);
453 typedef struct zfs_hold_cleanup_arg {
454 char zhca_spaname[MAXNAMELEN];
455 uint64_t zhca_spa_load_guid;
457 char zhca_htag[MAXNAMELEN];
458 } zfs_hold_cleanup_arg_t;
461 dsl_dataset_user_release_onexit(void *arg)
463 zfs_hold_cleanup_arg_t *ca = arg;
467 error = spa_open(ca->zhca_spaname, &spa, FTAG);
469 zfs_dbgmsg("couldn't release hold on pool=%s ds=%llu tag=%s "
470 "because pool is no longer loaded",
471 ca->zhca_spaname, ca->zhca_dsobj, ca->zhca_htag);
474 if (spa_load_guid(spa) != ca->zhca_spa_load_guid) {
475 zfs_dbgmsg("couldn't release hold on pool=%s ds=%llu tag=%s "
476 "because pool is no longer loaded (guid doesn't match)",
477 ca->zhca_spaname, ca->zhca_dsobj, ca->zhca_htag);
478 spa_close(spa, FTAG);
482 dsl_dataset_user_release_tmp(spa_get_dsl(spa),
483 ca->zhca_dsobj, ca->zhca_htag);
484 kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
485 spa_close(spa, FTAG);
489 dsl_register_onexit_hold_cleanup(dsl_dataset_t *ds, const char *htag,
492 zfs_hold_cleanup_arg_t *ca = kmem_alloc(sizeof (*ca), KM_PUSHPAGE);
493 spa_t *spa = dsl_dataset_get_spa(ds);
494 (void) strlcpy(ca->zhca_spaname, spa_name(spa),
495 sizeof (ca->zhca_spaname));
496 ca->zhca_spa_load_guid = spa_load_guid(spa);
497 ca->zhca_dsobj = ds->ds_object;
498 (void) strlcpy(ca->zhca_htag, htag, sizeof (ca->zhca_htag));
499 VERIFY0(zfs_onexit_add_cb(minor,
500 dsl_dataset_user_release_onexit, ca, NULL));
504 dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl)
510 err = dsl_pool_hold(dsname, FTAG, &dp);
513 err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
515 dsl_pool_rele(dp, FTAG);
519 if (ds->ds_phys->ds_userrefs_obj != 0) {
523 za = kmem_alloc(sizeof (zap_attribute_t), KM_PUSHPAGE);
524 for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
525 ds->ds_phys->ds_userrefs_obj);
526 zap_cursor_retrieve(&zc, za) == 0;
527 zap_cursor_advance(&zc)) {
528 fnvlist_add_uint64(nvl, za->za_name,
529 za->za_first_integer);
531 zap_cursor_fini(&zc);
532 kmem_free(za, sizeof (zap_attribute_t));
534 dsl_dataset_rele(ds, FTAG);
535 dsl_pool_rele(dp, FTAG);