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)