Print this page
6562 Refquota on receive doesn't account for overage
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
        
@@ -23,10 +23,11 @@
  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  * Copyright (c) 2014 RackTop Systems.
  * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
  * Copyright (c) 2014 Integros [integros.com]
+ * Copyright 2016, OmniTI Computer Consulting, Inc. All rights reserved.
  */
 
 #include <sys/dmu_objset.h>
 #include <sys/dsl_dataset.h>
 #include <sys/dsl_dir.h>
@@ -76,10 +77,12 @@
 
 #define DS_REF_MAX      (1ULL << 62)
 
 extern inline dsl_dataset_phys_t *dsl_dataset_phys(dsl_dataset_t *ds);
 
+extern int spa_asize_inflation;
+
 /*
  * Figure out how much of this delta should be propogated to the dsl_dir
  * layer.  If there's a refreservation, that space has already been
  * partially accounted for in our ancestors.
  */
@@ -2786,10 +2789,15 @@
 
 int
 dsl_dataset_clone_swap_check_impl(dsl_dataset_t *clone,
     dsl_dataset_t *origin_head, boolean_t force, void *owner, dmu_tx_t *tx)
 {
+        /*
+         * "slack" factor for received datasets with refquota set on them.
+         * See the bottom of this function for details on its use.
+         */
+        uint64_t refquota_slack = DMU_MAX_ACCESS * spa_asize_inflation;
         int64_t unused_refres_delta;
 
         /* they should both be heads */
         if (clone->ds_is_snapshot ||
             origin_head->ds_is_snapshot)
@@ -2828,14 +2836,26 @@
         if (unused_refres_delta > 0 &&
             unused_refres_delta >
             dsl_dir_space_available(origin_head->ds_dir, NULL, 0, TRUE))
                 return (SET_ERROR(ENOSPC));
 
-        /* clone can't be over the head's refquota */
+        /*
+         * The clone can't be too much over the head's refquota.
+         *
+         * To ensure that the entire refquota can be used, we allow one
+         * transaction to exceed the the refquota.  Therefore, this check
+         * needs to also allow for the space referenced to be more than the
+         * refquota.  The maximum amount of space that one transaction can use
+         * on disk is DMU_MAX_ACCESS * spa_asize_inflation.  Allowing this
+         * overage ensures that we are able to receive a filesystem that
+         * exceeds the refquota on the source system.
+         *
+         * So that overage is the refquota_slack we use below.
+         */
         if (origin_head->ds_quota != 0 &&
             dsl_dataset_phys(clone)->ds_referenced_bytes >
-            origin_head->ds_quota)
+            origin_head->ds_quota + refquota_slack)
                 return (SET_ERROR(EDQUOT));
 
         return (0);
 }
 
@@ -2845,12 +2865,17 @@
 {
         dsl_pool_t *dp = dmu_tx_pool(tx);
         int64_t unused_refres_delta;
 
         ASSERT(clone->ds_reserved == 0);
+        /*
+         * NOTE: On DEBUG kernels there could be a race between this and
+         * the check function if spa_asize_inflation is adjusted...
+         */
         ASSERT(origin_head->ds_quota == 0 ||
-            dsl_dataset_phys(clone)->ds_unique_bytes <= origin_head->ds_quota);
+            dsl_dataset_phys(clone)->ds_unique_bytes <= origin_head->ds_quota +
+            DMU_MAX_ACCESS * spa_asize_inflation);
         ASSERT3P(clone->ds_prev, ==, origin_head->ds_prev);
 
         /*
          * Swap per-dataset feature flags.
          */