]> granicus.if.org Git - zfs/commitdiff
Send stream should only list included snaps
authorTom Caputi <tcaputi@datto.com>
Thu, 28 Mar 2019 22:48:58 +0000 (18:48 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 28 Mar 2019 22:48:58 +0000 (15:48 -0700)
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 <apinchuk@datto.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #8533

lib/libzfs/libzfs_sendrecv.c

index f76900a9c55254618b217d20743db4869124bae7..b4500e63f1234ae5b41e5241a503f29a0c3cce30 100644 (file)
@@ -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 @@ out:
 
 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 @@ again:
        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);