Commit 7cc4ffc5 authored by Paolo Valente's avatar Paolo Valente Committed by Jens Axboe
Browse files

block, bfq: put reqs of waker and woken in dispatch list



Consider a new I/O request that arrives for a bfq_queue bfqq. If, when
this happens, the only active bfq_queues are bfqq and either its waker
bfq_queue or one of its woken bfq_queues, then there is no point in
queueing this new I/O request in bfqq for service. In fact, the
in-service queue and bfqq agree on serving this new I/O request as
soon as possible. So this commit puts this new I/O request directly
into the dispatch list.

Tested-by: default avatarJan Kara <jack@suse.cz>
Acked-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarPaolo Valente <paolo.valente@linaro.org>
Tested-by: default avatarOleksandr Natalenko <oleksandr@natalenko.name>
Link: https://lore.kernel.org/r/20210304174627.161-3-paolo.valente@linaro.org


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 2ec5a5c4
Loading
Loading
Loading
Loading
+43 −1
Original line number Diff line number Diff line
@@ -5649,7 +5649,49 @@ static void bfq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,

	spin_lock_irq(&bfqd->lock);
	bfqq = bfq_init_rq(rq);
	if (!bfqq || at_head || blk_rq_is_passthrough(rq)) {

	/*
	 * Reqs with at_head or passthrough flags set are to be put
	 * directly into dispatch list. Additional case for putting rq
	 * directly into the dispatch queue: the only active
	 * bfq_queues are bfqq and either its waker bfq_queue or one
	 * of its woken bfq_queues. The rationale behind this
	 * additional condition is as follows:
	 * - consider a bfq_queue, say Q1, detected as a waker of
	 *   another bfq_queue, say Q2
	 * - by definition of a waker, Q1 blocks the I/O of Q2, i.e.,
	 *   some I/O of Q1 needs to be completed for new I/O of Q2
	 *   to arrive.  A notable example of waker is journald
	 * - so, Q1 and Q2 are in any respect the queues of two
	 *   cooperating processes (or of two cooperating sets of
	 *   processes): the goal of Q1's I/O is doing what needs to
	 *   be done so that new Q2's I/O can finally be
	 *   issued. Therefore, if the service of Q1's I/O is delayed,
	 *   then Q2's I/O is delayed too.  Conversely, if Q2's I/O is
	 *   delayed, the goal of Q1's I/O is hindered.
	 * - as a consequence, if some I/O of Q1/Q2 arrives while
	 *   Q2/Q1 is the only queue in service, there is absolutely
	 *   no point in delaying the service of such an I/O. The
	 *   only possible result is a throughput loss
	 * - so, when the above condition holds, the best option is to
	 *   have the new I/O dispatched as soon as possible
	 * - the most effective and efficient way to attain the above
	 *   goal is to put the new I/O directly in the dispatch
	 *   list
	 * - as an additional restriction, Q1 and Q2 must be the only
	 *   busy queues for this commit to put the I/O of Q2/Q1 in
	 *   the dispatch list.  This is necessary, because, if also
	 *   other queues are waiting for service, then putting new
	 *   I/O directly in the dispatch list may evidently cause a
	 *   violation of service guarantees for the other queues
	 */
	if (!bfqq ||
	    (bfqq != bfqd->in_service_queue &&
	     bfqd->in_service_queue != NULL &&
	     bfq_tot_busy_queues(bfqd) == 1 + bfq_bfqq_busy(bfqq) &&
	     (bfqq->waker_bfqq == bfqd->in_service_queue ||
	      bfqd->in_service_queue->waker_bfqq == bfqq)) ||
	    at_head || blk_rq_is_passthrough(rq)) {
		if (at_head)
			list_add(&rq->queuelist, &bfqd->dispatch);
		else