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


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


4145             elem = nvlist_next_nvpair(errors, elem)) {
4146                 (void) snprintf(errbuf, sizeof (errbuf),
4147                     dgettext(TEXT_DOMAIN,
4148                     "cannot hold snapshot '%s'"), nvpair_name(elem));
4149                 switch (fnvpair_value_int32(elem)) {
4150                 case E2BIG:
4151                         /*
4152                          * Temporary tags wind up having the ds object id
4153                          * prepended. So even if we passed the length check
4154                          * above, it's still possible for the tag to wind
4155                          * up being slightly too long.
4156                          */
4157                         (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
4158                         break;
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();
4222         ha.snapname = snapname;




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

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

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


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




4105 
4106         ha.nvl = fnvlist_alloc();
4107         ha.snapname = snapname;
4108         ha.tag = tag;
4109         ha.recursive = recursive;
4110         (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4111         ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);
4112         fnvlist_free(ha.nvl);
4113 
4114         return (ret);
4115 }
4116 
4117 int
4118 zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
4119 {
4120         int ret;
4121         nvlist_t *errors;
4122         libzfs_handle_t *hdl = zhp->zfs_hdl;
4123         char errbuf[1024];
4124         nvpair_t *elem;
4125  
4126         ret = lzc_hold(holds, cleanup_fd, &errors);
4127  
4128         if (ret == 0)
4129                 return (0);
4130 
4131         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4132                 /* no hold-specific errors */
4133                 (void) snprintf(errbuf, sizeof (errbuf),
4134                     dgettext(TEXT_DOMAIN, "cannot hold"));
4135                 switch (ret) {
4136                 case ENOTSUP:
4137                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4138                             "pool must be upgraded"));
4139                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4140                         break;
4141                 case EINVAL:
4142                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4143                         break;
4144                 default:
4145                         (void) zfs_standard_error(hdl, ret, errbuf);
4146                 }
4147         }


4151             elem = nvlist_next_nvpair(errors, elem)) {
4152                 (void) snprintf(errbuf, sizeof (errbuf),
4153                     dgettext(TEXT_DOMAIN,
4154                     "cannot hold snapshot '%s'"), nvpair_name(elem));
4155                 switch (fnvpair_value_int32(elem)) {
4156                 case E2BIG:
4157                         /*
4158                          * Temporary tags wind up having the ds object id
4159                          * prepended. So even if we passed the length check
4160                          * above, it's still possible for the tag to wind
4161                          * up being slightly too long.
4162                          */
4163                         (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
4164                         break;
4165                 case EINVAL:
4166                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4167                         break;
4168                 case EEXIST:
4169                         (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
4170                         break;




4171                 default:
4172                         (void) zfs_standard_error(hdl,
4173                             fnvpair_value_int32(elem), errbuf);
4174                 }
4175         }
4176 
4177         fnvlist_free(errors);
4178         return (ret);
4179 }
4180 







4181 static int
4182 zfs_release_one(zfs_handle_t *zhp, void *arg)
4183 {
4184         struct holdarg *ha = arg;

4185         char name[ZFS_MAXNAMELEN];
4186         int rv = 0;
4187 
4188         (void) snprintf(name, sizeof (name),
4189             "%s@%s", zhp->zfs_name, ha->snapname);
4190 
4191         if (lzc_exists(name)) {

4192                 nvlist_t *holds = fnvlist_alloc();
4193                 fnvlist_add_boolean(holds, ha->tag);
4194                 fnvlist_add_nvlist(ha->nvl, name, holds);
4195                 fnvlist_free(holds);
4196         }
4197 
4198         if (ha->recursive)
4199                 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4200         zfs_close(zhp);
4201         return (rv);
4202 }
4203 
4204 int
4205 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4206     boolean_t recursive)
4207 {
4208         int ret;
4209         struct holdarg ha;
4210         nvlist_t *errors;
4211         nvpair_t *elem;
4212         libzfs_handle_t *hdl = zhp->zfs_hdl;
4213 
4214         ha.nvl = fnvlist_alloc();
4215         ha.snapname = snapname;