Commit 3f90ac71 authored by Martin K. Petersen's avatar Martin K. Petersen

Merge patch series "scsi: Allow scsi_execute users to request retries"

Mike Christie <michael.christie@oracle.com> says:

The following patches were made over Linus's tree which contains a fix
for sd which was not in Martin's branches.

The patches allow scsi_execute_cmd users to have scsi-ml retry the cmd
for it instead of the caller having to parse the error and loop
itself.

Link: https://lore.kernel.org/r/20240123002220.129141-1-michael.christie@oracle.comSigned-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parents 8179041f 25a1f7a0
...@@ -67,6 +67,15 @@ config SCSI_PROC_FS ...@@ -67,6 +67,15 @@ config SCSI_PROC_FS
If unsure say Y. If unsure say Y.
config SCSI_LIB_KUNIT_TEST
tristate "KUnit tests for SCSI Mid Layer's scsi_lib" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
help
Run SCSI Mid Layer's KUnit tests for scsi_lib.
If unsure say N.
comment "SCSI support type (disk, tape, CD-ROM)" comment "SCSI support type (disk, tape, CD-ROM)"
depends on SCSI depends on SCSI
......
...@@ -113,7 +113,6 @@ typedef struct { ...@@ -113,7 +113,6 @@ typedef struct {
struct scsi_device **dt; /* ptrs to data transfer elements */ struct scsi_device **dt; /* ptrs to data transfer elements */
u_int firsts[CH_TYPES]; u_int firsts[CH_TYPES];
u_int counts[CH_TYPES]; u_int counts[CH_TYPES];
u_int unit_attention;
u_int voltags; u_int voltags;
struct mutex lock; struct mutex lock;
} scsi_changer; } scsi_changer;
...@@ -186,17 +185,29 @@ static int ...@@ -186,17 +185,29 @@ static int
ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len, ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len,
void *buffer, unsigned int buflength, enum req_op op) void *buffer, unsigned int buflength, enum req_op op)
{ {
int errno, retries = 0, timeout, result; int errno = 0, timeout, result;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = 3,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS) timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS)
? timeout_init : timeout_move; ? timeout_init : timeout_move;
retry:
errno = 0;
result = scsi_execute_cmd(ch->device, cmd, op, buffer, buflength, result = scsi_execute_cmd(ch->device, cmd, op, buffer, buflength,
timeout * HZ, MAX_RETRIES, &exec_args); timeout * HZ, MAX_RETRIES, &exec_args);
if (result < 0) if (result < 0)
...@@ -205,14 +216,6 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len, ...@@ -205,14 +216,6 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len,
if (debug) if (debug)
scsi_print_sense_hdr(ch->device, ch->name, &sshdr); scsi_print_sense_hdr(ch->device, ch->name, &sshdr);
errno = ch_find_errno(&sshdr); errno = ch_find_errno(&sshdr);
switch(sshdr.sense_key) {
case UNIT_ATTENTION:
ch->unit_attention = 1;
if (retries++ < 3)
goto retry;
break;
}
} }
return errno; return errno;
} }
......
...@@ -46,9 +46,6 @@ static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h, ...@@ -46,9 +46,6 @@ static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h,
int ret = SCSI_DH_IO; int ret = SCSI_DH_IO;
switch (sshdr->sense_key) { switch (sshdr->sense_key) {
case UNIT_ATTENTION:
ret = SCSI_DH_IMM_RETRY;
break;
case NOT_READY: case NOT_READY:
if (sshdr->asc == 0x04 && sshdr->ascq == 2) { if (sshdr->asc == 0x04 && sshdr->ascq == 2) {
/* /*
...@@ -85,11 +82,24 @@ static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) ...@@ -85,11 +82,24 @@ static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
int ret, res; int ret, res;
blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV | blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = SCMD_FAILURE_NO_LIMIT,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
retry:
res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT, res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
HP_SW_RETRIES, &exec_args); HP_SW_RETRIES, &exec_args);
if (res > 0 && scsi_sense_valid(&sshdr)) { if (res > 0 && scsi_sense_valid(&sshdr)) {
...@@ -104,9 +114,6 @@ static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) ...@@ -104,9 +114,6 @@ static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
ret = SCSI_DH_IO; ret = SCSI_DH_IO;
} }
if (ret == SCSI_DH_IMM_RETRY)
goto retry;
return ret; return ret;
} }
...@@ -122,14 +129,31 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h) ...@@ -122,14 +129,31 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h)
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct scsi_device *sdev = h->sdev; struct scsi_device *sdev = h->sdev;
int res, rc; int res, rc;
int retry_cnt = HP_SW_RETRIES;
blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV | blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
struct scsi_failure failure_defs[] = {
{
/*
* LUN not ready - manual intervention required
*
* Switch-over in progress, retry.
*/
.sense = NOT_READY,
.asc = 0x04,
.ascq = 0x03,
.allowed = HP_SW_RETRIES,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
retry:
res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT, res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
HP_SW_RETRIES, &exec_args); HP_SW_RETRIES, &exec_args);
if (!res) { if (!res) {
...@@ -144,13 +168,6 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h) ...@@ -144,13 +168,6 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h)
switch (sshdr.sense_key) { switch (sshdr.sense_key) {
case NOT_READY: case NOT_READY:
if (sshdr.asc == 0x04 && sshdr.ascq == 3) { if (sshdr.asc == 0x04 && sshdr.ascq == 3) {
/*
* LUN not ready - manual intervention required
*
* Switch-over in progress, retry.
*/
if (--retry_cnt)
goto retry;
rc = SCSI_DH_RETRY; rc = SCSI_DH_RETRY;
break; break;
} }
......
...@@ -485,43 +485,17 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) ...@@ -485,43 +485,17 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
static int mode_select_handle_sense(struct scsi_device *sdev, static int mode_select_handle_sense(struct scsi_device *sdev,
struct scsi_sense_hdr *sense_hdr) struct scsi_sense_hdr *sense_hdr)
{ {
int err = SCSI_DH_IO;
struct rdac_dh_data *h = sdev->handler_data; struct rdac_dh_data *h = sdev->handler_data;
if (!scsi_sense_valid(sense_hdr)) if (!scsi_sense_valid(sense_hdr))
goto done; return SCSI_DH_IO;
switch (sense_hdr->sense_key) {
case NO_SENSE:
case ABORTED_COMMAND:
case UNIT_ATTENTION:
err = SCSI_DH_RETRY;
break;
case NOT_READY:
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x01)
/* LUN Not Ready and is in the Process of Becoming
* Ready
*/
err = SCSI_DH_RETRY;
break;
case ILLEGAL_REQUEST:
if (sense_hdr->asc == 0x91 && sense_hdr->ascq == 0x36)
/*
* Command Lock contention
*/
err = SCSI_DH_IMM_RETRY;
break;
default:
break;
}
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, " RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"MODE_SELECT returned with sense %02x/%02x/%02x", "MODE_SELECT returned with sense %02x/%02x/%02x",
(char *) h->ctlr->array_name, h->ctlr->index, (char *) h->ctlr->array_name, h->ctlr->index,
sense_hdr->sense_key, sense_hdr->asc, sense_hdr->ascq); sense_hdr->sense_key, sense_hdr->asc, sense_hdr->ascq);
done: return SCSI_DH_IO;
return err;
} }
static void send_mode_select(struct work_struct *work) static void send_mode_select(struct work_struct *work)
...@@ -530,7 +504,7 @@ static void send_mode_select(struct work_struct *work) ...@@ -530,7 +504,7 @@ static void send_mode_select(struct work_struct *work)
container_of(work, struct rdac_controller, ms_work); container_of(work, struct rdac_controller, ms_work);
struct scsi_device *sdev = ctlr->ms_sdev; struct scsi_device *sdev = ctlr->ms_sdev;
struct rdac_dh_data *h = sdev->handler_data; struct rdac_dh_data *h = sdev->handler_data;
int rc, err, retry_cnt = RDAC_RETRY_COUNT; int rc, err;
struct rdac_queue_data *tmp, *qdata; struct rdac_queue_data *tmp, *qdata;
LIST_HEAD(list); LIST_HEAD(list);
unsigned char cdb[MAX_COMMAND_SIZE]; unsigned char cdb[MAX_COMMAND_SIZE];
...@@ -538,8 +512,49 @@ static void send_mode_select(struct work_struct *work) ...@@ -538,8 +512,49 @@ static void send_mode_select(struct work_struct *work)
unsigned int data_size; unsigned int data_size;
blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV | blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |
REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
struct scsi_failure failure_defs[] = {
{
.sense = NO_SENSE,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = ABORTED_COMMAND,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
/* LUN Not Ready and is in the Process of Becoming Ready */
{
.sense = NOT_READY,
.asc = 0x04,
.ascq = 0x01,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Command Lock contention */
{
.sense = ILLEGAL_REQUEST,
.asc = 0x91,
.ascq = 0x36,
.allowed = SCMD_FAILURE_NO_LIMIT,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.total_allowed = RDAC_RETRY_COUNT,
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
spin_lock(&ctlr->ms_lock); spin_lock(&ctlr->ms_lock);
...@@ -548,15 +563,12 @@ static void send_mode_select(struct work_struct *work) ...@@ -548,15 +563,12 @@ static void send_mode_select(struct work_struct *work)
ctlr->ms_sdev = NULL; ctlr->ms_sdev = NULL;
spin_unlock(&ctlr->ms_lock); spin_unlock(&ctlr->ms_lock);
retry:
memset(cdb, 0, sizeof(cdb)); memset(cdb, 0, sizeof(cdb));
data_size = rdac_failover_get(ctlr, &list, cdb); data_size = rdac_failover_get(ctlr, &list, cdb);
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, " RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, queueing MODE_SELECT command",
"%s MODE_SELECT command", (char *)h->ctlr->array_name, h->ctlr->index);
(char *) h->ctlr->array_name, h->ctlr->index,
(retry_cnt == RDAC_RETRY_COUNT) ? "queueing" : "retrying");
rc = scsi_execute_cmd(sdev, cdb, opf, &h->ctlr->mode_select, data_size, rc = scsi_execute_cmd(sdev, cdb, opf, &h->ctlr->mode_select, data_size,
RDAC_TIMEOUT * HZ, RDAC_RETRIES, &exec_args); RDAC_TIMEOUT * HZ, RDAC_RETRIES, &exec_args);
...@@ -570,10 +582,6 @@ static void send_mode_select(struct work_struct *work) ...@@ -570,10 +582,6 @@ static void send_mode_select(struct work_struct *work)
err = SCSI_DH_IO; err = SCSI_DH_IO;
} else { } else {
err = mode_select_handle_sense(sdev, &sshdr); err = mode_select_handle_sense(sdev, &sshdr);
if (err == SCSI_DH_RETRY && retry_cnt--)
goto retry;
if (err == SCSI_DH_IMM_RETRY)
goto retry;
} }
list_for_each_entry_safe(qdata, tmp, &list, entry) { list_for_each_entry_safe(qdata, tmp, &list, entry) {
......
...@@ -184,6 +184,92 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason) ...@@ -184,6 +184,92 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
__scsi_queue_insert(cmd, reason, true); __scsi_queue_insert(cmd, reason, true);
} }
void scsi_failures_reset_retries(struct scsi_failures *failures)
{
struct scsi_failure *failure;
failures->total_retries = 0;
for (failure = failures->failure_definitions; failure->result;
failure++)
failure->retries = 0;
}
EXPORT_SYMBOL_GPL(scsi_failures_reset_retries);
/**
* scsi_check_passthrough - Determine if passthrough scsi_cmnd needs a retry.
* @scmd: scsi_cmnd to check.
* @failures: scsi_failures struct that lists failures to check for.
*
* Returns -EAGAIN if the caller should retry else 0.
*/
static int scsi_check_passthrough(struct scsi_cmnd *scmd,
struct scsi_failures *failures)
{
struct scsi_failure *failure;
struct scsi_sense_hdr sshdr;
enum sam_status status;
if (!failures)
return 0;
for (failure = failures->failure_definitions; failure->result;
failure++) {
if (failure->result == SCMD_FAILURE_RESULT_ANY)
goto maybe_retry;
if (host_byte(scmd->result) &&
host_byte(scmd->result) == host_byte(failure->result))
goto maybe_retry;
status = status_byte(scmd->result);
if (!status)
continue;
if (failure->result == SCMD_FAILURE_STAT_ANY &&
!scsi_status_is_good(scmd->result))
goto maybe_retry;
if (status != status_byte(failure->result))
continue;
if (status_byte(failure->result) != SAM_STAT_CHECK_CONDITION ||
failure->sense == SCMD_FAILURE_SENSE_ANY)
goto maybe_retry;
if (!scsi_command_normalize_sense(scmd, &sshdr))
return 0;
if (failure->sense != sshdr.sense_key)
continue;
if (failure->asc == SCMD_FAILURE_ASC_ANY)
goto maybe_retry;
if (failure->asc != sshdr.asc)
continue;
if (failure->ascq == SCMD_FAILURE_ASCQ_ANY ||
failure->ascq == sshdr.ascq)
goto maybe_retry;
}
return 0;
maybe_retry:
if (failure->allowed) {
if (failure->allowed == SCMD_FAILURE_NO_LIMIT ||
++failure->retries <= failure->allowed)
return -EAGAIN;
} else {
if (failures->total_allowed == SCMD_FAILURE_NO_LIMIT ||
++failures->total_retries <= failures->total_allowed)
return -EAGAIN;
}
return 0;
}
/** /**
* scsi_execute_cmd - insert request and wait for the result * scsi_execute_cmd - insert request and wait for the result
* @sdev: scsi_device * @sdev: scsi_device
...@@ -192,7 +278,7 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason) ...@@ -192,7 +278,7 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
* @buffer: data buffer * @buffer: data buffer
* @bufflen: len of buffer * @bufflen: len of buffer
* @timeout: request timeout in HZ * @timeout: request timeout in HZ
* @retries: number of times to retry request * @ml_retries: number of times SCSI midlayer will retry request
* @args: Optional args. See struct definition for field descriptions * @args: Optional args. See struct definition for field descriptions
* *
* Returns the scsi_cmnd result field if a command was executed, or a negative * Returns the scsi_cmnd result field if a command was executed, or a negative
...@@ -200,7 +286,7 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason) ...@@ -200,7 +286,7 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
*/ */
int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd, int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
blk_opf_t opf, void *buffer, unsigned int bufflen, blk_opf_t opf, void *buffer, unsigned int bufflen,
int timeout, int retries, int timeout, int ml_retries,
const struct scsi_exec_args *args) const struct scsi_exec_args *args)
{ {
static const struct scsi_exec_args default_args; static const struct scsi_exec_args default_args;
...@@ -214,6 +300,7 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd, ...@@ -214,6 +300,7 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
args->sense_len != SCSI_SENSE_BUFFERSIZE)) args->sense_len != SCSI_SENSE_BUFFERSIZE))
return -EINVAL; return -EINVAL;
retry:
req = scsi_alloc_request(sdev->request_queue, opf, args->req_flags); req = scsi_alloc_request(sdev->request_queue, opf, args->req_flags);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -227,7 +314,7 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd, ...@@ -227,7 +314,7 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
scmd = blk_mq_rq_to_pdu(req); scmd = blk_mq_rq_to_pdu(req);
scmd->cmd_len = COMMAND_SIZE(cmd[0]); scmd->cmd_len = COMMAND_SIZE(cmd[0]);
memcpy(scmd->cmnd, cmd, scmd->cmd_len); memcpy(scmd->cmnd, cmd, scmd->cmd_len);
scmd->allowed = retries; scmd->allowed = ml_retries;
scmd->flags |= args->scmd_flags; scmd->flags |= args->scmd_flags;
req->timeout = timeout; req->timeout = timeout;
req->rq_flags |= RQF_QUIET; req->rq_flags |= RQF_QUIET;
...@@ -237,6 +324,11 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd, ...@@ -237,6 +324,11 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
*/ */
blk_execute_rq(req, true); blk_execute_rq(req, true);
if (scsi_check_passthrough(scmd, args->failures) == -EAGAIN) {
blk_mq_free_request(req);
goto retry;
}
/* /*
* Some devices (USB mass-storage in particular) may transfer * Some devices (USB mass-storage in particular) may transfer
* garbage data together with a residue indicating that the data * garbage data together with a residue indicating that the data
...@@ -2170,11 +2262,25 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, int subpage, ...@@ -2170,11 +2262,25 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, int subpage,
unsigned char cmd[12]; unsigned char cmd[12];
int use_10_for_ms; int use_10_for_ms;
int header_length; int header_length;
int result, retry_count = retries; int result;
struct scsi_sense_hdr my_sshdr; struct scsi_sense_hdr my_sshdr;
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = retries,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
/* caller might not be interested in sense, but we need it */ /* caller might not be interested in sense, but we need it */
.sshdr = sshdr ? : &my_sshdr, .sshdr = sshdr ? : &my_sshdr,
.failures = &failures,
}; };
memset(data, 0, sizeof(*data)); memset(data, 0, sizeof(*data));
...@@ -2236,12 +2342,6 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, int subpage, ...@@ -2236,12 +2342,6 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, int subpage,
goto retry; goto retry;
} }
} }
if (scsi_status_is_check_condition(result) &&
sshdr->sense_key == UNIT_ATTENTION &&
retry_count) {
retry_count--;
goto retry;
}
} }
return -EIO; return -EIO;
} }
...@@ -3334,3 +3434,7 @@ void scsi_build_sense(struct scsi_cmnd *scmd, int desc, u8 key, u8 asc, u8 ascq) ...@@ -3334,3 +3434,7 @@ void scsi_build_sense(struct scsi_cmnd *scmd, int desc, u8 key, u8 asc, u8 ascq)
scmd->result = SAM_STAT_CHECK_CONDITION; scmd->result = SAM_STAT_CHECK_CONDITION;
} }
EXPORT_SYMBOL_GPL(scsi_build_sense); EXPORT_SYMBOL_GPL(scsi_build_sense);
#ifdef CONFIG_SCSI_KUNIT_TEST
#include "scsi_lib_test.c"
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit tests for scsi_lib.c.
*
* Copyright (C) 2023, Oracle Corporation
*/
#include <kunit/test.h>
#include <scsi/scsi_proto.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#define SCSI_LIB_TEST_MAX_ALLOWED 3
#define SCSI_LIB_TEST_TOTAL_MAX_ALLOWED 5
static void scsi_lib_test_multiple_sense(struct kunit *test)
{
struct scsi_failure multiple_sense_failure_defs[] = {
{
.sense = DATA_PROTECT,
.asc = 0x1,
.ascq = 0x1,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = UNIT_ATTENTION,
.asc = 0x11,
.ascq = 0x0,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = NOT_READY,
.asc = 0x11,
.ascq = 0x22,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = ABORTED_COMMAND,
.asc = 0x11,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = HARDWARE_ERROR,
.asc = SCMD_FAILURE_ASC_ANY,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = ILLEGAL_REQUEST,
.asc = 0x91,
.ascq = 0x36,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = multiple_sense_failure_defs,
};
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
struct scsi_cmnd sc = {
.sense_buffer = sense,
};
int i;
/* Match end of array */
scsi_build_sense(&sc, 0, ILLEGAL_REQUEST, 0x91, 0x36);
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
/* Basic match in array */
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x11, 0x0);
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
/* No matching sense entry */
scsi_build_sense(&sc, 0, MISCOMPARE, 0x11, 0x11);
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
/* Match using SCMD_FAILURE_ASCQ_ANY */
scsi_build_sense(&sc, 0, ABORTED_COMMAND, 0x11, 0x22);
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
/* Fail to match */
scsi_build_sense(&sc, 0, ABORTED_COMMAND, 0x22, 0x22);
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
/* Match using SCMD_FAILURE_ASC_ANY */
scsi_build_sense(&sc, 0, HARDWARE_ERROR, 0x11, 0x22);
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
/* No matching status entry */
sc.result = SAM_STAT_RESERVATION_CONFLICT;
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
/* Test hitting allowed limit */
scsi_build_sense(&sc, 0, NOT_READY, 0x11, 0x22);
for (i = 0; i < SCSI_LIB_TEST_MAX_ALLOWED; i++)
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
&failures));
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
/* reset retries so we can retest */
failures.failure_definitions = multiple_sense_failure_defs;
scsi_failures_reset_retries(&failures);
/* Test no retries allowed */
scsi_build_sense(&sc, 0, DATA_PROTECT, 0x1, 0x1);
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
}
static void scsi_lib_test_any_sense(struct kunit *test)
{
struct scsi_failure any_sense_failure_defs[] = {
{
.result = SCMD_FAILURE_SENSE_ANY,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = any_sense_failure_defs,
};
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
struct scsi_cmnd sc = {
.sense_buffer = sense,
};
/* Match using SCMD_FAILURE_SENSE_ANY */
failures.failure_definitions = any_sense_failure_defs;
scsi_build_sense(&sc, 0, MEDIUM_ERROR, 0x11, 0x22);
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
}
static void scsi_lib_test_host(struct kunit *test)
{
struct scsi_failure retryable_host_failure_defs[] = {
{
.result = DID_TRANSPORT_DISRUPTED << 16,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
},
{
.result = DID_TIME_OUT << 16,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = retryable_host_failure_defs,
};
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
struct scsi_cmnd sc = {
.sense_buffer = sense,
};
/* No matching host byte entry */
failures.failure_definitions = retryable_host_failure_defs;
sc.result = DID_NO_CONNECT << 16;
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
/* Matching host byte entry */
sc.result = DID_TIME_OUT << 16;
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
}
static void scsi_lib_test_any_failure(struct kunit *test)
{
struct scsi_failure any_failure_defs[] = {
{
.result = SCMD_FAILURE_RESULT_ANY,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = any_failure_defs,
};
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
struct scsi_cmnd sc = {
.sense_buffer = sense,
};
/* Match SCMD_FAILURE_RESULT_ANY */
failures.failure_definitions = any_failure_defs;
sc.result = DID_TRANSPORT_FAILFAST << 16;
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
}
static void scsi_lib_test_any_status(struct kunit *test)
{
struct scsi_failure any_status_failure_defs[] = {
{
.result = SCMD_FAILURE_STAT_ANY,
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = any_status_failure_defs,
};
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
struct scsi_cmnd sc = {
.sense_buffer = sense,
};
/* Test any status handling */
failures.failure_definitions = any_status_failure_defs;
sc.result = SAM_STAT_RESERVATION_CONFLICT;
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
}
static void scsi_lib_test_total_allowed(struct kunit *test)
{
struct scsi_failure total_allowed_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Fail all CCs except the UA above */
{
.sense = SCMD_FAILURE_SENSE_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Retry any other errors not listed above */
{
.result = SCMD_FAILURE_RESULT_ANY,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = total_allowed_defs,
};
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
struct scsi_cmnd sc = {
.sense_buffer = sense,
};
int i;
/* Test total_allowed */
failures.failure_definitions = total_allowed_defs;
scsi_failures_reset_retries(&failures);
failures.total_allowed = SCSI_LIB_TEST_TOTAL_MAX_ALLOWED;
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0);
for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++)
/* Retry since we under the total_allowed limit */
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
&failures));
sc.result = DID_TIME_OUT << 16;
/* We have now hit the total_allowed limit so no more retries */
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
}
static void scsi_lib_test_mixed_total(struct kunit *test)
{
struct scsi_failure mixed_total_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = 0x28,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = UNIT_ATTENTION,
.asc = 0x29,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.allowed = 1,
.result = DID_TIME_OUT << 16,
},
{}
};
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
struct scsi_failures failures = {
.failure_definitions = mixed_total_defs,
};
struct scsi_cmnd sc = {
.sense_buffer = sense,
};
int i;
/*
* Test total_allowed when there is a mix of per failure allowed
* and total_allowed limits.
*/
failures.failure_definitions = mixed_total_defs;
scsi_failures_reset_retries(&failures);
failures.total_allowed = SCSI_LIB_TEST_TOTAL_MAX_ALLOWED;
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0);
for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++)
/* Retry since we under the total_allowed limit */
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
&failures));
/* Do not retry since we are now over total_allowed limit */
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
scsi_failures_reset_retries(&failures);
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0);
for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++)
/* Retry since we under the total_allowed limit */
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
&failures));
sc.result = DID_TIME_OUT << 16;
/* Retry because this failure has a per failure limit */
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x29, 0x0);
/* total_allowed is now hit so no more retries */
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
}
static void scsi_lib_test_check_passthough(struct kunit *test)
{
scsi_lib_test_multiple_sense(test);
scsi_lib_test_any_sense(test);
scsi_lib_test_host(test);
scsi_lib_test_any_failure(test);
scsi_lib_test_any_status(test);
scsi_lib_test_total_allowed(test);
scsi_lib_test_mixed_total(test);
}
static struct kunit_case scsi_lib_test_cases[] = {
KUNIT_CASE(scsi_lib_test_check_passthough),
{}
};
static struct kunit_suite scsi_lib_test_suite = {
.name = "scsi_lib",
.test_cases = scsi_lib_test_cases,
};
kunit_test_suite(scsi_lib_test_suite);
...@@ -626,6 +626,7 @@ void scsi_sanitize_inquiry_string(unsigned char *s, int len) ...@@ -626,6 +626,7 @@ void scsi_sanitize_inquiry_string(unsigned char *s, int len)
} }
EXPORT_SYMBOL(scsi_sanitize_inquiry_string); EXPORT_SYMBOL(scsi_sanitize_inquiry_string);
/** /**
* scsi_probe_lun - probe a single LUN using a SCSI INQUIRY * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
* @sdev: scsi_device to probe * @sdev: scsi_device to probe
...@@ -647,10 +648,36 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, ...@@ -647,10 +648,36 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
int first_inquiry_len, try_inquiry_len, next_inquiry_len; int first_inquiry_len, try_inquiry_len, next_inquiry_len;
int response_len = 0; int response_len = 0;
int pass, count, result, resid; int pass, count, result, resid;
struct scsi_sense_hdr sshdr; struct scsi_failure failure_defs[] = {
/*
* not-ready to ready transition [asc/ascq=0x28/0x0] or
* power-on, reset [asc/ascq=0x29/0x0], continue. INQUIRY
* should not yield UNIT_ATTENTION but many buggy devices do
* so anyway.
*/
{
.sense = UNIT_ATTENTION,
.asc = 0x28,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = UNIT_ATTENTION,
.asc = 0x29,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.allowed = 1,
.result = DID_TIME_OUT << 16,
},
{}
};
struct scsi_failures failures = {
.total_allowed = 3,
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr,
.resid = &resid, .resid = &resid,
.failures = &failures,
}; };
*bflags = 0; *bflags = 0;
...@@ -668,6 +695,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, ...@@ -668,6 +695,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
pass, try_inquiry_len)); pass, try_inquiry_len));
/* Each pass gets up to three chances to ignore Unit Attention */ /* Each pass gets up to three chances to ignore Unit Attention */
scsi_failures_reset_retries(&failures);
for (count = 0; count < 3; ++count) { for (count = 0; count < 3; ++count) {
memset(scsi_cmd, 0, 6); memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY; scsi_cmd[0] = INQUIRY;
...@@ -684,22 +713,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, ...@@ -684,22 +713,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
"scsi scan: INQUIRY %s with code 0x%x\n", "scsi scan: INQUIRY %s with code 0x%x\n",
result ? "failed" : "successful", result)); result ? "failed" : "successful", result));
if (result > 0) { if (result == 0) {
/*
* not-ready to ready transition [asc/ascq=0x28/0x0]
* or power-on, reset [asc/ascq=0x29/0x0], continue.
* INQUIRY should not yield UNIT_ATTENTION
* but many buggy devices do so anyway.
*/
if (scsi_status_is_check_condition(result) &&
scsi_sense_valid(&sshdr)) {
if ((sshdr.sense_key == UNIT_ATTENTION) &&
((sshdr.asc == 0x28) ||
(sshdr.asc == 0x29)) &&
(sshdr.ascq == 0))
continue;
}
} else if (result == 0) {
/* /*
* if nothing was transferred, we try * if nothing was transferred, we try
* again. It's a workaround for some USB * again. It's a workaround for some USB
...@@ -1402,14 +1416,34 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag ...@@ -1402,14 +1416,34 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag
unsigned int length; unsigned int length;
u64 lun; u64 lun;
unsigned int num_luns; unsigned int num_luns;
unsigned int retries;
int result; int result;
struct scsi_lun *lunp, *lun_data; struct scsi_lun *lunp, *lun_data;
struct scsi_sense_hdr sshdr;
struct scsi_device *sdev; struct scsi_device *sdev;
struct Scsi_Host *shost = dev_to_shost(&starget->dev); struct Scsi_Host *shost = dev_to_shost(&starget->dev);
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Fail all CCs except the UA above */
{
.sense = SCMD_FAILURE_SENSE_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Retry any other errors not listed above */
{
.result = SCMD_FAILURE_RESULT_ANY,
},
{}
};
struct scsi_failures failures = {
.total_allowed = 3,
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .failures = &failures,
}; };
int ret = 0; int ret = 0;
...@@ -1480,29 +1514,18 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag ...@@ -1480,29 +1514,18 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag
* should come through as a check condition, and will not generate * should come through as a check condition, and will not generate
* a retry. * a retry.
*/ */
for (retries = 0; retries < 3; retries++) { scsi_failures_reset_retries(&failures);
SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev, SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev,
"scsi scan: Sending REPORT LUNS to (try %d)\n", "scsi scan: Sending REPORT LUNS\n"));
retries));
result = scsi_execute_cmd(sdev, scsi_cmd, REQ_OP_DRV_IN, result = scsi_execute_cmd(sdev, scsi_cmd, REQ_OP_DRV_IN, lun_data,
lun_data, length, length, SCSI_REPORT_LUNS_TIMEOUT, 3,
SCSI_REPORT_LUNS_TIMEOUT, 3,
&exec_args); &exec_args);
SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev, SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev,
"scsi scan: REPORT LUNS" "scsi scan: REPORT LUNS %s result 0x%x\n",
" %s (try %d) result 0x%x\n", result ? "failed" : "successful", result));
result ? "failed" : "successful",
retries, result));
if (result == 0)
break;
else if (scsi_sense_valid(&sshdr)) {
if (sshdr.sense_key != UNIT_ATTENTION)
break;
}
}
if (result) { if (result) {
/* /*
* The device probably does not support a REPORT LUN command * The device probably does not support a REPORT LUN command
......
...@@ -108,29 +108,30 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd, ...@@ -108,29 +108,30 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd,
enum req_op op, void *buffer, unsigned int bufflen, enum req_op op, void *buffer, unsigned int bufflen,
struct scsi_sense_hdr *sshdr) struct scsi_sense_hdr *sshdr)
{ {
int i, result;
struct scsi_sense_hdr sshdr_tmp;
blk_opf_t opf = op | REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | blk_opf_t opf = op | REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
REQ_FAILFAST_DRIVER; REQ_FAILFAST_DRIVER;
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = DV_RETRIES,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
/* bypass the SDEV_QUIESCE state with BLK_MQ_REQ_PM */
.req_flags = BLK_MQ_REQ_PM, .req_flags = BLK_MQ_REQ_PM,
.sshdr = sshdr ? : &sshdr_tmp, .sshdr = sshdr,
.failures = &failures,
}; };
sshdr = exec_args.sshdr; return scsi_execute_cmd(sdev, cmd, opf, buffer, bufflen, DV_TIMEOUT, 1,
&exec_args);
for(i = 0; i < DV_RETRIES; i++) {
/*
* The purpose of the RQF_PM flag below is to bypass the
* SDEV_QUIESCE state.
*/
result = scsi_execute_cmd(sdev, cmd, opf, buffer, bufflen,
DV_TIMEOUT, 1, &exec_args);
if (result < 0 || !scsi_sense_valid(sshdr) ||
sshdr->sense_key != UNIT_ATTENTION)
break;
}
return result;
} }
static struct { static struct {
......
...@@ -1645,36 +1645,35 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) ...@@ -1645,36 +1645,35 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
static int sd_sync_cache(struct scsi_disk *sdkp) static int sd_sync_cache(struct scsi_disk *sdkp)
{ {
int retries, res; int res;
struct scsi_device *sdp = sdkp->device; struct scsi_device *sdp = sdkp->device;
const int timeout = sdp->request_queue->rq_timeout const int timeout = sdp->request_queue->rq_timeout
* SD_FLUSH_TIMEOUT_MULTIPLIER; * SD_FLUSH_TIMEOUT_MULTIPLIER;
/* Leave the rest of the command zero to indicate flush everything. */
const unsigned char cmd[16] = { sdp->use_16_for_sync ?
SYNCHRONIZE_CACHE_16 : SYNCHRONIZE_CACHE };
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct scsi_failure failure_defs[] = {
{
.allowed = 3,
.result = SCMD_FAILURE_RESULT_ANY,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.req_flags = BLK_MQ_REQ_PM, .req_flags = BLK_MQ_REQ_PM,
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
if (!scsi_device_online(sdp)) if (!scsi_device_online(sdp))
return -ENODEV; return -ENODEV;
for (retries = 3; retries > 0; --retries) { res = scsi_execute_cmd(sdp, cmd, REQ_OP_DRV_IN, NULL, 0, timeout,
unsigned char cmd[16] = { 0 }; sdkp->max_retries, &exec_args);
if (sdp->use_16_for_sync)
cmd[0] = SYNCHRONIZE_CACHE_16;
else
cmd[0] = SYNCHRONIZE_CACHE;
/*
* Leave the rest of the command zero to indicate
* flush everything.
*/
res = scsi_execute_cmd(sdp, cmd, REQ_OP_DRV_IN, NULL, 0,
timeout, sdkp->max_retries, &exec_args);
if (res == 0)
break;
}
if (res) { if (res) {
sd_print_result(sdkp, "Synchronize Cache(10) failed", res); sd_print_result(sdkp, "Synchronize Cache(10) failed", res);
...@@ -1801,8 +1800,22 @@ static int sd_pr_in_command(struct block_device *bdev, u8 sa, ...@@ -1801,8 +1800,22 @@ static int sd_pr_in_command(struct block_device *bdev, u8 sa,
struct scsi_device *sdev = sdkp->device; struct scsi_device *sdev = sdkp->device;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
u8 cmd[10] = { PERSISTENT_RESERVE_IN, sa }; u8 cmd[10] = { PERSISTENT_RESERVE_IN, sa };
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = 5,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
int result; int result;
...@@ -1889,8 +1902,22 @@ static int sd_pr_out_command(struct block_device *bdev, u8 sa, u64 key, ...@@ -1889,8 +1902,22 @@ static int sd_pr_out_command(struct block_device *bdev, u8 sa, u64 key,
struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk); struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk);
struct scsi_device *sdev = sdkp->device; struct scsi_device *sdev = sdkp->device;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = 5,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
int result; int result;
u8 cmd[16] = { 0, }; u8 cmd[16] = { 0, };
...@@ -2235,40 +2262,59 @@ static int sd_done(struct scsi_cmnd *SCpnt) ...@@ -2235,40 +2262,59 @@ static int sd_done(struct scsi_cmnd *SCpnt)
static void static void
sd_spinup_disk(struct scsi_disk *sdkp) sd_spinup_disk(struct scsi_disk *sdkp)
{ {
unsigned char cmd[10]; static const u8 cmd[10] = { TEST_UNIT_READY };
unsigned long spintime_expire = 0; unsigned long spintime_expire = 0;
int retries, spintime; int spintime, sense_valid = 0;
unsigned int the_result; unsigned int the_result;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct scsi_failure failure_defs[] = {
/* Do not retry Medium Not Present */
{
.sense = UNIT_ATTENTION,
.asc = 0x3A,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = NOT_READY,
.asc = 0x3A,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Retry when scsi_status_is_good would return false 3 times */
{
.result = SCMD_FAILURE_STAT_ANY,
.allowed = 3,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
int sense_valid = 0;
spintime = 0; spintime = 0;
/* Spin up drives, as required. Only do this at boot time */ /* Spin up drives, as required. Only do this at boot time */
/* Spinup needs to be done for module loads too. */ /* Spinup needs to be done for module loads too. */
do {
retries = 0;
do { do {
bool media_was_present = sdkp->media_present; bool media_was_present = sdkp->media_present;
cmd[0] = TEST_UNIT_READY; scsi_failures_reset_retries(&failures);
memset((void *) &cmd[1], 0, 9);
the_result = scsi_execute_cmd(sdkp->device, cmd, REQ_OP_DRV_IN,
NULL, 0, SD_TIMEOUT,
sdkp->max_retries, &exec_args);
the_result = scsi_execute_cmd(sdkp->device, cmd,
REQ_OP_DRV_IN, NULL, 0,
SD_TIMEOUT,
sdkp->max_retries,
&exec_args);
if (the_result > 0) { if (the_result > 0) {
/* /*
* If the drive has indicated to us that it * If the drive has indicated to us that it doesn't
* doesn't have any media in it, don't bother * have any media in it, don't bother with any more
* with any more polling. * polling.
*/ */
if (media_not_present(sdkp, &sshdr)) { if (media_not_present(sdkp, &sshdr)) {
if (media_was_present) if (media_was_present)
...@@ -2276,14 +2322,8 @@ sd_spinup_disk(struct scsi_disk *sdkp) ...@@ -2276,14 +2322,8 @@ sd_spinup_disk(struct scsi_disk *sdkp)
"Media removed, stopped polling\n"); "Media removed, stopped polling\n");
return; return;
} }
sense_valid = scsi_sense_valid(&sshdr); sense_valid = scsi_sense_valid(&sshdr);
} }
retries++;
} while (retries < 3 &&
(!scsi_status_is_good(the_result) ||
(scsi_status_is_check_condition(the_result) &&
sense_valid && sshdr.sense_key == UNIT_ATTENTION)));
if (!scsi_status_is_check_condition(the_result)) { if (!scsi_status_is_check_condition(the_result)) {
/* no sense, TUR either succeeded or failed /* no sense, TUR either succeeded or failed
...@@ -2318,14 +2358,16 @@ sd_spinup_disk(struct scsi_disk *sdkp) ...@@ -2318,14 +2358,16 @@ sd_spinup_disk(struct scsi_disk *sdkp)
* Issue command to spin up drive when not ready * Issue command to spin up drive when not ready
*/ */
if (!spintime) { if (!spintime) {
/* Return immediately and start spin cycle */
const u8 start_cmd[10] = {
[0] = START_STOP,
[1] = 1,
[4] = sdkp->device->start_stop_pwr_cond ?
0x11 : 1,
};
sd_printk(KERN_NOTICE, sdkp, "Spinning up disk..."); sd_printk(KERN_NOTICE, sdkp, "Spinning up disk...");
cmd[0] = START_STOP; scsi_execute_cmd(sdkp->device, start_cmd,
cmd[1] = 1; /* Return immediately */
memset((void *) &cmd[2], 0, 8);
cmd[4] = 1; /* Start spin cycle */
if (sdkp->device->start_stop_pwr_cond)
cmd[4] |= 1 << 4;
scsi_execute_cmd(sdkp->device, cmd,
REQ_OP_DRV_IN, NULL, 0, REQ_OP_DRV_IN, NULL, 0,
SD_TIMEOUT, sdkp->max_retries, SD_TIMEOUT, sdkp->max_retries,
&exec_args); &exec_args);
...@@ -2546,42 +2588,58 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, ...@@ -2546,42 +2588,58 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp, static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
unsigned char *buffer) unsigned char *buffer)
{ {
unsigned char cmd[16]; static const u8 cmd[10] = { READ_CAPACITY };
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct scsi_failure failure_defs[] = {
/* Do not retry Medium Not Present */
{
.sense = UNIT_ATTENTION,
.asc = 0x3A,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = NOT_READY,
.asc = 0x3A,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Device reset might occur several times so retry a lot */
{
.sense = UNIT_ATTENTION,
.asc = 0x29,
.allowed = READ_CAPACITY_RETRIES_ON_RESET,
.result = SAM_STAT_CHECK_CONDITION,
},
/* Any other error not listed above retry 3 times */
{
.result = SCMD_FAILURE_RESULT_ANY,
.allowed = 3,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .sshdr = &sshdr,
.failures = &failures,
}; };
int sense_valid = 0; int sense_valid = 0;
int the_result; int the_result;
int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET;
sector_t lba; sector_t lba;
unsigned sector_size; unsigned sector_size;
do {
cmd[0] = READ_CAPACITY;
memset(&cmd[1], 0, 9);
memset(buffer, 0, 8); memset(buffer, 0, 8);
the_result = scsi_execute_cmd(sdp, cmd, REQ_OP_DRV_IN, buffer, the_result = scsi_execute_cmd(sdp, cmd, REQ_OP_DRV_IN, buffer,
8, SD_TIMEOUT, sdkp->max_retries, 8, SD_TIMEOUT, sdkp->max_retries,
&exec_args); &exec_args);
if (media_not_present(sdkp, &sshdr))
return -ENODEV;
if (the_result > 0) { if (the_result > 0) {
sense_valid = scsi_sense_valid(&sshdr); sense_valid = scsi_sense_valid(&sshdr);
if (sense_valid &&
sshdr.sense_key == UNIT_ATTENTION &&
sshdr.asc == 0x29 && sshdr.ascq == 0x00)
/* Device reset might occur several times,
* give it one more chance */
if (--reset_retries > 0)
continue;
}
retries--;
} while (the_result && retries); if (media_not_present(sdkp, &sshdr))
return -ENODEV;
}
if (the_result) { if (the_result) {
sd_print_result(sdkp, "Read Capacity(10) failed", the_result); sd_print_result(sdkp, "Read Capacity(10) failed", the_result);
......
...@@ -87,19 +87,32 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code, ...@@ -87,19 +87,32 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code,
0 0
}; };
unsigned char recv_page_code; unsigned char recv_page_code;
unsigned int retries = SES_RETRIES; struct scsi_failure failure_defs[] = {
struct scsi_sense_hdr sshdr; {
.sense = UNIT_ATTENTION,
.asc = 0x29,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = SES_RETRIES,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = NOT_READY,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = SES_RETRIES,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .failures = &failures,
}; };
do {
ret = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, buf, bufflen, ret = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, buf, bufflen,
SES_TIMEOUT, 1, &exec_args); SES_TIMEOUT, 1, &exec_args);
} while (ret > 0 && --retries && scsi_sense_valid(&sshdr) &&
(sshdr.sense_key == NOT_READY ||
(sshdr.sense_key == UNIT_ATTENTION && sshdr.asc == 0x29)));
if (unlikely(ret)) if (unlikely(ret))
return ret; return ret;
...@@ -131,19 +144,32 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code, ...@@ -131,19 +144,32 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code,
bufflen & 0xff, bufflen & 0xff,
0 0
}; };
struct scsi_sense_hdr sshdr; struct scsi_failure failure_defs[] = {
unsigned int retries = SES_RETRIES; {
.sense = UNIT_ATTENTION,
.asc = 0x29,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = SES_RETRIES,
.result = SAM_STAT_CHECK_CONDITION,
},
{
.sense = NOT_READY,
.asc = SCMD_FAILURE_ASC_ANY,
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = SES_RETRIES,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = { const struct scsi_exec_args exec_args = {
.sshdr = &sshdr, .failures = &failures,
}; };
do { result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_OUT, buf, bufflen,
result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_OUT, buf, SES_TIMEOUT, 1, &exec_args);
bufflen, SES_TIMEOUT, 1, &exec_args);
} while (result > 0 && --retries && scsi_sense_valid(&sshdr) &&
(sshdr.sense_key == NOT_READY ||
(sshdr.sense_key == UNIT_ATTENTION && sshdr.asc == 0x29)));
if (result) if (result)
sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n", sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n",
result); result);
......
...@@ -717,27 +717,29 @@ static int sr_probe(struct device *dev) ...@@ -717,27 +717,29 @@ static int sr_probe(struct device *dev)
static void get_sectorsize(struct scsi_cd *cd) static void get_sectorsize(struct scsi_cd *cd)
{ {
unsigned char cmd[10]; static const u8 cmd[10] = { READ_CAPACITY };
unsigned char buffer[8]; unsigned char buffer[8] = { };
int the_result, retries = 3; int the_result;
int sector_size; int sector_size;
struct request_queue *queue; struct request_queue *queue;
struct scsi_failure failure_defs[] = {
do { {
cmd[0] = READ_CAPACITY; .result = SCMD_FAILURE_RESULT_ANY,
memset((void *) &cmd[1], 0, 9); .allowed = 3,
memset(buffer, 0, sizeof(buffer)); },
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = {
.failures = &failures,
};
/* Do the command and wait.. */ /* Do the command and wait.. */
the_result = scsi_execute_cmd(cd->device, cmd, REQ_OP_DRV_IN, the_result = scsi_execute_cmd(cd->device, cmd, REQ_OP_DRV_IN, buffer,
buffer, sizeof(buffer), sizeof(buffer), SR_TIMEOUT, MAX_RETRIES,
SR_TIMEOUT, MAX_RETRIES, NULL); &exec_args);
retries--;
} while (the_result && retries);
if (the_result) { if (the_result) {
cd->capacity = 0x1fffff; cd->capacity = 0x1fffff;
sector_size = 2048; /* A guess, just in case */ sector_size = 2048; /* A guess, just in case */
......
...@@ -9519,7 +9519,17 @@ static int ufshcd_execute_start_stop(struct scsi_device *sdev, ...@@ -9519,7 +9519,17 @@ static int ufshcd_execute_start_stop(struct scsi_device *sdev,
struct scsi_sense_hdr *sshdr) struct scsi_sense_hdr *sshdr)
{ {
const unsigned char cdb[6] = { START_STOP, 0, 0, 0, pwr_mode << 4, 0 }; const unsigned char cdb[6] = { START_STOP, 0, 0, 0, pwr_mode << 4, 0 };
struct scsi_failure failure_defs[] = {
{
.allowed = 2,
.result = SCMD_FAILURE_RESULT_ANY,
},
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args args = { const struct scsi_exec_args args = {
.failures = &failures,
.sshdr = sshdr, .sshdr = sshdr,
.req_flags = BLK_MQ_REQ_PM, .req_flags = BLK_MQ_REQ_PM,
.scmd_flags = SCMD_FAIL_IF_RECOVERING, .scmd_flags = SCMD_FAIL_IF_RECOVERING,
...@@ -9545,7 +9555,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, ...@@ -9545,7 +9555,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct scsi_device *sdp; struct scsi_device *sdp;
unsigned long flags; unsigned long flags;
int ret, retries; int ret;
spin_lock_irqsave(hba->host->host_lock, flags); spin_lock_irqsave(hba->host->host_lock, flags);
sdp = hba->ufs_device_wlun; sdp = hba->ufs_device_wlun;
...@@ -9571,15 +9581,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, ...@@ -9571,15 +9581,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
* callbacks hence set the RQF_PM flag so that it doesn't resume the * callbacks hence set the RQF_PM flag so that it doesn't resume the
* already suspended childs. * already suspended childs.
*/ */
for (retries = 3; retries > 0; --retries) {
ret = ufshcd_execute_start_stop(sdp, pwr_mode, &sshdr); ret = ufshcd_execute_start_stop(sdp, pwr_mode, &sshdr);
/*
* scsi_execute() only returns a negative value if the request
* queue is dying.
*/
if (ret <= 0)
break;
}
if (ret) { if (ret) {
sdev_printk(KERN_WARNING, sdp, sdev_printk(KERN_WARNING, sdp,
"START_STOP failed for power mode: %d, result %x\n", "START_STOP failed for power mode: %d, result %x\n",
......
...@@ -489,6 +489,52 @@ extern int scsi_is_sdev_device(const struct device *); ...@@ -489,6 +489,52 @@ extern int scsi_is_sdev_device(const struct device *);
extern int scsi_is_target_device(const struct device *); extern int scsi_is_target_device(const struct device *);
extern void scsi_sanitize_inquiry_string(unsigned char *s, int len); extern void scsi_sanitize_inquiry_string(unsigned char *s, int len);
/*
* scsi_execute_cmd users can set scsi_failure.result to have
* scsi_check_passthrough fail/retry a command. scsi_failure.result can be a
* specific host byte or message code, or SCMD_FAILURE_RESULT_ANY can be used
* to match any host or message code.
*/
#define SCMD_FAILURE_RESULT_ANY 0x7fffffff
/*
* Set scsi_failure.result to SCMD_FAILURE_STAT_ANY to fail/retry any failure
* scsi_status_is_good returns false for.
*/
#define SCMD_FAILURE_STAT_ANY 0xff
/*
* The following can be set to the scsi_failure sense, asc and ascq fields to
* match on any sense, ASC, or ASCQ value.
*/
#define SCMD_FAILURE_SENSE_ANY 0xff
#define SCMD_FAILURE_ASC_ANY 0xff
#define SCMD_FAILURE_ASCQ_ANY 0xff
/* Always retry a matching failure. */
#define SCMD_FAILURE_NO_LIMIT -1
struct scsi_failure {
int result;
u8 sense;
u8 asc;
u8 ascq;
/*
* Number of times scsi_execute_cmd will retry the failure. It does
* not count for the total_allowed.
*/
s8 allowed;
/* Number of times the failure has been retried. */
s8 retries;
};
struct scsi_failures {
/*
* If a scsi_failure does not have a retry limit setup this limit will
* be used.
*/
int total_allowed;
int total_retries;
struct scsi_failure *failure_definitions;
};
/* Optional arguments to scsi_execute_cmd */ /* Optional arguments to scsi_execute_cmd */
struct scsi_exec_args { struct scsi_exec_args {
unsigned char *sense; /* sense buffer */ unsigned char *sense; /* sense buffer */
...@@ -497,12 +543,14 @@ struct scsi_exec_args { ...@@ -497,12 +543,14 @@ struct scsi_exec_args {
blk_mq_req_flags_t req_flags; /* BLK_MQ_REQ flags */ blk_mq_req_flags_t req_flags; /* BLK_MQ_REQ flags */
int scmd_flags; /* SCMD flags */ int scmd_flags; /* SCMD flags */
int *resid; /* residual length */ int *resid; /* residual length */
struct scsi_failures *failures; /* failures to retry */
}; };
int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd, int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
blk_opf_t opf, void *buffer, unsigned int bufflen, blk_opf_t opf, void *buffer, unsigned int bufflen,
int timeout, int retries, int timeout, int retries,
const struct scsi_exec_args *args); const struct scsi_exec_args *args);
void scsi_failures_reset_retries(struct scsi_failures *failures);
extern void sdev_disable_disk_events(struct scsi_device *sdev); extern void sdev_disable_disk_events(struct scsi_device *sdev);
extern void sdev_enable_disk_events(struct scsi_device *sdev); extern void sdev_enable_disk_events(struct scsi_device *sdev);
......
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