From: Hongchao Zhang Date: Fri, 2 Feb 2024 05:58:59 +0000 (+0800) Subject: LU-14535 quota: get all quota info in LFS X-Git-Url: https://git.whamcloud.com/gitweb?a=commitdiff_plain;h=76b0edaac6b07e566925ae7de6ae7fb7741f0f95;p=fs%2Flustre-release.git LU-14535 quota: get all quota info in LFS This patch adds option "-a" for LFS to get the quota info of all quota IDs. it iterates quota setting saved in global quota setting files "quota_master/md-0x0" and "quota_master/dt-0x0" from QMT and iterates the quota usage info saved in acct quota files in the backend FS (LDiskFS or ZFS) from QSDs, then merge the two kinds of quota info at client and print it in the similar way as "lfs quota -u|-g|-p". $lfs quota -a -u /mnt/lustre Filesystem /mnt/lustre, Disk usr quotas quota_id kbytes quota limit grace files quota limit grace root 9684 0 0 - 1019 0 0 - bin 4 0 102400 - 1 0 10240 - daemon 4 0 102400 - 1 0 10240 - adm 4 0 102400 - 1 0 10240 - lp 4 0 102400 - 1 0 10240 - sync 4 0 102400 - 1 0 10240 - shutdown 4 0 102400 - 1 0 10240 - halt 4 0 102400 - 1 0 10240 - mail 4 0 102400 - 1 0 10240 - $lfs quota -a -g /mnt/lustre Filesystem /mnt/lustre, Disk grp quotas quota_id kbytes quota limit grace files quota limit grace root 9684 0 0 - 1019 0 0 - bin 4 0 204800 - 1 0 20480 - daemon 4 0 204800 - 1 0 20480 - adm 4 0 204800 - 1 0 20480 - lp 4 0 204800 - 1 0 20480 - sync 4 0 204800 - 1 0 20480 - shutdown 4 0 204800 - 1 0 20480 - halt 4 0 204800 - 1 0 20480 - mail 4 0 204800 - 1 0 20480 - Lustre-change: https://review.whamcloud.com/42098 Lustre-commit: 3edc71803af3b4dc672313cd1ba395de724fbc59 Test-Parameters: testlist=sanity-quota env=SLOW=yes,ONLY=49,NUM_QIDS=20000 Signed-off-by: Hongchao Zhang Change-Id: I08feb928fbf34635ec9c5c341de993c718798dc9 Reviewed-on: https://review.whamcloud.com/c/ex/lustre-release/+/46328 Tested-by: jenkins Tested-by: Andreas Dilger Reviewed-by: Andreas Dilger --- diff --git a/contrib/scripts/checkpatch.pl b/contrib/scripts/checkpatch.pl index dfc9f9e..3fd83ab 100755 --- a/contrib/scripts/checkpatch.pl +++ b/contrib/scripts/checkpatch.pl @@ -460,7 +460,8 @@ our $logFunctions = qr{(?x: CDEBUG|CERROR|CL_LOCK_DEBUG|CWARN|DEBUG_REQ|LCONSOLE_[A-Z]*| panic| MODULE_[A-Z_]+| - seq_vprintf|seq_printf|seq_puts + seq_vprintf|seq_printf|seq_puts| + printf|fprintf )}; our $signature_tags = qr{(?xi: diff --git a/lustre/doc/lfs-quota.1 b/lustre/doc/lfs-quota.1 index 3b89c41..bd02da5 100644 --- a/lustre/doc/lfs-quota.1 +++ b/lustre/doc/lfs-quota.1 @@ -17,6 +17,10 @@ lfs-quota \- display quota limits and status for users, groups, or projects. .B lfs quota -t \fR<\fB-u\fR|\fB-g\fR|\fB-p\fR> <\fIfilesystem\fR> .br +.br +.B lfs quota -a \fR<\fB-u\fR|\fB-g\fR|\fB-p\fR> <\fIfilesystem\fR> +.br + .TP .SH DESCRIPTION .PP @@ -103,6 +107,10 @@ Display default project grace times for <\fIfilesystem\fR>. .B --pool <\fIpname\fR> Display user, group or project grace times per OST pool \fIpname\fR. .TP +.B lfs quota -a \fR<\fB-u\fR|\fB-g\fR|\fB-p\fR> <\fIfilesystem\fR> +.TP +Display all quota setting for all users, groups, or projects. +.TP .SH EXAMPLES .TP .B $ lfs quota /mnt/lustre diff --git a/lustre/include/lustre_quota.h b/lustre/include/lustre_quota.h index 3fdc688..1f70424 100644 --- a/lustre/include/lustre_quota.h +++ b/lustre/include/lustre_quota.h @@ -66,6 +66,25 @@ enum osd_qid_declare_flags { OSD_QID_FORCE = BIT(2), }; +/* the length of the buffer to contain the quotas gotten from QMT/QSD, + * the maximum is 128 quota IDs (each struct lquota_glb_rec for MD or DT), + * it can contain about 420 quota IDs for theirs quota usage. + */ +#define LQUOTA_ITER_BUFLEN \ + (128 * 2 * (sizeof(__u64) + sizeof(struct lquota_glb_rec))) + +struct lquota_iter { + struct list_head li_link; + __u32 li_md_size; + __u32 li_dt_size; + char li_buffer[]; +}; + +struct if_quotactl_iter { + struct list_head qci_link; + struct if_quotactl qci_qc; +}; + /* Index features supported by the global index objects * Only used for migration purpose and should be removed once on-disk migration * is no longer needed */ @@ -91,7 +110,7 @@ extern struct dt_index_features dt_quota_bgrp_features; struct qmt_handlers { /* Handle quotactl request from client. */ int (*qmth_quotactl)(const struct lu_env *, struct lu_device *, - struct obd_quotactl *); + struct obd_quotactl *, char *buf, int len); /* Handle dqacq/dqrel request from slave. */ int (*qmth_dqacq)(const struct lu_env *, struct lu_device *, @@ -246,7 +265,7 @@ struct lquota_trans { /* helper function used by MDT & OFD to retrieve quota accounting information * on slave */ -int lquotactl_slv(const struct lu_env *, struct dt_device *, - struct obd_quotactl *); +int lquotactl_slv(const struct lu_env *env, struct dt_device *dt, + struct obd_quotactl *obdq, char *buf, int len); /** @} quota */ #endif /* _LUSTRE_QUOTA_H */ diff --git a/lustre/include/lustre_req_layout.h b/lustre/include/lustre_req_layout.h index 1a26dcb..5897792 100644 --- a/lustre/include/lustre_req_layout.h +++ b/lustre/include/lustre_req_layout.h @@ -356,6 +356,7 @@ extern struct req_msg_field RMF_CAPA1; extern struct req_msg_field RMF_CAPA2; extern struct req_msg_field RMF_OBD_QUOTACHECK; extern struct req_msg_field RMF_OBD_QUOTACTL; +extern struct req_msg_field RMF_OBD_QUOTA_ITER; extern struct req_msg_field RMF_OBD_QUOTACTL_POOL; extern struct req_msg_field RMF_QUOTA_BODY; extern struct req_msg_field RMF_STRING; diff --git a/lustre/include/obd_class.h b/lustre/include/obd_class.h index e328b36..aad47c6 100644 --- a/lustre/include/obd_class.h +++ b/lustre/include/obd_class.h @@ -1281,6 +1281,23 @@ static inline int obd_quotactl(struct obd_export *exp, RETURN(rc); } +static inline int obd_quota_iter(struct obd_export *exp, + struct obd_quotactl *oqctl, + struct list_head *list) +{ + int rc = 0; + + do { + oqctl->qc_iter_list = (__u64)list; + rc = obd_quotactl(exp, oqctl); + if (rc) + break; + + } while (oqctl->qc_iter_md_offset || oqctl->qc_iter_dt_offset); + + return rc; +} + static inline int obd_health_check(const struct lu_env *env, struct obd_device *obd) { diff --git a/lustre/include/obd_support.h b/lustre/include/obd_support.h index 12227bb..eed619e 100644 --- a/lustre/include/obd_support.h +++ b/lustre/include/obd_support.h @@ -555,6 +555,7 @@ extern char obd_jobid_var[]; #define OBD_FAIL_QUOTA_PREACQ 0xA06 #define OBD_FAIL_QUOTA_RECALC 0xA07 #define OBD_FAIL_QUOTA_GRANT 0xA08 +#define OBD_FAIL_QUOTA_NOSYNC 0xA09 #define OBD_FAIL_LPROC_REMOVE 0xB00 diff --git a/lustre/include/uapi/linux/lustre/lustre_idl.h b/lustre/include/uapi/linux/lustre/lustre_idl.h index 2c742d7..dceff6a 100644 --- a/lustre/include/uapi/linux/lustre/lustre_idl.h +++ b/lustre/include/uapi/linux/lustre/lustre_idl.h @@ -1590,6 +1590,14 @@ struct obd_quotactl { char qc_poolname[]; }; +#define qc_iter_md_offset qc_dqblk.dqb_bhardlimit +#define qc_iter_dt_offset qc_dqblk.dqb_ihardlimit +#define qc_iter_md_buflen qc_dqblk.dqb_bsoftlimit +#define qc_iter_dt_buflen qc_dqblk.dqb_isoftlimit +#define qc_iter_list qc_dqblk.dqb_curspace +#define qc_iter_qid_start qc_dqblk.dqb_curinodes +#define qc_iter_qid_end qc_dqblk.dqb_btime + #define Q_COPY(out, in, member) (out)->member = (in)->member #define QCTL_COPY_NO_PNAME(out, in) \ diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index c033fe7..0ff9729 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -1398,6 +1398,9 @@ static inline __u64 lustre_stoqb(__kernel_size_t space) #define LUSTRE_Q_SETDEFAULT_POOL 0x800014 /* set default pool quota */ #define LUSTRE_Q_DELETEQID 0x800015 /* delete quota ID */ #define LUSTRE_Q_RESETQID 0x800016 /* reset quota ID */ +#define LUSTRE_Q_ITERQUOTA 0x800017 /* iterate quota information */ +#define LUSTRE_Q_ITEROQUOTA 0x800018 /* iterate obd quota information */ +#define LUSTRE_Q_GETALLQUOTA 0x800019 /* get all quota information */ /* 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. @@ -1559,6 +1562,13 @@ struct if_quotactl { char qc_poolname[]; }; +#define qc_allquota_count qc_dqblk.dqb_bhardlimit +#define qc_allquota_buffer qc_dqblk.dqb_bsoftlimit +#define qc_allquota_buflen qc_dqblk.dqb_curspace +#define qc_allquota_qid_start qc_dqblk.dqb_curinodes +#define qc_allquota_qid_end qc_dqblk.dqb_btime +#define qc_allquota_mark qc_dqblk.dqb_itime + /* swap layout flags */ #define SWAP_LAYOUTS_CHECK_DV1 (1 << 0) #define SWAP_LAYOUTS_CHECK_DV2 (1 << 1) diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index a347bef..b822e36 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include "llite_internal.h" @@ -1187,6 +1188,404 @@ static int check_owner(int type, int id) return 0; } +struct kmem_cache *quota_iter_slab; +static DEFINE_MUTEX(quotactl_iter_lock); + +struct ll_quotactl_iter_list { + __u64 lqil_mark; /* iter identifier */ + __u32 lqil_flags; /* what has been done */ + pid_t lqil_pid; /* debug calling task */ + time64_t lqil_iter_time; /* the time to iter */ + struct list_head lqil_sbi_list; /* list on ll_sb_info */ + struct list_head lqil_quotactl_iter_list; /* list of quota iters */ +}; + +void ll_quota_iter_check_and_cleanup(struct ll_sb_info *sbi, bool check) +{ + struct if_quotactl_iter *iter_rec = NULL; + struct ll_quotactl_iter_list *tmp, *ll_iter = NULL; + + if (!check) + mutex_lock("actl_iter_lock); + + list_for_each_entry_safe(ll_iter, tmp, &sbi->ll_all_quota_list, + lqil_sbi_list) { + if (check && + ll_iter->lqil_iter_time > (ktime_get_seconds() - 86400)) + continue; + + while ((iter_rec = list_first_entry_or_null( + &ll_iter->lqil_quotactl_iter_list, + struct if_quotactl_iter, + qci_link)) != NULL) { + list_del_init(&iter_rec->qci_link); + OBD_SLAB_FREE_PTR(iter_rec, quota_iter_slab); + } + + list_del_init(&ll_iter->lqil_sbi_list); + OBD_FREE_PTR(ll_iter); + } + + if (!check) + mutex_unlock("actl_iter_lock); +} + +/* iterate the quota usage from all QSDs */ +static int quotactl_iter_acct(struct list_head *quota_list, void *buffer, + __u64 size, __u64 *count, __u32 qtype, bool is_md) +{ + struct if_quotactl_iter *tmp, *iter = NULL; + struct lquota_acct_rec *acct; + __u64 qid, cur = 0; + int rc = 0; + + ENTRY; + + while (cur < size) { + if ((size - cur) < + (sizeof(qid) + sizeof(*acct))) { + rc = -EPROTO; + break; + } + + qid = *((__u64 *)(buffer + cur)); + cur += sizeof(qid); + acct = (struct lquota_acct_rec *)(buffer + cur); + cur += sizeof(*acct); + + iter = NULL; + list_for_each_entry(tmp, quota_list, qci_link) { + if (tmp->qci_qc.qc_id == (__u32)qid) { + iter = tmp; + break; + } + } + + if (iter == NULL) { + CDEBUG(D_QUOTA, "can't find the iter record for %llu\n", + qid); + + if (qid != 0) + continue; + + OBD_SLAB_ALLOC_PTR(iter, quota_iter_slab); + if (iter == NULL) { + rc = -ENOMEM; + break; + } + + INIT_LIST_HEAD(&iter->qci_link); + iter->qci_qc.qc_id = 0; + iter->qci_qc.qc_type = qtype; + (*count)++; + + list_add(&iter->qci_link, quota_list); + } + + if (is_md) { + iter->qci_qc.qc_dqblk.dqb_valid |= QIF_INODES; + iter->qci_qc.qc_dqblk.dqb_curinodes += acct->ispace; + iter->qci_qc.qc_dqblk.dqb_curspace += acct->bspace; + } else { + iter->qci_qc.qc_dqblk.dqb_valid |= QIF_SPACE; + iter->qci_qc.qc_dqblk.dqb_curspace += acct->bspace; + } + } + + RETURN(rc); +} + +/* iterate all quota settings from QMT */ +static int quotactl_iter_glb(struct list_head *quota_list, void *buffer, + __u64 size, __u64 *count, __u32 qtype, bool is_md) +{ + struct if_quotactl_iter *tmp, *iter = NULL; + struct lquota_glb_rec *glb; + __u64 qid, cur = 0; + bool inserted = false; + int rc = 0; + + ENTRY; + + while (cur < size) { + if ((size - cur) < + (sizeof(qid) + sizeof(*glb))) { + rc = -EPROTO; + break; + } + + qid = *((__u64 *)(buffer + cur)); + cur += sizeof(qid); + glb = (struct lquota_glb_rec *)(buffer + cur); + cur += sizeof(*glb); + + iter = NULL; + list_for_each_entry(tmp, quota_list, qci_link) { + if (tmp->qci_qc.qc_id == (__u32)qid) { + iter = tmp; + break; + } + } + + if (iter == NULL) { + OBD_SLAB_ALLOC_PTR(iter, quota_iter_slab); + if (iter == NULL) { + rc = -ENOMEM; + break; + } + + INIT_LIST_HEAD(&iter->qci_link); + + inserted = false; + list_for_each_entry(tmp, quota_list, qci_link) { + if (tmp->qci_qc.qc_id < qid) + continue; + + inserted = true; + list_add_tail(&iter->qci_link, + &tmp->qci_link); + break; + } + + if (!inserted) + list_add_tail(&iter->qci_link, quota_list); + + iter->qci_qc.qc_type = qtype; + iter->qci_qc.qc_id = (__u32)qid; + (*count)++; + } + + if (is_md) { + iter->qci_qc.qc_dqblk.dqb_valid |= QIF_ILIMITS; + iter->qci_qc.qc_dqblk.dqb_ihardlimit = + glb->qbr_hardlimit; + iter->qci_qc.qc_dqblk.dqb_isoftlimit = + glb->qbr_softlimit; + iter->qci_qc.qc_dqblk.dqb_itime = glb->qbr_time; + } else { + iter->qci_qc.qc_dqblk.dqb_valid |= QIF_BLIMITS; + iter->qci_qc.qc_dqblk.dqb_bhardlimit = + glb->qbr_hardlimit; + iter->qci_qc.qc_dqblk.dqb_bsoftlimit = + glb->qbr_softlimit; + iter->qci_qc.qc_dqblk.dqb_btime = glb->qbr_time; + } + } + + RETURN(rc); +} + +/* iterate the quota setting from QMT and all QSDs to get the quota information + * for all users or groups + **/ +static int quotactl_iter(struct ll_sb_info *sbi, struct if_quotactl *qctl) +{ + struct list_head iter_quota_glb_list; + struct list_head iter_obd_quota_md_list; + struct list_head iter_obd_quota_dt_list; + struct ll_quotactl_iter_list *ll_iter; + struct lquota_iter *iter; + struct obd_quotactl *oqctl; + __u64 count; + int rc = 0; + + ENTRY; + + OBD_ALLOC_PTR(ll_iter); + if (ll_iter == NULL) + RETURN(-ENOMEM); + + INIT_LIST_HEAD(&ll_iter->lqil_sbi_list); + INIT_LIST_HEAD(&ll_iter->lqil_quotactl_iter_list); + + mutex_lock("actl_iter_lock); + + if (!list_empty(&sbi->ll_all_quota_list)) + ll_quota_iter_check_and_cleanup(sbi, true); + + INIT_LIST_HEAD(&iter_quota_glb_list); + INIT_LIST_HEAD(&iter_obd_quota_md_list); + INIT_LIST_HEAD(&iter_obd_quota_dt_list); + + OBD_ALLOC_PTR(oqctl); + if (oqctl == NULL) + GOTO(out, rc = -ENOMEM); + + QCTL_COPY(oqctl, qctl); + oqctl->qc_iter_list = (__u64)&iter_quota_glb_list; + rc = obd_quotactl(sbi->ll_md_exp, oqctl); + if (rc) + GOTO(cleanup, rc); + + QCTL_COPY(oqctl, qctl); + oqctl->qc_cmd = LUSTRE_Q_ITEROQUOTA; + oqctl->qc_iter_list = (__u64)&iter_obd_quota_md_list; + rc = obd_quotactl(sbi->ll_md_exp, oqctl); + if (rc) + GOTO(cleanup, rc); + + QCTL_COPY(oqctl, qctl); + oqctl->qc_cmd = LUSTRE_Q_ITEROQUOTA; + oqctl->qc_iter_list = (__u64)&iter_obd_quota_dt_list; + rc = obd_quotactl(sbi->ll_dt_exp, oqctl); + if (rc) + GOTO(cleanup, rc); + + count = 0; + while ((iter = list_first_entry_or_null(&iter_quota_glb_list, + struct lquota_iter, li_link))) { + void *buffer; + + buffer = iter->li_buffer; + rc = quotactl_iter_glb(&ll_iter->lqil_quotactl_iter_list, + buffer, iter->li_md_size, &count, + oqctl->qc_type, true); + if (rc) + GOTO(cleanup, rc); + + buffer = iter->li_buffer + LQUOTA_ITER_BUFLEN / 2; + rc = quotactl_iter_glb(&ll_iter->lqil_quotactl_iter_list, + buffer, iter->li_dt_size, &count, + oqctl->qc_type, false); + + if (rc) + GOTO(cleanup, rc); + + list_del_init(&iter->li_link); + OBD_FREE_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + } + + while ((iter = list_first_entry_or_null(&iter_obd_quota_md_list, + struct lquota_iter, li_link))) { + rc = quotactl_iter_acct(&ll_iter->lqil_quotactl_iter_list, + iter->li_buffer, iter->li_md_size, + &count, oqctl->qc_type, true); + if (rc) + GOTO(cleanup, rc); + + list_del_init(&iter->li_link); + OBD_FREE_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + } + + while ((iter = list_first_entry_or_null(&iter_obd_quota_dt_list, + struct lquota_iter, li_link))) { + rc = quotactl_iter_acct(&ll_iter->lqil_quotactl_iter_list, + iter->li_buffer, iter->li_dt_size, + &count, oqctl->qc_type, false); + if (rc) + GOTO(cleanup, rc); + + list_del_init(&iter->li_link); + OBD_FREE_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + } + + ll_iter->lqil_mark = ((__u64)current->pid << 32) | + ((__u32)qctl->qc_type << 8) | + (ktime_get_seconds() & 0xFFFFFF); + ll_iter->lqil_flags = qctl->qc_type; + ll_iter->lqil_pid = current->pid; + ll_iter->lqil_iter_time = ktime_get_seconds(); + + list_add(&ll_iter->lqil_sbi_list, &sbi->ll_all_quota_list); + + qctl->qc_allquota_count = count; + qctl->qc_allquota_mark = ll_iter->lqil_mark; + GOTO(out, rc); + +cleanup: + ll_quota_iter_check_and_cleanup(sbi, true); + + while ((iter = list_first_entry_or_null(&iter_quota_glb_list, + struct lquota_iter, li_link))) { + list_del_init(&iter->li_link); + OBD_FREE_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + } + + while ((iter = list_first_entry_or_null(&iter_obd_quota_md_list, + struct lquota_iter, li_link))) { + list_del_init(&iter->li_link); + OBD_FREE_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + } + + while ((iter = list_first_entry_or_null(&iter_obd_quota_dt_list, + struct lquota_iter, li_link))) { + list_del_init(&iter->li_link); + OBD_FREE_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + } + + OBD_FREE_PTR(ll_iter); + +out: + OBD_FREE_PTR(oqctl); + + mutex_unlock("actl_iter_lock); + RETURN(rc); +} + +static int quotactl_getallquota(struct ll_sb_info *sbi, + struct if_quotactl *qctl) +{ + struct ll_quotactl_iter_list *ll_iter = NULL; + struct if_quotactl_iter *iter = NULL; + void __user *buffer = (void __user *)qctl->qc_allquota_buffer; + __u64 cur = 0, count = qctl->qc_allquota_buflen; + int rc = 0; + + ENTRY; + + mutex_lock("actl_iter_lock); + + while ((ll_iter = list_first_entry_or_null(&sbi->ll_all_quota_list, + struct ll_quotactl_iter_list, + lqil_sbi_list)) != NULL) { + if (qctl->qc_allquota_mark == ll_iter->lqil_mark) + break; + } + + if (!ll_iter) { + mutex_unlock("actl_iter_lock); + RETURN(-EBUSY); + } + + while ((iter = list_first_entry_or_null( + &ll_iter->lqil_quotactl_iter_list, + struct if_quotactl_iter, qci_link))) { + if (count - cur < sizeof(struct if_quotactl)) { + rc = -ERANGE; + break; + } + + if (copy_to_user(buffer + cur, &iter->qci_qc, + sizeof(struct if_quotactl))) { + rc = -EFAULT; + break; + } + + cur += sizeof(struct if_quotactl); + + list_del_init(&iter->qci_link); + OBD_SLAB_FREE_PTR(iter, quota_iter_slab); + } + + /* cleanup in case of error */ + while ((iter = list_first_entry_or_null( + &ll_iter->lqil_quotactl_iter_list, + struct if_quotactl_iter, qci_link))) { + list_del_init(&iter->qci_link); + OBD_SLAB_FREE_PTR(iter, quota_iter_slab); + } + + mutex_unlock("actl_iter_lock); + + RETURN(rc); +} + int quotactl_ioctl(struct super_block *sb, struct if_quotactl *qctl) { struct ll_sb_info *sbi = ll_s2sbi(sb); @@ -1217,6 +1616,8 @@ int quotactl_ioctl(struct super_block *sb, struct if_quotactl *qctl) case LUSTRE_Q_GETDEFAULT: case LUSTRE_Q_GETQUOTAPOOL: case LUSTRE_Q_GETDEFAULT_POOL: + case LUSTRE_Q_ITERQUOTA: + case LUSTRE_Q_GETALLQUOTA: if (check_owner(type, id) && (!capable(CAP_SYS_ADMIN))) RETURN(-EPERM); @@ -1229,7 +1630,11 @@ int quotactl_ioctl(struct super_block *sb, struct if_quotactl *qctl) RETURN(-ENOTSUPP); } - if (valid != QC_GENERAL) { + if (cmd == LUSTRE_Q_ITERQUOTA) { + rc = quotactl_iter(sbi, qctl); + } else if (cmd == LUSTRE_Q_GETALLQUOTA) { + rc = quotactl_getallquota(sbi, qctl); + } else if (valid != QC_GENERAL) { if (cmd == Q_GETINFO) qctl->qc_cmd = Q_GETOINFO; else if (cmd == Q_GETQUOTA || diff --git a/lustre/llite/llite_internal.h b/lustre/llite/llite_internal.h index 133c64bf..ee90a94 100644 --- a/lustre/llite/llite_internal.h +++ b/lustre/llite/llite_internal.h @@ -936,6 +936,9 @@ struct ll_sb_info { /* cached file security context xattr name. e.g: security.selinux */ char *ll_secctx_name; __u32 ll_secctx_name_size; + + /* LU-14535: the list of "lfs quota -a" */ + struct list_head ll_all_quota_list; }; #define SBI_DEFAULT_HEAT_DECAY_WEIGHT ((80 * 256 + 50) / 100) @@ -1210,6 +1213,7 @@ static inline void ll_dir_chain_fini(struct ll_dir_chain *chain) extern const struct file_operations ll_dir_operations; extern const struct inode_operations ll_dir_inode_operations; +extern struct kmem_cache *quota_iter_slab; #ifdef HAVE_DIR_CONTEXT int ll_dir_read(struct inode *inode, __u64 *pos, struct md_op_data *op_data, struct dir_context *ctx); @@ -1223,6 +1227,7 @@ 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 super_block *sb, struct if_quotactl *qctl); +void ll_quota_iter_check_and_cleanup(struct ll_sb_info *sbi, bool check); /* 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 3780072..b63d93f 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -194,6 +194,8 @@ static struct ll_sb_info *ll_init_sbi(struct lustre_sb_info *lsi) sbi->ll_oc_thrsh_count = SBI_DEFAULT_OPENCACHE_THRESHOLD_COUNT; sbi->ll_oc_max_ms = SBI_DEFAULT_OPENCACHE_THRESHOLD_MAX_MS; sbi->ll_oc_thrsh_ms = SBI_DEFAULT_OPENCACHE_THRESHOLD_MS; + + INIT_LIST_HEAD(&sbi->ll_all_quota_list); RETURN(sbi); out_destroy_ra: destroy_workqueue(sbi->ll_ra_info.ll_readahead_wq); @@ -1384,6 +1386,8 @@ void ll_put_super(struct super_block *sb) CDEBUG(D_VFSTRACE, "VFS Op: cfg_instance %s-%016lx (sb %p)\n", profilenm, cfg_instance, sb); + ll_quota_iter_check_and_cleanup(sbi, false); + cfg.cfg_instance = cfg_instance; lustre_end_log(sb, profilenm, &cfg); diff --git a/lustre/llite/super25.c b/lustre/llite/super25.c index 2557b20..28a65c0 100644 --- a/lustre/llite/super25.c +++ b/lustre/llite/super25.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -262,6 +263,12 @@ static int __init lustre_init(void) if (pcc_inode_slab == NULL) GOTO(out_cache, rc = -ENOMEM); + quota_iter_slab = kmem_cache_create("ll_quota_iter", + sizeof(struct if_quotactl_iter), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (quota_iter_slab == NULL) + GOTO(out_cache, rc = -ENOMEM); + rc = llite_tunables_register(); if (rc) GOTO(out_cache, rc); @@ -308,6 +315,7 @@ out_cache: kmem_cache_destroy(ll_inode_cachep); kmem_cache_destroy(ll_file_data_slab); kmem_cache_destroy(pcc_inode_slab); + kmem_cache_destroy(quota_iter_slab); return rc; } @@ -330,6 +338,7 @@ static void __exit lustre_exit(void) kmem_cache_destroy(ll_inode_cachep); kmem_cache_destroy(ll_file_data_slab); kmem_cache_destroy(pcc_inode_slab); + kmem_cache_destroy(quota_iter_slab); } MODULE_AUTHOR("OpenSFS, Inc. "); diff --git a/lustre/lmv/lmv_obd.c b/lustre/lmv/lmv_obd.c index fe07a6a..d77c5f0 100644 --- a/lustre/lmv/lmv_obd.c +++ b/lustre/lmv/lmv_obd.c @@ -4072,6 +4072,30 @@ static int lmv_quotactl(struct obd_device *unused, struct obd_export *exp, RETURN(-EIO); } + if (oqctl->qc_cmd == LUSTRE_Q_ITERQUOTA || + oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) { + struct list_head *lst = (struct list_head *)oqctl->qc_iter_list; + int err; + + if (oqctl->qc_cmd == LUSTRE_Q_ITERQUOTA) + RETURN(obd_quota_iter(tgt->ltd_exp, oqctl, lst)); + + lmv_foreach_connected_tgt(lmv, tgt) { + if (!tgt->ltd_active) + continue; + + err = obd_quota_iter(tgt->ltd_exp, oqctl, lst); + if (err) { + CERROR("%s: getquota failed mdt %d: rc = %d\n", + obd->obd_name, tgt->ltd_index, err); + if (!rc) + rc = err; + } + } + + RETURN(rc); + } + if (oqctl->qc_cmd != Q_GETOQUOTA) { rc = obd_quotactl(tgt->ltd_exp, oqctl); RETURN(rc); diff --git a/lustre/lov/lov_obd.c b/lustre/lov/lov_obd.c index 4cc892e..e7a75ae 100644 --- a/lustre/lov/lov_obd.c +++ b/lustre/lov/lov_obd.c @@ -1286,6 +1286,7 @@ static int lov_quotactl(struct obd_device *obd, struct obd_export *exp, struct lov_obd *lov = &obd->u.lov; struct lov_tgt_desc *tgt; struct pool_desc *pool = NULL; + struct list_head *lst = NULL; __u64 curspace = 0; __u64 bhardlimit = 0; int i, rc = 0; @@ -1293,7 +1294,8 @@ static int lov_quotactl(struct obd_device *obd, struct obd_export *exp, ENTRY; if (oqctl->qc_cmd != Q_GETOQUOTA && oqctl->qc_cmd != LUSTRE_Q_SETQUOTA && - oqctl->qc_cmd != LUSTRE_Q_GETQUOTAPOOL) { + oqctl->qc_cmd != LUSTRE_Q_GETQUOTAPOOL && + oqctl->qc_cmd != LUSTRE_Q_ITEROQUOTA) { rc = -EFAULT; CERROR("%s: bad quota opc %x for lov obd: rc = %d\n", obd->obd_name, oqctl->qc_cmd, rc); @@ -1309,6 +1311,9 @@ static int lov_quotactl(struct obd_device *obd, struct obd_export *exp, oqctl->qc_cmd = Q_GETOQUOTA; } + if (oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) + lst = (struct list_head *)oqctl->qc_iter_list; + /* for lov tgt */ lov_tgts_getref(obd); for (i = 0; i < lov->desc.ld_tgt_count; i++) { @@ -1335,7 +1340,11 @@ static int lov_quotactl(struct obd_device *obd, struct obd_export *exp, continue; } - err = obd_quotactl(tgt->ltd_exp, oqctl); + if (oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) + err = obd_quota_iter(tgt->ltd_exp, oqctl, lst); + else + err = obd_quotactl(tgt->ltd_exp, oqctl); + if (err) { if (tgt->ltd_active && !rc) rc = err; @@ -1355,6 +1364,7 @@ static int lov_quotactl(struct obd_device *obd, struct obd_export *exp, oqctl->qc_dqblk.dqb_curspace = curspace; oqctl->qc_dqblk.dqb_bhardlimit = bhardlimit; } + RETURN(rc); } diff --git a/lustre/mdc/mdc_request.c b/lustre/mdc/mdc_request.c index 22ce0db..8c1b541 100644 --- a/lustre/mdc/mdc_request.c +++ b/lustre/mdc/mdc_request.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -2069,7 +2070,6 @@ out: static int mdc_ioc_hsm_ct_start(struct obd_export *exp, struct lustre_kernelcomm *lk); - static int mdc_quotactl(struct obd_device *unused, struct obd_export *exp, struct obd_quotactl *oqctl) { @@ -2089,6 +2089,14 @@ static int mdc_quotactl(struct obd_device *unused, struct obd_export *exp, RCL_CLIENT, sizeof(*oqc) + LOV_MAXPOOLNAME + 1); + if (oqctl->qc_cmd == LUSTRE_Q_ITERQUOTA || + oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) + req_capsule_set_size(&req->rq_pill, &RMF_OBD_QUOTA_ITER, + RCL_SERVER, LQUOTA_ITER_BUFLEN); + else + req_capsule_set_size(&req->rq_pill, &RMF_OBD_QUOTA_ITER, + RCL_SERVER, 0); + rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_QUOTACTL); if (rc) { @@ -2111,7 +2119,42 @@ static int mdc_quotactl(struct obd_device *unused, struct obd_export *exp, if (req->rq_repmsg && (oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL))) { + struct list_head *lst = (struct list_head *)oqctl->qc_iter_list; + QCTL_COPY(oqctl, oqc); + + if (oqctl->qc_cmd == LUSTRE_Q_ITERQUOTA || + oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) { + void *buffer; + struct lquota_iter *iter; + + buffer = req_capsule_server_get(&req->rq_pill, + &RMF_OBD_QUOTA_ITER); + + if (buffer == NULL) { + CDEBUG(D_QUOTA, "%s: no buffer in iter req\n", + exp->exp_obd->obd_name); + + rc = -EPROTO; + GOTO(out, rc); + } + + OBD_ALLOC_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + if (iter == NULL) + GOTO(out, rc = -ENOMEM); + + INIT_LIST_HEAD(&iter->li_link); + list_add(&iter->li_link, lst); + + memcpy(iter->li_buffer, buffer, LQUOTA_ITER_BUFLEN); + iter->li_md_size = oqctl->qc_iter_md_buflen; + if (oqctl->qc_cmd == LUSTRE_Q_ITERQUOTA) + iter->li_dt_size = oqctl->qc_iter_dt_buflen; + + oqctl->qc_iter_md_buflen = 0; + oqctl->qc_iter_dt_buflen = 0; + } } else if (!rc) { rc = -EPROTO; CERROR("%s: cannot unpack obd_quotactl: rc = %d\n", diff --git a/lustre/mdt/mdt_handler.c b/lustre/mdt/mdt_handler.c index 729e740..1e7dfae 100644 --- a/lustre/mdt/mdt_handler.c +++ b/lustre/mdt/mdt_handler.c @@ -3230,16 +3230,24 @@ static int mdt_quotactl(struct tgt_session_info *tsi) struct obd_export *exp = tsi->tsi_exp; struct req_capsule *pill = tsi->tsi_pill; struct obd_quotactl *oqctl, *repoqc; - int id, rc; struct mdt_device *mdt = mdt_exp2dev(exp); struct lu_device *qmt = mdt->mdt_qmt_dev; struct lu_nodemap *nodemap; + char *buffer = NULL; + int id, rc; ENTRY; oqctl = req_capsule_client_get(pill, &RMF_OBD_QUOTACTL); if (!oqctl) RETURN(err_serious(-EPROTO)); + if (oqctl->qc_cmd == LUSTRE_Q_ITERQUOTA || + oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) + req_capsule_set_size(pill, &RMF_OBD_QUOTA_ITER, RCL_SERVER, + LQUOTA_ITER_BUFLEN); + else + req_capsule_set_size(pill, &RMF_OBD_QUOTA_ITER, RCL_SERVER, 0); + rc = req_capsule_server_pack(pill); if (rc) RETURN(err_serious(rc)); @@ -3268,12 +3276,14 @@ static int mdt_quotactl(struct tgt_session_info *tsi) case LUSTRE_Q_GETQUOTAPOOL: case LUSTRE_Q_GETINFOPOOL: case LUSTRE_Q_GETDEFAULT_POOL: + case LUSTRE_Q_ITERQUOTA: if (qmt == NULL) GOTO(out_nodemap, rc = -EOPNOTSUPP); /* slave quotactl */ fallthrough; case Q_GETOINFO: case Q_GETOQUOTA: + case LUSTRE_Q_ITEROQUOTA: break; default: rc = -EFAULT; @@ -3303,6 +3313,13 @@ static int mdt_quotactl(struct tgt_session_info *tsi) if (repoqc == NULL) GOTO(out_nodemap, rc = err_serious(-EFAULT)); + if (oqctl->qc_cmd == LUSTRE_Q_ITERQUOTA || + oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) { + buffer = req_capsule_server_get(pill, &RMF_OBD_QUOTA_ITER); + if (buffer == NULL) + GOTO(out_nodemap, rc = err_serious(-EFAULT)); + } + if (oqctl->qc_cmd == Q_SETINFO || oqctl->qc_cmd == Q_SETQUOTA) barrier_exit(tsi->tsi_tgt->lut_bottom); @@ -3330,15 +3347,20 @@ static int mdt_quotactl(struct tgt_session_info *tsi) case LUSTRE_Q_GETDEFAULT_POOL: case LUSTRE_Q_DELETEQID: case LUSTRE_Q_RESETQID: + case LUSTRE_Q_ITERQUOTA: /* forward quotactl request to QMT */ - rc = qmt_hdls.qmth_quotactl(tsi->tsi_env, qmt, oqctl); + rc = qmt_hdls.qmth_quotactl(tsi->tsi_env, qmt, oqctl, buffer, + buffer == NULL ? 0 : + LQUOTA_ITER_BUFLEN); break; case Q_GETOINFO: case Q_GETOQUOTA: + case LUSTRE_Q_ITEROQUOTA: /* slave quotactl */ rc = lquotactl_slv(tsi->tsi_env, tsi->tsi_tgt->lut_bottom, - oqctl); + oqctl, buffer, + buffer == NULL ? 0 : LQUOTA_ITER_BUFLEN); break; default: diff --git a/lustre/ofd/ofd_dev.c b/lustre/ofd/ofd_dev.c index 9d5acd7..2fd0f0b 100644 --- a/lustre/ofd/ofd_dev.c +++ b/lustre/ofd/ofd_dev.c @@ -2389,21 +2389,38 @@ static int ofd_quotactl(struct tgt_session_info *tsi) struct obd_quotactl *oqctl, *repoqc; struct lu_nodemap *nodemap; ktime_t kstart = ktime_get(); + char *buffer = NULL; int id; int rc; - ENTRY; oqctl = req_capsule_client_get(tsi->tsi_pill, &RMF_OBD_QUOTACTL); if (oqctl == NULL) RETURN(err_serious(-EPROTO)); + if (oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) + req_capsule_set_size(tsi->tsi_pill, &RMF_OBD_QUOTA_ITER, + RCL_SERVER, LQUOTA_ITER_BUFLEN); + else + req_capsule_set_size(tsi->tsi_pill, &RMF_OBD_QUOTA_ITER, + RCL_SERVER, 0); + + rc = req_capsule_server_pack(tsi->tsi_pill); + if (rc) + RETURN(err_serious(rc)); + repoqc = req_capsule_server_get(tsi->tsi_pill, &RMF_OBD_QUOTACTL); if (repoqc == NULL) RETURN(err_serious(-ENOMEM)); - *repoqc = *oqctl; + if (oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) { + buffer = req_capsule_server_get(tsi->tsi_pill, + &RMF_OBD_QUOTA_ITER); + if (buffer == NULL) + RETURN(err_serious(-ENOMEM)); + } + nodemap = nodemap_get_from_exp(tsi->tsi_exp); if (IS_ERR(nodemap)) RETURN(PTR_ERR(nodemap)); @@ -2427,7 +2444,8 @@ static int ofd_quotactl(struct tgt_session_info *tsi) if (repoqc->qc_id != id) swap(repoqc->qc_id, id); - rc = lquotactl_slv(tsi->tsi_env, tsi->tsi_tgt->lut_bottom, repoqc); + rc = lquotactl_slv(tsi->tsi_env, tsi->tsi_tgt->lut_bottom, repoqc, + buffer, buffer == NULL ? 0 : LQUOTA_ITER_BUFLEN); ofd_counter_incr(tsi->tsi_exp, LPROC_OFD_STATS_QUOTACTL, tsi->tsi_jobid, ktime_us_delta(ktime_get(), kstart)); @@ -2863,7 +2881,7 @@ TGT_OST_HDL_HP(HAS_BODY | HAS_REPLY | IS_MUTABLE, OST_PUNCH, ofd_punch_hdl, ofd_hp_punch), TGT_OST_HDL(HAS_BODY | HAS_REPLY, OST_SYNC, ofd_sync_hdl), -TGT_OST_HDL(HAS_REPLY, OST_QUOTACTL, ofd_quotactl), +TGT_OST_HDL(0, OST_QUOTACTL, ofd_quotactl), TGT_OST_HDL(HAS_BODY | HAS_REPLY, OST_LADVISE, ofd_ladvise_hdl), TGT_OST_HDL(HAS_BODY | HAS_REPLY | IS_MUTABLE, OST_FALLOCATE, ofd_fallocate_hdl), TGT_OST_HDL(HAS_BODY | HAS_REPLY, OST_SEEK, tgt_lseek), diff --git a/lustre/osc/osc_quota.c b/lustre/osc/osc_quota.c index e1606c5..068f0e5 100644 --- a/lustre/osc/osc_quota.c +++ b/lustre/osc/osc_quota.c @@ -30,6 +30,7 @@ #include #include +#include #include "osc_internal.h" @@ -287,16 +288,24 @@ int osc_quota_cleanup(struct obd_device *obd) int osc_quotactl(struct obd_device *unused, struct obd_export *exp, struct obd_quotactl *oqctl) { - struct ptlrpc_request *req; - struct obd_quotactl *oqc; - int rc; - ENTRY; + struct ptlrpc_request *req; + struct obd_quotactl *oqc; + int rc; + + ENTRY; - req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp), - &RQF_OST_QUOTACTL, LUSTRE_OST_VERSION, - OST_QUOTACTL); - if (req == NULL) - RETURN(-ENOMEM); + req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp), + &RQF_OST_QUOTACTL, LUSTRE_OST_VERSION, + OST_QUOTACTL); + if (req == NULL) + RETURN(-ENOMEM); + + if (oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) + req_capsule_set_size(&req->rq_pill, &RMF_OBD_QUOTA_ITER, + RCL_SERVER, LQUOTA_ITER_BUFLEN); + else + req_capsule_set_size(&req->rq_pill, &RMF_OBD_QUOTA_ITER, + RCL_SERVER, 0); oqc = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL); *oqc = *oqctl; @@ -309,14 +318,52 @@ int osc_quotactl(struct obd_device *unused, struct obd_export *exp, if (rc) CERROR("ptlrpc_queue_wait failed, rc: %d\n", rc); - if (req->rq_repmsg && - (oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL))) { - *oqctl = *oqc; - } else if (!rc) { - CERROR ("Can't unpack obd_quotactl\n"); - rc = -EPROTO; - } - ptlrpc_req_finished(req); + if (req->rq_repmsg) { + struct list_head *lst = (struct list_head *)oqctl->qc_iter_list; + + oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL); + if (!oqc) + GOTO(out, rc = -EPROTO); - RETURN(rc); + *oqctl = *oqc; + + if (oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) { + void *buffer; + struct lquota_iter *iter; + + buffer = req_capsule_server_get(&req->rq_pill, + &RMF_OBD_QUOTA_ITER); + + if (buffer == NULL) { + CDEBUG(D_QUOTA, "%s: no buffer in iter req\n", + exp->exp_obd->obd_name); + + rc = -EPROTO; + GOTO(out, rc); + } + + OBD_ALLOC_LARGE(iter, + sizeof(struct lquota_iter) + LQUOTA_ITER_BUFLEN); + if (iter == NULL) + GOTO(out, rc = -ENOMEM); + + INIT_LIST_HEAD(&iter->li_link); + list_add(&iter->li_link, lst); + + memcpy(iter->li_buffer, buffer, LQUOTA_ITER_BUFLEN); + iter->li_dt_size = oqctl->qc_iter_dt_buflen; + oqctl->qc_iter_md_buflen = 0; + oqctl->qc_iter_dt_buflen = 0; + } + } else if (!rc) { + CERROR("%s: cannot unpack obd_quotactl: rc = %d\n", + exp->exp_obd->obd_name, rc); + + rc = -EPROTO; + } + +out: + ptlrpc_req_finished(req); + + RETURN(rc); } diff --git a/lustre/ptlrpc/layout.c b/lustre/ptlrpc/layout.c index 3a75d9d..2ed76e9 100644 --- a/lustre/ptlrpc/layout.c +++ b/lustre/ptlrpc/layout.c @@ -106,6 +106,12 @@ static const struct req_msg_field *quotactl_only[] = { &RMF_OBD_QUOTACTL }; +static const struct req_msg_field *quotactl_server_only[] = { + &RMF_PTLRPC_BODY, + &RMF_OBD_QUOTACTL, + &RMF_OBD_QUOTA_ITER +}; + static const struct req_msg_field *quota_body_only[] = { &RMF_PTLRPC_BODY, &RMF_QUOTA_BODY @@ -1014,6 +1020,10 @@ struct req_msg_field RMF_OBD_QUOTACTL = lustre_swab_obd_quotactl, NULL); EXPORT_SYMBOL(RMF_OBD_QUOTACTL); +struct req_msg_field RMF_OBD_QUOTA_ITER = + DEFINE_MSGFL("quota_iter_key", 0, -1, NULL, NULL); +EXPORT_SYMBOL(RMF_OBD_QUOTA_ITER); + struct req_msg_field RMF_QUOTA_BODY = DEFINE_MSGF("quota_body", 0, sizeof(struct quota_body), lustre_swab_quota_body, NULL); @@ -1433,11 +1443,11 @@ struct req_format RQF_FLD_READ = EXPORT_SYMBOL(RQF_FLD_READ); struct req_format RQF_MDS_QUOTACTL = - DEFINE_REQ_FMT0("MDS_QUOTACTL", quotactl_only, quotactl_only); + DEFINE_REQ_FMT0("MDS_QUOTACTL", quotactl_only, quotactl_server_only); EXPORT_SYMBOL(RQF_MDS_QUOTACTL); struct req_format RQF_OST_QUOTACTL = - DEFINE_REQ_FMT0("OST_QUOTACTL", quotactl_only, quotactl_only); + DEFINE_REQ_FMT0("OST_QUOTACTL", quotactl_only, quotactl_server_only); EXPORT_SYMBOL(RQF_OST_QUOTACTL); struct req_format RQF_QUOTA_DQACQ = diff --git a/lustre/quota/lquota_internal.h b/lustre/quota/lquota_internal.h index 6b7a4a6..16902cf 100644 --- a/lustre/quota/lquota_internal.h +++ b/lustre/quota/lquota_internal.h @@ -448,6 +448,9 @@ struct dt_object *acct_obj_lookup(const struct lu_env *, struct dt_device *, void lquota_generate_fid(struct lu_fid *, int, int); int lquota_extract_fid(const struct lu_fid *, int *, int *); const struct dt_index_features *glb_idx_feature(struct lu_fid *); +int lquota_obj_iter(const struct lu_env *env, struct dt_device *dev, + struct dt_object *obj, struct obd_quotactl *oqctl, + char *buffer, int size, bool is_glb, bool is_md); /* lquota_entry.c */ /* site create/destroy */ diff --git a/lustre/quota/lquota_lib.c b/lustre/quota/lquota_lib.c index 8bbc58b..dc55e506 100644 --- a/lustre/quota/lquota_lib.c +++ b/lustre/quota/lquota_lib.c @@ -168,6 +168,162 @@ static struct dt_object *quota_obj_lookup(const struct lu_env *env, } /* + * Iterate quota settings managed by \a obj. + * + * \param env - is the environment passed by the caller + * \param dev - is the backend device holding the quota object + * \param obj - is the quota object to be iterated + * \param oqctl - is the quota ioctl object passed in by caller + * \param buf - is the buffer to save the retrieved quota settings + * \param size - is the size of the buffer + * \param is_glb - true to iterate the global quota settings + * \param is_md - true to iterate LQUOTA_MD quota settings + */ +int lquota_obj_iter(const struct lu_env *env, struct dt_device *dev, + struct dt_object *obj, struct obd_quotactl *oqctl, + char *buf, int size, bool is_glb, bool is_md) +{ + struct lquota_thread_info *qti = lquota_info(env); + const struct dt_it_ops *iops; + struct dt_it *it; + struct dt_key *key; + struct dt_rec *rec = (struct dt_rec *)&qti->qti_rec; + __u64 offset; + bool skip = true; + int cur = 0, rc; + int rec_size; + + ENTRY; + + iops = &obj->do_index_ops->dio_it; + it = iops->init(env, obj, 0); + if (IS_ERR(it)) { + rc = PTR_ERR(it); + CERROR("%s: failed to initialize iterator: rc = %ld\n", + obj->do_lu.lo_dev->ld_obd->obd_name, PTR_ERR(it)); + RETURN(rc); + } + + rc = iops->load(env, it, 0); + if (rc <= 0) { + if (is_md) + oqctl->qc_iter_md_offset = 0; + else + oqctl->qc_iter_dt_offset = 0; + + GOTO(out_fini, rc); + } + + if ((is_md && oqctl->qc_iter_md_offset == 0) || + (!is_md && oqctl->qc_iter_dt_offset == 0)) + skip = false; + + if (is_glb) + rec_size = sizeof(struct lquota_glb_rec); + else + rec_size = sizeof(struct lquota_acct_rec); + + if (is_md) + offset = oqctl->qc_iter_md_offset; + else + offset = oqctl->qc_iter_dt_offset; + + while ((size - cur) > (sizeof(__u64) + rec_size)) { + if (!skip) + goto get_setting; + + if (offset == iops->store(env, it)) + skip = false; + else { + rc = iops->next(env, it); + if (rc < 0) { + CERROR("%s: next failed: rc = %d\n", + obj->do_lu.lo_dev->ld_obd->obd_name, rc); + break; + } + + /* reach the end */ + if (rc > 0) { + if (is_md) + oqctl->qc_iter_md_offset = 0; + else + oqctl->qc_iter_dt_offset = 0; + + break; + } + + continue; + } + +get_setting: + key = iops->key(env, it); + if (IS_ERR(key)) { + CERROR("%s: failed to get key: rc = %ld\n", + obj->do_lu.lo_dev->ld_obd->obd_name, + PTR_ERR(key)); + + GOTO(out_fini, rc = PTR_ERR(key)); + } + + rc = iops->rec(env, it, rec, 0); + if (rc) { + CERROR("%s: failed to get rec: rc = %d\n", + obj->do_lu.lo_dev->ld_obd->obd_name, rc); + GOTO(out_fini, rc); + } + + if (oqctl->qc_iter_qid_end != 0 && + (*((__u64 *)key) < oqctl->qc_iter_qid_start || + *((__u64 *)key) > oqctl->qc_iter_qid_end)) + goto next; + + memcpy(buf + cur, key, sizeof(__u64)); + cur += sizeof(__u64); + + memcpy(buf + cur, rec, rec_size); + cur += rec_size; + +next: + rc = iops->next(env, it); + if (rc < 0) { + CERROR("%s: next failed: rc = %d\n", + obj->do_lu.lo_dev->ld_obd->obd_name, rc); + + GOTO(out_fini, rc); + } + + /* reach the end */ + if (rc > 0) { + if (is_md) + oqctl->qc_iter_md_offset = 0; + else + oqctl->qc_iter_dt_offset = 0; + + break; + } + + if (is_md) + oqctl->qc_iter_md_offset = iops->store(env, it); + else + oqctl->qc_iter_dt_offset = iops->store(env, it); + } + +out_fini: + if (rc >= 0) { + if (is_md) + oqctl->qc_iter_md_buflen = cur; + else + oqctl->qc_iter_dt_buflen = cur; + + rc = 0; + } + + iops->put(env, it); + iops->fini(env, it); + return rc < 0 ? rc : 0; +} + +/* * Helper routine to retrieve slave information. * This function converts a quotactl request into quota/accounting object * operations. It is independant of the slave stack which is only accessible @@ -178,16 +334,17 @@ static struct dt_object *quota_obj_lookup(const struct lu_env *env, * \param oqctl - is the quotactl request */ int lquotactl_slv(const struct lu_env *env, struct dt_device *dev, - struct obd_quotactl *oqctl) + struct obd_quotactl *oqctl, char *buffer, int size) { - struct lquota_thread_info *qti = lquota_info(env); + struct lquota_thread_info *qti = lquota_info(env); __u64 key; struct dt_object *obj, *obj_aux = NULL; struct obd_dqblk *dqblk = &oqctl->qc_dqblk; int rc; ENTRY; - if (oqctl->qc_cmd != Q_GETOQUOTA) { + if (oqctl->qc_cmd != Q_GETOQUOTA && + oqctl->qc_cmd != LUSTRE_Q_ITEROQUOTA) { /* as in many other places, dev->dd_lu_dev.ld_obd->obd_name * point to an invalid obd_name, to be fixed in LU-1574 */ CERROR("%s: Unsupported quotactl command: %x\n", @@ -209,6 +366,17 @@ int lquotactl_slv(const struct lu_env *env, struct dt_device *dev, if (obj->do_index_ops == NULL) GOTO(out, rc = -EINVAL); + if (oqctl->qc_cmd == LUSTRE_Q_ITEROQUOTA) { + if (lu_device_is_md(dev->dd_lu_dev.ld_site->ls_top_dev)) + rc = lquota_obj_iter(env, dev, obj, oqctl, buffer, size, + false, true); + else + rc = lquota_obj_iter(env, dev, obj, oqctl, buffer, size, + false, false); + + GOTO(out, rc); + } + /* lookup record storing space accounting information for this ID */ rc = dt_lookup(env, obj, (struct dt_rec *)&qti->qti_acct_rec, (struct dt_key *)&key); diff --git a/lustre/quota/qmt_handler.c b/lustre/quota/qmt_handler.c index d382aef..c8fb426 100644 --- a/lustre/quota/qmt_handler.c +++ b/lustre/quota/qmt_handler.c @@ -174,6 +174,9 @@ int qmt_set_with_lqe(const struct lu_env *env, struct qmt_device *qmt, th = qmt_trans_start(env, lqe); if (IS_ERR(th)) GOTO(out_nolock, rc = PTR_ERR(th)); + + if (CFS_FAIL_CHECK(OBD_FAIL_QUOTA_NOSYNC)) + th->th_sync = 0; } now = ktime_get_real_seconds(); @@ -389,6 +392,9 @@ static int qmt_delete_qid(const struct lu_env *env, struct qmt_device *qmt, if (IS_ERR(th)) GOTO(out, rc = PTR_ERR(th)); + if (CFS_FAIL_CHECK(OBD_FAIL_QUOTA_NOSYNC)) + th->th_sync = 0; + lqe_write_lock(lqe); rc = lquota_disk_delete(env, th, qpi->qpi_glb_obj[qtype], qid, &ver); @@ -544,15 +550,18 @@ out: * \param oqctl - is the quotactl request */ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld, - struct obd_quotactl *oqctl) + struct obd_quotactl *oqctl, char *buffer, int size) { struct qmt_thread_info *qti = qmt_info(env); union lquota_id *id = &qti->qti_id; struct qmt_device *qmt = lu2qmt_dev(ld); + struct dt_object *glb_obj; struct obd_dqblk *dqb = &oqctl->qc_dqblk; + struct qmt_pool_info *pool; char *poolname; int rc = 0; bool is_default = false; + bool is_first_iter = false; ENTRY; LASSERT(qmt != NULL); @@ -612,6 +621,52 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld, poolname); break; + case LUSTRE_Q_ITERQUOTA: + if (oqctl->qc_iter_md_offset == 0 && + oqctl->qc_iter_dt_offset == 0) + is_first_iter = true; + + if (is_first_iter || oqctl->qc_iter_md_offset != 0) { + pool = qmt_pool_lookup_name(env, qmt, LQUOTA_RES_MD, + NULL); + if (IS_ERR(pool)) + RETURN(PTR_ERR(pool)); + + glb_obj = pool->qpi_glb_obj[oqctl->qc_type]; + rc = lquota_obj_iter(env, lu2dt_dev(ld), glb_obj, + oqctl, buffer, size / 2, true, true); + + qpi_putref(env, pool); + + if (rc < 0 && rc != -ENOENT) + break; + + rc = 0; + } else { + oqctl->qc_iter_md_buflen = 0; + } + + if (is_first_iter || oqctl->qc_iter_dt_offset != 0) { + pool = qmt_pool_lookup_name(env, qmt, LQUOTA_RES_DT, + NULL); + if (IS_ERR(pool)) + RETURN(PTR_ERR(pool)); + + glb_obj = pool->qpi_glb_obj[oqctl->qc_type]; + rc = lquota_obj_iter(env, lu2dt_dev(ld), glb_obj, + oqctl, buffer + size / 2, size / 2, + true, false); + qpi_putref(env, pool); + + if (rc < 0 && rc != -ENOENT) + break; + + rc = 0; + } else { + oqctl->qc_iter_dt_buflen = 0; + } + break; + case LUSTRE_Q_GETDEFAULT: case LUSTRE_Q_GETDEFAULT_POOL: is_default = true; diff --git a/lustre/target/tgt_handler.c b/lustre/target/tgt_handler.c index 58533db..cd7e063 100644 --- a/lustre/target/tgt_handler.c +++ b/lustre/target/tgt_handler.c @@ -460,6 +460,12 @@ static int tgt_handle_request0(struct tgt_session_info *tsi, req_capsule_set_size(tsi->tsi_pill, &RMF_FILE_ENCCTX, RCL_SERVER, 0); + if (req_capsule_has_field(tsi->tsi_pill, &RMF_OBD_QUOTA_ITER, + RCL_SERVER)) { + req_capsule_set_size(tsi->tsi_pill, + &RMF_OBD_QUOTA_ITER, RCL_SERVER, 0); + } + rc = req_capsule_server_pack(tsi->tsi_pill); } diff --git a/lustre/tests/createmany.c b/lustre/tests/createmany.c index 3e47201..5905370 100644 --- a/lustre/tests/createmany.c +++ b/lustre/tests/createmany.c @@ -39,8 +39,10 @@ #include #include #include +#include #include #include +#include static void usage(const char *prog) { @@ -53,6 +55,10 @@ static void usage(const char *prog) "\t-u\tunlink file/dir (with optional )\n"); printf("\t-d\tuse directories instead of regular files\n" "\t-t\tstop creating files after have elapsed\n"); + printf("\t-S\tthe file size\n" + "\t-U\tthe start User ID of the file\n" + "\t-G\tthe start Group ID of the file\n" + "\t-P\tthe start Project ID of the file\n"); exit(EXIT_FAILURE); } @@ -82,6 +88,8 @@ int main(int argc, char ** argv) { bool do_open = false, do_keep = false, do_link = false; bool do_unlink = false, do_mknod = false, do_mkdir = false; + bool do_setsize = false, do_chuid = false, do_chgid = false; + bool do_chprj = false; char *filename, *progname; char *fmt = NULL, *fmt_unlink = NULL, *tgt = NULL; char *endp = NULL; @@ -90,6 +98,8 @@ int main(int argc, char ** argv) int has_fmt_spec = 0, unlink_has_fmt_spec = 0; long i, total, last_i = 0; int c, last_fd = -1, stderr_fd; + unsigned int uid = 0, gid = 0, pid = 0; + int size = 0; int rc = 0; /* Handle the deprecated positional last argument "-seconds" */ @@ -108,11 +118,15 @@ int main(int argc, char ** argv) else progname = argv[0]; - while ((c = getopt(argc, argv, "dl:kmor::t:u::")) != -1) { + while ((c = getopt(argc, argv, "dG:l:kmor::S:t:u::U:")) != -1) { switch (c) { case 'd': do_mkdir = true; break; + case 'G': + do_chgid = true; + gid = strtoul(optarg, NULL, 0); + break; case 'k': do_keep = true; break; @@ -126,6 +140,14 @@ int main(int argc, char ** argv) case 'o': do_open = true; break; + case 'P': + do_chprj = true; + pid = strtoul(optarg, NULL, 0); + break; + case 'S': + do_setsize = true; + size = atoi(optarg); + break; case 't': end = strtol(optarg, &endp, 0); if (end <= 0.0 || *endp != '\0') @@ -136,12 +158,21 @@ int main(int argc, char ** argv) do_unlink = true; fmt_unlink = optarg; break; + case 'U': + do_chuid = true; + uid = strtoul(optarg, NULL, 0); + break; case '?': fprintf(stderr, "Unknown option '%c'\n", optopt); usage(progname); } } + if (!do_open && (do_setsize || do_chuid || do_chgid || do_chprj)) { + fprintf(stderr, "error: -S, -U, -G, -P works only with -o\n"); + usage(progname); + } + if (do_open + do_mkdir + do_link + do_mknod > 1 || do_open + do_mkdir + do_link + do_mknod + do_unlink == 0) { fprintf(stderr, "error: only one of -o, -m, -l, -d\n"); @@ -182,6 +213,55 @@ int main(int argc, char ** argv) rc = errno; break; } + if (do_setsize) { + rc = lseek(fd, (size - 6) < 0 ? 0 : size - 6, + SEEK_SET); + if (rc < 0) { + printf("lseek(%s, %d) error: %s\n", + filename, size, strerror(errno)); + break; + } + rc = write(fd, "Lustre", 6); + if (rc < 0) { + printf("write(%s, %d) error: %s\n", + filename, 6, strerror(errno)); + break; + } + } + + if (do_chuid || do_chgid) { + rc = fchown(fd, do_chuid ? uid + i : -1, + do_chgid ? gid + i : -1); + if (rc < 0) { + printf("fchown(%s, %u, %u) error: %s\n", + filename, do_chuid ? uid : -1, + do_chgid ? gid : -1, + strerror(errno)); + break; + } + } + + if (do_chprj) { + struct fsxattr fsx; + + rc = ioctl(fd, FS_IOC_FSGETXATTR, &fsx); + if (rc < 0) { + printf("ioctl(%s) error: %s\n", + "FS_IOC_GETXATTR", + strerror(errno)); + break; + } + + fsx.fsx_projid = pid + i; + rc = ioctl(fd, FS_IOC_FSSETXATTR, &fsx); + if (rc < 0) { + printf("ioctl(%s, %d) error: %s\n", + "FS_IOC_SETXATTR", pid, + strerror(errno)); + break; + } + } + if (!do_keep) close(fd); else if (fd > last_fd) diff --git a/lustre/tests/sanity-quota.sh b/lustre/tests/sanity-quota.sh index a87b1ec..2e6e54c 100755 --- a/lustre/tests/sanity-quota.sh +++ b/lustre/tests/sanity-quota.sh @@ -3970,6 +3970,192 @@ test_48() } run_test 48 "lfs quota --delete should delete quota project ID" +test_get_allquota() { + local file_cnt=$1 + local start_qid=$2 + local end_qid=$3 + local u_blimit=$4 + local u_ilimit=$5 + local g_blimit=$6 + local g_ilimit=$7 + local TFILE="$DIR/$tdir/$tfile-0" + + local u_blimits + local u_ilimits + local g_blimits + local g_ilimits + local u_busage + local u_busage2 + local g_busage + local g_busage2 + local u_iusage + local u_iusage2 + local g_iusage + local g_iusage2 + local start + local total + + local qid_cnt=$file_cnt + + [ $end_qid -ne 0 ] && qid_cnt=$((end_qid - start_qid + 1)) + [ $end_qid -ge $file_cnt ] && + qid_cnt=$((qid_cnt - end_qid + file_cnt)) + [ $qid_cnt -le 0 ] && error "quota ID count is wrong" + + cnt=$($LFS quota -a -s $start_qid -e $end_qid -u $MOUNT | wc -l) + [ $cnt -ge $((qid_cnt + 2)) ] || error "failed to get all usr quota" + cnt=$($LFS quota -a -s $start_qid -e $end_qid -g $MOUNT | wc -l) + [ $cnt -ge $((qid_cnt + 2)) ] || error "failed to get all grp quota" + + cancel_lru_locks osc + sync; sync_all_data || true + sleep 5 + + eval $($LFS quota -a -s $start_qid -e $end_qid -u $MOUNT | + awk 'NR > 2 {printf("u_blimits[%d]=%d;u_ilimits[%d]=%d; \ + u_busage[%d]=%d;u_iusage[%d]=%d;", \ + NR, $4, NR, $8, NR, $2, NR, $6)}') + eval $($LFS quota -a -s $start_qid -e $end_qid -g $MOUNT | + awk 'NR > 2 {printf("g_blimits[%d]=%d;g_ilimits[%d]=%d; \ + g_busage[%d]=%d;g_iusage[%d]=%d;", \ + NR, $4, NR, $8, NR, $2, NR, $6)}') + + for i in $(seq $qid_cnt); do + [ $i -le 2 ] && continue + + [ ${u_ilimits[$i]} -eq $u_ilimit ] || + error "file limit for user ID $((start_qid + i - 3)) is wrong" + [ ${u_blimits[$i]} -eq $u_blimit ] || + error "block limit for user ID $((start_qid + i - 3)) is wrong" + [ ${g_ilimits[$i]} -eq $g_ilimit ] || + error "file limit for group ID $((start_qid + i - 3)) is wrong" + [ ${g_blimits[$i]} -eq $g_blimit ] || + error "block limit for group ID $((start_qid + i - 3)) is wrong" + done + + echo "Create $qid_cnt files..." + createmany -S 4k -U $start_qid -G $start_qid -o ${TFILE} $qid_cnt || + error "failed to create many files" + + cancel_lru_locks osc + sync; sync_all_data || true + sleep 5 + + start=$SECONDS + $LFS quota -a -s $start_qid -e $end_qid -u $MOUNT | tail -n 50 + total=$((SECONDS - start)) + (( end - start > 0 )) && + echo "time=$total, rate=$((qid_cnt / total))/s" || + echo "time=0, rate=$qid_cnt/0" + + start=$SECONDS + $LFS quota -a -s $start_qid -e $end_qid -g $MOUNT | tail -n 50 + total=$((SECONDS - start)) + (( end - start > 0 )) && + echo "time=$total, rate=$((qid_cnt / total))/s" || + echo "time=0, rate=$qid_cnt/0" + + cnt=$($LFS quota -a -s $start_qid -e $end_qid -u $MOUNT | wc -l) + [ $cnt -ge $((qid_cnt + 2)) ] || error "failed to get all usr quota" + cnt=$($LFS quota -a -s $start_qid -e $end_qid -g $MOUNT | wc -l) + [ $cnt -ge $((qid_cnt + 2)) ] || error "failed to get all grp quota" + + eval $($LFS quota -a -s $start_qid -e $end_qid -u $MOUNT | + awk 'NR > 2 {printf("u_blimits[%d]=%d;u_ilimits[%d]=%d; \ + u_busage2[%d]=%d;u_iusage2[%d]=%d;", \ + NR, $4, NR, $8, NR, $2, NR, $6)}') + eval $($LFS quota -a -s $start_qid -e $end_qid -g $MOUNT | + awk 'NR > 2 {printf("g_blimits[%d]=%d;g_ilimits[%d]=%d; \ + g_busage2[%d]=%d;g_iusage2[%d]=%d;", \ + NR, $4, NR, $8, NR, $2, NR, $6)}') + + sz=$((sz / 1024)) + for i in $(seq $qid_cnt); do + [ $i -le 2 ] && continue + + [ ${u_ilimits[$i]} -eq $u_ilimit ] || + error "file limit for user ID $((start_qid + i - 3)) is wrong" + [ ${u_blimits[$i]} -eq $u_blimit ] || + error "block limit for user ID $((start_qid + i - 3)) is wrong" + [ ${g_ilimits[$i]} -eq $g_ilimit ] || + error "file limit for group ID $((start_qid + i - 3)) is wrong" + [ ${g_blimits[$i]} -eq $g_blimit ] || + error "block limit for group ID $((start_qid + i - 3)) is wrong" + [ ${u_iusage2[$i]} -eq $((u_iusage[$i] + 1)) ] || + error "file usage for user ID $((start_qid + i - 3)) is wrong ${u_iusage[$i]}, ${u_iusage2[$i]}" + [ ${u_busage2[$i]} -ge $((u_busage[$i] + 4)) ] || + error "block usage for user ID $((start_qid + i - 3)) is wrong ${u_busage[$i]}, ${u_busage2[$i]}" + [ ${g_iusage2[$i]} -eq $((g_iusage[$i] + 1)) ] || + error "file usage for group ID $((start_qid + i - 3)) is wrong ${g_iusage[$i]}, ${g_iusage2[$i]}" + [ ${g_busage2[$i]} -ge $((g_busage[$i] + 4)) ] || + error "block usage for group ID $((start_qid + i - 3)) is wrong ${g_busage[$i]}, ${g_busage2[$i]}" + done + + unlinkmany ${TFILE} $qid_cnt +} + +test_49() +{ + (( MDS1_VERSION >= $(version_code 2.14.0-ddn141) )) || + skip "Need MDS version at least 2.15.60" + + local u_blimit=102400 + local u_ilimit=10240 + local g_blimit=204800 + local g_ilimit=20480 + local count=10 + + setup_quota_test || error "setup quota failed with $?" + stack_trap cleanup_quota_test EXIT + + [ "$SLOW" = "yes" ] && total_file_cnt=20000 || total_file_cnt=1000 + total_file_cnt=${NUM_QIDS:-$total_file_cnt} + + local start=$SECONDS + + echo "setquota for users and groups" + #define OBD_FAIL_QUOTA_NOSYNC 0xA09 + do_facet mds1 $LCTL set_param fail_loc=0xa09 + for i in $(seq $total_file_cnt); do + $LFS setquota -u $i -B ${u_blimit} -I ${u_ilimit} $MOUNT || + error "failed to setquota for usr $i" + $LFS setquota -g $i -B ${g_blimit} -I ${g_ilimit} $MOUNT || + error "failed to setquota for grp $i" + (( i % 1000 == 0)) && + echo "lfs setquota: $i / $((SECONDS - start)) seconds" + done + do_facet mds1 $LCTL set_param fail_loc=0 + + start=$SECONDS + $LFS quota -a -u $MOUNT | tail -n 100 + echo "get all usr quota: $total_file_cnt / $((SECONDS - start)) seconds" + + start=$SECONDS + $LFS quota -a -g $MOUNT | tail -n 100 + echo "get all grp quota: $total_file_cnt / $((SECONDS - start)) seconds" + + while true; do + test_get_allquota $total_file_cnt $count $((count + 5000)) \ + $u_blimit $u_ilimit $g_blimit $g_ilimit + test_get_allquota $total_file_cnt $count $((count + 5000)) \ + $u_blimit $u_ilimit $g_blimit $g_ilimit + + count=$((count + 5000)) + [ $count -gt $total_file_cnt ] && break + done; + + do_facet mds1 $LCTL set_param fail_loc=0xa08 + for i in $(seq $total_file_cnt); do + $LFS setquota -u $i --delete $MOUNT + $LFS setquota -g $i --delete $MOUNT + done + do_facet mds1 $LCTL set_param fail_loc=0 + + formatall + setupall +} +run_test 49 "lfs quota -a prints the quota usage for all quota IDs" + test_50() { ! is_project_quota_supported && skip "Project quota is not supported" @@ -4952,7 +5138,7 @@ get_slave_nr() { wait_update_facet mds1 \ "$LCTL get_param -n qmt.$FSNAME-QMT0000.dt-$pool.info \ >/dev/null 2>&1 || echo foo" "" || - error "mds1: failed to create quota pool $pool" + error "mds1: failed to create quota pool $pool" do_facet mds1 $LCTL get_param -n qmt.$FSNAME-QMT0000.dt-$pool.info | awk '/usr/ {getline; print $2}' diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 9c98846..f95643f 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -500,7 +500,8 @@ command_t cmdlist[] = { " [{-u|-g|-p} UNAME|UID|GNAME|GID|PROJID]\n" " [--pool ] \n" " quota -t <-u|-g|-p> [--pool ] \n" - " quota [-q] [-v] [h] {-U|-G|-P} [--pool ] "}, + " quota [-q] [-v] [h] {-U|-G|-P} [--pool ] \n" + " quota -a {-u|-g|-p} [-s start_qid] [-e end_qid] "}, {"project", lfs_project, 0, "Change or list project attribute for specified file or directory.\n" "usage: project [-d|-r] \n" @@ -8500,8 +8501,9 @@ static void kbytes2str(__u64 num, char *buf, int buflen, bool h) #define STRBUF_LEN 24 static void print_quota(char *mnt, struct if_quotactl *qctl, int type, - int rc, bool h, bool show_default) + int rc, bool h, bool show_default, bool show_qid) { + char *name; time_t now; time(&now); @@ -8539,10 +8541,26 @@ static void print_quota(char *mnt, struct if_quotactl *qctl, int type, iover = 3; } - if (strlen(mnt) > 15) - printf("%s\n%15s", mnt, ""); - else - printf("%15s", mnt); + if (show_qid) { + if (qctl->qc_type == USRQUOTA) { + if (uid2name(&name, qctl->qc_id)) + printf("%10u", qctl->qc_id); + else + printf("%10s", name); + } else if (qctl->qc_type == GRPQUOTA) { + if (gid2name(&name, qctl->qc_id)) + printf("%10u", qctl->qc_id); + else + printf("%10s", name); + } else { + printf("%10u", qctl->qc_id); + } + } else { + if (strlen(mnt) > 15) + printf("%s\n%15s", mnt, ""); + else + printf("%15s", mnt); + } if (show_default) snprintf(timebuf, sizeof(timebuf), "%llu", @@ -8724,7 +8742,7 @@ static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt, memset(&qctl->qc_dqblk, 0, sizeof(qctl->qc_dqblk)); print_quota(name, qctl, qctl->qc_valid, 0, h, - false); + false, false); rc = 0; continue; } @@ -8737,7 +8755,7 @@ static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt, } print_quota(obd_uuid2str(&qctl->obd_uuid), qctl, - qctl->qc_valid, 0, h, false); + qctl->qc_valid, 0, h, false, false); *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit : qctl->qc_dqblk.dqb_bhardlimit; } @@ -8748,35 +8766,16 @@ out: return rc ? : rc1; } -static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl, +static int print_one_quota(char *mnt, char *name, struct if_quotactl *qctl, int verbose, int quiet, bool human_readable, - bool show_default) + bool show_default, bool show_qid, int rc) { - int rc1 = 0, rc2 = 0, rc3 = 0; + int rc1 = 0, rc2 = 0; char *obd_type = (char *)qctl->obd_type; char *obd_uuid = (char *)qctl->obd_uuid.uuid; __u64 total_ialloc = 0, total_balloc = 0; int inacc; - rc1 = llapi_quotactl(mnt, qctl); - if (rc1 < 0) { - switch (rc1) { - case -ESRCH: - fprintf(stderr, "%s quotas are not enabled.\n", - qtype_name(qctl->qc_type)); - goto out; - case -EPERM: - fprintf(stderr, "Permission denied.\n"); - case -ENODEV: - case -ENOENT: - /* We already got error message. */ - goto out; - default: - fprintf(stderr, "Unexpected quotactl error: %s\n", - strerror(-rc1)); - } - } - if (!show_default && qctl->qc_id == 0) { qctl->qc_dqblk.dqb_bhardlimit = 0; qctl->qc_dqblk.dqb_bsoftlimit = 0; @@ -8795,13 +8794,13 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl, LQUOTA_FLAG(qctl->qc_dqblk.dqb_itime) & LQUOTA_FLAG_DEFAULT) qctl->qc_dqblk.dqb_itime &= LQUOTA_GRACE_MASK; - if ((qctl->qc_cmd == LUSTRE_Q_GETQUOTA || + if (!show_qid && (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == LUSTRE_Q_GETQUOTAPOOL || qctl->qc_cmd == LUSTRE_Q_GETDEFAULT_POOL || qctl->qc_cmd == LUSTRE_Q_GETDEFAULT) && !quiet) print_quota_title(name, qctl, human_readable, show_default); - if (rc1 && *obd_type) + if (rc && *obd_type) fprintf(stderr, "%s %s ", obd_type, obd_uuid); if (qctl->qc_valid != QC_GENERAL) @@ -8812,16 +8811,17 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl, ((qctl->qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) != (QIF_LIMITS|QIF_USAGE)); - print_quota(mnt, qctl, QC_GENERAL, rc1, human_readable, show_default); + print_quota(mnt, qctl, QC_GENERAL, rc, human_readable, show_default, + show_qid); - if (!show_default && verbose && + if (!show_qid && !show_default && verbose && qctl->qc_valid == QC_GENERAL && qctl->qc_cmd != LUSTRE_Q_GETINFO && qctl->qc_cmd != LUSTRE_Q_GETINFOPOOL) { char strbuf[STRBUF_LEN]; - rc2 = print_obd_quota(mnt, qctl, 1, human_readable, + rc1 = print_obd_quota(mnt, qctl, 1, human_readable, &total_ialloc); - rc3 = print_obd_quota(mnt, qctl, 0, human_readable, + rc2 = print_obd_quota(mnt, qctl, 0, human_readable, &total_balloc); kbytes2str(total_balloc, strbuf, sizeof(strbuf), human_readable); @@ -8829,21 +8829,119 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl, (uintmax_t)total_ialloc, strbuf); } - if (rc1 || rc2 || rc3 || inacc) - printf("Some errors happened when getting quota info. Some devices may be not working or deactivated. The data in \"[]\" is inaccurate.\n"); -out: + if (!show_qid && (rc || rc1 || rc2 || inacc)) + printf("%d Some errors happened when getting quota info. Some devices may be not working or deactivated. The data in \"[]\" is inaccurate.\n", inacc); + + if (rc) + return rc; if (rc1) return rc1; if (rc2) return rc2; - if (rc3) - return rc3; if (inacc) return -EIO; return 0; } +static int iter_all_quota(char *mnt, struct if_quotactl *qctl, int quiet, + bool human_readable) +{ + struct if_quotactl qctl_tmp, *qctl_iter; + void *buffer = NULL; + __u64 mark; + __u64 cur, buflen = 0; + int rc = 0; + + memcpy(&qctl_tmp, qctl, sizeof(struct if_quotactl)); + qctl_tmp.qc_cmd = LUSTRE_Q_ITERQUOTA; + rc = llapi_quotactl(mnt, &qctl_tmp); + if (rc) + goto out; + + buflen = qctl_tmp.qc_allquota_count * sizeof(struct if_quotactl); + buffer = malloc(buflen); + if (buffer == NULL) { + rc = -ENOMEM; + goto out; + } + + mark = qctl_tmp.qc_allquota_mark; + memcpy(&qctl_tmp, qctl, sizeof(struct if_quotactl)); + qctl_tmp.qc_cmd = LUSTRE_Q_GETALLQUOTA; + qctl_tmp.qc_allquota_buffer = (__u64)buffer; + qctl_tmp.qc_allquota_buflen = buflen; + qctl_tmp.qc_allquota_mark = mark; + rc = llapi_quotactl(mnt, &qctl_tmp); + if (rc) + goto out; + + printf("Filesystem %s, Disk %s quotas\n", mnt, + qtype_name(qctl->qc_type)); + printf("%10s%8s %7s%8s%8s%8s %7s%8s%8s\n", "quota_id", + human_readable ? "used" : "kbytes", "quota", "limit", "grace", + "files", "quota", "limit", "grace"); + + cur = 0; + while (cur < buflen) { + if ((buflen - cur) < sizeof(struct if_quotactl)) { + rc = -EFAULT; + break; + } + + qctl_iter = buffer + cur; + qctl_iter->qc_cmd = LUSTRE_Q_GETQUOTA; + cur += sizeof(struct if_quotactl); + + /* Is no file created for this quota ID yet? */ + if ((qctl_iter->qc_dqblk.dqb_valid & QIF_USAGE) != QIF_USAGE) + qctl_iter->qc_dqblk.dqb_valid |= QIF_USAGE; + + print_one_quota(mnt, NULL, qctl_iter, 0, quiet, + human_readable, false, true, 0); + } + +out: + if (buffer != NULL) + free(buffer); + + if (rc) + fprintf(stderr, "get all quota failed %d\n", rc); + + return rc; +} + +static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl, + int verbose, int quiet, bool human_readable, + bool show_default) +{ + int rc; + + rc = llapi_quotactl(mnt, qctl); + if (rc < 0) { + switch (rc) { + case -ESRCH: + fprintf(stderr, "%s quotas are not enabled.\n", + qtype_name(qctl->qc_type)); + break; + case -EPERM: + fprintf(stderr, "Permission denied.\n"); + case -ENODEV: + case -ENOENT: + /* We already got error message. */ + break; + default: + fprintf(stderr, "Unexpected quotactl error: %s\n", + strerror(-rc)); + } + + return rc; + } + + return print_one_quota(mnt, name, qctl, verbose, quiet, human_readable, + show_default, false, rc); +} + static int lfs_project(int argc, char **argv) { int ret = 0, err = 0, c, i; @@ -9012,6 +9110,7 @@ static int lfs_quota(int argc, char **argv) char *obd_uuid; int rc = 0, rc1 = 0, verbose = 0, quiet = 0; __u32 valid = QC_GENERAL, idx = 0; + __u32 start_qid = 0, end_qid = 0; bool human_readable = false; bool show_default = false; int qtype; @@ -9027,7 +9126,7 @@ static int lfs_quota(int argc, char **argv) qctl->qc_type = ALLQUOTA; obd_uuid = (char *)qctl->obd_uuid.uuid; - while ((c = getopt_long(argc, argv, "gGi:I:o:pPqtuUvh", + while ((c = getopt_long(argc, argv, "ae:gGi:I:o:pPqs:tuUvh", long_opts, NULL)) != -1) { switch (c) { case 'U': @@ -9054,6 +9153,9 @@ quota_type: } qctl->qc_type = qtype; break; + case 'a': + qctl->qc_cmd = LUSTRE_Q_ITERQUOTA; + break; case 't': qctl->qc_cmd = LUSTRE_Q_GETINFO; break; @@ -9083,6 +9185,12 @@ quota_type: goto out; } break; + case 's': + start_qid = strtoul(optarg, NULL, 0); + break; + case 'e': + end_qid = strtoul(optarg, NULL, 0); + break; case 'v': verbose = 1; break; @@ -9185,6 +9293,34 @@ quota_type: goto out; } } + } else if (qctl->qc_cmd == LUSTRE_Q_ITERQUOTA) { + if (optind + 1 != argc) { + fprintf(stderr, + "%s quota: mount point must be specified\n", + progname); + rc = CMD_HELP; + goto out; + } + + if (qctl->qc_type == ALLQUOTA) { + fprintf(stderr, "%s quota: no quota type to iterate\n", + progname); + rc = CMD_HELP; + goto out; + } + + if (end_qid != 0 && start_qid > end_qid) { + fprintf(stderr, + "%s quota: end qid is smaller than start qid\n", + progname); + rc = CMD_HELP; + goto out; + } + + qctl->qc_allquota_qid_start = start_qid; + qctl->qc_allquota_qid_end = end_qid; + rc = iter_all_quota(argv[optind], qctl, quiet, human_readable); + goto out; } else if (optind + 1 != argc || qctl->qc_type == ALLQUOTA) { fprintf(stderr, "%s quota: missing quota info argument(s)\n", progname);