[SCSI] qla4xxx: Fix MBOX intr switching from polling to intr mode for ISP83XX
authorVikas Chaudhary <vikas.chaudhary@qlogic.com>
Fri, 23 Nov 2012 11:58:38 +0000 (06:58 -0500)
committerJames Bottomley <JBottomley@Parallels.com>
Tue, 29 Jan 2013 02:55:06 +0000 (13:55 +1100)
Issue:
Mailbox command timed out after switching from polling mode to interrupt mode.

Events:-
 1. Mailbox interrupts are disabled
 2. FW generates AEN and at same time driver enables Mailbox Interrupt
 3. Driver issues new mailbox to Firmware

In above case driver will not get AEN interrupts generated by FW in step #2 as
FW generated this AEN when interrupts are disabled. During the same time
driver enabled the mailbox interrupt, so driver will not poll for interrupt.
Driver will never process AENs generated in step #2 and issues new mailbox to
FW, but now FW is not able to post mailbox completion as AENs generated before
are not processed by driver.

Fix:
Enable Mailbox / AEN interrupts before initializing FW in case of ISP83XX.
This will make sure we process all Mailbox and AENs in interrupt mode.

Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/qla4xxx/ql4_83xx.c
drivers/scsi/qla4xxx/ql4_def.h
drivers/scsi/qla4xxx/ql4_glbl.h
drivers/scsi/qla4xxx/ql4_init.c
drivers/scsi/qla4xxx/ql4_isr.c
drivers/scsi/qla4xxx/ql4_mbx.c
drivers/scsi/qla4xxx/ql4_nx.c
drivers/scsi/qla4xxx/ql4_os.c

index 6e9af20be12f8a720b06279a1800c579f2fc713b..417738366c1fcbeef92a0f1fadb11bb4fe3cfde2 100644 (file)
@@ -1351,31 +1351,58 @@ exit_start_fw:
 
 /*----------------------Interrupt Related functions ---------------------*/
 
-void qla4_83xx_disable_intrs(struct scsi_qla_host *ha)
+static void qla4_83xx_disable_iocb_intrs(struct scsi_qla_host *ha)
+{
+       if (test_and_clear_bit(AF_83XX_IOCB_INTR_ON, &ha->flags))
+               qla4_8xxx_intr_disable(ha);
+}
+
+static void qla4_83xx_disable_mbox_intrs(struct scsi_qla_host *ha)
 {
        uint32_t mb_int, ret;
 
-       if (test_and_clear_bit(AF_INTERRUPTS_ON, &ha->flags))
-               qla4_8xxx_mbx_intr_disable(ha);
+       if (test_and_clear_bit(AF_83XX_MBOX_INTR_ON, &ha->flags)) {
+               ret = readl(&ha->qla4_83xx_reg->mbox_int);
+               mb_int = ret & ~INT_ENABLE_FW_MB;
+               writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
+               writel(1, &ha->qla4_83xx_reg->leg_int_mask);
+       }
+}
 
-       ret = readl(&ha->qla4_83xx_reg->mbox_int);
-       mb_int = ret & ~INT_ENABLE_FW_MB;
-       writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
-       writel(1, &ha->qla4_83xx_reg->leg_int_mask);
+void qla4_83xx_disable_intrs(struct scsi_qla_host *ha)
+{
+       qla4_83xx_disable_mbox_intrs(ha);
+       qla4_83xx_disable_iocb_intrs(ha);
 }
 
-void qla4_83xx_enable_intrs(struct scsi_qla_host *ha)
+static void qla4_83xx_enable_iocb_intrs(struct scsi_qla_host *ha)
+{
+       if (!test_bit(AF_83XX_IOCB_INTR_ON, &ha->flags)) {
+               qla4_8xxx_intr_enable(ha);
+               set_bit(AF_83XX_IOCB_INTR_ON, &ha->flags);
+       }
+}
+
+void qla4_83xx_enable_mbox_intrs(struct scsi_qla_host *ha)
 {
        uint32_t mb_int;
 
-       qla4_8xxx_mbx_intr_enable(ha);
-       mb_int = INT_ENABLE_FW_MB;
-       writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
-       writel(0, &ha->qla4_83xx_reg->leg_int_mask);
+       if (!test_bit(AF_83XX_MBOX_INTR_ON, &ha->flags)) {
+               mb_int = INT_ENABLE_FW_MB;
+               writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
+               writel(0, &ha->qla4_83xx_reg->leg_int_mask);
+               set_bit(AF_83XX_MBOX_INTR_ON, &ha->flags);
+       }
+}
 
-       set_bit(AF_INTERRUPTS_ON, &ha->flags);
+
+void qla4_83xx_enable_intrs(struct scsi_qla_host *ha)
+{
+       qla4_83xx_enable_mbox_intrs(ha);
+       qla4_83xx_enable_iocb_intrs(ha);
 }
 
+
 void qla4_83xx_queue_mbox_cmd(struct scsi_qla_host *ha, uint32_t *mbx_cmd,
                              int incount)
 {
index 329d553eae943d9acc524af42652721df8ad3fa1..c71a371c452bf4b0602f553148d96d06f94dc5b0 100644 (file)
@@ -516,6 +516,8 @@ struct scsi_qla_host {
 #define AF_8XXX_RST_OWNER              25 /* 0x02000000 */
 #define AF_82XX_DUMP_READING           26 /* 0x04000000 */
 #define AF_83XX_NO_FW_DUMP             27 /* 0x08000000 */
+#define AF_83XX_IOCB_INTR_ON           28 /* 0x10000000 */
+#define AF_83XX_MBOX_INTR_ON           29 /* 0x20000000 */
 
        unsigned long dpc_flags;
 
index 57a5a3cf5770d5e402d88db55feead1b417a3175..7a2a35a4aed3b126e044d6135ccb03b2de92b35f 100644 (file)
@@ -253,12 +253,13 @@ void qla4_8xxx_set_rst_ready(struct scsi_qla_host *ha);
 void qla4_8xxx_clear_rst_ready(struct scsi_qla_host *ha);
 int qla4_8xxx_device_bootstrap(struct scsi_qla_host *ha);
 void qla4_8xxx_get_minidump(struct scsi_qla_host *ha);
-int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha);
-int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha);
+int qla4_8xxx_intr_disable(struct scsi_qla_host *ha);
+int qla4_8xxx_intr_enable(struct scsi_qla_host *ha);
 int qla4_8xxx_set_param(struct scsi_qla_host *ha, int param);
 int qla4_8xxx_update_idc_reg(struct scsi_qla_host *ha);
 int qla4_83xx_post_idc_ack(struct scsi_qla_host *ha);
 void qla4_83xx_disable_pause(struct scsi_qla_host *ha);
+void qla4_83xx_enable_mbox_intrs(struct scsi_qla_host *ha);
 
 extern int ql4xextended_error_logging;
 extern int ql4xdontresethba;
index 1aca1b4f70b820edd867ba566d883e4ec1b6793c..2045fd79095f366aae893073defbc0e7d14d18cd 100644 (file)
@@ -935,6 +935,16 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, int is_reset)
        if (ha->isp_ops->start_firmware(ha) == QLA_ERROR)
                goto exit_init_hba;
 
+       /*
+        * For ISP83XX, mailbox and IOCB interrupts are enabled separately.
+        * Mailbox interrupts must be enabled prior to issuing any mailbox
+        * command in order to prevent the possibility of losing interrupts
+        * while switching from polling to interrupt mode. IOCB interrupts are
+        * enabled via isp_ops->enable_intrs.
+        */
+       if (is_qla8032(ha))
+               qla4_83xx_enable_mbox_intrs(ha);
+
        if (qla4xxx_about_firmware(ha) == QLA_ERROR)
                goto exit_init_hba;
 
index 15ea81465ce4eaf7facddd6ecce4d682b36b6b5b..acbc2feaa0058d8f188815ff2db1ae9d6f595d3b 100644 (file)
@@ -1437,11 +1437,14 @@ irq_not_attached:
 
 void qla4xxx_free_irqs(struct scsi_qla_host *ha)
 {
-       if (test_bit(AF_MSIX_ENABLED, &ha->flags))
-               qla4_8xxx_disable_msix(ha);
-       else if (test_and_clear_bit(AF_MSI_ENABLED, &ha->flags)) {
-               free_irq(ha->pdev->irq, ha);
-               pci_disable_msi(ha->pdev);
-       } else if (test_and_clear_bit(AF_INTx_ENABLED, &ha->flags))
-               free_irq(ha->pdev->irq, ha);
+       if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags)) {
+               if (test_bit(AF_MSIX_ENABLED, &ha->flags)) {
+                       qla4_8xxx_disable_msix(ha);
+               } else if (test_and_clear_bit(AF_MSI_ENABLED, &ha->flags)) {
+                       free_irq(ha->pdev->irq, ha);
+                       pci_disable_msi(ha->pdev);
+               } else if (test_and_clear_bit(AF_INTx_ENABLED, &ha->flags)) {
+                       free_irq(ha->pdev->irq, ha);
+               }
+       }
 }
index 3d41034191f02588b6c94cc4ec7c4d6d53fb0650..1c57c227c808e26255ab159911535e64726d12d0 100644 (file)
@@ -43,6 +43,30 @@ void qla4xxx_process_mbox_intr(struct scsi_qla_host *ha, int out_count)
        }
 }
 
+/**
+ * qla4xxx_is_intr_poll_mode – Are we allowed to poll for interrupts?
+ * @ha: Pointer to host adapter structure.
+ * @ret: 1=polling mode, 0=non-polling mode
+ **/
+static int qla4xxx_is_intr_poll_mode(struct scsi_qla_host *ha)
+{
+       int rval = 1;
+
+       if (is_qla8032(ha)) {
+               if (test_bit(AF_IRQ_ATTACHED, &ha->flags) &&
+                   test_bit(AF_83XX_MBOX_INTR_ON, &ha->flags))
+                       rval = 0;
+       } else {
+               if (test_bit(AF_IRQ_ATTACHED, &ha->flags) &&
+                   test_bit(AF_INTERRUPTS_ON, &ha->flags) &&
+                   test_bit(AF_ONLINE, &ha->flags) &&
+                   !test_bit(AF_HA_REMOVAL, &ha->flags))
+                       rval = 0;
+       }
+
+       return rval;
+}
+
 /**
  * qla4xxx_mailbox_command - issues mailbox commands
  * @ha: Pointer to host adapter structure.
@@ -153,33 +177,28 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
        /*
         * Wait for completion: Poll or completion queue
         */
-       if (test_bit(AF_IRQ_ATTACHED, &ha->flags) &&
-           test_bit(AF_INTERRUPTS_ON, &ha->flags) &&
-           test_bit(AF_ONLINE, &ha->flags) &&
-           !test_bit(AF_HA_REMOVAL, &ha->flags)) {
-               /* Do not poll for completion. Use completion queue */
-               set_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags);
-               wait_for_completion_timeout(&ha->mbx_intr_comp, MBOX_TOV * HZ);
-               clear_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags);
-       } else {
+       if (qla4xxx_is_intr_poll_mode(ha)) {
                /* Poll for command to complete */
                wait_count = jiffies + MBOX_TOV * HZ;
                while (test_bit(AF_MBOX_COMMAND_DONE, &ha->flags) == 0) {
                        if (time_after_eq(jiffies, wait_count))
                                break;
-
                        /*
                         * Service the interrupt.
                         * The ISR will save the mailbox status registers
                         * to a temporary storage location in the adapter
                         * structure.
                         */
-
                        spin_lock_irqsave(&ha->hardware_lock, flags);
                        ha->isp_ops->process_mailbox_interrupt(ha, outCount);
                        spin_unlock_irqrestore(&ha->hardware_lock, flags);
                        msleep(10);
                }
+       } else {
+               /* Do not poll for completion. Use completion queue */
+               set_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags);
+               wait_for_completion_timeout(&ha->mbx_intr_comp, MBOX_TOV * HZ);
+               clear_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags);
        }
 
        /* Check for mailbox timeout. */
index 499a92db1cf64d3b1d44201592b289aefd38cd71..491668d439a249adf33ba380516543c4d4deda1f 100644 (file)
@@ -3463,7 +3463,7 @@ exit_validate_mac82:
 
 /* Interrupt handling helpers. */
 
-int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha)
+int qla4_8xxx_intr_enable(struct scsi_qla_host *ha)
 {
        uint32_t mbox_cmd[MBOX_REG_COUNT];
        uint32_t mbox_sts[MBOX_REG_COUNT];
@@ -3484,7 +3484,7 @@ int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha)
        return QLA_SUCCESS;
 }
 
-int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha)
+int qla4_8xxx_intr_disable(struct scsi_qla_host *ha)
 {
        uint32_t mbox_cmd[MBOX_REG_COUNT];
        uint32_t mbox_sts[MBOX_REG_COUNT];
@@ -3509,7 +3509,7 @@ int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha)
 void
 qla4_82xx_enable_intrs(struct scsi_qla_host *ha)
 {
-       qla4_8xxx_mbx_intr_enable(ha);
+       qla4_8xxx_intr_enable(ha);
 
        spin_lock_irq(&ha->hardware_lock);
        /* BIT 10 - reset */
@@ -3522,7 +3522,7 @@ void
 qla4_82xx_disable_intrs(struct scsi_qla_host *ha)
 {
        if (test_and_clear_bit(AF_INTERRUPTS_ON, &ha->flags))
-               qla4_8xxx_mbx_intr_disable(ha);
+               qla4_8xxx_intr_disable(ha);
 
        spin_lock_irq(&ha->hardware_lock);
        /* BIT 10 - set */
index 4cec123a6a6a5f02503bb3ebc4175479cbe10030..bfd1e68654ab3c8da951a8ad4836763cd6b7f8b3 100644 (file)
@@ -2978,6 +2978,7 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha)
                if (status == QLA_SUCCESS) {
                        if (!test_bit(AF_FW_RECOVERY, &ha->flags))
                                qla4xxx_cmd_wait(ha);
+
                        ha->isp_ops->disable_intrs(ha);
                        qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
                        qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
@@ -3508,10 +3509,8 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
 {
        qla4xxx_abort_active_cmds(ha, DID_NO_CONNECT << 16);
 
-       if (test_bit(AF_INTERRUPTS_ON, &ha->flags)) {
-               /* Turn-off interrupts on the card. */
-               ha->isp_ops->disable_intrs(ha);
-       }
+       /* Turn-off interrupts on the card. */
+       ha->isp_ops->disable_intrs(ha);
 
        if (is_qla40XX(ha)) {
                writel(set_rmask(CSR_SCSI_PROCESSOR_INTR),
@@ -3547,8 +3546,7 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
        }
 
        /* Detach interrupts */
-       if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags))
-               qla4xxx_free_irqs(ha);
+       qla4xxx_free_irqs(ha);
 
        /* free extra memory */
        qla4xxx_mem_free(ha);