Skip to content

Commit

Permalink
skip FREEOBJECTS for objects which can't exist
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Fabian-Gruenbichler committed Sep 25, 2017
1 parent e3bdab1 commit 94f51fb
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 10 deletions.
2 changes: 2 additions & 0 deletions include/sys/dmu_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
48 changes: 38 additions & 10 deletions module/zfs/dmu_send.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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;

/*
Expand All @@ -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);
}
Expand All @@ -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);
Expand Down

0 comments on commit 94f51fb

Please sign in to comment.