From 94f51fbcd6d0b849955754a4a9d5372c5f3a4d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= Date: Fri, 22 Sep 2017 14:55:32 +0200 Subject: [PATCH] skip FREEOBJECTS for objects which can't exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when sending an incremental stream based on a snapshot, the receiving side must have the same base snapshot. thus we do not need to send FREEOBJECTS records for any objects past the maximum one which exists locally. this allows us to send incremental streams (again) to older ZFS implementations which actually try to free all objects in a FREEOBJECTS record, instead of bailing out early. Signed-off-by: Fabian Grünbichler --- include/sys/dmu_impl.h | 2 ++ module/zfs/dmu_send.c | 48 +++++++++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/include/sys/dmu_impl.h b/include/sys/dmu_impl.h index 65e417e3f665..5601822e1124 100644 --- a/include/sys/dmu_impl.h +++ b/include/sys/dmu_impl.h @@ -266,6 +266,8 @@ typedef struct dmu_sendarg { uint64_t dsa_toguid; int dsa_err; dmu_pendop_t dsa_pending_op; + boolean_t dsa_incremental; + uint64_t dsa_incremental_maxobj; uint64_t dsa_featureflags; uint64_t dsa_last_data_object; uint64_t dsa_last_data_offset; diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index eb6bad1dbf38..2a13db7f60e3 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -227,11 +227,11 @@ dump_free(dmu_sendarg_t *dsp, uint64_t object, uint64_t offset, offset > dsp->dsa_last_data_offset)); /* - * If we are doing a non-incremental send, then there can't - * be any data in the dataset we're receiving into. Unless we're receiving - * a full send as a clone, a free record would simply be a no-op. - * If we disable the tunable for this, save space by not sending it to - * begin with. + * If we are doing a non-incremental send, then there can't be any data + * in the dataset we're receiving into. Unless we're receiving a full + * send as a clone, a free record would simply be a no-op. If we + * disable the tunable for this, save space by not sending it to begin + * with. */ if (!zfs_send_set_freerecords_bit && !dsp->dsa_incremental) return (0); @@ -472,6 +472,22 @@ dump_freeobjects(dmu_sendarg_t *dsp, uint64_t firstobj, uint64_t numobjs) if (!zfs_send_set_freerecords_bit && !dsp->dsa_incremental) return (0); + if (dsp->dsa_incremental_maxobj > 0) { + /* + * drop record, if it is only concerning objects which do not + * exist in base snapshot + */ + if (dsp->dsa_incremental_maxobj < firstobj) + return (0); + + /* + * reduce numobjs according to maximum possible object id of + * base snapshot + */ + if (dsp->dsa_incremental_maxobj < firstobj + numobjs) + numobjs = dsp->dsa_incremental_maxobj - firstobj; + } + /* * If there is a pending op, but it's not PENDING_FREEOBJECTS, * push it out, since free block aggregation can only be done for @@ -951,7 +967,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds, zfs_bookmark_phys_t *ancestor_zb, boolean_t is_clone, boolean_t embedok, boolean_t large_block_ok, boolean_t compressok, boolean_t rawok, int outfd, uint64_t resumeobj, uint64_t resumeoff, - vnode_t *vp, offset_t *off) + vnode_t *vp, offset_t *off, uint64_t incremental_maxobj) { objset_t *os; dmu_replay_record_t *drr; @@ -1075,6 +1091,8 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds, dsp->dsa_off = off; dsp->dsa_toguid = dsl_dataset_phys(to_ds)->ds_guid; dsp->dsa_pending_op = PENDING_NONE; + dsp->dsa_incremental = (ancestor_zb != NULL); + dsp->dsa_incremental_maxobj = incremental_maxobj; dsp->dsa_featureflags = featureflags; dsp->dsa_resume_object = resumeobj; dsp->dsa_resume_offset = resumeoff; @@ -1226,6 +1244,8 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, if (fromsnap != 0) { zfs_bookmark_phys_t zb; boolean_t is_clone; + objset_t *fromos = NULL; + uint64_t maxobj = 0; err = dsl_dataset_hold_obj(dp, fromsnap, FTAG, &fromds); if (err != 0) { @@ -1239,15 +1259,18 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, dsl_dataset_phys(fromds)->ds_creation_time; zb.zbm_creation_txg = dsl_dataset_phys(fromds)->ds_creation_txg; zb.zbm_guid = dsl_dataset_phys(fromds)->ds_guid; + VERIFY0(dmu_objset_from_ds(fromds, &fromos)); + maxobj = DNODES_PER_BLOCK * + (DMU_META_DNODE(fromos)->dn_maxblkid + 1); is_clone = (fromds->ds_dir != ds->ds_dir); dsl_dataset_rele(fromds, FTAG); err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone, embedok, large_block_ok, compressok, rawok, outfd, - 0, 0, vp, off); + 0, 0, vp, off, maxobj); } else { err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE, embedok, large_block_ok, compressok, rawok, outfd, - 0, 0, vp, off); + 0, 0, vp, off, 0); } dsl_dataset_rele_flags(ds, dsflags, FTAG); return (err); @@ -1290,6 +1313,8 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, if (fromsnap != NULL) { zfs_bookmark_phys_t zb; boolean_t is_clone = B_FALSE; + objset_t *fromos = NULL; + uint64_t maxobj = 0; int fsnamelen = strchr(tosnap, '@') - tosnap; /* @@ -1313,6 +1338,9 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, zb.zbm_creation_txg = dsl_dataset_phys(fromds)->ds_creation_txg; zb.zbm_guid = dsl_dataset_phys(fromds)->ds_guid; + VERIFY0(dmu_objset_from_ds(fromds, &fromos)); + maxobj = DNODES_PER_BLOCK * + (DMU_META_DNODE(fromos)->dn_maxblkid + 1); is_clone = (ds->ds_dir != fromds->ds_dir); dsl_dataset_rele(fromds, FTAG); } @@ -1330,11 +1358,11 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, } err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone, embedok, large_block_ok, compressok, rawok, - outfd, resumeobj, resumeoff, vp, off); + outfd, resumeobj, resumeoff, vp, off, maxobj); } else { err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE, embedok, large_block_ok, compressok, rawok, - outfd, resumeobj, resumeoff, vp, off); + outfd, resumeobj, resumeoff, vp, off, 0); } if (owned) dsl_dataset_disown(ds, dsflags, FTAG);