From: Wang Shilong Date: Wed, 6 Nov 2019 13:01:18 +0000 (+0800) Subject: LU-9555 quota: df should return projid-specific values X-Git-Tag: 2.13.57~19 X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=commitdiff_plain;h=e5c8f6670fbeea9ec8c6092dfa4369508da54485 LU-9555 quota: df should return projid-specific values With local ext4 and XFS filesystems, it is possible to use "df /path/to/directory" (statfs()) to return the current project quota usage for that directory as "used", and min(projid quota limit, free space) as "total". statfs() is a natural interface for users/applications, since it represents the used/maximum space for that subdirectory. Otherwise, the user will get EDQUOT back when the project quota runs out for that directory and applications will not be able to figure out how much data they could write into that directory. Change-Id: I7f357e6e4de6a1465a48c2c08c4aa86c4c848dbb Signed-off-by: Wang Shilong Reviewed-on: https://review.whamcloud.com/36685 Tested-by: jenkins Reviewed-by: Andreas Dilger Tested-by: Maloo Reviewed-by: Hongchao Zhang --- diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index 40232fd..0bf5480 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -1365,9 +1365,9 @@ struct obd_dqinfo { /* XXX: same as if_dqblk struct in kernel, plus one padding */ struct obd_dqblk { - __u64 dqb_bhardlimit; - __u64 dqb_bsoftlimit; - __u64 dqb_curspace; + __u64 dqb_bhardlimit; /* kbytes unit */ + __u64 dqb_bsoftlimit; /* kbytes unit */ + __u64 dqb_curspace; /* bytes unit */ __u64 dqb_ihardlimit; __u64 dqb_isoftlimit; __u64 dqb_curinodes; diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index 7b39aa0..ce9758b 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -1126,14 +1126,15 @@ static int check_owner(int type, int id) return 0; } -static int quotactl_ioctl(struct ll_sb_info *sbi, struct if_quotactl *qctl) +int quotactl_ioctl(struct ll_sb_info *sbi, struct if_quotactl *qctl) { - int cmd = qctl->qc_cmd; - int type = qctl->qc_type; - int id = qctl->qc_id; - int valid = qctl->qc_valid; - int rc = 0; - ENTRY; + int cmd = qctl->qc_cmd; + int type = qctl->qc_type; + int id = qctl->qc_id; + int valid = qctl->qc_valid; + int rc = 0; + + ENTRY; switch (cmd) { case Q_SETQUOTA: diff --git a/lustre/llite/llite_internal.h b/lustre/llite/llite_internal.h index ad2a7f5..a0e3036 100644 --- a/lustre/llite/llite_internal.h +++ b/lustre/llite/llite_internal.h @@ -1028,6 +1028,7 @@ int ll_get_mdt_idx_by_fid(struct ll_sb_info *sbi, const struct lu_fid *fid); struct page *ll_get_dir_page(struct inode *dir, struct md_op_data *op_data, __u64 offset, struct ll_dir_chain *chain); void ll_release_page(struct inode *inode, struct page *page, bool remove); +int quotactl_ioctl(struct ll_sb_info *sbi, struct if_quotactl *qctl); /* llite/namei.c */ extern const struct inode_operations ll_special_inode_operations; diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c index 5e6bfd3..3744aa2 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -2205,6 +2205,52 @@ out: RETURN(rc); } +static int ll_statfs_project(struct inode *inode, struct kstatfs *sfs) +{ + struct if_quotactl qctl = { + .qc_cmd = LUSTRE_Q_GETQUOTA, + .qc_type = PRJQUOTA, + .qc_valid = QC_GENERAL, + }; + u64 limit, curblock; + int ret; + + qctl.qc_id = ll_i2info(inode)->lli_projid; + ret = quotactl_ioctl(ll_i2sbi(inode), &qctl); + if (ret) { + /* ignore errors if project ID does not have + * a quota limit or feature unsupported. + */ + if (ret == -ESRCH || ret == -EOPNOTSUPP) + ret = 0; + return ret; + } + + limit = ((qctl.qc_dqblk.dqb_bsoftlimit ? + qctl.qc_dqblk.dqb_bsoftlimit : + qctl.qc_dqblk.dqb_bhardlimit) * 1024) / sfs->f_bsize; + if (limit && sfs->f_blocks > limit) { + curblock = (qctl.qc_dqblk.dqb_curspace + + sfs->f_bsize - 1) / sfs->f_bsize; + sfs->f_blocks = limit; + sfs->f_bfree = sfs->f_bavail = + (sfs->f_blocks > curblock) ? + (sfs->f_blocks - curblock) : 0; + } + + limit = qctl.qc_dqblk.dqb_isoftlimit ? + qctl.qc_dqblk.dqb_isoftlimit : + qctl.qc_dqblk.dqb_ihardlimit; + if (limit && sfs->f_files > limit) { + sfs->f_files = limit; + sfs->f_ffree = (sfs->f_files > + qctl.qc_dqblk.dqb_curinodes) ? + (sfs->f_files - qctl.qc_dqblk.dqb_curinodes) : 0; + } + + return 0; +} + int ll_statfs(struct dentry *de, struct kstatfs *sfs) { struct super_block *sb = de->d_sb; @@ -2241,6 +2287,8 @@ int ll_statfs(struct dentry *de, struct kstatfs *sfs) sfs->f_bavail = osfs.os_bavail; sfs->f_fsid.val[0] = (__u32)fsid; sfs->f_fsid.val[1] = (__u32)(fsid >> 32); + if (ll_i2info(de->d_inode)->lli_projid) + return ll_statfs_project(de->d_inode, sfs); ll_stats_ops_tally(ll_s2sbi(sb), LPROC_LL_STATFS, ktime_us_delta(ktime_get(), kstart)); diff --git a/lustre/tests/sanity-quota.sh b/lustre/tests/sanity-quota.sh index f999962..210a579 100755 --- a/lustre/tests/sanity-quota.sh +++ b/lustre/tests/sanity-quota.sh @@ -3561,6 +3561,50 @@ test_40d() { } run_test 40d "Stripe Directory inherit project quota properly" +test_41() { + is_project_quota_supported || + skip "Project quota is not supported" + setup_quota_test || error "setup quota failed with $?" + trap cleanup_quota_test EXIT + local dir="$DIR/$tdir/dir" + local blimit=102400 + local ilimit=4096 + local projid=$((testnum * 1000)) + + quota_init + + # enable mdt/ost quota + set_mdt_qtype ugp || error "enable mdt quota failed" + set_ost_qtype ugp || error "enable ost quota failed" + + test_mkdir -p $dir && change_project -sp $projid $dir + $LFS setquota -p $projid -b 0 -B ${blimit}K -i 0 -I $ilimit $dir || + error "set project quota failed" + + sync; sync_all_data + sleep_maxage + + # check if df output works as expected + echo "== global statfs: $MOUNT ==" + df -kP $MOUNT; df -iP $MOUNT; $LFS quota -p $projid $dir + echo + echo "== project statfs (prjid=$projid): $dir ==" + df -kP $dir; df -iP $dir + local bused=$(getquota -p $projid global curspace) + local iused=$(getquota -p $projid global curinodes) + # note trailing space to match double printf from awk + local expected="$blimit $bused $ilimit $iused " + + wait_update $HOSTNAME \ + "{ df -kP $dir; df -iP $dir; } | + awk '/$FSNAME/ { printf \\\"%d %d \\\", \\\$2,\\\$3 }'" \ + "$expected" || + error "failed to get correct statfs for project quota" + + cleanup_quota_test +} +run_test 41 "df should return projid-specific values" + test_50() { ! is_project_quota_supported && skip "Project quota is not supported"