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