CHROMIUM: virtwl: add dmabuf vfd support
This change supports the new dmabuf VFD type, which provides zero-copy
graphics output.
TEST=use linux_dmabuf protocol in sommelier
BUG=chromium:837209
Change-Id: Icdc1f7bca07be3766b123074d5f47311262fcfcd
Signed-off-by: David Reveman <reveman@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1030032
Reviewed-by: Zach Reizner <zachr@chromium.org>
diff --git a/drivers/virtio/virtio_wl.c b/drivers/virtio/virtio_wl.c
index 580b1c7..e14f84ed 100644
--- a/drivers/virtio/virtio_wl.c
+++ b/drivers/virtio/virtio_wl.c
@@ -111,6 +111,7 @@ static int virtwl_resp_err(unsigned int type)
switch (type) {
case VIRTIO_WL_RESP_OK:
case VIRTIO_WL_RESP_VFD_NEW:
+ case VIRTIO_WL_RESP_VFD_NEW_DMABUF:
return 0;
case VIRTIO_WL_RESP_ERR:
return -ENODEV; /* Device is no longer reliable */
@@ -875,8 +876,9 @@ static int virtwl_open(struct inode *inodep, struct file *filp)
return 0;
}
-static struct virtwl_vfd *do_new(struct virtwl_info *vi, uint32_t type,
- uint32_t size, bool nonblock)
+static struct virtwl_vfd *do_new(struct virtwl_info *vi,
+ struct virtwl_ioctl_new *ioctl_new,
+ size_t ioctl_new_size, bool nonblock)
{
struct virtio_wl_ctrl_vfd_new *ctrl_new;
struct virtwl_vfd *vfd;
@@ -885,10 +887,11 @@ static struct virtwl_vfd *do_new(struct virtwl_info *vi, uint32_t type,
struct scatterlist in_sg;
int ret = 0;
- if (type != VIRTWL_IOCTL_NEW_CTX &&
- type != VIRTWL_IOCTL_NEW_ALLOC &&
- type != VIRTWL_IOCTL_NEW_PIPE_READ &&
- type != VIRTWL_IOCTL_NEW_PIPE_WRITE)
+ if (ioctl_new->type != VIRTWL_IOCTL_NEW_CTX &&
+ ioctl_new->type != VIRTWL_IOCTL_NEW_ALLOC &&
+ ioctl_new->type != VIRTWL_IOCTL_NEW_PIPE_READ &&
+ ioctl_new->type != VIRTWL_IOCTL_NEW_PIPE_WRITE &&
+ ioctl_new->type != VIRTWL_IOCTL_NEW_DMABUF)
return ERR_PTR(-EINVAL);
ctrl_new = kzalloc(sizeof(*ctrl_new), GFP_KERNEL);
@@ -917,27 +920,33 @@ static struct virtwl_vfd *do_new(struct virtwl_info *vi, uint32_t type,
ret = 0;
ctrl_new->vfd_id = vfd->id;
- switch (type) {
+ switch (ioctl_new->type) {
case VIRTWL_IOCTL_NEW_CTX:
ctrl_new->hdr.type = VIRTIO_WL_CMD_VFD_NEW_CTX;
ctrl_new->flags = VIRTIO_WL_VFD_WRITE | VIRTIO_WL_VFD_READ;
- ctrl_new->size = 0;
break;
case VIRTWL_IOCTL_NEW_ALLOC:
ctrl_new->hdr.type = VIRTIO_WL_CMD_VFD_NEW;
- ctrl_new->flags = 0;
- ctrl_new->size = size;
+ ctrl_new->size = PAGE_ALIGN(ioctl_new->size);
break;
case VIRTWL_IOCTL_NEW_PIPE_READ:
ctrl_new->hdr.type = VIRTIO_WL_CMD_VFD_NEW_PIPE;
ctrl_new->flags = VIRTIO_WL_VFD_READ;
- ctrl_new->size = 0;
break;
case VIRTWL_IOCTL_NEW_PIPE_WRITE:
ctrl_new->hdr.type = VIRTIO_WL_CMD_VFD_NEW_PIPE;
ctrl_new->flags = VIRTIO_WL_VFD_WRITE;
- ctrl_new->size = 0;
break;
+ case VIRTWL_IOCTL_NEW_DMABUF:
+ /* Make sure ioctl_new contains enough data for NEW_DMABUF. */
+ if (ioctl_new_size == sizeof(*ioctl_new)) {
+ ctrl_new->hdr.type = VIRTIO_WL_CMD_VFD_NEW_DMABUF;
+ /* FIXME: convert from host byte order. */
+ memcpy(&ctrl_new->dmabuf, &ioctl_new->dmabuf,
+ sizeof(ioctl_new->dmabuf));
+ break;
+ }
+ /* fall-through */
default:
ret = -EINVAL;
goto remove_vfd;
@@ -963,6 +972,12 @@ static struct virtwl_vfd *do_new(struct virtwl_info *vi, uint32_t type,
mutex_unlock(&vfd->lock);
+ if (ioctl_new->type == VIRTWL_IOCTL_NEW_DMABUF) {
+ /* FIXME: convert to host byte order. */
+ memcpy(&ioctl_new->dmabuf, &ctrl_new->dmabuf,
+ sizeof(ctrl_new->dmabuf));
+ }
+
kfree(ctrl_new);
return vfd;
@@ -1073,26 +1088,25 @@ static long virtwl_vfd_ioctl(struct file *filp, unsigned int cmd,
}
}
-static long virtwl_ioctl_new(struct file *filp, void __user *ptr)
+static long virtwl_ioctl_new(struct file *filp, void __user *ptr,
+ size_t in_size)
{
struct virtwl_info *vi = filp->private_data;
struct virtwl_vfd *vfd;
- struct virtwl_ioctl_new ioctl_new;
+ struct virtwl_ioctl_new ioctl_new = {};
+ size_t size = min(in_size, sizeof(ioctl_new));
int ret;
/* Early check for user error. */
- ret = !access_ok(VERIFY_WRITE, ptr, sizeof(struct virtwl_ioctl_new));
+ ret = !access_ok(VERIFY_WRITE, ptr, size);
if (ret)
return -EFAULT;
- ret = copy_from_user(&ioctl_new, ptr, sizeof(struct virtwl_ioctl_new));
+ ret = copy_from_user(&ioctl_new, ptr, size);
if (ret)
return -EFAULT;
- ioctl_new.size = PAGE_ALIGN(ioctl_new.size);
-
- vfd = do_new(vi, ioctl_new.type, ioctl_new.size,
- filp->f_flags & O_NONBLOCK);
+ vfd = do_new(vi, &ioctl_new, size, filp->f_flags & O_NONBLOCK);
if (IS_ERR(vfd))
return PTR_ERR(vfd);
@@ -1104,7 +1118,7 @@ static long virtwl_ioctl_new(struct file *filp, void __user *ptr)
}
ioctl_new.fd = ret;
- ret = copy_to_user(ptr, &ioctl_new, sizeof(struct virtwl_ioctl_new));
+ ret = copy_to_user(ptr, &ioctl_new, size);
if (ret) {
/* The release operation will handle freeing this alloc */
sys_close(ioctl_new.fd);
@@ -1120,9 +1134,9 @@ static long virtwl_ioctl_ptr(struct file *filp, unsigned int cmd,
if (filp->f_op == &virtwl_vfd_fops)
return virtwl_vfd_ioctl(filp, cmd, ptr);
- switch (cmd) {
- case VIRTWL_IOCTL_NEW:
- return virtwl_ioctl_new(filp, ptr);
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(VIRTWL_IOCTL_NEW):
+ return virtwl_ioctl_new(filp, ptr, _IOC_SIZE(cmd));
default:
return -ENOTTY;
}
diff --git a/include/uapi/linux/virtio_wl.h b/include/uapi/linux/virtio_wl.h
index 2a5a166..89e0e42 100644
--- a/include/uapi/linux/virtio_wl.h
+++ b/include/uapi/linux/virtio_wl.h
@@ -34,9 +34,11 @@ enum virtio_wl_ctrl_type {
VIRTIO_WL_CMD_VFD_NEW_CTX, /* virtio_wl_ctrl_vfd_new */
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_RESP_OK = 0x1000,
VIRTIO_WL_RESP_VFD_NEW = 0x1001, /* virtio_wl_ctrl_vfd_new */
+ VIRTIO_WL_RESP_VFD_NEW_DMABUF = 0x1002, /* virtio_wl_ctrl_vfd_new */
VIRTIO_WL_RESP_ERR = 0x1100,
VIRTIO_WL_RESP_OUT_OF_MEMORY,
@@ -73,7 +75,19 @@ struct virtio_wl_ctrl_vfd_new {
__le32 vfd_id; /* MSB indicates device allocated vfd */
__le32 flags; /* virtio_wl_vfd_flags */
__le64 pfn; /* first guest physical page frame number if VFD_MAP */
- __le32 size; /* size in bytes if VIRTIO_WL_VFD_MAP */
+ __le32 size; /* size in bytes if VIRTIO_WL_CMD_VFD_NEW* */
+ /* buffer description if VIRTIO_WL_CMD_VFD_NEW_DMABUF */
+ struct {
+ __le32 width; /* width in pixels */
+ __le32 height; /* height in pixels */
+ __le32 format; /* fourcc format */
+ __le32 stride0; /* return stride0 */
+ __le32 stride1; /* return stride1 */
+ __le32 stride2; /* return stride2 */
+ __le32 offset0; /* return offset0 */
+ __le32 offset1; /* return offset1 */
+ __le32 offset2; /* return offset2 */
+ } dmabuf;
};
struct virtio_wl_ctrl_vfd_send {
diff --git a/include/uapi/linux/virtwl.h b/include/uapi/linux/virtwl.h
index 617c7ca..b9c0ed7 100644
--- a/include/uapi/linux/virtwl.h
+++ b/include/uapi/linux/virtwl.h
@@ -19,13 +19,30 @@ enum virtwl_ioctl_new_type {
VIRTWL_IOCTL_NEW_PIPE_READ,
/* create a new virtwl pipe that is writable via the returned fd */
VIRTWL_IOCTL_NEW_PIPE_WRITE,
+ /* create a new virtwl dmabuf that is writable via the returned fd */
+ VIRTWL_IOCTL_NEW_DMABUF,
};
struct virtwl_ioctl_new {
__u32 type; /* VIRTWL_IOCTL_NEW_* */
int fd; /* return fd */
__u32 flags; /* currently always 0 */
- __u32 size; /* size of allocation if type == VIRTWL_IOCTL_NEW_ALLOC */
+ union {
+ /* size of allocation if type == VIRTWL_IOCTL_NEW_ALLOC */
+ __u32 size;
+ /* buffer description if type == VIRTWL_IOCTL_NEW_DMABUF */
+ struct {
+ __u32 width; /* width in pixels */
+ __u32 height; /* height in pixels */
+ __u32 format; /* fourcc format */
+ __u32 stride0; /* return stride0 */
+ __u32 stride1; /* return stride1 */
+ __u32 stride2; /* return stride2 */
+ __u32 offset0; /* return offset0 */
+ __u32 offset1; /* return offset1 */
+ __u32 offset2; /* return offset2 */
+ } dmabuf;
+ };
};
struct virtwl_ioctl_txn {