summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_transport_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/scsi_transport_spi.c')
-rw-r--r--drivers/scsi/scsi_transport_spi.c168
1 files changed, 89 insertions, 79 deletions
diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c
index 7670919a087..ef577c8c218 100644
--- a/drivers/scsi/scsi_transport_spi.c
+++ b/drivers/scsi/scsi_transport_spi.c
@@ -28,14 +28,14 @@
#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>
#define SPI_PRINTK(x, l, f, a...) dev_printk(l, &(x)->dev, f , ##a)
-#define SPI_NUM_ATTRS 13 /* increase this if you add attributes */
+#define SPI_NUM_ATTRS 14 /* increase this if you add attributes */
#define SPI_OTHER_ATTRS 1 /* Increase this if you add "always
* on" attributes */
#define SPI_HOST_ATTRS 1
@@ -106,27 +106,31 @@ static int sprint_frac(char *dest, int value, int denom)
return result;
}
-/* 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)
+ 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 {
@@ -162,7 +166,8 @@ static inline enum spi_signal_type spi_signal_to_value(const char *name)
return SPI_SIGNAL_UNKNOWN;
}
-static int spi_host_setup(struct device *dev)
+static int spi_host_setup(struct transport_container *tc, struct device *dev,
+ struct class_device *cdev)
{
struct Scsi_Host *shost = dev_to_shost(dev);
@@ -196,7 +201,9 @@ static int spi_host_match(struct attribute_container *cont,
return &i->t.host_attrs.ac == cont;
}
-static int spi_device_configure(struct device *dev)
+static int spi_device_configure(struct transport_container *tc,
+ struct device *dev,
+ struct class_device *cdev)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct scsi_target *starget = sdev->sdev_target;
@@ -214,7 +221,9 @@ static int spi_device_configure(struct device *dev)
return 0;
}
-static int spi_setup_transport_attrs(struct device *dev)
+static int spi_setup_transport_attrs(struct transport_container *tc,
+ struct device *dev,
+ struct class_device *cdev)
{
struct scsi_target *starget = to_scsi_target(dev);
@@ -231,6 +240,7 @@ static int spi_setup_transport_attrs(struct device *dev)
spi_rd_strm(starget) = 0;
spi_rti(starget) = 0;
spi_pcomp_en(starget) = 0;
+ spi_hold_mcs(starget) = 0;
spi_dv_pending(starget) = 0;
spi_initial_dv(starget) = 0;
init_MUTEX(&spi_dv_sem(starget));
@@ -347,6 +357,7 @@ spi_transport_rd_attr(wr_flow, "%d\n");
spi_transport_rd_attr(rd_strm, "%d\n");
spi_transport_rd_attr(rti, "%d\n");
spi_transport_rd_attr(pcomp_en, "%d\n");
+spi_transport_rd_attr(hold_mcs, "%d\n");
/* we only care about the first child device so we return 1 */
static int child_iter(struct device *dev, void *data)
@@ -539,13 +550,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
@@ -590,14 +601,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)
@@ -609,14 +618,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)
@@ -628,25 +636,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;
}
@@ -667,12 +672,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;
@@ -680,7 +684,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)
@@ -726,9 +730,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
@@ -744,18 +748,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 {
@@ -764,12 +766,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;
@@ -777,17 +777,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? */
@@ -799,7 +798,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) {
@@ -820,7 +819,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:
@@ -846,7 +845,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;
}
@@ -856,7 +855,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
@@ -879,16 +878,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);
@@ -909,7 +904,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");
@@ -924,8 +919,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);
@@ -1028,10 +1021,17 @@ void spi_display_xfer_agreement(struct scsi_target *starget)
sprint_frac(tmp, picosec, 1000);
dev_info(&starget->dev,
- "%s %sSCSI %d.%d MB/s %s%s%s (%s ns, offset %d)\n",
- scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10,
- tp->dt ? "DT" : "ST", tp->iu ? " IU" : "",
- tp->qas ? " QAS" : "", tmp, tp->offset);
+ "%s %sSCSI %d.%d MB/s %s%s%s%s%s%s%s%s (%s ns, offset %d)\n",
+ scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10,
+ tp->dt ? "DT" : "ST",
+ tp->iu ? " IU" : "",
+ tp->qas ? " QAS" : "",
+ tp->rd_strm ? " RDSTRM" : "",
+ tp->rti ? " RTI" : "",
+ tp->wr_flow ? " WRFLOW" : "",
+ tp->pcomp_en ? " PCOMP" : "",
+ tp->hold_mcs ? " HMCS" : "",
+ tmp, tp->offset);
} else {
dev_info(&starget->dev, "%sasynchronous.\n",
tp->width ? "wide " : "");
@@ -1073,6 +1073,7 @@ static int spi_device_match(struct attribute_container *cont,
{
struct scsi_device *sdev;
struct Scsi_Host *shost;
+ struct spi_internal *i;
if (!scsi_is_sdev_device(dev))
return 0;
@@ -1085,6 +1086,9 @@ static int spi_device_match(struct attribute_container *cont,
/* Note: this class has no device attributes, so it has
* no per-HBA allocation and thus we don't need to distinguish
* the attribute containers for the device */
+ i = to_spi_internal(shost->transportt);
+ if (i->f->deny_binding && i->f->deny_binding(sdev->sdev_target))
+ return 0;
return 1;
}
@@ -1092,6 +1096,7 @@ static int spi_target_match(struct attribute_container *cont,
struct device *dev)
{
struct Scsi_Host *shost;
+ struct scsi_target *starget;
struct spi_internal *i;
if (!scsi_is_target_device(dev))
@@ -1103,7 +1108,11 @@ static int spi_target_match(struct attribute_container *cont,
return 0;
i = to_spi_internal(shost->transportt);
-
+ starget = to_scsi_target(dev);
+
+ if (i->f->deny_binding && i->f->deny_binding(starget))
+ return 0;
+
return &i->t.target_attrs.ac == cont;
}
@@ -1154,6 +1163,7 @@ spi_attach_transport(struct spi_function_template *ft)
SETUP_ATTRIBUTE(rd_strm);
SETUP_ATTRIBUTE(rti);
SETUP_ATTRIBUTE(pcomp_en);
+ SETUP_ATTRIBUTE(hold_mcs);
/* if you add an attribute but forget to increase SPI_NUM_ATTRS
* this bug will trigger */