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

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: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parents 8179041f 25a1f7a0
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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

+15 −12
Original line number Diff line number Diff line
@@ -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;
}
+33 −16
Original line number Diff line number Diff line
@@ -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 @@ static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
		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 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h)
	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;
		}
+46 −38
Original line number Diff line number Diff line
@@ -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) {
+114 −10
Original line number Diff line number Diff line
@@ -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
Loading