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,800 **** --- 791,801 ---- 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,984 **** nvlist_free(thisdbg); return (0); } ! static int ! hold_for_send(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, * 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); ! } ! return (error); } static void * send_progress_thread(void *arg) { --- 941,963 ---- nvlist_free(thisdbg); return (0); } ! static void ! gather_holds(zfs_handle_t *zhp, send_dump_data_t *sdd) { assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); /* ! * zfs_send() only sets snapholds for sends that need them, * e.g. replication and doall. */ ! if (sdd->snapholds == NULL) ! return; ! fnvlist_add_string(sdd->snapholds, zhp->zfs_name, sdd->holdtag); } static void * send_progress_thread(void *arg) {
*** 1041,1061 **** 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) { 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; ! } zfs_close(zhp); ! return (err); } if (sdd->seento || !sdd->seenfrom) { zfs_close(zhp); return (0); --- 1020,1035 ---- thissnap = strchr(zhp->zfs_name, '@') + 1; isfromsnap = (sdd->fromsnap != NULL && strcmp(sdd->fromsnap, thissnap) == 0); if (!sdd->seenfrom && isfromsnap) { ! gather_holds(zhp, sdd); sdd->seenfrom = B_TRUE; (void) strcpy(sdd->prevsnap, thissnap); ! sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID); zfs_close(zhp); ! return (0); } if (sdd->seento || !sdd->seenfrom) { zfs_close(zhp); return (0);
*** 1102,1119 **** */ zfs_close(zhp); return (0); } ! err = hold_for_send(zhp, sdd); ! if (err) { ! if (err == ENOENT) ! err = 0; ! zfs_close(zhp); ! return (err); ! } ! fromorigin = sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate); if (sdd->verbose) { uint64_t size; --- 1076,1086 ---- */ zfs_close(zhp); return (0); } ! gather_holds(zhp, sdd); fromorigin = sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate); if (sdd->verbose) { uint64_t size;
*** 1540,1551 **** --- 1507,1520 ---- 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,1572 **** --- 1532,1565 ---- 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) {