IB/ipath: Fix check for no interrupts to reliably fallback to INTx
authorDave Olson <dave.olson@qlogic.com>
Thu, 17 Apr 2008 04:09:30 +0000 (21:09 -0700)
committerRoland Dreier <rolandd@cisco.com>
Thu, 17 Apr 2008 04:09:30 +0000 (21:09 -0700)
Newer HCAs support MSI interrupts and also INTx interrupts.  Fix the
code so that INTx can be reliably enabled if MSI interrupts are not
working.

Signed-off-by: Dave Olson <dave.olson@qlogic.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
drivers/infiniband/hw/ipath/ipath_driver.c
drivers/infiniband/hw/ipath/ipath_init_chip.c
drivers/infiniband/hw/ipath/ipath_kernel.h

index 1245180f210cc3e8e74067a1ee999d95501f1543..a3141bbc177a41762afad3b7600796fefd9e355a 100644 (file)
@@ -138,19 +138,6 @@ static struct pci_driver ipath_driver = {
        },
 };
 
-static void ipath_check_status(struct work_struct *work)
-{
-       struct ipath_devdata *dd = container_of(work, struct ipath_devdata,
-                                               status_work.work);
-
-       /*
-        * If we don't have any interrupts, let the user know and
-        * don't bother checking again.
-        */
-       if (dd->ipath_int_counter == 0)
-               dev_err(&dd->pcidev->dev, "No interrupts detected.\n");
-}
-
 static inline void read_bars(struct ipath_devdata *dd, struct pci_dev *dev,
                             u32 *bar0, u32 *bar1)
 {
@@ -218,8 +205,6 @@ static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev)
        dd->pcidev = pdev;
        pci_set_drvdata(pdev, dd);
 
-       INIT_DELAYED_WORK(&dd->status_work, ipath_check_status);
-
        list_add(&dd->ipath_list, &ipath_dev_list);
 
 bail_unlock:
@@ -620,9 +605,6 @@ static int __devinit ipath_init_one(struct pci_dev *pdev,
        ipath_diag_add(dd);
        ipath_register_ib_device(dd);
 
-       /* Check that card status in STATUS_TIMEOUT seconds. */
-       schedule_delayed_work(&dd->status_work, HZ * STATUS_TIMEOUT);
-
        goto bail;
 
 bail_irqsetup:
@@ -753,7 +735,6 @@ static void __devexit ipath_remove_one(struct pci_dev *pdev)
         */
        ipath_shutdown_device(dd);
 
-       cancel_delayed_work(&dd->status_work);
        flush_scheduled_work();
 
        if (dd->verbs_dev)
@@ -2195,6 +2176,10 @@ void ipath_shutdown_device(struct ipath_devdata *dd)
                del_timer_sync(&dd->ipath_stats_timer);
                dd->ipath_stats_timer_active = 0;
        }
+       if (dd->ipath_intrchk_timer.data) {
+               del_timer_sync(&dd->ipath_intrchk_timer);
+               dd->ipath_intrchk_timer.data = 0;
+       }
 
        /*
         * clear all interrupts and errors, so that the next time the driver
index 1adafa97e0825f28339baaf121deb3f9ad1f7ea2..0db19c1b45c3e2e638f74e956d1e299b8f8e64aa 100644 (file)
@@ -665,6 +665,28 @@ done:
        return ret;
 }
 
+static void verify_interrupt(unsigned long opaque)
+{
+       struct ipath_devdata *dd = (struct ipath_devdata *) opaque;
+
+       if (!dd)
+               return; /* being torn down */
+
+       /*
+        * If we don't have any interrupts, let the user know and
+        * don't bother checking again.
+        */
+       if (dd->ipath_int_counter == 0) {
+               if (!dd->ipath_f_intr_fallback(dd))
+                       dev_err(&dd->pcidev->dev, "No interrupts detected, "
+                               "not usable.\n");
+               else /* re-arm the timer to see if fallback works */
+                       mod_timer(&dd->ipath_intrchk_timer, jiffies + HZ/2);
+       } else
+               ipath_cdbg(VERBOSE, "%u interrupts at timer check\n",
+                       dd->ipath_int_counter);
+}
+
 /**
  * ipath_init_chip - do the actual initialization sequence on the chip
  * @dd: the infinipath device
@@ -968,6 +990,20 @@ done:
                                         0ULL);
                        /* chip is usable; mark it as initialized */
                        *dd->ipath_statusp |= IPATH_STATUS_INITTED;
+
+                       /*
+                        * setup to verify we get an interrupt, and fallback
+                        * to an alternate if necessary and possible
+                        */
+                       if (!reinit) {
+                               init_timer(&dd->ipath_intrchk_timer);
+                               dd->ipath_intrchk_timer.function =
+                                       verify_interrupt;
+                               dd->ipath_intrchk_timer.data =
+                                       (unsigned long) dd;
+                       }
+                       dd->ipath_intrchk_timer.expires = jiffies + HZ/2;
+                       add_timer(&dd->ipath_intrchk_timer);
                } else
                        ipath_dev_err(dd, "No interrupts enabled, couldn't "
                                      "setup interrupt address\n");
index cef3296efb962b721afab650b269794c887cbc4c..56e51cd7d6b9df04c3b99353e5f66bc9db05c7b1 100644 (file)
@@ -426,6 +426,8 @@ struct ipath_devdata {
        struct class_device *diag_class_dev;
        /* timer used to prevent stats overflow, error throttling, etc. */
        struct timer_list ipath_stats_timer;
+       /* timer to verify interrupts work, and fallback if possible */
+       struct timer_list ipath_intrchk_timer;
        void *ipath_dummy_hdrq; /* used after port close */
        dma_addr_t ipath_dummy_hdrq_phys;
 
@@ -629,9 +631,6 @@ struct ipath_devdata {
        u32 ipath_overrun_thresh_errs;
        u32 ipath_lli_errs;
 
-       /* status check work */
-       struct delayed_work status_work;
-
        /*
         * Not all devices managed by a driver instance are the same
         * type, so these fields must be per-device.