[Intel-gfx] [RFC 08/29] drm/i915: Support vGPU guest framebuffer GEM object

Zhi Wang zhi.a.wang at intel.com
Thu Jan 28 02:21:30 PST 2016


From: Zhiyuan Lv <zhiyuan.lv at intel.com>

In the compositor mode of display, dom0/host needs to get the guest
framebuffer to do more rendering, so that the guest VM's screen can
show up in more fancy way, e.g., in an X window of dom0/host.

In order to do that, a new gem object type "gvtbuffer" is introduced
to i915. Different from normal gem object in i915, gvtbuffer does not
have its own backing storage. Instead, it borrows the page frames
of guest VM's framebuffer as its own backing storage.

>From high level, it works this way:
	a) gvt notifies kernel/userspace the guest OS page flip by
	   monitoring the related guest MMIO changes and commands.
	b) user space issue IOCTL to create gvtbuffer gem object.
	c) kernel creates the gem object, and record the guest FB base
           address (gfx address) from MMIO.
	d) When needed, the gvtbuffer will be bound to graphics
	   memory, and be used as normal gem object for rendering.

Guest framebuffer must be inside GGTT, whereas the gvtbuffer can be
in either GGTT or PPGTT, depending on the requirement of the
rendering.

Since the gvtbuffer corresponds to the guest framebuffer, which is
from guest physical memory, we may not be able to get "page struct"
for them. But i915 gem framework has had similar cases. A gem
object can have stolen memory as its backing storage. In such case,
the backing storage does not have "page struct" as well, and i915 has
handled the case in the framework well.

This patch was originally from daivid.j.cowperthwaite at intel.com, and
pretty some changes were made since then.

Signed-off-by: Zhiyuan Lv <zhiyuan.lv at intel.com>
---
 drivers/gpu/drm/i915/Makefile             |   1 +
 drivers/gpu/drm/i915/i915_dma.c           |   1 +
 drivers/gpu/drm/i915/i915_drv.h           |   3 +
 drivers/gpu/drm/i915/i915_gem_gvtbuffer.c | 247 ++++++++++++++++++++++++++++++
 include/uapi/drm/i915_drm.h               |  38 +++++
 5 files changed, 290 insertions(+)
 create mode 100644 drivers/gpu/drm/i915/i915_gem_gvtbuffer.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index d4df410..c4a6615 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -33,6 +33,7 @@ i915-y += i915_cmd_parser.o \
 	  i915_gem_stolen.o \
 	  i915_gem_tiling.o \
 	  i915_gem_userptr.o \
+	  i915_gem_gvtbuffer.o \
 	  i915_gpu_error.o \
 	  i915_trace_points.o \
 	  intel_lrc.o \
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index eca8e50..f150602 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1351,6 +1351,7 @@ const struct drm_ioctl_desc i915_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_GETPARAM, i915_gem_context_getparam_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_SETPARAM, i915_gem_context_setparam_ioctl, DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(I915_GEM_GVTBUFFER, i915_gem_gvtbuffer_ioctl, DRM_RENDER_ALLOW),
 };
 
 int i915_max_ioctl = ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index fc5ddee..d387754 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3238,6 +3238,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
 int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
 				    struct drm_file *file_priv);
 
+int i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data,
+			     struct drm_file *file);
+
 /* i915_gem_evict.c */
 int __must_check i915_gem_evict_something(struct drm_device *dev,
 					  struct i915_address_space *vm,
diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c
new file mode 100644
index 0000000..3aa2ee8
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright © 2012 - 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "i915_drv.h"
+#include "i915_trace.h"
+#include "intel_drv.h"
+#include <linux/swap.h>
+
+#include "gvt/fb_decoder.h"
+
+static int i915_gem_gvtbuffer_get_pages(struct drm_i915_gem_object *obj)
+{
+	BUG();
+	return -EINVAL;
+}
+
+static void i915_gem_gvtbuffer_put_pages(struct drm_i915_gem_object *obj)
+{
+	/* like stolen memory, this should only be called during free
+	 * after clearing pin count.
+	 */
+	sg_free_table(obj->pages);
+	kfree(obj->pages);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_gvtbuffer_ops = {
+	.get_pages = i915_gem_gvtbuffer_get_pages,
+	.put_pages = i915_gem_gvtbuffer_put_pages,
+};
+
+#define GEN8_DECODE_PTE(pte) \
+	((dma_addr_t)(((((u64)pte) >> 12) & 0x7ffffffULL) << 12))
+
+#define GEN7_DECODE_PTE(pte) \
+	((dma_addr_t)(((((u64)pte) & 0x7f0) << 28) | (u64)(pte & 0xfffff000)))
+
+static struct sg_table *
+i915_create_sg_pages_for_gvtbuffer(struct drm_device *dev,
+			     u32 start, u32 num_pages)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct sg_table *st;
+	struct scatterlist *sg;
+	int i;
+
+	st = kmalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL)
+		return NULL;
+
+	if (sg_alloc_table(st, num_pages, GFP_KERNEL)) {
+		kfree(st);
+		return NULL;
+	}
+
+	if (INTEL_INFO(dev)->gen >= 8) {
+		gen8_pte_t __iomem *gtt_entries =
+			(gen8_pte_t __iomem *)dev_priv->gtt.gsm +
+			(start >> PAGE_SHIFT);
+		for_each_sg(st->sgl, sg, num_pages, i) {
+			sg->offset = 0;
+			sg->length = PAGE_SIZE;
+			sg_dma_address(sg) =
+				GEN8_DECODE_PTE(readq(&gtt_entries[i]));
+			sg_dma_len(sg) = PAGE_SIZE;
+		}
+	} else {
+		gen6_pte_t __iomem *gtt_entries =
+			(gen6_pte_t __iomem *)dev_priv->gtt.gsm +
+			(start >> PAGE_SHIFT);
+		for_each_sg(st->sgl, sg, num_pages, i) {
+			sg->offset = 0;
+			sg->length = PAGE_SIZE;
+			sg_dma_address(sg) =
+				GEN7_DECODE_PTE(readq(&gtt_entries[i]));
+			sg_dma_len(sg) = PAGE_SIZE;
+		}
+	}
+
+	return st;
+}
+
+struct drm_i915_gem_object *
+i915_gem_object_create_gvtbuffer(struct drm_device *dev,
+				 u32 start, u32 num_pages)
+{
+	struct drm_i915_gem_object *obj;
+	obj = i915_gem_object_alloc(dev);
+	if (obj == NULL)
+		return NULL;
+
+	drm_gem_private_object_init(dev, &obj->base, num_pages << PAGE_SHIFT);
+	i915_gem_object_init(obj, &i915_gem_gvtbuffer_ops);
+
+	obj->pages = i915_create_sg_pages_for_gvtbuffer(dev, start, num_pages);
+	if (obj->pages == NULL) {
+		i915_gem_object_free(obj);
+		return NULL;
+	}
+
+	i915_gem_object_pin_pages(obj);
+	obj->cache_level = I915_CACHE_L3_LLC;
+
+	DRM_DEBUG_DRIVER("GVT_GEM: backing store base = 0x%x pages = 0x%x\n",
+			 start, num_pages);
+	return obj;
+}
+
+static int gvt_decode_information(struct drm_device *dev,
+				  struct drm_i915_gem_gvtbuffer *args)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 vmid = args->vmid;
+	struct gvt_fb_format fb;
+	struct gvt_primary_plane_format *p;
+	struct gvt_cursor_plane_format *c;
+	struct gvt_pipe_format *pipe;
+
+	if (gvt_decode_fb_format(dev_priv->vgpu.host_private_data, vmid, &fb))
+		return -EINVAL;
+
+	pipe = ((args->pipe_id >= I915_MAX_PIPES) ?
+		NULL : &fb.pipes[args->pipe_id]);
+
+	if (!pipe || !pipe->primary.enabled) {
+		DRM_DEBUG_DRIVER("GVT_GEM: Invalid pipe_id: %d\n",
+				 args->pipe_id);
+		return -EINVAL;
+	}
+
+	if ((args->plane_id) == I915_GVT_PLANE_PRIMARY) {
+		p = &pipe->primary;
+		args->enabled = p->enabled;
+		args->x_offset = p->x_offset;
+		args->y_offset = p->y_offset;
+		args->start = p->base;
+		args->width = p->width;
+		args->height = p->height;
+		args->stride = p->stride;
+		args->bpp = p->bpp;
+		args->hw_format = p->hw_format;
+		args->drm_format = p->drm_format;
+		args->tiled = p->tiled;
+	} else if ((args->plane_id) == I915_GVT_PLANE_CURSOR) {
+		c = &pipe->cursor;
+		args->enabled = c->enabled;
+		args->x_offset = c->x_hot;
+		args->y_offset = c->y_hot;
+		args->x_pos = c->x_pos;
+		args->y_pos = c->y_pos;
+		args->start = c->base;
+		args->width = c->width;
+		args->height = c->height;
+		args->stride = c->width * (c->bpp / 8);
+		args->bpp = c->bpp;
+		args->tiled = 0;
+	} else {
+		DRM_DEBUG_DRIVER("GVT_GEM: Invalid plaine_id: %d\n",
+				 args->plane_id);
+		return -EINVAL;
+	}
+
+	args->size = (((args->width * args->height * args->bpp) / 8) +
+		      (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+	if (args->start & (PAGE_SIZE - 1)) {
+		DRM_DEBUG_DRIVER("GVT_GEM: Not aligned fb start address: "
+				 "0x%x\n", args->start);
+		return -EINVAL;
+	}
+
+	if (((args->start >> PAGE_SHIFT) + args->size) >
+	    gtt_total_entries(dev_priv->gtt)) {
+		DRM_DEBUG_DRIVER("GVT: Invalid GTT offset or size\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/**
+ * Creates a new mm object that wraps some user memory.
+ */
+int
+i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data,
+			 struct drm_file *file)
+{
+	struct drm_i915_gem_gvtbuffer *args = data;
+	struct drm_i915_gem_object *obj;
+	u32 handle;
+	int ret;
+
+	if (INTEL_INFO(dev)->gen < 7)
+		return -EPERM;
+#if 0
+	if (!gvt_check_host())
+		return -EPERM;
+#endif
+	ret = gvt_decode_information(dev, args);
+	if (ret)
+		return ret;
+
+	if (args->flags & I915_GVTBUFFER_QUERY_ONLY)
+		return 0;
+
+	obj = i915_gem_object_create_gvtbuffer(dev, args->start, args->size);
+	if (!obj) {
+		DRM_DEBUG_DRIVER("GVT_GEM: Failed to create gem object"
+					" for VM FB!\n");
+		return -EINVAL;
+	}
+
+	obj->tiling_mode = args->tiled ? I915_TILING_X : I915_TILING_NONE;
+	obj->stride = args->tiled ? args->stride : 0;
+
+	ret = drm_gem_handle_create(file, &obj->base, &handle);
+	if (ret) {
+		/* TODO: Double confirm the error handling path */
+		i915_gem_object_free(obj);
+		return ret;
+	}
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_unreference(&obj->base);
+
+	args->handle = handle;
+	return 0;
+}
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 6a19371..5ba115a 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -230,6 +230,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_USERPTR		0x33
 #define DRM_I915_GEM_CONTEXT_GETPARAM	0x34
 #define DRM_I915_GEM_CONTEXT_SETPARAM	0x35
+#define DRM_I915_GEM_GVTBUFFER          0x36
 
 #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH		DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -283,6 +284,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_USERPTR			DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr)
 #define DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_GETPARAM, struct drm_i915_gem_context_param)
 #define DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param)
+#define DRM_IOCTL_I915_GEM_GVTBUFFER		DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GVTBUFFER, struct drm_i915_gem_gvtbuffer)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -1168,4 +1170,40 @@ struct drm_i915_gem_context_param {
 	__u64 value;
 };
 
+struct drm_i915_gem_gvtbuffer {
+        __u32 vmid;
+	__u32 plane_id;
+#define I915_GVT_PLANE_PRIMARY 1
+#define I915_GVT_PLANE_SPRITE 2
+#define I915_GVT_PLANE_CURSOR 3
+	__u32 pipe_id;
+	__u32 phys_pipe_id;
+	__u8  enabled;
+	__u8  tiled;
+	__u32 bpp;
+	__u32 hw_format;
+	__u32 drm_format;
+	__u32 start;
+	__u32 x_pos;
+	__u32 y_pos;
+	__u32 x_offset;
+	__u32 y_offset;
+	__u32 size;
+	__u32 width;
+	__u32 height;
+	__u32 stride;
+	__u64 user_ptr;
+	__u32 user_size;
+	__u32 flags;
+#define I915_GVTBUFFER_READ_ONLY (1<<0)
+#define I915_GVTBUFFER_QUERY_ONLY (1<<1)
+#define I915_GVTBUFFER_UNSYNCHRONIZED 0x80000000
+	/**
+	 * Returned handle for the object.
+	 *
+	 * Object handles are nonzero.
+	 */
+	__u32 handle;
+};
+
 #endif /* _UAPI_I915_DRM_H_ */
-- 
1.9.1



More information about the Intel-gfx mailing list