Add AUTHORS to master branch
[zfs.git] / module / zfs / dmu_send.c
index 857b9a3..ce59aac 100644 (file)
@@ -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.
  */
 
@@ -161,7 +161,9 @@ backup_cb(spa_t *spa, blkptr_t *bp, const zbookmark_t *zb,
        if (issig(JUSTLOOKING) && issig(FORREAL))
                return (EINTR);
 
-       if (bp == NULL && zb->zb_object == 0) {
+       if (zb->zb_object != 0 && DMU_OBJECT_IS_SPECIAL(zb->zb_object)) {
+               return (0);
+       } else if (bp == NULL && zb->zb_object == 0) {
                uint64_t span = BP_SPAN(dnp, zb->zb_level);
                uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT;
                err = dump_freeobjects(ba, dnobj, span >> DNODE_SHIFT);
@@ -391,6 +393,7 @@ recv_full_existing_check(void *arg1, void *arg2, dmu_tx_t *tx)
        dsl_dataset_t *ds = arg1;
        struct recvbeginsyncarg *rbsa = arg2;
        int err;
+       struct dsl_ds_destroyarg dsda = {0};
 
        /* must be a head ds */
        if (ds->ds_phys->ds_next_snap_obj != 0)
@@ -400,7 +403,8 @@ recv_full_existing_check(void *arg1, void *arg2, dmu_tx_t *tx)
        if (dsl_dir_is_clone(ds->ds_dir))
                return (EINVAL);
 
-       err = dsl_dataset_destroy_check(ds, rbsa->tag, tx);
+       dsda.ds = ds;
+       err = dsl_dataset_destroy_check(&dsda, rbsa->tag, tx);
        if (err)
                return (err);
 
@@ -425,13 +429,16 @@ recv_full_existing_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
        dsl_dir_t *dd = ds->ds_dir;
        uint64_t flags = DS_FLAG_INCONSISTENT | rbsa->dsflags;
        uint64_t dsobj;
+       struct dsl_ds_destroyarg dsda = {0};
 
        /*
         * NB: caller must provide an extra hold on the dsl_dir_t, so it
         * won't go away when dsl_dataset_destroy_sync() closes the
         * dataset.
         */
-       dsl_dataset_destroy_sync(ds, rbsa->tag, cr, tx);
+       dsda.ds = ds;
+       dsl_dataset_destroy_sync(&dsda, rbsa->tag, cr, tx);
+       ASSERT3P(dsda.rm_origin, ==, NULL);
 
        dsobj = dsl_dataset_create_sync_dd(dd, rbsa->origin, flags, tx);
 
@@ -481,7 +488,7 @@ recv_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx)
 
 /* ARGSUSED */
 static void
-recv_online_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+recv_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
 {
        dsl_dataset_t *ohds = arg1;
        struct recvbeginsyncarg *rbsa = arg2;
@@ -511,27 +518,13 @@ recv_online_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
            dp->dp_spa, tx, cr, "dataset = %lld", dsobj);
 }
 
-/* ARGSUSED */
-static void
-recv_offline_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
-{
-       dsl_dataset_t *ds = arg1;
-
-       dmu_buf_will_dirty(ds->ds_dbuf, tx);
-       ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
-
-       spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC,
-           ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld",
-           ds->ds_object);
-}
-
 /*
  * NB: callers *MUST* call dmu_recv_stream() if dmu_recv_begin()
  * succeeds; otherwise we will leak the holds on the datasets.
  */
 int
 dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *drrb,
-    boolean_t force, objset_t *origin, boolean_t online, dmu_recv_cookie_t *drc)
+    boolean_t force, objset_t *origin, dmu_recv_cookie_t *drc)
 {
        int err = 0;
        boolean_t byteswap;
@@ -580,36 +573,8 @@ dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *drrb,
        /*
         * Process the begin in syncing context.
         */
-       if (rbsa.fromguid && !(flags & DRR_FLAG_CLONE) && !online) {
-               /* offline incremental receive */
-               err = dsl_dataset_own(tofs, 0, dmu_recv_tag, &ds);
-               if (err)
-                       return (err);
-
-               /*
-                * Only do the rollback if the most recent snapshot
-                * matches the incremental source
-                */
-               if (force) {
-                       if (ds->ds_prev == NULL ||
-                           ds->ds_prev->ds_phys->ds_guid !=
-                           rbsa.fromguid) {
-                               dsl_dataset_disown(ds, dmu_recv_tag);
-                               return (ENODEV);
-                       }
-                       (void) dsl_dataset_rollback(ds, DMU_OST_NONE);
-               }
-               rbsa.force = B_FALSE;
-               err = dsl_sync_task_do(ds->ds_dir->dd_pool,
-                   recv_incremental_check,
-                   recv_offline_incremental_sync, ds, &rbsa, 1);
-               if (err) {
-                       dsl_dataset_disown(ds, dmu_recv_tag);
-                       return (err);
-               }
-               drc->drc_logical_ds = drc->drc_real_ds = ds;
-       } else if (rbsa.fromguid && !(flags & DRR_FLAG_CLONE)) {
-               /* online incremental receive */
+       if (rbsa.fromguid && !(flags & DRR_FLAG_CLONE)) {
+               /* incremental receive */
 
                /* tmp clone name is: tofs/%tosnap" */
                (void) snprintf(rbsa.clonelastname, sizeof (rbsa.clonelastname),
@@ -620,11 +585,18 @@ dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *drrb,
                if (err)
                        return (err);
 
+               /* must not have an incremental recv already in progress */
+               if (!mutex_tryenter(&ds->ds_recvlock)) {
+                       dsl_dataset_rele(ds, dmu_recv_tag);
+                       return (EBUSY);
+               }
+
                rbsa.force = force;
                err = dsl_sync_task_do(ds->ds_dir->dd_pool,
                    recv_incremental_check,
-                   recv_online_incremental_sync, ds, &rbsa, 5);
+                   recv_incremental_sync, ds, &rbsa, 5);
                if (err) {
+                       mutex_exit(&ds->ds_recvlock);
                        dsl_dataset_rele(ds, dmu_recv_tag);
                        return (err);
                }
@@ -775,11 +747,6 @@ restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro)
        dmu_tx_t *tx;
        void *data = NULL;
 
-       err = dmu_object_info(os, drro->drr_object, NULL);
-
-       if (err != 0 && err != ENOENT)
-               return (EINVAL);
-
        if (drro->drr_type == DMU_OT_NONE ||
            drro->drr_type >= DMU_OT_NUMTYPES ||
            drro->drr_bonustype >= DMU_OT_NUMTYPES ||
@@ -792,18 +759,21 @@ restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro)
                return (EINVAL);
        }
 
+       err = dmu_object_info(os, drro->drr_object, NULL);
+
+       if (err != 0 && err != ENOENT)
+               return (EINVAL);
+
        if (drro->drr_bonuslen) {
                data = restore_read(ra, P2ROUNDUP(drro->drr_bonuslen, 8));
                if (ra->err)
                        return (ra->err);
        }
 
-       tx = dmu_tx_create(os);
-
        if (err == ENOENT) {
                /* currently free, want to be allocated */
+               tx = dmu_tx_create(os);
                dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
-               dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, 1);
                err = dmu_tx_assign(tx, TXG_WAIT);
                if (err) {
                        dmu_tx_abort(tx);
@@ -812,27 +782,22 @@ restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro)
                err = dmu_object_claim(os, drro->drr_object,
                    drro->drr_type, drro->drr_blksz,
                    drro->drr_bonustype, drro->drr_bonuslen, tx);
+               dmu_tx_commit(tx);
        } else {
                /* currently allocated, want to be allocated */
-               dmu_tx_hold_bonus(tx, drro->drr_object);
-               /*
-                * We may change blocksize, so need to
-                * hold_write
-                */
-               dmu_tx_hold_write(tx, drro->drr_object, 0, 1);
-               err = dmu_tx_assign(tx, TXG_WAIT);
-               if (err) {
-                       dmu_tx_abort(tx);
-                       return (err);
-               }
-
                err = dmu_object_reclaim(os, drro->drr_object,
                    drro->drr_type, drro->drr_blksz,
-                   drro->drr_bonustype, drro->drr_bonuslen, tx);
+                   drro->drr_bonustype, drro->drr_bonuslen);
        }
-       if (err) {
-               dmu_tx_commit(tx);
+       if (err)
                return (EINVAL);
+
+       tx = dmu_tx_create(os);
+       dmu_tx_hold_bonus(tx, drro->drr_object);
+       err = dmu_tx_assign(tx, TXG_WAIT);
+       if (err) {
+               dmu_tx_abort(tx);
+               return (err);
        }
 
        dmu_object_set_checksum(os, drro->drr_object, drro->drr_checksum, tx);
@@ -936,26 +901,6 @@ restore_free(struct restorearg *ra, objset_t *os,
        return (err);
 }
 
-void
-dmu_recv_abort_cleanup(dmu_recv_cookie_t *drc)
-{
-       if (drc->drc_newfs || drc->drc_real_ds != drc->drc_logical_ds) {
-               /*
-                * online incremental or new fs: destroy the fs (which
-                * may be a clone) that we created
-                */
-               (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag);
-               if (drc->drc_real_ds != drc->drc_logical_ds)
-                       dsl_dataset_rele(drc->drc_logical_ds, dmu_recv_tag);
-       } else {
-               /*
-                * offline incremental: rollback to most recent snapshot.
-                */
-               (void) dsl_dataset_rollback(drc->drc_real_ds, DMU_OST_NONE);
-               dsl_dataset_disown(drc->drc_real_ds, dmu_recv_tag);
-       }
-}
-
 /*
  * NB: callers *must* call dmu_recv_end() if this succeeds.
  */
@@ -1083,11 +1028,17 @@ out:
 
        if (ra.err != 0) {
                /*
-                * rollback or destroy what we created, so we don't
-                * leave it in the restoring state.
+                * destroy what we created, so we don't leave it in the
+                * inconsistent restoring state.
                 */
                txg_wait_synced(drc->drc_real_ds->ds_dir->dd_pool, 0);
-               dmu_recv_abort_cleanup(drc);
+
+               (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag,
+                   B_FALSE);
+               if (drc->drc_real_ds != drc->drc_logical_ds) {
+                       mutex_exit(&drc->drc_logical_ds->ds_recvlock);
+                       dsl_dataset_rele(drc->drc_logical_ds, dmu_recv_tag);
+               }
        }
 
        kmem_free(ra.buf, ra.bufsize);
@@ -1154,7 +1105,9 @@ dmu_recv_end(dmu_recv_cookie_t *drc)
                        dsl_dataset_rele(ds, dmu_recv_tag);
                }
                /* dsl_dataset_destroy() will disown the ds */
-               (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag);
+               (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag,
+                   B_FALSE);
+               mutex_exit(&drc->drc_logical_ds->ds_recvlock);
                if (err)
                        return (err);
        }
@@ -1168,7 +1121,8 @@ dmu_recv_end(dmu_recv_cookie_t *drc)
        if (err) {
                if (drc->drc_newfs) {
                        ASSERT(ds == drc->drc_real_ds);
-                       (void) dsl_dataset_destroy(ds, dmu_recv_tag);
+                       (void) dsl_dataset_destroy(ds, dmu_recv_tag,
+                           B_FALSE);
                        return (err);
                } else {
                        (void) dsl_dataset_rollback(ds, DMU_OST_NONE);