diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-05 15:32:53 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-05 15:32:53 -0700 |
commit | cd3f07d1e6e59fade92a8edaff9315cc534199cf (patch) | |
tree | a2f30bccdfad1826a81c098c6766c2a4345fc171 /drivers | |
parent | dc822e57ee05856291a8c9324d2309076ee5f5e2 (diff) | |
parent | f7c9c6bb14f3104608a3a83cadea10a6943d2804 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (45 commits)
[SCSI] Fix block queue and elevator memory leak in scsi_alloc_sdev
[SCSI] scsi_dh_alua: Fix the time inteval for alua rtpg commands
[SCSI] scsi_transport_iscsi: Fix documentation os parameter
[SCSI] mv_sas: OCZ RevoDrive3 & zDrive R4 support
[SCSI] libfc: improve flogi retries to avoid lport stuck
[SCSI] libfc: avoid exchanges collision during lport reset
[SCSI] libfc: fix checking FC_TYPE_BLS
[SCSI] edd: Treat "XPRS" host bus type the same as "PCI"
[SCSI] isci: overriding max_concurr_spinup oem parameter by max(oem, user)
[SCSI] isci: revert bcn filtering
[SCSI] isci: Fix hard reset timeout conditions.
[SCSI] isci: No need to manage the pending reset bit on pending requests.
[SCSI] isci: Remove redundant isci_request.ttype field.
[SCSI] isci: Fix task management for SMP, SATA and on dev remove.
[SCSI] isci: No task_done callbacks in error handler paths.
[SCSI] isci: Handle task request timeouts correctly.
[SCSI] isci: Fix tag leak in tasks and terminated requests.
[SCSI] isci: Immediately fail I/O to removed devices.
[SCSI] isci: Lookup device references through requests in completions.
[SCSI] ipr: add definitions for additional adapter
...
Diffstat (limited to 'drivers')
44 files changed, 1754 insertions, 1076 deletions
diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c index f1b7f659d3c..e2295766580 100644 --- a/drivers/firmware/edd.c +++ b/drivers/firmware/edd.c @@ -151,7 +151,8 @@ edd_show_host_bus(struct edd_device *edev, char *buf) p += scnprintf(p, left, "\tbase_address: %x\n", info->params.interface_path.isa.base_address); } else if (!strncmp(info->params.host_bus_type, "PCIX", 4) || - !strncmp(info->params.host_bus_type, "PCI", 3)) { + !strncmp(info->params.host_bus_type, "PCI", 3) || + !strncmp(info->params.host_bus_type, "XPRS", 4)) { p += scnprintf(p, left, "\t%02x:%02x.%d channel: %u\n", info->params.interface_path.pci.bus, @@ -159,7 +160,6 @@ edd_show_host_bus(struct edd_device *edev, char *buf) info->params.interface_path.pci.function, info->params.interface_path.pci.channel); } else if (!strncmp(info->params.host_bus_type, "IBND", 4) || - !strncmp(info->params.host_bus_type, "XPRS", 4) || !strncmp(info->params.host_bus_type, "HTPT", 4)) { p += scnprintf(p, left, "\tTBD: %llx\n", @@ -668,7 +668,7 @@ edd_get_pci_dev(struct edd_device *edev) { struct edd_info *info = edd_dev_get_info(edev); - if (edd_dev_is_type(edev, "PCI")) { + if (edd_dev_is_type(edev, "PCI") || edd_dev_is_type(edev, "XPRS")) { return pci_get_bus_and_slot(info->params.interface_path.pci.bus, PCI_DEVFN(info->params.interface_path.pci.slot, info->params.interface_path.pci. diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index 63de1c7cd0c..049ea907e04 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -62,7 +62,7 @@ #include "bnx2fc_constants.h" #define BNX2FC_NAME "bnx2fc" -#define BNX2FC_VERSION "1.0.8" +#define BNX2FC_VERSION "1.0.9" #define PFX "bnx2fc: " @@ -145,6 +145,9 @@ #define REC_RETRY_COUNT 1 #define BNX2FC_NUM_ERR_BITS 63 +#define BNX2FC_RELOGIN_WAIT_TIME 200 +#define BNX2FC_RELOGIN_WAIT_CNT 10 + /* bnx2fc driver uses only one instance of fcoe_percpu_s */ extern struct fcoe_percpu_s bnx2fc_global; diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c index fd382fe33f6..ce0ce3e32f3 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_els.c +++ b/drivers/scsi/bnx2fc/bnx2fc_els.c @@ -268,17 +268,6 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) orig_io_req = cb_arg->aborted_io_req; srr_req = cb_arg->io_req; - if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) { - BNX2FC_IO_DBG(srr_req, "srr_compl: xid - 0x%x completed", - orig_io_req->xid); - goto srr_compl_done; - } - if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { - BNX2FC_IO_DBG(srr_req, "rec abts in prog " - "orig_io - 0x%x\n", - orig_io_req->xid); - goto srr_compl_done; - } if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &srr_req->req_flags)) { /* SRR timedout */ BNX2FC_IO_DBG(srr_req, "srr timed out, abort " @@ -290,6 +279,12 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) "failed. issue cleanup\n"); bnx2fc_initiate_cleanup(srr_req); } + if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || + test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { + BNX2FC_IO_DBG(srr_req, "srr_compl:xid 0x%x flags = %lx", + orig_io_req->xid, orig_io_req->req_flags); + goto srr_compl_done; + } orig_io_req->srr_retry++; if (orig_io_req->srr_retry <= SRR_RETRY_COUNT) { struct bnx2fc_rport *tgt = orig_io_req->tgt; @@ -311,6 +306,12 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) } goto srr_compl_done; } + if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || + test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { + BNX2FC_IO_DBG(srr_req, "srr_compl:xid - 0x%x flags = %lx", + orig_io_req->xid, orig_io_req->req_flags); + goto srr_compl_done; + } mp_req = &(srr_req->mp_req); fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 85bcc4b5596..8c6156a10d9 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -22,7 +22,7 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu); #define DRV_MODULE_NAME "bnx2fc" #define DRV_MODULE_VERSION BNX2FC_VERSION -#define DRV_MODULE_RELDATE "Oct 02, 2011" +#define DRV_MODULE_RELDATE "Oct 21, 2011" static char version[] __devinitdata = diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 0c64d184d73..84a78af83f9 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1103,7 +1103,10 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) struct fc_rport_libfc_priv *rp = rport->dd_data; struct bnx2fc_cmd *io_req; struct fc_lport *lport; + struct fc_rport_priv *rdata; struct bnx2fc_rport *tgt; + int logo_issued; + int wait_cnt = 0; int rc = FAILED; @@ -1192,8 +1195,40 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) } else { printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) " "already in abts processing\n", io_req->xid); + if (cancel_delayed_work(&io_req->timeout_work)) + kref_put(&io_req->refcount, + bnx2fc_cmd_release); /* drop timer hold */ + bnx2fc_initiate_cleanup(io_req); + + spin_unlock_bh(&tgt->tgt_lock); + + wait_for_completion(&io_req->tm_done); + + spin_lock_bh(&tgt->tgt_lock); + io_req->wait_for_comp = 0; + rdata = io_req->tgt->rdata; + logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO, + &tgt->flags); kref_put(&io_req->refcount, bnx2fc_cmd_release); spin_unlock_bh(&tgt->tgt_lock); + + if (!logo_issued) { + BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n", + tgt->flags); + mutex_lock(&lport->disc.disc_mutex); + lport->tt.rport_logoff(rdata); + mutex_unlock(&lport->disc.disc_mutex); + do { + msleep(BNX2FC_RELOGIN_WAIT_TIME); + /* + * If session not recovered, let SCSI-ml + * escalate error recovery. + */ + if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT) + return FAILED; + } while (!test_bit(BNX2FC_FLAG_SESSION_READY, + &tgt->flags)); + } return SUCCESS; } if (rc == FAILED) { @@ -1275,6 +1310,8 @@ void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req, io_req->refcount.refcount.counter, io_req->cmd_type); bnx2fc_scsi_done(io_req, DID_ERROR); kref_put(&io_req->refcount, bnx2fc_cmd_release); + if (io_req->wait_for_comp) + complete(&io_req->tm_done); } void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req, diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 7c05fd9dccf..339ea23a867 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -441,7 +441,15 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) spin_lock_irqsave(q->queue_lock, flags); sdev = q->queuedata; - if (sdev && sdev->scsi_dh_data) + if (!sdev) { + spin_unlock_irqrestore(q->queue_lock, flags); + err = SCSI_DH_NOSYS; + if (fn) + fn(data, err); + return err; + } + + if (sdev->scsi_dh_data) scsi_dh = sdev->scsi_dh_data->scsi_dh; dev = get_device(&sdev->sdev_gendev); if (!scsi_dh || !dev || diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 627f4b5e517..fe4df2da309 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -507,7 +507,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) int len, k, off, valid_states = 0; unsigned char *ucp; unsigned err; - unsigned long expiry, interval = 1; + unsigned long expiry, interval = 1000; expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT); retry: @@ -734,6 +734,7 @@ static int alua_bus_attach(struct scsi_device *sdev) spin_lock_irqsave(sdev->request_queue->queue_lock, flags); sdev->scsi_dh_data = scsi_dh_data; spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME); return 0; diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 61384ee4049..cefbe44bb84 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -2347,14 +2347,11 @@ static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) goto done; mac = fr_cb(fp)->granted_mac; - if (is_zero_ether_addr(mac)) { - /* pre-FIP */ - if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { - fc_frame_free(fp); - return; - } - } - fcoe_update_src_mac(lport, mac); + /* pre-FIP */ + if (is_zero_ether_addr(mac)) + fcoe_ctlr_recv_flogi(fip, lport, fp); + if (!is_zero_ether_addr(mac)) + fcoe_update_src_mac(lport, mac); done: fc_lport_flogi_resp(seq, fp, lport); } diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 4f7a5829ea4..351dc0b86fa 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -286,6 +286,7 @@ static void scsi_host_dev_release(struct device *dev) { struct Scsi_Host *shost = dev_to_shost(dev); struct device *parent = dev->parent; + struct request_queue *q; scsi_proc_hostdir_rm(shost->hostt); @@ -293,9 +294,11 @@ static void scsi_host_dev_release(struct device *dev) kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); - if (shost->uspace_req_q) { - kfree(shost->uspace_req_q->queuedata); - scsi_free_queue(shost->uspace_req_q); + q = shost->uspace_req_q; + if (q) { + kfree(q->queuedata); + q->queuedata = NULL; + scsi_free_queue(q); } scsi_destroy_command_freelist(shost); diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index bbdc9f960a6..e76107b2ade 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -48,6 +48,7 @@ #include <linux/bitmap.h> #include <linux/atomic.h> #include <linux/kthread.h> +#include <linux/jiffies.h> #include "hpsa_cmd.h" #include "hpsa.h" @@ -127,6 +128,10 @@ static struct board_type products[] = { static int number_of_controllers; +static struct list_head hpsa_ctlr_list = LIST_HEAD_INIT(hpsa_ctlr_list); +static spinlock_t lockup_detector_lock; +static struct task_struct *hpsa_lockup_detector; + static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id); static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id); static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg); @@ -484,6 +489,7 @@ static struct scsi_host_template hpsa_driver_template = { #endif .sdev_attrs = hpsa_sdev_attrs, .shost_attrs = hpsa_shost_attrs, + .max_sectors = 8192, }; @@ -566,16 +572,16 @@ static int hpsa_find_target_lun(struct ctlr_info *h, * assumes h->devlock is held */ int i, found = 0; - DECLARE_BITMAP(lun_taken, HPSA_MAX_SCSI_DEVS_PER_HBA); + DECLARE_BITMAP(lun_taken, HPSA_MAX_DEVICES); - memset(&lun_taken[0], 0, HPSA_MAX_SCSI_DEVS_PER_HBA >> 3); + memset(&lun_taken[0], 0, HPSA_MAX_DEVICES >> 3); for (i = 0; i < h->ndevices; i++) { if (h->dev[i]->bus == bus && h->dev[i]->target != -1) set_bit(h->dev[i]->target, lun_taken); } - for (i = 0; i < HPSA_MAX_SCSI_DEVS_PER_HBA; i++) { + for (i = 0; i < HPSA_MAX_DEVICES; i++) { if (!test_bit(i, lun_taken)) { /* *bus = 1; */ *target = i; @@ -598,7 +604,7 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno, unsigned char addr1[8], addr2[8]; struct hpsa_scsi_dev_t *sd; - if (n >= HPSA_MAX_SCSI_DEVS_PER_HBA) { + if (n >= HPSA_MAX_DEVICES) { dev_err(&h->pdev->dev, "too many devices, some will be " "inaccessible.\n"); return -1; @@ -673,7 +679,7 @@ static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno, struct hpsa_scsi_dev_t *removed[], int *nremoved) { /* assumes h->devlock is held */ - BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA); + BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES); removed[*nremoved] = h->dev[entry]; (*nremoved)++; @@ -702,7 +708,7 @@ static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry, int i; struct hpsa_scsi_dev_t *sd; - BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA); + BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES); sd = h->dev[entry]; removed[*nremoved] = h->dev[entry]; @@ -814,10 +820,8 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, int nadded, nremoved; struct Scsi_Host *sh = NULL; - added = kzalloc(sizeof(*added) * HPSA_MAX_SCSI_DEVS_PER_HBA, - GFP_KERNEL); - removed = kzalloc(sizeof(*removed) * HPSA_MAX_SCSI_DEVS_PER_HBA, - GFP_KERNEL); + added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL); + removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL); if (!added || !removed) { dev_warn(&h->pdev->dev, "out of memory in " @@ -1338,6 +1342,22 @@ static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h, wait_for_completion(&wait); } +static void hpsa_scsi_do_simple_cmd_core_if_no_lockup(struct ctlr_info *h, + struct CommandList *c) +{ + unsigned long flags; + + /* If controller lockup detected, fake a hardware error. */ + spin_lock_irqsave(&h->lock, flags); + if (unlikely(h->lockup_detected)) { + spin_unlock_irqrestore(&h->lock, flags); + c->err_info->CommandStatus = CMD_HARDWARE_ERR; + } else { + spin_unlock_irqrestore(&h->lock, flags); + hpsa_scsi_do_simple_cmd_core(h, c); + } +} + static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h, struct CommandList *c, int data_direction) { @@ -1735,7 +1755,6 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h, if (is_scsi_rev_5(h)) return 0; /* p1210m doesn't need to do this. */ -#define MAX_MSA2XXX_ENCLOSURES 32 if (*nmsa2xxx_enclosures >= MAX_MSA2XXX_ENCLOSURES) { dev_warn(&h->pdev->dev, "Maximum number of MSA2XXX " "enclosures exceeded. Check your hardware " @@ -1846,8 +1865,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) int raid_ctlr_position; DECLARE_BITMAP(lunzerobits, HPSA_MAX_TARGETS_PER_CTLR); - currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_SCSI_DEVS_PER_HBA, - GFP_KERNEL); + currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL); physdev_list = kzalloc(reportlunsize, GFP_KERNEL); logdev_list = kzalloc(reportlunsize, GFP_KERNEL); tmpdevice = kzalloc(sizeof(*tmpdevice), GFP_KERNEL); @@ -1870,6 +1888,13 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) /* Allocate the per device structures */ for (i = 0; i < ndevs_to_allocate; i++) { + if (i >= HPSA_MAX_DEVICES) { + dev_warn(&h->pdev->dev, "maximum devices (%d) exceeded." + " %d devices ignored.\n", HPSA_MAX_DEVICES, + ndevs_to_allocate - HPSA_MAX_DEVICES); + break; + } + currentsd[i] = kzalloc(sizeof(*currentsd[i]), GFP_KERNEL); if (!currentsd[i]) { dev_warn(&h->pdev->dev, "out of memory at %s:%d\n", @@ -1956,7 +1981,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) default: break; } - if (ncurrent >= HPSA_MAX_SCSI_DEVS_PER_HBA) + if (ncurrent >= HPSA_MAX_DEVICES) break; } adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent); @@ -2048,8 +2073,14 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, } memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr)); - /* Need a lock as this is being allocated from the pool */ spin_lock_irqsave(&h->lock, flags); + if (unlikely(h->lockup_detected)) { + spin_unlock_irqrestore(&h->lock, flags); + cmd->result = DID_ERROR << 16; + done(cmd); + return 0; + } + /* Need a lock as this is being allocated from the pool */ c = cmd_alloc(h); spin_unlock_irqrestore(&h->lock, flags); if (c == NULL) { /* trouble... */ @@ -2601,7 +2632,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) c->SG[0].Len = iocommand.buf_size; c->SG[0].Ext = 0; /* we are not chaining*/ } - hpsa_scsi_do_simple_cmd_core(h, c); + hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); if (iocommand.buf_size > 0) hpsa_pci_unmap(h->pdev, c, 1, PCI_DMA_BIDIRECTIONAL); check_ioctl_unit_attention(h, c); @@ -2724,7 +2755,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) c->SG[i].Ext = 0; } } - hpsa_scsi_do_simple_cmd_core(h, c); + hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); if (sg_used) hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL); check_ioctl_unit_attention(h, c); @@ -2872,6 +2903,8 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.Timeout = 0; c->Request.CDB[0] = BMIC_WRITE; c->Request.CDB[6] = BMIC_CACHE_FLUSH; + c->Request.CDB[7] = (size >> 8) & 0xFF; + c->Request.CDB[8] = size & 0xFF; break; case TEST_UNIT_READY: c->Request.CDBLen = 6; @@ -3091,6 +3124,7 @@ static irqreturn_t hpsa_intx_discard_completions(int irq, void *dev_id) if (interrupt_not_for_us(h)) return IRQ_NONE; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); while (interrupt_pending(h)) { raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) @@ -3110,6 +3144,7 @@ static irqreturn_t hpsa_msix_discard_completions(int irq, void *dev_id) return IRQ_NONE; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) raw_tag = next_command(h); @@ -3126,6 +3161,7 @@ static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id) if (interrupt_not_for_us(h)) return IRQ_NONE; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); while (interrupt_pending(h)) { raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) { @@ -3146,6 +3182,7 @@ static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id) u32 raw_tag; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) { if (hpsa_tag_contains_index(raw_tag)) @@ -4090,6 +4127,149 @@ static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h) kfree(h); } +static void remove_ctlr_from_lockup_detector_list(struct ctlr_info *h) +{ + assert_spin_locked(&lockup_detector_lock); + if (!hpsa_lockup_detector) + return; + if (h->lockup_detected) + return; /* already stopped the lockup detector */ + list_del(&h->lockup_list); +} + +/* Called when controller lockup detected. */ +static void fail_all_cmds_on_list(struct ctlr_info *h, struct list_head *list) +{ + struct CommandList *c = NULL; + + assert_spin_locked(&h->lock); + /* Mark all outstanding commands as failed and complete them. */ + while (!list_empty(list)) { + c = list_entry(list->next, struct CommandList, list); + c->err_info->CommandStatus = CMD_HARDWARE_ERR; + finish_cmd(c, c->Header.Tag.lower); + } +} + +static void controller_lockup_detected(struct ctlr_info *h) +{ + unsigned long flags; + + assert_spin_locked(&lockup_detector_lock); + remove_ctlr_from_lockup_detector_list(h); + h->access.set_intr_mask(h, HPSA_INTR_OFF); + spin_lock_irqsave(&h->lock, flags); + h->lockup_detected = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET); + spin_unlock_irqrestore(&h->lock, flags); + dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x\n", + h->lockup_detected); + pci_disable_device(h->pdev); + spin_lock_irqsave(&h->lock, flags); + fail_all_cmds_on_list(h, &h->cmpQ); + fail_all_cmds_on_list(h, &h->reqQ); + spin_unlock_irqrestore(&h->lock, flags); +} + +#define HEARTBEAT_SAMPLE_INTERVAL (10 * HZ) +#define HEARTBEAT_CHECK_MINIMUM_INTERVAL (HEARTBEAT_SAMPLE_INTERVAL / 2) + +static void detect_controller_lockup(struct ctlr_info *h) +{ + u64 now; + u32 heartbeat; + unsigned long flags; + + assert_spin_locked(&lockup_detector_lock); + now = get_jiffies_64(); + /* If we've received an interrupt recently, we're ok. */ + if (time_after64(h->last_intr_timestamp + + (HEARTBEAT_CHECK_MINIMUM_INTERVAL), now)) + return; + + /* + * If we've already checked the heartbeat recently, we're ok. + * This could happen if someone sends us a signal. We + * otherwise don't care about signals in this thread. + */ + if (time_after64(h->last_heartbeat_timestamp + + (HEARTBEAT_CHECK_MINIMUM_INTERVAL), now)) + return; + + /* If heartbeat has not changed since we last looked, we're not ok. */ + spin_lock_irqsave(&h->lock, flags); + heartbeat = readl(&h->cfgtable->HeartBeat); + spin_unlock_irqrestore(&h->lock, flags); + if (h->last_heartbeat == heartbeat) { + controller_lockup_detected(h); + return; + } + + /* We're ok. */ + h->last_heartbeat = heartbeat; + h->last_heartbeat_timestamp = now; +} + +static int detect_controller_lockup_thread(void *notused) +{ + struct ctlr_info *h; + unsigned long flags; + + while (1) { + struct list_head *this, *tmp; + + schedule_timeout_interruptible(HEARTBEAT_SAMPLE_INTERVAL); + if (kthread_should_stop()) + break; + spin_lock_irqsave(&lockup_detector_lock, flags); + list_for_each_safe(this, tmp, &hpsa_ctlr_list) { + h = list_entry(this, struct ctlr_info, lockup_list); + detect_controller_lockup(h); + } + spin_unlock_irqrestore(&lockup_detector_lock, flags); + } + return 0; +} + +static void add_ctlr_to_lockup_detector_list(struct ctlr_info *h) +{ + unsigned long flags; + + spin_lock_irqsave(&lockup_detector_lock, flags); + list_add_tail(&h->lockup_list, &hpsa_ctlr_list); + spin_unlock_irqrestore(&lockup_detector_lock, flags); +} + +static void start_controller_lockup_detector(struct ctlr_info *h) +{ + /* Start the lockup detector thread if not already started */ + if (!hpsa_lockup_detector) { + spin_lock_init(&lockup_detector_lock); + hpsa_lockup_detector = + kthread_run(detect_controller_lockup_thread, + NULL, "hpsa"); + } + if (!hpsa_lockup_detector) { + dev_warn(&h->pdev->dev, + "Could not start lockup detector thread\n"); + return; + } + add_ctlr_to_lockup_detector_list(h); +} + +static void stop_controller_lockup_detector(struct ctlr_info *h) +{ + unsigned long flags; + + spin_lock_irqsave(&lockup_detector_lock, flags); + remove_ctlr_from_lockup_detector_list(h); + /* If the list of ctlr's to monitor is empty, stop the thread */ + if (list_empty(&hpsa_ctlr_list)) { + kthread_stop(hpsa_lockup_detector); + hpsa_lockup_detector = NULL; + } + spin_unlock |