Print this page
Optimize creation and removal of temporary "user holds" placed on
snapshots by a zfs send, by ensuring all the required holds and
releases are done in a single dsl_sync_task.
Creation now collates the required holds during a dry run and
then uses a single lzc_hold call via zfs_hold_apply instead of
processing each snapshot in turn.
Defered (on exit) cleanup by the kernel is also now done in
dsl_sync_task by reusing dsl_dataset_user_release.
On a test with 11 volumes in a tree each with 8 snapshots on a
single HDD zpool this reduces the time required to perform a full
send from 20 seconds to under 0.8 seconds.
For reference eliminating the hold entirely reduces this 0.15
seconds.
While I'm here:-
* Remove some unused structures
* Fix nvlist_t leak in zfs_release_one

*** 4099,4126 **** zfs_close(zhp); return (rv); } int zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, boolean_t recursive, boolean_t enoent_ok, int cleanup_fd) { int ret; struct holdarg ha; - nvlist_t *errors; - libzfs_handle_t *hdl = zhp->zfs_hdl; - char errbuf[1024]; - nvpair_t *elem; ha.nvl = fnvlist_alloc(); ha.snapname = snapname; ha.tag = tag; ha.recursive = recursive; (void) zfs_hold_one(zfs_handle_dup(zhp), &ha); ! ret = lzc_hold(ha.nvl, cleanup_fd, &errors); fnvlist_free(ha.nvl); if (ret == 0) return (0); if (nvlist_next_nvpair(errors, NULL) == NULL) { /* no hold-specific errors */ --- 4099,4167 ---- zfs_close(zhp); return (rv); } int + zfs_hold_add(zfs_handle_t *zhp, const char *snapname, const char *tag, + boolean_t enoent_ok, nvlist_t *holds) + { + zfs_handle_t *szhp; + char name[ZFS_MAXNAMELEN]; + char errbuf[1024]; + int ret; + + (void) snprintf(name, sizeof (name), + "%s@%s", zhp->zfs_name, snapname); + + szhp = make_dataset_handle(zhp->zfs_hdl, name); + if (szhp) { + fnvlist_add_string(holds, name, tag); + zfs_close(szhp); + return (0); + } + + ret = ENOENT; + if (enoent_ok) + return (ret); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot hold snapshot '%s@%s'"), + zhp->zfs_name, snapname); + (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf); + + return (ret); + } + + int zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, boolean_t recursive, boolean_t enoent_ok, int cleanup_fd) { int ret; struct holdarg ha; ha.nvl = fnvlist_alloc(); ha.snapname = snapname; ha.tag = tag; ha.recursive = recursive; (void) zfs_hold_one(zfs_handle_dup(zhp), &ha); ! ret = zfs_hold_apply(zhp, enoent_ok, cleanup_fd, ha.nvl); fnvlist_free(ha.nvl); + return (ret); + } + + int + zfs_hold_apply(zfs_handle_t *zhp, boolean_t enoent_ok, int cleanup_fd, nvlist_t *holds) + { + int ret; + nvlist_t *errors; + libzfs_handle_t *hdl = zhp->zfs_hdl; + char errbuf[1024]; + nvpair_t *elem; + + ret = lzc_hold(holds, cleanup_fd, &errors); + if (ret == 0) return (0); if (nvlist_next_nvpair(errors, NULL) == NULL) { /* no hold-specific errors */
*** 4174,4190 **** fnvlist_free(errors); return (ret); } - struct releasearg { - nvlist_t *nvl; - const char *snapname; - const char *tag; - boolean_t recursive; - }; - static int zfs_release_one(zfs_handle_t *zhp, void *arg) { struct holdarg *ha = arg; zfs_handle_t *szhp; --- 4215,4224 ----
*** 4197,4206 **** --- 4231,4241 ---- szhp = make_dataset_handle(zhp->zfs_hdl, name); if (szhp) { nvlist_t *holds = fnvlist_alloc(); fnvlist_add_boolean(holds, ha->tag); fnvlist_add_nvlist(ha->nvl, name, holds); + fnvlist_free(holds); zfs_close(szhp); } if (ha->recursive) rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);