Re: [PATCH v2] Make SCSI Status CONDITION MET equivalent to GOOD

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 02/25/2018 07:29 PM, Douglas Gilbert wrote:
> The SCSI PRE-FETCH (10 or 16) command is present both on hard disks
> and some SSDs. It is useful when the address of the next block(s) to
> be read is known but it is not following the LBA of the current READ
> (so read-ahead won't help). It returns two "good" SCSI Status values.
> If the requested blocks have fitted (or will most likely fit (when
> the IMMED bit is set)) into the disk's cache, it returns CONDITION
> MET. If it didn't (or will not) fit then it returns GOOD status.
> 
> The primary goal of this patch is to stop the SCSI subsystem treating
> the CONDITION MET SCSI status as an error. The current state makes the
> PRE-FETCH command effectively unusable via pass-throughs. The hunt to
> find where the erroneous decision was made led to the
> scsi_io_completion() function in scsi_lib.c . This is a complicated
> function made more complicated by the intertwining of good and error
> (or special case) processing paths.
> 
> Future work: if the sd driver was to use the PRE-FETCH command,
> decide whether it and/or the block layer needs to know about the
> two different "good" statuses. If so a mechanism would be needed
> to do that.
> 
> ChangeLog to v2:
>   - further restructure the code, place some early non-zero
>     result processing in a new helper function:
>     scsi_io_completion_nz_result()
>   - this reduces the number of error checks on the zero result
>     path (the fast path) at the expense of some extra work for
>     the non-zero result processing
>   - rename some variables to make the code a little clearer
> 
> ChangeLog to v1:
>   - expand scsi_status_is_good() to check for CONDITION MET
>   - add another corner case in scsi_io_completion() adjacent
>     to the one for the RECOVERED ERROR sense key case. That
>     is another "non-error"
>   - structure code so extra checks are only on the error
>     path (i.e. when cmd->result is non-zero)
> 
> This patch is against mkp's 4.17/scsi-queue branch. It also applies
> to lk 4.15.x where it was re-tested on a SAS SSD.
> 
> Signed-off-by: Douglas Gilbert <dgilbert@xxxxxxxxxxxx>
> ---
>  drivers/scsi/scsi_lib.c | 140 +++++++++++++++++++++++++++++-------------------
>  include/scsi/scsi.h     |   2 +
>  2 files changed, 87 insertions(+), 55 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index aea5a1ae318b..e1e974f08d52 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -738,6 +738,73 @@ static blk_status_t __scsi_error_from_host_byte(struct scsi_cmnd *cmd,
>  	}
>  }
>  
> +/* Helper for scsi_io_completion() when cmd->result is non-zero. */
> +static int scsi_io_completion_nz_result(struct scsi_cmnd *cmd,
> +					blk_status_t *blk_statp)
> +{
> +	bool sense_valid;
> +	bool about_current;
> +	int result = cmd->result;
> +	struct request *req = cmd->request;
> +	struct scsi_sense_hdr sshdr;
> +
> +	sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
> +	about_current = sense_valid ? !scsi_sense_is_deferred(&sshdr) : true;
> +
> +	if (blk_rq_is_passthrough(req)) {
> +		if (sense_valid) {
> +			/*
> +			 * SG_IO wants current and deferred errors
> +			 */
> +			scsi_req(req)->sense_len =
> +				min(8 + cmd->sense_buffer[7],
> +				    SCSI_SENSE_BUFFERSIZE);
> +		}
> +		if (about_current)
> +			*blk_statp = __scsi_error_from_host_byte(cmd, result);
> +	} else if (blk_rq_bytes(req) == 0 && about_current) {
> +		/*
> +		 * Flush commands do not transfers any data, and thus cannot use
> +		 * good_bytes != blk_rq_bytes(req) as the signal for an error.
> +		 * This sets the error explicitly for the problem case.
> +		 */
> +		*blk_statp = __scsi_error_from_host_byte(cmd, result);
> +	}
> +	/*
> +	 * Recovered errors need reporting, but they're always treated as
> +	 * success, so fiddle the result code here.  For passthrough requests
> +	 * we already took a copy of the original into sreq->result which
> +	 * is what gets returned to the user
> +	 */
> +	if (sense_valid && (sshdr.sense_key == RECOVERED_ERROR)) {
> +		/*
> +		 * if ATA PASS-THROUGH INFORMATION AVAILABLE skip
> +		 * print since caller wants ATA registers. Only occurs
> +		 * on SCSI ATA PASS_THROUGH commands when CK_COND=1
> +		 */
> +		if ((sshdr.asc == 0x0) && (sshdr.ascq == 0x1d))
> +			;
> +		else if (!(req->rq_flags & RQF_QUIET))
> +			scsi_print_sense(cmd);
> +		result = 0;
> +		*blk_statp = BLK_STS_OK;
> +		/* for passthrough, blk_stat may be set */
> +	}
> +	/*
> +	 * Another corner case: the SCSI status byte is non-zero but 'good'.
> +	 * Example: PRE-FETCH command returns SAM_STAT_CONDITION_MET when
> +	 * it is able to fit nominated LBs in its cache (and SAM_STAT_GOOD
> +	 * if it can't fit). Treat SAM_STAT_CONDITION_MET and the related
> +	 * intermediate statuses (both obsolete in SAM-4) as good.
> +	 */
> +	if (status_byte(result) && scsi_status_is_good(result)) {
> +		result = 0;
> +		/* for passthrough error may be set */
> +		*blk_statp = BLK_STS_OK;
> +	}
> +	return result;
> +}
> +
>  /*
>   * Function:    scsi_io_completion()
>   *

Hmm. Can't we return blk_stat from this function, and adjusting the
'result' value after it with an if-clause like

if (blk_stat == BLK_STS_OK)
	result = 0;

That would cleanup up the function and avoid having (essentially) two
return values.

The only problem here is that __scsi_error_from_hostbyte() will return
BLK_STS_IOERR if result == 0; doubt that is intended.
And I guess it'll affect this issue, too.

Mind sending a separate patch for that?

> @@ -772,33 +839,23 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
>  	int result = cmd->result;
>  	struct request_queue *q = cmd->device->request_queue;
>  	struct request *req = cmd->request;
> -	blk_status_t error = BLK_STS_OK;
> +	blk_status_t blk_stat = BLK_STS_OK;	/* enum, BLK_STS_OK is 0 */
>  	struct scsi_sense_hdr sshdr;
> -	bool sense_valid = false;
> -	int sense_deferred = 0, level = 0;
> +	bool sense_valid_and_current = false;
> +	int level = 0;
>  	enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY,
>  	      ACTION_DELAYED_RETRY} action;
>  	unsigned long wait_for = (cmd->allowed + 1) * req->timeout;
>  
>  	if (result) {
> -		sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
> -		if (sense_valid)
> -			sense_deferred = scsi_sense_is_deferred(&sshdr);
> +		/* sense not about current command is termed: deferred */
> +		if (scsi_command_normalize_sense(cmd, &sshdr) &&
> +		    !scsi_sense_is_deferred(&sshdr))
> +			sense_valid_and_current = true;
> +		result = scsi_io_completion_nz_result(cmd, &blk_stat);
>  	}
>  
>  	if (blk_rq_is_passthrough(req)) {
> -		if (result) {
> -			if (sense_valid) {
> -				/*
> -				 * SG_IO wants current and deferred errors
> -				 */
> -				scsi_req(req)->sense_len =
> -					min(8 + cmd->sense_buffer[7],
> -					    SCSI_SENSE_BUFFERSIZE);
> -			}
> -			if (!sense_deferred)
> -				error = __scsi_error_from_host_byte(cmd, result);
> -		}
>  		/*
>  		 * __scsi_error_from_host_byte may have reset the host_byte
>  		 */
> @@ -816,13 +873,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
>  				BUG();
>  			return;
>  		}
> -	} else if (blk_rq_bytes(req) == 0 && result && !sense_deferred) {
> -		/*
> -		 * Flush commands do not transfers any data, and thus cannot use
> -		 * good_bytes != blk_rq_bytes(req) as the signal for an error.
> -		 * This sets the error explicitly for the problem case.
> -		 */
> -		error = __scsi_error_from_host_byte(cmd, result);
>  	}
>  
>  	/* no bidi support for !blk_rq_is_passthrough yet */
> @@ -836,40 +886,20 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
>  		"%u sectors total, %d bytes done.\n",
>  		blk_rq_sectors(req), good_bytes));
>  
> -	/*
> -	 * Recovered errors need reporting, but they're always treated as
> -	 * success, so fiddle the result code here.  For passthrough requests
> -	 * we already took a copy of the original into sreq->result which
> -	 * is what gets returned to the user
> -	 */
> -	if (sense_valid && (sshdr.sense_key == RECOVERED_ERROR)) {
> -		/* if ATA PASS-THROUGH INFORMATION AVAILABLE skip
> -		 * print since caller wants ATA registers. Only occurs on
> -		 * SCSI ATA PASS_THROUGH commands when CK_COND=1
> -		 */
> -		if ((sshdr.asc == 0x0) && (sshdr.ascq == 0x1d))
> -			;
> -		else if (!(req->rq_flags & RQF_QUIET))
> -			scsi_print_sense(cmd);
> -		result = 0;
> -		/* for passthrough error may be set */
> -		error = BLK_STS_OK;
> -	}
> -
>  	/*
>  	 * special case: failed zero length commands always need to
>  	 * drop down into the retry code. Otherwise, if we finished
>  	 * all bytes in the request we are done now.
>  	 */
> -	if (!(blk_rq_bytes(req) == 0 && error) &&
> -	    !scsi_end_request(req, error, good_bytes, 0))
> +	if (!(blk_rq_bytes(req) == 0 && blk_stat) &&
> +	    !scsi_end_request(req, blk_stat, good_bytes, 0))
>  		return;
>  
>  	/*
>  	 * Kill remainder if no retrys.
>  	 */
> -	if (error && scsi_noretry_cmd(cmd)) {
> -		if (scsi_end_request(req, error, blk_rq_bytes(req), 0))
> +	if (blk_stat && scsi_noretry_cmd(cmd)) {
> +		if (scsi_end_request(req, blk_stat, blk_rq_bytes(req), 0))
>  			BUG();
>  		return;
>  	}
> @@ -881,7 +911,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
>  	if (result == 0)
>  		goto requeue;
>  
> -	error = __scsi_error_from_host_byte(cmd, result);
> +	blk_stat = __scsi_error_from_host_byte(cmd, result);
>  
>  	if (host_byte(result) == DID_RESET) {
>  		/* Third party bus reset or reset for error recovery
> @@ -889,7 +919,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
>  		 * happens.
>  		 */
>  		action = ACTION_RETRY;
> -	} else if (sense_valid && !sense_deferred) {
> +	} else if (sense_valid_and_current) {
>  		switch (sshdr.sense_key) {
>  		case UNIT_ATTENTION:
>  			if (cmd->device->removable) {
> @@ -925,18 +955,18 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
>  				action = ACTION_REPREP;
>  			} else if (sshdr.asc == 0x10) /* DIX */ {
>  				action = ACTION_FAIL;
> -				error = BLK_STS_PROTECTION;
> +				blk_stat = BLK_STS_PROTECTION;
>  			/* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
>  			} else if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
>  				action = ACTION_FAIL;
> -				error = BLK_STS_TARGET;
> +				blk_stat = BLK_STS_TARGET;
>  			} else
>  				action = ACTION_FAIL;
>  			break;
>  		case ABORTED_COMMAND:
>  			action = ACTION_FAIL;
>  			if (sshdr.asc == 0x10) /* DIF */
> -				error = BLK_STS_PROTECTION;
> +				blk_stat = BLK_STS_PROTECTION;
>  			break;
>  		case NOT_READY:
>  			/* If the device is in the process of becoming
> @@ -999,7 +1029,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
>  				scsi_print_command(cmd);
>  			}
>  		}
> -		if (!scsi_end_request(req, error, blk_rq_err_bytes(req), 0))
> +		if (!scsi_end_request(req, blk_stat, blk_rq_err_bytes(req), 0))
>  			return;
>  		/*FALLTHRU*/
>  	case ACTION_REPREP:
> diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
> index cb85eddb47ea..eb7853c1a23b 100644
> --- a/include/scsi/scsi.h
> +++ b/include/scsi/scsi.h
> @@ -47,6 +47,8 @@ static inline int scsi_status_is_good(int status)
>  	 */
>  	status &= 0xfe;
>  	return ((status == SAM_STAT_GOOD) ||
> +		(status == SAM_STAT_CONDITION_MET) ||
> +		/* Next two "intermediate" statuses are obsolete in SAM-4 */
>  		(status == SAM_STAT_INTERMEDIATE) ||
>  		(status == SAM_STAT_INTERMEDIATE_CONDITION_MET) ||
>  		/* FIXME: this is obsolete in SAM-3 */
> 
Other than that it's a nice cleanup.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke		   Teamlead Storage & Networking
hare@xxxxxxx			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: F. Imendörffer, J. Smithard, J. Guild, D. Upmanyu, G. Norton
HRB 21284 (AG Nürnberg)



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux