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.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Martin K. Petersen 2024-01-29 21:21:10 -05:00
commit 3f90ac7138
13 changed files with 893 additions and 262 deletions

View File

@ -67,6 +67,15 @@ config SCSI_PROC_FS
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)"
depends on SCSI

View File

@ -113,7 +113,6 @@ typedef struct {
struct scsi_device **dt; /* ptrs to data transfer elements */
u_int firsts[CH_TYPES];
u_int counts[CH_TYPES];
u_int unit_attention;
u_int voltags;
struct mutex lock;
} scsi_changer;
@ -186,17 +185,29 @@ static int
ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len,
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_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 = {
.sshdr = &sshdr,
.failures = &failures,
};
timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS)
? timeout_init : timeout_move;
retry:
errno = 0;
result = scsi_execute_cmd(ch->device, cmd, op, buffer, buflength,
timeout * HZ, MAX_RETRIES, &exec_args);
if (result < 0)
@ -205,14 +216,6 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len,
if (debug)
scsi_print_sense_hdr(ch->device, ch->name, &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;
}

View File

@ -46,9 +46,6 @@ static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h,
int ret = SCSI_DH_IO;
switch (sshdr->sense_key) {
case UNIT_ATTENTION:
ret = SCSI_DH_IMM_RETRY;
break;
case NOT_READY:
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)
int ret, res;
blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
retry:
res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
HP_SW_RETRIES, &exec_args);
if (res > 0 && scsi_sense_valid(&sshdr)) {
@ -104,9 +114,6 @@ retry:
ret = SCSI_DH_IO;
}
if (ret == SCSI_DH_IMM_RETRY)
goto retry;
return ret;
}
@ -122,14 +129,31 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h)
struct scsi_sense_hdr sshdr;
struct scsi_device *sdev = h->sdev;
int res, rc;
int retry_cnt = HP_SW_RETRIES;
blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
retry:
res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
HP_SW_RETRIES, &exec_args);
if (!res) {
@ -144,13 +168,6 @@ retry:
switch (sshdr.sense_key) {
case NOT_READY:
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;
break;
}

View File

@ -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,
struct scsi_sense_hdr *sense_hdr)
{
int err = SCSI_DH_IO;
struct rdac_dh_data *h = sdev->handler_data;
if (!scsi_sense_valid(sense_hdr))
goto done;
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;
}
return SCSI_DH_IO;
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"MODE_SELECT returned with sense %02x/%02x/%02x",
(char *) h->ctlr->array_name, h->ctlr->index,
sense_hdr->sense_key, sense_hdr->asc, sense_hdr->ascq);
done:
return err;
return SCSI_DH_IO;
}
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);
struct scsi_device *sdev = ctlr->ms_sdev;
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;
LIST_HEAD(list);
unsigned char cdb[MAX_COMMAND_SIZE];
@ -538,8 +512,49 @@ static void send_mode_select(struct work_struct *work)
unsigned int data_size;
blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
spin_lock(&ctlr->ms_lock);
@ -548,15 +563,12 @@ static void send_mode_select(struct work_struct *work)
ctlr->ms_sdev = NULL;
spin_unlock(&ctlr->ms_lock);
retry:
memset(cdb, 0, sizeof(cdb));
data_size = rdac_failover_get(ctlr, &list, cdb);
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"%s MODE_SELECT command",
(char *) h->ctlr->array_name, h->ctlr->index,
(retry_cnt == RDAC_RETRY_COUNT) ? "queueing" : "retrying");
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, queueing MODE_SELECT command",
(char *)h->ctlr->array_name, h->ctlr->index);
rc = scsi_execute_cmd(sdev, cdb, opf, &h->ctlr->mode_select, data_size,
RDAC_TIMEOUT * HZ, RDAC_RETRIES, &exec_args);
@ -570,10 +582,6 @@ static void send_mode_select(struct work_struct *work)
err = SCSI_DH_IO;
} else {
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) {

View File

@ -184,6 +184,92 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
__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
* @sdev: scsi_device
@ -192,7 +278,7 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
* @buffer: data buffer
* @bufflen: len of buffer
* @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
*
* 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)
*/
int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
blk_opf_t opf, void *buffer, unsigned int bufflen,
int timeout, int retries,
int timeout, int ml_retries,
const struct scsi_exec_args *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,
args->sense_len != SCSI_SENSE_BUFFERSIZE))
return -EINVAL;
retry:
req = scsi_alloc_request(sdev->request_queue, opf, args->req_flags);
if (IS_ERR(req))
return PTR_ERR(req);
@ -227,7 +314,7 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
scmd = blk_mq_rq_to_pdu(req);
scmd->cmd_len = COMMAND_SIZE(cmd[0]);
memcpy(scmd->cmnd, cmd, scmd->cmd_len);
scmd->allowed = retries;
scmd->allowed = ml_retries;
scmd->flags |= args->scmd_flags;
req->timeout = timeout;
req->rq_flags |= RQF_QUIET;
@ -237,6 +324,11 @@ int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
*/
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
* 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,
unsigned char cmd[12];
int use_10_for_ms;
int header_length;
int result, retry_count = retries;
int result;
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 = {
/* caller might not be interested in sense, but we need it */
.sshdr = sshdr ? : &my_sshdr,
.failures = &failures,
};
memset(data, 0, sizeof(*data));
@ -2236,12 +2342,6 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, int subpage,
goto retry;
}
}
if (scsi_status_is_check_condition(result) &&
sshdr->sense_key == UNIT_ATTENTION &&
retry_count) {
retry_count--;
goto retry;
}
}
return -EIO;
}
@ -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;
}
EXPORT_SYMBOL_GPL(scsi_build_sense);
#ifdef CONFIG_SCSI_KUNIT_TEST
#include "scsi_lib_test.c"
#endif

View File

@ -0,0 +1,330 @@
// 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);

View File

@ -626,6 +626,7 @@ void scsi_sanitize_inquiry_string(unsigned char *s, int len)
}
EXPORT_SYMBOL(scsi_sanitize_inquiry_string);
/**
* scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
* @sdev: scsi_device to probe
@ -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 response_len = 0;
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 = {
.sshdr = &sshdr,
.resid = &resid,
.failures = &failures,
};
*bflags = 0;
@ -668,6 +695,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
pass, try_inquiry_len));
/* Each pass gets up to three chances to ignore Unit Attention */
scsi_failures_reset_retries(&failures);
for (count = 0; count < 3; ++count) {
memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY;
@ -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",
result ? "failed" : "successful", result));
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 (result == 0) {
/*
* if nothing was transferred, we try
* 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
unsigned int length;
u64 lun;
unsigned int num_luns;
unsigned int retries;
int result;
struct scsi_lun *lunp, *lun_data;
struct scsi_sense_hdr sshdr;
struct scsi_device *sdev;
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
int ret = 0;
@ -1480,29 +1514,18 @@ retry:
* should come through as a check condition, and will not generate
* a retry.
*/
for (retries = 0; retries < 3; retries++) {
SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev,
"scsi scan: Sending REPORT LUNS to (try %d)\n",
retries));
scsi_failures_reset_retries(&failures);
result = scsi_execute_cmd(sdev, scsi_cmd, REQ_OP_DRV_IN,
lun_data, length,
SCSI_REPORT_LUNS_TIMEOUT, 3,
&exec_args);
SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev,
"scsi scan: Sending REPORT LUNS\n"));
SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev,
"scsi scan: REPORT LUNS"
" %s (try %d) result 0x%x\n",
result ? "failed" : "successful",
retries, result));
if (result == 0)
break;
else if (scsi_sense_valid(&sshdr)) {
if (sshdr.sense_key != UNIT_ATTENTION)
break;
}
}
result = scsi_execute_cmd(sdev, scsi_cmd, REQ_OP_DRV_IN, lun_data,
length, SCSI_REPORT_LUNS_TIMEOUT, 3,
&exec_args);
SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev,
"scsi scan: REPORT LUNS %s result 0x%x\n",
result ? "failed" : "successful", result));
if (result) {
/*
* The device probably does not support a REPORT LUN command

View File

@ -108,29 +108,30 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd,
enum req_op op, void *buffer, unsigned int bufflen,
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 |
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 = {
/* bypass the SDEV_QUIESCE state with BLK_MQ_REQ_PM */
.req_flags = BLK_MQ_REQ_PM,
.sshdr = sshdr ? : &sshdr_tmp,
.sshdr = sshdr,
.failures = &failures,
};
sshdr = exec_args.sshdr;
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;
return scsi_execute_cmd(sdev, cmd, opf, buffer, bufflen, DV_TIMEOUT, 1,
&exec_args);
}
static struct {

View File

@ -1645,36 +1645,35 @@ out:
static int sd_sync_cache(struct scsi_disk *sdkp)
{
int retries, res;
int res;
struct scsi_device *sdp = sdkp->device;
const int timeout = sdp->request_queue->rq_timeout
* 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_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 = {
.req_flags = BLK_MQ_REQ_PM,
.sshdr = &sshdr,
.failures = &failures,
};
if (!scsi_device_online(sdp))
return -ENODEV;
for (retries = 3; retries > 0; --retries) {
unsigned char cmd[16] = { 0 };
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;
}
res = scsi_execute_cmd(sdp, cmd, REQ_OP_DRV_IN, NULL, 0, timeout,
sdkp->max_retries, &exec_args);
if (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,
struct scsi_device *sdev = sdkp->device;
struct scsi_sense_hdr sshdr;
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
int result;
@ -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_device *sdev = sdkp->device;
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
int result;
u8 cmd[16] = { 0, };
@ -2235,55 +2262,68 @@ static int sd_done(struct scsi_cmnd *SCpnt)
static void
sd_spinup_disk(struct scsi_disk *sdkp)
{
unsigned char cmd[10];
static const u8 cmd[10] = { TEST_UNIT_READY };
unsigned long spintime_expire = 0;
int retries, spintime;
int spintime, sense_valid = 0;
unsigned int the_result;
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
int sense_valid = 0;
spintime = 0;
/* Spin up drives, as required. Only do this at boot time */
/* Spinup needs to be done for module loads too. */
do {
retries = 0;
bool media_was_present = sdkp->media_present;
do {
bool media_was_present = sdkp->media_present;
scsi_failures_reset_retries(&failures);
cmd[0] = TEST_UNIT_READY;
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 drive has indicated to us that it
* doesn't have any media in it, don't bother
* with any more polling.
*/
if (media_not_present(sdkp, &sshdr)) {
if (media_was_present)
sd_printk(KERN_NOTICE, sdkp,
"Media removed, stopped polling\n");
return;
}
sense_valid = scsi_sense_valid(&sshdr);
if (the_result > 0) {
/*
* If the drive has indicated to us that it doesn't
* have any media in it, don't bother with any more
* polling.
*/
if (media_not_present(sdkp, &sshdr)) {
if (media_was_present)
sd_printk(KERN_NOTICE, sdkp,
"Media removed, stopped polling\n");
return;
}
retries++;
} while (retries < 3 &&
(!scsi_status_is_good(the_result) ||
(scsi_status_is_check_condition(the_result) &&
sense_valid && sshdr.sense_key == UNIT_ATTENTION)));
sense_valid = scsi_sense_valid(&sshdr);
}
if (!scsi_status_is_check_condition(the_result)) {
/* no sense, TUR either succeeded or failed
@ -2318,14 +2358,16 @@ sd_spinup_disk(struct scsi_disk *sdkp)
* Issue command to spin up drive when not ready
*/
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...");
cmd[0] = START_STOP;
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,
scsi_execute_cmd(sdkp->device, start_cmd,
REQ_OP_DRV_IN, NULL, 0,
SD_TIMEOUT, sdkp->max_retries,
&exec_args);
@ -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,
unsigned char *buffer)
{
unsigned char cmd[16];
static const u8 cmd[10] = { READ_CAPACITY };
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 = {
.sshdr = &sshdr,
.failures = &failures,
};
int sense_valid = 0;
int the_result;
int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET;
sector_t lba;
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,
8, SD_TIMEOUT, sdkp->max_retries,
&exec_args);
the_result = scsi_execute_cmd(sdp, cmd, REQ_OP_DRV_IN, buffer,
8, SD_TIMEOUT, sdkp->max_retries,
&exec_args);
if (the_result > 0) {
sense_valid = scsi_sense_valid(&sshdr);
if (media_not_present(sdkp, &sshdr))
return -ENODEV;
if (the_result > 0) {
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 (the_result) {
sd_print_result(sdkp, "Read Capacity(10) failed", the_result);

View File

@ -87,19 +87,32 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code,
0
};
unsigned char recv_page_code;
unsigned int retries = SES_RETRIES;
struct scsi_sense_hdr sshdr;
struct scsi_failure failure_defs[] = {
{
.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 = {
.sshdr = &sshdr,
.failures = &failures,
};
do {
ret = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, buf, bufflen,
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)));
ret = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, buf, bufflen,
SES_TIMEOUT, 1, &exec_args);
if (unlikely(ret))
return ret;
@ -131,19 +144,32 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code,
bufflen & 0xff,
0
};
struct scsi_sense_hdr sshdr;
unsigned int retries = SES_RETRIES;
struct scsi_failure failure_defs[] = {
{
.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 = {
.sshdr = &sshdr,
.failures = &failures,
};
do {
result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_OUT, buf,
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)));
result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_OUT, buf, bufflen,
SES_TIMEOUT, 1, &exec_args);
if (result)
sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n",
result);

View File

@ -717,27 +717,29 @@ fail:
static void get_sectorsize(struct scsi_cd *cd)
{
unsigned char cmd[10];
unsigned char buffer[8];
int the_result, retries = 3;
static const u8 cmd[10] = { READ_CAPACITY };
unsigned char buffer[8] = { };
int the_result;
int sector_size;
struct request_queue *queue;
struct scsi_failure failure_defs[] = {
{
.result = SCMD_FAILURE_RESULT_ANY,
.allowed = 3,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = {
.failures = &failures,
};
do {
cmd[0] = READ_CAPACITY;
memset((void *) &cmd[1], 0, 9);
memset(buffer, 0, sizeof(buffer));
/* Do the command and wait.. */
the_result = scsi_execute_cmd(cd->device, cmd, REQ_OP_DRV_IN,
buffer, sizeof(buffer),
SR_TIMEOUT, MAX_RETRIES, NULL);
retries--;
} while (the_result && retries);
/* Do the command and wait.. */
the_result = scsi_execute_cmd(cd->device, cmd, REQ_OP_DRV_IN, buffer,
sizeof(buffer), SR_TIMEOUT, MAX_RETRIES,
&exec_args);
if (the_result) {
cd->capacity = 0x1fffff;
sector_size = 2048; /* A guess, just in case */

View File

@ -9519,7 +9519,17 @@ static int ufshcd_execute_start_stop(struct scsi_device *sdev,
struct scsi_sense_hdr *sshdr)
{
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 = {
.failures = &failures,
.sshdr = sshdr,
.req_flags = BLK_MQ_REQ_PM,
.scmd_flags = SCMD_FAIL_IF_RECOVERING,
@ -9545,7 +9555,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
struct scsi_sense_hdr sshdr;
struct scsi_device *sdp;
unsigned long flags;
int ret, retries;
int ret;
spin_lock_irqsave(hba->host->host_lock, flags);
sdp = hba->ufs_device_wlun;
@ -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
* already suspended childs.
*/
for (retries = 3; retries > 0; --retries) {
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;
}
ret = ufshcd_execute_start_stop(sdp, pwr_mode, &sshdr);
if (ret) {
sdev_printk(KERN_WARNING, sdp,
"START_STOP failed for power mode: %d, result %x\n",

View File

@ -489,6 +489,52 @@ extern int scsi_is_sdev_device(const struct device *);
extern int scsi_is_target_device(const struct device *);
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 */
struct scsi_exec_args {
unsigned char *sense; /* sense buffer */
@ -497,12 +543,14 @@ struct scsi_exec_args {
blk_mq_req_flags_t req_flags; /* BLK_MQ_REQ flags */
int scmd_flags; /* SCMD flags */
int *resid; /* residual length */
struct scsi_failures *failures; /* failures to retry */
};
int scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
blk_opf_t opf, void *buffer, unsigned int bufflen,
int timeout, int retries,
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_enable_disk_events(struct scsi_device *sdev);