diff options
author | Jens Axboe <axboe@suse.de> | 2006-07-28 03:23:08 -0400 |
---|---|---|
committer | Jens Axboe <axboe@nelson.home.kernel.dk> | 2006-09-30 14:26:56 -0400 |
commit | 9817064b68fef7e4580c6df1ea597e106b9ff88b (patch) | |
tree | 76c27990626247613e9efa45b792d51ad79635d7 /block/as-iosched.c | |
parent | 4aff5e2333c9a1609662f2091f55c3f6fffdad36 (diff) |
[PATCH] elevator: move the backmerging logic into the elevator core
Right now, every IO scheduler implements its own backmerging (except for
noop, which does no merging). That results in duplicated code for
essentially the same operation, which is never a good thing. This patch
moves the backmerging out of the io schedulers and into the elevator
core. We save 1.6kb of text and as a bonus get backmerging for noop as
well. Win-win!
Signed-off-by: Jens Axboe <axboe@suse.de>
Diffstat (limited to 'block/as-iosched.c')
-rw-r--r-- | block/as-iosched.c | 139 |
1 files changed, 3 insertions, 136 deletions
diff --git a/block/as-iosched.c b/block/as-iosched.c index ad1cc4077819..6db494333c3a 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c | |||
@@ -14,7 +14,6 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/compiler.h> | 16 | #include <linux/compiler.h> |
17 | #include <linux/hash.h> | ||
18 | #include <linux/rbtree.h> | 17 | #include <linux/rbtree.h> |
19 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
20 | 19 | ||
@@ -95,7 +94,6 @@ struct as_data { | |||
95 | 94 | ||
96 | struct as_rq *next_arq[2]; /* next in sort order */ | 95 | struct as_rq *next_arq[2]; /* next in sort order */ |
97 | sector_t last_sector[2]; /* last REQ_SYNC & REQ_ASYNC sectors */ | 96 | sector_t last_sector[2]; /* last REQ_SYNC & REQ_ASYNC sectors */ |
98 | struct hlist_head *hash; /* request hash */ | ||
99 | 97 | ||
100 | unsigned long exit_prob; /* probability a task will exit while | 98 | unsigned long exit_prob; /* probability a task will exit while |
101 | being waited on */ | 99 | being waited on */ |
@@ -162,11 +160,6 @@ struct as_rq { | |||
162 | struct io_context *io_context; /* The submitting task */ | 160 | struct io_context *io_context; /* The submitting task */ |
163 | 161 | ||
164 | /* | 162 | /* |
165 | * request hash, key is the ending offset (for back merge lookup) | ||
166 | */ | ||
167 | struct hlist_node hash; | ||
168 | |||
169 | /* | ||
170 | * expire fifo | 163 | * expire fifo |
171 | */ | 164 | */ |
172 | struct list_head fifo; | 165 | struct list_head fifo; |
@@ -273,77 +266,6 @@ static void as_put_io_context(struct as_rq *arq) | |||
273 | } | 266 | } |
274 | 267 | ||
275 | /* | 268 | /* |
276 | * the back merge hash support functions | ||
277 | */ | ||
278 | static const int as_hash_shift = 6; | ||
279 | #define AS_HASH_BLOCK(sec) ((sec) >> 3) | ||
280 | #define AS_HASH_FN(sec) (hash_long(AS_HASH_BLOCK((sec)), as_hash_shift)) | ||
281 | #define AS_HASH_ENTRIES (1 << as_hash_shift) | ||
282 | #define rq_hash_key(rq) ((rq)->sector + (rq)->nr_sectors) | ||
283 | |||
284 | static inline void __as_del_arq_hash(struct as_rq *arq) | ||
285 | { | ||
286 | hlist_del_init(&arq->hash); | ||
287 | } | ||
288 | |||
289 | static inline void as_del_arq_hash(struct as_rq *arq) | ||
290 | { | ||
291 | if (!hlist_unhashed(&arq->hash)) | ||
292 | __as_del_arq_hash(arq); | ||
293 | } | ||
294 | |||
295 | static void as_add_arq_hash(struct as_data *ad, struct as_rq *arq) | ||
296 | { | ||
297 | struct request *rq = arq->request; | ||
298 | |||
299 | BUG_ON(!hlist_unhashed(&arq->hash)); | ||
300 | |||
301 | hlist_add_head(&arq->hash, &ad->hash[AS_HASH_FN(rq_hash_key(rq))]); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * move hot entry to front of chain | ||
306 | */ | ||
307 | static inline void as_hot_arq_hash(struct as_data *ad, struct as_rq *arq) | ||
308 | { | ||
309 | struct request *rq = arq->request; | ||
310 | struct hlist_head *head = &ad->hash[AS_HASH_FN(rq_hash_key(rq))]; | ||
311 | |||
312 | if (hlist_unhashed(&arq->hash)) { | ||
313 | WARN_ON(1); | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | if (&arq->hash != head->first) { | ||
318 | hlist_del(&arq->hash); | ||
319 | hlist_add_head(&arq->hash, head); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static struct request *as_find_arq_hash(struct as_data *ad, sector_t offset) | ||
324 | { | ||
325 | struct hlist_head *hash_list = &ad->hash[AS_HASH_FN(offset)]; | ||
326 | struct hlist_node *entry, *next; | ||
327 | struct as_rq *arq; | ||
328 | |||
329 | hlist_for_each_entry_safe(arq, entry, next, hash_list, hash) { | ||
330 | struct request *__rq = arq->request; | ||
331 | |||
332 | BUG_ON(hlist_unhashed(&arq->hash)); | ||
333 | |||
334 | if (!rq_mergeable(__rq)) { | ||
335 | as_del_arq_hash(arq); | ||
336 | continue; | ||
337 | } | ||
338 | |||
339 | if (rq_hash_key(__rq) == offset) | ||
340 | return __rq; | ||
341 | } | ||
342 | |||
343 | return NULL; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * rb tree support functions | 269 | * rb tree support functions |
348 | */ | 270 | */ |
349 | #define rb_entry_arq(node) rb_entry((node), struct as_rq, rb_node) | 271 | #define rb_entry_arq(node) rb_entry((node), struct as_rq, rb_node) |
@@ -1060,7 +982,6 @@ static void as_remove_queued_request(request_queue_t *q, struct request *rq) | |||
1060 | ad->next_arq[data_dir] = as_find_next_arq(ad, arq); | 982 | ad->next_arq[data_dir] = as_find_next_arq(ad, arq); |
1061 | 983 | ||
1062 | list_del_init(&arq->fifo); | 984 | list_del_init(&arq->fifo); |
1063 | as_del_arq_hash(arq); | ||
1064 | as_del_arq_rb(ad, arq); | 985 | as_del_arq_rb(ad, arq); |
1065 | } | 986 | } |
1066 | 987 | ||
@@ -1349,8 +1270,6 @@ static void as_add_request(request_queue_t *q, struct request *rq) | |||
1349 | } | 1270 | } |
1350 | 1271 | ||
1351 | as_add_arq_rb(ad, arq); | 1272 | as_add_arq_rb(ad, arq); |
1352 | if (rq_mergeable(arq->request)) | ||
1353 | as_add_arq_hash(ad, arq); | ||
1354 | 1273 | ||
1355 | /* | 1274 | /* |
1356 | * set expire time (only used for reads) and add to fifo list | 1275 | * set expire time (only used for reads) and add to fifo list |
@@ -1428,42 +1347,17 @@ as_merge(request_queue_t *q, struct request **req, struct bio *bio) | |||
1428 | struct as_data *ad = q->elevator->elevator_data; | 1347 | struct as_data *ad = q->elevator->elevator_data; |
1429 | sector_t rb_key = bio->bi_sector + bio_sectors(bio); | 1348 | sector_t rb_key = bio->bi_sector + bio_sectors(bio); |
1430 | struct request *__rq; | 1349 | struct request *__rq; |
1431 | int ret; | ||
1432 | |||
1433 | /* | ||
1434 | * see if the merge hash can satisfy a back merge | ||
1435 | */ | ||
1436 | __rq = as_find_arq_hash(ad, bio->bi_sector); | ||
1437 | if (__rq) { | ||
1438 | BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector); | ||
1439 | |||
1440 | if (elv_rq_merge_ok(__rq, bio)) { | ||
1441 | ret = ELEVATOR_BACK_MERGE; | ||
1442 | goto out; | ||
1443 | } | ||
1444 | } | ||
1445 | 1350 | ||
1446 | /* | 1351 | /* |
1447 | * check for front merge | 1352 | * check for front merge |
1448 | */ | 1353 | */ |
1449 | __rq = as_find_arq_rb(ad, rb_key, bio_data_dir(bio)); | 1354 | __rq = as_find_arq_rb(ad, rb_key, bio_data_dir(bio)); |
1450 | if (__rq) { | 1355 | if (__rq && elv_rq_merge_ok(__rq, bio)) { |
1451 | BUG_ON(rb_key != rq_rb_key(__rq)); | 1356 | *req = __rq; |
1452 | 1357 | return ELEVATOR_FRONT_MERGE; | |
1453 | if (elv_rq_merge_ok(__rq, bio)) { | ||
1454 | ret = ELEVATOR_FRONT_MERGE; | ||
1455 | goto out; | ||
1456 | } | ||
1457 | } | 1358 | } |
1458 | 1359 | ||
1459 | return ELEVATOR_NO_MERGE; | 1360 | return ELEVATOR_NO_MERGE; |
1460 | out: | ||
1461 | if (ret) { | ||
1462 | if (rq_mergeable(__rq)) | ||
1463 | as_hot_arq_hash(ad, RQ_DATA(__rq)); | ||
1464 | } | ||
1465 | *req = __rq; | ||
1466 | return ret; | ||
1467 | } | 1361 | } |
1468 | 1362 | ||
1469 | static void as_merged_request(request_queue_t *q, struct request *req) | 1363 | static void as_merged_request(request_queue_t *q, struct request *req) |
@@ -1472,12 +1366,6 @@ static void as_merged_request(request_queue_t *q, struct request *req) | |||
1472 | struct as_rq *arq = RQ_DATA(req); | 1366 | struct as_rq *arq = RQ_DATA(req); |
1473 | 1367 | ||
1474 | /* | 1368 | /* |
1475 | * hash always needs to be repositioned, key is end sector | ||
1476 | */ | ||
1477 | as_del_arq_hash(arq); | ||
1478 | as_add_arq_hash(ad, arq); | ||
1479 | |||
1480 | /* | ||
1481 | * if the merge was a front merge, we need to reposition request | 1369 | * if the merge was a front merge, we need to reposition request |
1482 | */ | 1370 | */ |
1483 | if (rq_rb_key(req) != arq->rb_key) { | 1371 | if (rq_rb_key(req) != arq->rb_key) { |
@@ -1501,13 +1389,6 @@ static void as_merged_requests(request_queue_t *q, struct request *req, | |||
1501 | BUG_ON(!arq); | 1389 | BUG_ON(!arq); |
1502 | BUG_ON(!anext); | 1390 | BUG_ON(!anext); |
1503 | 1391 | ||
1504 | /* | ||
1505 | * reposition arq (this is the merged request) in hash, and in rbtree | ||
1506 | * in case of a front merge | ||
1507 | */ | ||
1508 | as_del_arq_hash(arq); | ||
1509 | as_add_arq_hash(ad, arq); | ||
1510 | |||
1511 | if (rq_rb_key(req) != arq->rb_key) { | 1392 | if (rq_rb_key(req) != arq->rb_key) { |
1512 | as_del_arq_rb(ad, arq); | 1393 | as_del_arq_rb(ad, arq); |
1513 | as_add_arq_rb(ad, arq); | 1394 | as_add_arq_rb(ad, arq); |
@@ -1591,7 +1472,6 @@ static int as_set_request(request_queue_t *q, struct request *rq, | |||
1591 | arq->request = rq; | 1472 | arq->request = rq; |
1592 | arq->state = AS_RQ_PRESCHED; | 1473 | arq->state = AS_RQ_PRESCHED; |
1593 | arq->io_context = NULL; | 1474 | arq->io_context = NULL; |
1594 | INIT_HLIST_NODE(&arq->hash); | ||
1595 | INIT_LIST_HEAD(&arq->fifo); | 1475 | INIT_LIST_HEAD(&arq->fifo); |
1596 | rq->elevator_private = arq; | 1476 | rq->elevator_private = arq; |
1597 | return 0; | 1477 | return 0; |
@@ -1628,7 +1508,6 @@ static void as_exit_queue(elevator_t *e) | |||
1628 | 1508 | ||
1629 | mempool_destroy(ad->arq_pool); | 1509 | mempool_destroy(ad->arq_pool); |
1630 | put_io_context(ad->io_context); | 1510 | put_io_context(ad->io_context); |
1631 | kfree(ad->hash); | ||
1632 | kfree(ad); | 1511 | kfree(ad); |
1633 | } | 1512 | } |
1634 | 1513 | ||
@@ -1639,7 +1518,6 @@ static void as_exit_queue(elevator_t *e) | |||
1639 | static void *as_init_queue(request_queue_t *q, elevator_t *e) | 1518 | static void *as_init_queue(request_queue_t *q, elevator_t *e) |
1640 | { | 1519 | { |
1641 | struct as_data *ad; | 1520 | struct as_data *ad; |
1642 | int i; | ||
1643 | 1521 | ||
1644 | if (!arq_pool) | 1522 | if (!arq_pool) |
1645 | return NULL; | 1523 | return NULL; |
@@ -1651,17 +1529,9 @@ static void *as_init_queue(request_queue_t *q, elevator_t *e) | |||
1651 | 1529 | ||
1652 | ad->q = q; /* Identify what queue the data belongs to */ | 1530 | ad->q = q; /* Identify what queue the data belongs to */ |
1653 | 1531 | ||
1654 | ad->hash = kmalloc_node(sizeof(struct hlist_head)*AS_HASH_ENTRIES, | ||
1655 | GFP_KERNEL, q->node); | ||
1656 | if (!ad->hash) { | ||
1657 | kfree(ad); | ||
1658 | return NULL; | ||
1659 | } | ||
1660 | |||
1661 | ad->arq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, | 1532 | ad->arq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, |
1662 | mempool_free_slab, arq_pool, q->node); | 1533 | mempool_free_slab, arq_pool, q->node); |
1663 | if (!ad->arq_pool) { | 1534 | if (!ad->arq_pool) { |
1664 | kfree(ad->hash); | ||
1665 | kfree(ad); | 1535 | kfree(ad); |
1666 | return NULL; | 1536 | return NULL; |
1667 | } | 1537 | } |
@@ -1672,9 +1542,6 @@ static void *as_init_queue(request_queue_t *q, elevator_t *e) | |||
1672 | init_timer(&ad->antic_timer); | 1542 | init_timer(&ad->antic_timer); |
1673 | INIT_WORK(&ad->antic_work, as_work_handler, q); | 1543 | INIT_WORK(&ad->antic_work, as_work_handler, q); |
1674 | 1544 | ||
1675 | for (i = 0; i < AS_HASH_ENTRIES; i++) | ||
1676 | INIT_HLIST_HEAD(&ad->hash[i]); | ||
1677 | |||
1678 | INIT_LIST_HEAD(&ad->fifo_list[REQ_SYNC]); | 1545 | INIT_LIST_HEAD(&ad->fifo_list[REQ_SYNC]); |
1679 | INIT_LIST_HEAD(&ad->fifo_list[REQ_ASYNC]); | 1546 | INIT_LIST_HEAD(&ad->fifo_list[REQ_ASYNC]); |
1680 | ad->sort_list[REQ_SYNC] = RB_ROOT; | 1547 | ad->sort_list[REQ_SYNC] = RB_ROOT; |