[RFC v3 3/3] vhost: Add Vdmabuf backend

Vivek Kasireddy vivek.kasireddy at intel.com
Wed Feb 3 07:35:17 UTC 2021


This backend acts as the counterpart to the Vdmabuf Virtio frontend.
When it receives a new export event from the frontend, it raises an
event to alert the Qemu UI/userspace. Qemu then "imports" this buffer
using the Unique ID.

As part of the import step, a new dmabuf is created on the Host using
the page information obtained from the Guest. The fd associated with
this dmabuf is made available to Qemu UI/userspace which then creates
a texture from it for the purpose of displaying it.

Signed-off-by: Dongwon Kim <dongwon.kim at intel.com>
Signed-off-by: Vivek Kasireddy <vivek.kasireddy at intel.com>
---
 drivers/vhost/Kconfig      |    9 +
 drivers/vhost/Makefile     |    3 +
 drivers/vhost/vdmabuf.c    | 1446 ++++++++++++++++++++++++++++++++++++
 include/uapi/linux/vhost.h |    3 +
 4 files changed, 1461 insertions(+)
 create mode 100644 drivers/vhost/vdmabuf.c

diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
index 587fbae06182..9a99cc2611ca 100644
--- a/drivers/vhost/Kconfig
+++ b/drivers/vhost/Kconfig
@@ -89,4 +89,13 @@ config VHOST_CROSS_ENDIAN_LEGACY
 
 	  If unsure, say "N".
 
+config VHOST_VDMABUF
+	bool "Vhost backend for the Vdmabuf driver"
+	depends on KVM && EVENTFD
+	select VHOST
+	default n
+	help
+	  This driver works in pair with the Virtio Vdmabuf frontend. It can
+	  be used to create a dmabuf using the pages shared by the Guest.
+
 endif
diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile
index f3e1897cce85..5c2cea4a7eaf 100644
--- a/drivers/vhost/Makefile
+++ b/drivers/vhost/Makefile
@@ -17,3 +17,6 @@ obj-$(CONFIG_VHOST)	+= vhost.o
 
 obj-$(CONFIG_VHOST_IOTLB) += vhost_iotlb.o
 vhost_iotlb-y := iotlb.o
+
+obj-$(CONFIG_VHOST_VDMABUF) += vhost_vdmabuf.o
+vhost_vdmabuf-y := vdmabuf.o
diff --git a/drivers/vhost/vdmabuf.c b/drivers/vhost/vdmabuf.c
new file mode 100644
index 000000000000..1d6e9bcf6648
--- /dev/null
+++ b/drivers/vhost/vdmabuf.c
@@ -0,0 +1,1446 @@
+// SPDX-License-Identifier: (MIT OR GPL-2.0)
+
+/*
+ * Copyright © 2021 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 at intel.com>
+ *    Mateusz Polrola <mateusz.polrola at gmail.com>
+ *    Vivek Kasireddy <vivek.kasireddy at intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/hashtable.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/dma-buf.h>
+#include <linux/vhost.h>
+#include <linux/vfio.h>
+#include <linux/kvm_host.h>
+#include <linux/virtio_vdmabuf.h>
+
+#include "vhost.h"
+
+#define REFS_PER_PAGE (PAGE_SIZE/sizeof(long))
+
+enum {
+	VHOST_VDMABUF_FEATURES = VHOST_FEATURES,
+};
+
+static struct virtio_vdmabuf_info *drv_info;
+
+struct kvm_instance {
+	struct kvm *kvm;
+	struct list_head link;
+};
+
+struct vhost_vdmabuf {
+	struct vhost_dev dev;
+	struct vhost_virtqueue vqs[VDMABUF_VQ_MAX];
+	struct vhost_work send_work;
+	struct virtio_vdmabuf_event_queue *evq;
+	u64 vmid;
+
+	struct list_head msg_list;
+	struct list_head list;
+	struct kvm *kvm;
+};
+
+static inline void vhost_vdmabuf_add(struct vhost_vdmabuf *new)
+{
+	list_add_tail(&new->list, &drv_info->head_vdmabuf_list);
+}
+
+static inline struct vhost_vdmabuf *vhost_vdmabuf_find(u64 vmid)
+{
+	struct vhost_vdmabuf *found;
+
+	list_for_each_entry(found, &drv_info->head_vdmabuf_list, list)
+		if (found->vmid == vmid)
+			return found;
+
+	return NULL;
+}
+
+static inline bool vhost_vdmabuf_del(struct vhost_vdmabuf *vdmabuf)
+{
+	struct vhost_vdmabuf *iter, *temp;
+
+	list_for_each_entry_safe(iter, temp,
+				 &drv_info->head_vdmabuf_list,
+				 list)
+		if (iter == vdmabuf) {
+			list_del(&iter->list);
+			return true;
+		}
+
+	return false;
+}
+
+static inline void vhost_vdmabuf_del_all(void)
+{
+	struct vhost_vdmabuf *iter, *temp;
+
+	list_for_each_entry_safe(iter, temp,
+				 &drv_info->head_vdmabuf_list,
+				 list) {
+		list_del(&iter->list);
+		kfree(iter);
+	}
+}
+
+static void *map_gpa(struct kvm_vcpu *vcpu, gpa_t gpa)
+{
+	struct kvm_host_map map;
+	int ret;
+
+	ret = kvm_vcpu_map(vcpu, gpa_to_gfn(gpa), &map);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	else
+		return map.hva;
+}
+
+static void unmap_hva(struct kvm_vcpu *vcpu, gpa_t hva)
+{
+	struct page *page = virt_to_page(hva);
+	struct kvm_host_map map;
+
+	map.hva = (void *)hva;
+	map.page = page;
+
+	kvm_vcpu_unmap(vcpu, &map, true);
+}
+
+/* mapping guest's pages for the vdmabuf */
+static int
+vhost_vdmabuf_map_pages(u64 vmid,
+		        struct virtio_vdmabuf_shared_pages *pages_info)
+{
+	struct vhost_vdmabuf *vdmabuf = vhost_vdmabuf_find(vmid);
+	struct kvm_vcpu *vcpu;
+	void *paddr;
+	int npgs = REFS_PER_PAGE;
+	int last_nents, n_l2refs;
+	int i, j = 0, k = 0;
+
+	if (!vdmabuf || !vdmabuf->kvm || !pages_info || pages_info->pages)
+		return -EINVAL;
+
+	vcpu = kvm_get_vcpu_by_id(vdmabuf->kvm, 0);
+	if (!vcpu)
+		return -EINVAL;
+
+	last_nents = (pages_info->nents - 1) % npgs + 1;
+	n_l2refs = (pages_info->nents / npgs) + ((last_nents > 0) ? 1 : 0) -
+		   (last_nents == npgs);
+
+	pages_info->pages = kcalloc(pages_info->nents, sizeof(struct page *),
+				    GFP_KERNEL);
+	if (!pages_info->pages)
+		goto fail_page_alloc;
+
+	pages_info->l2refs = kcalloc(n_l2refs, sizeof(gpa_t *), GFP_KERNEL);
+	if (!pages_info->l2refs)
+		goto fail_l2refs;
+
+	pages_info->l3refs = (gpa_t *)map_gpa(vcpu, pages_info->ref);
+	if (IS_ERR(pages_info->l3refs))
+		goto fail_l3refs;
+
+	for (i = 0; i < n_l2refs; i++) {
+		pages_info->l2refs[i] = (gpa_t *)map_gpa(vcpu,
+							 pages_info->l3refs[i]);
+
+		if (IS_ERR(pages_info->l2refs[i]))
+			goto fail_mapping_l2;
+
+		/* last level-2 ref */
+		if (i == n_l2refs - 1)
+			npgs = last_nents;
+
+		for (j = 0; j < npgs; j++) {
+			paddr = map_gpa(vcpu, pages_info->l2refs[i][j]);
+			if (IS_ERR(paddr))
+				goto fail_mapping_l1;
+
+			pages_info->pages[k] = virt_to_page(paddr);
+			k++;
+		}
+		unmap_hva(vcpu, pages_info->l3refs[i]);
+	}
+
+	unmap_hva(vcpu, pages_info->ref);
+
+	return 0;
+
+fail_mapping_l1:
+	for (k = 0; k < j; k++)
+		unmap_hva(vcpu, pages_info->l2refs[i][k]);
+
+fail_mapping_l2:
+	for (j = 0; j < i; j++) {
+		for (k = 0; k < REFS_PER_PAGE; k++)
+			unmap_hva(vcpu, pages_info->l2refs[i][k]);
+	}
+
+	unmap_hva(vcpu, pages_info->l3refs[i]);
+	unmap_hva(vcpu, pages_info->ref);
+
+fail_l3refs:
+	kfree(pages_info->l2refs);
+
+fail_l2refs:
+	kfree(pages_info->pages);
+
+fail_page_alloc:
+	return -ENOMEM;
+}
+
+/* unmapping mapped pages */
+static int
+vhost_vdmabuf_unmap_pages(u64 vmid,
+			  struct virtio_vdmabuf_shared_pages *pages_info)
+{
+	struct vhost_vdmabuf *vdmabuf = vhost_vdmabuf_find(vmid);
+	struct kvm_vcpu *vcpu;
+	int last_nents = (pages_info->nents - 1) % REFS_PER_PAGE + 1;
+	int n_l2refs = (pages_info->nents / REFS_PER_PAGE) +
+		       ((last_nents > 0) ? 1 : 0) -
+		       (last_nents == REFS_PER_PAGE);
+	int i, j;
+
+	if (!vdmabuf || !vdmabuf->kvm || !pages_info || pages_info->pages)
+		return -EINVAL;
+
+	vcpu = kvm_get_vcpu_by_id(vdmabuf->kvm, 0);
+	if (!vcpu)
+		return -EINVAL;
+
+	for (i = 0; i < n_l2refs - 1; i++) {
+		for (j = 0; j < REFS_PER_PAGE; j++)
+			unmap_hva(vcpu, pages_info->l2refs[i][j]);
+	}
+
+	for (j = 0; j < last_nents; j++)
+		unmap_hva(vcpu, pages_info->l2refs[i][j]);
+
+	kfree(pages_info->l2refs);
+	kfree(pages_info->pages);
+	pages_info->pages = NULL;
+
+	return 0;
+}
+
+/* create sg_table with given pages and other parameters */
+static struct sg_table *new_sgt(struct page **pgs,
+				int first_ofst, int last_len,
+				int nents)
+{
+	struct sg_table *sgt;
+	struct scatterlist *sgl;
+	int i, ret;
+
+	sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+	if (!sgt)
+		return NULL;
+
+	ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
+	if (ret) {
+		kfree(sgt);
+		return NULL;
+	}
+
+	sgl = sgt->sgl;
+	sg_set_page(sgl, pgs[0], PAGE_SIZE-first_ofst, first_ofst);
+
+	for (i = 1; i < nents-1; i++) {
+		sgl = sg_next(sgl);
+		sg_set_page(sgl, pgs[i], PAGE_SIZE, 0);
+	}
+
+	/* more than 1 page */
+	if (nents > 1) {
+		sgl = sg_next(sgl);
+		sg_set_page(sgl, pgs[i], last_len, 0);
+	}
+
+	return sgt;
+}
+
+static struct sg_table
+*vhost_vdmabuf_dmabuf_map(struct dma_buf_attachment *attachment,
+			  enum dma_data_direction dir)
+{
+	struct virtio_vdmabuf_buf *imp;
+
+	if (!attachment->dmabuf || !attachment->dmabuf->priv)
+		return NULL;
+
+	imp = (struct virtio_vdmabuf_buf *)attachment->dmabuf->priv;
+
+	/* if buffer has never been mapped */
+	if (!imp->sgt) {
+		imp->sgt = new_sgt(imp->pages_info->pages,
+				   imp->pages_info->first_ofst,
+				   imp->pages_info->last_len,
+				   imp->pages_info->nents);
+
+		if (!imp->sgt)
+			return NULL;
+	}
+
+	if (!dma_map_sg(attachment->dev, imp->sgt->sgl,
+			imp->sgt->nents, dir)) {
+		sg_free_table(imp->sgt);
+		kfree(imp->sgt);
+		return NULL;
+	}
+
+	return imp->sgt;
+}
+
+static void
+vhost_vdmabuf_dmabuf_unmap(struct dma_buf_attachment *attachment,
+	   	           struct sg_table *sg,
+			   enum dma_data_direction dir)
+{
+	dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir);
+}
+
+static int vhost_vdmabuf_dmabuf_mmap(struct dma_buf *dmabuf,
+				     struct vm_area_struct *vma)
+{
+	struct virtio_vdmabuf_buf *imp;
+	u64 uaddr;
+	int i, err;
+
+	if (!dmabuf->priv)
+		return -EINVAL;
+
+	imp = (struct virtio_vdmabuf_buf *)dmabuf->priv;
+
+	if (!imp->pages_info)
+		return -EINVAL;
+
+	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+
+	uaddr = vma->vm_start;
+	for (i = 0; i < imp->pages_info->nents; i++) {
+		err = vm_insert_page(vma, uaddr,
+				     imp->pages_info->pages[i]);
+		if (err)
+			return err;
+
+		uaddr += PAGE_SIZE;
+	}
+
+	return 0;
+}
+
+static int vhost_vdmabuf_dmabuf_vmap(struct dma_buf *dmabuf,
+				     struct dma_buf_map *map)
+{
+	struct virtio_vdmabuf_buf *imp;
+	void *addr;
+
+	if (!dmabuf->priv)
+		return -EINVAL;
+
+	imp = (struct virtio_vdmabuf_buf *)dmabuf->priv;
+
+	if (!imp->pages_info)
+		return -EINVAL;
+
+	addr = vmap(imp->pages_info->pages, imp->pages_info->nents,
+                    0, PAGE_KERNEL);
+	if (IS_ERR(addr))
+		return PTR_ERR(addr);
+
+	return 0;
+}
+
+static void vhost_vdmabuf_dmabuf_release(struct dma_buf *dma_buf)
+{
+	struct virtio_vdmabuf_buf *imp;
+
+	if (!dma_buf->priv)
+		return;
+
+	imp = (struct virtio_vdmabuf_buf *)dma_buf->priv;
+	imp->dma_buf = NULL;
+	imp->valid = false;
+
+	vhost_vdmabuf_unmap_pages(imp->vmid, imp->pages_info);
+	virtio_vdmabuf_del_buf(drv_info, &imp->buf_id);
+
+	if (imp->sgt) {
+		sg_free_table(imp->sgt);
+		kfree(imp->sgt);
+		imp->sgt = NULL;
+	}
+
+	kfree(imp->priv);
+	kfree(imp->pages_info);
+	kfree(imp);
+}
+
+static const struct dma_buf_ops vhost_vdmabuf_dmabuf_ops = {
+	.map_dma_buf = vhost_vdmabuf_dmabuf_map,
+	.unmap_dma_buf = vhost_vdmabuf_dmabuf_unmap,
+	.release = vhost_vdmabuf_dmabuf_release,
+	.mmap = vhost_vdmabuf_dmabuf_mmap,
+	.vmap = vhost_vdmabuf_dmabuf_vmap,
+};
+
+/* exporting dmabuf as fd */
+static int vhost_vdmabuf_exp_fd(struct virtio_vdmabuf_buf *imp, int flags)
+{
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+	exp_info.ops = &vhost_vdmabuf_dmabuf_ops;
+
+	/* multiple of PAGE_SIZE, not considering offset */
+	exp_info.size = imp->pages_info->nents * PAGE_SIZE;
+	exp_info.flags = 0;
+	exp_info.priv = imp;
+
+	if (!imp->dma_buf) {
+		imp->dma_buf = dma_buf_export(&exp_info);
+		if (IS_ERR_OR_NULL(imp->dma_buf)) {
+			imp->dma_buf = NULL;
+			return -EINVAL;
+		}
+	}
+
+	return dma_buf_fd(imp->dma_buf, flags);
+}
+
+static int vhost_vdmabuf_add_event(struct vhost_vdmabuf *vdmabuf,
+				   struct virtio_vdmabuf_buf *buf_info)
+{
+	struct virtio_vdmabuf_event *e_oldest, *e_new;
+	struct virtio_vdmabuf_event_queue *evq = vdmabuf->evq;
+	unsigned long irqflags;
+
+	e_new = kzalloc(sizeof(*e_new), GFP_KERNEL);
+	if (!e_new)
+		return -ENOMEM;
+
+	e_new->e_data.hdr.buf_id = buf_info->buf_id;
+	e_new->e_data.data = (void *)buf_info->priv;
+	e_new->e_data.hdr.size = buf_info->sz_priv;
+
+	spin_lock_irqsave(&evq->e_lock, irqflags);
+
+	/* check current number of event then if it hits the max num (32)
+	 * then remove the oldest event in the list
+	 */
+	if (evq->pending > 31) {
+		e_oldest = list_first_entry(&evq->e_list,
+					    struct virtio_vdmabuf_event, link);
+		list_del(&e_oldest->link);
+		evq->pending--;
+		kfree(e_oldest);
+	}
+
+	list_add_tail(&e_new->link, &evq->e_list);
+
+	evq->pending++;
+
+	wake_up_interruptible(&evq->e_wait);
+	spin_unlock_irqrestore(&evq->e_lock, irqflags);
+
+	return 0;
+}
+
+static int send_msg_to_guest(u64 vmid, enum virtio_vdmabuf_cmd cmd, int *op)
+{
+	struct virtio_vdmabuf_msg *msg;
+	struct vhost_vdmabuf *vdmabuf;
+
+	vdmabuf = vhost_vdmabuf_find(vmid);
+	if (!vdmabuf) {
+		dev_err(drv_info->dev,
+			"can't find vdmabuf for : vmid = %llu\n", vmid);
+		return -EINVAL;
+	}
+
+	if (cmd != VIRTIO_VDMABUF_CMD_DMABUF_REL)
+		return -EINVAL;
+
+	msg = kvcalloc(1, sizeof(struct virtio_vdmabuf_msg),
+		       GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	memcpy(&msg->op[0], &op[0], 8 * sizeof(int));
+	msg->cmd = cmd;
+
+	list_add_tail(&msg->list, &vdmabuf->msg_list);
+	vhost_work_queue(&vdmabuf->dev, &vdmabuf->send_work);
+
+	return 0;
+}
+
+static int register_exported(struct vhost_vdmabuf *vdmabuf,
+			     virtio_vdmabuf_buf_id_t *buf_id, int *ops)
+{
+	struct virtio_vdmabuf_buf *imp;
+	int ret;
+
+	imp = kcalloc(1, sizeof(*imp), GFP_KERNEL);
+	if (!imp)
+		return -ENOMEM;
+
+	imp->pages_info = kcalloc(1, sizeof(struct virtio_vdmabuf_shared_pages),
+				  GFP_KERNEL);
+	if (!imp->pages_info) {
+		kfree(imp);
+		return -ENOMEM;
+	}
+
+	imp->sz_priv = ops[VIRTIO_VDMABUF_PRIVATE_DATA_SIZE];
+	if (imp->sz_priv) {
+		imp->priv = kcalloc(1, ops[VIRTIO_VDMABUF_PRIVATE_DATA_SIZE],
+				    GFP_KERNEL);
+		if (!imp->priv) {
+			kfree(imp->pages_info);
+			kfree(imp);
+			return -ENOMEM;
+		}
+	}
+
+	memcpy(&imp->buf_id, buf_id, sizeof(*buf_id));
+
+	imp->pages_info->nents = ops[VIRTIO_VDMABUF_NUM_PAGES_SHARED];
+	imp->pages_info->first_ofst = ops[VIRTIO_VDMABUF_FIRST_PAGE_DATA_OFFSET];
+	imp->pages_info->last_len = ops[VIRTIO_VDMABUF_LAST_PAGE_DATA_LENGTH];
+	imp->pages_info->ref = *(gpa_t *)&ops[VIRTIO_VDMABUF_REF_ADDR_UPPER_32BIT];
+	imp->vmid = vdmabuf->vmid;
+	imp->valid = true;
+
+	virtio_vdmabuf_add_buf(drv_info, imp);
+
+	/* transferring private data */
+	memcpy(imp->priv, &ops[VIRTIO_VDMABUF_PRIVATE_DATA_START],
+	       ops[VIRTIO_VDMABUF_PRIVATE_DATA_SIZE]);
+
+	/* generate import event */
+	ret = vhost_vdmabuf_add_event(vdmabuf, imp);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void send_to_recvq(struct vhost_vdmabuf *vdmabuf,
+			  struct vhost_virtqueue *vq)
+{
+	struct virtio_vdmabuf_msg *msg;
+	int head, in, out, in_size;
+	bool added = false;
+	int ret;
+
+	mutex_lock(&vq->mutex);
+
+	if (!vhost_vq_get_backend(vq))
+		goto out;
+
+	vhost_disable_notify(&vdmabuf->dev, vq);
+
+	for (;;) {
+		if (list_empty(&vdmabuf->msg_list))
+			break;
+
+		head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov),
+					 &out, &in, NULL, NULL);
+
+		if (head < 0 || head == vq->num)
+			break;
+
+		in_size = iov_length(&vq->iov[out], in);
+		if (in_size != sizeof(struct virtio_vdmabuf_msg)) {
+			dev_err(drv_info->dev, "rx msg with wrong size\n");
+			break;
+		}
+
+		msg = list_first_entry(&vdmabuf->msg_list,
+				       struct virtio_vdmabuf_msg, list);
+		list_del_init(&msg->list);
+
+		ret = __copy_to_user(vq->iov[out].iov_base, msg,
+				     sizeof(struct virtio_vdmabuf_msg));
+		if (ret) {
+			dev_err(drv_info->dev,
+				"fail to copy tx msg\n");
+			break;
+		}
+
+		vhost_add_used(vq, head, in_size);
+		added = true;
+
+		//kfree(msg);
+	}
+
+	vhost_enable_notify(&vdmabuf->dev, vq);
+	if (added)
+		vhost_signal(&vdmabuf->dev, vq);
+out:
+	mutex_unlock(&vq->mutex);
+}
+
+static void vhost_send_msg_work(struct vhost_work *work)
+{
+	struct vhost_vdmabuf *vdmabuf = container_of(work,
+					             struct vhost_vdmabuf,
+					             send_work);
+	struct vhost_virtqueue *vq = &vdmabuf->vqs[VDMABUF_VQ_RECV];
+
+	send_to_recvq(vdmabuf, vq);
+}
+
+/* parse incoming message from a guest */
+static int parse_msg(struct vhost_vdmabuf *vdmabuf,
+		     struct virtio_vdmabuf_msg *msg)
+{
+	virtio_vdmabuf_buf_id_t *buf_id;
+	struct virtio_vdmabuf_msg *vmid_msg;
+	int ret = 0;
+
+	switch (msg->cmd) {
+	case VIRTIO_VDMABUF_CMD_EXPORT:
+		buf_id = (virtio_vdmabuf_buf_id_t *)msg->op;
+		ret = register_exported(vdmabuf, buf_id, msg->op);
+
+		break;
+	case VIRTIO_VDMABUF_CMD_NEED_VMID:
+		vmid_msg = kvcalloc(1, sizeof(struct virtio_vdmabuf_msg),
+				    GFP_KERNEL);
+		if (!vmid_msg) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		vmid_msg->cmd = msg->cmd;
+		vmid_msg->op[0] = vdmabuf->vmid;
+		list_add_tail(&vmid_msg->list, &vdmabuf->msg_list);
+		vhost_work_queue(&vdmabuf->dev, &vdmabuf->send_work);
+
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static void vhost_vdmabuf_handle_send_kick(struct vhost_work *work)
+{
+	struct vhost_virtqueue *vq = container_of(work,
+						  struct vhost_virtqueue,
+						  poll.work);
+	struct vhost_vdmabuf *vdmabuf = container_of(vq->dev,
+					      	     struct vhost_vdmabuf,
+					      	     dev);
+	struct virtio_vdmabuf_msg msg;
+	int head, in, out, in_size;
+	bool added = false;
+	int ret;
+
+	mutex_lock(&vq->mutex);
+
+	if (!vhost_vq_get_backend(vq))
+		goto out;
+
+	vhost_disable_notify(&vdmabuf->dev, vq);
+
+	/* Make sure we will process all pending requests */
+	for (;;) {
+		head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov),
+					 &out, &in, NULL, NULL);
+
+		if (head < 0 || head == vq->num)
+			break;
+
+		in_size = iov_length(&vq->iov[in], out);
+		if (in_size != sizeof(struct virtio_vdmabuf_msg)) {
+			dev_err(drv_info->dev, "rx msg with wrong size\n");
+			break;
+		}
+
+		if (__copy_from_user(&msg, vq->iov[in].iov_base, in_size)) {
+			dev_err(drv_info->dev,
+				"err: can't get the msg from vq\n");
+			break;
+		}
+
+		ret = parse_msg(vdmabuf, &msg);
+		if (ret) {
+			dev_err(drv_info->dev,
+				"msg parse error: %d",
+				ret);
+			dev_err(drv_info->dev,
+				" cmd: %d\n", msg.cmd);
+
+			break;
+		}
+
+		vhost_add_used(vq, head, in_size);
+		added = true;
+	}
+
+	vhost_enable_notify(&vdmabuf->dev, vq);
+	if (added)
+		vhost_signal(&vdmabuf->dev, vq);
+out:
+	mutex_unlock(&vq->mutex);
+}
+
+static void vhost_vdmabuf_handle_recv_kick(struct vhost_work *work)
+{
+	struct vhost_virtqueue *vq = container_of(work,
+						  struct vhost_virtqueue,
+						  poll.work);
+	struct vhost_vdmabuf *vdmabuf = container_of(vq->dev,
+					      	     struct vhost_vdmabuf,
+					      	     dev);
+
+	send_to_recvq(vdmabuf, vq);
+}
+
+static int vhost_vdmabuf_get_kvm(struct notifier_block *nb,
+				 unsigned long event, void *data)
+{
+	struct kvm_instance *instance;
+	struct virtio_vdmabuf_info *drv = container_of(nb,
+						struct virtio_vdmabuf_info,
+						kvm_notifier);
+
+	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+	if (instance && event == KVM_EVENT_CREATE_VM) {
+		if (data) {
+			instance->kvm = data;
+			list_add_tail(&instance->link,
+				      &drv->kvm_instances);
+		}
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct kvm *find_kvm_instance(u64 vmid)
+{
+	struct kvm_instance *instance, *tmp;
+	struct kvm *kvm = NULL;
+
+	list_for_each_entry_safe(instance, tmp, &drv_info->kvm_instances,
+                                 link) {
+		if (instance->kvm->userspace_pid == vmid) {
+			kvm = instance->kvm;
+
+			list_del(&instance->link);
+			kfree(instance);
+			break;
+		}
+	}
+
+	return kvm;
+}
+
+static int vhost_vdmabuf_open(struct inode *inode, struct file *filp)
+{
+	struct vhost_vdmabuf *vdmabuf;
+	struct vhost_virtqueue **vqs;
+	int ret = 0;
+
+	if (!drv_info) {
+		pr_err("vhost-vdmabuf: can't open misc device\n");
+		return -EINVAL;
+	}
+
+	vdmabuf = kzalloc(sizeof(*vdmabuf), GFP_KERNEL |
+			   __GFP_RETRY_MAYFAIL);
+	if (!vdmabuf)
+		return -ENOMEM;
+
+	vqs = kmalloc_array(ARRAY_SIZE(vdmabuf->vqs), sizeof(*vqs),
+			    GFP_KERNEL);
+	if (!vqs) {
+		kfree(vdmabuf);
+		return -ENOMEM;
+	}
+
+	vdmabuf->evq = kcalloc(1, sizeof(*(vdmabuf->evq)), GFP_KERNEL);
+	if (!vdmabuf->evq) {
+		kfree(vdmabuf);
+		kfree(vqs);
+		return -ENOMEM;
+	}
+
+	vqs[VDMABUF_VQ_SEND] = &vdmabuf->vqs[VDMABUF_VQ_SEND];
+	vqs[VDMABUF_VQ_RECV] = &vdmabuf->vqs[VDMABUF_VQ_RECV];
+	vdmabuf->vqs[VDMABUF_VQ_SEND].handle_kick = vhost_vdmabuf_handle_send_kick;
+	vdmabuf->vqs[VDMABUF_VQ_RECV].handle_kick = vhost_vdmabuf_handle_recv_kick;
+
+	vhost_dev_init(&vdmabuf->dev, vqs, ARRAY_SIZE(vdmabuf->vqs),
+		       UIO_MAXIOV, 0, 0, true, NULL);
+
+	INIT_LIST_HEAD(&vdmabuf->msg_list);
+	vhost_work_init(&vdmabuf->send_work, vhost_send_msg_work);
+	vdmabuf->vmid = task_pid_nr(current);
+	vdmabuf->kvm = find_kvm_instance(vdmabuf->vmid);
+	vhost_vdmabuf_add(vdmabuf);
+
+	mutex_init(&vdmabuf->evq->e_readlock);
+	spin_lock_init(&vdmabuf->evq->e_lock);
+
+	/* Initialize event queue */
+	INIT_LIST_HEAD(&vdmabuf->evq->e_list);
+	init_waitqueue_head(&vdmabuf->evq->e_wait);
+
+	/* resetting number of pending events */
+	vdmabuf->evq->pending = 0;
+	filp->private_data = vdmabuf;
+
+	return ret;
+}
+
+static void vhost_vdmabuf_flush(struct vhost_vdmabuf *vdmabuf)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vdmabuf->vqs); i++)
+		if (vdmabuf->vqs[i].handle_kick)
+			vhost_poll_flush(&vdmabuf->vqs[i].poll);
+
+	vhost_work_flush(&vdmabuf->dev, &vdmabuf->send_work);
+}
+
+static int vhost_vdmabuf_release(struct inode *inode, struct file *filp)
+{
+	struct vhost_vdmabuf *vdmabuf = filp->private_data;
+	struct virtio_vdmabuf_event *e, *et;
+
+	if (!vhost_vdmabuf_del(vdmabuf))
+		return -EINVAL;
+
+	mutex_lock(&drv_info->g_mutex);
+
+	list_for_each_entry_safe(e, et, &vdmabuf->evq->e_list,
+				 link) {
+		list_del(&e->link);
+		kfree(e);
+		vdmabuf->evq->pending--;
+	}
+
+	vhost_vdmabuf_flush(vdmabuf);
+	vhost_dev_cleanup(&vdmabuf->dev);
+
+	kfree(vdmabuf->dev.vqs);
+	kvfree(vdmabuf);
+
+	filp->private_data = NULL;
+	mutex_unlock(&drv_info->g_mutex);
+
+	return 0;
+}
+
+static unsigned int vhost_vdmabuf_event_poll(struct file *filp,
+				    	     struct poll_table_struct *wait)
+{
+	struct vhost_vdmabuf *vdmabuf = filp->private_data;
+
+	poll_wait(filp, &vdmabuf->evq->e_wait, wait);
+
+	if (!list_empty(&vdmabuf->evq->e_list))
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static ssize_t vhost_vdmabuf_event_read(struct file *filp, char __user *buf,
+			       		size_t cnt, loff_t *ofst)
+{
+	struct vhost_vdmabuf *vdmabuf = filp->private_data;
+	int ret;
+
+	if (task_pid_nr(current) != vdmabuf->vmid) {
+		dev_err(drv_info->dev, "current process cannot read events\n");
+		return -EPERM;
+	}
+
+	/* make sure user buffer can be written */
+	if (!access_ok(buf, sizeof(*buf))) {
+		dev_err(drv_info->dev, "user buffer can't be written.\n");
+		return -EINVAL;
+	}
+
+	ret = mutex_lock_interruptible(&vdmabuf->evq->e_readlock);
+	if (ret)
+		return ret;
+
+	for (;;) {
+		struct virtio_vdmabuf_event *e = NULL;
+
+		spin_lock_irq(&vdmabuf->evq->e_lock);
+		if (!list_empty(&vdmabuf->evq->e_list)) {
+			e = list_first_entry(&vdmabuf->evq->e_list,
+					     struct virtio_vdmabuf_event, link);
+			list_del(&e->link);
+		}
+		spin_unlock_irq(&vdmabuf->evq->e_lock);
+
+		if (!e) {
+			if (ret)
+				break;
+
+			if (filp->f_flags & O_NONBLOCK) {
+				ret = -EAGAIN;
+				break;
+			}
+
+			mutex_unlock(&vdmabuf->evq->e_readlock);
+			ret = wait_event_interruptible(vdmabuf->evq->e_wait,
+					!list_empty(&vdmabuf->evq->e_list));
+
+			if (ret == 0)
+				ret = mutex_lock_interruptible(
+						&vdmabuf->evq->e_readlock);
+
+			if (ret)
+				return ret;
+		} else {
+			unsigned int len = (sizeof(e->e_data.hdr) +
+					    e->e_data.hdr.size);
+
+			if (len > cnt - ret) {
+put_back_event:
+				spin_lock_irq(&vdmabuf->evq->e_lock);
+				list_add(&e->link, &vdmabuf->evq->e_list);
+				spin_unlock_irq(&vdmabuf->evq->e_lock);
+				break;
+			}
+
+			if (copy_to_user(buf + ret, &e->e_data.hdr,
+					 sizeof(e->e_data.hdr))) {
+				if (ret == 0)
+					ret = -EFAULT;
+
+				goto put_back_event;
+			}
+
+			ret += sizeof(e->e_data.hdr);
+
+			if (copy_to_user(buf + ret, e->e_data.data,
+					 e->e_data.hdr.size)) {
+
+				struct virtio_vdmabuf_e_hdr dummy_hdr = {0};
+
+				ret -= sizeof(e->e_data.hdr);
+
+				/* nullifying hdr of the event in user buffer */
+				if (copy_to_user(buf + ret, &dummy_hdr,
+						 sizeof(dummy_hdr)))
+					dev_err(drv_info->dev,
+					   "fail to nullify invalid hdr\n");
+
+				ret = -EFAULT;
+
+				goto put_back_event;
+			}
+
+			ret += e->e_data.hdr.size;
+
+			spin_lock_irq(&vdmabuf->evq->e_lock);
+			vdmabuf->evq->pending--;
+			spin_unlock_irq(&vdmabuf->evq->e_lock);
+			kfree(e);
+		}
+	}
+
+	mutex_unlock(&vdmabuf->evq->e_readlock);
+
+	return ret;
+}
+
+static int vhost_vdmabuf_start(struct vhost_vdmabuf *vdmabuf)
+{
+        struct vhost_virtqueue *vq;
+        int i, ret;
+
+	mutex_lock(&vdmabuf->dev.mutex);
+
+        ret = vhost_dev_check_owner(&vdmabuf->dev);
+        if (ret)
+                goto err;
+
+	for (i = 0; i < ARRAY_SIZE(vdmabuf->vqs); i++) {
+		vq = &vdmabuf->vqs[i];
+
+		mutex_lock(&vq->mutex);
+
+		if (!vhost_vq_access_ok(vq)) {
+			ret = -EFAULT;
+			goto err_vq;
+		}
+
+		if (!vhost_vq_get_backend(vq)) {
+			vhost_vq_set_backend(vq, vdmabuf);
+			ret = vhost_vq_init_access(vq);
+			if (ret)
+				goto err_vq;
+		}
+
+		mutex_unlock(&vq->mutex);
+	}
+
+	mutex_unlock(&vdmabuf->dev.mutex);
+        return 0;
+
+err_vq:
+        vhost_vq_set_backend(vq, NULL);
+        mutex_unlock(&vq->mutex);
+
+	for (i = 0; i < ARRAY_SIZE(vdmabuf->vqs); i++) {
+		vq = &vdmabuf->vqs[i];
+
+		mutex_lock(&vq->mutex);
+		vhost_vq_set_backend(vq, NULL);
+		mutex_unlock(&vq->mutex);
+	}
+
+err:
+	mutex_unlock(&vdmabuf->dev.mutex);
+        return ret;
+}
+
+static int vhost_vdmabuf_stop(struct vhost_vdmabuf *vdmabuf)
+{
+        struct vhost_virtqueue *vq;
+        int i, ret;
+
+	mutex_lock(&vdmabuf->dev.mutex);
+
+        ret = vhost_dev_check_owner(&vdmabuf->dev);
+        if (ret)
+                goto err;
+
+	for (i = 0; i < ARRAY_SIZE(vdmabuf->vqs); i++) {
+		vq = &vdmabuf->vqs[i];
+
+		mutex_lock(&vq->mutex);
+		vhost_vq_set_backend(vq, NULL);
+		mutex_unlock(&vq->mutex);
+	}
+
+err:
+	mutex_unlock(&vdmabuf->dev.mutex);
+        return ret;
+}
+
+static int vhost_vdmabuf_set_features(struct vhost_vdmabuf *vdmabuf,
+				      u64 features)
+{
+	struct vhost_virtqueue *vq;
+	int i;
+
+	if (features & ~VHOST_VDMABUF_FEATURES)
+		return -EOPNOTSUPP;
+
+	mutex_lock(&vdmabuf->dev.mutex);
+	if ((features & (1 << VHOST_F_LOG_ALL)) &&
+	    !vhost_log_access_ok(&vdmabuf->dev)) {
+		mutex_unlock(&vdmabuf->dev.mutex);
+		return -EFAULT;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(vdmabuf->vqs); i++) {
+		vq = &vdmabuf->vqs[i];
+		mutex_lock(&vq->mutex);
+		vq->acked_features = features;
+		mutex_unlock(&vq->mutex);
+	}
+
+	mutex_unlock(&vdmabuf->dev.mutex);
+	return 0;
+}
+
+/* wrapper ioctl for vhost interface control */
+static int vhost_core_ioctl(struct file *filp, unsigned int cmd,
+			    unsigned long param)
+{
+	struct vhost_vdmabuf *vdmabuf = filp->private_data;
+	void __user *argp = (void __user *)param;
+	u64 features;
+	int ret, start;
+
+	switch (cmd) {
+	case VHOST_GET_FEATURES:
+		features = VHOST_VDMABUF_FEATURES;
+		if (copy_to_user(argp, &features, sizeof(features)))
+			return -EFAULT;
+		return 0;
+	case VHOST_SET_FEATURES:
+		if (copy_from_user(&features, argp, sizeof(features)))
+			return -EFAULT;
+		return vhost_vdmabuf_set_features(vdmabuf, features);
+	case VHOST_VDMABUF_SET_RUNNING:
+		if (copy_from_user(&start, argp, sizeof(start)))
+                        return -EFAULT;
+
+		if (start)
+                	return vhost_vdmabuf_start(vdmabuf);
+                else
+                        return vhost_vdmabuf_stop(vdmabuf);
+	default:
+		mutex_lock(&vdmabuf->dev.mutex);
+		ret = vhost_dev_ioctl(&vdmabuf->dev, cmd, argp);
+		if (ret == -ENOIOCTLCMD) {
+			ret = vhost_vring_ioctl(&vdmabuf->dev, cmd, argp);
+		} else {
+			vhost_vdmabuf_flush(vdmabuf);
+		}
+		mutex_unlock(&vdmabuf->dev.mutex);
+	}
+
+	return ret;
+}
+
+/*
+ * ioctl - importing vdmabuf from guest OS
+ *
+ * user parameters:
+ *
+ *	virtio_vdmabuf_buf_id_t buf_id - vdmabuf ID of imported buffer
+ *	int flags - flags
+ *	int fd - file handle of	the imported buffer
+ *
+ */
+static int import_ioctl(struct file *filp, void *data)
+{
+	struct vhost_vdmabuf *vdmabuf = filp->private_data;
+	struct virtio_vdmabuf_import *attr = data;
+	struct virtio_vdmabuf_buf *imp;
+	int ret = 0;
+
+	mutex_lock(&vdmabuf->dev.mutex);
+
+	/* look for dmabuf for the id */
+	imp = virtio_vdmabuf_find_buf(drv_info, &attr->buf_id);
+	if (!imp || !imp->valid) {
+		mutex_unlock(&vdmabuf->dev.mutex);
+		dev_err(drv_info->dev,
+			"no valid buf found with id = %llu\n", attr->buf_id.id);
+		return -ENOENT;
+	}
+
+	/* only if mapped pages are not present */
+	if (!imp->pages_info->pages) {
+		ret = vhost_vdmabuf_map_pages(vdmabuf->vmid, imp->pages_info);
+		if (ret < 0) {
+			dev_err(drv_info->dev,
+				"failed to map guest pages\n");
+			goto fail_map;
+		}
+	}
+
+	attr->fd = vhost_vdmabuf_exp_fd(imp, attr->flags);
+	if (attr->fd < 0) {
+		dev_err(drv_info->dev, "failed to get file descriptor\n");
+		goto fail_import;
+	}
+
+	imp->imported = true;
+
+	mutex_unlock(&vdmabuf->dev.mutex);
+	goto success;
+
+fail_import:
+	/* not imported yet? */
+	if (!imp->imported) {
+		vhost_vdmabuf_unmap_pages(vdmabuf->vmid, imp->pages_info);
+		if (imp->dma_buf)
+			kfree(imp->dma_buf);
+
+		if (imp->sgt) {
+			sg_free_table(imp->sgt);
+			kfree(imp->sgt);
+			imp->sgt = NULL;
+		}
+	}
+
+fail_map:
+	/* Check if buffer is still valid and if not remove it
+	 * from imported list.
+	 */
+	if (!imp->valid && !imp->imported) {
+		virtio_vdmabuf_del_buf(drv_info, &imp->buf_id);
+		kfree(imp->priv);
+		kfree(imp->pages_info);
+		kfree(imp);
+	}
+
+	ret =  attr->fd;
+	mutex_unlock(&vdmabuf->dev.mutex);
+
+success:
+	return ret;
+}
+
+static int release_ioctl(struct file *filp, void *data)
+{
+	struct vhost_vdmabuf *vdmabuf = filp->private_data;
+	struct virtio_vdmabuf_import *attr = data;
+	struct virtio_vdmabuf_buf *imp;
+	virtio_vdmabuf_buf_id_t buf_id = attr->buf_id;
+	int *op;
+	int ret = 0;
+
+	op = kcalloc(1, sizeof(int) * 65, GFP_KERNEL);
+	if (!op)
+		return -ENOMEM;
+
+	imp = virtio_vdmabuf_find_buf(drv_info, &buf_id);
+	if (!imp)
+		return -EINVAL;
+
+	imp->imported = false;
+
+	memcpy(op, &imp->buf_id, sizeof(imp->buf_id));
+
+	ret = send_msg_to_guest(vdmabuf->vmid, VIRTIO_VDMABUF_CMD_DMABUF_REL,
+				op);
+	if (ret < 0) {
+		dev_err(drv_info->dev, "fail to send release cmd\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * ioctl - querying various information of vdmabuf
+ *
+ * user parameters:
+ *
+ *	virtio_vdmabuf_buf_id_t buf_id - vdmabuf ID of imported buffer
+ *	unsigned long info - returned querying result
+ *
+ */
+static int query_ioctl(struct file *filp, void *data)
+{
+	struct virtio_vdmabuf_query *attr = data;
+	struct virtio_vdmabuf_buf *imp;
+	virtio_vdmabuf_buf_id_t buf_id = attr->buf_id;
+
+	/* query for imported dmabuf */
+	imp = virtio_vdmabuf_find_buf(drv_info, &buf_id);
+	if (!imp)
+		return -EINVAL;
+
+	switch (attr->item) {
+	/* size of dmabuf in byte */
+	case VIRTIO_VDMABUF_QUERY_SIZE:
+		if (imp->dma_buf) {
+			/* if local dma_buf is created (if it's
+			 * ever mapped), retrieve it directly
+			 * from struct dma_buf *
+			 */
+			attr->info = imp->dma_buf->size;
+		} else {
+			/* calcuate it from given nents, first_ofst
+			 * and last_len
+			 */
+			attr->info = ((imp->pages_info->nents)*PAGE_SIZE -
+				     (imp->pages_info->first_ofst) - PAGE_SIZE +
+				     (imp->pages_info->last_len));
+		}
+		break;
+
+	/* whether the buffer is used or not */
+	case VIRTIO_VDMABUF_QUERY_BUSY:
+		/* checks if it's used by importer */
+		attr->info = imp->imported;
+		break;
+
+	/* size of private info attached to buffer */
+	case VIRTIO_VDMABUF_QUERY_PRIV_INFO_SIZE:
+		attr->info = imp->sz_priv;
+		break;
+
+	/* copy private info attached to buffer */
+	case VIRTIO_VDMABUF_QUERY_PRIV_INFO:
+		if (imp->sz_priv > 0) {
+			int n;
+
+			n = copy_to_user((void __user *)attr->info,
+					imp->priv,
+					imp->sz_priv);
+			if (n != 0)
+				return -EINVAL;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct virtio_vdmabuf_ioctl_desc vhost_vdmabuf_ioctls[] = {
+	VIRTIO_VDMABUF_IOCTL_DEF(VIRTIO_VDMABUF_IOCTL_IMPORT, import_ioctl, 0),
+	VIRTIO_VDMABUF_IOCTL_DEF(VIRTIO_VDMABUF_IOCTL_RELEASE, release_ioctl, 0),
+	VIRTIO_VDMABUF_IOCTL_DEF(VIRTIO_VDMABUF_IOCTL_QUERY, query_ioctl, 0),
+};
+
+static long vhost_vdmabuf_ioctl(struct file *filp, unsigned int cmd,
+				unsigned long param)
+{
+	const struct virtio_vdmabuf_ioctl_desc *ioctl;
+	virtio_vdmabuf_ioctl_t func;
+	unsigned int nr;
+	int ret;
+	char *kdata;
+
+	/* check if cmd is vhost's */
+	if (_IOC_TYPE(cmd) == VHOST_VIRTIO) {
+		ret = vhost_core_ioctl(filp, cmd, param);
+		return ret;
+	}
+
+	nr = _IOC_NR(cmd);
+
+	if (nr >= ARRAY_SIZE(vhost_vdmabuf_ioctls)) {
+		dev_err(drv_info->dev, "invalid ioctl\n");
+		return -EINVAL;
+	}
+
+	ioctl = &vhost_vdmabuf_ioctls[nr];
+
+	func = ioctl->func;
+
+	if (unlikely(!func)) {
+		dev_err(drv_info->dev, "no function\n");
+		return -EINVAL;
+	}
+
+	kdata = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+	if (!kdata)
+		return -ENOMEM;
+
+	if (copy_from_user(kdata, (void __user *)param,
+			   _IOC_SIZE(cmd)) != 0) {
+		dev_err(drv_info->dev,
+			"failed to copy args from userspace\n");
+		ret = -EFAULT;
+		goto ioctl_error;
+	}
+
+	ret = func(filp, kdata);
+
+	if (copy_to_user((void __user *)param, kdata,
+			 _IOC_SIZE(cmd)) != 0) {
+		dev_err(drv_info->dev,
+			"failed to copy args back to userspace\n");
+		ret = -EFAULT;
+		goto ioctl_error;
+	}
+
+ioctl_error:
+	kfree(kdata);
+	return ret;
+}
+
+static const struct file_operations vhost_vdmabuf_fops = {
+	.owner = THIS_MODULE,
+	.open = vhost_vdmabuf_open,
+	.release = vhost_vdmabuf_release,
+	.read = vhost_vdmabuf_event_read,
+	.poll = vhost_vdmabuf_event_poll,
+	.unlocked_ioctl = vhost_vdmabuf_ioctl,
+};
+
+static struct miscdevice vhost_vdmabuf_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "vhost-vdmabuf",
+	.fops = &vhost_vdmabuf_fops,
+};
+
+static int __init vhost_vdmabuf_init(void)
+{
+	int ret = 0;
+
+	ret = misc_register(&vhost_vdmabuf_miscdev);
+	if (ret) {
+		pr_err("vhost-vdmabuf: driver can't be registered\n");
+		return ret;
+	}
+
+	dma_coerce_mask_and_coherent(vhost_vdmabuf_miscdev.this_device,
+				     DMA_BIT_MASK(64));
+
+	drv_info = kcalloc(1, sizeof(*drv_info), GFP_KERNEL);
+	if (!drv_info) {
+		misc_deregister(&vhost_vdmabuf_miscdev);
+		return -ENOMEM;
+	}
+
+	drv_info->dev = vhost_vdmabuf_miscdev.this_device;
+
+	hash_init(drv_info->buf_list);
+	mutex_init(&drv_info->g_mutex);
+
+	INIT_LIST_HEAD(&drv_info->head_vdmabuf_list);
+	INIT_LIST_HEAD(&drv_info->kvm_instances);
+
+	drv_info->kvm_notifier.notifier_call = vhost_vdmabuf_get_kvm;
+	ret = kvm_vm_register_notifier(&drv_info->kvm_notifier);
+
+	return ret;
+}
+
+static void __exit vhost_vdmabuf_deinit(void)
+{
+	misc_deregister(&vhost_vdmabuf_miscdev);
+	vhost_vdmabuf_del_all();
+
+	kvm_vm_unregister_notifier(&drv_info->kvm_notifier);
+	kfree(drv_info);
+	drv_info = NULL;
+}
+
+module_init(vhost_vdmabuf_init);
+module_exit(vhost_vdmabuf_deinit);
+
+MODULE_DESCRIPTION("Vhost Vdmabuf Driver");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/include/uapi/linux/vhost.h b/include/uapi/linux/vhost.h
index c998860d7bbc..87e0d1b8cc76 100644
--- a/include/uapi/linux/vhost.h
+++ b/include/uapi/linux/vhost.h
@@ -150,4 +150,7 @@
 /* Get the valid iova range */
 #define VHOST_VDPA_GET_IOVA_RANGE	_IOR(VHOST_VIRTIO, 0x78, \
 					     struct vhost_vdpa_iova_range)
+
+/* VHOST_VDMABUF specific defines */
+#define VHOST_VDMABUF_SET_RUNNING       _IOW(VHOST_VIRTIO, 0x80, int)
 #endif
-- 
2.26.2



More information about the dri-devel mailing list