Whamcloud - gitweb
LU-14535 quota: get all quota info in LFS 98/42098/65
authorHongchao Zhang <hongchao@whamcloud.com>
Sat, 20 Jan 2024 06:39:33 +0000 (14:39 +0800)
committerOleg Drokin <green@whamcloud.com>
Sat, 23 Mar 2024 05:51:07 +0000 (05:51 +0000)
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      -

This patch also fixes an deadlock issue in qmt_pool_recalc,
the rw_semaphore "qmt_pool_info.qpi_sarr.osts.op_rw_sem" has been
acquired in qmt_pool_recalc (read mode), but it was acquired once
more in qmt_seed_glbe_all (read mode) and will be stuck if there
is a pending write mode lock acquisition from another thread.

 qsd_reint_qpool D
 Call Trace:
    schedule+0x29/0x70
    rwsem_down_read_failed+0x105/0x1c0
    call_rwsem_down_read_failed+0x18/0x30
    down_read+0x20/0x40
    qmt_seed_glbe_all+0x3a0/0x800 [lquota]
    qmt_site_recalc_cb+0x3c7/0x800 [lquota]
    cfs_hash_for_each_tight+0x11e/0x330
    cfs_hash_for_each+0x10/0x20 [libcfs]
    qmt_pool_recalc+0x9fc/0x1310 [lquota]

 llog_process_th D
 Call Trace:
    schedule+0x29/0x70
    rwsem_down_write_failed+0x215/0x3c0
    call_rwsem_down_write_failed+0x17/0x30
    down_write+0x2d/0x3d
    lu_tgt_pool_remove+0x36/0x1e0 [obdclass]
    qmt_pool_add_rem+0x655/0x920 [lquota]
    qmt_pool_rem+0x10/0x20 [lquota]
    lod_pool_remove_q+0xd6/0x1d0 [lod]
    class_process_config+0x16f2/0x2b20
    class_config_llog_handler+0x839/0x1540
    llog_process_thread+0x913/0x1c10
    llog_process_thread_daemonize+0x9f/0xe0

Test-Parameters: testlist=sanity-quota env=SLOW=yes,ONLY=49,NUM_QIDS=20000
Change-Id: I08feb928fbf34635ec9c5c341de993c718798dc9
Signed-off-by: Hongchao Zhang <hongchao@whamcloud.com>
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/42098
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: James Simmons <jsimmons@infradead.org>
Reviewed-by: Sergey Cheremencev <scherementsev@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
29 files changed:
contrib/scripts/checkpatch.pl
lustre/doc/lfs-quota.1
lustre/include/lustre_quota.h
lustre/include/lustre_req_layout.h
lustre/include/obd_class.h
lustre/include/obd_support.h
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/llite/dir.c
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/llite/super25.c
lustre/lmv/lmv_obd.c
lustre/lov/lov_obd.c
lustre/mdc/mdc_request.c
lustre/mdt/mdt_handler.c
lustre/ofd/ofd_dev.c
lustre/osc/osc_quota.c
lustre/ptlrpc/layout.c
lustre/quota/lquota_internal.h
lustre/quota/lquota_lib.c
lustre/quota/qmt_entry.c
lustre/quota/qmt_handler.c
lustre/quota/qmt_internal.h
lustre/quota/qmt_pool.c
lustre/target/tgt_handler.c
lustre/tests/createmany.c
lustre/tests/sanity-quota.sh
lustre/utils/lfs.c

index 5f09cd1..236ed02 100755 (executable)
@@ -460,7 +460,8 @@ our $logFunctions = qr{(?x:
        CDEBUG|CERROR|CNETERR|CEMERG|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:
index 05ba99f..f4c5a0f 100644 (file)
@@ -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
@@ -106,6 +110,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
index 7e4796f..798ecf9 100644 (file)
@@ -67,6 +67,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[0];
+};
+
+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
@@ -95,7 +114,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 *env, struct lu_device *d,
-                            struct obd_quotactl *);
+                            struct obd_quotactl *, char *buf, int len);
 
        /* Handle dqacq/dqrel request from slave. */
        int (*qmth_dqacq)(const struct lu_env *env, struct lu_device *d,
@@ -261,7 +280,7 @@ struct lquota_trans {
  * on slave
  */
 int lquotactl_slv(const struct lu_env *env, struct dt_device *dt,
-                 struct obd_quotactl *obdq);
+                 struct obd_quotactl *obdq, char *buf, int len);
 
 /** @} quota */
 #endif /* _LUSTRE_QUOTA_H */
index 645c072..0ed7e33 100644 (file)
@@ -359,6 +359,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;
index 8ea0f75..f2f74be 100644 (file)
@@ -1323,6 +1323,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)
 {
index e21e547..028f14b 100644 (file)
@@ -560,6 +560,7 @@ extern bool obd_enable_health_write;
 #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
 
index 5630acb..c5c5bd5 100644 (file)
@@ -1557,6 +1557,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)                                    \
index b089e92..60f14b4 100644 (file)
@@ -1429,6 +1429,9 @@ static inline __u64 toqb(__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.
@@ -1591,6 +1594,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)
index c4b770b..04d7999 100644 (file)
@@ -55,6 +55,7 @@
 #include <lustre_fid.h>
 #include <lustre_kernelcomm.h>
 #include <lustre_swab.h>
+#include <lustre_quota.h>
 #include <libcfs/libcfs_crypto.h>
 
 #include "llite_internal.h"
@@ -1231,6 +1232,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(&quotactl_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(&quotactl_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(&quotactl_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(&quotactl_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(&quotactl_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(&quotactl_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(&quotactl_iter_lock);
+
+       RETURN(rc);
+}
+
 int quotactl_ioctl(struct super_block *sb, struct if_quotactl *qctl)
 {
        struct ll_sb_info *sbi = ll_s2sbi(sb);
@@ -1261,6 +1660,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);
@@ -1274,7 +1675,11 @@ int quotactl_ioctl(struct super_block *sb, struct if_quotactl *qctl)
                RETURN(-EOPNOTSUPP);
        }
 
-       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 ||
index 779c132..4527bac 100644 (file)
@@ -992,6 +992,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)
@@ -1265,6 +1268,7 @@ enum get_default_layout_type {
 
 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, int *partial_readdir_rc);
@@ -1278,6 +1282,7 @@ struct page *ll_get_dir_page(struct inode *dir, struct md_op_data *op_data,
                              __u64 offset, int *partial_readdir_rc);
 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;
index 7e67cf5..4649227 100644 (file)
@@ -226,6 +226,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:
        if (sbi->ll_foreign_symlink_prefix)
@@ -1548,6 +1550,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);
 
index 367b34f..be22b7d 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/version.h>
 #include <lustre_ha.h>
 #include <lustre_dlm.h>
+#include <lustre_quota.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/random.h>
@@ -264,6 +265,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);
@@ -301,6 +308,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;
 }
 
@@ -323,6 +331,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. <http://www.lustre.org/>");
index 8046f77..9fed3a8 100644 (file)
@@ -4087,6 +4087,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);
index c3b10a0..a0f7cb9 100644 (file)
@@ -1255,6 +1255,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 lov_pool_desc *pool = NULL;
+       struct list_head *lst = NULL;
        __u64 curspace = 0;
        __u64 bhardlimit = 0;
        int i, rc = 0;
@@ -1262,7 +1263,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);
@@ -1278,6 +1280,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++) {
@@ -1304,7 +1309,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;
@@ -1324,6 +1333,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);
 }
 
index af7ca89..52d6d3d 100644 (file)
@@ -57,6 +57,7 @@
 #include <lustre_log.h>
 #include <lustre_osc.h>
 #include <lustre_swab.h>
+#include <lustre_quota.h>
 #include <obd_class.h>
 
 #include "mdc_internal.h"
@@ -2067,7 +2068,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)
 {
@@ -2087,6 +2087,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) {
@@ -2109,7 +2117,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",
index ce05eb3..b512006 100644 (file)
@@ -3313,10 +3313,11 @@ 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;
 
@@ -3324,6 +3325,13 @@ static int mdt_quotactl(struct tgt_session_info *tsi)
        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));
@@ -3352,12 +3360,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;
@@ -3387,6 +3397,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);
 
@@ -3414,15 +3431,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:
index 77c91ac..32c9536 100644 (file)
@@ -2363,21 +2363,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));
@@ -2401,7 +2418,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));
@@ -2837,7 +2855,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),
index 55fb935..0acf616 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <obd_class.h>
 #include <lustre_osc.h>
+#include <lustre_quota.h>
 
 #include "osc_internal.h"
 
@@ -205,16 +206,24 @@ void 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;
@@ -227,14 +236,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);
 }
index 696c9dc..4fc1c3c 100644 (file)
@@ -104,6 +104,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
@@ -1036,6 +1042,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);
@@ -1446,11 +1456,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 =
index bd05ef0..645f42e 100644 (file)
@@ -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 */
index 12f175d..9181bb2 100644 (file)
@@ -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);
index fb441a1..7e67dea 100644 (file)
@@ -860,7 +860,8 @@ bool qmt_adjust_edquot_qunit_notify(const struct lu_env *env,
                        struct lqe_glbl_data *lgd = lqe_gl->lqe_glbl_data;
 
                        if (reseed) {
-                               qmt_seed_glbe_all(env, lgd, qunit, edquot);
+                               qmt_seed_glbe_all(env, lgd, qunit, edquot,
+                                                 false);
                        } else if (idx >= 0) {
                                int lge_idx = qmt_map_lge_idx(lgd, idx);
 
@@ -930,7 +931,7 @@ void qmt_revalidate_lqes(const struct lu_env *env,
 
        mutex_lock(&lqe_gl->lqe_glbl_data_lock);
        if (lqe_gl->lqe_glbl_data)
-               qmt_seed_glbe(env, lqe_gl->lqe_glbl_data);
+               qmt_seed_glbe(env, lqe_gl->lqe_glbl_data, false);
        mutex_unlock(&lqe_gl->lqe_glbl_data_lock);
 
        qmt_id_lock_notify(qmt, lqe_gl);
@@ -1146,7 +1147,7 @@ int qmt_map_lge_idx(struct lqe_glbl_data *lgd, int ostidx)
 }
 
 void qmt_seed_glbe_all(const struct lu_env *env, struct lqe_glbl_data *lgd,
-                      bool qunit, bool edquot)
+                      bool qunit, bool edquot, bool pool_locked)
 {
        struct qmt_pool_info *qpi;
        int i, j;
@@ -1182,7 +1183,9 @@ void qmt_seed_glbe_all(const struct lu_env *env, struct lqe_glbl_data *lgd,
 
                CDEBUG(D_QUOTA, "lqes_cnt %d, i %d\n", qti_lqes_cnt(env), i);
                qpi = lqe2qpi(lqe);
-               qmt_sarr_read_down(qpi);
+               if (!pool_locked)
+                       qmt_sarr_read_down(qpi);
+
                slaves_cnt = qmt_sarr_count(qpi);
 
                for (j = 0; j < slaves_cnt; j++) {
@@ -1249,7 +1252,8 @@ qunit_lbl:
                        }
                }
 
-               qmt_sarr_read_up(qpi);
+               if (!pool_locked)
+                       qmt_sarr_read_up(qpi);
        }
        /* TODO: only for debug purposes - remove it later */
        for (i = 0; i < lgd->lqeg_num_used; i++)
@@ -1291,7 +1295,7 @@ void qmt_setup_lqe_gd(const struct lu_env *env, struct qmt_device *qmt,
 
        qmt_pool_lqes_lookup_spec(env, qmt, pool_type,
                                  lqe_qtype(lqe), &lqe->lqe_id);
-       qmt_seed_glbe(env, lgd);
+       qmt_seed_glbe(env, lgd, false);
 
        lqe->lqe_glbl_data = lgd;
        qmt_id_lock_notify(qmt, lqe);
index 116d01d..ed55ea1 100644 (file)
@@ -125,7 +125,7 @@ static void qmt_set_id_notify(const struct lu_env *env, struct qmt_device *qmt,
 
        mutex_lock(&lqe_gl->lqe_glbl_data_lock);
        if (lqe_gl->lqe_glbl_data)
-               qmt_seed_glbe(env, lqe_gl->lqe_glbl_data);
+               qmt_seed_glbe(env, lqe_gl->lqe_glbl_data, false);
        mutex_unlock(&lqe_gl->lqe_glbl_data_lock);
 
        /* Even if slaves haven't enqueued quota lock yet,
@@ -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;
index 935bb8a..9aabef1 100644 (file)
@@ -516,13 +516,13 @@ void qmt_free_lqe_gd(struct lqe_glbl_data *);
 void qmt_setup_lqe_gd(const struct lu_env *,  struct qmt_device *,
                    struct lquota_entry *, struct lqe_glbl_data *, int);
 #define qmt_seed_glbe_edquot(env, lqeg) \
-               qmt_seed_glbe_all(env, lqeg, false, true)
+               qmt_seed_glbe_all(env, lqeg, false, true, false)
 #define qmt_seed_glbe_qunit(env, lqeg) \
-               qmt_seed_glbe_all(env, lqeg, true, false)
-#define qmt_seed_glbe(env, lqeg) \
-               qmt_seed_glbe_all(env, lqeg, true, true)
+               qmt_seed_glbe_all(env, lqeg, true, false, false)
+#define qmt_seed_glbe(env, lqeg, pool_locked) \
+               qmt_seed_glbe_all(env, lqeg, true, true, pool_locked)
 void qmt_seed_glbe_all(const struct lu_env *, struct lqe_glbl_data *,
-                      bool , bool);
+                      bool, bool, bool);
 
 /* qmt_handler.c */
 int qmt_set_with_lqe(const struct lu_env *env, struct qmt_device *qmt,
index e12308d..66c4aa4 100644 (file)
@@ -1207,8 +1207,10 @@ static int qmt_site_recalc_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
                                struct lquota_entry *lqeg = qti_lqes_glbl(env);
 
                                mutex_lock(&lqeg->lqe_glbl_data_lock);
-                               if (lqeg->lqe_glbl_data)
-                                       qmt_seed_glbe(env, lqeg->lqe_glbl_data);
+                               if (lqeg->lqe_glbl_data &&
+                                   qti_lqes_cnt(env) > 0)
+                                       qmt_seed_glbe(env, lqeg->lqe_glbl_data,
+                                                     true);
                                mutex_unlock(&lqeg->lqe_glbl_data_lock);
                                qmt_id_lock_notify(qmt, lqeg);
                        }
index 13868c3..a970d04 100644 (file)
@@ -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);
        }
 
index c293e0f..d728701 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/ioctl.h>
 #include <time.h>
 #include <unistd.h>
+#include <lustre/lustreapi.h>
 
 #include <linux/lustre/lustre_user.h>
 #include <lustre/lustreapi.h>
@@ -58,6 +60,10 @@ static void usage(const char *prog)
               "\t-u\tunlink file/dir (with optional <unlinkfmt>)\n");
        printf("\t-d\tuse directories instead of regular files\n"
               "\t-t\tstop creating files after <seconds> 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);
 }
@@ -88,6 +94,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;
        bool do_rmdir = false;
        int stripe_pattern = LMV_HASH_TYPE_FNV_1A_64;
        int stripe_offset = -1, stripe_count = 1;
@@ -99,6 +107,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" */
@@ -117,11 +127,15 @@ int main(int argc, char **argv)
        else
                progname = argv[0];
 
-       while ((c = getopt(argc, argv, "i:dl:kmor::t:u::")) != -1) {
+       while ((c = getopt(argc, argv, "i: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 'i':
                        stripe_offset = strtoul(optarg, &endp, 0);
                        if (*endp != '\0') {
@@ -143,6 +157,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')
@@ -153,12 +175,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");
@@ -203,6 +234,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)
index 4bd8d45..7f2db1f 100755 (executable)
@@ -4018,6 +4018,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.15.60) )) ||
+               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"
@@ -5000,7 +5186,7 @@ get_slave_nr() {
        wait_update_facet "--quiet" mds1 \
                "$LCTL get_param -n qmt.$FSNAME-QMT0000.dt-$pool.info \
                        >/dev/null 2>&1 || echo foo" "">/dev/null ||
-       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}'
index 7ac0367..36490c9 100644 (file)
@@ -429,7 +429,8 @@ command_t cmdlist[] = {
         "             [{-u|-g|-p} UNAME|UID|GNAME|GID|PROJID]\n"
         "             [--pool <OST pool name>] <filesystem>\n"
         "       quota -t <-u|-g|-p> [--pool <OST pool name>] <filesystem>\n"
-        "       quota [-q] [-v] [h] {-U|-G|-P} [--pool <OST pool name>] <filesystem>"},
+        "       quota [-q] [-v] [h] {-U|-G|-P} [--pool <OST pool name>] <filesystem>\n"
+        "       quota -a {-u|-g|-p} [-s start_qid] [-e end_qid] <filesystem>"},
        {"project", lfs_project, 0,
         "Change or list project attribute for specified file or directory.\n"
         "usage: project [-d|-r] <file|directory...>\n"
@@ -8844,8 +8845,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);
@@ -8883,10 +8885,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",
@@ -9068,7 +9086,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;
                        }
@@ -9081,7 +9099,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;
        }
@@ -9092,11 +9110,11 @@ 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;
@@ -9104,25 +9122,6 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl,
        bool use_default_for_file = false;
        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;
@@ -9145,13 +9144,13 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl,
                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)
@@ -9162,16 +9161,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);
@@ -9187,21 +9187,119 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl,
                printf("%cid %u is using default file quota setting\n",
                       *qtype_name(qctl->qc_type), qctl->qc_id);
 
-       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;
@@ -9370,6 +9468,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;
@@ -9389,7 +9488,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':
@@ -9416,6 +9515,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;
@@ -9445,6 +9547,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;
@@ -9558,6 +9666,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);