From: Yu Kuai <yukuai3@xxxxxxxxxx> Introduce struct sched_dispatch_ctx, and split the helper into blk_mq_dispatch_one_request() and blk_mq_finish_dispatch(). Also add comments about the non-error return value. Make code cleaner, and make it easier to add a new branch to dispatch a batch of requests at a time in the next patch. Signed-off-by: Yu Kuai <yukuai3@xxxxxxxxxx> --- block/blk-mq-sched.c | 196 ++++++++++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 78 deletions(-) diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index 42e3ec06c072..7b77cdb9395d 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c @@ -16,6 +16,16 @@ #include "blk-mq-sched.h" #include "blk-wbt.h" +struct sched_dispatch_ctx { + struct blk_mq_hw_ctx *hctx; + struct list_head rq_list; + + int count; + bool multi_hctxs; + bool run_queue; + bool busy; +}; + /* * Mark a hardware queue as needing a restart. */ @@ -74,92 +84,92 @@ static bool blk_mq_dispatch_hctx_list(struct list_head *rq_list) #define BLK_MQ_BUDGET_DELAY 3 /* ms units */ -/* - * Only SCSI implements .get_budget and .put_budget, and SCSI restarts - * its queue by itself in its completion handler, so we don't need to - * restart queue if .get_budget() fails to get the budget. - * - * Returns -EAGAIN if hctx->dispatch was found non-empty and run_work has to - * be run again. This is necessary to avoid starving flushes. - */ -static int __blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) +static bool blk_mq_should_dispatch(struct sched_dispatch_ctx *ctx) { - struct request_queue *q = hctx->queue; - struct elevator_queue *e = q->elevator; - bool multi_hctxs = false, run_queue = false; - bool dispatched = false, busy = false; - unsigned int max_dispatch; - LIST_HEAD(rq_list); - int count = 0; + struct elevator_queue *e = ctx->hctx->queue->elevator; - if (hctx->dispatch_busy) - max_dispatch = 1; - else - max_dispatch = hctx->queue->nr_requests; + if (e->type->ops.has_work && !e->type->ops.has_work(ctx->hctx)) + return false; - do { - struct request *rq; - int budget_token; + if (!list_empty_careful(&ctx->hctx->dispatch)) { + ctx->busy = true; + return false; + } - if (e->type->ops.has_work && !e->type->ops.has_work(hctx)) - break; + return true; +} - if (!list_empty_careful(&hctx->dispatch)) { - busy = true; - break; - } +static bool blk_mq_dispatch_one_request(struct sched_dispatch_ctx *ctx) +{ + struct request_queue *q = ctx->hctx->queue; + struct elevator_queue *e = q->elevator; + struct request *rq; + int budget_token; - budget_token = blk_mq_get_dispatch_budget(q); - if (budget_token < 0) - break; + if (!blk_mq_should_dispatch(ctx)) + return false; - if (blk_queue_sq_sched(q)) { - elevator_lock_irq(e); - rq = e->type->ops.dispatch_request(hctx); - elevator_unlock_irq(e); - } else { - rq = e->type->ops.dispatch_request(hctx); - } + budget_token = blk_mq_get_dispatch_budget(q); + if (budget_token < 0) + return false; - if (!rq) { - blk_mq_put_dispatch_budget(q, budget_token); - /* - * We're releasing without dispatching. Holding the - * budget could have blocked any "hctx"s with the - * same queue and if we didn't dispatch then there's - * no guarantee anyone will kick the queue. Kick it - * ourselves. - */ - run_queue = true; - break; - } - - blk_mq_set_rq_budget_token(rq, budget_token); + if (blk_queue_sq_sched(q)) { + elevator_lock_irq(e); + rq = e->type->ops.dispatch_request(ctx->hctx); + elevator_unlock_irq(e); + } else { + rq = e->type->ops.dispatch_request(ctx->hctx); + } + if (!rq) { + blk_mq_put_dispatch_budget(q, budget_token); /* - * Now this rq owns the budget which has to be released - * if this rq won't be queued to driver via .queue_rq() - * in blk_mq_dispatch_rq_list(). + * We're releasing without dispatching. Holding the + * budget could have blocked any "hctx"s with the + * same queue and if we didn't dispatch then there's + * no guarantee anyone will kick the queue. Kick it + * ourselves. */ - list_add_tail(&rq->queuelist, &rq_list); - count++; - if (rq->mq_hctx != hctx) - multi_hctxs = true; + ctx->run_queue = true; + return false; + } - /* - * If we cannot get tag for the request, stop dequeueing - * requests from the IO scheduler. We are unlikely to be able - * to submit them anyway and it creates false impression for - * scheduling heuristics that the device can take more IO. - */ - if (!blk_mq_get_driver_tag(rq)) - break; - } while (count < max_dispatch); + blk_mq_set_rq_budget_token(rq, budget_token); - if (!count) { - if (run_queue) - blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY); - } else if (multi_hctxs) { + /* + * Now this rq owns the budget which has to be released + * if this rq won't be queued to driver via .queue_rq() + * in blk_mq_dispatch_rq_list(). + */ + list_add_tail(&rq->queuelist, &ctx->rq_list); + ctx->count++; + if (rq->mq_hctx != ctx->hctx) + ctx->multi_hctxs = true; + + /* + * If we cannot get tag for the request, stop dequeueing + * requests from the IO scheduler. We are unlikely to be able + * to submit them anyway and it creates false impression for + * scheduling heuristics that the device can take more IO. + */ + return blk_mq_get_driver_tag(rq); +} + +/* + * Returns -EAGAIN if hctx->dispatch was found non-empty and run_work has to + * be run again. This is necessary to avoid starving flushes. + * Return 0 if no request is dispatched. + * Return 1 if at least one request is dispatched. + */ +static int blk_mq_finish_dispatch(struct sched_dispatch_ctx *ctx) +{ + bool dispatched = false; + + if (!ctx->count) { + if (ctx->run_queue) + blk_mq_delay_run_hw_queues(ctx->hctx->queue, + BLK_MQ_BUDGET_DELAY); + } else if (ctx->multi_hctxs) { /* * Requests from different hctx may be dequeued from some * schedulers, such as bfq and deadline. @@ -167,19 +177,49 @@ static int __blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) * Sort the requests in the list according to their hctx, * dispatch batching requests from same hctx at a time. */ - list_sort(NULL, &rq_list, sched_rq_cmp); + list_sort(NULL, &ctx->rq_list, sched_rq_cmp); do { - dispatched |= blk_mq_dispatch_hctx_list(&rq_list); - } while (!list_empty(&rq_list)); + dispatched |= blk_mq_dispatch_hctx_list(&ctx->rq_list); + } while (!list_empty(&ctx->rq_list)); } else { - dispatched = blk_mq_dispatch_rq_list(hctx, &rq_list, false); + dispatched = blk_mq_dispatch_rq_list(ctx->hctx, &ctx->rq_list, + false); } - if (busy) + if (ctx->busy) return -EAGAIN; + return !!dispatched; } +/* + * Only SCSI implements .get_budget and .put_budget, and SCSI restarts + * its queue by itself in its completion handler, so we don't need to + * restart queue if .get_budget() fails to get the budget. + * + * See blk_mq_finish_dispatch() for return values. + */ +static int __blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) +{ + unsigned int max_dispatch; + struct sched_dispatch_ctx ctx = { + .hctx = hctx, + .rq_list = LIST_HEAD_INIT(ctx.rq_list), + }; + + if (hctx->dispatch_busy) + max_dispatch = 1; + else + max_dispatch = hctx->queue->nr_requests; + + do { + if (!blk_mq_dispatch_one_request(&ctx)) + break; + } while (ctx.count < max_dispatch); + + return blk_mq_finish_dispatch(&ctx); +} + static int blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) { unsigned long end = jiffies + HZ; -- 2.39.2