Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ebab649 authored by Tim Sell's avatar Tim Sell Committed by Greg Kroah-Hartman
Browse files

staging: unisys: visorbus: use kernel timer instead of workqueue



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: default avatarTim Sell <Timothy.Sell@unisys.com>
Signed-off-by: default avatarDavid Kershner <david.kershner@unisys.com>
Acked-By: default avatarNeil Horman <nhorman@tuxdriver.com>
Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 83011b6c
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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;
+15 −39
Original line number Diff line number Diff line
@@ -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,34 +524,35 @@ 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))
	if (!dev->timer_active)
		return;
	del_timer_sync(&dev->timer);
	dev->timer_active = false;
	put_device(&dev->device);
}

@@ -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,