Path: blob/21.2-virgl/src/etnaviv/drm/etnaviv_bo.c
4565 views
/*1* Copyright (C) 2014 Etnaviv 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 (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE20* SOFTWARE.21*22* Authors:23* Christian Gmeiner <[email protected]>24*/2526#include "os/os_mman.h"27#include "util/hash_table.h"2829#include "etnaviv_priv.h"30#include "etnaviv_drmif.h"3132simple_mtx_t etna_drm_table_lock = _SIMPLE_MTX_INITIALIZER_NP;33void _etna_bo_del(struct etna_bo *bo);3435/* set buffer name, and add to table, call w/ etna_drm_table_lock held: */36static void set_name(struct etna_bo *bo, uint32_t name)37{38simple_mtx_assert_locked(&etna_drm_table_lock);3940bo->name = name;41/* add ourself into the name table: */42_mesa_hash_table_insert(bo->dev->name_table, &bo->name, bo);43}4445/* Called under etna_drm_table_lock */46void _etna_bo_del(struct etna_bo *bo)47{48VG_BO_FREE(bo);4950simple_mtx_assert_locked(&etna_drm_table_lock);5152if (bo->va)53util_vma_heap_free(&bo->dev->address_space, bo->va, bo->size);5455if (bo->map)56os_munmap(bo->map, bo->size);5758if (bo->handle) {59struct drm_gem_close req = {60.handle = bo->handle,61};6263if (bo->name)64_mesa_hash_table_remove_key(bo->dev->name_table, &bo->name);6566_mesa_hash_table_remove_key(bo->dev->handle_table, &bo->handle);67drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);68}6970free(bo);71}7273/* lookup a buffer from it's handle, call w/ etna_drm_table_lock held: */74static struct etna_bo *lookup_bo(void *tbl, uint32_t handle)75{76struct etna_bo *bo = NULL;77struct hash_entry *entry;7879simple_mtx_assert_locked(&etna_drm_table_lock);8081entry = _mesa_hash_table_search(tbl, &handle);8283if (entry) {84/* found, incr refcnt and return: */85bo = etna_bo_ref(entry->data);8687/* don't break the bucket if this bo was found in one */88list_delinit(&bo->list);89}9091return bo;92}9394/* allocate a new buffer object, call w/ etna_drm_table_lock held */95static struct etna_bo *bo_from_handle(struct etna_device *dev,96uint32_t size, uint32_t handle, uint32_t flags)97{98struct etna_bo *bo = calloc(sizeof(*bo), 1);99100simple_mtx_assert_locked(&etna_drm_table_lock);101102if (!bo) {103struct drm_gem_close req = {104.handle = handle,105};106107drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);108109return NULL;110}111112bo->dev = etna_device_ref(dev);113bo->size = size;114bo->handle = handle;115bo->flags = flags;116p_atomic_set(&bo->refcnt, 1);117list_inithead(&bo->list);118/* add ourselves to the handle table: */119_mesa_hash_table_insert(dev->handle_table, &bo->handle, bo);120121if (dev->use_softpin)122bo->va = util_vma_heap_alloc(&dev->address_space, bo->size, 4096);123124return bo;125}126127/* allocate a new (un-tiled) buffer object */128struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,129uint32_t flags)130{131struct etna_bo *bo;132int ret;133struct drm_etnaviv_gem_new req = {134.flags = flags,135};136137bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);138if (bo)139return bo;140141req.size = size;142ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,143&req, sizeof(req));144if (ret)145return NULL;146147simple_mtx_lock(&etna_drm_table_lock);148bo = bo_from_handle(dev, size, req.handle, flags);149bo->reuse = 1;150simple_mtx_unlock(&etna_drm_table_lock);151152VG_BO_ALLOC(bo);153154return bo;155}156157struct etna_bo *etna_bo_ref(struct etna_bo *bo)158{159p_atomic_inc(&bo->refcnt);160161return bo;162}163164/* get buffer info */165static int get_buffer_info(struct etna_bo *bo)166{167int ret;168struct drm_etnaviv_gem_info req = {169.handle = bo->handle,170};171172ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,173&req, sizeof(req));174if (ret) {175return ret;176}177178/* really all we need for now is mmap offset */179bo->offset = req.offset;180181return 0;182}183184/* import a buffer object from DRI2 name */185struct etna_bo *etna_bo_from_name(struct etna_device *dev,186uint32_t name)187{188struct etna_bo *bo;189struct drm_gem_open req = {190.name = name,191};192193simple_mtx_lock(&etna_drm_table_lock);194195/* check name table first, to see if bo is already open: */196bo = lookup_bo(dev->name_table, name);197if (bo)198goto out_unlock;199200if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {201ERROR_MSG("gem-open failed: %s", strerror(errno));202goto out_unlock;203}204205bo = lookup_bo(dev->handle_table, req.handle);206if (bo)207goto out_unlock;208209bo = bo_from_handle(dev, req.size, req.handle, 0);210if (bo) {211set_name(bo, name);212VG_BO_ALLOC(bo);213}214215out_unlock:216simple_mtx_unlock(&etna_drm_table_lock);217218return bo;219}220221/* import a buffer from dmabuf fd, does not take ownership of the222* fd so caller should close() the fd when it is otherwise done223* with it (even if it is still using the 'struct etna_bo *')224*/225struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)226{227struct etna_bo *bo;228int ret, size;229uint32_t handle;230231/* take the lock before calling drmPrimeFDToHandle to avoid232* racing against etna_bo_del, which might invalidate the233* returned handle.234*/235simple_mtx_lock(&etna_drm_table_lock);236237ret = drmPrimeFDToHandle(dev->fd, fd, &handle);238if (ret) {239simple_mtx_unlock(&etna_drm_table_lock);240return NULL;241}242243bo = lookup_bo(dev->handle_table, handle);244if (bo)245goto out_unlock;246247/* lseek() to get bo size */248size = lseek(fd, 0, SEEK_END);249lseek(fd, 0, SEEK_CUR);250251bo = bo_from_handle(dev, size, handle, 0);252253VG_BO_ALLOC(bo);254255out_unlock:256simple_mtx_unlock(&etna_drm_table_lock);257258return bo;259}260261/* destroy a buffer object */262void etna_bo_del(struct etna_bo *bo)263{264if (!bo)265return;266267struct etna_device *dev = bo->dev;268269simple_mtx_lock(&etna_drm_table_lock);270271/* Must test under table lock to avoid racing with the from_dmabuf/name272* paths, which rely on the BO refcount to be stable over the lookup, so273* they can grab a reference when the BO is found in the hash.274*/275if (!p_atomic_dec_zero(&bo->refcnt))276goto out;277278if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))279goto out;280281_etna_bo_del(bo);282etna_device_del_locked(dev);283out:284simple_mtx_unlock(&etna_drm_table_lock);285}286287/* get the global flink/DRI2 buffer name */288int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)289{290if (!bo->name) {291struct drm_gem_flink req = {292.handle = bo->handle,293};294int ret;295296ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);297if (ret) {298return ret;299}300301simple_mtx_lock(&etna_drm_table_lock);302set_name(bo, req.name);303simple_mtx_unlock(&etna_drm_table_lock);304bo->reuse = 0;305}306307*name = bo->name;308309return 0;310}311312uint32_t etna_bo_handle(struct etna_bo *bo)313{314return bo->handle;315}316317/* caller owns the dmabuf fd that is returned and is responsible318* to close() it when done319*/320int etna_bo_dmabuf(struct etna_bo *bo)321{322int ret, prime_fd;323324ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,325&prime_fd);326if (ret) {327ERROR_MSG("failed to get dmabuf fd: %d", ret);328return ret;329}330331bo->reuse = 0;332333return prime_fd;334}335336uint32_t etna_bo_size(struct etna_bo *bo)337{338return bo->size;339}340341uint32_t etna_bo_gpu_va(struct etna_bo *bo)342{343return bo->va;344}345346void *etna_bo_map(struct etna_bo *bo)347{348if (!bo->map) {349if (!bo->offset) {350get_buffer_info(bo);351}352353bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE,354MAP_SHARED, bo->dev->fd, bo->offset);355if (bo->map == MAP_FAILED) {356ERROR_MSG("mmap failed: %s", strerror(errno));357bo->map = NULL;358}359}360361return bo->map;362}363364int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)365{366struct drm_etnaviv_gem_cpu_prep req = {367.handle = bo->handle,368.op = op,369};370371get_abs_timeout(&req.timeout, 5000000000);372373return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,374&req, sizeof(req));375}376377void etna_bo_cpu_fini(struct etna_bo *bo)378{379struct drm_etnaviv_gem_cpu_fini req = {380.handle = bo->handle,381};382383drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,384&req, sizeof(req));385}386387388