Whamcloud - gitweb
Branch b1_4
authoradilger <adilger>
Wed, 1 Jun 2005 23:18:33 +0000 (23:18 +0000)
committeradilger <adilger>
Wed, 1 Jun 2005 23:18:33 +0000 (23:18 +0000)
Some minor updates from b1_4_quota that I had in my tree for a long time.
There are more updates that need to be landed, but these are pretty safe.

lustre/include/linux/lustre_quota.h
lustre/ldiskfs/lustre_quota_fmt.c
lustre/ldiskfs/quotafmt_test.c
lustre/mds/handler.c
lustre/mds/mds_open.c
lustre/mds/mds_reint.c
lustre/mds/quota_context.c
lustre/mds/quota_master.c
lustre/tests/quota_sanity.sh

index 7633ebe..91da654 100644 (file)
@@ -10,9 +10,6 @@
 #include <linux/quota.h>
 #include <linux/lustre_idl.h>
 
-/* XXX disable amdin quotafile delete dquot temporarily */
-#define QFMT_NO_DELETE 1
-
 #define QUSG(count, isblk)      (isblk ? toqb(count) : count)
 
 /* If the (quota limit < qunit * slave count), the slave which can't
index dd0ad3c..af6dad1 100644 (file)
@@ -536,7 +536,8 @@ static int remove_tree(struct lustre_dquot *dquot, uint *blk, int depth)
                int i;
                ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
                for (i = 0; i < LUSTRE_DQBLKSIZE && !buf[i]; i++);      /* Block got empty? */
-               if (i == LUSTRE_DQBLKSIZE) {
+               /* don't put the root block into free blk list! */
+               if (i == LUSTRE_DQBLKSIZE && *blk != LUSTRE_DQTREEOFF) {
                        put_free_dqblk(filp, info, buf, *blk);
                        *blk = 0;
                }
@@ -692,16 +693,10 @@ int lustre_commit_dquot(struct lustre_dquot *dquot)
        /* The block/inode usage in admin quotafile isn't the real usage
         * over all cluster, so keep the fake dquot entry on disk is
         * meaningless, just remove it */
-#ifndef        QFMT_NO_DELETE
        if (test_bit(DQ_FAKE_B, &dquot->dq_flags))
                rc = lustre_delete_dquot(dquot);
-       }
-       else {
+       else
                rc = lustre_write_dquot(dquot);
-       }
-#else
-       rc = lustre_write_dquot(dquot);
-#endif
        if (rc < 0)
                return rc;
 
index b3c9e44..6c354bb 100644 (file)
@@ -260,9 +260,6 @@ static int quotfmt_test_3(struct lustre_quota_info *lqi)
        int i = 0, rc = 0;
        ENTRY;
 
-#ifdef QFMT_NO_DELETE
-       RETURN(0);
-#endif
        dquot = get_rand_dquot(lqi);
        if (dquot == NULL)
                RETURN(-ENOMEM);
@@ -283,6 +280,7 @@ repeat:
                CERROR("read dquot failed! (rc:%d)\n", rc);
                GOTO(out, rc);
        }
+
        if (!dquot->dq_off || test_bit(DQ_FAKE_B, &dquot->dq_flags)) {
                CERROR("the dquot isn't committed\n");
                GOTO(out, rc = -EINVAL);
@@ -315,8 +313,6 @@ repeat:
        if (++i < 2)
                goto repeat;
 
-       print_quota_info(lqi);
-                       
 out:
        put_rand_dquot(dquot);
        RETURN(rc);
index 1f8efb6..9537e48 100644 (file)
@@ -1047,16 +1047,16 @@ static int mds_quotacheck_thread(void *data)
         struct obd_quotactl *oqctl;
         struct obd_run_ctxt saved;
         int rc;
-                                                                                                                 
+
         lock_kernel();
         ptlrpc_daemonize();
-                                                                                                                 
+
         SIGNAL_MASK_LOCK(current, flags);
         sigfillset(&current->blocked);
         RECALC_SIGPENDING;
         SIGNAL_MASK_UNLOCK(current, flags);
 
-        THREAD_NAME(current->comm, sizeof(current->comm) - 1, "%s", "quotacheck");
+        THREAD_NAME(current->comm, sizeof(current->comm) - 1, "quotacheck");
         unlock_kernel();
 
         complete(&qchki->qi_starting);
@@ -1730,6 +1730,15 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf)
                            "mds_ldlm_client", &obd->obd_ldlm_client);
         obd->obd_replayable = 1;
 
+        /* initialize quota master and quota context */
+        sema_init(&mds->mds_quota_info.qi_sem, 1);
+        rc = qctxt_init(&mds->mds_quota_ctxt, mds->mds_sb, dqacq_handler);
+        if (rc) {
+                CERROR("initialize quota context failed! (rc:%d)\n", rc);
+                qctxt_cleanup(&mds->mds_quota_ctxt, 0);
+                GOTO(err_fs, rc);
+        }
+
         rc = mds_postsetup(obd);
         if (rc)
                 GOTO(err_fs, rc);
@@ -1761,15 +1770,6 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf)
 
         ping_evictor_start();
 
-        sema_init(&mds->mds_quota_info.qi_sem, 1);
-        rc = qctxt_init(&mds->mds_quota_ctxt, mds->mds_sb, dqacq_handler);
-        if (rc) {
-                CERROR("initialize quota context failed! (rc:%d)\n", rc);
-                qctxt_cleanup(&mds->mds_quota_ctxt, 0);
-                GOTO(err_fs, rc);
-        }
-
-
         RETURN(0);
 
 err_fs:
index 54af699..5037d3d 100644 (file)
@@ -367,6 +367,9 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset,
                 RETURN(0);
         }
 
+        LASSERT(ret_logcookies);
+        LASSERT(setattr_async_flag);
+
         if (OBD_FAIL_CHECK_ONCE(OBD_FAIL_MDS_ALLOC_OBDO))
                 GOTO(out_ids, rc = -ENOMEM);
 
@@ -481,6 +484,7 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset,
                 if (logcookies == NULL)
                         GOTO(out_oa, rc = -ENOMEM);
                 *ret_logcookies = logcookies;
+
                 if (mds_log_op_setattr(obd, inode, lmm, lmm_size, logcookies,
                                        mds->mds_max_cookiesize) <= 0) {
                         OBD_FREE(logcookies, mds->mds_max_cookiesize);
@@ -731,16 +735,11 @@ static int mds_open_by_fid(struct ptlrpc_request *req, struct ll_fid *fid,
                            struct mds_body *body, int flags,
                            struct mds_update_record *rec,struct ldlm_reply *rep)
 {
-        struct obd_device *obd = req->rq_export->exp_obd;
         struct mds_obd *mds = mds_req2mds(req);
         struct dentry *dchild;
         char fidname[LL_FID_NAMELEN];
         int fidlen = 0, rc;
         void *handle = NULL;
-        struct llog_cookie *logcookies = NULL;
-        struct lov_mds_md *lmm = NULL;
-        int lmm_size = 0;
-        int setattr_async_flag = 0;
         ENTRY;
 
         fidlen = ll_fid2str(fidname, fid->id, fid->generation);
@@ -776,16 +775,9 @@ static int mds_open_by_fid(struct ptlrpc_request *req, struct ll_fid *fid,
 
  open:
         rc = mds_finish_open(req, dchild, body, flags, &handle, rec, rep,
-                             &logcookies, &setattr_async_flag);
+                             NULL, NULL);
         rc = mds_finish_transno(mds, dchild ? dchild->d_inode : NULL, handle,
                                 req, rc, rep ? rep->lock_policy_res1 : 0);
-        /* do mds to ost setattr for new created objects */
-        if (rc == 0 && setattr_async_flag) {
-                lmm = lustre_msg_buf(req->rq_repmsg, 2, 0);
-                lmm_size = req->rq_repmsg->buflens[2];
-                rc = mds_osc_setattr_async(obd, dchild->d_inode, lmm, lmm_size,
-                                           logcookies);
-        }
         /* XXX what do we do here if mds_finish_transno itself failed? */
 
         l_dput(dchild);
@@ -1103,6 +1095,8 @@ int mds_open(struct mds_update_record *rec, int offset,
                 mds_osc_setattr_async(obd, dchild->d_inode, lmm, lmm_size,
                                       logcookies);
         }
+        if (logcookies)
+                OBD_FREE(logcookies, mds->mds_max_cookiesize);
 
  cleanup_no_trans:
         switch (cleanup_phase) {
@@ -1132,7 +1126,7 @@ int mds_open(struct mds_update_record *rec, int offset,
                 else
                         ptlrpc_save_lock (req, &parent_lockh, parent_mode);
         }
-        
         /* trigger dqacq on the owner of child and parent */
         mds_adjust_qunit(obd, current->fsuid, current->fsgid, 
                          parent_uid, parent_gid, rc);
index ea46f39..b468231 100644 (file)
@@ -376,11 +376,11 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode,
         struct lov_stripe_md *lsm = NULL;
         struct obd_trans_info oti = { 0 };
         struct obdo *oa = NULL;
-        int  cleanup_phase = 0, rc = 0;
+        int rc;
         ENTRY;
 
         if (OBD_FAIL_CHECK(OBD_FAIL_MDS_OST_SETATTR))
-                GOTO(cleanup, rc);
+                RETURN(0);
 
         /* first get memory EA */
         oa = obdo_alloc();
@@ -389,14 +389,12 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode,
 
         LASSERT(lmm);
 
-        cleanup_phase = 1;
         rc = obd_unpackmd(mds->mds_osc_exp, &lsm, lmm, lmm_size);
         if (rc < 0) {
                 CERROR("Error unpack md %p\n", lmm);
-                GOTO(cleanup, rc);
+                GOTO(out, rc);
         }
 
-        cleanup_phase = 2;
         /* then fill oa */
         oa->o_id = lsm->lsm_object_id;
         oa->o_uid = inode->i_uid;
@@ -410,19 +408,12 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode,
         /* do setattr from mds to ost asynchronously */
         rc = obd_setattr_async(mds->mds_osc_exp, oa, lsm, &oti);
         if (rc)
-                CDEBUG(D_INODE, "mds to ost setattr objid 0x"LPX64" on ost error "
-                       "%d\n", lsm->lsm_object_id, rc);
-cleanup:
-        switch(cleanup_phase) {
-        case 2:
-                obd_free_memmd(mds->mds_osc_exp, &lsm);
-        case 1:
-                obdo_free(oa);
-        case 0:
-                if (logcookies)
-                        OBD_FREE(logcookies, mds->mds_max_cookiesize);
-        }
+                CDEBUG(D_INODE, "mds to ost setattr objid 0x"LPX64
+                       " on ost error %d\n", lsm->lsm_object_id, rc);
 
+        obd_free_memmd(mds->mds_osc_exp, &lsm);
+  out:
+        obdo_free(oa);
         RETURN(rc);
 }
 
@@ -529,6 +520,7 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset,
                         OBD_ALLOC(logcookies, mds->mds_max_cookiesize);
                         if (logcookies == NULL)
                                 GOTO(cleanup, rc = -ENOMEM);
+
                         if (mds_log_op_setattr(obd, inode, lmm, lmm_size,
                                                logcookies,
                                                mds->mds_max_cookiesize) <= 0) {
@@ -613,6 +605,8 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset,
         switch (cleanup_phase) {
         case 2:
                 OBD_FREE(lmm, mds->mds_max_mdsize);
+                if (logcookies)
+                        OBD_FREE(logcookies, mds->mds_max_cookiesize);
         case 1:
                 if ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) &&
                     rec->ur_eadata != NULL)
index c0bda90..d601edb 100644 (file)
@@ -442,8 +442,10 @@ schedule_dqacq(struct obd_device *obd,
                 /* build dqacq/dqrel request */
                 LASSERT(qctxt->lqc_import);
                 req = ptlrpc_prep_req(qctxt->lqc_import, opc, 1, &size, NULL);
-                if (!req)
+                if (!req) {
+                        dqacq_completion(obd, qctxt, qdata, -ENOMEM, opc);
                         RETURN(-ENOMEM);
+                }
 
                 reqdata = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*reqdata));
                 memcpy(reqdata, qdata, sizeof(*reqdata));
@@ -470,7 +472,7 @@ wait_completion:
                 if (qw.qw_rc == 0)
                         rc = -EAGAIN;
 
-                QDATA_DEBUG(p, "wait dqacq done. (rc:%d)\n", qw.qw_rc);
+                CDEBUG(D_QUOTA, "wait dqacq done. (rc:%d)\n", qw.qw_rc);
         }
         RETURN(rc);
 }
index e7ca633..d1c8797 100644 (file)
@@ -229,10 +229,15 @@ int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc)
         struct lustre_quota_info *info = &mds->mds_quota_info;
         struct lustre_dquot *dquot = NULL;
         __u64 *usage = NULL;
-        __u32 *limit = NULL;
+        __u32 hlimit = 0, slimit = 0;
+        time_t *time = NULL;
+        unsigned int grace = 0;
         int rc = 0;
         ENTRY;
 
+        /* slaves never acquires qunit for user root */
+        LASSERT(qdata->qd_id || qdata->qd_type == GRPQUOTA);
+
         dquot = lustre_dqget(obd, info, qdata->qd_id, qdata->qd_type);
         if (IS_ERR(dquot))
                 RETURN(PTR_ERR(dquot));
@@ -244,27 +249,48 @@ int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc)
         down(&dquot->dq_sem);
 
         if (qdata->qd_isblk) {
+                grace = info->qi_info[qdata->qd_type].dqi_bgrace;
                 usage = &dquot->dq_dqb.dqb_curspace;
-                limit = &dquot->dq_dqb.dqb_bhardlimit;
+                hlimit = dquot->dq_dqb.dqb_bhardlimit;
+                slimit = dquot->dq_dqb.dqb_bsoftlimit;
+                time = &dquot->dq_dqb.dqb_btime;
         } else {
+                grace = info->qi_info[qdata->qd_type].dqi_igrace;
                 usage = (__u64 *) & dquot->dq_dqb.dqb_curinodes;
-                limit = &dquot->dq_dqb.dqb_ihardlimit;
-        }
+                hlimit = dquot->dq_dqb.dqb_ihardlimit;
+                slimit = dquot->dq_dqb.dqb_isoftlimit;
+                time = &dquot->dq_dqb.dqb_itime;
+        } 
 
         /* if the quota limit in admin quotafile is zero, we just inform
          * slave to clear quota limit with zero qd_count */
-        if (*limit == 0) {
+        if (hlimit == 0 && slimit == 0) {
                 qdata->qd_count = 0;
                 GOTO(out, rc);
         }
+        
         if (opc == QUOTA_DQACQ) {
-                if (QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > *limit)
+                if (hlimit && 
+                    QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > hlimit)
                         GOTO(out, rc = -EDQUOT);
-                else
-                        *usage += qdata->qd_count;
+
+                if (slimit &&
+                    QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > slimit) {
+                        if (*time && CURRENT_SECONDS >= *time)
+                                GOTO(out, rc = -EDQUOT);
+                        else if (!*time)
+                                *time = CURRENT_SECONDS + grace;
+                }
+
+                *usage += qdata->qd_count;
+                
         } else if (opc == QUOTA_DQREL) {
                 LASSERT(*usage - qdata->qd_count >= 0);
                 *usage -= qdata->qd_count;
+
+                /* (usage <= soft limit) but not (usage < soft limit) */
+                if (!slimit || QUSG(*usage, qdata->qd_isblk) <= slimit)
+                        *time = 0;
         } else {
                 LBUG();
         }
@@ -517,7 +543,7 @@ static int mds_init_slave_ilimits(struct obd_device *obd,
         ENTRY;
 
         /* if we are going to set zero limit, needn't init slaves */
-        if (!oqctl->qc_dqblk.dqb_ihardlimit)
+        if (!oqctl->qc_dqblk.dqb_ihardlimit && !oqctl->qc_dqblk.dqb_isoftlimit)
                 RETURN(0);
 
         OBD_ALLOC(ioqc, sizeof(*ioqc));
@@ -562,7 +588,7 @@ static int mds_init_slave_blimits(struct obd_device *obd,
         ENTRY;
 
         /* if we are going to set zero limit, needn't init slaves */
-        if (!oqctl->qc_dqblk.dqb_bhardlimit)
+        if (!oqctl->qc_dqblk.dqb_bhardlimit && !oqctl->qc_dqblk.dqb_bsoftlimit)
                 RETURN(0);
 
         OBD_ALLOC(ioqc, sizeof(*ioqc));
@@ -634,7 +660,8 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
                 dquot->dq_dqb.dqb_bhardlimit = dqblk->dqb_bhardlimit;
                 dquot->dq_dqb.dqb_bsoftlimit = dqblk->dqb_bsoftlimit;
                 /* clear usage (limit pool) */
-                if (dquot->dq_dqb.dqb_bhardlimit == 0)
+                if (!dquot->dq_dqb.dqb_bhardlimit && 
+                    !dquot->dq_dqb.dqb_bsoftlimit)
                         dquot->dq_dqb.dqb_curspace = 0;
         }
 
@@ -642,7 +669,8 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
                 dquot->dq_dqb.dqb_ihardlimit = dqblk->dqb_ihardlimit;
                 dquot->dq_dqb.dqb_isoftlimit = dqblk->dqb_isoftlimit;
                 /* clear usage (limit pool) */
-                if (dquot->dq_dqb.dqb_ihardlimit == 0)
+                if (!dquot->dq_dqb.dqb_ihardlimit &&
+                    !dquot->dq_dqb.dqb_isoftlimit)
                         dquot->dq_dqb.dqb_curinodes = 0;
         }
 
@@ -660,7 +688,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
         if (rc)
                 GOTO(out, rc);
 
-        if (dqblk->dqb_valid & QIF_ILIMITS && !ihardlimit) {
+        if (dqblk->dqb_valid & QIF_ILIMITS && !(ihardlimit || isoftlimit)) {
                 rc = mds_init_slave_ilimits(obd, oqctl);
                 if (rc) {
                         CERROR("init slave ilimits failed! (rc:%d)\n", rc);
@@ -668,7 +696,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
                 }
         }
 
-        if (dqblk->dqb_valid & QIF_BLIMITS && !bhardlimit) {
+        if (dqblk->dqb_valid & QIF_BLIMITS && !(bhardlimit || bsoftlimit)) {
                 rc = mds_init_slave_blimits(obd, oqctl);
                 if (rc) {
                         CERROR("init slave blimits failed! (rc:%d)\n", rc);
index 9261668..59f1b72 100644 (file)
@@ -13,10 +13,12 @@ USER="quota_usr"
 TSTID=${TSTID:-60000}
 RUNAS=${RUNAS:-"runas -u $TSTID"}
 BLK_SZ=1024
-BUNIT_SZ=10 # 10 quota blocks
-BTUNE_SZ=5  # 5 quota blocks
+BUNIT_SZ=1000 # 1000 quota blocks
+BTUNE_SZ=500  # 500 quota blocks
 IUNIT_SZ=10 # 10 files
 ITUNE_SZ=5  # 5 files
+MAX_DQ_TIME=604800
+MAX_IQ_TIME=604800
 
 MOUNT="`cat /proc/mounts | grep "lustre" | awk '{print $2}'`"
 if [ -z "$MOUNT" ]; then
@@ -26,37 +28,46 @@ fi
 OSTCOUNT=`cat /proc/fs/lustre/lov/*/activeobd | head -n 1`
 TSTDIR="$MOUNT/quota_dir"
 
-# set_blk_tunables(bunit_sz, btune_sz)
-set_blk_tunables() {
-       # set bunit and btune size on all obdfilters
+# set_blk_tunables(btune_sz)
+set_blk_tunesz() {
+       # set btune size on all obdfilters
        for i in `ls /proc/fs/lustre/obdfilter/*/quota_btune_sz`; do
-               echo $(($2 * $BLK_SZ)) > $i
+               echo $(($1 * $BLK_SZ)) > $i
+       done
+       # set btune size on mds
+       for i in `ls /proc/fs/lustre/mds/mds*/quota_btune_sz`; do
+               echo $(($1 * $BLK_SZ)) > $i
        done
+}
+
+# se_blk_unitsz(bunit_sz)
+set_blk_unitsz() {
        for i in `ls /proc/fs/lustre/obdfilter/*/quota_bunit_sz`; do
                echo $(($1 * $BLK_SZ)) > $i
-       done;
-       # set bunit and btune size on mds
-       for i in `ls /proc/fs/lustre/mds/mds*/quota_btune_sz`; do
-               echo $(($2 * $BLK_SZ)) > $i
        done
        for i in `ls /proc/fs/lustre/mds/mds*/quota_bunit_sz`; do
                echo $(($1 * $BLK_SZ)) > $i
        done
 }
 
-# set_file_tunables(iunit_sz, itune_sz)
-set_file_tunables() {
+# set_file_tunesz(itune_sz)
+set_file_tunesz() {
        # set iunit and itune size on all obdfilters
        for i in `ls /proc/fs/lustre/obdfilter/*/quota_itune_sz`; do
-               echo $2 > $i
-       done
-       for i in `ls /proc/fs/lustre/obdfilter/*/quota_iunit_sz`; do
                echo $1 > $i
-       done;
+       done
        # set iunit and itune size on mds
        for i in `ls /proc/fs/lustre/mds/mds*/quota_itune_sz`; do
-               echo $2 > $i
+               echo $1 > $i
        done
+
+
+}
+# set_file_unitsz(iunit_sz)
+set_file_unitsz() {
+       for i in `ls /proc/fs/lustre/obdfilter/*/quota_iunit_sz`; do
+               echo $1 > $i
+       done;
        for i in `ls /proc/fs/lustre/mds/mds*/quota_iunit_sz`; do
                echo $1 > $i
        done
@@ -77,27 +88,29 @@ prepare_test() {
        fi
        
        RUNAS="runas -u $TSTID"
+       
        # set block tunables
-
-       set_blk_tunables $BUNIT_SZ $BTUNE_SZ
+       set_blk_tunesz $BTUNE_SZ
+       set_blk_unitsz $BUNIT_SZ
        # set file tunaables
-       set_file_tunables $IUNIT_SZ $ITUNE_SZ
+       set_file_tunesz $ITUNE_SZ
+       set_file_unitsz $IUNIT_SZ
 
        [ -d $TSTDIR ] || mkdir $TSTDIR 
        chmod 777 $TSTDIR
 }
 
-cleanup_test() {
-       # delete test user and group
-       userdel "$USER"
-       groupdel "$USER"
-       
+cleanup_test() {       
        # restore block tunables to default size
-       set_blk_tunables $((1024 * 100)) $((1024 * 50))
+       set_blk_unitsz $((1024 * 100))
+       set_blk_tunesz $((1024 * 50))
        # restore file tunables to default size
-       set_file_tunables 5000 2500 
+       set_file_unitsz 5000
+       set_file_tunesz 2500
 
        rm -fr $TSTDIR
+       # delete test user and group
+       userdel "$USER"
 }
 
 # set quota
@@ -105,6 +118,9 @@ test_1() {
        echo "== Enable quota"
        $LFS quotaoff -ug $MOUNT
        $LFS quotacheck -ug $MOUNT
+
+       $LFS setquota -u $USER 0 0 0 0 $MOUNT
+       $LFS setquota -g $USER 0 0 0 0 $MOUNT
        return 0
 }
 
@@ -123,7 +139,8 @@ test_2() {
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) > /dev/null 2>&1 || error "(usr) write failure, but expect success"
        echo "    Done"
        echo "    Write out of block quota ..."
-       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1
+       # this time maybe cache write,  ignore it's failure
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 || echo " " > /dev/null
        # flush cache, ensure noquota flag is setted on client
        sync; sleep 1; sync;
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$LIMIT > /dev/null 2>&1 && error "(usr) write success, but expect EDQUOT"
@@ -142,7 +159,8 @@ test_2() {
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) > /dev/null 2>&1 || error "(grp) write failure, but expect success"
        echo "    Done"
        echo "    Write out of block quota ..."
-       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1
+       # this time maybe cache write, ignore it's failure
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 || echo " " > /dev/null
        sync; sleep 1; sync;
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$LIMIT > /dev/null 2>&1 && error "(grp) write success, but expect EDQUOT"
        echo "    EDQUOT"
@@ -197,17 +215,135 @@ test_3() {
        return 0
 }
 
+test_block_soft() {
+       TESTFILE=$1
+       GRACE=$2
+       BS=$(($BUNIT_SZ * $BLK_SZ))
+
+       echo "    Write to exceed soft limit"
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success"
+       sync; sleep 1; sync;
+
+       echo "    Write before timer goes off"
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success"
+       sync; sleep 1; sync;
+       echo "    Done"
+       
+       echo "    Sleep $GRACE seconds ..."
+       sleep $GRACE
+
+       echo "    Write after timer goes off"
+       # maybe cache write, ignore.
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || echo " " > /dev/null
+       sync; sleep 1; sync;
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=1 seek=$(($BUNIT_SZ * 3)) >/dev/null 2>&1 && error "write success, but expect EDQUOT"
+       echo "    EDQUOT"
+
+       echo "    Unlink file to stop timer"
+       rm -f $TESTFILE
+       echo "    Done"
+
+       echo "    Write ..."
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success"
+       echo "    Done"
+
+       # cleanup
+       rm -f $TESTFILE
+}
+
 # block soft limit (start timer, timer goes off, stop timer)
 test_4() {
        echo "== Block soft limit"
-       echo "  ** skipped"
+       LIMIT=$(( $BUNIT_SZ * $(($OSTCOUNT + 1)) )) # 1 bunits each sever
+       TESTFILE="$TSTDIR/quota_tst40"
+       GRACE=10
+
+       echo "  User quota (soft limit: $LIMIT bytes  grace: $GRACE seconds)"
+       $LFS setquota -t -u $GRACE $MAX_IQ_TIME $MOUNT
+       $LFS setquota -u $USER $LIMIT 0 0 0 $MOUNT
+
+       test_block_soft $TESTFILE $GRACE
+       $LFS setquota -u $USER 0 0 0 0 $MOUNT
+
+       echo "  Group quota (soft limit: $LIMIT bytes  grace: $GRACE seconds)"
+       $LFS setquota -t -g $GRACE $MAX_IQ_TIME $MOUNT
+       $LFS setquota -g $USER $LIMIT 0 0 0 $MOUNT
+       TESTFILE="$TSTDIR/quota_tst41"
+
+       test_block_soft $TESTFILE $GRACE
+       $LFS setquota -g $USER 0 0 0 0 $MOUNT
+       
        return 0
 }
 
+test_file_soft() {
+       TESTFILE=$1
+       LIMIT=$2
+       GRACE=$3
+
+       echo "    Create files to exceed soft limit"
+       for i in `seq $LIMIT`; do
+               $RUNAS touch ${TESTFILE}_$i >/dev/null 2>&1 || error "touch failure, but expect success"
+       done
+       echo "    Done"
+
+       echo "    Create file before timer goes off"
+       $RUNAS touch ${TESTFILE}_before >/dev/null 2>&1 || error "touch before timer goes off failure, but expect success"
+       echo "    Done"
+
+       echo "    Sleep $GRACE seconds ..."
+       sleep $GRACE
+       
+       echo "    Create file after timer goes off"
+       for i in `seq $(($IUNIT_SZ - 1))`; do
+               $RUNAS touch ${TESTFILE}_after_$i >/dev/null 2>&1 || error "touch ${TESTFILE}_after_$i failure, but expect success"
+       done
+       $RUNAS touch ${TESTFILE}_after >/dev/null 2>&1 && error "touch after timer goes off success, but expect EDQUOT"
+       echo "    EDQUOT"
+
+       echo "    Unlink files to stop timer"
+       for i in `seq $LIMIT`; do
+               rm -f ${TESTFILE}_$i >/dev/null 2>&1 || error "rm ${TESTFILE}_$i failure"
+       done
+       rm -f ${TESTFILE}_before
+       for i in `seq $(($IUNIT_SZ - 1))`; do
+               rm -f ${TESTFILE}_after_$i >/dev/null 2>&1 || error "rm ${TESTFILE}_after_$i failure"
+       done
+       echo "    Done"
+
+       echo "    Create file"
+       $RUNAS touch ${TESTFILE}_xxx >/dev/null 2>&1 || error "touch after timer stop failure, but expect success"
+       echo "    Done"
+
+       # cleanup
+       rm -f ${TESTFILE}_xxx
+}
+
 # file soft limit (start timer, timer goes off, stop timer)
 test_5() {
        echo "== File soft limit"
-       echo "  ** skipped"
+       LIMIT=$(($IUNIT_SZ * 10))       # 10 iunits on mds
+       TESTFILE="$TSTDIR/quota_tst50"
+       GRACE=5
+
+       echo "  User quota (soft limit: $LIMIT files  grace: $GRACE seconds)"
+       $LFS setquota -t -u $MAX_DQ_TIME $GRACE $MOUNT
+       $LFS setquota -u $USER 0 0 $LIMIT 0 $MOUNT
+
+       test_file_soft $TESTFILE $LIMIT $GRACE
+       $LFS setquota -u $USER 0 0 0 0 $MOUNT
+
+       echo "  Group quota (soft limit: $LIMIT files  grace: $GRACE seconds)"
+       $LFS setquota -t -g $MAX_DQ_TIME $GRACE $MOUNT
+       $LFS setquota -g $USER 0 0 $LIMIT 0 $MOUNT
+       TESTFILE="$TSTDIR/quota_tst51"
+
+       test_file_soft $TESTFILE $LIMIT $GRACE
+       $LFS setquota -g $USER 0 0 0 0 $MOUNT
+       
+       # cleanup
+       $LFS setquota -t -u $MAX_DQ_TIME $MAX_IQ_TIME $MOUNT
+       $LFS setquota -t -g $MAX_DQ_TIME $MAX_IQ_TIME $MOUNT
        return 0
 }
 
@@ -270,8 +406,8 @@ test_7() {
        $RUNAS dd if=/dev/zero of=$FILEB bs=$BLK_SZ seek=$LIMIT count=$BUNIT_SZ >/dev/null 2>&1 && error "write fileb success, but expect EDQUOT"
        sync; sleep 1; sync;
        echo "  Write to OST0 return EDQUOT"
-       # this write of OST0 is cache write, will success
-       $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || error "write filea failure, but expect success"
+       # this write maybe cache write, ignore it's failure
+       $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || echo " " > /dev/null
        sync; sleep 1; sync;
        $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) seek=$(($BUNIT_SZ *2)) >/dev/null 2>&1 && error "write filea success, but expect EDQUOT"
        echo "  EDQUOT"