diff mbox

[RFC,v2,8/9] hyper_dmabuf: event-polling mechanism for detecting a new hyper_DMABUF

Message ID 20180214015008.9513-9-dongwon.kim@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Kim, Dongwon Feb. 14, 2018, 1:50 a.m. UTC
New method based on polling for a importing VM to know about a new
hyper_DMABUF exported to it.

For this, the userspace now can poll the device node to check if
there a new event, which is created if there's a new hyper_DMABUF
available in importing VM (just exported).

A poll function call was added to the device driver interface for this
new functionality. Event-generation functionalitywas also implemented in
all other relavant parts of driver.

This "event-polling" mechanism is optional feature and can be enabled
by setting a Kernel config option, "HYPER_DMABUF_EVENT_GEN".

Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
Signed-off-by: Mateusz Polrola <mateuszx.potrola@intel.com>
---
 drivers/dma-buf/hyper_dmabuf/Kconfig              |  20 +++
 drivers/dma-buf/hyper_dmabuf/Makefile             |   1 +
 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c   | 146 ++++++++++++++++++++++
 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h   |  11 ++
 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c | 122 ++++++++++++++++++
 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h |  38 ++++++
 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c  |   1 +
 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c   |  11 ++
 include/uapi/linux/hyper_dmabuf.h                 |  11 ++
 9 files changed, 361 insertions(+)
 create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c
 create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h
diff mbox

Patch

diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig
index 68f3d6ce2c1f..92510731af25 100644
--- a/drivers/dma-buf/hyper_dmabuf/Kconfig
+++ b/drivers/dma-buf/hyper_dmabuf/Kconfig
@@ -20,6 +20,16 @@  config HYPER_DMABUF_SYSFS
 
 	  The location of sysfs is under "...."
 
+config HYPER_DMABUF_EVENT_GEN
+        bool "Enable event-generation and polling operation"
+        default n
+        depends on HYPER_DMABUF
+        help
+          With this config enabled, hyper_dmabuf driver on the importer side
+          generates events and queue those up in the event list whenever a new
+          shared DMA-BUF is available. Events in the list can be retrieved by
+          read operation.
+
 config HYPER_DMABUF_XEN
         bool "Configure hyper_dmabuf for XEN hypervisor"
         default y
@@ -27,4 +37,14 @@  config HYPER_DMABUF_XEN
         help
           Enabling Hyper_DMABUF Backend for XEN hypervisor
 
+config HYPER_DMABUF_XEN_AUTO_RX_CH_ADD
+        bool "Enable automatic rx-ch add with 10 secs interval"
+        default y
+        depends on HYPER_DMABUF && HYPER_DMABUF_XEN
+        help
+          If enabled, driver reads a node in xenstore every 10 seconds
+          to check whether there is any tx comm ch configured by another
+          domain then initialize matched rx comm ch automatically for any
+          existing tx comm chs.
+
 endmenu
diff --git a/drivers/dma-buf/hyper_dmabuf/Makefile b/drivers/dma-buf/hyper_dmabuf/Makefile
index 578a669a0d3e..f573dd5c4054 100644
--- a/drivers/dma-buf/hyper_dmabuf/Makefile
+++ b/drivers/dma-buf/hyper_dmabuf/Makefile
@@ -11,6 +11,7 @@  ifneq ($(KERNELRELEASE),)
 				 hyper_dmabuf_id.o \
 				 hyper_dmabuf_remote_sync.o \
 				 hyper_dmabuf_query.o \
+				 hyper_dmabuf_event.o \
 
 ifeq ($(CONFIG_HYPER_DMABUF_XEN), y)
 	$(TARGET_MODULE)-objs += backends/xen/hyper_dmabuf_xen_comm.o \
diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c
index 3320f9dcc769..087f091ccae9 100644
--- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c
+++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c
@@ -41,6 +41,7 @@ 
 #include "hyper_dmabuf_ioctl.h"
 #include "hyper_dmabuf_list.h"
 #include "hyper_dmabuf_id.h"
+#include "hyper_dmabuf_event.h"
 
 #ifdef CONFIG_HYPER_DMABUF_XEN
 #include "backends/xen/hyper_dmabuf_xen_drv.h"
@@ -91,10 +92,138 @@  static int hyper_dmabuf_release(struct inode *inode, struct file *filp)
 	return 0;
 }
 
+#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN
+
+static unsigned int hyper_dmabuf_event_poll(struct file *filp,
+				     struct poll_table_struct *wait)
+{
+	poll_wait(filp, &hy_drv_priv->event_wait, wait);
+
+	if (!list_empty(&hy_drv_priv->event_list))
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static ssize_t hyper_dmabuf_event_read(struct file *filp, char __user *buffer,
+		size_t count, loff_t *offset)
+{
+	int ret;
+
+	/* only root can read events */
+	if (!capable(CAP_DAC_OVERRIDE)) {
+		dev_err(hy_drv_priv->dev,
+			"Only root can read events\n");
+		return -EPERM;
+	}
+
+	/* make sure user buffer can be written */
+	if (!access_ok(VERIFY_WRITE, buffer, count)) {
+		dev_err(hy_drv_priv->dev,
+			"User buffer can't be written.\n");
+		return -EINVAL;
+	}
+
+	ret = mutex_lock_interruptible(&hy_drv_priv->event_read_lock);
+	if (ret)
+		return ret;
+
+	while (1) {
+		struct hyper_dmabuf_event *e = NULL;
+
+		spin_lock_irq(&hy_drv_priv->event_lock);
+		if (!list_empty(&hy_drv_priv->event_list)) {
+			e = list_first_entry(&hy_drv_priv->event_list,
+					struct hyper_dmabuf_event, link);
+			list_del(&e->link);
+		}
+		spin_unlock_irq(&hy_drv_priv->event_lock);
+
+		if (!e) {
+			if (ret)
+				break;
+
+			if (filp->f_flags & O_NONBLOCK) {
+				ret = -EAGAIN;
+				break;
+			}
+
+			mutex_unlock(&hy_drv_priv->event_read_lock);
+			ret = wait_event_interruptible(hy_drv_priv->event_wait,
+				  !list_empty(&hy_drv_priv->event_list));
+
+			if (ret == 0)
+				ret = mutex_lock_interruptible(
+					&hy_drv_priv->event_read_lock);
+
+			if (ret)
+				return ret;
+		} else {
+			unsigned int length = (sizeof(e->event_data.hdr) +
+						      e->event_data.hdr.size);
+
+			if (length > count - ret) {
+put_back_event:
+				spin_lock_irq(&hy_drv_priv->event_lock);
+				list_add(&e->link, &hy_drv_priv->event_list);
+				spin_unlock_irq(&hy_drv_priv->event_lock);
+				break;
+			}
+
+			if (copy_to_user(buffer + ret, &e->event_data.hdr,
+					 sizeof(e->event_data.hdr))) {
+				if (ret == 0)
+					ret = -EFAULT;
+
+				goto put_back_event;
+			}
+
+			ret += sizeof(e->event_data.hdr);
+
+			if (copy_to_user(buffer + ret, e->event_data.data,
+					 e->event_data.hdr.size)) {
+				/* error while copying void *data */
+
+				struct hyper_dmabuf_event_hdr dummy_hdr = {0};
+
+				ret -= sizeof(e->event_data.hdr);
+
+				/* nullifying hdr of the event in user buffer */
+				if (copy_to_user(buffer + ret, &dummy_hdr,
+						 sizeof(dummy_hdr))) {
+					dev_err(hy_drv_priv->dev,
+						"failed to nullify invalid hdr already in userspace\n");
+				}
+
+				ret = -EFAULT;
+
+				goto put_back_event;
+			}
+
+			ret += e->event_data.hdr.size;
+			hy_drv_priv->pending--;
+			kfree(e);
+		}
+	}
+
+	mutex_unlock(&hy_drv_priv->event_read_lock);
+
+	return ret;
+}
+
+#endif
+
 static const struct file_operations hyper_dmabuf_driver_fops = {
 	.owner = THIS_MODULE,
 	.open = hyper_dmabuf_open,
 	.release = hyper_dmabuf_release,
+
+/* poll and read interfaces are needed only for event-polling */
+#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN
+	.read = hyper_dmabuf_event_read,
+	.poll = hyper_dmabuf_event_poll,
+#endif
+
 	.unlocked_ioctl = hyper_dmabuf_ioctl,
 };
 
@@ -194,6 +323,18 @@  static int __init hyper_dmabuf_drv_init(void)
 	}
 #endif
 
+#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN
+	mutex_init(&hy_drv_priv->event_read_lock);
+	spin_lock_init(&hy_drv_priv->event_lock);
+
+	/* Initialize event queue */
+	INIT_LIST_HEAD(&hy_drv_priv->event_list);
+	init_waitqueue_head(&hy_drv_priv->event_wait);
+
+	/* resetting number of pending events */
+	hy_drv_priv->pending = 0;
+#endif
+
 	if (hy_drv_priv->bknd_ops->init) {
 		ret = hy_drv_priv->bknd_ops->init();
 
@@ -250,6 +391,11 @@  static void hyper_dmabuf_drv_exit(void)
 	if (hy_drv_priv->id_queue)
 		hyper_dmabuf_free_hid_list();
 
+#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN
+	/* clean up event queue */
+	hyper_dmabuf_events_release();
+#endif
+
 	mutex_unlock(&hy_drv_priv->lock);
 
 	dev_info(hy_drv_priv->dev,
diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h
index 46119d762430..282a507b33bc 100644
--- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h
+++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h
@@ -32,6 +32,11 @@ 
 
 struct hyper_dmabuf_req;
 
+struct hyper_dmabuf_event {
+	struct hyper_dmabuf_event_data event_data;
+	struct list_head link;
+};
+
 struct hyper_dmabuf_private {
 	struct device *dev;
 
@@ -54,6 +59,12 @@  struct hyper_dmabuf_private {
 	/* flag that shows whether backend is initialized */
 	bool initialized;
 
+	wait_queue_head_t event_wait;
+	struct list_head event_list;
+
+	spinlock_t event_lock;
+	struct mutex event_read_lock;
+
 	/* # of pending events */
 	int pending;
 };
diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c
new file mode 100644
index 000000000000..942a1bb78755
--- /dev/null
+++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c
@@ -0,0 +1,122 @@ 
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Dongwon Kim <dongwon.kim@intel.com>
+ *    Mateusz Polrola <mateuszx.potrola@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include "hyper_dmabuf_drv.h"
+#include "hyper_dmabuf_struct.h"
+#include "hyper_dmabuf_list.h"
+#include "hyper_dmabuf_event.h"
+
+static void send_event(struct hyper_dmabuf_event *e)
+{
+	struct hyper_dmabuf_event *oldest;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&hy_drv_priv->event_lock, irqflags);
+
+	/* check current number of event then if it hits the max num allowed
+	 * then remove the oldest event in the list
+	 */
+	if (hy_drv_priv->pending > MAX_DEPTH_EVENT_QUEUE - 1) {
+		oldest = list_first_entry(&hy_drv_priv->event_list,
+				struct hyper_dmabuf_event, link);
+		list_del(&oldest->link);
+		hy_drv_priv->pending--;
+		kfree(oldest);
+	}
+
+	list_add_tail(&e->link,
+		      &hy_drv_priv->event_list);
+
+	hy_drv_priv->pending++;
+
+	wake_up_interruptible(&hy_drv_priv->event_wait);
+
+	spin_unlock_irqrestore(&hy_drv_priv->event_lock, irqflags);
+}
+
+void hyper_dmabuf_events_release(void)
+{
+	struct hyper_dmabuf_event *e, *et;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&hy_drv_priv->event_lock, irqflags);
+
+	list_for_each_entry_safe(e, et, &hy_drv_priv->event_list,
+				 link) {
+		list_del(&e->link);
+		kfree(e);
+		hy_drv_priv->pending--;
+	}
+
+	if (hy_drv_priv->pending) {
+		dev_err(hy_drv_priv->dev,
+			"possible leak on event_list\n");
+	}
+
+	spin_unlock_irqrestore(&hy_drv_priv->event_lock, irqflags);
+}
+
+int hyper_dmabuf_import_event(hyper_dmabuf_id_t hid)
+{
+	struct hyper_dmabuf_event *e;
+	struct imported_sgt_info *imported;
+
+	imported = hyper_dmabuf_find_imported(hid);
+
+	if (!imported) {
+		dev_err(hy_drv_priv->dev,
+			"can't find imported_sgt_info in the list\n");
+		return -EINVAL;
+	}
+
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+
+	if (!e)
+		return -ENOMEM;
+
+	e->event_data.hdr.event_type = HYPER_DMABUF_NEW_IMPORT;
+	e->event_data.hdr.hid = hid;
+	e->event_data.data = (void *)imported->priv;
+	e->event_data.hdr.size = imported->sz_priv;
+
+	send_event(e);
+
+	dev_dbg(hy_drv_priv->dev,
+		"event number = %d :", hy_drv_priv->pending);
+
+	dev_dbg(hy_drv_priv->dev,
+		"generating events for {%d, %d, %d, %d}\n",
+		imported->hid.id, imported->hid.rng_key[0],
+		imported->hid.rng_key[1], imported->hid.rng_key[2]);
+
+	return 0;
+}
diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h
new file mode 100644
index 000000000000..8f61198e623c
--- /dev/null
+++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h
@@ -0,0 +1,38 @@ 
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __HYPER_DMABUF_EVENT_H__
+#define __HYPER_DMABUF_EVENT_H__
+
+#define MAX_DEPTH_EVENT_QUEUE 32
+
+enum hyper_dmabuf_event_type {
+	HYPER_DMABUF_NEW_IMPORT = 0x10000,
+};
+
+void hyper_dmabuf_events_release(void);
+
+int hyper_dmabuf_import_event(hyper_dmabuf_id_t hid);
+
+#endif /* __HYPER_DMABUF_EVENT_H__ */
diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c
index f2f65a8ec47f..30c3af65fcde 100644
--- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c
+++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c
@@ -36,6 +36,7 @@ 
 #include "hyper_dmabuf_drv.h"
 #include "hyper_dmabuf_list.h"
 #include "hyper_dmabuf_id.h"
+#include "hyper_dmabuf_event.h"
 
 DECLARE_HASHTABLE(hyper_dmabuf_hash_imported, MAX_ENTRY_IMPORTED);
 DECLARE_HASHTABLE(hyper_dmabuf_hash_exported, MAX_ENTRY_EXPORTED);
diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c
index 1592d5cfaa52..8f2cf7ea827d 100644
--- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c
+++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c
@@ -35,6 +35,7 @@ 
 #include "hyper_dmabuf_drv.h"
 #include "hyper_dmabuf_msg.h"
 #include "hyper_dmabuf_remote_sync.h"
+#include "hyper_dmabuf_event.h"
 #include "hyper_dmabuf_list.h"
 
 struct cmd_process {
@@ -179,6 +180,11 @@  static void cmd_process_work(struct work_struct *work)
 			/* updating priv data */
 			memcpy(imported->priv, &req->op[9], req->op[8]);
 
+#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN
+			/* generating import event */
+			hyper_dmabuf_import_event(imported->hid);
+#endif
+
 			break;
 		}
 
@@ -219,6 +225,11 @@  static void cmd_process_work(struct work_struct *work)
 		imported->valid = true;
 		hyper_dmabuf_register_imported(imported);
 
+#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN
+		/* generating import event */
+		hyper_dmabuf_import_event(imported->hid);
+#endif
+
 		break;
 
 	case HYPER_DMABUF_OPS_TO_REMOTE:
diff --git a/include/uapi/linux/hyper_dmabuf.h b/include/uapi/linux/hyper_dmabuf.h
index 4f8e8ac0375b..dd73db9bf37d 100644
--- a/include/uapi/linux/hyper_dmabuf.h
+++ b/include/uapi/linux/hyper_dmabuf.h
@@ -32,6 +32,17 @@  typedef struct {
 	int rng_key[3]; /* 12bytes long random number */
 } hyper_dmabuf_id_t;
 
+struct hyper_dmabuf_event_hdr {
+	int event_type; /* one type only for now - new import */
+	hyper_dmabuf_id_t hid; /* hyper_dmabuf_id of specific hyper_dmabuf */
+	int size; /* size of data */
+};
+
+struct hyper_dmabuf_event_data {
+	struct hyper_dmabuf_event_hdr hdr;
+	void *data; /* private data */
+};
+
 #define IOCTL_HYPER_DMABUF_TX_CH_SETUP \
 _IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_hyper_dmabuf_tx_ch_setup))
 struct ioctl_hyper_dmabuf_tx_ch_setup {