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