xref: /openbsd-src/sys/dev/pci/drm/i915/selftests/i915_vma.c (revision 1bb76ff151c0aba8e3312a604e4cd2e5195cf4b7)
1c349dbc7Sjsg /*
2c349dbc7Sjsg  * Copyright © 2016 Intel Corporation
3c349dbc7Sjsg  *
4c349dbc7Sjsg  * Permission is hereby granted, free of charge, to any person obtaining a
5c349dbc7Sjsg  * copy of this software and associated documentation files (the "Software"),
6c349dbc7Sjsg  * to deal in the Software without restriction, including without limitation
7c349dbc7Sjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c349dbc7Sjsg  * and/or sell copies of the Software, and to permit persons to whom the
9c349dbc7Sjsg  * Software is furnished to do so, subject to the following conditions:
10c349dbc7Sjsg  *
11c349dbc7Sjsg  * The above copyright notice and this permission notice (including the next
12c349dbc7Sjsg  * paragraph) shall be included in all copies or substantial portions of the
13c349dbc7Sjsg  * Software.
14c349dbc7Sjsg  *
15c349dbc7Sjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16c349dbc7Sjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17c349dbc7Sjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18c349dbc7Sjsg  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19c349dbc7Sjsg  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20c349dbc7Sjsg  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21c349dbc7Sjsg  * IN THE SOFTWARE.
22c349dbc7Sjsg  *
23c349dbc7Sjsg  */
24c349dbc7Sjsg 
25c349dbc7Sjsg #include <linux/prime_numbers.h>
26c349dbc7Sjsg 
27c349dbc7Sjsg #include "gem/i915_gem_context.h"
28*1bb76ff1Sjsg #include "gem/i915_gem_internal.h"
29c349dbc7Sjsg #include "gem/selftests/mock_context.h"
30c349dbc7Sjsg 
31c349dbc7Sjsg #include "i915_scatterlist.h"
32c349dbc7Sjsg #include "i915_selftest.h"
33c349dbc7Sjsg 
34c349dbc7Sjsg #include "mock_gem_device.h"
35c349dbc7Sjsg #include "mock_gtt.h"
36c349dbc7Sjsg 
assert_vma(struct i915_vma * vma,struct drm_i915_gem_object * obj,struct i915_gem_context * ctx)37c349dbc7Sjsg static bool assert_vma(struct i915_vma *vma,
38c349dbc7Sjsg 		       struct drm_i915_gem_object *obj,
39c349dbc7Sjsg 		       struct i915_gem_context *ctx)
40c349dbc7Sjsg {
41c349dbc7Sjsg 	bool ok = true;
42c349dbc7Sjsg 
43*1bb76ff1Sjsg 	if (vma->vm != ctx->vm) {
44c349dbc7Sjsg 		pr_err("VMA created with wrong VM\n");
45c349dbc7Sjsg 		ok = false;
46c349dbc7Sjsg 	}
47c349dbc7Sjsg 
48c349dbc7Sjsg 	if (vma->size != obj->base.size) {
49c349dbc7Sjsg 		pr_err("VMA created with wrong size, found %llu, expected %zu\n",
50c349dbc7Sjsg 		       vma->size, obj->base.size);
51c349dbc7Sjsg 		ok = false;
52c349dbc7Sjsg 	}
53c349dbc7Sjsg 
54*1bb76ff1Sjsg 	if (vma->gtt_view.type != I915_GTT_VIEW_NORMAL) {
55c349dbc7Sjsg 		pr_err("VMA created with wrong type [%d]\n",
56*1bb76ff1Sjsg 		       vma->gtt_view.type);
57c349dbc7Sjsg 		ok = false;
58c349dbc7Sjsg 	}
59c349dbc7Sjsg 
60c349dbc7Sjsg 	return ok;
61c349dbc7Sjsg }
62c349dbc7Sjsg 
63c349dbc7Sjsg static struct i915_vma *
checked_vma_instance(struct drm_i915_gem_object * obj,struct i915_address_space * vm,const struct i915_gtt_view * view)64c349dbc7Sjsg checked_vma_instance(struct drm_i915_gem_object *obj,
65c349dbc7Sjsg 		     struct i915_address_space *vm,
66*1bb76ff1Sjsg 		     const struct i915_gtt_view *view)
67c349dbc7Sjsg {
68c349dbc7Sjsg 	struct i915_vma *vma;
69c349dbc7Sjsg 	bool ok = true;
70c349dbc7Sjsg 
71c349dbc7Sjsg 	vma = i915_vma_instance(obj, vm, view);
72c349dbc7Sjsg 	if (IS_ERR(vma))
73c349dbc7Sjsg 		return vma;
74c349dbc7Sjsg 
75c349dbc7Sjsg 	/* Manual checks, will be reinforced by i915_vma_compare! */
76c349dbc7Sjsg 	if (vma->vm != vm) {
77c349dbc7Sjsg 		pr_err("VMA's vm [%p] does not match request [%p]\n",
78c349dbc7Sjsg 		       vma->vm, vm);
79c349dbc7Sjsg 		ok = false;
80c349dbc7Sjsg 	}
81c349dbc7Sjsg 
82c349dbc7Sjsg 	if (i915_is_ggtt(vm) != i915_vma_is_ggtt(vma)) {
83c349dbc7Sjsg 		pr_err("VMA ggtt status [%d] does not match parent [%d]\n",
84c349dbc7Sjsg 		       i915_vma_is_ggtt(vma), i915_is_ggtt(vm));
85c349dbc7Sjsg 		ok = false;
86c349dbc7Sjsg 	}
87c349dbc7Sjsg 
88c349dbc7Sjsg 	if (i915_vma_compare(vma, vm, view)) {
89c349dbc7Sjsg 		pr_err("i915_vma_compare failed with create parameters!\n");
90c349dbc7Sjsg 		return ERR_PTR(-EINVAL);
91c349dbc7Sjsg 	}
92c349dbc7Sjsg 
93c349dbc7Sjsg 	if (i915_vma_compare(vma, vma->vm,
94*1bb76ff1Sjsg 			     i915_vma_is_ggtt(vma) ? &vma->gtt_view : NULL)) {
95c349dbc7Sjsg 		pr_err("i915_vma_compare failed with itself\n");
96c349dbc7Sjsg 		return ERR_PTR(-EINVAL);
97c349dbc7Sjsg 	}
98c349dbc7Sjsg 
99c349dbc7Sjsg 	if (!ok) {
100c349dbc7Sjsg 		pr_err("i915_vma_compare failed to detect the difference!\n");
101c349dbc7Sjsg 		return ERR_PTR(-EINVAL);
102c349dbc7Sjsg 	}
103c349dbc7Sjsg 
104c349dbc7Sjsg 	return vma;
105c349dbc7Sjsg }
106c349dbc7Sjsg 
create_vmas(struct drm_i915_private * i915,struct list_head * objects,struct list_head * contexts)107c349dbc7Sjsg static int create_vmas(struct drm_i915_private *i915,
108c349dbc7Sjsg 		       struct list_head *objects,
109c349dbc7Sjsg 		       struct list_head *contexts)
110c349dbc7Sjsg {
111c349dbc7Sjsg 	struct drm_i915_gem_object *obj;
112c349dbc7Sjsg 	struct i915_gem_context *ctx;
113c349dbc7Sjsg 	int pinned;
114c349dbc7Sjsg 
115c349dbc7Sjsg 	list_for_each_entry(obj, objects, st_link) {
116c349dbc7Sjsg 		for (pinned = 0; pinned <= 1; pinned++) {
117c349dbc7Sjsg 			list_for_each_entry(ctx, contexts, link) {
118c349dbc7Sjsg 				struct i915_address_space *vm;
119c349dbc7Sjsg 				struct i915_vma *vma;
120c349dbc7Sjsg 				int err;
121c349dbc7Sjsg 
122*1bb76ff1Sjsg 				vm = i915_gem_context_get_eb_vm(ctx);
123c349dbc7Sjsg 				vma = checked_vma_instance(obj, vm, NULL);
124c349dbc7Sjsg 				i915_vm_put(vm);
125c349dbc7Sjsg 				if (IS_ERR(vma))
126c349dbc7Sjsg 					return PTR_ERR(vma);
127c349dbc7Sjsg 
128c349dbc7Sjsg 				if (!assert_vma(vma, obj, ctx)) {
129c349dbc7Sjsg 					pr_err("VMA lookup/create failed\n");
130c349dbc7Sjsg 					return -EINVAL;
131c349dbc7Sjsg 				}
132c349dbc7Sjsg 
133c349dbc7Sjsg 				if (!pinned) {
134c349dbc7Sjsg 					err = i915_vma_pin(vma, 0, 0, PIN_USER);
135c349dbc7Sjsg 					if (err) {
136c349dbc7Sjsg 						pr_err("Failed to pin VMA\n");
137c349dbc7Sjsg 						return err;
138c349dbc7Sjsg 					}
139c349dbc7Sjsg 				} else {
140c349dbc7Sjsg 					i915_vma_unpin(vma);
141c349dbc7Sjsg 				}
142c349dbc7Sjsg 			}
143c349dbc7Sjsg 		}
144c349dbc7Sjsg 	}
145c349dbc7Sjsg 
146c349dbc7Sjsg 	return 0;
147c349dbc7Sjsg }
148c349dbc7Sjsg 
igt_vma_create(void * arg)149c349dbc7Sjsg static int igt_vma_create(void *arg)
150c349dbc7Sjsg {
151c349dbc7Sjsg 	struct i915_ggtt *ggtt = arg;
152c349dbc7Sjsg 	struct drm_i915_private *i915 = ggtt->vm.i915;
153c349dbc7Sjsg 	struct drm_i915_gem_object *obj, *on;
154c349dbc7Sjsg 	struct i915_gem_context *ctx, *cn;
155c349dbc7Sjsg 	unsigned long num_obj, num_ctx;
156c349dbc7Sjsg 	unsigned long no, nc;
157c349dbc7Sjsg 	IGT_TIMEOUT(end_time);
158c349dbc7Sjsg 	DRM_LIST_HEAD(contexts);
159c349dbc7Sjsg 	DRM_LIST_HEAD(objects);
160c349dbc7Sjsg 	int err = -ENOMEM;
161c349dbc7Sjsg 
162c349dbc7Sjsg 	/* Exercise creating many vma amonst many objections, checking the
163c349dbc7Sjsg 	 * vma creation and lookup routines.
164c349dbc7Sjsg 	 */
165c349dbc7Sjsg 
166c349dbc7Sjsg 	no = 0;
167c349dbc7Sjsg 	for_each_prime_number(num_obj, ULONG_MAX - 1) {
168c349dbc7Sjsg 		for (; no < num_obj; no++) {
169c349dbc7Sjsg 			obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
170c349dbc7Sjsg 			if (IS_ERR(obj))
171c349dbc7Sjsg 				goto out;
172c349dbc7Sjsg 
173c349dbc7Sjsg 			list_add(&obj->st_link, &objects);
174c349dbc7Sjsg 		}
175c349dbc7Sjsg 
176c349dbc7Sjsg 		nc = 0;
177c349dbc7Sjsg 		for_each_prime_number(num_ctx, 2 * BITS_PER_LONG) {
178c349dbc7Sjsg 			for (; nc < num_ctx; nc++) {
179c349dbc7Sjsg 				ctx = mock_context(i915, "mock");
180c349dbc7Sjsg 				if (!ctx)
181c349dbc7Sjsg 					goto out;
182c349dbc7Sjsg 
183c349dbc7Sjsg 				list_move(&ctx->link, &contexts);
184c349dbc7Sjsg 			}
185c349dbc7Sjsg 
186c349dbc7Sjsg 			err = create_vmas(i915, &objects, &contexts);
187c349dbc7Sjsg 			if (err)
188c349dbc7Sjsg 				goto out;
189c349dbc7Sjsg 
190c349dbc7Sjsg 			if (igt_timeout(end_time,
191c349dbc7Sjsg 					"%s timed out: after %lu objects in %lu contexts\n",
192c349dbc7Sjsg 					__func__, no, nc))
193c349dbc7Sjsg 				goto end;
194c349dbc7Sjsg 		}
195c349dbc7Sjsg 
196c349dbc7Sjsg 		list_for_each_entry_safe(ctx, cn, &contexts, link) {
197c349dbc7Sjsg 			list_del_init(&ctx->link);
198c349dbc7Sjsg 			mock_context_close(ctx);
199c349dbc7Sjsg 		}
200c349dbc7Sjsg 
201c349dbc7Sjsg 		cond_resched();
202c349dbc7Sjsg 	}
203c349dbc7Sjsg 
204c349dbc7Sjsg end:
205c349dbc7Sjsg 	/* Final pass to lookup all created contexts */
206c349dbc7Sjsg 	err = create_vmas(i915, &objects, &contexts);
207c349dbc7Sjsg out:
208c349dbc7Sjsg 	list_for_each_entry_safe(ctx, cn, &contexts, link) {
209c349dbc7Sjsg 		list_del_init(&ctx->link);
210c349dbc7Sjsg 		mock_context_close(ctx);
211c349dbc7Sjsg 	}
212c349dbc7Sjsg 
213c349dbc7Sjsg 	list_for_each_entry_safe(obj, on, &objects, st_link)
214c349dbc7Sjsg 		i915_gem_object_put(obj);
215c349dbc7Sjsg 	return err;
216c349dbc7Sjsg }
217c349dbc7Sjsg 
218c349dbc7Sjsg struct pin_mode {
219c349dbc7Sjsg 	u64 size;
220c349dbc7Sjsg 	u64 flags;
221c349dbc7Sjsg 	bool (*assert)(const struct i915_vma *,
222c349dbc7Sjsg 		       const struct pin_mode *mode,
223c349dbc7Sjsg 		       int result);
224c349dbc7Sjsg 	const char *string;
225c349dbc7Sjsg };
226c349dbc7Sjsg 
assert_pin_valid(const struct i915_vma * vma,const struct pin_mode * mode,int result)227c349dbc7Sjsg static bool assert_pin_valid(const struct i915_vma *vma,
228c349dbc7Sjsg 			     const struct pin_mode *mode,
229c349dbc7Sjsg 			     int result)
230c349dbc7Sjsg {
231c349dbc7Sjsg 	if (result)
232c349dbc7Sjsg 		return false;
233c349dbc7Sjsg 
234c349dbc7Sjsg 	if (i915_vma_misplaced(vma, mode->size, 0, mode->flags))
235c349dbc7Sjsg 		return false;
236c349dbc7Sjsg 
237c349dbc7Sjsg 	return true;
238c349dbc7Sjsg }
239c349dbc7Sjsg 
240c349dbc7Sjsg __maybe_unused
assert_pin_enospc(const struct i915_vma * vma,const struct pin_mode * mode,int result)241c349dbc7Sjsg static bool assert_pin_enospc(const struct i915_vma *vma,
242c349dbc7Sjsg 			      const struct pin_mode *mode,
243c349dbc7Sjsg 			      int result)
244c349dbc7Sjsg {
245c349dbc7Sjsg 	return result == -ENOSPC;
246c349dbc7Sjsg }
247c349dbc7Sjsg 
248c349dbc7Sjsg __maybe_unused
assert_pin_einval(const struct i915_vma * vma,const struct pin_mode * mode,int result)249c349dbc7Sjsg static bool assert_pin_einval(const struct i915_vma *vma,
250c349dbc7Sjsg 			      const struct pin_mode *mode,
251c349dbc7Sjsg 			      int result)
252c349dbc7Sjsg {
253c349dbc7Sjsg 	return result == -EINVAL;
254c349dbc7Sjsg }
255c349dbc7Sjsg 
igt_vma_pin1(void * arg)256c349dbc7Sjsg static int igt_vma_pin1(void *arg)
257c349dbc7Sjsg {
258c349dbc7Sjsg 	struct i915_ggtt *ggtt = arg;
259c349dbc7Sjsg 	const struct pin_mode modes[] = {
260c349dbc7Sjsg #define VALID(sz, fl) { .size = (sz), .flags = (fl), .assert = assert_pin_valid, .string = #sz ", " #fl ", (valid) " }
261c349dbc7Sjsg #define __INVALID(sz, fl, check, eval) { .size = (sz), .flags = (fl), .assert = (check), .string = #sz ", " #fl ", (invalid " #eval ")" }
262c349dbc7Sjsg #define INVALID(sz, fl) __INVALID(sz, fl, assert_pin_einval, EINVAL)
263c349dbc7Sjsg #define NOSPACE(sz, fl) __INVALID(sz, fl, assert_pin_enospc, ENOSPC)
264c349dbc7Sjsg 		VALID(0, PIN_GLOBAL),
265c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_MAPPABLE),
266c349dbc7Sjsg 
267c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_OFFSET_BIAS | 4096),
268c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_OFFSET_BIAS | 8192),
269c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_OFFSET_BIAS | (ggtt->mappable_end - 4096)),
270c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_MAPPABLE | PIN_OFFSET_BIAS | (ggtt->mappable_end - 4096)),
271c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_OFFSET_BIAS | (ggtt->vm.total - 4096)),
272c349dbc7Sjsg 
273c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_MAPPABLE | PIN_OFFSET_FIXED | (ggtt->mappable_end - 4096)),
274c349dbc7Sjsg 		INVALID(0, PIN_GLOBAL | PIN_MAPPABLE | PIN_OFFSET_FIXED | ggtt->mappable_end),
275c349dbc7Sjsg 		VALID(0, PIN_GLOBAL | PIN_OFFSET_FIXED | (ggtt->vm.total - 4096)),
276c349dbc7Sjsg 		INVALID(0, PIN_GLOBAL | PIN_OFFSET_FIXED | ggtt->vm.total),
277c349dbc7Sjsg 		INVALID(0, PIN_GLOBAL | PIN_OFFSET_FIXED | round_down(U64_MAX, PAGE_SIZE)),
278c349dbc7Sjsg 
279c349dbc7Sjsg 		VALID(4096, PIN_GLOBAL),
280c349dbc7Sjsg 		VALID(8192, PIN_GLOBAL),
281c349dbc7Sjsg 		VALID(ggtt->mappable_end - 4096, PIN_GLOBAL | PIN_MAPPABLE),
282c349dbc7Sjsg 		VALID(ggtt->mappable_end, PIN_GLOBAL | PIN_MAPPABLE),
283c349dbc7Sjsg 		NOSPACE(ggtt->mappable_end + 4096, PIN_GLOBAL | PIN_MAPPABLE),
284c349dbc7Sjsg 		VALID(ggtt->vm.total - 4096, PIN_GLOBAL),
285c349dbc7Sjsg 		VALID(ggtt->vm.total, PIN_GLOBAL),
286c349dbc7Sjsg 		NOSPACE(ggtt->vm.total + 4096, PIN_GLOBAL),
287c349dbc7Sjsg 		NOSPACE(round_down(U64_MAX, PAGE_SIZE), PIN_GLOBAL),
288c349dbc7Sjsg 		INVALID(8192, PIN_GLOBAL | PIN_MAPPABLE | PIN_OFFSET_FIXED | (ggtt->mappable_end - 4096)),
289c349dbc7Sjsg 		INVALID(8192, PIN_GLOBAL | PIN_OFFSET_FIXED | (ggtt->vm.total - 4096)),
290c349dbc7Sjsg 		INVALID(8192, PIN_GLOBAL | PIN_OFFSET_FIXED | (round_down(U64_MAX, PAGE_SIZE) - 4096)),
291c349dbc7Sjsg 
292c349dbc7Sjsg 		VALID(8192, PIN_GLOBAL | PIN_OFFSET_BIAS | (ggtt->mappable_end - 4096)),
293c349dbc7Sjsg 
294c349dbc7Sjsg #if !IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
295c349dbc7Sjsg 		/* Misusing BIAS is a programming error (it is not controllable
296c349dbc7Sjsg 		 * from userspace) so when debugging is enabled, it explodes.
297c349dbc7Sjsg 		 * However, the tests are still quite interesting for checking
298c349dbc7Sjsg 		 * variable start, end and size.
299c349dbc7Sjsg 		 */
300c349dbc7Sjsg 		NOSPACE(0, PIN_GLOBAL | PIN_MAPPABLE | PIN_OFFSET_BIAS | ggtt->mappable_end),
301c349dbc7Sjsg 		NOSPACE(0, PIN_GLOBAL | PIN_OFFSET_BIAS | ggtt->vm.total),
302c349dbc7Sjsg 		NOSPACE(8192, PIN_GLOBAL | PIN_MAPPABLE | PIN_OFFSET_BIAS | (ggtt->mappable_end - 4096)),
303c349dbc7Sjsg 		NOSPACE(8192, PIN_GLOBAL | PIN_OFFSET_BIAS | (ggtt->vm.total - 4096)),
304c349dbc7Sjsg #endif
305c349dbc7Sjsg 		{ },
306c349dbc7Sjsg #undef NOSPACE
307c349dbc7Sjsg #undef INVALID
308c349dbc7Sjsg #undef __INVALID
309c349dbc7Sjsg #undef VALID
310c349dbc7Sjsg 	}, *m;
311c349dbc7Sjsg 	struct drm_i915_gem_object *obj;
312c349dbc7Sjsg 	struct i915_vma *vma;
313c349dbc7Sjsg 	int err = -EINVAL;
314c349dbc7Sjsg 
315c349dbc7Sjsg 	/* Exercise all the weird and wonderful i915_vma_pin requests,
316c349dbc7Sjsg 	 * focusing on error handling of boundary conditions.
317c349dbc7Sjsg 	 */
318c349dbc7Sjsg 
319c349dbc7Sjsg 	GEM_BUG_ON(!drm_mm_clean(&ggtt->vm.mm));
320c349dbc7Sjsg 
321c349dbc7Sjsg 	obj = i915_gem_object_create_internal(ggtt->vm.i915, PAGE_SIZE);
322c349dbc7Sjsg 	if (IS_ERR(obj))
323c349dbc7Sjsg 		return PTR_ERR(obj);
324c349dbc7Sjsg 
325c349dbc7Sjsg 	vma = checked_vma_instance(obj, &ggtt->vm, NULL);
326c349dbc7Sjsg 	if (IS_ERR(vma))
327c349dbc7Sjsg 		goto out;
328c349dbc7Sjsg 
329c349dbc7Sjsg 	for (m = modes; m->assert; m++) {
330c349dbc7Sjsg 		err = i915_vma_pin(vma, m->size, 0, m->flags);
331c349dbc7Sjsg 		if (!m->assert(vma, m, err)) {
332c349dbc7Sjsg 			pr_err("%s to pin single page into GGTT with mode[%d:%s]: size=%llx flags=%llx, err=%d\n",
333c349dbc7Sjsg 			       m->assert == assert_pin_valid ? "Failed" : "Unexpectedly succeeded",
334c349dbc7Sjsg 			       (int)(m - modes), m->string, m->size, m->flags,
335c349dbc7Sjsg 			       err);
336c349dbc7Sjsg 			if (!err)
337c349dbc7Sjsg 				i915_vma_unpin(vma);
338c349dbc7Sjsg 			err = -EINVAL;
339c349dbc7Sjsg 			goto out;
340c349dbc7Sjsg 		}
341c349dbc7Sjsg 
342c349dbc7Sjsg 		if (!err) {
343c349dbc7Sjsg 			i915_vma_unpin(vma);
344*1bb76ff1Sjsg 			err = i915_vma_unbind_unlocked(vma);
345c349dbc7Sjsg 			if (err) {
346c349dbc7Sjsg 				pr_err("Failed to unbind single page from GGTT, err=%d\n", err);
347c349dbc7Sjsg 				goto out;
348c349dbc7Sjsg 			}
349c349dbc7Sjsg 		}
350c349dbc7Sjsg 
351c349dbc7Sjsg 		cond_resched();
352c349dbc7Sjsg 	}
353c349dbc7Sjsg 
354c349dbc7Sjsg 	err = 0;
355c349dbc7Sjsg out:
356c349dbc7Sjsg 	i915_gem_object_put(obj);
357c349dbc7Sjsg 	return err;
358c349dbc7Sjsg }
359c349dbc7Sjsg 
rotated_index(const struct intel_rotation_info * r,unsigned int n,unsigned int x,unsigned int y)360c349dbc7Sjsg static unsigned long rotated_index(const struct intel_rotation_info *r,
361c349dbc7Sjsg 				   unsigned int n,
362c349dbc7Sjsg 				   unsigned int x,
363c349dbc7Sjsg 				   unsigned int y)
364c349dbc7Sjsg {
3655ca02815Sjsg 	return (r->plane[n].src_stride * (r->plane[n].height - y - 1) +
366c349dbc7Sjsg 		r->plane[n].offset + x);
367c349dbc7Sjsg }
368c349dbc7Sjsg 
369c349dbc7Sjsg static struct scatterlist *
assert_rotated(struct drm_i915_gem_object * obj,const struct intel_rotation_info * r,unsigned int n,struct scatterlist * sg)370c349dbc7Sjsg assert_rotated(struct drm_i915_gem_object *obj,
371c349dbc7Sjsg 	       const struct intel_rotation_info *r, unsigned int n,
372c349dbc7Sjsg 	       struct scatterlist *sg)
373c349dbc7Sjsg {
374c349dbc7Sjsg 	unsigned int x, y;
375c349dbc7Sjsg 
376c349dbc7Sjsg 	for (x = 0; x < r->plane[n].width; x++) {
3775ca02815Sjsg 		unsigned int left;
3785ca02815Sjsg 
379c349dbc7Sjsg 		for (y = 0; y < r->plane[n].height; y++) {
380c349dbc7Sjsg 			unsigned long src_idx;
381c349dbc7Sjsg 			dma_addr_t src;
382c349dbc7Sjsg 
383c349dbc7Sjsg 			if (!sg) {
384c349dbc7Sjsg 				pr_err("Invalid sg table: too short at plane %d, (%d, %d)!\n",
385c349dbc7Sjsg 				       n, x, y);
386c349dbc7Sjsg 				return ERR_PTR(-EINVAL);
387c349dbc7Sjsg 			}
388c349dbc7Sjsg 
389c349dbc7Sjsg 			src_idx = rotated_index(r, n, x, y);
390c349dbc7Sjsg 			src = i915_gem_object_get_dma_address(obj, src_idx);
391c349dbc7Sjsg 
392c349dbc7Sjsg 			if (sg_dma_len(sg) != PAGE_SIZE) {
393c349dbc7Sjsg 				pr_err("Invalid sg.length, found %d, expected %lu for rotated page (%d, %d) [src index %lu]\n",
394c349dbc7Sjsg 				       sg_dma_len(sg), PAGE_SIZE,
395c349dbc7Sjsg 				       x, y, src_idx);
396c349dbc7Sjsg 				return ERR_PTR(-EINVAL);
397c349dbc7Sjsg 			}
398c349dbc7Sjsg 
399c349dbc7Sjsg 			if (sg_dma_address(sg) != src) {
400c349dbc7Sjsg 				pr_err("Invalid address for rotated page (%d, %d) [src index %lu]\n",
401c349dbc7Sjsg 				       x, y, src_idx);
402c349dbc7Sjsg 				return ERR_PTR(-EINVAL);
403c349dbc7Sjsg 			}
404c349dbc7Sjsg 
405c349dbc7Sjsg 			sg = sg_next(sg);
406c349dbc7Sjsg 		}
4075ca02815Sjsg 
4085ca02815Sjsg 		left = (r->plane[n].dst_stride - y) * PAGE_SIZE;
4095ca02815Sjsg 
4105ca02815Sjsg 		if (!left)
4115ca02815Sjsg 			continue;
4125ca02815Sjsg 
4135ca02815Sjsg 		if (!sg) {
4145ca02815Sjsg 			pr_err("Invalid sg table: too short at plane %d, (%d, %d)!\n",
4155ca02815Sjsg 			       n, x, y);
4165ca02815Sjsg 			return ERR_PTR(-EINVAL);
4175ca02815Sjsg 		}
4185ca02815Sjsg 
4195ca02815Sjsg 		if (sg_dma_len(sg) != left) {
4205ca02815Sjsg 			pr_err("Invalid sg.length, found %d, expected %u for rotated page (%d, %d)\n",
4215ca02815Sjsg 			       sg_dma_len(sg), left, x, y);
4225ca02815Sjsg 			return ERR_PTR(-EINVAL);
4235ca02815Sjsg 		}
4245ca02815Sjsg 
4255ca02815Sjsg 		if (sg_dma_address(sg) != 0) {
4265ca02815Sjsg 			pr_err("Invalid address, found %pad, expected 0 for remapped page (%d, %d)\n",
4275ca02815Sjsg 			       &sg_dma_address(sg), x, y);
4285ca02815Sjsg 			return ERR_PTR(-EINVAL);
4295ca02815Sjsg 		}
4305ca02815Sjsg 
4315ca02815Sjsg 		sg = sg_next(sg);
432c349dbc7Sjsg 	}
433c349dbc7Sjsg 
434c349dbc7Sjsg 	return sg;
435c349dbc7Sjsg }
436c349dbc7Sjsg 
remapped_index(const struct intel_remapped_info * r,unsigned int n,unsigned int x,unsigned int y)437c349dbc7Sjsg static unsigned long remapped_index(const struct intel_remapped_info *r,
438c349dbc7Sjsg 				    unsigned int n,
439c349dbc7Sjsg 				    unsigned int x,
440c349dbc7Sjsg 				    unsigned int y)
441c349dbc7Sjsg {
4425ca02815Sjsg 	return (r->plane[n].src_stride * y +
443c349dbc7Sjsg 		r->plane[n].offset + x);
444c349dbc7Sjsg }
445c349dbc7Sjsg 
446c349dbc7Sjsg static struct scatterlist *
assert_remapped(struct drm_i915_gem_object * obj,const struct intel_remapped_info * r,unsigned int n,struct scatterlist * sg)447c349dbc7Sjsg assert_remapped(struct drm_i915_gem_object *obj,
448c349dbc7Sjsg 		const struct intel_remapped_info *r, unsigned int n,
449c349dbc7Sjsg 		struct scatterlist *sg)
450c349dbc7Sjsg {
451c349dbc7Sjsg 	unsigned int x, y;
452c349dbc7Sjsg 	unsigned int left = 0;
453c349dbc7Sjsg 	unsigned int offset;
454c349dbc7Sjsg 
455c349dbc7Sjsg 	for (y = 0; y < r->plane[n].height; y++) {
456c349dbc7Sjsg 		for (x = 0; x < r->plane[n].width; x++) {
457c349dbc7Sjsg 			unsigned long src_idx;
458c349dbc7Sjsg 			dma_addr_t src;
459c349dbc7Sjsg 
460c349dbc7Sjsg 			if (!sg) {
461c349dbc7Sjsg 				pr_err("Invalid sg table: too short at plane %d, (%d, %d)!\n",
462c349dbc7Sjsg 				       n, x, y);
463c349dbc7Sjsg 				return ERR_PTR(-EINVAL);
464c349dbc7Sjsg 			}
465c349dbc7Sjsg 			if (!left) {
466c349dbc7Sjsg 				offset = 0;
467c349dbc7Sjsg 				left = sg_dma_len(sg);
468c349dbc7Sjsg 			}
469c349dbc7Sjsg 
470c349dbc7Sjsg 			src_idx = remapped_index(r, n, x, y);
471c349dbc7Sjsg 			src = i915_gem_object_get_dma_address(obj, src_idx);
472c349dbc7Sjsg 
473c349dbc7Sjsg 			if (left < PAGE_SIZE || left & (PAGE_SIZE-1)) {
474c349dbc7Sjsg 				pr_err("Invalid sg.length, found %d, expected %lu for remapped page (%d, %d) [src index %lu]\n",
475c349dbc7Sjsg 				       sg_dma_len(sg), PAGE_SIZE,
476c349dbc7Sjsg 				       x, y, src_idx);
477c349dbc7Sjsg 				return ERR_PTR(-EINVAL);
478c349dbc7Sjsg 			}
479c349dbc7Sjsg 
480c349dbc7Sjsg 			if (sg_dma_address(sg) + offset != src) {
481c349dbc7Sjsg 				pr_err("Invalid address for remapped page (%d, %d) [src index %lu]\n",
482c349dbc7Sjsg 				       x, y, src_idx);
483c349dbc7Sjsg 				return ERR_PTR(-EINVAL);
484c349dbc7Sjsg 			}
485c349dbc7Sjsg 
486c349dbc7Sjsg 			left -= PAGE_SIZE;
487c349dbc7Sjsg 			offset += PAGE_SIZE;
488c349dbc7Sjsg 
489c349dbc7Sjsg 
490c349dbc7Sjsg 			if (!left)
491c349dbc7Sjsg 				sg = sg_next(sg);
492c349dbc7Sjsg 		}
4935ca02815Sjsg 
4945ca02815Sjsg 		if (left) {
4955ca02815Sjsg 			pr_err("Unexpected sg tail with %d size for remapped page (%d, %d)\n",
4965ca02815Sjsg 			       left,
4975ca02815Sjsg 			       x, y);
4985ca02815Sjsg 			return ERR_PTR(-EINVAL);
4995ca02815Sjsg 		}
5005ca02815Sjsg 
5015ca02815Sjsg 		left = (r->plane[n].dst_stride - r->plane[n].width) * PAGE_SIZE;
5025ca02815Sjsg 
5035ca02815Sjsg 		if (!left)
5045ca02815Sjsg 			continue;
5055ca02815Sjsg 
5065ca02815Sjsg 		if (!sg) {
5075ca02815Sjsg 			pr_err("Invalid sg table: too short at plane %d, (%d, %d)!\n",
5085ca02815Sjsg 			       n, x, y);
5095ca02815Sjsg 			return ERR_PTR(-EINVAL);
5105ca02815Sjsg 		}
5115ca02815Sjsg 
5125ca02815Sjsg 		if (sg_dma_len(sg) != left) {
5135ca02815Sjsg 			pr_err("Invalid sg.length, found %u, expected %u for remapped page (%d, %d)\n",
5145ca02815Sjsg 			       sg_dma_len(sg), left,
5155ca02815Sjsg 			       x, y);
5165ca02815Sjsg 			return ERR_PTR(-EINVAL);
5175ca02815Sjsg 		}
5185ca02815Sjsg 
5195ca02815Sjsg 		if (sg_dma_address(sg) != 0) {
5205ca02815Sjsg 			pr_err("Invalid address, found %pad, expected 0 for remapped page (%d, %d)\n",
5215ca02815Sjsg 			       &sg_dma_address(sg),
5225ca02815Sjsg 			       x, y);
5235ca02815Sjsg 			return ERR_PTR(-EINVAL);
5245ca02815Sjsg 		}
5255ca02815Sjsg 
5265ca02815Sjsg 		sg = sg_next(sg);
5275ca02815Sjsg 		left = 0;
528c349dbc7Sjsg 	}
529c349dbc7Sjsg 
530c349dbc7Sjsg 	return sg;
531c349dbc7Sjsg }
532c349dbc7Sjsg 
remapped_size(enum i915_gtt_view_type view_type,const struct intel_remapped_plane_info * a,const struct intel_remapped_plane_info * b)533*1bb76ff1Sjsg static unsigned int remapped_size(enum i915_gtt_view_type view_type,
5345ca02815Sjsg 				  const struct intel_remapped_plane_info *a,
535c349dbc7Sjsg 				  const struct intel_remapped_plane_info *b)
536c349dbc7Sjsg {
5375ca02815Sjsg 
538*1bb76ff1Sjsg 	if (view_type == I915_GTT_VIEW_ROTATED)
5395ca02815Sjsg 		return a->dst_stride * a->width + b->dst_stride * b->width;
5405ca02815Sjsg 	else
5415ca02815Sjsg 		return a->dst_stride * a->height + b->dst_stride * b->height;
542c349dbc7Sjsg }
543c349dbc7Sjsg 
igt_vma_rotate_remap(void * arg)544c349dbc7Sjsg static int igt_vma_rotate_remap(void *arg)
545c349dbc7Sjsg {
546c349dbc7Sjsg 	struct i915_ggtt *ggtt = arg;
547c349dbc7Sjsg 	struct i915_address_space *vm = &ggtt->vm;
548c349dbc7Sjsg 	struct drm_i915_gem_object *obj;
549c349dbc7Sjsg 	const struct intel_remapped_plane_info planes[] = {
5505ca02815Sjsg 		{ .width = 1, .height = 1, .src_stride = 1 },
5515ca02815Sjsg 		{ .width = 2, .height = 2, .src_stride = 2 },
5525ca02815Sjsg 		{ .width = 4, .height = 4, .src_stride = 4 },
5535ca02815Sjsg 		{ .width = 8, .height = 8, .src_stride = 8 },
554c349dbc7Sjsg 
5555ca02815Sjsg 		{ .width = 3, .height = 5, .src_stride = 3 },
5565ca02815Sjsg 		{ .width = 3, .height = 5, .src_stride = 4 },
5575ca02815Sjsg 		{ .width = 3, .height = 5, .src_stride = 5 },
558c349dbc7Sjsg 
5595ca02815Sjsg 		{ .width = 5, .height = 3, .src_stride = 5 },
5605ca02815Sjsg 		{ .width = 5, .height = 3, .src_stride = 7 },
5615ca02815Sjsg 		{ .width = 5, .height = 3, .src_stride = 9 },
562c349dbc7Sjsg 
5635ca02815Sjsg 		{ .width = 4, .height = 6, .src_stride = 6 },
5645ca02815Sjsg 		{ .width = 6, .height = 4, .src_stride = 6 },
5655ca02815Sjsg 
5665ca02815Sjsg 		{ .width = 2, .height = 2, .src_stride = 2, .dst_stride = 2 },
5675ca02815Sjsg 		{ .width = 3, .height = 3, .src_stride = 3, .dst_stride = 4 },
5685ca02815Sjsg 		{ .width = 5, .height = 6, .src_stride = 7, .dst_stride = 8 },
5695ca02815Sjsg 
570c349dbc7Sjsg 		{ }
571c349dbc7Sjsg 	}, *a, *b;
572*1bb76ff1Sjsg 	enum i915_gtt_view_type types[] = {
573*1bb76ff1Sjsg 		I915_GTT_VIEW_ROTATED,
574*1bb76ff1Sjsg 		I915_GTT_VIEW_REMAPPED,
575c349dbc7Sjsg 		0,
576c349dbc7Sjsg 	}, *t;
577c349dbc7Sjsg 	const unsigned int max_pages = 64;
578c349dbc7Sjsg 	int err = -ENOMEM;
579c349dbc7Sjsg 
580c349dbc7Sjsg 	/* Create VMA for many different combinations of planes and check
581c349dbc7Sjsg 	 * that the page layout within the rotated VMA match our expectations.
582c349dbc7Sjsg 	 */
583c349dbc7Sjsg 
584c349dbc7Sjsg 	obj = i915_gem_object_create_internal(vm->i915, max_pages * PAGE_SIZE);
585c349dbc7Sjsg 	if (IS_ERR(obj))
586c349dbc7Sjsg 		goto out;
587c349dbc7Sjsg 
588c349dbc7Sjsg 	for (t = types; *t; t++) {
589c349dbc7Sjsg 	for (a = planes; a->width; a++) {
590c349dbc7Sjsg 		for (b = planes + ARRAY_SIZE(planes); b-- != planes; ) {
591*1bb76ff1Sjsg 			struct i915_gtt_view view = {
5925ca02815Sjsg 				.type = *t,
5935ca02815Sjsg 				.remapped.plane[0] = *a,
5945ca02815Sjsg 				.remapped.plane[1] = *b,
5955ca02815Sjsg 			};
5965ca02815Sjsg 			struct intel_remapped_plane_info *plane_info = view.remapped.plane;
597c349dbc7Sjsg 			unsigned int n, max_offset;
598c349dbc7Sjsg 
5995ca02815Sjsg 			max_offset = max(plane_info[0].src_stride * plane_info[0].height,
6005ca02815Sjsg 					 plane_info[1].src_stride * plane_info[1].height);
601c349dbc7Sjsg 			GEM_BUG_ON(max_offset > max_pages);
602c349dbc7Sjsg 			max_offset = max_pages - max_offset;
603c349dbc7Sjsg 
6045ca02815Sjsg 			if (!plane_info[0].dst_stride)
605*1bb76ff1Sjsg 				plane_info[0].dst_stride = view.type == I915_GTT_VIEW_ROTATED ?
6065ca02815Sjsg 									plane_info[0].height :
6075ca02815Sjsg 									plane_info[0].width;
6085ca02815Sjsg 			if (!plane_info[1].dst_stride)
609*1bb76ff1Sjsg 				plane_info[1].dst_stride = view.type == I915_GTT_VIEW_ROTATED ?
6105ca02815Sjsg 									plane_info[1].height :
6115ca02815Sjsg 									plane_info[1].width;
612c349dbc7Sjsg 
6135ca02815Sjsg 			for_each_prime_number_from(plane_info[0].offset, 0, max_offset) {
6145ca02815Sjsg 				for_each_prime_number_from(plane_info[1].offset, 0, max_offset) {
615c349dbc7Sjsg 					struct scatterlist *sg;
616c349dbc7Sjsg 					struct i915_vma *vma;
6175ca02815Sjsg 					unsigned int expected_pages;
618c349dbc7Sjsg 
619c349dbc7Sjsg 					vma = checked_vma_instance(obj, vm, &view);
620c349dbc7Sjsg 					if (IS_ERR(vma)) {
621c349dbc7Sjsg 						err = PTR_ERR(vma);
622c349dbc7Sjsg 						goto out_object;
623c349dbc7Sjsg 					}
624c349dbc7Sjsg 
625c349dbc7Sjsg 					err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
626c349dbc7Sjsg 					if (err) {
627c349dbc7Sjsg 						pr_err("Failed to pin VMA, err=%d\n", err);
628c349dbc7Sjsg 						goto out_object;
629c349dbc7Sjsg 					}
630c349dbc7Sjsg 
6315ca02815Sjsg 					expected_pages = remapped_size(view.type, &plane_info[0], &plane_info[1]);
6325ca02815Sjsg 
633*1bb76ff1Sjsg 					if (view.type == I915_GTT_VIEW_ROTATED &&
6345ca02815Sjsg 					    vma->size != expected_pages * PAGE_SIZE) {
635c349dbc7Sjsg 						pr_err("VMA is wrong size, expected %lu, found %llu\n",
6365ca02815Sjsg 						       PAGE_SIZE * expected_pages, vma->size);
637c349dbc7Sjsg 						err = -EINVAL;
638c349dbc7Sjsg 						goto out_object;
639c349dbc7Sjsg 					}
640c349dbc7Sjsg 
641*1bb76ff1Sjsg 					if (view.type == I915_GTT_VIEW_REMAPPED &&
6425ca02815Sjsg 					    vma->size > expected_pages * PAGE_SIZE) {
643c349dbc7Sjsg 						pr_err("VMA is wrong size, expected %lu, found %llu\n",
6445ca02815Sjsg 						       PAGE_SIZE * expected_pages, vma->size);
645c349dbc7Sjsg 						err = -EINVAL;
646c349dbc7Sjsg 						goto out_object;
647c349dbc7Sjsg 					}
648c349dbc7Sjsg 
6495ca02815Sjsg 					if (vma->pages->nents > expected_pages) {
650c349dbc7Sjsg 						pr_err("sg table is wrong sizeo, expected %u, found %u nents\n",
6515ca02815Sjsg 						       expected_pages, vma->pages->nents);
652c349dbc7Sjsg 						err = -EINVAL;
653c349dbc7Sjsg 						goto out_object;
654c349dbc7Sjsg 					}
655c349dbc7Sjsg 
656c349dbc7Sjsg 					if (vma->node.size < vma->size) {
657c349dbc7Sjsg 						pr_err("VMA binding too small, expected %llu, found %llu\n",
658c349dbc7Sjsg 						       vma->size, vma->node.size);
659c349dbc7Sjsg 						err = -EINVAL;
660c349dbc7Sjsg 						goto out_object;
661c349dbc7Sjsg 					}
662c349dbc7Sjsg 
663c349dbc7Sjsg 					if (vma->pages == obj->mm.pages) {
664c349dbc7Sjsg 						pr_err("VMA using unrotated object pages!\n");
665c349dbc7Sjsg 						err = -EINVAL;
666c349dbc7Sjsg 						goto out_object;
667c349dbc7Sjsg 					}
668c349dbc7Sjsg 
669c349dbc7Sjsg 					sg = vma->pages->sgl;
670c349dbc7Sjsg 					for (n = 0; n < ARRAY_SIZE(view.rotated.plane); n++) {
671*1bb76ff1Sjsg 						if (view.type == I915_GTT_VIEW_ROTATED)
672c349dbc7Sjsg 							sg = assert_rotated(obj, &view.rotated, n, sg);
673c349dbc7Sjsg 						else
674c349dbc7Sjsg 							sg = assert_remapped(obj, &view.remapped, n, sg);
675c349dbc7Sjsg 						if (IS_ERR(sg)) {
6765ca02815Sjsg 							pr_err("Inconsistent %s VMA pages for plane %d: [(%d, %d, %d, %d, %d), (%d, %d, %d, %d, %d)]\n",
677*1bb76ff1Sjsg 							       view.type == I915_GTT_VIEW_ROTATED ?
678c349dbc7Sjsg 							       "rotated" : "remapped", n,
6795ca02815Sjsg 							       plane_info[0].width,
6805ca02815Sjsg 							       plane_info[0].height,
6815ca02815Sjsg 							       plane_info[0].src_stride,
6825ca02815Sjsg 							       plane_info[0].dst_stride,
6835ca02815Sjsg 							       plane_info[0].offset,
6845ca02815Sjsg 							       plane_info[1].width,
6855ca02815Sjsg 							       plane_info[1].height,
6865ca02815Sjsg 							       plane_info[1].src_stride,
6875ca02815Sjsg 							       plane_info[1].dst_stride,
6885ca02815Sjsg 							       plane_info[1].offset);
689c349dbc7Sjsg 							err = -EINVAL;
690c349dbc7Sjsg 							goto out_object;
691c349dbc7Sjsg 						}
692c349dbc7Sjsg 					}
693c349dbc7Sjsg 
694c349dbc7Sjsg 					i915_vma_unpin(vma);
695*1bb76ff1Sjsg 					err = i915_vma_unbind_unlocked(vma);
696*1bb76ff1Sjsg 					if (err) {
697*1bb76ff1Sjsg 						pr_err("Unbinding returned %i\n", err);
698*1bb76ff1Sjsg 						goto out_object;
699*1bb76ff1Sjsg 					}
700c349dbc7Sjsg 					cond_resched();
701c349dbc7Sjsg 				}
702c349dbc7Sjsg 			}
703c349dbc7Sjsg 		}
704c349dbc7Sjsg 	}
705c349dbc7Sjsg 	}
706c349dbc7Sjsg 
707c349dbc7Sjsg out_object:
708c349dbc7Sjsg 	i915_gem_object_put(obj);
709c349dbc7Sjsg out:
710c349dbc7Sjsg 	return err;
711c349dbc7Sjsg }
712c349dbc7Sjsg 
assert_partial(struct drm_i915_gem_object * obj,struct i915_vma * vma,unsigned long offset,unsigned long size)713c349dbc7Sjsg static bool assert_partial(struct drm_i915_gem_object *obj,
714c349dbc7Sjsg 			   struct i915_vma *vma,
715c349dbc7Sjsg 			   unsigned long offset,
716c349dbc7Sjsg 			   unsigned long size)
717c349dbc7Sjsg {
718c349dbc7Sjsg 	struct sgt_iter sgt;
719c349dbc7Sjsg 	dma_addr_t dma;
720c349dbc7Sjsg 
721c349dbc7Sjsg 	for_each_sgt_daddr(dma, sgt, vma->pages) {
722c349dbc7Sjsg 		dma_addr_t src;
723c349dbc7Sjsg 
724c349dbc7Sjsg 		if (!size) {
725c349dbc7Sjsg 			pr_err("Partial scattergather list too long\n");
726c349dbc7Sjsg 			return false;
727c349dbc7Sjsg 		}
728c349dbc7Sjsg 
729c349dbc7Sjsg 		src = i915_gem_object_get_dma_address(obj, offset);
730c349dbc7Sjsg 		if (src != dma) {
731c349dbc7Sjsg 			pr_err("DMA mismatch for partial page offset %lu\n",
732c349dbc7Sjsg 			       offset);
733c349dbc7Sjsg 			return false;
734c349dbc7Sjsg 		}
735c349dbc7Sjsg 
736c349dbc7Sjsg 		offset++;
737c349dbc7Sjsg 		size--;
738c349dbc7Sjsg 	}
739c349dbc7Sjsg 
740c349dbc7Sjsg 	return true;
741c349dbc7Sjsg }
742c349dbc7Sjsg 
assert_pin(struct i915_vma * vma,struct i915_gtt_view * view,u64 size,const char * name)743c349dbc7Sjsg static bool assert_pin(struct i915_vma *vma,
744*1bb76ff1Sjsg 		       struct i915_gtt_view *view,
745c349dbc7Sjsg 		       u64 size,
746c349dbc7Sjsg 		       const char *name)
747c349dbc7Sjsg {
748c349dbc7Sjsg 	bool ok = true;
749c349dbc7Sjsg 
750c349dbc7Sjsg 	if (vma->size != size) {
751c349dbc7Sjsg 		pr_err("(%s) VMA is wrong size, expected %llu, found %llu\n",
752c349dbc7Sjsg 		       name, size, vma->size);
753c349dbc7Sjsg 		ok = false;
754c349dbc7Sjsg 	}
755c349dbc7Sjsg 
756c349dbc7Sjsg 	if (vma->node.size < vma->size) {
757c349dbc7Sjsg 		pr_err("(%s) VMA binding too small, expected %llu, found %llu\n",
758c349dbc7Sjsg 		       name, vma->size, vma->node.size);
759c349dbc7Sjsg 		ok = false;
760c349dbc7Sjsg 	}
761c349dbc7Sjsg 
762*1bb76ff1Sjsg 	if (view && view->type != I915_GTT_VIEW_NORMAL) {
763*1bb76ff1Sjsg 		if (memcmp(&vma->gtt_view, view, sizeof(*view))) {
764c349dbc7Sjsg 			pr_err("(%s) VMA mismatch upon creation!\n",
765c349dbc7Sjsg 			       name);
766c349dbc7Sjsg 			ok = false;
767c349dbc7Sjsg 		}
768c349dbc7Sjsg 
769c349dbc7Sjsg 		if (vma->pages == vma->obj->mm.pages) {
770c349dbc7Sjsg 			pr_err("(%s) VMA using original object pages!\n",
771c349dbc7Sjsg 			       name);
772c349dbc7Sjsg 			ok = false;
773c349dbc7Sjsg 		}
774c349dbc7Sjsg 	} else {
775*1bb76ff1Sjsg 		if (vma->gtt_view.type != I915_GTT_VIEW_NORMAL) {
776c349dbc7Sjsg 			pr_err("Not the normal ggtt view! Found %d\n",
777*1bb76ff1Sjsg 			       vma->gtt_view.type);
778c349dbc7Sjsg 			ok = false;
779c349dbc7Sjsg 		}
780c349dbc7Sjsg 
781c349dbc7Sjsg 		if (vma->pages != vma->obj->mm.pages) {
782c349dbc7Sjsg 			pr_err("VMA not using object pages!\n");
783c349dbc7Sjsg 			ok = false;
784c349dbc7Sjsg 		}
785c349dbc7Sjsg 	}
786c349dbc7Sjsg 
787c349dbc7Sjsg 	return ok;
788c349dbc7Sjsg }
789c349dbc7Sjsg 
igt_vma_partial(void * arg)790c349dbc7Sjsg static int igt_vma_partial(void *arg)
791c349dbc7Sjsg {
792c349dbc7Sjsg 	struct i915_ggtt *ggtt = arg;
793c349dbc7Sjsg 	struct i915_address_space *vm = &ggtt->vm;
794c349dbc7Sjsg 	const unsigned int npages = 1021; /* prime! */
795c349dbc7Sjsg 	struct drm_i915_gem_object *obj;
796c349dbc7Sjsg 	const struct phase {
797c349dbc7Sjsg 		const char *name;
798c349dbc7Sjsg 	} phases[] = {
799c349dbc7Sjsg 		{ "create" },
800c349dbc7Sjsg 		{ "lookup" },
801c349dbc7Sjsg 		{ },
802c349dbc7Sjsg 	}, *p;
803c349dbc7Sjsg 	unsigned int sz, offset;
804c349dbc7Sjsg 	struct i915_vma *vma;
805c349dbc7Sjsg 	int err = -ENOMEM;
806c349dbc7Sjsg 
807c349dbc7Sjsg 	/* Create lots of different VMA for the object and check that
808c349dbc7Sjsg 	 * we are returned the same VMA when we later request the same range.
809c349dbc7Sjsg 	 */
810c349dbc7Sjsg 
811c349dbc7Sjsg 	obj = i915_gem_object_create_internal(vm->i915, npages * PAGE_SIZE);
812c349dbc7Sjsg 	if (IS_ERR(obj))
813c349dbc7Sjsg 		goto out;
814c349dbc7Sjsg 
815c349dbc7Sjsg 	for (p = phases; p->name; p++) { /* exercise both create/lookup */
816c349dbc7Sjsg 		unsigned int count, nvma;
817c349dbc7Sjsg 
818c349dbc7Sjsg 		nvma = 0;
819c349dbc7Sjsg 		for_each_prime_number_from(sz, 1, npages) {
820c349dbc7Sjsg 			for_each_prime_number_from(offset, 0, npages - sz) {
821*1bb76ff1Sjsg 				struct i915_gtt_view view;
822c349dbc7Sjsg 
823*1bb76ff1Sjsg 				view.type = I915_GTT_VIEW_PARTIAL;
824c349dbc7Sjsg 				view.partial.offset = offset;
825c349dbc7Sjsg 				view.partial.size = sz;
826c349dbc7Sjsg 
827c349dbc7Sjsg 				if (sz == npages)
828*1bb76ff1Sjsg 					view.type = I915_GTT_VIEW_NORMAL;
829c349dbc7Sjsg 
830c349dbc7Sjsg 				vma = checked_vma_instance(obj, vm, &view);
831c349dbc7Sjsg 				if (IS_ERR(vma)) {
832c349dbc7Sjsg 					err = PTR_ERR(vma);
833c349dbc7Sjsg 					goto out_object;
834c349dbc7Sjsg 				}
835c349dbc7Sjsg 
836c349dbc7Sjsg 				err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
837c349dbc7Sjsg 				if (err)
838c349dbc7Sjsg 					goto out_object;
839c349dbc7Sjsg 
840c349dbc7Sjsg 				if (!assert_pin(vma, &view, sz*PAGE_SIZE, p->name)) {
841c349dbc7Sjsg 					pr_err("(%s) Inconsistent partial pinning for (offset=%d, size=%d)\n",
842c349dbc7Sjsg 					       p->name, offset, sz);
843c349dbc7Sjsg 					err = -EINVAL;
844c349dbc7Sjsg 					goto out_object;
845c349dbc7Sjsg 				}
846c349dbc7Sjsg 
847c349dbc7Sjsg 				if (!assert_partial(obj, vma, offset, sz)) {
848c349dbc7Sjsg 					pr_err("(%s) Inconsistent partial pages for (offset=%d, size=%d)\n",
849c349dbc7Sjsg 					       p->name, offset, sz);
850c349dbc7Sjsg 					err = -EINVAL;
851c349dbc7Sjsg 					goto out_object;
852c349dbc7Sjsg 				}
853c349dbc7Sjsg 
854c349dbc7Sjsg 				i915_vma_unpin(vma);
855c349dbc7Sjsg 				nvma++;
856*1bb76ff1Sjsg 				err = i915_vma_unbind_unlocked(vma);
857*1bb76ff1Sjsg 				if (err) {
858*1bb76ff1Sjsg 					pr_err("Unbinding returned %i\n", err);
859*1bb76ff1Sjsg 					goto out_object;
860*1bb76ff1Sjsg 				}
861c349dbc7Sjsg 
862c349dbc7Sjsg 				cond_resched();
863c349dbc7Sjsg 			}
864c349dbc7Sjsg 		}
865c349dbc7Sjsg 
866c349dbc7Sjsg 		count = 0;
867c349dbc7Sjsg 		list_for_each_entry(vma, &obj->vma.list, obj_link)
868c349dbc7Sjsg 			count++;
869c349dbc7Sjsg 		if (count != nvma) {
870c349dbc7Sjsg 			pr_err("(%s) All partial vma were not recorded on the obj->vma_list: found %u, expected %u\n",
871c349dbc7Sjsg 			       p->name, count, nvma);
872c349dbc7Sjsg 			err = -EINVAL;
873c349dbc7Sjsg 			goto out_object;
874c349dbc7Sjsg 		}
875c349dbc7Sjsg 
876c349dbc7Sjsg 		/* Check that we did create the whole object mapping */
877c349dbc7Sjsg 		vma = checked_vma_instance(obj, vm, NULL);
878c349dbc7Sjsg 		if (IS_ERR(vma)) {
879c349dbc7Sjsg 			err = PTR_ERR(vma);
880c349dbc7Sjsg 			goto out_object;
881c349dbc7Sjsg 		}
882c349dbc7Sjsg 
883c349dbc7Sjsg 		err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
884c349dbc7Sjsg 		if (err)
885c349dbc7Sjsg 			goto out_object;
886c349dbc7Sjsg 
887c349dbc7Sjsg 		if (!assert_pin(vma, NULL, obj->base.size, p->name)) {
888c349dbc7Sjsg 			pr_err("(%s) inconsistent full pin\n", p->name);
889c349dbc7Sjsg 			err = -EINVAL;
890c349dbc7Sjsg 			goto out_object;
891c349dbc7Sjsg 		}
892c349dbc7Sjsg 
893c349dbc7Sjsg 		i915_vma_unpin(vma);
894c349dbc7Sjsg 
895*1bb76ff1Sjsg 		err = i915_vma_unbind_unlocked(vma);
896*1bb76ff1Sjsg 		if (err) {
897*1bb76ff1Sjsg 			pr_err("Unbinding returned %i\n", err);
898*1bb76ff1Sjsg 			goto out_object;
899*1bb76ff1Sjsg 		}
900*1bb76ff1Sjsg 
901c349dbc7Sjsg 		count = 0;
902c349dbc7Sjsg 		list_for_each_entry(vma, &obj->vma.list, obj_link)
903c349dbc7Sjsg 			count++;
904c349dbc7Sjsg 		if (count != nvma) {
905c349dbc7Sjsg 			pr_err("(%s) allocated an extra full vma!\n", p->name);
906c349dbc7Sjsg 			err = -EINVAL;
907c349dbc7Sjsg 			goto out_object;
908c349dbc7Sjsg 		}
909c349dbc7Sjsg 	}
910c349dbc7Sjsg 
911c349dbc7Sjsg out_object:
912c349dbc7Sjsg 	i915_gem_object_put(obj);
913c349dbc7Sjsg out:
914c349dbc7Sjsg 	return err;
915c349dbc7Sjsg }
916c349dbc7Sjsg 
i915_vma_mock_selftests(void)917c349dbc7Sjsg int i915_vma_mock_selftests(void)
918c349dbc7Sjsg {
919c349dbc7Sjsg 	static const struct i915_subtest tests[] = {
920c349dbc7Sjsg 		SUBTEST(igt_vma_create),
921c349dbc7Sjsg 		SUBTEST(igt_vma_pin1),
922c349dbc7Sjsg 		SUBTEST(igt_vma_rotate_remap),
923c349dbc7Sjsg 		SUBTEST(igt_vma_partial),
924c349dbc7Sjsg 	};
925c349dbc7Sjsg 	struct drm_i915_private *i915;
926*1bb76ff1Sjsg 	struct intel_gt *gt;
927c349dbc7Sjsg 	int err;
928c349dbc7Sjsg 
929c349dbc7Sjsg 	i915 = mock_gem_device();
930c349dbc7Sjsg 	if (!i915)
931c349dbc7Sjsg 		return -ENOMEM;
932c349dbc7Sjsg 
933*1bb76ff1Sjsg 	/* allocate the ggtt */
934*1bb76ff1Sjsg 	err = intel_gt_assign_ggtt(to_gt(i915));
935*1bb76ff1Sjsg 	if (err)
936c349dbc7Sjsg 		goto out_put;
937c349dbc7Sjsg 
938*1bb76ff1Sjsg 	gt = to_gt(i915);
939*1bb76ff1Sjsg 
940*1bb76ff1Sjsg 	mock_init_ggtt(gt);
941*1bb76ff1Sjsg 
942*1bb76ff1Sjsg 	err = i915_subtests(tests, gt->ggtt);
943c349dbc7Sjsg 
944c349dbc7Sjsg 	mock_device_flush(i915);
945c349dbc7Sjsg 	i915_gem_drain_freed_objects(i915);
946*1bb76ff1Sjsg 	mock_fini_ggtt(gt->ggtt);
947*1bb76ff1Sjsg 
948c349dbc7Sjsg out_put:
949ad8b1aafSjsg 	mock_destroy_device(i915);
950c349dbc7Sjsg 	return err;
951c349dbc7Sjsg }
952c349dbc7Sjsg 
igt_vma_remapped_gtt(void * arg)953c349dbc7Sjsg static int igt_vma_remapped_gtt(void *arg)
954c349dbc7Sjsg {
955c349dbc7Sjsg 	struct drm_i915_private *i915 = arg;
956c349dbc7Sjsg 	const struct intel_remapped_plane_info planes[] = {
9575ca02815Sjsg 		{ .width = 1, .height = 1, .src_stride = 1 },
9585ca02815Sjsg 		{ .width = 2, .height = 2, .src_stride = 2 },
9595ca02815Sjsg 		{ .width = 4, .height = 4, .src_stride = 4 },
9605ca02815Sjsg 		{ .width = 8, .height = 8, .src_stride = 8 },
961c349dbc7Sjsg 
9625ca02815Sjsg 		{ .width = 3, .height = 5, .src_stride = 3 },
9635ca02815Sjsg 		{ .width = 3, .height = 5, .src_stride = 4 },
9645ca02815Sjsg 		{ .width = 3, .height = 5, .src_stride = 5 },
965c349dbc7Sjsg 
9665ca02815Sjsg 		{ .width = 5, .height = 3, .src_stride = 5 },
9675ca02815Sjsg 		{ .width = 5, .height = 3, .src_stride = 7 },
9685ca02815Sjsg 		{ .width = 5, .height = 3, .src_stride = 9 },
969c349dbc7Sjsg 
9705ca02815Sjsg 		{ .width = 4, .height = 6, .src_stride = 6 },
9715ca02815Sjsg 		{ .width = 6, .height = 4, .src_stride = 6 },
9725ca02815Sjsg 
9735ca02815Sjsg 		{ .width = 2, .height = 2, .src_stride = 2, .dst_stride = 2 },
9745ca02815Sjsg 		{ .width = 3, .height = 3, .src_stride = 3, .dst_stride = 4 },
9755ca02815Sjsg 		{ .width = 5, .height = 6, .src_stride = 7, .dst_stride = 8 },
9765ca02815Sjsg 
977c349dbc7Sjsg 		{ }
978c349dbc7Sjsg 	}, *p;
979*1bb76ff1Sjsg 	enum i915_gtt_view_type types[] = {
980*1bb76ff1Sjsg 		I915_GTT_VIEW_ROTATED,
981*1bb76ff1Sjsg 		I915_GTT_VIEW_REMAPPED,
982c349dbc7Sjsg 		0,
983c349dbc7Sjsg 	}, *t;
984c349dbc7Sjsg 	struct drm_i915_gem_object *obj;
985c349dbc7Sjsg 	intel_wakeref_t wakeref;
986c349dbc7Sjsg 	int err = 0;
987c349dbc7Sjsg 
988*1bb76ff1Sjsg 	if (!i915_ggtt_has_aperture(to_gt(i915)->ggtt))
9895ca02815Sjsg 		return 0;
9905ca02815Sjsg 
991c349dbc7Sjsg 	obj = i915_gem_object_create_internal(i915, 10 * 10 * PAGE_SIZE);
992c349dbc7Sjsg 	if (IS_ERR(obj))
993c349dbc7Sjsg 		return PTR_ERR(obj);
994c349dbc7Sjsg 
995c349dbc7Sjsg 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
996c349dbc7Sjsg 
997c349dbc7Sjsg 	for (t = types; *t; t++) {
998c349dbc7Sjsg 		for (p = planes; p->width; p++) {
999*1bb76ff1Sjsg 			struct i915_gtt_view view = {
1000c349dbc7Sjsg 				.type = *t,
1001c349dbc7Sjsg 				.rotated.plane[0] = *p,
1002c349dbc7Sjsg 			};
10035ca02815Sjsg 			struct intel_remapped_plane_info *plane_info = view.rotated.plane;
1004c349dbc7Sjsg 			struct i915_vma *vma;
1005c349dbc7Sjsg 			u32 __iomem *map;
1006c349dbc7Sjsg 			unsigned int x, y;
1007c349dbc7Sjsg 
1008ad8b1aafSjsg 			i915_gem_object_lock(obj, NULL);
1009c349dbc7Sjsg 			err = i915_gem_object_set_to_gtt_domain(obj, true);
1010c349dbc7Sjsg 			i915_gem_object_unlock(obj);
1011c349dbc7Sjsg 			if (err)
1012c349dbc7Sjsg 				goto out;
1013c349dbc7Sjsg 
10145ca02815Sjsg 			if (!plane_info[0].dst_stride)
1015*1bb76ff1Sjsg 				plane_info[0].dst_stride = *t == I915_GTT_VIEW_ROTATED ?
10165ca02815Sjsg 								 p->height : p->width;
10175ca02815Sjsg 
1018c349dbc7Sjsg 			vma = i915_gem_object_ggtt_pin(obj, &view, 0, 0, PIN_MAPPABLE);
1019c349dbc7Sjsg 			if (IS_ERR(vma)) {
1020c349dbc7Sjsg 				err = PTR_ERR(vma);
1021c349dbc7Sjsg 				goto out;
1022c349dbc7Sjsg 			}
1023c349dbc7Sjsg 
1024*1bb76ff1Sjsg 			GEM_BUG_ON(vma->gtt_view.type != *t);
1025c349dbc7Sjsg 
1026c349dbc7Sjsg 			map = i915_vma_pin_iomap(vma);
1027c349dbc7Sjsg 			i915_vma_unpin(vma);
1028c349dbc7Sjsg 			if (IS_ERR(map)) {
1029c349dbc7Sjsg 				err = PTR_ERR(map);
1030c349dbc7Sjsg 				goto out;
1031c349dbc7Sjsg 			}
1032c349dbc7Sjsg 
10335ca02815Sjsg 			for (y = 0 ; y < plane_info[0].height; y++) {
10345ca02815Sjsg 				for (x = 0 ; x < plane_info[0].width; x++) {
1035c349dbc7Sjsg 					unsigned int offset;
1036c349dbc7Sjsg 					u32 val = y << 16 | x;
1037c349dbc7Sjsg 
1038*1bb76ff1Sjsg 					if (*t == I915_GTT_VIEW_ROTATED)
10395ca02815Sjsg 						offset = (x * plane_info[0].dst_stride + y) * PAGE_SIZE;
1040c349dbc7Sjsg 					else
10415ca02815Sjsg 						offset = (y * plane_info[0].dst_stride + x) * PAGE_SIZE;
1042c349dbc7Sjsg 
1043c349dbc7Sjsg 					iowrite32(val, &map[offset / sizeof(*map)]);
1044c349dbc7Sjsg 				}
1045c349dbc7Sjsg 			}
1046c349dbc7Sjsg 
1047c349dbc7Sjsg 			i915_vma_unpin_iomap(vma);
1048c349dbc7Sjsg 
1049c349dbc7Sjsg 			vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
1050c349dbc7Sjsg 			if (IS_ERR(vma)) {
1051c349dbc7Sjsg 				err = PTR_ERR(vma);
1052c349dbc7Sjsg 				goto out;
1053c349dbc7Sjsg 			}
1054c349dbc7Sjsg 
1055*1bb76ff1Sjsg 			GEM_BUG_ON(vma->gtt_view.type != I915_GTT_VIEW_NORMAL);
1056c349dbc7Sjsg 
1057c349dbc7Sjsg 			map = i915_vma_pin_iomap(vma);
1058c349dbc7Sjsg 			i915_vma_unpin(vma);
1059c349dbc7Sjsg 			if (IS_ERR(map)) {
1060c349dbc7Sjsg 				err = PTR_ERR(map);
1061c349dbc7Sjsg 				goto out;
1062c349dbc7Sjsg 			}
1063c349dbc7Sjsg 
10645ca02815Sjsg 			for (y = 0 ; y < plane_info[0].height; y++) {
10655ca02815Sjsg 				for (x = 0 ; x < plane_info[0].width; x++) {
1066c349dbc7Sjsg 					unsigned int offset, src_idx;
1067c349dbc7Sjsg 					u32 exp = y << 16 | x;
1068c349dbc7Sjsg 					u32 val;
1069c349dbc7Sjsg 
1070*1bb76ff1Sjsg 					if (*t == I915_GTT_VIEW_ROTATED)
1071c349dbc7Sjsg 						src_idx = rotated_index(&view.rotated, 0, x, y);
1072c349dbc7Sjsg 					else
1073c349dbc7Sjsg 						src_idx = remapped_index(&view.remapped, 0, x, y);
1074c349dbc7Sjsg 					offset = src_idx * PAGE_SIZE;
1075c349dbc7Sjsg 
1076c349dbc7Sjsg 					val = ioread32(&map[offset / sizeof(*map)]);
1077c349dbc7Sjsg 					if (val != exp) {
1078c349dbc7Sjsg 						pr_err("%s VMA write test failed, expected 0x%x, found 0x%x\n",
1079*1bb76ff1Sjsg 						       *t == I915_GTT_VIEW_ROTATED ? "Rotated" : "Remapped",
10805ca02815Sjsg 						       exp, val);
1081c349dbc7Sjsg 						i915_vma_unpin_iomap(vma);
10825ca02815Sjsg 						err = -EINVAL;
1083c349dbc7Sjsg 						goto out;
1084c349dbc7Sjsg 					}
1085c349dbc7Sjsg 				}
1086c349dbc7Sjsg 			}
1087c349dbc7Sjsg 			i915_vma_unpin_iomap(vma);
1088c349dbc7Sjsg 
1089c349dbc7Sjsg 			cond_resched();
1090c349dbc7Sjsg 		}
1091c349dbc7Sjsg 	}
1092c349dbc7Sjsg 
1093c349dbc7Sjsg out:
1094c349dbc7Sjsg 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
1095c349dbc7Sjsg 	i915_gem_object_put(obj);
1096c349dbc7Sjsg 
1097c349dbc7Sjsg 	return err;
1098c349dbc7Sjsg }
1099c349dbc7Sjsg 
i915_vma_live_selftests(struct drm_i915_private * i915)1100c349dbc7Sjsg int i915_vma_live_selftests(struct drm_i915_private *i915)
1101c349dbc7Sjsg {
1102c349dbc7Sjsg 	static const struct i915_subtest tests[] = {
1103c349dbc7Sjsg 		SUBTEST(igt_vma_remapped_gtt),
1104c349dbc7Sjsg 	};
1105c349dbc7Sjsg 
1106*1bb76ff1Sjsg 	return i915_live_subtests(tests, i915);
1107c349dbc7Sjsg }
1108