From: Hongchao Zhang Date: Fri, 21 Jan 2022 00:43:56 +0000 (+0800) Subject: LU-15218 quota: delete unused quota ID X-Git-Tag: 2.15.0-RC1~19 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=78be823f33396819724330d7154f054c52e11944;p=fs%2Flustre-release.git LU-15218 quota: delete unused quota ID Add lfs option '--delete' to delete unused quota ID. Signed-off-by: Hongchao Zhang Change-Id: I0d8e6b61dc23c7b22b6054bcced087b8dc94a277 Reviewed-on: https://review.whamcloud.com/45548 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Li Dongyang Reviewed-by: Oleg Drokin --- diff --git a/lustre/doc/lfs-setquota.1 b/lustre/doc/lfs-setquota.1 index fb9eeb6..5b07f63 100644 --- a/lustre/doc/lfs-setquota.1 +++ b/lustre/doc/lfs-setquota.1 @@ -15,7 +15,7 @@ lfs setquota \- set quota limits or grace time for users, groups or projects. [\fB--inode-grace\fR|\fB-i\fR \fIINODE_GRACE_TIME\fR] <\fIfilesystem\fR> .TP .BR "lfs setquota " { -u | --user | -g | --group | -p | --projid "} " \fIUID\fR|\fIGID\fR|\fIPROJID\fR - [\fB--default|-d\fR] <\fIfilesystem\fR> + [\fB--default|-D\fR] <\fIfilesystem\fR> .TP .BR "lfs setquota " { -U | --default-usr | -G | --default-grp | -P | --default-prj } [\fB--block-softlimit\fR|\fB-b\fR \fIBLOCK_SOFTLIMIT\fR[\fBkMGTPE\fR]] @@ -23,6 +23,9 @@ lfs setquota \- set quota limits or grace time for users, groups or projects. [\fB--inode-softlimit\fR|\fB-i\fR \fIINODE_SOFTLIMIT\fR[\fBkMGTPE\fR]] [\fB--inode-hardlimit\fR|\fB-I\fR \fIINODE_HARDLIMIT\fR[\fBkMGTPE\fR]] <\fIfilesystem\fR> .TP +.BR "lfs setquota " { -u | --user | -g | --group | -p | --projid "} " \fIUID\fR|\fIGID\fR|\fIPROJID\fR + [\fB--delete\fR] <\fIfilesystem\fR> +.TP .SH DESCRIPTION .TP .BR "lfs setquota " {\fB-u|-g|-p\fR} @@ -41,7 +44,10 @@ Specify block softlimit, zero means unlimited. Specify block hardlimit, zero means unlimited. The block hardlimit should be greater than block softlimit when it's being specified. .TP -.BR -d|--default +.BR --delete +Delete the unused UID|GID|PROJID. +.TP +.BR -D|--default Set user/group/project to use the default quota limits. .TP .BR -g | --group \fIGROUPNAME\fR|\fIGID @@ -127,7 +133,7 @@ Set default project quota limit. .B $ lfs setquota -u bob --block-softlimit 2G --block-hardlimit 1G /mnt/lustre Set quotas of user `bob': 1GB block hardlimit and 2 GB block softlimit .TP -.B $ lfs setquota -u bob -d /mnt/lustre +.B $ lfs setquota -u bob -D /mnt/lustre Set quotas of user `bob' to use default quota setting .TP .B $ lfs setquota -U --block-softlimit 1G --block-hardlimit 2G /mnt/lustre @@ -142,6 +148,15 @@ Set hard block limit 1G for user 'ivan' per pool 'flash_pool' .TP .B $ lfs setquota -t -u --block-grace 1000 --pool flash_pool /mnt/lustre Set grace time 1000 seconds for block quotas per pool 'flash_pool' +.TP +.BR "lfs setquota " { -u | -g | -p "} " \fIUID\fR|\fIGID\fR|\fIPROJID\fR " " [\fB--delete\fR] " " <\fIfilesystem\fR> +Command deletes the unused UID|GID|PROJID from Quota settings. +.TP +.PP +.SH EXAMPLES +.TP +.B $ lfs setquota -u bob --delete /mnt/lustre +Delete unused user 'bob'. .SH SEE ALSO .BR lfs (1), .BR lfs-quota(1) diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index 4e3b720..b7cccc8 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -1291,6 +1291,7 @@ static inline __u64 toqb(__kernel_size_t space) #define LUSTRE_Q_SETINFOPOOL 0x800012 /* set pool quota info */ #define LUSTRE_Q_GETDEFAULT_POOL 0x800013 /* get default pool quota*/ #define LUSTRE_Q_SETDEFAULT_POOL 0x800014 /* set default pool quota */ +#define LUSTRE_Q_DELETEQID 0x800015 /* delete quota ID */ /* In the current Lustre implementation, the grace time is either the time * or the timestamp to be used after some quota ID exceeds the soft limt, * 48 bits should be enough, its high 16 bits can be used as quota flags. @@ -1315,6 +1316,7 @@ static inline __u64 toqb(__kernel_size_t space) * and high 16 bits will contain this flag (see above comment). * */ #define LQUOTA_FLAG_DEFAULT 0x0001 +#define LQUOTA_FLAG_DELETED 0x0002 #define LUSTRE_Q_CMD_IS_POOL(cmd) \ (cmd == LUSTRE_Q_GETQUOTAPOOL || \ diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index 7aaa059..0a3b4b6 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -1188,6 +1188,7 @@ int quotactl_ioctl(struct super_block *sb, struct if_quotactl *qctl) case LUSTRE_Q_SETQUOTAPOOL: case LUSTRE_Q_SETINFOPOOL: case LUSTRE_Q_SETDEFAULT_POOL: + case LUSTRE_Q_DELETEQID: if (!capable(CAP_SYS_ADMIN)) RETURN(-EPERM); diff --git a/lustre/mdt/mdt_handler.c b/lustre/mdt/mdt_handler.c index b6de915..b2db7c2 100644 --- a/lustre/mdt/mdt_handler.c +++ b/lustre/mdt/mdt_handler.c @@ -3219,6 +3219,7 @@ static int mdt_quotactl(struct tgt_session_info *tsi) case LUSTRE_Q_SETQUOTAPOOL: case LUSTRE_Q_SETINFOPOOL: case LUSTRE_Q_SETDEFAULT_POOL: + case LUSTRE_Q_DELETEQID: if (!nodemap_can_setquota(nodemap, oqctl->qc_type, oqctl->qc_id)) GOTO(out_nodemap, rc = -EPERM); @@ -3289,6 +3290,7 @@ static int mdt_quotactl(struct tgt_session_info *tsi) case LUSTRE_Q_GETINFOPOOL: case LUSTRE_Q_SETDEFAULT_POOL: case LUSTRE_Q_GETDEFAULT_POOL: + case LUSTRE_Q_DELETEQID: /* forward quotactl request to QMT */ rc = qmt_hdls.qmth_quotactl(tsi->tsi_env, qmt, oqctl); break; diff --git a/lustre/quota/lquota_disk.c b/lustre/quota/lquota_disk.c index 4353f1a..bc71833 100644 --- a/lustre/quota/lquota_disk.c +++ b/lustre/quota/lquota_disk.c @@ -701,6 +701,36 @@ out: return rc; } +int lquota_disk_delete(const struct lu_env *env, struct thandle *th, + struct dt_object *obj, __u64 qid, __u64 *ver) +{ + struct lquota_thread_info *qti = lquota_info(env); + struct dt_key *key = (struct dt_key *)&qid; + int rc; + + ENTRY; + + LASSERT(dt_object_exists(obj)); + LASSERT(obj->do_index_ops != NULL); + + /* lock index */ + dt_write_lock(env, obj, 0); + + /* check whether there is already an existing record for this ID */ + rc = dt_lookup(env, obj, (struct dt_rec *)&qti->qti_rec, key); + if (rc == 0) { + rc = dt_delete(env, obj, key, th); + if (rc == 0 && ver != NULL) { + *ver = dt_version_get(env, obj); + (*ver)++; + dt_version_set(env, obj, *ver, th); + } + } + + dt_write_unlock(env, obj); + RETURN(rc); +} + /* * Update version of an index file * diff --git a/lustre/quota/lquota_internal.h b/lustre/quota/lquota_internal.h index c8d69d8..f10ed72 100644 --- a/lustre/quota/lquota_internal.h +++ b/lustre/quota/lquota_internal.h @@ -186,7 +186,8 @@ struct lquota_entry { lqe_gl:1, /* glimpse is in progress */ lqe_nopreacq:1, /* pre-acquire disabled */ lqe_is_default:1, /* the default quota is used */ - lqe_is_global:1; /* lqe belongs to global pool "0x0"*/ + lqe_is_global:1, /* lqe belongs to global pool "0x0"*/ + lqe_is_deleted:1; /* lqe will be deleted soon */ struct lqe_glbl_data *lqe_glbl_data; }; @@ -456,6 +457,18 @@ struct lquota_entry *lqe_locate_find(const struct lu_env *, struct lquota_site *, union lquota_id *, bool); +static inline void lqe_set_deleted(struct lquota_entry *lqe) +{ + lqe->lqe_enforced = 0; + lqe->lqe_edquot = 0; + lqe->lqe_is_default = 0; + lqe->lqe_hardlimit = 0; + lqe->lqe_softlimit = 0; + lqe->lqe_gracetime = 0; + + lqe->lqe_is_deleted = 1; +} + /* lquota_disk.c */ struct dt_object *lquota_disk_dir_find_create(const struct lu_env *, struct dt_device *, @@ -484,6 +497,8 @@ int lquota_disk_declare_write(const struct lu_env *, struct thandle *, int lquota_disk_write(const struct lu_env *, struct thandle *, struct dt_object *, union lquota_id *, struct dt_rec *, __u32, __u64 *); +int lquota_disk_delete(const struct lu_env *env, struct thandle *th, + struct dt_object *obj, __u64 qid, __u64 *ver); int lquota_disk_update_ver(const struct lu_env *, struct dt_device *, struct dt_object *, __u64); int lquota_disk_write_glb(const struct lu_env *, struct dt_object *, __u64, diff --git a/lustre/quota/qmt_handler.c b/lustre/quota/qmt_handler.c index c355da2..40679e0 100644 --- a/lustre/quota/qmt_handler.c +++ b/lustre/quota/qmt_handler.c @@ -335,9 +335,74 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt, if (IS_ERR(lqe)) RETURN(PTR_ERR(lqe)); + lqe->lqe_is_deleted = 0; rc = qmt_set_with_lqe(env, qmt, lqe, hard, soft, time, valid, is_default, is_updated); + if (rc == 0) + lqe->lqe_is_deleted = 0; + + lqe_putref(lqe); + RETURN(rc); +} + +/* + * Delete the quota setting of the specified quota ID + * + * \param env - is the environment passed by the caller + * \param qmt - is the quota master target + * \param restype - is the pool type, either block (i.e. LQUOTA_RES_DT) or + * inode (i.e. LQUOTA_RES_MD) + * \param qtype - is the quota type + * \param qid - is the quota indentifier for which we want to delete its + * quota settings. + */ +static int qmt_delete_qid(const struct lu_env *env, struct qmt_device *qmt, + __u8 restype, __u8 qtype, __u64 qid) +{ + struct qmt_thread_info *qti = qmt_info(env); + union lquota_id *quota_id = &qti->qti_id; + struct thandle *th = NULL; + struct qmt_pool_info *qpi = NULL; + struct lquota_entry *lqe = NULL; + __u64 ver = 0; + int rc; + + ENTRY; + + quota_id->qid_uid = qid; + lqe = qmt_pool_lqe_lookup(env, qmt, restype, qtype, quota_id, NULL); + if (IS_ERR(lqe)) + RETURN(PTR_ERR(lqe)); + + lqe_write_lock(lqe); + + qpi = qmt_pool_lookup_glb(env, qmt, restype); + if (IS_ERR(qpi)) + GOTO(out, rc = -ENOMEM); + + th = qmt_trans_start(env, lqe); + if (IS_ERR(th)) + GOTO(out, rc = PTR_ERR(th)); + + rc = lquota_disk_delete(env, th, + qpi->qpi_glb_obj[qtype], qid, &ver); + + dt_trans_stop(env, qmt->qmt_child, th); + + if (rc == 0) { + lqe_set_deleted(lqe); + qmt_glb_lock_notify(env, lqe, ver); + } else if (rc == -ENOENT) { + rc = 0; + } + +out: + if (!IS_ERR_OR_NULL(qpi)) + qpi_putref(env, qpi); + + lqe_write_unlock(lqe); lqe_putref(lqe); + RETURN(rc); } @@ -482,6 +547,16 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld, false, poolname); break; + case LUSTRE_Q_DELETEQID: + rc = qmt_delete_qid(env, qmt, LQUOTA_RES_MD, oqctl->qc_type, + oqctl->qc_id); + if (rc) + break; + + rc = qmt_delete_qid(env, qmt, LQUOTA_RES_DT, oqctl->qc_type, + oqctl->qc_id); + break; + default: CERROR("%s: unsupported quotactl command: %d\n", qmt->qmt_svname, oqctl->qc_cmd); diff --git a/lustre/quota/qmt_lock.c b/lustre/quota/qmt_lock.c index 171d42d..38bde58 100644 --- a/lustre/quota/qmt_lock.c +++ b/lustre/quota/qmt_lock.c @@ -793,6 +793,11 @@ void qmt_glb_lock_notify(const struct lu_env *env, struct lquota_entry *lqe, qti->qti_gl_desc.lquota_desc.gl_time = LQUOTA_GRACE_FLAG(0, LQUOTA_FLAG_DEFAULT); + } else if (lqe->lqe_is_deleted) { + qti->qti_gl_desc.lquota_desc.gl_hardlimit = 0; + qti->qti_gl_desc.lquota_desc.gl_softlimit = 0; + qti->qti_gl_desc.lquota_desc.gl_time = LQUOTA_GRACE_FLAG(0, + LQUOTA_FLAG_DELETED); } else { qti->qti_gl_desc.lquota_desc.gl_hardlimit = lqe->lqe_hardlimit; qti->qti_gl_desc.lquota_desc.gl_softlimit = lqe->lqe_softlimit; diff --git a/lustre/quota/qsd_writeback.c b/lustre/quota/qsd_writeback.c index e32045e..58f0629 100644 --- a/lustre/quota/qsd_writeback.c +++ b/lustre/quota/qsd_writeback.c @@ -292,12 +292,48 @@ static int qsd_process_upd(const struct lu_env *env, struct qsd_upd_rec *upd) return 1; } + if (upd->qur_global && + (LQUOTA_FLAG(upd->qur_rec.lqr_glb_rec.qbr_time) & + LQUOTA_FLAG_DELETED)) { + struct thandle *th = NULL; + struct dt_object *obj; + + obj = qqi->qqi_glb_obj; + + th = dt_trans_create(env, qqi->qqi_qsd->qsd_dev); + if (IS_ERR(th)) + RETURN(PTR_ERR(th)); + + rc = lquota_disk_declare_write(env, th, obj, &upd->qur_qid); + if (rc) + GOTO(out_del, rc); + + rc = dt_trans_start_local(env, qqi->qqi_qsd->qsd_dev, th); + if (rc) + GOTO(out_del, rc); + + rc = lquota_disk_delete(env, th, obj, upd->qur_qid.qid_uid, + NULL); + if (rc == -ENOENT) + rc = 0; + +out_del: + dt_trans_stop(env, qqi->qqi_qsd->qsd_dev, th); + if (lqe != NULL) + lqe_set_deleted(lqe); + + qsd_bump_version(qqi, upd->qur_ver, true); + RETURN(rc); + } + if (lqe == NULL) { lqe = lqe_locate(env, qqi->qqi_site, &upd->qur_qid); if (IS_ERR(lqe)) GOTO(out, rc = PTR_ERR(lqe)); } + lqe->lqe_is_deleted = 0; + /* The in-memory lqe update for slave index copy isn't deferred, * we shouldn't touch it here. */ if (upd->qur_global) { diff --git a/lustre/tests/sanity-quota.sh b/lustre/tests/sanity-quota.sh index 89ba55e..80be73d 100755 --- a/lustre/tests/sanity-quota.sh +++ b/lustre/tests/sanity-quota.sh @@ -3590,6 +3590,68 @@ test_41() { } run_test 41 "df should return projid-specific values" +test_delete_qid() +{ + local qslv_file=$1 + local qtype_file=$2 + local qtype=$3 + local qid=$4 + local osd="osd-ldiskfs" + + [ "$ost1_FSTYPE" = zfs ] && osd="osd-zfs" + + rm -f $DIR/$tdir/$tfile + $LFS setstripe -i 0 -c 1 $DIR/$tdir/$tfile + chmod a+rw $DIR/$tdir/$tfile + + $LFS setquota $qtype $qid -B 300M $MOUNT + $RUNAS dd if=/dev/zero of=$DIR/$tdir/$tfile bs=1M count=1 || + error "failed to dd" + + do_facet $SINGLEMDS \ + "cat /proc/fs/lustre/qmt/$FSNAME-QMT0000/dt-0x0/$qtype_file | + grep -E 'id: *$qid'" || error "QMT: no qid $qid is found" + echo $osd + do_facet ost1 \ + "cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file | + grep -E 'id: *$qid'" || error "QSD: no qid $qid is found" + + $LFS setquota $qtype $qid --delete $MOUNT + do_facet $SINGLEMDS \ + "cat /proc/fs/lustre/qmt/$FSNAME-QMT0000/dt-0x0/$qtype_file | + grep -E 'id: *$qid'" && error "QMT: qid $qid is not deleted" + sleep 5 + do_facet ost1 \ + "cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file | + grep -E 'id: *$qid'" && error "QSD: qid $qid is not deleted" + + $LFS setquota $qtype $qid -B 500M $MOUNT + $RUNAS dd if=/dev/zero of=$DIR/$tdir/$tfile bs=1M count=1 || + error "failed to dd" + do_facet $SINGLEMDS \ + "cat /proc/fs/lustre/qmt/$FSNAME-QMT0000/dt-0x0/$qtype_file | + grep -E 'id: *$qid'" || error "QMT: qid $pid is not recreated" + cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file + do_facet ost1 \ + "cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file | + grep -E 'id: *$qid'" || error "QSD: qid $qid is not recreated" +} + +test_48() +{ + setup_quota_test || error "setup quota failed with $?" + set_ost_qtype $QTYPE || error "enable ost quota failed" + quota_init + + test_delete_qid "quota_slave/limit_user" "glb-usr" "-u" $TSTID + test_delete_qid "quota_slave/limit_group" "glb-grp" "-g" $TSTID + is_project_quota_supported && + test_delete_qid "quota_slave/limit_project" "glb-prj" "-p" "10000" + + cleanup_quota_test +} +run_test 48 "lfs quota --delete should delete quota project ID" + test_50() { ! is_project_quota_supported && skip "Project quota is not supported" diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index b6261f6..3a1511f 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -444,7 +444,8 @@ command_t cmdlist[] = { "Usage: getname [--help|-h] [--instance|-i] [--fsname|-n] [path ...]"}, #ifdef HAVE_SYS_QUOTA_H {"setquota", lfs_setquota, 0, "Set filesystem quotas.\n" - "usage: setquota [-t][-d] {-u|-U|-g|-G|-p|-P} {-b|-B|-i|-I LIMIT} [--pool POOL] FILESYSTEM"}, + "usage: setquota [-t][-D] {-u|-U|-g|-G|-p|-P} {-b|-B|-i|-I LIMIT} [--pool POOL] FILESYSTEM\n" + " setquota {-u|-g|-p} --delete FILESYSTEM\n"}, {"quota", lfs_quota, 0, "Display disk usage and limits.\n" "usage: quota [-q] [-v] [-h] [-o OBD_UUID|-i MDT_IDX|-I OST_IDX]\n" " [{-u|-g|-p} UNAME|UID|GNAME|GID|PROJID]\n" @@ -3330,6 +3331,7 @@ static void lfs_mirror_list_free(struct mirror_args *mirror_list) } enum { + LFS_SETQUOTA_DELETE = 1, LFS_POOL_OPT = 3, LFS_COMP_COUNT_OPT, LFS_COMP_START_OPT, @@ -7678,6 +7680,8 @@ int lfs_setquota(int argc, char **argv) { .val = 'B', .name = "block-hardlimit", .has_arg = required_argument }, { .val = 'd', .name = "default", .has_arg = no_argument }, + { .val = LFS_SETQUOTA_DELETE, + .name = "delete", .has_arg = no_argument }, { .val = 'g', .name = "group", .has_arg = required_argument }, { .val = 'G', .name = "default-grp", .has_arg = no_argument }, { .val = 'h', .name = "help", .has_arg = no_argument }, @@ -7715,8 +7719,7 @@ int lfs_setquota(int argc, char **argv) * so it can be used as a marker that qc_type * isn't reinitialized from command line */ - - while ((c = getopt_long(argc, argv, "b:B:dg:Ghi:I:p:Pu:U", + while ((c = getopt_long(argc, argv, "b:B:dDg:Ghi:I:p:Pu:U", long_opts, NULL)) != -1) { switch (c) { case 'U': @@ -7774,9 +7777,19 @@ quota_type_def: } qctl->qc_type = qtype; break; +#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 0, 53, 0) case 'd': - qctl->qc_cmd = LUSTRE_Q_SETDEFAULT; +#if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 15, 53, 0) + fprintf(stderr, "'-d' deprecatd, use '-D' or '--default'\n"); +#endif + /* falltrrough */ +#endif + case 'D': use_default = true; + qctl->qc_cmd = LUSTRE_Q_SETDEFAULT; + break; + case LFS_SETQUOTA_DELETE: + qctl->qc_cmd = LUSTRE_Q_DELETEQID; break; case 'b': ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024); @@ -7854,7 +7867,8 @@ quota_type_def: goto out; } - if (!use_default && limit_mask == 0) { + if (!use_default && qctl->qc_cmd != LUSTRE_Q_DELETEQID && + limit_mask == 0) { fprintf(stderr, "%s setquota: at least one limit must be specified\n", progname); @@ -7862,9 +7876,10 @@ quota_type_def: goto out; } - if (use_default && limit_mask != 0) { + if ((use_default || qctl->qc_cmd == LUSTRE_Q_DELETEQID) && + limit_mask != 0) { fprintf(stderr, - "%s setquota: limits should not be specified when using default quota\n", + "%s setquota: limits should not be specified when using default quota or deleting quota ID\n", progname); rc = CMD_HELP; goto out; @@ -7878,6 +7893,14 @@ quota_type_def: goto out; } + if (qctl->qc_cmd == LUSTRE_Q_DELETEQID && qctl->qc_id == 0) { + fprintf(stderr, + "%s setquota: can not delete root user/group/project\n", + progname); + rc = CMD_HELP; + goto out; + } + if (optind != argc - 1) { fprintf(stderr, "%s setquota: filesystem not specified or unexpected argument '%s'\n",