vc4: Fix return value handling for BO waits.
[mesa.git] / src / gallium / drivers / vc4 / vc4_bufmgr.c
1 /*
2 * Copyright © 2014 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 <errno.h>
25 #include <err.h>
26 #include <sys/mman.h>
27 #include <fcntl.h>
28 #include <xf86drm.h>
29 #include <xf86drmMode.h>
30
31 #include "util/u_memory.h"
32 #include "util/ralloc.h"
33
34 #include "vc4_context.h"
35 #include "vc4_screen.h"
36
37 #define container_of(ptr, type, field) \
38 (type*)((char*)ptr - offsetof(type, field))
39
40 static struct vc4_bo *
41 vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name)
42 {
43 struct vc4_bo_cache *cache = &screen->bo_cache;
44 uint32_t page_index = size / 4096 - 1;
45
46 if (cache->size_list_size <= page_index)
47 return NULL;
48
49 struct vc4_bo *bo = NULL;
50 pipe_mutex_lock(cache->lock);
51 if (!is_empty_list(&cache->size_list[page_index])) {
52 struct simple_node *node = last_elem(&cache->size_list[page_index]);
53 bo = container_of(node, struct vc4_bo, size_list);
54 pipe_reference_init(&bo->reference, 1);
55 remove_from_list(&bo->time_list);
56 remove_from_list(&bo->size_list);
57
58 bo->name = name;
59 }
60 pipe_mutex_unlock(cache->lock);
61 return bo;
62 }
63
64 struct vc4_bo *
65 vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name)
66 {
67 struct vc4_bo *bo;
68 int ret;
69
70 size = align(size, 4096);
71
72 bo = vc4_bo_from_cache(screen, size, name);
73 if (bo)
74 return bo;
75
76 bo = CALLOC_STRUCT(vc4_bo);
77 if (!bo)
78 return NULL;
79
80 pipe_reference_init(&bo->reference, 1);
81 bo->screen = screen;
82 bo->size = size;
83 bo->name = name;
84 bo->private = true;
85
86 if (!using_vc4_simulator) {
87 struct drm_vc4_create_bo create;
88 memset(&create, 0, sizeof(create));
89
90 create.size = size;
91
92 ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_CREATE_BO, &create);
93 bo->handle = create.handle;
94 } else {
95 struct drm_mode_create_dumb create;
96 memset(&create, 0, sizeof(create));
97
98 create.width = 128;
99 create.bpp = 8;
100 create.height = (size + 127) / 128;
101
102 ret = drmIoctl(screen->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
103 bo->handle = create.handle;
104 assert(create.size >= size);
105 }
106 if (ret != 0) {
107 fprintf(stderr, "create ioctl failure\n");
108 abort();
109 }
110
111 return bo;
112 }
113
114 void
115 vc4_bo_last_unreference(struct vc4_bo *bo)
116 {
117 struct vc4_screen *screen = bo->screen;
118
119 struct timespec time;
120 clock_gettime(CLOCK_MONOTONIC, &time);
121 pipe_mutex_lock(screen->bo_cache.lock);
122 vc4_bo_last_unreference_locked_timed(bo, time.tv_sec);
123 pipe_mutex_unlock(screen->bo_cache.lock);
124 }
125
126 static void
127 vc4_bo_free(struct vc4_bo *bo)
128 {
129 struct vc4_screen *screen = bo->screen;
130
131 if (bo->map) {
132 #ifdef USE_VC4_SIMULATOR
133 if (bo->simulator_winsys_map) {
134 free(bo->map);
135 bo->map = bo->simulator_winsys_map;
136 }
137 #endif
138 munmap(bo->map, bo->size);
139 }
140
141 struct drm_gem_close c;
142 memset(&c, 0, sizeof(c));
143 c.handle = bo->handle;
144 int ret = drmIoctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);
145 if (ret != 0)
146 fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));
147
148 free(bo);
149 }
150
151 static void
152 free_stale_bos(struct vc4_screen *screen, time_t time)
153 {
154 while (!is_empty_list(&screen->bo_cache.time_list)) {
155 struct simple_node *node =
156 first_elem(&screen->bo_cache.time_list);
157 struct vc4_bo *bo = container_of(node, struct vc4_bo, time_list);
158
159 /* If it's more than a second old, free it. */
160 if (time - bo->free_time > 2) {
161 remove_from_list(&bo->time_list);
162 remove_from_list(&bo->size_list);
163 vc4_bo_free(bo);
164 } else {
165 break;
166 }
167 }
168 }
169
170 void
171 vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time)
172 {
173 struct vc4_screen *screen = bo->screen;
174 struct vc4_bo_cache *cache = &screen->bo_cache;
175 uint32_t page_index = bo->size / 4096 - 1;
176
177 if (!bo->private) {
178 vc4_bo_free(bo);
179 return;
180 }
181
182 if (cache->size_list_size <= page_index) {
183 struct simple_node *new_list =
184 ralloc_array(screen, struct simple_node, page_index + 1);
185
186 /* Move old list contents over (since the array has moved, and
187 * therefore the pointers to the list heads have to change.
188 */
189 for (int i = 0; i < cache->size_list_size; i++) {
190 struct simple_node *old_head = &cache->size_list[i];
191 if (is_empty_list(old_head))
192 make_empty_list(&new_list[i]);
193 else {
194 new_list[i].next = old_head->next;
195 new_list[i].prev = old_head->prev;
196 new_list[i].next->prev = &new_list[i];
197 new_list[i].prev->next = &new_list[i];
198 }
199 }
200 for (int i = cache->size_list_size; i < page_index + 1; i++)
201 make_empty_list(&new_list[i]);
202
203 cache->size_list = new_list;
204 cache->size_list_size = page_index + 1;
205 }
206
207 bo->free_time = time;
208 insert_at_tail(&cache->size_list[page_index], &bo->size_list);
209 insert_at_tail(&cache->time_list, &bo->time_list);
210
211 free_stale_bos(screen, time);
212 }
213
214 static struct vc4_bo *
215 vc4_bo_open_handle(struct vc4_screen *screen,
216 uint32_t winsys_stride,
217 uint32_t handle, uint32_t size)
218 {
219 struct vc4_bo *bo = CALLOC_STRUCT(vc4_bo);
220
221 assert(size);
222
223 pipe_reference_init(&bo->reference, 1);
224 bo->screen = screen;
225 bo->handle = handle;
226 bo->size = size;
227 bo->name = "winsys";
228 bo->private = false;
229
230 #ifdef USE_VC4_SIMULATOR
231 vc4_bo_map(bo);
232 bo->simulator_winsys_map = bo->map;
233 bo->simulator_winsys_stride = winsys_stride;
234 bo->map = malloc(bo->size);
235 #endif
236
237 return bo;
238 }
239
240 struct vc4_bo *
241 vc4_bo_open_name(struct vc4_screen *screen, uint32_t name,
242 uint32_t winsys_stride)
243 {
244 struct drm_gem_open o = {
245 .name = name
246 };
247 int ret = drmIoctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o);
248 if (ret) {
249 fprintf(stderr, "Failed to open bo %d: %s\n",
250 name, strerror(errno));
251 return NULL;
252 }
253
254 return vc4_bo_open_handle(screen, winsys_stride, o.handle, o.size);
255 }
256
257 struct vc4_bo *
258 vc4_bo_open_dmabuf(struct vc4_screen *screen, int fd, uint32_t winsys_stride)
259 {
260 uint32_t handle;
261 int ret = drmPrimeFDToHandle(screen->fd, fd, &handle);
262 int size;
263 if (ret) {
264 fprintf(stderr, "Failed to get vc4 handle for dmabuf %d\n", fd);
265 return NULL;
266 }
267
268 /* Determine the size of the bo we were handed. */
269 size = lseek(fd, 0, SEEK_END);
270 if (size == -1) {
271 fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd);
272 return NULL;
273 }
274
275 return vc4_bo_open_handle(screen, winsys_stride, handle, size);
276 }
277
278 int
279 vc4_bo_get_dmabuf(struct vc4_bo *bo)
280 {
281 int fd;
282 int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle,
283 O_CLOEXEC, &fd);
284 if (ret != 0) {
285 fprintf(stderr, "Failed to export gem bo %d to dmabuf\n",
286 bo->handle);
287 return -1;
288 }
289 bo->private = false;
290
291 return fd;
292 }
293
294 struct vc4_bo *
295 vc4_bo_alloc_mem(struct vc4_screen *screen, const void *data, uint32_t size,
296 const char *name)
297 {
298 void *map;
299 struct vc4_bo *bo;
300
301 bo = vc4_bo_alloc(screen, size, name);
302 map = vc4_bo_map(bo);
303 memcpy(map, data, size);
304 return bo;
305 }
306
307 bool
308 vc4_bo_flink(struct vc4_bo *bo, uint32_t *name)
309 {
310 struct drm_gem_flink flink = {
311 .handle = bo->handle,
312 };
313 int ret = drmIoctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink);
314 if (ret) {
315 fprintf(stderr, "Failed to flink bo %d: %s\n",
316 bo->handle, strerror(errno));
317 free(bo);
318 return false;
319 }
320
321 bo->private = false;
322 *name = flink.name;
323
324 return true;
325 }
326
327 bool
328 vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns)
329 {
330 if (screen->finished_seqno >= seqno)
331 return true;
332
333 struct drm_vc4_wait_seqno wait;
334 memset(&wait, 0, sizeof(wait));
335 wait.seqno = seqno;
336 wait.timeout_ns = timeout_ns;
337
338 int ret;
339 if (!using_vc4_simulator)
340 ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait);
341 else {
342 wait.seqno = screen->finished_seqno;
343 ret = 0;
344 }
345
346 if (ret == 0) {
347 screen->finished_seqno = wait.seqno;
348 return true;
349 }
350
351 if (errno != ETIME) {
352 fprintf(stderr, "wait failed: %d\n", ret);
353 abort();
354 }
355
356 return false;
357 }
358
359 bool
360 vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns)
361 {
362 struct vc4_screen *screen = bo->screen;
363
364 struct drm_vc4_wait_bo wait;
365 memset(&wait, 0, sizeof(wait));
366 wait.handle = bo->handle;
367 wait.timeout_ns = timeout_ns;
368
369 int ret;
370 if (!using_vc4_simulator)
371 ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_WAIT_BO, &wait);
372 else
373 ret = 0;
374
375 if (ret == 0)
376 return true;
377
378 if (errno != ETIME) {
379 fprintf(stderr, "wait failed: %d\n", ret);
380 abort();
381 }
382
383 return false;
384 }
385
386 void *
387 vc4_bo_map_unsynchronized(struct vc4_bo *bo)
388 {
389 uint64_t offset;
390 int ret;
391
392 if (bo->map)
393 return bo->map;
394
395 if (!using_vc4_simulator) {
396 struct drm_vc4_mmap_bo map;
397 memset(&map, 0, sizeof(map));
398 map.handle = bo->handle;
399 ret = drmIoctl(bo->screen->fd, DRM_IOCTL_VC4_MMAP_BO, &map);
400 offset = map.offset;
401 } else {
402 struct drm_mode_map_dumb map;
403 memset(&map, 0, sizeof(map));
404 map.handle = bo->handle;
405 ret = drmIoctl(bo->screen->fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
406 offset = map.offset;
407 }
408 if (ret != 0) {
409 fprintf(stderr, "map ioctl failure\n");
410 abort();
411 }
412
413 bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
414 bo->screen->fd, offset);
415 if (bo->map == MAP_FAILED) {
416 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
417 bo->handle, (long long)offset, bo->size);
418 abort();
419 }
420
421 return bo->map;
422 }
423
424 void *
425 vc4_bo_map(struct vc4_bo *bo)
426 {
427 void *map = vc4_bo_map_unsynchronized(bo);
428
429 bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE);
430 if (!ok) {
431 fprintf(stderr, "BO wait for map failed\n");
432 abort();
433 }
434
435 return map;
436 }
437
438 void
439 vc4_bufmgr_destroy(struct pipe_screen *pscreen)
440 {
441 struct vc4_screen *screen = vc4_screen(pscreen);
442 struct vc4_bo_cache *cache = &screen->bo_cache;
443
444 while (!is_empty_list(&cache->time_list)) {
445 struct simple_node *node = first_elem(&cache->time_list);
446 struct vc4_bo *bo = container_of(node, struct vc4_bo, time_list);
447
448 remove_from_list(&bo->time_list);
449 remove_from_list(&bo->size_list);
450 vc4_bo_free(bo);
451 }
452 }