summaryrefslogtreecommitdiff
path: root/drivers/scsi/hpsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/hpsa.c')
-rw-r--r--drivers/scsi/hpsa.c74
1 files changed, 72 insertions, 2 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index a2c99245b82c..87f8a1b8d2ea 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -2355,8 +2355,23 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
return FAILED;
}
+static void swizzle_abort_tag(u8 *tag)
+{
+ u8 original_tag[8];
+
+ memcpy(original_tag, tag, 8);
+ tag[0] = original_tag[3];
+ tag[1] = original_tag[2];
+ tag[2] = original_tag[1];
+ tag[3] = original_tag[0];
+ tag[4] = original_tag[7];
+ tag[5] = original_tag[6];
+ tag[6] = original_tag[5];
+ tag[7] = original_tag[4];
+}
+
static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
- struct CommandList *abort)
+ struct CommandList *abort, int swizzle)
{
int rc = IO_OK;
struct CommandList *c;
@@ -2369,6 +2384,8 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
}
fill_cmd(c, HPSA_ABORT_MSG, h, abort, 0, 0, scsi3addr, TYPE_MSG);
+ if (swizzle)
+ swizzle_abort_tag(&c->Request.CDB[4]);
hpsa_scsi_do_simple_cmd_core(h, c);
dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd_core completed.\n",
__func__, abort->Header.Tag.upper, abort->Header.Tag.lower);
@@ -2428,6 +2445,59 @@ static struct CommandList *hpsa_find_cmd_in_queue(struct ctlr_info *h,
return NULL;
}
+static struct CommandList *hpsa_find_cmd_in_queue_by_tag(struct ctlr_info *h,
+ u8 *tag, struct list_head *queue_head)
+{
+ unsigned long flags;
+ struct CommandList *c;
+
+ spin_lock_irqsave(&h->lock, flags);
+ list_for_each_entry(c, queue_head, list) {
+ if (memcmp(&c->Header.Tag, tag, 8) != 0)
+ continue;
+ spin_unlock_irqrestore(&h->lock, flags);
+ return c;
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
+ return NULL;
+}
+
+/* Some Smart Arrays need the abort tag swizzled, and some don't. It's hard to
+ * tell which kind we're dealing with, so we send the abort both ways. There
+ * shouldn't be any collisions between swizzled and unswizzled tags due to the
+ * way we construct our tags but we check anyway in case the assumptions which
+ * make this true someday become false.
+ */
+static int hpsa_send_abort_both_ways(struct ctlr_info *h,
+ unsigned char *scsi3addr, struct CommandList *abort)
+{
+ u8 swizzled_tag[8];
+ struct CommandList *c;
+ int rc = 0, rc2 = 0;
+
+ /* we do not expect to find the swizzled tag in our queue, but
+ * check anyway just to be sure the assumptions which make this
+ * the case haven't become wrong.
+ */
+ memcpy(swizzled_tag, &abort->Request.CDB[4], 8);
+ swizzle_abort_tag(swizzled_tag);
+ c = hpsa_find_cmd_in_queue_by_tag(h, swizzled_tag, &h->cmpQ);
+ if (c != NULL) {
+ dev_warn(&h->pdev->dev, "Unexpectedly found byte-swapped tag in completion queue.\n");
+ return hpsa_send_abort(h, scsi3addr, abort, 0);
+ }
+ rc = hpsa_send_abort(h, scsi3addr, abort, 0);
+
+ /* if the command is still in our queue, we can't conclude that it was
+ * aborted (it might have just completed normally) but in any case
+ * we don't need to try to abort it another way.
+ */
+ c = hpsa_find_cmd_in_queue(h, abort->scsi_cmd, &h->cmpQ);
+ if (c)
+ rc2 = hpsa_send_abort(h, scsi3addr, abort, 1);
+ return rc && rc2;
+}
+
/* Send an abort for the specified command.
* If the device and controller support it,
* send a task abort request.
@@ -2512,7 +2582,7 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
* by the firmware (but not to the scsi mid layer) but we can't
* distinguish which. Send the abort down.
*/
- rc = hpsa_send_abort(h, dev->scsi3addr, abort);
+ rc = hpsa_send_abort_both_ways(h, dev->scsi3addr, abort);
if (rc != 0) {
dev_dbg(&h->pdev->dev, "%s Request FAILED.\n", msg);
dev_warn(&h->pdev->dev, "FAILED abort on device C%d:B%d:T%d:L%d\n",