Print this page
3740 Poor ZFS send / receive performance due to snapshot hold / release processing
Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
@@ -791,10 +791,11 @@
boolean_t seenfrom, seento, replicate, doall, fromorigin;
boolean_t verbose, dryrun, parsable, progress;
int outfd;
boolean_t err;
nvlist_t *fss;
+ nvlist_t *snapholds;
avl_tree_t *fsavl;
snapfilter_cb_t *filter_cb;
void *filter_cb_arg;
nvlist_t *debugnv;
char holdtag[ZFS_MAXNAMELEN];
@@ -940,45 +941,23 @@
nvlist_free(thisdbg);
return (0);
}
-static int
-hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
+static void
+gather_holds(zfs_handle_t *zhp, send_dump_data_t *sdd)
{
- zfs_handle_t *pzhp;
- int error = 0;
- char *thissnap;
-
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- if (sdd->dryrun)
- return (0);
-
/*
- * zfs_send() only opens a cleanup_fd for sends that need it,
+ * zfs_send() only sets snapholds for sends that need them,
* e.g. replication and doall.
*/
- if (sdd->cleanup_fd == -1)
- return (0);
-
- thissnap = strchr(zhp->zfs_name, '@') + 1;
- *(thissnap - 1) = '\0';
- pzhp = zfs_open(zhp->zfs_hdl, zhp->zfs_name, ZFS_TYPE_DATASET);
- *(thissnap - 1) = '@';
-
- /*
- * It's OK if the parent no longer exists. The send code will
- * handle that error.
- */
- if (pzhp) {
- error = zfs_hold(pzhp, thissnap, sdd->holdtag,
- B_FALSE, B_TRUE, sdd->cleanup_fd);
- zfs_close(pzhp);
- }
+ if (sdd->snapholds == NULL)
+ return;
- return (error);
+ fnvlist_add_string(sdd->snapholds, zhp->zfs_name, sdd->holdtag);
}
static void *
send_progress_thread(void *arg)
{
@@ -1041,21 +1020,16 @@
thissnap = strchr(zhp->zfs_name, '@') + 1;
isfromsnap = (sdd->fromsnap != NULL &&
strcmp(sdd->fromsnap, thissnap) == 0);
if (!sdd->seenfrom && isfromsnap) {
- err = hold_for_send(zhp, sdd);
- if (err == 0) {
+ gather_holds(zhp, sdd);
sdd->seenfrom = B_TRUE;
(void) strcpy(sdd->prevsnap, thissnap);
- sdd->prevsnap_obj = zfs_prop_get_int(zhp,
- ZFS_PROP_OBJSETID);
- } else if (err == ENOENT) {
- err = 0;
- }
+ sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
zfs_close(zhp);
- return (err);
+ return (0);
}
if (sdd->seento || !sdd->seenfrom) {
zfs_close(zhp);
return (0);
@@ -1102,18 +1076,11 @@
*/
zfs_close(zhp);
return (0);
}
- err = hold_for_send(zhp, sdd);
- if (err) {
- if (err == ENOENT)
- err = 0;
- zfs_close(zhp);
- return (err);
- }
-
+ gather_holds(zhp, sdd);
fromorigin = sdd->prevsnap[0] == '\0' &&
(sdd->fromorigin || sdd->replicate);
if (sdd->verbose) {
uint64_t size;
@@ -1540,12 +1507,14 @@
sdd.cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
if (sdd.cleanup_fd < 0) {
err = errno;
goto stderr_out;
}
+ sdd.snapholds = fnvlist_alloc();
} else {
sdd.cleanup_fd = -1;
+ sdd.snapholds = NULL;
}
if (flags->verbose) {
/*
* Do a verbose no-op dry run to get all the verbose output
* before generating any data. Then do a non-verbose real
@@ -1563,10 +1532,34 @@
zfs_nicenum(sdd.size, buf, sizeof (buf));
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"total estimated size is %s\n"), buf);
}
}
+
+ if (sdd.snapholds != NULL) {
+ /* Holds are required. */
+ if (!flags->verbose) {
+ /*
+ * A verbose dry run wasn't done so do a non-verbose
+ * dry run to gather snapshot hold's.
+ */
+ sdd.dryrun = B_TRUE;
+ err = dump_filesystems(zhp, &sdd);
+ sdd.dryrun = flags->dryrun;
+ }
+
+ if (err != 0) {
+ fnvlist_free(sdd.snapholds);
+ goto stderr_out;
+ }
+
+ err = zfs_hold_nvl(zhp, sdd.cleanup_fd, sdd.snapholds);
+ fnvlist_free(sdd.snapholds);
+ if (err != 0)
+ goto stderr_out;
+ }
+
err = dump_filesystems(zhp, &sdd);
fsavl_destroy(fsavl);
nvlist_free(fss);
if (flags->dedup) {