Commit f1342709 authored by Keith Busch's avatar Keith Busch Committed by Jens Axboe

scsi: Do not rely on blk-mq for double completions

The scsi timeout error handling had been directly updating the block
layer's request state to prevent a error handling and a natural completion
from completing the same request twice. Fix this layering violation
by having scsi control the fate of its commands with scsi owned flags
rather than use blk-mq's.
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarKeith Busch <keith.busch@intel.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 16c15eb1
...@@ -297,19 +297,19 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) ...@@ -297,19 +297,19 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
if (rtn == BLK_EH_DONE) { if (rtn == BLK_EH_DONE) {
/* /*
* For blk-mq, we must set the request state to complete now * Set the command to complete first in order to prevent a real
* before sending the request to the scsi error handler. This * completion from releasing the command while error handling
* will prevent a use-after-free in the event the LLD manages * is using it. If the command was already completed, then the
* to complete the request before the error handler finishes * lower level driver beat the timeout handler, and it is safe
* processing this timed out request. * to return without escalating error recovery.
* *
* If the request was already completed, then the LLD beat the * If timeout handling lost the race to a real completion, the
* time out handler from transferring the request to the scsi * block layer may ignore that due to a fake timeout injection,
* error handler. In that case we can return immediately as no * so return RESET_TIMER to allow error handling another shot
* further action is required. * at this command.
*/ */
if (!blk_mq_mark_complete(req)) if (test_and_set_bit(SCMD_STATE_COMPLETE, &scmd->state))
return rtn; return BLK_EH_RESET_TIMER;
if (scsi_abort_command(scmd) != SUCCESS) { if (scsi_abort_command(scmd) != SUCCESS) {
set_host_byte(scmd, DID_TIME_OUT); set_host_byte(scmd, DID_TIME_OUT);
scsi_eh_scmd_add(scmd); scsi_eh_scmd_add(scmd);
......
...@@ -1642,8 +1642,18 @@ static blk_status_t scsi_mq_prep_fn(struct request *req) ...@@ -1642,8 +1642,18 @@ static blk_status_t scsi_mq_prep_fn(struct request *req)
static void scsi_mq_done(struct scsi_cmnd *cmd) static void scsi_mq_done(struct scsi_cmnd *cmd)
{ {
if (unlikely(test_and_set_bit(SCMD_STATE_COMPLETE, &cmd->state)))
return;
trace_scsi_dispatch_cmd_done(cmd); trace_scsi_dispatch_cmd_done(cmd);
blk_mq_complete_request(cmd->request);
/*
* If the block layer didn't complete the request due to a timeout
* injection, scsi must clear its internal completed state so that the
* timeout handler will see it needs to escalate its own error
* recovery.
*/
if (unlikely(!blk_mq_complete_request(cmd->request)))
clear_bit(SCMD_STATE_COMPLETE, &cmd->state);
} }
static void scsi_mq_put_budget(struct blk_mq_hw_ctx *hctx) static void scsi_mq_put_budget(struct blk_mq_hw_ctx *hctx)
...@@ -1702,6 +1712,7 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx, ...@@ -1702,6 +1712,7 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx,
if (!scsi_host_queue_ready(q, shost, sdev)) if (!scsi_host_queue_ready(q, shost, sdev))
goto out_dec_target_busy; goto out_dec_target_busy;
clear_bit(SCMD_STATE_COMPLETE, &cmd->state);
if (!(req->rq_flags & RQF_DONTPREP)) { if (!(req->rq_flags & RQF_DONTPREP)) {
ret = scsi_mq_prep_fn(req); ret = scsi_mq_prep_fn(req);
if (ret != BLK_STS_OK) if (ret != BLK_STS_OK)
......
...@@ -61,6 +61,9 @@ struct scsi_pointer { ...@@ -61,6 +61,9 @@ struct scsi_pointer {
/* flags preserved across unprep / reprep */ /* flags preserved across unprep / reprep */
#define SCMD_PRESERVED_FLAGS (SCMD_UNCHECKED_ISA_DMA | SCMD_INITIALIZED) #define SCMD_PRESERVED_FLAGS (SCMD_UNCHECKED_ISA_DMA | SCMD_INITIALIZED)
/* for scmd->state */
#define SCMD_STATE_COMPLETE (1 << 0)
struct scsi_cmnd { struct scsi_cmnd {
struct scsi_request req; struct scsi_request req;
struct scsi_device *device; struct scsi_device *device;
...@@ -145,6 +148,7 @@ struct scsi_cmnd { ...@@ -145,6 +148,7 @@ struct scsi_cmnd {
int result; /* Status code from lower level driver */ int result; /* Status code from lower level driver */
int flags; /* Command flags */ int flags; /* Command flags */
unsigned long state; /* Command completion state */
unsigned char tag; /* SCSI-II queued command tag */ unsigned char tag; /* SCSI-II queued command tag */
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment