Print this page
4012 Upper limit of zfs set bounds check for refreservation on volumes is too low

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 ↓ 14 lines elided ↑ open up ↑
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  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) 2013 by Delphix. All rights reserved.
  25      - * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
       25 + * Copyright 2013 DEY Storage Systems, Inc.
  26   26   * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  27   27   * Copyright (c) 2013 Martin Matuska. All rights reserved.
  28   28   * Copyright (c) 2013 Steven Hartland. All rights reserved.
  29   29   */
  30   30  
  31   31  #include <ctype.h>
  32   32  #include <errno.h>
  33   33  #include <libintl.h>
  34   34  #include <math.h>
  35   35  #include <stdio.h>
↓ open down ↓ 1166 lines elided ↑ open up ↑
1202 1202  
1203 1203                  /*
1204 1204                   * For changes to existing volumes, we have some additional
1205 1205                   * checks to enforce.
1206 1206                   */
1207 1207                  if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1208 1208                          uint64_t volsize = zfs_prop_get_int(zhp,
1209 1209                              ZFS_PROP_VOLSIZE);
1210 1210                          uint64_t blocksize = zfs_prop_get_int(zhp,
1211 1211                              ZFS_PROP_VOLBLOCKSIZE);
     1212 +                        int ncopies = zfs_prop_get_int(zhp, ZFS_PROP_COPIES);
1212 1213                          char buf[64];
1213 1214  
1214 1215                          switch (prop) {
1215 1216                          case ZFS_PROP_RESERVATION:
1216 1217                          case ZFS_PROP_REFRESERVATION:
1217      -                                if (intval > volsize) {
     1218 +                                if (intval >
     1219 +                                    zvol_volsize_to_reservation_impl(volsize,
     1220 +                                    blocksize, ncopies)) {
1218 1221                                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1219 1222                                              "'%s' is greater than current "
1220 1223                                              "volume size"), propname);
1221 1224                                          (void) zfs_error(hdl, EZFS_BADPROP,
1222 1225                                              errbuf);
1223 1226                                          goto error;
1224 1227                                  }
1225 1228                                  break;
1226 1229  
1227 1230                          case ZFS_PROP_VOLSIZE:
↓ open down ↓ 50 lines elided ↑ open up ↑
1278 1281  }
1279 1282  
1280 1283  int
1281 1284  zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
1282 1285  {
1283 1286          uint64_t old_volsize;
1284 1287          uint64_t new_volsize;
1285 1288          uint64_t old_reservation;
1286 1289          uint64_t new_reservation;
1287 1290          zfs_prop_t resv_prop;
1288      -        nvlist_t *props;
     1291 +        uint64_t volblocksize;
     1292 +        int ncopies;
1289 1293  
1290 1294          /*
1291 1295           * If this is an existing volume, and someone is setting the volsize,
1292 1296           * make sure that it matches the reservation, or add it if necessary.
1293 1297           */
1294 1298          old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
1295 1299          if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1296 1300                  return (-1);
1297 1301          old_reservation = zfs_prop_get_int(zhp, resv_prop);
     1302 +        volblocksize = zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE);
     1303 +        ncopies = zfs_prop_get_int(zhp, ZFS_PROP_COPIES);
1298 1304  
1299      -        props = fnvlist_alloc();
1300      -        fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
1301      -            zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
1302      -
1303      -        if ((zvol_volsize_to_reservation(old_volsize, props) !=
1304      -            old_reservation) || nvlist_exists(nvl,
1305      -            zfs_prop_to_name(resv_prop))) {
1306      -                fnvlist_free(props);
     1305 +        if ((zvol_volsize_to_reservation_impl(old_volsize, volblocksize,
     1306 +            ncopies) != old_reservation) || nvlist_exists(nvl,
     1307 +            zfs_prop_to_name(resv_prop)))
1307 1308                  return (0);
1308      -        }
1309 1309          if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1310      -            &new_volsize) != 0) {
1311      -                fnvlist_free(props);
     1310 +            &new_volsize) != 0)
1312 1311                  return (-1);
1313      -        }
1314      -        new_reservation = zvol_volsize_to_reservation(new_volsize, props);
1315      -        fnvlist_free(props);
     1312 +        new_reservation = zvol_volsize_to_reservation_impl(new_volsize,
     1313 +            volblocksize, ncopies);
1316 1314  
1317 1315          if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
1318 1316              new_reservation) != 0) {
1319 1317                  (void) no_memory(zhp->zfs_hdl);
1320 1318                  return (-1);
1321 1319          }
1322 1320          return (1);
1323 1321  }
1324 1322  
1325 1323  void
↓ open down ↓ 3127 lines elided ↑ open up ↑
4453 4451                  default:
4454 4452                          err = zfs_standard_error_fmt(hdl, errno, errbuf);
4455 4453                          break;
4456 4454                  }
4457 4455          }
4458 4456  
4459 4457          return (err);
4460 4458  }
4461 4459  
4462 4460  /*
4463      - * Convert the zvol's volume size to an appropriate reservation.
     4461 + * Convert the zvol's volume size to an appropriate reservation. This is a
     4462 + * convenience front-end to zvol_volsize_to_reservation_impl.
4464 4463   * Note: If this routine is updated, it is necessary to update the ZFS test
4465 4464   * suite's shell version in reservation.kshlib.
4466 4465   */
4467 4466  uint64_t
4468 4467  zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
4469 4468  {
4470      -        uint64_t numdb;
4471      -        uint64_t nblocks, volblocksize;
     4469 +        uint64_t volblocksize;
4472 4470          int ncopies;
4473 4471          char *strval;
4474 4472  
4475 4473          if (nvlist_lookup_string(props,
4476 4474              zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
4477 4475                  ncopies = atoi(strval);
4478 4476          else
4479 4477                  ncopies = 1;
4480 4478          if (nvlist_lookup_uint64(props,
4481 4479              zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
4482 4480              &volblocksize) != 0)
4483 4481                  volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
     4482 +
     4483 +        return (zvol_volsize_to_reservation_impl(volsize, volblocksize,
     4484 +            ncopies));
     4485 +}
     4486 +
     4487 +/*
     4488 + * Computes the required reservation to completely contain all blocks of a
     4489 + * zvol at a given volsize.
     4490 + */
     4491 +uint64_t
     4492 +zvol_volsize_to_reservation_impl(uint64_t volsize, uint64_t volblocksize,
     4493 +    int ncopies)
     4494 +{
     4495 +        uint64_t numdb;
     4496 +        uint64_t nblocks;
     4497 +
4484 4498          nblocks = volsize/volblocksize;
4485 4499          /* start with metadnode L0-L6 */
4486 4500          numdb = 7;
4487 4501          /* calculate number of indirects */
4488 4502          while (nblocks > 1) {
4489 4503                  nblocks += DNODES_PER_LEVEL - 1;
4490 4504                  nblocks /= DNODES_PER_LEVEL;
4491 4505                  numdb += nblocks;
4492 4506          }
4493 4507          numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
4494 4508          volsize *= ncopies;
4495 4509          /*
4496 4510           * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
4497 4511           * compressed, but in practice they compress down to about
4498 4512           * 1100 bytes
4499 4513           */
4500 4514          numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
4501 4515          volsize += numdb;
4502 4516          return (volsize);
4503 4517  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX