Add support for user/group dnode accounting & quota

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>
This commit is contained in:
Jinshan Xiong
2016-10-04 11:46:10 -07:00
committed by Brian Behlendorf
parent af322debaa
commit 1de321e626
43 changed files with 1119 additions and 98 deletions
+10 -13
View 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
+1 -4
View 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) {
+26
View 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
}
@@ -51,6 +51,7 @@ SUBDIRS = \
sparse \
threadsappend \
truncate \
upgrade \
userquota \
vdev_zaps \
write_dirs \
@@ -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"
@@ -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
#
@@ -0,0 +1,5 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/upgrade
dist_pkgdata_SCRIPTS = \
setup.ksh \
cleanup.ksh \
upgrade_userobj_001_pos.ksh
@@ -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
@@ -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
@@ -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!"
@@ -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
@@ -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"
@@ -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
@@ -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)
@@ -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"
@@ -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"
@@ -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
}
#
@@ -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"
@@ -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"