GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / gpu / drm / i915 / selftests / i915_gem_evict.c
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 }