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,32 ****
--- 23,33 ----
   * 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,85 ****
--- 77,88 ----
  
  #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,2795 ****
--- 2789,2803 ----
  
  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,2841 ****
          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 */
          if (origin_head->ds_quota != 0 &&
              dsl_dataset_phys(clone)->ds_referenced_bytes >
!             origin_head->ds_quota)
                  return (SET_ERROR(EDQUOT));
  
          return (0);
  }
  
--- 2836,2861 ----
          if (unused_refres_delta > 0 &&
              unused_refres_delta >
              dsl_dir_space_available(origin_head->ds_dir, NULL, 0, TRUE))
                  return (SET_ERROR(ENOSPC));
  
!         /*
!          * 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 + refquota_slack)
                  return (SET_ERROR(EDQUOT));
  
          return (0);
  }
  
*** 2845,2856 ****
  {
          dsl_pool_t *dp = dmu_tx_pool(tx);
          int64_t unused_refres_delta;
  
          ASSERT(clone->ds_reserved == 0);
          ASSERT(origin_head->ds_quota == 0 ||
!             dsl_dataset_phys(clone)->ds_unique_bytes <= origin_head->ds_quota);
          ASSERT3P(clone->ds_prev, ==, origin_head->ds_prev);
  
          /*
           * Swap per-dataset feature flags.
           */
--- 2865,2881 ----
  {
          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 +
!             DMU_MAX_ACCESS * spa_asize_inflation);
          ASSERT3P(clone->ds_prev, ==, origin_head->ds_prev);
  
          /*
           * Swap per-dataset feature flags.
           */