OSDN Git Service

vc4: Don't try to put our dmabuf-exported BOs into the BO cache.
[android-x86/external-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 == -ETIME) {
347                 return false;
348         } else if (ret != 0) {
349                 fprintf(stderr, "wait failed\n");
350                 abort();
351         } else {
352                 screen->finished_seqno = wait.seqno;
353                 return true;
354         }
355 }
356
357 bool
358 vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns)
359 {
360         struct vc4_screen *screen = bo->screen;
361
362         struct drm_vc4_wait_bo wait;
363         memset(&wait, 0, sizeof(wait));
364         wait.handle = bo->handle;
365         wait.timeout_ns = timeout_ns;
366
367         int ret;
368         if (!using_vc4_simulator)
369                 ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_WAIT_BO, &wait);
370         else
371                 ret = 0;
372
373         if (ret == -ETIME) {
374                 return false;
375         } else if (ret != 0) {
376                 fprintf(stderr, "wait failed\n");
377                 abort();
378         } else {
379                 return true;
380         }
381 }
382
383 void *
384 vc4_bo_map_unsynchronized(struct vc4_bo *bo)
385 {
386         uint64_t offset;
387         int ret;
388
389         if (bo->map)
390                 return bo->map;
391
392         if (!using_vc4_simulator) {
393                 struct drm_vc4_mmap_bo map;
394                 memset(&map, 0, sizeof(map));
395                 map.handle = bo->handle;
396                 ret = drmIoctl(bo->screen->fd, DRM_IOCTL_VC4_MMAP_BO, &map);
397                 offset = map.offset;
398         } else {
399                 struct drm_mode_map_dumb map;
400                 memset(&map, 0, sizeof(map));
401                 map.handle = bo->handle;
402                 ret = drmIoctl(bo->screen->fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
403                 offset = map.offset;
404         }
405         if (ret != 0) {
406                 fprintf(stderr, "map ioctl failure\n");
407                 abort();
408         }
409
410         bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
411                        bo->screen->fd, offset);
412         if (bo->map == MAP_FAILED) {
413                 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
414                         bo->handle, (long long)offset, bo->size);
415                 abort();
416         }
417
418         return bo->map;
419 }
420
421 void *
422 vc4_bo_map(struct vc4_bo *bo)
423 {
424         void *map = vc4_bo_map_unsynchronized(bo);
425
426         bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE);
427         if (!ok) {
428                 fprintf(stderr, "BO wait for map failed\n");
429                 abort();
430         }
431
432         return map;
433 }
434
435 void
436 vc4_bufmgr_destroy(struct pipe_screen *pscreen)
437 {
438         struct vc4_screen *screen = vc4_screen(pscreen);
439         struct vc4_bo_cache *cache = &screen->bo_cache;
440
441         while (!is_empty_list(&cache->time_list)) {
442                 struct simple_node *node = first_elem(&cache->time_list);
443                 struct vc4_bo *bo = container_of(node, struct vc4_bo, time_list);
444
445                 remove_from_list(&bo->time_list);
446                 remove_from_list(&bo->size_list);
447                 vc4_bo_free(bo);
448         }
449 }