2 * Copyright (C) 2017-2019 Lima Project
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
25 #include <sys/types.h>
30 #include "drm-uapi/lima_drm.h"
32 #include "util/u_hash_table.h"
33 #include "util/u_math.h"
34 #include "util/os_time.h"
35 #include "os/os_mman.h"
37 #include "state_tracker/drm_driver.h"
39 #include "lima_screen.h"
41 #include "lima_util.h"
43 #define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x)))
45 static unsigned handle_hash(void *key
)
47 return PTR_TO_UINT(key
);
50 static int handle_compare(void *key1
, void *key2
)
52 return PTR_TO_UINT(key1
) != PTR_TO_UINT(key2
);
55 bool lima_bo_table_init(struct lima_screen
*screen
)
57 screen
->bo_handles
= util_hash_table_create(handle_hash
, handle_compare
);
58 if (!screen
->bo_handles
)
61 screen
->bo_flink_names
= util_hash_table_create(handle_hash
, handle_compare
);
62 if (!screen
->bo_flink_names
)
65 mtx_init(&screen
->bo_table_lock
, mtx_plain
);
69 util_hash_table_destroy(screen
->bo_handles
);
73 bool lima_bo_cache_init(struct lima_screen
*screen
)
75 mtx_init(&screen
->bo_cache_lock
, mtx_plain
);
76 list_inithead(&screen
->bo_cache_time
);
77 for (int i
= 0; i
< NR_BO_CACHE_BUCKETS
; i
++)
78 list_inithead(&screen
->bo_cache_buckets
[i
]);
83 void lima_bo_table_fini(struct lima_screen
*screen
)
85 mtx_destroy(&screen
->bo_table_lock
);
86 util_hash_table_destroy(screen
->bo_handles
);
87 util_hash_table_destroy(screen
->bo_flink_names
);
91 lima_bo_cache_remove(struct lima_bo
*bo
)
93 list_del(&bo
->size_list
);
94 list_del(&bo
->time_list
);
97 static void lima_close_kms_handle(struct lima_screen
*screen
, uint32_t handle
)
99 struct drm_gem_close args
= {
103 drmIoctl(screen
->fd
, DRM_IOCTL_GEM_CLOSE
, &args
);
107 lima_bo_free(struct lima_bo
*bo
)
109 struct lima_screen
*screen
= bo
->screen
;
110 mtx_lock(&screen
->bo_table_lock
);
111 util_hash_table_remove(screen
->bo_handles
,
112 (void *)(uintptr_t)bo
->handle
);
114 util_hash_table_remove(screen
->bo_flink_names
,
115 (void *)(uintptr_t)bo
->flink_name
);
116 mtx_unlock(&screen
->bo_table_lock
);
121 lima_close_kms_handle(screen
, bo
->handle
);
125 void lima_bo_cache_fini(struct lima_screen
*screen
)
127 mtx_destroy(&screen
->bo_cache_lock
);
129 list_for_each_entry_safe(struct lima_bo
, entry
,
130 &screen
->bo_cache_time
, time_list
) {
131 lima_bo_cache_remove(entry
);
136 static bool lima_bo_get_info(struct lima_bo
*bo
)
138 struct drm_lima_gem_info req
= {
139 .handle
= bo
->handle
,
142 if(drmIoctl(bo
->screen
->fd
, DRM_IOCTL_LIMA_GEM_INFO
, &req
))
145 bo
->offset
= req
.offset
;
151 lima_bucket_index(unsigned size
)
153 /* Round down to POT to compute a bucket index */
155 unsigned bucket_index
= util_logbase2(size
);
157 /* Clamp the bucket index; all huge allocations will be
158 * sorted into the largest bucket */
159 bucket_index
= CLAMP(bucket_index
, MIN_BO_CACHE_BUCKET
,
160 MAX_BO_CACHE_BUCKET
);
163 return (bucket_index
- MIN_BO_CACHE_BUCKET
);
166 static struct list_head
*
167 lima_bo_cache_get_bucket(struct lima_screen
*screen
, unsigned size
)
169 return &screen
->bo_cache_buckets
[lima_bucket_index(size
)];
173 lima_bo_cache_free_stale_bos(struct lima_screen
*screen
, time_t time
)
175 list_for_each_entry_safe(struct lima_bo
, entry
,
176 &screen
->bo_cache_time
, time_list
) {
177 /* Free BOs that are sitting idle for longer than 5 seconds */
178 if (time
- entry
->free_time
> 6) {
179 lima_bo_cache_remove(entry
);
187 lima_bo_cache_put(struct lima_bo
*bo
)
192 struct lima_screen
*screen
= bo
->screen
;
194 mtx_lock(&screen
->bo_cache_lock
);
195 struct list_head
*bucket
= lima_bo_cache_get_bucket(screen
, bo
->size
);
198 mtx_unlock(&screen
->bo_cache_lock
);
202 struct timespec time
;
203 clock_gettime(CLOCK_MONOTONIC
, &time
);
204 bo
->free_time
= time
.tv_sec
;
205 list_addtail(&bo
->size_list
, bucket
);
206 list_addtail(&bo
->time_list
, &screen
->bo_cache_time
);
207 lima_bo_cache_free_stale_bos(screen
, time
.tv_sec
);
208 mtx_unlock(&screen
->bo_cache_lock
);
213 static struct lima_bo
*
214 lima_bo_cache_get(struct lima_screen
*screen
, uint32_t size
, uint32_t flags
)
216 struct lima_bo
*bo
= NULL
;
217 mtx_lock(&screen
->bo_cache_lock
);
218 struct list_head
*bucket
= lima_bo_cache_get_bucket(screen
, size
);
221 mtx_unlock(&screen
->bo_cache_lock
);
225 list_for_each_entry_safe(struct lima_bo
, entry
, bucket
, size_list
) {
226 if (entry
->size
>= size
&&
227 entry
->flags
== flags
) {
228 /* Check if BO is idle. If it's not it's better to allocate new one */
229 if (!lima_bo_wait(entry
, LIMA_GEM_WAIT_WRITE
, 0))
232 lima_bo_cache_remove(entry
);
233 p_atomic_set(&entry
->refcnt
, 1);
239 mtx_unlock(&screen
->bo_cache_lock
);
244 struct lima_bo
*lima_bo_create(struct lima_screen
*screen
,
245 uint32_t size
, uint32_t flags
)
249 /* Try to get bo from cache first */
250 bo
= lima_bo_cache_get(screen
, size
, flags
);
254 size
= align(size
, LIMA_PAGE_SIZE
);
256 struct drm_lima_gem_create req
= {
261 if (!(bo
= calloc(1, sizeof(*bo
))))
264 list_inithead(&bo
->time_list
);
265 list_inithead(&bo
->size_list
);
267 if (drmIoctl(screen
->fd
, DRM_IOCTL_LIMA_GEM_CREATE
, &req
))
272 bo
->flags
= req
.flags
;
273 bo
->handle
= req
.handle
;
274 bo
->cacheable
= !(lima_debug
& LIMA_DEBUG_NO_BO_CACHE
);
275 p_atomic_set(&bo
->refcnt
, 1);
277 if (!lima_bo_get_info(bo
))
283 lima_close_kms_handle(screen
, bo
->handle
);
289 void lima_bo_unreference(struct lima_bo
*bo
)
291 if (!p_atomic_dec_zero(&bo
->refcnt
))
294 /* Try to put it into cache */
295 if (lima_bo_cache_put(bo
))
301 void *lima_bo_map(struct lima_bo
*bo
)
304 bo
->map
= os_mmap(0, bo
->size
, PROT_READ
| PROT_WRITE
,
305 MAP_SHARED
, bo
->screen
->fd
, bo
->offset
);
306 if (bo
->map
== MAP_FAILED
)
313 void lima_bo_unmap(struct lima_bo
*bo
)
316 os_munmap(bo
->map
, bo
->size
);
321 bool lima_bo_export(struct lima_bo
*bo
, struct winsys_handle
*handle
)
323 struct lima_screen
*screen
= bo
->screen
;
325 /* Don't cache exported BOs */
326 bo
->cacheable
= false;
328 switch (handle
->type
) {
329 case WINSYS_HANDLE_TYPE_SHARED
:
330 if (!bo
->flink_name
) {
331 struct drm_gem_flink flink
= {
332 .handle
= bo
->handle
,
335 if (drmIoctl(screen
->fd
, DRM_IOCTL_GEM_FLINK
, &flink
))
338 bo
->flink_name
= flink
.name
;
340 mtx_lock(&screen
->bo_table_lock
);
341 util_hash_table_set(screen
->bo_flink_names
,
342 (void *)(uintptr_t)bo
->flink_name
, bo
);
343 mtx_unlock(&screen
->bo_table_lock
);
345 handle
->handle
= bo
->flink_name
;
348 case WINSYS_HANDLE_TYPE_KMS
:
349 mtx_lock(&screen
->bo_table_lock
);
350 util_hash_table_set(screen
->bo_handles
,
351 (void *)(uintptr_t)bo
->handle
, bo
);
352 mtx_unlock(&screen
->bo_table_lock
);
354 handle
->handle
= bo
->handle
;
357 case WINSYS_HANDLE_TYPE_FD
:
358 if (drmPrimeHandleToFD(screen
->fd
, bo
->handle
, DRM_CLOEXEC
,
359 (int*)&handle
->handle
))
362 mtx_lock(&screen
->bo_table_lock
);
363 util_hash_table_set(screen
->bo_handles
,
364 (void *)(uintptr_t)bo
->handle
, bo
);
365 mtx_unlock(&screen
->bo_table_lock
);
373 struct lima_bo
*lima_bo_import(struct lima_screen
*screen
,
374 struct winsys_handle
*handle
)
376 struct lima_bo
*bo
= NULL
;
377 struct drm_gem_open req
= {0};
378 uint32_t dma_buf_size
= 0;
379 unsigned h
= handle
->handle
;
381 mtx_lock(&screen
->bo_table_lock
);
383 /* Convert a DMA buf handle to a KMS handle now. */
384 if (handle
->type
== WINSYS_HANDLE_TYPE_FD
) {
385 uint32_t prime_handle
;
388 /* Get a KMS handle. */
389 if (drmPrimeFDToHandle(screen
->fd
, h
, &prime_handle
)) {
390 mtx_unlock(&screen
->bo_table_lock
);
394 /* Query the buffer size. */
395 size
= lseek(h
, 0, SEEK_END
);
396 if (size
== (off_t
)-1) {
397 mtx_unlock(&screen
->bo_table_lock
);
398 lima_close_kms_handle(screen
, prime_handle
);
401 lseek(h
, 0, SEEK_SET
);
407 switch (handle
->type
) {
408 case WINSYS_HANDLE_TYPE_SHARED
:
409 bo
= util_hash_table_get(screen
->bo_flink_names
,
410 (void *)(uintptr_t)h
);
412 case WINSYS_HANDLE_TYPE_KMS
:
413 case WINSYS_HANDLE_TYPE_FD
:
414 bo
= util_hash_table_get(screen
->bo_handles
,
415 (void *)(uintptr_t)h
);
418 mtx_unlock(&screen
->bo_table_lock
);
423 p_atomic_inc(&bo
->refcnt
);
424 /* Don't cache imported BOs */
425 bo
->cacheable
= false;
426 mtx_unlock(&screen
->bo_table_lock
);
430 if (!(bo
= calloc(1, sizeof(*bo
)))) {
431 mtx_unlock(&screen
->bo_table_lock
);
432 if (handle
->type
== WINSYS_HANDLE_TYPE_FD
)
433 lima_close_kms_handle(screen
, h
);
437 /* Don't cache imported BOs */
438 bo
->cacheable
= false;
439 list_inithead(&bo
->time_list
);
440 list_inithead(&bo
->size_list
);
442 p_atomic_set(&bo
->refcnt
, 1);
444 switch (handle
->type
) {
445 case WINSYS_HANDLE_TYPE_SHARED
:
447 if (drmIoctl(screen
->fd
, DRM_IOCTL_GEM_OPEN
, &req
)) {
448 mtx_unlock(&screen
->bo_table_lock
);
452 bo
->handle
= req
.handle
;
456 case WINSYS_HANDLE_TYPE_FD
:
458 bo
->size
= dma_buf_size
;
465 if (lima_bo_get_info(bo
)) {
466 if (handle
->type
== WINSYS_HANDLE_TYPE_SHARED
)
467 util_hash_table_set(screen
->bo_flink_names
,
468 (void *)(uintptr_t)bo
->flink_name
, bo
);
469 util_hash_table_set(screen
->bo_handles
,
470 (void*)(uintptr_t)bo
->handle
, bo
);
473 lima_close_kms_handle(screen
, bo
->handle
);
478 mtx_unlock(&screen
->bo_table_lock
);
483 bool lima_bo_wait(struct lima_bo
*bo
, uint32_t op
, uint64_t timeout_ns
)
490 abs_timeout
= os_time_get_absolute_timeout(timeout_ns
);
492 if (abs_timeout
== OS_TIMEOUT_INFINITE
)
493 abs_timeout
= INT64_MAX
;
495 struct drm_lima_gem_wait req
= {
496 .handle
= bo
->handle
,
498 .timeout_ns
= abs_timeout
,
501 return drmIoctl(bo
->screen
->fd
, DRM_IOCTL_LIMA_GEM_WAIT
, &req
) == 0;