Path: blob/21.2-virgl/src/gallium/drivers/lima/lima_bo.c
4565 views
/*1* Copyright (C) 2017-2019 Lima Project2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice shall be included in11* all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR17* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19* OTHER DEALINGS IN THE SOFTWARE.20*21*/2223#include <stdlib.h>24#include <sys/types.h>25#include <unistd.h>26#include <fcntl.h>2728#include "xf86drm.h"29#include "drm-uapi/lima_drm.h"3031#include "util/u_hash_table.h"32#include "util/u_math.h"33#include "util/os_time.h"34#include "os/os_mman.h"3536#include "frontend/drm_driver.h"3738#include "lima_screen.h"39#include "lima_bo.h"40#include "lima_util.h"4142bool lima_bo_table_init(struct lima_screen *screen)43{44screen->bo_handles = util_hash_table_create_ptr_keys();45if (!screen->bo_handles)46return false;4748screen->bo_flink_names = util_hash_table_create_ptr_keys();49if (!screen->bo_flink_names)50goto err_out0;5152mtx_init(&screen->bo_table_lock, mtx_plain);53return true;5455err_out0:56_mesa_hash_table_destroy(screen->bo_handles, NULL);57return false;58}5960bool lima_bo_cache_init(struct lima_screen *screen)61{62mtx_init(&screen->bo_cache_lock, mtx_plain);63list_inithead(&screen->bo_cache_time);64for (int i = 0; i < NR_BO_CACHE_BUCKETS; i++)65list_inithead(&screen->bo_cache_buckets[i]);6667return true;68}6970void lima_bo_table_fini(struct lima_screen *screen)71{72mtx_destroy(&screen->bo_table_lock);73_mesa_hash_table_destroy(screen->bo_handles, NULL);74_mesa_hash_table_destroy(screen->bo_flink_names, NULL);75}7677static void78lima_bo_cache_remove(struct lima_bo *bo)79{80list_del(&bo->size_list);81list_del(&bo->time_list);82}8384static void lima_close_kms_handle(struct lima_screen *screen, uint32_t handle)85{86struct drm_gem_close args = {87.handle = handle,88};8990drmIoctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &args);91}9293static void94lima_bo_free(struct lima_bo *bo)95{96struct lima_screen *screen = bo->screen;9798if (lima_debug & LIMA_DEBUG_BO_CACHE)99fprintf(stderr, "%s: %p (size=%d)\n", __func__,100bo, bo->size);101102mtx_lock(&screen->bo_table_lock);103_mesa_hash_table_remove_key(screen->bo_handles,104(void *)(uintptr_t)bo->handle);105if (bo->flink_name)106_mesa_hash_table_remove_key(screen->bo_flink_names,107(void *)(uintptr_t)bo->flink_name);108mtx_unlock(&screen->bo_table_lock);109110if (bo->map)111lima_bo_unmap(bo);112113lima_close_kms_handle(screen, bo->handle);114free(bo);115}116117void lima_bo_cache_fini(struct lima_screen *screen)118{119mtx_destroy(&screen->bo_cache_lock);120121list_for_each_entry_safe(struct lima_bo, entry,122&screen->bo_cache_time, time_list) {123lima_bo_cache_remove(entry);124lima_bo_free(entry);125}126}127128static bool lima_bo_get_info(struct lima_bo *bo)129{130struct drm_lima_gem_info req = {131.handle = bo->handle,132};133134if(drmIoctl(bo->screen->fd, DRM_IOCTL_LIMA_GEM_INFO, &req))135return false;136137bo->offset = req.offset;138bo->va = req.va;139return true;140}141142static unsigned143lima_bucket_index(unsigned size)144{145/* Round down to POT to compute a bucket index */146147unsigned bucket_index = util_logbase2(size);148149/* Clamp the bucket index; all huge allocations will be150* sorted into the largest bucket */151bucket_index = CLAMP(bucket_index, MIN_BO_CACHE_BUCKET,152MAX_BO_CACHE_BUCKET);153154/* Reindex from 0 */155return (bucket_index - MIN_BO_CACHE_BUCKET);156}157158static struct list_head *159lima_bo_cache_get_bucket(struct lima_screen *screen, unsigned size)160{161return &screen->bo_cache_buckets[lima_bucket_index(size)];162}163164static void165lima_bo_cache_free_stale_bos(struct lima_screen *screen, time_t time)166{167unsigned cnt = 0;168list_for_each_entry_safe(struct lima_bo, entry,169&screen->bo_cache_time, time_list) {170/* Free BOs that are sitting idle for longer than 5 seconds */171if (time - entry->free_time > 6) {172lima_bo_cache_remove(entry);173lima_bo_free(entry);174cnt++;175} else176break;177}178if ((lima_debug & LIMA_DEBUG_BO_CACHE) && cnt)179fprintf(stderr, "%s: freed %d stale BOs\n", __func__, cnt);180}181182static void183lima_bo_cache_print_stats(struct lima_screen *screen)184{185fprintf(stderr, "===============\n");186fprintf(stderr, "BO cache stats:\n");187unsigned total_size = 0;188for (int i = 0; i < NR_BO_CACHE_BUCKETS; i++) {189struct list_head *bucket = &screen->bo_cache_buckets[i];190unsigned bucket_size = 0;191list_for_each_entry(struct lima_bo, entry, bucket, size_list) {192bucket_size += entry->size;193total_size += entry->size;194}195fprintf(stderr, "Bucket #%d, BOs: %d, size: %u\n", i,196list_length(bucket),197bucket_size);198}199fprintf(stderr, "Total size: %u\n", total_size);200}201202static bool203lima_bo_cache_put(struct lima_bo *bo)204{205if (!bo->cacheable)206return false;207208struct lima_screen *screen = bo->screen;209210mtx_lock(&screen->bo_cache_lock);211struct list_head *bucket = lima_bo_cache_get_bucket(screen, bo->size);212213if (!bucket) {214mtx_unlock(&screen->bo_cache_lock);215return false;216}217218struct timespec time;219clock_gettime(CLOCK_MONOTONIC, &time);220bo->free_time = time.tv_sec;221list_addtail(&bo->size_list, bucket);222list_addtail(&bo->time_list, &screen->bo_cache_time);223lima_bo_cache_free_stale_bos(screen, time.tv_sec);224if (lima_debug & LIMA_DEBUG_BO_CACHE) {225fprintf(stderr, "%s: put BO: %p (size=%d)\n", __func__, bo, bo->size);226lima_bo_cache_print_stats(screen);227}228mtx_unlock(&screen->bo_cache_lock);229230return true;231}232233static struct lima_bo *234lima_bo_cache_get(struct lima_screen *screen, uint32_t size, uint32_t flags)235{236/* we won't cache heap buffer */237if (flags & LIMA_BO_FLAG_HEAP)238return NULL;239240struct lima_bo *bo = NULL;241mtx_lock(&screen->bo_cache_lock);242struct list_head *bucket = lima_bo_cache_get_bucket(screen, size);243244if (!bucket) {245mtx_unlock(&screen->bo_cache_lock);246return false;247}248249list_for_each_entry_safe(struct lima_bo, entry, bucket, size_list) {250if (entry->size >= size) {251/* Check if BO is idle. If it's not it's better to allocate new one */252if (!lima_bo_wait(entry, LIMA_GEM_WAIT_WRITE, 0)) {253if (lima_debug & LIMA_DEBUG_BO_CACHE) {254fprintf(stderr, "%s: found BO %p but it's busy\n", __func__,255entry);256}257break;258}259260lima_bo_cache_remove(entry);261p_atomic_set(&entry->refcnt, 1);262entry->flags = flags;263bo = entry;264if (lima_debug & LIMA_DEBUG_BO_CACHE) {265fprintf(stderr, "%s: got BO: %p (size=%d), requested size %d\n",266__func__, bo, bo->size, size);267lima_bo_cache_print_stats(screen);268}269break;270}271}272273mtx_unlock(&screen->bo_cache_lock);274275return bo;276}277278struct lima_bo *lima_bo_create(struct lima_screen *screen,279uint32_t size, uint32_t flags)280{281struct lima_bo *bo;282283size = align(size, LIMA_PAGE_SIZE);284285/* Try to get bo from cache first */286bo = lima_bo_cache_get(screen, size, flags);287if (bo)288return bo;289290struct drm_lima_gem_create req = {291.size = size,292.flags = flags,293};294295if (!(bo = calloc(1, sizeof(*bo))))296return NULL;297298list_inithead(&bo->time_list);299list_inithead(&bo->size_list);300301if (drmIoctl(screen->fd, DRM_IOCTL_LIMA_GEM_CREATE, &req))302goto err_out0;303304bo->screen = screen;305bo->size = req.size;306bo->flags = req.flags;307bo->handle = req.handle;308bo->cacheable = !(lima_debug & LIMA_DEBUG_NO_BO_CACHE ||309flags & LIMA_BO_FLAG_HEAP);310p_atomic_set(&bo->refcnt, 1);311312if (!lima_bo_get_info(bo))313goto err_out1;314315if (lima_debug & LIMA_DEBUG_BO_CACHE)316fprintf(stderr, "%s: %p (size=%d)\n", __func__,317bo, bo->size);318319return bo;320321err_out1:322lima_close_kms_handle(screen, bo->handle);323err_out0:324free(bo);325return NULL;326}327328void lima_bo_unreference(struct lima_bo *bo)329{330if (!p_atomic_dec_zero(&bo->refcnt))331return;332333/* Try to put it into cache */334if (lima_bo_cache_put(bo))335return;336337lima_bo_free(bo);338}339340void *lima_bo_map(struct lima_bo *bo)341{342if (!bo->map) {343bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE,344MAP_SHARED, bo->screen->fd, bo->offset);345if (bo->map == MAP_FAILED)346bo->map = NULL;347}348349return bo->map;350}351352void lima_bo_unmap(struct lima_bo *bo)353{354if (bo->map) {355os_munmap(bo->map, bo->size);356bo->map = NULL;357}358}359360bool lima_bo_export(struct lima_bo *bo, struct winsys_handle *handle)361{362struct lima_screen *screen = bo->screen;363364/* Don't cache exported BOs */365bo->cacheable = false;366367switch (handle->type) {368case WINSYS_HANDLE_TYPE_SHARED:369if (!bo->flink_name) {370struct drm_gem_flink flink = {371.handle = bo->handle,372.name = 0,373};374if (drmIoctl(screen->fd, DRM_IOCTL_GEM_FLINK, &flink))375return false;376377bo->flink_name = flink.name;378379mtx_lock(&screen->bo_table_lock);380_mesa_hash_table_insert(screen->bo_flink_names,381(void *)(uintptr_t)bo->flink_name, bo);382mtx_unlock(&screen->bo_table_lock);383}384handle->handle = bo->flink_name;385return true;386387case WINSYS_HANDLE_TYPE_KMS:388mtx_lock(&screen->bo_table_lock);389_mesa_hash_table_insert(screen->bo_handles,390(void *)(uintptr_t)bo->handle, bo);391mtx_unlock(&screen->bo_table_lock);392393handle->handle = bo->handle;394return true;395396case WINSYS_HANDLE_TYPE_FD:397if (drmPrimeHandleToFD(screen->fd, bo->handle, DRM_CLOEXEC,398(int*)&handle->handle))399return false;400401mtx_lock(&screen->bo_table_lock);402_mesa_hash_table_insert(screen->bo_handles,403(void *)(uintptr_t)bo->handle, bo);404mtx_unlock(&screen->bo_table_lock);405return true;406407default:408return false;409}410}411412struct lima_bo *lima_bo_import(struct lima_screen *screen,413struct winsys_handle *handle)414{415struct lima_bo *bo = NULL;416struct drm_gem_open req = {0};417uint32_t dma_buf_size = 0;418unsigned h = handle->handle;419420mtx_lock(&screen->bo_table_lock);421422/* Convert a DMA buf handle to a KMS handle now. */423if (handle->type == WINSYS_HANDLE_TYPE_FD) {424uint32_t prime_handle;425off_t size;426427/* Get a KMS handle. */428if (drmPrimeFDToHandle(screen->fd, h, &prime_handle)) {429mtx_unlock(&screen->bo_table_lock);430return NULL;431}432433/* Query the buffer size. */434size = lseek(h, 0, SEEK_END);435if (size == (off_t)-1) {436mtx_unlock(&screen->bo_table_lock);437lima_close_kms_handle(screen, prime_handle);438return NULL;439}440lseek(h, 0, SEEK_SET);441442dma_buf_size = size;443h = prime_handle;444}445446switch (handle->type) {447case WINSYS_HANDLE_TYPE_SHARED:448bo = util_hash_table_get(screen->bo_flink_names,449(void *)(uintptr_t)h);450break;451case WINSYS_HANDLE_TYPE_KMS:452case WINSYS_HANDLE_TYPE_FD:453bo = util_hash_table_get(screen->bo_handles,454(void *)(uintptr_t)h);455break;456default:457mtx_unlock(&screen->bo_table_lock);458return NULL;459}460461if (bo) {462p_atomic_inc(&bo->refcnt);463/* Don't cache imported BOs */464bo->cacheable = false;465mtx_unlock(&screen->bo_table_lock);466return bo;467}468469if (!(bo = calloc(1, sizeof(*bo)))) {470mtx_unlock(&screen->bo_table_lock);471if (handle->type == WINSYS_HANDLE_TYPE_FD)472lima_close_kms_handle(screen, h);473return NULL;474}475476/* Don't cache imported BOs */477bo->cacheable = false;478list_inithead(&bo->time_list);479list_inithead(&bo->size_list);480bo->screen = screen;481p_atomic_set(&bo->refcnt, 1);482483switch (handle->type) {484case WINSYS_HANDLE_TYPE_SHARED:485req.name = h;486if (drmIoctl(screen->fd, DRM_IOCTL_GEM_OPEN, &req)) {487mtx_unlock(&screen->bo_table_lock);488free(bo);489return NULL;490}491bo->handle = req.handle;492bo->flink_name = h;493bo->size = req.size;494break;495case WINSYS_HANDLE_TYPE_FD:496bo->handle = h;497bo->size = dma_buf_size;498break;499default:500/* not possible */501assert(0);502}503504if (lima_bo_get_info(bo)) {505if (handle->type == WINSYS_HANDLE_TYPE_SHARED)506_mesa_hash_table_insert(screen->bo_flink_names,507(void *)(uintptr_t)bo->flink_name, bo);508_mesa_hash_table_insert(screen->bo_handles,509(void*)(uintptr_t)bo->handle, bo);510}511else {512lima_close_kms_handle(screen, bo->handle);513free(bo);514bo = NULL;515}516517mtx_unlock(&screen->bo_table_lock);518519return bo;520}521522bool lima_bo_wait(struct lima_bo *bo, uint32_t op, uint64_t timeout_ns)523{524int64_t abs_timeout;525526if (timeout_ns == 0)527abs_timeout = 0;528else529abs_timeout = os_time_get_absolute_timeout(timeout_ns);530531if (abs_timeout == OS_TIMEOUT_INFINITE)532abs_timeout = INT64_MAX;533534struct drm_lima_gem_wait req = {535.handle = bo->handle,536.op = op,537.timeout_ns = abs_timeout,538};539540return drmIoctl(bo->screen->fd, DRM_IOCTL_LIMA_GEM_WAIT, &req) == 0;541}542543544