Print this page
OS-1566 dataset quota for ZFS datasets

*** 19,28 **** --- 19,29 ---- * CDDL HEADER END */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ /* Portions Copyright 2010 Robert Milkowski */ #include <sys/cred.h>
*** 688,698 **** /* You can only clone snapshots, not the head datasets. */ if (!dsl_dataset_is_snapshot(oa->clone_origin)) return (EINVAL); } ! return (0); } static void dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx) { --- 689,699 ---- /* You can only clone snapshots, not the head datasets. */ if (!dsl_dataset_is_snapshot(oa->clone_origin)) return (EINVAL); } ! return (dsl_dir_dscount_check(dd, tx, 1, NULL)); } static void dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx) {
*** 703,712 **** --- 704,715 ---- dsl_dataset_t *ds; blkptr_t *bp; ASSERT(dmu_tx_is_syncing(tx)); + dsl_dir_dscount_adjust(dd, tx, 1, B_TRUE, B_TRUE); + obj = dsl_dataset_create_sync(dd, oa->lastname, oa->clone_origin, oa->flags, oa->cr, tx); VERIFY3U(0, ==, dsl_dataset_hold_obj(dd->dd_pool, obj, FTAG, &ds)); bp = dsl_dataset_get_blkptr(ds);
*** 805,814 **** --- 808,818 ---- typedef struct snapallarg { dsl_sync_task_group_t *saa_dstg; boolean_t saa_needsuspend; nvlist_t *saa_props; + uint64_t saa_tot_cnt; /* the following are used only if 'temporary' is set: */ boolean_t saa_temporary; const char *saa_htag; struct dsl_ds_holdarg *saa_ha;
*** 829,840 **** snapallarg_t *saa = soa->soa_saa; int error; /* The props have already been checked by zfs_check_userprops(). */ error = dsl_dataset_snapshot_check(os->os_dsl_dataset, ! soa->soa_snapname, tx); if (error) return (error); if (saa->saa_temporary) { /* --- 833,880 ---- snapallarg_t *saa = soa->soa_saa; int error; /* The props have already been checked by zfs_check_userprops(). */ + /* + * The saa_tot_cnt is used to track how many snapshots there are going + * to be at the highest level of the snapshot tree. This is necessary + * because the counts are not actually adjusted when we are checking, + * only when we finally sync. For a single snapshot, this is easy, the + * count is 1, but it gets more complicated for recursive snapshots. + * + * We only enforce the snapshot quota at the level where the snapshot + * is being taken. This is to prevent datasets with a full snapshot + * count at a lower level from blocking recursive snapshots being taken + * at a higher level. For example, the quota is only enforced on 'a' + * and 'b' when taking a recursive snapshot of a/b@x with the following + * existing state: + * a/b (0 snaps, snap quota is 5) + * a/b/c (0 snaps, snap quota is none) + * a/b/d (1 snaps, snap quota is 1) + * A recursive snapshot of a/b will be allowed since it results in + * 3 new snapshots (a/b@x, a/b/c@x, a/b/d@x), even though a/b/d already + * has 1 snapshot and has hit its quota (note that the existing + * snapshot on a/b/d is being counted against the quota on a/b). When + * the snapshot completes, a/b will have a snapshot count of 4 and + * a/b/d will have a count of 2. As can be seen, this means that + * datasets can have a snapshot count > their quota. + * + * In order to properly handle recursive snapshots, we increment the + * total count in open context, but this count is not validated in open + * context. This gives us the maximum count to validate at the + * top-level dataset when we're in syncing context. We then use a count + * of 0 in syncing conext as we descend the tree past the top-level + * snapshot so that lower levels are not being validated against their + * quota. + */ + if (!dmu_tx_is_syncing(tx)) + saa->saa_tot_cnt++; error = dsl_dataset_snapshot_check(os->os_dsl_dataset, ! soa->soa_snapname, saa->saa_tot_cnt, tx); ! if (dmu_tx_is_syncing(tx)) ! saa->saa_tot_cnt = 0; if (error) return (error); if (saa->saa_temporary) { /*