diff options
author | Jens Axboe <axboe@suse.de> | 2006-07-28 03:48:51 -0400 |
---|---|---|
committer | Jens Axboe <axboe@nelson.home.kernel.dk> | 2006-09-30 14:29:39 -0400 |
commit | 53b03744e5699832e6c5b04f2ec506d8b0c50c38 (patch) | |
tree | 7205d972bf6f25be3a3f2c360130b269dadcb3b2 | |
parent | b5deef901282628d88c784f4c9d2f0583ec3b355 (diff) |
[PATCH] cfq-iosched: Kill O(N) runtime of cfq_resort_rr_list()
Currently it scales with number of processes in that priority group,
which is potentially not very nice as it's called quite often.
Basically we always need to do tail inserts, except for the case of a
new process. So just mark/detect a queue as such.
Signed-off-by: Jens Axboe <axboe@suse.de>
-rw-r--r-- | block/cfq-iosched.c | 53 |
1 files changed, 22 insertions, 31 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 0452108a932e..c6e649f3cae7 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c | |||
@@ -152,7 +152,6 @@ struct cfq_queue { | |||
152 | unsigned long slice_start; | 152 | unsigned long slice_start; |
153 | unsigned long slice_end; | 153 | unsigned long slice_end; |
154 | unsigned long slice_left; | 154 | unsigned long slice_left; |
155 | unsigned long service_last; | ||
156 | 155 | ||
157 | /* number of requests that are on the dispatch list */ | 156 | /* number of requests that are on the dispatch list */ |
158 | int on_dispatch[2]; | 157 | int on_dispatch[2]; |
@@ -174,6 +173,7 @@ enum cfqq_state_flags { | |||
174 | CFQ_CFQQ_FLAG_fifo_expire, | 173 | CFQ_CFQQ_FLAG_fifo_expire, |
175 | CFQ_CFQQ_FLAG_idle_window, | 174 | CFQ_CFQQ_FLAG_idle_window, |
176 | CFQ_CFQQ_FLAG_prio_changed, | 175 | CFQ_CFQQ_FLAG_prio_changed, |
176 | CFQ_CFQQ_FLAG_queue_new, | ||
177 | }; | 177 | }; |
178 | 178 | ||
179 | #define CFQ_CFQQ_FNS(name) \ | 179 | #define CFQ_CFQQ_FNS(name) \ |
@@ -198,6 +198,7 @@ CFQ_CFQQ_FNS(must_dispatch); | |||
198 | CFQ_CFQQ_FNS(fifo_expire); | 198 | CFQ_CFQQ_FNS(fifo_expire); |
199 | CFQ_CFQQ_FNS(idle_window); | 199 | CFQ_CFQQ_FNS(idle_window); |
200 | CFQ_CFQQ_FNS(prio_changed); | 200 | CFQ_CFQQ_FNS(prio_changed); |
201 | CFQ_CFQQ_FNS(queue_new); | ||
201 | #undef CFQ_CFQQ_FNS | 202 | #undef CFQ_CFQQ_FNS |
202 | 203 | ||
203 | static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short); | 204 | static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short); |
@@ -350,7 +351,7 @@ cfq_find_next_rq(struct cfq_data *cfqd, struct cfq_queue *cfqq, | |||
350 | static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) | 351 | static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) |
351 | { | 352 | { |
352 | struct cfq_data *cfqd = cfqq->cfqd; | 353 | struct cfq_data *cfqd = cfqq->cfqd; |
353 | struct list_head *list, *entry; | 354 | struct list_head *list; |
354 | 355 | ||
355 | BUG_ON(!cfq_cfqq_on_rr(cfqq)); | 356 | BUG_ON(!cfq_cfqq_on_rr(cfqq)); |
356 | 357 | ||
@@ -375,31 +376,26 @@ static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted) | |||
375 | } | 376 | } |
376 | 377 | ||
377 | /* | 378 | /* |
378 | * if queue was preempted, just add to front to be fair. busy_rr | 379 | * If this queue was preempted or is new (never been serviced), let |
379 | * isn't sorted, but insert at the back for fairness. | 380 | * it be added first for fairness but beind other new queues. |
381 | * Otherwise, just add to the back of the list. | ||
380 | */ | 382 | */ |
381 | if (preempted || list == &cfqd->busy_rr) { | 383 | if (preempted || cfq_cfqq_queue_new(cfqq)) { |
382 | if (preempted) | 384 | struct list_head *n = list; |
383 | list = list->prev; | 385 | struct cfq_queue *__cfqq; |
384 | 386 | ||
385 | list_add_tail(&cfqq->cfq_list, list); | 387 | while (n->next != list) { |
386 | return; | 388 | __cfqq = list_entry_cfqq(n->next); |
387 | } | 389 | if (!cfq_cfqq_queue_new(__cfqq)) |
390 | break; | ||
388 | 391 | ||
389 | /* | 392 | n = n->next; |
390 | * sort by when queue was last serviced | 393 | } |
391 | */ | ||
392 | entry = list; | ||
393 | while ((entry = entry->prev) != list) { | ||
394 | struct cfq_queue *__cfqq = list_entry_cfqq(entry); | ||
395 | 394 | ||
396 | if (!__cfqq->service_last) | 395 | list = n; |
397 | break; | ||
398 | if (time_before(__cfqq->service_last, cfqq->service_last)) | ||
399 | break; | ||
400 | } | 396 | } |
401 | 397 | ||
402 | list_add(&cfqq->cfq_list, entry); | 398 | list_add_tail(&cfqq->cfq_list, list); |
403 | } | 399 | } |
404 | 400 | ||
405 | /* | 401 | /* |
@@ -591,13 +587,12 @@ __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, | |||
591 | if (cfq_cfqq_wait_request(cfqq)) | 587 | if (cfq_cfqq_wait_request(cfqq)) |
592 | del_timer(&cfqd->idle_slice_timer); | 588 | del_timer(&cfqd->idle_slice_timer); |
593 | 589 | ||
594 | if (!preempted && !cfq_cfqq_dispatched(cfqq)) { | 590 | if (!preempted && !cfq_cfqq_dispatched(cfqq)) |
595 | cfqq->service_last = now; | ||
596 | cfq_schedule_dispatch(cfqd); | 591 | cfq_schedule_dispatch(cfqd); |
597 | } | ||
598 | 592 | ||
599 | cfq_clear_cfqq_must_dispatch(cfqq); | 593 | cfq_clear_cfqq_must_dispatch(cfqq); |
600 | cfq_clear_cfqq_wait_request(cfqq); | 594 | cfq_clear_cfqq_wait_request(cfqq); |
595 | cfq_clear_cfqq_queue_new(cfqq); | ||
601 | 596 | ||
602 | /* | 597 | /* |
603 | * store what was left of this slice, if the queue idled out | 598 | * store what was left of this slice, if the queue idled out |
@@ -1297,13 +1292,13 @@ retry: | |||
1297 | hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); | 1292 | hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); |
1298 | atomic_set(&cfqq->ref, 0); | 1293 | atomic_set(&cfqq->ref, 0); |
1299 | cfqq->cfqd = cfqd; | 1294 | cfqq->cfqd = cfqd; |
1300 | cfqq->service_last = 0; | ||
1301 | /* | 1295 | /* |
1302 | * set ->slice_left to allow preemption for a new process | 1296 | * set ->slice_left to allow preemption for a new process |
1303 | */ | 1297 | */ |
1304 | cfqq->slice_left = 2 * cfqd->cfq_slice_idle; | 1298 | cfqq->slice_left = 2 * cfqd->cfq_slice_idle; |
1305 | cfq_mark_cfqq_idle_window(cfqq); | 1299 | cfq_mark_cfqq_idle_window(cfqq); |
1306 | cfq_mark_cfqq_prio_changed(cfqq); | 1300 | cfq_mark_cfqq_prio_changed(cfqq); |
1301 | cfq_mark_cfqq_queue_new(cfqq); | ||
1307 | cfq_init_prio_data(cfqq); | 1302 | cfq_init_prio_data(cfqq); |
1308 | } | 1303 | } |
1309 | 1304 | ||
@@ -1672,12 +1667,8 @@ static void cfq_completed_request(request_queue_t *q, struct request *rq) | |||
1672 | if (!cfq_class_idle(cfqq)) | 1667 | if (!cfq_class_idle(cfqq)) |
1673 | cfqd->last_end_request = now; | 1668 | cfqd->last_end_request = now; |
1674 | 1669 | ||
1675 | if (!cfq_cfqq_dispatched(cfqq)) { | 1670 | if (!cfq_cfqq_dispatched(cfqq) && cfq_cfqq_on_rr(cfqq)) |
1676 | if (cfq_cfqq_on_rr(cfqq)) { | 1671 | cfq_resort_rr_list(cfqq, 0); |
1677 | cfqq->service_last = now; | ||
1678 | cfq_resort_rr_list(cfqq, 0); | ||
1679 | } | ||
1680 | } | ||
1681 | 1672 | ||
1682 | if (sync) | 1673 | if (sync) |
1683 | RQ_CIC(rq)->last_end_request = now; | 1674 | RQ_CIC(rq)->last_end_request = now; |