CHROMIUM: virtwl: add dmabuf sync ioctl

This can be used to synchronize access to dmabuf backed virtio
buffers. The virtio wayland device will use this to issue
matching DMA_BUF_SYNC ioctls. The dmabuf driver on the host side
can then handle this correctly (e.g. flush memory before it is
potentially scanned out by display hardware).

TEST=cache-line artifacts no longer noticeable
BUG=chromium:837209

Change-Id: I0e797123fc0cfbc179a44e98ffd2ce7def53e2f5
Signed-off-by: David Reveman <reveman@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1076927
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
diff --git a/drivers/virtio/virtio_wl.c b/drivers/virtio/virtio_wl.c
index 2e5f9d6..6636313 100644
--- a/drivers/virtio/virtio_wl.c
+++ b/drivers/virtio/virtio_wl.c
@@ -58,6 +58,8 @@
 #include <linux/virtio.h>
 #include <linux/virtio_wl.h>
 
+#include <uapi/linux/dma-buf.h>
+
 #define VFD_ILLEGAL_SIGN_BIT 0x80000000
 #define VFD_HOST_VFD_ID_BIT 0x40000000
 
@@ -774,6 +776,46 @@
 	return ret;
 }
 
+static int virtwl_vfd_dmabuf_sync(struct file *filp, u32 flags)
+{
+	struct virtio_wl_ctrl_vfd_dmabuf_sync *ctrl_dmabuf_sync;
+	struct virtwl_vfd *vfd = filp->private_data;
+	struct virtwl_info *vi = vfd->vi;
+	struct completion finish_completion;
+	struct scatterlist out_sg;
+	struct scatterlist in_sg;
+	int ret = 0;
+
+	ctrl_dmabuf_sync = kzalloc(sizeof(*ctrl_dmabuf_sync), GFP_KERNEL);
+	if (!ctrl_dmabuf_sync)
+		return -ENOMEM;
+
+	ctrl_dmabuf_sync->hdr.type = VIRTIO_WL_CMD_VFD_DMABUF_SYNC;
+	ctrl_dmabuf_sync->vfd_id = vfd->id;
+	ctrl_dmabuf_sync->flags = flags;
+
+	sg_init_one(&in_sg, &ctrl_dmabuf_sync->hdr,
+		    sizeof(struct virtio_wl_ctrl_vfd_dmabuf_sync));
+	sg_init_one(&out_sg, &ctrl_dmabuf_sync->hdr,
+		    sizeof(struct virtio_wl_ctrl_hdr));
+
+	init_completion(&finish_completion);
+	ret = vq_queue_out(vi, &out_sg, &in_sg, &finish_completion,
+			   false /* block */);
+	if (ret) {
+		pr_warn("virtwl: failed to queue dmabuf sync vfd id %u: %d\n",
+			vfd->id,
+			ret);
+		goto free_ctrl_dmabuf_sync;
+	}
+
+	wait_for_completion(&finish_completion);
+
+free_ctrl_dmabuf_sync:
+	kfree(ctrl_dmabuf_sync);
+	return ret;
+}
+
 static ssize_t virtwl_vfd_read(struct file *filp, char __user *buffer,
 			       size_t size, loff_t *pos)
 {
@@ -1074,6 +1116,22 @@
 	return ret;
 }
 
+static long virtwl_ioctl_dmabuf_sync(struct file *filp, void __user *ptr)
+{
+	struct virtwl_ioctl_dmabuf_sync ioctl_dmabuf_sync;
+	int ret;
+
+	ret = copy_from_user(&ioctl_dmabuf_sync, ptr,
+			     sizeof(struct virtwl_ioctl_dmabuf_sync));
+	if (ret)
+		return -EFAULT;
+
+	if (ioctl_dmabuf_sync.flags & ~DMA_BUF_SYNC_VALID_FLAGS_MASK)
+		return -EINVAL;
+
+	return virtwl_vfd_dmabuf_sync(filp, ioctl_dmabuf_sync.flags);
+}
+
 static long virtwl_vfd_ioctl(struct file *filp, unsigned int cmd,
 			     void __user *ptr)
 {
@@ -1082,6 +1140,8 @@
 		return virtwl_ioctl_send(filp, ptr);
 	case VIRTWL_IOCTL_RECV:
 		return virtwl_ioctl_recv(filp, ptr);
+	case VIRTWL_IOCTL_DMABUF_SYNC:
+		return virtwl_ioctl_dmabuf_sync(filp, ptr);
 	default:
 		return -ENOTTY;
 	}
diff --git a/include/uapi/linux/virtio_wl.h b/include/uapi/linux/virtio_wl.h
index 89e0e42..76bf43e 100644
--- a/include/uapi/linux/virtio_wl.h
+++ b/include/uapi/linux/virtio_wl.h
@@ -35,6 +35,7 @@
 	VIRTIO_WL_CMD_VFD_NEW_PIPE, /* virtio_wl_ctrl_vfd_new */
 	VIRTIO_WL_CMD_VFD_HUP, /* virtio_wl_ctrl_vfd */
 	VIRTIO_WL_CMD_VFD_NEW_DMABUF, /* virtio_wl_ctrl_vfd_new */
+	VIRTIO_WL_CMD_VFD_DMABUF_SYNC, /* virtio_wl_ctrl_vfd_dmabuf_sync */
 
 	VIRTIO_WL_RESP_OK = 0x1000,
 	VIRTIO_WL_RESP_VFD_NEW = 0x1001, /* virtio_wl_ctrl_vfd_new */
@@ -104,4 +105,10 @@
 	/* the remainder is raw data */
 };
 
+struct virtio_wl_ctrl_vfd_dmabuf_sync {
+	struct virtio_wl_ctrl_hdr hdr;
+	__le32 vfd_id;
+	__le32 flags;
+};
+
 #endif /* _LINUX_VIRTIO_WL_H */
diff --git a/include/uapi/linux/virtwl.h b/include/uapi/linux/virtwl.h
index b9c0ed7..9390413 100644
--- a/include/uapi/linux/virtwl.h
+++ b/include/uapi/linux/virtwl.h
@@ -51,9 +51,14 @@
 	__u8 data[0];
 };
 
+struct virtwl_ioctl_dmabuf_sync {
+	__u32 flags; /* synchronization flags (see dma-buf.h) */
+};
+
 #define VIRTWL_IOCTL_NEW VIRTWL_IOWR(0x00, struct virtwl_ioctl_new)
 #define VIRTWL_IOCTL_SEND VIRTWL_IOR(0x01, struct virtwl_ioctl_txn)
 #define VIRTWL_IOCTL_RECV VIRTWL_IOW(0x02, struct virtwl_ioctl_txn)
-#define VIRTWL_IOCTL_MAXNR 3
+#define VIRTWL_IOCTL_DMABUF_SYNC VIRTWL_IOR(0x03, \
+					    struct virtwl_ioctl_dmabuf_sync)
 
 #endif /* _LINUX_VIRTWL_H */