|
@@ -3182,6 +3182,13 @@ static unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd,
|
|
jiffies + nsecs_to_jiffies(bfqq->bfqd->bfq_slice_idle) + 4);
|
|
jiffies + nsecs_to_jiffies(bfqq->bfqd->bfq_slice_idle) + 4);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool bfq_bfqq_injectable(struct bfq_queue *bfqq)
|
|
|
|
+{
|
|
|
|
+ return BFQQ_SEEKY(bfqq) && bfqq->wr_coeff == 1 &&
|
|
|
|
+ blk_queue_nonrot(bfqq->bfqd->queue) &&
|
|
|
|
+ bfqq->bfqd->hw_tag;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* bfq_bfqq_expire - expire a queue.
|
|
* bfq_bfqq_expire - expire a queue.
|
|
* @bfqd: device owning the queue.
|
|
* @bfqd: device owning the queue.
|
|
@@ -3291,6 +3298,8 @@ void bfq_bfqq_expire(struct bfq_data *bfqd,
|
|
if (ref == 1) /* bfqq is gone, no more actions on it */
|
|
if (ref == 1) /* bfqq is gone, no more actions on it */
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ bfqq->injected_service = 0;
|
|
|
|
+
|
|
/* mark bfqq as waiting a request only if a bic still points to it */
|
|
/* mark bfqq as waiting a request only if a bic still points to it */
|
|
if (!bfq_bfqq_busy(bfqq) &&
|
|
if (!bfq_bfqq_busy(bfqq) &&
|
|
reason != BFQQE_BUDGET_TIMEOUT &&
|
|
reason != BFQQE_BUDGET_TIMEOUT &&
|
|
@@ -3629,6 +3638,30 @@ static bool bfq_bfqq_must_idle(struct bfq_queue *bfqq)
|
|
return RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_better_to_idle(bfqq);
|
|
return RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_better_to_idle(bfqq);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static struct bfq_queue *bfq_choose_bfqq_for_injection(struct bfq_data *bfqd)
|
|
|
|
+{
|
|
|
|
+ struct bfq_queue *bfqq;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * A linear search; but, with a high probability, very few
|
|
|
|
+ * steps are needed to find a candidate queue, i.e., a queue
|
|
|
|
+ * with enough budget left for its next request. In fact:
|
|
|
|
+ * - BFQ dynamically updates the budget of every queue so as
|
|
|
|
+ * to accommodate the expected backlog of the queue;
|
|
|
|
+ * - if a queue gets all its requests dispatched as injected
|
|
|
|
+ * service, then the queue is removed from the active list
|
|
|
|
+ * (and re-added only if it gets new requests, but with
|
|
|
|
+ * enough budget for its new backlog).
|
|
|
|
+ */
|
|
|
|
+ list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list)
|
|
|
|
+ if (!RB_EMPTY_ROOT(&bfqq->sort_list) &&
|
|
|
|
+ bfq_serv_to_charge(bfqq->next_rq, bfqq) <=
|
|
|
|
+ bfq_bfqq_budget_left(bfqq))
|
|
|
|
+ return bfqq;
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Select a queue for service. If we have a current queue in service,
|
|
* Select a queue for service. If we have a current queue in service,
|
|
* check whether to continue servicing it, or retrieve and set a new one.
|
|
* check whether to continue servicing it, or retrieve and set a new one.
|
|
@@ -3710,10 +3743,19 @@ check_queue:
|
|
* No requests pending. However, if the in-service queue is idling
|
|
* No requests pending. However, if the in-service queue is idling
|
|
* for a new request, or has requests waiting for a completion and
|
|
* for a new request, or has requests waiting for a completion and
|
|
* may idle after their completion, then keep it anyway.
|
|
* may idle after their completion, then keep it anyway.
|
|
|
|
+ *
|
|
|
|
+ * Yet, to boost throughput, inject service from other queues if
|
|
|
|
+ * possible.
|
|
*/
|
|
*/
|
|
if (bfq_bfqq_wait_request(bfqq) ||
|
|
if (bfq_bfqq_wait_request(bfqq) ||
|
|
(bfqq->dispatched != 0 && bfq_better_to_idle(bfqq))) {
|
|
(bfqq->dispatched != 0 && bfq_better_to_idle(bfqq))) {
|
|
- bfqq = NULL;
|
|
|
|
|
|
+ if (bfq_bfqq_injectable(bfqq) &&
|
|
|
|
+ bfqq->injected_service * bfqq->inject_coeff <
|
|
|
|
+ bfqq->entity.service * 10)
|
|
|
|
+ bfqq = bfq_choose_bfqq_for_injection(bfqd);
|
|
|
|
+ else
|
|
|
|
+ bfqq = NULL;
|
|
|
|
+
|
|
goto keep_queue;
|
|
goto keep_queue;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3803,6 +3845,14 @@ static struct request *bfq_dispatch_rq_from_bfqq(struct bfq_data *bfqd,
|
|
|
|
|
|
bfq_dispatch_remove(bfqd->queue, rq);
|
|
bfq_dispatch_remove(bfqd->queue, rq);
|
|
|
|
|
|
|
|
+ if (bfqq != bfqd->in_service_queue) {
|
|
|
|
+ if (likely(bfqd->in_service_queue))
|
|
|
|
+ bfqd->in_service_queue->injected_service +=
|
|
|
|
+ bfq_serv_to_charge(rq, bfqq);
|
|
|
|
+
|
|
|
|
+ goto return_rq;
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* If weight raising has to terminate for bfqq, then next
|
|
* If weight raising has to terminate for bfqq, then next
|
|
* function causes an immediate update of bfqq's weight,
|
|
* 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,
|
|
* belongs to CLASS_IDLE and other queues are waiting for
|
|
* belongs to CLASS_IDLE and other queues are waiting for
|
|
* service.
|
|
* service.
|
|
*/
|
|
*/
|
|
- if (bfqd->busy_queues > 1 && bfq_class_idle(bfqq))
|
|
|
|
- goto expire;
|
|
|
|
-
|
|
|
|
- return rq;
|
|
|
|
|
|
+ if (!(bfqd->busy_queues > 1 && bfq_class_idle(bfqq)))
|
|
|
|
+ goto return_rq;
|
|
|
|
|
|
-expire:
|
|
|
|
bfq_bfqq_expire(bfqd, bfqq, false, BFQQE_BUDGET_EXHAUSTED);
|
|
bfq_bfqq_expire(bfqd, bfqq, false, BFQQE_BUDGET_EXHAUSTED);
|
|
|
|
+
|
|
|
|
+return_rq:
|
|
return rq;
|
|
return rq;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4232,6 +4281,13 @@ static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq,
|
|
bfq_mark_bfqq_has_short_ttime(bfqq);
|
|
bfq_mark_bfqq_has_short_ttime(bfqq);
|
|
bfq_mark_bfqq_sync(bfqq);
|
|
bfq_mark_bfqq_sync(bfqq);
|
|
bfq_mark_bfqq_just_created(bfqq);
|
|
bfq_mark_bfqq_just_created(bfqq);
|
|
|
|
+ /*
|
|
|
|
+ * Aggressively inject a lot of service: up to 90%.
|
|
|
|
+ * This coefficient remains constant during bfqq life,
|
|
|
|
+ * but this behavior might be changed, after enough
|
|
|
|
+ * testing and tuning.
|
|
|
|
+ */
|
|
|
|
+ bfqq->inject_coeff = 1;
|
|
} else
|
|
} else
|
|
bfq_clear_bfqq_sync(bfqq);
|
|
bfq_clear_bfqq_sync(bfqq);
|
|
|
|
|