diff options
author | Jens Axboe <jens.axboe@oracle.com> | 2007-02-05 11:56:25 +0100 |
---|---|---|
committer | Jens Axboe <axboe@carl.home.kernel.dk> | 2007-02-11 23:14:45 +0100 |
commit | 99f9628aba4d8fb3b8d955c9efded0d0a1995fad (patch) | |
tree | e1f0547190c0872bdb99a31d6750d739739d2ffe /block | |
parent | cfq-iosched: document the cfqq flags (diff) | |
download | linux-99f9628aba4d8fb3b8d955c9efded0d0a1995fad.tar.xz linux-99f9628aba4d8fb3b8d955c9efded0d0a1995fad.zip |
[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>
Diffstat (limited to 'block')
-rw-r--r-- | block/cfq-iosched.c | 48 |
1 files changed, 34 insertions, 14 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 975cdfc8d614..7a8ef0f09699 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -146,9 +146,9 @@ struct cfq_queue { /* 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 @@ cfq_find_next_rq(struct cfq_data *cfqd, struct cfq_queue *cfqq, 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 @@ static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) 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 @@ static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) 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 @@ __cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) */ 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 @@ static void cfq_completed_request(request_queue_t *q, struct request *rq) 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; |