forked from mirrors/linux
		
	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
					
				
					 3 changed files with 27 additions and 12 deletions
				
			
		|  | @ -297,19 +297,19 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) | |||
| 
 | ||||
| 	if (rtn == BLK_EH_DONE) { | ||||
| 		/*
 | ||||
| 		 * For blk-mq, we must set the request state to complete now | ||||
| 		 * before sending the request to the scsi error handler. This | ||||
| 		 * will prevent a use-after-free in the event the LLD manages | ||||
| 		 * to complete the request before the error handler finishes | ||||
| 		 * processing this timed out request. | ||||
| 		 * Set the command to complete first in order to prevent a real | ||||
| 		 * completion from releasing the command while error handling | ||||
| 		 * is using it. If the command was already completed, then the | ||||
| 		 * lower level driver beat the timeout handler, and it is safe | ||||
| 		 * to return without escalating error recovery. | ||||
| 		 * | ||||
| 		 * If the request was already completed, then the LLD beat the | ||||
| 		 * time out handler from transferring the request to the scsi | ||||
| 		 * error handler. In that case we can return immediately as no | ||||
| 		 * further action is required. | ||||
| 		 * If timeout handling lost the race to a real completion, the | ||||
| 		 * block layer may ignore that due to a fake timeout injection, | ||||
| 		 * so return RESET_TIMER to allow error handling another shot | ||||
| 		 * at this command. | ||||
| 		 */ | ||||
| 		if (!blk_mq_mark_complete(req)) | ||||
| 			return rtn; | ||||
| 		if (test_and_set_bit(SCMD_STATE_COMPLETE, &scmd->state)) | ||||
| 			return BLK_EH_RESET_TIMER; | ||||
| 		if (scsi_abort_command(scmd) != SUCCESS) { | ||||
| 			set_host_byte(scmd, DID_TIME_OUT); | ||||
| 			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) | ||||
| { | ||||
| 	if (unlikely(test_and_set_bit(SCMD_STATE_COMPLETE, &cmd->state))) | ||||
| 		return; | ||||
| 	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) | ||||
|  | @ -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)) | ||||
| 		goto out_dec_target_busy; | ||||
| 
 | ||||
| 	clear_bit(SCMD_STATE_COMPLETE, &cmd->state); | ||||
| 	if (!(req->rq_flags & RQF_DONTPREP)) { | ||||
| 		ret = scsi_mq_prep_fn(req); | ||||
| 		if (ret != BLK_STS_OK) | ||||
|  |  | |||
|  | @ -61,6 +61,9 @@ struct scsi_pointer { | |||
| /* flags preserved across unprep / reprep */ | ||||
| #define SCMD_PRESERVED_FLAGS	(SCMD_UNCHECKED_ISA_DMA | SCMD_INITIALIZED) | ||||
| 
 | ||||
| /* for scmd->state */ | ||||
| #define SCMD_STATE_COMPLETE	(1 << 0) | ||||
| 
 | ||||
| struct scsi_cmnd { | ||||
| 	struct scsi_request req; | ||||
| 	struct scsi_device *device; | ||||
|  | @ -145,6 +148,7 @@ struct scsi_cmnd { | |||
| 
 | ||||
| 	int result;		/* Status code from lower level driver */ | ||||
| 	int flags;		/* Command flags */ | ||||
| 	unsigned long state;	/* Command completion state */ | ||||
| 
 | ||||
| 	unsigned char tag;	/* SCSI-II queued command tag */ | ||||
| }; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Keith Busch
						Keith Busch