Skip to content

Commit e5c0ca1

Browse files
Chengming Zhouaxboe
authored andcommitted
blk-mq: release scheduler resource when request completes
Chuck reported [1] an IO hang problem on NFS exports that reside on SATA devices and bisected to commit 615939a ("blk-mq: defer to the normal submission path for post-flush requests"). We analysed the IO hang problem, found there are two postflush requests waiting for each other. The first postflush request completed the REQ_FSEQ_DATA sequence, so go to the REQ_FSEQ_POSTFLUSH sequence and added in the flush pending list, but failed to blk_kick_flush() because of the second postflush request which is inflight waiting in scheduler queue. The second postflush waiting in scheduler queue can't be dispatched because the first postflush hasn't released scheduler resource even though it has completed by itself. Fix it by releasing scheduler resource when the first postflush request completed, so the second postflush can be dispatched and completed, then make blk_kick_flush() succeed. While at it, remove the check for e->ops.finish_request, as all schedulers set that. Reaffirm this requirement by adding a WARN_ON_ONCE() at scheduler registration time, just like we do for insert_requests and dispatch_request. [1] https://lore.kernel.org/all/7A57C7AE-A51A-4254-888B-FE15CA21F9E9@oracle.com/ Link: https://lore.kernel.org/linux-block/20230819031206.2744005-1-chengming.zhou@linux.dev/ Reported-by: kernel test robot <oliver.sang@intel.com> Closes: https://lore.kernel.org/oe-lkp/202308172100.8ce4b853-oliver.sang@intel.com Fixes: 615939a ("blk-mq: defer to the normal submission path for post-flush requests") Reported-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Chengming Zhou <zhouchengming@bytedance.com> Tested-by: Chuck Lever <chuck.lever@oracle.com> Link: https://lore.kernel.org/r/20230813152325.3017343-1-chengming.zhou@linux.dev [axboe: folded in incremental fix and added tags] Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent c984ff1 commit e5c0ca1

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

block/blk-mq.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,21 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q,
681681
}
682682
EXPORT_SYMBOL_GPL(blk_mq_alloc_request_hctx);
683683

684+
static void blk_mq_finish_request(struct request *rq)
685+
{
686+
struct request_queue *q = rq->q;
687+
688+
if (rq->rq_flags & RQF_USE_SCHED) {
689+
q->elevator->type->ops.finish_request(rq);
690+
/*
691+
* For postflush request that may need to be
692+
* completed twice, we should clear this flag
693+
* to avoid double finish_request() on the rq.
694+
*/
695+
rq->rq_flags &= ~RQF_USE_SCHED;
696+
}
697+
}
698+
684699
static void __blk_mq_free_request(struct request *rq)
685700
{
686701
struct request_queue *q = rq->q;
@@ -707,9 +722,7 @@ void blk_mq_free_request(struct request *rq)
707722
{
708723
struct request_queue *q = rq->q;
709724

710-
if ((rq->rq_flags & RQF_USE_SCHED) &&
711-
q->elevator->type->ops.finish_request)
712-
q->elevator->type->ops.finish_request(rq);
725+
blk_mq_finish_request(rq);
713726

714727
if (unlikely(laptop_mode && !blk_rq_is_passthrough(rq)))
715728
laptop_io_completion(q->disk->bdi);
@@ -1020,6 +1033,8 @@ inline void __blk_mq_end_request(struct request *rq, blk_status_t error)
10201033
if (blk_mq_need_time_stamp(rq))
10211034
__blk_mq_end_request_acct(rq, ktime_get_ns());
10221035

1036+
blk_mq_finish_request(rq);
1037+
10231038
if (rq->end_io) {
10241039
rq_qos_done(rq->q, rq);
10251040
if (rq->end_io(rq, error) == RQ_END_IO_FREE)
@@ -1074,6 +1089,8 @@ void blk_mq_end_request_batch(struct io_comp_batch *iob)
10741089
if (iob->need_ts)
10751090
__blk_mq_end_request_acct(rq, now);
10761091

1092+
blk_mq_finish_request(rq);
1093+
10771094
rq_qos_done(rq->q, rq);
10781095

10791096
/*

block/elevator.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ void elv_unregister_queue(struct request_queue *q)
499499

500500
int elv_register(struct elevator_type *e)
501501
{
502+
/* finish request is mandatory */
503+
if (WARN_ON_ONCE(!e->ops.finish_request))
504+
return -EINVAL;
502505
/* insert_requests and dispatch_request are mandatory */
503506
if (WARN_ON_ONCE(!e->ops.insert_requests || !e->ops.dispatch_request))
504507
return -EINVAL;

0 commit comments

Comments
 (0)