summaryrefslogtreecommitdiffstats
path: root/block/bfq-iosched.c
diff options
context:
space:
mode:
authorPaolo Valente <paolo.valente@linaro.org>2018-09-14 10:23:08 -0400
committerJens Axboe <axboe@kernel.dk>2018-09-14 15:06:03 -0400
commitd0edc2473be9d70f999282e1ca7863ad6ae704dc (patch)
treefe4bb533764d2511c398ca42a054d46b2ad7b16f /block/bfq-iosched.c
parentcbeb869a3d1110450186b738199963c5e68c2a71 (diff)
block, bfq: inject other-queue I/O into seeky idle queues on NCQ flash
The Achilles' heel of BFQ is its failing to reach a high throughput with sync random I/O on flash storage with internal queueing, in case the processes doing I/O have differentiated weights. The cause of this failure is as follows. If at least two processes do sync I/O, and have a different weight from each other, then BFQ plugs I/O dispatching every time one of these processes, while it is being served, remains temporarily without pending I/O requests. This plugging is necessary to guarantee that every process enjoys a bandwidth proportional to its weight; but it empties the internal queue(s) of the drive. And this kills throughput with random I/O. So, if some processes have differentiated weights and do both sync and random I/O, the end result is a throughput collapse. This commit tries to counter this problem by injecting the service of other processes, in a controlled way, while the process in service happens to have no I/O. This injection is performed only if the medium is non rotational and performs internal queueing, and the process in service does random I/O (service injection might be beneficial for sequential I/O too, we'll work on that). As an example of the benefits of this commit, on a PLEXTOR PX-256M5S SSD, and with five processes having differentiated weights and doing sync random 4KB I/O, this commit makes the throughput with bfq grow by 400%, from 25 to 100MB/s. This higher throughput is 10MB/s lower than that reached with none. As some less random I/O is added to the mix, the throughput becomes equal to or higher than that with none. This commit is a very first attempt to recover throughput without losing control, and certainly has many limitations. One is, e.g., that the processes whose service is injected are not chosen so as to distribute the extra bandwidth they receive in accordance to their weights. Thus there might be loss of weighted fairness in some cases. Anyway, this loss concerns extra service, which would not have been received at all without this commit. Other limitations and issues will probably show up with usage. Signed-off-by: Paolo Valente <paolo.valente@linaro.org> Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/bfq-iosched.c')
-rw-r--r--block/bfq-iosched.c68
1 files changed, 62 insertions, 6 deletions
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 653100fb719e..d94838bcc135 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -3182,6 +3182,13 @@ static unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd,
3182 jiffies + nsecs_to_jiffies(bfqq->bfqd->bfq_slice_idle) + 4); 3182 jiffies + nsecs_to_jiffies(bfqq->bfqd->bfq_slice_idle) + 4);
3183} 3183}
3184 3184
3185static bool bfq_bfqq_injectable(struct bfq_queue *bfqq)
3186{
3187 return BFQQ_SEEKY(bfqq) && bfqq->wr_coeff == 1 &&
3188 blk_queue_nonrot(bfqq->bfqd->queue) &&
3189 bfqq->bfqd->hw_tag;
3190}
3191
3185/** 3192/**
3186 * bfq_bfqq_expire - expire a queue. 3193 * bfq_bfqq_expire - expire a queue.
3187 * @bfqd: device owning the queue. 3194 * @bfqd: device owning the queue.
@@ -3291,6 +3298,8 @@ void bfq_bfqq_expire(struct bfq_data *bfqd,
3291 if (ref == 1) /* bfqq is gone, no more actions on it */ 3298 if (ref == 1) /* bfqq is gone, no more actions on it */
3292 return; 3299 return;
3293 3300
3301 bfqq->injected_service = 0;
3302
3294 /* mark bfqq as waiting a request only if a bic still points to it */ 3303 /* mark bfqq as waiting a request only if a bic still points to it */
3295 if (!bfq_bfqq_busy(bfqq) && 3304 if (!bfq_bfqq_busy(bfqq) &&
3296 reason != BFQQE_BUDGET_TIMEOUT && 3305 reason != BFQQE_BUDGET_TIMEOUT &&
@@ -3629,6 +3638,30 @@ static bool bfq_bfqq_must_idle(struct bfq_queue *bfqq)
3629 return RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_better_to_idle(bfqq); 3638 return RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_better_to_idle(bfqq);
3630} 3639}
3631 3640
3641static struct bfq_queue *bfq_choose_bfqq_for_injection(struct bfq_data *bfqd)
3642{
3643 struct bfq_queue *bfqq;
3644
3645 /*
3646 * A linear search; but, with a high probability, very few
3647 * steps are needed to find a candidate queue, i.e., a queue
3648 * with enough budget left for its next request. In fact:
3649 * - BFQ dynamically updates the budget of every queue so as
3650 * to accommodate the expected backlog of the queue;
3651 * - if a queue gets all its requests dispatched as injected
3652 * service, then the queue is removed from the active list
3653 * (and re-added only if it gets new requests, but with
3654 * enough budget for its new backlog).
3655 */
3656 list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list)
3657 if (!RB_EMPTY_ROOT(&bfqq->sort_list) &&
3658 bfq_serv_to_charge(bfqq->next_rq, bfqq) <=
3659 bfq_bfqq_budget_left(bfqq))
3660 return bfqq;
3661
3662 return NULL;
3663}
3664
3632/* 3665/*
3633 * Select a queue for service. If we have a current queue in service, 3666 * Select a queue for service. If we have a current queue in service,
3634 * check whether to continue servicing it, or retrieve and set a new one. 3667 * check whether to continue servicing it, or retrieve and set a new one.
@@ -3710,10 +3743,19 @@ check_queue:
3710 * No requests pending. However, if the in-service queue is idling 3743 * No requests pending. However, if the in-service queue is idling
3711 * for a new request, or has requests waiting for a completion and 3744 * for a new request, or has requests waiting for a completion and
3712 * may idle after their completion, then keep it anyway. 3745 * may idle after their completion, then keep it anyway.
3746 *
3747 * Yet, to boost throughput, inject service from other queues if
3748 * possible.
3713 */ 3749 */
3714 if (bfq_bfqq_wait_request(bfqq) || 3750 if (bfq_bfqq_wait_request(bfqq) ||
3715 (bfqq->dispatched != 0 && bfq_better_to_idle(bfqq))) { 3751 (bfqq->dispatched != 0 && bfq_better_to_idle(bfqq))) {
3716 bfqq = NULL; 3752 if (bfq_bfqq_injectable(bfqq) &&
3753 bfqq->injected_service * bfqq->inject_coeff <
3754 bfqq->entity.service * 10)
3755 bfqq = bfq_choose_bfqq_for_injection(bfqd);
3756 else
3757 bfqq = NULL;
3758
3717 goto keep_queue; 3759 goto keep_queue;
3718 } 3760 }
3719 3761
@@ -3803,6 +3845,14 @@ static struct request *bfq_dispatch_rq_from_bfqq(struct bfq_data *bfqd,
3803 3845
3804 bfq_dispatch_remove(bfqd->queue, rq); 3846 bfq_dispatch_remove(bfqd->queue, rq);
3805 3847
3848 if (bfqq != bfqd->in_service_queue) {
3849 if (likely(bfqd->in_service_queue))
3850 bfqd->in_service_queue->injected_service +=
3851 bfq_serv_to_charge(rq, bfqq);
3852
3853 goto return_rq;
3854 }
3855
3806 /* 3856 /*
3807 * If weight raising has to terminate for bfqq, then next 3857 * If weight raising has to terminate for bfqq, then next
3808 * function causes an immediate update of bfqq's weight, 3858 * function causes an immediate update of bfqq's weight,
@@ -3821,13 +3871,12 @@ static struct request *bfq_dispatch_rq_from_bfqq(struct bfq_data *bfqd,
3821 * belongs to CLASS_IDLE and other queues are waiting for 3871 * belongs to CLASS_IDLE and other queues are waiting for
3822 * service. 3872 * service.
3823 */ 3873 */
3824 if (bfqd->busy_queues > 1 && bfq_class_idle(bfqq)) 3874 if (!(bfqd->busy_queues > 1 && bfq_class_idle(bfqq)))
3825 goto expire; 3875 goto return_rq;
3826
3827 return rq;
3828 3876
3829expire:
3830 bfq_bfqq_expire(bfqd, bfqq, false, BFQQE_BUDGET_EXHAUSTED); 3877 bfq_bfqq_expire(bfqd, bfqq, false, BFQQE_BUDGET_EXHAUSTED);
3878
3879return_rq:
3831 return rq; 3880 return rq;
3832} 3881}
3833 3882
@@ -4232,6 +4281,13 @@ static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq,
4232 bfq_mark_bfqq_has_short_ttime(bfqq); 4281 bfq_mark_bfqq_has_short_ttime(bfqq);
4233 bfq_mark_bfqq_sync(bfqq); 4282 bfq_mark_bfqq_sync(bfqq);
4234 bfq_mark_bfqq_just_created(bfqq); 4283 bfq_mark_bfqq_just_created(bfqq);
4284 /*
4285 * Aggressively inject a lot of service: up to 90%.
4286 * This coefficient remains constant during bfqq life,
4287 * but this behavior might be changed, after enough
4288 * testing and tuning.
4289 */
4290 bfqq->inject_coeff = 1;
4235 } else 4291 } else
4236 bfq_clear_bfqq_sync(bfqq); 4292 bfq_clear_bfqq_sync(bfqq);
4237 4293