|
@@ -64,6 +64,10 @@ MODULE_DESCRIPTION("Driver for HP Smart Array Controllers");
|
|
|
MODULE_SUPPORTED_DEVICE("HP Smart Array Controllers");
|
|
|
MODULE_VERSION("3.6.26");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
+static int cciss_tape_cmds = 6;
|
|
|
+module_param(cciss_tape_cmds, int, 0644);
|
|
|
+MODULE_PARM_DESC(cciss_tape_cmds,
|
|
|
+ "number of commands to allocate for tape devices (default: 6)");
|
|
|
|
|
|
static DEFINE_MUTEX(cciss_mutex);
|
|
|
static struct proc_dir_entry *proc_cciss;
|
|
@@ -194,6 +198,8 @@ static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev,
|
|
|
static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev,
|
|
|
unsigned long *memory_bar);
|
|
|
static inline u32 cciss_tag_discard_error_bits(ctlr_info_t *h, u32 tag);
|
|
|
+static __devinit int write_driver_ver_to_cfgtable(
|
|
|
+ CfgTable_struct __iomem *cfgtable);
|
|
|
|
|
|
/* performant mode helper functions */
|
|
|
static void calc_bucket_map(int *bucket, int num_buckets, int nsgs,
|
|
@@ -556,7 +562,7 @@ static void __devinit cciss_procinit(ctlr_info_t *h)
|
|
|
#define to_hba(n) container_of(n, struct ctlr_info, dev)
|
|
|
#define to_drv(n) container_of(n, drive_info_struct, dev)
|
|
|
|
|
|
-/* List of controllers which cannot be reset on kexec with reset_devices */
|
|
|
+/* List of controllers which cannot be hard reset on kexec with reset_devices */
|
|
|
static u32 unresettable_controller[] = {
|
|
|
0x324a103C, /* Smart Array P712m */
|
|
|
0x324b103C, /* SmartArray P711m */
|
|
@@ -574,23 +580,45 @@ static u32 unresettable_controller[] = {
|
|
|
0x409D0E11, /* Smart Array 6400 EM */
|
|
|
};
|
|
|
|
|
|
-static int ctlr_is_resettable(struct ctlr_info *h)
|
|
|
+/* List of controllers which cannot even be soft reset */
|
|
|
+static u32 soft_unresettable_controller[] = {
|
|
|
+ 0x409C0E11, /* Smart Array 6400 */
|
|
|
+ 0x409D0E11, /* Smart Array 6400 EM */
|
|
|
+};
|
|
|
+
|
|
|
+static int ctlr_is_hard_resettable(u32 board_id)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++)
|
|
|
- if (unresettable_controller[i] == h->board_id)
|
|
|
+ if (unresettable_controller[i] == board_id)
|
|
|
return 0;
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+static int ctlr_is_soft_resettable(u32 board_id)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(soft_unresettable_controller); i++)
|
|
|
+ if (soft_unresettable_controller[i] == board_id)
|
|
|
+ return 0;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int ctlr_is_resettable(u32 board_id)
|
|
|
+{
|
|
|
+ return ctlr_is_hard_resettable(board_id) ||
|
|
|
+ ctlr_is_soft_resettable(board_id);
|
|
|
+}
|
|
|
+
|
|
|
static ssize_t host_show_resettable(struct device *dev,
|
|
|
struct device_attribute *attr,
|
|
|
char *buf)
|
|
|
{
|
|
|
struct ctlr_info *h = to_hba(dev);
|
|
|
|
|
|
- return snprintf(buf, 20, "%d\n", ctlr_is_resettable(h));
|
|
|
+ return snprintf(buf, 20, "%d\n", ctlr_is_resettable(h->board_id));
|
|
|
}
|
|
|
static DEVICE_ATTR(resettable, S_IRUGO, host_show_resettable, NULL);
|
|
|
|
|
@@ -2567,7 +2595,7 @@ static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
|
|
|
}
|
|
|
} else if (cmd_type == TYPE_MSG) {
|
|
|
switch (cmd) {
|
|
|
- case 0: /* ABORT message */
|
|
|
+ case CCISS_ABORT_MSG:
|
|
|
c->Request.CDBLen = 12;
|
|
|
c->Request.Type.Attribute = ATTR_SIMPLE;
|
|
|
c->Request.Type.Direction = XFER_WRITE;
|
|
@@ -2577,16 +2605,16 @@ static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
|
|
|
/* buff contains the tag of the command to abort */
|
|
|
memcpy(&c->Request.CDB[4], buff, 8);
|
|
|
break;
|
|
|
- case 1: /* RESET message */
|
|
|
+ case CCISS_RESET_MSG:
|
|
|
c->Request.CDBLen = 16;
|
|
|
c->Request.Type.Attribute = ATTR_SIMPLE;
|
|
|
c->Request.Type.Direction = XFER_NONE;
|
|
|
c->Request.Timeout = 0;
|
|
|
memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
|
|
|
c->Request.CDB[0] = cmd; /* reset */
|
|
|
- c->Request.CDB[1] = 0x03; /* reset a target */
|
|
|
+ c->Request.CDB[1] = CCISS_RESET_TYPE_TARGET;
|
|
|
break;
|
|
|
- case 3: /* No-Op message */
|
|
|
+ case CCISS_NOOP_MSG:
|
|
|
c->Request.CDBLen = 1;
|
|
|
c->Request.Type.Attribute = ATTR_SIMPLE;
|
|
|
c->Request.Type.Direction = XFER_WRITE;
|
|
@@ -2615,6 +2643,31 @@ static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
+static int __devinit cciss_send_reset(ctlr_info_t *h, unsigned char *scsi3addr,
|
|
|
+ u8 reset_type)
|
|
|
+{
|
|
|
+ CommandList_struct *c;
|
|
|
+ int return_status;
|
|
|
+
|
|
|
+ c = cmd_alloc(h);
|
|
|
+ if (!c)
|
|
|
+ return -ENOMEM;
|
|
|
+ return_status = fill_cmd(h, c, CCISS_RESET_MSG, NULL, 0, 0,
|
|
|
+ CTLR_LUNID, TYPE_MSG);
|
|
|
+ c->Request.CDB[1] = reset_type; /* fill_cmd defaults to target reset */
|
|
|
+ if (return_status != IO_OK) {
|
|
|
+ cmd_special_free(h, c);
|
|
|
+ return return_status;
|
|
|
+ }
|
|
|
+ c->waiting = NULL;
|
|
|
+ enqueue_cmd_and_start_io(h, c);
|
|
|
+ /* Don't wait for completion, the reset won't complete. Don't free
|
|
|
+ * the command either. This is the last command we will send before
|
|
|
+ * re-initializing everything, so it doesn't matter and won't leak.
|
|
|
+ */
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int check_target_status(ctlr_info_t *h, CommandList_struct *c)
|
|
|
{
|
|
|
switch (c->err_info->ScsiStatus) {
|
|
@@ -3461,6 +3514,63 @@ static inline u32 process_nonindexed_cmd(ctlr_info_t *h, u32 raw_tag)
|
|
|
return next_command(h);
|
|
|
}
|
|
|
|
|
|
+/* Some controllers, like p400, will give us one interrupt
|
|
|
+ * after a soft reset, even if we turned interrupts off.
|
|
|
+ * Only need to check for this in the cciss_xxx_discard_completions
|
|
|
+ * functions.
|
|
|
+ */
|
|
|
+static int ignore_bogus_interrupt(ctlr_info_t *h)
|
|
|
+{
|
|
|
+ if (likely(!reset_devices))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (likely(h->interrupts_enabled))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ dev_info(&h->pdev->dev, "Received interrupt while interrupts disabled "
|
|
|
+ "(known firmware bug.) Ignoring.\n");
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t cciss_intx_discard_completions(int irq, void *dev_id)
|
|
|
+{
|
|
|
+ ctlr_info_t *h = dev_id;
|
|
|
+ unsigned long flags;
|
|
|
+ u32 raw_tag;
|
|
|
+
|
|
|
+ if (ignore_bogus_interrupt(h))
|
|
|
+ return IRQ_NONE;
|
|
|
+
|
|
|
+ if (interrupt_not_for_us(h))
|
|
|
+ return IRQ_NONE;
|
|
|
+ spin_lock_irqsave(&h->lock, flags);
|
|
|
+ while (interrupt_pending(h)) {
|
|
|
+ raw_tag = get_next_completion(h);
|
|
|
+ while (raw_tag != FIFO_EMPTY)
|
|
|
+ raw_tag = next_command(h);
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&h->lock, flags);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t cciss_msix_discard_completions(int irq, void *dev_id)
|
|
|
+{
|
|
|
+ ctlr_info_t *h = dev_id;
|
|
|
+ unsigned long flags;
|
|
|
+ u32 raw_tag;
|
|
|
+
|
|
|
+ if (ignore_bogus_interrupt(h))
|
|
|
+ return IRQ_NONE;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&h->lock, flags);
|
|
|
+ raw_tag = get_next_completion(h);
|
|
|
+ while (raw_tag != FIFO_EMPTY)
|
|
|
+ raw_tag = next_command(h);
|
|
|
+ spin_unlock_irqrestore(&h->lock, flags);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
static irqreturn_t do_cciss_intx(int irq, void *dev_id)
|
|
|
{
|
|
|
ctlr_info_t *h = dev_id;
|
|
@@ -4078,6 +4188,9 @@ static int __devinit cciss_find_cfgtables(ctlr_info_t *h)
|
|
|
cfg_base_addr_index) + cfg_offset, sizeof(h->cfgtable));
|
|
|
if (!h->cfgtable)
|
|
|
return -ENOMEM;
|
|
|
+ rc = write_driver_ver_to_cfgtable(h->cfgtable);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
/* Find performant mode table. */
|
|
|
trans_offset = readl(&h->cfgtable->TransMethodOffset);
|
|
|
h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
|
|
@@ -4112,7 +4225,7 @@ static void __devinit cciss_get_max_perf_mode_cmds(struct ctlr_info *h)
|
|
|
static void __devinit cciss_find_board_params(ctlr_info_t *h)
|
|
|
{
|
|
|
cciss_get_max_perf_mode_cmds(h);
|
|
|
- h->nr_cmds = h->max_commands - 4; /* Allow room for some ioctls */
|
|
|
+ h->nr_cmds = h->max_commands - 4 - cciss_tape_cmds;
|
|
|
h->maxsgentries = readl(&(h->cfgtable->MaxSGElements));
|
|
|
/*
|
|
|
* Limit in-command s/g elements to 32 save dma'able memory.
|
|
@@ -4348,7 +4461,7 @@ static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, u
|
|
|
tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
|
|
|
if ((tag & ~3) == paddr32)
|
|
|
break;
|
|
|
- schedule_timeout_uninterruptible(HZ);
|
|
|
+ msleep(CCISS_POST_RESET_NOOP_TIMEOUT_MSECS);
|
|
|
}
|
|
|
|
|
|
iounmap(vaddr);
|
|
@@ -4375,11 +4488,10 @@ static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, u
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#define cciss_soft_reset_controller(p) cciss_message(p, 1, 0)
|
|
|
#define cciss_noop(p) cciss_message(p, 3, 0)
|
|
|
|
|
|
static int cciss_controller_hard_reset(struct pci_dev *pdev,
|
|
|
- void * __iomem vaddr, bool use_doorbell)
|
|
|
+ void * __iomem vaddr, u32 use_doorbell)
|
|
|
{
|
|
|
u16 pmcsr;
|
|
|
int pos;
|
|
@@ -4390,8 +4502,7 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev,
|
|
|
* other way using the doorbell register.
|
|
|
*/
|
|
|
dev_info(&pdev->dev, "using doorbell to reset controller\n");
|
|
|
- writel(DOORBELL_CTLR_RESET, vaddr + SA5_DOORBELL);
|
|
|
- msleep(1000);
|
|
|
+ writel(use_doorbell, vaddr + SA5_DOORBELL);
|
|
|
} else { /* Try to do it the PCI power state way */
|
|
|
|
|
|
/* Quoting from the Open CISS Specification: "The Power
|
|
@@ -4422,12 +4533,64 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev,
|
|
|
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
|
|
|
pmcsr |= PCI_D0;
|
|
|
pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
|
|
|
-
|
|
|
- msleep(500);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static __devinit void init_driver_version(char *driver_version, int len)
|
|
|
+{
|
|
|
+ memset(driver_version, 0, len);
|
|
|
+ strncpy(driver_version, "cciss " DRIVER_NAME, len - 1);
|
|
|
+}
|
|
|
+
|
|
|
+static __devinit int write_driver_ver_to_cfgtable(
|
|
|
+ CfgTable_struct __iomem *cfgtable)
|
|
|
+{
|
|
|
+ char *driver_version;
|
|
|
+ int i, size = sizeof(cfgtable->driver_version);
|
|
|
+
|
|
|
+ driver_version = kmalloc(size, GFP_KERNEL);
|
|
|
+ if (!driver_version)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ init_driver_version(driver_version, size);
|
|
|
+ for (i = 0; i < size; i++)
|
|
|
+ writeb(driver_version[i], &cfgtable->driver_version[i]);
|
|
|
+ kfree(driver_version);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static __devinit void read_driver_ver_from_cfgtable(
|
|
|
+ CfgTable_struct __iomem *cfgtable, unsigned char *driver_ver)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < sizeof(cfgtable->driver_version); i++)
|
|
|
+ driver_ver[i] = readb(&cfgtable->driver_version[i]);
|
|
|
+}
|
|
|
+
|
|
|
+static __devinit int controller_reset_failed(
|
|
|
+ CfgTable_struct __iomem *cfgtable)
|
|
|
+{
|
|
|
+
|
|
|
+ char *driver_ver, *old_driver_ver;
|
|
|
+ int rc, size = sizeof(cfgtable->driver_version);
|
|
|
+
|
|
|
+ old_driver_ver = kmalloc(2 * size, GFP_KERNEL);
|
|
|
+ if (!old_driver_ver)
|
|
|
+ return -ENOMEM;
|
|
|
+ driver_ver = old_driver_ver + size;
|
|
|
+
|
|
|
+ /* After a reset, the 32 bytes of "driver version" in the cfgtable
|
|
|
+ * should have been changed, otherwise we know the reset failed.
|
|
|
+ */
|
|
|
+ init_driver_version(old_driver_ver, size);
|
|
|
+ read_driver_ver_from_cfgtable(cfgtable, driver_ver);
|
|
|
+ rc = !memcmp(driver_ver, old_driver_ver, size);
|
|
|
+ kfree(old_driver_ver);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
/* This does a hard reset of the controller using PCI power management
|
|
|
* states or using the doorbell register. */
|
|
|
static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
|
|
@@ -4437,10 +4600,10 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
|
|
|
u64 cfg_base_addr_index;
|
|
|
void __iomem *vaddr;
|
|
|
unsigned long paddr;
|
|
|
- u32 misc_fw_support, active_transport;
|
|
|
+ u32 misc_fw_support;
|
|
|
int rc;
|
|
|
CfgTable_struct __iomem *cfgtable;
|
|
|
- bool use_doorbell;
|
|
|
+ u32 use_doorbell;
|
|
|
u32 board_id;
|
|
|
u16 command_register;
|
|
|
|
|
@@ -4464,12 +4627,16 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
|
|
|
* likely not be happy. Just forbid resetting this conjoined mess.
|
|
|
*/
|
|
|
cciss_lookup_board_id(pdev, &board_id);
|
|
|
- if (board_id == 0x409C0E11 || board_id == 0x409D0E11) {
|
|
|
+ if (!ctlr_is_resettable(board_id)) {
|
|
|
dev_warn(&pdev->dev, "Cannot reset Smart Array 640x "
|
|
|
"due to shared cache module.");
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
+ /* if controller is soft- but not hard resettable... */
|
|
|
+ if (!ctlr_is_hard_resettable(board_id))
|
|
|
+ return -ENOTSUPP; /* try soft reset later. */
|
|
|
+
|
|
|
/* Save the PCI command register */
|
|
|
pci_read_config_word(pdev, 4, &command_register);
|
|
|
/* Turn the board off. This is so that later pci_restore_state()
|
|
@@ -4497,16 +4664,28 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
|
|
|
rc = -ENOMEM;
|
|
|
goto unmap_vaddr;
|
|
|
}
|
|
|
+ rc = write_driver_ver_to_cfgtable(cfgtable);
|
|
|
+ if (rc)
|
|
|
+ goto unmap_vaddr;
|
|
|
|
|
|
- /* If reset via doorbell register is supported, use that. */
|
|
|
- misc_fw_support = readl(&cfgtable->misc_fw_support);
|
|
|
- use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
|
|
|
-
|
|
|
- /* The doorbell reset seems to cause lockups on some Smart
|
|
|
- * Arrays (e.g. P410, P410i, maybe others). Until this is
|
|
|
- * fixed or at least isolated, avoid the doorbell reset.
|
|
|
+ /* If reset via doorbell register is supported, use that.
|
|
|
+ * There are two such methods. Favor the newest method.
|
|
|
*/
|
|
|
- use_doorbell = 0;
|
|
|
+ misc_fw_support = readl(&cfgtable->misc_fw_support);
|
|
|
+ use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET2;
|
|
|
+ if (use_doorbell) {
|
|
|
+ use_doorbell = DOORBELL_CTLR_RESET2;
|
|
|
+ } else {
|
|
|
+ use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
|
|
|
+ if (use_doorbell) {
|
|
|
+ dev_warn(&pdev->dev, "Controller claims that "
|
|
|
+ "'Bit 2 doorbell reset' is "
|
|
|
+ "supported, but not 'bit 5 doorbell reset'. "
|
|
|
+ "Firmware update is recommended.\n");
|
|
|
+ rc = -ENOTSUPP; /* use the soft reset */
|
|
|
+ goto unmap_cfgtable;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell);
|
|
|
if (rc)
|
|
@@ -4524,30 +4703,31 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
|
|
|
msleep(CCISS_POST_RESET_PAUSE_MSECS);
|
|
|
|
|
|
/* Wait for board to become not ready, then ready. */
|
|
|
- dev_info(&pdev->dev, "Waiting for board to become ready.\n");
|
|
|
+ dev_info(&pdev->dev, "Waiting for board to reset.\n");
|
|
|
rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY);
|
|
|
- if (rc) /* Don't bail, might be E500, etc. which can't be reset */
|
|
|
- dev_warn(&pdev->dev,
|
|
|
- "failed waiting for board to become not ready\n");
|
|
|
+ if (rc) {
|
|
|
+ dev_warn(&pdev->dev, "Failed waiting for board to hard reset."
|
|
|
+ " Will try soft reset.\n");
|
|
|
+ rc = -ENOTSUPP; /* Not expected, but try soft reset later */
|
|
|
+ goto unmap_cfgtable;
|
|
|
+ }
|
|
|
rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_READY);
|
|
|
if (rc) {
|
|
|
dev_warn(&pdev->dev,
|
|
|
- "failed waiting for board to become ready\n");
|
|
|
+ "failed waiting for board to become ready "
|
|
|
+ "after hard reset\n");
|
|
|
goto unmap_cfgtable;
|
|
|
}
|
|
|
- dev_info(&pdev->dev, "board ready.\n");
|
|
|
|
|
|
- /* Controller should be in simple mode at this point. If it's not,
|
|
|
- * It means we're on one of those controllers which doesn't support
|
|
|
- * the doorbell reset method and on which the PCI power management reset
|
|
|
- * method doesn't work (P800, for example.)
|
|
|
- * In those cases, don't try to proceed, as it generally doesn't work.
|
|
|
- */
|
|
|
- active_transport = readl(&cfgtable->TransportActive);
|
|
|
- if (active_transport & PERFORMANT_MODE) {
|
|
|
- dev_warn(&pdev->dev, "Unable to successfully reset controller,"
|
|
|
- " Ignoring controller.\n");
|
|
|
- rc = -ENODEV;
|
|
|
+ rc = controller_reset_failed(vaddr);
|
|
|
+ if (rc < 0)
|
|
|
+ goto unmap_cfgtable;
|
|
|
+ if (rc) {
|
|
|
+ dev_warn(&pdev->dev, "Unable to successfully hard reset "
|
|
|
+ "controller. Will try soft reset.\n");
|
|
|
+ rc = -ENOTSUPP; /* Not expected, but try soft reset later */
|
|
|
+ } else {
|
|
|
+ dev_info(&pdev->dev, "Board ready after hard reset.\n");
|
|
|
}
|
|
|
|
|
|
unmap_cfgtable:
|
|
@@ -4574,11 +4754,12 @@ static __devinit int cciss_init_reset_devices(struct pci_dev *pdev)
|
|
|
* due to concerns about shared bbwc between 6402/6404 pair.
|
|
|
*/
|
|
|
if (rc == -ENOTSUPP)
|
|
|
- return 0; /* just try to do the kdump anyhow. */
|
|
|
+ return rc; /* just try to do the kdump anyhow. */
|
|
|
if (rc)
|
|
|
return -ENODEV;
|
|
|
|
|
|
/* Now try to get the controller to respond to a no-op */
|
|
|
+ dev_warn(&pdev->dev, "Waiting for controller to respond to no-op\n");
|
|
|
for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) {
|
|
|
if (cciss_noop(pdev) == 0)
|
|
|
break;
|
|
@@ -4591,6 +4772,148 @@ static __devinit int cciss_init_reset_devices(struct pci_dev *pdev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static __devinit int cciss_allocate_cmd_pool(ctlr_info_t *h)
|
|
|
+{
|
|
|
+ h->cmd_pool_bits = kmalloc(
|
|
|
+ DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) *
|
|
|
+ sizeof(unsigned long), GFP_KERNEL);
|
|
|
+ h->cmd_pool = pci_alloc_consistent(h->pdev,
|
|
|
+ h->nr_cmds * sizeof(CommandList_struct),
|
|
|
+ &(h->cmd_pool_dhandle));
|
|
|
+ h->errinfo_pool = pci_alloc_consistent(h->pdev,
|
|
|
+ h->nr_cmds * sizeof(ErrorInfo_struct),
|
|
|
+ &(h->errinfo_pool_dhandle));
|
|
|
+ if ((h->cmd_pool_bits == NULL)
|
|
|
+ || (h->cmd_pool == NULL)
|
|
|
+ || (h->errinfo_pool == NULL)) {
|
|
|
+ dev_err(&h->pdev->dev, "out of memory");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static __devinit int cciss_allocate_scatterlists(ctlr_info_t *h)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* zero it, so that on free we need not know how many were alloc'ed */
|
|
|
+ h->scatter_list = kzalloc(h->max_commands *
|
|
|
+ sizeof(struct scatterlist *), GFP_KERNEL);
|
|
|
+ if (!h->scatter_list)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ for (i = 0; i < h->nr_cmds; i++) {
|
|
|
+ h->scatter_list[i] = kmalloc(sizeof(struct scatterlist) *
|
|
|
+ h->maxsgentries, GFP_KERNEL);
|
|
|
+ if (h->scatter_list[i] == NULL) {
|
|
|
+ dev_err(&h->pdev->dev, "could not allocate "
|
|
|
+ "s/g lists\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void cciss_free_scatterlists(ctlr_info_t *h)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (h->scatter_list) {
|
|
|
+ for (i = 0; i < h->nr_cmds; i++)
|
|
|
+ kfree(h->scatter_list[i]);
|
|
|
+ kfree(h->scatter_list);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void cciss_free_cmd_pool(ctlr_info_t *h)
|
|
|
+{
|
|
|
+ kfree(h->cmd_pool_bits);
|
|
|
+ if (h->cmd_pool)
|
|
|
+ pci_free_consistent(h->pdev,
|
|
|
+ h->nr_cmds * sizeof(CommandList_struct),
|
|
|
+ h->cmd_pool, h->cmd_pool_dhandle);
|
|
|
+ if (h->errinfo_pool)
|
|
|
+ pci_free_consistent(h->pdev,
|
|
|
+ h->nr_cmds * sizeof(ErrorInfo_struct),
|
|
|
+ h->errinfo_pool, h->errinfo_pool_dhandle);
|
|
|
+}
|
|
|
+
|
|
|
+static int cciss_request_irq(ctlr_info_t *h,
|
|
|
+ irqreturn_t (*msixhandler)(int, void *),
|
|
|
+ irqreturn_t (*intxhandler)(int, void *))
|
|
|
+{
|
|
|
+ if (h->msix_vector || h->msi_vector) {
|
|
|
+ if (!request_irq(h->intr[PERF_MODE_INT], msixhandler,
|
|
|
+ IRQF_DISABLED, h->devname, h))
|
|
|
+ return 0;
|
|
|
+ dev_err(&h->pdev->dev, "Unable to get msi irq %d"
|
|
|
+ " for %s\n", h->intr[PERF_MODE_INT],
|
|
|
+ h->devname);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!request_irq(h->intr[PERF_MODE_INT], intxhandler,
|
|
|
+ IRQF_DISABLED, h->devname, h))
|
|
|
+ return 0;
|
|
|
+ dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
|
|
|
+ h->intr[PERF_MODE_INT], h->devname);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+static int __devinit cciss_kdump_soft_reset(ctlr_info_t *h)
|
|
|
+{
|
|
|
+ if (cciss_send_reset(h, CTLR_LUNID, CCISS_RESET_TYPE_CONTROLLER)) {
|
|
|
+ dev_warn(&h->pdev->dev, "Resetting array controller failed.\n");
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n");
|
|
|
+ if (cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) {
|
|
|
+ dev_warn(&h->pdev->dev, "Soft reset had no effect.\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n");
|
|
|
+ if (cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) {
|
|
|
+ dev_warn(&h->pdev->dev, "Board failed to become ready "
|
|
|
+ "after soft reset.\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void cciss_undo_allocations_after_kdump_soft_reset(ctlr_info_t *h)
|
|
|
+{
|
|
|
+ int ctlr = h->ctlr;
|
|
|
+
|
|
|
+ free_irq(h->intr[PERF_MODE_INT], h);
|
|
|
+#ifdef CONFIG_PCI_MSI
|
|
|
+ if (h->msix_vector)
|
|
|
+ pci_disable_msix(h->pdev);
|
|
|
+ else if (h->msi_vector)
|
|
|
+ pci_disable_msi(h->pdev);
|
|
|
+#endif /* CONFIG_PCI_MSI */
|
|
|
+ cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
|
|
|
+ cciss_free_scatterlists(h);
|
|
|
+ cciss_free_cmd_pool(h);
|
|
|
+ kfree(h->blockFetchTable);
|
|
|
+ if (h->reply_pool)
|
|
|
+ pci_free_consistent(h->pdev, h->max_commands * sizeof(__u64),
|
|
|
+ h->reply_pool, h->reply_pool_dhandle);
|
|
|
+ if (h->transtable)
|
|
|
+ iounmap(h->transtable);
|
|
|
+ if (h->cfgtable)
|
|
|
+ iounmap(h->cfgtable);
|
|
|
+ if (h->vaddr)
|
|
|
+ iounmap(h->vaddr);
|
|
|
+ unregister_blkdev(h->major, h->devname);
|
|
|
+ cciss_destroy_hba_sysfs_entry(h);
|
|
|
+ pci_release_regions(h->pdev);
|
|
|
+ kfree(h);
|
|
|
+ hba[ctlr] = NULL;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This is it. Find all the controllers and register them. I really hate
|
|
|
* stealing all these major device numbers.
|
|
@@ -4601,15 +4924,28 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|
|
{
|
|
|
int i;
|
|
|
int j = 0;
|
|
|
- int k = 0;
|
|
|
int rc;
|
|
|
+ int try_soft_reset = 0;
|
|
|
int dac, return_code;
|
|
|
InquiryData_struct *inq_buff;
|
|
|
ctlr_info_t *h;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
rc = cciss_init_reset_devices(pdev);
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
+ if (rc) {
|
|
|
+ if (rc != -ENOTSUPP)
|
|
|
+ return rc;
|
|
|
+ /* If the reset fails in a particular way (it has no way to do
|
|
|
+ * a proper hard reset, so returns -ENOTSUPP) we can try to do
|
|
|
+ * a soft reset once we get the controller configured up to the
|
|
|
+ * point that it can accept a command.
|
|
|
+ */
|
|
|
+ try_soft_reset = 1;
|
|
|
+ rc = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+reinit_after_soft_reset:
|
|
|
+
|
|
|
i = alloc_cciss_hba(pdev);
|
|
|
if (i < 0)
|
|
|
return -1;
|
|
@@ -4627,6 +4963,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|
|
sprintf(h->devname, "cciss%d", i);
|
|
|
h->ctlr = i;
|
|
|
|
|
|
+ if (cciss_tape_cmds < 2)
|
|
|
+ cciss_tape_cmds = 2;
|
|
|
+ if (cciss_tape_cmds > 16)
|
|
|
+ cciss_tape_cmds = 16;
|
|
|
+
|
|
|
init_completion(&h->scan_wait);
|
|
|
|
|
|
if (cciss_create_hba_sysfs_entry(h))
|
|
@@ -4662,62 +5003,20 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|
|
|
|
|
/* make sure the board interrupts are off */
|
|
|
h->access.set_intr_mask(h, CCISS_INTR_OFF);
|
|
|
- if (h->msi_vector || h->msix_vector) {
|
|
|
- if (request_irq(h->intr[PERF_MODE_INT],
|
|
|
- do_cciss_msix_intr,
|
|
|
- IRQF_DISABLED, h->devname, h)) {
|
|
|
- dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
|
|
|
- h->intr[PERF_MODE_INT], h->devname);
|
|
|
- goto clean2;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (request_irq(h->intr[PERF_MODE_INT], do_cciss_intx,
|
|
|
- IRQF_DISABLED, h->devname, h)) {
|
|
|
- dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
|
|
|
- h->intr[PERF_MODE_INT], h->devname);
|
|
|
- goto clean2;
|
|
|
- }
|
|
|
- }
|
|
|
+ rc = cciss_request_irq(h, do_cciss_msix_intr, do_cciss_intx);
|
|
|
+ if (rc)
|
|
|
+ goto clean2;
|
|
|
|
|
|
dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
|
|
|
h->devname, pdev->device, pci_name(pdev),
|
|
|
h->intr[PERF_MODE_INT], dac ? "" : " not");
|
|
|
|
|
|
- h->cmd_pool_bits =
|
|
|
- kmalloc(DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG)
|
|
|
- * sizeof(unsigned long), GFP_KERNEL);
|
|
|
- h->cmd_pool = (CommandList_struct *)
|
|
|
- pci_alloc_consistent(h->pdev,
|
|
|
- h->nr_cmds * sizeof(CommandList_struct),
|
|
|
- &(h->cmd_pool_dhandle));
|
|
|
- h->errinfo_pool = (ErrorInfo_struct *)
|
|
|
- pci_alloc_consistent(h->pdev,
|
|
|
- h->nr_cmds * sizeof(ErrorInfo_struct),
|
|
|
- &(h->errinfo_pool_dhandle));
|
|
|
- if ((h->cmd_pool_bits == NULL)
|
|
|
- || (h->cmd_pool == NULL)
|
|
|
- || (h->errinfo_pool == NULL)) {
|
|
|
- dev_err(&h->pdev->dev, "out of memory");
|
|
|
+ if (cciss_allocate_cmd_pool(h))
|
|
|
goto clean4;
|
|
|
- }
|
|
|
|
|
|
- /* Need space for temp scatter list */
|
|
|
- h->scatter_list = kmalloc(h->max_commands *
|
|
|
- sizeof(struct scatterlist *),
|
|
|
- GFP_KERNEL);
|
|
|
- if (!h->scatter_list)
|
|
|
+ if (cciss_allocate_scatterlists(h))
|
|
|
goto clean4;
|
|
|
|
|
|
- for (k = 0; k < h->nr_cmds; k++) {
|
|
|
- h->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
|
|
|
- h->maxsgentries,
|
|
|
- GFP_KERNEL);
|
|
|
- if (h->scatter_list[k] == NULL) {
|
|
|
- dev_err(&h->pdev->dev,
|
|
|
- "could not allocate s/g lists\n");
|
|
|
- goto clean4;
|
|
|
- }
|
|
|
- }
|
|
|
h->cmd_sg_list = cciss_allocate_sg_chain_blocks(h,
|
|
|
h->chainsize, h->nr_cmds);
|
|
|
if (!h->cmd_sg_list && h->chainsize > 0)
|
|
@@ -4741,6 +5040,62 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|
|
h->gendisk[j] = NULL;
|
|
|
}
|
|
|
|
|
|
+ /* At this point, the controller is ready to take commands.
|
|
|
+ * Now, if reset_devices and the hard reset didn't work, try
|
|
|
+ * the soft reset and see if that works.
|
|
|
+ */
|
|
|
+ if (try_soft_reset) {
|
|
|
+
|
|
|
+ /* This is kind of gross. We may or may not get a completion
|
|
|
+ * from the soft reset command, and if we do, then the value
|
|
|
+ * from the fifo may or may not be valid. So, we wait 10 secs
|
|
|
+ * after the reset throwing away any completions we get during
|
|
|
+ * that time. Unregister the interrupt handler and register
|
|
|
+ * fake ones to scoop up any residual completions.
|
|
|
+ */
|
|
|
+ spin_lock_irqsave(&h->lock, flags);
|
|
|
+ h->access.set_intr_mask(h, CCISS_INTR_OFF);
|
|
|
+ spin_unlock_irqrestore(&h->lock, flags);
|
|
|
+ free_irq(h->intr[PERF_MODE_INT], h);
|
|
|
+ rc = cciss_request_irq(h, cciss_msix_discard_completions,
|
|
|
+ cciss_intx_discard_completions);
|
|
|
+ if (rc) {
|
|
|
+ dev_warn(&h->pdev->dev, "Failed to request_irq after "
|
|
|
+ "soft reset.\n");
|
|
|
+ goto clean4;
|
|
|
+ }
|
|
|
+
|
|
|
+ rc = cciss_kdump_soft_reset(h);
|
|
|
+ if (rc) {
|
|
|
+ dev_warn(&h->pdev->dev, "Soft reset failed.\n");
|
|
|
+ goto clean4;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_info(&h->pdev->dev, "Board READY.\n");
|
|
|
+ dev_info(&h->pdev->dev,
|
|
|
+ "Waiting for stale completions to drain.\n");
|
|
|
+ h->access.set_intr_mask(h, CCISS_INTR_ON);
|
|
|
+ msleep(10000);
|
|
|
+ h->access.set_intr_mask(h, CCISS_INTR_OFF);
|
|
|
+
|
|
|
+ rc = controller_reset_failed(h->cfgtable);
|
|
|
+ if (rc)
|
|
|
+ dev_info(&h->pdev->dev,
|
|
|
+ "Soft reset appears to have failed.\n");
|
|
|
+
|
|
|
+ /* since the controller's reset, we have to go back and re-init
|
|
|
+ * everything. Easiest to just forget what we've done and do it
|
|
|
+ * all over again.
|
|
|
+ */
|
|
|
+ cciss_undo_allocations_after_kdump_soft_reset(h);
|
|
|
+ try_soft_reset = 0;
|
|
|
+ if (rc)
|
|
|
+ /* don't go to clean4, we already unallocated */
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ goto reinit_after_soft_reset;
|
|
|
+ }
|
|
|
+
|
|
|
cciss_scsi_setup(h);
|
|
|
|
|
|
/* Turn the interrupts on so we can service requests */
|
|
@@ -4775,21 +5130,9 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|
|
return 1;
|
|
|
|
|
|
clean4:
|
|
|
- kfree(h->cmd_pool_bits);
|
|
|
- /* Free up sg elements */
|
|
|
- for (k-- ; k >= 0; k--)
|
|
|
- kfree(h->scatter_list[k]);
|
|
|
- kfree(h->scatter_list);
|
|
|
+ cciss_free_cmd_pool(h);
|
|
|
+ cciss_free_scatterlists(h);
|
|
|
cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
|
|
|
- if (h->cmd_pool)
|
|
|
- pci_free_consistent(h->pdev,
|
|
|
- h->nr_cmds * sizeof(CommandList_struct),
|
|
|
- h->cmd_pool, h->cmd_pool_dhandle);
|
|
|
- if (h->errinfo_pool)
|
|
|
- pci_free_consistent(h->pdev,
|
|
|
- h->nr_cmds * sizeof(ErrorInfo_struct),
|
|
|
- h->errinfo_pool,
|
|
|
- h->errinfo_pool_dhandle);
|
|
|
free_irq(h->intr[PERF_MODE_INT], h);
|
|
|
clean2:
|
|
|
unregister_blkdev(h->major, h->devname);
|
|
@@ -4887,16 +5230,16 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
|
|
|
iounmap(h->cfgtable);
|
|
|
iounmap(h->vaddr);
|
|
|
|
|
|
- pci_free_consistent(h->pdev, h->nr_cmds * sizeof(CommandList_struct),
|
|
|
- h->cmd_pool, h->cmd_pool_dhandle);
|
|
|
- pci_free_consistent(h->pdev, h->nr_cmds * sizeof(ErrorInfo_struct),
|
|
|
- h->errinfo_pool, h->errinfo_pool_dhandle);
|
|
|
- kfree(h->cmd_pool_bits);
|
|
|
+ cciss_free_cmd_pool(h);
|
|
|
/* Free up sg elements */
|
|
|
for (j = 0; j < h->nr_cmds; j++)
|
|
|
kfree(h->scatter_list[j]);
|
|
|
kfree(h->scatter_list);
|
|
|
cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
|
|
|
+ kfree(h->blockFetchTable);
|
|
|
+ if (h->reply_pool)
|
|
|
+ pci_free_consistent(h->pdev, h->max_commands * sizeof(__u64),
|
|
|
+ h->reply_pool, h->reply_pool_dhandle);
|
|
|
/*
|
|
|
* Deliberately omit pci_disable_device(): it does something nasty to
|
|
|
* Smart Array controllers that pci_enable_device does not undo
|