Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33aa687d authored by James Bottomley's avatar James Bottomley
Browse files

[SCSI] convert SPI transport class to scsi_execute



This one's slightly more difficult.  The transport class uses
REQ_FAILFAST, so another interface (scsi_execute) had to be invented to
take the extra flag.  Also, the sense functions are shifted around to
allow spi_execute to place data directly into a struct scsi_sense_hdr.
With this change, there's probably a lot of unnecessary sense buffer
allocation going on which we can fix later.

Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 1cf72699
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -1847,12 +1847,16 @@ EXPORT_SYMBOL(scsi_reset_provider);
int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
                         struct scsi_sense_hdr *sshdr)
{
	if (!sense_buffer || !sb_len || (sense_buffer[0] & 0x70) != 0x70)
	if (!sense_buffer || !sb_len)
		return 0;

	memset(sshdr, 0, sizeof(struct scsi_sense_hdr));

	sshdr->response_code = (sense_buffer[0] & 0x7f);

	if (!scsi_sense_valid(sshdr))
		return 0;

	if (sshdr->response_code >= 0x72) {
		/*
		 * descriptor format
+7 −6
Original line number Diff line number Diff line
@@ -282,7 +282,7 @@ void scsi_wait_req(struct scsi_request *sreq, const void *cmnd, void *buffer,
EXPORT_SYMBOL(scsi_wait_req);

/**
 * scsi_execute_req - insert request and wait for the result
 * scsi_execute - insert request and wait for the result
 * @sdev:	scsi device
 * @cmd:	scsi command
 * @data_direction: data direction
@@ -291,13 +291,14 @@ EXPORT_SYMBOL(scsi_wait_req);
 * @sense:	optional sense buffer
 * @timeout:	request timeout in seconds
 * @retries:	number of times to retry request
 * @flags:	or into request flags;
 *
 * scsi_execute_req returns the req->errors value which is the
 * the scsi_cmnd result field.
 **/
int scsi_execute_req(struct scsi_device *sdev, unsigned char *cmd,
int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
		 int data_direction, void *buffer, unsigned bufflen,
		     unsigned char *sense, int timeout, int retries)
		 unsigned char *sense, int timeout, int retries, int flags)
{
	struct request *req;
	int write = (data_direction == DMA_TO_DEVICE);
@@ -314,7 +315,7 @@ int scsi_execute_req(struct scsi_device *sdev, unsigned char *cmd,
	req->sense = sense;
	req->sense_len = 0;
	req->timeout = timeout;
	req->flags |= REQ_BLOCK_PC | REQ_SPECIAL;
	req->flags |= flags | REQ_BLOCK_PC | REQ_SPECIAL;

	/*
	 * head injection *required* here otherwise quiesce won't work
@@ -328,7 +329,7 @@ int scsi_execute_req(struct scsi_device *sdev, unsigned char *cmd,
	return ret;
}

EXPORT_SYMBOL(scsi_execute_req);
EXPORT_SYMBOL(scsi_execute);

/*
 * Function:    scsi_init_cmd_errh()
+57 −67
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@
#include "scsi_priv.h"
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_request.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_spi.h>
@@ -108,25 +108,33 @@ static int sprint_frac(char *dest, int value, int denom)

/* Modification of scsi_wait_req that will clear UNIT ATTENTION conditions
 * resulting from (likely) bus and device resets */
static void spi_wait_req(struct scsi_request *sreq, const void *cmd,
			 void *buffer, unsigned bufflen)
static int spi_execute(struct scsi_device *sdev, const void *cmd,
		       enum dma_data_direction dir,
		       void *buffer, unsigned bufflen,
		       struct scsi_sense_hdr *sshdr)
{
	int i;
	int i, result;
	unsigned char sense[SCSI_SENSE_BUFFERSIZE];

	for(i = 0; i < DV_RETRIES; i++) {
		sreq->sr_request->flags |= REQ_FAILFAST;

		scsi_wait_req(sreq, cmd, buffer, bufflen,
			      DV_TIMEOUT, /* retries */ 1);
		if (sreq->sr_result & DRIVER_SENSE) {
			struct scsi_sense_hdr sshdr;

			if (scsi_request_normalize_sense(sreq, &sshdr)
			    && sshdr.sense_key == UNIT_ATTENTION)
		/* FIXME: need to set REQ_FAILFAST */
		result = scsi_execute(sdev, cmd, dir, buffer, bufflen,
				      sense, DV_TIMEOUT, /* retries */ 1,
				      REQ_FAILFAST);
		if (result & DRIVER_SENSE) {
			struct scsi_sense_hdr sshdr_tmp;
			if (!sshdr)
				sshdr = &sshdr_tmp;

			if (scsi_normalize_sense(sense, sizeof(*sense),
						 sshdr)
			    && sshdr->sense_key == UNIT_ATTENTION)
				continue;
		}
		break;
	}
	return result;
}

static struct {
@@ -546,13 +554,13 @@ enum spi_compare_returns {
/* This is for read/write Domain Validation:  If the device supports
 * an echo buffer, we do read/write tests to it */
static enum spi_compare_returns
spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
spi_dv_device_echo_buffer(struct scsi_device *sdev, u8 *buffer,
			  u8 *ptr, const int retries)
{
	struct scsi_device *sdev = sreq->sr_device;
	int len = ptr - buffer;
	int j, k, r;
	int j, k, r, result;
	unsigned int pattern = 0x0000ffff;
	struct scsi_sense_hdr sshdr;

	const char spi_write_buffer[] = {
		WRITE_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0
@@ -597,14 +605,12 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
	}

	for (r = 0; r < retries; r++) {
		sreq->sr_cmd_len = 0;	/* wait_req to fill in */
		sreq->sr_data_direction = DMA_TO_DEVICE;
		spi_wait_req(sreq, spi_write_buffer, buffer, len);
		if(sreq->sr_result || !scsi_device_online(sdev)) {
			struct scsi_sense_hdr sshdr;
		result = spi_execute(sdev, spi_write_buffer, DMA_TO_DEVICE,
				     buffer, len, &sshdr);
		if(result || !scsi_device_online(sdev)) {

			scsi_device_set_state(sdev, SDEV_QUIESCE);
			if (scsi_request_normalize_sense(sreq, &sshdr)
			if (scsi_sense_valid(&sshdr)
			    && sshdr.sense_key == ILLEGAL_REQUEST
			    /* INVALID FIELD IN CDB */
			    && sshdr.asc == 0x24 && sshdr.ascq == 0x00)
@@ -616,14 +622,13 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
				return SPI_COMPARE_SKIP_TEST;


			SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result);
			SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Write Buffer failure %x\n", result);
			return SPI_COMPARE_FAILURE;
		}

		memset(ptr, 0, len);
		sreq->sr_cmd_len = 0;	/* wait_req to fill in */
		sreq->sr_data_direction = DMA_FROM_DEVICE;
		spi_wait_req(sreq, spi_read_buffer, ptr, len);
		spi_execute(sdev, spi_read_buffer, DMA_FROM_DEVICE,
			    ptr, len, NULL);
		scsi_device_set_state(sdev, SDEV_QUIESCE);

		if (memcmp(buffer, ptr, len) != 0)
@@ -635,25 +640,22 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
/* This is for the simplest form of Domain Validation: a read test
 * on the inquiry data from the device */
static enum spi_compare_returns
spi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer,
spi_dv_device_compare_inquiry(struct scsi_device *sdev, u8 *buffer,
			      u8 *ptr, const int retries)
{
	int r;
	const int len = sreq->sr_device->inquiry_len;
	struct scsi_device *sdev = sreq->sr_device;
	int r, result;
	const int len = sdev->inquiry_len;
	const char spi_inquiry[] = {
		INQUIRY, 0, 0, 0, len, 0
	};

	for (r = 0; r < retries; r++) {
		sreq->sr_cmd_len = 0;	/* wait_req to fill in */
		sreq->sr_data_direction = DMA_FROM_DEVICE;

		memset(ptr, 0, len);

		spi_wait_req(sreq, spi_inquiry, ptr, len);
		result = spi_execute(sdev, spi_inquiry, DMA_FROM_DEVICE,
				     ptr, len, NULL);
		
		if(sreq->sr_result || !scsi_device_online(sdev)) {
		if(result || !scsi_device_online(sdev)) {
			scsi_device_set_state(sdev, SDEV_QUIESCE);
			return SPI_COMPARE_FAILURE;
		}
@@ -674,12 +676,11 @@ spi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer,
}

static enum spi_compare_returns
spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr,
spi_dv_retrain(struct scsi_device *sdev, u8 *buffer, u8 *ptr,
	       enum spi_compare_returns 
	       (*compare_fn)(struct scsi_request *, u8 *, u8 *, int))
	       (*compare_fn)(struct scsi_device *, u8 *, u8 *, int))
{
	struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt);
	struct scsi_device *sdev = sreq->sr_device;
	struct spi_internal *i = to_spi_internal(sdev->host->transportt);
	struct scsi_target *starget = sdev->sdev_target;
	int period = 0, prevperiod = 0; 
	enum spi_compare_returns retval;
@@ -687,7 +688,7 @@ spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr,

	for (;;) {
		int newperiod;
		retval = compare_fn(sreq, buffer, ptr, DV_LOOPS);
		retval = compare_fn(sdev, buffer, ptr, DV_LOOPS);

		if (retval == SPI_COMPARE_SUCCESS
		    || retval == SPI_COMPARE_SKIP_TEST)
@@ -733,9 +734,9 @@ spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr,
}

static int
spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
spi_dv_device_get_echo_buffer(struct scsi_device *sdev, u8 *buffer)
{
	int l;
	int l, result;

	/* first off do a test unit ready.  This can error out 
	 * because of reservations or some other reason.  If it
@@ -751,18 +752,16 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
	};

	
	sreq->sr_cmd_len = 0;
	sreq->sr_data_direction = DMA_NONE;

	/* We send a set of three TURs to clear any outstanding 
	 * unit attention conditions if they exist (Otherwise the
	 * buffer tests won't be happy).  If the TUR still fails
	 * (reservation conflict, device not ready, etc) just
	 * skip the write tests */
	for (l = 0; ; l++) {
		spi_wait_req(sreq, spi_test_unit_ready, NULL, 0);
		result = spi_execute(sdev, spi_test_unit_ready, DMA_NONE, 
				     NULL, 0, NULL);

		if(sreq->sr_result) {
		if(result) {
			if(l >= 3)
				return 0;
		} else {
@@ -771,12 +770,10 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
		}
	}

	sreq->sr_cmd_len = 0;
	sreq->sr_data_direction = DMA_FROM_DEVICE;

	spi_wait_req(sreq, spi_read_buffer_descriptor, buffer, 4);
	result = spi_execute(sdev, spi_read_buffer_descriptor, 
			     DMA_FROM_DEVICE, buffer, 4, NULL);

	if (sreq->sr_result)
	if (result)
		/* Device has no echo buffer */
		return 0;

@@ -784,17 +781,16 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
}

static void
spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
{
	struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt);
	struct scsi_device *sdev = sreq->sr_device;
	struct spi_internal *i = to_spi_internal(sdev->host->transportt);
	struct scsi_target *starget = sdev->sdev_target;
	int len = sdev->inquiry_len;
	/* first set us up for narrow async */
	DV_SET(offset, 0);
	DV_SET(width, 0);
	
	if (spi_dv_device_compare_inquiry(sreq, buffer, buffer, DV_LOOPS)
	if (spi_dv_device_compare_inquiry(sdev, buffer, buffer, DV_LOOPS)
	    != SPI_COMPARE_SUCCESS) {
		SPI_PRINTK(starget, KERN_ERR, "Domain Validation Initial Inquiry Failed\n");
		/* FIXME: should probably offline the device here? */
@@ -806,7 +802,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
	    scsi_device_wide(sdev)) {
		i->f->set_width(starget, 1);

		if (spi_dv_device_compare_inquiry(sreq, buffer,
		if (spi_dv_device_compare_inquiry(sdev, buffer,
						   buffer + len,
						   DV_LOOPS)
		    != SPI_COMPARE_SUCCESS) {
@@ -827,7 +823,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)

	len = 0;
	if (scsi_device_dt(sdev))
		len = spi_dv_device_get_echo_buffer(sreq, buffer);
		len = spi_dv_device_get_echo_buffer(sdev, buffer);

 retry:

@@ -853,7 +849,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)

	if (len == 0) {
		SPI_PRINTK(starget, KERN_INFO, "Domain Validation skipping write tests\n");
		spi_dv_retrain(sreq, buffer, buffer + len,
		spi_dv_retrain(sdev, buffer, buffer + len,
			       spi_dv_device_compare_inquiry);
		return;
	}
@@ -863,7 +859,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
		len = SPI_MAX_ECHO_BUFFER_SIZE;
	}

	if (spi_dv_retrain(sreq, buffer, buffer + len,
	if (spi_dv_retrain(sdev, buffer, buffer + len,
			   spi_dv_device_echo_buffer)
	    == SPI_COMPARE_SKIP_TEST) {
		/* OK, the stupid drive can't do a write echo buffer
@@ -886,16 +882,12 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
void
spi_dv_device(struct scsi_device *sdev)
{
	struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL);
	struct scsi_target *starget = sdev->sdev_target;
	u8 *buffer;
	const int len = SPI_MAX_ECHO_BUFFER_SIZE*2;

	if (unlikely(!sreq))
		return;

	if (unlikely(scsi_device_get(sdev)))
		goto out_free_req;
		return;

	buffer = kmalloc(len, GFP_KERNEL);

@@ -916,7 +908,7 @@ spi_dv_device(struct scsi_device *sdev)

	SPI_PRINTK(starget, KERN_INFO, "Beginning Domain Validation\n");

	spi_dv_device_internal(sreq, buffer);
	spi_dv_device_internal(sdev, buffer);

	SPI_PRINTK(starget, KERN_INFO, "Ending Domain Validation\n");

@@ -931,8 +923,6 @@ spi_dv_device(struct scsi_device *sdev)
	kfree(buffer);
 out_put:
	scsi_device_put(sdev);
 out_free_req:
	scsi_release_request(sreq);
}
EXPORT_SYMBOL(spi_dv_device);

+13 −0
Original line number Diff line number Diff line
@@ -256,6 +256,19 @@ extern void int_to_scsilun(unsigned int, struct scsi_lun *);
extern const char *scsi_device_state_name(enum scsi_device_state);
extern int scsi_is_sdev_device(const struct device *);
extern int scsi_is_target_device(const struct device *);
extern int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
			int data_direction, void *buffer, unsigned bufflen,
			unsigned char *sense, int timeout, int retries,
			int flag);

static inline int
scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
		 int data_direction, void *buffer, unsigned bufflen,
		 unsigned char *sense, int timeout, int retries)
{
	return scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense,
			    timeout, retries, 0);
}
static inline int scsi_device_online(struct scsi_device *sdev)
{
	return sdev->sdev_state != SDEV_OFFLINE;
+8 −0
Original line number Diff line number Diff line
@@ -26,6 +26,14 @@ struct scsi_sense_hdr { /* See SPC-3 section 4.5 */
	u8 additional_length;	/* always 0 for fixed sense format */
};

static inline int scsi_sense_valid(struct scsi_sense_hdr *sshdr)
{
	if (!sshdr)
		return 0;

	return (sshdr->response_code & 0x70) == 0x70;
}


extern void scsi_add_timer(struct scsi_cmnd *, int,
		void (*)(struct scsi_cmnd *));
Loading