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: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <keith.busch@intel.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
16c15eb16a
commit
f1342709d1
@ -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)
|
|||||||
|
|
||||||
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,
|
|||||||
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 {
|
|||||||
/* 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 {
|
|||||||
|
|
||||||
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 */
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user