summaryrefslogtreecommitdiffstats
path: root/module/zfs/dmu_objset.c
diff options
context:
space:
mode:
Diffstat (limited to 'module/zfs/dmu_objset.c')
-rw-r--r--module/zfs/dmu_objset.c310
1 files changed, 185 insertions, 125 deletions
diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c
index 52d55d566..0f07a4cc9 100644
--- a/module/zfs/dmu_objset.c
+++ b/module/zfs/dmu_objset.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
/* Portions Copyright 2010 Robert Milkowski */
@@ -525,9 +526,10 @@ dmu_objset_evict_dbufs(objset_t *os)
void
dmu_objset_evict(objset_t *os)
{
- dsl_dataset_t *ds = os->os_dsl_dataset;
int t;
+ dsl_dataset_t *ds = os->os_dsl_dataset;
+
for (t = 0; t < TXG_SIZE; t++)
ASSERT(!dmu_objset_is_dirty(os, t));
@@ -698,30 +700,33 @@ dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx)
spa_t *spa = dd->dd_pool->dp_spa;
struct oscarg *oa = arg2;
uint64_t obj;
+ dsl_dataset_t *ds;
+ blkptr_t *bp;
ASSERT(dmu_tx_is_syncing(tx));
obj = dsl_dataset_create_sync(dd, oa->lastname,
oa->clone_origin, oa->flags, oa->cr, tx);
- if (oa->clone_origin == NULL) {
- dsl_pool_t *dp = dd->dd_pool;
- dsl_dataset_t *ds;
- blkptr_t *bp;
- objset_t *os;
-
- VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, obj, FTAG, &ds));
- bp = dsl_dataset_get_blkptr(ds);
- ASSERT(BP_IS_HOLE(bp));
-
- os = dmu_objset_create_impl(spa, ds, bp, oa->type, tx);
+ VERIFY3U(0, ==, dsl_dataset_hold_obj(dd->dd_pool, obj, FTAG, &ds));
+ bp = dsl_dataset_get_blkptr(ds);
+ if (BP_IS_HOLE(bp)) {
+ objset_t *os =
+ dmu_objset_create_impl(spa, ds, bp, oa->type, tx);
if (oa->userfunc)
oa->userfunc(os, oa->userarg, oa->cr, tx);
- dsl_dataset_rele(ds, FTAG);
}
- spa_history_log_internal(LOG_DS_CREATE, spa, tx, "dataset = %llu", obj);
+ if (oa->clone_origin == NULL) {
+ spa_history_log_internal_ds(ds, "create", tx, "");
+ } else {
+ char namebuf[MAXNAMELEN];
+ dsl_dataset_name(oa->clone_origin, namebuf);
+ spa_history_log_internal_ds(ds, "clone", tx,
+ "origin=%s (%llu)", namebuf, oa->clone_origin->ds_object);
+ }
+ dsl_dataset_rele(ds, FTAG);
}
int
@@ -798,34 +803,40 @@ dmu_objset_destroy(const char *name, boolean_t defer)
return (error);
}
-struct snaparg {
- dsl_sync_task_group_t *dstg;
- char *snapname;
- char *htag;
- char failed[MAXPATHLEN];
- boolean_t recursive;
- boolean_t needsuspend;
- boolean_t temporary;
- nvlist_t *props;
- struct dsl_ds_holdarg *ha; /* only needed in the temporary case */
- dsl_dataset_t *newds;
-};
+typedef struct snapallarg {
+ dsl_sync_task_group_t *saa_dstg;
+ boolean_t saa_needsuspend;
+ nvlist_t *saa_props;
+
+ /* the following are used only if 'temporary' is set: */
+ boolean_t saa_temporary;
+ const char *saa_htag;
+ struct dsl_ds_holdarg *saa_ha;
+ dsl_dataset_t *saa_newds;
+} snapallarg_t;
+
+typedef struct snaponearg {
+ const char *soa_longname; /* long snap name */
+ const char *soa_snapname; /* short snap name */
+ snapallarg_t *soa_saa;
+} snaponearg_t;
static int
snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
objset_t *os = arg1;
- struct snaparg *sn = arg2;
+ snaponearg_t *soa = arg2;
+ snapallarg_t *saa = soa->soa_saa;
int error;
/* The props have already been checked by zfs_check_userprops(). */
error = dsl_dataset_snapshot_check(os->os_dsl_dataset,
- sn->snapname, tx);
+ soa->soa_snapname, tx);
if (error)
return (error);
- if (sn->temporary) {
+ if (saa->saa_temporary) {
/*
* Ideally we would just call
* dsl_dataset_user_hold_check() and
@@ -843,12 +854,13 @@ snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
* Not checking number of tags because the tag will be
* unique, as it will be the only tag.
*/
- if (strlen(sn->htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
+ if (strlen(saa->saa_htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
return (E2BIG);
- sn->ha = kmem_alloc(sizeof(struct dsl_ds_holdarg), KM_PUSHPAGE);
- sn->ha->temphold = B_TRUE;
- sn->ha->htag = sn->htag;
+ saa->saa_ha = kmem_alloc(sizeof (struct dsl_ds_holdarg),
+ KM_PUSHPAGE);
+ saa->saa_ha->temphold = B_TRUE;
+ saa->saa_ha->htag = saa->saa_htag;
}
return (error);
}
@@ -858,24 +870,25 @@ snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
{
objset_t *os = arg1;
dsl_dataset_t *ds = os->os_dsl_dataset;
- struct snaparg *sn = arg2;
+ snaponearg_t *soa = arg2;
+ snapallarg_t *saa = soa->soa_saa;
- dsl_dataset_snapshot_sync(ds, sn->snapname, tx);
+ dsl_dataset_snapshot_sync(ds, soa->soa_snapname, tx);
- if (sn->props) {
+ if (saa->saa_props != NULL) {
dsl_props_arg_t pa;
- pa.pa_props = sn->props;
+ pa.pa_props = saa->saa_props;
pa.pa_source = ZPROP_SRC_LOCAL;
dsl_props_set_sync(ds->ds_prev, &pa, tx);
}
- if (sn->temporary) {
+ if (saa->saa_temporary) {
struct dsl_ds_destroyarg da;
- dsl_dataset_user_hold_sync(ds->ds_prev, sn->ha, tx);
- kmem_free(sn->ha, sizeof (struct dsl_ds_holdarg));
- sn->ha = NULL;
- sn->newds = ds->ds_prev;
+ dsl_dataset_user_hold_sync(ds->ds_prev, saa->saa_ha, tx);
+ kmem_free(saa->saa_ha, sizeof (struct dsl_ds_holdarg));
+ saa->saa_ha = NULL;
+ saa->saa_newds = ds->ds_prev;
da.ds = ds->ds_prev;
da.defer = B_TRUE;
@@ -884,135 +897,182 @@ snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
}
static int
-dmu_objset_snapshot_one(const char *name, void *arg)
+snapshot_one_impl(const char *snapname, void *arg)
{
- struct snaparg *sn = arg;
+ char *fsname;
+ snapallarg_t *saa = arg;
+ snaponearg_t *soa;
objset_t *os;
int err;
- char *cp;
- /*
- * If the objset starts with a '%', then ignore it unless it was
- * explicitly named (ie, not recursive). These hidden datasets
- * are always inconsistent, and by not opening them here, we can
- * avoid a race with dsl_dir_destroy_check().
- */
- cp = strrchr(name, '/');
- if (cp && cp[1] == '%' && sn->recursive)
- return (0);
+ fsname = kmem_zalloc(MAXPATHLEN, KM_PUSHPAGE);
+ (void) strlcpy(fsname, snapname, MAXPATHLEN);
+ strchr(fsname, '@')[0] = '\0';
- (void) strcpy(sn->failed, name);
-
- /*
- * Check permissions if we are doing a recursive snapshot. The
- * permission checks for the starting dataset have already been
- * performed in zfs_secpolicy_snapshot()
- */
- if (sn->recursive && (err = zfs_secpolicy_snapshot_perms(name, CRED())))
- return (err);
-
- err = dmu_objset_hold(name, sn, &os);
+ err = dmu_objset_hold(fsname, saa, &os);
+ kmem_free(fsname, MAXPATHLEN);
if (err != 0)
return (err);
/*
* If the objset is in an inconsistent state (eg, in the process
- * of being destroyed), don't snapshot it. As with %hidden
- * datasets, we return EBUSY if this name was explicitly
- * requested (ie, not recursive), and otherwise ignore it.
+ * of being destroyed), don't snapshot it.
*/
if (os->os_dsl_dataset->ds_phys->ds_flags & DS_FLAG_INCONSISTENT) {
- dmu_objset_rele(os, sn);
- return (sn->recursive ? 0 : EBUSY);
+ dmu_objset_rele(os, saa);
+ return (EBUSY);
}
- if (sn->needsuspend) {
+ if (saa->saa_needsuspend) {
err = zil_suspend(dmu_objset_zil(os));
if (err) {
- dmu_objset_rele(os, sn);
+ dmu_objset_rele(os, saa);
return (err);
}
}
- dsl_sync_task_create(sn->dstg, snapshot_check, snapshot_sync,
- os, sn, 3);
+
+ soa = kmem_zalloc(sizeof (*soa), KM_PUSHPAGE);
+ soa->soa_saa = saa;
+ soa->soa_longname = snapname;
+ soa->soa_snapname = strchr(snapname, '@') + 1;
+
+ dsl_sync_task_create(saa->saa_dstg, snapshot_check, snapshot_sync,
+ os, soa, 3);
return (0);
}
+/*
+ * The snapshots must all be in the same pool.
+ */
int
-dmu_objset_snapshot(char *fsname, char *snapname, char *tag,
- nvlist_t *props, boolean_t recursive, boolean_t temporary, int cleanup_fd)
+dmu_objset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors)
{
dsl_sync_task_t *dst;
- struct snaparg *sn;
+ snapallarg_t saa = { 0 };
spa_t *spa;
- minor_t minor;
+ int rv = 0;
int err;
+ nvpair_t *pair;
- sn = kmem_alloc(sizeof (struct snaparg), KM_SLEEP);
- (void) strcpy(sn->failed, fsname);
+ pair = nvlist_next_nvpair(snaps, NULL);
+ if (pair == NULL)
+ return (0);
- err = spa_open(fsname, &spa, FTAG);
- if (err) {
- kmem_free(sn, sizeof (struct snaparg));
+ err = spa_open(nvpair_name(pair), &spa, FTAG);
+ if (err)
return (err);
+ saa.saa_dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
+ saa.saa_props = props;
+ saa.saa_needsuspend = (spa_version(spa) < SPA_VERSION_FAST_SNAP);
+
+ for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(snaps, pair)) {
+ err = snapshot_one_impl(nvpair_name(pair), &saa);
+ if (err != 0) {
+ if (errors != NULL) {
+ fnvlist_add_int32(errors,
+ nvpair_name(pair), err);
+ }
+ rv = err;
+ }
}
- if (temporary) {
- if (cleanup_fd < 0) {
- spa_close(spa, FTAG);
- return (EINVAL);
- }
- if ((err = zfs_onexit_fd_hold(cleanup_fd, &minor)) != 0) {
- spa_close(spa, FTAG);
- return (err);
+ /*
+ * If any call to snapshot_one_impl() failed, don't execute the
+ * sync task. The error handling code below will clean up the
+ * snaponearg_t from any successful calls to
+ * snapshot_one_impl().
+ */
+ if (rv == 0)
+ err = dsl_sync_task_group_wait(saa.saa_dstg);
+ if (err != 0)
+ rv = err;
+
+ for (dst = list_head(&saa.saa_dstg->dstg_tasks); dst;
+ dst = list_next(&saa.saa_dstg->dstg_tasks, dst)) {
+ objset_t *os = dst->dst_arg1;
+ snaponearg_t *soa = dst->dst_arg2;
+ if (dst->dst_err != 0) {
+ if (errors != NULL) {
+ fnvlist_add_int32(errors,
+ soa->soa_longname, dst->dst_err);
+ }
+ rv = dst->dst_err;
}
+
+ if (saa.saa_needsuspend)
+ zil_resume(dmu_objset_zil(os));
+ dmu_objset_rele(os, &saa);
+ kmem_free(soa, sizeof (*soa));
}
- sn->dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
- sn->snapname = snapname;
- sn->htag = tag;
- sn->props = props;
- sn->recursive = recursive;
- sn->needsuspend = (spa_version(spa) < SPA_VERSION_FAST_SNAP);
- sn->temporary = temporary;
- sn->ha = NULL;
- sn->newds = NULL;
-
- if (recursive) {
- err = dmu_objset_find(fsname,
- dmu_objset_snapshot_one, sn, DS_FIND_CHILDREN);
- } else {
- err = dmu_objset_snapshot_one(fsname, sn);
+ dsl_sync_task_group_destroy(saa.saa_dstg);
+ spa_close(spa, FTAG);
+ return (rv);
+}
+
+int
+dmu_objset_snapshot_one(const char *fsname, const char *snapname)
+{
+ int err;
+ char *longsnap = kmem_asprintf("%s@%s", fsname, snapname);
+ nvlist_t *snaps = fnvlist_alloc();
+
+ fnvlist_add_boolean(snaps, longsnap);
+ err = dmu_objset_snapshot(snaps, NULL, NULL);
+ fnvlist_free(snaps);
+ strfree(longsnap);
+ return (err);
+}
+
+int
+dmu_objset_snapshot_tmp(const char *snapname, const char *tag, int cleanup_fd)
+{
+ dsl_sync_task_t *dst;
+ snapallarg_t saa = { 0 };
+ spa_t *spa;
+ minor_t minor;
+ int err;
+
+ err = spa_open(snapname, &spa, FTAG);
+ if (err)
+ return (err);
+ saa.saa_dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
+ saa.saa_htag = tag;
+ saa.saa_needsuspend = (spa_version(spa) < SPA_VERSION_FAST_SNAP);
+ saa.saa_temporary = B_TRUE;
+
+ if (cleanup_fd < 0) {
+ spa_close(spa, FTAG);
+ return (EINVAL);
+ }
+ if ((err = zfs_onexit_fd_hold(cleanup_fd, &minor)) != 0) {
+ spa_close(spa, FTAG);
+ return (err);
}
+ err = snapshot_one_impl(snapname, &saa);
+
if (err == 0)
- err = dsl_sync_task_group_wait(sn->dstg);
+ err = dsl_sync_task_group_wait(saa.saa_dstg);
- for (dst = list_head(&sn->dstg->dstg_tasks); dst;
- dst = list_next(&sn->dstg->dstg_tasks, dst)) {
+ for (dst = list_head(&saa.saa_dstg->dstg_tasks); dst;
+ dst = list_next(&saa.saa_dstg->dstg_tasks, dst)) {
objset_t *os = dst->dst_arg1;
- dsl_dataset_t *ds = os->os_dsl_dataset;
- if (dst->dst_err) {
- dsl_dataset_name(ds, sn->failed);
- } else if (temporary) {
- dsl_register_onexit_hold_cleanup(sn->newds, tag, minor);
- }
- if (sn->needsuspend)
+ dsl_register_onexit_hold_cleanup(saa.saa_newds, tag, minor);
+ if (saa.saa_needsuspend)
zil_resume(dmu_objset_zil(os));
- dmu_objset_rele(os, sn);
+ dmu_objset_rele(os, &saa);
}
- if (err)
- (void) strcpy(fsname, sn->failed);
- if (temporary)
- zfs_onexit_fd_rele(cleanup_fd);
- dsl_sync_task_group_destroy(sn->dstg);
+ zfs_onexit_fd_rele(cleanup_fd);
+ dsl_sync_task_group_destroy(saa.saa_dstg);
spa_close(spa, FTAG);
- kmem_free(sn, sizeof (struct snaparg));
return (err);
}
+
static void
dmu_objset_sync_dnodes(list_t *list, list_t *newlist, dmu_tx_t *tx)
{
@@ -1159,7 +1219,7 @@ dmu_objset_sync(objset_t *os, zio_t *pio, dmu_tx_t *tx)
dmu_objset_sync_dnodes(&os->os_dirty_dnodes[txgoff], newlist, tx);
list = &DMU_META_DNODE(os)->dn_dirty_records[txgoff];
- while ((dr = list_head(list)) != NULL) {
+ while ((dr = list_head(list))) {
ASSERT(dr->dr_dbuf->db_level == 0);
list_remove(list, dr);
if (dr->dr_zio)
@@ -1219,7 +1279,7 @@ dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx)
ASSERT(list_head(list) == NULL || dmu_objset_userused_enabled(os));
- while ((dn = list_head(list)) != NULL) {
+ while ((dn = list_head(list))) {
int flags;
ASSERT(!DMU_OBJECT_IS_SPECIAL(dn->dn_object));
ASSERT(dn->dn_phys->dn_type == DMU_OT_NONE ||