mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
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:
commit
3f90ac7138
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
330
drivers/scsi/scsi_lib_test.c
Normal file
330
drivers/scsi/scsi_lib_test.c
Normal 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);
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user