diff options
Diffstat (limited to 'drivers/scsi/hpsa.c')
-rw-r--r-- | drivers/scsi/hpsa.c | 74 |
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", |