aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/hpsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/hpsa.c')
-rw-r--r--drivers/scsi/hpsa.c849
1 files changed, 191 insertions, 658 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 73daace478cb..9934947073e6 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -60,7 +60,7 @@
* HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.'
* with an optional trailing '-' followed by a byte value (0-255).
*/
-#define HPSA_DRIVER_VERSION "3.4.18-0"
+#define HPSA_DRIVER_VERSION "3.4.20-0"
#define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
#define HPSA "hpsa"
@@ -258,7 +258,6 @@ static int hpsa_scan_finished(struct Scsi_Host *sh,
static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth);
static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
-static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd);
static int hpsa_slave_alloc(struct scsi_device *sdev);
static int hpsa_slave_configure(struct scsi_device *sdev);
static void hpsa_slave_destroy(struct scsi_device *sdev);
@@ -326,7 +325,7 @@ static inline bool hpsa_is_cmd_idle(struct CommandList *c)
static inline bool hpsa_is_pending_event(struct CommandList *c)
{
- return c->abort_pending || c->reset_pending;
+ return c->reset_pending;
}
/* extract sense key, asc, and ascq from sense data. -1 means invalid. */
@@ -581,12 +580,6 @@ static u32 soft_unresettable_controller[] = {
0x409D0E11, /* Smart Array 6400 EM */
};
-static u32 needs_abort_tags_swizzled[] = {
- 0x323D103C, /* Smart Array P700m */
- 0x324a103C, /* Smart Array P712m */
- 0x324b103C, /* SmartArray P711m */
-};
-
static int board_id_in_array(u32 a[], int nelems, u32 board_id)
{
int i;
@@ -615,12 +608,6 @@ static int ctlr_is_resettable(u32 board_id)
ctlr_is_soft_resettable(board_id);
}
-static int ctlr_needs_abort_tags_swizzled(u32 board_id)
-{
- return board_id_in_array(needs_abort_tags_swizzled,
- ARRAY_SIZE(needs_abort_tags_swizzled), board_id);
-}
-
static ssize_t host_show_resettable(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -928,8 +915,8 @@ static struct device_attribute *hpsa_shost_attrs[] = {
NULL,
};
-#define HPSA_NRESERVED_CMDS (HPSA_CMDS_RESERVED_FOR_ABORTS + \
- HPSA_CMDS_RESERVED_FOR_DRIVER + HPSA_MAX_CONCURRENT_PASSTHRUS)
+#define HPSA_NRESERVED_CMDS (HPSA_CMDS_RESERVED_FOR_DRIVER +\
+ HPSA_MAX_CONCURRENT_PASSTHRUS)
static struct scsi_host_template hpsa_driver_template = {
.module = THIS_MODULE,
@@ -941,7 +928,6 @@ static struct scsi_host_template hpsa_driver_template = {
.change_queue_depth = hpsa_change_queue_depth,
.this_id = -1,
.use_clustering = ENABLE_CLUSTERING,
- .eh_abort_handler = hpsa_eh_abort_handler,
.eh_device_reset_handler = hpsa_eh_device_reset_handler,
.ioctl = hpsa_ioctl,
.slave_alloc = hpsa_slave_alloc,
@@ -1110,6 +1096,7 @@ static int is_firmware_flash_cmd(u8 *cdb)
*/
#define HEARTBEAT_SAMPLE_INTERVAL_DURING_FLASH (240 * HZ)
#define HEARTBEAT_SAMPLE_INTERVAL (30 * HZ)
+#define HPSA_EVENT_MONITOR_INTERVAL (15 * HZ)
static void dial_down_lockup_detection_during_fw_flash(struct ctlr_info *h,
struct CommandList *c)
{
@@ -1859,10 +1846,13 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h,
* A reset can cause a device status to change
* re-schedule the scan to see what happened.
*/
+ spin_lock_irqsave(&h->reset_lock, flags);
if (h->reset_in_progress) {
h->drv_req_rescan = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
return;
}
+ spin_unlock_irqrestore(&h->reset_lock, flags);
added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL);
removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL);
@@ -2066,10 +2056,13 @@ static int hpsa_slave_configure(struct scsi_device *sdev)
sd = sdev->hostdata;
sdev->no_uld_attach = !sd || !sd->expose_device;
- if (sd)
- queue_depth = sd->queue_depth != 0 ?
- sd->queue_depth : sdev->host->can_queue;
- else
+ if (sd) {
+ if (sd->external)
+ queue_depth = EXTERNAL_QD;
+ else
+ queue_depth = sd->queue_depth != 0 ?
+ sd->queue_depth : sdev->host->can_queue;
+ } else
queue_depth = sdev->host->can_queue;
scsi_change_queue_depth(sdev, queue_depth);
@@ -2354,26 +2347,12 @@ static void hpsa_cmd_resolve_events(struct ctlr_info *h,
bool do_wake = false;
/*
- * Prevent the following race in the abort handler:
- *
- * 1. LLD is requested to abort a SCSI command
- * 2. The SCSI command completes
- * 3. The struct CommandList associated with step 2 is made available
- * 4. New I/O request to LLD to another LUN re-uses struct CommandList
- * 5. Abort handler follows scsi_cmnd->host_scribble and
- * finds struct CommandList and tries to aborts it
- * Now we have aborted the wrong command.
- *
- * Reset c->scsi_cmd here so that the abort or reset handler will know
+ * Reset c->scsi_cmd here so that the reset handler will know
* this command has completed. Then, check to see if the handler is
* waiting for this command, and, if so, wake it.
*/
c->scsi_cmd = SCSI_CMD_IDLE;
mb(); /* Declare command idle before checking for pending events. */
- if (c->abort_pending) {
- do_wake = true;
- c->abort_pending = false;
- }
if (c->reset_pending) {
unsigned long flags;
struct hpsa_scsi_dev_t *dev;
@@ -2416,20 +2395,6 @@ static void hpsa_retry_cmd(struct ctlr_info *h, struct CommandList *c)
queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work);
}
-static void hpsa_set_scsi_cmd_aborted(struct scsi_cmnd *cmd)
-{
- cmd->result = DID_ABORT << 16;
-}
-
-static void hpsa_cmd_abort_and_free(struct ctlr_info *h, struct CommandList *c,
- struct scsi_cmnd *cmd)
-{
- hpsa_set_scsi_cmd_aborted(cmd);
- dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n",
- c->Request.CDB, c->err_info->ScsiStatus);
- hpsa_cmd_resolve_and_free(h, c);
-}
-
static void process_ioaccel2_completion(struct ctlr_info *h,
struct CommandList *c, struct scsi_cmnd *cmd,
struct hpsa_scsi_dev_t *dev)
@@ -2554,12 +2519,9 @@ static void complete_scsi_command(struct CommandList *cp)
return hpsa_cmd_free_and_done(h, cp, cmd);
}
- if ((unlikely(hpsa_is_pending_event(cp)))) {
+ if ((unlikely(hpsa_is_pending_event(cp))))
if (cp->reset_pending)
return hpsa_cmd_free_and_done(h, cp, cmd);
- if (cp->abort_pending)
- return hpsa_cmd_abort_and_free(h, cp, cmd);
- }
if (cp->cmd_type == CMD_IOACCEL2)
return process_ioaccel2_completion(h, cp, cmd, dev);
@@ -2679,8 +2641,8 @@ static void complete_scsi_command(struct CommandList *cp)
cp->Request.CDB);
break;
case CMD_ABORTED:
- /* Return now to avoid calling scsi_done(). */
- return hpsa_cmd_abort_and_free(h, cp, cmd);
+ cmd->result = DID_ABORT << 16;
+ break;
case CMD_ABORT_FAILED:
cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "CDB %16phN : abort failed\n",
@@ -3090,7 +3052,7 @@ static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev,
if (unlikely(rc))
atomic_set(&dev->reset_cmds_out, 0);
else
- wait_for_device_to_become_ready(h, scsi3addr, 0);
+ rc = wait_for_device_to_become_ready(h, scsi3addr, 0);
mutex_unlock(&h->reset_mutex);
return rc;
@@ -3353,6 +3315,11 @@ static void hpsa_get_enclosure_info(struct ctlr_info *h,
bmic_device_index = GET_BMIC_DRIVE_NUMBER(&rle->lunid[0]);
+ if (encl_dev->target == -1 || encl_dev->lun == -1) {
+ rc = IO_OK;
+ goto out;
+ }
+
if (bmic_device_index == 0xFF00 || MASKED_DEVICE(&rle->lunid[0])) {
rc = IO_OK;
goto out;
@@ -3781,53 +3748,6 @@ static unsigned char hpsa_volume_offline(struct ctlr_info *h,
return HPSA_LV_OK;
}
-/*
- * Find out if a logical device supports aborts by simply trying one.
- * Smart Array may claim not to support aborts on logical drives, but
- * if a MSA2000 * is connected, the drives on that will be presented
- * by the Smart Array as logical drives, and aborts may be sent to
- * those devices successfully. So the simplest way to find out is
- * to simply try an abort and see how the device responds.
- */
-static int hpsa_device_supports_aborts(struct ctlr_info *h,
- unsigned char *scsi3addr)
-{
- struct CommandList *c;
- struct ErrorInfo *ei;
- int rc = 0;
-
- u64 tag = (u64) -1; /* bogus tag */
-
- /* Assume that physical devices support aborts */
- if (!is_logical_dev_addr_mode(scsi3addr))
- return 1;
-
- c = cmd_alloc(h);
-
- (void) fill_cmd(c, HPSA_ABORT_MSG, h, &tag, 0, 0, scsi3addr, TYPE_MSG);
- (void) hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE,
- DEFAULT_TIMEOUT);
- /* no unmap needed here because no data xfer. */
- ei = c->err_info;
- switch (ei->CommandStatus) {
- case CMD_INVALID:
- rc = 0;
- break;
- case CMD_UNABORTABLE:
- case CMD_ABORT_FAILED:
- rc = 1;
- break;
- case CMD_TMF_STATUS:
- rc = hpsa_evaluate_tmf_status(h, c);
- break;
- default:
- rc = 0;
- break;
- }
- cmd_free(h, c);
- return rc;
-}
-
static int hpsa_update_device_info(struct ctlr_info *h,
unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device,
unsigned char *is_OBDR_device)
@@ -3907,6 +3827,9 @@ static int hpsa_update_device_info(struct ctlr_info *h,
this_device->queue_depth = h->nr_cmds;
}
+ if (this_device->external)
+ this_device->queue_depth = EXTERNAL_QD;
+
if (is_OBDR_device) {
/* See if this is a One-Button-Disaster-Recovery device
* by looking for "$DR-10" at offset 43 in inquiry data.
@@ -3924,31 +3847,6 @@ bail_out:
return rc;
}
-static void hpsa_update_device_supports_aborts(struct ctlr_info *h,
- struct hpsa_scsi_dev_t *dev, u8 *scsi3addr)
-{
- unsigned long flags;
- int rc, entry;
- /*
- * See if this device supports aborts. If we already know
- * the device, we already know if it supports aborts, otherwise
- * we have to find out if it supports aborts by trying one.
- */
- spin_lock_irqsave(&h->devlock, flags);
- rc = hpsa_scsi_find_entry(dev, h->dev, h->ndevices, &entry);
- if ((rc == DEVICE_SAME || rc == DEVICE_UPDATED) &&
- entry >= 0 && entry < h->ndevices) {
- dev->supports_aborts = h->dev[entry]->supports_aborts;
- spin_unlock_irqrestore(&h->devlock, flags);
- } else {
- spin_unlock_irqrestore(&h->devlock, flags);
- dev->supports_aborts =
- hpsa_device_supports_aborts(h, scsi3addr);
- if (dev->supports_aborts < 0)
- dev->supports_aborts = 0;
- }
-}
-
/*
* Helper function to assign bus, target, lun mapping of devices.
* Logical drive target and lun are assigned at this time, but
@@ -3986,35 +3884,6 @@ static void figure_bus_target_lun(struct ctlr_info *h,
0, lunid & 0x3fff);
}
-
-/*
- * Get address of physical disk used for an ioaccel2 mode command:
- * 1. Extract ioaccel2 handle from the command.
- * 2. Find a matching ioaccel2 handle from list of physical disks.
- * 3. Return:
- * 1 and set scsi3addr to address of matching physical
- * 0 if no matching physical disk was found.
- */
-static int hpsa_get_pdisk_of_ioaccel2(struct ctlr_info *h,
- struct CommandList *ioaccel2_cmd_to_abort, unsigned char *scsi3addr)
-{
- struct io_accel2_cmd *c2 =
- &h->ioaccel2_cmd_pool[ioaccel2_cmd_to_abort->cmdindex];
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&h->devlock, flags);
- for (i = 0; i < h->ndevices; i++)
- if (h->dev[i]->ioaccel_handle == le32_to_cpu(c2->scsi_nexus)) {
- memcpy(scsi3addr, h->dev[i]->scsi3addr,
- sizeof(h->dev[i]->scsi3addr));
- spin_unlock_irqrestore(&h->devlock, flags);
- return 1;
- }
- spin_unlock_irqrestore(&h->devlock, flags);
- return 0;
-}
-
static int figure_external_status(struct ctlr_info *h, int raid_ctlr_position,
int i, int nphysicals, int nlocal_logicals)
{
@@ -4115,14 +3984,6 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h,
int rc;
struct ext_report_lun_entry *rle;
- /*
- * external targets don't support BMIC
- */
- if (dev->external) {
- dev->queue_depth = 7;
- return;
- }
-
rle = &rlep->LUN[rle_index];
dev->ioaccel_handle = rle->ioaccel_handle;
@@ -4387,7 +4248,6 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
}
figure_bus_target_lun(h, lunaddrbytes, tmpdevice);
- hpsa_update_device_supports_aborts(h, tmpdevice, lunaddrbytes);
this_device = currentsd[ncurrent];
/* Turn on discovery_polling if there are ext target devices.
@@ -4584,7 +4444,55 @@ sglist_finished:
return 0;
}
-#define IO_ACCEL_INELIGIBLE (1)
+#define BUFLEN 128
+static inline void warn_zero_length_transfer(struct ctlr_info *h,
+ u8 *cdb, int cdb_len,
+ const char *func)
+{
+ char buf[BUFLEN];
+ int outlen;
+ int i;
+
+ outlen = scnprintf(buf, BUFLEN,
+ "%s: Blocking zero-length request: CDB:", func);
+ for (i = 0; i < cdb_len; i++)
+ outlen += scnprintf(buf+outlen, BUFLEN - outlen,
+ "%02hhx", cdb[i]);
+ dev_warn(&h->pdev->dev, "%s\n", buf);
+}
+
+#define IO_ACCEL_INELIGIBLE 1
+/* zero-length transfers trigger hardware errors. */
+static bool is_zero_length_transfer(u8 *cdb)
+{
+ u32 block_cnt;
+
+ /* Block zero-length transfer sizes on certain commands. */
+ switch (cdb[0]) {
+ case READ_10:
+ case WRITE_10:
+ case VERIFY: /* 0x2F */
+ case WRITE_VERIFY: /* 0x2E */
+ block_cnt = get_unaligned_be16(&cdb[7]);
+ break;
+ case READ_12:
+ case WRITE_12:
+ case VERIFY_12: /* 0xAF */
+ case WRITE_VERIFY_12: /* 0xAE */
+ block_cnt = get_unaligned_be32(&cdb[6]);
+ break;
+ case READ_16:
+ case WRITE_16:
+ case VERIFY_16: /* 0x8F */
+ block_cnt = get_unaligned_be32(&cdb[10]);
+ break;
+ default:
+ return false;
+ }
+
+ return block_cnt == 0;
+}
+
static int fixup_ioaccel_cdb(u8 *cdb, int *cdb_len)
{
int is_write = 0;
@@ -4651,6 +4559,12 @@ static int hpsa_scsi_ioaccel1_queue_command(struct ctlr_info *h,
BUG_ON(cmd->cmd_len > IOACCEL1_IOFLAGS_CDBLEN_MAX);
+ if (is_zero_length_transfer(cdb)) {
+ warn_zero_length_transfer(h, cdb, cdb_len, __func__);
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
+
if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
atomic_dec(&phys_disk->ioaccel_cmds_out);
return IO_ACCEL_INELIGIBLE;
@@ -4815,6 +4729,12 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
+ if (is_zero_length_transfer(cdb)) {
+ warn_zero_length_transfer(h, cdb, cdb_len, __func__);
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
+
if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
atomic_dec(&phys_disk->ioaccel_cmds_out);
return IO_ACCEL_INELIGIBLE;
@@ -5460,9 +5380,7 @@ static void hpsa_command_resubmit_worker(struct work_struct *work)
return hpsa_cmd_free_and_done(c->h, c, cmd);
}
if (c->reset_pending)
- return hpsa_cmd_resolve_and_free(c->h, c);
- if (c->abort_pending)
- return hpsa_cmd_abort_and_free(c->h, c, cmd);
+ return hpsa_cmd_free_and_done(c->h, c, cmd);
if (c->cmd_type == CMD_IOACCEL2) {
struct ctlr_info *h = c->h;
struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
@@ -5613,10 +5531,14 @@ static void hpsa_scan_start(struct Scsi_Host *sh)
/*
* Do the scan after a reset completion
*/
+ spin_lock_irqsave(&h->reset_lock, flags);
if (h->reset_in_progress) {
h->drv_req_rescan = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+ hpsa_scan_complete(h);
return;
}
+ spin_unlock_irqrestore(&h->reset_lock, flags);
hpsa_update_scsi_devices(h);
@@ -5828,24 +5750,37 @@ static int wait_for_device_to_become_ready(struct ctlr_info *h,
*/
static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
{
- int rc;
+ int rc = SUCCESS;
struct ctlr_info *h;
struct hpsa_scsi_dev_t *dev;
u8 reset_type;
char msg[48];
+ unsigned long flags;
/* find the controller to which the command to be aborted was sent */
h = sdev_to_hba(scsicmd->device);
if (h == NULL) /* paranoia */
return FAILED;
- if (lockup_detected(h))
- return FAILED;
+ spin_lock_irqsave(&h->reset_lock, flags);
+ h->reset_in_progress = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+
+ if (lockup_detected(h)) {
+ rc = FAILED;
+ goto return_reset_status;
+ }
dev = scsicmd->device->hostdata;
if (!dev) {
dev_err(&h->pdev->dev, "%s: device lookup failed\n", __func__);
- return FAILED;
+ rc = FAILED;
+ goto return_reset_status;
+ }
+
+ if (dev->devtype == TYPE_ENCLOSURE) {
+ rc = SUCCESS;
+ goto return_reset_status;
}
/* if controller locked up, we can guarantee command won't complete */
@@ -5854,7 +5789,8 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
"cmd %d RESET FAILED, lockup detected",
hpsa_get_cmd_index(scsicmd));
hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
- return FAILED;
+ rc = FAILED;
+ goto return_reset_status;
}
/* this reset request might be the result of a lockup; check */
@@ -5863,12 +5799,15 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
"cmd %d RESET FAILED, new lockup detected",
hpsa_get_cmd_index(scsicmd));
hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
- return FAILED;
+ rc = FAILED;
+ goto return_reset_status;
}
/* Do not attempt on controller */
- if (is_hba_lunid(dev->scsi3addr))
- return SUCCESS;
+ if (is_hba_lunid(dev->scsi3addr)) {
+ rc = SUCCESS;
+ goto return_reset_status;
+ }
if (is_logical_dev_addr_mode(dev->scsi3addr))
reset_type = HPSA_DEVICE_RESET_MSG;
@@ -5879,446 +5818,26 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
reset_type == HPSA_DEVICE_RESET_MSG ? "logical " : "physical ");
hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
- h->reset_in_progress = 1;
-
/* send a reset to the SCSI LUN which the command was sent to */
rc = hpsa_do_reset(h, dev, dev->scsi3addr, reset_type,
DEFAULT_REPLY_QUEUE);
+ if (rc == 0)
+ rc = SUCCESS;
+ else
+ rc = FAILED;
+
sprintf(msg, "reset %s %s",
reset_type == HPSA_DEVICE_RESET_MSG ? "logical " : "physical ",
- rc == 0 ? "completed successfully" : "failed");
+ rc == SUCCESS ? "completed successfully" : "failed");
hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
- h->reset_in_progress = 0;
- return rc == 0 ? SUCCESS : 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 void hpsa_get_tag(struct ctlr_info *h,
- struct CommandList *c, __le32 *taglower, __le32 *tagupper)
-{
- u64 tag;
- if (c->cmd_type == CMD_IOACCEL1) {
- struct io_accel1_cmd *cm1 = (struct io_accel1_cmd *)
- &h->ioaccel_cmd_pool[c->cmdindex];
- tag = le64_to_cpu(cm1->tag);
- *tagupper = cpu_to_le32(tag >> 32);
- *taglower = cpu_to_le32(tag);
- return;
- }
- if (c->cmd_type == CMD_IOACCEL2) {
- struct io_accel2_cmd *cm2 = (struct io_accel2_cmd *)
- &h->ioaccel2_cmd_pool[c->cmdindex];
- /* upper tag not used in ioaccel2 mode */
- memset(tagupper, 0, sizeof(*tagupper));
- *taglower = cm2->Tag;
- return;
- }
- tag = le64_to_cpu(c->Header.tag);
- *tagupper = cpu_to_le32(tag >> 32);
- *taglower = cpu_to_le32(tag);
-}
-
-static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
- struct CommandList *abort, int reply_queue)
-{
- int rc = IO_OK;
- struct CommandList *c;
- struct ErrorInfo *ei;
- __le32 tagupper, taglower;
-
- c = cmd_alloc(h);
-
- /* fill_cmd can't fail here, no buffer to map */
- (void) fill_cmd(c, HPSA_ABORT_MSG, h, &abort->Header.tag,
- 0, 0, scsi3addr, TYPE_MSG);
- if (h->needs_abort_tags_swizzled)
- swizzle_abort_tag(&c->Request.CDB[4]);
- (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, DEFAULT_TIMEOUT);
- hpsa_get_tag(h, abort, &taglower, &tagupper);
- dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd(abort) completed.\n",
- __func__, tagupper, taglower);
- /* no unmap needed here because no data xfer. */
-
- ei = c->err_info;
- switch (ei->CommandStatus) {
- case CMD_SUCCESS:
- break;
- case CMD_TMF_STATUS:
- rc = hpsa_evaluate_tmf_status(h, c);
- break;
- case CMD_UNABORTABLE: /* Very common, don't make noise. */
- rc = -1;
- break;
- default:
- dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: interpreting error.\n",
- __func__, tagupper, taglower);
- hpsa_scsi_interpret_error(h, c);
- rc = -1;
- break;
- }
- cmd_free(h, c);
- dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: Finished.\n",
- __func__, tagupper, taglower);
- return rc;
-}
-
-static void setup_ioaccel2_abort_cmd(struct CommandList *c, struct ctlr_info *h,
- struct CommandList *command_to_abort, int reply_queue)
-{
- struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
- struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2;
- struct io_accel2_cmd *c2a =
- &h->ioaccel2_cmd_pool[command_to_abort->cmdindex];
- struct scsi_cmnd *scmd = command_to_abort->scsi_cmd;
- struct hpsa_scsi_dev_t *dev = scmd->device->hostdata;
- if (!dev)
- return;
-
- /*
- * We're overlaying struct hpsa_tmf_struct on top of something which
- * was allocated as a struct io_accel2_cmd, so we better be sure it
- * actually fits, and doesn't overrun the error info space.
- */
- BUILD_BUG_ON(sizeof(struct hpsa_tmf_struct) >
- sizeof(struct io_accel2_cmd));
- BUG_ON(offsetof(struct io_accel2_cmd, error_data) <
- offsetof(struct hpsa_tmf_struct, error_len) +
- sizeof(ac->error_len));
-
- c->cmd_type = IOACCEL2_TMF;
- c->scsi_cmd = SCSI_CMD_BUSY;
-
- /* Adjust the DMA address to point to the accelerated command buffer */
- c->busaddr = (u32) h->ioaccel2_cmd_pool_dhandle +
- (c->cmdindex * sizeof(struct io_accel2_cmd));
- BUG_ON(c->busaddr & 0x0000007F);
-
- memset(ac, 0, sizeof(*c2)); /* yes this is correct */
- ac->iu_type = IOACCEL2_IU_TMF_TYPE;
- ac->reply_queue = reply_queue;
- ac->tmf = IOACCEL2_TMF_ABORT;
- ac->it_nexus = cpu_to_le32(dev->ioaccel_handle);
- memset(ac->lun_id, 0, sizeof(ac->lun_id));
- ac->tag = cpu_to_le64(c->cmdindex << DIRECT_LOOKUP_SHIFT);
- ac->abort_tag = cpu_to_le64(le32_to_cpu(c2a->Tag));
- ac->error_ptr = cpu_to_le64(c->busaddr +
- offsetof(struct io_accel2_cmd, error_data));
- ac->error_len = cpu_to_le32(sizeof(c2->error_data));
-}
-
-/* ioaccel2 path firmware cannot handle abort task requests.
- * Change abort requests to physical target reset, and send to the
- * address of the physical disk used for the ioaccel 2 command.
- * Return 0 on success (IO_OK)
- * -1 on failure
- */
-
-static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
- unsigned char *scsi3addr, struct CommandList *abort, int reply_queue)
-{
- int rc = IO_OK;
- struct scsi_cmnd *scmd; /* scsi command within request being aborted */
- struct hpsa_scsi_dev_t *dev; /* device to which scsi cmd was sent */
- unsigned char phys_scsi3addr[8]; /* addr of phys disk with volume */
- unsigned char *psa = &phys_scsi3addr[0];
-
- /* Get a pointer to the hpsa logical device. */
- scmd = abort->scsi_cmd;
- dev = (struct hpsa_scsi_dev_t *)(scmd->device->hostdata);
- if (dev == NULL) {
- dev_warn(&h->pdev->dev,
- "Cannot abort: no device pointer for command.\n");
- return -1; /* not abortable */
- }
-
- if (h->raid_offload_debug > 0)
- dev_info(&h->pdev->dev,
- "scsi %d:%d:%d:%d %s scsi3addr 0x%8phN\n",
- h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
- "Reset as abort", scsi3addr);
-
- if (!dev->offload_enabled) {
- dev_warn(&h->pdev->dev,
- "Can't abort: device is not operating in HP SSD Smart Path mode.\n");
- return -1; /* not abortable */
- }
-
- /* Incoming scsi3addr is logical addr. We need physical disk addr. */
- if (!hpsa_get_pdisk_of_ioaccel2(h, abort, psa)) {
- dev_warn(&h->pdev->dev, "Can't abort: Failed lookup of physical address.\n");
- return -1; /* not abortable */
- }
-
- /* send the reset */
- if (h->raid_offload_debug > 0)
- dev_info(&h->pdev->dev,
- "Reset as abort: Resetting physical device at scsi3addr 0x%8phN\n",
- psa);
- rc = hpsa_do_reset(h, dev, psa, HPSA_PHYS_TARGET_RESET, reply_queue);
- if (rc != 0) {
- dev_warn(&h->pdev->dev,
- "Reset as abort: Failed on physical device at scsi3addr 0x%8phN\n",
- psa);
- return rc; /* failed to reset */
- }
-
- /* wait for device to recover */
- if (wait_for_device_to_become_ready(h, psa, reply_queue) != 0) {
- dev_warn(&h->pdev->dev,
- "Reset as abort: Failed: Device never recovered from reset: 0x%8phN\n",
- psa);
- return -1; /* failed to recover */
- }
-
- /* device recovered */
- dev_info(&h->pdev->dev,
- "Reset as abort: Device recovered from reset: scsi3addr 0x%8phN\n",
- psa);
-
- return rc; /* success */
-}
-
-static int hpsa_send_abort_ioaccel2(struct ctlr_info *h,
- struct CommandList *abort, int reply_queue)
-{
- int rc = IO_OK;
- struct CommandList *c;
- __le32 taglower, tagupper;
- struct hpsa_scsi_dev_t *dev;
- struct io_accel2_cmd *c2;
-
- dev = abort->scsi_cmd->device->hostdata;
- if (!dev)
- return -1;
-
- if (!dev->offload_enabled && !dev->hba_ioaccel_enabled)
- return -1;
-
- c = cmd_alloc(h);
- setup_ioaccel2_abort_cmd(c, h, abort, reply_queue);
- c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
- (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, DEFAULT_TIMEOUT);
- hpsa_get_tag(h, abort, &taglower, &tagupper);
- dev_dbg(&h->pdev->dev,
- "%s: Tag:0x%08x:%08x: do_simple_cmd(ioaccel2 abort) completed.\n",
- __func__, tagupper, taglower);
- /* no unmap needed here because no data xfer. */
-
- dev_dbg(&h->pdev->dev,
- "%s: Tag:0x%08x:%08x: abort service response = 0x%02x.\n",
- __func__, tagupper, taglower, c2->error_data.serv_response);
- switch (c2->error_data.serv_response) {
- case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE:
- case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS:
- rc = 0;
- break;
- case IOACCEL2_SERV_RESPONSE_TMF_REJECTED:
- case IOACCEL2_SERV_RESPONSE_FAILURE:
- case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN:
- rc = -1;
- break;
- default:
- dev_warn(&h->pdev->dev,
- "%s: Tag:0x%08x:%08x: unknown abort service response 0x%02x\n",
- __func__, tagupper, taglower,
- c2->error_data.serv_response);
- rc = -1;
- }
- cmd_free(h, c);
- dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: Finished.\n", __func__,
- tagupper, taglower);
+return_reset_status:
+ spin_lock_irqsave(&h->reset_lock, flags);
+ h->reset_in_progress = 0;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
return rc;
}
-static int hpsa_send_abort_both_ways(struct ctlr_info *h,
- struct hpsa_scsi_dev_t *dev, struct CommandList *abort, int reply_queue)
-{
- /*
- * ioccelerator mode 2 commands should be aborted via the
- * accelerated path, since RAID path is unaware of these commands,
- * but not all underlying firmware can handle abort TMF.
- * Change abort to physical device reset when abort TMF is unsupported.
- */
- if (abort->cmd_type == CMD_IOACCEL2) {
- if ((HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags) ||
- dev->physical_device)
- return hpsa_send_abort_ioaccel2(h, abort,
- reply_queue);
- else
- return hpsa_send_reset_as_abort_ioaccel2(h,
- dev->scsi3addr,
- abort, reply_queue);
- }
- return hpsa_send_abort(h, dev->scsi3addr, abort, reply_queue);
-}
-
-/* Find out which reply queue a command was meant to return on */
-static int hpsa_extract_reply_queue(struct ctlr_info *h,
- struct CommandList *c)
-{
- if (c->cmd_type == CMD_IOACCEL2)
- return h->ioaccel2_cmd_pool[c->cmdindex].reply_queue;
- return c->Header.ReplyQueue;
-}
-
-/*
- * Limit concurrency of abort commands to prevent
- * over-subscription of commands
- */
-static inline int wait_for_available_abort_cmd(struct ctlr_info *h)
-{
-#define ABORT_CMD_WAIT_MSECS 5000
- return !wait_event_timeout(h->abort_cmd_wait_queue,
- atomic_dec_if_positive(&h->abort_cmds_available) >= 0,
- msecs_to_jiffies(ABORT_CMD_WAIT_MSECS));
-}
-
-/* Send an abort for the specified command.
- * If the device and controller support it,
- * send a task abort request.
- */
-static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
-{
-
- int rc;
- struct ctlr_info *h;
- struct hpsa_scsi_dev_t *dev;
- struct CommandList *abort; /* pointer to command to be aborted */
- struct scsi_cmnd *as; /* ptr to scsi cmd inside aborted command. */
- char msg[256]; /* For debug messaging. */
- int ml = 0;
- __le32 tagupper, taglower;
- int refcount, reply_queue;
-
- if (sc == NULL)
- return FAILED;
-
- if (sc->device == NULL)
- return FAILED;
-
- /* Find the controller of the command to be aborted */
- h = sdev_to_hba(sc->device);
- if (h == NULL)
- return FAILED;
-
- /* Find the device of the command to be aborted */
- dev = sc->device->hostdata;
- if (!dev) {
- dev_err(&h->pdev->dev, "%s FAILED, Device lookup failed.\n",
- msg);
- return FAILED;
- }
-
- /* If controller locked up, we can guarantee command won't complete */
- if (lockup_detected(h)) {
- hpsa_show_dev_msg(KERN_WARNING, h, dev,
- "ABORT FAILED, lockup detected");
- return FAILED;
- }
-
- /* This is a good time to check if controller lockup has occurred */
- if (detect_controller_lockup(h)) {
- hpsa_show_dev_msg(KERN_WARNING, h, dev,
- "ABORT FAILED, new lockup detected");
- return FAILED;
- }
-
- /* Check that controller supports some kind of task abort */
- if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) &&
- !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
- return FAILED;
-
- memset(msg, 0, sizeof(msg));
- ml += sprintf(msg+ml, "scsi %d:%d:%d:%llu %s %p",
- h->scsi_host->host_no, sc->device->channel,
- sc->device->id, sc->device->lun,
- "Aborting command", sc);
-
- /* Get SCSI command to be aborted */
- abort = (struct CommandList *) sc->host_scribble;
- if (abort == NULL) {
- /* This can happen if the command already completed. */
- return SUCCESS;
- }
- refcount = atomic_inc_return(&abort->refcount);
- if (refcount == 1) { /* Command is done already. */
- cmd_free(h, abort);
- return SUCCESS;
- }
-
- /* Don't bother trying the abort if we know it won't work. */
- if (abort->cmd_type != CMD_IOACCEL2 &&
- abort->cmd_type != CMD_IOACCEL1 && !dev->supports_aborts) {
- cmd_free(h, abort);
- return FAILED;
- }
-
- /*
- * Check that we're aborting the right command.
- * It's possible the CommandList already completed and got re-used.
- */
- if (abort->scsi_cmd != sc) {
- cmd_free(h, abort);
- return SUCCESS;
- }
-
- abort->abort_pending = true;
- hpsa_get_tag(h, abort, &taglower, &tagupper);
- reply_queue = hpsa_extract_reply_queue(h, abort);
- ml += sprintf(msg+ml, "Tag:0x%08x:%08x ", tagupper, taglower);
- as = abort->scsi_cmd;
- if (as != NULL)
- ml += sprintf(msg+ml,
- "CDBLen: %d CDB: 0x%02x%02x... SN: 0x%lx ",
- as->cmd_len, as->cmnd[0], as->cmnd[1],
- as->serial_number);
- dev_warn(&h->pdev->dev, "%s BEING SENT\n", msg);
- hpsa_show_dev_msg(KERN_WARNING, h, dev, "Aborting command");
-
- /*
- * Command is in flight, or possibly already completed
- * by the firmware (but not to the scsi mid layer) but we can't
- * distinguish which. Send the abort down.
- */
- if (wait_for_available_abort_cmd(h)) {
- dev_warn(&h->pdev->dev,
- "%s FAILED, timeout waiting for an abort command to become available.\n",
- msg);
- cmd_free(h, abort);
- return FAILED;
- }
- rc = hpsa_send_abort_both_ways(h, dev, abort, reply_queue);
- atomic_inc(&h->abort_cmds_available);
- wake_up_all(&h->abort_cmd_wait_queue);
- if (rc != 0) {
- dev_warn(&h->pdev->dev, "%s SENT, FAILED\n", msg);
- hpsa_show_dev_msg(KERN_WARNING, h, dev,
- "FAILED to abort command");
- cmd_free(h, abort);
- return FAILED;
- }
- dev_info(&h->pdev->dev, "%s SENT, SUCCESS\n", msg);
- wait_event(h->event_sync_wait_queue,
- abort->scsi_cmd != sc || lockup_detected(h));
- cmd_free(h, abort);
- return !lockup_detected(h) ? SUCCESS : FAILED;
-}
-
/*
* For operations with an associated SCSI command, a command block is allocated
* at init, and managed by cmd_tagged_alloc() and cmd_tagged_free() using the
@@ -6364,9 +5883,7 @@ static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c)
{
/*
* Release our reference to the block. We don't need to do anything
- * else to free it, because it is accessed by index. (There's no point
- * in checking the result of the decrement, since we cannot guarantee
- * that there isn't a concurrent abort which is also accessing it.)
+ * else to free it, because it is accessed by index.
*/
(void)atomic_dec(&c->refcount);
}
@@ -6905,7 +6422,6 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
int cmd_type)
{
int pci_dir = XFER_NONE;
- u64 tag; /* for commands to be aborted */
c->cmd_type = CMD_IOCTL_PEND;
c->scsi_cmd = SCSI_CMD_BUSY;
@@ -7089,27 +6605,6 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
c->Request.CDB[6] = 0x00;
c->Request.CDB[7] = 0x00;
break;
- case HPSA_ABORT_MSG:
- memcpy(&tag, buff, sizeof(tag));
- dev_dbg(&h->pdev->dev,
- "Abort Tag:0x%016llx using rqst Tag:0x%016llx",
- tag, c->Header.tag);
- c->Request.CDBLen = 16;
- c->Request.type_attr_dir =
- TYPE_ATTR_DIR(cmd_type,
- ATTR_SIMPLE, XFER_WRITE);
- c->Request.Timeout = 0; /* Don't time out */
- c->Request.CDB[0] = HPSA_TASK_MANAGEMENT;
- c->Request.CDB[1] = HPSA_TMF_ABORT_TASK;
- c->Request.CDB[2] = 0x00; /* reserved */
- c->Request.CDB[3] = 0x00; /* reserved */
- /* Tag to abort goes in CDB[4]-CDB[11] */
- memcpy(&c->Request.CDB[4], &tag, sizeof(tag));
- c->Request.CDB[12] = 0x00; /* reserved */
- c->Request.CDB[13] = 0x00; /* reserved */
- c->Request.CDB[14] = 0x00; /* reserved */
- c->Request.CDB[15] = 0x00; /* reserved */
- break;
default:
dev_warn(&h->pdev->dev, "unknown message type %d\n",
cmd);
@@ -8067,9 +7562,6 @@ static int hpsa_pci_init(struct ctlr_info *h)
h->product_name = products[prod_index].product_name;
h->access = *(products[prod_index].access);
- h->needs_abort_tags_swizzled =
- ctlr_needs_abort_tags_swizzled(h->board_id);
-
pci_disable_link_state(h->pdev, PCIE_LINK_STATE_L0S |
PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
@@ -8627,41 +8119,79 @@ out:
return rc;
}
-static void hpsa_rescan_ctlr_worker(struct work_struct *work)
+static void hpsa_perform_rescan(struct ctlr_info *h)
{
+ struct Scsi_Host *sh = NULL;
unsigned long flags;
- struct ctlr_info *h = container_of(to_delayed_work(work),
- struct ctlr_info, rescan_ctlr_work);
-
-
- if (h->remove_in_progress)
- return;
/*
* Do the scan after the reset
*/
+ spin_lock_irqsave(&h->reset_lock, flags);
if (h->reset_in_progress) {
h->drv_req_rescan = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
return;
}
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+
+ sh = scsi_host_get(h->scsi_host);
+ if (sh != NULL) {
+ hpsa_scan_start(sh);
+ scsi_host_put(sh);
+ h->drv_req_rescan = 0;
+ }
+}
+
+/*
+ * watch for controller events
+ */
+static void hpsa_event_monitor_worker(struct work_struct *work)
+{
+ struct ctlr_info *h = container_of(to_delayed_work(work),
+ struct ctlr_info, event_monitor_work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&h->lock, flags);
+ if (h->remove_in_progress) {
+ spin_unlock_irqrestore(&h->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
- if (hpsa_ctlr_needs_rescan(h) || hpsa_offline_devices_ready(h)) {
- scsi_host_get(h->scsi_host);
+ if (hpsa_ctlr_needs_rescan(h)) {
hpsa_ack_ctlr_events(h);
- hpsa_scan_start(h->scsi_host);
- scsi_host_put(h->scsi_host);
+ hpsa_perform_rescan(h);
+ }
+
+ spin_lock_irqsave(&h->lock, flags);
+ if (!h->remove_in_progress)
+ schedule_delayed_work(&h->event_monitor_work,
+ HPSA_EVENT_MONITOR_INTERVAL);
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static void hpsa_rescan_ctlr_worker(struct work_struct *work)
+{
+ unsigned long flags;
+ struct ctlr_info *h = container_of(to_delayed_work(work),
+ struct ctlr_info, rescan_ctlr_work);
+
+ spin_lock_irqsave(&h->lock, flags);
+ if (h->remove_in_progress) {
+ spin_unlock_irqrestore(&h->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ if (h->drv_req_rescan || hpsa_offline_devices_ready(h)) {
+ hpsa_perform_rescan(h);
} else if (h->discovery_polling) {
hpsa_disable_rld_caching(h);
if (hpsa_luns_changed(h)) {
- struct Scsi_Host *sh = NULL;
-
dev_info(&h->pdev->dev,
"driver discovery polling rescan.\n");
- sh = scsi_host_get(h->scsi_host);
- if (sh != NULL) {
- hpsa_scan_start(sh);
- scsi_host_put(sh);
- }
+ hpsa_perform_rescan(h);
}
}
spin_lock_irqsave(&h->lock, flags);
@@ -8750,8 +8280,8 @@ reinit_after_soft_reset:
spin_lock_init(&h->lock);
spin_lock_init(&h->offline_device_lock);
spin_lock_init(&h->scan_lock);
+ spin_lock_init(&h->reset_lock);
atomic_set(&h->passthru_cmds_avail, HPSA_MAX_CONCURRENT_PASSTHRUS);
- atomic_set(&h->abort_cmds_available, HPSA_CMDS_RESERVED_FOR_ABORTS);
/* Allocate and clear per-cpu variable lockup_detected */
h->lockup_detected = alloc_percpu(u32);
@@ -8803,7 +8333,6 @@ reinit_after_soft_reset:
if (rc)
goto clean5; /* cmd, irq, shost, pci, lu, aer/h */
init_waitqueue_head(&h->scan_wait_queue);
- init_waitqueue_head(&h->abort_cmd_wait_queue);
init_waitqueue_head(&h->event_sync_wait_queue);
mutex_init(&h->reset_mutex);
h->scan_finished = 1; /* no scan currently in progress */
@@ -8926,6 +8455,9 @@ reinit_after_soft_reset:
INIT_DELAYED_WORK(&h->rescan_ctlr_work, hpsa_rescan_ctlr_worker);
queue_delayed_work(h->rescan_ctlr_wq, &h->rescan_ctlr_work,
h->heartbeat_sample_interval);
+ INIT_DELAYED_WORK(&h->event_monitor_work, hpsa_event_monitor_worker);
+ schedule_delayed_work(&h->event_monitor_work,
+ HPSA_EVENT_MONITOR_INTERVAL);
return 0;
clean7: /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
@@ -9094,6 +8626,7 @@ static void hpsa_remove_one(struct pci_dev *pdev)
spin_unlock_irqrestore(&h->lock, flags);
cancel_delayed_work_sync(&h->monitor_ctlr_work);
cancel_delayed_work_sync(&h->rescan_ctlr_work);
+ cancel_delayed_work_sync(&h->event_monitor_work);
destroy_workqueue(h->rescan_ctlr_wq);
destroy_workqueue(h->resubmit_wq);