From f94b3cbf43d62f4962e71cfe7ba8c6f0602e2a45 Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Thu, 28 Mar 2019 18:48:58 -0400 Subject: [PATCH] Send stream should only list included snaps Currently, zfs send streams will include a list of all snapshots on the source side if the '-p' option is provided. This can cause performance problems on the receive side, especially if those snapshots aren't present on the destination. These problems arise because guid_to_name(), which is used for several receive side functions, will search the entire receive-side pool if it can't find a snapshot with a matching guid. This patch corrects the issue by ensuring only streams that require this list of snapshots include them. Reviewed-by: Alek Pinchuk Reviewed-by: Brian Behlendorf Reviewed-by: Matt Ahrens Signed-off-by: Tom Caputi Closes #8533 --- lib/libzfs/libzfs_sendrecv.c | 107 +++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index f76900a9c552..b4500e63f123 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -625,6 +625,7 @@ typedef struct send_data { const char *tosnap; boolean_t recursive; boolean_t raw; + boolean_t doall; boolean_t replicate; boolean_t verbose; boolean_t backup; @@ -949,14 +950,37 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg) sd->parent_fromsnap_guid = 0; sd->parent_snaps = fnvlist_alloc(); sd->snapprops = fnvlist_alloc(); - if (!sd->replicate && fromsnap_txg != 0) - min_txg = fromsnap_txg; - if (!sd->replicate && tosnap_txg != 0) - max_txg = tosnap_txg; if (sd->holds) VERIFY(0 == nvlist_alloc(&sd->snapholds, NV_UNIQUE_NAME, 0)); - (void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd, - min_txg, max_txg); + + + /* + * If this is a "doall" send, a replicate send or we're just trying + * to gather a list of previous snapshots, iterate through all the + * snaps in the txg range. Otherwise just look at the one we're + * interested in. + */ + if (sd->doall || sd->replicate || sd->tosnap == NULL) { + if (!sd->replicate && fromsnap_txg != 0) + min_txg = fromsnap_txg; + if (!sd->replicate && tosnap_txg != 0) + max_txg = tosnap_txg; + (void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd, + min_txg, max_txg); + } else { + char snapname[MAXPATHLEN] = { 0 }; + zfs_handle_t *snap; + + (void) snprintf(snapname, sizeof (snapname) - 1, "%s@%s", + zhp->zfs_name, sd->tosnap); + if (sd->fromsnap != NULL) + sd->seenfrom = B_TRUE; + snap = zfs_open(zhp->zfs_hdl, snapname, + ZFS_TYPE_SNAPSHOT); + if (snap != NULL) + (void) send_iterate_snap(snap, sd); + } + fnvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps); fnvlist_add_nvlist(nvfs, "snapprops", sd->snapprops); if (sd->holds) @@ -987,10 +1011,9 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg) static int gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap, - const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t replicate, - boolean_t verbose, boolean_t backup, boolean_t holds, boolean_t props, - nvlist_t **nvlp, - avl_tree_t **avlp) + const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t doall, + boolean_t replicate, boolean_t verbose, boolean_t backup, boolean_t holds, + boolean_t props, nvlist_t **nvlp, avl_tree_t **avlp) { zfs_handle_t *zhp; send_data_t sd = { 0 }; @@ -1006,6 +1029,7 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap, sd.tosnap = tosnap; sd.recursive = recursive; sd.raw = raw; + sd.doall = doall; sd.replicate = replicate; sd.verbose = verbose; sd.backup = backup; @@ -1478,15 +1502,49 @@ dump_filesystem(zfs_handle_t *zhp, void *arg) if (sdd->fromsnap == NULL || missingfrom) sdd->seenfrom = B_TRUE; - if (!sdd->replicate && sdd->fromsnap != NULL) - min_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, - sdd->fromsnap); - if (!sdd->replicate && sdd->tosnap != NULL) - max_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, - sdd->tosnap); - rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg, - min_txg, max_txg); + + /* + * Iterate through all snapshots and process the ones we will be + * sending. If we only have a "from" and "to" snapshot to deal + * with, we can avoid iterating through all the other snapshots. + */ + if (sdd->doall || sdd->replicate || sdd->tosnap == NULL) { + if (!sdd->replicate && sdd->fromsnap != NULL) + min_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, + sdd->fromsnap); + if (!sdd->replicate && sdd->tosnap != NULL) + max_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, + sdd->tosnap); + rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg, + min_txg, max_txg); + } else { + char snapname[MAXPATHLEN] = { 0 }; + zfs_handle_t *snap; + + if (!sdd->seenfrom) { + (void) snprintf(snapname, sizeof (snapname) - 1, + "%s@%s", zhp->zfs_name, sdd->fromsnap); + snap = zfs_open(zhp->zfs_hdl, snapname, + ZFS_TYPE_SNAPSHOT); + if (snap != NULL) + rv = dump_snapshot(snap, sdd); + else + rv = -1; + } + + if (rv == 0) { + (void) snprintf(snapname, sizeof (snapname) - 1, + "%s@%s", zhp->zfs_name, sdd->tosnap); + snap = zfs_open(zhp->zfs_hdl, snapname, + ZFS_TYPE_SNAPSHOT); + if (snap != NULL) + rv = dump_snapshot(snap, sdd); + else + rv = -1; + } + } + if (!sdd->seenfrom) { (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "WARNING: could not send %s@%s:\n" @@ -1965,8 +2023,9 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name, fromsnap, tosnap, flags->replicate, flags->raw, - flags->replicate, flags->verbose, flags->backup, - flags->holds, flags->props, &fss, &fsavl); + flags->doall, flags->replicate, flags->verbose, + flags->backup, flags->holds, flags->props, &fss, + &fsavl); if (err) goto err_out; VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss)); @@ -2870,8 +2929,8 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs, VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0)); if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL, - recursive, B_TRUE, recursive, B_FALSE, B_FALSE, B_FALSE, B_TRUE, - &local_nv, &local_avl)) != 0) + recursive, B_TRUE, B_FALSE, recursive, B_FALSE, B_FALSE, + B_FALSE, B_TRUE, &local_nv, &local_avl)) != 0) return (error); /* @@ -4304,8 +4363,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, */ *cp = '\0'; if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE, - B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_TRUE, &local_nv, - &local_avl) == 0) { + B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_TRUE, + &local_nv, &local_avl) == 0) { *cp = '@'; fs = fsavl_find(local_avl, drrb->drr_toguid, NULL); fsavl_destroy(local_avl);