1 /*
2 * Copyright © 2014-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 <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/perf/cpu_trace.h"
32 #include "util/u_hash_table.h"
33 #include "util/u_memory.h"
34 #include "util/ralloc.h"
35
36 #include "v3d_context.h"
37 #include "v3d_screen.h"
38
39 static bool dump_stats = false;
40
41 static void
42 v3d_bo_cache_free_all(struct v3d_bo_cache *cache);
43
44 static void
v3d_bo_dump_stats(struct v3d_screen * screen)45 v3d_bo_dump_stats(struct v3d_screen *screen)
46 {
47 struct v3d_bo_cache *cache = &screen->bo_cache;
48
49 uint32_t cache_count = 0;
50 uint32_t cache_size = 0;
51 list_for_each_entry(struct v3d_bo, bo, &cache->time_list, time_list) {
52 cache_count++;
53 cache_size += bo->size;
54 }
55
56 fprintf(stderr, " BOs allocated: %d\n", screen->bo_count);
57 fprintf(stderr, " BOs size: %dkb\n", screen->bo_size / 1024);
58 fprintf(stderr, " BOs cached: %d\n", cache_count);
59 fprintf(stderr, " BOs cached size: %dkb\n", cache_size / 1024);
60
61 if (!list_is_empty(&cache->time_list)) {
62 struct v3d_bo *first = list_first_entry(&cache->time_list,
63 struct v3d_bo,
64 time_list);
65 struct v3d_bo *last = list_last_entry(&cache->time_list,
66 struct v3d_bo,
67 time_list);
68
69 fprintf(stderr, " oldest cache time: %ld\n",
70 (long)first->free_time);
71 fprintf(stderr, " newest cache time: %ld\n",
72 (long)last->free_time);
73
74 struct timespec time;
75 clock_gettime(CLOCK_MONOTONIC, &time);
76 fprintf(stderr, " now: %jd\n",
77 (intmax_t)time.tv_sec);
78 }
79 }
80
81 static void
v3d_bo_remove_from_cache(struct v3d_bo_cache * cache,struct v3d_bo * bo)82 v3d_bo_remove_from_cache(struct v3d_bo_cache *cache, struct v3d_bo *bo)
83 {
84 list_del(&bo->time_list);
85 list_del(&bo->size_list);
86 }
87
88 static struct v3d_bo *
v3d_bo_from_cache(struct v3d_screen * screen,uint32_t size,const char * name)89 v3d_bo_from_cache(struct v3d_screen *screen, uint32_t size, const char *name)
90 {
91 struct v3d_bo_cache *cache = &screen->bo_cache;
92 uint32_t page_index = size / 4096 - 1;
93
94 if (cache->size_list_size <= page_index)
95 return NULL;
96
97 struct v3d_bo *bo = NULL;
98 mtx_lock(&cache->lock);
99 if (!list_is_empty(&cache->size_list[page_index])) {
100 bo = list_first_entry(&cache->size_list[page_index],
101 struct v3d_bo, size_list);
102
103 /* Check that the BO has gone idle. If not, then we want to
104 * allocate something new instead, since we assume that the
105 * user will proceed to CPU map it and fill it with stuff.
106 */
107 if (!v3d_bo_wait(bo, 0, NULL)) {
108 mtx_unlock(&cache->lock);
109 return NULL;
110 }
111
112 pipe_reference_init(&bo->reference, 1);
113 v3d_bo_remove_from_cache(cache, bo);
114
115 bo->name = name;
116 }
117 mtx_unlock(&cache->lock);
118 return bo;
119 }
120
121 struct v3d_bo *
v3d_bo_alloc(struct v3d_screen * screen,uint32_t size,const char * name)122 v3d_bo_alloc(struct v3d_screen *screen, uint32_t size, const char *name)
123 {
124 struct v3d_bo *bo;
125 int ret;
126
127 /* The CLIF dumping requires that there is no whitespace in the name.
128 */
129 assert(!strchr(name, ' '));
130
131 size = align(size, 4096);
132
133 bo = v3d_bo_from_cache(screen, size, name);
134 if (bo) {
135 if (dump_stats) {
136 fprintf(stderr, "Allocated %s %dkb from cache:\n",
137 name, size / 1024);
138 v3d_bo_dump_stats(screen);
139 }
140 return bo;
141 }
142
143 bo = CALLOC_STRUCT(v3d_bo);
144 if (!bo)
145 return NULL;
146
147 pipe_reference_init(&bo->reference, 1);
148 bo->screen = screen;
149 bo->size = size;
150 bo->name = name;
151 bo->private = true;
152
153 struct drm_v3d_create_bo create = {
154 .size = size
155 };
156
157 retry:
158 ret = v3d_ioctl(screen->fd, DRM_IOCTL_V3D_CREATE_BO, &create);
159
160 if (ret != 0) {
161 if (!list_is_empty(&screen->bo_cache.time_list)) {
162 v3d_bo_cache_free_all(&screen->bo_cache);
163 goto retry;
164 }
165
166 mesa_loge("Failed to allocate device memory for BO\n");
167 free(bo);
168 return NULL;
169 }
170
171 bo->handle = create.handle;
172 bo->offset = create.offset;
173
174 screen->bo_count++;
175 screen->bo_size += bo->size;
176 if (dump_stats) {
177 fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024);
178 v3d_bo_dump_stats(screen);
179 }
180
181 return bo;
182 }
183
184 void
v3d_bo_last_unreference(struct v3d_bo * bo)185 v3d_bo_last_unreference(struct v3d_bo *bo)
186 {
187 struct v3d_screen *screen = bo->screen;
188
189 struct timespec time;
190 clock_gettime(CLOCK_MONOTONIC, &time);
191 mtx_lock(&screen->bo_cache.lock);
192 v3d_bo_last_unreference_locked_timed(bo, time.tv_sec);
193 mtx_unlock(&screen->bo_cache.lock);
194 }
195
196 static void
v3d_bo_free(struct v3d_bo * bo)197 v3d_bo_free(struct v3d_bo *bo)
198 {
199 struct v3d_screen *screen = bo->screen;
200
201 if (bo->map) {
202 #if USE_V3D_SIMULATOR
203 if (bo->name &&
204 strcmp(bo->name, "winsys") == 0) {
205 free(bo->map);
206 } else
207 #endif
208 {
209 munmap(bo->map, bo->size);
210 VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0));
211 }
212 }
213
214 struct drm_gem_close c;
215 memset(&c, 0, sizeof(c));
216 c.handle = bo->handle;
217 int ret = v3d_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);
218 if (ret != 0)
219 fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));
220
221 screen->bo_count--;
222 screen->bo_size -= bo->size;
223
224 if (dump_stats) {
225 fprintf(stderr, "Freed %s%s%dkb:\n",
226 bo->name ? bo->name : "",
227 bo->name ? " " : "",
228 bo->size / 1024);
229 v3d_bo_dump_stats(screen);
230 }
231
232 free(bo);
233 }
234
235 static void
free_stale_bos(struct v3d_screen * screen,time_t time)236 free_stale_bos(struct v3d_screen *screen, time_t time)
237 {
238 struct v3d_bo_cache *cache = &screen->bo_cache;
239 bool freed_any = false;
240
241 list_for_each_entry_safe(struct v3d_bo, bo, &cache->time_list,
242 time_list) {
243 /* If it's more than a second old, free it. */
244 if (time - bo->free_time > 2) {
245 if (dump_stats && !freed_any) {
246 fprintf(stderr, "Freeing stale BOs:\n");
247 v3d_bo_dump_stats(screen);
248 freed_any = true;
249 }
250 v3d_bo_remove_from_cache(cache, bo);
251 v3d_bo_free(bo);
252 } else {
253 break;
254 }
255 }
256
257 if (dump_stats && freed_any) {
258 fprintf(stderr, "Freed stale BOs:\n");
259 v3d_bo_dump_stats(screen);
260 }
261 }
262
263 static void
v3d_bo_cache_free_all(struct v3d_bo_cache * cache)264 v3d_bo_cache_free_all(struct v3d_bo_cache *cache)
265 {
266 mtx_lock(&cache->lock);
267 list_for_each_entry_safe(struct v3d_bo, bo, &cache->time_list,
268 time_list) {
269 v3d_bo_remove_from_cache(cache, bo);
270 v3d_bo_free(bo);
271 }
272 mtx_unlock(&cache->lock);
273 }
274
275 void
v3d_bo_last_unreference_locked_timed(struct v3d_bo * bo,time_t time)276 v3d_bo_last_unreference_locked_timed(struct v3d_bo *bo, time_t time)
277 {
278 struct v3d_screen *screen = bo->screen;
279 struct v3d_bo_cache *cache = &screen->bo_cache;
280 uint32_t page_index = bo->size / 4096 - 1;
281
282 if (!bo->private) {
283 v3d_bo_free(bo);
284 return;
285 }
286
287 if (cache->size_list_size <= page_index) {
288 struct list_head *new_list =
289 ralloc_array(screen, struct list_head, page_index + 1);
290
291 /* Move old list contents over (since the array has moved, and
292 * therefore the pointers to the list heads have to change).
293 */
294 for (int i = 0; i < cache->size_list_size; i++) {
295 struct list_head *old_head = &cache->size_list[i];
296 if (list_is_empty(old_head))
297 list_inithead(&new_list[i]);
298 else {
299 new_list[i].next = old_head->next;
300 new_list[i].prev = old_head->prev;
301 new_list[i].next->prev = &new_list[i];
302 new_list[i].prev->next = &new_list[i];
303 }
304 }
305 for (int i = cache->size_list_size; i < page_index + 1; i++)
306 list_inithead(&new_list[i]);
307
308 cache->size_list = new_list;
309 cache->size_list_size = page_index + 1;
310 }
311
312 bo->free_time = time;
313 list_addtail(&bo->size_list, &cache->size_list[page_index]);
314 list_addtail(&bo->time_list, &cache->time_list);
315 if (dump_stats) {
316 fprintf(stderr, "Freed %s %dkb to cache:\n",
317 bo->name, bo->size / 1024);
318 v3d_bo_dump_stats(screen);
319 }
320 bo->name = NULL;
321
322 free_stale_bos(screen, time);
323 }
324
325 static struct v3d_bo *
v3d_bo_open_handle(struct v3d_screen * screen,uint32_t handle,uint32_t size)326 v3d_bo_open_handle(struct v3d_screen *screen,
327 uint32_t handle, uint32_t size)
328 {
329 struct v3d_bo *bo;
330
331 /* Note: the caller is responsible for locking screen->bo_handles_mutex.
332 * This allows the lock to cover the actual BO import, avoiding a race.
333 */
334
335 assert(size);
336
337 bo = util_hash_table_get(screen->bo_handles, (void*)(uintptr_t)handle);
338 if (bo) {
339 pipe_reference(NULL, &bo->reference);
340 goto done;
341 }
342
343 bo = CALLOC_STRUCT(v3d_bo);
344 pipe_reference_init(&bo->reference, 1);
345 bo->screen = screen;
346 bo->handle = handle;
347 bo->size = size;
348 bo->name = "winsys";
349 bo->private = false;
350
351 #if USE_V3D_SIMULATOR
352 v3d_simulator_open_from_handle(screen->fd, bo->handle, bo->size);
353 bo->map = malloc(bo->size);
354 #endif
355
356 struct drm_v3d_get_bo_offset get = {
357 .handle = handle,
358 };
359 int ret = v3d_ioctl(screen->fd, DRM_IOCTL_V3D_GET_BO_OFFSET, &get);
360 if (ret) {
361 fprintf(stderr, "Failed to get BO offset: %s\n",
362 strerror(errno));
363 free(bo->map);
364 free(bo);
365 bo = NULL;
366 goto done;
367 }
368 bo->offset = get.offset;
369 assert(bo->offset != 0);
370
371 _mesa_hash_table_insert(screen->bo_handles, (void *)(uintptr_t)handle, bo);
372
373 screen->bo_count++;
374 screen->bo_size += bo->size;
375
376 done:
377 mtx_unlock(&screen->bo_handles_mutex);
378 return bo;
379 }
380
381 struct v3d_bo *
v3d_bo_open_name(struct v3d_screen * screen,uint32_t name)382 v3d_bo_open_name(struct v3d_screen *screen, uint32_t name)
383 {
384 struct drm_gem_open o = {
385 .name = name
386 };
387 mtx_lock(&screen->bo_handles_mutex);
388
389 int ret = v3d_ioctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o);
390 if (ret) {
391 fprintf(stderr, "Failed to open bo %d: %s\n",
392 name, strerror(errno));
393 mtx_unlock(&screen->bo_handles_mutex);
394 return NULL;
395 }
396
397 return v3d_bo_open_handle(screen, o.handle, o.size);
398 }
399
400 struct v3d_bo *
v3d_bo_open_dmabuf(struct v3d_screen * screen,int fd)401 v3d_bo_open_dmabuf(struct v3d_screen *screen, int fd)
402 {
403 uint32_t handle;
404
405 mtx_lock(&screen->bo_handles_mutex);
406
407 int ret = drmPrimeFDToHandle(screen->fd, fd, &handle);
408 int size;
409 if (ret) {
410 fprintf(stderr, "Failed to get v3d handle for dmabuf %d\n", fd);
411 mtx_unlock(&screen->bo_handles_mutex);
412 return NULL;
413 }
414
415 /* Determine the size of the bo we were handed. */
416 size = lseek(fd, 0, SEEK_END);
417 if (size == -1) {
418 fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd);
419 mtx_unlock(&screen->bo_handles_mutex);
420 return NULL;
421 }
422
423 return v3d_bo_open_handle(screen, handle, size);
424 }
425
426 int
v3d_bo_get_dmabuf(struct v3d_bo * bo)427 v3d_bo_get_dmabuf(struct v3d_bo *bo)
428 {
429 int fd;
430 int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle,
431 O_CLOEXEC, &fd);
432 if (ret != 0) {
433 fprintf(stderr, "Failed to export gem bo %d to dmabuf\n",
434 bo->handle);
435 return -1;
436 }
437
438 mtx_lock(&bo->screen->bo_handles_mutex);
439 bo->private = false;
440 _mesa_hash_table_insert(bo->screen->bo_handles, (void *)(uintptr_t)bo->handle, bo);
441 mtx_unlock(&bo->screen->bo_handles_mutex);
442
443 return fd;
444 }
445
446 bool
v3d_bo_flink(struct v3d_bo * bo,uint32_t * name)447 v3d_bo_flink(struct v3d_bo *bo, uint32_t *name)
448 {
449 struct drm_gem_flink flink = {
450 .handle = bo->handle,
451 };
452 int ret = v3d_ioctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink);
453 if (ret) {
454 fprintf(stderr, "Failed to flink bo %d: %s\n",
455 bo->handle, strerror(errno));
456 free(bo);
457 return false;
458 }
459
460 bo->private = false;
461 *name = flink.name;
462
463 return true;
464 }
465
v3d_wait_bo_ioctl(int fd,uint32_t handle,uint64_t timeout_ns)466 static int v3d_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns)
467 {
468 struct drm_v3d_wait_bo wait = {
469 .handle = handle,
470 .timeout_ns = timeout_ns,
471 };
472 int ret = v3d_ioctl(fd, DRM_IOCTL_V3D_WAIT_BO, &wait);
473 if (ret == -1)
474 return -errno;
475 else
476 return 0;
477
478 }
479
480 bool
v3d_bo_wait(struct v3d_bo * bo,uint64_t timeout_ns,const char * reason)481 v3d_bo_wait(struct v3d_bo *bo, uint64_t timeout_ns, const char *reason)
482 {
483 struct v3d_screen *screen = bo->screen;
484
485 MESA_TRACE_FUNC();
486
487 if (V3D_DBG(PERF) && timeout_ns && reason) {
488 if (v3d_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) {
489 fprintf(stderr, "Blocking on %s BO for %s\n",
490 bo->name, reason);
491 }
492 }
493
494 int ret = v3d_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns);
495 if (ret) {
496 if (ret != -ETIME) {
497 fprintf(stderr, "wait failed: %d\n", ret);
498 abort();
499 }
500
501 return false;
502 }
503
504 return true;
505 }
506
507 void *
v3d_bo_map_unsynchronized(struct v3d_bo * bo)508 v3d_bo_map_unsynchronized(struct v3d_bo *bo)
509 {
510 uint64_t offset;
511 int ret;
512
513 if (bo->map)
514 return bo->map;
515
516 struct drm_v3d_mmap_bo map;
517 memset(&map, 0, sizeof(map));
518 map.handle = bo->handle;
519 ret = v3d_ioctl(bo->screen->fd, DRM_IOCTL_V3D_MMAP_BO, &map);
520 offset = map.offset;
521 if (ret != 0) {
522 fprintf(stderr, "map ioctl failure\n");
523 abort();
524 }
525
526 bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
527 bo->screen->fd, offset);
528 if (bo->map == MAP_FAILED) {
529 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
530 bo->handle, (long long)offset, bo->size);
531 abort();
532 }
533 VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false));
534
535 return bo->map;
536 }
537
538 void *
v3d_bo_map(struct v3d_bo * bo)539 v3d_bo_map(struct v3d_bo *bo)
540 {
541 void *map = v3d_bo_map_unsynchronized(bo);
542
543 bool ok = v3d_bo_wait(bo, OS_TIMEOUT_INFINITE, "bo map");
544 if (!ok) {
545 fprintf(stderr, "BO wait for map failed\n");
546 abort();
547 }
548
549 return map;
550 }
551
552 void
v3d_bufmgr_destroy(struct pipe_screen * pscreen)553 v3d_bufmgr_destroy(struct pipe_screen *pscreen)
554 {
555 struct v3d_screen *screen = v3d_screen(pscreen);
556 struct v3d_bo_cache *cache = &screen->bo_cache;
557
558 v3d_bo_cache_free_all(cache);
559
560 if (dump_stats) {
561 fprintf(stderr, "BO stats after screen destroy:\n");
562 v3d_bo_dump_stats(screen);
563 }
564 }
565