1 /*
2 * Copyright © 2017 Broadcom
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 #include "igt.h"
25 #include "igt_vc4.h"
26 #include <unistd.h>
27 #include <signal.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <inttypes.h>
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/ioctl.h>
36 #include "vc4_drm.h"
37
38 struct igt_vc4_bo {
39 struct igt_list node;
40 int handle;
41 void *map;
42 size_t size;
43 };
44
45 static jmp_buf jmp;
46
sigtrap(int sig)47 static void __attribute__((noreturn)) sigtrap(int sig)
48 {
49 longjmp(jmp, sig);
50 }
51
igt_vc4_alloc_mmap_max_bo(int fd,struct igt_list * list,size_t size)52 static void igt_vc4_alloc_mmap_max_bo(int fd, struct igt_list *list,
53 size_t size)
54 {
55 struct igt_vc4_bo *bo;
56 struct drm_vc4_create_bo create = {
57 .size = size,
58 };
59
60 while (true) {
61 if (igt_ioctl(fd, DRM_IOCTL_VC4_CREATE_BO, &create))
62 break;
63
64 bo = malloc(sizeof(*bo));
65 igt_assert(bo);
66 bo->handle = create.handle;
67 bo->size = create.size;
68 bo->map = igt_vc4_mmap_bo(fd, bo->handle, bo->size,
69 PROT_READ | PROT_WRITE);
70 igt_list_add_tail(&bo->node, list);
71 }
72 }
73
igt_vc4_unmap_free_bo_pool(int fd,struct igt_list * list)74 static void igt_vc4_unmap_free_bo_pool(int fd, struct igt_list *list)
75 {
76 struct igt_vc4_bo *bo;
77
78 while (!igt_list_empty(list)) {
79 bo = igt_list_first_entry(list, bo, node);
80 igt_assert(bo);
81 igt_list_del(&bo->node);
82 munmap(bo->map, bo->size);
83 gem_close(fd, bo->handle);
84 free(bo);
85 }
86 }
87
igt_vc4_trigger_purge(int fd)88 static void igt_vc4_trigger_purge(int fd)
89 {
90 struct igt_list list;
91
92 igt_list_init(&list);
93
94 /* Try to allocate as much as we can to trigger a purge. */
95 igt_vc4_alloc_mmap_max_bo(fd, &list, 64 * 1024);
96 igt_assert(!igt_list_empty(&list));
97 igt_vc4_unmap_free_bo_pool(fd, &list);
98 }
99
igt_vc4_purgeable_subtest_prepare(int fd,struct igt_list * list)100 static void igt_vc4_purgeable_subtest_prepare(int fd, struct igt_list *list)
101 {
102 igt_vc4_unmap_free_bo_pool(fd, list);
103 igt_vc4_alloc_mmap_max_bo(fd, list, 64 * 1024);
104 igt_assert(!igt_list_empty(list));
105 }
106
107 igt_main
108 {
109 struct igt_vc4_bo *bo;
110 struct igt_list list;
111 uint32_t *map;
112 int fd, ret;
113
114 igt_fixture {
115 uint64_t val = 0;
116
117 fd = drm_open_driver(DRIVER_VC4);
118 igt_vc4_get_param(fd, DRM_VC4_PARAM_SUPPORTS_MADVISE, &val);
119 igt_require(val);
120 igt_list_init(&list);
121 }
122
123 igt_subtest("mark-willneed") {
124 igt_vc4_purgeable_subtest_prepare(fd, &list);
125 igt_list_for_each(bo, &list, node)
126 igt_assert(igt_vc4_purgeable_bo(fd, bo->handle,
127 false));
128 }
129
130 igt_subtest("mark-purgeable") {
131 igt_vc4_purgeable_subtest_prepare(fd, &list);
132 igt_list_for_each(bo, &list, node)
133 igt_vc4_purgeable_bo(fd, bo->handle, true);
134
135 igt_list_for_each(bo, &list, node)
136 igt_vc4_purgeable_bo(fd, bo->handle, false);
137 }
138
139 igt_subtest("mark-purgeable-twice") {
140 igt_vc4_purgeable_subtest_prepare(fd, &list);
141 bo = igt_list_first_entry(&list, bo, node);
142 igt_vc4_purgeable_bo(fd, bo->handle, true);
143 igt_vc4_purgeable_bo(fd, bo->handle, true);
144 igt_vc4_purgeable_bo(fd, bo->handle, false);
145 }
146
147 igt_subtest("mark-unpurgeable-twice") {
148 igt_vc4_purgeable_subtest_prepare(fd, &list);
149 bo = igt_list_first_entry(&list, bo, node);
150 igt_vc4_purgeable_bo(fd, bo->handle, true);
151 igt_vc4_purgeable_bo(fd, bo->handle, false);
152 igt_vc4_purgeable_bo(fd, bo->handle, false);
153 }
154
155 igt_subtest("access-purgeable-bo-mem") {
156 igt_vc4_purgeable_subtest_prepare(fd, &list);
157 bo = igt_list_first_entry(&list, bo, node);
158 map = (uint32_t *)bo->map;
159
160 /* Mark the BO as purgeable, but do not try to allocate a new
161 * BO. This should leave the BO in a non-purged state unless
162 * someone else tries to allocated a new BO.
163 */
164 igt_vc4_purgeable_bo(fd, bo->handle, true);
165
166 /* Accessing a purgeable BO should generate a SIGBUS event if
167 * the BO has been purged by the system in the meantime.
168 */
169 signal(SIGSEGV, sigtrap);
170 signal(SIGBUS, sigtrap);
171 ret = setjmp(jmp);
172 if (!ret)
173 *map = 0xdeadbeef;
174 else
175 igt_assert(ret == SIGBUS);
176 signal(SIGBUS, SIG_DFL);
177 signal(SIGSEGV, SIG_DFL);
178 }
179
180 igt_subtest("access-purged-bo-mem") {
181 igt_vc4_purgeable_subtest_prepare(fd, &list);
182
183 /* Mark the first BO in our list as purgeable and try to
184 * allocate a new one. This should trigger a purge and render
185 * the first BO inaccessible.
186 */
187 bo = igt_list_first_entry(&list, bo, node);
188 map = (uint32_t *)bo->map;
189 igt_vc4_purgeable_bo(fd, bo->handle, true);
190
191 /* Trigger a purge. */
192 igt_vc4_trigger_purge(fd);
193
194 /* Accessing a purged BO should generate a SIGBUS event. */
195 signal(SIGSEGV, sigtrap);
196 signal(SIGBUS, sigtrap);
197 ret = setjmp(jmp);
198 if (!ret)
199 *map = 0;
200 igt_assert(ret == SIGBUS);
201 signal(SIGBUS, SIG_DFL);
202 signal(SIGSEGV, SIG_DFL);
203 igt_vc4_purgeable_bo(fd, bo->handle, false);
204 }
205
206 igt_subtest("mark-unpurgeable-check-retained") {
207 igt_vc4_purgeable_subtest_prepare(fd, &list);
208 igt_list_for_each(bo, &list, node) {
209 map = (uint32_t *)bo->map;
210 *map = 0xdeadbeef;
211 igt_vc4_purgeable_bo(fd, bo->handle, true);
212 }
213
214 igt_list_for_each(bo, &list, node) {
215 map = (uint32_t *)bo->map;
216 if (igt_vc4_purgeable_bo(fd, bo->handle, false))
217 igt_assert(*map == 0xdeadbeef);
218 }
219 }
220
221 igt_subtest("mark-unpurgeable-purged") {
222 igt_vc4_purgeable_subtest_prepare(fd, &list);
223
224 igt_list_for_each(bo, &list, node)
225 igt_vc4_purgeable_bo(fd, bo->handle, true);
226
227 /* Trigger a purge. */
228 igt_vc4_trigger_purge(fd);
229
230 bo = igt_list_first_entry(&list, bo, node);
231 map = (uint32_t *)bo->map;
232
233 igt_assert(!igt_vc4_purgeable_bo(fd, bo->handle, false));
234
235 /* Purged BOs are unusable and any access to their
236 * mmap-ed region should trigger a SIGBUS.
237 */
238 signal(SIGSEGV, sigtrap);
239 signal(SIGBUS, sigtrap);
240 ret = setjmp(jmp);
241 if (!ret)
242 *map = 0;
243 igt_assert(ret == SIGBUS);
244 signal(SIGBUS, SIG_DFL);
245 signal(SIGSEGV, SIG_DFL);
246 }
247
248 igt_subtest("free-purged-bo") {
249 igt_vc4_purgeable_subtest_prepare(fd, &list);
250 bo = igt_list_first_entry(&list, bo, node);
251 igt_vc4_purgeable_bo(fd, bo->handle, true);
252
253 /* Trigger a purge. */
254 igt_vc4_trigger_purge(fd);
255
256 igt_list_del(&bo->node);
257 munmap(bo->map, bo->size);
258 gem_close(fd, bo->handle);
259 free(bo);
260 }
261
262 igt_fixture
263 close(fd);
264 }
265