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


   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2012 by Delphix. All rights reserved.
  25  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  26  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  27  * Copyright (c) 2013 Martin Matuska. All rights reserved.

  28  */
  29 
  30 #include <ctype.h>
  31 #include <errno.h>
  32 #include <libintl.h>
  33 #include <math.h>
  34 #include <stdio.h>
  35 #include <stdlib.h>
  36 #include <strings.h>
  37 #include <unistd.h>
  38 #include <stddef.h>
  39 #include <zone.h>
  40 #include <fcntl.h>
  41 #include <sys/mntent.h>
  42 #include <sys/mount.h>
  43 #include <priv.h>
  44 #include <pwd.h>
  45 #include <grp.h>
  46 #include <stddef.h>
  47 #include <ucred.h>


4065                                 return (ret);
4066                         zua++;
4067                         zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
4068                 }
4069         }
4070 
4071         return (0);
4072 }
4073 
4074 struct holdarg {
4075         nvlist_t *nvl;
4076         const char *snapname;
4077         const char *tag;
4078         boolean_t recursive;
4079 };
4080 
4081 static int
4082 zfs_hold_one(zfs_handle_t *zhp, void *arg)
4083 {
4084         struct holdarg *ha = arg;
4085         zfs_handle_t *szhp;
4086         char name[ZFS_MAXNAMELEN];
4087         int rv = 0;
4088 
4089         (void) snprintf(name, sizeof (name),
4090             "%s@%s", zhp->zfs_name, ha->snapname);
4091 
4092         szhp = make_dataset_handle(zhp->zfs_hdl, name);
4093         if (szhp) {
4094                 fnvlist_add_string(ha->nvl, name, ha->tag);
4095                 zfs_close(szhp);
4096         }
4097 
4098         if (ha->recursive)
4099                 rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
4100         zfs_close(zhp);
4101         return (rv);
4102 }
4103 
4104 int
4105 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4106     boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
4107 {
4108         int ret;
4109         struct holdarg ha;
4110         nvlist_t *errors;
4111         libzfs_handle_t *hdl = zhp->zfs_hdl;
4112         char errbuf[1024];
4113         nvpair_t *elem;
4114 
4115         ha.nvl = fnvlist_alloc();
4116         ha.snapname = snapname;
4117         ha.tag = tag;
4118         ha.recursive = recursive;
4119         (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4120 
4121         if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {


4122                 fnvlist_free(ha.nvl);
4123                 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                 }
4131                 return (ret);
4132         }
4133 
4134         ret = lzc_hold(ha.nvl, cleanup_fd, &errors);
4135         fnvlist_free(ha.nvl);
4136 
4137         if (ret == 0)

















4138                 return (0);

4139 
4140         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4141                 /* no hold-specific errors */
4142                 (void) snprintf(errbuf, sizeof (errbuf),
4143                     dgettext(TEXT_DOMAIN, "cannot hold"));
4144                 switch (ret) {
4145                 case ENOTSUP:
4146                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4147                             "pool must be upgraded"));
4148                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4149                         break;
4150                 case EINVAL:
4151                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4152                         break;
4153                 default:
4154                         (void) zfs_standard_error(hdl, ret, errbuf);
4155                 }
4156         }
4157 
4158         for (elem = nvlist_next_nvpair(errors, NULL);


4160             elem = nvlist_next_nvpair(errors, elem)) {
4161                 (void) snprintf(errbuf, sizeof (errbuf),
4162                     dgettext(TEXT_DOMAIN,
4163                     "cannot hold snapshot '%s'"), nvpair_name(elem));
4164                 switch (fnvpair_value_int32(elem)) {
4165                 case E2BIG:
4166                         /*
4167                          * Temporary tags wind up having the ds object id
4168                          * prepended. So even if we passed the length check
4169                          * above, it's still possible for the tag to wind
4170                          * up being slightly too long.
4171                          */
4172                         (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
4173                         break;
4174                 case EINVAL:
4175                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4176                         break;
4177                 case EEXIST:
4178                         (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
4179                         break;
4180                 case ENOENT:
4181                         if (enoent_ok)
4182                                 return (ENOENT);
4183                         /* FALLTHROUGH */
4184                 default:
4185                         (void) zfs_standard_error(hdl,
4186                             fnvpair_value_int32(elem), errbuf);
4187                 }
4188         }
4189 
4190         fnvlist_free(errors);
4191         return (ret);
4192 }
4193 
4194 struct releasearg {
4195         nvlist_t *nvl;
4196         const char *snapname;
4197         const char *tag;
4198         boolean_t recursive;
4199 };
4200 
4201 static int
4202 zfs_release_one(zfs_handle_t *zhp, void *arg)
4203 {
4204         struct holdarg *ha = arg;
4205         zfs_handle_t *szhp;
4206         char name[ZFS_MAXNAMELEN];
4207         int rv = 0;
4208 
4209         (void) snprintf(name, sizeof (name),
4210             "%s@%s", zhp->zfs_name, ha->snapname);
4211 
4212         szhp = make_dataset_handle(zhp->zfs_hdl, name);
4213         if (szhp) {
4214                 nvlist_t *holds = fnvlist_alloc();
4215                 fnvlist_add_boolean(holds, ha->tag);
4216                 fnvlist_add_nvlist(ha->nvl, name, holds);
4217                 zfs_close(szhp);
4218         }
4219 
4220         if (ha->recursive)
4221                 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4222         zfs_close(zhp);
4223         return (rv);
4224 }
4225 
4226 int
4227 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4228     boolean_t recursive)
4229 {
4230         int ret;
4231         struct holdarg ha;
4232         nvlist_t *errors;
4233         nvpair_t *elem;
4234         libzfs_handle_t *hdl = zhp->zfs_hdl;
4235         char errbuf[1024];
4236 
4237         ha.nvl = fnvlist_alloc();
4238         ha.snapname = snapname;
4239         ha.tag = tag;
4240         ha.recursive = recursive;
4241         (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
4242 
4243         if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {
4244                 fnvlist_free(ha.nvl);
4245                 ret = ENOENT;
4246                 (void) snprintf(errbuf, sizeof (errbuf),
4247                     dgettext(TEXT_DOMAIN,
4248                     "cannot release hold from snapshot '%s@%s'"),
4249                     zhp->zfs_name, snapname);
4250                 (void) zfs_standard_error(hdl, ret, errbuf);
4251                 return (ret);
4252         }
4253 

4254         ret = lzc_release(ha.nvl, &errors);
4255         fnvlist_free(ha.nvl);
4256 
4257         if (ret == 0)


4258                 return (0);

4259 
4260         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4261                 /* no hold-specific errors */
4262                 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4263                     "cannot release"));
4264                 switch (errno) {
4265                 case ENOTSUP:
4266                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4267                             "pool must be upgraded"));
4268                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4269                         break;
4270                 default:
4271                         (void) zfs_standard_error_fmt(hdl, errno, errbuf);
4272                 }
4273         }
4274 
4275         for (elem = nvlist_next_nvpair(errors, NULL);
4276             elem != NULL;
4277             elem = nvlist_next_nvpair(errors, elem)) {
4278                 (void) snprintf(errbuf, sizeof (errbuf),




   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2012 by Delphix. All rights reserved.
  25  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  26  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  27  * Copyright (c) 2013 Martin Matuska. All rights reserved.
  28  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  29  */
  30 
  31 #include <ctype.h>
  32 #include <errno.h>
  33 #include <libintl.h>
  34 #include <math.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <strings.h>
  38 #include <unistd.h>
  39 #include <stddef.h>
  40 #include <zone.h>
  41 #include <fcntl.h>
  42 #include <sys/mntent.h>
  43 #include <sys/mount.h>
  44 #include <priv.h>
  45 #include <pwd.h>
  46 #include <grp.h>
  47 #include <stddef.h>
  48 #include <ucred.h>


4066                                 return (ret);
4067                         zua++;
4068                         zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
4069                 }
4070         }
4071 
4072         return (0);
4073 }
4074 
4075 struct holdarg {
4076         nvlist_t *nvl;
4077         const char *snapname;
4078         const char *tag;
4079         boolean_t recursive;
4080 };
4081 
4082 static int
4083 zfs_hold_one(zfs_handle_t *zhp, void *arg)
4084 {
4085         struct holdarg *ha = arg;

4086         char name[ZFS_MAXNAMELEN];
4087         int rv = 0;
4088 
4089         (void) snprintf(name, sizeof (name),
4090             "%s@%s", zhp->zfs_name, ha->snapname);
4091 
4092         if (lzc_exists(name))

4093                 fnvlist_add_string(ha->nvl, name, ha->tag);


4094 
4095         if (ha->recursive)
4096                 rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
4097         zfs_close(zhp);
4098         return (rv);
4099 }
4100 
4101 int
4102 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4103     boolean_t recursive, int cleanup_fd)
4104 {
4105         int ret;
4106         struct holdarg ha;




4107 
4108         ha.nvl = fnvlist_alloc();
4109         ha.snapname = snapname;
4110         ha.tag = tag;
4111         ha.recursive = recursive;
4112         (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4113 
4114         if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {
4115                 char errbuf[1024];
4116 
4117                 fnvlist_free(ha.nvl);
4118                 ret = ENOENT;

4119                 (void) snprintf(errbuf, sizeof (errbuf),
4120                     dgettext(TEXT_DOMAIN,
4121                     "cannot hold snapshot '%s@%s'"),
4122                     zhp->zfs_name, snapname);
4123                 (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf);

4124                 return (ret);
4125         }
4126 
4127         ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);
4128         fnvlist_free(ha.nvl);
4129 
4130         return (ret);
4131 }
4132 
4133 int
4134 zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
4135 {
4136         int ret;
4137         nvlist_t *errors;
4138         libzfs_handle_t *hdl = zhp->zfs_hdl;
4139         char errbuf[1024];
4140         nvpair_t *elem;
4141 
4142         errors = NULL;
4143         ret = lzc_hold(holds, cleanup_fd, &errors);
4144 
4145         if (ret == 0) {
4146                 /* There may be errors even in the success case. */
4147                 fnvlist_free(errors);
4148                 return (0);
4149         }
4150 
4151         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4152                 /* no hold-specific errors */
4153                 (void) snprintf(errbuf, sizeof (errbuf),
4154                     dgettext(TEXT_DOMAIN, "cannot hold"));
4155                 switch (ret) {
4156                 case ENOTSUP:
4157                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4158                             "pool must be upgraded"));
4159                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4160                         break;
4161                 case EINVAL:
4162                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4163                         break;
4164                 default:
4165                         (void) zfs_standard_error(hdl, ret, errbuf);
4166                 }
4167         }
4168 
4169         for (elem = nvlist_next_nvpair(errors, NULL);


4171             elem = nvlist_next_nvpair(errors, elem)) {
4172                 (void) snprintf(errbuf, sizeof (errbuf),
4173                     dgettext(TEXT_DOMAIN,
4174                     "cannot hold snapshot '%s'"), nvpair_name(elem));
4175                 switch (fnvpair_value_int32(elem)) {
4176                 case E2BIG:
4177                         /*
4178                          * Temporary tags wind up having the ds object id
4179                          * prepended. So even if we passed the length check
4180                          * above, it's still possible for the tag to wind
4181                          * up being slightly too long.
4182                          */
4183                         (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
4184                         break;
4185                 case EINVAL:
4186                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4187                         break;
4188                 case EEXIST:
4189                         (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
4190                         break;




4191                 default:
4192                         (void) zfs_standard_error(hdl,
4193                             fnvpair_value_int32(elem), errbuf);
4194                 }
4195         }
4196 
4197         fnvlist_free(errors);
4198         return (ret);
4199 }
4200 







4201 static int
4202 zfs_release_one(zfs_handle_t *zhp, void *arg)
4203 {
4204         struct holdarg *ha = arg;

4205         char name[ZFS_MAXNAMELEN];
4206         int rv = 0;
4207 
4208         (void) snprintf(name, sizeof (name),
4209             "%s@%s", zhp->zfs_name, ha->snapname);
4210 
4211         if (lzc_exists(name)) {

4212                 nvlist_t *holds = fnvlist_alloc();
4213                 fnvlist_add_boolean(holds, ha->tag);
4214                 fnvlist_add_nvlist(ha->nvl, name, holds);
4215                 fnvlist_free(holds);
4216         }
4217 
4218         if (ha->recursive)
4219                 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4220         zfs_close(zhp);
4221         return (rv);
4222 }
4223 
4224 int
4225 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4226     boolean_t recursive)
4227 {
4228         int ret;
4229         struct holdarg ha;
4230         nvlist_t *errors;
4231         nvpair_t *elem;
4232         libzfs_handle_t *hdl = zhp->zfs_hdl;
4233         char errbuf[1024];
4234 
4235         ha.nvl = fnvlist_alloc();
4236         ha.snapname = snapname;
4237         ha.tag = tag;
4238         ha.recursive = recursive;
4239         (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
4240 
4241         if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {
4242                 fnvlist_free(ha.nvl);
4243                 ret = ENOENT;
4244                 (void) snprintf(errbuf, sizeof (errbuf),
4245                     dgettext(TEXT_DOMAIN,
4246                     "cannot release hold from snapshot '%s@%s'"),
4247                     zhp->zfs_name, snapname);
4248                 (void) zfs_standard_error(hdl, ret, errbuf);
4249                 return (ret);
4250         }
4251 
4252         errors = NULL;
4253         ret = lzc_release(ha.nvl, &errors);
4254         fnvlist_free(ha.nvl);
4255 
4256         if (ret == 0) {
4257                 /* There may be errors even in the success case. */
4258                 fnvlist_free(errors);
4259                 return (0);
4260         }
4261 
4262         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4263                 /* no hold-specific errors */
4264                 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4265                     "cannot release"));
4266                 switch (errno) {
4267                 case ENOTSUP:
4268                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4269                             "pool must be upgraded"));
4270                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4271                         break;
4272                 default:
4273                         (void) zfs_standard_error_fmt(hdl, errno, errbuf);
4274                 }
4275         }
4276 
4277         for (elem = nvlist_next_nvpair(errors, NULL);
4278             elem != NULL;
4279             elem = nvlist_next_nvpair(errors, elem)) {
4280                 (void) snprintf(errbuf, sizeof (errbuf),