diff usr/src/uts/common/fs/zfs/dsl_dataset.c @ 5481:1364fb7de75d

6619182 new non-sparse zvols should get refreservations 6623412 clone swap needs to check for enough space due to refreservation 6627460 Setting quota to itself should be a successful noop
author ck153898
date Wed, 14 Nov 2007 12:28:57 -0800
parents e140313199cc
children 85721e07d1fa
line wrap: on
line diff
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c	Wed Nov 14 08:38:43 2007 -0800
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c	Wed Nov 14 12:28:57 2007 -0800
@@ -2336,6 +2336,7 @@
 	dsl_dataset_t *cds; /* clone dataset */
 	dsl_dataset_t *ohds; /* origin's head dataset */
 	boolean_t force;
+	int64_t unused_refres_delta; /* change in unconsumed refreservation */
 };
 
 /* ARGSUSED */
@@ -2344,9 +2345,6 @@
 {
 	struct cloneswaparg *csa = arg1;
 
-	if (csa->ohds->ds_reserved != 0)
-		return (EINVAL);
-
 	/* they should both be heads */
 	if (dsl_dataset_is_snapshot(csa->cds) ||
 	    dsl_dataset_is_snapshot(csa->ohds))
@@ -2368,6 +2366,19 @@
 	/* ohds shouldn't be modified unless 'force' */
 	if (!csa->force && dsl_dataset_modified_since_lastsnap(csa->ohds))
 		return (ETXTBSY);
+
+	/* adjust amount of any unconsumed refreservation */
+	csa->unused_refres_delta =
+	    (int64_t)MIN(csa->ohds->ds_reserved,
+	    csa->ohds->ds_phys->ds_unique_bytes) -
+	    (int64_t)MIN(csa->ohds->ds_reserved,
+	    csa->cds->ds_phys->ds_unique_bytes);
+
+	if (csa->unused_refres_delta > 0 &&
+	    csa->unused_refres_delta >
+	    dsl_dir_space_available(csa->ohds->ds_dir, NULL, 0, TRUE))
+		return (ENOSPC);
+
 	return (0);
 }
 
@@ -2382,6 +2393,9 @@
 	uint64_t unique = 0;
 	int err;
 
+	ASSERT(csa->cds->ds_reserved == 0);
+	ASSERT(csa->cds->ds_quota == csa->ohds->ds_quota);
+
 	dmu_buf_will_dirty(csa->cds->ds_dbuf, tx);
 	dmu_buf_will_dirty(csa->ohds->ds_dbuf, tx);
 	dmu_buf_will_dirty(csa->cds->ds_prev->ds_dbuf, tx);
@@ -2405,13 +2419,6 @@
 	}
 	VERIFY(err == ENOENT);
 
-	/* undo any accounting due to a refreservation */
-	if (csa->ohds->ds_reserved > csa->ohds->ds_phys->ds_unique_bytes) {
-		dsl_dir_diduse_space(csa->ohds->ds_dir,
-		    csa->ohds->ds_phys->ds_unique_bytes -
-		    csa->ohds->ds_reserved, 0, 0, tx);
-	}
-
 	/* reset origin's unique bytes */
 	csa->cds->ds_prev->ds_phys->ds_unique_bytes = unique;
 
@@ -2454,13 +2461,6 @@
 		(y) = __tmp; \
 	}
 
-	/* redo any accounting due to a refreservation */
-	if (csa->ohds->ds_reserved > csa->ohds->ds_phys->ds_unique_bytes) {
-		dsl_dir_diduse_space(csa->ohds->ds_dir,
-		    csa->ohds->ds_reserved -
-		    csa->ohds->ds_phys->ds_unique_bytes, 0, 0, tx);
-	}
-
 	/* swap ds_*_bytes */
 	SWITCH64(csa->ohds->ds_phys->ds_used_bytes,
 	    csa->cds->ds_phys->ds_used_bytes);
@@ -2468,6 +2468,12 @@
 	    csa->cds->ds_phys->ds_compressed_bytes);
 	SWITCH64(csa->ohds->ds_phys->ds_uncompressed_bytes,
 	    csa->cds->ds_phys->ds_uncompressed_bytes);
+	SWITCH64(csa->ohds->ds_phys->ds_unique_bytes,
+	    csa->cds->ds_phys->ds_unique_bytes);
+
+	/* apply any parent delta for change in unconsumed refreservation */
+	dsl_dir_diduse_space(csa->ohds->ds_dir, csa->unused_refres_delta,
+	    0, 0, tx);
 
 	/* swap deadlists */
 	bplist_close(&csa->cds->ds_deadlist);
@@ -2478,13 +2484,10 @@
 	    csa->cds->ds_phys->ds_deadlist_obj));
 	VERIFY(0 == bplist_open(&csa->ohds->ds_deadlist, dp->dp_meta_objset,
 	    csa->ohds->ds_phys->ds_deadlist_obj));
-	/* fix up clone's unique */
-	dsl_dataset_recalc_head_uniq(csa->cds);
-
 }
 
 /*
- * Swap the clone "cosname" with its origin head file system.
+ * Swap 'clone' with its origin head file system.
  */
 int
 dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head,
@@ -2624,14 +2627,17 @@
 	if (err)
 		return (err);
 
-	/*
-	 * If someone removes a file, then tries to set the quota, we
-	 * want to make sure the file freeing takes effect.
-	 */
-	txg_wait_open(ds->ds_dir->dd_pool, 0);
-
-	err = dsl_sync_task_do(ds->ds_dir->dd_pool, dsl_dataset_set_quota_check,
-	    dsl_dataset_set_quota_sync, ds, &quota, 0);
+	if (quota != ds->ds_quota) {
+		/*
+		 * If someone removes a file, then tries to set the quota, we
+		 * want to make sure the file freeing takes effect.
+		 */
+		txg_wait_open(ds->ds_dir->dd_pool, 0);
+
+		err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+		    dsl_dataset_set_quota_check, dsl_dataset_set_quota_sync,
+		    ds, &quota, 0);
+	}
 	dsl_dataset_close(ds, DS_MODE_STANDARD, FTAG);
 	return (err);
 }