162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2015 Broadcom 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * DOC: VC4 GEM BO management support 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * The VC4 GPU architecture (both scanout and rendering) has direct 1062306a36Sopenharmony_ci * access to system memory with no MMU in between. To support it, we 1162306a36Sopenharmony_ci * use the GEM DMA helper functions to allocate contiguous ranges of 1262306a36Sopenharmony_ci * physical memory for our BOs. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Since the DMA allocator is very slow, we keep a cache of recently 1562306a36Sopenharmony_ci * freed BOs around so that the kernel's allocation of objects for 3D 1662306a36Sopenharmony_ci * rendering can return quickly. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/dma-buf.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "vc4_drv.h" 2462306a36Sopenharmony_ci#include "uapi/drm/vc4_drm.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct drm_gem_object_funcs vc4_gem_object_funcs; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const char * const bo_type_names[] = { 2962306a36Sopenharmony_ci "kernel", 3062306a36Sopenharmony_ci "V3D", 3162306a36Sopenharmony_ci "V3D shader", 3262306a36Sopenharmony_ci "dumb", 3362306a36Sopenharmony_ci "binner", 3462306a36Sopenharmony_ci "RCL", 3562306a36Sopenharmony_ci "BCL", 3662306a36Sopenharmony_ci "kernel BO cache", 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic bool is_user_label(int label) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci return label >= VC4_BO_TYPE_COUNT; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void vc4_bo_stats_print(struct drm_printer *p, struct vc4_dev *vc4) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci int i; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci for (i = 0; i < vc4->num_labels; i++) { 4962306a36Sopenharmony_ci if (!vc4->bo_labels[i].num_allocated) 5062306a36Sopenharmony_ci continue; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci drm_printf(p, "%30s: %6dkb BOs (%d)\n", 5362306a36Sopenharmony_ci vc4->bo_labels[i].name, 5462306a36Sopenharmony_ci vc4->bo_labels[i].size_allocated / 1024, 5562306a36Sopenharmony_ci vc4->bo_labels[i].num_allocated); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 5962306a36Sopenharmony_ci if (vc4->purgeable.num) 6062306a36Sopenharmony_ci drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "userspace BO cache", 6162306a36Sopenharmony_ci vc4->purgeable.size / 1024, vc4->purgeable.num); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (vc4->purgeable.purged_num) 6462306a36Sopenharmony_ci drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "total purged BO", 6562306a36Sopenharmony_ci vc4->purgeable.purged_size / 1024, 6662306a36Sopenharmony_ci vc4->purgeable.purged_num); 6762306a36Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct drm_debugfs_entry *entry = m->private; 7362306a36Sopenharmony_ci struct drm_device *dev = entry->dev; 7462306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 7562306a36Sopenharmony_ci struct drm_printer p = drm_seq_file_printer(m); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci vc4_bo_stats_print(&p, vc4); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Takes ownership of *name and returns the appropriate slot for it in 8362306a36Sopenharmony_ci * the bo_labels[] array, extending it as necessary. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * This is inefficient and could use a hash table instead of walking 8662306a36Sopenharmony_ci * an array and strcmp()ing. However, the assumption is that user 8762306a36Sopenharmony_ci * labeling will be infrequent (scanout buffers and other long-lived 8862306a36Sopenharmony_ci * objects, or debug driver builds), so we can live with it for now. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic int vc4_get_user_label(struct vc4_dev *vc4, const char *name) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci int i; 9362306a36Sopenharmony_ci int free_slot = -1; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (i = 0; i < vc4->num_labels; i++) { 9662306a36Sopenharmony_ci if (!vc4->bo_labels[i].name) { 9762306a36Sopenharmony_ci free_slot = i; 9862306a36Sopenharmony_ci } else if (strcmp(vc4->bo_labels[i].name, name) == 0) { 9962306a36Sopenharmony_ci kfree(name); 10062306a36Sopenharmony_ci return i; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (free_slot != -1) { 10562306a36Sopenharmony_ci WARN_ON(vc4->bo_labels[free_slot].num_allocated != 0); 10662306a36Sopenharmony_ci vc4->bo_labels[free_slot].name = name; 10762306a36Sopenharmony_ci return free_slot; 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci u32 new_label_count = vc4->num_labels + 1; 11062306a36Sopenharmony_ci struct vc4_label *new_labels = 11162306a36Sopenharmony_ci krealloc(vc4->bo_labels, 11262306a36Sopenharmony_ci new_label_count * sizeof(*new_labels), 11362306a36Sopenharmony_ci GFP_KERNEL); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (!new_labels) { 11662306a36Sopenharmony_ci kfree(name); 11762306a36Sopenharmony_ci return -1; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci free_slot = vc4->num_labels; 12162306a36Sopenharmony_ci vc4->bo_labels = new_labels; 12262306a36Sopenharmony_ci vc4->num_labels = new_label_count; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci vc4->bo_labels[free_slot].name = name; 12562306a36Sopenharmony_ci vc4->bo_labels[free_slot].num_allocated = 0; 12662306a36Sopenharmony_ci vc4->bo_labels[free_slot].size_allocated = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return free_slot; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void vc4_bo_set_label(struct drm_gem_object *gem_obj, int label) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(gem_obj); 13562306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(gem_obj->dev); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (label != -1) { 14062306a36Sopenharmony_ci vc4->bo_labels[label].num_allocated++; 14162306a36Sopenharmony_ci vc4->bo_labels[label].size_allocated += gem_obj->size; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci vc4->bo_labels[bo->label].num_allocated--; 14562306a36Sopenharmony_ci vc4->bo_labels[bo->label].size_allocated -= gem_obj->size; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (vc4->bo_labels[bo->label].num_allocated == 0 && 14862306a36Sopenharmony_ci is_user_label(bo->label)) { 14962306a36Sopenharmony_ci /* Free user BO label slots on last unreference. 15062306a36Sopenharmony_ci * Slots are just where we track the stats for a given 15162306a36Sopenharmony_ci * name, and once a name is unused we can reuse that 15262306a36Sopenharmony_ci * slot. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci kfree(vc4->bo_labels[bo->label].name); 15562306a36Sopenharmony_ci vc4->bo_labels[bo->label].name = NULL; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci bo->label = label; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic uint32_t bo_page_index(size_t size) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci return (size / PAGE_SIZE) - 1; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void vc4_bo_destroy(struct vc4_bo *bo) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct drm_gem_object *obj = &bo->base.base; 16962306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(obj->dev); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci vc4_bo_set_label(obj, -1); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (bo->validated_shader) { 17662306a36Sopenharmony_ci kfree(bo->validated_shader->uniform_addr_offsets); 17762306a36Sopenharmony_ci kfree(bo->validated_shader->texture_samples); 17862306a36Sopenharmony_ci kfree(bo->validated_shader); 17962306a36Sopenharmony_ci bo->validated_shader = NULL; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mutex_destroy(&bo->madv_lock); 18362306a36Sopenharmony_ci drm_gem_dma_free(&bo->base); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void vc4_bo_remove_from_cache(struct vc4_bo *bo) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 19162306a36Sopenharmony_ci list_del(&bo->unref_head); 19262306a36Sopenharmony_ci list_del(&bo->size_head); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev, 19662306a36Sopenharmony_ci size_t size) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 19962306a36Sopenharmony_ci uint32_t page_index = bo_page_index(size); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (vc4->bo_cache.size_list_size <= page_index) { 20262306a36Sopenharmony_ci uint32_t new_size = max(vc4->bo_cache.size_list_size * 2, 20362306a36Sopenharmony_ci page_index + 1); 20462306a36Sopenharmony_ci struct list_head *new_list; 20562306a36Sopenharmony_ci uint32_t i; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci new_list = kmalloc_array(new_size, sizeof(struct list_head), 20862306a36Sopenharmony_ci GFP_KERNEL); 20962306a36Sopenharmony_ci if (!new_list) 21062306a36Sopenharmony_ci return NULL; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Rebase the old cached BO lists to their new list 21362306a36Sopenharmony_ci * head locations. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci for (i = 0; i < vc4->bo_cache.size_list_size; i++) { 21662306a36Sopenharmony_ci struct list_head *old_list = 21762306a36Sopenharmony_ci &vc4->bo_cache.size_list[i]; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (list_empty(old_list)) 22062306a36Sopenharmony_ci INIT_LIST_HEAD(&new_list[i]); 22162306a36Sopenharmony_ci else 22262306a36Sopenharmony_ci list_replace(old_list, &new_list[i]); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci /* And initialize the brand new BO list heads. */ 22562306a36Sopenharmony_ci for (i = vc4->bo_cache.size_list_size; i < new_size; i++) 22662306a36Sopenharmony_ci INIT_LIST_HEAD(&new_list[i]); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci kfree(vc4->bo_cache.size_list); 22962306a36Sopenharmony_ci vc4->bo_cache.size_list = new_list; 23062306a36Sopenharmony_ci vc4->bo_cache.size_list_size = new_size; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return &vc4->bo_cache.size_list[page_index]; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void vc4_bo_cache_purge(struct drm_device *dev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci mutex_lock(&vc4->bo_lock); 24162306a36Sopenharmony_ci while (!list_empty(&vc4->bo_cache.time_list)) { 24262306a36Sopenharmony_ci struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, 24362306a36Sopenharmony_ci struct vc4_bo, unref_head); 24462306a36Sopenharmony_ci vc4_bo_remove_from_cache(bo); 24562306a36Sopenharmony_ci vc4_bo_destroy(bo); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_civoid vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 25562306a36Sopenharmony_ci return; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 25862306a36Sopenharmony_ci list_add_tail(&bo->size_head, &vc4->purgeable.list); 25962306a36Sopenharmony_ci vc4->purgeable.num++; 26062306a36Sopenharmony_ci vc4->purgeable.size += bo->base.base.size; 26162306a36Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void vc4_bo_remove_from_purgeable_pool_locked(struct vc4_bo *bo) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 26962306a36Sopenharmony_ci return; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* list_del_init() is used here because the caller might release 27262306a36Sopenharmony_ci * the purgeable lock in order to acquire the madv one and update the 27362306a36Sopenharmony_ci * madv status. 27462306a36Sopenharmony_ci * During this short period of time a user might decide to mark 27562306a36Sopenharmony_ci * the BO as unpurgeable, and if bo->madv is set to 27662306a36Sopenharmony_ci * VC4_MADV_DONTNEED it will try to remove the BO from the 27762306a36Sopenharmony_ci * purgeable list which will fail if the ->next/prev fields 27862306a36Sopenharmony_ci * are set to LIST_POISON1/LIST_POISON2 (which is what 27962306a36Sopenharmony_ci * list_del() does). 28062306a36Sopenharmony_ci * Re-initializing the list element guarantees that list_del() 28162306a36Sopenharmony_ci * will work correctly even if it's a NOP. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci list_del_init(&bo->size_head); 28462306a36Sopenharmony_ci vc4->purgeable.num--; 28562306a36Sopenharmony_ci vc4->purgeable.size -= bo->base.base.size; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_civoid vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 29362306a36Sopenharmony_ci vc4_bo_remove_from_purgeable_pool_locked(bo); 29462306a36Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void vc4_bo_purge(struct drm_gem_object *obj) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 30062306a36Sopenharmony_ci struct drm_device *dev = obj->dev; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci WARN_ON(!mutex_is_locked(&bo->madv_lock)); 30362306a36Sopenharmony_ci WARN_ON(bo->madv != VC4_MADV_DONTNEED); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci dma_free_wc(dev->dev, obj->size, bo->base.vaddr, bo->base.dma_addr); 30862306a36Sopenharmony_ci bo->base.vaddr = NULL; 30962306a36Sopenharmony_ci bo->madv = __VC4_MADV_PURGED; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void vc4_bo_userspace_cache_purge(struct drm_device *dev) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 31762306a36Sopenharmony_ci while (!list_empty(&vc4->purgeable.list)) { 31862306a36Sopenharmony_ci struct vc4_bo *bo = list_first_entry(&vc4->purgeable.list, 31962306a36Sopenharmony_ci struct vc4_bo, size_head); 32062306a36Sopenharmony_ci struct drm_gem_object *obj = &bo->base.base; 32162306a36Sopenharmony_ci size_t purged_size = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci vc4_bo_remove_from_purgeable_pool_locked(bo); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Release the purgeable lock while we're purging the BO so 32662306a36Sopenharmony_ci * that other people can continue inserting things in the 32762306a36Sopenharmony_ci * purgeable pool without having to wait for all BOs to be 32862306a36Sopenharmony_ci * purged. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 33162306a36Sopenharmony_ci mutex_lock(&bo->madv_lock); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Since we released the purgeable pool lock before acquiring 33462306a36Sopenharmony_ci * the BO madv one, the user may have marked the BO as WILLNEED 33562306a36Sopenharmony_ci * and re-used it in the meantime. 33662306a36Sopenharmony_ci * Before purging the BO we need to make sure 33762306a36Sopenharmony_ci * - it is still marked as DONTNEED 33862306a36Sopenharmony_ci * - it has not been re-inserted in the purgeable list 33962306a36Sopenharmony_ci * - it is not used by HW blocks 34062306a36Sopenharmony_ci * If one of these conditions is not met, just skip the entry. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci if (bo->madv == VC4_MADV_DONTNEED && 34362306a36Sopenharmony_ci list_empty(&bo->size_head) && 34462306a36Sopenharmony_ci !refcount_read(&bo->usecnt)) { 34562306a36Sopenharmony_ci purged_size = bo->base.base.size; 34662306a36Sopenharmony_ci vc4_bo_purge(obj); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci mutex_unlock(&bo->madv_lock); 34962306a36Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (purged_size) { 35262306a36Sopenharmony_ci vc4->purgeable.purged_size += purged_size; 35362306a36Sopenharmony_ci vc4->purgeable.purged_num++; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, 36062306a36Sopenharmony_ci uint32_t size, 36162306a36Sopenharmony_ci enum vc4_kernel_bo_type type) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 36462306a36Sopenharmony_ci uint32_t page_index = bo_page_index(size); 36562306a36Sopenharmony_ci struct vc4_bo *bo = NULL; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci mutex_lock(&vc4->bo_lock); 36862306a36Sopenharmony_ci if (page_index >= vc4->bo_cache.size_list_size) 36962306a36Sopenharmony_ci goto out; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (list_empty(&vc4->bo_cache.size_list[page_index])) 37262306a36Sopenharmony_ci goto out; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci bo = list_first_entry(&vc4->bo_cache.size_list[page_index], 37562306a36Sopenharmony_ci struct vc4_bo, size_head); 37662306a36Sopenharmony_ci vc4_bo_remove_from_cache(bo); 37762306a36Sopenharmony_ci kref_init(&bo->base.base.refcount); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ciout: 38062306a36Sopenharmony_ci if (bo) 38162306a36Sopenharmony_ci vc4_bo_set_label(&bo->base.base, type); 38262306a36Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 38362306a36Sopenharmony_ci return bo; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * vc4_create_object - Implementation of driver->gem_create_object. 38862306a36Sopenharmony_ci * @dev: DRM device 38962306a36Sopenharmony_ci * @size: Size in bytes of the memory the object will reference 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * This lets the DMA helpers allocate object structs for us, and keep 39262306a36Sopenharmony_ci * our BO stats correct. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistruct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 39762306a36Sopenharmony_ci struct vc4_bo *bo; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 40062306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci bo = kzalloc(sizeof(*bo), GFP_KERNEL); 40362306a36Sopenharmony_ci if (!bo) 40462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 40762306a36Sopenharmony_ci refcount_set(&bo->usecnt, 0); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci mutex_init(&bo->madv_lock); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci mutex_lock(&vc4->bo_lock); 41262306a36Sopenharmony_ci bo->label = VC4_BO_TYPE_KERNEL; 41362306a36Sopenharmony_ci vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++; 41462306a36Sopenharmony_ci vc4->bo_labels[VC4_BO_TYPE_KERNEL].size_allocated += size; 41562306a36Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci bo->base.base.funcs = &vc4_gem_object_funcs; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return &bo->base.base; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistruct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, 42362306a36Sopenharmony_ci bool allow_unzeroed, enum vc4_kernel_bo_type type) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci size_t size = roundup(unaligned_size, PAGE_SIZE); 42662306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 42762306a36Sopenharmony_ci struct drm_gem_dma_object *dma_obj; 42862306a36Sopenharmony_ci struct vc4_bo *bo; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 43162306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (size == 0) 43462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* First, try to get a vc4_bo from the kernel BO cache. */ 43762306a36Sopenharmony_ci bo = vc4_bo_get_from_cache(dev, size, type); 43862306a36Sopenharmony_ci if (bo) { 43962306a36Sopenharmony_ci if (!allow_unzeroed) 44062306a36Sopenharmony_ci memset(bo->base.vaddr, 0, bo->base.base.size); 44162306a36Sopenharmony_ci return bo; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci dma_obj = drm_gem_dma_create(dev, size); 44562306a36Sopenharmony_ci if (IS_ERR(dma_obj)) { 44662306a36Sopenharmony_ci /* 44762306a36Sopenharmony_ci * If we've run out of DMA memory, kill the cache of 44862306a36Sopenharmony_ci * DMA allocations we've got laying around and try again. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci vc4_bo_cache_purge(dev); 45162306a36Sopenharmony_ci dma_obj = drm_gem_dma_create(dev, size); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (IS_ERR(dma_obj)) { 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * Still not enough DMA memory, purge the userspace BO 45762306a36Sopenharmony_ci * cache and retry. 45862306a36Sopenharmony_ci * This is sub-optimal since we purge the whole userspace 45962306a36Sopenharmony_ci * BO cache which forces user that want to re-use the BO to 46062306a36Sopenharmony_ci * restore its initial content. 46162306a36Sopenharmony_ci * Ideally, we should purge entries one by one and retry 46262306a36Sopenharmony_ci * after each to see if DMA allocation succeeds. Or even 46362306a36Sopenharmony_ci * better, try to find an entry with at least the same 46462306a36Sopenharmony_ci * size. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci vc4_bo_userspace_cache_purge(dev); 46762306a36Sopenharmony_ci dma_obj = drm_gem_dma_create(dev, size); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (IS_ERR(dma_obj)) { 47162306a36Sopenharmony_ci struct drm_printer p = drm_info_printer(vc4->base.dev); 47262306a36Sopenharmony_ci DRM_ERROR("Failed to allocate from GEM DMA helper:\n"); 47362306a36Sopenharmony_ci vc4_bo_stats_print(&p, vc4); 47462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci bo = to_vc4_bo(&dma_obj->base); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* By default, BOs do not support the MADV ioctl. This will be enabled 47962306a36Sopenharmony_ci * only on BOs that are exposed to userspace (V3D, V3D_SHADER and DUMB 48062306a36Sopenharmony_ci * BOs). 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci bo->madv = __VC4_MADV_NOTSUPP; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci mutex_lock(&vc4->bo_lock); 48562306a36Sopenharmony_ci vc4_bo_set_label(&dma_obj->base, type); 48662306a36Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return bo; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ciint vc4_bo_dumb_create(struct drm_file *file_priv, 49262306a36Sopenharmony_ci struct drm_device *dev, 49362306a36Sopenharmony_ci struct drm_mode_create_dumb *args) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 49662306a36Sopenharmony_ci struct vc4_bo *bo = NULL; 49762306a36Sopenharmony_ci int ret; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 50062306a36Sopenharmony_ci return -ENODEV; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ret = vc4_dumb_fixup_args(args); 50362306a36Sopenharmony_ci if (ret) 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci bo = vc4_bo_create(dev, args->size, false, VC4_BO_TYPE_DUMB); 50762306a36Sopenharmony_ci if (IS_ERR(bo)) 50862306a36Sopenharmony_ci return PTR_ERR(bo); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); 51362306a36Sopenharmony_ci drm_gem_object_put(&bo->base.base); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return ret; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic void vc4_bo_cache_free_old(struct drm_device *dev) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 52162306a36Sopenharmony_ci unsigned long expire_time = jiffies - msecs_to_jiffies(1000); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci while (!list_empty(&vc4->bo_cache.time_list)) { 52662306a36Sopenharmony_ci struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, 52762306a36Sopenharmony_ci struct vc4_bo, unref_head); 52862306a36Sopenharmony_ci if (time_before(expire_time, bo->free_time)) { 52962306a36Sopenharmony_ci mod_timer(&vc4->bo_cache.time_timer, 53062306a36Sopenharmony_ci round_jiffies_up(jiffies + 53162306a36Sopenharmony_ci msecs_to_jiffies(1000))); 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci vc4_bo_remove_from_cache(bo); 53662306a36Sopenharmony_ci vc4_bo_destroy(bo); 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* Called on the last userspace/kernel unreference of the BO. Returns 54162306a36Sopenharmony_ci * it to the BO cache if possible, otherwise frees it. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_cistatic void vc4_free_object(struct drm_gem_object *gem_bo) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct drm_device *dev = gem_bo->dev; 54662306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 54762306a36Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(gem_bo); 54862306a36Sopenharmony_ci struct list_head *cache_list; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Remove the BO from the purgeable list. */ 55162306a36Sopenharmony_ci mutex_lock(&bo->madv_lock); 55262306a36Sopenharmony_ci if (bo->madv == VC4_MADV_DONTNEED && !refcount_read(&bo->usecnt)) 55362306a36Sopenharmony_ci vc4_bo_remove_from_purgeable_pool(bo); 55462306a36Sopenharmony_ci mutex_unlock(&bo->madv_lock); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci mutex_lock(&vc4->bo_lock); 55762306a36Sopenharmony_ci /* If the object references someone else's memory, we can't cache it. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci if (gem_bo->import_attach) { 56062306a36Sopenharmony_ci vc4_bo_destroy(bo); 56162306a36Sopenharmony_ci goto out; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Don't cache if it was publicly named. */ 56562306a36Sopenharmony_ci if (gem_bo->name) { 56662306a36Sopenharmony_ci vc4_bo_destroy(bo); 56762306a36Sopenharmony_ci goto out; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* If this object was partially constructed but DMA allocation 57162306a36Sopenharmony_ci * had failed, just free it. Can also happen when the BO has been 57262306a36Sopenharmony_ci * purged. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci if (!bo->base.vaddr) { 57562306a36Sopenharmony_ci vc4_bo_destroy(bo); 57662306a36Sopenharmony_ci goto out; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); 58062306a36Sopenharmony_ci if (!cache_list) { 58162306a36Sopenharmony_ci vc4_bo_destroy(bo); 58262306a36Sopenharmony_ci goto out; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (bo->validated_shader) { 58662306a36Sopenharmony_ci kfree(bo->validated_shader->uniform_addr_offsets); 58762306a36Sopenharmony_ci kfree(bo->validated_shader->texture_samples); 58862306a36Sopenharmony_ci kfree(bo->validated_shader); 58962306a36Sopenharmony_ci bo->validated_shader = NULL; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Reset madv and usecnt before adding the BO to the cache. */ 59362306a36Sopenharmony_ci bo->madv = __VC4_MADV_NOTSUPP; 59462306a36Sopenharmony_ci refcount_set(&bo->usecnt, 0); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci bo->t_format = false; 59762306a36Sopenharmony_ci bo->free_time = jiffies; 59862306a36Sopenharmony_ci list_add(&bo->size_head, cache_list); 59962306a36Sopenharmony_ci list_add(&bo->unref_head, &vc4->bo_cache.time_list); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci vc4_bo_set_label(&bo->base.base, VC4_BO_TYPE_KERNEL_CACHE); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci vc4_bo_cache_free_old(dev); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ciout: 60662306a36Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void vc4_bo_cache_time_work(struct work_struct *work) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct vc4_dev *vc4 = 61262306a36Sopenharmony_ci container_of(work, struct vc4_dev, bo_cache.time_work); 61362306a36Sopenharmony_ci struct drm_device *dev = &vc4->base; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci mutex_lock(&vc4->bo_lock); 61662306a36Sopenharmony_ci vc4_bo_cache_free_old(dev); 61762306a36Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ciint vc4_bo_inc_usecnt(struct vc4_bo *bo) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 62362306a36Sopenharmony_ci int ret; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 62662306a36Sopenharmony_ci return -ENODEV; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Fast path: if the BO is already retained by someone, no need to 62962306a36Sopenharmony_ci * check the madv status. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci if (refcount_inc_not_zero(&bo->usecnt)) 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci mutex_lock(&bo->madv_lock); 63562306a36Sopenharmony_ci switch (bo->madv) { 63662306a36Sopenharmony_ci case VC4_MADV_WILLNEED: 63762306a36Sopenharmony_ci if (!refcount_inc_not_zero(&bo->usecnt)) 63862306a36Sopenharmony_ci refcount_set(&bo->usecnt, 1); 63962306a36Sopenharmony_ci ret = 0; 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case VC4_MADV_DONTNEED: 64262306a36Sopenharmony_ci /* We shouldn't use a BO marked as purgeable if at least 64362306a36Sopenharmony_ci * someone else retained its content by incrementing usecnt. 64462306a36Sopenharmony_ci * Luckily the BO hasn't been purged yet, but something wrong 64562306a36Sopenharmony_ci * is happening here. Just throw an error instead of 64662306a36Sopenharmony_ci * authorizing this use case. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci case __VC4_MADV_PURGED: 64962306a36Sopenharmony_ci /* We can't use a purged BO. */ 65062306a36Sopenharmony_ci default: 65162306a36Sopenharmony_ci /* Invalid madv value. */ 65262306a36Sopenharmony_ci ret = -EINVAL; 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci mutex_unlock(&bo->madv_lock); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_civoid vc4_bo_dec_usecnt(struct vc4_bo *bo) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 66562306a36Sopenharmony_ci return; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* Fast path: if the BO is still retained by someone, no need to test 66862306a36Sopenharmony_ci * the madv value. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci if (refcount_dec_not_one(&bo->usecnt)) 67162306a36Sopenharmony_ci return; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci mutex_lock(&bo->madv_lock); 67462306a36Sopenharmony_ci if (refcount_dec_and_test(&bo->usecnt) && 67562306a36Sopenharmony_ci bo->madv == VC4_MADV_DONTNEED) 67662306a36Sopenharmony_ci vc4_bo_add_to_purgeable_pool(bo); 67762306a36Sopenharmony_ci mutex_unlock(&bo->madv_lock); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic void vc4_bo_cache_time_timer(struct timer_list *t) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct vc4_dev *vc4 = from_timer(vc4, t, bo_cache.time_timer); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci schedule_work(&vc4->bo_cache.time_work); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic struct dma_buf *vc4_prime_export(struct drm_gem_object *obj, int flags) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 69062306a36Sopenharmony_ci struct dma_buf *dmabuf; 69162306a36Sopenharmony_ci int ret; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (bo->validated_shader) { 69462306a36Sopenharmony_ci DRM_DEBUG("Attempting to export shader BO\n"); 69562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* Note: as soon as the BO is exported it becomes unpurgeable, because 69962306a36Sopenharmony_ci * noone ever decrements the usecnt even if the reference held by the 70062306a36Sopenharmony_ci * exported BO is released. This shouldn't be a problem since we don't 70162306a36Sopenharmony_ci * expect exported BOs to be marked as purgeable. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci ret = vc4_bo_inc_usecnt(bo); 70462306a36Sopenharmony_ci if (ret) { 70562306a36Sopenharmony_ci DRM_ERROR("Failed to increment BO usecnt\n"); 70662306a36Sopenharmony_ci return ERR_PTR(ret); 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci dmabuf = drm_gem_prime_export(obj, flags); 71062306a36Sopenharmony_ci if (IS_ERR(dmabuf)) 71162306a36Sopenharmony_ci vc4_bo_dec_usecnt(bo); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return dmabuf; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic vm_fault_t vc4_fault(struct vm_fault *vmf) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 71962306a36Sopenharmony_ci struct drm_gem_object *obj = vma->vm_private_data; 72062306a36Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* The only reason we would end up here is when user-space accesses 72362306a36Sopenharmony_ci * BO's memory after it's been purged. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci mutex_lock(&bo->madv_lock); 72662306a36Sopenharmony_ci WARN_ON(bo->madv != __VC4_MADV_PURGED); 72762306a36Sopenharmony_ci mutex_unlock(&bo->madv_lock); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { 73762306a36Sopenharmony_ci DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); 73862306a36Sopenharmony_ci return -EINVAL; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (bo->madv != VC4_MADV_WILLNEED) { 74262306a36Sopenharmony_ci DRM_DEBUG("mmapping of %s BO not allowed\n", 74362306a36Sopenharmony_ci bo->madv == VC4_MADV_DONTNEED ? 74462306a36Sopenharmony_ci "purgeable" : "purged"); 74562306a36Sopenharmony_ci return -EINVAL; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return drm_gem_dma_mmap(&bo->base, vma); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic const struct vm_operations_struct vc4_vm_ops = { 75262306a36Sopenharmony_ci .fault = vc4_fault, 75362306a36Sopenharmony_ci .open = drm_gem_vm_open, 75462306a36Sopenharmony_ci .close = drm_gem_vm_close, 75562306a36Sopenharmony_ci}; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic const struct drm_gem_object_funcs vc4_gem_object_funcs = { 75862306a36Sopenharmony_ci .free = vc4_free_object, 75962306a36Sopenharmony_ci .export = vc4_prime_export, 76062306a36Sopenharmony_ci .get_sg_table = drm_gem_dma_object_get_sg_table, 76162306a36Sopenharmony_ci .vmap = drm_gem_dma_object_vmap, 76262306a36Sopenharmony_ci .mmap = vc4_gem_object_mmap, 76362306a36Sopenharmony_ci .vm_ops = &vc4_vm_ops, 76462306a36Sopenharmony_ci}; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic int vc4_grab_bin_bo(struct vc4_dev *vc4, struct vc4_file *vc4file) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci if (!vc4->v3d) 76962306a36Sopenharmony_ci return -ENODEV; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (vc4file->bin_bo_used) 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return vc4_v3d_bin_bo_get(vc4, &vc4file->bin_bo_used); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ciint vc4_create_bo_ioctl(struct drm_device *dev, void *data, 77862306a36Sopenharmony_ci struct drm_file *file_priv) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct drm_vc4_create_bo *args = data; 78162306a36Sopenharmony_ci struct vc4_file *vc4file = file_priv->driver_priv; 78262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 78362306a36Sopenharmony_ci struct vc4_bo *bo = NULL; 78462306a36Sopenharmony_ci int ret; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 78762306a36Sopenharmony_ci return -ENODEV; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = vc4_grab_bin_bo(vc4, vc4file); 79062306a36Sopenharmony_ci if (ret) 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* 79462306a36Sopenharmony_ci * We can't allocate from the BO cache, because the BOs don't 79562306a36Sopenharmony_ci * get zeroed, and that might leak data between users. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci bo = vc4_bo_create(dev, args->size, false, VC4_BO_TYPE_V3D); 79862306a36Sopenharmony_ci if (IS_ERR(bo)) 79962306a36Sopenharmony_ci return PTR_ERR(bo); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); 80462306a36Sopenharmony_ci drm_gem_object_put(&bo->base.base); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci return ret; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ciint vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, 81062306a36Sopenharmony_ci struct drm_file *file_priv) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 81362306a36Sopenharmony_ci struct drm_vc4_mmap_bo *args = data; 81462306a36Sopenharmony_ci struct drm_gem_object *gem_obj; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 81762306a36Sopenharmony_ci return -ENODEV; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 82062306a36Sopenharmony_ci if (!gem_obj) { 82162306a36Sopenharmony_ci DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); 82262306a36Sopenharmony_ci return -EINVAL; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* The mmap offset was set up at BO allocation time. */ 82662306a36Sopenharmony_ci args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci drm_gem_object_put(gem_obj); 82962306a36Sopenharmony_ci return 0; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciint 83362306a36Sopenharmony_civc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, 83462306a36Sopenharmony_ci struct drm_file *file_priv) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct drm_vc4_create_shader_bo *args = data; 83762306a36Sopenharmony_ci struct vc4_file *vc4file = file_priv->driver_priv; 83862306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 83962306a36Sopenharmony_ci struct vc4_bo *bo = NULL; 84062306a36Sopenharmony_ci int ret; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 84362306a36Sopenharmony_ci return -ENODEV; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (args->size == 0) 84662306a36Sopenharmony_ci return -EINVAL; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (args->size % sizeof(u64) != 0) 84962306a36Sopenharmony_ci return -EINVAL; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (args->flags != 0) { 85262306a36Sopenharmony_ci DRM_INFO("Unknown flags set: 0x%08x\n", args->flags); 85362306a36Sopenharmony_ci return -EINVAL; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (args->pad != 0) { 85762306a36Sopenharmony_ci DRM_INFO("Pad set: 0x%08x\n", args->pad); 85862306a36Sopenharmony_ci return -EINVAL; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci ret = vc4_grab_bin_bo(vc4, vc4file); 86262306a36Sopenharmony_ci if (ret) 86362306a36Sopenharmony_ci return ret; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci bo = vc4_bo_create(dev, args->size, true, VC4_BO_TYPE_V3D_SHADER); 86662306a36Sopenharmony_ci if (IS_ERR(bo)) 86762306a36Sopenharmony_ci return PTR_ERR(bo); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (copy_from_user(bo->base.vaddr, 87262306a36Sopenharmony_ci (void __user *)(uintptr_t)args->data, 87362306a36Sopenharmony_ci args->size)) { 87462306a36Sopenharmony_ci ret = -EFAULT; 87562306a36Sopenharmony_ci goto fail; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci /* Clear the rest of the memory from allocating from the BO 87862306a36Sopenharmony_ci * cache. 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_ci memset(bo->base.vaddr + args->size, 0, 88162306a36Sopenharmony_ci bo->base.base.size - args->size); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci bo->validated_shader = vc4_validate_shader(&bo->base); 88462306a36Sopenharmony_ci if (!bo->validated_shader) { 88562306a36Sopenharmony_ci ret = -EINVAL; 88662306a36Sopenharmony_ci goto fail; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* We have to create the handle after validation, to avoid 89062306a36Sopenharmony_ci * races for users to do doing things like mmap the shader BO. 89162306a36Sopenharmony_ci */ 89262306a36Sopenharmony_ci ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cifail: 89562306a36Sopenharmony_ci drm_gem_object_put(&bo->base.base); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci return ret; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci/** 90162306a36Sopenharmony_ci * vc4_set_tiling_ioctl() - Sets the tiling modifier for a BO. 90262306a36Sopenharmony_ci * @dev: DRM device 90362306a36Sopenharmony_ci * @data: ioctl argument 90462306a36Sopenharmony_ci * @file_priv: DRM file for this fd 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * The tiling state of the BO decides the default modifier of an fb if 90762306a36Sopenharmony_ci * no specific modifier was set by userspace, and the return value of 90862306a36Sopenharmony_ci * vc4_get_tiling_ioctl() (so that userspace can treat a BO it 90962306a36Sopenharmony_ci * received from dmabuf as the same tiling format as the producer 91062306a36Sopenharmony_ci * used). 91162306a36Sopenharmony_ci */ 91262306a36Sopenharmony_ciint vc4_set_tiling_ioctl(struct drm_device *dev, void *data, 91362306a36Sopenharmony_ci struct drm_file *file_priv) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 91662306a36Sopenharmony_ci struct drm_vc4_set_tiling *args = data; 91762306a36Sopenharmony_ci struct drm_gem_object *gem_obj; 91862306a36Sopenharmony_ci struct vc4_bo *bo; 91962306a36Sopenharmony_ci bool t_format; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 92262306a36Sopenharmony_ci return -ENODEV; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (args->flags != 0) 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci switch (args->modifier) { 92862306a36Sopenharmony_ci case DRM_FORMAT_MOD_NONE: 92962306a36Sopenharmony_ci t_format = false; 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: 93262306a36Sopenharmony_ci t_format = true; 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci default: 93562306a36Sopenharmony_ci return -EINVAL; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 93962306a36Sopenharmony_ci if (!gem_obj) { 94062306a36Sopenharmony_ci DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); 94162306a36Sopenharmony_ci return -ENOENT; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci bo = to_vc4_bo(gem_obj); 94462306a36Sopenharmony_ci bo->t_format = t_format; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci drm_gem_object_put(gem_obj); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci/** 95262306a36Sopenharmony_ci * vc4_get_tiling_ioctl() - Gets the tiling modifier for a BO. 95362306a36Sopenharmony_ci * @dev: DRM device 95462306a36Sopenharmony_ci * @data: ioctl argument 95562306a36Sopenharmony_ci * @file_priv: DRM file for this fd 95662306a36Sopenharmony_ci * 95762306a36Sopenharmony_ci * Returns the tiling modifier for a BO as set by vc4_set_tiling_ioctl(). 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ciint vc4_get_tiling_ioctl(struct drm_device *dev, void *data, 96062306a36Sopenharmony_ci struct drm_file *file_priv) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 96362306a36Sopenharmony_ci struct drm_vc4_get_tiling *args = data; 96462306a36Sopenharmony_ci struct drm_gem_object *gem_obj; 96562306a36Sopenharmony_ci struct vc4_bo *bo; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 96862306a36Sopenharmony_ci return -ENODEV; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (args->flags != 0 || args->modifier != 0) 97162306a36Sopenharmony_ci return -EINVAL; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 97462306a36Sopenharmony_ci if (!gem_obj) { 97562306a36Sopenharmony_ci DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); 97662306a36Sopenharmony_ci return -ENOENT; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci bo = to_vc4_bo(gem_obj); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (bo->t_format) 98162306a36Sopenharmony_ci args->modifier = DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 98262306a36Sopenharmony_ci else 98362306a36Sopenharmony_ci args->modifier = DRM_FORMAT_MOD_NONE; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci drm_gem_object_put(gem_obj); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ciint vc4_bo_debugfs_init(struct drm_minor *minor) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct drm_device *drm = minor->dev; 99362306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(drm); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (!vc4->v3d) 99662306a36Sopenharmony_ci return -ENODEV; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci drm_debugfs_add_file(drm, "bo_stats", vc4_bo_stats_debugfs, NULL); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic void vc4_bo_cache_destroy(struct drm_device *dev, void *unused); 100462306a36Sopenharmony_ciint vc4_bo_cache_init(struct drm_device *dev) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 100762306a36Sopenharmony_ci int ret; 100862306a36Sopenharmony_ci int i; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 101162306a36Sopenharmony_ci return -ENODEV; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* Create the initial set of BO labels that the kernel will 101462306a36Sopenharmony_ci * use. This lets us avoid a bunch of string reallocation in 101562306a36Sopenharmony_ci * the kernel's draw and BO allocation paths. 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_ci vc4->bo_labels = kcalloc(VC4_BO_TYPE_COUNT, sizeof(*vc4->bo_labels), 101862306a36Sopenharmony_ci GFP_KERNEL); 101962306a36Sopenharmony_ci if (!vc4->bo_labels) 102062306a36Sopenharmony_ci return -ENOMEM; 102162306a36Sopenharmony_ci vc4->num_labels = VC4_BO_TYPE_COUNT; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(bo_type_names) != VC4_BO_TYPE_COUNT); 102462306a36Sopenharmony_ci for (i = 0; i < VC4_BO_TYPE_COUNT; i++) 102562306a36Sopenharmony_ci vc4->bo_labels[i].name = bo_type_names[i]; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci ret = drmm_mutex_init(dev, &vc4->bo_lock); 102862306a36Sopenharmony_ci if (ret) { 102962306a36Sopenharmony_ci kfree(vc4->bo_labels); 103062306a36Sopenharmony_ci return ret; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci INIT_LIST_HEAD(&vc4->bo_cache.time_list); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); 103662306a36Sopenharmony_ci timer_setup(&vc4->bo_cache.time_timer, vc4_bo_cache_time_timer, 0); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return drmm_add_action_or_reset(dev, vc4_bo_cache_destroy, NULL); 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic void vc4_bo_cache_destroy(struct drm_device *dev, void *unused) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 104462306a36Sopenharmony_ci int i; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci del_timer(&vc4->bo_cache.time_timer); 104762306a36Sopenharmony_ci cancel_work_sync(&vc4->bo_cache.time_work); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci vc4_bo_cache_purge(dev); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci for (i = 0; i < vc4->num_labels; i++) { 105262306a36Sopenharmony_ci if (vc4->bo_labels[i].num_allocated) { 105362306a36Sopenharmony_ci DRM_ERROR("Destroying BO cache with %d %s " 105462306a36Sopenharmony_ci "BOs still allocated\n", 105562306a36Sopenharmony_ci vc4->bo_labels[i].num_allocated, 105662306a36Sopenharmony_ci vc4->bo_labels[i].name); 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (is_user_label(i)) 106062306a36Sopenharmony_ci kfree(vc4->bo_labels[i].name); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci kfree(vc4->bo_labels); 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ciint vc4_label_bo_ioctl(struct drm_device *dev, void *data, 106662306a36Sopenharmony_ci struct drm_file *file_priv) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 106962306a36Sopenharmony_ci struct drm_vc4_label_bo *args = data; 107062306a36Sopenharmony_ci char *name; 107162306a36Sopenharmony_ci struct drm_gem_object *gem_obj; 107262306a36Sopenharmony_ci int ret = 0, label; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 107562306a36Sopenharmony_ci return -ENODEV; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (!args->len) 107862306a36Sopenharmony_ci return -EINVAL; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci name = strndup_user(u64_to_user_ptr(args->name), args->len + 1); 108162306a36Sopenharmony_ci if (IS_ERR(name)) 108262306a36Sopenharmony_ci return PTR_ERR(name); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 108562306a36Sopenharmony_ci if (!gem_obj) { 108662306a36Sopenharmony_ci DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); 108762306a36Sopenharmony_ci kfree(name); 108862306a36Sopenharmony_ci return -ENOENT; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci mutex_lock(&vc4->bo_lock); 109262306a36Sopenharmony_ci label = vc4_get_user_label(vc4, name); 109362306a36Sopenharmony_ci if (label != -1) 109462306a36Sopenharmony_ci vc4_bo_set_label(gem_obj, label); 109562306a36Sopenharmony_ci else 109662306a36Sopenharmony_ci ret = -ENOMEM; 109762306a36Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci drm_gem_object_put(gem_obj); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return ret; 110262306a36Sopenharmony_ci} 1103