xfs: Sort delayed write buffers before dispatch
[deliverable/linux.git] / fs / xfs / linux-2.6 / xfs_buf.c
index b306265caa337b954cebe82698c3b923179bd301..4556a4c31e36f8eded1f1bcf50df1af19450ee3d 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/migrate.h>
 #include <linux/backing-dev.h>
 #include <linux/freezer.h>
+#include <linux/list_sort.h>
 
 #include "xfs_sb.h"
 #include "xfs_inum.h"
@@ -1877,14 +1878,42 @@ xfs_buf_delwri_split(
 
 }
 
+/*
+ * Compare function is more complex than it needs to be because
+ * the return value is only 32 bits and we are doing comparisons
+ * on 64 bit values
+ */
+static int
+xfs_buf_cmp(
+       void            *priv,
+       struct list_head *a,
+       struct list_head *b)
+{
+       struct xfs_buf  *ap = container_of(a, struct xfs_buf, b_list);
+       struct xfs_buf  *bp = container_of(b, struct xfs_buf, b_list);
+       xfs_daddr_t             diff;
+
+       diff = ap->b_bn - bp->b_bn;
+       if (diff < 0)
+               return -1;
+       if (diff > 0)
+               return 1;
+       return 0;
+}
+
+void
+xfs_buf_delwri_sort(
+       xfs_buftarg_t   *target,
+       struct list_head *list)
+{
+       list_sort(NULL, list, xfs_buf_cmp);
+}
+
 STATIC int
 xfsbufd(
        void            *data)
 {
-       struct list_head tmp;
-       xfs_buftarg_t   *target = (xfs_buftarg_t *)data;
-       int             count;
-       xfs_buf_t       *bp;
+       xfs_buftarg_t   *target = (xfs_buftarg_t *)data;
 
        current->flags |= PF_MEMALLOC;
 
@@ -1893,6 +1922,8 @@ xfsbufd(
        do {
                long    age = xfs_buf_age_centisecs * msecs_to_jiffies(10);
                long    tout = xfs_buf_timer_centisecs * msecs_to_jiffies(10);
+               int     count = 0;
+               struct list_head tmp;
 
                if (unlikely(freezing(current))) {
                        set_bit(XBT_FORCE_SLEEP, &target->bt_flags);
@@ -1907,11 +1938,10 @@ xfsbufd(
                schedule_timeout_interruptible(tout);
 
                xfs_buf_delwri_split(target, &tmp, age);
-               count = 0;
+               list_sort(NULL, &tmp, xfs_buf_cmp);
                while (!list_empty(&tmp)) {
-                       bp = list_entry(tmp.next, xfs_buf_t, b_list);
-                       ASSERT(target == bp->b_target);
-
+                       struct xfs_buf *bp;
+                       bp = list_first_entry(&tmp, struct xfs_buf, b_list);
                        list_del_init(&bp->b_list);
                        xfs_buf_iostrategy(bp);
                        count++;
@@ -1937,42 +1967,45 @@ xfs_flush_buftarg(
        xfs_buftarg_t   *target,
        int             wait)
 {
-       struct list_head tmp;
-       xfs_buf_t       *bp, *n;
+       xfs_buf_t       *bp;
        int             pincount = 0;
+       LIST_HEAD(tmp_list);
+       LIST_HEAD(wait_list);
 
        xfs_buf_runall_queues(xfsconvertd_workqueue);
        xfs_buf_runall_queues(xfsdatad_workqueue);
        xfs_buf_runall_queues(xfslogd_workqueue);
 
        set_bit(XBT_FORCE_FLUSH, &target->bt_flags);
-       pincount = xfs_buf_delwri_split(target, &tmp, 0);
+       pincount = xfs_buf_delwri_split(target, &tmp_list, 0);
 
        /*
-        * Dropped the delayed write list lock, now walk the temporary list
+        * Dropped the delayed write list lock, now walk the temporary list.
+        * All I/O is issued async and then if we need to wait for completion
+        * we do that after issuing all the IO.
         */
-       list_for_each_entry_safe(bp, n, &tmp, b_list) {
+       list_sort(NULL, &tmp_list, xfs_buf_cmp);
+       while (!list_empty(&tmp_list)) {
+               bp = list_first_entry(&tmp_list, struct xfs_buf, b_list);
                ASSERT(target == bp->b_target);
-               if (wait)
+               list_del_init(&bp->b_list);
+               if (wait) {
                        bp->b_flags &= ~XBF_ASYNC;
-               else
-                       list_del_init(&bp->b_list);
-
+                       list_add(&bp->b_list, &wait_list);
+               }
                xfs_buf_iostrategy(bp);
        }
 
-       if (wait)
+       if (wait) {
+               /* Expedite and wait for IO to complete. */
                blk_run_address_space(target->bt_mapping);
+               while (!list_empty(&wait_list)) {
+                       bp = list_first_entry(&wait_list, struct xfs_buf, b_list);
 
-       /*
-        * Remaining list items must be flushed before returning
-        */
-       while (!list_empty(&tmp)) {
-               bp = list_entry(tmp.next, xfs_buf_t, b_list);
-
-               list_del_init(&bp->b_list);
-               xfs_iowait(bp);
-               xfs_buf_relse(bp);
+                       list_del_init(&bp->b_list);
+                       xfs_iowait(bp);
+                       xfs_buf_relse(bp);
+               }
        }
 
        return pincount;
This page took 0.0288 seconds and 5 git commands to generate.