staging: unisys: visorbus: use kernel timer instead of workqueue
authorTim Sell <Timothy.Sell@unisys.com>
Sat, 11 Jun 2016 01:48:08 +0000 (21:48 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 15 Aug 2016 18:44:24 +0000 (20:44 +0200)
A kernel timer is now used as the vehicle to periodically call the
channel_interrupt function of registered visor drivers, instead of a
workqueue.

This simplifies a lot of things by making periodic_work.c and
periodic_work.h no longer necessary.  This change also means that the
channel_interrupt() callbacks registered by visor drivers (via
visorbus_register_visor_driver()) will now be called in atomic context
(i.e., canNOT sleep) rather than kernel thread context (CAN sleep).
Fortunately this did NOT necessitate any change to the existing
channel_interrupt() callbacks, because none of them ever perform any
operations that would be invalid in atomic context.

Signed-off-by: Tim Sell <Timothy.Sell@unisys.com>
Signed-off-by: David Kershner <david.kershner@unisys.com>
Acked-By: Neil Horman <nhorman@tuxdriver.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/unisys/include/visorbus.h
drivers/staging/unisys/visorbus/visorbus_main.c

index 9baf1ec70d0132eab09b751cc21eafbcf6ab43d4..9bb88bbb2078412cd563ac8349c0faaafa76669f 100644 (file)
@@ -34,8 +34,9 @@
 #include <linux/poll.h>
 #include <linux/kernel.h>
 #include <linux/uuid.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
 
-#include "periodic_work.h"
 #include "channel.h"
 
 struct visor_driver;
@@ -126,8 +127,8 @@ struct visor_driver {
  * device:                     Device struct meant for use by the bus driver
  *                             only.
  * list_all:                   Used by the bus driver to enumerate devices.
- * periodic_work:              Device work queue. Private use by bus driver
- *                             only.
+ * timer:                      Timer fired periodically to do interrupt-type
+ *                             activity.
  * being_removed:              Indicates that the device is being removed from
  *                             the bus. Private bus driver use only.
  * visordriver_callback_lock:  Used by the bus driver to lock when handling
@@ -157,7 +158,8 @@ struct visor_device {
        /* These fields are for private use by the bus driver only. */
        struct device device;
        struct list_head list_all;
-       struct periodic_work *periodic_work;
+       struct timer_list timer;
+       bool timer_active;
        bool being_removed;
        struct semaphore visordriver_callback_lock;
        bool pausing;
index ebdd5de1feb72e9532a24419c30a5fdadf351018..e98e720fb8685b4ce8ccf4fc58a6b5c0a8341935 100644 (file)
@@ -19,7 +19,6 @@
 #include "visorbus.h"
 #include "visorbus_private.h"
 #include "version.h"
-#include "periodic_work.h"
 #include "vbuschannel.h"
 #include "guestlinuxdebug.h"
 #include "vmcallinterface.h"
@@ -116,7 +115,6 @@ struct bus_type visorbus_type = {
        .bus_groups = visorbus_bus_groups,
 };
 
-static struct workqueue_struct *periodic_dev_workqueue;
 static long long bus_count;    /** number of bus instances */
                                        /** ever-increasing */
 
@@ -222,10 +220,6 @@ visorbus_release_device(struct device *xdev)
 {
        struct visor_device *dev = to_visor_device(xdev);
 
-       if (dev->periodic_work) {
-               visor_periodic_work_destroy(dev->periodic_work);
-               dev->periodic_work = NULL;
-       }
        if (dev->visorchannel) {
                visorchannel_destroy(dev->visorchannel);
                dev->visorchannel = NULL;
@@ -530,35 +524,36 @@ unregister_driver_attributes(struct visor_driver *drv)
 }
 
 static void
-dev_periodic_work(void *xdev)
+dev_periodic_work(unsigned long __opaque)
 {
-       struct visor_device *dev = xdev;
+       struct visor_device *dev = (struct visor_device *)__opaque;
        struct visor_driver *drv = to_visor_driver(dev->device.driver);
 
-       down(&dev->visordriver_callback_lock);
        if (drv->channel_interrupt)
                drv->channel_interrupt(dev);
-       up(&dev->visordriver_callback_lock);
-       if (!visor_periodic_work_nextperiod(dev->periodic_work))
-               put_device(&dev->device);
+       mod_timer(&dev->timer, jiffies + POLLJIFFIES_NORMALCHANNEL);
 }
 
 static void
 dev_start_periodic_work(struct visor_device *dev)
 {
-       if (dev->being_removed)
+       if (dev->being_removed || dev->timer_active)
                return;
        /* now up by at least 2 */
        get_device(&dev->device);
-       if (!visor_periodic_work_start(dev->periodic_work))
-               put_device(&dev->device);
+       dev->timer.expires = jiffies + POLLJIFFIES_NORMALCHANNEL;
+       add_timer(&dev->timer);
+       dev->timer_active = true;
 }
 
 static void
 dev_stop_periodic_work(struct visor_device *dev)
 {
-       if (visor_periodic_work_stop(dev->periodic_work))
-               put_device(&dev->device);
+       if (!dev->timer_active)
+               return;
+       del_timer_sync(&dev->timer);
+       dev->timer_active = false;
+       put_device(&dev->device);
 }
 
 /** This is called automatically upon adding a visor_device (device_add), or
@@ -776,17 +771,9 @@ create_visor_device(struct visor_device *dev)
        dev->device.release = visorbus_release_device;
        /* keep a reference just for us (now 2) */
        get_device(&dev->device);
-       dev->periodic_work =
-               visor_periodic_work_create(POLLJIFFIES_NORMALCHANNEL,
-                                          periodic_dev_workqueue,
-                                          dev_periodic_work,
-                                          dev, dev_name(&dev->device));
-       if (!dev->periodic_work) {
-               POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, chipset_dev_no,
-                                DIAG_SEVERITY_ERR);
-               err = -EINVAL;
-               goto err_put;
-       }
+       init_timer(&dev->timer);
+       dev->timer.data = (unsigned long)(dev);
+       dev->timer.function = dev_periodic_work;
 
        /* bus_id must be a unique name with respect to this bus TYPE
         * (NOT bus instance).  That's why we need to include the bus
@@ -1268,13 +1255,6 @@ visorbus_init(void)
                goto error;
        }
 
-       periodic_dev_workqueue = create_singlethread_workqueue("visorbus_dev");
-       if (!periodic_dev_workqueue) {
-               POSTCODE_LINUX_2(CREATE_WORKQUEUE_PC, DIAG_SEVERITY_ERR);
-               err = -ENOMEM;
-               goto error;
-       }
-
        /* This enables us to receive notifications when devices appear for
         * which this service partition is to be a server for.
         */
@@ -1297,10 +1277,6 @@ visorbus_exit(void)
        visorchipset_register_busdev(NULL, NULL, NULL);
        remove_all_visor_devices();
 
-       flush_workqueue(periodic_dev_workqueue); /* better not be any work! */
-       destroy_workqueue(periodic_dev_workqueue);
-       periodic_dev_workqueue = NULL;
-
        list_for_each_safe(listentry, listtmp, &list_all_bus_instances) {
                struct visor_device *dev = list_entry(listentry,
                                                      struct visor_device,