diff mbox

[v2,4/6] virtio-gpu: add 3d/virgl support

Message ID 1442828417-6165-5-git-send-email-kraxel@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Gerd Hoffmann Sept. 21, 2015, 9:40 a.m. UTC
Add the bits needed for opengl rendering support: query
capabilities, new virtio commands, drm ioctls.

Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 drivers/gpu/drm/virtio/Makefile        |   3 +-
 drivers/gpu/drm/virtio/virtgpu_drv.c   |  10 +
 drivers/gpu/drm/virtio/virtgpu_drv.h   |  60 ++++
 drivers/gpu/drm/virtio/virtgpu_gem.c   |  41 +++
 drivers/gpu/drm/virtio/virtgpu_ioctl.c | 572 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/virtio/virtgpu_kms.c   | 135 +++++++-
 drivers/gpu/drm/virtio/virtgpu_ttm.c   |   1 +
 drivers/gpu/drm/virtio/virtgpu_vq.c    | 265 +++++++++++++++
 include/uapi/drm/Kbuild                |   1 +
 include/uapi/drm/virtgpu_drm.h         | 167 ++++++++++
 include/uapi/linux/virtio_gpu.h        | 112 ++++++-
 11 files changed, 1364 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/virtio/virtgpu_ioctl.c
 create mode 100644 include/uapi/drm/virtgpu_drm.h

Comments

Michael S. Tsirkin Sept. 21, 2015, 10:15 a.m. UTC | #1
On Mon, Sep 21, 2015 at 11:40:15AM +0200, Gerd Hoffmann wrote:
> Add the bits needed for opengl rendering support: query
> capabilities, new virtio commands, drm ioctls.
> 
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>


Looks good to me overall.
Some minor comments below.

I had to fix dri-devel mailing list address.
Hope I did it correctly.


> ---
>  drivers/gpu/drm/virtio/Makefile        |   3 +-
>  drivers/gpu/drm/virtio/virtgpu_drv.c   |  10 +
>  drivers/gpu/drm/virtio/virtgpu_drv.h   |  60 ++++
>  drivers/gpu/drm/virtio/virtgpu_gem.c   |  41 +++
>  drivers/gpu/drm/virtio/virtgpu_ioctl.c | 572 +++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/virtio/virtgpu_kms.c   | 135 +++++++-
>  drivers/gpu/drm/virtio/virtgpu_ttm.c   |   1 +
>  drivers/gpu/drm/virtio/virtgpu_vq.c    | 265 +++++++++++++++
>  include/uapi/drm/Kbuild                |   1 +
>  include/uapi/drm/virtgpu_drm.h         | 167 ++++++++++
>  include/uapi/linux/virtio_gpu.h        | 112 ++++++-
>  11 files changed, 1364 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/gpu/drm/virtio/virtgpu_ioctl.c
>  create mode 100644 include/uapi/drm/virtgpu_drm.h
> 
> diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
> index 2ee1602..da7bf19 100644
> --- a/drivers/gpu/drm/virtio/Makefile
> +++ b/drivers/gpu/drm/virtio/Makefile
> @@ -6,6 +6,7 @@ ccflags-y := -Iinclude/drm
>  
>  virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_drm_bus.o virtgpu_gem.o \
>  	virtgpu_fb.o virtgpu_display.o virtgpu_vq.o virtgpu_ttm.o \
> -	virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o
> +	virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o \
> +	virtgpu_ioctl.o
>  
>  obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
> index 7d9610a..957e455 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_drv.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
> @@ -73,6 +73,9 @@ static struct virtio_device_id id_table[] = {
>  };
>  
>  static unsigned int features[] = {
> +#ifdef __LITTLE_ENDIAN
> +	VIRTIO_GPU_FEATURE_VIRGL,
> +#endif
>  };

Why is virgl LE specific? Just curious.

>  static struct virtio_driver virtio_gpu_driver = {
>  	.feature_table = features,
> @@ -114,6 +117,8 @@ static struct drm_driver driver = {
>  	.set_busid = drm_virtio_set_busid,
>  	.load = virtio_gpu_driver_load,
>  	.unload = virtio_gpu_driver_unload,
> +	.open = virtio_gpu_driver_open,
> +	.postclose = virtio_gpu_driver_postclose,
>  
>  	.dumb_create = virtio_gpu_mode_dumb_create,
>  	.dumb_map_offset = virtio_gpu_mode_dumb_mmap,
> @@ -125,8 +130,13 @@ static struct drm_driver driver = {
>  #endif
>  
>  	.gem_free_object = virtio_gpu_gem_free_object,
> +	.gem_open_object = virtio_gpu_gem_object_open,
> +	.gem_close_object = virtio_gpu_gem_object_close,
>  	.fops = &virtio_gpu_driver_fops,
>  
> +	.ioctls = virtio_gpu_ioctls,
> +	.num_ioctls = DRM_VIRTIO_NUM_IOCTLS,
> +
>  	.name = DRIVER_NAME,
>  	.desc = DRIVER_DESC,
>  	.date = DRIVER_DATE,
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
> index 6d4db2d..2719108 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_drv.h
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
> @@ -146,6 +146,21 @@ struct virtio_gpu_queue {
>  	struct work_struct dequeue_work;
>  };
>  
> +struct virtio_gpu_drv_capset {
> +	uint32_t id;
> +	uint32_t max_version;
> +	uint32_t max_size;
> +};
> +
> +struct virtio_gpu_drv_cap_cache {
> +	struct list_head head;
> +	void *caps_cache;
> +	uint32_t id;
> +	uint32_t version;
> +	uint32_t size;
> +	atomic_t is_valid;
> +};
> +
>  struct virtio_gpu_device {
>  	struct device *dev;
>  	struct drm_device *ddev;
> @@ -179,7 +194,13 @@ struct virtio_gpu_device {
>  	struct idr	ctx_id_idr;
>  	spinlock_t ctx_id_idr_lock;
>  
> +	bool has_virgl_3d;
> +
>  	struct work_struct config_changed_work;
> +
> +	struct virtio_gpu_drv_capset *capsets;
> +	uint32_t num_capsets;
> +	struct list_head cap_cache;
>  };
>  
>  struct virtio_gpu_fpriv {
> @@ -193,6 +214,8 @@ extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
>  /* virtio_kms.c */
>  int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags);
>  int virtio_gpu_driver_unload(struct drm_device *dev);
> +int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file);
> +void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file);
>  
>  /* virtio_gem.c */
>  void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj);
> @@ -203,6 +226,10 @@ int virtio_gpu_gem_create(struct drm_file *file,
>  			  uint64_t size,
>  			  struct drm_gem_object **obj_p,
>  			  uint32_t *handle_p);
> +int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
> +			       struct drm_file *file);
> +void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
> +				 struct drm_file *file);
>  struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
>  						  size_t size, bool kernel,
>  						  bool pinned);
> @@ -260,10 +287,43 @@ void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
>  int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev);
>  void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
>  					   uint32_t resource_id);
> +int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx);
> +int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
> +			      int idx, int version,
> +			      struct virtio_gpu_drv_cap_cache **cache_p);
> +void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
> +				   uint32_t nlen, const char *name);
> +void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev,
> +				    uint32_t id);
> +void virtio_gpu_cmd_context_attach_resource(struct virtio_gpu_device *vgdev,
> +					    uint32_t ctx_id,
> +					    uint32_t resource_id);
> +void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev,
> +					    uint32_t ctx_id,
> +					    uint32_t resource_id);
> +void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev,
> +			   void *data, uint32_t data_size,
> +			   uint32_t ctx_id, struct virtio_gpu_fence **fence);
> +void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
> +					  uint32_t resource_id, uint32_t ctx_id,
> +					  uint64_t offset, uint32_t level,
> +					  struct virtio_gpu_box *box,
> +					  struct virtio_gpu_fence **fence);
> +void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
> +					uint32_t resource_id, uint32_t ctx_id,
> +					uint64_t offset, uint32_t level,
> +					struct virtio_gpu_box *box,
> +					struct virtio_gpu_fence **fence);
> +void
> +virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev,
> +				  struct virtio_gpu_resource_create_3d *rc_3d,
> +				  struct virtio_gpu_fence **fence);
>  void virtio_gpu_ctrl_ack(struct virtqueue *vq);
>  void virtio_gpu_cursor_ack(struct virtqueue *vq);
> +void virtio_gpu_fence_ack(struct virtqueue *vq);
>  void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
>  void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
> +void virtio_gpu_dequeue_fence_func(struct work_struct *work);
>  
>  /* virtio_gpu_display.c */
>  int virtio_gpu_framebuffer_init(struct drm_device *dev,
> diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
> index cfa0d27..1feb7ce 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_gem.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
> @@ -138,3 +138,44 @@ int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
>  	drm_gem_object_unreference_unlocked(gobj);
>  	return 0;
>  }
> +
> +int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
> +			       struct drm_file *file)
> +{
> +	struct virtio_gpu_device *vgdev = obj->dev->dev_private;
> +	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
> +	struct virtio_gpu_object *qobj = gem_to_virtio_gpu_obj(obj);
> +	int r;
> +
> +	if (!vgdev->has_virgl_3d)
> +		return 0;
> +
> +	r = virtio_gpu_object_reserve(qobj, false);
> +	if (r)
> +		return r;
> +
> +	virtio_gpu_cmd_context_attach_resource(vgdev, vfpriv->ctx_id,
> +					       qobj->hw_res_handle);
> +	virtio_gpu_object_unreserve(qobj);
> +	return 0;
> +}
> +
> +void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
> +				 struct drm_file *file)
> +{
> +	struct virtio_gpu_device *vgdev = obj->dev->dev_private;
> +	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
> +	struct virtio_gpu_object *qobj = gem_to_virtio_gpu_obj(obj);
> +	int r;
> +
> +	if (!vgdev->has_virgl_3d)
> +		return;
> +
> +	r = virtio_gpu_object_reserve(qobj, false);
> +	if (r)
> +		return;
> +
> +	virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
> +						qobj->hw_res_handle);
> +	virtio_gpu_object_unreserve(qobj);
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
> new file mode 100644
> index 0000000..d5c9e02
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
> @@ -0,0 +1,572 @@
> +/*
> + * Copyright (C) 2015 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * Authors:
> + *    Dave Airlie
> + *    Alon Levy
> + *
> + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
> + */
> +
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +#include <drm/virtgpu_drm.h>
> +#include "ttm/ttm_execbuf_util.h"
> +
> +static void convert_to_hw_box(struct virtio_gpu_box *dst,
> +			      const struct drm_virtgpu_3d_box *src)
> +{
> +	dst->x = cpu_to_le32(src->x);
> +	dst->y = cpu_to_le32(src->y);
> +	dst->z = cpu_to_le32(src->z);
> +	dst->w = cpu_to_le32(src->w);
> +	dst->h = cpu_to_le32(src->h);
> +	dst->d = cpu_to_le32(src->d);
> +}
> +
> +static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data,
> +				struct drm_file *file_priv)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct drm_virtgpu_map *virtio_gpu_map = data;
> +
> +	return virtio_gpu_mode_dumb_mmap(file_priv, vgdev->ddev,
> +					 virtio_gpu_map->handle,
> +					 &virtio_gpu_map->offset);
> +}
> +
> +static int virtio_gpu_object_list_validate(struct ww_acquire_ctx *ticket,
> +					   struct list_head *head)
> +{
> +	struct ttm_validate_buffer *buf;
> +	struct ttm_buffer_object *bo;
> +	struct virtio_gpu_object *qobj;
> +	int ret;
> +
> +	ret = ttm_eu_reserve_buffers(ticket, head, true, NULL);
> +	if (ret != 0)
> +		return ret;
> +
> +	list_for_each_entry(buf, head, head) {
> +		bo = buf->bo;
> +		qobj = container_of(bo, struct virtio_gpu_object, tbo);
> +		ret = ttm_bo_validate(bo, &qobj->placement, false, false);
> +		if (ret) {
> +			ttm_eu_backoff_reservation(ticket, head);
> +			return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static void virtio_gpu_unref_list(struct list_head *head)
> +{
> +	struct ttm_validate_buffer *buf;
> +	struct ttm_buffer_object *bo;
> +	struct virtio_gpu_object *qobj;
> +	list_for_each_entry(buf, head, head) {
> +		bo = buf->bo;
> +		qobj = container_of(bo, struct virtio_gpu_object, tbo);
> +
> +		drm_gem_object_unreference_unlocked(&qobj->gem_base);
> +	}
> +}
> +
> +static int virtio_gpu_execbuffer(struct drm_device *dev,
> +				 struct drm_virtgpu_execbuffer *exbuf,
> +				 struct drm_file *drm_file)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv;
> +	struct drm_gem_object *gobj;
> +	struct virtio_gpu_fence *fence;
> +	struct virtio_gpu_object *qobj;
> +	int ret;
> +	uint32_t *bo_handles = NULL;
> +	void __user *user_bo_handles = NULL;
> +	struct list_head validate_list;
> +	struct ttm_validate_buffer *buflist = NULL;
> +	struct ttm_validate_buffer cmdbuffer;
> +	int i;
> +	struct ww_acquire_ctx ticket;
> +	void *buf;
> +
> +	if (vgdev->has_virgl_3d == false)
> +		return -ENOSYS;
> +
> +	memset(&cmdbuffer, 0, sizeof(struct ttm_validate_buffer));
> +	INIT_LIST_HEAD(&validate_list);
> +	if (exbuf->num_bo_handles) {
> +
> +		bo_handles = drm_malloc_ab(exbuf->num_bo_handles,
> +					   sizeof(uint32_t));
> +		buflist = drm_calloc_large(exbuf->num_bo_handles,
> +					   sizeof(struct ttm_validate_buffer));
> +		if (!bo_handles || !buflist) {
> +			drm_free_large(bo_handles);
> +			drm_free_large(buflist);
> +			return -ENOMEM;
> +		}
> +
> +		user_bo_handles = (void __user *)(uintptr_t)exbuf->bo_handles;
> +		if (copy_from_user(bo_handles, user_bo_handles,
> +				   exbuf->num_bo_handles * sizeof(uint32_t))) {
> +			ret = -EFAULT;
> +			drm_free_large(bo_handles);
> +			return ret;
> +		}
> +
> +		for (i = 0; i < exbuf->num_bo_handles; i++) {
> +			gobj = drm_gem_object_lookup(dev,
> +						     drm_file, bo_handles[i]);
> +			if (!gobj) {
> +				drm_free_large(bo_handles);
> +				drm_free_large(buflist);
> +				return -ENOENT;
> +			}
> +
> +			qobj = gem_to_virtio_gpu_obj(gobj);
> +			buflist[i].bo = &qobj->tbo;
> +
> +			list_add(&buflist[i].head, &validate_list);
> +		}
> +		drm_free_large(bo_handles);
> +	}
> +
> +	ret = virtio_gpu_object_list_validate(&ticket, &validate_list);
> +	if (ret)
> +		goto out_free;
> +
> +	buf = kmalloc(exbuf->size, GFP_KERNEL);
> +	if (!buf) {
> +		ret = -ENOMEM;
> +		goto out_unresv;
> +	}
> +	if (copy_from_user(buf, (void __user *)(uintptr_t)exbuf->command,
> +			   exbuf->size)) {
> +		kfree(buf);
> +		ret = -EFAULT;
> +		goto out_unresv;
> +	}
> +	virtio_gpu_cmd_submit(vgdev, buf, exbuf->size,
> +			      vfpriv->ctx_id, &fence);
> +
> +	ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f);
> +
> +	/* fence the command bo */
> +	virtio_gpu_unref_list(&validate_list);
> +	drm_free_large(buflist);
> +	fence_put(&fence->f);
> +	return 0;
> +
> +out_unresv:
> +	ttm_eu_backoff_reservation(&ticket, &validate_list);
> +out_free:
> +	virtio_gpu_unref_list(&validate_list);
> +	drm_free_large(buflist);
> +	return ret;
> +}
> +
> +/*
> + * Usage of execbuffer:
> + * Relocations need to take into account the full VIRTIO_GPUDrawable size.
> + * However, the command as passed from user space must *not* contain the initial
> + * VIRTIO_GPUReleaseInfo struct (first XXX bytes)
> + */
> +static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
> +				       struct drm_file *file_priv)
> +{
> +	struct drm_virtgpu_execbuffer *execbuffer = data;
> +	return virtio_gpu_execbuffer(dev, execbuffer, file_priv);
> +}
> +
> +
> +static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
> +				     struct drm_file *file_priv)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct drm_virtgpu_getparam *param = data;
> +	int value;
> +
> +	switch (param->param) {
> +	case VIRTGPU_PARAM_3D_FEATURES:
> +		value = vgdev->has_virgl_3d == true ? 1 : 0;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (copy_to_user((void __user *)(unsigned long)param->value,
> +			 &value, sizeof(int))) {
> +		return -EFAULT;
> +	}
> +	return 0;
> +}
> +
> +static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
> +					    struct drm_file *file_priv)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct drm_virtgpu_resource_create *rc = data;
> +	int ret;
> +	uint32_t res_id;
> +	struct virtio_gpu_object *qobj;
> +	struct drm_gem_object *obj;
> +	uint32_t handle = 0;
> +	uint32_t size;
> +	struct list_head validate_list;
> +	struct ttm_validate_buffer mainbuf;
> +	struct virtio_gpu_fence *fence = NULL;
> +	struct ww_acquire_ctx ticket;
> +	struct virtio_gpu_resource_create_3d rc_3d;
> +
> +	if (vgdev->has_virgl_3d == false) {
> +		if (rc->depth > 1)
> +			return -EINVAL;
> +		if (rc->nr_samples > 1)
> +			return -EINVAL;
> +		if (rc->last_level > 1)
> +			return -EINVAL;
> +		if (rc->target != 2)
> +			return -EINVAL;
> +		if (rc->array_size > 1)
> +			return -EINVAL;
> +	}
> +
> +	INIT_LIST_HEAD(&validate_list);
> +	memset(&mainbuf, 0, sizeof(struct ttm_validate_buffer));
> +
> +	virtio_gpu_resource_id_get(vgdev, &res_id);
> +
> +	size = rc->size;
> +
> +	/* allocate a single page size object */
> +	if (size == 0)
> +		size = PAGE_SIZE;
> +
> +	qobj = virtio_gpu_alloc_object(dev, size, false, false);
> +	if (IS_ERR(qobj)) {
> +		ret = PTR_ERR(qobj);
> +		goto fail_id;
> +	}
> +	obj = &qobj->gem_base;
> +
> +	if (!vgdev->has_virgl_3d) {
> +		virtio_gpu_cmd_create_resource(vgdev, res_id, rc->format,
> +					       rc->width, rc->height);
> +
> +		ret = virtio_gpu_object_attach(vgdev, qobj, res_id, NULL);
> +	} else {
> +		/* use a gem reference since unref list undoes them */
> +		drm_gem_object_reference(&qobj->gem_base);
> +		mainbuf.bo = &qobj->tbo;
> +		list_add(&mainbuf.head, &validate_list);
> +
> +		ret = virtio_gpu_object_list_validate(&ticket, &validate_list);
> +		if (ret) {
> +			DRM_DEBUG("failed to validate\n");
> +			goto fail_unref;
> +		}
> +
> +		rc_3d.resource_id = cpu_to_le32(res_id);
> +		rc_3d.target = cpu_to_le32(rc->target);
> +		rc_3d.format = cpu_to_le32(rc->format);
> +		rc_3d.bind = cpu_to_le32(rc->bind);
> +		rc_3d.width = cpu_to_le32(rc->width);
> +		rc_3d.height = cpu_to_le32(rc->height);
> +		rc_3d.depth = cpu_to_le32(rc->depth);
> +		rc_3d.array_size = cpu_to_le32(rc->array_size);
> +		rc_3d.last_level = cpu_to_le32(rc->last_level);
> +		rc_3d.nr_samples = cpu_to_le32(rc->nr_samples);
> +		rc_3d.flags = cpu_to_le32(rc->flags);
> +
> +		virtio_gpu_cmd_resource_create_3d(vgdev, &rc_3d, NULL);
> +		ret = virtio_gpu_object_attach(vgdev, qobj, res_id, &fence);
> +		if (ret)
> +			goto fail_unref;
> +		ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f);
> +	}
> +
> +	qobj->hw_res_handle = res_id;
> +
> +	ret = drm_gem_handle_create(file_priv, obj, &handle);
> +	if (ret) {
> +
> +		drm_gem_object_release(obj);
> +		if (vgdev->has_virgl_3d) {
> +			virtio_gpu_unref_list(&validate_list);
> +			fence_put(&fence->f);
> +		}
> +		return ret;
> +	}
> +	drm_gem_object_unreference_unlocked(obj);
> +
> +	rc->res_handle = res_id; /* similiar to a VM address */
> +	rc->bo_handle = handle;
> +
> +	if (vgdev->has_virgl_3d) {
> +		virtio_gpu_unref_list(&validate_list);
> +		fence_put(&fence->f);
> +	}
> +	return 0;
> +fail_unref:
> +	if (vgdev->has_virgl_3d) {
> +		virtio_gpu_unref_list(&validate_list);
> +		fence_put(&fence->f);
> +	}
> +//fail_obj:
> +//	drm_gem_object_handle_unreference_unlocked(obj);
> +fail_id:
> +	virtio_gpu_resource_id_put(vgdev, res_id);
> +	return ret;
> +}
> +
> +static int virtio_gpu_resource_info_ioctl(struct drm_device *dev, void *data,
> +					  struct drm_file *file_priv)
> +{
> +	struct drm_virtgpu_resource_info *ri = data;
> +	struct drm_gem_object *gobj = NULL;
> +	struct virtio_gpu_object *qobj = NULL;
> +
> +	gobj = drm_gem_object_lookup(dev, file_priv, ri->bo_handle);
> +	if (gobj == NULL)
> +		return -ENOENT;
> +
> +	qobj = gem_to_virtio_gpu_obj(gobj);
> +
> +	ri->size = qobj->gem_base.size;
> +	ri->res_handle = qobj->hw_res_handle;
> +	drm_gem_object_unreference_unlocked(gobj);
> +	return 0;
> +}
> +
> +static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev,
> +					       void *data,
> +					       struct drm_file *file)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
> +	struct drm_virtgpu_3d_transfer_from_host *args = data;
> +	struct drm_gem_object *gobj = NULL;
> +	struct virtio_gpu_object *qobj = NULL;
> +	struct virtio_gpu_fence *fence;
> +	int ret;
> +	u32 offset = args->offset;
> +	struct virtio_gpu_box box;
> +
> +	if (vgdev->has_virgl_3d == false)
> +		return -ENOSYS;
> +
> +	gobj = drm_gem_object_lookup(dev, file, args->bo_handle);
> +	if (gobj == NULL)
> +		return -ENOENT;
> +
> +	qobj = gem_to_virtio_gpu_obj(gobj);
> +
> +	ret = virtio_gpu_object_reserve(qobj, false);
> +	if (ret)
> +		goto out;
> +
> +	ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
> +			      true, false);
> +	if (unlikely(ret))
> +		goto out_unres;
> +
> +	convert_to_hw_box(&box, &args->box);
> +	virtio_gpu_cmd_transfer_from_host_3d
> +		(vgdev, qobj->hw_res_handle,
> +		 vfpriv->ctx_id, offset, args->level,
> +		 &box, &fence);
> +	reservation_object_add_excl_fence(qobj->tbo.resv,
> +					  &fence->f);
> +
> +	fence_put(&fence->f);
> +out_unres:
> +	virtio_gpu_object_unreserve(qobj);
> +out:
> +	drm_gem_object_unreference_unlocked(gobj);
> +	return ret;
> +}
> +
> +static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
> +					     struct drm_file *file)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
> +	struct drm_virtgpu_3d_transfer_to_host *args = data;
> +	struct drm_gem_object *gobj = NULL;
> +	struct virtio_gpu_object *qobj = NULL;
> +	struct virtio_gpu_fence *fence;
> +	struct virtio_gpu_box box;
> +	int ret;
> +	u32 offset = args->offset;
> +
> +	gobj = drm_gem_object_lookup(dev, file, args->bo_handle);
> +	if (gobj == NULL)
> +		return -ENOENT;
> +
> +	qobj = gem_to_virtio_gpu_obj(gobj);
> +
> +	ret = virtio_gpu_object_reserve(qobj, false);
> +	if (ret)
> +		goto out;
> +
> +	ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
> +			      true, false);
> +	if (unlikely(ret))
> +		goto out_unres;
> +
> +	convert_to_hw_box(&box, &args->box);
> +	if (!vgdev->has_virgl_3d) {
> +		virtio_gpu_cmd_transfer_to_host_2d
> +			(vgdev, qobj->hw_res_handle, offset,
> +			 box.w, box.h, box.x, box.y, NULL);
> +	} else {
> +		virtio_gpu_cmd_transfer_to_host_3d
> +			(vgdev, qobj->hw_res_handle,
> +			 vfpriv ? vfpriv->ctx_id : 0, offset,
> +			 args->level, &box, &fence);
> +		reservation_object_add_excl_fence(qobj->tbo.resv,
> +						  &fence->f);
> +		fence_put(&fence->f);
> +	}
> +
> +out_unres:
> +	virtio_gpu_object_unreserve(qobj);
> +out:
> +	drm_gem_object_unreference_unlocked(gobj);
> +	return ret;
> +}
> +
> +static int virtio_gpu_wait_ioctl(struct drm_device *dev, void *data,
> +			    struct drm_file *file)
> +{
> +	struct drm_virtgpu_3d_wait *args = data;
> +	struct drm_gem_object *gobj = NULL;
> +	struct virtio_gpu_object *qobj = NULL;
> +	int ret;
> +	bool nowait = false;
> +
> +	gobj = drm_gem_object_lookup(dev, file, args->handle);
> +	if (gobj == NULL)
> +		return -ENOENT;
> +
> +	qobj = gem_to_virtio_gpu_obj(gobj);
> +
> +	if (args->flags & VIRTGPU_WAIT_NOWAIT)
> +		nowait = true;
> +	ret = virtio_gpu_object_wait(qobj, nowait);
> +
> +	drm_gem_object_unreference_unlocked(gobj);
> +	return ret;
> +}
> +
> +static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
> +				void *data, struct drm_file *file)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct drm_virtgpu_get_caps *args = data;
> +	int size;
> +	int i;
> +	int found_valid = -1;
> +	int ret;
> +	struct virtio_gpu_drv_cap_cache *cache_ent;
> +	void *ptr;
> +	if (vgdev->num_capsets == 0)
> +		return -ENOSYS;
> +
> +	spin_lock(&vgdev->display_info_lock);
> +	for (i = 0; i < vgdev->num_capsets; i++) {
> +		if (vgdev->capsets[i].id == args->cap_set_id) {
> +			if (vgdev->capsets[i].max_version >= args->cap_set_ver) {
> +				found_valid = i;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (found_valid == -1) {
> +		spin_unlock(&vgdev->display_info_lock);
> +		return -EINVAL;
> +	}
> +
> +	size = vgdev->capsets[found_valid].max_size;
> +	if (args->size > size) {
> +		spin_unlock(&vgdev->display_info_lock);
> +		return -EINVAL;
> +	}
> +
> +	list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
> +		if (cache_ent->id == args->cap_set_id &&
> +		    cache_ent->version == args->cap_set_ver) {
> +			ptr = cache_ent->caps_cache;
> +			spin_unlock(&vgdev->display_info_lock);
> +			goto copy_exit;
> +		}
> +	}
> +	spin_unlock(&vgdev->display_info_lock);
> +
> +	/* not in cache - need to talk to hw */
> +	virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver,
> +				  &cache_ent);
> +
> +	ret = wait_event_timeout(vgdev->resp_wq,
> +				 atomic_read(&cache_ent->is_valid), 5 * HZ);
> +
> +	ptr = cache_ent->caps_cache;
> +
> +copy_exit:
> +	if (copy_to_user((void __user *)(unsigned long)args->addr, ptr, size))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_MAP, virtio_gpu_map_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_EXECBUFFER, virtio_gpu_execbuffer_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_GETPARAM, virtio_gpu_getparam_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE,
> +			  virtio_gpu_resource_create_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_INFO, virtio_gpu_resource_info_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +
> +	/* make transfer async to the main ring? - no sure, can we
> +	   thread these in the underlying GL */
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_FROM_HOST,
> +			  virtio_gpu_transfer_from_host_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_TO_HOST,
> +			  virtio_gpu_transfer_to_host_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_WAIT, virtio_gpu_wait_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +
> +	DRM_IOCTL_DEF_DRV(VIRTGPU_GET_CAPS, virtio_gpu_get_caps_ioctl,
> +			  DRM_AUTH|DRM_UNLOCKED),
> +};
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
> index 782766c..12a1091 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -52,6 +52,41 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
>  		      events_clear, &events_clear);
>  }
>  
> +static void virtio_gpu_ctx_id_get(struct virtio_gpu_device *vgdev,
> +				  uint32_t *resid)
> +{
> +	int handle;
> +
> +	idr_preload(GFP_KERNEL);
> +	spin_lock(&vgdev->ctx_id_idr_lock);
> +	handle = idr_alloc(&vgdev->ctx_id_idr, NULL, 1, 0, 0);
> +	spin_unlock(&vgdev->ctx_id_idr_lock);
> +	idr_preload_end();
> +	*resid = handle;
> +}
> +
> +static void virtio_gpu_ctx_id_put(struct virtio_gpu_device *vgdev, uint32_t id)
> +{
> +	spin_lock(&vgdev->ctx_id_idr_lock);
> +	idr_remove(&vgdev->ctx_id_idr, id);
> +	spin_unlock(&vgdev->ctx_id_idr_lock);
> +}
> +
> +static void virtio_gpu_context_create(struct virtio_gpu_device *vgdev,
> +				      uint32_t nlen, const char *name,
> +				      uint32_t *ctx_id)
> +{
> +	virtio_gpu_ctx_id_get(vgdev, ctx_id);
> +	virtio_gpu_cmd_context_create(vgdev, *ctx_id, nlen, name);
> +}
> +
> +static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev,
> +				      uint32_t ctx_id)
> +{
> +	virtio_gpu_cmd_context_destroy(vgdev, ctx_id);
> +	virtio_gpu_ctx_id_put(vgdev, ctx_id);
> +}
> +
>  static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
>  			       void (*work_func)(struct work_struct *work))
>  {
> @@ -60,6 +95,36 @@ static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
>  	INIT_WORK(&vgvq->dequeue_work, work_func);
>  }
>  
> +static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev,
> +				   int num_capsets)
> +{
> +	int i, ret;
> +
> +	vgdev->capsets = kcalloc(num_capsets,
> +				 sizeof(struct virtio_gpu_drv_capset),
> +				 GFP_KERNEL);
> +	if (!vgdev->capsets) {
> +		DRM_ERROR("failed to allocate cap sets\n");
> +		return;
> +	}
> +	for (i = 0; i < num_capsets; i++) {
> +		virtio_gpu_cmd_get_capset_info(vgdev, i);
> +		ret = wait_event_timeout(vgdev->resp_wq,
> +					 vgdev->capsets[i].id > 0, 5 * HZ);
> +		if (ret == 0) {
> +			DRM_ERROR("timed out waiting for cap set %d\n", i);
> +			kfree(vgdev->capsets);
> +			vgdev->capsets = NULL;
> +			return;
> +		}
> +		DRM_INFO("cap set %d: id %d, max-version %d, max-size %d\n",
> +			 i, vgdev->capsets[i].id,
> +			 vgdev->capsets[i].max_version,
> +			 vgdev->capsets[i].max_size);
> +	}
> +	vgdev->num_capsets = num_capsets;
> +}
> +
>  int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  {
>  	static vq_callback_t *callbacks[] = {
> @@ -70,7 +135,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  	struct virtio_gpu_device *vgdev;
>  	/* this will expand later */
>  	struct virtqueue *vqs[2];
> -	u32 num_scanouts;
> +	u32 num_scanouts, num_capsets;
>  	int ret;
>  
>  	if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
> @@ -96,9 +161,17 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  
>  	spin_lock_init(&vgdev->fence_drv.lock);
>  	INIT_LIST_HEAD(&vgdev->fence_drv.fences);
> +	INIT_LIST_HEAD(&vgdev->cap_cache);
>  	INIT_WORK(&vgdev->config_changed_work,
>  		  virtio_gpu_config_changed_work_func);
>  
> +#ifdef __LITTLE_ENDIAN
> +	if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_FEATURE_VIRGL))
> +		vgdev->has_virgl_3d = true;
> +#endif

You might be able to get by without this ifdef as
VIRTIO_GPU_FEATURE_VIRGL won't be set without it.


> +	DRM_INFO("virgl 3d acceleration %s\n",
> +		 vgdev->has_virgl_3d ? "enabled" : "not available");
> +
>  	ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs,
>  					    callbacks, names);
>  	if (ret) {
> @@ -129,6 +202,11 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  		ret = -EINVAL;
>  		goto err_scanouts;
>  	}
> +	DRM_INFO("number of scanouts: %d\n", num_scanouts);
> +
> +	virtio_cread(vgdev->vdev, struct virtio_gpu_config,
> +		     num_capsets, &num_capsets);
> +	DRM_INFO("number of cap sets: %d\n", num_capsets);
>  
>  	ret = virtio_gpu_modeset_init(vgdev);
>  	if (ret)
> @@ -137,6 +215,8 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  	virtio_device_ready(vgdev->vdev);
>  	vgdev->vqs_ready = true;
>  
> +	if (num_capsets)
> +		virtio_gpu_get_capsets(vgdev, num_capsets);
>  	virtio_gpu_cmd_get_display_info(vgdev);
>  	wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending,
>  			   5 * HZ);
> @@ -157,6 +237,16 @@ err_vqs:
>  	return ret;
>  }
>  
> +static void virtio_gpu_cleanup_cap_cache(struct virtio_gpu_device *vgdev)
> +{
> +	struct virtio_gpu_drv_cap_cache *cache_ent, *tmp;
> +
> +	list_for_each_entry_safe(cache_ent, tmp, &vgdev->cap_cache, head) {
> +		kfree(cache_ent->caps_cache);
> +		kfree(cache_ent);
> +	}
> +}
> +
>  int virtio_gpu_driver_unload(struct drm_device *dev)
>  {
>  	struct virtio_gpu_device *vgdev = dev->dev_private;
> @@ -170,6 +260,49 @@ int virtio_gpu_driver_unload(struct drm_device *dev)
>  	virtio_gpu_modeset_fini(vgdev);
>  	virtio_gpu_ttm_fini(vgdev);
>  	virtio_gpu_free_vbufs(vgdev);
> +	virtio_gpu_cleanup_cap_cache(vgdev);
> +	kfree(vgdev->capsets);
>  	kfree(vgdev);
>  	return 0;
>  }
> +
> +int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct virtio_gpu_fpriv *vfpriv;
> +	uint32_t id;
> +	char dbgname[64], tmpname[TASK_COMM_LEN];
> +
> +	/* can't create contexts without 3d renderer */
> +	if (!vgdev->has_virgl_3d)
> +		return 0;
> +
> +	get_task_comm(tmpname, current);
> +	snprintf(dbgname, sizeof(dbgname), "%s", tmpname);
> +	dbgname[63] = 0;
> +	/* allocate a virt GPU context for this opener */
> +	vfpriv = kzalloc(sizeof(*vfpriv), GFP_KERNEL);
> +	if (!vfpriv)
> +		return -ENOMEM;
> +
> +	virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname, &id);
> +
> +	vfpriv->ctx_id = id;
> +	file->driver_priv = vfpriv;
> +	return 0;
> +}
> +
> +void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file)
> +{
> +	struct virtio_gpu_device *vgdev = dev->dev_private;
> +	struct virtio_gpu_fpriv *vfpriv;
> +
> +	if (!vgdev->has_virgl_3d)
> +		return;
> +
> +	vfpriv = file->driver_priv;
> +
> +	virtio_gpu_context_destroy(vgdev, vfpriv->ctx_id);
> +	kfree(vfpriv);
> +	file->driver_priv = NULL;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c
> index b092d7b..9fd924c 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_ttm.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c
> @@ -32,6 +32,7 @@
>  #include <ttm/ttm_module.h>
>  #include <drm/drmP.h>
>  #include <drm/drm.h>
> +#include <drm/virtgpu_drm.h>
>  #include "virtgpu_drv.h"
>  
>  #include <linux/delay.h>
> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
> index ee25e9a..ac4f642 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_vq.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
> @@ -586,6 +586,47 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
>  		drm_kms_helper_hotplug_event(vgdev->ddev);
>  }
>  
> +static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
> +					      struct virtio_gpu_vbuffer *vbuf)
> +{
> +	struct virtio_gpu_get_capset_info *cmd =
> +		(struct virtio_gpu_get_capset_info *)vbuf->buf;
> +	struct virtio_gpu_resp_capset_info *resp =
> +		(struct virtio_gpu_resp_capset_info *)vbuf->resp_buf;
> +	int i = le32_to_cpu(cmd->capset_index);
> +
> +	spin_lock(&vgdev->display_info_lock);
> +	vgdev->capsets[i].id = le32_to_cpu(resp->capset_id);
> +	vgdev->capsets[i].max_version = le32_to_cpu(resp->capset_max_version);
> +	vgdev->capsets[i].max_size = le32_to_cpu(resp->capset_max_size);
> +	spin_unlock(&vgdev->display_info_lock);
> +	wake_up(&vgdev->resp_wq);
> +}
> +
> +static void virtio_gpu_cmd_capset_cb(struct virtio_gpu_device *vgdev,
> +				     struct virtio_gpu_vbuffer *vbuf)
> +{
> +	struct virtio_gpu_get_capset *cmd =
> +		(struct virtio_gpu_get_capset *)vbuf->buf;
> +	struct virtio_gpu_resp_capset *resp =
> +		(struct virtio_gpu_resp_capset *)vbuf->resp_buf;
> +	struct virtio_gpu_drv_cap_cache *cache_ent;
> +
> +	spin_lock(&vgdev->display_info_lock);
> +	list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
> +		if (cache_ent->version == le32_to_cpu(cmd->capset_version) &&
> +		    cache_ent->id == le32_to_cpu(cmd->capset_id)) {
> +			memcpy(cache_ent->caps_cache, resp->capset_data,
> +			       cache_ent->size);
> +			atomic_set(&cache_ent->is_valid, 1);
> +			break;
> +		}
> +	}
> +	spin_unlock(&vgdev->display_info_lock);
> +	wake_up(&vgdev->resp_wq);
> +}
> +
> +
>  int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
>  {
>  	struct virtio_gpu_ctrl_hdr *cmd_p;
> @@ -609,6 +650,230 @@ int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
>  	return 0;
>  }
>  
> +int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx)
> +{
> +	struct virtio_gpu_get_capset_info *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +	void *resp_buf;
> +
> +	resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_capset_info),
> +			   GFP_KERNEL);
> +	if (!resp_buf)
> +		return -ENOMEM;
> +
> +	cmd_p = virtio_gpu_alloc_cmd_resp
> +		(vgdev, &virtio_gpu_cmd_get_capset_info_cb, &vbuf,
> +		 sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_capset_info),
> +		 resp_buf);
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_CAPSET_INFO);
> +	cmd_p->capset_index = cpu_to_le32(idx);
> +	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +	return 0;
> +}
> +

I note that all callers ignore the recurn code from
virtio_gpu_queue_ctrl_buffer.

Is there a reason it can't fail?

Shouldn't it be made void then?


> +int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
> +			      int idx, int version,
> +			      struct virtio_gpu_drv_cap_cache **cache_p)
> +{
> +	struct virtio_gpu_get_capset *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +	int max_size = vgdev->capsets[idx].max_size;
> +	struct virtio_gpu_drv_cap_cache *cache_ent;
> +	void *resp_buf;
> +
> +	if (idx > vgdev->num_capsets)
> +		return -EINVAL;
> +
> +	if (version > vgdev->capsets[idx].max_version)
> +		return -EINVAL;
> +
> +	cache_ent = kzalloc(sizeof(*cache_ent), GFP_KERNEL);
> +	if (!cache_ent)
> +		return -ENOMEM;
> +
> +	cache_ent->caps_cache = kmalloc(max_size, GFP_KERNEL);
> +	if (!cache_ent->caps_cache) {
> +		kfree(cache_ent);
> +		return -ENOMEM;
> +	}
> +
> +	resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_capset) + max_size,
> +			   GFP_KERNEL);
> +	if (!resp_buf) {
> +		kfree(cache_ent->caps_cache);
> +		kfree(cache_ent);
> +		return -ENOMEM;
> +	}
> +
> +	cache_ent->version = version;
> +	cache_ent->id = vgdev->capsets[idx].id;
> +	atomic_set(&cache_ent->is_valid, 0);
> +	cache_ent->size = max_size;
> +	spin_lock(&vgdev->display_info_lock);
> +	list_add_tail(&cache_ent->head, &vgdev->cap_cache);
> +	spin_unlock(&vgdev->display_info_lock);
> +
> +	cmd_p = virtio_gpu_alloc_cmd_resp
> +		(vgdev, &virtio_gpu_cmd_capset_cb, &vbuf, sizeof(*cmd_p),
> +		 sizeof(struct virtio_gpu_resp_capset) + max_size,
> +		 resp_buf);
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_CAPSET);
> +	cmd_p->capset_id = cpu_to_le32(vgdev->capsets[idx].id);
> +	cmd_p->capset_version = cpu_to_le32(version);
> +	*cache_p = cache_ent;
> +	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> +	return 0;
> +}
> +
> +void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
> +				   uint32_t nlen, const char *name)
> +{
> +	struct virtio_gpu_ctx_create *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_CREATE);
> +	cmd_p->hdr.ctx_id = cpu_to_le32(id);
> +	cmd_p->nlen = cpu_to_le32(nlen);
> +	strncpy(cmd_p->debug_name, name, 63);
> +	cmd_p->debug_name[63] = 0;

sizeof (cmd_p->debug_name) - 1 will be a but prettier.

> +	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +}
> +
> +void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev,
> +				    uint32_t id)
> +{
> +	struct virtio_gpu_ctx_destroy *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_DESTROY);
> +	cmd_p->hdr.ctx_id = cpu_to_le32(id);
> +	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +}
> +
> +void virtio_gpu_cmd_context_attach_resource(struct virtio_gpu_device *vgdev,
> +					    uint32_t ctx_id,
> +					    uint32_t resource_id)
> +{
> +	struct virtio_gpu_ctx_resource *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE);
> +	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
> +	cmd_p->resource_id = cpu_to_le32(resource_id);
> +	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> +}
> +
> +void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev,
> +					    uint32_t ctx_id,
> +					    uint32_t resource_id)
> +{
> +	struct virtio_gpu_ctx_resource *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE);
> +	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
> +	cmd_p->resource_id = cpu_to_le32(resource_id);
> +	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +}
> +
> +void
> +virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev,
> +				  struct virtio_gpu_resource_create_3d *rc_3d,
> +				  struct virtio_gpu_fence **fence)
> +{
> +	struct virtio_gpu_resource_create_3d *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	*cmd_p = *rc_3d;
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D);
> +	cmd_p->hdr.flags = 0;
> +
> +	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
> +}
> +
> +void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
> +					uint32_t resource_id, uint32_t ctx_id,
> +					uint64_t offset, uint32_t level,
> +					struct virtio_gpu_box *box,
> +					struct virtio_gpu_fence **fence)
> +{
> +	struct virtio_gpu_transfer_host_3d *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D);
> +	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
> +	cmd_p->resource_id = cpu_to_le32(resource_id);
> +	cmd_p->box = *box;
> +	cmd_p->offset = cpu_to_le64(offset);
> +	cmd_p->level = cpu_to_le32(level);
> +
> +	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
> +}
> +
> +void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
> +					  uint32_t resource_id, uint32_t ctx_id,
> +					  uint64_t offset, uint32_t level,
> +					  struct virtio_gpu_box *box,
> +					  struct virtio_gpu_fence **fence)
> +{
> +	struct virtio_gpu_transfer_host_3d *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D);
> +	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
> +	cmd_p->resource_id = cpu_to_le32(resource_id);
> +	cmd_p->box = *box;
> +	cmd_p->offset = cpu_to_le64(offset);
> +	cmd_p->level = cpu_to_le32(level);
> +
> +	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
> +}
> +
> +void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev,
> +			   void *data, uint32_t data_size,
> +			   uint32_t ctx_id, struct virtio_gpu_fence **fence)
> +{
> +	struct virtio_gpu_cmd_submit *cmd_p;
> +	struct virtio_gpu_vbuffer *vbuf;
> +
> +	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +	memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +	vbuf->data_buf = data;
> +	vbuf->data_size = data_size;
> +
> +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_SUBMIT_3D);
> +	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
> +	cmd_p->size = cpu_to_le32(data_size);
> +
> +	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
> +}
> +
>  int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
>  			     struct virtio_gpu_object *obj,
>  			     uint32_t resource_id,
> diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild
> index 2d9a25d..38d4370 100644
> --- a/include/uapi/drm/Kbuild
> +++ b/include/uapi/drm/Kbuild
> @@ -17,3 +17,4 @@ header-y += tegra_drm.h
>  header-y += via_drm.h
>  header-y += vmwgfx_drm.h
>  header-y += msm_drm.h
> +header-y += virtgpu_drm.h
> diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
> new file mode 100644
> index 0000000..fc9e2d6
> --- /dev/null
> +++ b/include/uapi/drm/virtgpu_drm.h
> @@ -0,0 +1,167 @@
> +/*
> + * Copyright 2013 Red Hat
> + * All Rights Reserved.
> + *
> + * 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 AND/OR ITS SUPPLIERS 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 VIRTGPU_DRM_H
> +#define VIRTGPU_DRM_H
> +
> +#include <stddef.h>
> +#include "drm/drm.h"
> +
> +/* Please note that modifications to all structs defined here are
> + * subject to backwards-compatibility constraints.
> + *
> + * Do not use pointers, use uint64_t instead for 32 bit / 64 bit user/kernel
> + * compatibility Keep fields aligned to their size
> + */
> +
> +#define DRM_VIRTGPU_MAP         0x01
> +#define DRM_VIRTGPU_EXECBUFFER  0x02
> +#define DRM_VIRTGPU_GETPARAM    0x03
> +#define DRM_VIRTGPU_RESOURCE_CREATE 0x04
> +#define DRM_VIRTGPU_RESOURCE_INFO     0x05
> +#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x06
> +#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07
> +#define DRM_VIRTGPU_WAIT     0x08
> +#define DRM_VIRTGPU_GET_CAPS  0x09
> +
> +struct drm_virtgpu_map {
> +	uint64_t offset; /* use for mmap system call */
> +	uint32_t handle;
> +	uint32_t pad;
> +};
> +
> +struct drm_virtgpu_execbuffer {
> +	uint32_t		flags;		/* for future use */
> +	uint32_t size;
> +	uint64_t command; /* void* */
> +	uint64_t bo_handles;
> +	uint32_t num_bo_handles;
> +	uint32_t pad;
> +};
> +
> +#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
> +
> +struct drm_virtgpu_getparam {
> +	uint64_t param;
> +	uint64_t value;
> +};
> +
> +/* NO_BO flags? NO resource flag? */
> +/* resource flag for y_0_top */
> +struct drm_virtgpu_resource_create {
> +	uint32_t target;
> +	uint32_t format;
> +	uint32_t bind;
> +	uint32_t width;
> +	uint32_t height;
> +	uint32_t depth;
> +	uint32_t array_size;
> +	uint32_t last_level;
> +	uint32_t nr_samples;
> +	uint32_t flags;
> +	uint32_t bo_handle; /* if this is set - recreate a new resource attached to this bo ? */
> +	uint32_t res_handle;  /* returned by kernel */
> +	uint32_t size;        /* validate transfer in the host */
> +	uint32_t stride;      /* validate transfer in the host */
> +};
> +
> +struct drm_virtgpu_resource_info {
> +	uint32_t bo_handle;
> +	uint32_t res_handle;
> +	uint32_t size;
> +	uint32_t stride;
> +};
> +
> +struct drm_virtgpu_3d_box {
> +	uint32_t x;
> +	uint32_t y;
> +	uint32_t z;
> +	uint32_t w;
> +	uint32_t h;
> +	uint32_t d;
> +};
> +
> +struct drm_virtgpu_3d_transfer_to_host {
> +	uint32_t bo_handle;
> +	struct drm_virtgpu_3d_box box;
> +	uint32_t level;
> +	uint32_t offset;
> +};
> +
> +struct drm_virtgpu_3d_transfer_from_host {
> +	uint32_t bo_handle;
> +	struct drm_virtgpu_3d_box box;
> +	uint32_t level;
> +	uint32_t offset;
> +};
> +
> +#define VIRTGPU_WAIT_NOWAIT 1 /* like it */
> +struct drm_virtgpu_3d_wait {
> +	uint32_t handle; /* 0 is an invalid handle */
> +	uint32_t flags;
> +};
> +
> +struct drm_virtgpu_get_caps {
> +	uint32_t cap_set_id;
> +	uint32_t cap_set_ver;
> +	uint64_t addr;
> +	uint32_t size;
> +	uint32_t pad;
> +};
> +
> +#define DRM_IOCTL_VIRTGPU_MAP \
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map)
> +
> +#define DRM_IOCTL_VIRTGPU_EXECBUFFER \
> +	DRM_IOW(DRM_COMMAND_BASE + DRM_VIRTGPU_EXECBUFFER,\
> +		struct drm_virtgpu_execbuffer)
> +
> +#define DRM_IOCTL_VIRTGPU_GETPARAM \
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GETPARAM,\
> +		struct drm_virtgpu_getparam)
> +
> +#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE			\
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE,	\
> +		struct drm_virtgpu_resource_create)
> +
> +#define DRM_IOCTL_VIRTGPU_RESOURCE_INFO \
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_INFO, \
> +		 struct drm_virtgpu_resource_info)
> +
> +#define DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST \
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_FROM_HOST,	\
> +		struct drm_virtgpu_3d_transfer_from_host)
> +
> +#define DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST \
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_TO_HOST,	\
> +		struct drm_virtgpu_3d_transfer_to_host)
> +
> +#define DRM_IOCTL_VIRTGPU_WAIT				\
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT,	\
> +		struct drm_virtgpu_3d_wait)
> +
> +#define DRM_IOCTL_VIRTGPU_GET_CAPS \
> +	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \
> +	struct drm_virtgpu_get_caps)
> +
> +#endif
> diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
> index 478be52..7f4f9ce 100644
> --- a/include/uapi/linux/virtio_gpu.h
> +++ b/include/uapi/linux/virtio_gpu.h
> @@ -40,6 +40,8 @@
>  
>  #include <linux/types.h>
>  
> +#define VIRTIO_GPU_FEATURE_VIRGL 0
> +

I'd prefer it named VIRTIO_GPU_F_VIRGL for consistency with other
devices.



>  enum virtio_gpu_ctrl_type {
>  	VIRTIO_GPU_UNDEFINED = 0,
>  
> @@ -52,6 +54,18 @@ enum virtio_gpu_ctrl_type {
>  	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
>  	VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
>  	VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
> +	VIRTIO_GPU_CMD_GET_CAPSET_INFO,
> +	VIRTIO_GPU_CMD_GET_CAPSET,
> +
> +	/* 3d commands */
> +	VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
> +	VIRTIO_GPU_CMD_CTX_DESTROY,
> +	VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
> +	VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
> +	VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
> +	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
> +	VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
> +	VIRTIO_GPU_CMD_SUBMIT_3D,
>  
>  	/* cursor commands */
>  	VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
> @@ -60,6 +74,8 @@ enum virtio_gpu_ctrl_type {
>  	/* success responses */
>  	VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
>  	VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
> +	VIRTIO_GPU_RESP_OK_CAPSET_INFO,
> +	VIRTIO_GPU_RESP_OK_CAPSET,
>  
>  	/* error responses */
>  	VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
> @@ -180,13 +196,107 @@ struct virtio_gpu_resp_display_info {
>  	} pmodes[VIRTIO_GPU_MAX_SCANOUTS];
>  };
>  
> +/* data passed in the control vq, 3d related */
> +
> +struct virtio_gpu_box {
> +	__le32 x, y, z;
> +	__le32 w, h, d;
> +};
> +
> +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
> +struct virtio_gpu_transfer_host_3d {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	struct virtio_gpu_box box;
> +	__le64 offset;
> +	__le32 resource_id;
> +	__le32 level;
> +	__le32 stride;
> +	__le32 layer_stride;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
> +#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0)
> +struct virtio_gpu_resource_create_3d {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	__le32 resource_id;
> +	__le32 target;
> +	__le32 format;
> +	__le32 bind;
> +	__le32 width;
> +	__le32 height;
> +	__le32 depth;
> +	__le32 array_size;
> +	__le32 last_level;
> +	__le32 nr_samples;
> +	__le32 flags;
> +	__le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_CTX_CREATE */
> +struct virtio_gpu_ctx_create {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	__le32 nlen;
> +	__le32 padding;
> +	char debug_name[64];
> +};
> +
> +/* VIRTIO_GPU_CMD_CTX_DESTROY */
> +struct virtio_gpu_ctx_destroy {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +};
> +
> +/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
> +struct virtio_gpu_ctx_resource {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	__le32 resource_id;
> +	__le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_SUBMIT_3D */
> +struct virtio_gpu_cmd_submit {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	__le32 size;
> +	__le32 padding;
> +};
> +
> +#define VIRTIO_GPU_CAPSET_VIRGL 1
> +
> +/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
> +struct virtio_gpu_get_capset_info {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	__le32 capset_index;
> +	__le32 padding;
> +};
> +
> +/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
> +struct virtio_gpu_resp_capset_info {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	__le32 capset_id;
> +	__le32 capset_max_version;
> +	__le32 capset_max_size;
> +	__le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_GET_CAPSET */
> +struct virtio_gpu_get_capset {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	__le32 capset_id;
> +	__le32 capset_version;
> +};
> +
> +/* VIRTIO_GPU_RESP_OK_CAPSET */
> +struct virtio_gpu_resp_capset {
> +	struct virtio_gpu_ctrl_hdr hdr;
> +	uint8_t capset_data[];
> +};
> +
>  #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
>  
>  struct virtio_gpu_config {
>  	__u32 events_read;
>  	__u32 events_clear;
>  	__u32 num_scanouts;
> -	__u32 reserved;
> +	__u32 num_capsets;
>  };
>  
>  /* simple formats for fbcon/X use */
> -- 
> 1.8.3.1
Gerd Hoffmann Sept. 22, 2015, 3:18 p.m. UTC | #2
Hi,

> >  static unsigned int features[] = {
> > +#ifdef __LITTLE_ENDIAN
> > +	VIRTIO_GPU_FEATURE_VIRGL,
> > +#endif
> >  };
> 
> Why is virgl LE specific? Just curious.

gallium command stream is native endian, and we only support little
endian guests on little endian hosts for that reason.

Supporting bigendian guests on bigendian hosts should be possible too
(with a VIRGl_BE feature flag), but with even the powerpc world moving
to little endian I see little reason to actually bother.

To be clear: this affects 3d acceleration only, 2d mode works fine on
every host/guest endian combination.

> > +#ifdef __LITTLE_ENDIAN
> > +	if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_FEATURE_VIRGL))
> > +		vgdev->has_virgl_3d = true;
> > +#endif
> 
> You might be able to get by without this ifdef as
> VIRTIO_GPU_FEATURE_VIRGL won't be set without it.

Yes, right, this #ifdef can go away.

> > +int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx)
> > +{
> > +	struct virtio_gpu_get_capset_info *cmd_p;
> > +	struct virtio_gpu_vbuffer *vbuf;
> > +	void *resp_buf;
> > +
> > +	resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_capset_info),
> > +			   GFP_KERNEL);
> > +	if (!resp_buf)
> > +		return -ENOMEM;
> > +
> > +	cmd_p = virtio_gpu_alloc_cmd_resp
> > +		(vgdev, &virtio_gpu_cmd_get_capset_info_cb, &vbuf,
> > +		 sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_capset_info),
> > +		 resp_buf);
> > +	memset(cmd_p, 0, sizeof(*cmd_p));
> > +
> > +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_CAPSET_INFO);
> > +	cmd_p->capset_index = cpu_to_le32(idx);
> > +	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> > +	return 0;
> > +}
> > +
> 
> I note that all callers ignore the recurn code from
> virtio_gpu_queue_ctrl_buffer.
> 
> Is there a reason it can't fail?

It can only fail when vgdev->vqs_ready is false.  Which can only happen
when we are about to unload the driver.  In that case we probably don't
worry much about the failure ...

> Shouldn't it be made void then?

I guess we should ...

> > +	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_CREATE);
> > +	cmd_p->hdr.ctx_id = cpu_to_le32(id);
> > +	cmd_p->nlen = cpu_to_le32(nlen);
> > +	strncpy(cmd_p->debug_name, name, 63);
> > +	cmd_p->debug_name[63] = 0;
> 
> sizeof (cmd_p->debug_name) - 1 will be a but prettier.

Agree.

> > diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
> > index 478be52..7f4f9ce 100644
> > --- a/include/uapi/linux/virtio_gpu.h
> > +++ b/include/uapi/linux/virtio_gpu.h
> > @@ -40,6 +40,8 @@
> >  
> >  #include <linux/types.h>
> >  
> > +#define VIRTIO_GPU_FEATURE_VIRGL 0
> > +
> 
> I'd prefer it named VIRTIO_GPU_F_VIRGL for consistency with other
> devices.

Makes sense, I'll change it.

thanks,
  Gerd
diff mbox

Patch

diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
index 2ee1602..da7bf19 100644
--- a/drivers/gpu/drm/virtio/Makefile
+++ b/drivers/gpu/drm/virtio/Makefile
@@ -6,6 +6,7 @@  ccflags-y := -Iinclude/drm
 
 virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_drm_bus.o virtgpu_gem.o \
 	virtgpu_fb.o virtgpu_display.o virtgpu_vq.o virtgpu_ttm.o \
-	virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o
+	virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o \
+	virtgpu_ioctl.o
 
 obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 7d9610a..957e455 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -73,6 +73,9 @@  static struct virtio_device_id id_table[] = {
 };
 
 static unsigned int features[] = {
+#ifdef __LITTLE_ENDIAN
+	VIRTIO_GPU_FEATURE_VIRGL,
+#endif
 };
 static struct virtio_driver virtio_gpu_driver = {
 	.feature_table = features,
@@ -114,6 +117,8 @@  static struct drm_driver driver = {
 	.set_busid = drm_virtio_set_busid,
 	.load = virtio_gpu_driver_load,
 	.unload = virtio_gpu_driver_unload,
+	.open = virtio_gpu_driver_open,
+	.postclose = virtio_gpu_driver_postclose,
 
 	.dumb_create = virtio_gpu_mode_dumb_create,
 	.dumb_map_offset = virtio_gpu_mode_dumb_mmap,
@@ -125,8 +130,13 @@  static struct drm_driver driver = {
 #endif
 
 	.gem_free_object = virtio_gpu_gem_free_object,
+	.gem_open_object = virtio_gpu_gem_object_open,
+	.gem_close_object = virtio_gpu_gem_object_close,
 	.fops = &virtio_gpu_driver_fops,
 
+	.ioctls = virtio_gpu_ioctls,
+	.num_ioctls = DRM_VIRTIO_NUM_IOCTLS,
+
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
 	.date = DRIVER_DATE,
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 6d4db2d..2719108 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -146,6 +146,21 @@  struct virtio_gpu_queue {
 	struct work_struct dequeue_work;
 };
 
+struct virtio_gpu_drv_capset {
+	uint32_t id;
+	uint32_t max_version;
+	uint32_t max_size;
+};
+
+struct virtio_gpu_drv_cap_cache {
+	struct list_head head;
+	void *caps_cache;
+	uint32_t id;
+	uint32_t version;
+	uint32_t size;
+	atomic_t is_valid;
+};
+
 struct virtio_gpu_device {
 	struct device *dev;
 	struct drm_device *ddev;
@@ -179,7 +194,13 @@  struct virtio_gpu_device {
 	struct idr	ctx_id_idr;
 	spinlock_t ctx_id_idr_lock;
 
+	bool has_virgl_3d;
+
 	struct work_struct config_changed_work;
+
+	struct virtio_gpu_drv_capset *capsets;
+	uint32_t num_capsets;
+	struct list_head cap_cache;
 };
 
 struct virtio_gpu_fpriv {
@@ -193,6 +214,8 @@  extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
 /* virtio_kms.c */
 int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags);
 int virtio_gpu_driver_unload(struct drm_device *dev);
+int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file);
+void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file);
 
 /* virtio_gem.c */
 void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj);
@@ -203,6 +226,10 @@  int virtio_gpu_gem_create(struct drm_file *file,
 			  uint64_t size,
 			  struct drm_gem_object **obj_p,
 			  uint32_t *handle_p);
+int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
+			       struct drm_file *file);
+void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
+				 struct drm_file *file);
 struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
 						  size_t size, bool kernel,
 						  bool pinned);
@@ -260,10 +287,43 @@  void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
 int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev);
 void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
 					   uint32_t resource_id);
+int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx);
+int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
+			      int idx, int version,
+			      struct virtio_gpu_drv_cap_cache **cache_p);
+void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
+				   uint32_t nlen, const char *name);
+void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev,
+				    uint32_t id);
+void virtio_gpu_cmd_context_attach_resource(struct virtio_gpu_device *vgdev,
+					    uint32_t ctx_id,
+					    uint32_t resource_id);
+void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev,
+					    uint32_t ctx_id,
+					    uint32_t resource_id);
+void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev,
+			   void *data, uint32_t data_size,
+			   uint32_t ctx_id, struct virtio_gpu_fence **fence);
+void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
+					  uint32_t resource_id, uint32_t ctx_id,
+					  uint64_t offset, uint32_t level,
+					  struct virtio_gpu_box *box,
+					  struct virtio_gpu_fence **fence);
+void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
+					uint32_t resource_id, uint32_t ctx_id,
+					uint64_t offset, uint32_t level,
+					struct virtio_gpu_box *box,
+					struct virtio_gpu_fence **fence);
+void
+virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev,
+				  struct virtio_gpu_resource_create_3d *rc_3d,
+				  struct virtio_gpu_fence **fence);
 void virtio_gpu_ctrl_ack(struct virtqueue *vq);
 void virtio_gpu_cursor_ack(struct virtqueue *vq);
+void virtio_gpu_fence_ack(struct virtqueue *vq);
 void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
 void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
+void virtio_gpu_dequeue_fence_func(struct work_struct *work);
 
 /* virtio_gpu_display.c */
 int virtio_gpu_framebuffer_init(struct drm_device *dev,
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index cfa0d27..1feb7ce 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -138,3 +138,44 @@  int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
 	drm_gem_object_unreference_unlocked(gobj);
 	return 0;
 }
+
+int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
+			       struct drm_file *file)
+{
+	struct virtio_gpu_device *vgdev = obj->dev->dev_private;
+	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
+	struct virtio_gpu_object *qobj = gem_to_virtio_gpu_obj(obj);
+	int r;
+
+	if (!vgdev->has_virgl_3d)
+		return 0;
+
+	r = virtio_gpu_object_reserve(qobj, false);
+	if (r)
+		return r;
+
+	virtio_gpu_cmd_context_attach_resource(vgdev, vfpriv->ctx_id,
+					       qobj->hw_res_handle);
+	virtio_gpu_object_unreserve(qobj);
+	return 0;
+}
+
+void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
+				 struct drm_file *file)
+{
+	struct virtio_gpu_device *vgdev = obj->dev->dev_private;
+	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
+	struct virtio_gpu_object *qobj = gem_to_virtio_gpu_obj(obj);
+	int r;
+
+	if (!vgdev->has_virgl_3d)
+		return;
+
+	r = virtio_gpu_object_reserve(qobj, false);
+	if (r)
+		return;
+
+	virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
+						qobj->hw_res_handle);
+	virtio_gpu_object_unreserve(qobj);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
new file mode 100644
index 0000000..d5c9e02
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -0,0 +1,572 @@ 
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * Authors:
+ *    Dave Airlie
+ *    Alon Levy
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ */
+
+#include <drm/drmP.h>
+#include "virtgpu_drv.h"
+#include <drm/virtgpu_drm.h>
+#include "ttm/ttm_execbuf_util.h"
+
+static void convert_to_hw_box(struct virtio_gpu_box *dst,
+			      const struct drm_virtgpu_3d_box *src)
+{
+	dst->x = cpu_to_le32(src->x);
+	dst->y = cpu_to_le32(src->y);
+	dst->z = cpu_to_le32(src->z);
+	dst->w = cpu_to_le32(src->w);
+	dst->h = cpu_to_le32(src->h);
+	dst->d = cpu_to_le32(src->d);
+}
+
+static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct drm_virtgpu_map *virtio_gpu_map = data;
+
+	return virtio_gpu_mode_dumb_mmap(file_priv, vgdev->ddev,
+					 virtio_gpu_map->handle,
+					 &virtio_gpu_map->offset);
+}
+
+static int virtio_gpu_object_list_validate(struct ww_acquire_ctx *ticket,
+					   struct list_head *head)
+{
+	struct ttm_validate_buffer *buf;
+	struct ttm_buffer_object *bo;
+	struct virtio_gpu_object *qobj;
+	int ret;
+
+	ret = ttm_eu_reserve_buffers(ticket, head, true, NULL);
+	if (ret != 0)
+		return ret;
+
+	list_for_each_entry(buf, head, head) {
+		bo = buf->bo;
+		qobj = container_of(bo, struct virtio_gpu_object, tbo);
+		ret = ttm_bo_validate(bo, &qobj->placement, false, false);
+		if (ret) {
+			ttm_eu_backoff_reservation(ticket, head);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static void virtio_gpu_unref_list(struct list_head *head)
+{
+	struct ttm_validate_buffer *buf;
+	struct ttm_buffer_object *bo;
+	struct virtio_gpu_object *qobj;
+	list_for_each_entry(buf, head, head) {
+		bo = buf->bo;
+		qobj = container_of(bo, struct virtio_gpu_object, tbo);
+
+		drm_gem_object_unreference_unlocked(&qobj->gem_base);
+	}
+}
+
+static int virtio_gpu_execbuffer(struct drm_device *dev,
+				 struct drm_virtgpu_execbuffer *exbuf,
+				 struct drm_file *drm_file)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv;
+	struct drm_gem_object *gobj;
+	struct virtio_gpu_fence *fence;
+	struct virtio_gpu_object *qobj;
+	int ret;
+	uint32_t *bo_handles = NULL;
+	void __user *user_bo_handles = NULL;
+	struct list_head validate_list;
+	struct ttm_validate_buffer *buflist = NULL;
+	struct ttm_validate_buffer cmdbuffer;
+	int i;
+	struct ww_acquire_ctx ticket;
+	void *buf;
+
+	if (vgdev->has_virgl_3d == false)
+		return -ENOSYS;
+
+	memset(&cmdbuffer, 0, sizeof(struct ttm_validate_buffer));
+	INIT_LIST_HEAD(&validate_list);
+	if (exbuf->num_bo_handles) {
+
+		bo_handles = drm_malloc_ab(exbuf->num_bo_handles,
+					   sizeof(uint32_t));
+		buflist = drm_calloc_large(exbuf->num_bo_handles,
+					   sizeof(struct ttm_validate_buffer));
+		if (!bo_handles || !buflist) {
+			drm_free_large(bo_handles);
+			drm_free_large(buflist);
+			return -ENOMEM;
+		}
+
+		user_bo_handles = (void __user *)(uintptr_t)exbuf->bo_handles;
+		if (copy_from_user(bo_handles, user_bo_handles,
+				   exbuf->num_bo_handles * sizeof(uint32_t))) {
+			ret = -EFAULT;
+			drm_free_large(bo_handles);
+			return ret;
+		}
+
+		for (i = 0; i < exbuf->num_bo_handles; i++) {
+			gobj = drm_gem_object_lookup(dev,
+						     drm_file, bo_handles[i]);
+			if (!gobj) {
+				drm_free_large(bo_handles);
+				drm_free_large(buflist);
+				return -ENOENT;
+			}
+
+			qobj = gem_to_virtio_gpu_obj(gobj);
+			buflist[i].bo = &qobj->tbo;
+
+			list_add(&buflist[i].head, &validate_list);
+		}
+		drm_free_large(bo_handles);
+	}
+
+	ret = virtio_gpu_object_list_validate(&ticket, &validate_list);
+	if (ret)
+		goto out_free;
+
+	buf = kmalloc(exbuf->size, GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out_unresv;
+	}
+	if (copy_from_user(buf, (void __user *)(uintptr_t)exbuf->command,
+			   exbuf->size)) {
+		kfree(buf);
+		ret = -EFAULT;
+		goto out_unresv;
+	}
+	virtio_gpu_cmd_submit(vgdev, buf, exbuf->size,
+			      vfpriv->ctx_id, &fence);
+
+	ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f);
+
+	/* fence the command bo */
+	virtio_gpu_unref_list(&validate_list);
+	drm_free_large(buflist);
+	fence_put(&fence->f);
+	return 0;
+
+out_unresv:
+	ttm_eu_backoff_reservation(&ticket, &validate_list);
+out_free:
+	virtio_gpu_unref_list(&validate_list);
+	drm_free_large(buflist);
+	return ret;
+}
+
+/*
+ * Usage of execbuffer:
+ * Relocations need to take into account the full VIRTIO_GPUDrawable size.
+ * However, the command as passed from user space must *not* contain the initial
+ * VIRTIO_GPUReleaseInfo struct (first XXX bytes)
+ */
+static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
+				       struct drm_file *file_priv)
+{
+	struct drm_virtgpu_execbuffer *execbuffer = data;
+	return virtio_gpu_execbuffer(dev, execbuffer, file_priv);
+}
+
+
+static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
+				     struct drm_file *file_priv)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct drm_virtgpu_getparam *param = data;
+	int value;
+
+	switch (param->param) {
+	case VIRTGPU_PARAM_3D_FEATURES:
+		value = vgdev->has_virgl_3d == true ? 1 : 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (copy_to_user((void __user *)(unsigned long)param->value,
+			 &value, sizeof(int))) {
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
+					    struct drm_file *file_priv)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct drm_virtgpu_resource_create *rc = data;
+	int ret;
+	uint32_t res_id;
+	struct virtio_gpu_object *qobj;
+	struct drm_gem_object *obj;
+	uint32_t handle = 0;
+	uint32_t size;
+	struct list_head validate_list;
+	struct ttm_validate_buffer mainbuf;
+	struct virtio_gpu_fence *fence = NULL;
+	struct ww_acquire_ctx ticket;
+	struct virtio_gpu_resource_create_3d rc_3d;
+
+	if (vgdev->has_virgl_3d == false) {
+		if (rc->depth > 1)
+			return -EINVAL;
+		if (rc->nr_samples > 1)
+			return -EINVAL;
+		if (rc->last_level > 1)
+			return -EINVAL;
+		if (rc->target != 2)
+			return -EINVAL;
+		if (rc->array_size > 1)
+			return -EINVAL;
+	}
+
+	INIT_LIST_HEAD(&validate_list);
+	memset(&mainbuf, 0, sizeof(struct ttm_validate_buffer));
+
+	virtio_gpu_resource_id_get(vgdev, &res_id);
+
+	size = rc->size;
+
+	/* allocate a single page size object */
+	if (size == 0)
+		size = PAGE_SIZE;
+
+	qobj = virtio_gpu_alloc_object(dev, size, false, false);
+	if (IS_ERR(qobj)) {
+		ret = PTR_ERR(qobj);
+		goto fail_id;
+	}
+	obj = &qobj->gem_base;
+
+	if (!vgdev->has_virgl_3d) {
+		virtio_gpu_cmd_create_resource(vgdev, res_id, rc->format,
+					       rc->width, rc->height);
+
+		ret = virtio_gpu_object_attach(vgdev, qobj, res_id, NULL);
+	} else {
+		/* use a gem reference since unref list undoes them */
+		drm_gem_object_reference(&qobj->gem_base);
+		mainbuf.bo = &qobj->tbo;
+		list_add(&mainbuf.head, &validate_list);
+
+		ret = virtio_gpu_object_list_validate(&ticket, &validate_list);
+		if (ret) {
+			DRM_DEBUG("failed to validate\n");
+			goto fail_unref;
+		}
+
+		rc_3d.resource_id = cpu_to_le32(res_id);
+		rc_3d.target = cpu_to_le32(rc->target);
+		rc_3d.format = cpu_to_le32(rc->format);
+		rc_3d.bind = cpu_to_le32(rc->bind);
+		rc_3d.width = cpu_to_le32(rc->width);
+		rc_3d.height = cpu_to_le32(rc->height);
+		rc_3d.depth = cpu_to_le32(rc->depth);
+		rc_3d.array_size = cpu_to_le32(rc->array_size);
+		rc_3d.last_level = cpu_to_le32(rc->last_level);
+		rc_3d.nr_samples = cpu_to_le32(rc->nr_samples);
+		rc_3d.flags = cpu_to_le32(rc->flags);
+
+		virtio_gpu_cmd_resource_create_3d(vgdev, &rc_3d, NULL);
+		ret = virtio_gpu_object_attach(vgdev, qobj, res_id, &fence);
+		if (ret)
+			goto fail_unref;
+		ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f);
+	}
+
+	qobj->hw_res_handle = res_id;
+
+	ret = drm_gem_handle_create(file_priv, obj, &handle);
+	if (ret) {
+
+		drm_gem_object_release(obj);
+		if (vgdev->has_virgl_3d) {
+			virtio_gpu_unref_list(&validate_list);
+			fence_put(&fence->f);
+		}
+		return ret;
+	}
+	drm_gem_object_unreference_unlocked(obj);
+
+	rc->res_handle = res_id; /* similiar to a VM address */
+	rc->bo_handle = handle;
+
+	if (vgdev->has_virgl_3d) {
+		virtio_gpu_unref_list(&validate_list);
+		fence_put(&fence->f);
+	}
+	return 0;
+fail_unref:
+	if (vgdev->has_virgl_3d) {
+		virtio_gpu_unref_list(&validate_list);
+		fence_put(&fence->f);
+	}
+//fail_obj:
+//	drm_gem_object_handle_unreference_unlocked(obj);
+fail_id:
+	virtio_gpu_resource_id_put(vgdev, res_id);
+	return ret;
+}
+
+static int virtio_gpu_resource_info_ioctl(struct drm_device *dev, void *data,
+					  struct drm_file *file_priv)
+{
+	struct drm_virtgpu_resource_info *ri = data;
+	struct drm_gem_object *gobj = NULL;
+	struct virtio_gpu_object *qobj = NULL;
+
+	gobj = drm_gem_object_lookup(dev, file_priv, ri->bo_handle);
+	if (gobj == NULL)
+		return -ENOENT;
+
+	qobj = gem_to_virtio_gpu_obj(gobj);
+
+	ri->size = qobj->gem_base.size;
+	ri->res_handle = qobj->hw_res_handle;
+	drm_gem_object_unreference_unlocked(gobj);
+	return 0;
+}
+
+static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev,
+					       void *data,
+					       struct drm_file *file)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
+	struct drm_virtgpu_3d_transfer_from_host *args = data;
+	struct drm_gem_object *gobj = NULL;
+	struct virtio_gpu_object *qobj = NULL;
+	struct virtio_gpu_fence *fence;
+	int ret;
+	u32 offset = args->offset;
+	struct virtio_gpu_box box;
+
+	if (vgdev->has_virgl_3d == false)
+		return -ENOSYS;
+
+	gobj = drm_gem_object_lookup(dev, file, args->bo_handle);
+	if (gobj == NULL)
+		return -ENOENT;
+
+	qobj = gem_to_virtio_gpu_obj(gobj);
+
+	ret = virtio_gpu_object_reserve(qobj, false);
+	if (ret)
+		goto out;
+
+	ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
+			      true, false);
+	if (unlikely(ret))
+		goto out_unres;
+
+	convert_to_hw_box(&box, &args->box);
+	virtio_gpu_cmd_transfer_from_host_3d
+		(vgdev, qobj->hw_res_handle,
+		 vfpriv->ctx_id, offset, args->level,
+		 &box, &fence);
+	reservation_object_add_excl_fence(qobj->tbo.resv,
+					  &fence->f);
+
+	fence_put(&fence->f);
+out_unres:
+	virtio_gpu_object_unreserve(qobj);
+out:
+	drm_gem_object_unreference_unlocked(gobj);
+	return ret;
+}
+
+static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
+					     struct drm_file *file)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
+	struct drm_virtgpu_3d_transfer_to_host *args = data;
+	struct drm_gem_object *gobj = NULL;
+	struct virtio_gpu_object *qobj = NULL;
+	struct virtio_gpu_fence *fence;
+	struct virtio_gpu_box box;
+	int ret;
+	u32 offset = args->offset;
+
+	gobj = drm_gem_object_lookup(dev, file, args->bo_handle);
+	if (gobj == NULL)
+		return -ENOENT;
+
+	qobj = gem_to_virtio_gpu_obj(gobj);
+
+	ret = virtio_gpu_object_reserve(qobj, false);
+	if (ret)
+		goto out;
+
+	ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
+			      true, false);
+	if (unlikely(ret))
+		goto out_unres;
+
+	convert_to_hw_box(&box, &args->box);
+	if (!vgdev->has_virgl_3d) {
+		virtio_gpu_cmd_transfer_to_host_2d
+			(vgdev, qobj->hw_res_handle, offset,
+			 box.w, box.h, box.x, box.y, NULL);
+	} else {
+		virtio_gpu_cmd_transfer_to_host_3d
+			(vgdev, qobj->hw_res_handle,
+			 vfpriv ? vfpriv->ctx_id : 0, offset,
+			 args->level, &box, &fence);
+		reservation_object_add_excl_fence(qobj->tbo.resv,
+						  &fence->f);
+		fence_put(&fence->f);
+	}
+
+out_unres:
+	virtio_gpu_object_unreserve(qobj);
+out:
+	drm_gem_object_unreference_unlocked(gobj);
+	return ret;
+}
+
+static int virtio_gpu_wait_ioctl(struct drm_device *dev, void *data,
+			    struct drm_file *file)
+{
+	struct drm_virtgpu_3d_wait *args = data;
+	struct drm_gem_object *gobj = NULL;
+	struct virtio_gpu_object *qobj = NULL;
+	int ret;
+	bool nowait = false;
+
+	gobj = drm_gem_object_lookup(dev, file, args->handle);
+	if (gobj == NULL)
+		return -ENOENT;
+
+	qobj = gem_to_virtio_gpu_obj(gobj);
+
+	if (args->flags & VIRTGPU_WAIT_NOWAIT)
+		nowait = true;
+	ret = virtio_gpu_object_wait(qobj, nowait);
+
+	drm_gem_object_unreference_unlocked(gobj);
+	return ret;
+}
+
+static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
+				void *data, struct drm_file *file)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct drm_virtgpu_get_caps *args = data;
+	int size;
+	int i;
+	int found_valid = -1;
+	int ret;
+	struct virtio_gpu_drv_cap_cache *cache_ent;
+	void *ptr;
+	if (vgdev->num_capsets == 0)
+		return -ENOSYS;
+
+	spin_lock(&vgdev->display_info_lock);
+	for (i = 0; i < vgdev->num_capsets; i++) {
+		if (vgdev->capsets[i].id == args->cap_set_id) {
+			if (vgdev->capsets[i].max_version >= args->cap_set_ver) {
+				found_valid = i;
+				break;
+			}
+		}
+	}
+
+	if (found_valid == -1) {
+		spin_unlock(&vgdev->display_info_lock);
+		return -EINVAL;
+	}
+
+	size = vgdev->capsets[found_valid].max_size;
+	if (args->size > size) {
+		spin_unlock(&vgdev->display_info_lock);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
+		if (cache_ent->id == args->cap_set_id &&
+		    cache_ent->version == args->cap_set_ver) {
+			ptr = cache_ent->caps_cache;
+			spin_unlock(&vgdev->display_info_lock);
+			goto copy_exit;
+		}
+	}
+	spin_unlock(&vgdev->display_info_lock);
+
+	/* not in cache - need to talk to hw */
+	virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver,
+				  &cache_ent);
+
+	ret = wait_event_timeout(vgdev->resp_wq,
+				 atomic_read(&cache_ent->is_valid), 5 * HZ);
+
+	ptr = cache_ent->caps_cache;
+
+copy_exit:
+	if (copy_to_user((void __user *)(unsigned long)args->addr, ptr, size))
+		return -EFAULT;
+
+	return 0;
+}
+
+struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
+	DRM_IOCTL_DEF_DRV(VIRTGPU_MAP, virtio_gpu_map_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+
+	DRM_IOCTL_DEF_DRV(VIRTGPU_EXECBUFFER, virtio_gpu_execbuffer_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+
+	DRM_IOCTL_DEF_DRV(VIRTGPU_GETPARAM, virtio_gpu_getparam_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+
+	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE,
+			  virtio_gpu_resource_create_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+
+	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_INFO, virtio_gpu_resource_info_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+
+	/* make transfer async to the main ring? - no sure, can we
+	   thread these in the underlying GL */
+	DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_FROM_HOST,
+			  virtio_gpu_transfer_from_host_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_TO_HOST,
+			  virtio_gpu_transfer_to_host_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+
+	DRM_IOCTL_DEF_DRV(VIRTGPU_WAIT, virtio_gpu_wait_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+
+	DRM_IOCTL_DEF_DRV(VIRTGPU_GET_CAPS, virtio_gpu_get_caps_ioctl,
+			  DRM_AUTH|DRM_UNLOCKED),
+};
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 782766c..12a1091 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -52,6 +52,41 @@  static void virtio_gpu_config_changed_work_func(struct work_struct *work)
 		      events_clear, &events_clear);
 }
 
+static void virtio_gpu_ctx_id_get(struct virtio_gpu_device *vgdev,
+				  uint32_t *resid)
+{
+	int handle;
+
+	idr_preload(GFP_KERNEL);
+	spin_lock(&vgdev->ctx_id_idr_lock);
+	handle = idr_alloc(&vgdev->ctx_id_idr, NULL, 1, 0, 0);
+	spin_unlock(&vgdev->ctx_id_idr_lock);
+	idr_preload_end();
+	*resid = handle;
+}
+
+static void virtio_gpu_ctx_id_put(struct virtio_gpu_device *vgdev, uint32_t id)
+{
+	spin_lock(&vgdev->ctx_id_idr_lock);
+	idr_remove(&vgdev->ctx_id_idr, id);
+	spin_unlock(&vgdev->ctx_id_idr_lock);
+}
+
+static void virtio_gpu_context_create(struct virtio_gpu_device *vgdev,
+				      uint32_t nlen, const char *name,
+				      uint32_t *ctx_id)
+{
+	virtio_gpu_ctx_id_get(vgdev, ctx_id);
+	virtio_gpu_cmd_context_create(vgdev, *ctx_id, nlen, name);
+}
+
+static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev,
+				      uint32_t ctx_id)
+{
+	virtio_gpu_cmd_context_destroy(vgdev, ctx_id);
+	virtio_gpu_ctx_id_put(vgdev, ctx_id);
+}
+
 static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
 			       void (*work_func)(struct work_struct *work))
 {
@@ -60,6 +95,36 @@  static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
 	INIT_WORK(&vgvq->dequeue_work, work_func);
 }
 
+static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev,
+				   int num_capsets)
+{
+	int i, ret;
+
+	vgdev->capsets = kcalloc(num_capsets,
+				 sizeof(struct virtio_gpu_drv_capset),
+				 GFP_KERNEL);
+	if (!vgdev->capsets) {
+		DRM_ERROR("failed to allocate cap sets\n");
+		return;
+	}
+	for (i = 0; i < num_capsets; i++) {
+		virtio_gpu_cmd_get_capset_info(vgdev, i);
+		ret = wait_event_timeout(vgdev->resp_wq,
+					 vgdev->capsets[i].id > 0, 5 * HZ);
+		if (ret == 0) {
+			DRM_ERROR("timed out waiting for cap set %d\n", i);
+			kfree(vgdev->capsets);
+			vgdev->capsets = NULL;
+			return;
+		}
+		DRM_INFO("cap set %d: id %d, max-version %d, max-size %d\n",
+			 i, vgdev->capsets[i].id,
+			 vgdev->capsets[i].max_version,
+			 vgdev->capsets[i].max_size);
+	}
+	vgdev->num_capsets = num_capsets;
+}
+
 int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 {
 	static vq_callback_t *callbacks[] = {
@@ -70,7 +135,7 @@  int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 	struct virtio_gpu_device *vgdev;
 	/* this will expand later */
 	struct virtqueue *vqs[2];
-	u32 num_scanouts;
+	u32 num_scanouts, num_capsets;
 	int ret;
 
 	if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
@@ -96,9 +161,17 @@  int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 
 	spin_lock_init(&vgdev->fence_drv.lock);
 	INIT_LIST_HEAD(&vgdev->fence_drv.fences);
+	INIT_LIST_HEAD(&vgdev->cap_cache);
 	INIT_WORK(&vgdev->config_changed_work,
 		  virtio_gpu_config_changed_work_func);
 
+#ifdef __LITTLE_ENDIAN
+	if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_FEATURE_VIRGL))
+		vgdev->has_virgl_3d = true;
+#endif
+	DRM_INFO("virgl 3d acceleration %s\n",
+		 vgdev->has_virgl_3d ? "enabled" : "not available");
+
 	ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs,
 					    callbacks, names);
 	if (ret) {
@@ -129,6 +202,11 @@  int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 		ret = -EINVAL;
 		goto err_scanouts;
 	}
+	DRM_INFO("number of scanouts: %d\n", num_scanouts);
+
+	virtio_cread(vgdev->vdev, struct virtio_gpu_config,
+		     num_capsets, &num_capsets);
+	DRM_INFO("number of cap sets: %d\n", num_capsets);
 
 	ret = virtio_gpu_modeset_init(vgdev);
 	if (ret)
@@ -137,6 +215,8 @@  int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 	virtio_device_ready(vgdev->vdev);
 	vgdev->vqs_ready = true;
 
+	if (num_capsets)
+		virtio_gpu_get_capsets(vgdev, num_capsets);
 	virtio_gpu_cmd_get_display_info(vgdev);
 	wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending,
 			   5 * HZ);
@@ -157,6 +237,16 @@  err_vqs:
 	return ret;
 }
 
+static void virtio_gpu_cleanup_cap_cache(struct virtio_gpu_device *vgdev)
+{
+	struct virtio_gpu_drv_cap_cache *cache_ent, *tmp;
+
+	list_for_each_entry_safe(cache_ent, tmp, &vgdev->cap_cache, head) {
+		kfree(cache_ent->caps_cache);
+		kfree(cache_ent);
+	}
+}
+
 int virtio_gpu_driver_unload(struct drm_device *dev)
 {
 	struct virtio_gpu_device *vgdev = dev->dev_private;
@@ -170,6 +260,49 @@  int virtio_gpu_driver_unload(struct drm_device *dev)
 	virtio_gpu_modeset_fini(vgdev);
 	virtio_gpu_ttm_fini(vgdev);
 	virtio_gpu_free_vbufs(vgdev);
+	virtio_gpu_cleanup_cap_cache(vgdev);
+	kfree(vgdev->capsets);
 	kfree(vgdev);
 	return 0;
 }
+
+int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct virtio_gpu_fpriv *vfpriv;
+	uint32_t id;
+	char dbgname[64], tmpname[TASK_COMM_LEN];
+
+	/* can't create contexts without 3d renderer */
+	if (!vgdev->has_virgl_3d)
+		return 0;
+
+	get_task_comm(tmpname, current);
+	snprintf(dbgname, sizeof(dbgname), "%s", tmpname);
+	dbgname[63] = 0;
+	/* allocate a virt GPU context for this opener */
+	vfpriv = kzalloc(sizeof(*vfpriv), GFP_KERNEL);
+	if (!vfpriv)
+		return -ENOMEM;
+
+	virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname, &id);
+
+	vfpriv->ctx_id = id;
+	file->driver_priv = vfpriv;
+	return 0;
+}
+
+void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct virtio_gpu_fpriv *vfpriv;
+
+	if (!vgdev->has_virgl_3d)
+		return;
+
+	vfpriv = file->driver_priv;
+
+	virtio_gpu_context_destroy(vgdev, vfpriv->ctx_id);
+	kfree(vfpriv);
+	file->driver_priv = NULL;
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c
index b092d7b..9fd924c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ttm.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c
@@ -32,6 +32,7 @@ 
 #include <ttm/ttm_module.h>
 #include <drm/drmP.h>
 #include <drm/drm.h>
+#include <drm/virtgpu_drm.h>
 #include "virtgpu_drv.h"
 
 #include <linux/delay.h>
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index ee25e9a..ac4f642 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -586,6 +586,47 @@  static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
 		drm_kms_helper_hotplug_event(vgdev->ddev);
 }
 
+static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
+					      struct virtio_gpu_vbuffer *vbuf)
+{
+	struct virtio_gpu_get_capset_info *cmd =
+		(struct virtio_gpu_get_capset_info *)vbuf->buf;
+	struct virtio_gpu_resp_capset_info *resp =
+		(struct virtio_gpu_resp_capset_info *)vbuf->resp_buf;
+	int i = le32_to_cpu(cmd->capset_index);
+
+	spin_lock(&vgdev->display_info_lock);
+	vgdev->capsets[i].id = le32_to_cpu(resp->capset_id);
+	vgdev->capsets[i].max_version = le32_to_cpu(resp->capset_max_version);
+	vgdev->capsets[i].max_size = le32_to_cpu(resp->capset_max_size);
+	spin_unlock(&vgdev->display_info_lock);
+	wake_up(&vgdev->resp_wq);
+}
+
+static void virtio_gpu_cmd_capset_cb(struct virtio_gpu_device *vgdev,
+				     struct virtio_gpu_vbuffer *vbuf)
+{
+	struct virtio_gpu_get_capset *cmd =
+		(struct virtio_gpu_get_capset *)vbuf->buf;
+	struct virtio_gpu_resp_capset *resp =
+		(struct virtio_gpu_resp_capset *)vbuf->resp_buf;
+	struct virtio_gpu_drv_cap_cache *cache_ent;
+
+	spin_lock(&vgdev->display_info_lock);
+	list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
+		if (cache_ent->version == le32_to_cpu(cmd->capset_version) &&
+		    cache_ent->id == le32_to_cpu(cmd->capset_id)) {
+			memcpy(cache_ent->caps_cache, resp->capset_data,
+			       cache_ent->size);
+			atomic_set(&cache_ent->is_valid, 1);
+			break;
+		}
+	}
+	spin_unlock(&vgdev->display_info_lock);
+	wake_up(&vgdev->resp_wq);
+}
+
+
 int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
 {
 	struct virtio_gpu_ctrl_hdr *cmd_p;
@@ -609,6 +650,230 @@  int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
 	return 0;
 }
 
+int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx)
+{
+	struct virtio_gpu_get_capset_info *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+	void *resp_buf;
+
+	resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_capset_info),
+			   GFP_KERNEL);
+	if (!resp_buf)
+		return -ENOMEM;
+
+	cmd_p = virtio_gpu_alloc_cmd_resp
+		(vgdev, &virtio_gpu_cmd_get_capset_info_cb, &vbuf,
+		 sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_capset_info),
+		 resp_buf);
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_CAPSET_INFO);
+	cmd_p->capset_index = cpu_to_le32(idx);
+	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+	return 0;
+}
+
+int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
+			      int idx, int version,
+			      struct virtio_gpu_drv_cap_cache **cache_p)
+{
+	struct virtio_gpu_get_capset *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+	int max_size = vgdev->capsets[idx].max_size;
+	struct virtio_gpu_drv_cap_cache *cache_ent;
+	void *resp_buf;
+
+	if (idx > vgdev->num_capsets)
+		return -EINVAL;
+
+	if (version > vgdev->capsets[idx].max_version)
+		return -EINVAL;
+
+	cache_ent = kzalloc(sizeof(*cache_ent), GFP_KERNEL);
+	if (!cache_ent)
+		return -ENOMEM;
+
+	cache_ent->caps_cache = kmalloc(max_size, GFP_KERNEL);
+	if (!cache_ent->caps_cache) {
+		kfree(cache_ent);
+		return -ENOMEM;
+	}
+
+	resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_capset) + max_size,
+			   GFP_KERNEL);
+	if (!resp_buf) {
+		kfree(cache_ent->caps_cache);
+		kfree(cache_ent);
+		return -ENOMEM;
+	}
+
+	cache_ent->version = version;
+	cache_ent->id = vgdev->capsets[idx].id;
+	atomic_set(&cache_ent->is_valid, 0);
+	cache_ent->size = max_size;
+	spin_lock(&vgdev->display_info_lock);
+	list_add_tail(&cache_ent->head, &vgdev->cap_cache);
+	spin_unlock(&vgdev->display_info_lock);
+
+	cmd_p = virtio_gpu_alloc_cmd_resp
+		(vgdev, &virtio_gpu_cmd_capset_cb, &vbuf, sizeof(*cmd_p),
+		 sizeof(struct virtio_gpu_resp_capset) + max_size,
+		 resp_buf);
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_CAPSET);
+	cmd_p->capset_id = cpu_to_le32(vgdev->capsets[idx].id);
+	cmd_p->capset_version = cpu_to_le32(version);
+	*cache_p = cache_ent;
+	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+
+	return 0;
+}
+
+void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
+				   uint32_t nlen, const char *name)
+{
+	struct virtio_gpu_ctx_create *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_CREATE);
+	cmd_p->hdr.ctx_id = cpu_to_le32(id);
+	cmd_p->nlen = cpu_to_le32(nlen);
+	strncpy(cmd_p->debug_name, name, 63);
+	cmd_p->debug_name[63] = 0;
+	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+}
+
+void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev,
+				    uint32_t id)
+{
+	struct virtio_gpu_ctx_destroy *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_DESTROY);
+	cmd_p->hdr.ctx_id = cpu_to_le32(id);
+	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+}
+
+void virtio_gpu_cmd_context_attach_resource(struct virtio_gpu_device *vgdev,
+					    uint32_t ctx_id,
+					    uint32_t resource_id)
+{
+	struct virtio_gpu_ctx_resource *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE);
+	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
+	cmd_p->resource_id = cpu_to_le32(resource_id);
+	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+
+}
+
+void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev,
+					    uint32_t ctx_id,
+					    uint32_t resource_id)
+{
+	struct virtio_gpu_ctx_resource *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE);
+	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
+	cmd_p->resource_id = cpu_to_le32(resource_id);
+	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+}
+
+void
+virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev,
+				  struct virtio_gpu_resource_create_3d *rc_3d,
+				  struct virtio_gpu_fence **fence)
+{
+	struct virtio_gpu_resource_create_3d *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	*cmd_p = *rc_3d;
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D);
+	cmd_p->hdr.flags = 0;
+
+	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
+}
+
+void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
+					uint32_t resource_id, uint32_t ctx_id,
+					uint64_t offset, uint32_t level,
+					struct virtio_gpu_box *box,
+					struct virtio_gpu_fence **fence)
+{
+	struct virtio_gpu_transfer_host_3d *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D);
+	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
+	cmd_p->resource_id = cpu_to_le32(resource_id);
+	cmd_p->box = *box;
+	cmd_p->offset = cpu_to_le64(offset);
+	cmd_p->level = cpu_to_le32(level);
+
+	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
+}
+
+void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
+					  uint32_t resource_id, uint32_t ctx_id,
+					  uint64_t offset, uint32_t level,
+					  struct virtio_gpu_box *box,
+					  struct virtio_gpu_fence **fence)
+{
+	struct virtio_gpu_transfer_host_3d *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D);
+	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
+	cmd_p->resource_id = cpu_to_le32(resource_id);
+	cmd_p->box = *box;
+	cmd_p->offset = cpu_to_le64(offset);
+	cmd_p->level = cpu_to_le32(level);
+
+	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
+}
+
+void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev,
+			   void *data, uint32_t data_size,
+			   uint32_t ctx_id, struct virtio_gpu_fence **fence)
+{
+	struct virtio_gpu_cmd_submit *cmd_p;
+	struct virtio_gpu_vbuffer *vbuf;
+
+	cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+	memset(cmd_p, 0, sizeof(*cmd_p));
+
+	vbuf->data_buf = data;
+	vbuf->data_size = data_size;
+
+	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_SUBMIT_3D);
+	cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
+	cmd_p->size = cpu_to_le32(data_size);
+
+	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
+}
+
 int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
 			     struct virtio_gpu_object *obj,
 			     uint32_t resource_id,
diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild
index 2d9a25d..38d4370 100644
--- a/include/uapi/drm/Kbuild
+++ b/include/uapi/drm/Kbuild
@@ -17,3 +17,4 @@  header-y += tegra_drm.h
 header-y += via_drm.h
 header-y += vmwgfx_drm.h
 header-y += msm_drm.h
+header-y += virtgpu_drm.h
diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
new file mode 100644
index 0000000..fc9e2d6
--- /dev/null
+++ b/include/uapi/drm/virtgpu_drm.h
@@ -0,0 +1,167 @@ 
+/*
+ * Copyright 2013 Red Hat
+ * All Rights Reserved.
+ *
+ * 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 AND/OR ITS SUPPLIERS 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 VIRTGPU_DRM_H
+#define VIRTGPU_DRM_H
+
+#include <stddef.h>
+#include "drm/drm.h"
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints.
+ *
+ * Do not use pointers, use uint64_t instead for 32 bit / 64 bit user/kernel
+ * compatibility Keep fields aligned to their size
+ */
+
+#define DRM_VIRTGPU_MAP         0x01
+#define DRM_VIRTGPU_EXECBUFFER  0x02
+#define DRM_VIRTGPU_GETPARAM    0x03
+#define DRM_VIRTGPU_RESOURCE_CREATE 0x04
+#define DRM_VIRTGPU_RESOURCE_INFO     0x05
+#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x06
+#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07
+#define DRM_VIRTGPU_WAIT     0x08
+#define DRM_VIRTGPU_GET_CAPS  0x09
+
+struct drm_virtgpu_map {
+	uint64_t offset; /* use for mmap system call */
+	uint32_t handle;
+	uint32_t pad;
+};
+
+struct drm_virtgpu_execbuffer {
+	uint32_t		flags;		/* for future use */
+	uint32_t size;
+	uint64_t command; /* void* */
+	uint64_t bo_handles;
+	uint32_t num_bo_handles;
+	uint32_t pad;
+};
+
+#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
+
+struct drm_virtgpu_getparam {
+	uint64_t param;
+	uint64_t value;
+};
+
+/* NO_BO flags? NO resource flag? */
+/* resource flag for y_0_top */
+struct drm_virtgpu_resource_create {
+	uint32_t target;
+	uint32_t format;
+	uint32_t bind;
+	uint32_t width;
+	uint32_t height;
+	uint32_t depth;
+	uint32_t array_size;
+	uint32_t last_level;
+	uint32_t nr_samples;
+	uint32_t flags;
+	uint32_t bo_handle; /* if this is set - recreate a new resource attached to this bo ? */
+	uint32_t res_handle;  /* returned by kernel */
+	uint32_t size;        /* validate transfer in the host */
+	uint32_t stride;      /* validate transfer in the host */
+};
+
+struct drm_virtgpu_resource_info {
+	uint32_t bo_handle;
+	uint32_t res_handle;
+	uint32_t size;
+	uint32_t stride;
+};
+
+struct drm_virtgpu_3d_box {
+	uint32_t x;
+	uint32_t y;
+	uint32_t z;
+	uint32_t w;
+	uint32_t h;
+	uint32_t d;
+};
+
+struct drm_virtgpu_3d_transfer_to_host {
+	uint32_t bo_handle;
+	struct drm_virtgpu_3d_box box;
+	uint32_t level;
+	uint32_t offset;
+};
+
+struct drm_virtgpu_3d_transfer_from_host {
+	uint32_t bo_handle;
+	struct drm_virtgpu_3d_box box;
+	uint32_t level;
+	uint32_t offset;
+};
+
+#define VIRTGPU_WAIT_NOWAIT 1 /* like it */
+struct drm_virtgpu_3d_wait {
+	uint32_t handle; /* 0 is an invalid handle */
+	uint32_t flags;
+};
+
+struct drm_virtgpu_get_caps {
+	uint32_t cap_set_id;
+	uint32_t cap_set_ver;
+	uint64_t addr;
+	uint32_t size;
+	uint32_t pad;
+};
+
+#define DRM_IOCTL_VIRTGPU_MAP \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map)
+
+#define DRM_IOCTL_VIRTGPU_EXECBUFFER \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_VIRTGPU_EXECBUFFER,\
+		struct drm_virtgpu_execbuffer)
+
+#define DRM_IOCTL_VIRTGPU_GETPARAM \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GETPARAM,\
+		struct drm_virtgpu_getparam)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE			\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE,	\
+		struct drm_virtgpu_resource_create)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_INFO \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_INFO, \
+		 struct drm_virtgpu_resource_info)
+
+#define DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_FROM_HOST,	\
+		struct drm_virtgpu_3d_transfer_from_host)
+
+#define DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_TO_HOST,	\
+		struct drm_virtgpu_3d_transfer_to_host)
+
+#define DRM_IOCTL_VIRTGPU_WAIT				\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT,	\
+		struct drm_virtgpu_3d_wait)
+
+#define DRM_IOCTL_VIRTGPU_GET_CAPS \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \
+	struct drm_virtgpu_get_caps)
+
+#endif
diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
index 478be52..7f4f9ce 100644
--- a/include/uapi/linux/virtio_gpu.h
+++ b/include/uapi/linux/virtio_gpu.h
@@ -40,6 +40,8 @@ 
 
 #include <linux/types.h>
 
+#define VIRTIO_GPU_FEATURE_VIRGL 0
+
 enum virtio_gpu_ctrl_type {
 	VIRTIO_GPU_UNDEFINED = 0,
 
@@ -52,6 +54,18 @@  enum virtio_gpu_ctrl_type {
 	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
 	VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
 	VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+	VIRTIO_GPU_CMD_GET_CAPSET_INFO,
+	VIRTIO_GPU_CMD_GET_CAPSET,
+
+	/* 3d commands */
+	VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
+	VIRTIO_GPU_CMD_CTX_DESTROY,
+	VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
+	VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
+	VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
+	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
+	VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
+	VIRTIO_GPU_CMD_SUBMIT_3D,
 
 	/* cursor commands */
 	VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
@@ -60,6 +74,8 @@  enum virtio_gpu_ctrl_type {
 	/* success responses */
 	VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
 	VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+	VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+	VIRTIO_GPU_RESP_OK_CAPSET,
 
 	/* error responses */
 	VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
@@ -180,13 +196,107 @@  struct virtio_gpu_resp_display_info {
 	} pmodes[VIRTIO_GPU_MAX_SCANOUTS];
 };
 
+/* data passed in the control vq, 3d related */
+
+struct virtio_gpu_box {
+	__le32 x, y, z;
+	__le32 w, h, d;
+};
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
+struct virtio_gpu_transfer_host_3d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_box box;
+	__le64 offset;
+	__le32 resource_id;
+	__le32 level;
+	__le32 stride;
+	__le32 layer_stride;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
+#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0)
+struct virtio_gpu_resource_create_3d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 target;
+	__le32 format;
+	__le32 bind;
+	__le32 width;
+	__le32 height;
+	__le32 depth;
+	__le32 array_size;
+	__le32 last_level;
+	__le32 nr_samples;
+	__le32 flags;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_CTX_CREATE */
+struct virtio_gpu_ctx_create {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 nlen;
+	__le32 padding;
+	char debug_name[64];
+};
+
+/* VIRTIO_GPU_CMD_CTX_DESTROY */
+struct virtio_gpu_ctx_destroy {
+	struct virtio_gpu_ctrl_hdr hdr;
+};
+
+/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
+struct virtio_gpu_ctx_resource {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_SUBMIT_3D */
+struct virtio_gpu_cmd_submit {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 size;
+	__le32 padding;
+};
+
+#define VIRTIO_GPU_CAPSET_VIRGL 1
+
+/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
+struct virtio_gpu_get_capset_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_index;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
+struct virtio_gpu_resp_capset_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_id;
+	__le32 capset_max_version;
+	__le32 capset_max_size;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_GET_CAPSET */
+struct virtio_gpu_get_capset {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_id;
+	__le32 capset_version;
+};
+
+/* VIRTIO_GPU_RESP_OK_CAPSET */
+struct virtio_gpu_resp_capset {
+	struct virtio_gpu_ctrl_hdr hdr;
+	uint8_t capset_data[];
+};
+
 #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
 
 struct virtio_gpu_config {
 	__u32 events_read;
 	__u32 events_clear;
 	__u32 num_scanouts;
-	__u32 reserved;
+	__u32 num_capsets;
 };
 
 /* simple formats for fbcon/X use */