block: attempt to merge with existing requests on plug flush

One of the disadvantages of on-stack plugging is that we potentially
lose out on merging since all pending IO isn't always visible to
everybody. When we flush the on-stack plugs, right now we don't do
any checks to see if potential merge candidates could be utilized.

Correct this by adding a new insert variant, ELEVATOR_INSERT_SORT_MERGE.
It works just ELEVATOR_INSERT_SORT, but first checks whether we can
merge with an existing request before doing the insertion (if we fail
merging).

This fixes a regression with multiple processes issuing IO that
can be merged.

Thanks to Shaohua Li <shaohua.li@intel.com> for testing and fixing
an accounting bug.

Signed-off-by: Jens Axboe <jaxboe@fusionio.com>

+59 -4
+1 -1
block/blk-core.c
··· 2685 2685 /* 2686 2686 * rq is already accounted, so use raw insert 2687 2687 */ 2688 - __elv_add_request(q, rq, ELEVATOR_INSERT_SORT); 2688 + __elv_add_request(q, rq, ELEVATOR_INSERT_SORT_MERGE); 2689 2689 } 2690 2690 2691 2691 if (q) {
+6
block/blk-merge.c
··· 465 465 466 466 return 0; 467 467 } 468 + 469 + int blk_attempt_req_merge(struct request_queue *q, struct request *rq, 470 + struct request *next) 471 + { 472 + return attempt_merge(q, rq, next); 473 + }
+2
block/blk.h
··· 103 103 struct bio *bio); 104 104 int attempt_back_merge(struct request_queue *q, struct request *rq); 105 105 int attempt_front_merge(struct request_queue *q, struct request *rq); 106 + int blk_attempt_req_merge(struct request_queue *q, struct request *rq, 107 + struct request *next); 106 108 void blk_recalc_rq_segments(struct request *rq); 107 109 void blk_rq_set_mixed_merge(struct request *rq); 108 110
+49 -3
block/elevator.c
··· 521 521 return ELEVATOR_NO_MERGE; 522 522 } 523 523 524 + /* 525 + * Attempt to do an insertion back merge. Only check for the case where 526 + * we can append 'rq' to an existing request, so we can throw 'rq' away 527 + * afterwards. 528 + * 529 + * Returns true if we merged, false otherwise 530 + */ 531 + static bool elv_attempt_insert_merge(struct request_queue *q, 532 + struct request *rq) 533 + { 534 + struct request *__rq; 535 + 536 + if (blk_queue_nomerges(q)) 537 + return false; 538 + 539 + /* 540 + * First try one-hit cache. 541 + */ 542 + if (q->last_merge && blk_attempt_req_merge(q, q->last_merge, rq)) 543 + return true; 544 + 545 + if (blk_queue_noxmerges(q)) 546 + return false; 547 + 548 + /* 549 + * See if our hash lookup can find a potential backmerge. 550 + */ 551 + __rq = elv_rqhash_find(q, blk_rq_pos(rq)); 552 + if (__rq && blk_attempt_req_merge(q, __rq, rq)) 553 + return true; 554 + 555 + return false; 556 + } 557 + 524 558 void elv_merged_request(struct request_queue *q, struct request *rq, int type) 525 559 { 526 560 struct elevator_queue *e = q->elevator; ··· 572 538 struct request *next) 573 539 { 574 540 struct elevator_queue *e = q->elevator; 541 + const int next_sorted = next->cmd_flags & REQ_SORTED; 575 542 576 - if (e->ops->elevator_merge_req_fn) 543 + if (next_sorted && e->ops->elevator_merge_req_fn) 577 544 e->ops->elevator_merge_req_fn(q, rq, next); 578 545 579 546 elv_rqhash_reposition(q, rq); 580 - elv_rqhash_del(q, next); 581 547 582 - q->nr_sorted--; 548 + if (next_sorted) { 549 + elv_rqhash_del(q, next); 550 + q->nr_sorted--; 551 + } 552 + 583 553 q->last_merge = rq; 584 554 } 585 555 ··· 685 647 __blk_run_queue(q, false); 686 648 break; 687 649 650 + case ELEVATOR_INSERT_SORT_MERGE: 651 + /* 652 + * If we succeed in merging this request with one in the 653 + * queue already, we are done - rq has now been freed, 654 + * so no need to do anything further. 655 + */ 656 + if (elv_attempt_insert_merge(q, rq)) 657 + break; 688 658 case ELEVATOR_INSERT_SORT: 689 659 BUG_ON(rq->cmd_type != REQ_TYPE_FS && 690 660 !(rq->cmd_flags & REQ_DISCARD));
+1
include/linux/elevator.h
··· 166 166 #define ELEVATOR_INSERT_SORT 3 167 167 #define ELEVATOR_INSERT_REQUEUE 4 168 168 #define ELEVATOR_INSERT_FLUSH 5 169 + #define ELEVATOR_INSERT_SORT_MERGE 6 169 170 170 171 /* 171 172 * return values from elevator_may_queue_fn