blk-mq: fix race with timeouts and requeue events

If a requeue event races with a timeout, we can get into the
situation where we attempt to complete a request from the
timeout handler when it's not start anymore. This causes a crash.
So have the timeout handler check that REQ_ATOM_STARTED is still
set on the request - if not, we ignore the event. If this happens,
the request has now been marked as complete. As a consequence, we
need to ensure to clear REQ_ATOM_COMPLETE in blk_mq_start_request(),
as to maintain proper request state.

Signed-off-by: Jens Axboe <axboe@fb.com>
This commit is contained in:
Jens Axboe
2014-04-24 08:51:47 -06:00
parent 70ab0b2d51
commit 87ee7b1121
4 changed files with 42 additions and 18 deletions

View File

@@ -96,11 +96,7 @@ static void blk_rq_timed_out(struct request *req)
__blk_complete_request(req);
break;
case BLK_EH_RESET_TIMER:
if (q->mq_ops)
blk_mq_add_timer(req);
else
blk_add_timer(req);
blk_add_timer(req);
blk_clear_rq_complete(req);
break;
case BLK_EH_NOT_HANDLED:
@@ -170,7 +166,8 @@ void blk_abort_request(struct request *req)
}
EXPORT_SYMBOL_GPL(blk_abort_request);
void __blk_add_timer(struct request *req, struct list_head *timeout_list)
static void __blk_add_timer(struct request *req,
struct list_head *timeout_list)
{
struct request_queue *q = req->q;
unsigned long expiry;
@@ -225,6 +222,11 @@ void __blk_add_timer(struct request *req, struct list_head *timeout_list)
*/
void blk_add_timer(struct request *req)
{
__blk_add_timer(req, &req->q->timeout_list);
struct request_queue *q = req->q;
if (q->mq_ops)
__blk_add_timer(req, NULL);
else
__blk_add_timer(req, &req->q->timeout_list);
}