]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/dmu_send.c
Fix send/recv lost spill block
[mirror_zfs.git] / module / zfs / dmu_send.c
index ad64d666bee75177c0d1128114a96f81f2780141..a6ff5ce3e4465524dcb131a60885cef8a419c69b 100644 (file)
@@ -64,6 +64,8 @@ int zfs_send_corrupt_data = B_FALSE;
 int zfs_send_queue_length = SPA_MAXBLOCKSIZE;
 /* Set this tunable to FALSE to disable setting of DRR_FLAG_FREERECORDS */
 int zfs_send_set_freerecords_bit = B_TRUE;
+/* Set this tunable to FALSE is disable sending unmodified spill blocks. */
+int zfs_send_unmodified_spill_blocks = B_TRUE;
 
 /*
  * Use this to override the recordsize calculation for fast zfs send estimates.
@@ -99,6 +101,8 @@ typedef struct dump_bytes_io {
        int             dbi_len;
 } dump_bytes_io_t;
 
+static int do_dump(dmu_sendarg_t *dsa, struct send_block_record *data);
+
 static void
 dump_bytes_cb(void *arg)
 {
@@ -436,6 +440,12 @@ dump_spill(dmu_sendarg_t *dsp, const blkptr_t *bp, uint64_t object, void *data)
        drrs->drr_length = blksz;
        drrs->drr_toguid = dsp->dsa_toguid;
 
+       /* See comment in dump_dnode() for full details */
+       if (zfs_send_unmodified_spill_blocks &&
+           (bp->blk_birth <= dsp->dsa_fromtxg)) {
+               drrs->drr_flags |= DRR_SPILL_UNMODIFIED;
+       }
+
        /* handle raw send fields */
        if (dsp->dsa_featureflags & DMU_BACKUP_FEATURE_RAW) {
                ASSERT(BP_IS_PROTECTED(bp));
@@ -587,6 +597,14 @@ dump_dnode(dmu_sendarg_t *dsp, const blkptr_t *bp, uint64_t object,
                }
        }
 
+       /*
+        * DRR_OBJECT_SPILL is set for every dnode which references a
+        * spill block.  This allows the receiving pool to definitively
+        * determine when a spill block should be kept or freed.
+        */
+       if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR)
+               drro->drr_flags |= DRR_OBJECT_SPILL;
+
        if (dump_record(dsp, DN_BONUS(dnp), bonuslen) != 0)
                return (SET_ERROR(EINTR));
 
@@ -594,8 +612,34 @@ dump_dnode(dmu_sendarg_t *dsp, const blkptr_t *bp, uint64_t object,
        if (dump_free(dsp, object, (dnp->dn_maxblkid + 1) *
            (dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT), DMU_OBJECT_END) != 0)
                return (SET_ERROR(EINTR));
+
+       /*
+        * Send DRR_SPILL records for unmodified spill blocks.  This is useful
+        * because changing certain attributes of the object (e.g. blocksize)
+        * can cause old versions of ZFS to incorrectly remove a spill block.
+        * Including these records in the stream forces an up to date version
+        * to always be written ensuring they're never lost.  Current versions
+        * of the code which understand the DRR_FLAG_SPILL_BLOCK feature can
+        * ignore these unmodified spill blocks.
+        */
+       if (zfs_send_unmodified_spill_blocks &&
+           (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) &&
+           (DN_SPILL_BLKPTR(dnp)->blk_birth <= dsp->dsa_fromtxg)) {
+               struct send_block_record record;
+
+               bzero(&record, sizeof (struct send_block_record));
+               record.eos_marker = B_FALSE;
+               record.bp = *DN_SPILL_BLKPTR(dnp);
+               SET_BOOKMARK(&(record.zb), dmu_objset_id(dsp->dsa_os),
+                   object, 0, DMU_SPILL_BLKID);
+
+               if (do_dump(dsp, &record) != 0)
+                       return (SET_ERROR(EINTR));
+       }
+
        if (dsp->dsa_err != 0)
                return (SET_ERROR(EINTR));
+
        return (0);
 }
 
@@ -1036,6 +1080,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds,
        /* raw send implies compressok */
        if (compressok || rawok)
                featureflags |= DMU_BACKUP_FEATURE_COMPRESSED;
+
        if (rawok && os->os_encrypted)
                featureflags |= DMU_BACKUP_FEATURE_RAW;
 
@@ -1064,6 +1109,8 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds,
        if (zfs_send_set_freerecords_bit)
                drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_FREERECORDS;
 
+       drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_SPILL_BLOCK;
+
        if (ancestor_zb != NULL) {
                drr->drr_u.drr_begin.drr_fromguid =
                    ancestor_zb->zbm_guid;
@@ -1084,6 +1131,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds,
        dsp->dsa_os = os;
        dsp->dsa_off = off;
        dsp->dsa_toguid = dsl_dataset_phys(to_ds)->ds_guid;
+       dsp->dsa_fromtxg = fromtxg;
        dsp->dsa_pending_op = PENDING_NONE;
        dsp->dsa_featureflags = featureflags;
        dsp->dsa_resume_object = resumeobj;
@@ -1552,4 +1600,8 @@ MODULE_PARM_DESC(zfs_send_corrupt_data, "Allow sending corrupt data");
 
 module_param(zfs_send_queue_length, int, 0644);
 MODULE_PARM_DESC(zfs_send_queue_length, "Maximum send queue length");
+
+module_param(zfs_send_unmodified_spill_blocks, int, 0644);
+MODULE_PARM_DESC(zfs_send_unmodified_spill_blocks,
+       "Send unmodified spill blocks");
 #endif