diff options
| author | Jens Axboe <axboe@suse.de> | 2006-06-16 05:23:00 -0400 |
|---|---|---|
| committer | Jens Axboe <axboe@nelson.home.kernel.dk> | 2006-06-23 11:10:39 -0400 |
| commit | caaa5f9f0a75d1dc5e812e69afdbb8720e077fd3 (patch) | |
| tree | 07aba6bac5a57cb3efd23762acfd719e255775ab /block | |
| parent | 35e6077cb16f93517ba5a51ba849b186d2474d60 (diff) | |
[PATCH] cfq-iosched: many performance fixes
This is a collection of patches that greatly improve CFQ performance
in some circumstances.
- Change the idling logic to only kick in after a request is done and we
are deciding what to do. Before the idling included the request service
time, so it was hard to adjust. Now it's true think/idle time.
- Take advantage of TCQ/NCQ/queueing for seeky sync workloads, but keep
it in control for sync and sequential (or close to) workloads.
- Expire queues immediately and move on to other busy queues, if we are
not going to idle after the current one finishes.
- Don't rearm idle timer if there are no busy queues. Just leave the
system idle.
Signed-off-by: Jens Axboe <axboe@suse.de>
Diffstat (limited to 'block')
| -rw-r--r-- | block/cfq-iosched.c | 116 |
1 files changed, 76 insertions, 40 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 84b75f88c279..13c4793fdf5f 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c | |||
| @@ -26,7 +26,7 @@ static const int cfq_back_penalty = 2; /* penalty of a backwards seek */ | |||
| 26 | static const int cfq_slice_sync = HZ / 10; | 26 | static const int cfq_slice_sync = HZ / 10; |
| 27 | static int cfq_slice_async = HZ / 25; | 27 | static int cfq_slice_async = HZ / 25; |
| 28 | static const int cfq_slice_async_rq = 2; | 28 | static const int cfq_slice_async_rq = 2; |
| 29 | static int cfq_slice_idle = HZ / 70; | 29 | static int cfq_slice_idle = HZ / 125; |
| 30 | 30 | ||
| 31 | #define CFQ_IDLE_GRACE (HZ / 10) | 31 | #define CFQ_IDLE_GRACE (HZ / 10) |
| 32 | #define CFQ_SLICE_SCALE (5) | 32 | #define CFQ_SLICE_SCALE (5) |
| @@ -906,6 +906,8 @@ static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd) | |||
| 906 | return cfqq; | 906 | return cfqq; |
| 907 | } | 907 | } |
| 908 | 908 | ||
| 909 | #define CIC_SEEKY(cic) ((cic)->seek_mean > (128 * 1024)) | ||
| 910 | |||
| 909 | static int cfq_arm_slice_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq) | 911 | static int cfq_arm_slice_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq) |
| 910 | 912 | ||
| 911 | { | 913 | { |
| @@ -939,7 +941,7 @@ static int cfq_arm_slice_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq) | |||
| 939 | * fair distribution of slice time for a process doing back-to-back | 941 | * fair distribution of slice time for a process doing back-to-back |
| 940 | * seeks. so allow a little bit of time for him to submit a new rq | 942 | * seeks. so allow a little bit of time for him to submit a new rq |
| 941 | */ | 943 | */ |
| 942 | if (sample_valid(cic->seek_samples) && cic->seek_mean > 131072) | 944 | if (sample_valid(cic->seek_samples) && CIC_SEEKY(cic)) |
| 943 | sl = 2; | 945 | sl = 2; |
| 944 | 946 | ||
| 945 | mod_timer(&cfqd->idle_slice_timer, jiffies + sl); | 947 | mod_timer(&cfqd->idle_slice_timer, jiffies + sl); |
| @@ -1038,8 +1040,10 @@ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd) | |||
| 1038 | */ | 1040 | */ |
| 1039 | if (!RB_EMPTY(&cfqq->sort_list)) | 1041 | if (!RB_EMPTY(&cfqq->sort_list)) |
| 1040 | goto keep_queue; | 1042 | goto keep_queue; |
| 1041 | else if (cfq_cfqq_class_sync(cfqq) && | 1043 | else if (cfq_cfqq_dispatched(cfqq)) { |
| 1042 | time_before(now, cfqq->slice_end)) { | 1044 | cfqq = NULL; |
| 1045 | goto keep_queue; | ||
| 1046 | } else if (cfq_cfqq_class_sync(cfqq)) { | ||
| 1043 | if (cfq_arm_slice_timer(cfqd, cfqq)) | 1047 | if (cfq_arm_slice_timer(cfqd, cfqq)) |
| 1044 | return NULL; | 1048 | return NULL; |
| 1045 | } | 1049 | } |
| @@ -1088,8 +1092,7 @@ __cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq, | |||
| 1088 | } while (dispatched < max_dispatch); | 1092 | } while (dispatched < max_dispatch); |
| 1089 | 1093 | ||
| 1090 | /* | 1094 | /* |
| 1091 | * if slice end isn't set yet, set it. if at least one request was | 1095 | * if slice end isn't set yet, set it. |
| 1092 | * sync, use the sync time slice value | ||
| 1093 | */ | 1096 | */ |
| 1094 | if (!cfqq->slice_end) | 1097 | if (!cfqq->slice_end) |
| 1095 | cfq_set_prio_slice(cfqd, cfqq); | 1098 | cfq_set_prio_slice(cfqd, cfqq); |
| @@ -1100,7 +1103,8 @@ __cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq, | |||
| 1100 | */ | 1103 | */ |
| 1101 | if ((!cfq_cfqq_sync(cfqq) && | 1104 | if ((!cfq_cfqq_sync(cfqq) && |
| 1102 | cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) || | 1105 | cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) || |
| 1103 | cfq_class_idle(cfqq)) | 1106 | cfq_class_idle(cfqq) || |
| 1107 | !cfq_cfqq_idle_window(cfqq)) | ||
| 1104 | cfq_slice_expired(cfqd, 0); | 1108 | cfq_slice_expired(cfqd, 0); |
| 1105 | 1109 | ||
| 1106 | return dispatched; | 1110 | return dispatched; |
| @@ -1109,10 +1113,11 @@ __cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq, | |||
| 1109 | static int | 1113 | static int |
| 1110 | cfq_forced_dispatch_cfqqs(struct list_head *list) | 1114 | cfq_forced_dispatch_cfqqs(struct list_head *list) |
| 1111 | { | 1115 | { |
| 1112 | int dispatched = 0; | ||
| 1113 | struct cfq_queue *cfqq, *next; | 1116 | struct cfq_queue *cfqq, *next; |
| 1114 | struct cfq_rq *crq; | 1117 | struct cfq_rq *crq; |
| 1118 | int dispatched; | ||
| 1115 | 1119 | ||
| 1120 | dispatched = 0; | ||
| 1116 | list_for_each_entry_safe(cfqq, next, list, cfq_list) { | 1121 | list_for_each_entry_safe(cfqq, next, list, cfq_list) { |
| 1117 | while ((crq = cfqq->next_crq)) { | 1122 | while ((crq = cfqq->next_crq)) { |
| 1118 | cfq_dispatch_insert(cfqq->cfqd->queue, crq); | 1123 | cfq_dispatch_insert(cfqq->cfqd->queue, crq); |
| @@ -1120,6 +1125,7 @@ cfq_forced_dispatch_cfqqs(struct list_head *list) | |||
| 1120 | } | 1125 | } |
| 1121 | BUG_ON(!list_empty(&cfqq->fifo)); | 1126 | BUG_ON(!list_empty(&cfqq->fifo)); |
| 1122 | } | 1127 | } |
| 1128 | |||
| 1123 | return dispatched; | 1129 | return dispatched; |
| 1124 | } | 1130 | } |
| 1125 | 1131 | ||
| @@ -1146,7 +1152,8 @@ static int | |||
| 1146 | cfq_dispatch_requests(request_queue_t *q, int force) | 1152 | cfq_dispatch_requests(request_queue_t *q, int force) |
| 1147 | { | 1153 | { |
| 1148 | struct cfq_data *cfqd = q->elevator->elevator_data; | 1154 | struct cfq_data *cfqd = q->elevator->elevator_data; |
| 1149 | struct cfq_queue *cfqq; | 1155 | struct cfq_queue *cfqq, *prev_cfqq; |
| 1156 | int dispatched; | ||
| 1150 | 1157 | ||
| 1151 | if (!cfqd->busy_queues) | 1158 | if (!cfqd->busy_queues) |
| 1152 | return 0; | 1159 | return 0; |
| @@ -1154,10 +1161,17 @@ cfq_dispatch_requests(request_queue_t *q, int force) | |||
| 1154 | if (unlikely(force)) | 1161 | if (unlikely(force)) |
| 1155 | return cfq_forced_dispatch(cfqd); | 1162 | return cfq_forced_dispatch(cfqd); |
| 1156 | 1163 | ||
| 1157 | cfqq = cfq_select_queue(cfqd); | 1164 | dispatched = 0; |
| 1158 | if (cfqq) { | 1165 | prev_cfqq = NULL; |
| 1166 | while ((cfqq = cfq_select_queue(cfqd)) != NULL) { | ||
| 1159 | int max_dispatch; | 1167 | int max_dispatch; |
| 1160 | 1168 | ||
| 1169 | /* | ||
| 1170 | * Don't repeat dispatch from the previous queue. | ||
| 1171 | */ | ||
| 1172 | if (prev_cfqq == cfqq) | ||
| 1173 | break; | ||
| 1174 | |||
| 1161 | cfq_clear_cfqq_must_dispatch(cfqq); | 1175 | cfq_clear_cfqq_must_dispatch(cfqq); |
| 1162 | cfq_clear_cfqq_wait_request(cfqq); | 1176 | cfq_clear_cfqq_wait_request(cfqq); |
| 1163 | del_timer(&cfqd->idle_slice_timer); | 1177 | del_timer(&cfqd->idle_slice_timer); |
| @@ -1166,10 +1180,19 @@ cfq_dispatch_requests(request_queue_t *q, int force) | |||
| 1166 | if (cfq_class_idle(cfqq)) | 1180 | if (cfq_class_idle(cfqq)) |
| 1167 | max_dispatch = 1; | 1181 | max_dispatch = 1; |
| 1168 | 1182 | ||
| 1169 | return __cfq_dispatch_requests(cfqd, cfqq, max_dispatch); | 1183 | dispatched += __cfq_dispatch_requests(cfqd, cfqq, max_dispatch); |
| 1184 | |||
| 1185 | /* | ||
| 1186 | * If the dispatch cfqq has idling enabled and is still | ||
| 1187 | * the active queue, break out. | ||
| 1188 | */ | ||
| 1189 | if (cfq_cfqq_idle_window(cfqq) && cfqd->active_queue) | ||
| 1190 | break; | ||
| 1191 | |||
| 1192 | prev_cfqq = cfqq; | ||
| 1170 | } | 1193 | } |
| 1171 | 1194 | ||
| 1172 | return 0; | 1195 | return dispatched; |
| 1173 | } | 1196 | } |
| 1174 | 1197 | ||
| 1175 | /* | 1198 | /* |
| @@ -1375,24 +1398,28 @@ static inline void changed_ioprio(struct cfq_io_context *cic) | |||
| 1375 | { | 1398 | { |
| 1376 | struct cfq_data *cfqd = cic->key; | 1399 | struct cfq_data *cfqd = cic->key; |
| 1377 | struct cfq_queue *cfqq; | 1400 | struct cfq_queue *cfqq; |
| 1378 | if (cfqd) { | ||
| 1379 | spin_lock(cfqd->queue->queue_lock); | ||
| 1380 | cfqq = cic->cfqq[ASYNC]; | ||
| 1381 | if (cfqq) { | ||
| 1382 | struct cfq_queue *new_cfqq; | ||
| 1383 | new_cfqq = cfq_get_queue(cfqd, CFQ_KEY_ASYNC, | ||
| 1384 | cic->ioc->task, GFP_ATOMIC); | ||
| 1385 | if (new_cfqq) { | ||
| 1386 | cic->cfqq[ASYNC] = new_cfqq; | ||
| 1387 | cfq_put_queue(cfqq); | ||
| 1388 | } | ||
| 1389 | } | ||
| 1390 | cfqq = cic->cfqq[SYNC]; | ||
| 1391 | if (cfqq) | ||
| 1392 | cfq_mark_cfqq_prio_changed(cfqq); | ||
| 1393 | 1401 | ||
| 1394 | spin_unlock(cfqd->queue->queue_lock); | 1402 | if (unlikely(!cfqd)) |
| 1403 | return; | ||
| 1404 | |||
| 1405 | spin_lock(cfqd->queue->queue_lock); | ||
| 1406 | |||
| 1407 | cfqq = cic->cfqq[ASYNC]; | ||
| 1408 | if (cfqq) { | ||
| 1409 | struct cfq_queue *new_cfqq; | ||
| 1410 | new_cfqq = cfq_get_queue(cfqd, CFQ_KEY_ASYNC, cic->ioc->task, | ||
| 1411 | GFP_ATOMIC); | ||
| 1412 | if (new_cfqq) { | ||
| 1413 | cic->cfqq[ASYNC] = new_cfqq; | ||
| 1414 | cfq_put_queue(cfqq); | ||
| 1415 | } | ||
| 1395 | } | 1416 | } |
| 1417 | |||
| 1418 | cfqq = cic->cfqq[SYNC]; | ||
| 1419 | if (cfqq) | ||
| 1420 | cfq_mark_cfqq_prio_changed(cfqq); | ||
| 1421 | |||
| 1422 | spin_unlock(cfqd->queue->queue_lock); | ||
| 1396 | } | 1423 | } |
| 1397 | 1424 | ||
| 1398 | /* | 1425 | /* |
| @@ -1461,8 +1488,7 @@ retry: | |||
| 1461 | * set ->slice_left to allow preemption for a new process | 1488 | * set ->slice_left to allow preemption for a new process |
| 1462 | */ | 1489 | */ |
| 1463 | cfqq->slice_left = 2 * cfqd->cfq_slice_idle; | 1490 | cfqq->slice_left = 2 * cfqd->cfq_slice_idle; |
| 1464 | if (!cfqd->hw_tag) | 1491 | cfq_mark_cfqq_idle_window(cfqq); |
| 1465 | cfq_mark_cfqq_idle_window(cfqq); | ||
| 1466 | cfq_mark_cfqq_prio_changed(cfqq); | 1492 | cfq_mark_cfqq_prio_changed(cfqq); |
| 1467 | cfq_init_prio_data(cfqq); | 1493 | cfq_init_prio_data(cfqq); |
| 1468 | } | 1494 | } |
| @@ -1653,7 +1679,8 @@ cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, | |||
| 1653 | { | 1679 | { |
| 1654 | int enable_idle = cfq_cfqq_idle_window(cfqq); | 1680 | int enable_idle = cfq_cfqq_idle_window(cfqq); |
| 1655 | 1681 | ||
| 1656 | if (!cic->ioc->task || !cfqd->cfq_slice_idle || cfqd->hw_tag) | 1682 | if (!cic->ioc->task || !cfqd->cfq_slice_idle || |
| 1683 | (cfqd->hw_tag && CIC_SEEKY(cic))) | ||
| 1657 | enable_idle = 0; | 1684 | enable_idle = 0; |
| 1658 | else if (sample_valid(cic->ttime_samples)) { | 1685 | else if (sample_valid(cic->ttime_samples)) { |
| 1659 | if (cic->ttime_mean > cfqd->cfq_slice_idle) | 1686 | if (cic->ttime_mean > cfqd->cfq_slice_idle) |
| @@ -1683,7 +1710,7 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, | |||
| 1683 | return 0; | 1710 | return 0; |
| 1684 | 1711 | ||
| 1685 | if (!cfqq) | 1712 | if (!cfqq) |
| 1686 | return 1; | 1713 | return 0; |
| 1687 | 1714 | ||
| 1688 | if (cfq_class_idle(cfqq)) | 1715 | if (cfq_class_idle(cfqq)) |
| 1689 | return 1; | 1716 | return 1; |
| @@ -1715,7 +1742,7 @@ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) | |||
| 1715 | cfqq->slice_left = cfq_prio_to_slice(cfqd, cfqq) / 2; | 1742 | cfqq->slice_left = cfq_prio_to_slice(cfqd, cfqq) / 2; |
| 1716 | 1743 | ||
| 1717 | cfqq->slice_end = cfqq->slice_left + jiffies; | 1744 | cfqq->slice_end = cfqq->slice_left + jiffies; |
| 1718 | __cfq_slice_expired(cfqd, cfqq, 1); | 1745 | cfq_slice_expired(cfqd, 1); |
| 1719 | __cfq_set_active_queue(cfqd, cfqq); | 1746 | __cfq_set_active_queue(cfqd, cfqq); |
| 1720 | } | 1747 | } |
| 1721 | 1748 | ||
| @@ -1834,11 +1861,23 @@ static void cfq_completed_request(request_queue_t *q, struct request *rq) | |||
| 1834 | cfqq->service_last = now; | 1861 | cfqq->service_last = now; |
| 1835 | cfq_resort_rr_list(cfqq, 0); | 1862 | cfq_resort_rr_list(cfqq, 0); |
| 1836 | } | 1863 | } |
| 1837 | cfq_schedule_dispatch(cfqd); | ||
| 1838 | } | 1864 | } |
| 1839 | 1865 | ||
| 1840 | if (cfq_crq_is_sync(crq)) | 1866 | if (sync) |
| 1841 | crq->io_context->last_end_request = now; | 1867 | crq->io_context->last_end_request = now; |
| 1868 | |||
| 1869 | /* | ||
| 1870 | * If this is the active queue, check if it needs to be expired, | ||
| 1871 | * or if we want to idle in case it has no pending requests. | ||
| 1872 | */ | ||
| 1873 | if (cfqd->active_queue == cfqq) { | ||
| 1874 | if (time_after(now, cfqq->slice_end)) | ||
| 1875 | cfq_slice_expired(cfqd, 0); | ||
| 1876 | else if (sync && RB_EMPTY(&cfqq->sort_list)) { | ||
| 1877 | if (!cfq_arm_slice_timer(cfqd, cfqq)) | ||
| 1878 | cfq_schedule_dispatch(cfqd); | ||
| 1879 | } | ||
| 1880 | } | ||
| 1842 | } | 1881 | } |
| 1843 | 1882 | ||
| 1844 | static struct request * | 1883 | static struct request * |
| @@ -2106,11 +2145,8 @@ static void cfq_idle_slice_timer(unsigned long data) | |||
| 2106 | * only expire and reinvoke request handler, if there are | 2145 | * only expire and reinvoke request handler, if there are |
| 2107 | * other queues with pending requests | 2146 | * other queues with pending requests |
| 2108 | */ | 2147 | */ |
| 2109 | if (!cfqd->busy_queues) { | 2148 | if (!cfqd->busy_queues) |
| 2110 | cfqd->idle_slice_timer.expires = min(now + cfqd->cfq_slice_idle, cfqq->slice_end); | ||
| 2111 | add_timer(&cfqd->idle_slice_timer); | ||
| 2112 | goto out_cont; | 2149 | goto out_cont; |
| 2113 | } | ||
| 2114 | 2150 | ||
| 2115 | /* | 2151 | /* |
| 2116 | * not expired and it has a request pending, let it dispatch | 2152 | * not expired and it has a request pending, let it dispatch |
