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

@@ -20,11 +20,11 @@
  */
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
+ * Copyright 2013 DEY Storage Systems, Inc.
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2013 Martin Matuska. All rights reserved.
  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  */
 

@@ -1207,16 +1207,19 @@
                 if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
                         uint64_t volsize = zfs_prop_get_int(zhp,
                             ZFS_PROP_VOLSIZE);
                         uint64_t blocksize = zfs_prop_get_int(zhp,
                             ZFS_PROP_VOLBLOCKSIZE);
+                        int ncopies = zfs_prop_get_int(zhp, ZFS_PROP_COPIES);
                         char buf[64];
 
                         switch (prop) {
                         case ZFS_PROP_RESERVATION:
                         case ZFS_PROP_REFRESERVATION:
-                                if (intval > volsize) {
+                                if (intval >
+                                    zvol_volsize_to_reservation_impl(volsize,
+                                    blocksize, ncopies)) {
                                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                             "'%s' is greater than current "
                                             "volume size"), propname);
                                         (void) zfs_error(hdl, EZFS_BADPROP,
                                             errbuf);

@@ -1283,38 +1286,33 @@
         uint64_t old_volsize;
         uint64_t new_volsize;
         uint64_t old_reservation;
         uint64_t new_reservation;
         zfs_prop_t resv_prop;
-        nvlist_t *props;
+        uint64_t volblocksize;
+        int ncopies;
 
         /*
          * If this is an existing volume, and someone is setting the volsize,
          * make sure that it matches the reservation, or add it if necessary.
          */
         old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
         if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
                 return (-1);
         old_reservation = zfs_prop_get_int(zhp, resv_prop);
+        volblocksize = zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE);
+        ncopies = zfs_prop_get_int(zhp, ZFS_PROP_COPIES);
 
-        props = fnvlist_alloc();
-        fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
-            zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
-
-        if ((zvol_volsize_to_reservation(old_volsize, props) !=
-            old_reservation) || nvlist_exists(nvl,
-            zfs_prop_to_name(resv_prop))) {
-                fnvlist_free(props);
+        if ((zvol_volsize_to_reservation_impl(old_volsize, volblocksize,
+            ncopies) != old_reservation) || nvlist_exists(nvl,
+            zfs_prop_to_name(resv_prop)))
                 return (0);
-        }
         if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
-            &new_volsize) != 0) {
-                fnvlist_free(props);
+            &new_volsize) != 0)
                 return (-1);
-        }
-        new_reservation = zvol_volsize_to_reservation(new_volsize, props);
-        fnvlist_free(props);
+        new_reservation = zvol_volsize_to_reservation_impl(new_volsize,
+            volblocksize, ncopies);
 
         if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
             new_reservation) != 0) {
                 (void) no_memory(zhp->zfs_hdl);
                 return (-1);

@@ -4458,19 +4456,19 @@
 
         return (err);
 }
 
 /*
- * Convert the zvol's volume size to an appropriate reservation.
+ * Convert the zvol's volume size to an appropriate reservation. This is a
+ * convenience front-end to zvol_volsize_to_reservation_impl.
  * Note: If this routine is updated, it is necessary to update the ZFS test
  * suite's shell version in reservation.kshlib.
  */
 uint64_t
 zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
 {
-        uint64_t numdb;
-        uint64_t nblocks, volblocksize;
+        uint64_t volblocksize;
         int ncopies;
         char *strval;
 
         if (nvlist_lookup_string(props,
             zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)

@@ -4479,10 +4477,26 @@
                 ncopies = 1;
         if (nvlist_lookup_uint64(props,
             zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
             &volblocksize) != 0)
                 volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
+
+        return (zvol_volsize_to_reservation_impl(volsize, volblocksize,
+            ncopies));
+}
+
+/*
+ * Computes the required reservation to completely contain all blocks of a
+ * zvol at a given volsize.
+ */
+uint64_t
+zvol_volsize_to_reservation_impl(uint64_t volsize, uint64_t volblocksize,
+    int ncopies)
+{
+        uint64_t numdb;
+        uint64_t nblocks;
+
         nblocks = volsize/volblocksize;
         /* start with metadnode L0-L6 */
         numdb = 7;
         /* calculate number of indirects */
         while (nblocks > 1) {