Commit 4420b095 authored by Paolo Valente's avatar Paolo Valente Committed by Jens Axboe

block, bfq: do not expire a queue that will deserve dispatch plugging

For some bfq_queues, BFQ plugs I/O dispatching when the queue becomes
idle, and keeps the plug until a new request of the queue arrives, or
a timeout fires. BFQ does so either to boost throughput or to preserve
service guarantees for the queue.

More precisely, for such a queue, plugging starts when the queue
happens to have either no request enqueued, or no request in flight,
that is, no request already dispatched but not yet completed.

On the opposite end, BFQ may happen to expire a queue with no request
enqueued, without doing any plugging, if the queue still has some
request in flight. Unfortunately, such a premature expiration causes
the queue to lose its chance to enjoy dispatch plugging a moment
later, i.e., when its in-flight requests finally get completed. This
breaks service guarantees for the queue.

This commit prevents BFQ from expiring an empty queue if the latter
still has in-flight requests.
Tested-by: default avatarHolger Hoffstätte <holger@applied-asynchrony.com>
Tested-by: default avatarOleksandr Natalenko <oleksandr@natalenko.name>
Signed-off-by: default avatarPaolo Valente <paolo.valente@linaro.org>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 0471559c
...@@ -3597,8 +3597,14 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) ...@@ -3597,8 +3597,14 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd)
bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue"); bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue");
/*
* Do not expire bfqq for budget timeout if bfqq may be about
* to enjoy device idling. The reason why, in this case, we
* prevent bfqq from expiring is the same as in the comments
* on the case where bfq_bfqq_must_idle() returns true, in
* bfq_completed_request().
*/
if (bfq_may_expire_for_budg_timeout(bfqq) && if (bfq_may_expire_for_budg_timeout(bfqq) &&
!bfq_bfqq_wait_request(bfqq) &&
!bfq_bfqq_must_idle(bfqq)) !bfq_bfqq_must_idle(bfqq))
goto expire; goto expire;
...@@ -4674,8 +4680,32 @@ static void bfq_completed_request(struct bfq_queue *bfqq, struct bfq_data *bfqd) ...@@ -4674,8 +4680,32 @@ static void bfq_completed_request(struct bfq_queue *bfqq, struct bfq_data *bfqd)
* or if we want to idle in case it has no pending requests. * or if we want to idle in case it has no pending requests.
*/ */
if (bfqd->in_service_queue == bfqq) { if (bfqd->in_service_queue == bfqq) {
if (bfqq->dispatched == 0 && bfq_bfqq_must_idle(bfqq)) { if (bfq_bfqq_must_idle(bfqq)) {
bfq_arm_slice_timer(bfqd); if (bfqq->dispatched == 0)
bfq_arm_slice_timer(bfqd);
/*
* If we get here, we do not expire bfqq, even
* if bfqq was in budget timeout or had no
* more requests (as controlled in the next
* conditional instructions). The reason for
* not expiring bfqq is as follows.
*
* Here bfqq->dispatched > 0 holds, but
* bfq_bfqq_must_idle() returned true. This
* implies that, even if no request arrives
* for bfqq before bfqq->dispatched reaches 0,
* bfqq will, however, not be expired on the
* completion event that causes bfqq->dispatch
* to reach zero. In contrast, on this event,
* bfqq will start enjoying device idling
* (I/O-dispatch plugging).
*
* But, if we expired bfqq here, bfqq would
* not have the chance to enjoy device idling
* when bfqq->dispatched finally reaches
* zero. This would expose bfqq to violation
* of its reserved service guarantees.
*/
return; return;
} else if (bfq_may_expire_for_budg_timeout(bfqq)) } else if (bfq_may_expire_for_budg_timeout(bfqq))
bfq_bfqq_expire(bfqd, bfqq, false, bfq_bfqq_expire(bfqd, bfqq, false,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment