Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[firefly-linux-kernel-4.4.55.git] / fs / xfs / xfs_log_cil.c
index f6b79e5325dd4426da54b940fd1bea0ca170a6e8..f506c457011eb1970128fa637dce35500d2a4b54 100644 (file)
@@ -463,12 +463,40 @@ xlog_cil_push(
                spin_unlock(&cil->xc_push_lock);
                goto out_skip;
        }
-       spin_unlock(&cil->xc_push_lock);
 
 
        /* check for a previously pushed seqeunce */
-       if (push_seq < cil->xc_ctx->sequence)
+       if (push_seq < cil->xc_ctx->sequence) {
+               spin_unlock(&cil->xc_push_lock);
                goto out_skip;
+       }
+
+       /*
+        * We are now going to push this context, so add it to the committing
+        * list before we do anything else. This ensures that anyone waiting on
+        * this push can easily detect the difference between a "push in
+        * progress" and "CIL is empty, nothing to do".
+        *
+        * IOWs, a wait loop can now check for:
+        *      the current sequence not being found on the committing list;
+        *      an empty CIL; and
+        *      an unchanged sequence number
+        * to detect a push that had nothing to do and therefore does not need
+        * waiting on. If the CIL is not empty, we get put on the committing
+        * list before emptying the CIL and bumping the sequence number. Hence
+        * an empty CIL and an unchanged sequence number means we jumped out
+        * above after doing nothing.
+        *
+        * Hence the waiter will either find the commit sequence on the
+        * committing list or the sequence number will be unchanged and the CIL
+        * still dirty. In that latter case, the push has not yet started, and
+        * so the waiter will have to continue trying to check the CIL
+        * committing list until it is found. In extreme cases of delay, the
+        * sequence may fully commit between the attempts the wait makes to wait
+        * on the commit sequence.
+        */
+       list_add(&ctx->committing, &cil->xc_committing);
+       spin_unlock(&cil->xc_push_lock);
 
        /*
         * pull all the log vectors off the items in the CIL, and
@@ -532,7 +560,6 @@ xlog_cil_push(
         */
        spin_lock(&cil->xc_push_lock);
        cil->xc_current_sequence = new_ctx->sequence;
-       list_add(&ctx->committing, &cil->xc_committing);
        spin_unlock(&cil->xc_push_lock);
        up_write(&cil->xc_ctx_lock);
 
@@ -855,13 +882,15 @@ restart:
         * Hence by the time we have got here it our sequence may not have been
         * pushed yet. This is true if the current sequence still matches the
         * push sequence after the above wait loop and the CIL still contains
-        * dirty objects.
+        * dirty objects. This is guaranteed by the push code first adding the
+        * context to the committing list before emptying the CIL.
         *
-        * When the push occurs, it will empty the CIL and atomically increment
-        * the currect sequence past the push sequence and move it into the
-        * committing list. Of course, if the CIL is clean at the time of the
-        * push, it won't have pushed the CIL at all, so in that case we should
-        * try the push for this sequence again from the start just in case.
+        * Hence if we don't find the context in the committing list and the
+        * current sequence number is unchanged then the CIL contents are
+        * significant.  If the CIL is empty, if means there was nothing to push
+        * and that means there is nothing to wait for. If the CIL is not empty,
+        * it means we haven't yet started the push, because if it had started
+        * we would have found the context on the committing list.
         */
        if (sequence == cil->xc_current_sequence &&
            !list_empty(&cil->xc_cil)) {