Commit 2d31c684 authored by Davide Zini's avatar Davide Zini Committed by Jens Axboe

block, bfq: inject I/O to underutilized actuators

The main service scheme of BFQ for sync I/O is serving one sync
bfq_queue at a time, for a while. In particular, BFQ enforces this
scheme when it deems the latter necessary to boost throughput or
to preserve service guarantees. Unfortunately, when BFQ enforces
this policy, only one actuator at a time gets served for a while,
because each bfq_queue contains I/O only for one actuator. The
other actuators may remain underutilized.

Actually, BFQ may serve (inject) extra I/O, taken from other
bfq_queues, in parallel with that of the in-service queue. This
injection mechanism may provide the ground for dealing also with
the above actuator-underutilization problem. Yet BFQ does not take
the actuator load into account when choosing which queue to pick
extra I/O from. In addition, BFQ may happen to inject extra I/O
only when the in-service queue is temporarily empty.

In view of these facts, this commit extends the
injection mechanism in such a way that the latter:
(1) takes into account also the actuator load;
(2) checks such a load on each dispatch, and injects I/O for an
    underutilized actuator, if there is one and there is I/O for it.

To perform the check in (2), this commit introduces a load
threshold, currently set to 4.  A linear scan of each actuator is
performed, until an actuator is found for which the following two
conditions hold: the load of the actuator is below the threshold,
and there is at least one non-in-service queue that contains I/O
for that actuator. If such a pair (actuator, queue) is found, then
the head request of that queue is returned for dispatch, instead
of the head request of the in-service queue.

We have set the threshold, empirically, to the minimum possible
value for which an actuator is fully utilized, or close to be
fully utilized. By doing so, injected I/O 'steals' as few
drive-queue slots as possibile to the in-service queue. This
reduces as much as possible the probability that the service of
I/O from the in-service bfq_queue gets delayed because of slot
exhaustion, i.e., because all the slots of the drive queue are
filled with I/O injected from other queues (NCQ provides for 32
slots).

This new mechanism also counters actuator underutilization in the
case of asymmetric configurations of bfq_queues. Namely if there
are few bfq_queues containing I/O for some actuators and many
bfq_queues containing I/O for other actuators. Or if the
bfq_queues containing I/O for some actuators have lower weights
than the other bfq_queues.
Reviewed-by: default avatarDamien Le Moal <damien.lemoal@opensource.wdc.com>
Signed-off-by: default avatarPaolo Valente <paolo.valente@linaro.org>
Signed-off-by: default avatarDavide Zini <davidezini2@gmail.com>
Link: https://lore.kernel.org/r/20230103145503.71712-8-paolo.valente@linaro.orgSigned-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 4fdb3b9f
...@@ -706,7 +706,7 @@ void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, ...@@ -706,7 +706,7 @@ void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq,
bfq_activate_bfqq(bfqd, bfqq); bfq_activate_bfqq(bfqd, bfqq);
} }
if (!bfqd->in_service_queue && !bfqd->rq_in_driver) if (!bfqd->in_service_queue && !bfqd->tot_rq_in_driver)
bfq_schedule_dispatch(bfqd); bfq_schedule_dispatch(bfqd);
/* release extra ref taken above, bfqq may happen to be freed now */ /* release extra ref taken above, bfqq may happen to be freed now */
bfq_put_queue(bfqq); bfq_put_queue(bfqq);
......
This diff is collapsed.
...@@ -590,7 +590,12 @@ struct bfq_data { ...@@ -590,7 +590,12 @@ struct bfq_data {
/* number of queued requests */ /* number of queued requests */
int queued; int queued;
/* number of requests dispatched and waiting for completion */ /* number of requests dispatched and waiting for completion */
int rq_in_driver; int tot_rq_in_driver;
/*
* number of requests dispatched and waiting for completion
* for each actuator
*/
int rq_in_driver[BFQ_MAX_ACTUATORS];
/* true if the device is non rotational and performs queueing */ /* true if the device is non rotational and performs queueing */
bool nonrot_with_queueing; bool nonrot_with_queueing;
...@@ -684,8 +689,13 @@ struct bfq_data { ...@@ -684,8 +689,13 @@ struct bfq_data {
/* maximum budget allotted to a bfq_queue before rescheduling */ /* maximum budget allotted to a bfq_queue before rescheduling */
int bfq_max_budget; int bfq_max_budget;
/* list of all the bfq_queues active on the device */ /*
struct list_head active_list; * List of all the bfq_queues active for a specific actuator
* on the device. Keeping active queues separate on a
* per-actuator basis helps implementing per-actuator
* injection more efficiently.
*/
struct list_head active_list[BFQ_MAX_ACTUATORS];
/* list of all the bfq_queues idle on the device */ /* list of all the bfq_queues idle on the device */
struct list_head idle_list; struct list_head idle_list;
...@@ -821,6 +831,29 @@ struct bfq_data { ...@@ -821,6 +831,29 @@ struct bfq_data {
sector_t sector[BFQ_MAX_ACTUATORS]; sector_t sector[BFQ_MAX_ACTUATORS];
sector_t nr_sectors[BFQ_MAX_ACTUATORS]; sector_t nr_sectors[BFQ_MAX_ACTUATORS];
struct blk_independent_access_range ia_ranges[BFQ_MAX_ACTUATORS]; struct blk_independent_access_range ia_ranges[BFQ_MAX_ACTUATORS];
/*
* If the number of I/O requests queued in the device for a
* given actuator is below next threshold, then the actuator
* is deemed as underutilized. If this condition is found to
* hold for some actuator upon a dispatch, but (i) the
* in-service queue does not contain I/O for that actuator,
* while (ii) some other queue does contain I/O for that
* actuator, then the head I/O request of the latter queue is
* returned (injected), instead of the head request of the
* currently in-service queue.
*
* We set the threshold, empirically, to the minimum possible
* value for which an actuator is fully utilized, or close to
* be fully utilized. By doing so, injected I/O 'steals' as
* few drive-queue slots as possibile to the in-service
* queue. This reduces as much as possible the probability
* that the service of I/O from the in-service bfq_queue gets
* delayed because of slot exhaustion, i.e., because all the
* slots of the drive queue are filled with I/O injected from
* other queues (NCQ provides for 32 slots).
*/
unsigned int actuator_load_threshold;
}; };
enum bfqq_state_flags { enum bfqq_state_flags {
......
...@@ -493,7 +493,7 @@ static void bfq_active_insert(struct bfq_service_tree *st, ...@@ -493,7 +493,7 @@ static void bfq_active_insert(struct bfq_service_tree *st,
bfq_update_active_tree(node); bfq_update_active_tree(node);
if (bfqq) if (bfqq)
list_add(&bfqq->bfqq_list, &bfqq->bfqd->active_list); list_add(&bfqq->bfqq_list, &bfqq->bfqd->active_list[bfqq->actuator_idx]);
bfq_inc_active_entities(entity); bfq_inc_active_entities(entity);
} }
......
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