]> granicus.if.org Git - zfs/commitdiff
Add support for user/group dnode accounting & quota
authorJinshan Xiong <jinshan.xiong@intel.com>
Tue, 4 Oct 2016 18:46:10 +0000 (11:46 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 7 Oct 2016 16:45:13 +0000 (09:45 -0700)
This patch tracks dnode usage for each user/group in the
DMU_USER/GROUPUSED_OBJECT ZAPs. ZAP entries dedicated to dnode
accounting have the key prefixed with "obj-" followed by the UID/GID
in string format (as done for the block accounting).
A new SPA feature has been added for dnode accounting as well as
a new ZPL version. The SPA feature must be enabled in the pool
before upgrading the zfs filesystem. During the zfs version upgrade,
a "quotacheck" will be executed by marking all dnode as dirty.

ZoL-bug-id: https://github.com/zfsonlinux/zfs/issues/3500

Signed-off-by: Jinshan Xiong <jinshan.xiong@intel.com>
Signed-off-by: Johann Lombardi <johann.lombardi@intel.com>
43 files changed:
cmd/zdb/zdb.c
cmd/zfs/zfs_main.c
configure.ac
include/sys/dmu.h
include/sys/dmu_objset.h
include/sys/dnode.h
include/sys/dsl_deleg.h
include/sys/fs/zfs.h
include/sys/spa_impl.h
include/sys/zfs_vfsops.h
include/zfeature_common.h
include/zfs_deleg.h
lib/libzfs/libzfs_dataset.c
man/man5/zpool-features.5
man/man8/zfs.8
module/zcommon/zfs_deleg.c
module/zcommon/zfs_prop.c
module/zfs/dmu_objset.c
module/zfs/dnode_sync.c
module/zfs/spa.c
module/zfs/zfeature_common.c
module/zfs/zfs_acl.c
module/zfs/zfs_ioctl.c
module/zfs/zfs_vfsops.c
tests/runfiles/linux.run
tests/zfs-tests/cmd/mkfiles/mkfiles.c
tests/zfs-tests/include/libtest.shlib
tests/zfs-tests/tests/functional/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg
tests/zfs-tests/tests/functional/redundancy/redundancy.kshlib
tests/zfs-tests/tests/functional/upgrade/Makefile.am [new file with mode: 0644]
tests/zfs-tests/tests/functional/upgrade/cleanup.ksh [new file with mode: 0644]
tests/zfs-tests/tests/functional/upgrade/setup.ksh [new file with mode: 0644]
tests/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos.ksh [new file with mode: 0644]
tests/zfs-tests/tests/functional/userquota/Makefile.am
tests/zfs-tests/tests/functional/userquota/groupspace_003_pos.ksh [new file with mode: 0644]
tests/zfs-tests/tests/functional/userquota/userquota_001_pos.ksh
tests/zfs-tests/tests/functional/userquota/userquota_004_pos.ksh
tests/zfs-tests/tests/functional/userquota/userquota_010_pos.ksh
tests/zfs-tests/tests/functional/userquota/userquota_013_pos.ksh [new file with mode: 0644]
tests/zfs-tests/tests/functional/userquota/userquota_common.kshlib
tests/zfs-tests/tests/functional/userquota/userspace_002_pos.ksh
tests/zfs-tests/tests/functional/userquota/userspace_003_pos.ksh [new file with mode: 0644]

index 611e9229461cdc80b60c0b1209f46d26fa5486eb..7a0f55f75e9b6b16c91af227e6f3050279ffde7a 100644 (file)
@@ -1946,11 +1946,13 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header)
        }
 
        if (verbosity >= 4) {
-               (void) printf("\tdnode flags: %s%s%s\n",
+               (void) printf("\tdnode flags: %s%s%s%s\n",
                    (dn->dn_phys->dn_flags & DNODE_FLAG_USED_BYTES) ?
                    "USED_BYTES " : "",
                    (dn->dn_phys->dn_flags & DNODE_FLAG_USERUSED_ACCOUNTED) ?
                    "USERUSED_ACCOUNTED " : "",
+                   (dn->dn_phys->dn_flags & DNODE_FLAG_USEROBJUSED_ACCOUNTED) ?
+                   "USEROBJUSED_ACCOUNTED " : "",
                    (dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR) ?
                    "SPILL_BLKPTR" : "");
                (void) printf("\tdnode maxblkid: %llu\n",
index fedc1a04da34af95424dce59e7db26df945b4f57..8a49e31e55596d5b33f0bd2164680046fef0f63f 100644 (file)
@@ -2223,10 +2223,14 @@ enum us_field_types {
        USFIELD_TYPE,
        USFIELD_NAME,
        USFIELD_USED,
-       USFIELD_QUOTA
+       USFIELD_QUOTA,
+       USFIELD_OBJUSED,
+       USFIELD_OBJQUOTA
 };
-static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" };
-static char *us_field_names[] = { "type", "name", "used", "quota" };
+static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA",
+                                   "OBJUSED", "OBJQUOTA" };
+static char *us_field_names[] = { "type", "name", "used", "quota",
+                                   "objused", "objquota" };
 #define        USFIELD_LAST    (sizeof (us_field_names) / sizeof (char *))
 
 #define        USTYPE_PSX_GRP  (1 << 0)
@@ -2374,6 +2378,20 @@ compare_nums:
        return (0);
 }
 
+static boolean_t
+zfs_prop_is_user(unsigned p)
+{
+       return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||
+           p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA);
+}
+
+static boolean_t
+zfs_prop_is_group(unsigned p)
+{
+       return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||
+           p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA);
+}
+
 static inline const char *
 us_type2str(unsigned field_type)
 {
@@ -2463,7 +2481,7 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
 
        if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
                /* POSIX or -i */
-               if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
+               if (zfs_prop_is_group(prop)) {
                        type = USTYPE_PSX_GRP;
                        if (!cb->cb_numname) {
                                struct group *g;
@@ -2538,10 +2556,22 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
                propname = "used";
                if (!nvlist_exists(props, "quota"))
                        (void) nvlist_add_uint64(props, "quota", 0);
-       } else {
+       } else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA) {
                propname = "quota";
                if (!nvlist_exists(props, "used"))
                        (void) nvlist_add_uint64(props, "used", 0);
+       } else if (prop == ZFS_PROP_USEROBJUSED ||
+                   prop == ZFS_PROP_GROUPOBJUSED) {
+               propname = "objused";
+               if (!nvlist_exists(props, "objquota"))
+                       (void) nvlist_add_uint64(props, "objquota", 0);
+       } else if (prop == ZFS_PROP_USEROBJQUOTA ||
+                   prop == ZFS_PROP_GROUPOBJQUOTA) {
+               propname = "objquota";
+               if (!nvlist_exists(props, "objused"))
+                       (void) nvlist_add_uint64(props, "objused", 0);
+       } else {
+               return (-1);
        }
        sizeidx = us_field_index(propname);
        if (sizelen > cb->cb_width[sizeidx])
@@ -2574,7 +2604,7 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
                data_type_t type;
                uint32_t val32;
                uint64_t val64;
-               char *strval = NULL;
+               char *strval = "-";
 
                while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
                        if (strcmp(nvpair_name(nvp),
@@ -2582,7 +2612,7 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
                                break;
                }
 
-               type = nvpair_type(nvp);
+               type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp);
                switch (type) {
                case DATA_TYPE_UINT32:
                        (void) nvpair_value_uint32(nvp, &val32);
@@ -2593,13 +2623,16 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
                case DATA_TYPE_STRING:
                        (void) nvpair_value_string(nvp, &strval);
                        break;
+               case DATA_TYPE_UNKNOWN:
+                       break;
                default:
                        (void) fprintf(stderr, "invalid data type\n");
                }
 
                switch (field) {
                case USFIELD_TYPE:
-                       strval = (char *)us_type2str(val32);
+                       if (type == DATA_TYPE_UINT32)
+                               strval = (char *)us_type2str(val32);
                        break;
                case USFIELD_NAME:
                        if (type == DATA_TYPE_UINT64) {
@@ -2610,6 +2643,8 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
                        break;
                case USFIELD_USED:
                case USFIELD_QUOTA:
+               case USFIELD_OBJUSED:
+               case USFIELD_OBJQUOTA:
                        if (type == DATA_TYPE_UINT64) {
                                if (parsable) {
                                        (void) sprintf(valstr, "%llu",
@@ -2618,7 +2653,8 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
                                        zfs_nicenum(val64, valstr,
                                            sizeof (valstr));
                                }
-                               if (field == USFIELD_QUOTA &&
+                               if ((field == USFIELD_QUOTA ||
+                                   field == USFIELD_OBJQUOTA) &&
                                    strcmp(valstr, "0") == 0)
                                        strval = "none";
                                else
@@ -2690,7 +2726,7 @@ zfs_do_userspace(int argc, char **argv)
        uu_avl_t *avl_tree;
        uu_avl_walk_t *walk;
        char *delim;
-       char deffields[] = "type,name,used,quota";
+       char deffields[] = "type,name,used,quota,objused,objquota";
        char *ofield = NULL;
        char *tfield = NULL;
        int cfield = 0;
@@ -2839,11 +2875,12 @@ zfs_do_userspace(int argc, char **argv)
                cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
 
        for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
-               if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) &&
+               if ((zfs_prop_is_user(p) &&
                    !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
-                   ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) &&
+                   (zfs_prop_is_group(p) &&
                    !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))))
                        continue;
+
                cb.cb_prop = p;
                if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
                        return (ret);
@@ -4099,6 +4136,11 @@ zfs_do_receive(int argc, char **argv)
 #define        ZFS_DELEG_PERM_GROUPQUOTA       "groupquota"
 #define        ZFS_DELEG_PERM_USERUSED         "userused"
 #define        ZFS_DELEG_PERM_GROUPUSED        "groupused"
+#define        ZFS_DELEG_PERM_USEROBJQUOTA     "userobjquota"
+#define        ZFS_DELEG_PERM_GROUPOBJQUOTA    "groupobjquota"
+#define        ZFS_DELEG_PERM_USEROBJUSED      "userobjused"
+#define        ZFS_DELEG_PERM_GROUPOBJUSED     "groupobjused"
+
 #define        ZFS_DELEG_PERM_HOLD             "hold"
 #define        ZFS_DELEG_PERM_RELEASE          "release"
 #define        ZFS_DELEG_PERM_DIFF             "diff"
@@ -4129,6 +4171,10 @@ static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
        { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
        { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
        { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
+       { ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA },
+       { ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED },
+       { ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA },
+       { ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED },
        { NULL, ZFS_DELEG_NOTE_NONE }
 };
 
@@ -4206,6 +4252,10 @@ deleg_perm_type(zfs_deleg_note_t note)
        case ZFS_DELEG_NOTE_USERPROP:
        case ZFS_DELEG_NOTE_USERQUOTA:
        case ZFS_DELEG_NOTE_USERUSED:
+       case ZFS_DELEG_NOTE_USEROBJQUOTA:
+       case ZFS_DELEG_NOTE_USEROBJUSED:
+       case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
+       case ZFS_DELEG_NOTE_GROUPOBJUSED:
                /* other */
                return (gettext("other"));
        default:
@@ -4709,6 +4759,19 @@ deleg_perm_comment(zfs_deleg_note_t note)
        case ZFS_DELEG_NOTE_USERUSED:
                str = gettext("Allows reading any userused@... property");
                break;
+       case ZFS_DELEG_NOTE_USEROBJQUOTA:
+               str = gettext("Allows accessing any userobjquota@... property");
+               break;
+       case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
+               str = gettext("Allows accessing any \n\t\t\t\t"
+                   "groupobjquota@... property");
+               break;
+       case ZFS_DELEG_NOTE_GROUPOBJUSED:
+               str = gettext("Allows reading any groupobjused@... property");
+               break;
+       case ZFS_DELEG_NOTE_USEROBJUSED:
+               str = gettext("Allows reading any userobjused@... property");
+               break;
                /* other */
        default:
                str = "";
index edcf299585c51a7ba6a8ad239a3719df34998d50..f01a4d8728033e963b40eed18a97e73566db7742 100644 (file)
@@ -274,6 +274,7 @@ AC_CONFIG_FILES([
        tests/zfs-tests/tests/functional/threadsappend/Makefile
        tests/zfs-tests/tests/functional/truncate/Makefile
        tests/zfs-tests/tests/functional/userquota/Makefile
+       tests/zfs-tests/tests/functional/upgrade/Makefile
        tests/zfs-tests/tests/functional/vdev_zaps/Makefile
        tests/zfs-tests/tests/functional/write_dirs/Makefile
        tests/zfs-tests/tests/functional/xattr/Makefile
index b67acb52c932e6ba053b18f1be2cda9e9a8b2fbf..ec89e90184a340d165f48969acb3f7de91700001 100644 (file)
@@ -256,6 +256,12 @@ void zfs_znode_byteswap(void *buf, size_t size);
 #define        DMU_USERUSED_OBJECT     (-1ULL)
 #define        DMU_GROUPUSED_OBJECT    (-2ULL)
 
+/*
+ * Zap prefix for object accounting in DMU_{USER,GROUP}USED_OBJECT.
+ */
+#define        DMU_OBJACCT_PREFIX      "obj-"
+#define        DMU_OBJACCT_PREFIX_LEN  4
+
 /*
  * artificial blkids for bonus buffer and spill blocks
  */
index 1674897c2fb46a288f71f485854ade60956b204d..68fb5cffb9325899c27ea16e92a1409741e1e6dc 100644 (file)
@@ -56,6 +56,7 @@ struct dmu_tx;
        (arc_buf_size(buf) > OBJSET_OLD_PHYS_SIZE)
 
 #define        OBJSET_FLAG_USERACCOUNTING_COMPLETE     (1ULL<<0)
+#define        OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE  (1ULL<<1)
 
 typedef struct objset_phys {
        dnode_phys_t os_meta_dnode;
@@ -68,6 +69,8 @@ typedef struct objset_phys {
        dnode_phys_t os_groupused_dnode;
 } objset_phys_t;
 
+typedef int (*dmu_objset_upgrade_cb_t)(objset_t *);
+
 struct objset {
        /* Immutable: */
        struct dsl_dataset *os_dsl_dataset;
@@ -125,6 +128,13 @@ struct objset {
        kmutex_t os_user_ptr_lock;
        void *os_user_ptr;
        sa_os_t *os_sa;
+
+       /* kernel thread to upgrade this dataset */
+       kmutex_t os_upgrade_lock;
+       taskqid_t os_upgrade_id;
+       dmu_objset_upgrade_cb_t os_upgrade_cb;
+       boolean_t os_upgrade_exit;
+       int os_upgrade_status;
 };
 
 #define        DMU_META_OBJSET         0
@@ -173,6 +183,17 @@ void dmu_objset_userquota_get_ids(dnode_t *dn, boolean_t before, dmu_tx_t *tx);
 boolean_t dmu_objset_userused_enabled(objset_t *os);
 int dmu_objset_userspace_upgrade(objset_t *os);
 boolean_t dmu_objset_userspace_present(objset_t *os);
+boolean_t dmu_objset_userobjused_enabled(objset_t *os);
+void dmu_objset_userobjspace_upgrade(objset_t *os);
+boolean_t dmu_objset_userobjspace_present(objset_t *os);
+
+static inline boolean_t dmu_objset_userobjspace_upgradable(objset_t *os)
+{
+       return (dmu_objset_type(os) == DMU_OST_ZFS &&
+           dmu_objset_userobjused_enabled(os) &&
+           !dmu_objset_userobjspace_present(os));
+}
+
 int dmu_fsname(const char *snapname, char *buf);
 
 void dmu_objset_evict_done(objset_t *os);
index fe36e59891513313e810357fc428e857e7c6a9b5..fe4cd3e4bf375338dde06a181dc5531b7ce1c34f 100644 (file)
@@ -126,11 +126,14 @@ enum dnode_dirtycontext {
 };
 
 /* Is dn_used in bytes?  if not, it's in multiples of SPA_MINBLOCKSIZE */
-#define        DNODE_FLAG_USED_BYTES           (1<<0)
-#define        DNODE_FLAG_USERUSED_ACCOUNTED   (1<<1)
+#define        DNODE_FLAG_USED_BYTES                   (1 << 0)
+#define        DNODE_FLAG_USERUSED_ACCOUNTED           (1 << 1)
 
 /* Does dnode have a SA spill blkptr in bonus? */
-#define        DNODE_FLAG_SPILL_BLKPTR (1<<2)
+#define        DNODE_FLAG_SPILL_BLKPTR                 (1 << 2)
+
+/* User/Group dnode accounting */
+#define        DNODE_FLAG_USEROBJUSED_ACCOUNTED        (1 << 3)
 
 typedef struct dnode_phys {
        uint8_t dn_type;                /* dmu_object_type_t */
index 59e8e055551ad0b5ca9ba447e3fa3361d00ca980..d399d1da973be0d0c488eac1af3786e8722ff3ed 100644 (file)
@@ -51,8 +51,12 @@ extern "C" {
 #define        ZFS_DELEG_PERM_VSCAN            "vscan"
 #define        ZFS_DELEG_PERM_USERQUOTA        "userquota"
 #define        ZFS_DELEG_PERM_GROUPQUOTA       "groupquota"
+#define        ZFS_DELEG_PERM_USEROBJQUOTA     "userobjquota"
+#define        ZFS_DELEG_PERM_GROUPOBJQUOTA    "groupobjquota"
 #define        ZFS_DELEG_PERM_USERUSED         "userused"
 #define        ZFS_DELEG_PERM_GROUPUSED        "groupused"
+#define        ZFS_DELEG_PERM_USEROBJUSED      "userobjused"
+#define        ZFS_DELEG_PERM_GROUPOBJUSED     "groupobjused"
 #define        ZFS_DELEG_PERM_HOLD             "hold"
 #define        ZFS_DELEG_PERM_RELEASE          "release"
 #define        ZFS_DELEG_PERM_DIFF             "diff"
index 9d3008934fa2d0fd9535ade1e0805980b85a3335..5c93f53dec36a309a0fe98b789d58380dce4da7a 100644 (file)
@@ -171,6 +171,10 @@ typedef enum {
        ZFS_PROP_USERQUOTA,
        ZFS_PROP_GROUPUSED,
        ZFS_PROP_GROUPQUOTA,
+       ZFS_PROP_USEROBJUSED,
+       ZFS_PROP_USEROBJQUOTA,
+       ZFS_PROP_GROUPOBJUSED,
+       ZFS_PROP_GROUPOBJQUOTA,
        ZFS_NUM_USERQUOTA_PROPS
 } zfs_userquota_prop_t;
 
index 7b9e1ee0c93e3f9a4de7914a2275ceafd0aa66fc..cb1d16ad55475117b1dbe7345471907880186742 100644 (file)
@@ -277,6 +277,8 @@ struct spa {
         */
        spa_config_lock_t spa_config_lock[SCL_LOCKS]; /* config changes */
        refcount_t      spa_refcount;           /* number of opens */
+
+       taskq_t         *spa_upgrade_taskq;     /* taskq for upgrade jobs */
 };
 
 extern char *spa_config_path;
index efaefdaccbc11bdf420da417deac59db1bcd3ee3..b59ace5b1996b151ee06ea0e4ad7b0384d8e1ee9 100644 (file)
@@ -110,6 +110,8 @@ typedef struct zfs_sb {
        kmutex_t        z_lock;
        uint64_t        z_userquota_obj;
        uint64_t        z_groupquota_obj;
+       uint64_t        z_userobjquota_obj;
+       uint64_t        z_groupobjquota_obj;
        uint64_t        z_replay_eof;   /* New end of file - replay only */
        sa_attr_type_t  *z_attr_table;  /* SA attr mapping->id */
        uint64_t        z_hold_size;    /* znode hold array size */
@@ -190,6 +192,8 @@ extern boolean_t zfs_owner_overquota(zfs_sb_t *zsb, struct znode *,
     boolean_t isgroup);
 extern boolean_t zfs_fuid_overquota(zfs_sb_t *zsb, boolean_t isgroup,
     uint64_t fuid);
+extern boolean_t zfs_fuid_overobjquota(zfs_sb_t *zsb, boolean_t isgroup,
+    uint64_t fuid);
 extern int zfs_set_version(zfs_sb_t *zsb, uint64_t newvers);
 extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop,
     uint64_t *value);
index f0548018170fc173772eb5399492b321f5d47786..acf76381b7c075af9b6fb265c2993c64efc50fac 100644 (file)
@@ -54,6 +54,7 @@ typedef enum spa_feature {
        SPA_FEATURE_SHA512,
        SPA_FEATURE_SKEIN,
        SPA_FEATURE_EDONR,
+       SPA_FEATURE_USEROBJ_ACCOUNTING,
        SPA_FEATURES
 } spa_feature_t;
 
index 16133c59f33f395b2efb12013e5b88069f9d2c12..95db9921f574b57d2e36cec5b30d386e9db9189d 100644 (file)
@@ -63,6 +63,10 @@ typedef enum {
        ZFS_DELEG_NOTE_GROUPQUOTA,
        ZFS_DELEG_NOTE_USERUSED,
        ZFS_DELEG_NOTE_GROUPUSED,
+       ZFS_DELEG_NOTE_USEROBJQUOTA,
+       ZFS_DELEG_NOTE_GROUPOBJQUOTA,
+       ZFS_DELEG_NOTE_USEROBJUSED,
+       ZFS_DELEG_NOTE_GROUPOBJUSED,
        ZFS_DELEG_NOTE_HOLD,
        ZFS_DELEG_NOTE_RELEASE,
        ZFS_DELEG_NOTE_DIFF,
index 5ecf96985377570553b34eb5d5ca805ec6a236f8..7d8179d1f92f870f0d34f98a7e8ce9db8258e55c 100755 (executable)
@@ -944,7 +944,9 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        }
 
                        if (uqtype != ZFS_PROP_USERQUOTA &&
-                           uqtype != ZFS_PROP_GROUPQUOTA) {
+                           uqtype != ZFS_PROP_GROUPQUOTA &&
+                           uqtype != ZFS_PROP_USEROBJQUOTA &&
+                           uqtype != ZFS_PROP_GROUPOBJQUOTA) {
                                zfs_error_aux(hdl,
                                    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
                                    propname);
@@ -2741,8 +2743,12 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
                return (EINVAL);
        *typep = type;
 
-       isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED);
-       isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED);
+       isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED ||
+                   type == ZFS_PROP_USEROBJQUOTA ||
+                   type == ZFS_PROP_USEROBJUSED);
+       isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED ||
+                   type == ZFS_PROP_GROUPOBJQUOTA ||
+                   type == ZFS_PROP_GROUPOBJUSED);
 
        cp = strchr(propname, '@') + 1;
 
@@ -2875,7 +2881,8 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
                (void) snprintf(propbuf, proplen, "%llu",
                    (u_longlong_t)propvalue);
        } else if (propvalue == 0 &&
-           (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
+           (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA)) {
                (void) strlcpy(propbuf, "none", proplen);
        } else {
                zfs_nicenum(propvalue, propbuf, proplen);
@@ -4333,6 +4340,13 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
                if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
                        char errbuf[1024];
 
+                       if ((errno == ENOTSUP &&
+                           (type == ZFS_PROP_USEROBJUSED ||
+                           type == ZFS_PROP_GROUPOBJUSED ||
+                           type == ZFS_PROP_USEROBJQUOTA ||
+                           type == ZFS_PROP_GROUPOBJQUOTA)))
+                               break;
+
                        (void) snprintf(errbuf, sizeof (errbuf),
                            dgettext(TEXT_DOMAIN,
                            "cannot get used/quota for %s"), zc.zc_name);
index ffefd4129654f30f7215992d9145c02eef9412be..ccc7ab47e34c73611385a292e49e868c481d6e1c 100644 (file)
@@ -563,5 +563,31 @@ Booting off of pools using \fBedonr\fR is \fBNOT\fR supported
 -- any attempt to enable \fBedonr\fR on a root pool will fail with an
 error.
 
+.sp
+.ne 2
+.na
+\fB\fBuserobj_accounting\fR\fR
+.ad
+.RS 4n
+.TS
+l l .
+GUID   org.zfsonlinux:userobj_accounting
+READ\-ONLY COMPATIBLE  yes
+DEPENDENCIES   extensible_dataset
+.TE
+
+This feature allows administrators to account the object usage information
+by user and group.
+
+This feature becomes \fBactive\fR as soon as it is enabled and will never
+return to being \fBenabled\fR. Each filesystem will be upgraded automatically
+when remounted, or when new files are created under that filesystem.
+The upgrade can also be started manually on filesystems by running
+`zfs set version=current <pool/fs>`. The upgrade process runs in the background
+and may take a while to complete for filesystems containing a large number of
+files.
+
+.RE
+
 .SH "SEE ALSO"
 \fBzpool\fR(8)
index e543ba51d58fe9cb265b280f5121486134aadef4..d8c15062902c16f7091f6af7e90e9385fd51b69d 100644 (file)
@@ -663,6 +663,8 @@ The amount of space consumed by snapshots of this dataset. In particular, it is
 .sp
 .ne 2
 .na
+\fB\fBuserobjused@\fR\fIuser\fR\fR
+.br
 \fB\fBuserused@\fR\fIuser\fR\fR
 .ad
 .sp .6
@@ -699,6 +701,11 @@ The \fBuserused@\fR... properties are not displayed by \fBzfs get all\fR. The us
 .RE
 Files created on Linux always have POSIX owners.
 
+.RS 4n
+The \fBuserobjused\fR is similar to \fBuserused\fR but instead it counts the number of objects consumed by \fIuser\fR. This feature doesn't count the internal objects used by ZFS, therefore it may under count a few objects comparing with the results of third-party tool such as \fBdfs -i\fR.
+When the property \fBxattr=on\fR is set on a fileset, ZFS will create additional objects per-file to store extended attributes. These additional objects are reflected in the \fBuserobjused\fR value and are counted against the user's \fBuserobjquota\fR. When a filesystem is configured to use \fBxattr=sa\fR no additional internal objects are required.
+.RE
+
 .sp
 .ne 2
 .na
@@ -713,6 +720,8 @@ This property is set to the number of user holds on this snapshot. User holds ar
 .ne 2
 .na
 \fB\fBgroupused@\fR\fIgroup\fR\fR
+.br
+\fB\fBgroupobjused@\fR\fIgroup\fR\fR
 .ad
 .sp .6
 .RS 4n
@@ -721,6 +730,11 @@ The amount of space consumed by the specified group in this dataset. Space is ch
 Unprivileged users can only access their own groups' space usage. The root user, or a user who has been granted the \fBgroupused\fR privilege with \fBzfs allow\fR, can access all groups' usage.
 .RE
 
+.RS 4n
+The \fBgroupobjused\fR is similar to \fBgroupused\fR but instead it counts the number of objects consumed by \fIgroup\fR.
+When the property \fBxattr=on\fR is set on a fileset, ZFS will create additional objects per-file to store extended attributes. These additional objects are reflected in the \fBgroupobjused\fR value and are counted against the group's \fBgroupobjquota.\fR. When a filesystem is configured to use \fBxattr=sa\fR no additional internal objects are required.
+.RE
+
 .sp
 .ne 2
 .na
@@ -1081,6 +1095,8 @@ a zone. This feature must be enabled to be used (see \fBzpool-features\fR(5)).
 .ne 2
 .na
 \fB\fBuserquota@\fR\fIuser\fR=\fBnone\fR | \fIsize\fR\fR
+.br
+\fB\fBuserobjquota@\fR\fIuser\fR=\fBnone\fR | \fIcount\fR\fR
 .ad
 .sp .6
 .RS 4n
@@ -1118,16 +1134,26 @@ This property is not available on volumes, on file systems before version 4, or
 .RE
 Files created on Linux always have POSIX owners.
 
+.RS 4
+The \fBuserobjquota\fR is similar to \fBuserquota\fR but it limits the number of objects a \fIuser\fR can create.
+Please refer to \fBuserobjused\fR for more information about how ZFS counts object usage.
+.RE
+
 .sp
 .ne 2
 .na
 \fB\fBgroupquota@\fR\fIgroup\fR=\fBnone\fR\fR | \fIsize\fR
+.br
+\fB\fBgroupobjquota@\fR\fIgroup\fR=\fBnone\fR\fR | \fIcount\fR
 .ad
 .sp .6
 .RS 4n
 Limits the amount of space consumed by the specified group. Group space consumption is identified by the \fBuserquota@\fR\fIuser\fR property.
 .sp
 Unprivileged users can access only their own groups' space usage. The root user, or a user who has been granted the \fBgroupquota\fR privilege with \fBzfs allow\fR, can get and set all groups' quotas.
+
+The \fBgroupobjquota\fR is similar to \fBgroupquota\fR but it limits that the \fIgroup\fR can consume \fIcount\fR number of objects at most.
+Please refer to \fBuserobjused\fR for more information about how zfs counts object usage.
 .RE
 
 .sp
@@ -2386,8 +2412,8 @@ Upgrades to the specified \fIversion\fR. If the \fB-V\fR flag is not specified,
 .sp .6
 .RS 4n
 Displays space consumed by, and quotas on, each user in the specified
-filesystem or snapshot. This corresponds to the \fBuserused@\fR\fIuser\fR and
-\fBuserquota@\fR\fIuser\fR properties.
+filesystem or snapshot. This corresponds to the \fBuserused@\fR\fIuser\fR, \fBuserobjused@\fR\fIuser\fR,
+\fBuserquota@\fR\fIuser\fR, and \fBuserobjquota@\fR\fIuser\fR properties.
 .sp
 .ne 2
 .na
@@ -3141,10 +3167,14 @@ send             subcommand
 share            subcommand     Allows sharing file systems over NFS or SMB
                                 protocols
 snapshot         subcommand     Must also have the 'mount' ability
+groupobjquota    other          Allows accessing any groupobjquota@... property
 groupquota       other          Allows accessing any groupquota@... property
+groupobjused     other          Allows reading any groupobjused@... property
 groupused        other          Allows reading any groupused@... property
 userprop         other          Allows changing any user property
+userobjquota     other          Allows accessing any userobjquota@... property
 userquota        other          Allows accessing any userquota@... property
+userobjused      other          Allows reading any userobjused@... property
 userused         other          Allows reading any userused@... property
 
 acltype          property
index f6e41da9d7eac1e3cf100e059c1fbd25f6d9893c..647a24e5ff1f1c6070e5c1a96b2aad6bc0b90f87 100644 (file)
@@ -62,6 +62,10 @@ zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = {
        {ZFS_DELEG_PERM_GROUPQUOTA},
        {ZFS_DELEG_PERM_USERUSED},
        {ZFS_DELEG_PERM_GROUPUSED},
+       {ZFS_DELEG_PERM_USEROBJQUOTA},
+       {ZFS_DELEG_PERM_GROUPOBJQUOTA},
+       {ZFS_DELEG_PERM_USEROBJUSED},
+       {ZFS_DELEG_PERM_GROUPOBJUSED},
        {ZFS_DELEG_PERM_HOLD},
        {ZFS_DELEG_PERM_RELEASE},
        {NULL}
index 029075ebec93caf703372bc6e82ca1765d7764ca..1802750f946369b610359671d12b203acc19e67f 100644 (file)
@@ -52,7 +52,11 @@ const char *zfs_userquota_prop_prefixes[] = {
        "userused@",
        "userquota@",
        "groupused@",
-       "groupquota@"
+       "groupquota@",
+       "userobjused@",
+       "userobjquota@",
+       "groupobjused@",
+       "groupobjquota@"
 };
 
 zprop_desc_t *
index 970ee4f086ca3af9b0765b0304bf6366001920a3..c6b6eb745c0d27eda208bb1d0fe620e3043769c3 100644 (file)
@@ -31,6 +31,7 @@
 
 /* Portions Copyright 2010 Robert Milkowski */
 
+#include <sys/zfeature.h>
 #include <sys/cred.h>
 #include <sys/zfs_context.h>
 #include <sys/dmu_objset.h>
@@ -53,6 +54,7 @@
 #include <sys/dsl_destroy.h>
 #include <sys/vdev.h>
 #include <sys/policy.h>
+#include <sys/spa_impl.h>
 
 /*
  * Needed to close a window in dnode_move() that allows the objset to be freed
@@ -77,6 +79,9 @@ int dmu_rescan_dnode_threshold = 1 << DN_MAX_INDBLKSHIFT;
 
 static void dmu_objset_find_dp_cb(void *arg);
 
+static void dmu_objset_upgrade(objset_t *os, dmu_objset_upgrade_cb_t cb);
+static void dmu_objset_upgrade_stop(objset_t *os);
+
 void
 dmu_objset_init(void)
 {
@@ -519,6 +524,8 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
                    DMU_GROUPUSED_OBJECT, &os->os_groupused_dnode);
        }
 
+       mutex_init(&os->os_upgrade_lock, NULL, MUTEX_DEFAULT, NULL);
+
        *osp = os;
        return (0);
 }
@@ -625,6 +632,9 @@ dmu_objset_own(const char *name, dmu_objset_type_t type,
        err = dmu_objset_own_impl(ds, type, readonly, tag, osp);
        dsl_pool_rele(dp, FTAG);
 
+       if (err == 0 && dmu_objset_userobjspace_upgradable(*osp))
+               dmu_objset_userobjspace_upgrade(*osp);
+
        return (err);
 }
 
@@ -685,6 +695,10 @@ dmu_objset_refresh_ownership(objset_t *os, void *tag)
 void
 dmu_objset_disown(objset_t *os, void *tag)
 {
+       /*
+        * Stop upgrading thread
+        */
+       dmu_objset_upgrade_stop(os);
        dsl_dataset_disown(os->os_dsl_dataset, tag);
 }
 
@@ -859,6 +873,12 @@ dmu_objset_create_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
        os->os_phys->os_type = type;
        if (dmu_objset_userused_enabled(os)) {
                os->os_phys->os_flags |= OBJSET_FLAG_USERACCOUNTING_COMPLETE;
+               if (dmu_objset_userobjused_enabled(os)) {
+                       ds->ds_feature_activation_needed[
+                           SPA_FEATURE_USEROBJ_ACCOUNTING] = B_TRUE;
+                       os->os_phys->os_flags |=
+                           OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE;
+               }
                os->os_flags = os->os_phys->os_flags;
        }
 
@@ -1067,6 +1087,60 @@ dmu_objset_snapshot_one(const char *fsname, const char *snapname)
        return (err);
 }
 
+static void
+dmu_objset_upgrade_task_cb(void *data)
+{
+       objset_t *os = data;
+
+       mutex_enter(&os->os_upgrade_lock);
+       os->os_upgrade_status = EINTR;
+       if (!os->os_upgrade_exit) {
+               mutex_exit(&os->os_upgrade_lock);
+
+               os->os_upgrade_status = os->os_upgrade_cb(os);
+               mutex_enter(&os->os_upgrade_lock);
+       }
+       os->os_upgrade_exit = B_TRUE;
+       os->os_upgrade_id = 0;
+       mutex_exit(&os->os_upgrade_lock);
+}
+
+static void
+dmu_objset_upgrade(objset_t *os, dmu_objset_upgrade_cb_t cb)
+{
+       if (os->os_upgrade_id != 0)
+               return;
+
+       mutex_enter(&os->os_upgrade_lock);
+       if (os->os_upgrade_id == 0 && os->os_upgrade_status == 0) {
+               os->os_upgrade_exit = B_FALSE;
+               os->os_upgrade_cb = cb;
+               os->os_upgrade_id = taskq_dispatch(
+                   os->os_spa->spa_upgrade_taskq,
+                   dmu_objset_upgrade_task_cb, os, TQ_SLEEP);
+               if (os->os_upgrade_id == 0)
+                       os->os_upgrade_status = ENOMEM;
+       }
+       mutex_exit(&os->os_upgrade_lock);
+}
+
+static void
+dmu_objset_upgrade_stop(objset_t *os)
+{
+       mutex_enter(&os->os_upgrade_lock);
+       os->os_upgrade_exit = B_TRUE;
+       if (os->os_upgrade_id != 0) {
+               taskqid_t id = os->os_upgrade_id;
+
+               os->os_upgrade_id = 0;
+               mutex_exit(&os->os_upgrade_lock);
+
+               taskq_cancel_id(os->os_spa->spa_upgrade_taskq, id);
+       } else {
+               mutex_exit(&os->os_upgrade_lock);
+       }
+}
+
 static void
 dmu_objset_sync_dnodes(list_t *list, list_t *newlist, dmu_tx_t *tx)
 {
@@ -1257,6 +1331,13 @@ dmu_objset_userused_enabled(objset_t *os)
            DMU_USERUSED_DNODE(os) != NULL);
 }
 
+boolean_t
+dmu_objset_userobjused_enabled(objset_t *os)
+{
+       return (dmu_objset_userused_enabled(os) &&
+           spa_feature_is_enabled(os->os_spa, SPA_FEATURE_USEROBJ_ACCOUNTING));
+}
+
 static void
 do_userquota_update(objset_t *os, uint64_t used, uint64_t flags,
     uint64_t user, uint64_t group, boolean_t subtract, dmu_tx_t *tx)
@@ -1272,6 +1353,25 @@ do_userquota_update(objset_t *os, uint64_t used, uint64_t flags,
        }
 }
 
+static void
+do_userobjquota_update(objset_t *os, uint64_t flags, uint64_t user,
+    uint64_t group, boolean_t subtract, dmu_tx_t *tx)
+{
+       if (flags & DNODE_FLAG_USEROBJUSED_ACCOUNTED) {
+               char name[20 + DMU_OBJACCT_PREFIX_LEN];
+
+               (void) snprintf(name, sizeof (name), DMU_OBJACCT_PREFIX "%llx",
+                   (longlong_t)user);
+               VERIFY0(zap_increment(os, DMU_USERUSED_OBJECT, name,
+                   subtract ? -1 : 1, tx));
+
+               (void) snprintf(name, sizeof (name), DMU_OBJACCT_PREFIX "%llx",
+                   (longlong_t)group);
+               VERIFY0(zap_increment(os, DMU_GROUPUSED_OBJECT, name,
+                   subtract ? -1 : 1, tx));
+       }
+}
+
 void
 dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx)
 {
@@ -1310,11 +1410,15 @@ dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx)
                if (flags & DN_ID_OLD_EXIST)  {
                        do_userquota_update(os, dn->dn_oldused, dn->dn_oldflags,
                            dn->dn_olduid, dn->dn_oldgid, B_TRUE, tx);
+                       do_userobjquota_update(os, dn->dn_oldflags,
+                           dn->dn_olduid, dn->dn_oldgid, B_TRUE, tx);
                }
                if (flags & DN_ID_NEW_EXIST) {
                        do_userquota_update(os, DN_USED_BYTES(dn->dn_phys),
                            dn->dn_phys->dn_flags,  dn->dn_newuid,
                            dn->dn_newgid, B_FALSE, tx);
+                       do_userobjquota_update(os, dn->dn_phys->dn_flags,
+                           dn->dn_newuid, dn->dn_newgid, B_FALSE, tx);
                }
 
                mutex_enter(&dn->dn_mtx);
@@ -1486,19 +1590,19 @@ dmu_objset_userspace_present(objset_t *os)
            OBJSET_FLAG_USERACCOUNTING_COMPLETE);
 }
 
-int
-dmu_objset_userspace_upgrade(objset_t *os)
+boolean_t
+dmu_objset_userobjspace_present(objset_t *os)
+{
+       return (os->os_phys->os_flags &
+           OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE);
+}
+
+static int
+dmu_objset_space_upgrade(objset_t *os)
 {
        uint64_t obj;
        int err = 0;
 
-       if (dmu_objset_userspace_present(os))
-               return (0);
-       if (!dmu_objset_userused_enabled(os))
-               return (SET_ERROR(ENOTSUP));
-       if (dmu_objset_is_snapshot(os))
-               return (SET_ERROR(EINVAL));
-
        /*
         * We simply need to mark every object dirty, so that it will be
         * synced out and now accounted.  If this is called
@@ -1512,6 +1616,13 @@ dmu_objset_userspace_upgrade(objset_t *os)
                dmu_buf_t *db;
                int objerr;
 
+               mutex_enter(&os->os_upgrade_lock);
+               if (os->os_upgrade_exit)
+                       err = SET_ERROR(EINTR);
+               mutex_exit(&os->os_upgrade_lock);
+               if (err != 0)
+                       return (err);
+
                if (issig(JUSTLOOKING) && issig(FORREAL))
                        return (SET_ERROR(EINTR));
 
@@ -1529,12 +1640,60 @@ dmu_objset_userspace_upgrade(objset_t *os)
                dmu_buf_rele(db, FTAG);
                dmu_tx_commit(tx);
        }
+       return (0);
+}
+
+int
+dmu_objset_userspace_upgrade(objset_t *os)
+{
+       int err = 0;
+
+       if (dmu_objset_userspace_present(os))
+               return (0);
+       if (dmu_objset_is_snapshot(os))
+               return (SET_ERROR(EINVAL));
+       if (!dmu_objset_userused_enabled(os))
+               return (SET_ERROR(ENOTSUP));
+
+       err = dmu_objset_space_upgrade(os);
+       if (err)
+               return (err);
 
        os->os_flags |= OBJSET_FLAG_USERACCOUNTING_COMPLETE;
        txg_wait_synced(dmu_objset_pool(os), 0);
        return (0);
 }
 
+static int
+dmu_objset_userobjspace_upgrade_cb(objset_t *os)
+{
+       int err = 0;
+
+       if (dmu_objset_userobjspace_present(os))
+               return (0);
+       if (dmu_objset_is_snapshot(os))
+               return (SET_ERROR(EINVAL));
+       if (!dmu_objset_userobjused_enabled(os))
+               return (SET_ERROR(ENOTSUP));
+
+       dmu_objset_ds(os)->ds_feature_activation_needed[
+           SPA_FEATURE_USEROBJ_ACCOUNTING] = B_TRUE;
+
+       err = dmu_objset_space_upgrade(os);
+       if (err)
+               return (err);
+
+       os->os_flags |= OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE;
+       txg_wait_synced(dmu_objset_pool(os), 0);
+       return (0);
+}
+
+void
+dmu_objset_userobjspace_upgrade(objset_t *os)
+{
+       dmu_objset_upgrade(os, dmu_objset_userobjspace_upgrade_cb);
+}
+
 void
 dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
     uint64_t *usedobjsp, uint64_t *availobjsp)
@@ -2096,4 +2255,7 @@ EXPORT_SYMBOL(dmu_objset_userquota_get_ids);
 EXPORT_SYMBOL(dmu_objset_userused_enabled);
 EXPORT_SYMBOL(dmu_objset_userspace_upgrade);
 EXPORT_SYMBOL(dmu_objset_userspace_present);
+EXPORT_SYMBOL(dmu_objset_userobjused_enabled);
+EXPORT_SYMBOL(dmu_objset_userobjspace_upgrade);
+EXPORT_SYMBOL(dmu_objset_userobjspace_present);
 #endif
index b19f50af9c72ec3972e9e7b16cbf37c53c3d1c98..6d1fa3339ae6646e5858cecccab2f186f4faf710 100644 (file)
@@ -570,12 +570,17 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
                dn->dn_oldused = DN_USED_BYTES(dn->dn_phys);
                dn->dn_oldflags = dn->dn_phys->dn_flags;
                dn->dn_phys->dn_flags |= DNODE_FLAG_USERUSED_ACCOUNTED;
+               if (dmu_objset_userobjused_enabled(dn->dn_objset))
+                       dn->dn_phys->dn_flags |=
+                           DNODE_FLAG_USEROBJUSED_ACCOUNTED;
                mutex_exit(&dn->dn_mtx);
                dmu_objset_userquota_get_ids(dn, B_FALSE, tx);
        } else {
                /* Once we account for it, we should always account for it. */
                ASSERT(!(dn->dn_phys->dn_flags &
                    DNODE_FLAG_USERUSED_ACCOUNTED));
+               ASSERT(!(dn->dn_phys->dn_flags &
+                   DNODE_FLAG_USEROBJUSED_ACCOUNTED));
        }
 
        mutex_enter(&dn->dn_mtx);
index c2f914e11c093aa5896809620ead9932646e2412..0a480d3ece8ef064ed4b923e402cfbd159cb0ac4 100644 (file)
@@ -1167,6 +1167,13 @@ spa_activate(spa_t *spa, int mode)
         */
        spa->spa_zvol_taskq = taskq_create("z_zvol", 1, defclsyspri,
            1, INT_MAX, 0);
+
+       /*
+        * The taskq to upgrade datasets in this pool. Currently used by
+        * feature SPA_FEATURE_USEROBJ_ACCOUNTING.
+        */
+       spa->spa_upgrade_taskq = taskq_create("z_upgrade", boot_ncpus,
+           defclsyspri, 1, INT_MAX, TASKQ_DYNAMIC);
 }
 
 /*
@@ -1190,6 +1197,11 @@ spa_deactivate(spa_t *spa)
                spa->spa_zvol_taskq = NULL;
        }
 
+       if (spa->spa_upgrade_taskq) {
+               taskq_destroy(spa->spa_upgrade_taskq);
+               spa->spa_upgrade_taskq = NULL;
+       }
+
        txg_list_destroy(&spa->spa_vdev_txg_list);
 
        list_destroy(&spa->spa_config_dirty_list);
index 9beb4903e5bd119c71e4c3eebe34d9fa9c58c6d7..ccd65a7b74d9eeacaea22849041f9166d6c93cec 100644 (file)
@@ -285,4 +285,15 @@ zpool_feature_init(void)
            "Edon-R hash algorithm.",
            ZFEATURE_FLAG_PER_DATASET, edonr_deps);
        }
+       {
+       static const spa_feature_t userobj_accounting_deps[] = {
+               SPA_FEATURE_EXTENSIBLE_DATASET,
+               SPA_FEATURE_NONE
+       };
+       zfeature_register(SPA_FEATURE_USEROBJ_ACCOUNTING,
+           "org.zfsonlinux:userobj_accounting", "userobj_accounting",
+           "User/Group object accounting.",
+           ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
+           userobj_accounting_deps);
+       }
 }
index 451000010390ffece77127f87a620097fe17e1a7..7198c7ebff16f002f05407c9da73bcbc1fb6d16f 100644 (file)
@@ -1886,7 +1886,9 @@ boolean_t
 zfs_acl_ids_overquota(zfs_sb_t *zsb, zfs_acl_ids_t *acl_ids)
 {
        return (zfs_fuid_overquota(zsb, B_FALSE, acl_ids->z_fuid) ||
-           zfs_fuid_overquota(zsb, B_TRUE, acl_ids->z_fgid));
+           zfs_fuid_overquota(zsb, B_TRUE, acl_ids->z_fgid) ||
+           zfs_fuid_overobjquota(zsb, B_FALSE, acl_ids->z_fuid) ||
+           zfs_fuid_overobjquota(zsb, B_TRUE, acl_ids->z_fgid));
 }
 
 /*
index e5704e258d5aa56d541afe0744b82f28d6fd48d3..549a8311690f53a22a61cc1c283e933271d1bfca 100644 (file)
@@ -244,9 +244,14 @@ static const char *userquota_perms[] = {
        ZFS_DELEG_PERM_USERQUOTA,
        ZFS_DELEG_PERM_GROUPUSED,
        ZFS_DELEG_PERM_GROUPQUOTA,
+       ZFS_DELEG_PERM_USEROBJUSED,
+       ZFS_DELEG_PERM_USEROBJQUOTA,
+       ZFS_DELEG_PERM_GROUPOBJUSED,
+       ZFS_DELEG_PERM_GROUPOBJQUOTA,
 };
 
 static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
+static int zfs_ioc_userobjspace_upgrade(zfs_cmd_t *zc);
 static int zfs_check_settable(const char *name, nvpair_t *property,
     cred_t *cr);
 static int zfs_check_clearable(char *dataset, nvlist_t *props,
@@ -1171,7 +1176,9 @@ zfs_secpolicy_userspace_one(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
                 * themself, allow it.
                 */
                if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
-                   zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
+                   zc->zc_objset_type == ZFS_PROP_USERQUOTA ||
+                   zc->zc_objset_type == ZFS_PROP_USEROBJUSED ||
+                   zc->zc_objset_type == ZFS_PROP_USEROBJQUOTA) {
                        if (zc->zc_guid == crgetuid(cr))
                                return (0);
                } else {
@@ -2426,6 +2433,7 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source,
                        zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
                        (void) strcpy(zc->zc_name, dsname);
                        (void) zfs_ioc_userspace_upgrade(zc);
+                       (void) zfs_ioc_userobjspace_upgrade(zc);
                        kmem_free(zc, sizeof (zfs_cmd_t));
                }
                break;
@@ -3720,13 +3728,23 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
                            zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
                        const char *gq_prefix =
                            zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
+                       const char *uiq_prefix =
+                           zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA];
+                       const char *giq_prefix =
+                           zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA];
 
                        if (strncmp(propname, uq_prefix,
                            strlen(uq_prefix)) == 0) {
                                perm = ZFS_DELEG_PERM_USERQUOTA;
+                       } else if (strncmp(propname, uiq_prefix,
+                           strlen(uiq_prefix)) == 0) {
+                               perm = ZFS_DELEG_PERM_USEROBJQUOTA;
                        } else if (strncmp(propname, gq_prefix,
                            strlen(gq_prefix)) == 0) {
                                perm = ZFS_DELEG_PERM_GROUPQUOTA;
+                       } else if (strncmp(propname, giq_prefix,
+                           strlen(giq_prefix)) == 0) {
+                               perm = ZFS_DELEG_PERM_GROUPOBJQUOTA;
                        } else {
                                /* USERUSED and GROUPUSED are read-only */
                                return (SET_ERROR(EINVAL));
@@ -4927,6 +4945,48 @@ zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
        return (error);
 }
 
+/*
+ * inputs:
+ * zc_name             name of filesystem
+ *
+ * outputs:
+ * none
+ */
+static int
+zfs_ioc_userobjspace_upgrade(zfs_cmd_t *zc)
+{
+       objset_t *os;
+       int error;
+
+       error = dmu_objset_hold(zc->zc_name, FTAG, &os);
+       if (error != 0)
+               return (error);
+
+       dsl_dataset_long_hold(dmu_objset_ds(os), FTAG);
+       dsl_pool_rele(dmu_objset_pool(os), FTAG);
+
+       if (dmu_objset_userobjspace_upgradable(os)) {
+               mutex_enter(&os->os_upgrade_lock);
+               if (os->os_upgrade_id == 0) {
+                       /* clear potential error code and retry */
+                       os->os_upgrade_status = 0;
+                       mutex_exit(&os->os_upgrade_lock);
+
+                       dmu_objset_userobjspace_upgrade(os);
+               } else {
+                       mutex_exit(&os->os_upgrade_lock);
+               }
+
+               taskq_wait_id(os->os_spa->spa_upgrade_taskq, os->os_upgrade_id);
+               error = os->os_upgrade_status;
+       }
+
+       dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
+       dsl_dataset_rele(dmu_objset_ds(os), FTAG);
+
+       return (error);
+}
+
 static int
 zfs_ioc_share(zfs_cmd_t *zc)
 {
index d8b27461aacc9571275e3350ec5510793c42d43c..63b7f923087ff92d7d77d58ce5fb2b26f9bc44a8 100644 (file)
@@ -431,17 +431,22 @@ zfs_userquota_prop_to_obj(zfs_sb_t *zsb, zfs_userquota_prop_t type)
 {
        switch (type) {
        case ZFS_PROP_USERUSED:
+       case ZFS_PROP_USEROBJUSED:
                return (DMU_USERUSED_OBJECT);
        case ZFS_PROP_GROUPUSED:
+       case ZFS_PROP_GROUPOBJUSED:
                return (DMU_GROUPUSED_OBJECT);
        case ZFS_PROP_USERQUOTA:
                return (zsb->z_userquota_obj);
        case ZFS_PROP_GROUPQUOTA:
                return (zsb->z_groupquota_obj);
+       case ZFS_PROP_USEROBJQUOTA:
+               return (zsb->z_userobjquota_obj);
+       case ZFS_PROP_GROUPOBJQUOTA:
+               return (zsb->z_groupobjquota_obj);
        default:
-               return (SET_ERROR(ENOTSUP));
+               return (ZFS_NO_OBJECT);
        }
-       return (0);
 }
 
 int
@@ -453,16 +458,25 @@ zfs_userspace_many(zfs_sb_t *zsb, zfs_userquota_prop_t type,
        zap_attribute_t za;
        zfs_useracct_t *buf = vbuf;
        uint64_t obj;
+       int offset = 0;
 
        if (!dmu_objset_userspace_present(zsb->z_os))
                return (SET_ERROR(ENOTSUP));
 
+       if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA) &&
+           !dmu_objset_userobjspace_present(zsb->z_os))
+               return (SET_ERROR(ENOTSUP));
+
        obj = zfs_userquota_prop_to_obj(zsb, type);
-       if (obj == 0) {
+       if (obj == ZFS_NO_OBJECT) {
                *bufsizep = 0;
                return (0);
        }
 
+       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED)
+               offset = DMU_OBJACCT_PREFIX_LEN;
+
        for (zap_cursor_init_serialized(&zc, zsb->z_os, obj, *cookiep);
            (error = zap_cursor_retrieve(&zc, &za)) == 0;
            zap_cursor_advance(&zc)) {
@@ -470,7 +484,15 @@ zfs_userspace_many(zfs_sb_t *zsb, zfs_userquota_prop_t type,
                    *bufsizep)
                        break;
 
-               fuidstr_to_sid(zsb, za.za_name,
+               /*
+                * skip object quota (with zap name prefix DMU_OBJACCT_PREFIX)
+                * when dealing with block quota and vice versa.
+                */
+               if ((offset > 0) != (strncmp(za.za_name, DMU_OBJACCT_PREFIX,
+                   DMU_OBJACCT_PREFIX_LEN) == 0))
+                       continue;
+
+               fuidstr_to_sid(zsb, za.za_name + offset,
                    buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid);
 
                buf->zu_space = za.za_first_integer;
@@ -511,7 +533,8 @@ int
 zfs_userspace_one(zfs_sb_t *zsb, zfs_userquota_prop_t type,
     const char *domain, uint64_t rid, uint64_t *valp)
 {
-       char buf[32];
+       char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+       int offset = 0;
        int err;
        uint64_t obj;
 
@@ -520,11 +543,21 @@ zfs_userspace_one(zfs_sb_t *zsb, zfs_userquota_prop_t type,
        if (!dmu_objset_userspace_present(zsb->z_os))
                return (SET_ERROR(ENOTSUP));
 
+       if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA) &&
+           !dmu_objset_userobjspace_present(zsb->z_os))
+               return (SET_ERROR(ENOTSUP));
+
        obj = zfs_userquota_prop_to_obj(zsb, type);
-       if (obj == 0)
+       if (obj == ZFS_NO_OBJECT)
                return (0);
 
-       err = id_to_fuidstr(zsb, domain, rid, buf, B_FALSE);
+       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED) {
+               strncpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN);
+               offset = DMU_OBJACCT_PREFIX_LEN;
+       }
+
+       err = id_to_fuidstr(zsb, domain, rid, buf + offset, B_FALSE);
        if (err)
                return (err);
 
@@ -545,14 +578,25 @@ zfs_set_userquota(zfs_sb_t *zsb, zfs_userquota_prop_t type,
        uint64_t *objp;
        boolean_t fuid_dirtied;
 
-       if (type != ZFS_PROP_USERQUOTA && type != ZFS_PROP_GROUPQUOTA)
-               return (SET_ERROR(EINVAL));
-
        if (zsb->z_version < ZPL_VERSION_USERSPACE)
                return (SET_ERROR(ENOTSUP));
 
-       objp = (type == ZFS_PROP_USERQUOTA) ? &zsb->z_userquota_obj :
-           &zsb->z_groupquota_obj;
+       switch (type) {
+       case ZFS_PROP_USERQUOTA:
+               objp = &zsb->z_userquota_obj;
+               break;
+       case ZFS_PROP_GROUPQUOTA:
+               objp = &zsb->z_groupquota_obj;
+               break;
+       case ZFS_PROP_USEROBJQUOTA:
+               objp = &zsb->z_userobjquota_obj;
+               break;
+       case ZFS_PROP_GROUPOBJQUOTA:
+               objp = &zsb->z_groupobjquota_obj;
+               break;
+       default:
+               return (SET_ERROR(EINVAL));
+       }
 
        err = id_to_fuidstr(zsb, domain, rid, buf, B_TRUE);
        if (err)
@@ -597,10 +641,40 @@ zfs_set_userquota(zfs_sb_t *zsb, zfs_userquota_prop_t type,
 }
 EXPORT_SYMBOL(zfs_set_userquota);
 
+boolean_t
+zfs_fuid_overobjquota(zfs_sb_t *zsb, boolean_t isgroup, uint64_t fuid)
+{
+       char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+       uint64_t used, quota, usedobj, quotaobj;
+       int err;
+
+       if (!dmu_objset_userobjspace_present(zsb->z_os)) {
+               if (dmu_objset_userobjspace_upgradable(zsb->z_os))
+                       dmu_objset_userobjspace_upgrade(zsb->z_os);
+               return (B_FALSE);
+       }
+
+       usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT;
+       quotaobj = isgroup ? zsb->z_groupobjquota_obj : zsb->z_userobjquota_obj;
+       if (quotaobj == 0 || zsb->z_replay)
+               return (B_FALSE);
+
+       (void) sprintf(buf, "%llx", (longlong_t)fuid);
+       err = zap_lookup(zsb->z_os, quotaobj, buf, 8, 1, &quota);
+       if (err != 0)
+               return (B_FALSE);
+
+       (void) sprintf(buf, DMU_OBJACCT_PREFIX "%llx", (longlong_t)fuid);
+       err = zap_lookup(zsb->z_os, usedobj, buf, 8, 1, &used);
+       if (err != 0)
+               return (B_FALSE);
+       return (used >= quota);
+}
+
 boolean_t
 zfs_fuid_overquota(zfs_sb_t *zsb, boolean_t isgroup, uint64_t fuid)
 {
-       char buf[32];
+       char buf[20];
        uint64_t used, quota, usedobj, quotaobj;
        int err;
 
@@ -777,6 +851,18 @@ zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp)
        if (error && error != ENOENT)
                goto out;
 
+       error = zap_lookup(os, MASTER_NODE_OBJ,
+           zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA],
+           8, 1, &zsb->z_userobjquota_obj);
+       if (error && error != ENOENT)
+               goto out;
+
+       error = zap_lookup(os, MASTER_NODE_OBJ,
+           zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA],
+           8, 1, &zsb->z_groupobjquota_obj);
+       if (error && error != ENOENT)
+               goto out;
+
        error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1,
            &zsb->z_fuid_obj);
        if (error && error != ENOENT)
index 92f867ab96e25e675ed898f839b5aec92c6b0b9a..f707205b3fd997279d335cad6fadfd216ae2a75b 100644 (file)
@@ -602,21 +602,18 @@ tests = ['sparse_001_pos']
 [tests/functional/truncate]
 tests = ['truncate_001_pos', 'truncate_002_pos']
 
-# DISABLED:
-# groupspace_001_pos
-# groupspace_002_pos
-# userquota_001_pos
-# userquota_004_pos
-# userquota_007_pos
-# userquota_010_pos
-# userspace_001_pos
-# userspace_002_pos
+[tests/functional/upgrade]
+tests = [ 'upgrade_userobj_001_pos' ]
+
 [tests/functional/userquota]
 tests = [
-    'userquota_002_pos', 'userquota_003_pos',
-    'userquota_005_neg', 'userquota_006_pos',
-    'userquota_008_pos', 'userquota_009_pos',
-    'userquota_011_pos', 'userquota_012_neg']
+    'userquota_001_pos', 'userquota_002_pos', 'userquota_003_pos',
+    'userquota_004_pos', 'userquota_005_neg', 'userquota_006_pos',
+    'userquota_007_pos', 'userquota_008_pos', 'userquota_009_pos',
+    'userquota_010_pos', 'userquota_011_pos', 'userquota_012_neg',
+    'userquota_013_pos',
+    'userspace_001_pos', 'userspace_002_pos', 'userspace_003_pos',
+    'groupspace_001_pos', 'groupspace_002_pos', 'groupspace_003_pos' ]
 
 # DISABLED:
 # vdev_zaps_007_pos -- fails due to a pre-existing issue with zpool split
index 418fb9d07ca53a3e1e488185f948a49ff4d894fa..62dee162790a483b3a08a46dda7db1eeb2eff426 100644 (file)
@@ -48,10 +48,7 @@ main(int argc, char **argv)
        if (argc == 4 && sscanf(argv[3], "%u", &first_file) != 1)
                usage("Invalid first file", -3);
 
-       if (numfiles < first_file)
-               usage("First file larger than last file", -3);
-
-       for (i = first_file; i <= numfiles; i++) {
+       for (i = first_file; i < first_file + numfiles; i++) {
                int fd;
                (void) snprintf(buf, MAXPATHLEN, "%s%u", argv[1], i);
                if ((fd = open(buf, O_CREAT | O_EXCL, O_RDWR)) == -1) {
index 62ba3a9eb3b3fd89866fe3453a59a76b5400aff2..1857cf91fe2bef1147749e232ab9da0dea6a6d53 100644 (file)
@@ -2857,3 +2857,29 @@ function block_device_wait
                $UDEVADM settle
        fi
 }
+
+#
+# Synchronize all the data in pool
+#
+# $1 pool name
+#
+function sync_pool #pool
+{
+       typeset pool=${1:-$TESTPOOL}
+
+       log_must $SYNC
+       log_must $SLEEP 2
+       # Flush all the pool data.
+       typeset -i ret
+       $ZPOOL scrub $pool >/dev/null 2>&1
+       ret=$?
+       (( $ret != 0 )) && \
+       log_fail "$ZPOOL scrub $pool failed."
+
+       while ! is_pool_scrubbed $pool; do
+               if is_pool_resilvered $pool ; then
+                       log_fail "$pool should not be resilver completed."
+               fi
+               log_must $SLEEP 2
+       done
+}
index ed01eafb489863d9579b2c45cbfcd32d55381143..ffba71b519900e97df4c73ce0554691878b82e88 100644 (file)
@@ -51,6 +51,7 @@ SUBDIRS = \
        sparse \
        threadsappend \
        truncate \
+       upgrade \
        userquota \
        vdev_zaps \
        write_dirs \
index 3807d0af63d820b982587595fcb74a531f4366ed..699229fef78d91e603b1ed345e9796980f436425 100644 (file)
@@ -38,7 +38,8 @@ typeset -a properties=("size" "capacity" "altroot" "health" "guid" "version"
     "feature@large_blocks" "feature@large_dnode" "feature@filesystem_limits"
     "feature@spacemap_histogram" "feature@enabled_txg" "feature@hole_birth"
     "feature@extensible_dataset" "feature@bookmarks" "feature@embedded_data"
-    "feature@sha512" "feature@skein" "feature@edonr")
+    "feature@sha512" "feature@skein" "feature@edonr"
+    "feature@userobj_accounting")
 else
 typeset -a properties=("size" "capacity" "altroot" "health" "guid" "version"
     "bootfs" ""leaked" delegation" "autoreplace" "cachefile" "dedupditto" "dedupratio"
index cb8271797bafd2ba2482523ddc11cda3505414d6..56e2bd19d87668f6d805e346604f0a89c83daabd 100644 (file)
@@ -213,32 +213,6 @@ function get_vdevs #pool cnt
        $ECHO "$vdevs"
 }
 
-#
-# Synchronize all the data in pool
-#
-# $1 pool name
-#
-function sync_pool #pool
-{
-       typeset pool=$1
-
-       log_must $SYNC
-       log_must $SLEEP 2
-       # Flush all the pool data.
-       typeset -i ret
-       $ZPOOL scrub $pool >/dev/null 2>&1
-       ret=$?
-       (( $ret != 0 )) && \
-               log_fail "$ZPOOL scrub $pool failed."
-
-       while ! is_pool_scrubbed $pool; do
-               if is_pool_resilvered $pool ; then
-                       log_fail "$pool should not be resilver completed."
-               fi
-               log_must $SLEEP 2
-       done
-}
-
 #
 # Create and replace the same name virtual device files
 #
diff --git a/tests/zfs-tests/tests/functional/upgrade/Makefile.am b/tests/zfs-tests/tests/functional/upgrade/Makefile.am
new file mode 100644 (file)
index 0000000..3103434
--- /dev/null
@@ -0,0 +1,5 @@
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/upgrade
+dist_pkgdata_SCRIPTS = \
+       setup.ksh \
+       cleanup.ksh \
+       upgrade_userobj_001_pos.ksh
diff --git a/tests/zfs-tests/tests/functional/upgrade/cleanup.ksh b/tests/zfs-tests/tests/functional/upgrade/cleanup.ksh
new file mode 100644 (file)
index 0000000..6b0eb9d
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. No rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+log_must $ZPOOL destroy $TESTPOOL
+
+log_must $RM /tmp/zpool_upgrade_test.dat
+
+default_cleanup
diff --git a/tests/zfs-tests/tests/functional/upgrade/setup.ksh b/tests/zfs-tests/tests/functional/upgrade/setup.ksh
new file mode 100644 (file)
index 0000000..57b4835
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. No rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+# create a pool without any features
+log_must $MKFILE 128m /tmp/zpool_upgrade_test.dat
+log_must $ZPOOL create -d -m $TESTDIR $TESTPOOL /tmp/zpool_upgrade_test.dat
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos.ksh b/tests/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos.ksh
new file mode 100644 (file)
index 0000000..49087f5
--- /dev/null
@@ -0,0 +1,98 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2013 by Jinshan Xiong. No rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+#
+# Check that zfs upgrade for object count accounting works.
+# Since userobjaccounting is a per dataset feature, this test case
+# will create multiple dataset and try different upgrade method.
+#
+# STRATEGY:
+# 1. Create a pool with all features disabled
+# 2. Create a few dataset for testing
+# 3. Make sure automatic upgrade work
+# 4. Make sure manual upgrade work
+#
+
+function cleanup
+{
+       datasetexists $TESTPOOL/fs1 && log_must $ZFS destroy $TESTPOOL/fs1
+       datasetexists $TESTPOOL/fs2 && log_must $ZFS destroy $TESTPOOL/fs2
+}
+
+verify_runnable "global"
+
+log_assert "pool upgrade for userobj accounting should work"
+log_onexit cleanup
+
+log_must $MKFILES $TESTDIR/tf $((RANDOM % 1000 + 1))
+log_must $ZFS create $TESTPOOL/fs1
+log_must $MKFILES $TESTDIR/fs1/tf $((RANDOM % 1000 + 1))
+log_must $ZFS create $TESTPOOL/fs2
+log_must $MKFILES $TESTDIR/fs2/tf $((RANDOM % 1000 + 1))
+log_must $ZFS umount $TESTPOOL/fs2
+
+# Make sure userobj accounting is disabled
+$ZFS userspace -o objused -H $TESTPOOL | $HEAD -n 1 | $GREP -q "-" ||
+       log_fail "userobj accounting should be disabled initially"
+
+# Upgrade zpool to support all features
+log_must $ZPOOL upgrade $TESTPOOL
+
+# Make sure userobj accounting is disabled again
+$ZFS userspace -o objused -H $TESTPOOL | $HEAD -n 1 | $GREP -q "-" ||
+       log_fail "userobj accounting should be disabled after pool upgrade"
+
+# Create a file in fs1 should trigger dataset upgrade
+log_must $MKFILE 1m $TESTDIR/fs1/tf
+sync_pool
+
+# Make sure userobj accounting is working for fs1
+$ZFS userspace -o objused -H $TESTPOOL/fs1 | $HEAD -n 1 | $GREP -q "-" &&
+       log_fail "userobj accounting should be enabled for $TESTPOOL/fs1"
+
+# Mount a dataset should trigger upgrade
+log_must $ZFS mount $TESTPOOL/fs2
+sync_pool
+
+# Make sure userobj accounting is working for fs2
+$ZFS userspace -o objused -H $TESTPOOL/fs2 | $HEAD -n 1 | $GREP -q "-" &&
+       log_fail "userobj accounting should be enabled for $TESTPOOL/fs2"
+
+# All in all, after having been through this, the dataset for testpool
+# still shouldn't be upgraded
+$ZFS userspace -o objused -H $TESTPOOL | $HEAD -n 1 | $GREP -q "-" ||
+       log_fail "userobj accounting should be disabled for $TESTPOOL"
+
+# Manual upgrade root dataset
+log_must $ZFS set version=current $TESTPOOL
+$ZFS userspace -o objused -H $TESTPOOL | $HEAD -n 1 | $GREP -q "-" &&
+       log_fail "userobj accounting should be enabled for $TESTPOOL"
+
+log_pass "all tests passed - what a lucky day!"
index b726599641de1f441e3bb997b64ba73492a35523..b7f38f98e9b8bee344128bfdd6fcdcedfadeade6 100644 (file)
@@ -6,6 +6,7 @@ dist_pkgdata_SCRIPTS = \
        cleanup.ksh \
        groupspace_001_pos.ksh \
        groupspace_002_pos.ksh \
+       groupspace_003_pos.ksh \
        userquota_001_pos.ksh \
        userquota_002_pos.ksh \
        userquota_003_pos.ksh \
@@ -18,5 +19,7 @@ dist_pkgdata_SCRIPTS = \
        userquota_010_pos.ksh \
        userquota_011_pos.ksh \
        userquota_012_neg.ksh \
+       userquota_013_pos.ksh \
        userspace_001_pos.ksh \
-       userspace_002_pos.ksh
+       userspace_002_pos.ksh \
+       userspace_003_pos.ksh
diff --git a/tests/zfs-tests/tests/functional/userquota/groupspace_003_pos.ksh b/tests/zfs-tests/tests/functional/userquota/groupspace_003_pos.ksh
new file mode 100644 (file)
index 0000000..7ea8cd5
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
+
+#
+# DESCRIPTION:
+#       Check the user used and groupspace object counts in zfs groupspace
+#
+#
+# STRATEGY:
+#      1. set zfs groupquota to a fs
+#      2. create objects for different users in the same group
+#      3. use zfs groupspace to check the object count
+#
+
+function cleanup
+{
+       if datasetexists $snapfs; then
+               log_must $ZFS destroy $snapfs
+       fi
+
+       log_must $RM -f ${QFILE}_*
+       log_must cleanup_quota
+}
+
+function group_object_count
+{
+       typeset fs=$1
+       typeset user=$2
+       typeset cnt=$($ZFS groupspace -oname,objused $fs | $GREP $user |
+                       $AWK '{print $2}')
+       echo $cnt
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs groupspace object used"
+
+mkmount_writable $QFS
+log_must $ZFS set xattr=sa $QFS
+
+((user1_cnt = RANDOM % 100 + 1))
+((user2_cnt = RANDOM % 100 + 1))
+log_must user_run $QUSER1 $MKFILES ${QFILE}_1 $user1_cnt
+log_must user_run $QUSER2 $MKFILES ${QFILE}_2 $user2_cnt
+((grp_cnt = user1_cnt + user2_cnt))
+sync_pool
+
+typeset snapfs=$QFS@snap
+
+log_must $ZFS snapshot $snapfs
+
+log_must eval "$ZFS groupspace $QFS >/dev/null 2>&1"
+log_must eval "$ZFS groupspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+       log_note "check the object count in zfs groupspace $fs"
+        [[ $(group_object_count $fs $QGROUP) -eq $grp_cnt ]] ||
+                log_fail "expected $grp_cnt"
+done
+
+log_note "file removal"
+log_must $RM ${QFILE}_*
+sync_pool
+
+[[ $(group_object_count $QFS $QGROUP) -eq 0 ]] ||
+        log_fail "expected 0 files for $QGROUP"
+
+[[ $(group_object_count $snapfs $QGROUP) -eq $grp_cnt ]] ||
+        log_fail "expected $grp_cnt files for $QGROUP"
+
+cleanup
+log_pass "Check the zfs groupspace object used pass as expect"
index b134a7677f7e072d8da028642f284e78d6a2a56a..7a4f8f3ba575c77fb1bee9bbb4ac2be8696e8b09 100755 (executable)
@@ -58,7 +58,7 @@ mkmount_writable $QFS
 log_note "Check the userquota@$QUSER1"
 log_must $ZFS set userquota@$QUSER1=$UQUOTA_SIZE $QFS
 log_must user_run $QUSER1 $MKFILE $UQUOTA_SIZE $QFILE
-$SYNC
+sync_pool
 log_mustnot user_run $QUSER1 $MKFILE 1 $OFILE
 cleanup_quota
 
@@ -66,7 +66,7 @@ log_note "Check the groupquota@$QGROUP"
 log_must $ZFS set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
 mkmount_writable $QFS
 log_must user_run $QUSER1 $MKFILE $GQUOTA_SIZE $QFILE
-$SYNC
+sync_pool
 log_mustnot user_run $QUSER1 $MKFILE 1 $OFILE
 
 cleanup_quota
index 6bdcf1c769d303e8314e2448c217db684c83cccb..74906326171dafebff6b6032949ebbaa1122fc4a 100755 (executable)
@@ -50,6 +50,7 @@ log_onexit cleanup
 
 log_assert "Check the basic function of {user|group} used"
 
+sync_pool
 typeset user_used=$(get_value "userused@$QUSER1" $QFS)
 typeset group_used=$(get_value "groupused@$QGROUP" $QFS)
 
@@ -62,7 +63,7 @@ fi
 
 mkmount_writable $QFS
 log_must user_run $QUSER1 $MKFILE 100m $QFILE
-$SYNC
+sync_pool
 
 user_used=$(get_value "userused@$QUSER1" $QFS)
 group_used=$(get_value "groupused@$QGROUP" $QFS)
index b9260585fb1a1592471642bf84aae320136eaa97..7f7f9672ddf3817c022b4a7e357846ad407b3dd4 100755 (executable)
@@ -57,7 +57,7 @@ log_must $ZFS set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
 
 mkmount_writable $QFS
 log_must user_run $QUSER1 $MKFILE $UQUOTA_SIZE $QFILE
-$SYNC
+sync_pool
 
 log_must eval "$ZFS get -p userused@$QUSER1 $QFS >/dev/null 2>&1"
 log_must eval "$ZFS get -p groupused@$GROUPUSED $QFS >/dev/null 2>&1"
diff --git a/tests/zfs-tests/tests/functional/userquota/userquota_013_pos.ksh b/tests/zfs-tests/tests/functional/userquota/userquota_013_pos.ksh
new file mode 100644 (file)
index 0000000..a84a455
--- /dev/null
@@ -0,0 +1,77 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+#       Check the basic function of the userobjquota and groupobjquota
+#
+#
+# STRATEGY:
+#       1. Set userobjquota and overwrite the quota size
+#       2. Creating new object should fail with Disc quota exceeded
+#       3. Set groupobjquota and overwrite the quota size
+#       4. Creating new object should fail with Disc quota exceeded
+#
+#
+
+function cleanup
+{
+       log_must $RM -f ${QFILE}_*
+       cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "If creating object exceeds {user|group}objquota count, it will fail"
+
+mkmount_writable $QFS
+log_must $ZFS set xattr=sa $QFS
+
+log_note "Check the userobjquota@$QUSER1"
+log_must $ZFS set userobjquota@$QUSER1=100 $QFS
+log_must user_run $QUSER1 $MKFILES ${QFILE}_1 100
+sync_pool
+log_mustnot user_run $QUSER1 $MKFILE 1 $OFILE
+cleanup_quota
+
+log_note "Check the groupobjquota@$QGROUP"
+log_must $ZFS set groupobjquota@$QGROUP=200 $QFS
+mkmount_writable $QFS
+log_must user_run $QUSER1 $MKFILES ${QFILE}_2 100
+sync_pool
+log_mustnot user_run $QUSER2 $MKFILE 1 $OFILE
+
+cleanup
+log_pass "Creating objects exceeds {user|group}objquota count, it as expect"
index 771919602e3c726907df925cc213bc34461ba95c..2b50e293eeb2a682893e5a425686bc205f400411 100644 (file)
@@ -38,8 +38,11 @@ function cleanup_quota
 {
        if datasetexists $QFS; then
                log_must $ZFS set userquota@$QUSER1=none $QFS
+               log_must $ZFS set userobjquota@$QUSER1=none $QFS
                log_must $ZFS set userquota@$QUSER2=none $QFS
+               log_must $ZFS set userobjquota@$QUSER2=none $QFS
                log_must $ZFS set groupquota@$QGROUP=none $QFS
+               log_must $ZFS set groupobjquota@$QGROUP=none $QFS
                recovery_writable $QFS
        fi
 
@@ -47,7 +50,7 @@ function cleanup_quota
        [[ -f $OFILE ]] && log_must $RM -f $OFILE
        $SYNC
 
-        return 0
+       return 0
 }
 
 #
index b29052db6894647bf1e27915063e00f218efd96e..cb84cf927b223b49cabd6ddeb26f5b2e80397e1d 100755 (executable)
@@ -75,7 +75,7 @@ for fs in "$QFS" "$snapfs"; do
        log_must eval "$ZFS userspace $fs | $GREP $QUSER1 | $GREP 100M"
 
        log_note "check the user used size in zfs userspace $fs"
-       log_must eval "$ZFS userspace $fs | $GREP $QUSER1 | $GREP 50.0M"
+       log_must eval "$ZFS userspace $fs | $GREP $QUSER1 | $GREP 50\\.\*M"
 done
 
 log_pass "Check the zfs userspace used and quota"
diff --git a/tests/zfs-tests/tests/functional/userquota/userspace_003_pos.ksh b/tests/zfs-tests/tests/functional/userquota/userspace_003_pos.ksh
new file mode 100644 (file)
index 0000000..421de65
--- /dev/null
@@ -0,0 +1,116 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
+
+#
+# DESCRIPTION:
+#       Check the user used object accounting in zfs userspace
+#
+#
+# STRATEGY:
+#       1. create a bunch of files by specific users
+#      2. use zfs userspace to check the used objects
+#      3. change the owner of test files and verify object count
+#      4. delete files and verify object count
+#
+
+function cleanup
+{
+       if datasetexists $snapfs; then
+               log_must $ZFS destroy $snapfs
+       fi
+
+       log_must $RM -f ${QFILE}_*
+       log_must cleanup_quota
+}
+
+function user_object_count
+{
+       typeset fs=$1
+       typeset user=$2
+       typeset cnt=$($ZFS userspace -oname,objused $fs |
+           $AWK /$user/'{print $2}')
+       echo $cnt
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs userspace object used"
+
+mkmount_writable $QFS
+log_must $ZFS set xattr=sa $QFS
+
+((user1_cnt = RANDOM % 100 + 1))
+((user2_cnt = RANDOM % 100 + 1))
+
+log_must user_run $QUSER1 $MKFILES ${QFILE}_1 $user1_cnt
+log_must user_run $QUSER2 $MKFILES ${QFILE}_2 $user2_cnt
+sync_pool
+
+typeset snapfs=$QFS@snap
+
+log_must $ZFS snapshot $snapfs
+
+log_must eval "$ZFS userspace $QFS >/dev/null 2>&1"
+log_must eval "$ZFS userspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+       log_note "check the user used objects in zfs userspace $fs"
+       [[ $(user_object_count $fs $QUSER1) -eq $user1_cnt ]] ||
+               log_fail "expected $user1_cnt"
+       [[ $(user_object_count $fs $QUSER2) -eq $user2_cnt ]] ||
+               log_fail "expected $user2_cnt"
+done
+
+log_note "change the owner of files"
+log_must $CHOWN $QUSER2 ${QFILE}_1*
+sync_pool
+
+[[ $(user_object_count $QFS $QUSER1) -eq 0 ]] ||
+       log_fail "expected 0 files for $QUSER1"
+
+[[ $(user_object_count $snapfs $QUSER1) -eq $user1_cnt ]] ||
+       log_fail "expected $user_cnt files for $QUSER1 in snapfs"
+
+[[ $(user_object_count $QFS $QUSER2) -eq $((user1_cnt+user2_cnt)) ]] ||
+       log_fail "expected $((user1_cnt+user2_cnt)) files for $QUSER2"
+
+log_note "file removal"
+log_must $RM ${QFILE}_*
+sync_pool
+
+[[ $(user_object_count $QFS $QUSER2) -eq 0 ]] ||
+        log_fail "expected 0 files for $QUSER2"
+
+cleanup
+log_pass "Check the zfs userspace object used"