[PATCH 24/24] onstack

Chris Wilson chris at chris-wilson.co.uk
Sat May 20 17:51:15 UTC 2017


---
 drivers/gpu/drm/i915/i915_drv.c            |   4 +-
 drivers/gpu/drm/i915/i915_drv.h            |   6 +-
 drivers/gpu/drm/i915/i915_gem_execbuffer.c | 737 +++++++++++++----------------
 3 files changed, 327 insertions(+), 420 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index e3c6d052d1c9..1f26273bc4f6 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2595,8 +2595,8 @@ static const struct drm_ioctl_desc i915_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(I915_VBLANK_SWAP, drm_noop, DRM_AUTH),
 	DRM_IOCTL_DEF_DRV(I915_HWS_ADDR, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF_DRV(I915_GEM_INIT, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-	DRM_IOCTL_DEF_DRV(I915_GEM_EXECBUFFER, i915_gem_execbuffer, DRM_AUTH),
-	DRM_IOCTL_DEF_DRV(I915_GEM_EXECBUFFER2_WR, i915_gem_execbuffer2, DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(I915_GEM_EXECBUFFER, drm_noop, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(I915_GEM_EXECBUFFER2_WR, i915_gem_execbuffer2_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(I915_GEM_PIN, i915_gem_reject_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF_DRV(I915_GEM_UNPIN, i915_gem_reject_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF_DRV(I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 10beae1a13c8..973da1401fa1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3189,10 +3189,8 @@ int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
 			      struct drm_file *file_priv);
 int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
 			     struct drm_file *file_priv);
-int i915_gem_execbuffer(struct drm_device *dev, void *data,
-			struct drm_file *file_priv);
-int i915_gem_execbuffer2(struct drm_device *dev, void *data,
-			 struct drm_file *file_priv);
+int i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv);
 int i915_gem_busy_ioctl(struct drm_device *dev, void *data,
 			struct drm_file *file_priv);
 int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index a98cc2a067f6..f602ac4a7ad8 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -47,16 +47,16 @@ enum {
 #define DBG_FORCE_RELOC 0 /* choose one of the above! */
 };
 
-#define __EXEC_OBJECT_HAS_REF		BIT(31)
-#define __EXEC_OBJECT_HAS_PIN		BIT(30)
-#define __EXEC_OBJECT_HAS_FENCE		BIT(29)
-#define __EXEC_OBJECT_NEEDS_MAP		BIT(28)
-#define __EXEC_OBJECT_NEEDS_BIAS	BIT(27)
-#define __EXEC_OBJECT_INTERNAL_FLAGS	(~0u << 27) /* all of the above */
+#define __EXEC_OBJECT_VALIDATED		BIT(31)
+#define __EXEC_OBJECT_HAS_REF		BIT(30)
+#define __EXEC_OBJECT_HAS_PIN		BIT(29)
+#define __EXEC_OBJECT_HAS_FENCE		BIT(28)
+#define __EXEC_OBJECT_NEEDS_MAP		BIT(27)
+#define __EXEC_OBJECT_NEEDS_BIAS	BIT(26)
+#define __EXEC_OBJECT_INTERNAL_FLAGS	(~0u << 26) /* all of the above */
 #define __EXEC_OBJECT_RESERVED (__EXEC_OBJECT_HAS_PIN | __EXEC_OBJECT_HAS_FENCE)
 
 #define __EXEC_HAS_RELOC	BIT(31)
-#define __EXEC_VALIDATED	BIT(30)
 #define UPDATE			PIN_OFFSET_FIXED
 
 #define BATCH_OFFSET_BIAS (256*1024)
@@ -190,9 +190,9 @@ struct i915_execbuffer {
 	struct drm_i915_private *i915; /** i915 backpointer */
 	struct drm_file *file; /** per-file lookup tables and limits */
 	struct drm_i915_gem_execbuffer2 *args; /** ioctl parameters */
-	struct drm_i915_gem_exec_object2 *exec; /** ioctl execobj[] */
 	struct i915_vma **vma;
 	unsigned int *flags;
+	u64 *offsets;
 
 	struct intel_engine_cs *engine; /** engine to queue the request to */
 	struct i915_gem_context *ctx; /** context for building the request */
@@ -309,7 +309,9 @@ static bool
 eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
 		 const struct i915_vma *vma)
 {
-	if (!(*vma->exec_flags & __EXEC_OBJECT_HAS_PIN))
+	unsigned int flags = *vma->exec_flags;
+
+	if (!(flags & __EXEC_OBJECT_HAS_PIN))
 		return true;
 
 	if (vma->node.size < entry->pad_to_size)
@@ -318,15 +320,15 @@ eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
 	if (entry->alignment && !IS_ALIGNED(vma->node.start, entry->alignment))
 		return true;
 
-	if (entry->flags & EXEC_OBJECT_PINNED &&
+	if (flags & EXEC_OBJECT_PINNED &&
 	    vma->node.start != entry->offset)
 		return true;
 
-	if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS &&
+	if (flags & __EXEC_OBJECT_NEEDS_BIAS &&
 	    vma->node.start < BATCH_OFFSET_BIAS)
 		return true;
 
-	if (!(entry->flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS) &&
+	if (!(flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS) &&
 	    (vma->node.start + vma->node.size - 1) >> 32)
 		return true;
 
@@ -338,6 +340,7 @@ eb_pin_vma(struct i915_execbuffer *eb,
 	   const struct drm_i915_gem_exec_object2 *entry,
 	   struct i915_vma *vma)
 {
+	unsigned int ctl = *vma->exec_flags;
 	u64 flags;
 
 	if (vma->node.size)
@@ -346,13 +349,13 @@ eb_pin_vma(struct i915_execbuffer *eb,
 		flags = entry->offset & PIN_OFFSET_MASK;
 
 	flags |= PIN_USER | PIN_NOEVICT | PIN_OFFSET_FIXED;
-	if (unlikely(entry->flags & EXEC_OBJECT_NEEDS_GTT))
+	if (unlikely(ctl & EXEC_OBJECT_NEEDS_GTT))
 		flags |= PIN_GLOBAL;
 
 	if (unlikely(i915_vma_pin(vma, 0, 0, flags)))
 		return;
 
-	if (unlikely(entry->flags & EXEC_OBJECT_NEEDS_FENCE)) {
+	if (unlikely(ctl & EXEC_OBJECT_NEEDS_FENCE)) {
 		if (unlikely(i915_vma_get_fence(vma))) {
 			i915_vma_unpin(vma);
 			return;
@@ -422,8 +425,8 @@ eb_validate_vma(struct i915_execbuffer *eb,
 	}
 
 	if (unlikely(vma->exec_flags)) {
-		DRM_DEBUG("Object [handle %d, index %d] appears more than once in object list\n",
-			  entry->handle, (int)(entry - eb->exec));
+		DRM_DEBUG("Object [handle %d] appears more than once in object list\n",
+			  entry->handle);
 		return -EINVAL;
 	}
 
@@ -431,14 +434,16 @@ eb_validate_vma(struct i915_execbuffer *eb,
 }
 
 static int
-eb_add_vma(struct i915_execbuffer *eb, unsigned int i, struct i915_vma *vma)
+eb_add_vma(struct i915_execbuffer *eb,
+	   unsigned int i, struct i915_vma *vma,
+	   struct drm_i915_gem_exec_object2 *entry,
+	   unsigned int flags)
 {
-	struct drm_i915_gem_exec_object2 *entry = &eb->exec[i];
 	int err;
 
 	GEM_BUG_ON(i915_vma_is_closed(vma));
 
-	if (!(eb->args->flags & __EXEC_VALIDATED)) {
+	if (!(flags & __EXEC_OBJECT_VALIDATED)) {
 		err = eb_validate_vma(eb, entry, vma);
 		if (unlikely(err))
 			return err;
@@ -473,7 +478,8 @@ eb_add_vma(struct i915_execbuffer *eb, unsigned int i, struct i915_vma *vma)
 	 * to find the right target VMA when doing relocations.
 	 */
 	eb->vma[i] = vma;
-	eb->flags[i] = entry->flags;
+	eb->offsets[i] = 0;
+	eb->flags[i] = entry->flags | flags;
 	vma->exec_flags = &eb->flags[i];
 
 	err = 0;
@@ -486,8 +492,8 @@ eb_add_vma(struct i915_execbuffer *eb, unsigned int i, struct i915_vma *vma)
 			err = i915_vma_unbind(vma);
 	} else {
 		if (entry->offset != vma->node.start) {
-			entry->offset = vma->node.start | UPDATE;
 			eb->args->flags |= __EXEC_HAS_RELOC;
+			eb->offsets[i] = vma->node.start | UPDATE;
 		}
 	}
 	return err;
@@ -510,15 +516,22 @@ static inline int use_cpu_reloc(const struct reloc_cache *cache,
 		obj->cache_level != I915_CACHE_NONE);
 }
 
-static int eb_reserve_vma(struct i915_execbuffer *eb, struct i915_vma *vma)
+static int eb_reserve_vma(const struct i915_execbuffer *eb,
+			  struct i915_vma *vma)
 {
-	struct drm_i915_gem_exec_object2 *entry =
-		&eb->exec[vma->exec_flags - eb->flags];
+	unsigned long idx = vma->exec_flags - eb->flags;
+	const struct drm_i915_gem_exec_object2 *user =
+		u64_to_user_ptr(eb->args->buffers_ptr);
+	struct drm_i915_gem_exec_object2 entry;
+	unsigned int ctl = *vma->exec_flags;
 	u64 flags;
 	int err;
 
+	if (__copy_from_user(&entry, &user[idx], sizeof(entry)))
+		return -EFAULT;
+
 	flags = PIN_USER | PIN_NONBLOCK;
-	if (entry->flags & EXEC_OBJECT_NEEDS_GTT)
+	if (ctl & EXEC_OBJECT_NEEDS_GTT)
 		flags |= PIN_GLOBAL;
 
 	if (!drm_mm_node_allocated(&vma->node)) {
@@ -526,34 +539,35 @@ static int eb_reserve_vma(struct i915_execbuffer *eb, struct i915_vma *vma)
 		 * Wa32bitGeneralStateOffset & Wa32bitInstructionBaseOffset,
 		 * limit address to the first 4GBs for unflagged objects.
 		 */
-		if (!(entry->flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
+		if (!(ctl & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
 			flags |= PIN_ZONE_4G;
 
-		if (entry->flags & __EXEC_OBJECT_NEEDS_MAP)
+		if (ctl & __EXEC_OBJECT_NEEDS_MAP)
 			flags |= PIN_MAPPABLE;
 
-		if (entry->flags & EXEC_OBJECT_PINNED) {
-			flags |= entry->offset | PIN_OFFSET_FIXED;
+		if (ctl & EXEC_OBJECT_PINNED) {
+			flags |= entry.offset | PIN_OFFSET_FIXED;
 			/* force overlapping PINNED checks */
 			flags &= ~PIN_NONBLOCK;
-		} else if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS) {
+		} else if (ctl & __EXEC_OBJECT_NEEDS_BIAS) {
 			flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS;
 		}
 	}
 
-	err = i915_vma_pin(vma, entry->pad_to_size, entry->alignment, flags);
+	err = i915_vma_pin(vma, entry.pad_to_size, entry.alignment, flags);
 	if (err)
 		return err;
 
-	if (entry->offset != vma->node.start) {
-		entry->offset = vma->node.start | UPDATE;
+	eb->offsets[idx] = 0;
+	if (entry.offset != vma->node.start) {
 		eb->args->flags |= __EXEC_HAS_RELOC;
+		eb->offsets[idx] = vma->node.start | UPDATE;
 	}
 
 	*vma->exec_flags |= __EXEC_OBJECT_HAS_PIN;
-	GEM_BUG_ON(eb_vma_misplaced(entry, vma));
+	GEM_BUG_ON(eb_vma_misplaced(&entry, vma));
 
-	if (unlikely(entry->flags & EXEC_OBJECT_NEEDS_FENCE)) {
+	if (unlikely(ctl & EXEC_OBJECT_NEEDS_FENCE)) {
 		err = i915_vma_get_fence(vma);
 		if (unlikely(err)) {
 			i915_vma_unpin(vma);
@@ -685,15 +699,15 @@ static int eb_select_context(struct i915_execbuffer *eb)
 	return 0;
 }
 
-static int eb_lookup_vmas(struct i915_execbuffer *eb)
+static int eb_lookup_vmas(struct i915_execbuffer *eb, unsigned int flags)
 {
-#define INTERMEDIATE BIT(0)
-	const unsigned int count = eb->buffer_count;
+#define N_EXEC(x) ((x) / sizeof(struct drm_i915_gem_exec_object2))
+	struct drm_i915_gem_exec_object2 stack[N_EXEC(768)];
+	const struct drm_i915_gem_exec_object2 *user =
+		u64_to_user_ptr(eb->args->buffers_ptr);
 	struct i915_gem_context_vma_lut *lut = &eb->ctx->vma_lut;
-	struct i915_vma *vma;
-	struct idr *idr;
-	unsigned int i;
-	int slow_pass = -1;
+	struct drm_i915_gem_object *uninitialized_var(obj);
+	unsigned int i, remain;
 	int err;
 
 	INIT_LIST_HEAD(&eb->relocs);
@@ -703,105 +717,74 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
 		flush_work(&lut->resize);
 	GEM_BUG_ON(lut->ht_size & I915_CTX_RESIZE_IN_PROGRESS);
 
-	for (i = 0; i < count; i++) {
-		hlist_for_each_entry(vma,
-				     ht_head(lut, eb->exec[i].handle),
-				     ctx_node) {
-			if (vma->ctx_handle != eb->exec[i].handle)
-				continue;
-
-			err = eb_add_vma(eb, i, vma);
-			if (unlikely(err))
-				return err;
-			goto next_vma;
-		}
-
-		if (slow_pass < 0)
-			slow_pass = i;
-
-		eb->vma[i] = NULL;
+	i = 0;
+	remain = eb->buffer_count;
+	do {
+		struct drm_i915_gem_exec_object2 *exec = stack;
+		unsigned int count =
+			min_t(unsigned int, remain, ARRAY_SIZE(stack));
 
-next_vma: ;
-	}
+		GEM_BUG_ON(!count);
 
-	if (slow_pass < 0)
-		goto out;
+		if (__copy_from_user(exec, user, count * sizeof(exec[0]))) {
+			err = -EFAULT;
+			goto err;
+		}
 
-	spin_lock(&eb->file->table_lock);
-	/*
-	 * Grab a reference to the object and release the lock so we can lookup
-	 * or create the VMA without using GFP_ATOMIC
-	 */
-	idr = &eb->file->object_idr;
-	for (i = slow_pass; i < count; i++) {
-		struct drm_i915_gem_object *obj;
+		user += count;
+		remain -= count;
 
-		if (eb->vma[i])
-			continue;
+		do {
+			struct hlist_head *hl = ht_head(lut, exec->handle);
+			struct i915_vma *vma;
 
-		obj = to_intel_bo(idr_find(idr, eb->exec[i].handle));
-		if (unlikely(!obj)) {
-			spin_unlock(&eb->file->table_lock);
-			DRM_DEBUG("Invalid object handle %d at index %d\n",
-				  eb->exec[i].handle, i);
-			err = -ENOENT;
-			goto err;
-		}
+			hlist_for_each_entry(vma, hl, ctx_node) {
+				GEM_BUG_ON(vma->ctx != eb->ctx);
 
-		eb->vma[i] =
-			(struct i915_vma *)ptr_pack_bits(obj, INTERMEDIATE, 1);
-	}
-	spin_unlock(&eb->file->table_lock);
+				if (vma->ctx_handle != exec->handle)
+					continue;
 
-	for (i = slow_pass; i < count; i++) {
-		struct drm_i915_gem_object *obj;
-		unsigned int is_obj;
+				goto add_vma;
+			}
 
-		obj = (typeof(obj))ptr_unpack_bits(eb->vma[i], &is_obj, 1);
-		if (!is_obj)
-			continue;
+			obj = i915_gem_object_lookup(eb->file, exec->handle);
+			if (unlikely(!obj)) {
+				err = -ENOENT;
+				goto err;
+			}
 
-		/*
-		 * NOTE: We can leak any vmas created here when something fails
-		 * later on. But that's no issue since vma_unbind can deal with
-		 * vmas which are not actually bound. And since only
-		 * lookup_or_create exists as an interface to get at the vma
-		 * from the (obj, vm) we don't run the risk of creating
-		 * duplicated vmas for the same vm.
-		 */
-		vma = i915_vma_instance(obj, eb->vm, NULL);
-		if (unlikely(IS_ERR(vma))) {
-			DRM_DEBUG("Failed to lookup VMA\n");
-			err = PTR_ERR(vma);
-			goto err;
-		}
+			flags |= __EXEC_OBJECT_HAS_REF;
 
-		/* First come, first served */
-		if (!vma->ctx) {
-			vma->ctx = eb->ctx;
-			vma->ctx_handle = eb->exec[i].handle;
-			hlist_add_head(&vma->ctx_node,
-				       ht_head(lut, eb->exec[i].handle));
-			lut->ht_count++;
-			lut->ht_size |= I915_CTX_RESIZE_IN_PROGRESS;
-			if (i915_vma_is_ggtt(vma)) {
-				GEM_BUG_ON(obj->vma_hashed);
-				obj->vma_hashed = vma;
+			vma = i915_vma_instance(obj, eb->vm, NULL);
+			if (unlikely(IS_ERR(vma))) {
+				err = PTR_ERR(vma);
+				goto err;
 			}
 
-			i915_vma_get(vma);
-		}
+			/* First come, first served */
+			if (!vma->ctx) {
+				vma->ctx = eb->ctx;
+				vma->ctx_handle = exec->handle;
+				hlist_add_head(&vma->ctx_node, hl);
+				lut->ht_count++;
+				lut->ht_size |= I915_CTX_RESIZE_IN_PROGRESS;
+				if (i915_vma_is_ggtt(vma)) {
+					GEM_BUG_ON(obj->vma_hashed);
+					obj->vma_hashed = vma;
+				}
+
+				/* transfer ref to ctx */
+				flags &= ~__EXEC_OBJECT_HAS_REF;
+			}
 
-		err = eb_add_vma(eb, i, vma);
-		if (unlikely(err))
-			goto err;
+add_vma:
+			err = eb_add_vma(eb, i, vma, exec, flags);
+			if (unlikely(err))
+				goto err;
 
-		/* Only after we validated the user didn't use our bits */
-		if (vma->ctx != eb->ctx) {
-			i915_vma_get(vma);
-			eb->flags[i] |= __EXEC_OBJECT_HAS_REF;
-		}
-	}
+			flags &= ~__EXEC_OBJECT_HAS_REF;
+		} while (i++, exec++, --count);
+	} while (remain);
 
 	if (lut->ht_size & I915_CTX_RESIZE_IN_PROGRESS) {
 		if (ht_needs_resize(lut))
@@ -810,7 +793,6 @@ next_vma: ;
 			lut->ht_size &= ~I915_CTX_RESIZE_IN_PROGRESS;
 	}
 
-out:
 	/* take note of the batch buffer before we might reorder the lists */
 	i = eb_batch_index(eb);
 	eb->batch = eb->vma[i];
@@ -825,22 +807,19 @@ next_vma: ;
 	 * Note that actual hangs have only been observed on gen7, but for
 	 * paranoia do it everywhere.
 	 */
-	if (!(eb->exec[i].flags & EXEC_OBJECT_PINNED))
-		eb->exec[i].flags |= __EXEC_OBJECT_NEEDS_BIAS;
+	if (!(eb->flags[i] & EXEC_OBJECT_PINNED))
+		eb->flags[i] |= __EXEC_OBJECT_NEEDS_BIAS;
 	if (eb->reloc_cache.has_fence)
-		eb->exec[i].flags |= EXEC_OBJECT_NEEDS_FENCE;
+		eb->flags[i] |= EXEC_OBJECT_NEEDS_FENCE;
 
-	eb->args->flags |= __EXEC_VALIDATED;
 	return eb_reserve(eb);
 
 err:
-	for (i = slow_pass; i < count; i++) {
-		if (ptr_unmask_bits(eb->vma[i], 1))
-			eb->vma[i] = NULL;
-	}
+	if (flags & __EXEC_OBJECT_HAS_REF)
+		i915_gem_object_put(obj);
 	lut->ht_size &= ~I915_CTX_RESIZE_IN_PROGRESS;
+	eb->vma[i] = NULL;
 	return err;
-#undef INTERMEDIATE
 }
 
 static struct i915_vma *
@@ -872,6 +851,9 @@ static void eb_reset_vmas(const struct i915_execbuffer *eb)
 		struct i915_vma *vma = eb->vma[i];
 		unsigned int *flags = &eb->flags[i];
 
+		if (!vma)
+			break;
+
 		eb_unreserve_vma(vma, flags);
 		vma->exec_flags = NULL;
 
@@ -891,17 +873,14 @@ static void eb_release_vmas(const struct i915_execbuffer *eb)
 	const unsigned int count = eb->buffer_count;
 	unsigned int i;
 
-	if (!eb->exec)
-		return;
-
 	for (i = 0; i < count; i++) {
 		struct i915_vma *vma = eb->vma[i];
 		unsigned int *flags = &eb->flags[i];
 
 		if (!vma)
-			continue;
+			break;
 
-		GEM_BUG_ON(vma->exec_flags != &eb->flags[i]);
+		GEM_BUG_ON(vma->exec_flags != flags);
 
 		eb_unreserve_vma(vma, flags);
 		vma->exec_flags = NULL;
@@ -1451,12 +1430,21 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct i915_vma *vma)
 #define N_RELOC(x) ((x) / sizeof(struct drm_i915_gem_relocation_entry))
 	struct drm_i915_gem_relocation_entry stack[N_RELOC(512)];
 	struct drm_i915_gem_relocation_entry __user *urelocs;
-	const struct drm_i915_gem_exec_object2 *entry =
-		&eb->exec[vma->exec_flags - eb->flags];
+	const struct drm_i915_gem_exec_object2 *exec =
+		u64_to_user_ptr(eb->args->buffers_ptr);
+	unsigned int idx = vma->exec_flags - eb->flags;
 	unsigned int remain;
+	u64 uptr;
+	int err;
+
+	err = __get_user(uptr, &exec[idx].relocs_ptr);
+	if (unlikely(err))
+		return err;
+
+	err = __get_user(remain, &exec[idx].relocation_count);
+	if (unlikely(err))
+		return err;
 
-	urelocs = u64_to_user_ptr(entry->relocs_ptr);
-	remain = entry->relocation_count;
 	if (unlikely(remain > N_RELOC(ULONG_MAX)))
 		return -EINVAL;
 
@@ -1466,6 +1454,7 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct i915_vma *vma)
 	 * the updated relocation values.
 	 */
 
+	urelocs = u64_to_user_ptr(uptr);
 	do {
 		struct drm_i915_gem_relocation_entry *r = stack;
 		unsigned int count =
@@ -1532,18 +1521,21 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct i915_vma *vma)
 	return remain;
 }
 
+struct eb_relocs {
+	struct drm_i915_gem_relocation_entry *relocs;
+	unsigned int count;
+};
+
 static int
-eb_relocate_vma_slow(struct i915_execbuffer *eb, struct i915_vma *vma)
+eb_relocate_vma_slow(struct i915_execbuffer *eb, struct i915_vma *vma,
+		     struct eb_relocs *r)
 {
-	const struct drm_i915_gem_exec_object2 *entry =
-		&eb->exec[vma->exec_flags - eb->flags];
-	struct drm_i915_gem_relocation_entry *relocs =
-		u64_to_ptr(typeof(*relocs), entry->relocs_ptr);
-	unsigned int i;
+	struct drm_i915_gem_relocation_entry *relocs = r->relocs;
+	unsigned int count = r->count;
 	int err;
 
-	for (i = 0; i < entry->relocation_count; i++) {
-		u64 offset = eb_relocate_entry(eb, vma, &relocs[i]);
+	while (count--) {
+		u64 offset = eb_relocate_entry(eb, vma, relocs++);
 
 		if ((s64)offset < 0) {
 			err = (int)offset;
@@ -1570,59 +1562,67 @@ static int eb_relocate(struct i915_execbuffer *eb)
 	return 0;
 }
 
-static int check_relocations(const struct drm_i915_gem_exec_object2 *entry)
+static int check_relocations(unsigned int count, const void __user *addr)
 {
-	const char __user *addr, *end;
+	const void __user *end;
 	unsigned long size;
 	char __maybe_unused c;
 
-	size = entry->relocation_count;
-	if (size == 0)
-		return 0;
-
-	if (size > N_RELOC(ULONG_MAX))
+	if (count > N_RELOC(ULONG_MAX))
 		return -EINVAL;
 
-	addr = u64_to_user_ptr(entry->relocs_ptr);
-	size *= sizeof(struct drm_i915_gem_relocation_entry);
-	if (!access_ok(VERIFY_WRITE, addr, size))
+	size = count * sizeof(struct drm_i915_gem_relocation_entry);
+	if (!access_ok(VERIFY_READ, addr, size))
 		return -EFAULT;
 
 	end = addr + size;
 	for (; addr < end; addr += PAGE_SIZE) {
-		int err = __get_user(c, addr);
+		int err = __get_user(c, (const char __user *)addr);
 		if (err)
 			return err;
 	}
-	return __get_user(c, end - 1);
+	return __get_user(c, (const char __user *)end - 1);
 }
 
-static int eb_copy_relocations(const struct i915_execbuffer *eb)
+static struct eb_relocs *eb_copy_relocations(const struct i915_execbuffer *eb)
 {
+	const struct drm_i915_gem_exec_object2 *exec =
+		u64_to_user_ptr(eb->args->buffers_ptr);
 	const unsigned int count = eb->buffer_count;
+	struct eb_relocs *r;
 	unsigned int i;
 	int err;
 
+	r = kvmalloc_array(count, sizeof(*r), GFP_TEMPORARY | __GFP_ZERO);
+	if (!r)
+		return ERR_PTR(-ENOMEM);
+
 	for (i = 0; i < count; i++) {
-		const unsigned int nreloc = eb->exec[i].relocation_count;
 		struct drm_i915_gem_relocation_entry __user *urelocs;
-		struct drm_i915_gem_relocation_entry *relocs;
-		unsigned long size;
-		unsigned long copied;
+		unsigned int copied;
+		unsigned int nreloc;
+		u64 uptr;
+
+		err = __get_user(nreloc, &exec[i].relocation_count);
+		if (unlikely(err))
+			goto err;
 
 		if (nreloc == 0)
 			continue;
 
-		err = check_relocations(&eb->exec[i]);
-		if (err)
+		err = __get_user(uptr, &exec[i].relocs_ptr);
+		if (unlikely(err))
 			goto err;
 
-		urelocs = u64_to_user_ptr(eb->exec[i].relocs_ptr);
-		size = nreloc * sizeof(*relocs);
+		urelocs = u64_to_user_ptr(uptr);
+		err = check_relocations(nreloc, urelocs);
+		if (err)
+			goto err;
 
-		relocs = kvmalloc_array(size, 1, GFP_TEMPORARY);
-		if (!relocs) {
-			kvfree(relocs);
+		r[i].relocs = kvmalloc_array(nreloc,
+					     sizeof(*r->relocs),
+					     GFP_TEMPORARY);
+		if (!r[i].relocs) {
 			err = -ENOMEM;
 			goto err;
 		}
@@ -1631,18 +1631,20 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb)
 		copied = 0;
 		do {
 			unsigned int len =
-				min_t(u64, BIT_ULL(31), size - copied);
-
-			if (__copy_from_user((char *)relocs + copied,
-					     (char *)urelocs + copied,
-					     len)) {
-				kvfree(relocs);
+				min_t(u64,
+				      BIT_ULL(31) / sizeof(*r->relocs),
+				      nreloc - copied);
+
+			if (__copy_from_user(r[i].relocs + copied,
+					     urelocs + copied,
+					     len * sizeof(*r->relocs))) {
+				kvfree(r[i].relocs);
 				err = -EFAULT;
 				goto err;
 			}
 
 			copied += len;
-		} while (copied < size);
+		} while (copied < nreloc);
 
 		/*
 		 * As we do not update the known relocation offsets after
@@ -1662,33 +1664,43 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb)
 end_user:
 		user_access_end();
 
-		eb->exec[i].relocs_ptr = (uintptr_t)relocs;
+		r[i].count = nreloc;
 	}
 
-	return 0;
+	return r;
 
 err:
-	while (i--) {
-		struct drm_i915_gem_relocation_entry *relocs =
-			u64_to_ptr(typeof(*relocs), eb->exec[i].relocs_ptr);
-		if (eb->exec[i].relocation_count)
-			kvfree(relocs);
-	}
-	return err;
+	while (i--)
+		kvfree(r[i].relocs);
+	kvfree(r);
+	return ERR_PTR(err);
 }
 
 static int eb_prefault_relocations(const struct i915_execbuffer *eb)
 {
+	const struct drm_i915_gem_exec_object2 *exec =
+		u64_to_user_ptr(eb->args->buffers_ptr);
 	const unsigned int count = eb->buffer_count;
 	unsigned int i;
+	int err;
 
 	if (unlikely(i915.prefault_disable))
 		return 0;
 
 	for (i = 0; i < count; i++) {
-		int err;
+		unsigned int nreloc;
+		u64 uptr;
+
+		if (__get_user(nreloc, &exec[i].relocation_count))
+			return -EFAULT;
 
-		err = check_relocations(&eb->exec[i]);
+		if (nreloc == 0)
+			continue;
+
+		if (__get_user(uptr, &exec[i].relocs_ptr))
+			return -EFAULT;
+
+		err = check_relocations(nreloc, u64_to_user_ptr(uptr));
 		if (err)
 			return err;
 	}
@@ -1699,7 +1711,7 @@ static int eb_prefault_relocations(const struct i915_execbuffer *eb)
 static int eb_relocate_slow(struct i915_execbuffer *eb)
 {
 	struct drm_device *dev = &eb->i915->drm;
-	bool have_copy = false;
+	struct eb_relocs *r = NULL;
 	struct i915_vma *vma;
 	int err = 0;
 
@@ -1728,9 +1740,11 @@ static int eb_relocate_slow(struct i915_execbuffer *eb)
 	 */
 	if (!err) {
 		err = eb_prefault_relocations(eb);
-	} else if (!have_copy) {
-		err = eb_copy_relocations(eb);
-		have_copy = err == 0;
+	} else if (!r) {
+		err = 0;
+		r = eb_copy_relocations(eb);
+		if (IS_ERR(r))
+			err = PTR_ERR(r);
 	} else {
 		cond_resched();
 		err = 0;
@@ -1750,21 +1764,22 @@ static int eb_relocate_slow(struct i915_execbuffer *eb)
 	}
 
 	/* reacquire the objects */
-	err = eb_lookup_vmas(eb);
+	err = eb_lookup_vmas(eb, __EXEC_OBJECT_VALIDATED);
 	if (err)
 		goto err;
 
 	GEM_BUG_ON(!eb->batch);
 
 	list_for_each_entry(vma, &eb->relocs, reloc_link) {
-		if (!have_copy) {
+		if (!r) {
 			pagefault_disable();
 			err = eb_relocate_vma(eb, vma);
 			pagefault_enable();
 			if (err)
 				goto repeat;
 		} else {
-			err = eb_relocate_vma_slow(eb, vma);
+			err = eb_relocate_vma_slow(eb, vma,
+						   &r[vma->exec_flags - eb->flags]);
 			if (err)
 				goto err;
 		}
@@ -1782,24 +1797,16 @@ static int eb_relocate_slow(struct i915_execbuffer *eb)
 		goto repeat;
 
 out:
-	if (have_copy) {
+	if (r) {
 		const unsigned int count = eb->buffer_count;
 		unsigned int i;
 
-		for (i = 0; i < count; i++) {
-			const struct drm_i915_gem_exec_object2 *entry =
-				&eb->exec[i];
-			struct drm_i915_gem_relocation_entry *relocs;
-
-			if (!entry->relocation_count)
-				continue;
-
-			relocs = u64_to_ptr(typeof(*relocs), entry->relocs_ptr);
-			kvfree(relocs);
-		}
+		for (i = 0; i < count; i++)
+			kvfree(r[i].relocs);
+		kvfree(r);
 	}
 
-	return err ?: have_copy;
+	return err ?: !!r;
 }
 
 static void eb_export_fence(struct i915_vma *vma,
@@ -1869,7 +1876,7 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
 		if (unlikely(flags & __EXEC_OBJECT_HAS_REF))
 			i915_vma_put(vma);
 	}
-	eb->exec = NULL;
+	eb->buffer_count = 0;
 
 	/* Unconditionally flush any chipset caches (for streaming writes). */
 	i915_gem_chipset_flush(eb->i915);
@@ -1878,26 +1885,34 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
 	return eb->engine->emit_flush(eb->request, EMIT_INVALIDATE);
 }
 
-static bool i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec)
+static int eb_check_args(struct drm_i915_gem_execbuffer2 *args)
 {
-	if (exec->flags & __I915_EXEC_ILLEGAL_FLAGS)
-		return false;
+	if (args->flags & __I915_EXEC_ILLEGAL_FLAGS)
+		return -EINVAL;
 
 	/* Kernel clipping was a DRI1 misfeature */
-	if (exec->num_cliprects || exec->cliprects_ptr)
-		return false;
+	if (args->num_cliprects | args->cliprects_ptr)
+		return -EINVAL;
 
-	if (exec->DR4 == 0xffffffff) {
+	if (args->DR4 == 0xffffffff) {
 		DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
-		exec->DR4 = 0;
+		args->DR4 = 0;
 	}
-	if (exec->DR1 || exec->DR4)
-		return false;
+	if (args->DR1 | args->DR4)
+		return -EINVAL;
 
-	if ((exec->batch_start_offset | exec->batch_len) & 0x7)
-		return false;
+	if ((args->batch_start_offset | args->batch_len) & 0x7)
+		return -EINVAL;
 
-	return true;
+	if (args->buffer_count < 1 || args->buffer_count > N_EXEC(SIZE_MAX) - 1)
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_READ,
+		       args->buffers_ptr,
+		       args->buffer_count * sizeof(struct drm_i915_gem_exec_object2)))
+		return -EFAULT;
+
+	return 0;
 }
 
 void i915_vma_move_to_active(struct i915_vma *vma,
@@ -2117,16 +2132,18 @@ eb_select_engine(struct drm_i915_private *dev_priv,
 	return engine;
 }
 
-static int
-i915_gem_do_execbuffer(struct drm_device *dev,
-		       struct drm_file *file,
-		       struct drm_i915_gem_execbuffer2 *args,
-		       struct drm_i915_gem_exec_object2 *exec)
+int
+i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
+			   struct drm_file *file)
 {
+	const size_t sz = (sizeof(struct i915_vma *) +
+			   sizeof(unsigned int) +
+			   sizeof(u64));
 	struct i915_execbuffer eb;
 	struct dma_fence *in_fence = NULL;
 	struct sync_file *out_fence = NULL;
 	int out_fence_fd = -1;
+	char onstack[900 - sizeof(eb)];
 	int err;
 
 	BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS &
@@ -2134,56 +2151,81 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 
 	eb.i915 = to_i915(dev);
 	eb.file = file;
-	eb.args = args;
-	if (DBG_FORCE_RELOC || !(args->flags & I915_EXEC_NO_RELOC))
-		args->flags |= __EXEC_HAS_RELOC;
-	eb.exec = exec;
-	eb.vma = (struct i915_vma **)(exec + args->buffer_count + 1);
-	eb.flags = (unsigned int *)(eb.vma + args->buffer_count + 1);
+	eb.args = data;
+
+	err = eb_check_args(eb.args);
+	if (unlikely(err))
+		return err;
+
+	/* Allocate an extra slot for use by the command parser */
+	if (eb.args->buffer_count + 1 > sizeof(onstack) / sz) {
+		eb.vma = kvmalloc_array(eb.args->buffer_count + 1, sz,
+					__GFP_NOWARN | GFP_TEMPORARY);
+		if (!eb.vma) {
+			DRM_DEBUG("Failed to allocate exec list for %d buffers\n",
+				  eb.args->buffer_count);
+			return -ENOMEM;
+		}
+	} else {
+		eb.vma = (typeof(eb.vma))onstack;
+	}
+
+	if (DBG_FORCE_RELOC || !(eb.args->flags & I915_EXEC_NO_RELOC))
+		eb.args->flags |= __EXEC_HAS_RELOC;
+	eb.flags = (unsigned int *)(eb.vma + eb.args->buffer_count + 1);
+	eb.offsets = (u64 *)(eb.flags + eb.args->buffer_count + 1);
 	eb.invalid_flags = __EXEC_OBJECT_UNKNOWN_FLAGS;
 	if (USES_FULL_PPGTT(eb.i915))
 		eb.invalid_flags |= EXEC_OBJECT_NEEDS_GTT;
 	reloc_cache_init(&eb.reloc_cache, eb.i915);
 
-	eb.buffer_count = args->buffer_count;
-	eb.batch_start_offset = args->batch_start_offset;
-	eb.batch_len = args->batch_len;
+	eb.buffer_count = eb.args->buffer_count;
+	eb.batch_start_offset = eb.args->batch_start_offset;
+	eb.batch_len = eb.args->batch_len;
 
 	eb.batch_flags = 0;
-	if (args->flags & I915_EXEC_SECURE) {
-		if (!drm_is_current_master(file) || !capable(CAP_SYS_ADMIN))
-		    return -EPERM;
+	if (eb.args->flags & I915_EXEC_SECURE) {
+		if (!drm_is_current_master(file) || !capable(CAP_SYS_ADMIN)) {
+			err = -EPERM;
+			goto out;
+		}
 
 		eb.batch_flags |= I915_DISPATCH_SECURE;
 	}
-	if (args->flags & I915_EXEC_IS_PINNED)
+	if (eb.args->flags & I915_EXEC_IS_PINNED)
 		eb.batch_flags |= I915_DISPATCH_PINNED;
 
-	eb.engine = eb_select_engine(eb.i915, file, args);
-	if (!eb.engine)
-		return -EINVAL;
+	eb.engine = eb_select_engine(eb.i915, file, eb.args);
+	if (!eb.engine) {
+		err = -EINVAL;
+		goto out;
+	}
 
-	if (args->flags & I915_EXEC_RESOURCE_STREAMER) {
+	if (eb.args->flags & I915_EXEC_RESOURCE_STREAMER) {
 		if (!HAS_RESOURCE_STREAMER(eb.i915)) {
 			DRM_DEBUG("RS is only allowed for Haswell, Gen8 and above\n");
-			return -EINVAL;
+			err = -EINVAL;
+			goto out;
 		}
 		if (eb.engine->id != RCS) {
 			DRM_DEBUG("RS is not available on %s\n",
 				 eb.engine->name);
-			return -EINVAL;
+			err = -EINVAL;
+			goto out;
 		}
 
 		eb.batch_flags |= I915_DISPATCH_RS;
 	}
 
-	if (args->flags & I915_EXEC_FENCE_IN) {
-		in_fence = sync_file_get_fence(lower_32_bits(args->rsvd2));
-		if (!in_fence)
-			return -EINVAL;
+	if (eb.args->flags & I915_EXEC_FENCE_IN) {
+		in_fence = sync_file_get_fence(lower_32_bits(eb.args->rsvd2));
+		if (!in_fence) {
+			err = -EINVAL;
+			goto out;
+		}
 	}
 
-	if (args->flags & I915_EXEC_FENCE_OUT) {
+	if (eb.args->flags & I915_EXEC_FENCE_OUT) {
 		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
 		if (out_fence_fd < 0) {
 			err = out_fence_fd;
@@ -2191,8 +2233,10 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 		}
 	}
 
-	if (eb_create(&eb))
-		return -ENOMEM;
+	if (eb_create(&eb)) {
+		err = -ENOMEM;
+		goto out;
+	}
 
 	err = eb_select_context(&eb);
 	if (unlikely(err))
@@ -2211,12 +2255,12 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	if (err)
 		goto err_rpm;
 
-	err = eb_lookup_vmas(&eb);
-	if (likely(!err && args->flags & __EXEC_HAS_RELOC))
+	err = eb_lookup_vmas(&eb, 0);
+	if (likely(!err && eb.args->flags & __EXEC_HAS_RELOC))
 		err = eb_relocate(&eb);
 	if (err == -EAGAIN || err == -EFAULT)
 		err = eb_relocate_slow(&eb);
-	if (err && args->flags & I915_EXEC_NO_RELOC)
+	if (err && eb.args->flags & I915_EXEC_NO_RELOC)
 		/*
 		 * If the user expects the execobject.offset and
 		 * reloc.presumed_offset to be an exact match,
@@ -2224,7 +2268,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 		 * the execobject.offset until we have completed
 		 * relocation.
 		 */
-		args->flags &= ~__EXEC_HAS_RELOC;
+		eb.args->flags &= ~__EXEC_HAS_RELOC;
 	if (err < 0)
 		goto err_vma;
 
@@ -2336,8 +2380,8 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	if (out_fence) {
 		if (err == 0) {
 			fd_install(out_fence_fd, out_fence->file);
-			args->rsvd2 &= GENMASK_ULL(0, 31); /* keep in-fence */
-			args->rsvd2 |= (u64)out_fence_fd << 32;
+			eb.args->rsvd2 &= GENMASK_ULL(0, 31); /* keep in-fence */
+			eb.args->rsvd2 |= (u64)out_fence_fd << 32;
 			out_fence_fd = -1;
 		} else {
 			fput(out_fence->file);
@@ -2359,142 +2403,6 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 		put_unused_fd(out_fence_fd);
 err_in_fence:
 	dma_fence_put(in_fence);
-	return err;
-}
-
-/*
- * Legacy execbuffer just creates an exec2 list from the original exec object
- * list array and passes it to the real function.
- */
-int
-i915_gem_execbuffer(struct drm_device *dev, void *data,
-		    struct drm_file *file)
-{
-	const size_t sz = (sizeof(struct drm_i915_gem_exec_object2) +
-			   sizeof(struct i915_vma *) +
-			   sizeof(unsigned int));
-	struct drm_i915_gem_execbuffer *args = data;
-	struct drm_i915_gem_execbuffer2 exec2;
-	struct drm_i915_gem_exec_object *exec_list = NULL;
-	struct drm_i915_gem_exec_object2 *exec2_list = NULL;
-	unsigned int i;
-	int err;
-
-	if (args->buffer_count < 1 || args->buffer_count > SIZE_MAX / sz - 1) {
-		DRM_DEBUG("execbuf2 with %d buffers\n", args->buffer_count);
-		return -EINVAL;
-	}
-
-	exec2.buffers_ptr = args->buffers_ptr;
-	exec2.buffer_count = args->buffer_count;
-	exec2.batch_start_offset = args->batch_start_offset;
-	exec2.batch_len = args->batch_len;
-	exec2.DR1 = args->DR1;
-	exec2.DR4 = args->DR4;
-	exec2.num_cliprects = args->num_cliprects;
-	exec2.cliprects_ptr = args->cliprects_ptr;
-	exec2.flags = I915_EXEC_RENDER;
-	i915_execbuffer2_set_context_id(exec2, 0);
-
-	if (!i915_gem_check_execbuffer(&exec2))
-		return -EINVAL;
-
-	/* Copy in the exec list from userland */
-	exec_list = kvmalloc_array(args->buffer_count, sizeof(*exec_list),
-				   __GFP_NOWARN | GFP_TEMPORARY);
-	exec2_list = kvmalloc_array(args->buffer_count + 1, sz,
-				    __GFP_NOWARN | GFP_TEMPORARY);
-	if (exec_list == NULL || exec2_list == NULL) {
-		DRM_DEBUG("Failed to allocate exec list for %d buffers\n",
-			  args->buffer_count);
-		kvfree(exec_list);
-		kvfree(exec2_list);
-		return -ENOMEM;
-	}
-	err = copy_from_user(exec_list,
-			     u64_to_user_ptr(args->buffers_ptr),
-			     sizeof(*exec_list) * args->buffer_count);
-	if (err) {
-		DRM_DEBUG("copy %d exec entries failed %d\n",
-			  args->buffer_count, err);
-		kvfree(exec_list);
-		kvfree(exec2_list);
-		return -EFAULT;
-	}
-
-	for (i = 0; i < args->buffer_count; i++) {
-		exec2_list[i].handle = exec_list[i].handle;
-		exec2_list[i].relocation_count = exec_list[i].relocation_count;
-		exec2_list[i].relocs_ptr = exec_list[i].relocs_ptr;
-		exec2_list[i].alignment = exec_list[i].alignment;
-		exec2_list[i].offset = exec_list[i].offset;
-		if (INTEL_GEN(to_i915(dev)) < 4)
-			exec2_list[i].flags = EXEC_OBJECT_NEEDS_FENCE;
-		else
-			exec2_list[i].flags = 0;
-	}
-
-	err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list);
-	if (exec2.flags & __EXEC_HAS_RELOC) {
-		struct drm_i915_gem_exec_object __user *user_exec_list =
-			u64_to_user_ptr(args->buffers_ptr);
-
-		/* Copy the new buffer offsets back to the user's exec list. */
-		for (i = 0; i < args->buffer_count; i++) {
-			if (!(exec2_list[i].offset & UPDATE))
-				continue;
-
-			exec2_list[i].offset =
-				gen8_canonical_addr(exec2_list[i].offset & PIN_OFFSET_MASK);
-			exec2_list[i].offset &= PIN_OFFSET_MASK;
-			if (__copy_to_user(&user_exec_list[i].offset,
-					   &exec2_list[i].offset,
-					   sizeof(user_exec_list[i].offset)))
-				break;
-		}
-	}
-
-	kvfree(exec_list);
-	kvfree(exec2_list);
-	return err;
-}
-
-int
-i915_gem_execbuffer2(struct drm_device *dev, void *data,
-		     struct drm_file *file)
-{
-	const size_t sz = (sizeof(struct drm_i915_gem_exec_object2) +
-			   sizeof(struct i915_vma *) +
-			   sizeof(unsigned int));
-	struct drm_i915_gem_execbuffer2 *args = data;
-	struct drm_i915_gem_exec_object2 *exec2_list;
-	int err;
-
-	if (args->buffer_count < 1 || args->buffer_count > SIZE_MAX / sz - 1) {
-		DRM_DEBUG("execbuf2 with %d buffers\n", args->buffer_count);
-		return -EINVAL;
-	}
-
-	if (!i915_gem_check_execbuffer(args))
-		return -EINVAL;
-
-	/* Allocate an extra slot for use by the command parser */
-	exec2_list = kvmalloc_array(args->buffer_count + 1, sz,
-				    __GFP_NOWARN | GFP_TEMPORARY);
-	if (exec2_list == NULL) {
-		DRM_DEBUG("Failed to allocate exec list for %d buffers\n",
-			  args->buffer_count);
-		return -ENOMEM;
-	}
-	if (copy_from_user(exec2_list,
-			   u64_to_user_ptr(args->buffers_ptr),
-			   sizeof(*exec2_list) * args->buffer_count)) {
-		DRM_DEBUG("copy %d exec entries failed\n", args->buffer_count);
-		kvfree(exec2_list);
-		return -EFAULT;
-	}
-
-	err = i915_gem_do_execbuffer(dev, file, args, exec2_list);
 
 	/*
 	 * Now that we have begun execution of the batchbuffer, we ignore
@@ -2502,28 +2410,29 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
 	 * updated the associated relocations, we try to write out the current
 	 * object locations irrespective of any error.
 	 */
-	if (args->flags & __EXEC_HAS_RELOC) {
-		struct drm_i915_gem_exec_object2 __user *user_exec_list =
-			u64_to_user_ptr(args->buffers_ptr);
+	if (eb.args->flags & __EXEC_HAS_RELOC) {
+		struct drm_i915_gem_exec_object2 __user *user =
+			u64_to_user_ptr(eb.args->buffers_ptr);
 		unsigned int i;
 
 		/* Copy the new buffer offsets back to the user's exec list. */
 		user_access_begin();
-		for (i = 0; i < args->buffer_count; i++) {
-			if (!(exec2_list[i].offset & UPDATE))
+		for (i = 0; i < eb.args->buffer_count; i++) {
+			u64 offset;
+
+			if (!(eb.offsets[i] & UPDATE))
 				continue;
 
-			exec2_list[i].offset =
-				gen8_canonical_addr(exec2_list[i].offset & PIN_OFFSET_MASK);
-			unsafe_put_user(exec2_list[i].offset,
-					&user_exec_list[i].offset,
-					end_user);
+			offset = gen8_canonical_addr(eb.offsets[i] & ~UPDATE);
+			unsafe_put_user(offset, &user[i].offset, end_user);
 		}
 end_user:
 		user_access_end();
 	}
 
-	args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
-	kvfree(exec2_list);
+out:
+	eb.args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
+	if (eb.vma != (typeof(eb.vma))onstack)
+		kvfree(eb.vma);
 	return err;
 }
-- 
2.11.0



More information about the Intel-gfx-trybot mailing list