1 /*
2  * Copyright © 2016 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  */
24 
25 #include "../i915_selftest.h"
26 
27 #include "mock_gem_device.h"
28 
29 static int populate_ggtt(struct drm_i915_private *i915)
30 {
31 	struct drm_i915_gem_object *obj;
32 	u64 size;
33 
34 	for (size = 0;
35 	     size + I915_GTT_PAGE_SIZE <= i915->ggtt.base.total;
36 	     size += I915_GTT_PAGE_SIZE) {
37 		struct i915_vma *vma;
38 
39 		obj = i915_gem_object_create_internal(i915, I915_GTT_PAGE_SIZE);
40 		if (IS_ERR(obj))
41 			return PTR_ERR(obj);
42 
43 		vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
44 		if (IS_ERR(vma))
45 			return PTR_ERR(vma);
46 	}
47 
48 	if (!list_empty(&i915->mm.unbound_list)) {
49 		size = 0;
50 		list_for_each_entry(obj, &i915->mm.unbound_list, global_link)
51 			size++;
52 
53 		pr_err("Found %lld objects unbound!\n", size);
54 		return -EINVAL;
55 	}
56 
57 	if (list_empty(&i915->ggtt.base.inactive_list)) {
58 		pr_err("No objects on the GGTT inactive list!\n");
59 		return -EINVAL;
60 	}
61 
62 	return 0;
63 }
64 
65 static void unpin_ggtt(struct drm_i915_private *i915)
66 {
67 	struct i915_vma *vma;
68 
69 	list_for_each_entry(vma, &i915->ggtt.base.inactive_list, vm_link)
70 		i915_vma_unpin(vma);
71 }
72 
73 static void cleanup_objects(struct drm_i915_private *i915)
74 {
75 	struct drm_i915_gem_object *obj, *on;
76 
77 	list_for_each_entry_safe(obj, on, &i915->mm.unbound_list, global_link)
78 		i915_gem_object_put(obj);
79 
80 	list_for_each_entry_safe(obj, on, &i915->mm.bound_list, global_link)
81 		i915_gem_object_put(obj);
82 
83 	mutex_unlock(&i915->drm.struct_mutex);
84 
85 	i915_gem_drain_freed_objects(i915);
86 
87 	mutex_lock(&i915->drm.struct_mutex);
88 }
89 
90 static int igt_evict_something(void *arg)
91 {
92 	struct drm_i915_private *i915 = arg;
93 	struct i915_ggtt *ggtt = &i915->ggtt;
94 	int err;
95 
96 	/* Fill the GGTT with pinned objects and try to evict one. */
97 
98 	err = populate_ggtt(i915);
99 	if (err)
100 		goto cleanup;
101 
102 	/* Everything is pinned, nothing should happen */
103 	err = i915_gem_evict_something(&ggtt->base,
104 				       I915_GTT_PAGE_SIZE, 0, 0,
105 				       0, U64_MAX,
106 				       0);
107 	if (err != -ENOSPC) {
108 		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
109 		       err);
110 		goto cleanup;
111 	}
112 
113 	unpin_ggtt(i915);
114 
115 	/* Everything is unpinned, we should be able to evict something */
116 	err = i915_gem_evict_something(&ggtt->base,
117 				       I915_GTT_PAGE_SIZE, 0, 0,
118 				       0, U64_MAX,
119 				       0);
120 	if (err) {
121 		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
122 		       err);
123 		goto cleanup;
124 	}
125 
126 cleanup:
127 	cleanup_objects(i915);
128 	return err;
129 }
130 
131 static int igt_overcommit(void *arg)
132 {
133 	struct drm_i915_private *i915 = arg;
134 	struct drm_i915_gem_object *obj;
135 	struct i915_vma *vma;
136 	int err;
137 
138 	/* Fill the GGTT with pinned objects and then try to pin one more.
139 	 * We expect it to fail.
140 	 */
141 
142 	err = populate_ggtt(i915);
143 	if (err)
144 		goto cleanup;
145 
146 	obj = i915_gem_object_create_internal(i915, I915_GTT_PAGE_SIZE);
147 	if (IS_ERR(obj)) {
148 		err = PTR_ERR(obj);
149 		goto cleanup;
150 	}
151 
152 	list_move(&obj->global_link, &i915->mm.unbound_list);
153 
154 	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
155 	if (!IS_ERR(vma) || PTR_ERR(vma) != -ENOSPC) {
156 		pr_err("Failed to evict+insert, i915_gem_object_ggtt_pin returned err=%d\n", (int)PTR_ERR(vma));
157 		err = -EINVAL;
158 		goto cleanup;
159 	}
160 
161 cleanup:
162 	cleanup_objects(i915);
163 	return err;
164 }
165 
166 static int igt_evict_for_vma(void *arg)
167 {
168 	struct drm_i915_private *i915 = arg;
169 	struct i915_ggtt *ggtt = &i915->ggtt;
170 	struct drm_mm_node target = {
171 		.start = 0,
172 		.size = 4096,
173 	};
174 	int err;
175 
176 	/* Fill the GGTT with pinned objects and try to evict a range. */
177 
178 	err = populate_ggtt(i915);
179 	if (err)
180 		goto cleanup;
181 
182 	/* Everything is pinned, nothing should happen */
183 	err = i915_gem_evict_for_node(&ggtt->base, &target, 0);
184 	if (err != -ENOSPC) {
185 		pr_err("i915_gem_evict_for_node on a full GGTT returned err=%d\n",
186 		       err);
187 		goto cleanup;
188 	}
189 
190 	unpin_ggtt(i915);
191 
192 	/* Everything is unpinned, we should be able to evict the node */
193 	err = i915_gem_evict_for_node(&ggtt->base, &target, 0);
194 	if (err) {
195 		pr_err("i915_gem_evict_for_node returned err=%d\n",
196 		       err);
197 		goto cleanup;
198 	}
199 
200 cleanup:
201 	cleanup_objects(i915);
202 	return err;
203 }
204 
205 static void mock_color_adjust(const struct drm_mm_node *node,
206 			      unsigned long color,
207 			      u64 *start,
208 			      u64 *end)
209 {
210 }
211 
212 static int igt_evict_for_cache_color(void *arg)
213 {
214 	struct drm_i915_private *i915 = arg;
215 	struct i915_ggtt *ggtt = &i915->ggtt;
216 	const unsigned long flags = PIN_OFFSET_FIXED;
217 	struct drm_mm_node target = {
218 		.start = I915_GTT_PAGE_SIZE * 2,
219 		.size = I915_GTT_PAGE_SIZE,
220 		.color = I915_CACHE_LLC,
221 	};
222 	struct drm_i915_gem_object *obj;
223 	struct i915_vma *vma;
224 	int err;
225 
226 	/* Currently the use of color_adjust is limited to cache domains within
227 	 * the ggtt, and so the presence of mm.color_adjust is assumed to be
228 	 * i915_gtt_color_adjust throughout our driver, so using a mock color
229 	 * adjust will work just fine for our purposes.
230 	 */
231 	ggtt->base.mm.color_adjust = mock_color_adjust;
232 
233 	obj = i915_gem_object_create_internal(i915, I915_GTT_PAGE_SIZE);
234 	if (IS_ERR(obj)) {
235 		err = PTR_ERR(obj);
236 		goto cleanup;
237 	}
238 	i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
239 
240 	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
241 				       I915_GTT_PAGE_SIZE | flags);
242 	if (IS_ERR(vma)) {
243 		pr_err("[0]i915_gem_object_ggtt_pin failed\n");
244 		err = PTR_ERR(vma);
245 		goto cleanup;
246 	}
247 
248 	obj = i915_gem_object_create_internal(i915, I915_GTT_PAGE_SIZE);
249 	if (IS_ERR(obj)) {
250 		err = PTR_ERR(obj);
251 		goto cleanup;
252 	}
253 	i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
254 
255 	/* Neighbouring; same colour - should fit */
256 	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
257 				       (I915_GTT_PAGE_SIZE * 2) | flags);
258 	if (IS_ERR(vma)) {
259 		pr_err("[1]i915_gem_object_ggtt_pin failed\n");
260 		err = PTR_ERR(vma);
261 		goto cleanup;
262 	}
263 
264 	i915_vma_unpin(vma);
265 
266 	/* Remove just the second vma */
267 	err = i915_gem_evict_for_node(&ggtt->base, &target, 0);
268 	if (err) {
269 		pr_err("[0]i915_gem_evict_for_node returned err=%d\n", err);
270 		goto cleanup;
271 	}
272 
273 	/* Attempt to remove the first *pinned* vma, by removing the (empty)
274 	 * neighbour -- this should fail.
275 	 */
276 	target.color = I915_CACHE_L3_LLC;
277 
278 	err = i915_gem_evict_for_node(&ggtt->base, &target, 0);
279 	if (!err) {
280 		pr_err("[1]i915_gem_evict_for_node returned err=%d\n", err);
281 		err = -EINVAL;
282 		goto cleanup;
283 	}
284 
285 	err = 0;
286 
287 cleanup:
288 	unpin_ggtt(i915);
289 	cleanup_objects(i915);
290 	ggtt->base.mm.color_adjust = NULL;
291 	return err;
292 }
293 
294 static int igt_evict_vm(void *arg)
295 {
296 	struct drm_i915_private *i915 = arg;
297 	struct i915_ggtt *ggtt = &i915->ggtt;
298 	int err;
299 
300 	/* Fill the GGTT with pinned objects and try to evict everything. */
301 
302 	err = populate_ggtt(i915);
303 	if (err)
304 		goto cleanup;
305 
306 	/* Everything is pinned, nothing should happen */
307 	err = i915_gem_evict_vm(&ggtt->base);
308 	if (err) {
309 		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
310 		       err);
311 		goto cleanup;
312 	}
313 
314 	unpin_ggtt(i915);
315 
316 	err = i915_gem_evict_vm(&ggtt->base);
317 	if (err) {
318 		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
319 		       err);
320 		goto cleanup;
321 	}
322 
323 cleanup:
324 	cleanup_objects(i915);
325 	return err;
326 }
327 
328 int i915_gem_evict_mock_selftests(void)
329 {
330 	static const struct i915_subtest tests[] = {
331 		SUBTEST(igt_evict_something),
332 		SUBTEST(igt_evict_for_vma),
333 		SUBTEST(igt_evict_for_cache_color),
334 		SUBTEST(igt_evict_vm),
335 		SUBTEST(igt_overcommit),
336 	};
337 	struct drm_i915_private *i915;
338 	int err;
339 
340 	i915 = mock_gem_device();
341 	if (!i915)
342 		return -ENOMEM;
343 
344 	mutex_lock(&i915->drm.struct_mutex);
345 	err = i915_subtests(tests, i915);
346 	mutex_unlock(&i915->drm.struct_mutex);
347 
348 	drm_dev_unref(&i915->drm);
349 	return err;
350 }
351