mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
blk-mq: don't bring back flags of a completed request
The timeout handler sets and clears RQF_TIMED_OUT in a request it only
holds a reference to, with a plain read-modify-write, while the
completion clears flags in rq_flags the same way. If the two interleave,
blk_mq_finish_request() runs twice with the flags back: the zone write
plug reference is dropped twice and the scheduler's ->finish_request()
is called again.
Change RQF_TIMED_OUT with cmpxchg, so the handler never writes back a
flag that changed under it.
Fixes: e5c0ca1365 ("blk-mq: release scheduler resource when request completes")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20261028164415.1262013-31-axboe@kernel.dk
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
620cfc7a66
commit
9531230ee1
|
|
@ -1639,7 +1639,7 @@ EXPORT_SYMBOL_GPL(blk_mq_queue_inflight);
|
|||
|
||||
static void blk_mq_rq_timed_out(struct request *req)
|
||||
{
|
||||
req->rq_flags |= RQF_TIMED_OUT;
|
||||
blk_rq_change_timed_out(req, true);
|
||||
if (req->q->mq_ops->timeout) {
|
||||
enum blk_eh_timer_return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,8 @@ void blk_add_timer(struct request *req)
|
|||
if (!req->timeout)
|
||||
req->timeout = q->rq_timeout;
|
||||
|
||||
req->rq_flags &= ~RQF_TIMED_OUT;
|
||||
if (req->rq_flags & RQF_TIMED_OUT)
|
||||
blk_rq_change_timed_out(req, false);
|
||||
|
||||
expiry = jiffies + req->timeout;
|
||||
WRITE_ONCE(req->deadline, expiry);
|
||||
|
|
|
|||
14
block/blk.h
14
block/blk.h
|
|
@ -332,6 +332,20 @@ static inline void bio_integrity_free(struct bio *bio)
|
|||
unsigned long blk_rq_timeout(unsigned long timeout);
|
||||
void blk_add_timer(struct request *req);
|
||||
|
||||
/*
|
||||
* The timeout handler changes RQF_TIMED_OUT in a request which it does not
|
||||
* own: whoever completes it changes rq_flags at the same time, with plain
|
||||
* loads and stores. Never write back a flag that has changed under us.
|
||||
*/
|
||||
static inline void blk_rq_change_timed_out(struct request *rq, bool set)
|
||||
{
|
||||
req_flags_t old = READ_ONCE(rq->rq_flags), new;
|
||||
|
||||
do {
|
||||
new = set ? old | RQF_TIMED_OUT : old & ~RQF_TIMED_OUT;
|
||||
} while (!try_cmpxchg(&rq->rq_flags, &old, new));
|
||||
}
|
||||
|
||||
enum bio_merge_status {
|
||||
BIO_MERGE_OK,
|
||||
BIO_MERGE_NONE,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user