OSDN Git Service

21e3bde2ee25bc4f7571dac24f40637fb680744c
[android-x86/external-mesa.git] / src / gallium / drivers / vc4 / vc4_bufmgr.c
1 /*
2  * Copyright © 2014-2015 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 static bool dump_stats = false;
38
39 static void
40 vc4_bo_cache_free_all(struct vc4_bo_cache *cache);
41
42 static void
43 vc4_bo_dump_stats(struct vc4_screen *screen)
44 {
45         struct vc4_bo_cache *cache = &screen->bo_cache;
46
47         fprintf(stderr, "  BOs allocated:   %d\n", screen->bo_count);
48         fprintf(stderr, "  BOs size:        %dkb\n", screen->bo_size / 1024);
49         fprintf(stderr, "  BOs cached:      %d\n", cache->bo_count);
50         fprintf(stderr, "  BOs cached size: %dkb\n", cache->bo_size / 1024);
51
52         if (!list_empty(&cache->time_list)) {
53                 struct vc4_bo *first = LIST_ENTRY(struct vc4_bo,
54                                                   cache->time_list.next,
55                                                   time_list);
56                 struct vc4_bo *last = LIST_ENTRY(struct vc4_bo,
57                                                   cache->time_list.prev,
58                                                   time_list);
59
60                 fprintf(stderr, "  oldest cache time: %ld\n",
61                         (long)first->free_time);
62                 fprintf(stderr, "  newest cache time: %ld\n",
63                         (long)last->free_time);
64
65                 struct timespec time;
66                 clock_gettime(CLOCK_MONOTONIC, &time);
67                 fprintf(stderr, "  now:               %ld\n",
68                         time.tv_sec);
69         }
70 }
71
72 static void
73 vc4_bo_remove_from_cache(struct vc4_bo_cache *cache, struct vc4_bo *bo)
74 {
75         list_del(&bo->time_list);
76         list_del(&bo->size_list);
77         cache->bo_count--;
78         cache->bo_size -= bo->size;
79 }
80
81 static struct vc4_bo *
82 vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name)
83 {
84         struct vc4_bo_cache *cache = &screen->bo_cache;
85         uint32_t page_index = size / 4096 - 1;
86
87         if (cache->size_list_size <= page_index)
88                 return NULL;
89
90         struct vc4_bo *bo = NULL;
91         pipe_mutex_lock(cache->lock);
92         if (!list_empty(&cache->size_list[page_index])) {
93                 bo = LIST_ENTRY(struct vc4_bo, cache->size_list[page_index].next,
94                                 size_list);
95
96                 /* Check that the BO has gone idle.  If not, then we want to
97                  * allocate something new instead, since we assume that the
98                  * user will proceed to CPU map it and fill it with stuff.
99                  */
100                 if (!vc4_bo_wait(bo, 0, NULL)) {
101                         pipe_mutex_unlock(cache->lock);
102                         return NULL;
103                 }
104
105                 pipe_reference_init(&bo->reference, 1);
106                 vc4_bo_remove_from_cache(cache, bo);
107
108                 bo->name = name;
109         }
110         pipe_mutex_unlock(cache->lock);
111         return bo;
112 }
113
114 struct vc4_bo *
115 vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name)
116 {
117         struct vc4_bo *bo;
118         int ret;
119
120         size = align(size, 4096);
121
122         bo = vc4_bo_from_cache(screen, size, name);
123         if (bo) {
124                 if (dump_stats) {
125                         fprintf(stderr, "Allocated %s %dkb from cache:\n",
126                                 name, size / 1024);
127                         vc4_bo_dump_stats(screen);
128                 }
129                 return bo;
130         }
131
132         bo = CALLOC_STRUCT(vc4_bo);
133         if (!bo)
134                 return NULL;
135
136         pipe_reference_init(&bo->reference, 1);
137         bo->screen = screen;
138         bo->size = size;
139         bo->name = name;
140         bo->private = true;
141
142         bool cleared_and_retried = false;
143 retry:
144         if (!using_vc4_simulator) {
145                 struct drm_vc4_create_bo create;
146                 memset(&create, 0, sizeof(create));
147
148                 create.size = size;
149
150                 ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_CREATE_BO, &create);
151                 bo->handle = create.handle;
152         } else {
153                 struct drm_mode_create_dumb create;
154                 memset(&create, 0, sizeof(create));
155
156                 create.width = 128;
157                 create.bpp = 8;
158                 create.height = (size + 127) / 128;
159
160                 ret = drmIoctl(screen->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
161                 bo->handle = create.handle;
162                 assert(create.size >= size);
163         }
164         if (ret != 0) {
165                 if (!list_empty(&screen->bo_cache.time_list) &&
166                     !cleared_and_retried) {
167                         cleared_and_retried = true;
168                         vc4_bo_cache_free_all(&screen->bo_cache);
169                         goto retry;
170                 }
171
172                 free(bo);
173                 return NULL;
174         }
175
176         screen->bo_count++;
177         screen->bo_size += bo->size;
178         if (dump_stats) {
179                 fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024);
180                 vc4_bo_dump_stats(screen);
181         }
182
183         return bo;
184 }
185
186 void
187 vc4_bo_last_unreference(struct vc4_bo *bo)
188 {
189         struct vc4_screen *screen = bo->screen;
190
191         struct timespec time;
192         clock_gettime(CLOCK_MONOTONIC, &time);
193         pipe_mutex_lock(screen->bo_cache.lock);
194         vc4_bo_last_unreference_locked_timed(bo, time.tv_sec);
195         pipe_mutex_unlock(screen->bo_cache.lock);
196 }
197
198 static void
199 vc4_bo_free(struct vc4_bo *bo)
200 {
201         struct vc4_screen *screen = bo->screen;
202
203         if (bo->map) {
204 #ifdef USE_VC4_SIMULATOR
205                 if (bo->simulator_winsys_map) {
206                         free(bo->map);
207                         bo->map = bo->simulator_winsys_map;
208                 }
209 #endif
210                 munmap(bo->map, bo->size);
211         }
212
213         struct drm_gem_close c;
214         memset(&c, 0, sizeof(c));
215         c.handle = bo->handle;
216         int ret = drmIoctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);
217         if (ret != 0)
218                 fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));
219
220         screen->bo_count--;
221         screen->bo_size -= bo->size;
222
223         if (dump_stats) {
224                 fprintf(stderr, "Freed %s%s%dkb:\n",
225                         bo->name ? bo->name : "",
226                         bo->name ? " " : "",
227                         bo->size / 1024);
228                 vc4_bo_dump_stats(screen);
229         }
230
231         free(bo);
232 }
233
234 static void
235 free_stale_bos(struct vc4_screen *screen, time_t time)
236 {
237         struct vc4_bo_cache *cache = &screen->bo_cache;
238         bool freed_any = false;
239
240         list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list,
241                                  time_list) {
242                 if (dump_stats && !freed_any) {
243                         fprintf(stderr, "Freeing stale BOs:\n");
244                         vc4_bo_dump_stats(screen);
245                         freed_any = true;
246                 }
247
248                 /* If it's more than a second old, free it. */
249                 if (time - bo->free_time > 2) {
250                         vc4_bo_remove_from_cache(cache, bo);
251                         vc4_bo_free(bo);
252                 } else {
253                         break;
254                 }
255         }
256
257         if (dump_stats && freed_any) {
258                 fprintf(stderr, "Freed stale BOs:\n");
259                 vc4_bo_dump_stats(screen);
260         }
261 }
262
263 static void
264 vc4_bo_cache_free_all(struct vc4_bo_cache *cache)
265 {
266         pipe_mutex_lock(cache->lock);
267         list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list,
268                                  time_list) {
269                 vc4_bo_remove_from_cache(cache, bo);
270                 vc4_bo_free(bo);
271         }
272         pipe_mutex_unlock(cache->lock);
273 }
274
275 void
276 vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time)
277 {
278         struct vc4_screen *screen = bo->screen;
279         struct vc4_bo_cache *cache = &screen->bo_cache;
280         uint32_t page_index = bo->size / 4096 - 1;
281
282         if (!bo->private) {
283                 vc4_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_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         cache->bo_count++;
316         cache->bo_size += bo->size;
317         if (dump_stats) {
318                 fprintf(stderr, "Freed %s %dkb to cache:\n",
319                         bo->name, bo->size / 1024);
320                 vc4_bo_dump_stats(screen);
321         }
322         bo->name = NULL;
323
324         free_stale_bos(screen, time);
325 }
326
327 static struct vc4_bo *
328 vc4_bo_open_handle(struct vc4_screen *screen,
329                    uint32_t winsys_stride,
330                    uint32_t handle, uint32_t size)
331 {
332         struct vc4_bo *bo = CALLOC_STRUCT(vc4_bo);
333
334         assert(size);
335
336         pipe_reference_init(&bo->reference, 1);
337         bo->screen = screen;
338         bo->handle = handle;
339         bo->size = size;
340         bo->name = "winsys";
341         bo->private = false;
342
343 #ifdef USE_VC4_SIMULATOR
344         vc4_bo_map(bo);
345         bo->simulator_winsys_map = bo->map;
346         bo->simulator_winsys_stride = winsys_stride;
347         bo->map = malloc(bo->size);
348 #endif
349
350         return bo;
351 }
352
353 struct vc4_bo *
354 vc4_bo_open_name(struct vc4_screen *screen, uint32_t name,
355                  uint32_t winsys_stride)
356 {
357         struct drm_gem_open o = {
358                 .name = name
359         };
360         int ret = drmIoctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o);
361         if (ret) {
362                 fprintf(stderr, "Failed to open bo %d: %s\n",
363                         name, strerror(errno));
364                 return NULL;
365         }
366
367         return vc4_bo_open_handle(screen, winsys_stride, o.handle, o.size);
368 }
369
370 struct vc4_bo *
371 vc4_bo_open_dmabuf(struct vc4_screen *screen, int fd, uint32_t winsys_stride)
372 {
373         uint32_t handle;
374         int ret = drmPrimeFDToHandle(screen->fd, fd, &handle);
375         int size;
376         if (ret) {
377                 fprintf(stderr, "Failed to get vc4 handle for dmabuf %d\n", fd);
378                 return NULL;
379         }
380
381         /* Determine the size of the bo we were handed. */
382         size = lseek(fd, 0, SEEK_END);
383         if (size == -1) {
384                 fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd);
385                 return NULL;
386         }
387
388         return vc4_bo_open_handle(screen, winsys_stride, handle, size);
389 }
390
391 int
392 vc4_bo_get_dmabuf(struct vc4_bo *bo)
393 {
394         int fd;
395         int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle,
396                                      O_CLOEXEC, &fd);
397         if (ret != 0) {
398                 fprintf(stderr, "Failed to export gem bo %d to dmabuf\n",
399                         bo->handle);
400                 return -1;
401         }
402         bo->private = false;
403
404         return fd;
405 }
406
407 struct vc4_bo *
408 vc4_bo_alloc_shader(struct vc4_screen *screen, const void *data, uint32_t size)
409 {
410         struct vc4_bo *bo;
411         int ret;
412
413         bo = CALLOC_STRUCT(vc4_bo);
414         if (!bo)
415                 return NULL;
416
417         pipe_reference_init(&bo->reference, 1);
418         bo->screen = screen;
419         bo->size = align(size, 4096);
420         bo->name = "code";
421         bo->private = false; /* Make sure it doesn't go back to the cache. */
422
423         if (!using_vc4_simulator) {
424                 struct drm_vc4_create_shader_bo create = {
425                         .size = size,
426                         .data = (uintptr_t)data,
427                 };
428
429                 ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_CREATE_SHADER_BO,
430                                &create);
431                 bo->handle = create.handle;
432         } else {
433                 struct drm_mode_create_dumb create;
434                 memset(&create, 0, sizeof(create));
435
436                 create.width = 128;
437                 create.bpp = 8;
438                 create.height = (size + 127) / 128;
439
440                 ret = drmIoctl(screen->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
441                 bo->handle = create.handle;
442                 assert(create.size >= size);
443
444                 vc4_bo_map(bo);
445                 memcpy(bo->map, data, size);
446         }
447         if (ret != 0) {
448                 fprintf(stderr, "create shader ioctl failure\n");
449                 abort();
450         }
451
452         screen->bo_count++;
453         screen->bo_size += bo->size;
454         if (dump_stats) {
455                 fprintf(stderr, "Allocated shader %dkb:\n", bo->size / 1024);
456                 vc4_bo_dump_stats(screen);
457         }
458
459         return bo;
460 }
461
462 bool
463 vc4_bo_flink(struct vc4_bo *bo, uint32_t *name)
464 {
465         struct drm_gem_flink flink = {
466                 .handle = bo->handle,
467         };
468         int ret = drmIoctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink);
469         if (ret) {
470                 fprintf(stderr, "Failed to flink bo %d: %s\n",
471                         bo->handle, strerror(errno));
472                 free(bo);
473                 return false;
474         }
475
476         bo->private = false;
477         *name = flink.name;
478
479         return true;
480 }
481
482 static int vc4_wait_seqno_ioctl(int fd, uint64_t seqno, uint64_t timeout_ns)
483 {
484         if (using_vc4_simulator)
485                 return 0;
486
487         struct drm_vc4_wait_seqno wait = {
488                 .seqno = seqno,
489                 .timeout_ns = timeout_ns,
490         };
491         int ret = drmIoctl(fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait);
492         if (ret == -1)
493                 return -errno;
494         else
495                 return 0;
496
497 }
498
499 bool
500 vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns,
501                const char *reason)
502 {
503         if (screen->finished_seqno >= seqno)
504                 return true;
505
506         if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) {
507                 if (vc4_wait_seqno_ioctl(screen->fd, seqno, 0) == -ETIME) {
508                         fprintf(stderr, "Blocking on seqno %lld for %s\n",
509                                 (long long)seqno, reason);
510                 }
511         }
512
513         int ret = vc4_wait_seqno_ioctl(screen->fd, seqno, timeout_ns);
514         if (ret) {
515                 if (ret != -ETIME) {
516                         fprintf(stderr, "wait failed: %d\n", ret);
517                         abort();
518                 }
519
520                 return false;
521         }
522
523         screen->finished_seqno = seqno;
524         return true;
525 }
526
527 static int vc4_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns)
528 {
529         if (using_vc4_simulator)
530                 return 0;
531
532         struct drm_vc4_wait_bo wait = {
533                 .handle = handle,
534                 .timeout_ns = timeout_ns,
535         };
536         int ret = drmIoctl(fd, DRM_IOCTL_VC4_WAIT_BO, &wait);
537         if (ret == -1)
538                 return -errno;
539         else
540                 return 0;
541
542 }
543
544 bool
545 vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns, const char *reason)
546 {
547         struct vc4_screen *screen = bo->screen;
548
549         if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) {
550                 if (vc4_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) {
551                         fprintf(stderr, "Blocking on %s BO for %s\n",
552                                 bo->name, reason);
553                 }
554         }
555
556         int ret = vc4_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns);
557         if (ret) {
558                 if (ret != -ETIME) {
559                         fprintf(stderr, "wait failed: %d\n", ret);
560                         abort();
561                 }
562
563                 return false;
564         }
565
566         return true;
567 }
568
569 void *
570 vc4_bo_map_unsynchronized(struct vc4_bo *bo)
571 {
572         uint64_t offset;
573         int ret;
574
575         if (bo->map)
576                 return bo->map;
577
578         if (!using_vc4_simulator) {
579                 struct drm_vc4_mmap_bo map;
580                 memset(&map, 0, sizeof(map));
581                 map.handle = bo->handle;
582                 ret = drmIoctl(bo->screen->fd, DRM_IOCTL_VC4_MMAP_BO, &map);
583                 offset = map.offset;
584         } else {
585                 struct drm_mode_map_dumb map;
586                 memset(&map, 0, sizeof(map));
587                 map.handle = bo->handle;
588                 ret = drmIoctl(bo->screen->fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
589                 offset = map.offset;
590         }
591         if (ret != 0) {
592                 fprintf(stderr, "map ioctl failure\n");
593                 abort();
594         }
595
596         bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
597                        bo->screen->fd, offset);
598         if (bo->map == MAP_FAILED) {
599                 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
600                         bo->handle, (long long)offset, bo->size);
601                 abort();
602         }
603
604         return bo->map;
605 }
606
607 void *
608 vc4_bo_map(struct vc4_bo *bo)
609 {
610         void *map = vc4_bo_map_unsynchronized(bo);
611
612         bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE, "bo map");
613         if (!ok) {
614                 fprintf(stderr, "BO wait for map failed\n");
615                 abort();
616         }
617
618         return map;
619 }
620
621 void
622 vc4_bufmgr_destroy(struct pipe_screen *pscreen)
623 {
624         struct vc4_screen *screen = vc4_screen(pscreen);
625         struct vc4_bo_cache *cache = &screen->bo_cache;
626
627         vc4_bo_cache_free_all(cache);
628
629         if (dump_stats) {
630                 fprintf(stderr, "BO stats after screen destroy:\n");
631                 vc4_bo_dump_stats(screen);
632         }
633 }