17ec681f3Smrg/* 27ec681f3Smrg * Copyright 2019 Collabora, Ltd. 37ec681f3Smrg * 47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 57ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 67ec681f3Smrg * to deal in the Software without restriction, including without limitation 77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 97ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 107ec681f3Smrg * 117ec681f3Smrg * The above copyright notice and this permission notice (including the next 127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 137ec681f3Smrg * Software. 147ec681f3Smrg * 157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 207ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 217ec681f3Smrg * SOFTWARE. 227ec681f3Smrg * 237ec681f3Smrg * Authors (Collabora): 247ec681f3Smrg * Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com> 257ec681f3Smrg */ 267ec681f3Smrg#include <errno.h> 277ec681f3Smrg#include <stdio.h> 287ec681f3Smrg#include <fcntl.h> 297ec681f3Smrg#include <xf86drm.h> 307ec681f3Smrg#include <pthread.h> 317ec681f3Smrg#include "drm-uapi/panfrost_drm.h" 327ec681f3Smrg 337ec681f3Smrg#include "pan_bo.h" 347ec681f3Smrg#include "pan_device.h" 357ec681f3Smrg#include "pan_util.h" 367ec681f3Smrg#include "wrap.h" 377ec681f3Smrg 387ec681f3Smrg#include "os/os_mman.h" 397ec681f3Smrg 407ec681f3Smrg#include "util/u_inlines.h" 417ec681f3Smrg#include "util/u_math.h" 427ec681f3Smrg 437ec681f3Smrg/* This file implements a userspace BO cache. Allocating and freeing 447ec681f3Smrg * GPU-visible buffers is very expensive, and even the extra kernel roundtrips 457ec681f3Smrg * adds more work than we would like at this point. So caching BOs in userspace 467ec681f3Smrg * solves both of these problems and does not require kernel updates. 477ec681f3Smrg * 487ec681f3Smrg * Cached BOs are sorted into a bucket based on rounding their size down to the 497ec681f3Smrg * nearest power-of-two. Each bucket contains a linked list of free panfrost_bo 507ec681f3Smrg * objects. Putting a BO into the cache is accomplished by adding it to the 517ec681f3Smrg * corresponding bucket. Getting a BO from the cache consists of finding the 527ec681f3Smrg * appropriate bucket and sorting. A cache eviction is a kernel-level free of a 537ec681f3Smrg * BO and removing it from the bucket. We special case evicting all BOs from 547ec681f3Smrg * the cache, since that's what helpful in practice and avoids extra logic 557ec681f3Smrg * around the linked list. 567ec681f3Smrg */ 577ec681f3Smrg 587ec681f3Smrgstatic struct panfrost_bo * 597ec681f3Smrgpanfrost_bo_alloc(struct panfrost_device *dev, size_t size, 607ec681f3Smrg uint32_t flags, const char *label) 617ec681f3Smrg{ 627ec681f3Smrg struct drm_panfrost_create_bo create_bo = { .size = size }; 637ec681f3Smrg struct panfrost_bo *bo; 647ec681f3Smrg int ret; 657ec681f3Smrg 667ec681f3Smrg if (dev->kernel_version->version_major > 1 || 677ec681f3Smrg dev->kernel_version->version_minor >= 1) { 687ec681f3Smrg if (flags & PAN_BO_GROWABLE) 697ec681f3Smrg create_bo.flags |= PANFROST_BO_HEAP; 707ec681f3Smrg if (!(flags & PAN_BO_EXECUTE)) 717ec681f3Smrg create_bo.flags |= PANFROST_BO_NOEXEC; 727ec681f3Smrg } 737ec681f3Smrg 747ec681f3Smrg ret = drmIoctl(dev->fd, DRM_IOCTL_PANFROST_CREATE_BO, &create_bo); 757ec681f3Smrg if (ret) { 767ec681f3Smrg fprintf(stderr, "DRM_IOCTL_PANFROST_CREATE_BO failed: %m\n"); 777ec681f3Smrg return NULL; 787ec681f3Smrg } 797ec681f3Smrg 807ec681f3Smrg bo = pan_lookup_bo(dev, create_bo.handle); 817ec681f3Smrg assert(!memcmp(bo, &((struct panfrost_bo){}), sizeof(*bo))); 827ec681f3Smrg 837ec681f3Smrg bo->size = create_bo.size; 847ec681f3Smrg bo->ptr.gpu = create_bo.offset; 857ec681f3Smrg bo->gem_handle = create_bo.handle; 867ec681f3Smrg bo->flags = flags; 877ec681f3Smrg bo->dev = dev; 887ec681f3Smrg bo->label = label; 897ec681f3Smrg return bo; 907ec681f3Smrg} 917ec681f3Smrg 927ec681f3Smrgstatic void 937ec681f3Smrgpanfrost_bo_free(struct panfrost_bo *bo) 947ec681f3Smrg{ 957ec681f3Smrg struct drm_gem_close gem_close = { .handle = bo->gem_handle }; 967ec681f3Smrg int ret; 977ec681f3Smrg 987ec681f3Smrg ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &gem_close); 997ec681f3Smrg if (ret) { 1007ec681f3Smrg fprintf(stderr, "DRM_IOCTL_GEM_CLOSE failed: %m\n"); 1017ec681f3Smrg assert(0); 1027ec681f3Smrg } 1037ec681f3Smrg 1047ec681f3Smrg /* BO will be freed with the sparse array, but zero to indicate free */ 1057ec681f3Smrg memset(bo, 0, sizeof(*bo)); 1067ec681f3Smrg} 1077ec681f3Smrg 1087ec681f3Smrg/* Returns true if the BO is ready, false otherwise. 1097ec681f3Smrg * access_type is encoding the type of access one wants to ensure is done. 1107ec681f3Smrg * Waiting is always done for writers, but if wait_readers is set then readers 1117ec681f3Smrg * are also waited for. 1127ec681f3Smrg */ 1137ec681f3Smrgbool 1147ec681f3Smrgpanfrost_bo_wait(struct panfrost_bo *bo, int64_t timeout_ns, bool wait_readers) 1157ec681f3Smrg{ 1167ec681f3Smrg struct drm_panfrost_wait_bo req = { 1177ec681f3Smrg .handle = bo->gem_handle, 1187ec681f3Smrg .timeout_ns = timeout_ns, 1197ec681f3Smrg }; 1207ec681f3Smrg int ret; 1217ec681f3Smrg 1227ec681f3Smrg /* If the BO has been exported or imported we can't rely on the cached 1237ec681f3Smrg * state, we need to call the WAIT_BO ioctl. 1247ec681f3Smrg */ 1257ec681f3Smrg if (!(bo->flags & PAN_BO_SHARED)) { 1267ec681f3Smrg /* If ->gpu_access is 0, the BO is idle, no need to wait. */ 1277ec681f3Smrg if (!bo->gpu_access) 1287ec681f3Smrg return true; 1297ec681f3Smrg 1307ec681f3Smrg /* If the caller only wants to wait for writers and no 1317ec681f3Smrg * writes are pending, we don't have to wait. 1327ec681f3Smrg */ 1337ec681f3Smrg if (!wait_readers && !(bo->gpu_access & PAN_BO_ACCESS_WRITE)) 1347ec681f3Smrg return true; 1357ec681f3Smrg } 1367ec681f3Smrg 1377ec681f3Smrg /* The ioctl returns >= 0 value when the BO we are waiting for is ready 1387ec681f3Smrg * -1 otherwise. 1397ec681f3Smrg */ 1407ec681f3Smrg ret = drmIoctl(bo->dev->fd, DRM_IOCTL_PANFROST_WAIT_BO, &req); 1417ec681f3Smrg if (ret != -1) { 1427ec681f3Smrg /* Set gpu_access to 0 so that the next call to bo_wait() 1437ec681f3Smrg * doesn't have to call the WAIT_BO ioctl. 1447ec681f3Smrg */ 1457ec681f3Smrg bo->gpu_access = 0; 1467ec681f3Smrg return true; 1477ec681f3Smrg } 1487ec681f3Smrg 1497ec681f3Smrg /* If errno is not ETIMEDOUT or EBUSY that means the handle we passed 1507ec681f3Smrg * is invalid, which shouldn't happen here. 1517ec681f3Smrg */ 1527ec681f3Smrg assert(errno == ETIMEDOUT || errno == EBUSY); 1537ec681f3Smrg return false; 1547ec681f3Smrg} 1557ec681f3Smrg 1567ec681f3Smrg/* Helper to calculate the bucket index of a BO */ 1577ec681f3Smrg 1587ec681f3Smrgstatic unsigned 1597ec681f3Smrgpan_bucket_index(unsigned size) 1607ec681f3Smrg{ 1617ec681f3Smrg /* Round down to POT to compute a bucket index */ 1627ec681f3Smrg 1637ec681f3Smrg unsigned bucket_index = util_logbase2(size); 1647ec681f3Smrg 1657ec681f3Smrg /* Clamp the bucket index; all huge allocations will be 1667ec681f3Smrg * sorted into the largest bucket */ 1677ec681f3Smrg 1687ec681f3Smrg bucket_index = MIN2(bucket_index, MAX_BO_CACHE_BUCKET); 1697ec681f3Smrg 1707ec681f3Smrg /* The minimum bucket size must equal the minimum allocation 1717ec681f3Smrg * size; the maximum we clamped */ 1727ec681f3Smrg 1737ec681f3Smrg assert(bucket_index >= MIN_BO_CACHE_BUCKET); 1747ec681f3Smrg assert(bucket_index <= MAX_BO_CACHE_BUCKET); 1757ec681f3Smrg 1767ec681f3Smrg /* Reindex from 0 */ 1777ec681f3Smrg return (bucket_index - MIN_BO_CACHE_BUCKET); 1787ec681f3Smrg} 1797ec681f3Smrg 1807ec681f3Smrgstatic struct list_head * 1817ec681f3Smrgpan_bucket(struct panfrost_device *dev, unsigned size) 1827ec681f3Smrg{ 1837ec681f3Smrg return &dev->bo_cache.buckets[pan_bucket_index(size)]; 1847ec681f3Smrg} 1857ec681f3Smrg 1867ec681f3Smrg/* Tries to fetch a BO of sufficient size with the appropriate flags from the 1877ec681f3Smrg * BO cache. If it succeeds, it returns that BO and removes the BO from the 1887ec681f3Smrg * cache. If it fails, it returns NULL signaling the caller to allocate a new 1897ec681f3Smrg * BO. */ 1907ec681f3Smrg 1917ec681f3Smrgstatic struct panfrost_bo * 1927ec681f3Smrgpanfrost_bo_cache_fetch(struct panfrost_device *dev, 1937ec681f3Smrg size_t size, uint32_t flags, const char *label, 1947ec681f3Smrg bool dontwait) 1957ec681f3Smrg{ 1967ec681f3Smrg pthread_mutex_lock(&dev->bo_cache.lock); 1977ec681f3Smrg struct list_head *bucket = pan_bucket(dev, size); 1987ec681f3Smrg struct panfrost_bo *bo = NULL; 1997ec681f3Smrg 2007ec681f3Smrg /* Iterate the bucket looking for something suitable */ 2017ec681f3Smrg list_for_each_entry_safe(struct panfrost_bo, entry, bucket, 2027ec681f3Smrg bucket_link) { 2037ec681f3Smrg if (entry->size < size || entry->flags != flags) 2047ec681f3Smrg continue; 2057ec681f3Smrg 2067ec681f3Smrg /* If the oldest BO in the cache is busy, likely so is 2077ec681f3Smrg * everything newer, so bail. */ 2087ec681f3Smrg if (!panfrost_bo_wait(entry, dontwait ? 0 : INT64_MAX, 2097ec681f3Smrg PAN_BO_ACCESS_RW)) 2107ec681f3Smrg break; 2117ec681f3Smrg 2127ec681f3Smrg struct drm_panfrost_madvise madv = { 2137ec681f3Smrg .handle = entry->gem_handle, 2147ec681f3Smrg .madv = PANFROST_MADV_WILLNEED, 2157ec681f3Smrg }; 2167ec681f3Smrg int ret; 2177ec681f3Smrg 2187ec681f3Smrg /* This one works, splice it out of the cache */ 2197ec681f3Smrg list_del(&entry->bucket_link); 2207ec681f3Smrg list_del(&entry->lru_link); 2217ec681f3Smrg 2227ec681f3Smrg ret = drmIoctl(dev->fd, DRM_IOCTL_PANFROST_MADVISE, &madv); 2237ec681f3Smrg if (!ret && !madv.retained) { 2247ec681f3Smrg panfrost_bo_free(entry); 2257ec681f3Smrg continue; 2267ec681f3Smrg } 2277ec681f3Smrg /* Let's go! */ 2287ec681f3Smrg bo = entry; 2297ec681f3Smrg bo->label = label; 2307ec681f3Smrg break; 2317ec681f3Smrg } 2327ec681f3Smrg pthread_mutex_unlock(&dev->bo_cache.lock); 2337ec681f3Smrg 2347ec681f3Smrg return bo; 2357ec681f3Smrg} 2367ec681f3Smrg 2377ec681f3Smrgstatic void 2387ec681f3Smrgpanfrost_bo_cache_evict_stale_bos(struct panfrost_device *dev) 2397ec681f3Smrg{ 2407ec681f3Smrg struct timespec time; 2417ec681f3Smrg 2427ec681f3Smrg clock_gettime(CLOCK_MONOTONIC, &time); 2437ec681f3Smrg list_for_each_entry_safe(struct panfrost_bo, entry, 2447ec681f3Smrg &dev->bo_cache.lru, lru_link) { 2457ec681f3Smrg /* We want all entries that have been used more than 1 sec 2467ec681f3Smrg * ago to be dropped, others can be kept. 2477ec681f3Smrg * Note the <= 2 check and not <= 1. It's here to account for 2487ec681f3Smrg * the fact that we're only testing ->tv_sec, not ->tv_nsec. 2497ec681f3Smrg * That means we might keep entries that are between 1 and 2 2507ec681f3Smrg * seconds old, but we don't really care, as long as unused BOs 2517ec681f3Smrg * are dropped at some point. 2527ec681f3Smrg */ 2537ec681f3Smrg if (time.tv_sec - entry->last_used <= 2) 2547ec681f3Smrg break; 2557ec681f3Smrg 2567ec681f3Smrg list_del(&entry->bucket_link); 2577ec681f3Smrg list_del(&entry->lru_link); 2587ec681f3Smrg panfrost_bo_free(entry); 2597ec681f3Smrg } 2607ec681f3Smrg} 2617ec681f3Smrg 2627ec681f3Smrg/* Tries to add a BO to the cache. Returns if it was 2637ec681f3Smrg * successful */ 2647ec681f3Smrg 2657ec681f3Smrgstatic bool 2667ec681f3Smrgpanfrost_bo_cache_put(struct panfrost_bo *bo) 2677ec681f3Smrg{ 2687ec681f3Smrg struct panfrost_device *dev = bo->dev; 2697ec681f3Smrg 2707ec681f3Smrg if (bo->flags & PAN_BO_SHARED || dev->debug & PAN_DBG_NO_CACHE) 2717ec681f3Smrg return false; 2727ec681f3Smrg 2737ec681f3Smrg /* Must be first */ 2747ec681f3Smrg pthread_mutex_lock(&dev->bo_cache.lock); 2757ec681f3Smrg 2767ec681f3Smrg struct list_head *bucket = pan_bucket(dev, MAX2(bo->size, 4096)); 2777ec681f3Smrg struct drm_panfrost_madvise madv; 2787ec681f3Smrg struct timespec time; 2797ec681f3Smrg 2807ec681f3Smrg madv.handle = bo->gem_handle; 2817ec681f3Smrg madv.madv = PANFROST_MADV_DONTNEED; 2827ec681f3Smrg madv.retained = 0; 2837ec681f3Smrg 2847ec681f3Smrg drmIoctl(dev->fd, DRM_IOCTL_PANFROST_MADVISE, &madv); 2857ec681f3Smrg 2867ec681f3Smrg /* Add us to the bucket */ 2877ec681f3Smrg list_addtail(&bo->bucket_link, bucket); 2887ec681f3Smrg 2897ec681f3Smrg /* Add us to the LRU list and update the last_used field. */ 2907ec681f3Smrg list_addtail(&bo->lru_link, &dev->bo_cache.lru); 2917ec681f3Smrg clock_gettime(CLOCK_MONOTONIC, &time); 2927ec681f3Smrg bo->last_used = time.tv_sec; 2937ec681f3Smrg 2947ec681f3Smrg /* Let's do some cleanup in the BO cache while we hold the 2957ec681f3Smrg * lock. 2967ec681f3Smrg */ 2977ec681f3Smrg panfrost_bo_cache_evict_stale_bos(dev); 2987ec681f3Smrg 2997ec681f3Smrg /* Update the label to help debug BO cache memory usage issues */ 3007ec681f3Smrg bo->label = "Unused (BO cache)"; 3017ec681f3Smrg 3027ec681f3Smrg /* Must be last */ 3037ec681f3Smrg pthread_mutex_unlock(&dev->bo_cache.lock); 3047ec681f3Smrg return true; 3057ec681f3Smrg} 3067ec681f3Smrg 3077ec681f3Smrg/* Evicts all BOs from the cache. Called during context 3087ec681f3Smrg * destroy or during low-memory situations (to free up 3097ec681f3Smrg * memory that may be unused by us just sitting in our 3107ec681f3Smrg * cache, but still reserved from the perspective of the 3117ec681f3Smrg * OS) */ 3127ec681f3Smrg 3137ec681f3Smrgvoid 3147ec681f3Smrgpanfrost_bo_cache_evict_all( 3157ec681f3Smrg struct panfrost_device *dev) 3167ec681f3Smrg{ 3177ec681f3Smrg pthread_mutex_lock(&dev->bo_cache.lock); 3187ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(dev->bo_cache.buckets); ++i) { 3197ec681f3Smrg struct list_head *bucket = &dev->bo_cache.buckets[i]; 3207ec681f3Smrg 3217ec681f3Smrg list_for_each_entry_safe(struct panfrost_bo, entry, bucket, 3227ec681f3Smrg bucket_link) { 3237ec681f3Smrg list_del(&entry->bucket_link); 3247ec681f3Smrg list_del(&entry->lru_link); 3257ec681f3Smrg panfrost_bo_free(entry); 3267ec681f3Smrg } 3277ec681f3Smrg } 3287ec681f3Smrg pthread_mutex_unlock(&dev->bo_cache.lock); 3297ec681f3Smrg} 3307ec681f3Smrg 3317ec681f3Smrgvoid 3327ec681f3Smrgpanfrost_bo_mmap(struct panfrost_bo *bo) 3337ec681f3Smrg{ 3347ec681f3Smrg struct drm_panfrost_mmap_bo mmap_bo = { .handle = bo->gem_handle }; 3357ec681f3Smrg int ret; 3367ec681f3Smrg 3377ec681f3Smrg if (bo->ptr.cpu) 3387ec681f3Smrg return; 3397ec681f3Smrg 3407ec681f3Smrg ret = drmIoctl(bo->dev->fd, DRM_IOCTL_PANFROST_MMAP_BO, &mmap_bo); 3417ec681f3Smrg if (ret) { 3427ec681f3Smrg fprintf(stderr, "DRM_IOCTL_PANFROST_MMAP_BO failed: %m\n"); 3437ec681f3Smrg assert(0); 3447ec681f3Smrg } 3457ec681f3Smrg 3467ec681f3Smrg bo->ptr.cpu = os_mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, 3477ec681f3Smrg bo->dev->fd, mmap_bo.offset); 3487ec681f3Smrg if (bo->ptr.cpu == MAP_FAILED) { 3497ec681f3Smrg bo->ptr.cpu = NULL; 3507ec681f3Smrg fprintf(stderr, 3517ec681f3Smrg "mmap failed: result=%p size=0x%llx fd=%i offset=0x%llx %m\n", 3527ec681f3Smrg bo->ptr.cpu, (long long)bo->size, bo->dev->fd, 3537ec681f3Smrg (long long)mmap_bo.offset); 3547ec681f3Smrg } 3557ec681f3Smrg} 3567ec681f3Smrg 3577ec681f3Smrgstatic void 3587ec681f3Smrgpanfrost_bo_munmap(struct panfrost_bo *bo) 3597ec681f3Smrg{ 3607ec681f3Smrg if (!bo->ptr.cpu) 3617ec681f3Smrg return; 3627ec681f3Smrg 3637ec681f3Smrg if (os_munmap((void *) (uintptr_t)bo->ptr.cpu, bo->size)) { 3647ec681f3Smrg perror("munmap"); 3657ec681f3Smrg abort(); 3667ec681f3Smrg } 3677ec681f3Smrg 3687ec681f3Smrg bo->ptr.cpu = NULL; 3697ec681f3Smrg} 3707ec681f3Smrg 3717ec681f3Smrgstruct panfrost_bo * 3727ec681f3Smrgpanfrost_bo_create(struct panfrost_device *dev, size_t size, 3737ec681f3Smrg uint32_t flags, const char *label) 3747ec681f3Smrg{ 3757ec681f3Smrg struct panfrost_bo *bo; 3767ec681f3Smrg 3777ec681f3Smrg /* Kernel will fail (confusingly) with EPERM otherwise */ 3787ec681f3Smrg assert(size > 0); 3797ec681f3Smrg 3807ec681f3Smrg /* To maximize BO cache usage, don't allocate tiny BOs */ 3817ec681f3Smrg size = ALIGN_POT(size, 4096); 3827ec681f3Smrg 3837ec681f3Smrg /* GROWABLE BOs cannot be mmapped */ 3847ec681f3Smrg if (flags & PAN_BO_GROWABLE) 3857ec681f3Smrg assert(flags & PAN_BO_INVISIBLE); 3867ec681f3Smrg 3877ec681f3Smrg /* Before creating a BO, we first want to check the cache but without 3887ec681f3Smrg * waiting for BO readiness (BOs in the cache can still be referenced 3897ec681f3Smrg * by jobs that are not finished yet). 3907ec681f3Smrg * If the cached allocation fails we fall back on fresh BO allocation, 3917ec681f3Smrg * and if that fails too, we try one more time to allocate from the 3927ec681f3Smrg * cache, but this time we accept to wait. 3937ec681f3Smrg */ 3947ec681f3Smrg bo = panfrost_bo_cache_fetch(dev, size, flags, label, true); 3957ec681f3Smrg if (!bo) 3967ec681f3Smrg bo = panfrost_bo_alloc(dev, size, flags, label); 3977ec681f3Smrg if (!bo) 3987ec681f3Smrg bo = panfrost_bo_cache_fetch(dev, size, flags, label, false); 3997ec681f3Smrg 4007ec681f3Smrg if (!bo) 4017ec681f3Smrg fprintf(stderr, "BO creation failed\n"); 4027ec681f3Smrg 4037ec681f3Smrg assert(bo); 4047ec681f3Smrg 4057ec681f3Smrg /* Only mmap now if we know we need to. For CPU-invisible buffers, we 4067ec681f3Smrg * never map since we don't care about their contents; they're purely 4077ec681f3Smrg * for GPU-internal use. But we do trace them anyway. */ 4087ec681f3Smrg 4097ec681f3Smrg if (!(flags & (PAN_BO_INVISIBLE | PAN_BO_DELAY_MMAP))) 4107ec681f3Smrg panfrost_bo_mmap(bo); 4117ec681f3Smrg 4127ec681f3Smrg p_atomic_set(&bo->refcnt, 1); 4137ec681f3Smrg 4147ec681f3Smrg if (dev->debug & (PAN_DBG_TRACE | PAN_DBG_SYNC)) { 4157ec681f3Smrg if (flags & PAN_BO_INVISIBLE) 4167ec681f3Smrg pandecode_inject_mmap(bo->ptr.gpu, NULL, bo->size, NULL); 4177ec681f3Smrg else if (!(flags & PAN_BO_DELAY_MMAP)) 4187ec681f3Smrg pandecode_inject_mmap(bo->ptr.gpu, bo->ptr.cpu, bo->size, NULL); 4197ec681f3Smrg } 4207ec681f3Smrg 4217ec681f3Smrg return bo; 4227ec681f3Smrg} 4237ec681f3Smrg 4247ec681f3Smrgvoid 4257ec681f3Smrgpanfrost_bo_reference(struct panfrost_bo *bo) 4267ec681f3Smrg{ 4277ec681f3Smrg if (bo) { 4287ec681f3Smrg ASSERTED int count = p_atomic_inc_return(&bo->refcnt); 4297ec681f3Smrg assert(count != 1); 4307ec681f3Smrg } 4317ec681f3Smrg} 4327ec681f3Smrg 4337ec681f3Smrgvoid 4347ec681f3Smrgpanfrost_bo_unreference(struct panfrost_bo *bo) 4357ec681f3Smrg{ 4367ec681f3Smrg if (!bo) 4377ec681f3Smrg return; 4387ec681f3Smrg 4397ec681f3Smrg /* Don't return to cache if there are still references */ 4407ec681f3Smrg if (p_atomic_dec_return(&bo->refcnt)) 4417ec681f3Smrg return; 4427ec681f3Smrg 4437ec681f3Smrg struct panfrost_device *dev = bo->dev; 4447ec681f3Smrg 4457ec681f3Smrg pthread_mutex_lock(&dev->bo_map_lock); 4467ec681f3Smrg 4477ec681f3Smrg /* Someone might have imported this BO while we were waiting for the 4487ec681f3Smrg * lock, let's make sure it's still not referenced before freeing it. 4497ec681f3Smrg */ 4507ec681f3Smrg if (p_atomic_read(&bo->refcnt) == 0) { 4517ec681f3Smrg /* When the reference count goes to zero, we need to cleanup */ 4527ec681f3Smrg panfrost_bo_munmap(bo); 4537ec681f3Smrg 4547ec681f3Smrg if (dev->debug & (PAN_DBG_TRACE | PAN_DBG_SYNC)) 4557ec681f3Smrg pandecode_inject_free(bo->ptr.gpu, bo->size); 4567ec681f3Smrg 4577ec681f3Smrg /* Rather than freeing the BO now, we'll cache the BO for later 4587ec681f3Smrg * allocations if we're allowed to. 4597ec681f3Smrg */ 4607ec681f3Smrg if (!panfrost_bo_cache_put(bo)) 4617ec681f3Smrg panfrost_bo_free(bo); 4627ec681f3Smrg 4637ec681f3Smrg } 4647ec681f3Smrg pthread_mutex_unlock(&dev->bo_map_lock); 4657ec681f3Smrg} 4667ec681f3Smrg 4677ec681f3Smrgstruct panfrost_bo * 4687ec681f3Smrgpanfrost_bo_import(struct panfrost_device *dev, int fd) 4697ec681f3Smrg{ 4707ec681f3Smrg struct panfrost_bo *bo; 4717ec681f3Smrg struct drm_panfrost_get_bo_offset get_bo_offset = {0,}; 4727ec681f3Smrg ASSERTED int ret; 4737ec681f3Smrg unsigned gem_handle; 4747ec681f3Smrg 4757ec681f3Smrg ret = drmPrimeFDToHandle(dev->fd, fd, &gem_handle); 4767ec681f3Smrg assert(!ret); 4777ec681f3Smrg 4787ec681f3Smrg pthread_mutex_lock(&dev->bo_map_lock); 4797ec681f3Smrg bo = pan_lookup_bo(dev, gem_handle); 4807ec681f3Smrg 4817ec681f3Smrg if (!bo->dev) { 4827ec681f3Smrg get_bo_offset.handle = gem_handle; 4837ec681f3Smrg ret = drmIoctl(dev->fd, DRM_IOCTL_PANFROST_GET_BO_OFFSET, &get_bo_offset); 4847ec681f3Smrg assert(!ret); 4857ec681f3Smrg 4867ec681f3Smrg bo->dev = dev; 4877ec681f3Smrg bo->ptr.gpu = (mali_ptr) get_bo_offset.offset; 4887ec681f3Smrg bo->size = lseek(fd, 0, SEEK_END); 4897ec681f3Smrg /* Sometimes this can fail and return -1. size of -1 is not 4907ec681f3Smrg * a nice thing for mmap to try mmap. Be more robust also 4917ec681f3Smrg * for zero sized maps and fail nicely too 4927ec681f3Smrg */ 4937ec681f3Smrg if ((bo->size == 0) || (bo->size == (size_t)-1)) { 4947ec681f3Smrg pthread_mutex_unlock(&dev->bo_map_lock); 4957ec681f3Smrg return NULL; 4967ec681f3Smrg } 4977ec681f3Smrg bo->flags = PAN_BO_SHARED; 4987ec681f3Smrg bo->gem_handle = gem_handle; 4997ec681f3Smrg p_atomic_set(&bo->refcnt, 1); 5007ec681f3Smrg // TODO map and unmap on demand? 5017ec681f3Smrg panfrost_bo_mmap(bo); 5027ec681f3Smrg } else { 5037ec681f3Smrg /* bo->refcnt == 0 can happen if the BO 5047ec681f3Smrg * was being released but panfrost_bo_import() acquired the 5057ec681f3Smrg * lock before panfrost_bo_unreference(). In that case, refcnt 5067ec681f3Smrg * is 0 and we can't use panfrost_bo_reference() directly, we 5077ec681f3Smrg * have to re-initialize the refcnt(). 5087ec681f3Smrg * Note that panfrost_bo_unreference() checks 5097ec681f3Smrg * refcnt value just after acquiring the lock to 5107ec681f3Smrg * make sure the object is not freed if panfrost_bo_import() 5117ec681f3Smrg * acquired it in the meantime. 5127ec681f3Smrg */ 5137ec681f3Smrg if (p_atomic_read(&bo->refcnt) == 0) 5147ec681f3Smrg p_atomic_set(&bo->refcnt, 1); 5157ec681f3Smrg else 5167ec681f3Smrg panfrost_bo_reference(bo); 5177ec681f3Smrg assert(bo->ptr.cpu); 5187ec681f3Smrg } 5197ec681f3Smrg pthread_mutex_unlock(&dev->bo_map_lock); 5207ec681f3Smrg 5217ec681f3Smrg return bo; 5227ec681f3Smrg} 5237ec681f3Smrg 5247ec681f3Smrgint 5257ec681f3Smrgpanfrost_bo_export(struct panfrost_bo *bo) 5267ec681f3Smrg{ 5277ec681f3Smrg struct drm_prime_handle args = { 5287ec681f3Smrg .handle = bo->gem_handle, 5297ec681f3Smrg .flags = DRM_CLOEXEC, 5307ec681f3Smrg }; 5317ec681f3Smrg 5327ec681f3Smrg int ret = drmIoctl(bo->dev->fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args); 5337ec681f3Smrg if (ret == -1) 5347ec681f3Smrg return -1; 5357ec681f3Smrg 5367ec681f3Smrg bo->flags |= PAN_BO_SHARED; 5377ec681f3Smrg return args.fd; 5387ec681f3Smrg} 5397ec681f3Smrg 540