[PATCH] Mapping snooped user pages into the GTT

Chris Wilson chris at chris-wilson.co.uk
Fri Jan 7 08:07:36 PST 2011


I've been looking at how we can improve upload/download performance on our
UMA gfx. One under-used aspect of the IGP is its ability to blit between
snooped-and-unsnooped memory i.e. from normal ram into the GTT. Only the
BLT has this ability, almost all other functions of the GPU most be from
unsnooped memory. SNB introduces its own cache handling that we need to
exploit further, however the BLT remains and is orders of magnitude faster
for read back than an UC read by the CPU... 

It is surprisingly simple to replace the shmem_getpage with
get_user_pages and insert a user-bo into the GTT. The downside is that
this pins user-memory until it becomes inactive and so care needs to be
taken by the client to synchronize appropriately. Another issue that I've
not dealt with successfully is tracking memory protection on user pages.
Different processes will need different protection on potentially the same
PTEs. (I've also looked at implementing 2D pwrite/pread using the BLT but
I'm dissatisfied with the per-request overhead; though that too is still
many times faster for pread.)

I'm slightly concerned about pinning user-memory for undeterminable
lengths of time. However, this is no worse than any other bo, and the
pages will be reaped under memory pressure by eviction.

Any other comments or suggestions?
-Chris

---
 drivers/gpu/drm/drm_gem.c              |    3 +-
 drivers/gpu/drm/i915/Makefile          |    1 +
 drivers/gpu/drm/i915/i915_dma.c        |    3 +-
 drivers/gpu/drm/i915/i915_drv.h        |   12 +++
 drivers/gpu/drm/i915/i915_gem.c        |   93 ++++++++++++++------
 drivers/gpu/drm/i915/i915_gem_gtt.c    |    4 +-
 drivers/gpu/drm/i915/i915_gem_io.c     |   10 ++
 drivers/gpu/drm/i915/i915_gem_tiling.c |    5 +
 drivers/gpu/drm/i915/i915_gem_vmap.c   |  145 ++++++++++++++++++++++++++++++++
 include/drm/i915_drm.h                 |   16 ++++
 10 files changed, 260 insertions(+), 32 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/i915_gem_vmap.c

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index ea1c4b0..adb886a 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -425,7 +425,8 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
 void
 drm_gem_object_release(struct drm_gem_object *obj)
 {
-	fput(obj->filp);
+	if (obj->filp)
+		fput(obj->filp);
 }
 EXPORT_SYMBOL(drm_gem_object_release);
 
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 07a351f..4b901c5 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
 	  i915_gem_gtt.o \
 	  i915_gem_io.o \
 	  i915_gem_tiling.o \
+	  i915_gem_vmap.o \
 	  i915_trace_points.o \
 	  intel_display.o \
 	  intel_crt.o \
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 8def614..52efa11 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -783,7 +783,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
 		value = INTEL_INFO(dev)->gen >= 4;
 		break;
 	case I915_PARAM_HAS_2D_IO:
-		/* depends on GEM */
+	case I915_PARAM_HAS_VMAP:
 		value = dev_priv->has_gem;
 		break;
 	default:
@@ -2256,6 +2256,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 	DRM_IOCTL_DEF_DRV(I915_GEM_PREAD_2D, i915_gem_pread_2d_ioctl, DRM_UNLOCKED),
 	DRM_IOCTL_DEF_DRV(I915_GEM_PWRITE_2D, i915_gem_pwrite_2d_ioctl, DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(I915_GEM_VMAP, i915_gem_vmap_ioctl, DRM_UNLOCKED),
 };
 
 int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 64033cc..6899bde 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -695,6 +695,11 @@ typedef struct drm_i915_private {
 	struct intel_fbdev *fbdev;
 } drm_i915_private_t;
 
+struct drm_i915_gem_object_ops {
+	int (*get_pages)(struct drm_i915_gem_object *, gfp_t, u32 *offset);
+	void (*put_pages)(struct drm_i915_gem_object *);
+};
+
 struct drm_i915_gem_object {
 	struct drm_gem_object base;
 
@@ -782,6 +787,7 @@ struct drm_i915_gem_object {
 	unsigned int fenced_gpu_access:1;
 
 	struct page **pages;
+	int num_pages;
 
 	/**
 	 * DMAR support
@@ -1097,6 +1103,7 @@ void i915_gem_flush_ring(struct drm_device *dev,
 			 uint32_t flush_domains);
 struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
 						  size_t size);
+void i915_gem_object_init(struct drm_i915_gem_object *obj);
 void i915_gem_free_object(struct drm_gem_object *obj);
 int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
 				     uint32_t alignment,
@@ -1113,6 +1120,11 @@ void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
 				    struct intel_ring_buffer *ring,
 				    u32 seqno);
 
+/* i915_gem_vmap.c */
+int
+i915_gem_vmap_ioctl(struct drm_device *dev, void *data,
+		    struct drm_file *file);
+
 /**
  * Returns true if seq1 is later than seq2.
  */
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 439ad78..d529de4 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -513,6 +513,12 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
 		goto unlock;
 	}
 
+	if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+		/* XXX worth handling? */
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/* Bounds check source.  */
 	if (args->offset > obj->base.size ||
 	    args->size > obj->base.size - args->offset) {
@@ -954,6 +960,12 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
 		goto unlock;
 	}
 
+	if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+		/* XXX worth handling? */
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/* Bounds check destination. */
 	if (args->offset > obj->base.size ||
 	    args->size > obj->base.size - args->offset) {
@@ -1125,6 +1137,11 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
 	if (obj == NULL)
 		return -ENOENT;
 
+	if (to_intel_bo(obj)->agp_type == AGP_USER_CACHED_MEMORY) {
+		drm_gem_object_unreference_unlocked(obj);
+		return -EINVAL;
+	}
+
 	if (obj->size > dev_priv->mm.gtt_mappable_end) {
 		drm_gem_object_unreference_unlocked(obj);
 		return -E2BIG;
@@ -1484,25 +1501,26 @@ unlock:
 
 static int
 i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
-			      gfp_t gfpmask)
+			      gfp_t gfpmask,
+			      u32 *offset)
 {
-	int page_count, i;
 	struct address_space *mapping;
 	struct inode *inode;
 	struct page *page;
+	int i;
 
 	/* Get the list of pages out of our struct file.  They'll be pinned
 	 * at this point until we release them.
 	 */
-	page_count = obj->base.size / PAGE_SIZE;
+	obj->num_pages = obj->base.size / PAGE_SIZE;
 	BUG_ON(obj->pages != NULL);
-	obj->pages = drm_malloc_ab(page_count, sizeof(struct page *));
+	obj->pages = drm_malloc_ab(obj->num_pages, sizeof(struct page *));
 	if (obj->pages == NULL)
 		return -ENOMEM;
 
 	inode = obj->base.filp->f_path.dentry->d_inode;
 	mapping = inode->i_mapping;
-	for (i = 0; i < page_count; i++) {
+	for (i = 0; i < obj->num_pages; i++) {
 		page = read_cache_page_gfp(mapping, i,
 					   GFP_HIGHUSER |
 					   __GFP_COLD |
@@ -1517,6 +1535,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
 	if (obj->tiling_mode != I915_TILING_NONE)
 		i915_gem_object_do_bit_17_swizzle(obj);
 
+	*offset = 0;
 	return 0;
 
 err_pages:
@@ -1531,7 +1550,6 @@ err_pages:
 static void
 i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
 {
-	int page_count = obj->base.size / PAGE_SIZE;
 	int i;
 
 	BUG_ON(obj->madv == __I915_MADV_PURGED);
@@ -1542,7 +1560,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
 	if (obj->madv == I915_MADV_DONTNEED)
 		obj->dirty = 0;
 
-	for (i = 0; i < page_count; i++) {
+	for (i = 0; i < obj->num_pages; i++) {
 		if (obj->dirty)
 			set_page_dirty(obj->pages[i]);
 
@@ -1643,6 +1661,9 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj)
 {
 	struct inode *inode;
 
+	if (obj->base.filp == NULL)
+		return;
+
 	/* Our goal here is to return as much of the memory as
 	 * is possible back to the system as we are called from OOM.
 	 * To do this we must instruct the shmfs to drop all of its
@@ -2090,6 +2111,7 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
 int
 i915_gem_object_unbind(struct drm_i915_gem_object *obj)
 {
+	const struct drm_i915_gem_object_ops *ops = obj->base.driver_private;
 	int ret = 0;
 
 	if (obj->gtt_space == NULL)
@@ -2127,7 +2149,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
 		return ret;
 
 	i915_gem_gtt_unbind_object(obj);
-	i915_gem_object_put_pages_gtt(obj);
+	ops->put_pages(obj);
 
 	list_del_init(&obj->gtt_list);
 	list_del_init(&obj->mm_list);
@@ -2667,11 +2689,13 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
 			    unsigned alignment,
 			    bool map_and_fenceable)
 {
+	const struct drm_i915_gem_object_ops *ops = obj->base.driver_private;
 	struct drm_device *dev = obj->base.dev;
 	drm_i915_private_t *dev_priv = dev->dev_private;
 	struct drm_mm_node *free_space;
 	gfp_t gfpmask = __GFP_NORETRY | __GFP_NOWARN;
 	u32 size, fence_size, fence_alignment, unfenced_alignment;
+	u32 offset;
 	bool mappable, fenceable;
 	int ret;
 
@@ -2737,7 +2761,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
 		goto search_free;
 	}
 
-	ret = i915_gem_object_get_pages_gtt(obj, gfpmask);
+	ret = ops->get_pages(obj, gfpmask, &offset);
 	if (ret) {
 		drm_mm_put_block(obj->gtt_space);
 		obj->gtt_space = NULL;
@@ -2765,7 +2789,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
 
 	ret = i915_gem_gtt_bind_object(obj);
 	if (ret) {
-		i915_gem_object_put_pages_gtt(obj);
+		ops->put_pages(obj);
 		drm_mm_put_block(obj->gtt_space);
 		obj->gtt_space = NULL;
 
@@ -2787,11 +2811,11 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
 	BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
 	BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
 
-	obj->gtt_offset = obj->gtt_space->start;
+	obj->gtt_offset = obj->gtt_space->start + offset;
 
 	fenceable =
 		obj->gtt_space->size == fence_size &&
-		(obj->gtt_space->start & (fence_alignment -1)) == 0;
+		(obj->gtt_offset & (fence_alignment -1)) == 0;
 
 	mappable =
 		obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end;
@@ -2809,7 +2833,7 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj)
 	 * to GPU, and we can ignore the cache flush because it'll happen
 	 * again at bind time.
 	 */
-	if (obj->pages == NULL)
+	if (obj->pages == NULL || obj->agp_type == AGP_USER_CACHED_MEMORY)
 		return;
 
 	trace_i915_gem_object_clflush(obj);
@@ -3464,6 +3488,31 @@ unlock:
 	return ret;
 }
 
+void
+i915_gem_object_init(struct drm_i915_gem_object *obj)
+{
+	obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+	obj->base.read_domains = I915_GEM_DOMAIN_CPU;
+
+	obj->agp_type = AGP_USER_MEMORY;
+
+	obj->fence_reg = I915_FENCE_REG_NONE;
+	INIT_LIST_HEAD(&obj->mm_list);
+	INIT_LIST_HEAD(&obj->gtt_list);
+	INIT_LIST_HEAD(&obj->ring_list);
+	INIT_LIST_HEAD(&obj->exec_list);
+	INIT_LIST_HEAD(&obj->gpu_write_list);
+	obj->madv = I915_MADV_WILLNEED;
+
+	/* Avoid an unnecessary call to unbind on the first bind. */
+	obj->map_and_fenceable = true;
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_object_ops = {
+	.get_pages = i915_gem_object_get_pages_gtt,
+	.put_pages = i915_gem_object_put_pages_gtt,
+};
+
 struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
 						  size_t size)
 {
@@ -3479,22 +3528,10 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
 		return NULL;
 	}
 
-	i915_gem_info_add_obj(dev_priv, size);
+	obj->base.driver_private = (void *)&i915_gem_object_ops;
 
-	obj->base.write_domain = I915_GEM_DOMAIN_CPU;
-	obj->base.read_domains = I915_GEM_DOMAIN_CPU;
-
-	obj->agp_type = AGP_USER_MEMORY;
-	obj->base.driver_private = NULL;
-	obj->fence_reg = I915_FENCE_REG_NONE;
-	INIT_LIST_HEAD(&obj->mm_list);
-	INIT_LIST_HEAD(&obj->gtt_list);
-	INIT_LIST_HEAD(&obj->ring_list);
-	INIT_LIST_HEAD(&obj->exec_list);
-	INIT_LIST_HEAD(&obj->gpu_write_list);
-	obj->madv = I915_MADV_WILLNEED;
-	/* Avoid an unnecessary call to unbind on the first bind. */
-	obj->map_and_fenceable = true;
+	i915_gem_object_init(obj);
+	i915_gem_info_add_obj(dev_priv, size);
 
 	return obj;
 }
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 92161bb..429f529 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -64,7 +64,7 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj)
 
 	if (dev_priv->mm.gtt->needs_dmar) {
 		ret = intel_gtt_map_memory(obj->pages,
-					   obj->base.size >> PAGE_SHIFT,
+					   obj->num_pages,
 					   &obj->sg_list,
 					   &obj->num_sg);
 		if (ret != 0)
@@ -76,7 +76,7 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj)
 					    obj->agp_type);
 	} else
 		intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
-				       obj->base.size >> PAGE_SHIFT,
+				       obj->num_pages,
 				       obj->pages,
 				       obj->agp_type);
 
diff --git a/drivers/gpu/drm/i915/i915_gem_io.c b/drivers/gpu/drm/i915/i915_gem_io.c
index 6c1def1..e83b8e3 100644
--- a/drivers/gpu/drm/i915/i915_gem_io.c
+++ b/drivers/gpu/drm/i915/i915_gem_io.c
@@ -549,6 +549,11 @@ i915_gem_pwrite_2d_ioctl(struct drm_device *dev,
 		goto unlock;
 	}
 
+	if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+		ret = -EINVAL;
+		goto unref;
+	}
+
 	/* Bounds check destination. */
 	offset = args->dst_x * args->cpp + args->dst_y * args->dst_stride;
 	size = args->dst_stride * (args->height-1) + args->width * args->cpp;
@@ -961,6 +966,11 @@ i915_gem_pread_2d_ioctl(struct drm_device *dev,
 		goto unlock;
 	}
 
+	if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+		ret = -EINVAL;
+		goto unref;
+	}
+
 	/* Bounds check source. */
 	offset = args->src_x * args->cpp + args->src_y * args->src_stride;
 	size = args->src_stride * (args->height-1) + args->width * args->cpp;
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 22a32b9..3a9c88e 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -294,6 +294,11 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
 	if (obj == NULL)
 		return -ENOENT;
 
+	if (obj->agp_type == AGP_USER_CACHED_MEMORY) {
+		drm_gem_object_unreference_unlocked(&obj->base);
+		return -EINVAL;
+	}
+
 	if (!i915_tiling_ok(dev,
 			    args->stride, obj->base.size, args->tiling_mode)) {
 		drm_gem_object_unreference_unlocked(&obj->base);
diff --git a/drivers/gpu/drm/i915/i915_gem_vmap.c b/drivers/gpu/drm/i915/i915_gem_vmap.c
new file mode 100644
index 0000000..780a514
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem_vmap.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright © 2010 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 "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "i915_trace.h"
+#include "intel_drv.h"
+#include <linux/swap.h>
+
+struct i915_gem_vmap_object {
+	struct drm_i915_gem_object gem;
+	uintptr_t user_ptr;
+	int read_only;
+};
+
+static struct i915_gem_vmap_object *to_vmap_object(struct drm_i915_gem_object *obj)
+{
+	return container_of(obj, struct i915_gem_vmap_object, gem);
+}
+
+static int
+i915_gem_vmap_get_pages(struct drm_i915_gem_object *obj, gfp_t gfp, u32 *offset)
+{
+	struct i915_gem_vmap_object *vmap = to_vmap_object(obj);
+	loff_t first_data_page, last_data_page;
+	int pinned_pages, i;
+
+	if (!access_ok(vmap->read_only ? VERIFY_READ : VERIFY_WRITE,
+		       (char __user *)vmap->user_ptr,
+		       vmap->gem.base.size))
+		return -EFAULT;
+
+	first_data_page = vmap->user_ptr / PAGE_SIZE;
+	last_data_page = (vmap->user_ptr + vmap->gem.base.size - 1) / PAGE_SIZE;
+	vmap->gem.num_pages = last_data_page - first_data_page + 1;
+
+	vmap->gem.pages = drm_malloc_ab(vmap->gem.num_pages,
+				       	sizeof(struct page *));
+	if (vmap->gem.pages == NULL)
+		return -ENOMEM;
+
+	pinned_pages = get_user_pages_fast(vmap->user_ptr,
+					   vmap->gem.num_pages,
+					   !vmap->read_only,
+					   vmap->gem.pages);
+	if (pinned_pages < vmap->gem.num_pages) {
+		for (i = 0; i < pinned_pages; i++)
+			page_cache_release(vmap->gem.pages[i]);
+		drm_free_large(vmap->gem.pages);
+		return -EFAULT;
+	}
+
+	*offset = vmap->user_ptr & ~PAGE_MASK;
+	return 0;
+}
+
+static void
+i915_gem_vmap_put_pages(struct drm_i915_gem_object *obj)
+{
+	int i;
+
+	for (i = 0; i < obj->num_pages; i++) {
+		if (obj->dirty)
+			set_page_dirty(obj->pages[i]);
+
+		mark_page_accessed(obj->pages[i]);
+		page_cache_release(obj->pages[i]);
+	}
+
+	obj->dirty = 0;
+	drm_free_large(obj->pages);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_vmap_ops = {
+	.get_pages = i915_gem_vmap_get_pages,
+	.put_pages = i915_gem_vmap_put_pages,
+};
+
+/**
+ * Creates a new mm object that wraps some user memory.
+ */
+int
+i915_gem_vmap_ioctl(struct drm_device *dev, void *data,
+		    struct drm_file *file)
+{
+	struct drm_i915_gem_vmap *args = data;
+	struct i915_gem_vmap_object *obj;
+	int ret;
+	u32 handle;
+
+	/* Allocate the new object */
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (obj == NULL)
+		return -ENOMEM;
+
+	obj->gem.base.driver_private = (void *)&i915_gem_vmap_ops;
+
+	obj->gem.base.dev = dev;
+	obj->gem.base.size = args->user_size;
+
+	kref_init(&obj->gem.base.refcount);
+	atomic_set(&obj->gem.base.handle_count, 0);
+
+	i915_gem_object_init(&obj->gem);
+	obj->gem.agp_type = AGP_USER_CACHED_MEMORY;
+
+	obj->user_ptr = args->user_ptr;
+	obj->read_only = args->flags & I915_VMAP_READ_ONLY;
+
+	ret = drm_gem_handle_create(file, &obj->gem.base, &handle);
+	if (ret) {
+		drm_gem_object_release(&obj->gem.base);
+		kfree(obj);
+		return ret;
+	}
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_unreference(&obj->gem.base);
+
+	args->handle = handle;
+	return 0;
+}
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index d3c93be..3b8b7f9 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -200,6 +200,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_EXECBUFFER2	0x29
 #define DRM_I915_GEM_PREAD_2D	0x2a
 #define DRM_I915_GEM_PWRITE_2D	0x2b
+#define DRM_I915_GEM_VMAP	0x2c
 
 #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)
@@ -243,6 +244,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_OVERLAY_ATTRS	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
 #define DRM_IOCTL_I915_GEM_PREAD_2D	DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD_2D, struct drm_i915_gem_pread_2d)
 #define DRM_IOCTL_I915_GEM_PWRITE_2D	DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE_2D, struct drm_i915_gem_pwrite_2d)
+#define DRM_IOCTL_I915_GEM_VMAP	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_VMAP, struct drm_i915_gem_vmap)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -295,6 +297,7 @@ typedef struct drm_i915_irq_wait {
 #define I915_PARAM_HAS_COHERENT_RINGS	 13
 #define I915_PARAM_HAS_EXEC_CONSTANTS	 14
 #define I915_PARAM_HAS_2D_IO		 15
+#define I915_PARAM_HAS_VMAP		 16
 
 typedef struct drm_i915_getparam {
 	int param;
@@ -392,6 +395,19 @@ struct drm_i915_gem_create {
 	__u32 pad;
 };
 
+struct drm_i915_gem_vmap {
+	__u64 user_ptr;
+	__u32 user_size;
+	__u32 flags;
+#define I915_VMAP_READ_ONLY 0x1
+	/**
	 * Returned handle for the object.
+	 *
+	 * Object handles are nonzero.
+	 */
+	__u32 handle;
+};
+
 struct drm_i915_gem_pread {
 	/** Handle for the object being read. */
 	__u32 handle;
-- 
1.7.2.3



More information about the dri-devel mailing list