From: David Kershner <david.kersh...@unisys.com>

The new interrupt code was NOT distinguishing between the availability of
an irq (i.e., visor_device.irq != 0), and the fact that we were in fact
operating in real interrupt mode (i.e., request_irq() succeeded).  This
could cause us to do the wrong thing in visorbus_enable_channel_interrupts,
visorbus_disable_channel_interrupts and visorbus_rearm_channel_interrupts.
This was fixed by adding a boolean named visor_device.irq_requested, which
is true iff request_irq() succeeded.

We were not properly restoring interrupt-related state after a failed
attempt of visorbus_register_channel_interrupts(), nor after the device
was unattached from a function driver.  Most-notably, we did NOT call
free_irq() (the book-end to request_irq()).  This was fixed via a new
visorbus_unregister_for_channel_interrupts.

Signed-off-by: David Kershner <david.kersh...@unisys.com>
Signed-off-by: Tim Sell <timothy.s...@unisys.com>
Signed-off-by: Benjamin Romer <benjamin.ro...@unisys.com>
---
 drivers/staging/unisys/include/visorbus.h       |  2 +
 drivers/staging/unisys/visorbus/visorbus_main.c | 65 ++++++++++++++++++++++---
 2 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/drivers/staging/unisys/include/visorbus.h 
b/drivers/staging/unisys/include/visorbus.h
index e2251f7..23f4704 100644
--- a/drivers/staging/unisys/include/visorbus.h
+++ b/drivers/staging/unisys/include/visorbus.h
@@ -162,6 +162,7 @@ struct visor_device {
        int irq;
        int wait_ms;
        int recv_queue;         /* specifies which queue to receive msgs on */
+       bool irq_requested;     /* true iff request_irq() succeeded */
 };
 
 #define to_visor_device(x) container_of(x, struct visor_device, device)
@@ -181,6 +182,7 @@ int visorbus_registerdevnode(struct visor_device *dev,
                             const char *name, int major, int minor);
 int visorbus_register_for_channel_interrupts(struct visor_device *dev,
                                             u32 queue);
+int visorbus_unregister_for_channel_interrupts(struct visor_device *dev);
 void visorbus_enable_channel_interrupts(struct visor_device *dev);
 void visorbus_disable_channel_interrupts(struct visor_device *dev);
 void visorbus_rearm_channel_interrupts(struct visor_device *dev);
diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c 
b/drivers/staging/unisys/visorbus/visorbus_main.c
index 962af12..27aa3df 100644
--- a/drivers/staging/unisys/visorbus/visorbus_main.c
+++ b/drivers/staging/unisys/visorbus/visorbus_main.c
@@ -723,6 +723,14 @@ dev_periodic_work(void *xdev)
        struct visor_device *dev = xdev;
        struct visor_driver *drv = to_visor_driver(dev->device.driver);
 
+       /*
+        * Note that channel_interrupt() is called withOUT
+        * visordriver_callback_lock(), unlike the other callbacks.  This both
+        * makes the behavior consistent between polling versus NON-polling
+        * modes, and enables us to handle I/Os while in the function driver's
+        * probe() function, which is necessary particularly for the
+        * scsi_scan_host() call in the visorhba case.
+        */
        if (drv->channel_interrupt)
                drv->channel_interrupt(dev);
 }
@@ -734,8 +742,10 @@ dev_start_periodic_work(struct visor_device *dev)
                return;
        /* now up by at least 2 */
        get_device(&dev->device);
-       if (!visor_periodic_work_start(dev->periodic_work))
+       if (!visor_periodic_work_start(dev->periodic_work)) {
+               dev_err(&dev->device, "%s failed\n", __func__);
                put_device(&dev->device);
+       }
 }
 
 static void
@@ -786,13 +796,18 @@ visordriver_probe_device(struct device *xdev)
        get_device(&dev->device);
        visorbus_set_channel_state(dev, CHANNELCLI_OWNED);
        if (!drv->probe) {
+               dev_err(xdev, "%s function driver provide no probe()\n",
+                       __func__);
                up(&dev->visordriver_callback_lock);
                rc = -1;
                goto away;
        }
        rc = drv->probe(dev);
-       if (rc < 0)
+       if (rc < 0) {
+               dev_err(xdev, "%s function driver probe() errored\n",
+                       __func__);
                goto away;
+       }
 
        fix_vbus_dev_info(dev);
        up(&dev->visordriver_callback_lock);
@@ -830,6 +845,7 @@ visordriver_remove_device(struct device *xdev)
        }
        up(&dev->visordriver_callback_lock);
        dev_stop_periodic_work(dev);
+       visorbus_unregister_for_channel_interrupts(dev);
        devmajorminor_remove_all_files(dev);
 
        visorbus_set_channel_state(dev, CHANNELCLI_ATTACHED);
@@ -964,7 +980,7 @@ EXPORT_SYMBOL_GPL(visorbus_registerdevnode);
 void
 visorbus_rearm_channel_interrupts(struct visor_device *dev)
 {
-       if (dev->irq)
+       if (dev->irq_requested)
                visorchannel_set_sig_features(dev->visorchannel,
                                              dev->recv_queue,
                                              ULTRA_CHANNEL_ENABLE_INTS);
@@ -976,24 +992,30 @@ EXPORT_SYMBOL_GPL(visorbus_rearm_channel_interrupts);
 void
 visorbus_enable_channel_interrupts(struct visor_device *dev)
 {
-       if (dev->irq)
+       if (dev->irq_requested) {
+               dev_dbg(&dev->device, "%s real interrupts\n", __func__);
                visorchannel_set_sig_features(dev->visorchannel,
                                              dev->recv_queue,
                                              ULTRA_CHANNEL_ENABLE_INTS);
-       else
+       } else {
+               dev_dbg(&dev->device, "%s polling\n", __func__);
                dev_start_periodic_work(dev);
+       }
 }
 EXPORT_SYMBOL_GPL(visorbus_enable_channel_interrupts);
 
 void
 visorbus_disable_channel_interrupts(struct visor_device *dev)
 {
-       if (!dev->irq)
+       if (!dev->irq_requested) {
+               dev_dbg(&dev->device, "%s real interrupts\n", __func__);
                visorchannel_clear_sig_features(dev->visorchannel,
                                                dev->recv_queue,
                                                ULTRA_CHANNEL_ENABLE_INTS);
-       else
+       } else {
+               dev_dbg(&dev->device, "%s polling\n", __func__);
                dev_stop_periodic_work(dev);
+       }
 }
 EXPORT_SYMBOL_GPL(visorbus_disable_channel_interrupts);
 
@@ -1074,6 +1096,32 @@ int visorbus_clear_channel_features(struct visor_device 
*dev, u64 feature_bits)
        return err;
 }
 
+int visorbus_unregister_for_channel_interrupts(struct visor_device *dev)
+{
+       int err = 0;
+
+       if (dev->irq_requested) {
+               free_irq(dev->irq, dev);
+               dev->irq_requested = false;
+       }
+       err = visorbus_set_channel_features(dev, ULTRA_IO_CHANNEL_IS_POLLING);
+       if (err) {
+               dev_err(&dev->device,
+                       "%s failed to set polling flag from chan (%d)\n",
+                       __func__, err);
+       }
+       err = visorbus_clear_channel_features(dev,
+                                             ULTRA_IO_DRIVER_ENABLES_INTS |
+                                             ULTRA_IO_DRIVER_DISABLES_INTS);
+       if (err) {
+               dev_err(&dev->device,
+                       "%s failed to clear enable ints from chan (%d)\n",
+                       __func__, err);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(visorbus_unregister_for_channel_interrupts);
+
 #define INTERRUPT_VECTOR_MASK 0x3f
 int visorbus_register_for_channel_interrupts(struct visor_device *dev,
                                             u32 queue)
@@ -1093,6 +1141,7 @@ int visorbus_register_for_channel_interrupts(struct 
visor_device *dev,
        }
 
        dev_info(&dev->device, "IRQ=%d registered\n", dev->irq);
+       dev->irq_requested = true;
 
        err = visorbus_set_channel_features(dev, ULTRA_IO_DRIVER_ENABLES_INTS |
                                            ULTRA_IO_DRIVER_DISABLES_INTS);
@@ -1116,7 +1165,7 @@ int visorbus_register_for_channel_interrupts(struct 
visor_device *dev,
        return 0;
 
 stay_in_polling:
-       dev->irq = 0;
+       visorbus_unregister_for_channel_interrupts(dev);
        return err;
 }
 EXPORT_SYMBOL_GPL(visorbus_register_for_channel_interrupts);
-- 
2.5.0

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to