Whamcloud - gitweb
LU-7816 quota: add default quota setting support 06/32306/16
authorHongchao Zhang <hongchao.zhang@intel.com>
Tue, 5 Jun 2018 22:23:42 +0000 (18:23 -0400)
committerOleg Drokin <green@whamcloud.com>
Tue, 3 Jul 2018 18:05:26 +0000 (18:05 +0000)
Similar function which is motivated by GPFS which is friendly
feature for cluster administrators to manage quota.

Lazy Quota default setting support, here is basic idea:

Default quota setting is global quota setting for user, group,
project quotas, if default quota is set for one quota type,
newer created users/groups/projects will inherit this setting
automatically, since Lustre itself don't have ideas when new
users created, they could only know when this users trying to
acquire space from Lustre.

So we try to implement lazy quota setting inherit, Slave firstly
check if there exists default quota setting, if exists, it will
force slave to acquire quota from master, and master will detect
whether default quota is set, then it will set this quota and also
return proper grant space to slave.

To implement this and reuse existed quota APIs, we try to manage
the default quota in the quota record of 0 id, and enforce the
quota check when reading the quota recored from disk.

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, then 48bits should be enough for it, its high 16bits
can be used as kinds of quota flags, this patch will use one of
them as the default quota flag.

The global quota record used by default quota will set its soft
and hard limit as zero, its grace time will contain the default flag.

Use lfs setquota -U/-G/-P <mnt> to set default quota.
Use lfs setquota -u/-g/-p foo -d <mnt> to set foo to use default quota
Use lfs quota -U/-G/-P <mnt> to show default quota.

Test-Parameters: envdefinitions=DEBUG_SIZE=64

Change-Id: Ib23007360921832b3c7d5710ab50324bc5067286
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Hongchao Zhang <hongchao.zhang@intel.com>
Reviewed-on: https://review.whamcloud.com/32306
Reviewed-by: Fan Yong <fan.yong@intel.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
17 files changed:
lustre/doc/lfs-setquota.1
lustre/doc/lfs.1
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/llite/dir.c
lustre/mdt/mdt_handler.c
lustre/quota/lquota_internal.h
lustre/quota/qmt_entry.c
lustre/quota/qmt_handler.c
lustre/quota/qmt_internal.h
lustre/quota/qmt_lock.c
lustre/quota/qsd_entry.c
lustre/quota/qsd_internal.h
lustre/quota/qsd_lock.c
lustre/quota/qsd_reint.c
lustre/quota/qsd_writeback.c
lustre/tests/sanity-quota.sh
lustre/utils/lfs.c

index 1657801..1bd6ca6 100644 (file)
@@ -7,11 +7,20 @@ lfs setquota \- set quota limits or grace time for users, groups or projects.
        [\fB--block-hardlimit|-B\fR <\fIblock-hardlimit\fR>[kMGTPE]]
        [\fB--inode-softlimit|-i\fR <\fIinode-softlimit\fR>[kMGTPE]]
        [\fB--inode-hardlimit|-I\fR <\fIinode-hardlimit\fR>[kMGTPE]] <\fIfilesystem\fR>
-.br
+.TP
 .B lfs setquota \fB-t\fR {\fB-u|-g|-p\fR}
        [\fB--block-grace|-b\fR <\fIblock-grace\fR>]
        [\fB--inode-grace|-i\fR <\fIinode-grace\fR>] <\fIfilesystem\fR>
-.br
+.TP
+.B lfs setquota {\fB-u|--user|-g|--group|-p|--projid\fR} <\fIuname|uid|gname|gid|projid\fR>
+       [\fB--default|-d\fR] <\fIfilesystem\fR>
+.TP
+.B lfs setquota {\fB-U|--default-usr|-G|--default-grp|-P|--default-prj\fR}
+       [\fB--block-softlimit|-b\fR <\fIblock-softlimit\fR>[kMGTPE]]
+       [\fB--block-hardlimit|-B\fR <\fIblock-hardlimit\fR>[kMGTPE]]
+       [\fB--inode-softlimit|-i\fR <\fIinode-softlimit\fR>[kMGTPE]]
+       [\fB--inode-hardlimit|-I\fR <\fIinode-hardlimit\fR>[kMGTPE]] <\fIfilesystem\fR>
+.TP
 .SH DESCRIPTION
 .PP
 .BR "lfs setquota " {\fB-u|-g|-p\fR}
@@ -33,6 +42,9 @@ Set group quota for name \fIgname\fR or \fIgid\fR.
 .B -p|--project <\fIprojid\fR>
 Set project quota for \fIprojid\fR.
 .TP
+.B -d|--default
+Set user/group/project to use the default quota limits.
+.TP
 .B -b|--block-softlimit <\fIblock-softlimit\fR>
 Specify block softlimit, zero means unlimited.
 .TP
@@ -57,7 +69,7 @@ the quota limit for a short time to complete their work, without having to \
 grant each user a larger hard quota limit.  The soft quota limit is reset once \
 the user, group, or project reduces their space usage below the soft quota \
 limit. Grace time is specified in "XXwXXdXXhXXmXXs" format or as an integer \
-seconds value.
+seconds value, and the maximum is 2^48 - 1 seconds.
 .TP
 .B -t
 Set quota grace times.
@@ -76,10 +88,33 @@ Specify grace time for block quota.
 .TP
 .B -i|--inode-grace <\fIindoe-grace\fR>
 Specify grace time for inode quota.
+.PP
+.BR "lfs setquota " {\fB-U|--default-usr|-G|--default-grp|-P|--default-prj\fR}
+.TP
+Command sets the filesystem default limits for user,group,project quotas, \
+if set, users/groups/projects without specific quota setting will use \
+default quota limits automatically.
+.TP
+.B -U|--default-usr
+Set default user quota limit.
+.TP
+.B -G|--default-grp
+Set default group quota limit.
+.TP
+.B -P|--default-prj
+Set default project quota limit.
+.TP
+.PP
 .SH EXAMPLES
 .TP
-.B $ lfs setquota -u bob --block-softlimit 2000000 --block-hardlimit 1000000 /mnt/lustre
-Set quotas of user `bob': 1GB block quota hardlimit and 2 GB block quota softlimit
+.B $ lfs setquota -u bob --block-softlimit 2G --block-hardlimit 1G /mnt/lustre
+Set quotas of user `bob': 1GB block hardlimit and 2 GB block softlimit
+.TP
+.B $ lfs setquota -u bob -d /mnt/lustre
+Set quotas of user `bob' to use default quota setting
+.TP
+.B $ lfs setquota -U --block-softlimit 1G --block-hardlimit 2G /mnt/lustre
+Set system default user quota: 1 GB block softlimit and 2 GB block hardlimit
 .TP
 .B $ lfs setquota -t -u --block-grace 1000 --inode-grace 1w4d /mnt/lustre
 Set grace times for user quotas: 1000 seconds for block quotas, 1 week and 4 \
index 29cd139..4f8f96c 100644 (file)
@@ -107,6 +107,8 @@ lfs \- client utility for Lustre-specific file layout and other attributes
 .br
 .B lfs quotacheck [-ug] <filesystem>
 .br
+.B lfs quota <-U|-G|-P> <filesystem>
+.br
 .B lfs quotaon [-ugf] <filesystem>
 .br
 .B lfs quotaoff [-ug] <filesystem>
@@ -264,6 +266,9 @@ List all the MDTs
 .B $ lfs quota -u bob /mnt/lustre
 List quotas of user `bob'
 .TP
+.B $ lfs quota -U /mnt/lustre
+List user quotas of system default setting
+.TP
 .B $ lfs quota -t -u /mnt/lustre
 Show grace times for user quotas on /mnt/lustre
 .TP
index d5c3564..81b0df2 100644 (file)
@@ -830,6 +830,28 @@ static inline __u64 lustre_stoqb(size_t space)
 /* lustre-specific control commands */
 #define LUSTRE_Q_INVALIDATE  0x80000b     /* deprecated as of 2.4 */
 #define LUSTRE_Q_FINVALIDATE 0x80000c     /* deprecated as of 2.4 */
+#define LUSTRE_Q_GETDEFAULT  0x80000d     /* get default quota */
+#define LUSTRE_Q_SETDEFAULT  0x80000e     /* set default quota */
+
+/* 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.
+ * */
+#define LQUOTA_GRACE_BITS      48
+#define LQUOTA_GRACE_MASK      ((1ULL << LQUOTA_GRACE_BITS) - 1)
+#define LQUOTA_GRACE_MAX       LQUOTA_GRACE_MASK
+#define LQUOTA_GRACE(t)                (t & LQUOTA_GRACE_MASK)
+#define LQUOTA_FLAG(t)         (t >> LQUOTA_GRACE_BITS)
+#define LQUOTA_GRACE_FLAG(t, f)        ((__u64)t | (__u64)f << LQUOTA_GRACE_BITS)
+
+/* different quota flags */
+
+/* the default quota flag, the corresponding quota ID will use the default
+ * quota setting, the hardlimit and softlimit of its quota record in the global
+ * quota file will be set to 0, the low 48 bits of the grace will be set to 0
+ * and high 16 bits will contain this flag (see above comment).
+ * */
+#define LQUOTA_FLAG_DEFAULT    0x0001
 
 #define ALLQUOTA 255       /* set all quota */
 static inline char *qtype_name(int qtype)
index 44b1747..c6bdf98 100644 (file)
@@ -1009,23 +1009,25 @@ static int quotactl_ioctl(struct ll_sb_info *sbi, struct if_quotactl *qctl)
         int rc = 0;
         ENTRY;
 
-        switch (cmd) {
-        case Q_SETQUOTA:
-        case Q_SETINFO:
+       switch (cmd) {
+       case Q_SETQUOTA:
+       case Q_SETINFO:
+       case LUSTRE_Q_SETDEFAULT:
                if (!cfs_capable(CFS_CAP_SYS_ADMIN))
                        RETURN(-EPERM);
                break;
        case Q_GETQUOTA:
+       case LUSTRE_Q_GETDEFAULT:
                if (check_owner(type, id) &&
                    (!cfs_capable(CFS_CAP_SYS_ADMIN)))
                        RETURN(-EPERM);
-                break;
-        case Q_GETINFO:
-                break;
-        default:
-                CERROR("unsupported quotactl op: %#x\n", cmd);
-                RETURN(-ENOTTY);
-        }
+               break;
+       case Q_GETINFO:
+               break;
+       default:
+               CERROR("unsupported quotactl op: %#x\n", cmd);
+               RETURN(-ENOTSUPP);
+       }
 
         if (valid != QC_GENERAL) {
                 if (cmd == Q_GETINFO)
index 508aafa..86d6335 100644 (file)
@@ -2334,10 +2334,12 @@ static int mdt_quotactl(struct tgt_session_info *tsi)
                /* master quotactl */
        case Q_SETINFO:
        case Q_SETQUOTA:
+       case LUSTRE_Q_SETDEFAULT:
                if (!nodemap_can_setquota(nodemap))
                        GOTO(out_nodemap, rc = -EPERM);
        case Q_GETINFO:
        case Q_GETQUOTA:
+       case LUSTRE_Q_GETDEFAULT:
                if (qmt == NULL)
                        GOTO(out_nodemap, rc = -EOPNOTSUPP);
                /* slave quotactl */
@@ -2387,6 +2389,8 @@ static int mdt_quotactl(struct tgt_session_info *tsi)
        case Q_SETINFO:
        case Q_SETQUOTA:
        case Q_GETQUOTA:
+       case LUSTRE_Q_SETDEFAULT:
+       case LUSTRE_Q_GETDEFAULT:
                /* forward quotactl request to QMT */
                rc = qmt_hdls.qmth_quotactl(tsi->tsi_env, qmt, oqctl);
                break;
index 8332525..ab1eab0 100644 (file)
@@ -178,11 +178,12 @@ struct lquota_entry {
        } u;
 
        /* flags describing the state of the lquota_entry */
-       unsigned long   lqe_enforced:1,/* quota enforced or not */
-                       lqe_uptodate:1,/* successfully read from disk */
-                       lqe_edquot:1,  /* id out of quota space on QMT */
-                       lqe_gl:1,      /* glimpse is in progress */
-                       lqe_nopreacq:1;/* pre-acquire disabled */
+       unsigned long   lqe_enforced:1,   /* quota enforced or not */
+                       lqe_uptodate:1,   /* successfully read from disk */
+                       lqe_edquot:1,     /* id out of quota space on QMT */
+                       lqe_gl:1,         /* glimpse is in progress */
+                       lqe_nopreacq:1,   /* pre-acquire disabled */
+                       lqe_is_default:1; /* the default quota is used */
 };
 
 /* Compartment within which lquota_entry are unique.
index 615a29a..c6bd58f 100644 (file)
@@ -46,6 +46,55 @@ static void qmt_lqe_init(struct lquota_entry *lqe, void *arg)
        init_rwsem(&lqe->lqe_sem);
 }
 
+/* Apply the default quota setting to the specified quota entry
+ *
+ * \param env          - is the environment passed by the caller
+ * \param pool         - is the quota pool of the quota entry
+ * \param lqe          - is the lquota_entry object to apply default quota on
+ * \param create_record        - if true, an global quota record will be created and
+ *                        write to the disk.
+ *
+ * \retval 0           : success
+ * \retval -ve         : other appropriate errors
+ */
+int qmt_lqe_set_default(const struct lu_env *env, struct qmt_pool_info *pool,
+                       struct lquota_entry *lqe, bool create_record)
+{
+       struct lquota_entry     *lqe_def;
+       int                     rc = 0;
+
+       ENTRY;
+
+       if (lqe->lqe_id.qid_uid == 0)
+               RETURN(0);
+
+       lqe_def = pool->qpi_grace_lqe[lqe->lqe_site->lqs_qtype];
+
+       LQUOTA_DEBUG(lqe, "inherit default quota");
+
+       lqe->lqe_is_default = true;
+       lqe->lqe_hardlimit = lqe_def->lqe_hardlimit;
+       lqe->lqe_softlimit = lqe_def->lqe_softlimit;
+
+       if (create_record) {
+               lqe->lqe_uptodate = true;
+               rc = qmt_set_with_lqe(env, pool->qpi_qmt, lqe, 0, 0,
+                                     LQUOTA_GRACE_FLAG(0, LQUOTA_FLAG_DEFAULT),
+                                     QIF_TIMES, true, false);
+
+               if (rc != 0)
+                       LQUOTA_ERROR(lqe, "failed to create the global quota"
+                                    " record: %d", rc);
+       }
+
+       if (lqe->lqe_hardlimit == 0 && lqe->lqe_softlimit == 0)
+               lqe->lqe_enforced = false;
+       else
+               lqe->lqe_enforced = true;
+
+       RETURN(rc);
+}
+
 /*
  * Update a lquota entry. This is done by reading quota settings from the global
  * index. The lquota entry must be write locked.
@@ -70,22 +119,19 @@ static int qmt_lqe_read(const struct lu_env *env, struct lquota_entry *lqe,
 
        switch (rc) {
        case -ENOENT:
-               /* no such entry, assume quota isn't enforced for this user */
-               lqe->lqe_enforced = false;
+               qmt_lqe_set_default(env, pool, lqe, true);
                break;
        case 0:
                /* copy quota settings from on-disk record */
                lqe->lqe_granted   = qti->qti_glb_rec.qbr_granted;
                lqe->lqe_hardlimit = qti->qti_glb_rec.qbr_hardlimit;
                lqe->lqe_softlimit = qti->qti_glb_rec.qbr_softlimit;
-               lqe->lqe_gracetime = qti->qti_glb_rec.qbr_time;
-
-               if (lqe->lqe_hardlimit == 0 && lqe->lqe_softlimit == 0)
-                       /* {hard,soft}limit=0 means no quota enforced */
-                       lqe->lqe_enforced = false;
-               else
-                       lqe->lqe_enforced  = true;
+               lqe->lqe_gracetime = LQUOTA_GRACE(qti->qti_glb_rec.qbr_time);
 
+               if (lqe->lqe_hardlimit == 0 && lqe->lqe_softlimit == 0 &&
+                   (LQUOTA_FLAG(qti->qti_glb_rec.qbr_time) &
+                    LQUOTA_FLAG_DEFAULT))
+                       qmt_lqe_set_default(env, pool, lqe, false);
                break;
        default:
                LQUOTA_ERROR(lqe, "failed to read quota entry from disk, rc:%d",
@@ -93,6 +139,13 @@ static int qmt_lqe_read(const struct lu_env *env, struct lquota_entry *lqe,
                RETURN(rc);
        }
 
+       if (lqe->lqe_id.qid_uid == 0 ||
+           (lqe->lqe_hardlimit == 0 && lqe->lqe_softlimit == 0))
+               /* {hard,soft}limit=0 means no quota enforced */
+               lqe->lqe_enforced = false;
+       else
+               lqe->lqe_enforced  = true;
+
        LQUOTA_DEBUG(lqe, "read");
        RETURN(0);
 }
@@ -113,8 +166,8 @@ static void qmt_lqe_debug(struct lquota_entry *lqe, void *arg,
 
        libcfs_debug_vmsg2(msgdata, fmt, args,
                           "qmt:%s pool:%d-%s id:%llu enforced:%d hard:%llu"
-                          " soft:%llu granted:%llu time:%llu qunit:"
-                          "%llu edquot:%d may_rel:%llu revoke:%lld\n",
+                          " soft:%llu granted:%llu time:%llu qunit: %llu"
+                          " edquot:%d may_rel:%llu revoke:%lld default:%s\n",
                           pool->qpi_qmt->qmt_svname,
                           pool->qpi_key & 0x0000ffff,
                           RES_NAME(pool->qpi_key >> 16),
@@ -122,7 +175,8 @@ static void qmt_lqe_debug(struct lquota_entry *lqe, void *arg,
                           lqe->lqe_hardlimit, lqe->lqe_softlimit,
                           lqe->lqe_granted, lqe->lqe_gracetime,
                           lqe->lqe_qunit, lqe->lqe_edquot, lqe->lqe_may_rel,
-                          lqe->lqe_revoke_time);
+                          lqe->lqe_revoke_time,
+                          lqe->lqe_is_default ? "yes" : "no");
 }
 
 /*
@@ -264,9 +318,15 @@ int qmt_glb_write(const struct lu_env *env, struct thandle *th,
 
        /* fill global index with updated quota settings */
        rec->qbr_granted   = lqe->lqe_granted;
-       rec->qbr_hardlimit = lqe->lqe_hardlimit;
-       rec->qbr_softlimit = lqe->lqe_softlimit;
-       rec->qbr_time      = lqe->lqe_gracetime;
+       if (lqe->lqe_is_default) {
+               rec->qbr_hardlimit = 0;
+               rec->qbr_softlimit = 0;
+               rec->qbr_time      = LQUOTA_GRACE_FLAG(0, LQUOTA_FLAG_DEFAULT);
+       } else {
+               rec->qbr_hardlimit = lqe->lqe_hardlimit;
+               rec->qbr_softlimit = lqe->lqe_softlimit;
+               rec->qbr_time      = lqe->lqe_gracetime;
+       }
 
        /* write new quota settings */
        rc = lquota_disk_write(env, th, LQE_GLB_OBJ(lqe), &lqe->lqe_id,
index a75909f..4621682 100644 (file)
  */
 static int qmt_get(const struct lu_env *env, struct qmt_device *qmt,
                   __u16 pool_id, __u8 restype, __u8 qtype, union lquota_id *id,
-                  __u64 *hard, __u64 *soft, __u64 *time)
+                  __u64 *hard, __u64 *soft, __u64 *time, bool is_default)
 {
        struct lquota_entry     *lqe;
        ENTRY;
 
+       LASSERT(!is_default || id->qid_uid == 0);
+
        /* look-up lqe structure containing quota settings */
        lqe = qmt_pool_lqe_lookup(env, qmt, pool_id, restype, qtype, id);
        if (IS_ERR(lqe))
@@ -67,37 +69,59 @@ static int qmt_get(const struct lu_env *env, struct qmt_device *qmt,
                *hard = lqe->lqe_hardlimit;
        if (soft != NULL)
                *soft = lqe->lqe_softlimit;
-       if (time != NULL)
+       if (time != NULL) {
                *time = lqe->lqe_gracetime;
+               if (lqe->lqe_is_default)
+                       *time |= (__u64)LQUOTA_FLAG_DEFAULT <<
+                                                       LQUOTA_GRACE_BITS;
+       }
        lqe_read_unlock(lqe);
 
        lqe_putref(lqe);
        RETURN(0);
 }
 
+struct qmt_entry_iter_data {
+       const struct lu_env *qeid_env;
+       struct qmt_device   *qeid_qmt;
+};
+
+static int qmt_entry_iter_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+                            struct hlist_node *hnode, void *d)
+{
+       struct qmt_entry_iter_data *iter = (struct qmt_entry_iter_data *)d;
+       struct lquota_entry     *lqe;
+
+       lqe = hlist_entry(hnode, struct lquota_entry, lqe_hash);
+       LASSERT(atomic_read(&lqe->lqe_ref) > 0);
+
+       if (lqe->lqe_id.qid_uid == 0 || !lqe->lqe_is_default)
+               return 0;
+
+       return qmt_set_with_lqe(iter->qeid_env, iter->qeid_qmt, lqe, 0, 0, 0, 0,
+                               true, true);
+}
+
 /*
- * Update quota settings for a given identifier.
+ * Update quota settings for a given lqe.
  *
- * \param env     - is the environment passed by the caller
- * \param qmt     - is the quota master target
- * \param pool_id - is the 16-bit pool identifier
- * \param restype - is the pool type, either block (i.e. LQUOTA_RES_DT) or inode
- *                  (i.e. LQUOTA_RES_MD)
- * \param qtype   - is the quota type
- * \param id      - is the quota indentifier for which we want to modify quota
- *                  settings.
- * \param hard    - is the new hard limit
- * \param soft    - is the new soft limit
- * \param time    - is the new grace time
- * \param valid   - is the list of settings to change
+ * \param env        - is the environment passed by the caller
+ * \param qmt        - is the quota master target
+ * \param lqe        - is the lquota_entry for which we want to modify quota
+ *                     settings.
+ * \param hard       - is the new hard limit
+ * \param soft       - is the new soft limit
+ * \param time       - is the new grace time
+ * \param valid      - is the list of settings to change
+ * \param is_default - true for default quota setting
+ * \param is_updated - true if the lqe is updated and no need to write back
  */
-static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
-                  __u16 pool_id, __u8 restype, __u8 qtype,
-                  union lquota_id *id, __u64 hard, __u64 soft, __u64 time,
-                  __u32 valid)
+
+int qmt_set_with_lqe(const struct lu_env *env, struct qmt_device *qmt,
+                    struct lquota_entry *lqe, __u64 hard, __u64 soft,
+                    __u64 time, __u32 valid, bool is_default, bool is_updated)
 {
        struct qmt_thread_info  *qti = qmt_info(env);
-       struct lquota_entry     *lqe;
        struct thandle          *th = NULL;
        time64_t now;
        __u64                    ver;
@@ -105,16 +129,14 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
        int                      rc = 0;
        ENTRY;
 
-       /* look-up quota entry associated with this ID */
-       lqe = qmt_pool_lqe_lookup(env, qmt, pool_id, restype, qtype, id);
-       if (IS_ERR(lqe))
-               RETURN(PTR_ERR(lqe));
-
-       /* allocate & start transaction with enough credits to update quota
-        * settings in the global index file */
-       th = qmt_trans_start(env, lqe, &qti->qti_restore);
-       if (IS_ERR(th))
-               GOTO(out_nolock, rc = PTR_ERR(th));
+       /* need to write back to global quota file? */
+       if (!is_updated) {
+               /* allocate & start transaction with enough credits to update
+                * quota  settings in the global index file */
+               th = qmt_trans_start(env, lqe, &qti->qti_restore);
+               if (IS_ERR(th))
+                       GOTO(out_nolock, rc = PTR_ERR(th));
+       }
 
        now = ktime_get_real_seconds();
 
@@ -122,6 +144,14 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
        LQUOTA_DEBUG(lqe, "changing quota settings valid:%x hard:%llu soft:"
                     "%llu time:%llu", valid, hard, soft, time);
 
+       if (is_default && lqe->lqe_id.qid_uid != 0) {
+               LQUOTA_DEBUG(lqe, "set qid %llu to use default quota setting",
+                            lqe->lqe_id.qid_uid);
+
+               qmt_lqe_set_default(env, lqe->lqe_site->lqs_parent, lqe, false);
+               GOTO(quota_set, 0);
+       }
+
        if ((valid & QIF_TIMES) != 0 && lqe->lqe_gracetime != time) {
                /* change time settings */
                lqe->lqe_gracetime = time;
@@ -134,13 +164,14 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
                if (rc)
                        GOTO(out, rc);
 
-               /* recompute qunit in case it was never initialized */
-               qmt_revalidate(env, lqe);
-
                /* change quota limits */
                lqe->lqe_hardlimit = hard;
                lqe->lqe_softlimit = soft;
 
+quota_set:
+               /* recompute qunit in case it was never initialized */
+               qmt_revalidate(env, lqe);
+
                /* clear grace time */
                if (lqe->lqe_softlimit == 0 ||
                    lqe->lqe_granted <= lqe->lqe_softlimit)
@@ -152,7 +183,8 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
                         lqe->lqe_gracetime = now + qmt_lqe_grace(lqe);
 
                /* change enforced status based on new parameters */
-               if (lqe->lqe_hardlimit == 0 && lqe->lqe_softlimit == 0)
+               if (lqe->lqe_id.qid_uid == 0 || (lqe->lqe_hardlimit == 0 &&
+                   lqe->lqe_softlimit == 0))
                        lqe->lqe_enforced = false;
                else
                        lqe->lqe_enforced = true;
@@ -161,12 +193,22 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
        }
 
        if (dirtied) {
-               /* write new quota settings to disk */
-               rc = qmt_glb_write(env, th, lqe, LQUOTA_BUMP_VER, &ver);
-               if (rc) {
-                       /* restore initial quota settings */
-                       qmt_restore(lqe, &qti->qti_restore);
-                       GOTO(out, rc);
+               if (!is_default && lqe->lqe_is_default) {
+                       LQUOTA_DEBUG(lqe, "the qid %llu has been set quota"
+                                    " explicitly, clear the default flag",
+                                    lqe->lqe_id.qid_uid);
+
+                       qmt_lqe_clear_default(lqe);
+               }
+
+               if (!is_updated) {
+                       /* write new quota settings to disk */
+                       rc = qmt_glb_write(env, th, lqe, LQUOTA_BUMP_VER, &ver);
+                       if (rc) {
+                               /* restore initial quota settings */
+                               qmt_restore(lqe, &qti->qti_restore);
+                               GOTO(out, rc);
+                       }
                }
 
                /* compute new qunit value now that we have modified the quota
@@ -179,19 +221,67 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
        EXIT;
 out:
        lqe_write_unlock(lqe);
-out_nolock:
-       lqe_putref(lqe);
 
+out_nolock:
        if (th != NULL && !IS_ERR(th))
                dt_trans_stop(env, qmt->qmt_child, th);
 
-       if (rc == 0 && dirtied)
+       if (rc == 0 && dirtied) {
                qmt_glb_lock_notify(env, lqe, ver);
+               if (lqe->lqe_id.qid_uid == 0) {
+                       struct qmt_entry_iter_data iter_data;
+
+                       LQUOTA_DEBUG(lqe, "notify all lqe with default quota");
+                       iter_data.qeid_env = env;
+                       iter_data.qeid_qmt = qmt;
+                       cfs_hash_for_each_safe(lqe->lqe_site->lqs_hash,
+                                              qmt_entry_iter_cb, &iter_data);
+               }
+       }
 
        return rc;
 }
 
 /*
+ * Update quota settings for a given identifier.
+ *
+ * \param env        - is the environment passed by the caller
+ * \param qmt        - is the quota master target
+ * \param pool_id    - is the 16-bit pool identifier
+ * \param restype    - is the pool type, either block (i.e. LQUOTA_RES_DT) or
+ *                     inode (i.e. LQUOTA_RES_MD)
+ * \param qtype      - is the quota type
+ * \param id         - is the quota indentifier for which we want to modify
+ *                     quota settings.
+ * \param hard       - is the new hard limit
+ * \param soft       - is the new soft limit
+ * \param time       - is the new grace time
+ * \param valid      - is the list of settings to change
+ * \param is_default - true for default quota setting
+ * \param is_updated - true if the lqe is updated and no need to write back
+ */
+static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
+                  __u16 pool_id, __u8 restype, __u8 qtype,
+                  union lquota_id *id, __u64 hard, __u64 soft, __u64 time,
+                  __u32 valid, bool is_default, bool is_updated)
+{
+       struct lquota_entry *lqe;
+       int rc;
+       ENTRY;
+
+       /* look-up quota entry associated with this ID */
+       lqe = qmt_pool_lqe_lookup(env, qmt, pool_id, restype, qtype, id);
+       if (IS_ERR(lqe))
+                       RETURN(PTR_ERR(lqe));
+
+       rc = qmt_set_with_lqe(env, qmt, lqe, hard, soft, time, valid,
+                             is_default, is_updated);
+
+       lqe_putref(lqe);
+       RETURN(rc);
+}
+
+/*
  * Handle quotactl request.
  *
  * \param env   - is the environment passed by the caller
@@ -206,6 +296,7 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
        struct qmt_device       *qmt = lu2qmt_dev(ld);
        struct obd_dqblk        *dqb = &oqctl->qc_dqblk;
        int                      rc = 0;
+       bool                     is_default = false;
        ENTRY;
 
        LASSERT(qmt != NULL);
@@ -222,13 +313,13 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
 
                /* read inode grace time */
                rc = qmt_get(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type, id,
-                            NULL, NULL, &oqctl->qc_dqinfo.dqi_igrace);
+                            NULL, NULL, &oqctl->qc_dqinfo.dqi_igrace, false);
                if (rc)
                        break;
 
                /* read block grace time */
                rc = qmt_get(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type, id,
-                            NULL, NULL, &oqctl->qc_dqinfo.dqi_bgrace);
+                            NULL, NULL, &oqctl->qc_dqinfo.dqi_bgrace, false);
                break;
 
        case Q_SETINFO:  /* modify grace times */
@@ -243,7 +334,7 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
                        /* set inode grace time */
                        rc = qmt_set(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type,
                                     id, 0, 0, oqctl->qc_dqinfo.dqi_igrace,
-                                    QIF_TIMES);
+                                    QIF_TIMES, false, false);
                        if (rc)
                                break;
                }
@@ -252,23 +343,20 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
                        /* set block grace time */
                        rc = qmt_set(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type,
                                     id, 0, 0, oqctl->qc_dqinfo.dqi_bgrace,
-                                    QIF_TIMES);
+                                    QIF_TIMES, false, false);
                break;
 
+       case LUSTRE_Q_GETDEFAULT:
+               is_default = true;
+
        case Q_GETQUOTA: /* consult quota limit */
-               /* There is no quota limit for root user & group */
-               if (oqctl->qc_id == 0) {
-                       memset(dqb, 0, sizeof(*dqb));
-                       dqb->dqb_valid = QIF_LIMITS | QIF_TIMES;
-                       break;
-               }
                /* extract quota ID from quotactl request */
                id->qid_uid = oqctl->qc_id;
 
                /* look-up inode quota settings */
                rc = qmt_get(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type, id,
                             &dqb->dqb_ihardlimit, &dqb->dqb_isoftlimit,
-                            &dqb->dqb_itime);
+                            &dqb->dqb_itime, is_default);
                if (rc)
                        break;
 
@@ -279,7 +367,7 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
                /* look-up block quota settings */
                rc = qmt_get(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type, id,
                             &dqb->dqb_bhardlimit, &dqb->dqb_bsoftlimit,
-                            &dqb->dqb_btime);
+                            &dqb->dqb_btime, is_default);
                if (rc)
                        break;
 
@@ -288,10 +376,10 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
                dqb->dqb_curspace = 0;
                break;
 
+       case LUSTRE_Q_SETDEFAULT:
+               is_default = true;
+
        case Q_SETQUOTA: /* change quota limits */
-               if (oqctl->qc_id == 0)
-                       /* can't enforce a quota limit for root user & group */
-                       RETURN(-EPERM);
                /* extract quota ID from quotactl request */
                id->qid_uid = oqctl->qc_id;
 
@@ -300,7 +388,8 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
                        rc = qmt_set(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type,
                                     id, dqb->dqb_ihardlimit,
                                     dqb->dqb_isoftlimit, dqb->dqb_itime,
-                                    dqb->dqb_valid & QIF_IFLAGS);
+                                    dqb->dqb_valid & QIF_IFLAGS, is_default,
+                                    false);
                        if (rc)
                                break;
                }
@@ -310,7 +399,8 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
                        rc = qmt_set(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type,
                                     id, dqb->dqb_bhardlimit,
                                     dqb->dqb_bsoftlimit, dqb->dqb_btime,
-                                    dqb->dqb_valid & QIF_BFLAGS);
+                                    dqb->dqb_valid & QIF_BFLAGS, is_default,
+                                    false);
                break;
 
        default:
index 3a3e597..1ac3367 100644 (file)
@@ -290,6 +290,14 @@ static inline bool qmt_space_exhausted(struct lquota_entry *lqe, __u64 now)
        return (qmt_hard_exhausted(lqe) || qmt_soft_exhausted(lqe, now));
 }
 
+/* helper routine clearing the default quota setting  */
+static inline void qmt_lqe_clear_default(struct lquota_entry *lqe)
+{
+       lqe->lqe_is_default = false;
+       lqe->lqe_gracetime &= ~((__u64)LQUOTA_FLAG_DEFAULT <<
+                                                       LQUOTA_GRACE_BITS);
+}
+
 /* number of seconds to wait for slaves to release quota space after
  * rebalancing */
 #define QMT_REBA_TIMEOUT 2
@@ -307,6 +315,8 @@ struct lquota_entry *qmt_pool_lqe_lookup(const struct lu_env *,
                                         union lquota_id *);
 /* qmt_entry.c */
 extern struct lquota_entry_operations qmt_lqe_ops;
+int qmt_lqe_set_default(const struct lu_env *env, struct qmt_pool_info *pool,
+                       struct lquota_entry *lqe, bool create_record);
 struct thandle *qmt_trans_start_with_slv(const struct lu_env *,
                                         struct lquota_entry *,
                                         struct dt_object *,
@@ -327,6 +337,9 @@ void qmt_revalidate(const struct lu_env *, struct lquota_entry *);
 __u64 qmt_alloc_expand(struct lquota_entry *, __u64, __u64);
 
 /* qmt_handler.c */
+int qmt_set_with_lqe(const struct lu_env *env, struct qmt_device *qmt,
+                    struct lquota_entry *lqe, __u64 hard, __u64 soft,
+                    __u64 time, __u32 valid, bool is_default, bool is_updated);
 int qmt_dqacq0(const struct lu_env *, struct lquota_entry *,
               struct qmt_device *, struct obd_uuid *, __u32, __u64, __u64,
               struct quota_body *);
index 0437709..4a8aa81 100644 (file)
@@ -631,9 +631,17 @@ void qmt_glb_lock_notify(const struct lu_env *env, struct lquota_entry *lqe,
        /* send glimpse callback to notify slaves of new quota settings */
        qti->qti_gl_desc.lquota_desc.gl_id        = lqe->lqe_id;
        qti->qti_gl_desc.lquota_desc.gl_flags     = 0;
-       qti->qti_gl_desc.lquota_desc.gl_hardlimit = lqe->lqe_hardlimit;
-       qti->qti_gl_desc.lquota_desc.gl_softlimit = lqe->lqe_softlimit;
-       qti->qti_gl_desc.lquota_desc.gl_time      = lqe->lqe_gracetime;
+       if (lqe->lqe_is_default) {
+               qti->qti_gl_desc.lquota_desc.gl_hardlimit = 0;
+               qti->qti_gl_desc.lquota_desc.gl_softlimit = 0;
+               qti->qti_gl_desc.lquota_desc.gl_time = LQUOTA_GRACE_FLAG(0,
+                                                       LQUOTA_FLAG_DEFAULT);
+
+       } else {
+               qti->qti_gl_desc.lquota_desc.gl_hardlimit = lqe->lqe_hardlimit;
+               qti->qti_gl_desc.lquota_desc.gl_softlimit = lqe->lqe_softlimit;
+               qti->qti_gl_desc.lquota_desc.gl_time = lqe->lqe_gracetime;
+       }
        qti->qti_gl_desc.lquota_desc.gl_ver       = ver;
 
        /* look up ldlm resource associated with global index */
index e6fbafe..40519a4 100644 (file)
@@ -81,12 +81,21 @@ static int qsd_lqe_read(const struct lu_env *env, struct lquota_entry *lqe,
                lqe->lqe_enforced = false;
                break;
        case 0:
-               if (qti->qti_glb_rec.qbr_hardlimit == 0 &&
-                   qti->qti_glb_rec.qbr_softlimit == 0)
-                       /* quota isn't enforced for this use */
-                       lqe->lqe_enforced = false;
-               else
+               if (lqe->lqe_id.qid_uid == 0) {
+                       qqi->qqi_default_hardlimit =
+                                               qti->qti_glb_rec.qbr_hardlimit;
+                       qqi->qqi_default_softlimit =
+                                               qti->qti_glb_rec.qbr_softlimit;
+                       qqi->qqi_default_gracetime =
+                                               qti->qti_glb_rec.qbr_granted;
+               }
+
+               if (lqe->lqe_id.qid_uid != 0 &&
+                   (qti->qti_glb_rec.qbr_hardlimit != 0 ||
+                    qti->qti_glb_rec.qbr_softlimit != 0))
                        lqe->lqe_enforced = true;
+               else
+                       lqe->lqe_enforced = false;
                break;
        default:
                LQUOTA_ERROR(lqe, "failed to read quota entry from global "
@@ -94,6 +103,29 @@ static int qsd_lqe_read(const struct lu_env *env, struct lquota_entry *lqe,
                return rc;
        }
 
+       if (lqe->lqe_id.qid_uid != 0 &&
+           (rc == -ENOENT ||
+            (LQUOTA_FLAG(qti->qti_glb_rec.qbr_time) & LQUOTA_FLAG_DEFAULT &&
+             qti->qti_glb_rec.qbr_hardlimit == 0 &&
+             qti->qti_glb_rec.qbr_softlimit == 0))) {
+               struct lquota_entry *lqe_def;
+               union lquota_id qid = { {0} };
+
+               /* ensure the lqe storing the default quota setting loaded */
+               lqe_def = lqe_locate(env, qqi->qqi_site, &qid);
+
+               lqe->lqe_is_default = true;
+
+               if (qqi->qqi_default_hardlimit != 0 ||
+                   qqi->qqi_default_softlimit != 0) {
+                       LQUOTA_DEBUG(lqe, "enforced by default quota");
+                       lqe->lqe_enforced = true;
+               }
+
+               if (lqe_def != NULL)
+                       lqe_putref(lqe_def);
+       }
+
        /* read record from slave index copy to find out how much space is
         * currently owned by this slave */
        rc = lquota_disk_read(env, qqi->qqi_slv_obj, &lqe->lqe_id,
@@ -138,15 +170,15 @@ static void qsd_lqe_debug(struct lquota_entry *lqe, void *arg,
        struct qsd_qtype_info   *qqi = (struct qsd_qtype_info *)arg;
 
        libcfs_debug_vmsg2(msgdata, fmt, args,
-                          "qsd:%s qtype:%s id:%llu enforced:%d granted:"
-                          "%llu pending:%llu waiting:%llu req:%d usage:"
-                          "%llu qunit:%llu qtune:%llu edquot:%d\n",
+                          "qsd:%s qtype:%s id:%llu enforced:%d granted: %llu"
+                          " pending:%llu waiting:%llu req:%d usage: %llu"
+                          " qunit:%llu qtune:%llu edquot:%d default:%s\n",
                           qqi->qqi_qsd->qsd_svname, qtype_name(qqi->qqi_qtype),
                           lqe->lqe_id.qid_uid, lqe->lqe_enforced,
                           lqe->lqe_granted, lqe->lqe_pending_write,
                           lqe->lqe_waiting_write, lqe->lqe_pending_req,
                           lqe->lqe_usage, lqe->lqe_qunit, lqe->lqe_qtune,
-                          lqe->lqe_edquot);
+                          lqe->lqe_edquot, lqe->lqe_is_default ? "yes" : "no");
 }
 
 /*
@@ -318,9 +350,22 @@ int qsd_update_lqe(const struct lu_env *env, struct lquota_entry *lqe,
        if (global) {
                struct lquota_glb_rec *glb_rec = (struct lquota_glb_rec *)rec;
 
+               /* doesn't change quota enforcement if the quota entry is still
+                * using default quota. */
+               if (LQUOTA_FLAG(glb_rec->qbr_time) & LQUOTA_FLAG_DEFAULT &&
+                   glb_rec->qbr_hardlimit == 0 && glb_rec->qbr_softlimit == 0)
+                       RETURN(0);
+
+               LQUOTA_DEBUG(lqe, "the ID has been set quota, so clear the"
+                            " default quota flag");
+               lqe->lqe_is_default = false;
+
                /* change enforcement status based on new hard/soft limit */
-               lqe->lqe_enforced = (glb_rec->qbr_hardlimit ||
-                                    glb_rec->qbr_softlimit) ? true : false;
+               if (lqe->lqe_id.qid_uid != 0 && (glb_rec->qbr_hardlimit != 0 ||
+                   glb_rec->qbr_softlimit != 0))
+                       lqe->lqe_enforced = true;
+               else
+                       lqe->lqe_enforced = false;
 
                LQUOTA_DEBUG(lqe, "updating global index hardlimit: %llu, "
                             "softlimit: %llu", glb_rec->qbr_hardlimit,
index 1ffed37..a4f6734 100644 (file)
@@ -176,7 +176,12 @@ struct qsd_qtype_info {
                                qqi_acct_failed:1; /* failed to setup acct */
 
        /* A list of references to this instance, for debugging */
-       struct lu_ref            qqi_reference;
+       struct lu_ref           qqi_reference;
+
+       /* default quota setting*/
+       __u64                   qqi_default_hardlimit;
+       __u64                   qqi_default_softlimit;
+       __u64                   qqi_default_gracetime;
 };
 
 /*
@@ -349,6 +354,8 @@ int qsd_write_version(const struct lu_env *, struct qsd_qtype_info *,
 /* qsd_lock.c */
 extern struct ldlm_enqueue_info qsd_glb_einfo;
 extern struct ldlm_enqueue_info qsd_id_einfo;
+void qsd_update_default_quota(struct qsd_qtype_info *qqi, __u64 hardlimit,
+                             __u64 softlimit, __u64 gracetime);
 int qsd_id_lock_match(struct lustre_handle *, struct lustre_handle *);
 int qsd_id_lock_cancel(const struct lu_env *, struct lquota_entry *);
 
index 7b962e1..b0e2402 100644 (file)
@@ -231,6 +231,49 @@ static int qsd_glb_blocking_ast(struct ldlm_lock *lock,
        RETURN(rc);
 }
 
+static int qsd_entry_def_iter_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+                                struct hlist_node *hnode, void *data)
+{
+       struct qsd_qtype_info   *qqi = (struct qsd_qtype_info *)data;
+       struct lquota_entry     *lqe;
+
+       lqe = hlist_entry(hnode, struct lquota_entry, lqe_hash);
+       LASSERT(atomic_read(&lqe->lqe_ref) > 0);
+
+       if (lqe->lqe_id.qid_uid == 0 || !lqe->lqe_is_default)
+               return 0;
+
+       lqe_write_lock(lqe);
+       if (qqi->qqi_default_hardlimit == 0 && qqi->qqi_default_softlimit == 0)
+               lqe->lqe_enforced = false;
+       else
+               lqe->lqe_enforced = true;
+       lqe_write_unlock(lqe);
+
+       return 0;
+}
+
+/* Update the quota entries after receiving default quota update
+ *
+ * \param qqi       - is the qsd_qtype_info associated with the quota entries
+ * \param hardlimit - new hardlimit of default quota
+ * \param softlimit - new softlimit of default quota
+ * \param gracetime - new gracetime of default quota
+ */
+void qsd_update_default_quota(struct qsd_qtype_info *qqi, __u64 hardlimit,
+                             __u64 softlimit, __u64 gracetime)
+{
+       CDEBUG(D_QUOTA, "%s: update default quota setting from QMT.\n",
+              qqi->qqi_qsd->qsd_svname);
+
+       qqi->qqi_default_hardlimit = hardlimit;
+       qqi->qqi_default_softlimit = softlimit;
+       qqi->qqi_default_gracetime = gracetime;
+
+       cfs_hash_for_each_safe(qqi->qqi_site->lqs_hash,
+                              qsd_entry_def_iter_cb, qqi);
+}
+
 /*
  * Glimpse callback handler for global quota lock.
  *
@@ -273,6 +316,10 @@ static int qsd_glb_glimpse_ast(struct ldlm_lock *lock, void *data)
        rec.qbr_time      = desc->gl_time;
        rec.qbr_granted   = 0;
 
+       if (desc->gl_id.qid_uid == 0)
+               qsd_update_default_quota(qqi, desc->gl_hardlimit,
+                                        desc->gl_softlimit, desc->gl_time);
+
        /* We can't afford disk io in the context of glimpse callback handling
         * thread, so the on-disk global limits update has to be deferred. */
        qsd_upd_schedule(qqi, NULL, &desc->gl_id, (union lquota_rec *)&rec,
index c2fb89b..a74d9f5 100644 (file)
@@ -89,6 +89,14 @@ static int qsd_reint_qid(const struct lu_env *env, struct qsd_qtype_info *qqi,
 
        rc = qsd_update_index(env, qqi, qid, global, 0, rec);
 out:
+
+       if (global && qid->qid_uid == 0) {
+               struct lquota_glb_rec *glb_rec = (struct lquota_glb_rec *)rec;
+               qsd_update_default_quota(qqi, glb_rec->qbr_hardlimit,
+                                        glb_rec->qbr_softlimit,
+                                        glb_rec->qbr_time);
+       }
+
        lqe_putref(lqe);
        RETURN(rc);
 }
index 51a985c..4729c69 100644 (file)
@@ -307,6 +307,21 @@ static int qsd_process_upd(const struct lu_env *env, struct qsd_upd_rec *upd)
        rc = qsd_update_index(env, qqi, &upd->qur_qid, upd->qur_global,
                              upd->qur_ver, &upd->qur_rec);
 out:
+       if (upd->qur_global && rc == 0 &&
+           upd->qur_rec.lqr_glb_rec.qbr_softlimit == 0 &&
+           upd->qur_rec.lqr_glb_rec.qbr_hardlimit == 0 &&
+           (LQUOTA_FLAG(upd->qur_rec.lqr_glb_rec.qbr_time) &
+                                                       LQUOTA_FLAG_DEFAULT)) {
+               lqe->lqe_is_default = true;
+               if (qqi->qqi_default_softlimit == 0 &&
+                   qqi->qqi_default_hardlimit == 0)
+                       lqe->lqe_enforced = false;
+               else
+                       lqe->lqe_enforced = true;
+
+               LQUOTA_DEBUG(lqe, "update to use default quota");
+       }
+
        if (lqe && !IS_ERR(lqe)) {
                lqe_putref(lqe);
                upd->qur_lqe = NULL;
index be624ea..814fff7 100755 (executable)
@@ -3087,10 +3087,182 @@ test_60() {
                error "root user should succeed"
 
        cleanup_quota_test
-       resetquota -u $TSTUSR
+       resetquota -g $TSTUSR
 }
 run_test 60 "Test quota for root with setgid"
 
+# test default quota
+test_default_quota() {
+       [ $(lustre_version_code $SINGLEMDS) -lt $(version_code 2.11.51) ] &&
+               skip "Not supported before 2.11.51." && return
+
+       local qtype=$1
+       local qpool=$2
+       local qid=$TSTUSR
+       local qprjid=$TSTPRJID
+       local qdtype="-U"
+       local qs="-b"
+       local qh="-B"
+       local LIMIT=102400 #100M disk space
+       local TESTFILE="$DIR/$tdir/$tfile-0"
+
+       [ $qtype == "-p" ] && ! is_project_quota_supported &&
+               echo "Project quota is not supported" && return 0
+
+       [ $qtype == "-u" ] && qdtype="-U"
+       [ $qtype == "-g" ] && qdtype="-G"
+       [ $qtype == "-p" ] && {
+               qdtype="-P"
+               qid=$qprjid
+       }
+
+       [ $qpool == "meta" ] && {
+               LIMIT=10240 #10K inodes
+               qs="-i"
+               qh="-I"
+       }
+
+       setup_quota_test || error "setup quota failed with $?"
+       trap cleanup_quota_test EXIT
+
+       quota_init
+
+       # enable mdt/ost quota
+       set_mdt_qtype $QTYPE || error "enable mdt quota failed"
+       set_ost_qtype $QTYPE || error "enable ost quota failed"
+
+       log "set to use default quota"
+       $LFS setquota $qtype $qid -d $DIR ||
+               error "set $qid to use default quota failed"
+
+       log "set default quota"
+       $LFS setquota $qdtype $qs ${LIMIT} $qh ${LIMIT} $DIR ||
+               error "set $qid default quota failed"
+
+       log "get default quota"
+       $LFS quota $qdtype $DIR || error "get default quota failed"
+
+       if [ $qpool == "data" ]; then
+               local SLIMIT=$($LFS quota $qdtype $DIR | grep "$MOUNT" | \
+                                                       awk '{print $2}')
+               [ $SLIMIT -eq $LIMIT ] ||
+                       error "the returned default quota is wrong"
+       else
+               local SLIMIT=$($LFS quota $qdtype $DIR | grep "$MOUNT" | \
+                                                       awk '{print $5}')
+               [ $SLIMIT -eq $LIMIT ] ||
+                       error "the returned default quota is wrong"
+       fi
+
+       # make sure the system is clean
+       local USED=$(getquota $qtype $qid global curspace)
+       [ $USED -ne 0 ] && error "Used space for $qid isn't 0."
+
+       $SETSTRIPE $TESTFILE -c 1 || error "setstripe $TESTFILE failed"
+       chown $TSTUSR.$TSTUSR $TESTFILE || error "chown $TESTFILE failed"
+
+       [ $qtype == "-p" ] && change_project -sp $TSTPRJID $DIR/$tdir
+
+       log "Test not out of quota"
+       if [ $qpool == "data" ]; then
+               $RUNAS $DD of=$TESTFILE count=$((LIMIT/2 >> 10)) ||
+                       quota_error $qtype $qid "write failed, expect succeed"
+       else
+               $RUNAS createmany -m $TESTFILE $((LIMIT/2)) ||
+                       quota_error $qtype $qid "create failed, expect succeed"
+
+               unlinkmany $TESTFILE $((LIMIT/2))
+       fi
+
+       log "Test out of quota"
+       # flush cache, ensure noquota flag is set on client
+       cancel_lru_locks osc
+       cancel_lru_locks mdc
+       sync; sync_all_data || true
+       if [ $qpool == "data" ]; then
+               $RUNAS $DD of=$TESTFILE count=$((LIMIT*2 >> 10)) &&
+                       quota_error $qtype $qid "write succeed, expect EDQUOT"
+       else
+               $RUNAS createmany -m $TESTFILE $((LIMIT*2)) &&
+                       quota_error $qtype $qid "create succeed, expect EDQUOT"
+
+               unlinkmany $TESTFILE $((LIMIT*2))
+       fi
+
+       log "Increase default quota"
+       # increase default quota
+       $LFS setquota $qdtype $qs $((LIMIT*3)) $qh $((LIMIT*3)) $DIR ||
+               error "set default quota failed"
+
+       cancel_lru_locks osc
+       cancel_lru_locks mdc
+       sync; sync_all_data || true
+       if [ $qpool == "data" ]; then
+               $RUNAS $DD of=$TESTFILE count=$((LIMIT*2 >> 10)) ||
+                       quota_error $qtype $qid "write failed, expect succeed"
+       else
+               $RUNAS createmany -m $TESTFILE $((LIMIT*2)) ||
+                       quota_error $qtype $qid "create failed, expect succeed"
+
+               unlinkmany $TESTFILE $((LIMIT*2))
+       fi
+
+       log "Set quota to override default quota"
+       $LFS setquota $qtype $qid $qs ${LIMIT} $qh ${LIMIT} $DIR ||
+               error "set $qid quota failed"
+
+       cancel_lru_locks osc
+       cancel_lru_locks mdc
+       sync; sync_all_data || true
+       if [ $qpool == "data" ]; then
+               $RUNAS $DD of=$TESTFILE count=$((LIMIT*2 >> 10)) &&
+                       quota_error $qtype $qid "write succeed, expect EQUOT"
+       else
+               $RUNAS createmany -m $TESTFILE $((LIMIT*2)) &&
+                       quota_error $qtype $qid "create succeed, expect EQUOT"
+
+               unlinkmany $TESTFILE $((LIMIT*2))
+       fi
+
+       log "Set to use default quota again"
+       $LFS setquota $qtype $qid -d $DIR ||
+               error "set $qid to use default quota failed"
+
+       cancel_lru_locks osc
+       cancel_lru_locks mdc
+       sync; sync_all_data || true
+       if [ $qpool == "data" ]; then
+               $RUNAS $DD of=$TESTFILE count=$((LIMIT*2 >> 10)) ||
+                       quota_error $qtype $qid "write failed, expect succeed"
+       else
+               $RUNAS createmany -m $TESTFILE $((LIMIT*2)) ||
+                       quota_error $qtype $qid "create failed, expect succeed"
+
+               unlinkmany $TESTFILE $((LIMIT*2))
+       fi
+
+       log "Cleanup"
+       rm -f $TESTFILE
+       wait_delete_completed || error "wait_delete_completed failed"
+       sync_all_data || true
+       $LFS setquota $qdtype -b 0 -B 0 -i 0 -I 0 $DIR ||
+               error "reset default quota failed"
+       $LFS setquota $qtype $qid -b 0 -B 0 -i 0 -I 0 $DIR ||
+               error "reset quota failed"
+
+       cleanup_quota_test
+}
+
+test_61() {
+       test_default_quota "-u" "data"
+       test_default_quota "-u" "meta"
+       test_default_quota "-g" "data"
+       test_default_quota "-g" "meta"
+       test_default_quota "-p" "data"
+       test_default_quota "-p" "meta"
+}
+run_test 61 "default quota tests"
+
 quota_fini()
 {
        do_nodes $(comma_list $(nodes_list)) "lctl set_param debug=-quota"
index 321975f..445cfa7 100644 (file)
@@ -414,13 +414,25 @@ command_t cmdlist[] = {
         "       setquota [-t] <-u|--user|-g|--group|-p|--projid>\n"
          "                [--block-grace <block-grace>]\n"
          "                [--inode-grace <inode-grace>] <filesystem>\n"
+        "       setquota <-U|-G|-P>\n"
+        "                -b <block-softlimit> -B <block-hardlimit>\n"
+        "                -i <inode-softlimit> -I <inode-hardlimit> <filesystem>\n"
+        "       setquota <-U|--default-usr|-G|--default-grp|-P|--default-prj>\n"
+        "                [--block-softlimit <block-softlimit>]\n"
+        "                [--block-hardlimit <block-hardlimit>]\n"
+        "                [--inode-softlimit <inode-softlimit>]\n"
+        "                [--inode-hardlimit <inode-hardlimit>] <filesystem>\n"
+        "       setquota <-u|-g|-p> <uname>|<uid>|<gname>|<gid>|<projid>\n"
+        "                <-d|--default>\n"
          "       -b can be used instead of --block-softlimit/--block-grace\n"
          "       -B can be used instead of --block-hardlimit\n"
          "       -i can be used instead of --inode-softlimit/--inode-grace\n"
-        "       -I can be used instead of --inode-hardlimit\n\n"
+        "       -I can be used instead of --inode-hardlimit\n"
+        "       -d can be used instead of --default\n\n"
         "Note: The total quota space will be split into many qunits and\n"
         "      balanced over all server targets, the minimal qunit size is\n"
         "      1M bytes for block space and 1K inodes for inode space.\n\n"
+        "      The maximum quota grace time is 2^48 - 1 seconds.\n\n"
         "      Quota space rebalancing process will stop when this mininum\n"
         "      value is reached. As a result, quota exceeded can be returned\n"
         "      while many targets still have 1MB or 1K inodes of spare\n"
@@ -429,7 +441,8 @@ command_t cmdlist[] = {
         "usage: quota [-q] [-v] [-h] [-o <obd_uuid>|-i <mdt_idx>|-I "
                       "<ost_idx>]\n"
         "             [<-u|-g|-p> <uname>|<uid>|<gname>|<gid>|<projid>] <filesystem>\n"
-        "       quota [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>] -t <-u|-g|-p> <filesystem>"},
+        "       quota [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>] -t <-u|-g|-p> <filesystem>\n"
+       "        quota [-q] [-v] [h] <-U|-G|-P> <filesystem>"},
        {"project", lfs_project, 0,
         "Change or list project attribute for specified file or directory.\n"
         "usage: project [-d|-r] <file|directory...>\n"
@@ -5361,6 +5374,12 @@ quota_type:
                 return CMD_HELP;
         }
 
+       if ((dqb->dqb_valid | QIF_BTIME && dqi->dqi_bgrace >= UINT_MAX) ||
+           (dqb->dqb_valid | QIF_ITIME && dqi->dqi_igrace >= UINT_MAX)) {
+               fprintf(stderr, "error: grace time is too large\n");
+               return CMD_HELP;
+       }
+
         mnt = argv[optind];
         rc = llapi_quotactl(mnt, &qctl);
         if (rc) {
@@ -5381,25 +5400,30 @@ quota_type:
 
 int lfs_setquota(int argc, char **argv)
 {
-        int c, rc;
-        struct if_quotactl qctl;
-        char *mnt, *obd_type = (char *)qctl.obd_type;
-        struct obd_dqblk *dqb = &qctl.qc_dqblk;
-        struct option long_opts[] = {
+       int c, rc = 0;
+       struct if_quotactl qctl;
+       char *mnt, *obd_type = (char *)qctl.obd_type;
+       struct obd_dqblk *dqb = &qctl.qc_dqblk;
+       struct option long_opts[] = {
        { .val = 'b',   .name = "block-softlimit",
                                                .has_arg = required_argument },
        { .val = 'B',   .name = "block-hardlimit",
                                                .has_arg = required_argument },
+       { .val = 'd',   .name = "default",      .has_arg = no_argument },
        { .val = 'g',   .name = "group",        .has_arg = required_argument },
+       { .val = 'G',   .name = "default-grp",  .has_arg = no_argument },
        { .val = 'i',   .name = "inode-softlimit",
                                                .has_arg = required_argument },
        { .val = 'I',   .name = "inode-hardlimit",
                                                .has_arg = required_argument },
        { .val = 'p',   .name = "projid",       .has_arg = required_argument },
+       { .val = 'P',   .name = "default-prj",  .has_arg = no_argument },
        { .val = 'u',   .name = "user",         .has_arg = required_argument },
+       { .val = 'U',   .name = "default-usr",  .has_arg = no_argument },
        { .name = NULL } };
-        unsigned limit_mask = 0;
-        char *endptr;
+       unsigned limit_mask = 0;
+       char *endptr;
+       bool use_default = false;
        int qtype;
 
        if (has_times_option(argc, argv))
@@ -5411,37 +5435,65 @@ int lfs_setquota(int argc, char **argv)
                                  * so it can be used as a marker that qc_type
                                  * isn't reinitialized from command line */
 
-       while ((c = getopt_long(argc, argv, "b:B:g:i:I:p:u:",
+       while ((c = getopt_long(argc, argv, "b:B:dg:Gi:I:p:Pu:U",
                long_opts, NULL)) != -1) {
                switch (c) {
+               case 'U':
+                       qctl.qc_cmd = LUSTRE_Q_SETDEFAULT;
+                       qtype = USRQUOTA;
+                       qctl.qc_id = 0;
+                       goto quota_type_def;
                case 'u':
                        qtype = USRQUOTA;
                        rc = name2uid(&qctl.qc_id, optarg);
                        goto quota_type;
+               case 'G':
+                       qctl.qc_cmd = LUSTRE_Q_SETDEFAULT;
+                       qtype = GRPQUOTA;
+                       qctl.qc_id = 0;
+                       goto quota_type_def;
                 case 'g':
                        qtype = GRPQUOTA;
                        rc = name2gid(&qctl.qc_id, optarg);
                        goto quota_type;
+               case 'P':
+                       qctl.qc_cmd = LUSTRE_Q_SETDEFAULT;
+                       qtype = PRJQUOTA;
+                       qctl.qc_id = 0;
+                       goto quota_type_def;
                case 'p':
                        qtype = PRJQUOTA;
                        rc = name2projid(&qctl.qc_id, optarg);
 quota_type:
-                       if (qctl.qc_type != ALLQUOTA) {
-                               fprintf(stderr,
-                                       "%s setquota: only one of -u, -g or -p may be specified\n",
-                                       progname);
-                               return CMD_HELP;
-                       }
-                       qctl.qc_type = qtype;
                        if (rc) {
                                qctl.qc_id = strtoul(optarg, &endptr, 10);
                                if (*endptr != '\0') {
-                                       fprintf(stderr,
-                                               "%s setquota: invalid id '%s'\n",
-                                               progname, optarg);
+                                       fprintf(stderr, "%s setquota: invalid"
+                                               " id '%s'\n", progname, optarg);
                                        return -1;
                                }
                        }
+
+                       if (qctl.qc_id == 0) {
+                               fprintf(stderr, "%s setquota: can't set quota"
+                                       " for root usr/group/project.\n",
+                                       progname);
+                               return -1;
+                       }
+
+quota_type_def:
+                       if (qctl.qc_type != ALLQUOTA) {
+                               fprintf(stderr,
+                                       "%s setquota: only one of -u, -U, -g,"
+                                       " -G, -p or -P may be specified\n",
+                                       progname);
+                               return CMD_HELP;
+                       }
+                       qctl.qc_type = qtype;
+                       break;
+               case 'd':
+                       qctl.qc_cmd = LUSTRE_Q_SETDEFAULT;
+                       use_default = true;
                        break;
                case 'b':
                        ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
@@ -5504,13 +5556,29 @@ quota_type:
                return CMD_HELP;
        }
 
-       if (limit_mask == 0) {
+       if (!use_default && limit_mask == 0) {
                fprintf(stderr,
                        "%s setquota: at least one limit must be specified\n",
                        progname);
                return CMD_HELP;
        }
 
+       if (use_default && limit_mask != 0) {
+               fprintf(stderr,
+                       "%s setquota: limits should not be specified when"
+                       " using default quota\n",
+                       progname);
+               return CMD_HELP;
+       }
+
+       if (use_default && qctl.qc_id == 0) {
+               fprintf(stderr,
+                       "%s setquota: can not set default quota for root"
+                       " user/group/project\n",
+                       progname);
+               return CMD_HELP;
+       }
+
        if (optind != argc - 1) {
                fprintf(stderr,
                        "%s setquota: filesystem not specified or unexpected argument '%s'\n",
@@ -5520,42 +5588,50 @@ quota_type:
 
         mnt = argv[optind];
 
-        if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
-            (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
-                /* sigh, we can't just set blimits/ilimits */
-                struct if_quotactl tmp_qctl = {.qc_cmd  = LUSTRE_Q_GETQUOTA,
-                                               .qc_type = qctl.qc_type,
-                                               .qc_id   = qctl.qc_id};
-
-                rc = llapi_quotactl(mnt, &tmp_qctl);
+       if (use_default) {
+               dqb->dqb_bhardlimit = 0;
+               dqb->dqb_bsoftlimit = 0;
+               dqb->dqb_ihardlimit = 0;
+               dqb->dqb_isoftlimit = 0;
+               dqb->dqb_itime = 0;
+               dqb->dqb_btime = 0;
+               dqb->dqb_valid |= QIF_LIMITS | QIF_TIMES;
+       } else if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
+                  (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
+               /* sigh, we can't just set blimits/ilimits */
+               struct if_quotactl tmp_qctl = {.qc_cmd  = LUSTRE_Q_GETQUOTA,
+                                              .qc_type = qctl.qc_type,
+                                              .qc_id   = qctl.qc_id};
+
+               rc = llapi_quotactl(mnt, &tmp_qctl);
                if (rc < 0)
                        return rc;
 
-                if (!(limit_mask & BHLIMIT))
-                        dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
-                if (!(limit_mask & BSLIMIT))
-                        dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
-                if (!(limit_mask & IHLIMIT))
-                        dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
-                if (!(limit_mask & ISLIMIT))
-                        dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
-
-                /* Keep grace times if we have got no softlimit arguments */
-                if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
-                        dqb->dqb_valid |= QIF_BTIME;
-                        dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
-                }
+               if (!(limit_mask & BHLIMIT))
+                       dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
+               if (!(limit_mask & BSLIMIT))
+                       dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
+               if (!(limit_mask & IHLIMIT))
+                       dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
+               if (!(limit_mask & ISLIMIT))
+                       dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
+
+               /* Keep grace times if we have got no softlimit arguments */
+               if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
+                       dqb->dqb_valid |= QIF_BTIME;
+                       dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
+               }
 
-                if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
-                        dqb->dqb_valid |= QIF_ITIME;
-                        dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
-                }
-        }
+               if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
+                       dqb->dqb_valid |= QIF_ITIME;
+                       dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
+               }
+       }
 
-        dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
-        dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
+       dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
+       dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
 
-        rc = llapi_quotactl(mnt, &qctl);
+       rc = llapi_quotactl(mnt, &qctl);
        if (rc) {
                if (*obd_type)
                        fprintf(stderr,
@@ -5624,15 +5700,22 @@ static void diff2str(time_t seconds, char *buf, time_t now)
 }
 
 static void print_quota_title(char *name, struct if_quotactl *qctl,
-                             bool human_readable)
+                             bool human_readable, bool show_default)
 {
-       printf("Disk quotas for %s %s (%cid %u):\n",
-              qtype_name(qctl->qc_type), name,
-              *qtype_name(qctl->qc_type), qctl->qc_id);
-       printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
-              "Filesystem", human_readable ? "used" : "kbytes",
-              "quota", "limit", "grace",
-              "files", "quota", "limit", "grace");
+       if (show_default) {
+               printf("Disk default %s quota:\n", qtype_name(qctl->qc_type));
+               printf("%15s %8s%8s%8s %8s%8s%8s\n",
+                      "Filesystem", "bquota", "blimit", "bgrace",
+                      "iquota", "ilimit", "igrace");
+       } else {
+               printf("Disk quotas for %s %s (%cid %u):\n",
+                      qtype_name(qctl->qc_type), name,
+                      *qtype_name(qctl->qc_type), qctl->qc_id);
+               printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
+                      "Filesystem", human_readable ? "used" : "kbytes",
+                      "quota", "limit", "grace",
+                      "files", "quota", "limit", "grace");
+       }
 }
 
 static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
@@ -5659,20 +5742,21 @@ static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
 
 #define STRBUF_LEN     32
 static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
-                       int rc, bool h)
+                       int rc, bool h, bool show_default)
 {
-        time_t now;
+       time_t now;
 
-        time(&now);
+       time(&now);
 
-        if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
+       if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA ||
+           qctl->qc_cmd == LUSTRE_Q_GETDEFAULT) {
                int bover = 0, iover = 0;
                struct obd_dqblk *dqb = &qctl->qc_dqblk;
                char numbuf[3][STRBUF_LEN];
                char timebuf[40];
                char strbuf[STRBUF_LEN];
 
-                if (dqb->dqb_bhardlimit &&
+               if (dqb->dqb_bhardlimit &&
                    lustre_stoqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
                         bover = 1;
                 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
@@ -5702,6 +5786,9 @@ static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
 
                if (bover)
                        diff2str(dqb->dqb_btime, timebuf, now);
+               else if (show_default)
+                       snprintf(timebuf, sizeof(timebuf), "%llu",
+                                dqb->dqb_btime);
 
                kbytes2str(lustre_stoqb(dqb->dqb_curspace),
                           strbuf, sizeof(strbuf), h);
@@ -5722,15 +5809,23 @@ static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
                sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS) ?
                        "%s" : "[%s]", strbuf);
 
-               printf(" %7s%c %6s %7s %7s",
-                      numbuf[0], bover ? '*' : ' ', numbuf[1],
-                      numbuf[2], bover > 1 ? timebuf : "-");
+               if (show_default)
+                       printf(" %6s %7s %7s", numbuf[1], numbuf[2], timebuf);
+               else
+                       printf(" %7s%c %6s %7s %7s",
+                              numbuf[0], bover ? '*' : ' ', numbuf[1],
+                              numbuf[2], bover > 1 ? timebuf : "-");
+
 
                if (iover)
                        diff2str(dqb->dqb_itime, timebuf, now);
+               else if (show_default)
+                       snprintf(timebuf, sizeof(timebuf), "%llu",
+                                dqb->dqb_itime);
 
-               sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
-                       "%ju" : "[%ju]", (uintmax_t)dqb->dqb_curinodes);
+               snprintf(numbuf[0], sizeof(numbuf),
+                        (dqb->dqb_valid & QIF_INODES) ? "%ju" : "[%ju]",
+                        (uintmax_t)dqb->dqb_curinodes);
 
                if (type == QC_GENERAL)
                        sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS) ?
@@ -5742,7 +5837,9 @@ static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
                sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
                        "%ju" : "[%ju]", (uintmax_t)dqb->dqb_ihardlimit);
 
-               if (type != QC_OSTIDX)
+               if (show_default)
+                       printf(" %6s %7s %7s", numbuf[1], numbuf[2], timebuf);
+               else if (type != QC_OSTIDX)
                        printf(" %7s%c %6s %7s %7s",
                               numbuf[0], iover ? '*' : ' ', numbuf[1],
                               numbuf[2], iover > 1 ? timebuf : "-");
@@ -5793,7 +5890,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);
+                           qctl->qc_valid, 0, h, false);
                *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit :
                                   qctl->qc_dqblk.dqb_bhardlimit;
        }
@@ -5803,12 +5900,15 @@ out:
 }
 
 static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl,
-                          int verbose, int quiet, bool human_readable)
+                          int verbose, int quiet, bool human_readable,
+                          bool show_default)
 {
        int rc1 = 0, rc2 = 0, rc3 = 0;
        char *obd_type = (char *)qctl->obd_type;
        char *obd_uuid = (char *)qctl->obd_uuid.uuid;
        __u64 total_ialloc = 0, total_balloc = 0;
+       bool use_default_for_blk = false;
+       bool use_default_for_file = false;
        int inacc;
 
        rc1 = llapi_quotactl(mnt, qctl);
@@ -5830,8 +5930,31 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl,
                }
        }
 
-       if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
-               print_quota_title(name, qctl, human_readable);
+       if (!show_default && qctl->qc_id == 0) {
+               qctl->qc_dqblk.dqb_bhardlimit = 0;
+               qctl->qc_dqblk.dqb_bsoftlimit = 0;
+               qctl->qc_dqblk.dqb_ihardlimit = 0;
+               qctl->qc_dqblk.dqb_isoftlimit = 0;
+               qctl->qc_dqblk.dqb_btime = 0;
+               qctl->qc_dqblk.dqb_itime = 0;
+               qctl->qc_dqblk.dqb_valid |= QIF_LIMITS | QIF_TIMES;
+       }
+
+       if (qctl->qc_dqblk.dqb_valid & QIF_BTIME &&
+           LQUOTA_FLAG(qctl->qc_dqblk.dqb_btime) & LQUOTA_FLAG_DEFAULT) {
+               use_default_for_blk = true;
+               qctl->qc_dqblk.dqb_btime &= LQUOTA_GRACE_MASK;
+       }
+
+       if (qctl->qc_dqblk.dqb_valid & QIF_ITIME &&
+           LQUOTA_FLAG(qctl->qc_dqblk.dqb_itime) & LQUOTA_FLAG_DEFAULT) {
+               use_default_for_file = true;
+               qctl->qc_dqblk.dqb_itime &= LQUOTA_GRACE_MASK;
+       }
+
+       if ((qctl->qc_cmd == LUSTRE_Q_GETQUOTA ||
+            qctl->qc_cmd == LUSTRE_Q_GETDEFAULT) && !quiet)
+               print_quota_title(name, qctl, human_readable, show_default);
 
        if (rc1 && *obd_type)
                fprintf(stderr, "%s %s ", obd_type, obd_uuid);
@@ -5843,10 +5966,10 @@ 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);
+       print_quota(mnt, qctl, QC_GENERAL, rc1, human_readable, show_default);
 
-       if (qctl->qc_valid == QC_GENERAL && qctl->qc_cmd != LUSTRE_Q_GETINFO &&
-           verbose) {
+       if (!show_default && verbose &&
+           qctl->qc_valid == QC_GENERAL && qctl->qc_cmd != LUSTRE_Q_GETINFO) {
                char strbuf[STRBUF_LEN];
 
                rc2 = print_obd_quota(mnt, qctl, 1, human_readable,
@@ -5860,6 +5983,14 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl,
                       strbuf);
        }
 
+       if (use_default_for_blk)
+               printf("%cid %u is using default block quota setting\n",
+                      *qtype_name(qctl->qc_type), qctl->qc_id);
+
+       if (use_default_for_file)
+               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. "
@@ -6034,16 +6165,23 @@ static int lfs_quota(int argc, char **argv)
        char *endptr;
        __u32 valid = QC_GENERAL, idx = 0;
        bool human_readable = false;
+       bool show_default = false;
        int qtype;
 
-       while ((c = getopt(argc, argv, "gi:I:o:pqtuvh")) != -1) {
+       while ((c = getopt(argc, argv, "gGi:I:o:pPqtuUvh")) != -1) {
                switch (c) {
+               case 'U':
+                       show_default = true;
                case 'u':
                        qtype = USRQUOTA;
                        goto quota_type;
+               case 'G':
+                       show_default = true;
                case 'g':
                        qtype = GRPQUOTA;
                        goto quota_type;
+               case 'P':
+                       show_default = true;
                case 'p':
                        qtype = PRJQUOTA;
 quota_type:
@@ -6100,7 +6238,7 @@ quota_type:
 
         /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
        if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == ALLQUOTA &&
-           optind == argc - 1) {
+           optind == argc - 1 && !show_default) {
 
                qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
                qctl.qc_valid = valid;
@@ -6119,7 +6257,7 @@ quota_type:
                                name = "<unknown>";
                        mnt = argv[optind];
                        rc1 = get_print_quota(mnt, name, &qctl, verbose, quiet,
-                                             human_readable);
+                                             human_readable, show_default);
                        if (rc1 && !rc)
                                rc = rc1;
                }
@@ -6127,28 +6265,37 @@ quota_type:
        /* lfs quota -u username /path/to/lustre/mount */
        } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
                /* options should be followed by u/g-name and mntpoint */
-               if (optind + 2 != argc || qctl.qc_type == ALLQUOTA) {
+               if ((!show_default && optind + 2 != argc) ||
+                   (show_default && optind + 1 != argc) ||
+                   qctl.qc_type == ALLQUOTA) {
                        fprintf(stderr,
                                "%s quota: name and mount point must be specified\n",
                                progname);
                        return CMD_HELP;
                }
 
-               name = argv[optind++];
-               switch (qctl.qc_type) {
-               case USRQUOTA:
-                       rc = name2uid(&qctl.qc_id, name);
-                       break;
-               case GRPQUOTA:
-                       rc = name2gid(&qctl.qc_id, name);
-                       break;
-               case PRJQUOTA:
-                       rc = name2projid(&qctl.qc_id, name);
-                       break;
-               default:
-                       rc = -ENOTSUP;
-                       break;
+               if (!show_default) {
+                       name = argv[optind++];
+                       switch (qctl.qc_type) {
+                       case USRQUOTA:
+                               rc = name2uid(&qctl.qc_id, name);
+                               break;
+                       case GRPQUOTA:
+                               rc = name2gid(&qctl.qc_id, name);
+                               break;
+                       case PRJQUOTA:
+                               rc = name2projid(&qctl.qc_id, name);
+                               break;
+                       default:
+                               rc = -ENOTSUP;
+                               break;
+                       }
+               } else {
+                       qctl.qc_valid = QC_GENERAL;
+                       qctl.qc_cmd = LUSTRE_Q_GETDEFAULT;
+                       qctl.qc_id = 0;
                }
+
                if (rc) {
                        qctl.qc_id = strtoul(name, &endptr, 10);
                        if (*endptr != '\0') {
@@ -6165,7 +6312,7 @@ quota_type:
 
        mnt = argv[optind];
        rc = get_print_quota(mnt, name, &qctl, verbose, quiet,
-                            human_readable);
+                            human_readable, show_default);
        return rc;
 }
 #endif /* HAVE_SYS_QUOTA_H! */