6818183 zfs snapshot -r is slow due to set_snap_props() doing txg_wait_synced() for each new snapshot
--- a/usr/src/cmd/ztest/ztest.c Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/cmd/ztest/ztest.c Sun Apr 12 20:42:39 2009 -0700
@@ -1428,7 +1428,8 @@
error = dmu_objset_destroy(snapname);
if (error != 0 && error != ENOENT)
fatal(0, "dmu_objset_destroy() = %d", error);
- error = dmu_objset_snapshot(osname, strchr(snapname, '@')+1, FALSE);
+ error = dmu_objset_snapshot(osname, strchr(snapname, '@')+1,
+ NULL, FALSE);
if (error == ENOSPC)
ztest_record_enospc("dmu_take_snapshot");
else if (error != 0 && error != EEXIST)
@@ -1481,7 +1482,8 @@
if (error != 0 && error != ENOENT)
fatal(0, "dmu_objset_destroy() = %d", error);
- error = dmu_objset_snapshot(osname, strchr(snap1name, '@')+1, FALSE);
+ error = dmu_objset_snapshot(osname, strchr(snap1name, '@')+1,
+ NULL, FALSE);
if (error && error != EEXIST) {
if (error == ENOSPC) {
ztest_record_enospc("dmu_take_snapshot");
@@ -1507,7 +1509,7 @@
}
error = dmu_objset_snapshot(clone1name, strchr(snap2name, '@')+1,
- FALSE);
+ NULL, FALSE);
if (error && error != EEXIST) {
if (error == ENOSPC) {
ztest_record_enospc("dmu_take_snapshot");
@@ -1517,7 +1519,7 @@
}
error = dmu_objset_snapshot(clone1name, strchr(snap3name, '@')+1,
- FALSE);
+ NULL, FALSE);
if (error && error != EEXIST) {
if (error == ENOSPC) {
ztest_record_enospc("dmu_take_snapshot");
--- a/usr/src/uts/common/fs/zfs/dmu_objset.c Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/dmu_objset.c Sun Apr 12 20:42:39 2009 -0700
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -704,13 +704,33 @@
char *snapname;
char failed[MAXPATHLEN];
boolean_t checkperms;
- list_t objsets;
+ nvlist_t *props;
};
-struct osnode {
- list_node_t node;
- objset_t *os;
-};
+static int
+snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ objset_t *os = arg1;
+ struct snaparg *sn = arg2;
+
+ /* The props have already been checked by zfs_check_userprops(). */
+
+ return (dsl_dataset_snapshot_check(os->os->os_dsl_dataset,
+ sn->snapname, tx));
+}
+
+static void
+snapshot_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ objset_t *os = arg1;
+ dsl_dataset_t *ds = os->os->os_dsl_dataset;
+ struct snaparg *sn = arg2;
+
+ dsl_dataset_snapshot_sync(ds, sn->snapname, cr, tx);
+
+ if (sn->props)
+ dsl_props_set_sync(ds->ds_prev, sn->props, cr, tx);
+}
static int
dmu_objset_snapshot_one(char *name, void *arg)
@@ -747,13 +767,8 @@
*/
err = zil_suspend(dmu_objset_zil(os));
if (err == 0) {
- struct osnode *osn;
- dsl_sync_task_create(sn->dstg, dsl_dataset_snapshot_check,
- dsl_dataset_snapshot_sync, os->os->os_dsl_dataset,
- sn->snapname, 3);
- osn = kmem_alloc(sizeof (struct osnode), KM_SLEEP);
- osn->os = os;
- list_insert_tail(&sn->objsets, osn);
+ dsl_sync_task_create(sn->dstg, snapshot_check,
+ snapshot_sync, os, sn, 3);
} else {
dmu_objset_close(os);
}
@@ -762,11 +777,11 @@
}
int
-dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive)
+dmu_objset_snapshot(char *fsname, char *snapname,
+ nvlist_t *props, boolean_t recursive)
{
dsl_sync_task_t *dst;
- struct osnode *osn;
- struct snaparg sn = { 0 };
+ struct snaparg sn;
spa_t *spa;
int err;
@@ -778,8 +793,7 @@
sn.dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
sn.snapname = snapname;
- list_create(&sn.objsets, sizeof (struct osnode),
- offsetof(struct osnode, node));
+ sn.props = props;
if (recursive) {
sn.checkperms = B_TRUE;
@@ -790,27 +804,19 @@
err = dmu_objset_snapshot_one(fsname, &sn);
}
- if (err)
- goto out;
-
- err = dsl_sync_task_group_wait(sn.dstg);
+ if (err == 0)
+ err = dsl_sync_task_group_wait(sn.dstg);
for (dst = list_head(&sn.dstg->dstg_tasks); dst;
dst = list_next(&sn.dstg->dstg_tasks, dst)) {
- dsl_dataset_t *ds = dst->dst_arg1;
+ objset_t *os = dst->dst_arg1;
+ dsl_dataset_t *ds = os->os->os_dsl_dataset;
if (dst->dst_err)
dsl_dataset_name(ds, sn.failed);
+ zil_resume(dmu_objset_zil(os));
+ dmu_objset_close(os);
}
-out:
- while (osn = list_head(&sn.objsets)) {
- list_remove(&sn.objsets, osn);
- zil_resume(dmu_objset_zil(osn->os));
- dmu_objset_close(osn->os);
- kmem_free(osn, sizeof (struct osnode));
- }
- list_destroy(&sn.objsets);
-
if (err)
(void) strcpy(fsname, sn.failed);
dsl_sync_task_group_destroy(sn.dstg);
--- a/usr/src/uts/common/fs/zfs/dsl_prop.c Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_prop.c Sun Apr 12 20:42:39 2009 -0700
@@ -413,7 +413,7 @@
"%s=%s dataset = %llu", psa->name, valstr, ds->ds_object);
}
-static void
+void
dsl_props_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dmu.h Sun Apr 12 20:42:39 2009 -0700
@@ -171,7 +171,8 @@
int dmu_objset_destroy(const char *name);
int dmu_snapshots_destroy(char *fsname, char *snapname);
int dmu_objset_rollback(objset_t *os);
-int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
+int dmu_objset_snapshot(char *fsname, char *snapname, struct nvlist *props,
+ boolean_t recursive);
int dmu_objset_rename(const char *name, const char *newname,
boolean_t recursive);
int dmu_objset_find(char *name, int func(char *, void *), void *arg,
--- a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h Sun Apr 12 20:42:39 2009 -0700
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -106,7 +106,8 @@
void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
int dmu_objset_destroy(const char *name);
int dmu_objset_rollback(objset_t *os);
-int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
+int dmu_objset_snapshot(char *fsname, char *snapname, nvlist_t *props,
+ boolean_t recursive);
void dmu_objset_stats(objset_t *os, nvlist_t *nv);
void dmu_objset_fast_stat(objset_t *os, dmu_objset_stats_t *stat);
void dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
--- a/usr/src/uts/common/fs/zfs/sys/dsl_prop.h Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_prop.h Sun Apr 12 20:42:39 2009 -0700
@@ -29,6 +29,7 @@
#include <sys/dmu.h>
#include <sys/dsl_pool.h>
#include <sys/zfs_context.h>
+#include <sys/dsl_synctask.h>
#ifdef __cplusplus
extern "C" {
@@ -64,6 +65,7 @@
int dsl_prop_get_dd(struct dsl_dir *dd, const char *propname,
int intsz, int numints, void *buf, char *setpoint);
+dsl_syncfunc_t dsl_props_set_sync;
int dsl_prop_set(const char *ddname, const char *propname,
int intsz, int numints, const void *buf);
int dsl_props_set(const char *dsname, nvlist_t *nvl);
--- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c Sun Apr 12 20:42:39 2009 -0700
@@ -736,7 +736,7 @@
return (err);
if (err == 0) {
- err = dmu_objset_snapshot(name, dirname, B_FALSE);
+ err = dmu_objset_snapshot(name, dirname, NULL, B_FALSE);
if (err)
return (err);
err = lookupnameat(dirname, seg, follow, NULL, vpp, dvp);
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Sun Apr 12 10:48:30 2009 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Sun Apr 12 20:42:39 2009 -0700
@@ -1623,6 +1623,37 @@
}
/*
+ * Check that all the properties are valid user properties.
+ */
+static int
+zfs_check_userprops(char *fsname, nvlist_t *nvl)
+{
+ nvpair_t *elem = NULL;
+ int error = 0;
+
+ while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
+ const char *propname = nvpair_name(elem);
+ char *valstr;
+
+ if (!zfs_prop_user(propname) ||
+ nvpair_type(elem) != DATA_TYPE_STRING)
+ return (EINVAL);
+
+ if (error = zfs_secpolicy_write_perms(fsname,
+ ZFS_DELEG_PERM_USERPROP, CRED()))
+ return (error);
+
+ if (strlen(propname) >= ZAP_MAXNAMELEN)
+ return (ENAMETOOLONG);
+
+ VERIFY(nvpair_value_string(elem, &valstr) == 0);
+ if (strlen(valstr) >= ZAP_MAXVALUELEN)
+ return (E2BIG);
+ }
+ return (0);
+}
+
+/*
* inputs:
* zc_name name of filesystem
* zc_value name of property to set
@@ -2219,27 +2250,6 @@
return (error);
}
-struct snap_prop_arg {
- nvlist_t *nvprops;
- const char *snapname;
-};
-
-static int
-set_snap_props(char *name, void *arg)
-{
- struct snap_prop_arg *snpa = arg;
- int len = strlen(name) + strlen(snpa->snapname) + 2;
- char *buf = kmem_alloc(len, KM_SLEEP);
- int err;
-
- (void) snprintf(buf, len, "%s@%s", name, snpa->snapname);
- err = zfs_set_prop_nvlist(buf, snpa->nvprops);
- if (err)
- (void) dmu_objset_destroy(buf);
- kmem_free(buf, len);
- return (err);
-}
-
/*
* inputs:
* zc_name name of filesystem
@@ -2263,26 +2273,20 @@
&nvprops)) != 0)
return (error);
- error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, recursive);
-
- /*
- * It would be nice to do this atomically.
- */
- if (error == 0) {
- struct snap_prop_arg snpa;
- snpa.nvprops = nvprops;
- snpa.snapname = zc->zc_value;
- if (recursive) {
- error = dmu_objset_find(zc->zc_name,
- set_snap_props, &snpa, DS_FIND_CHILDREN);
- if (error) {
- (void) dmu_snapshots_destroy(zc->zc_name,
- zc->zc_value);
- }
- } else {
- error = set_snap_props(zc->zc_name, &snpa);
- }
+ error = zfs_check_userprops(zc->zc_name, nvprops);
+ if (error)
+ goto out;
+
+ if (nvprops != NULL && nvlist_next_nvpair(nvprops, NULL) != NULL &&
+ zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
+ error = ENOTSUP;
+ goto out;
}
+
+ error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
+ nvprops, recursive);
+
+out:
nvlist_free(nvprops);
return (error);
}