Whamcloud - gitweb
LU-1438 quota: fix race in quota_chk_acq_common()
authorNiu Yawei <niu@whamcloud.com>
Mon, 28 May 2012 09:12:08 +0000 (02:12 -0700)
committerJohann Lombardi <johann@whamcloud.com>
Wed, 30 May 2012 09:16:43 +0000 (05:16 -0400)
quota_check_common() & qctxt_adjust_qunit() uses different way
to check if quota is enforced on certain ID, which could result
in infinite loop in quota_chk_acq_common() when the QB/QI_SET
flag is cleared just after checking.

This patch used a non-instrusive way to fix this rare race.

Signed-off-by: Niu Yawei <niu@whamcloud.com>
Change-Id: I7212e9fc85e98a40e36d2773c02f838ca68339bb
Reviewed-on: http://review.whamcloud.com/2927
Tested-by: Hudson
Tested-by: Maloo <whamcloud.maloo@gmail.com>
Reviewed-by: Johann Lombardi <johann@whamcloud.com>
lustre/quota/quota_context.c

index 255dbd0..12fb024 100644 (file)
@@ -1132,7 +1132,31 @@ qctxt_adjust_qunit(struct obd_device *obd, struct lustre_quota_ctxt *qctxt,
         struct qunit_data qdata[MAXQUOTAS];
         ENTRY;
 
-        if (quota_is_set(obd, uid, gid, isblk ? QB_SET : QI_SET) == 0)
+        /* XXX In quota_chk_acq_common(), we do something like:
+         *
+         *     while (quota_check_common() & QUOTA_RET_ACQUOTA) {
+         *            rc = qctxt_adjust_qunit();
+         *     }
+         *
+         *     to make sure the slave acquired enough quota from master.
+         *
+         *     Unfortunately, qctxt_adjust_qunit() checks QB/QI_SET to
+         *     decide if do real DQACQ or not, but quota_check_common()
+         *     doesn't check QB/QI_SET flags. This inconsistence could
+         *     lead into an infinite loop.
+         *
+         *     We can't fix it by simply adding QB/QI_SET checking in the
+         *     quota_check_common(), since we must guarantee that the
+         *     paried quota_pending_commit() has same QB/QI_SET, but the
+         *     flags can be actually cleared at any time...
+         *
+         *     A quick non-intrusive solution is to just skip the
+         *     QB/QI_SET checking here when the @wait is non-zero.
+         *     (If the @wait is non-zero, the caller must have already
+         *     checked the QB/QI_SET).
+         */
+        if (!wait &&
+            quota_is_set(obd, uid, gid, isblk ? QB_SET : QI_SET) == 0)
                 RETURN(0);
 
         for (i = 0; i < MAXQUOTAS; i++) {