6833815 scheduled snapshots deleted per snapshot policy can lead to replication failures
--- a/usr/src/cmd/zfs/zfs_main.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/cmd/zfs/zfs_main.c Wed Aug 19 11:15:14 2009 -0600
@@ -2681,15 +2681,19 @@
int i;
const char *tag;
boolean_t recursive = B_FALSE;
+ boolean_t temphold = B_FALSE;
+ const char *opts = holding ? "rt" : "r";
int c;
- int (*func)(zfs_handle_t *, const char *, const char *, boolean_t);
/* check options */
- while ((c = getopt(argc, argv, "r")) != -1) {
+ while ((c = getopt(argc, argv, opts)) != -1) {
switch (c) {
case 'r':
recursive = B_TRUE;
break;
+ case 't':
+ temphold = B_TRUE;
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -2708,16 +2712,10 @@
--argc;
++argv;
- if (holding) {
- if (tag[0] == '.') {
- /* tags starting with '.' are reserved for libzfs */
- (void) fprintf(stderr,
- gettext("tag may not start with '.'\n"));
- usage(B_FALSE);
- }
- func = zfs_hold;
- } else {
- func = zfs_release;
+ if (holding && tag[0] == '.') {
+ /* tags starting with '.' are reserved for libzfs */
+ (void) fprintf(stderr, gettext("tag may not start with '.'\n"));
+ usage(B_FALSE);
}
for (i = 0; i < argc; ++i) {
@@ -2742,8 +2740,14 @@
++errors;
continue;
}
- if (func(zhp, delim+1, tag, recursive) != 0)
- ++errors;
+ if (holding) {
+ if (zfs_hold(zhp, delim+1, tag, recursive,
+ temphold) != 0)
+ ++errors;
+ } else {
+ if (zfs_release(zhp, delim+1, tag, recursive) != 0)
+ ++errors;
+ }
zfs_close(zhp);
}
@@ -2751,9 +2755,10 @@
}
/*
- * zfs hold [-r] <tag> <snap> ...
+ * zfs hold [-r] [-t] <tag> <snap> ...
*
* -r Recursively hold
+ * -t Temporary hold (hidden option)
*
* Apply a user-hold with the given tag to the list of snapshots.
*/
--- a/usr/src/lib/libzfs/common/libzfs.h Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h Wed Aug 19 11:15:14 2009 -0600
@@ -119,6 +119,7 @@
EZFS_UNPLAYED_LOGS, /* log device has unplayed logs */
EZFS_REFTAG_RELE, /* snapshot release: tag not found */
EZFS_REFTAG_HOLD, /* snapshot hold: tag already exists */
+ EZFS_TAGTOOLONG, /* snapshot hold/rele: tag too long */
EZFS_UNKNOWN
};
@@ -450,6 +451,7 @@
extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
+extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
/*
* Functions to create and destroy datasets.
@@ -466,8 +468,13 @@
extern int zfs_send(zfs_handle_t *, const char *, const char *,
boolean_t, boolean_t, boolean_t, boolean_t, int);
extern int zfs_promote(zfs_handle_t *);
-extern int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t);
+extern int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t,
+ boolean_t);
+extern int zfs_hold_range(zfs_handle_t *, const char *, const char *,
+ const char *, boolean_t);
extern int zfs_release(zfs_handle_t *, const char *, const char *, boolean_t);
+extern int zfs_release_range(zfs_handle_t *, const char *, const char *,
+ const char *);
typedef int (*zfs_userspace_cb_t)(void *arg, const char *domain,
uid_t rid, uint64_t space);
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c Wed Aug 19 11:15:14 2009 -0600
@@ -4034,15 +4034,18 @@
int
zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
- boolean_t recursive)
+ boolean_t recursive, boolean_t temphold)
{
zfs_cmd_t zc = { 0 };
libzfs_handle_t *hdl = zhp->zfs_hdl;
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- (void) strlcpy(zc.zc_string, tag, sizeof (zc.zc_string));
+ if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
+ >= sizeof (zc.zc_string))
+ return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
zc.zc_cookie = recursive;
+ zc.zc_temphold = temphold;
if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) {
char errbuf[ZFS_MAXNAMELEN+32];
@@ -4070,6 +4073,86 @@
return (0);
}
+struct hold_range_arg {
+ zfs_handle_t *origin;
+ const char *fromsnap;
+ const char *tosnap;
+ char lastsnapheld[ZFS_MAXNAMELEN];
+ const char *tag;
+ boolean_t temphold;
+ boolean_t seento;
+ boolean_t seenfrom;
+ boolean_t holding;
+};
+
+static int
+zfs_hold_range_one(zfs_handle_t *zhp, void *arg)
+{
+ struct hold_range_arg *hra = arg;
+ const char *thissnap;
+ int error;
+
+ thissnap = strchr(zfs_get_name(zhp), '@') + 1;
+
+ if (hra->fromsnap && !hra->seenfrom &&
+ strcmp(hra->fromsnap, thissnap) == 0)
+ hra->seenfrom = B_TRUE;
+
+ /* snap is older or newer than the desired range, ignore it */
+ if (hra->seento || !hra->seenfrom) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ if (hra->holding) {
+ error = zfs_hold(hra->origin, thissnap, hra->tag, B_FALSE,
+ hra->temphold);
+ if (error == 0) {
+ (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp),
+ sizeof (hra->lastsnapheld));
+ }
+ } else {
+ error = zfs_release(hra->origin, thissnap, hra->tag, B_FALSE);
+ }
+
+ if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0)
+ hra->seento = B_TRUE;
+
+ zfs_close(zhp);
+ return (error);
+}
+
+/*
+ * Add a user hold on the set of snapshots starting with fromsnap up to
+ * and including tosnap. If we're unable to to acquire a particular hold,
+ * undo any holds up to that point.
+ */
+int
+zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
+ const char *tag, boolean_t temphold)
+{
+ struct hold_range_arg arg = { 0 };
+ int error;
+
+ arg.origin = zhp;
+ arg.fromsnap = fromsnap;
+ arg.tosnap = tosnap;
+ arg.tag = tag;
+ arg.temphold = temphold;
+ arg.holding = B_TRUE;
+
+ error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg);
+
+ /*
+ * Make sure we either hold the entire range or none.
+ */
+ if (error && arg.lastsnapheld[0] != '\0') {
+ (void) zfs_release_range(zhp, fromsnap,
+ (const char *)arg.lastsnapheld, tag);
+ }
+ return (error);
+}
+
int
zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
boolean_t recursive)
@@ -4079,7 +4162,9 @@
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- (void) strlcpy(zc.zc_string, tag, sizeof (zc.zc_string));
+ if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
+ >= sizeof (zc.zc_string))
+ return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
zc.zc_cookie = recursive;
if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) {
@@ -4107,3 +4192,21 @@
return (0);
}
+
+/*
+ * Release a user hold from the set of snapshots starting with fromsnap
+ * up to and including tosnap.
+ */
+int
+zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
+ const char *tag)
+{
+ struct hold_range_arg arg = { 0 };
+
+ arg.origin = zhp;
+ arg.fromsnap = fromsnap;
+ arg.tosnap = tosnap;
+ arg.tag = tag;
+
+ return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg));
+}
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c Wed Aug 19 11:15:14 2009 -0600
@@ -415,7 +415,7 @@
return (0);
}
-static int
+int
zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
{
int ret = 0;
@@ -724,6 +724,8 @@
int err;
nvlist_t *fss = NULL;
avl_tree_t *fsavl = NULL;
+ char holdtag[128];
+ static uint64_t holdseq;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot send '%s'"), zhp->zfs_name);
@@ -742,6 +744,12 @@
assert(fromsnap || doall);
+ (void) snprintf(holdtag, sizeof (holdtag), ".send-%d-%llu",
+ getpid(), (u_longlong_t)holdseq);
+ ++holdseq;
+ err = zfs_hold_range(zhp, fromsnap, tosnap, holdtag, B_TRUE);
+ if (err)
+ return (err);
if (replicate) {
nvlist_t *hdrnv;
@@ -754,8 +762,11 @@
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
fromsnap, tosnap, &fss, &fsavl);
- if (err)
+ if (err) {
+ (void) zfs_release_range(zhp, fromsnap, tosnap,
+ holdtag);
return (err);
+ }
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
err = nvlist_pack(hdrnv, &packbuf, &buflen,
NV_ENCODE_XDR, 0);
@@ -763,6 +774,8 @@
if (err) {
fsavl_destroy(fsavl);
nvlist_free(fss);
+ (void) zfs_release_range(zhp, fromsnap, tosnap,
+ holdtag);
return (zfs_standard_error(zhp->zfs_hdl,
err, errbuf));
}
@@ -788,6 +801,8 @@
if (err == -1) {
fsavl_destroy(fsavl);
nvlist_free(fss);
+ (void) zfs_release_range(zhp, fromsnap, tosnap,
+ holdtag);
return (zfs_standard_error(zhp->zfs_hdl,
errno, errbuf));
}
@@ -801,6 +816,8 @@
if (err == -1) {
fsavl_destroy(fsavl);
nvlist_free(fss);
+ (void) zfs_release_range(zhp, fromsnap, tosnap,
+ holdtag);
return (zfs_standard_error(zhp->zfs_hdl,
errno, errbuf));
}
@@ -833,6 +850,7 @@
return (zfs_standard_error(zhp->zfs_hdl,
errno, errbuf));
}
+ (void) zfs_release_range(zhp, fromsnap, tosnap, holdtag);
}
return (err || sdd.err);
--- a/usr/src/lib/libzfs/common/libzfs_util.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_util.c Wed Aug 19 11:15:14 2009 -0600
@@ -218,6 +218,8 @@
case EZFS_REFTAG_HOLD:
return (dgettext(TEXT_DOMAIN, "tag already exists on this "
"dataset"));
+ case EZFS_TAGTOOLONG:
+ return (dgettext(TEXT_DOMAIN, "tag too long"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
--- a/usr/src/lib/libzfs/common/mapfile-vers Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/lib/libzfs/common/mapfile-vers Wed Aug 19 11:15:14 2009 -0600
@@ -62,6 +62,7 @@
zfs_get_user_props;
zfs_get_type;
zfs_hold;
+ zfs_hold_range;
zfs_iscsi_perm_check;
zfs_is_mounted;
zfs_is_shared;
@@ -73,6 +74,7 @@
zfs_iter_filesystems;
zfs_iter_root;
zfs_iter_snapshots;
+ zfs_iter_snapshots_sorted;
zfs_mount;
zfs_name_to_prop;
zfs_name_valid;
@@ -107,6 +109,7 @@
zfs_receive;
zfs_refresh_properties;
zfs_release;
+ zfs_release_range;
zfs_rename;
zfs_rollback;
zfs_send;
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c Wed Aug 19 11:15:14 2009 -0600
@@ -1000,6 +1000,11 @@
return (error);
dsda->rm_origin = origin;
dsl_dataset_make_exclusive(origin, tag);
+
+ if (origin->ds_objset != NULL) {
+ dmu_objset_evict(origin->ds_objset);
+ origin->ds_objset = NULL;
+ }
}
return (0);
@@ -1721,6 +1726,11 @@
ASSERT3U(err, ==, 0);
ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||
ds->ds_phys->ds_unique_bytes == 0);
+
+ if (ds->ds_prev != NULL) {
+ dsl_dataset_rele(ds->ds_prev, ds);
+ ds->ds_prev = ds_prev = NULL;
+ }
}
err = zio_wait(zio);
@@ -1782,19 +1792,9 @@
/*
* Remove the origin of the clone we just destroyed.
*/
- dsl_dataset_t *origin = ds->ds_prev;
struct dsl_ds_destroyarg ndsda = {0};
- ASSERT3P(origin, ==, dsda->rm_origin);
- if (origin->ds_objset) {
- dmu_objset_evict(origin->ds_objset);
- origin->ds_objset = NULL;
- }
-
- dsl_dataset_rele(ds->ds_prev, ds);
- ds->ds_prev = NULL;
-
- ndsda.ds = origin;
+ ndsda.ds = dsda->rm_origin;
dsl_dataset_destroy_sync(&ndsda, tag, cr, tx);
}
}
@@ -3210,11 +3210,22 @@
return (err);
}
+struct dsl_ds_holdarg {
+ dsl_sync_task_group_t *dstg;
+ char *htag;
+ char *snapname;
+ boolean_t recursive;
+ boolean_t gotone;
+ boolean_t temphold;
+ char failed[MAXPATHLEN];
+};
+
static int
dsl_dataset_user_hold_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
- char *htag = arg2;
+ struct dsl_ds_holdarg *ha = arg2;
+ char *htag = ha->htag;
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
int error = 0;
@@ -3224,9 +3235,6 @@
if (!dsl_dataset_is_snapshot(ds))
return (EINVAL);
- if (strlen(htag) >= ZAP_MAXNAMELEN)
- return (ENAMETOOLONG);
-
/* tags must be unique */
mutex_enter(&ds->ds_lock);
if (ds->ds_phys->ds_userrefs_obj) {
@@ -3246,8 +3254,10 @@
dsl_dataset_user_hold_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
- char *htag = arg2;
- objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ struct dsl_ds_holdarg *ha = arg2;
+ char *htag = ha->htag;
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+ objset_t *mos = dp->dp_meta_objset;
time_t now = gethrestime_sec();
uint64_t zapobj;
@@ -3268,20 +3278,16 @@
VERIFY(0 == zap_add(mos, zapobj, htag, 8, 1, &now, tx));
+ if (ha->temphold) {
+ VERIFY(0 == dsl_pool_user_hold(dp, ds->ds_object,
+ htag, &now, tx));
+ }
+
spa_history_internal_log(LOG_DS_USER_HOLD,
- ds->ds_dir->dd_pool->dp_spa, tx, cr, "<%s> dataset = %llu",
- htag, ds->ds_object);
+ dp->dp_spa, tx, cr, "<%s> temp = %d dataset = %llu", htag,
+ (int)ha->temphold, ds->ds_object);
}
-struct dsl_ds_holdarg {
- dsl_sync_task_group_t *dstg;
- char *htag;
- char *snapname;
- boolean_t recursive;
- boolean_t gotone;
- char failed[MAXPATHLEN];
-};
-
static int
dsl_dataset_user_hold_one(char *dsname, void *arg)
{
@@ -3297,7 +3303,7 @@
if (error == 0) {
ha->gotone = B_TRUE;
dsl_sync_task_create(ha->dstg, dsl_dataset_user_hold_check,
- dsl_dataset_user_hold_sync, ds, ha->htag, 0);
+ dsl_dataset_user_hold_sync, ds, ha, 0);
} else if (error == ENOENT && ha->recursive) {
error = 0;
} else {
@@ -3308,7 +3314,7 @@
int
dsl_dataset_user_hold(char *dsname, char *snapname, char *htag,
- boolean_t recursive)
+ boolean_t recursive, boolean_t temphold)
{
struct dsl_ds_holdarg *ha;
dsl_sync_task_t *dst;
@@ -3329,6 +3335,7 @@
ha->htag = htag;
ha->snapname = snapname;
ha->recursive = recursive;
+ ha->temphold = temphold;
if (recursive) {
error = dmu_objset_find(dsname, dsl_dataset_user_hold_one,
ha, DS_FIND_CHILDREN);
@@ -3446,16 +3453,19 @@
{
struct dsl_ds_releasearg *ra = arg1;
dsl_dataset_t *ds = ra->ds;
- spa_t *spa = ds->ds_dir->dd_pool->dp_spa;
- objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+ objset_t *mos = dp->dp_meta_objset;
uint64_t zapobj;
uint64_t dsobj = ds->ds_object;
uint64_t refs;
+ int error;
mutex_enter(&ds->ds_lock);
ds->ds_userrefs--;
refs = ds->ds_userrefs;
mutex_exit(&ds->ds_lock);
+ error = dsl_pool_user_release(dp, ds->ds_object, ra->htag, tx);
+ VERIFY(error == 0 || error == ENOENT);
zapobj = ds->ds_phys->ds_userrefs_obj;
VERIFY(0 == zap_remove(mos, zapobj, ra->htag, tx));
if (ds->ds_userrefs == 0 && ds->ds_phys->ds_num_children == 1 &&
@@ -3470,7 +3480,7 @@
}
spa_history_internal_log(LOG_DS_USER_RELEASE,
- spa, tx, cr, "<%s> %lld dataset = %llu",
+ dp->dp_spa, tx, cr, "<%s> %lld dataset = %llu",
ra->htag, (longlong_t)refs, dsobj);
}
@@ -3486,9 +3496,6 @@
boolean_t own = B_FALSE;
boolean_t might_destroy;
- if (strlen(ha->htag) >= ZAP_MAXNAMELEN)
- return (ENAMETOOLONG);
-
/* alloc a buffer to hold dsname@snapname, plus the terminating NULL */
name = kmem_asprintf("%s@%s", dsname, ha->snapname);
error = dsl_dataset_hold(name, dtag, &ds);
@@ -3601,6 +3608,34 @@
return (error);
}
+/*
+ * Called at spa_load time to release a stale temporary user hold.
+ */
+int
+dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, char *htag)
+{
+ dsl_dataset_t *ds;
+ char *snap;
+ char *name;
+ int namelen;
+ int error;
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ error = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
+ rw_exit(&dp->dp_config_rwlock);
+ if (error)
+ return (error);
+ namelen = dsl_dataset_namelen(ds)+1;
+ name = kmem_alloc(namelen, KM_SLEEP);
+ dsl_dataset_name(ds, name);
+ dsl_dataset_rele(ds, FTAG);
+
+ snap = strchr(name, '@');
+ *snap = '\0';
+ ++snap;
+ return (dsl_dataset_user_release(name, snap, htag, B_FALSE));
+}
+
int
dsl_dataset_get_holds(const char *dsname, nvlist_t **nvp)
{
--- a/usr/src/uts/common/fs/zfs/dsl_pool.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_pool.c Wed Aug 19 11:15:14 2009 -0600
@@ -142,6 +142,14 @@
goto out;
}
+ err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_TMP_USERREFS, sizeof (uint64_t), 1,
+ &dp->dp_tmp_userrefs_obj);
+ if (err == ENOENT)
+ err = 0;
+ if (err)
+ goto out;
+
/* get scrub status */
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_SCRUB_FUNC, sizeof (uint32_t), 1,
@@ -649,3 +657,108 @@
{
return (dp->dp_vnrele_taskq);
}
+
+/*
+ * Walk through the pool-wide zap object of temporary snapshot user holds
+ * and release them.
+ */
+void
+dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp)
+{
+ zap_attribute_t za;
+ zap_cursor_t zc;
+ objset_t *mos = dp->dp_meta_objset;
+ uint64_t zapobj = dp->dp_tmp_userrefs_obj;
+
+ if (zapobj == 0)
+ return;
+ ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS);
+
+ for (zap_cursor_init(&zc, mos, zapobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ char *htag;
+ uint64_t dsobj;
+
+ htag = strchr(za.za_name, '-');
+ *htag = '\0';
+ ++htag;
+ dsobj = strtonum(za.za_name, NULL);
+ (void) dsl_dataset_user_release_tmp(dp, dsobj, htag);
+ }
+ zap_cursor_fini(&zc);
+}
+
+/*
+ * Create the pool-wide zap object for storing temporary snapshot holds.
+ */
+void
+dsl_pool_user_hold_create_obj(dsl_pool_t *dp, dmu_tx_t *tx)
+{
+ objset_t *mos = dp->dp_meta_objset;
+
+ ASSERT(dp->dp_tmp_userrefs_obj == 0);
+ ASSERT(dmu_tx_is_syncing(tx));
+
+ dp->dp_tmp_userrefs_obj = zap_create(mos, DMU_OT_USERREFS,
+ DMU_OT_NONE, 0, tx);
+
+ VERIFY(zap_add(mos, DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_TMP_USERREFS,
+ sizeof (uint64_t), 1, &dp->dp_tmp_userrefs_obj, tx) == 0);
+}
+
+static int
+dsl_pool_user_hold_rele_impl(dsl_pool_t *dp, uint64_t dsobj,
+ const char *tag, time_t *t, dmu_tx_t *tx, boolean_t holding)
+{
+ objset_t *mos = dp->dp_meta_objset;
+ uint64_t zapobj = dp->dp_tmp_userrefs_obj;
+ char *name;
+ int error;
+
+ ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS);
+ ASSERT(dmu_tx_is_syncing(tx));
+
+ /*
+ * If the pool was created prior to SPA_VERSION_USERREFS, the
+ * zap object for temporary holds might not exist yet.
+ */
+ if (zapobj == 0) {
+ if (holding) {
+ dsl_pool_user_hold_create_obj(dp, tx);
+ zapobj = dp->dp_tmp_userrefs_obj;
+ } else {
+ return (ENOENT);
+ }
+ }
+
+ name = kmem_asprintf("%llx-%s", (u_longlong_t)dsobj, tag);
+ if (holding)
+ error = zap_add(mos, zapobj, name, 8, 1, t, tx);
+ else
+ error = zap_remove(mos, zapobj, name, tx);
+ strfree(name);
+
+ return (error);
+}
+
+/*
+ * Add a temporary hold for the given dataset object and tag.
+ */
+int
+dsl_pool_user_hold(dsl_pool_t *dp, uint64_t dsobj, const char *tag,
+ time_t *t, dmu_tx_t *tx)
+{
+ return (dsl_pool_user_hold_rele_impl(dp, dsobj, tag, t, tx, B_TRUE));
+}
+
+/*
+ * Release a temporary hold for the given dataset object and tag.
+ */
+int
+dsl_pool_user_release(dsl_pool_t *dp, uint64_t dsobj, const char *tag,
+ dmu_tx_t *tx)
+{
+ return (dsl_pool_user_hold_rele_impl(dp, dsobj, tag, NULL,
+ tx, B_FALSE));
+}
--- a/usr/src/uts/common/fs/zfs/spa.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/spa.c Wed Aug 19 11:15:14 2009 -0600
@@ -1606,6 +1606,11 @@
*/
(void) dmu_objset_find(spa_name(spa),
dsl_destroy_inconsistent, NULL, DS_FIND_CHILDREN);
+
+ /*
+ * Clean up any stale temporary dataset userrefs.
+ */
+ dsl_pool_clean_tmp_userrefs(spa->spa_dsl_pool);
}
error = 0;
--- a/usr/src/uts/common/fs/zfs/spa_errlog.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/spa_errlog.c Wed Aug 19 11:15:14 2009 -0600
@@ -58,7 +58,6 @@
* This is a stripped-down version of strtoull, suitable only for converting
* lowercase hexidecimal numbers that don't overflow.
*/
-#ifdef _KERNEL
uint64_t
strtonum(const char *str, char **nptr)
{
@@ -85,7 +84,6 @@
return (val);
}
-#endif
/*
* Convert a bookmark to a string.
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dmu.h Wed Aug 19 11:15:14 2009 -0600
@@ -200,6 +200,7 @@
#define DMU_POOL_HISTORY "history"
#define DMU_POOL_PROPS "pool_props"
#define DMU_POOL_L2CACHE "l2cache"
+#define DMU_POOL_TMP_USERREFS "tmp_userrefs"
/* 4x8 zbookmark_t */
#define DMU_POOL_SCRUB_BOOKMARK "scrub_bookmark"
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h Wed Aug 19 11:15:14 2009 -0600
@@ -197,9 +197,11 @@
int dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head,
boolean_t force);
int dsl_dataset_user_hold(char *dsname, char *snapname, char *htag,
- boolean_t recursive);
+ boolean_t recursive, boolean_t temphold);
int dsl_dataset_user_release(char *dsname, char *snapname, char *htag,
boolean_t recursive);
+int dsl_dataset_user_release_tmp(struct dsl_pool *dp, uint64_t dsobj,
+ char *htag);
int dsl_dataset_get_holds(const char *dsname, nvlist_t **nvp);
blkptr_t *dsl_dataset_get_blkptr(dsl_dataset_t *ds);
--- a/usr/src/uts/common/fs/zfs/sys/dsl_pool.h Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_pool.h Wed Aug 19 11:15:14 2009 -0600
@@ -85,6 +85,7 @@
hrtime_t dp_read_overhead;
uint64_t dp_throughput;
uint64_t dp_write_limit;
+ uint64_t dp_tmp_userrefs_obj;
/* Uses dp_lock */
kmutex_t dp_lock;
@@ -146,6 +147,12 @@
taskq_t *dsl_pool_vnrele_taskq(dsl_pool_t *dp);
+extern int dsl_pool_user_hold(dsl_pool_t *dp, uint64_t dsobj,
+ const char *tag, time_t *t, dmu_tx_t *tx);
+extern int dsl_pool_user_release(dsl_pool_t *dp, uint64_t dsobj,
+ const char *tag, dmu_tx_t *tx);
+extern void dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp);
+
#ifdef __cplusplus
}
#endif
--- a/usr/src/uts/common/fs/zfs/sys/spa.h Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h Wed Aug 19 11:15:14 2009 -0600
@@ -478,6 +478,7 @@
extern boolean_t spa_is_root(spa_t *spa);
extern boolean_t spa_writeable(spa_t *spa);
extern int spa_mode(spa_t *spa);
+extern uint64_t strtonum(const char *str, char **nptr);
/* history logging */
typedef enum history_log_type {
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h Wed Aug 19 11:15:14 2009 -0600
@@ -166,6 +166,7 @@
struct drr_begin zc_begin_record;
zinject_record_t zc_inject_record;
boolean_t zc_defer_destroy;
+ boolean_t zc_temphold;
} zfs_cmd_t;
typedef struct zfs_useracct {
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Wed Aug 19 11:15:14 2009 -0600
@@ -3474,6 +3474,7 @@
* zc_value short name of snap
* zc_string user-supplied tag for this reference
* zc_cookie recursive flag
+ * zc_temphold set if hold is temporary
*
* outputs: none
*/
@@ -3486,7 +3487,7 @@
return (EINVAL);
return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
- zc->zc_string, recursive));
+ zc->zc_string, recursive, zc->zc_temphold));
}
/*
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c Tue Aug 18 20:06:58 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c Wed Aug 19 11:15:14 2009 -0600
@@ -630,7 +630,6 @@
fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr,
char *domainbuf, int buflen, uid_t *ridp)
{
- extern uint64_t strtonum(const char *str, char **nptr);
uint64_t fuid;
const char *domain;