Print this page
3740 Poor ZFS send / receive performance due to snapshot hold / release processing
Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>

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 ↓ 17 lines elided ↑ open up ↑
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright (c) 2012 by Delphix. All rights reserved.
  25   25   * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  26   26   * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  27   27   * Copyright (c) 2013 Martin Matuska. All rights reserved.
       28 + * Copyright (c) 2013 Steven Hartland. All rights reserved.
  28   29   */
  29   30  
  30   31  #include <ctype.h>
  31   32  #include <errno.h>
  32   33  #include <libintl.h>
  33   34  #include <math.h>
  34   35  #include <stdio.h>
  35   36  #include <stdlib.h>
  36   37  #include <strings.h>
  37   38  #include <unistd.h>
↓ open down ↓ 3059 lines elided ↑ open up ↑
3097 3098  
3098 3099  struct destroydata {
3099 3100          nvlist_t *nvl;
3100 3101          const char *snapname;
3101 3102  };
3102 3103  
3103 3104  static int
3104 3105  zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
3105 3106  {
3106 3107          struct destroydata *dd = arg;
3107      -        zfs_handle_t *szhp;
3108 3108          char name[ZFS_MAXNAMELEN];
3109 3109          int rv = 0;
3110 3110  
3111 3111          (void) snprintf(name, sizeof (name),
3112 3112              "%s@%s", zhp->zfs_name, dd->snapname);
3113 3113  
3114      -        szhp = make_dataset_handle(zhp->zfs_hdl, name);
3115      -        if (szhp) {
     3114 +        if (lzc_exists(name))
3116 3115                  verify(nvlist_add_boolean(dd->nvl, name) == 0);
3117      -                zfs_close(szhp);
3118      -        }
3119 3116  
3120 3117          rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
3121 3118          zfs_close(zhp);
3122 3119          return (rv);
3123 3120  }
3124 3121  
3125 3122  /*
3126 3123   * Destroys all snapshots with the given name in zhp & descendants.
3127 3124   */
3128 3125  int
3129 3126  zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
3130 3127  {
3131 3128          int ret;
3132 3129          struct destroydata dd = { 0 };
3133 3130  
3134 3131          dd.snapname = snapname;
3135 3132          verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
3136 3133          (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
3137 3134  
3138      -        if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) {
     3135 +        if (nvlist_empty(dd.nvl)) {
3139 3136                  ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
3140 3137                      dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
3141 3138                      zhp->zfs_name, snapname);
3142 3139          } else {
3143 3140                  ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);
3144 3141          }
3145 3142          nvlist_free(dd.nvl);
3146 3143          return (ret);
3147 3144  }
3148 3145  
↓ open down ↓ 4 lines elided ↑ open up ↑
3153 3150  zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
3154 3151  {
3155 3152          int ret;
3156 3153          nvlist_t *errlist;
3157 3154  
3158 3155          ret = lzc_destroy_snaps(snaps, defer, &errlist);
3159 3156  
3160 3157          if (ret == 0)
3161 3158                  return (0);
3162 3159  
3163      -        if (nvlist_next_nvpair(errlist, NULL) == NULL) {
     3160 +        if (nvlist_empty(errlist)) {
3164 3161                  char errbuf[1024];
3165 3162                  (void) snprintf(errbuf, sizeof (errbuf),
3166 3163                      dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));
3167 3164  
3168 3165                  ret = zfs_standard_error(hdl, ret, errbuf);
3169 3166          }
3170 3167          for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
3171 3168              pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
3172 3169                  char errbuf[1024];
3173 3170                  (void) snprintf(errbuf, sizeof (errbuf),
↓ open down ↓ 901 lines elided ↑ open up ↑
4075 4072          nvlist_t *nvl;
4076 4073          const char *snapname;
4077 4074          const char *tag;
4078 4075          boolean_t recursive;
4079 4076  };
4080 4077  
4081 4078  static int
4082 4079  zfs_hold_one(zfs_handle_t *zhp, void *arg)
4083 4080  {
4084 4081          struct holdarg *ha = arg;
4085      -        zfs_handle_t *szhp;
4086 4082          char name[ZFS_MAXNAMELEN];
4087 4083          int rv = 0;
4088 4084  
4089 4085          (void) snprintf(name, sizeof (name),
4090 4086              "%s@%s", zhp->zfs_name, ha->snapname);
4091 4087  
4092      -        szhp = make_dataset_handle(zhp->zfs_hdl, name);
4093      -        if (szhp) {
     4088 +        if (lzc_exists(name))
4094 4089                  fnvlist_add_string(ha->nvl, name, ha->tag);
4095      -                zfs_close(szhp);
4096      -        }
4097 4090  
4098 4091          if (ha->recursive)
4099 4092                  rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
4100 4093          zfs_close(zhp);
4101 4094          return (rv);
4102 4095  }
4103 4096  
4104 4097  int
4105 4098  zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4106      -    boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
     4099 +    boolean_t recursive, int cleanup_fd)
4107 4100  {
4108 4101          int ret;
4109 4102          struct holdarg ha;
4110      -        nvlist_t *errors;
4111      -        libzfs_handle_t *hdl = zhp->zfs_hdl;
4112      -        char errbuf[1024];
4113      -        nvpair_t *elem;
4114 4103  
4115 4104          ha.nvl = fnvlist_alloc();
4116 4105          ha.snapname = snapname;
4117 4106          ha.tag = tag;
4118 4107          ha.recursive = recursive;
4119 4108          (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4120 4109  
4121      -        if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {
     4110 +        if (nvlist_empty(ha.nvl)) {
     4111 +                char errbuf[1024];
     4112 +
4122 4113                  fnvlist_free(ha.nvl);
4123 4114                  ret = ENOENT;
4124      -                if (!enoent_ok) {
4125      -                        (void) snprintf(errbuf, sizeof (errbuf),
4126      -                            dgettext(TEXT_DOMAIN,
4127      -                            "cannot hold snapshot '%s@%s'"),
4128      -                            zhp->zfs_name, snapname);
4129      -                        (void) zfs_standard_error(hdl, ret, errbuf);
4130      -                }
     4115 +                (void) snprintf(errbuf, sizeof (errbuf),
     4116 +                    dgettext(TEXT_DOMAIN,
     4117 +                    "cannot hold snapshot '%s@%s'"),
     4118 +                    zhp->zfs_name, snapname);
     4119 +                (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf);
4131 4120                  return (ret);
4132 4121          }
4133 4122  
4134      -        ret = lzc_hold(ha.nvl, cleanup_fd, &errors);
     4123 +        ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);
4135 4124          fnvlist_free(ha.nvl);
4136 4125  
4137      -        if (ret == 0)
     4126 +        return (ret);
     4127 +}
     4128 +
     4129 +int
     4130 +zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
     4131 +{
     4132 +        int ret;
     4133 +        nvlist_t *errors;
     4134 +        libzfs_handle_t *hdl = zhp->zfs_hdl;
     4135 +        char errbuf[1024];
     4136 +        nvpair_t *elem;
     4137 +
     4138 +        errors = NULL;
     4139 +        ret = lzc_hold(holds, cleanup_fd, &errors);
     4140 +
     4141 +        if (ret == 0) {
     4142 +                /* There may be errors even in the success case. */
     4143 +                fnvlist_free(errors);
4138 4144                  return (0);
     4145 +        }
4139 4146  
4140      -        if (nvlist_next_nvpair(errors, NULL) == NULL) {
     4147 +        if (nvlist_empty(errors)) {
4141 4148                  /* no hold-specific errors */
4142 4149                  (void) snprintf(errbuf, sizeof (errbuf),
4143 4150                      dgettext(TEXT_DOMAIN, "cannot hold"));
4144 4151                  switch (ret) {
4145 4152                  case ENOTSUP:
4146 4153                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4147 4154                              "pool must be upgraded"));
4148 4155                          (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4149 4156                          break;
4150 4157                  case EINVAL:
↓ open down ↓ 19 lines elided ↑ open up ↑
4170 4177                           * up being slightly too long.
4171 4178                           */
4172 4179                          (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
4173 4180                          break;
4174 4181                  case EINVAL:
4175 4182                          (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4176 4183                          break;
4177 4184                  case EEXIST:
4178 4185                          (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
4179 4186                          break;
4180      -                case ENOENT:
4181      -                        if (enoent_ok)
4182      -                                return (ENOENT);
4183      -                        /* FALLTHROUGH */
4184 4187                  default:
4185 4188                          (void) zfs_standard_error(hdl,
4186 4189                              fnvpair_value_int32(elem), errbuf);
4187 4190                  }
4188 4191          }
4189 4192  
4190 4193          fnvlist_free(errors);
4191 4194          return (ret);
4192 4195  }
4193 4196  
4194      -struct releasearg {
4195      -        nvlist_t *nvl;
4196      -        const char *snapname;
4197      -        const char *tag;
4198      -        boolean_t recursive;
4199      -};
4200      -
4201 4197  static int
4202 4198  zfs_release_one(zfs_handle_t *zhp, void *arg)
4203 4199  {
4204 4200          struct holdarg *ha = arg;
4205      -        zfs_handle_t *szhp;
4206 4201          char name[ZFS_MAXNAMELEN];
4207 4202          int rv = 0;
4208 4203  
4209 4204          (void) snprintf(name, sizeof (name),
4210 4205              "%s@%s", zhp->zfs_name, ha->snapname);
4211 4206  
4212      -        szhp = make_dataset_handle(zhp->zfs_hdl, name);
4213      -        if (szhp) {
     4207 +        if (lzc_exists(name)) {
4214 4208                  nvlist_t *holds = fnvlist_alloc();
4215 4209                  fnvlist_add_boolean(holds, ha->tag);
4216 4210                  fnvlist_add_nvlist(ha->nvl, name, holds);
4217      -                zfs_close(szhp);
     4211 +                fnvlist_free(holds);
4218 4212          }
4219 4213  
4220 4214          if (ha->recursive)
4221 4215                  rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4222 4216          zfs_close(zhp);
4223 4217          return (rv);
4224 4218  }
4225 4219  
4226 4220  int
4227 4221  zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4228 4222      boolean_t recursive)
4229 4223  {
4230 4224          int ret;
4231 4225          struct holdarg ha;
4232      -        nvlist_t *errors;
     4226 +        nvlist_t *errors = NULL;
4233 4227          nvpair_t *elem;
4234 4228          libzfs_handle_t *hdl = zhp->zfs_hdl;
4235 4229          char errbuf[1024];
4236 4230  
4237 4231          ha.nvl = fnvlist_alloc();
4238 4232          ha.snapname = snapname;
4239 4233          ha.tag = tag;
4240 4234          ha.recursive = recursive;
4241 4235          (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
4242 4236  
4243      -        if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {
     4237 +        if (nvlist_empty(ha.nvl)) {
4244 4238                  fnvlist_free(ha.nvl);
4245 4239                  ret = ENOENT;
4246 4240                  (void) snprintf(errbuf, sizeof (errbuf),
4247 4241                      dgettext(TEXT_DOMAIN,
4248 4242                      "cannot release hold from snapshot '%s@%s'"),
4249 4243                      zhp->zfs_name, snapname);
4250 4244                  (void) zfs_standard_error(hdl, ret, errbuf);
4251 4245                  return (ret);
4252 4246          }
4253 4247  
4254 4248          ret = lzc_release(ha.nvl, &errors);
4255 4249          fnvlist_free(ha.nvl);
4256 4250  
4257      -        if (ret == 0)
     4251 +        if (ret == 0) {
     4252 +                /* There may be errors even in the success case. */
     4253 +                fnvlist_free(errors);
4258 4254                  return (0);
     4255 +        }
4259 4256  
4260      -        if (nvlist_next_nvpair(errors, NULL) == NULL) {
     4257 +        if (nvlist_empty(errors)) {
4261 4258                  /* no hold-specific errors */
4262 4259                  (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4263 4260                      "cannot release"));
4264 4261                  switch (errno) {
4265 4262                  case ENOTSUP:
4266 4263                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4267 4264                              "pool must be upgraded"));
4268 4265                          (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4269 4266                          break;
4270 4267                  default:
↓ open down ↓ 221 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX