Linux virtualization list
 help / color / mirror / Atom feed
* Re: [PATCH v2 5/6] virtio-gpu: add basic prime support
From: Daniel Vetter @ 2015-09-22  9:40 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Dave Airlie, open list:VIRTIO GPU DRIVER, open list,
	open list:VIRTIO GPU DRIVER, dri-devel
In-Reply-To: <1442828417-6165-6-git-send-email-kraxel@redhat.com>

On Mon, Sep 21, 2015 at 11:40:16AM +0200, Gerd Hoffmann wrote:
> From: Dave Airlie <airlied@redhat.com>
> 
> This is enough to enable DRI3.
> 
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  drivers/gpu/drm/virtio/Makefile        |  2 +-
>  drivers/gpu/drm/virtio/virtgpu_drv.c   | 13 +++++-
>  drivers/gpu/drm/virtio/virtgpu_drv.h   | 12 ++++++
>  drivers/gpu/drm/virtio/virtgpu_prime.c | 72 ++++++++++++++++++++++++++++++++++
>  4 files changed, 97 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/virtio/virtgpu_prime.c
> 
> diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
> index da7bf19..3fb8eac 100644
> --- a/drivers/gpu/drm/virtio/Makefile
> +++ b/drivers/gpu/drm/virtio/Makefile
> @@ -7,6 +7,6 @@ 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_ioctl.o
> +	virtgpu_ioctl.o virtgpu_prime.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 957e455..1245d09 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_drv.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
> @@ -113,7 +113,7 @@ static const struct file_operations virtio_gpu_driver_fops = {
>  
>  
>  static struct drm_driver driver = {
> -	.driver_features = DRIVER_MODESET | DRIVER_GEM,
> +	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
>  	.set_busid = drm_virtio_set_busid,
>  	.load = virtio_gpu_driver_load,
>  	.unload = virtio_gpu_driver_unload,
> @@ -128,6 +128,17 @@ static struct drm_driver driver = {
>  	.debugfs_init = virtio_gpu_debugfs_init,
>  	.debugfs_cleanup = virtio_gpu_debugfs_takedown,
>  #endif
> +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_export = drm_gem_prime_export,
> +	.gem_prime_import = drm_gem_prime_import,
> +	.gem_prime_pin = virtgpu_gem_prime_pin,
> +	.gem_prime_unpin = virtgpu_gem_prime_unpin,
> +	.gem_prime_get_sg_table = virtgpu_gem_prime_get_sg_table,
> +	.gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table,
> +	.gem_prime_vmap = virtgpu_gem_prime_vmap,
> +	.gem_prime_vunmap = virtgpu_gem_prime_vunmap,
> +	.gem_prime_mmap = virtgpu_gem_prime_mmap,
>  
>  	.gem_free_object = virtio_gpu_gem_free_object,
>  	.gem_open_object = virtio_gpu_gem_object_open,
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
> index 2719108..79f0abe 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_drv.h
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
> @@ -359,6 +359,18 @@ int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
>  void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo);
>  int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait);
>  
> +/* virtgpu_prime.c */
> +int virtgpu_gem_prime_pin(struct drm_gem_object *obj);
> +void virtgpu_gem_prime_unpin(struct drm_gem_object *obj);
> +struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj);
> +struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
> +        struct drm_device *dev, struct dma_buf_attachment *attach,
> +        struct sg_table *sgt);
> +void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj);
> +void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
> +int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
> +                                struct vm_area_struct *vma);
> +
>  static inline struct virtio_gpu_object*
>  virtio_gpu_object_ref(struct virtio_gpu_object *bo)
>  {
> diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c
> new file mode 100644
> index 0000000..724c231
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_prime.c
> @@ -0,0 +1,72 @@
> +/*
> + * Copyright 2014 Canonical
> + *
> + * 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.
> + *
> + * Authors: Andreas Pokorny
> + */
> +
> +#include "virtgpu_drv.h"
> +
> +/* Empty Implementations as there should not be any other driver for a virtual
> + * device that might share buffers with virtgpu */
> +
> +int virtgpu_gem_prime_pin(struct drm_gem_object *obj)
> +{
> +	WARN_ONCE(1, "not implemented");
> +	return -ENOSYS;
> +}
> +
> +void virtgpu_gem_prime_unpin(struct drm_gem_object *obj)
> +{
> +	WARN_ONCE(1, "not implemented");
> +}
> +
> +
> +struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj)
> +{
> +	WARN_ONCE(1, "not implemented");
> +	return ERR_PTR(-ENOSYS);
> +}
> +
> +struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
> +	struct drm_device *dev, struct dma_buf_attachment *attach,
> +	struct sg_table *table)
> +{
> +	WARN_ONCE(1, "not implemented");
> +	return ERR_PTR(-ENOSYS);
> +}
> +
> +void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj)
> +{
> +	WARN_ONCE(1, "not implemented");
> +	return ERR_PTR(-ENOSYS);
> +}
> +
> +void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
> +{
> +	WARN_ONCE(1, "not implemented");
> +}
> +
> +int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
> +		       struct vm_area_struct *area)
> +{
> +	WARN_ONCE(1, "not implemented");
> +	return ENOSYS;

This can get called by userspace, so please don't WARN here. Also missing
negate sign:

	return -ENOSYS;

Cheers, Daniel

> +}
> -- 
> 1.8.3.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply

* Re: [PATCH v7] pci: quirk to skip msi disable on shutdown
From: Bjorn Helgaas via Virtualization @ 2015-09-21 22:10 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Fam Zheng, linux-pci, linux-kernel, Ulrich Obergfell, Yinghai Lu,
	Eric W. Biederman, virtualization
In-Reply-To: <20150921223622-mutt-send-email-mst@redhat.com>

On Mon, Sep 21, 2015 at 10:42:13PM +0300, Michael S. Tsirkin wrote:
> On Mon, Sep 21, 2015 at 01:21:47PM -0500, Bjorn Helgaas wrote:
> > On Sun, Sep 06, 2015 at 06:32:35PM +0300, Michael S. Tsirkin wrote:
> > > On some hypervisors, virtio devices tend to generate spurious interrupts
> > > when switching between MSI and non-MSI mode.  Normally, either MSI or
> > > non-MSI is used and all is well, but during shutdown, linux disables MSI
> > > which then causes an "irq %d: nobody cared" message, with irq being
> > > subsequently disabled.
> > 
> > My understanding is:
> > 
> >   Linux disables MSI/MSI-X during device shutdown.  If the device
> >   signals an interrupt after that, it may use INTx.
> > 
> > This INTx interrupt is not necessarily spurious.  Using INTx to signal an
> > interrupt that occurs when MSI is disabled seems like reasonable behavior
> > for any PCI device.
> > And it doesn't seem related to switching between MSI and non-MSI mode.
> > Yes, the INTx happens *after* disabling MSI, but it is not at all
> > *because* we disabled MSI.  So I wouldn't say "they generate spurious
> > interrupts when switching between MSI and non-MSI."
> > 
> > Why doesn't virtio-pci just register an INTx handler in addition to an MSI
> > handler?
> 
> The handler causes an expensive exit to the hypervisor,
> and the INTx lines are shared with other devices.

Do we care?  Is this a performance path?  I thought we were in a kexec
shutdown path.

> Seems silly to slow them down just so we can do something
> that triggers the device bug.  The bus master is disabled by that time,
> if linux can just desist from touching MSI enable device won't
> send either INTx (because MSI is on) or MSI
> (because bus master is on) and all will be well.

It would also be silly to put special-purpose code in the PCI core
if there's a reasonable way to handle this in a driver.

Can you describe exactly what the device bug is?  Apparently you're
saying that if we shut down MSI, it triggers the bug?  And I guess
you're talking about a virtio device as implemented in qemu or other
hypervisors?

If we leave MSI enabled (as your patch does), then the device has MSI
enabled and Bus Master disabled.  I can see these possibilities:

  1) the device never recognizes an interrupt condition
  2) the device sets the pending bit but doesn't issue the MSI write,
     so the OS doesn't see the interrupt unless it polls for it
  3) the device signals MSI and we still have an MSI handler
     registered, so we silently handle it
  4) the device signals INTx

You seem to suggest that if we leave MSI enabled (as your patch does),
we're in case 1.  But I doubt that disabling MSI causes the device to
interrupt.

Case 2 seems more likely to me: the device recognized an interrupt
condition, e.g., an event occurred, and the OS simply doesn't see the
interrupt because the device can't issue the MSI message.

Case 3 does seem like it would be a device bug, because the device
shouldn't do an MSI write when Bus Master is disabled.  I don't see
this case mentioned explicitly in the PCI spec, but PCIe r3.0 spec sec
7.5.1.1 does make it clear that disabling Bus Master also disables MSI
messages.

I don't know whether case 4 would be legal or not.  But apparently it
doesn't happen with the virtio device anyway, so it's not really a
concern here.

Bjorn

^ permalink raw reply

* Re: [PATCH v7] pci: quirk to skip msi disable on shutdown
From: Michael S. Tsirkin @ 2015-09-21 19:42 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Fam Zheng, linux-pci, linux-kernel, Ulrich Obergfell, Yinghai Lu,
	Eric W. Biederman, virtualization
In-Reply-To: <20150921182147.GC27964@google.com>

On Mon, Sep 21, 2015 at 01:21:47PM -0500, Bjorn Helgaas wrote:
> On Sun, Sep 06, 2015 at 06:32:35PM +0300, Michael S. Tsirkin wrote:
> > On some hypervisors, virtio devices tend to generate spurious interrupts
> > when switching between MSI and non-MSI mode.  Normally, either MSI or
> > non-MSI is used and all is well, but during shutdown, linux disables MSI
> > which then causes an "irq %d: nobody cared" message, with irq being
> > subsequently disabled.
> 
> My understanding is:
> 
>   Linux disables MSI/MSI-X during device shutdown.  If the device
>   signals an interrupt after that, it may use INTx.
> 
> This INTx interrupt is not necessarily spurious.  Using INTx to signal an
> interrupt that occurs when MSI is disabled seems like reasonable behavior
> for any PCI device.
> And it doesn't seem related to switching between MSI and non-MSI mode.
> Yes, the INTx happens *after* disabling MSI, but it is not at all
> *because* we disabled MSI.  So I wouldn't say "they generate spurious
> interrupts when switching between MSI and non-MSI."
> 
> Why doesn't virtio-pci just register an INTx handler in addition to an MSI
> handler?
> 
> Bjorn

The handler causes an expensive exit to the hypervisor,
and the INTx lines are shared with other devices.

Seems silly to slow them down just so we can do something
that triggers the device bug.  The bus master is disabled by that time,
if linux can just desist from touching MSI enable device won't
send either INTx (because MSI is on) or MSI
(because bus master is on) and all will be well.

-- 
MST

^ permalink raw reply

* Re: [PATCH v7] pci: quirk to skip msi disable on shutdown
From: Bjorn Helgaas via Virtualization @ 2015-09-21 18:21 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Fam Zheng, linux-pci, linux-kernel, Ulrich Obergfell, Yinghai Lu,
	Eric W. Biederman, virtualization
In-Reply-To: <1441553385-3810-1-git-send-email-mst@redhat.com>

On Sun, Sep 06, 2015 at 06:32:35PM +0300, Michael S. Tsirkin wrote:
> On some hypervisors, virtio devices tend to generate spurious interrupts
> when switching between MSI and non-MSI mode.  Normally, either MSI or
> non-MSI is used and all is well, but during shutdown, linux disables MSI
> which then causes an "irq %d: nobody cared" message, with irq being
> subsequently disabled.

My understanding is:

  Linux disables MSI/MSI-X during device shutdown.  If the device
  signals an interrupt after that, it may use INTx.

This INTx interrupt is not necessarily spurious.  Using INTx to signal an
interrupt that occurs when MSI is disabled seems like reasonable behavior
for any PCI device.

And it doesn't seem related to switching between MSI and non-MSI mode.
Yes, the INTx happens *after* disabling MSI, but it is not at all
*because* we disabled MSI.  So I wouldn't say "they generate spurious
interrupts when switching between MSI and non-MSI."

Why doesn't virtio-pci just register an INTx handler in addition to an MSI
handler?

Bjorn

^ permalink raw reply

* Re: RFC: virtio-peer shared memory based peer communication device
From: Jan Kiszka @ 2015-09-21 12:32 UTC (permalink / raw)
  To: Michael S. Tsirkin, Claudio Fontana
  Cc: Zhang, Yang Z, virtio-dev@lists.oasis-open.org,
	opnfv-tech-discuss@lists.opnfv.org, qemu-devel@nongnu.org,
	virtualization@lists.linux-foundation.org
In-Reply-To: <20150921150953-mutt-send-email-mst@redhat.com>

On 2015-09-21 14:13, Michael S. Tsirkin wrote:
> On Fri, Sep 18, 2015 at 06:29:27PM +0200, Claudio Fontana wrote:
>> Hello,
>>
>> this is a first RFC for virtio-peer 0.1, which is still very much a work in progress:
>>
>> https://github.com/hw-claudio/virtio-peer/wiki
>>
>> It is also available as PDF there, but the text is reproduced here for commenting:
>>
>> Peer shared memory communication device (virtio-peer)
>>
>> General Overview
>>
>> (I recommend looking at the PDF for some clarifying pictures)
>>
>> The Virtio Peer shared memory communication device (virtio-peer) is a
>> virtual device which allows high performance low latency guest to
>> guest communication. It uses a new queue extension feature tentatively
>> called VIRTIO_F_WINDOW which indicates that descriptor tables,
>> available and used rings and Queue Data reside in physical memory
>> ranges called Windows, each identified with an unique identifier
>> called WindowID.
> 
> So if I had to summarize the difference from regular virtio,
> I'd say the main one is that this uses window id + offset
> instead of the physical address.
> 
> 
> My question is - why do it?
> 
> All windows are in memory space, are they not?
> 
> How about guest using full physical addresses,
> and hypervisor sending the window physical address
> to VM2?
> 
> VM2 can uses that to find both window id and offset.
> 
> 
> This way at least VM1 can use regular virtio without changes.

What would be the value of having different drivers in VM1 and VM2,
specifically if both run Linux?

Jan

-- 
Siemens AG, Corporate Technology, CT RTC ITP SES-DE
Corporate Competence Center Embedded Linux

^ permalink raw reply

* Re: RFC: virtio-peer shared memory based peer communication device
From: Paolo Bonzini @ 2015-09-21 12:15 UTC (permalink / raw)
  To: Jan Kiszka, Claudio Fontana, Zhang, Yang Z, Michael S. Tsirkin,
	qemu-devel@nongnu.org, virtualization@lists.linux-foundation.org,
	virtio-dev@lists.oasis-open.org,
	opnfv-tech-discuss@lists.opnfv.org
In-Reply-To: <55FFE03E.6080106@siemens.com>



On 21/09/2015 12:47, Jan Kiszka wrote:
>> > Apart from the windows idea, how does virtio-peer compare to virtio-rpmsg?
> rpmsg is a very specialized thing. It targets single AMP cores, assuming
> that those have full access to the main memory.

Yes, this is why I did say "apart from the windows idea".

> And it is also a
> centralized approach where all message go through the main Linux
> instance. I suspect we could cover that use case as well with generic
> inter-vm shared memory device, but I didn't think about all details yet.

The virtqueue handling seems very similar between the two.  However, the
messages for rpmsg however have a small header (struct rpmsg_hdr in
include/linux/rpmsg.h) and there is a weird feature bit VIRTIO_RPMSG_F_NS.

So I guess virtio-rpmsg and virtio-peer are about as similar as
virtio-serial and virtio-peer.

Paolo

^ permalink raw reply

* Re: RFC: virtio-peer shared memory based peer communication device
From: Michael S. Tsirkin @ 2015-09-21 12:13 UTC (permalink / raw)
  To: Claudio Fontana
  Cc: virtio-dev@lists.oasis-open.org, Jan Kiszka,
	qemu-devel@nongnu.org, virtualization@lists.linux-foundation.org,
	Zhang, Yang Z, opnfv-tech-discuss@lists.opnfv.org
In-Reply-To: <55FC3BE7.5010503@huawei.com>

On Fri, Sep 18, 2015 at 06:29:27PM +0200, Claudio Fontana wrote:
> Hello,
> 
> this is a first RFC for virtio-peer 0.1, which is still very much a work in progress:
> 
> https://github.com/hw-claudio/virtio-peer/wiki
> 
> It is also available as PDF there, but the text is reproduced here for commenting:
> 
> Peer shared memory communication device (virtio-peer)
> 
> General Overview
> 
> (I recommend looking at the PDF for some clarifying pictures)
> 
> The Virtio Peer shared memory communication device (virtio-peer) is a
> virtual device which allows high performance low latency guest to
> guest communication. It uses a new queue extension feature tentatively
> called VIRTIO_F_WINDOW which indicates that descriptor tables,
> available and used rings and Queue Data reside in physical memory
> ranges called Windows, each identified with an unique identifier
> called WindowID.

So if I had to summarize the difference from regular virtio,
I'd say the main one is that this uses window id + offset
instead of the physical address.


My question is - why do it?

All windows are in memory space, are they not?

How about guest using full physical addresses,
and hypervisor sending the window physical address
to VM2?

VM2 can uses that to find both window id and offset.


This way at least VM1 can use regular virtio without changes.

-- 
MST

^ permalink raw reply

* Re: RFC: virtio-peer shared memory based peer communication device
From: Jan Kiszka @ 2015-09-21 10:47 UTC (permalink / raw)
  To: Paolo Bonzini, Claudio Fontana, Zhang, Yang Z, Michael S. Tsirkin,
	qemu-devel@nongnu.org, virtualization@lists.linux-foundation.org,
	virtio-dev@lists.oasis-open.org,
	opnfv-tech-discuss@lists.opnfv.org
In-Reply-To: <55FC7DFA.80204@redhat.com>

On 2015-09-18 23:11, Paolo Bonzini wrote:
> On 18/09/2015 18:29, Claudio Fontana wrote:
>>
>> this is a first RFC for virtio-peer 0.1, which is still very much a work in progress:
>>
>> https://github.com/hw-claudio/virtio-peer/wiki
>>
>> It is also available as PDF there, but the text is reproduced here for commenting:
>>
>> Peer shared memory communication device (virtio-peer)
> 
> Apart from the windows idea, how does virtio-peer compare to virtio-rpmsg?

rpmsg is a very specialized thing. It targets single AMP cores, assuming
that those have full access to the main memory. And it is also a
centralized approach where all message go through the main Linux
instance. I suspect we could cover that use case as well with generic
inter-vm shared memory device, but I didn't think about all details yet.

Jan

-- 
Siemens AG, Corporate Technology, CT RTC ITP SES-DE
Corporate Competence Center Embedded Linux

^ permalink raw reply

* Re: [PATCH v2 4/6] virtio-gpu: add 3d/virgl support
From: Michael S. Tsirkin @ 2015-09-21 10:15 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: David Airlie, open list:ABI/API, open list,
	open list:VIRTIO GPU DRIVER, open list:VIRTIO GPU DRIVER,
	Dave Airlie
In-Reply-To: <1442828417-6165-5-git-send-email-kraxel@redhat.com>

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

^ permalink raw reply

* [PATCH v2 6/6] virtio-gpu: mark as a render gpu
From: Gerd Hoffmann @ 2015-09-21  9:40 UTC (permalink / raw)
  To: dri-devel
  Cc: David Airlie, open list, open list:VIRTIO GPU DRIVER,
	open list:VIRTIO GPU DRIVER, Dave Airlie
In-Reply-To: <1442828417-6165-1-git-send-email-kraxel@redhat.com>

From: Dave Airlie <airlied@redhat.com>

Also add DRM_RENDER_ALLOW to the ioctls.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 drivers/gpu/drm/virtio/virtgpu_drv.c   |  2 +-
 drivers/gpu/drm/virtio/virtgpu_ioctl.c | 18 +++++++++---------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 1245d09..e00298e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -113,7 +113,7 @@ static const struct file_operations virtio_gpu_driver_fops = {
 
 
 static struct drm_driver driver = {
-	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER,
 	.set_busid = drm_virtio_set_busid,
 	.load = virtio_gpu_driver_load,
 	.unload = virtio_gpu_driver_unload,
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index d5c9e02..d297f38 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -540,33 +540,33 @@ copy_exit:
 
 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_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 
 	DRM_IOCTL_DEF_DRV(VIRTGPU_EXECBUFFER, virtio_gpu_execbuffer_ioctl,
-			  DRM_AUTH|DRM_UNLOCKED),
+			  DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 
 	DRM_IOCTL_DEF_DRV(VIRTGPU_GETPARAM, virtio_gpu_getparam_ioctl,
-			  DRM_AUTH|DRM_UNLOCKED),
+			  DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 
 	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE,
 			  virtio_gpu_resource_create_ioctl,
-			  DRM_AUTH|DRM_UNLOCKED),
+			  DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 
 	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_INFO, virtio_gpu_resource_info_ioctl,
-			  DRM_AUTH|DRM_UNLOCKED),
+			  DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 
 	/* 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_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_TO_HOST,
 			  virtio_gpu_transfer_to_host_ioctl,
-			  DRM_AUTH|DRM_UNLOCKED),
+			  DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 
 	DRM_IOCTL_DEF_DRV(VIRTGPU_WAIT, virtio_gpu_wait_ioctl,
-			  DRM_AUTH|DRM_UNLOCKED),
+			  DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 
 	DRM_IOCTL_DEF_DRV(VIRTGPU_GET_CAPS, virtio_gpu_get_caps_ioctl,
-			  DRM_AUTH|DRM_UNLOCKED),
+			  DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 };
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH v2 5/6] virtio-gpu: add basic prime support
From: Gerd Hoffmann @ 2015-09-21  9:40 UTC (permalink / raw)
  To: dri-devel
  Cc: David Airlie, open list, open list:VIRTIO GPU DRIVER,
	open list:VIRTIO GPU DRIVER, Dave Airlie
In-Reply-To: <1442828417-6165-1-git-send-email-kraxel@redhat.com>

From: Dave Airlie <airlied@redhat.com>

This is enough to enable DRI3.

Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 drivers/gpu/drm/virtio/Makefile        |  2 +-
 drivers/gpu/drm/virtio/virtgpu_drv.c   | 13 +++++-
 drivers/gpu/drm/virtio/virtgpu_drv.h   | 12 ++++++
 drivers/gpu/drm/virtio/virtgpu_prime.c | 72 ++++++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/virtio/virtgpu_prime.c

diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
index da7bf19..3fb8eac 100644
--- a/drivers/gpu/drm/virtio/Makefile
+++ b/drivers/gpu/drm/virtio/Makefile
@@ -7,6 +7,6 @@ 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_ioctl.o
+	virtgpu_ioctl.o virtgpu_prime.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 957e455..1245d09 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -113,7 +113,7 @@ static const struct file_operations virtio_gpu_driver_fops = {
 
 
 static struct drm_driver driver = {
-	.driver_features = DRIVER_MODESET | DRIVER_GEM,
+	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
 	.set_busid = drm_virtio_set_busid,
 	.load = virtio_gpu_driver_load,
 	.unload = virtio_gpu_driver_unload,
@@ -128,6 +128,17 @@ static struct drm_driver driver = {
 	.debugfs_init = virtio_gpu_debugfs_init,
 	.debugfs_cleanup = virtio_gpu_debugfs_takedown,
 #endif
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_pin = virtgpu_gem_prime_pin,
+	.gem_prime_unpin = virtgpu_gem_prime_unpin,
+	.gem_prime_get_sg_table = virtgpu_gem_prime_get_sg_table,
+	.gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table,
+	.gem_prime_vmap = virtgpu_gem_prime_vmap,
+	.gem_prime_vunmap = virtgpu_gem_prime_vunmap,
+	.gem_prime_mmap = virtgpu_gem_prime_mmap,
 
 	.gem_free_object = virtio_gpu_gem_free_object,
 	.gem_open_object = virtio_gpu_gem_object_open,
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 2719108..79f0abe 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -359,6 +359,18 @@ int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
 void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo);
 int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait);
 
+/* virtgpu_prime.c */
+int virtgpu_gem_prime_pin(struct drm_gem_object *obj);
+void virtgpu_gem_prime_unpin(struct drm_gem_object *obj);
+struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
+        struct drm_device *dev, struct dma_buf_attachment *attach,
+        struct sg_table *sgt);
+void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj);
+void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
+                                struct vm_area_struct *vma);
+
 static inline struct virtio_gpu_object*
 virtio_gpu_object_ref(struct virtio_gpu_object *bo)
 {
diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c
new file mode 100644
index 0000000..724c231
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_prime.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 Canonical
+ *
+ * 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.
+ *
+ * Authors: Andreas Pokorny
+ */
+
+#include "virtgpu_drv.h"
+
+/* Empty Implementations as there should not be any other driver for a virtual
+ * device that might share buffers with virtgpu */
+
+int virtgpu_gem_prime_pin(struct drm_gem_object *obj)
+{
+	WARN_ONCE(1, "not implemented");
+	return -ENOSYS;
+}
+
+void virtgpu_gem_prime_unpin(struct drm_gem_object *obj)
+{
+	WARN_ONCE(1, "not implemented");
+}
+
+
+struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+	WARN_ONCE(1, "not implemented");
+	return ERR_PTR(-ENOSYS);
+}
+
+struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
+	struct drm_device *dev, struct dma_buf_attachment *attach,
+	struct sg_table *table)
+{
+	WARN_ONCE(1, "not implemented");
+	return ERR_PTR(-ENOSYS);
+}
+
+void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj)
+{
+	WARN_ONCE(1, "not implemented");
+	return ERR_PTR(-ENOSYS);
+}
+
+void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+	WARN_ONCE(1, "not implemented");
+}
+
+int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
+		       struct vm_area_struct *area)
+{
+	WARN_ONCE(1, "not implemented");
+	return ENOSYS;
+}
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH v2 4/6] virtio-gpu: add 3d/virgl support
From: Gerd Hoffmann @ 2015-09-21  9:40 UTC (permalink / raw)
  To: dri-devel
  Cc: Michael S. Tsirkin, David Airlie, open list:ABI/API, open list,
	open list:VIRTIO GPU DRIVER, open list:VIRTIO GPU DRIVER,
	Dave Airlie
In-Reply-To: <1442828417-6165-1-git-send-email-kraxel@redhat.com>

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

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 */
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH v2 3/6] virtio-gpu: wait for cursor updates finish
From: Gerd Hoffmann @ 2015-09-21  9:40 UTC (permalink / raw)
  To: dri-devel
  Cc: David Airlie, open list, open list:VIRTIO GPU DRIVER,
	open list:VIRTIO GPU DRIVER
In-Reply-To: <1442828417-6165-1-git-send-email-kraxel@redhat.com>

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 drivers/gpu/drm/virtio/virtgpu_display.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index 4e160ef..97e5274 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -90,6 +90,9 @@ static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc,
 					   cpu_to_le32(64),
 					   cpu_to_le32(64),
 					   0, 0, &fence);
+	reservation_object_add_excl_fence(qobj->tbo.resv,
+					  &fence->f);
+	virtio_gpu_object_wait(qobj, false);
 
 	output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
 	output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle);
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH v2 2/6] virtio-gpu: add & use virtio_gpu_queue_fenced_ctrl_buffer
From: Gerd Hoffmann @ 2015-09-21  9:40 UTC (permalink / raw)
  To: dri-devel
  Cc: David Airlie, open list, open list:VIRTIO GPU DRIVER,
	open list:VIRTIO GPU DRIVER
In-Reply-To: <1442828417-6165-1-git-send-email-kraxel@redhat.com>

Add helper function to handle the submission of fenced control requests.
Make sure we initialize the fence while holding the virtqueue lock, so
requests can't be reordered.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 drivers/gpu/drm/virtio/virtgpu_fence.c |  2 +-
 drivers/gpu/drm/virtio/virtgpu_vq.c    | 40 +++++++++++++++++++++++++++++-----
 2 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c
index 1da6326..793ad9f 100644
--- a/drivers/gpu/drm/virtio/virtgpu_fence.c
+++ b/drivers/gpu/drm/virtio/virtgpu_fence.c
@@ -81,7 +81,7 @@ int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
 	struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
 	unsigned long irq_flags;
 
-	*fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL);
+	*fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_ATOMIC);
 	if ((*fence) == NULL)
 		return -ENOMEM;
 
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 5b9bc24..ee25e9a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -347,6 +347,38 @@ static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
 	return rc;
 }
 
+static int virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev,
+					       struct virtio_gpu_vbuffer *vbuf,
+					       struct virtio_gpu_ctrl_hdr *hdr,
+					       struct virtio_gpu_fence **fence)
+{
+	struct virtqueue *vq = vgdev->ctrlq.vq;
+	int rc;
+
+again:
+	spin_lock(&vgdev->ctrlq.qlock);
+
+	/*
+	 * Make sure we have enouth space in the virtqueue.  If not
+	 * wait here until we have.
+	 *
+	 * Without that virtio_gpu_queue_ctrl_buffer_nolock might have
+	 * to wait for free space, which can result in fence ids being
+	 * submitted out-of-order.
+	 */
+	if (vq->num_free < 3) {
+		spin_unlock(&vgdev->ctrlq.qlock);
+		wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= 3);
+		goto again;
+	}
+
+	if (fence)
+		virtio_gpu_fence_emit(vgdev, hdr, fence);
+	rc = virtio_gpu_queue_ctrl_buffer_locked(vgdev, vbuf);
+	spin_unlock(&vgdev->ctrlq.qlock);
+	return rc;
+}
+
 static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
 				   struct virtio_gpu_vbuffer *vbuf)
 {
@@ -499,9 +531,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
 	cmd_p->r.x = x;
 	cmd_p->r.y = y;
 
-	if (fence)
-		virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
-	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
 }
 
 static void
@@ -524,9 +554,7 @@ virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev,
 	vbuf->data_buf = ents;
 	vbuf->data_size = sizeof(*ents) * nents;
 
-	if (fence)
-		virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
-	virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+	virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
 }
 
 static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH v2 1/6] virtio-gpu: add virtio_gpu_queue_ctrl_buffer_locked
From: Gerd Hoffmann @ 2015-09-21  9:40 UTC (permalink / raw)
  To: dri-devel
  Cc: David Airlie, open list, open list:VIRTIO GPU DRIVER,
	open list:VIRTIO GPU DRIVER
In-Reply-To: <1442828417-6165-1-git-send-email-kraxel@redhat.com>

Add virtio_gpu_queue_ctrl_buffer_locked function, which does the same as
virtio_gpu_queue_ctrl_buffer but does not take the virtqueue lock.  The
caller must hold the lock instead.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 drivers/gpu/drm/virtio/virtgpu_vq.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 1698669f..5b9bc24 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -293,8 +293,8 @@ void virtio_gpu_dequeue_cursor_func(struct work_struct *work)
 	wake_up(&vgdev->cursorq.ack_queue);
 }
 
-static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
-					struct virtio_gpu_vbuffer *vbuf)
+static int virtio_gpu_queue_ctrl_buffer_locked(struct virtio_gpu_device *vgdev,
+					       struct virtio_gpu_vbuffer *vbuf)
 {
 	struct virtqueue *vq = vgdev->ctrlq.vq;
 	struct scatterlist *sgs[3], vcmd, vout, vresp;
@@ -320,7 +320,6 @@ static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
 		incnt++;
 	}
 
-	spin_lock(&vgdev->ctrlq.qlock);
 retry:
 	ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
 	if (ret == -ENOSPC) {
@@ -331,13 +330,23 @@ retry:
 	} else {
 		virtqueue_kick(vq);
 	}
-	spin_unlock(&vgdev->ctrlq.qlock);
 
 	if (!ret)
 		ret = vq->num_free;
 	return ret;
 }
 
+static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
+					struct virtio_gpu_vbuffer *vbuf)
+{
+	int rc;
+
+	spin_lock(&vgdev->ctrlq.qlock);
+	rc = virtio_gpu_queue_ctrl_buffer_locked(vgdev, vbuf);
+	spin_unlock(&vgdev->ctrlq.qlock);
+	return rc;
+}
+
 static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
 				   struct virtio_gpu_vbuffer *vbuf)
 {
-- 
1.8.3.1

^ permalink raw reply related

* Re: [RFC PATCH 0/2] virtio nvme
From: Ming Lin @ 2015-09-18 23:05 UTC (permalink / raw)
  To: Nicholas A. Bellinger
  Cc: Minturn, Dave B, linux-nvme, Linux Virtualization, target-devel,
	Christoph Hellwig
In-Reply-To: <1442610544.10492.33.camel@haakon3.risingtidesystems.com>

On Fri, Sep 18, 2015 at 2:09 PM, Nicholas A. Bellinger
<nab@linux-iscsi.org> wrote:
> On Fri, 2015-09-18 at 11:12 -0700, Ming Lin wrote:
>> On Thu, 2015-09-17 at 17:55 -0700, Nicholas A. Bellinger wrote:
>> > On Thu, 2015-09-17 at 16:31 -0700, Ming Lin wrote:
>> > > On Wed, 2015-09-16 at 23:10 -0700, Nicholas A. Bellinger wrote:
>> > > > Hi Ming & Co,
>
> <SNIP>
>
>> > > > > I think the future "LIO NVMe target" only speaks NVMe protocol.
>> > > > >
>> > > > > Nick(CCed), could you correct me if I'm wrong?
>> > > > >
>> > > > > For SCSI stack, we have:
>> > > > > virtio-scsi(guest)
>> > > > > tcm_vhost(or vhost_scsi, host)
>> > > > > LIO-scsi-target
>> > > > >
>> > > > > For NVMe stack, we'll have similar components:
>> > > > > virtio-nvme(guest)
>> > > > > vhost_nvme(host)
>> > > > > LIO-NVMe-target
>> > > > >
>> > > >
>> > > > I think it's more interesting to consider a 'vhost style' driver that
>> > > > can be used with unmodified nvme host OS drivers.
>> > > >
>> > > > Dr. Hannes (CC'ed) had done something like this for megasas a few years
>> > > > back using specialized QEMU emulation + eventfd based LIO fabric driver,
>> > > > and got it working with Linux + MSFT guests.
>> > > >
>> > > > Doing something similar for nvme would (potentially) be on par with
>> > > > current virtio-scsi+vhost-scsi small-block performance for scsi-mq
>> > > > guests, without the extra burden of a new command set specific virtio
>> > > > driver.
>> > >
>> > > Trying to understand it.
>> > > Is it like below?
>> > >
>> > >   .------------------------.   MMIO   .---------------------------------------.
>> > >   | Guest                  |--------> | Qemu                                  |
>> > >   | Unmodified NVMe driver |<-------- | NVMe device simulation(eventfd based) |
>> > >   '------------------------'          '---------------------------------------'
>> > >                                                   |          ^
>> > >                                       write NVMe  |          |  notify command
>> > >                                       command     |          |  completion
>> > >                                       to eventfd  |          |  to eventfd
>> > >                                                   v          |
>> > >                                       .--------------------------------------.
>> > >                                       | Host:                                |
>> > >                                       | eventfd based LIO NVMe fabric driver |
>> > >                                       '--------------------------------------'
>> > >                                                         |
>> > >                                                         | nvme_queue_rq()
>> > >                                                         v
>> > >                                        .--------------------------------------.
>> > >                                        | NVMe driver                          |
>> > >                                        '--------------------------------------'
>> > >                                                         |
>> > >                                                         |
>> > >                                                         v
>> > >                                        .-------------------------------------.
>> > >                                        | NVMe device                         |
>> > >                                        '-------------------------------------'
>> > >
>> >
>> > Correct.  The LIO driver on KVM host would be handling some amount of
>> > NVMe host interface emulation in kernel code, and would be able to
>> > decode nvme Read/Write/Flush operations and translate -> submit to
>> > existing backend drivers.
>>
>> Let me call the "eventfd based LIO NVMe fabric driver" as
>> "tcm_eventfd_nvme"
>>
>> Currently, LIO frontend driver(iscsi, fc, vhost-scsi etc) talk to LIO
>> backend driver(fileio, iblock etc) with SCSI commands.
>>
>> Did you mean the "tcm_eventfd_nvme" driver need to translate NVMe
>> commands to SCSI commands and then submit to backend driver?
>>
>
> IBLOCK + FILEIO + RD_MCP don't speak SCSI, they simply process I/Os with
> LBA + length based on SGL memory or pass along a FLUSH with LBA +
> length.
>
> So once the 'tcm_eventfd_nvme' driver on KVM host receives a nvme host
> hardware frame via eventfd, it would decode the frame and send along the
> Read/Write/Flush when exposing existing (non nvme native) backend
> drivers.
>
> This doesn't apply to PSCSI backend driver of course, because it expects
> to process actual SCSI CDBs.
>
>> But I thought the future "LIO NVMe target" can support frontend driver
>> talk to backend driver directly with NVMe commands without translation.
>>
>
> The native target_core_nvme backend driver is not processing nvme
> commands per-say, but simply exposing nvme hardware queue resources to a
> frontend fabric driver.
>
> For the nvme-over-fabrics case, backend nvme submission/complete queues
> are mapped to RDMA hardware queues.  So essentially the nvme physical
> region page (PRP) is mapped to ib_sgl->addr.
>
> For a 'tcm_eventfd_nvme' style host-paravirt fabric case, it's less
> clear how exposing native nvme backend hardware resources would work, or
> if there is a significant performance benefit over just using
> submit_bio() for Read/Write/Flush.

Now it's much more clear. I'll do a tcm_eventfd_nvme prototype.

Thanks for all the detail explanation.

>
> --nab
>

^ permalink raw reply

* Re: RFC: virtio-peer shared memory based peer communication device
From: Paolo Bonzini @ 2015-09-18 21:11 UTC (permalink / raw)
  To: Claudio Fontana, Zhang, Yang Z, Michael S. Tsirkin,
	qemu-devel@nongnu.org, virtualization@lists.linux-foundation.org,
	virtio-dev@lists.oasis-open.org,
	opnfv-tech-discuss@lists.opnfv.org
  Cc: Jan Kiszka
In-Reply-To: <55FC3BE7.5010503@huawei.com>



On 18/09/2015 18:29, Claudio Fontana wrote:
> 
> this is a first RFC for virtio-peer 0.1, which is still very much a work in progress:
> 
> https://github.com/hw-claudio/virtio-peer/wiki
> 
> It is also available as PDF there, but the text is reproduced here for commenting:
> 
> Peer shared memory communication device (virtio-peer)

Apart from the windows idea, how does virtio-peer compare to virtio-rpmsg?

Paolo

^ permalink raw reply

* Re: [RFC PATCH 0/2] virtio nvme
From: Nicholas A. Bellinger @ 2015-09-18 21:09 UTC (permalink / raw)
  To: Ming Lin
  Cc: Minturn, Dave B, linux-nvme, Linux Virtualization, target-devel,
	Christoph Hellwig
In-Reply-To: <1442599957.11660.8.camel@ssi>

On Fri, 2015-09-18 at 11:12 -0700, Ming Lin wrote:
> On Thu, 2015-09-17 at 17:55 -0700, Nicholas A. Bellinger wrote:
> > On Thu, 2015-09-17 at 16:31 -0700, Ming Lin wrote:
> > > On Wed, 2015-09-16 at 23:10 -0700, Nicholas A. Bellinger wrote:
> > > > Hi Ming & Co,

<SNIP>

> > > > > I think the future "LIO NVMe target" only speaks NVMe protocol.
> > > > > 
> > > > > Nick(CCed), could you correct me if I'm wrong?
> > > > > 
> > > > > For SCSI stack, we have:
> > > > > virtio-scsi(guest)
> > > > > tcm_vhost(or vhost_scsi, host)
> > > > > LIO-scsi-target
> > > > > 
> > > > > For NVMe stack, we'll have similar components:
> > > > > virtio-nvme(guest)
> > > > > vhost_nvme(host)
> > > > > LIO-NVMe-target
> > > > > 
> > > > 
> > > > I think it's more interesting to consider a 'vhost style' driver that
> > > > can be used with unmodified nvme host OS drivers.
> > > > 
> > > > Dr. Hannes (CC'ed) had done something like this for megasas a few years
> > > > back using specialized QEMU emulation + eventfd based LIO fabric driver,
> > > > and got it working with Linux + MSFT guests.
> > > > 
> > > > Doing something similar for nvme would (potentially) be on par with
> > > > current virtio-scsi+vhost-scsi small-block performance for scsi-mq
> > > > guests, without the extra burden of a new command set specific virtio
> > > > driver.
> > > 
> > > Trying to understand it.
> > > Is it like below?
> > > 
> > >   .------------------------.   MMIO   .---------------------------------------.
> > >   | Guest                  |--------> | Qemu                                  |
> > >   | Unmodified NVMe driver |<-------- | NVMe device simulation(eventfd based) |
> > >   '------------------------'          '---------------------------------------'
> > >                                                   |          ^
> > >                                       write NVMe  |          |  notify command
> > >                                       command     |          |  completion
> > >                                       to eventfd  |          |  to eventfd
> > >                                                   v          |
> > >                                       .--------------------------------------.
> > >                                       | Host:                                |
> > >                                       | eventfd based LIO NVMe fabric driver |
> > >                                       '--------------------------------------'
> > >                                                         |
> > >                                                         | nvme_queue_rq()
> > >                                                         v
> > >                                        .--------------------------------------.
> > >                                        | NVMe driver                          |
> > >                                        '--------------------------------------'
> > >                                                         |
> > >                                                         |
> > >                                                         v
> > >                                        .-------------------------------------.
> > >                                        | NVMe device                         |
> > >                                        '-------------------------------------'
> > > 
> > 
> > Correct.  The LIO driver on KVM host would be handling some amount of
> > NVMe host interface emulation in kernel code, and would be able to
> > decode nvme Read/Write/Flush operations and translate -> submit to
> > existing backend drivers.
> 
> Let me call the "eventfd based LIO NVMe fabric driver" as
> "tcm_eventfd_nvme"
> 
> Currently, LIO frontend driver(iscsi, fc, vhost-scsi etc) talk to LIO
> backend driver(fileio, iblock etc) with SCSI commands.
> 
> Did you mean the "tcm_eventfd_nvme" driver need to translate NVMe
> commands to SCSI commands and then submit to backend driver?
> 

IBLOCK + FILEIO + RD_MCP don't speak SCSI, they simply process I/Os with
LBA + length based on SGL memory or pass along a FLUSH with LBA +
length.

So once the 'tcm_eventfd_nvme' driver on KVM host receives a nvme host
hardware frame via eventfd, it would decode the frame and send along the
Read/Write/Flush when exposing existing (non nvme native) backend
drivers.

This doesn't apply to PSCSI backend driver of course, because it expects
to process actual SCSI CDBs.

> But I thought the future "LIO NVMe target" can support frontend driver
> talk to backend driver directly with NVMe commands without translation.
> 

The native target_core_nvme backend driver is not processing nvme
commands per-say, but simply exposing nvme hardware queue resources to a
frontend fabric driver.

For the nvme-over-fabrics case, backend nvme submission/complete queues
are mapped to RDMA hardware queues.  So essentially the nvme physical
region page (PRP) is mapped to ib_sgl->addr.

For a 'tcm_eventfd_nvme' style host-paravirt fabric case, it's less
clear how exposing native nvme backend hardware resources would work, or
if there is a significant performance benefit over just using
submit_bio() for Read/Write/Flush.

--nab

^ permalink raw reply

* Re: [RFC PATCH 0/2] virtio nvme
From: Ming Lin @ 2015-09-18 18:12 UTC (permalink / raw)
  To: Nicholas A. Bellinger
  Cc: Minturn, Dave B, linux-nvme, Linux Virtualization, target-devel,
	Christoph Hellwig
In-Reply-To: <1442537752.11959.35.camel@haakon3.risingtidesystems.com>

On Thu, 2015-09-17 at 17:55 -0700, Nicholas A. Bellinger wrote:
> On Thu, 2015-09-17 at 16:31 -0700, Ming Lin wrote:
> > On Wed, 2015-09-16 at 23:10 -0700, Nicholas A. Bellinger wrote:
> > > Hi Ming & Co,
> > > 
> > > On Thu, 2015-09-10 at 10:28 -0700, Ming Lin wrote:
> > > > On Thu, 2015-09-10 at 15:38 +0100, Stefan Hajnoczi wrote:
> > > > > On Thu, Sep 10, 2015 at 6:48 AM, Ming Lin <mlin@kernel.org> wrote:
> > > > > > These 2 patches added virtio-nvme to kernel and qemu,
> > > > > > basically modified from virtio-blk and nvme code.
> > > > > >
> > > > > > As title said, request for your comments.
> > > 
> > > <SNIP>
> > > 
> > > > > 
> > > > > At first glance it seems like the virtio_nvme guest driver is just
> > > > > another block driver like virtio_blk, so I'm not clear why a
> > > > > virtio-nvme device makes sense.
> > > > 
> > > > I think the future "LIO NVMe target" only speaks NVMe protocol.
> > > > 
> > > > Nick(CCed), could you correct me if I'm wrong?
> > > > 
> > > > For SCSI stack, we have:
> > > > virtio-scsi(guest)
> > > > tcm_vhost(or vhost_scsi, host)
> > > > LIO-scsi-target
> > > > 
> > > > For NVMe stack, we'll have similar components:
> > > > virtio-nvme(guest)
> > > > vhost_nvme(host)
> > > > LIO-NVMe-target
> > > > 
> > > 
> > > I think it's more interesting to consider a 'vhost style' driver that
> > > can be used with unmodified nvme host OS drivers.
> > > 
> > > Dr. Hannes (CC'ed) had done something like this for megasas a few years
> > > back using specialized QEMU emulation + eventfd based LIO fabric driver,
> > > and got it working with Linux + MSFT guests.
> > > 
> > > Doing something similar for nvme would (potentially) be on par with
> > > current virtio-scsi+vhost-scsi small-block performance for scsi-mq
> > > guests, without the extra burden of a new command set specific virtio
> > > driver.
> > 
> > Trying to understand it.
> > Is it like below?
> > 
> >   .------------------------.   MMIO   .---------------------------------------.
> >   | Guest                  |--------> | Qemu                                  |
> >   | Unmodified NVMe driver |<-------- | NVMe device simulation(eventfd based) |
> >   '------------------------'          '---------------------------------------'
> >                                                   |          ^
> >                                       write NVMe  |          |  notify command
> >                                       command     |          |  completion
> >                                       to eventfd  |          |  to eventfd
> >                                                   v          |
> >                                       .--------------------------------------.
> >                                       | Host:                                |
> >                                       | eventfd based LIO NVMe fabric driver |
> >                                       '--------------------------------------'
> >                                                         |
> >                                                         | nvme_queue_rq()
> >                                                         v
> >                                        .--------------------------------------.
> >                                        | NVMe driver                          |
> >                                        '--------------------------------------'
> >                                                         |
> >                                                         |
> >                                                         v
> >                                        .-------------------------------------.
> >                                        | NVMe device                         |
> >                                        '-------------------------------------'
> > 
> 
> Correct.  The LIO driver on KVM host would be handling some amount of
> NVMe host interface emulation in kernel code, and would be able to
> decode nvme Read/Write/Flush operations and translate -> submit to
> existing backend drivers.

Let me call the "eventfd based LIO NVMe fabric driver" as
"tcm_eventfd_nvme"

Currently, LIO frontend driver(iscsi, fc, vhost-scsi etc) talk to LIO
backend driver(fileio, iblock etc) with SCSI commands.

Did you mean the "tcm_eventfd_nvme" driver need to translate NVMe
commands to SCSI commands and then submit to backend driver?

But I thought the future "LIO NVMe target" can support frontend driver
talk to backend driver directly with NVMe commands without translation.

Am I wrong?

> 
> As with the nvme-over-fabric case, it would be possible to do a mapping
> between backend driver queue resources for real NVMe hardware (eg:
> target_core_nvme), but since it would still be doing close to the same
> amount of software emulation for both backend driver cases, I wouldn't
> expect there to be much performance advantage over just using normal
> submit_bio().
> 
> --nab
> 

^ permalink raw reply

* RFC: virtio-peer shared memory based peer communication device
From: Claudio Fontana @ 2015-09-18 16:29 UTC (permalink / raw)
  To: Zhang, Yang Z, Michael S. Tsirkin, qemu-devel@nongnu.org,
	virtualization@lists.linux-foundation.org,
	virtio-dev@lists.oasis-open.org,
	opnfv-tech-discuss@lists.opnfv.org
  Cc: Jan Kiszka
In-Reply-To: <55EFF03A.4000109@huawei.com>

Hello,

this is a first RFC for virtio-peer 0.1, which is still very much a work in progress:

https://github.com/hw-claudio/virtio-peer/wiki

It is also available as PDF there, but the text is reproduced here for commenting:

Peer shared memory communication device (virtio-peer)

General Overview

(I recommend looking at the PDF for some clarifying pictures)

The Virtio Peer shared memory communication device (virtio-peer) is a virtual device which allows high performance low latency guest to guest communication. It uses a new queue extension feature tentatively called VIRTIO_F_WINDOW which indicates that descriptor tables, available and used rings and Queue Data reside in physical memory ranges called Windows, each identified with an unique identifier called WindowID.

Each queue is configured to belong to a specific WindowID, and during queue identification and configuration, the Physical Guest Addresses in the queue configuration fields are to be considered as offsets in octets from the start of the corresponding Window.

For example for PCI, in the virtio_pci_common_cfg structure these fields are affected:

le64 queue_desc;
le64 queue_avail;
le64 queue_used;

For MMIO instead these MMIO Device layout fields are affected:

QueueDescLow, QueueDescHigh
QueueAvailLow, QueueAvailHigh
QueueUsedLow, QueueUsedHigh

For PCI a new virtio_pci_cap of cfg type VIRTIO_PCI_CAP_WINDOW_CFG is defined.

It contains the following fields:

struct virtio_pci_window_cap {
   struct virtio_pci_cap cap;
}

This configuration structure is used to identify the existing Windows, their WindowIDs, ranges and flags. The WindowID is read from the cap.bar field. The Window starting physical guest address is calculated by starting from the contents of the PCI BAR register with index WindowID, plus the cap.offset. The Window size is read from the cap.length field.

XXX TODO XXX describe also the new MMIO registers here.
Virtqueue discovery:

We are faced with two main options with regards to virtqueue discovery in this model.

OPTION1: The simplest option is to make the previous fields read-only when using Windows, and have the virtualization environment / hypervisor provide the starting addresses of the descriptor table, avail ring and used rings, possibly allowing more flexibility on the Queue Data. OPTION2: The other option is to have the guest completely in control of the allocation decisions inside its write Window, including the virtqueue data structures starting addresses inside the Window, and provide a simple virtqueue peer initialization mechanism.

The virtio-peer device is the simplest device implementation which makes use of the Window feature, containing only two virtqueues. In addition to the Desc Table and Rings, these virtqueues also contain Queue Data areas inside the respective Windows. It uses two Windows, one for data which is read-only for the driver (read Window), and a separate one for data which is read-write for the driver (write Window).

In the Descriptor Table of each virtqueue, the field le64 addr; is added to the Queue Data address of the corresponding Window to obtain the physical guest address of a buffer. A value of length in a descriptor which exceeds the Queue Data area is invalid, and its use will cause undefined behavior.

The driver must consider the Desc Table, Avail Ring and Queue Data area of the receiveq as read-only, and the Used Ring as read-write. The Desc Table, Avail Ring and Queue Data of the receiveq will be therefore allocated inside the read Window, while the Used ring will be allocated in the write Window. The driver must consider the Desc Table, Avail Ring and Queue Data area of the transmitq as read-write, and the Used Ring as read-only. The Desc Table, Avail Ring and Queue Data of the transmitq will be therefore allocated inside the write Window, while the Used Ring will be allocated in the read Window.

Note that in OPTION1, this is done by the hypervisor, while in OPTION2, this is fully under control of the peers (with some hypervisor involvement during initialization).

5.7.1 Device ID 13

5.7.2 Virtqueues 0 receiveq (RX), 1 transmitq (TX)

5.7.3 Feature Bits Possibly VIRTIO_F_MULTICAST (NOT clear yet left out for now)

5.7.4 Device configuration layout

struct virtio_peer_config {
    le64 queue_data_offset;
    le32 queue_data_size;
    u8 queue_flags; /* read-only flags*/
    u8 queue_window_idr; /* read-only */
    u8 queue_window_idw; /* read-only */
}

The fields above are queue-specific, and are thus selected by writing to the queue selector field in the common configuration structure.

queue_data_offset is the offset from the start of the Window of the Queue Data area, queue_data_size is the size of the Queue Data area. For the Read Window, the queue_data_offset and queue_data_size are read-only. For the Write Window, the queue_data_offset and queue_data_size are read-write.

The queue_flags if a flag bitfield with the following bits already defined: (1) = FLAGS_REMOTE : this queue descr, avail, and data is read-only and initialized by the remote peer, while the used ring is initialized by the driver. If this flag is not set, this queue descr, avail, and data is read-write and initialized by the driver, while the used ring is initialized by the remote peer. queue_window_idr and queue_window_idw identify the read-window and write-window for this queue (Window IDs).

5.7.5 Device Initialization Initialization of the virtqueues follows the generic procedure for Virtqueue Initialization with the following modifications.

OPTION1: the driver needs to replace the step "Allocate and zero" of the data structures and the write to the queue configuration registers with a read from the queue configuration registers to obtain the addresses of the virtqueue data structures.

OPTION 2: for each virtqueue, the driver allocates and zeroes the data structures as usual only for the read-write data structures, while skipping the read-only queue structures, which will be initialized by the device from the point of view of the driver (they are meant to be initialized by the peer). The queue_flags configuration field can be used to easily determine which fields are to be initialized, and the queue window id registers that are used to reach the data structures.

This feature under OPTION 2 adds the requirement to enable all virtqueues before the DRIVER_OK (which is already done in practice, as usual by writing 1 to the queue_enable field). Driver attempts to read back from the queue_enable field for a queue which has not been also enabled by the remote peer will have the device return 0 (disabled) until the remote peer has also initialized its own share of the data structures for the same virtqueue as it appears in the remote peer. All the queue configuration fields which still need remote initialization (queue_desc, queue_avail, queue_used) have a reset value of 0.

When the FEATURE BIT is detected, the virtio driver will delay setting of the DRIVER_OK status for the device. When both peers have enabled the queues by writing 1 to the queue_enable fields, the driver will be notified via a configuration change interrupt (VIRTIO_PCI_ISR_CONFIG). This will allow the driver to read the necessary queue configuration fields as initialized by the remote peer, and proceed setting the DRIVER_OK status for the device to signal the completion of the initialization steps.
5.7.6 Device Operation

Data is received from the peer on the receive virtqueue. Data is transmitted to the peer using the transmit virtqueue.
5.7.6.1

OMISSIS
5.7.6.2 Transmitting data

Transmitting a chunk of data of arbitrary size is done by following the steps 3.2.1 to 3.2.1.4. The device will update the used field as described in 3.2.2.
5.7.6.2.1 Packet Transmission Interrupt

OMISSIS
5.7.6.3 Receiving data

Receiving data consists in the driver checking the receiveq available ring to be able to find the receive buffers. The procedure is the one usually performed by the device, involving update of the Used ring and a notification, as described in chapter 3.2.2
5.7.xxx: Additional notes and TODOS

Just a note: the Indirect Descriptors feature (VIRTIO_RING_F_INDIRECT) may not compatible with this feature, and thus will not be negotiated by the device (?verify)

Notification mechanisms need to be looked at in detail. Mostly we should be able to reuse the existing notification mechanisms, for OPTION2 configuration change we have identified the ISR_CONFIG notification method above.

MMIO needs to be written down.

PCI capabilities need to be checked again, and the fields in CFG_WINDOW in particular. An alternative could be to extend the pci common configuration structure for the queue- specific extensions, but seems not compatible with multiple features involving similar extensions. Need to consider MMIO, as it's less extensible.

MULTICAST is out of scope of these notes, but seems feasible with some hard work without involving copies by sharing at least the transmit buffer in the producer, but the use case with peers being added and removed dynamically requires a much more complex study. Can this be solved with multiple queues, one for each peer, and configuration change notification interrupts that can disable a queue in the producer when a peer leaves, without taking down the whole device? Would need much more study.

^ permalink raw reply

* [PULL] vhost: cleanups and fixes
From: Michael S. Tsirkin @ 2015-09-18 10:42 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: kvm, mst, netdev, pmorel, linux-kernel, virtualization

The following changes since commit 997e120843e82609c8d99a9d5714e6cf91e14cbe:

  virtio_balloon: do not change memory amount visible via /proc/meminfo (2015-09-08 13:32:11 +0300)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost.git tags/for_linus

for you to fetch changes up to ad2aa04218de9bd734d593adb0ac59854ec0cb68:

  virtio/s390: handle failures of READ_VQ_CONF ccw (2015-09-16 12:48:08 +0300)

----------------------------------------------------------------
virtio: fixes on top of 4.3-rc1

This fixes the virtio-test tool, and improves
the error handling for virtio-ccw.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

----------------------------------------------------------------
Michael S. Tsirkin (3):
      tools/virtio: fix build after 4.2 changes
      vhost: move features to core
      tools/virtio: propagate V=X to kernel build

Pierre Morel (1):
      virtio/s390: handle failures of READ_VQ_CONF ccw

 drivers/vhost/vhost.h            |  4 +++-
 tools/virtio/asm/barrier.h       |  2 ++
 tools/virtio/linux/export.h      |  3 +++
 tools/virtio/linux/kernel.h      |  8 ++++++++
 drivers/s390/virtio/virtio_ccw.c | 10 +++++++++-
 drivers/vhost/net.c              |  3 +--
 drivers/vhost/scsi.c             |  4 +---
 drivers/vhost/test.c             |  3 +++
 tools/virtio/Makefile            |  2 +-
 9 files changed, 31 insertions(+), 8 deletions(-)
 create mode 100644 tools/virtio/linux/export.h

^ permalink raw reply

* Re: [RFC PATCH 0/2] virtio nvme
From: Nicholas A. Bellinger @ 2015-09-18  0:55 UTC (permalink / raw)
  To: Ming Lin
  Cc: Minturn, Dave B, linux-nvme, Linux Virtualization, target-devel,
	Christoph Hellwig
In-Reply-To: <1442532687.4750.6.camel@ssi>

On Thu, 2015-09-17 at 16:31 -0700, Ming Lin wrote:
> On Wed, 2015-09-16 at 23:10 -0700, Nicholas A. Bellinger wrote:
> > Hi Ming & Co,
> > 
> > On Thu, 2015-09-10 at 10:28 -0700, Ming Lin wrote:
> > > On Thu, 2015-09-10 at 15:38 +0100, Stefan Hajnoczi wrote:
> > > > On Thu, Sep 10, 2015 at 6:48 AM, Ming Lin <mlin@kernel.org> wrote:
> > > > > These 2 patches added virtio-nvme to kernel and qemu,
> > > > > basically modified from virtio-blk and nvme code.
> > > > >
> > > > > As title said, request for your comments.
> > 
> > <SNIP>
> > 
> > > > 
> > > > At first glance it seems like the virtio_nvme guest driver is just
> > > > another block driver like virtio_blk, so I'm not clear why a
> > > > virtio-nvme device makes sense.
> > > 
> > > I think the future "LIO NVMe target" only speaks NVMe protocol.
> > > 
> > > Nick(CCed), could you correct me if I'm wrong?
> > > 
> > > For SCSI stack, we have:
> > > virtio-scsi(guest)
> > > tcm_vhost(or vhost_scsi, host)
> > > LIO-scsi-target
> > > 
> > > For NVMe stack, we'll have similar components:
> > > virtio-nvme(guest)
> > > vhost_nvme(host)
> > > LIO-NVMe-target
> > > 
> > 
> > I think it's more interesting to consider a 'vhost style' driver that
> > can be used with unmodified nvme host OS drivers.
> > 
> > Dr. Hannes (CC'ed) had done something like this for megasas a few years
> > back using specialized QEMU emulation + eventfd based LIO fabric driver,
> > and got it working with Linux + MSFT guests.
> > 
> > Doing something similar for nvme would (potentially) be on par with
> > current virtio-scsi+vhost-scsi small-block performance for scsi-mq
> > guests, without the extra burden of a new command set specific virtio
> > driver.
> 
> Trying to understand it.
> Is it like below?
> 
>   .------------------------.   MMIO   .---------------------------------------.
>   | Guest                  |--------> | Qemu                                  |
>   | Unmodified NVMe driver |<-------- | NVMe device simulation(eventfd based) |
>   '------------------------'          '---------------------------------------'
>                                                   |          ^
>                                       write NVMe  |          |  notify command
>                                       command     |          |  completion
>                                       to eventfd  |          |  to eventfd
>                                                   v          |
>                                       .--------------------------------------.
>                                       | Host:                                |
>                                       | eventfd based LIO NVMe fabric driver |
>                                       '--------------------------------------'
>                                                         |
>                                                         | nvme_queue_rq()
>                                                         v
>                                        .--------------------------------------.
>                                        | NVMe driver                          |
>                                        '--------------------------------------'
>                                                         |
>                                                         |
>                                                         v
>                                        .-------------------------------------.
>                                        | NVMe device                         |
>                                        '-------------------------------------'
> 

Correct.  The LIO driver on KVM host would be handling some amount of
NVMe host interface emulation in kernel code, and would be able to
decode nvme Read/Write/Flush operations and translate -> submit to
existing backend drivers.

As with the nvme-over-fabric case, it would be possible to do a mapping
between backend driver queue resources for real NVMe hardware (eg:
target_core_nvme), but since it would still be doing close to the same
amount of software emulation for both backend driver cases, I wouldn't
expect there to be much performance advantage over just using normal
submit_bio().

--nab

^ permalink raw reply

* Re: [RFC PATCH 0/2] virtio nvme
From: Ming Lin @ 2015-09-17 23:31 UTC (permalink / raw)
  To: Nicholas A. Bellinger
  Cc: Minturn, Dave B, linux-nvme, Linux Virtualization, target-devel,
	Christoph Hellwig
In-Reply-To: <1442470241.23132.77.camel@haakon3.risingtidesystems.com>

On Wed, 2015-09-16 at 23:10 -0700, Nicholas A. Bellinger wrote:
> Hi Ming & Co,
> 
> On Thu, 2015-09-10 at 10:28 -0700, Ming Lin wrote:
> > On Thu, 2015-09-10 at 15:38 +0100, Stefan Hajnoczi wrote:
> > > On Thu, Sep 10, 2015 at 6:48 AM, Ming Lin <mlin@kernel.org> wrote:
> > > > These 2 patches added virtio-nvme to kernel and qemu,
> > > > basically modified from virtio-blk and nvme code.
> > > >
> > > > As title said, request for your comments.
> 
> <SNIP>
> 
> > > 
> > > At first glance it seems like the virtio_nvme guest driver is just
> > > another block driver like virtio_blk, so I'm not clear why a
> > > virtio-nvme device makes sense.
> > 
> > I think the future "LIO NVMe target" only speaks NVMe protocol.
> > 
> > Nick(CCed), could you correct me if I'm wrong?
> > 
> > For SCSI stack, we have:
> > virtio-scsi(guest)
> > tcm_vhost(or vhost_scsi, host)
> > LIO-scsi-target
> > 
> > For NVMe stack, we'll have similar components:
> > virtio-nvme(guest)
> > vhost_nvme(host)
> > LIO-NVMe-target
> > 
> 
> I think it's more interesting to consider a 'vhost style' driver that
> can be used with unmodified nvme host OS drivers.
> 
> Dr. Hannes (CC'ed) had done something like this for megasas a few years
> back using specialized QEMU emulation + eventfd based LIO fabric driver,
> and got it working with Linux + MSFT guests.
> 
> Doing something similar for nvme would (potentially) be on par with
> current virtio-scsi+vhost-scsi small-block performance for scsi-mq
> guests, without the extra burden of a new command set specific virtio
> driver.

Trying to understand it.
Is it like below?

  .------------------------.   MMIO   .---------------------------------------.
  | Guest                  |--------> | Qemu                                  |
  | Unmodified NVMe driver |<-------- | NVMe device simulation(eventfd based) |
  '------------------------'          '---------------------------------------'
                                                  |          ^
                                      write NVMe  |          |  notify command
                                      command     |          |  completion
                                      to eventfd  |          |  to eventfd
                                                  v          |
                                      .--------------------------------------.
                                      | Host:                                |
                                      | eventfd based LIO NVMe fabric driver |
                                      '--------------------------------------'
                                                        |
                                                        | nvme_queue_rq()
                                                        v
                                       .--------------------------------------.
                                       | NVMe driver                          |
                                       '--------------------------------------'
                                                        |
                                                        |
                                                        v
                                       .-------------------------------------.
                                       | NVMe device                         |
                                       '-------------------------------------'

^ permalink raw reply

* Re: DEFINE_IDA causing memory leaks? (was Re: [PATCH 1/2] virtio: fix memory leak of virtio ida cache layers)
From: Suman Anna @ 2015-09-17 22:32 UTC (permalink / raw)
  To: Hannes Reinecke, Michael S. Tsirkin
  Cc: open-iscsi, JBottomley, linux-kernel, linux-scsi, virtualization
In-Reply-To: <55FA630A.4020707@suse.de>

On 09/17/2015 01:51 AM, Hannes Reinecke wrote:
> On 09/17/2015 07:33 AM, Michael S. Tsirkin wrote:
>> On Wed, Sep 16, 2015 at 07:29:17PM -0500, Suman Anna wrote:
>>> The virtio core uses a static ida named virtio_index_ida for
>>> assigning index numbers to virtio devices during registration.
>>> The ida core may allocate some internal idr cache layers and
>>> an ida bitmap upon any ida allocation, and all these layers are
>>> truely freed only upon the ida destruction. The virtio_index_ida
>>> is not destroyed at present, leading to a memory leak when using
>>> the virtio core as a module and atleast one virtio device is
>>> registered and unregistered.
>>>
>>> Fix this by invoking ida_destroy() in the virtio core module
>>> exit.
>>>
>>> Cc: "Michael S. Tsirkin" <mst@redhat.com>
>>> Signed-off-by: Suman Anna <s-anna@ti.com>
>>
>> Interesting.
>> Will the same apply to e.g. sd_index_ida in drivers/scsi/sd.c
>> or iscsi_sess_ida in drivers/scsi/scsi_transport_iscsi.c?

Yes, I would think so as long as they are used as modules and the
modules are loaded & unloaded with a registration in between. For
built-in, it is not an issue.

A search on lkml yielded a similar fixup patches recently from Johannes
Thumshirn (actually those are using a idr, but both ida and idr use
common logic), see
https://patchwork.kernel.org/patch/6748601/ for one such patch.

>>
>> If no, why not?
>>
>> One doesn't generally expect to have to free global variables.
>> Maybe we should forbid DEFINE_IDA in modules?
>>
>> James, could you comment on this please?
>>
> Well, looking at the code 'ida_destroy' only need to be called
> if you want/need to do a general cleanup.
> It shouldn't be required if you do correct reference counting
> on your objects, and call idr_remove() on each of them.
> 
> Unless I'm misreading something.

Yeah, I should have written a better last sentence in the first para,
its not clear in the commit description without looking at my cover
letter. The memory leak is seen only when used as module and the module
is removed with atleast one ida allocation.

regards
Suman

> 
> Seems like a topic for KS; Johannes had a larger patchset recently to
> clean up idr, which run into very much the same issues.
> 
> Cheers,
> 
> Hannes
> 

^ permalink raw reply

* Re: [RFC PATCH 0/2] virtio nvme
From: Nicholas A. Bellinger @ 2015-09-17 21:43 UTC (permalink / raw)
  To: Ming Lin
  Cc: Minturn, Dave B, linux-nvme, Linux Virtualization, target-devel,
	Christoph Hellwig
In-Reply-To: <CAF1ivSaOZwjg3OEdj+miXsWwbpf-Y-tmN7wmjZooxTPDQ5r9tg@mail.gmail.com>

On Thu, 2015-09-17 at 11:18 -0700, Ming Lin wrote:
> On Wed, Sep 16, 2015 at 11:10 PM, Nicholas A. Bellinger
> <nab@linux-iscsi.org> wrote:
> > Hi Ming & Co,
> >> >
> >> > At first glance it seems like the virtio_nvme guest driver is just
> >> > another block driver like virtio_blk, so I'm not clear why a
> >> > virtio-nvme device makes sense.
> >>
> >> I think the future "LIO NVMe target" only speaks NVMe protocol.
> >>
> >> Nick(CCed), could you correct me if I'm wrong?
> >>
> >> For SCSI stack, we have:
> >> virtio-scsi(guest)
> >> tcm_vhost(or vhost_scsi, host)
> >> LIO-scsi-target
> >>
> >> For NVMe stack, we'll have similar components:
> >> virtio-nvme(guest)
> >> vhost_nvme(host)
> >> LIO-NVMe-target
> >>
> >
> > I think it's more interesting to consider a 'vhost style' driver that
> > can be used with unmodified nvme host OS drivers.
> >
> > Dr. Hannes (CC'ed) had done something like this for megasas a few years
> > back using specialized QEMU emulation + eventfd based LIO fabric driver,
> > and got it working with Linux + MSFT guests.
> 
> Are the patches already in qemu upstream and LIO upstream?
> 
> I found you played it in 2010. Is it?
> [QEMU-KVM]: Megasas + TCM_Loop + SG_IO into Windows XP guests
> https://groups.google.com/forum/#!topic/linux-iscsi-target-dev/3hdaI6H3X0A
> 

AFAIK it was never submitted for kernel/QEMU upstream inclusion.

Dr Hannes, would you be so kind to put this code in a public repo to
grok as an example for nvme host-aware emulation..?

--nab

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox