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


4084         zfs_handle_t *szhp;
4085         char name[ZFS_MAXNAMELEN];
4086         int rv = 0;
4087 
4088         (void) snprintf(name, sizeof (name),
4089             "%s@%s", zhp->zfs_name, ha->snapname);
4090 
4091         szhp = make_dataset_handle(zhp->zfs_hdl, name);
4092         if (szhp) {
4093                 fnvlist_add_string(ha->nvl, name, ha->tag);
4094                 zfs_close(szhp);
4095         }
4096 
4097         if (ha->recursive)
4098                 rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
4099         zfs_close(zhp);
4100         return (rv);
4101 }
4102 
4103 int































4104 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4105     boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
4106 {
4107         int ret;
4108         struct holdarg ha;
4109         nvlist_t *errors;
4110         libzfs_handle_t *hdl = zhp->zfs_hdl;
4111         char errbuf[1024];
4112         nvpair_t *elem;
4113 
4114         ha.nvl = fnvlist_alloc();
4115         ha.snapname = snapname;
4116         ha.tag = tag;
4117         ha.recursive = recursive;
4118         (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4119         ret = lzc_hold(ha.nvl, cleanup_fd, &errors);
4120         fnvlist_free(ha.nvl);
4121 














4122         if (ret == 0)
4123                 return (0);
4124 
4125         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4126                 /* no hold-specific errors */
4127                 (void) snprintf(errbuf, sizeof (errbuf),
4128                     dgettext(TEXT_DOMAIN, "cannot hold"));
4129                 switch (ret) {
4130                 case ENOTSUP:
4131                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4132                             "pool must be upgraded"));
4133                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4134                         break;
4135                 case EINVAL:
4136                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4137                         break;
4138                 default:
4139                         (void) zfs_standard_error(hdl, ret, errbuf);
4140                 }
4141         }


4159                 case EINVAL:
4160                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4161                         break;
4162                 case EEXIST:
4163                         (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
4164                         break;
4165                 case ENOENT:
4166                         if (enoent_ok)
4167                                 return (ENOENT);
4168                         /* FALLTHROUGH */
4169                 default:
4170                         (void) zfs_standard_error(hdl,
4171                             fnvpair_value_int32(elem), errbuf);
4172                 }
4173         }
4174 
4175         fnvlist_free(errors);
4176         return (ret);
4177 }
4178 
4179 struct releasearg {
4180         nvlist_t *nvl;
4181         const char *snapname;
4182         const char *tag;
4183         boolean_t recursive;
4184 };
4185 
4186 static int
4187 zfs_release_one(zfs_handle_t *zhp, void *arg)
4188 {
4189         struct holdarg *ha = arg;
4190         zfs_handle_t *szhp;
4191         char name[ZFS_MAXNAMELEN];
4192         int rv = 0;
4193 
4194         (void) snprintf(name, sizeof (name),
4195             "%s@%s", zhp->zfs_name, ha->snapname);
4196 
4197         szhp = make_dataset_handle(zhp->zfs_hdl, name);
4198         if (szhp) {
4199                 nvlist_t *holds = fnvlist_alloc();
4200                 fnvlist_add_boolean(holds, ha->tag);
4201                 fnvlist_add_nvlist(ha->nvl, name, holds);

4202                 zfs_close(szhp);
4203         }
4204 
4205         if (ha->recursive)
4206                 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4207         zfs_close(zhp);
4208         return (rv);
4209 }
4210 
4211 int
4212 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4213     boolean_t recursive)
4214 {
4215         int ret;
4216         struct holdarg ha;
4217         nvlist_t *errors;
4218         nvpair_t *elem;
4219         libzfs_handle_t *hdl = zhp->zfs_hdl;
4220 
4221         ha.nvl = fnvlist_alloc();




4084         zfs_handle_t *szhp;
4085         char name[ZFS_MAXNAMELEN];
4086         int rv = 0;
4087 
4088         (void) snprintf(name, sizeof (name),
4089             "%s@%s", zhp->zfs_name, ha->snapname);
4090 
4091         szhp = make_dataset_handle(zhp->zfs_hdl, name);
4092         if (szhp) {
4093                 fnvlist_add_string(ha->nvl, name, ha->tag);
4094                 zfs_close(szhp);
4095         }
4096 
4097         if (ha->recursive)
4098                 rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
4099         zfs_close(zhp);
4100         return (rv);
4101 }
4102 
4103 int
4104 zfs_hold_add(zfs_handle_t *zhp, const char *snapname, const char *tag,
4105     boolean_t enoent_ok, nvlist_t *holds)
4106 {
4107         zfs_handle_t *szhp;
4108         char name[ZFS_MAXNAMELEN];
4109         char errbuf[1024];
4110         int ret;
4111 
4112         (void) snprintf(name, sizeof (name),
4113             "%s@%s", zhp->zfs_name, snapname);
4114 
4115         szhp = make_dataset_handle(zhp->zfs_hdl, name);
4116         if (szhp) {
4117                 fnvlist_add_string(holds, name, tag);
4118                 zfs_close(szhp);
4119                 return (0);
4120         }
4121 
4122         ret = ENOENT;
4123         if (enoent_ok)
4124                 return (ret);
4125 
4126         (void) snprintf(errbuf, sizeof (errbuf),
4127             dgettext(TEXT_DOMAIN, "cannot hold snapshot '%s@%s'"),
4128             zhp->zfs_name, snapname);
4129         (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf);
4130 
4131         return (ret);
4132 }
4133 
4134 int
4135 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4136     boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
4137 {
4138         int ret;
4139         struct holdarg ha;




4140 
4141         ha.nvl = fnvlist_alloc();
4142         ha.snapname = snapname;
4143         ha.tag = tag;
4144         ha.recursive = recursive;
4145         (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4146         ret = zfs_hold_apply(zhp, enoent_ok, cleanup_fd, ha.nvl);
4147         fnvlist_free(ha.nvl);
4148 
4149         return (ret);
4150 }
4151 
4152 int
4153 zfs_hold_apply(zfs_handle_t *zhp, boolean_t enoent_ok, int cleanup_fd, nvlist_t *holds)
4154 {
4155         int ret;
4156         nvlist_t *errors;
4157         libzfs_handle_t *hdl = zhp->zfs_hdl;
4158         char errbuf[1024];
4159         nvpair_t *elem;
4160 
4161         ret = lzc_hold(holds, cleanup_fd, &errors);
4162 
4163         if (ret == 0)
4164                 return (0);
4165 
4166         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4167                 /* no hold-specific errors */
4168                 (void) snprintf(errbuf, sizeof (errbuf),
4169                     dgettext(TEXT_DOMAIN, "cannot hold"));
4170                 switch (ret) {
4171                 case ENOTSUP:
4172                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4173                             "pool must be upgraded"));
4174                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4175                         break;
4176                 case EINVAL:
4177                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4178                         break;
4179                 default:
4180                         (void) zfs_standard_error(hdl, ret, errbuf);
4181                 }
4182         }


4200                 case EINVAL:
4201                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4202                         break;
4203                 case EEXIST:
4204                         (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
4205                         break;
4206                 case ENOENT:
4207                         if (enoent_ok)
4208                                 return (ENOENT);
4209                         /* FALLTHROUGH */
4210                 default:
4211                         (void) zfs_standard_error(hdl,
4212                             fnvpair_value_int32(elem), errbuf);
4213                 }
4214         }
4215 
4216         fnvlist_free(errors);
4217         return (ret);
4218 }
4219 







4220 static int
4221 zfs_release_one(zfs_handle_t *zhp, void *arg)
4222 {
4223         struct holdarg *ha = arg;
4224         zfs_handle_t *szhp;
4225         char name[ZFS_MAXNAMELEN];
4226         int rv = 0;
4227 
4228         (void) snprintf(name, sizeof (name),
4229             "%s@%s", zhp->zfs_name, ha->snapname);
4230 
4231         szhp = make_dataset_handle(zhp->zfs_hdl, name);
4232         if (szhp) {
4233                 nvlist_t *holds = fnvlist_alloc();
4234                 fnvlist_add_boolean(holds, ha->tag);
4235                 fnvlist_add_nvlist(ha->nvl, name, holds);
4236                 fnvlist_free(holds);
4237                 zfs_close(szhp);
4238         }
4239 
4240         if (ha->recursive)
4241                 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4242         zfs_close(zhp);
4243         return (rv);
4244 }
4245 
4246 int
4247 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4248     boolean_t recursive)
4249 {
4250         int ret;
4251         struct holdarg ha;
4252         nvlist_t *errors;
4253         nvpair_t *elem;
4254         libzfs_handle_t *hdl = zhp->zfs_hdl;
4255 
4256         ha.nvl = fnvlist_alloc();