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: Jan Kara <j...@suse.cz>
Acked-by: Jan Kara <j...@suse.cz>
Signed-off-by: Paolo Valente <paolo.vale...@linaro.org>
---
 block/bfq-iosched.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index a83149407336..a9c1a14b64f4 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -5640,7 +5640,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
-- 
2.20.1

Reply via email to