--- /dev/null
+From stable-bounces@linux.kernel.org Thu Sep 27 15:52:26 2007
+From: James Bottomley <James.Bottomley@SteelEye.com>
+To: stable@kernel.org
+Date: Thu, 27 Sep 2007 18:51:57 -0400
+Message-Id: <1190933517.5392.27.camel@localhost.localdomain>
+Subject: [SCSI] scsi_transport_spi: fix domain validation failure from incorrect width setting
+
+Domain Validation in the SPI transport class is failing on boxes with
+damaged cables (and failing to the extent that the box hangs). The
+problem is that the first test it does is a cable integrity test for
+wide transfers and if this fails, it turns the wide bit off. The
+problem is that the next set of tests it does turns wide back on
+again, with the result that it runs through the entirety of DV with a
+known bad setting and then hangs the system.
+
+The attached patch fixes the problem by physically nailing the wide
+setting to what it deduces it should be for the whole of Domain
+Validation.
+
+Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
+---
+This one's a long standing bug which can cause a machine hang on certain
+boxes with cabling problems, it just went upstream as commit
+
+2302827c95fe0f441025acd5133e532d2eef322b
+
+ drivers/scsi/scsi_transport_spi.c | 28 ++++++++++++++++++++++------
+ 1 file changed, 22 insertions(+), 6 deletions(-)
+
+--- linux-2.6.22.9.orig/drivers/scsi/scsi_transport_spi.c
++++ linux-2.6.22.9/drivers/scsi/scsi_transport_spi.c
+@@ -787,10 +787,12 @@ spi_dv_device_internal(struct scsi_devic
+ struct scsi_target *starget = sdev->sdev_target;
+ struct Scsi_Host *shost = sdev->host;
+ int len = sdev->inquiry_len;
++ int min_period = spi_min_period(starget);
++ int max_width = spi_max_width(starget);
+ /* first set us up for narrow async */
+ DV_SET(offset, 0);
+ DV_SET(width, 0);
+-
++
+ if (spi_dv_device_compare_inquiry(sdev, buffer, buffer, DV_LOOPS)
+ != SPI_COMPARE_SUCCESS) {
+ starget_printk(KERN_ERR, starget, "Domain Validation Initial Inquiry Failed\n");
+@@ -798,9 +800,13 @@ spi_dv_device_internal(struct scsi_devic
+ return;
+ }
+
++ if (!scsi_device_wide(sdev)) {
++ spi_max_width(starget) = 0;
++ max_width = 0;
++ }
++
+ /* test width */
+- if (i->f->set_width && spi_max_width(starget) &&
+- scsi_device_wide(sdev)) {
++ if (i->f->set_width && max_width) {
+ i->f->set_width(starget, 1);
+
+ if (spi_dv_device_compare_inquiry(sdev, buffer,
+@@ -809,6 +815,11 @@ spi_dv_device_internal(struct scsi_devic
+ != SPI_COMPARE_SUCCESS) {
+ starget_printk(KERN_ERR, starget, "Wide Transfers Fail\n");
+ i->f->set_width(starget, 0);
++ /* Make sure we don't force wide back on by asking
++ * for a transfer period that requires it */
++ max_width = 0;
++ if (min_period < 10)
++ min_period = 10;
+ }
+ }
+
+@@ -828,7 +839,8 @@ spi_dv_device_internal(struct scsi_devic
+
+ /* now set up to the maximum */
+ DV_SET(offset, spi_max_offset(starget));
+- DV_SET(period, spi_min_period(starget));
++ DV_SET(period, min_period);
++
+ /* try QAS requests; this should be harmless to set if the
+ * target supports it */
+ if (scsi_device_qas(sdev)) {
+@@ -837,14 +849,14 @@ spi_dv_device_internal(struct scsi_devic
+ DV_SET(qas, 0);
+ }
+
+- if (scsi_device_ius(sdev) && spi_min_period(starget) < 9) {
++ if (scsi_device_ius(sdev) && min_period < 9) {
+ /* This u320 (or u640). Set IU transfers */
+ DV_SET(iu, 1);
+ /* Then set the optional parameters */
+ DV_SET(rd_strm, 1);
+ DV_SET(wr_flow, 1);
+ DV_SET(rti, 1);
+- if (spi_min_period(starget) == 8)
++ if (min_period == 8)
+ DV_SET(pcomp_en, 1);
+ } else {
+ DV_SET(iu, 0);
+@@ -862,6 +874,10 @@ spi_dv_device_internal(struct scsi_devic
+ } else {
+ DV_SET(dt, 1);
+ }
++ /* set width last because it will pull all the other
++ * parameters down to required values */
++ DV_SET(width, max_width);
++
+ /* Do the read only INQUIRY tests */
+ spi_dv_retrain(sdev, buffer, buffer + sdev->inquiry_len,
+ spi_dv_device_compare_inquiry);