Skip to content

Commit

Permalink
Add support for user/group dnode accounting & quota
Browse files Browse the repository at this point in the history
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 "dn-" 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: #3500

Signed-off-by: Johann Lombardi <[email protected]>
Signed-off-by: Jinshan Xiong <[email protected]>
Change-Id: I899ff446cbf2aa7e355e7d98a83d614f1cd4624b
  • Loading branch information
Jinshan Xiong committed Feb 13, 2016
1 parent eea9309 commit 76004c7
Show file tree
Hide file tree
Showing 18 changed files with 289 additions and 27 deletions.
4 changes: 3 additions & 1 deletion cmd/zdb/zdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1937,11 +1937,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",
Expand Down
36 changes: 33 additions & 3 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2817,11 +2817,15 @@ 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 ((((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||
p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA) &&
!(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) &&
!(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))))
((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||
p == ZFS_PROP_GROUPOBJUSED ||
p == ZFS_PROP_GROUPOBJQUOTA) &&
!(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP)))))
continue;

cb.cb_prop = p;
if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
return (ret);
Expand Down Expand Up @@ -3993,6 +3997,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"
Expand Down Expand Up @@ -4023,6 +4032,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 }
};

Expand Down Expand Up @@ -4100,6 +4113,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:
Expand Down Expand Up @@ -4603,6 +4620,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 = "";
Expand Down
4 changes: 4 additions & 0 deletions include/sys/dmu_objset.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -172,6 +173,9 @@ 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);
int dmu_objset_userobjspace_upgrade(objset_t *os);
boolean_t dmu_objset_userobjspace_present(objset_t *os);
int dmu_fsname(const char *snapname, char *buf);

void dmu_objset_evict_done(objset_t *os);
Expand Down
3 changes: 3 additions & 0 deletions include/sys/dnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ enum dnode_dirtycontext {
/* Does dnode have a SA spill blkptr in bonus? */
#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 */
uint8_t dn_indblkshift; /* ln2(indirect block size) */
Expand Down
4 changes: 4 additions & 0 deletions include/sys/dsl_deleg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 5 additions & 0 deletions include/sys/fs/zfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,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;

Expand Down Expand Up @@ -885,6 +889,7 @@ typedef enum zfs_ioc {
ZFS_IOC_BOOKMARK,
ZFS_IOC_GET_BOOKMARKS,
ZFS_IOC_DESTROY_BOOKMARKS,
ZFS_IOC_USEROBJSPACE_UPGRADE,

/*
* Linux - 3/64 numbers reserved.
Expand Down
2 changes: 2 additions & 0 deletions include/sys/zfs_vfsops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions include/zfeature_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ typedef enum spa_feature {
SPA_FEATURE_BOOKMARKS,
SPA_FEATURE_FS_SS_LIMIT,
SPA_FEATURE_LARGE_BLOCKS,
SPA_FEATURE_USEROBJ_ACCOUNTING,
SPA_FEATURES
} spa_feature_t;

Expand Down
4 changes: 4 additions & 0 deletions include/zfs_deleg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
15 changes: 11 additions & 4 deletions lib/libzfs/libzfs_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,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);
Expand Down Expand Up @@ -2736,8 +2738,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;

Expand Down Expand Up @@ -2870,7 +2876,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);
Expand Down
21 changes: 21 additions & 0 deletions man/man5/zpool-features.5
Original file line number Diff line number Diff line change
Expand Up @@ -432,5 +432,26 @@ set larger than 128KB, and will return to being \fBenabled\fR once all
filesystems that have ever had their recordsize larger than 128KB are destroyed.
.RE

.sp
.ne 2
.na
\fB\fBuserobj_accounting\fR\fR
.ad
.RS 4n
.TS
l l .
GUID org.zfsonlinux:user_object_accounting
READ\-ONLY COMPATIBLE yes
DEPENDENCIES none
.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.

.RE

.SH "SEE ALSO"
\fBzpool\fR(8)
4 changes: 4 additions & 0 deletions module/zcommon/zfs_deleg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
6 changes: 5 additions & 1 deletion module/zcommon/zfs_prop.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ const char *zfs_userquota_prop_prefixes[] = {
"userused@",
"userquota@",
"groupused@",
"groupquota@"
"groupquota@",
"userobjused@",
"userobjquota@",
"groupobjused@",
"groupobjquota@"
};

zprop_desc_t *
Expand Down
Loading

0 comments on commit 76004c7

Please sign in to comment.