/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
+/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
#include <sys/dmu.h>
#include <sys/dmu_impl.h>
#include <zfs_fletcher.h>
#include <sys/avl.h>
#include <sys/ddt.h>
+#include <sys/zfs_onexit.h>
static char *dmu_recv_tag = "dmu_recv_tag";
if (!rbsa->force && dsl_dataset_modified_since_lastsnap(ds))
return (ETXTBSY);
+ /* new snapshot name must not exist */
+ err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_phys->ds_snapnames_zapobj, rbsa->tosnap, 8, 1, &val);
+ if (err == 0)
+ return (EEXIST);
+ if (err != ENOENT)
+ return (err);
+
if (rbsa->fromguid) {
/* if incremental, most recent snapshot must match fromguid */
if (ds->ds_prev == NULL)
if (err != ENOENT)
return (err);
- /* new snapshot name must not exist */
- err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
- ds->ds_phys->ds_snapnames_zapobj, rbsa->tosnap, 8, 1, &val);
- if (err == 0)
- return (EEXIST);
- if (err != ENOENT)
- return (err);
return (0);
}
dp->dp_spa, tx, "dataset = %lld", dsobj);
}
-
static boolean_t
dmu_recv_verify_features(dsl_dataset_t *ds, struct drr_begin *drrb)
{
return (err);
if (dmu_recv_verify_features(ds, drrb)) {
- dsl_dataset_rele(ds, dmu_recv_tag);
+ dsl_dataset_rele(ds, FTAG);
return (ENOTSUP);
}
uint64_t voff;
int bufsize; /* amount of memory allocated for buf */
zio_cksum_t cksum;
- avl_tree_t guid_to_ds_map;
+ avl_tree_t *guid_to_ds_map;
};
typedef struct guid_map_entry {
return (0);
}
-/*
- * This function is a callback used by dmu_objset_find() (which
- * enumerates the object sets) to build an avl tree that maps guids
- * to datasets. The resulting table is used when processing DRR_WRITE_BYREF
- * send stream records. These records, which are used in dedup'ed
- * streams, do not contain data themselves, but refer to a copy
- * of the data block that has already been written because it was
- * earlier in the stream. That previous copy is identified by the
- * guid of the dataset with the referenced data.
- */
-int
-find_ds_by_guid(const char *name, void *arg)
+static void
+free_guid_map_onexit(void *arg)
{
- avl_tree_t *guid_map = arg;
- dsl_dataset_t *ds, *snapds;
+ avl_tree_t *ca = arg;
+ void *cookie = NULL;
guid_map_entry_t *gmep;
- dsl_pool_t *dp;
- int err;
- uint64_t lastobj, firstobj;
- if (dsl_dataset_hold(name, FTAG, &ds) != 0)
- return (0);
-
- dp = ds->ds_dir->dd_pool;
- rw_enter(&dp->dp_config_rwlock, RW_READER);
- firstobj = ds->ds_dir->dd_phys->dd_origin_obj;
- lastobj = ds->ds_phys->ds_prev_snap_obj;
-
- while (lastobj != firstobj) {
- err = dsl_dataset_hold_obj(dp, lastobj, guid_map, &snapds);
- if (err) {
- /*
- * Skip this snapshot and move on. It's not
- * clear why this would ever happen, but the
- * remainder of the snapshot streadm can be
- * processed.
- */
- rw_exit(&dp->dp_config_rwlock);
- dsl_dataset_rele(ds, FTAG);
- return (0);
- }
-
- gmep = kmem_alloc(sizeof (guid_map_entry_t), KM_SLEEP);
- gmep->guid = snapds->ds_phys->ds_guid;
- gmep->gme_ds = snapds;
- avl_add(guid_map, gmep);
- lastobj = snapds->ds_phys->ds_prev_snap_obj;
+ while ((gmep = avl_destroy_nodes(ca, &cookie)) != NULL) {
+ dsl_dataset_rele(gmep->gme_ds, ca);
+ kmem_free(gmep, sizeof (guid_map_entry_t));
}
-
- rw_exit(&dp->dp_config_rwlock);
- dsl_dataset_rele(ds, FTAG);
-
- return (0);
+ avl_destroy(ca);
+ kmem_free(ca, sizeof (avl_tree_t));
}
static void *
return (rv);
}
-static void
+noinline static void
backup_byteswap(dmu_replay_record_t *drr)
{
#define DO64(X) (drr->drr_u.X = BSWAP_64(drr->drr_u.X))
DO64(drr_end.drr_checksum.zc_word[3]);
DO64(drr_end.drr_toguid);
break;
+ default:
+ break;
}
#undef DO64
#undef DO32
}
-static int
+noinline static int
restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro)
{
int err;
}
/* ARGSUSED */
-static int
+noinline static int
restore_freeobjects(struct restorearg *ra, objset_t *os,
struct drr_freeobjects *drrfo)
{
return (0);
}
-static int
+noinline static int
restore_write(struct restorearg *ra, objset_t *os,
struct drr_write *drrw)
{
*/
if (drrwbr->drr_toguid != drrwbr->drr_refguid) {
gmesrch.guid = drrwbr->drr_refguid;
- if ((gmep = avl_find(&ra->guid_to_ds_map, &gmesrch,
+ if ((gmep = avl_find(ra->guid_to_ds_map, &gmesrch,
&where)) == NULL) {
return (EINVAL);
}
ref_os = os;
}
- if (err = dmu_buf_hold(ref_os, drrwbr->drr_refobject,
- drrwbr->drr_refoffset, FTAG, &dbp, DMU_READ_PREFETCH))
+ err = dmu_buf_hold(ref_os, drrwbr->drr_refobject,
+ drrwbr->drr_refoffset, FTAG, &dbp, DMU_READ_PREFETCH);
+ if (err)
return (err);
tx = dmu_tx_create(os);
}
/* ARGSUSED */
-static int
+noinline static int
restore_free(struct restorearg *ra, objset_t *os,
struct drr_free *drrf)
{
* NB: callers *must* call dmu_recv_end() if this succeeds.
*/
int
-dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp)
+dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp,
+ int cleanup_fd, uint64_t *action_handlep)
{
struct restorearg ra = { 0 };
dmu_replay_record_t *drr;
objset_t *os;
zio_cksum_t pcksum;
- guid_map_entry_t *gmep;
int featureflags;
if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC))
ra.vp = vp;
ra.voff = *voffp;
ra.bufsize = 1<<20;
- ra.buf = kmem_alloc(ra.bufsize, KM_SLEEP);
+ ra.buf = vmem_alloc(ra.bufsize, KM_SLEEP);
/* these were verified in dmu_recv_begin */
ASSERT(DMU_GET_STREAM_HDRTYPE(drc->drc_drrb->drr_versioninfo) ==
/* if this stream is dedup'ed, set up the avl tree for guid mapping */
if (featureflags & DMU_BACKUP_FEATURE_DEDUP) {
- avl_create(&ra.guid_to_ds_map, guid_compare,
- sizeof (guid_map_entry_t),
- offsetof(guid_map_entry_t, avlnode));
- (void) dmu_objset_find(drc->drc_top_ds, find_ds_by_guid,
- (void *)&ra.guid_to_ds_map,
- DS_FIND_CHILDREN);
+ minor_t minor;
+
+ if (cleanup_fd == -1) {
+ ra.err = EBADF;
+ goto out;
+ }
+ ra.err = zfs_onexit_fd_hold(cleanup_fd, &minor);
+ if (ra.err) {
+ cleanup_fd = -1;
+ goto out;
+ }
+
+ if (*action_handlep == 0) {
+ ra.guid_to_ds_map =
+ kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
+ avl_create(ra.guid_to_ds_map, guid_compare,
+ sizeof (guid_map_entry_t),
+ offsetof(guid_map_entry_t, avlnode));
+ ra.err = zfs_onexit_add_cb(minor,
+ free_guid_map_onexit, ra.guid_to_ds_map,
+ action_handlep);
+ if (ra.err)
+ goto out;
+ } else {
+ ra.err = zfs_onexit_cb_data(minor, *action_handlep,
+ (void **)&ra.guid_to_ds_map);
+ if (ra.err)
+ goto out;
+ }
+
+ drc->drc_guid_to_ds_map = ra.guid_to_ds_map;
}
/*
ASSERT(ra.err != 0);
out:
+ if ((featureflags & DMU_BACKUP_FEATURE_DEDUP) && (cleanup_fd != -1))
+ zfs_onexit_fd_rele(cleanup_fd);
+
if (ra.err != 0) {
/*
* destroy what we created, so we don't leave it in the
}
}
- if (featureflags & DMU_BACKUP_FEATURE_DEDUP) {
- void *cookie = NULL;
-
- while (gmep = avl_destroy_nodes(&ra.guid_to_ds_map, &cookie)) {
- dsl_dataset_rele(gmep->gme_ds, &ra.guid_to_ds_map);
- kmem_free(gmep, sizeof (guid_map_entry_t));
- }
- avl_destroy(&ra.guid_to_ds_map);
- }
-
- kmem_free(ra.buf, ra.bufsize);
+ vmem_free(ra.buf, ra.bufsize);
*voffp = ra.voff;
return (ra.err);
}
}
static int
+add_ds_to_guidmap(avl_tree_t *guid_map, dsl_dataset_t *ds)
+{
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+ uint64_t snapobj = ds->ds_phys->ds_prev_snap_obj;
+ dsl_dataset_t *snapds;
+ guid_map_entry_t *gmep;
+ int err;
+
+ ASSERT(guid_map != NULL);
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ err = dsl_dataset_hold_obj(dp, snapobj, guid_map, &snapds);
+ if (err == 0) {
+ gmep = kmem_alloc(sizeof (guid_map_entry_t), KM_SLEEP);
+ gmep->guid = snapds->ds_phys->ds_guid;
+ gmep->gme_ds = snapds;
+ avl_add(guid_map, gmep);
+ }
+
+ rw_exit(&dp->dp_config_rwlock);
+ return (err);
+}
+
+static int
dmu_recv_existing_end(dmu_recv_cookie_t *drc)
{
struct recvendsyncarg resa;
out:
mutex_exit(&ds->ds_recvlock);
+ if (err == 0 && drc->drc_guid_to_ds_map != NULL)
+ (void) add_ds_to_guidmap(drc->drc_guid_to_ds_map, ds);
dsl_dataset_disown(ds, dmu_recv_tag);
(void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag, B_FALSE);
return (err);
/* clean up the fs we just recv'd into */
(void) dsl_dataset_destroy(ds, dmu_recv_tag, B_FALSE);
} else {
+ if (drc->drc_guid_to_ds_map != NULL)
+ (void) add_ds_to_guidmap(drc->drc_guid_to_ds_map, ds);
/* release the hold from dmu_recv_begin */
dsl_dataset_disown(ds, dmu_recv_tag);
}