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         }
4142 
4143         for (elem = nvlist_next_nvpair(errors, NULL);


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;
4223         ha.tag = tag;
4224         ha.recursive = recursive;
4225         (void) zfs_release_one(zfs_handle_dup(zhp), &ha);

4226         ret = lzc_release(ha.nvl, &errors);
4227         fnvlist_free(ha.nvl);
4228 
4229         if (ret == 0)


4230                 return (0);

4231 
4232         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4233                 /* no hold-specific errors */
4234                 char errbuf[1024];
4235 
4236                 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4237                     "cannot release"));
4238                 switch (errno) {
4239                 case ENOTSUP:
4240                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4241                             "pool must be upgraded"));
4242                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4243                         break;
4244                 default:
4245                         (void) zfs_standard_error_fmt(hdl, errno, errbuf);
4246                 }
4247         }
4248 
4249         for (elem = nvlist_next_nvpair(errors, NULL);
4250             elem != NULL;




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         errors = NULL;
4127         ret = lzc_hold(holds, cleanup_fd, &errors);
4128  
4129         if (ret == 0) {
4130                 /* There may be errors even in the success case. */
4131                 fnvlist_free(errors);
4132                 return (0);
4133         }
4134 
4135         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4136                 /* no hold-specific errors */
4137                 (void) snprintf(errbuf, sizeof (errbuf),
4138                     dgettext(TEXT_DOMAIN, "cannot hold"));
4139                 switch (ret) {
4140                 case ENOTSUP:
4141                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4142                             "pool must be upgraded"));
4143                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4144                         break;
4145                 case EINVAL:
4146                         (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4147                         break;
4148                 default:
4149                         (void) zfs_standard_error(hdl, ret, errbuf);
4150                 }
4151         }
4152 
4153         for (elem = nvlist_next_nvpair(errors, NULL);


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




4175                 default:
4176                         (void) zfs_standard_error(hdl,
4177                             fnvpair_value_int32(elem), errbuf);
4178                 }
4179         }
4180 
4181         fnvlist_free(errors);
4182         return (ret);
4183 }
4184 







4185 static int
4186 zfs_release_one(zfs_handle_t *zhp, void *arg)
4187 {
4188         struct holdarg *ha = arg;

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

4196                 nvlist_t *holds = fnvlist_alloc();
4197                 fnvlist_add_boolean(holds, ha->tag);
4198                 fnvlist_add_nvlist(ha->nvl, name, holds);
4199                 fnvlist_free(holds);
4200         }
4201 
4202         if (ha->recursive)
4203                 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4204         zfs_close(zhp);
4205         return (rv);
4206 }
4207 
4208 int
4209 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4210     boolean_t recursive)
4211 {
4212         int ret;
4213         struct holdarg ha;
4214         nvlist_t *errors;
4215         nvpair_t *elem;
4216         libzfs_handle_t *hdl = zhp->zfs_hdl;
4217 
4218         ha.nvl = fnvlist_alloc();
4219         ha.snapname = snapname;
4220         ha.tag = tag;
4221         ha.recursive = recursive;
4222         (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
4223         errors = NULL;
4224         ret = lzc_release(ha.nvl, &errors);
4225         fnvlist_free(ha.nvl);
4226 
4227         if (ret == 0) {
4228                 /* There may be errors even in the success case. */
4229                 fnvlist_free(errors);
4230                 return (0);
4231         }
4232 
4233         if (nvlist_next_nvpair(errors, NULL) == NULL) {
4234                 /* no hold-specific errors */
4235                 char errbuf[1024];
4236 
4237                 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4238                     "cannot release"));
4239                 switch (errno) {
4240                 case ENOTSUP:
4241                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4242                             "pool must be upgraded"));
4243                         (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4244                         break;
4245                 default:
4246                         (void) zfs_standard_error_fmt(hdl, errno, errbuf);
4247                 }
4248         }
4249 
4250         for (elem = nvlist_next_nvpair(errors, NULL);
4251             elem != NULL;