[PATCH] cfq-iosched: use last service point as the fairness criteria

Right now we use slice_start, which gives async queues an unfair
advantage. Chance that to service_last, and base the resorter
on that.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 975cdfc..7a8ef0f 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -146,9 +146,9 @@
 	/* fifo list of requests in sort_list */
 	struct list_head fifo;
 
-	unsigned long slice_start;
 	unsigned long slice_end;
 	unsigned long slice_left;
+	unsigned long service_last;
 
 	/* number of requests that are on the dispatch list */
 	int on_dispatch[2];
@@ -355,7 +355,8 @@
 static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted)
 {
 	struct cfq_data *cfqd = cfqq->cfqd;
-	struct list_head *list;
+	struct list_head *list, *n;
+	struct cfq_queue *__cfqq;
 
 	/*
 	 * Resorting requires the cfqq to be on the RR list already.
@@ -383,15 +384,13 @@
 			list = &cfqd->rr_list[cfqq->ioprio];
 	}
 
-	/*
-	 * If this queue was preempted or is new (never been serviced), let
-	 * it be added first for fairness but beind other new queues.
-	 * Otherwise, just add to the back  of the list.
-	 */
 	if (preempted || cfq_cfqq_queue_new(cfqq)) {
-		struct list_head *n = list;
-		struct cfq_queue *__cfqq;
-
+		/*
+		 * If this queue was preempted or is new (never been serviced),
+		 * let it be added first for fairness but beind other new
+		 * queues.
+		 */
+		n = list;
 		while (n->next != list) {
 			__cfqq = list_entry_cfqq(n->next);
 			if (!cfq_cfqq_queue_new(__cfqq))
@@ -399,11 +398,32 @@
 
 			n = n->next;
 		}
+		list_add_tail(&cfqq->cfq_list, n);
+	} else if (!cfq_cfqq_class_sync(cfqq)) {
+		/*
+		 * async queue always goes to the end. this wont be overly
+		 * unfair to writes, as the sort of the sync queue wont be
+		 * allowed to pass the async queue again.
+		 */
+		list_add_tail(&cfqq->cfq_list, list);
+	} else {
+		/*
+		 * sort by last service, but don't cross a new or async
+		 * queue. we don't cross a new queue because it hasn't been
+		 * service before, and we don't cross an async queue because
+		 * it gets added to the end on expire.
+		 */
+		n = list;
+		while ((n = n->prev) != list) {
+			struct cfq_queue *__cfqq = list_entry_cfqq(n);
 
-		list = n;
+			if (!cfq_cfqq_class_sync(cfqq) || !__cfqq->service_last)
+				break;
+			if (time_before(__cfqq->service_last, cfqq->service_last))
+				break;
+		}
+		list_add(&cfqq->cfq_list, n);
 	}
-
-	list_add_tail(&cfqq->cfq_list, list);
 }
 
 /*
@@ -608,7 +628,6 @@
 		 */
 		del_timer(&cfqd->idle_class_timer);
 
-		cfqq->slice_start = jiffies;
 		cfqq->slice_end = 0;
 		cfqq->slice_left = 0;
 		cfq_clear_cfqq_must_alloc_slice(cfqq);
@@ -1688,6 +1707,7 @@
 	WARN_ON(!cfqq->on_dispatch[sync]);
 	cfqd->rq_in_driver--;
 	cfqq->on_dispatch[sync]--;
+	cfqq->service_last = now;
 
 	if (!cfq_class_idle(cfqq))
 		cfqd->last_end_request = now;