• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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