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

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libzfs/common/libzfs_dataset.c
          +++ new/usr/src/lib/libzfs/common/libzfs_dataset.c
↓ open down ↓ 4093 lines elided ↑ open up ↑
4094 4094                  zfs_close(szhp);
4095 4095          }
4096 4096  
4097 4097          if (ha->recursive)
4098 4098                  rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
4099 4099          zfs_close(zhp);
4100 4100          return (rv);
4101 4101  }
4102 4102  
4103 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
4104 4135  zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4105 4136      boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
4106 4137  {
4107 4138          int ret;
4108 4139          struct holdarg ha;
4109      -        nvlist_t *errors;
4110      -        libzfs_handle_t *hdl = zhp->zfs_hdl;
4111      -        char errbuf[1024];
4112      -        nvpair_t *elem;
4113 4140  
4114 4141          ha.nvl = fnvlist_alloc();
4115 4142          ha.snapname = snapname;
4116 4143          ha.tag = tag;
4117 4144          ha.recursive = recursive;
4118 4145          (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4119      -        ret = lzc_hold(ha.nvl, cleanup_fd, &errors);
     4146 +        ret = zfs_hold_apply(zhp, enoent_ok, cleanup_fd, ha.nvl);
4120 4147          fnvlist_free(ha.nvl);
4121 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 +
4122 4163          if (ret == 0)
4123 4164                  return (0);
4124 4165  
4125 4166          if (nvlist_next_nvpair(errors, NULL) == NULL) {
4126 4167                  /* no hold-specific errors */
4127 4168                  (void) snprintf(errbuf, sizeof (errbuf),
4128 4169                      dgettext(TEXT_DOMAIN, "cannot hold"));
4129 4170                  switch (ret) {
4130 4171                  case ENOTSUP:
4131 4172                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
↓ open down ↓ 37 lines elided ↑ open up ↑
4169 4210                  default:
4170 4211                          (void) zfs_standard_error(hdl,
4171 4212                              fnvpair_value_int32(elem), errbuf);
4172 4213                  }
4173 4214          }
4174 4215  
4175 4216          fnvlist_free(errors);
4176 4217          return (ret);
4177 4218  }
4178 4219  
4179      -struct releasearg {
4180      -        nvlist_t *nvl;
4181      -        const char *snapname;
4182      -        const char *tag;
4183      -        boolean_t recursive;
4184      -};
4185      -
4186 4220  static int
4187 4221  zfs_release_one(zfs_handle_t *zhp, void *arg)
4188 4222  {
4189 4223          struct holdarg *ha = arg;
4190 4224          zfs_handle_t *szhp;
4191 4225          char name[ZFS_MAXNAMELEN];
4192 4226          int rv = 0;
4193 4227  
4194 4228          (void) snprintf(name, sizeof (name),
4195 4229              "%s@%s", zhp->zfs_name, ha->snapname);
4196 4230  
4197 4231          szhp = make_dataset_handle(zhp->zfs_hdl, name);
4198 4232          if (szhp) {
4199 4233                  nvlist_t *holds = fnvlist_alloc();
4200 4234                  fnvlist_add_boolean(holds, ha->tag);
4201 4235                  fnvlist_add_nvlist(ha->nvl, name, holds);
     4236 +                fnvlist_free(holds);
4202 4237                  zfs_close(szhp);
4203 4238          }
4204 4239  
4205 4240          if (ha->recursive)
4206 4241                  rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4207 4242          zfs_close(zhp);
4208 4243          return (rv);
4209 4244  }
4210 4245  
4211 4246  int
↓ open down ↓ 256 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX