18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2015 Broadcom 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/** 78c2ecf20Sopenharmony_ci * DOC: VC4 GEM BO management support 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * The VC4 GPU architecture (both scanout and rendering) has direct 108c2ecf20Sopenharmony_ci * access to system memory with no MMU in between. To support it, we 118c2ecf20Sopenharmony_ci * use the GEM CMA helper functions to allocate contiguous ranges of 128c2ecf20Sopenharmony_ci * physical memory for our BOs. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Since the CMA allocator is very slow, we keep a cache of recently 158c2ecf20Sopenharmony_ci * freed BOs around so that the kernel's allocation of objects for 3D 168c2ecf20Sopenharmony_ci * rendering can return quickly. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/dma-buf.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "vc4_drv.h" 228c2ecf20Sopenharmony_ci#include "uapi/drm/vc4_drm.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const char * const bo_type_names[] = { 258c2ecf20Sopenharmony_ci "kernel", 268c2ecf20Sopenharmony_ci "V3D", 278c2ecf20Sopenharmony_ci "V3D shader", 288c2ecf20Sopenharmony_ci "dumb", 298c2ecf20Sopenharmony_ci "binner", 308c2ecf20Sopenharmony_ci "RCL", 318c2ecf20Sopenharmony_ci "BCL", 328c2ecf20Sopenharmony_ci "kernel BO cache", 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic bool is_user_label(int label) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return label >= VC4_BO_TYPE_COUNT; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void vc4_bo_stats_print(struct drm_printer *p, struct vc4_dev *vc4) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int i; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci for (i = 0; i < vc4->num_labels; i++) { 458c2ecf20Sopenharmony_ci if (!vc4->bo_labels[i].num_allocated) 468c2ecf20Sopenharmony_ci continue; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci drm_printf(p, "%30s: %6dkb BOs (%d)\n", 498c2ecf20Sopenharmony_ci vc4->bo_labels[i].name, 508c2ecf20Sopenharmony_ci vc4->bo_labels[i].size_allocated / 1024, 518c2ecf20Sopenharmony_ci vc4->bo_labels[i].num_allocated); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 558c2ecf20Sopenharmony_ci if (vc4->purgeable.num) 568c2ecf20Sopenharmony_ci drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "userspace BO cache", 578c2ecf20Sopenharmony_ci vc4->purgeable.size / 1024, vc4->purgeable.num); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (vc4->purgeable.purged_num) 608c2ecf20Sopenharmony_ci drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "total purged BO", 618c2ecf20Sopenharmony_ci vc4->purgeable.purged_size / 1024, 628c2ecf20Sopenharmony_ci vc4->purgeable.purged_num); 638c2ecf20Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *)m->private; 698c2ecf20Sopenharmony_ci struct drm_device *dev = node->minor->dev; 708c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 718c2ecf20Sopenharmony_ci struct drm_printer p = drm_seq_file_printer(m); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci vc4_bo_stats_print(&p, vc4); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Takes ownership of *name and returns the appropriate slot for it in 798c2ecf20Sopenharmony_ci * the bo_labels[] array, extending it as necessary. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * This is inefficient and could use a hash table instead of walking 828c2ecf20Sopenharmony_ci * an array and strcmp()ing. However, the assumption is that user 838c2ecf20Sopenharmony_ci * labeling will be infrequent (scanout buffers and other long-lived 848c2ecf20Sopenharmony_ci * objects, or debug driver builds), so we can live with it for now. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic int vc4_get_user_label(struct vc4_dev *vc4, const char *name) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int i; 898c2ecf20Sopenharmony_ci int free_slot = -1; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0; i < vc4->num_labels; i++) { 928c2ecf20Sopenharmony_ci if (!vc4->bo_labels[i].name) { 938c2ecf20Sopenharmony_ci free_slot = i; 948c2ecf20Sopenharmony_ci } else if (strcmp(vc4->bo_labels[i].name, name) == 0) { 958c2ecf20Sopenharmony_ci kfree(name); 968c2ecf20Sopenharmony_ci return i; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (free_slot != -1) { 1018c2ecf20Sopenharmony_ci WARN_ON(vc4->bo_labels[free_slot].num_allocated != 0); 1028c2ecf20Sopenharmony_ci vc4->bo_labels[free_slot].name = name; 1038c2ecf20Sopenharmony_ci return free_slot; 1048c2ecf20Sopenharmony_ci } else { 1058c2ecf20Sopenharmony_ci u32 new_label_count = vc4->num_labels + 1; 1068c2ecf20Sopenharmony_ci struct vc4_label *new_labels = 1078c2ecf20Sopenharmony_ci krealloc(vc4->bo_labels, 1088c2ecf20Sopenharmony_ci new_label_count * sizeof(*new_labels), 1098c2ecf20Sopenharmony_ci GFP_KERNEL); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!new_labels) { 1128c2ecf20Sopenharmony_ci kfree(name); 1138c2ecf20Sopenharmony_ci return -1; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci free_slot = vc4->num_labels; 1178c2ecf20Sopenharmony_ci vc4->bo_labels = new_labels; 1188c2ecf20Sopenharmony_ci vc4->num_labels = new_label_count; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci vc4->bo_labels[free_slot].name = name; 1218c2ecf20Sopenharmony_ci vc4->bo_labels[free_slot].num_allocated = 0; 1228c2ecf20Sopenharmony_ci vc4->bo_labels[free_slot].size_allocated = 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return free_slot; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void vc4_bo_set_label(struct drm_gem_object *gem_obj, int label) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(gem_obj); 1318c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(gem_obj->dev); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (label != -1) { 1368c2ecf20Sopenharmony_ci vc4->bo_labels[label].num_allocated++; 1378c2ecf20Sopenharmony_ci vc4->bo_labels[label].size_allocated += gem_obj->size; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci vc4->bo_labels[bo->label].num_allocated--; 1418c2ecf20Sopenharmony_ci vc4->bo_labels[bo->label].size_allocated -= gem_obj->size; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (vc4->bo_labels[bo->label].num_allocated == 0 && 1448c2ecf20Sopenharmony_ci is_user_label(bo->label)) { 1458c2ecf20Sopenharmony_ci /* Free user BO label slots on last unreference. 1468c2ecf20Sopenharmony_ci * Slots are just where we track the stats for a given 1478c2ecf20Sopenharmony_ci * name, and once a name is unused we can reuse that 1488c2ecf20Sopenharmony_ci * slot. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci kfree(vc4->bo_labels[bo->label].name); 1518c2ecf20Sopenharmony_ci vc4->bo_labels[bo->label].name = NULL; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci bo->label = label; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic uint32_t bo_page_index(size_t size) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return (size / PAGE_SIZE) - 1; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void vc4_bo_destroy(struct vc4_bo *bo) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct drm_gem_object *obj = &bo->base.base; 1658c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(obj->dev); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci vc4_bo_set_label(obj, -1); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (bo->validated_shader) { 1728c2ecf20Sopenharmony_ci kfree(bo->validated_shader->uniform_addr_offsets); 1738c2ecf20Sopenharmony_ci kfree(bo->validated_shader->texture_samples); 1748c2ecf20Sopenharmony_ci kfree(bo->validated_shader); 1758c2ecf20Sopenharmony_ci bo->validated_shader = NULL; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci drm_gem_cma_free_object(obj); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void vc4_bo_remove_from_cache(struct vc4_bo *bo) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 1868c2ecf20Sopenharmony_ci list_del(&bo->unref_head); 1878c2ecf20Sopenharmony_ci list_del(&bo->size_head); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev, 1918c2ecf20Sopenharmony_ci size_t size) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 1948c2ecf20Sopenharmony_ci uint32_t page_index = bo_page_index(size); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (vc4->bo_cache.size_list_size <= page_index) { 1978c2ecf20Sopenharmony_ci uint32_t new_size = max(vc4->bo_cache.size_list_size * 2, 1988c2ecf20Sopenharmony_ci page_index + 1); 1998c2ecf20Sopenharmony_ci struct list_head *new_list; 2008c2ecf20Sopenharmony_ci uint32_t i; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci new_list = kmalloc_array(new_size, sizeof(struct list_head), 2038c2ecf20Sopenharmony_ci GFP_KERNEL); 2048c2ecf20Sopenharmony_ci if (!new_list) 2058c2ecf20Sopenharmony_ci return NULL; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Rebase the old cached BO lists to their new list 2088c2ecf20Sopenharmony_ci * head locations. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci for (i = 0; i < vc4->bo_cache.size_list_size; i++) { 2118c2ecf20Sopenharmony_ci struct list_head *old_list = 2128c2ecf20Sopenharmony_ci &vc4->bo_cache.size_list[i]; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (list_empty(old_list)) 2158c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&new_list[i]); 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci list_replace(old_list, &new_list[i]); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci /* And initialize the brand new BO list heads. */ 2208c2ecf20Sopenharmony_ci for (i = vc4->bo_cache.size_list_size; i < new_size; i++) 2218c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&new_list[i]); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci kfree(vc4->bo_cache.size_list); 2248c2ecf20Sopenharmony_ci vc4->bo_cache.size_list = new_list; 2258c2ecf20Sopenharmony_ci vc4->bo_cache.size_list_size = new_size; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return &vc4->bo_cache.size_list[page_index]; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void vc4_bo_cache_purge(struct drm_device *dev) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci mutex_lock(&vc4->bo_lock); 2368c2ecf20Sopenharmony_ci while (!list_empty(&vc4->bo_cache.time_list)) { 2378c2ecf20Sopenharmony_ci struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, 2388c2ecf20Sopenharmony_ci struct vc4_bo, unref_head); 2398c2ecf20Sopenharmony_ci vc4_bo_remove_from_cache(bo); 2408c2ecf20Sopenharmony_ci vc4_bo_destroy(bo); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 2508c2ecf20Sopenharmony_ci list_add_tail(&bo->size_head, &vc4->purgeable.list); 2518c2ecf20Sopenharmony_ci vc4->purgeable.num++; 2528c2ecf20Sopenharmony_ci vc4->purgeable.size += bo->base.base.size; 2538c2ecf20Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void vc4_bo_remove_from_purgeable_pool_locked(struct vc4_bo *bo) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* list_del_init() is used here because the caller might release 2618c2ecf20Sopenharmony_ci * the purgeable lock in order to acquire the madv one and update the 2628c2ecf20Sopenharmony_ci * madv status. 2638c2ecf20Sopenharmony_ci * During this short period of time a user might decide to mark 2648c2ecf20Sopenharmony_ci * the BO as unpurgeable, and if bo->madv is set to 2658c2ecf20Sopenharmony_ci * VC4_MADV_DONTNEED it will try to remove the BO from the 2668c2ecf20Sopenharmony_ci * purgeable list which will fail if the ->next/prev fields 2678c2ecf20Sopenharmony_ci * are set to LIST_POISON1/LIST_POISON2 (which is what 2688c2ecf20Sopenharmony_ci * list_del() does). 2698c2ecf20Sopenharmony_ci * Re-initializing the list element guarantees that list_del() 2708c2ecf20Sopenharmony_ci * will work correctly even if it's a NOP. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci list_del_init(&bo->size_head); 2738c2ecf20Sopenharmony_ci vc4->purgeable.num--; 2748c2ecf20Sopenharmony_ci vc4->purgeable.size -= bo->base.base.size; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_civoid vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 2828c2ecf20Sopenharmony_ci vc4_bo_remove_from_purgeable_pool_locked(bo); 2838c2ecf20Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void vc4_bo_purge(struct drm_gem_object *obj) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 2898c2ecf20Sopenharmony_ci struct drm_device *dev = obj->dev; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&bo->madv_lock)); 2928c2ecf20Sopenharmony_ci WARN_ON(bo->madv != VC4_MADV_DONTNEED); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci dma_free_wc(dev->dev, obj->size, bo->base.vaddr, bo->base.paddr); 2978c2ecf20Sopenharmony_ci bo->base.vaddr = NULL; 2988c2ecf20Sopenharmony_ci bo->madv = __VC4_MADV_PURGED; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void vc4_bo_userspace_cache_purge(struct drm_device *dev) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 3068c2ecf20Sopenharmony_ci while (!list_empty(&vc4->purgeable.list)) { 3078c2ecf20Sopenharmony_ci struct vc4_bo *bo = list_first_entry(&vc4->purgeable.list, 3088c2ecf20Sopenharmony_ci struct vc4_bo, size_head); 3098c2ecf20Sopenharmony_ci struct drm_gem_object *obj = &bo->base.base; 3108c2ecf20Sopenharmony_ci size_t purged_size = 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci vc4_bo_remove_from_purgeable_pool_locked(bo); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Release the purgeable lock while we're purging the BO so 3158c2ecf20Sopenharmony_ci * that other people can continue inserting things in the 3168c2ecf20Sopenharmony_ci * purgeable pool without having to wait for all BOs to be 3178c2ecf20Sopenharmony_ci * purged. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 3208c2ecf20Sopenharmony_ci mutex_lock(&bo->madv_lock); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Since we released the purgeable pool lock before acquiring 3238c2ecf20Sopenharmony_ci * the BO madv one, the user may have marked the BO as WILLNEED 3248c2ecf20Sopenharmony_ci * and re-used it in the meantime. 3258c2ecf20Sopenharmony_ci * Before purging the BO we need to make sure 3268c2ecf20Sopenharmony_ci * - it is still marked as DONTNEED 3278c2ecf20Sopenharmony_ci * - it has not been re-inserted in the purgeable list 3288c2ecf20Sopenharmony_ci * - it is not used by HW blocks 3298c2ecf20Sopenharmony_ci * If one of these conditions is not met, just skip the entry. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci if (bo->madv == VC4_MADV_DONTNEED && 3328c2ecf20Sopenharmony_ci list_empty(&bo->size_head) && 3338c2ecf20Sopenharmony_ci !refcount_read(&bo->usecnt)) { 3348c2ecf20Sopenharmony_ci purged_size = bo->base.base.size; 3358c2ecf20Sopenharmony_ci vc4_bo_purge(obj); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci mutex_unlock(&bo->madv_lock); 3388c2ecf20Sopenharmony_ci mutex_lock(&vc4->purgeable.lock); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (purged_size) { 3418c2ecf20Sopenharmony_ci vc4->purgeable.purged_size += purged_size; 3428c2ecf20Sopenharmony_ci vc4->purgeable.purged_num++; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci mutex_unlock(&vc4->purgeable.lock); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, 3498c2ecf20Sopenharmony_ci uint32_t size, 3508c2ecf20Sopenharmony_ci enum vc4_kernel_bo_type type) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 3538c2ecf20Sopenharmony_ci uint32_t page_index = bo_page_index(size); 3548c2ecf20Sopenharmony_ci struct vc4_bo *bo = NULL; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci size = roundup(size, PAGE_SIZE); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci mutex_lock(&vc4->bo_lock); 3598c2ecf20Sopenharmony_ci if (page_index >= vc4->bo_cache.size_list_size) 3608c2ecf20Sopenharmony_ci goto out; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (list_empty(&vc4->bo_cache.size_list[page_index])) 3638c2ecf20Sopenharmony_ci goto out; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci bo = list_first_entry(&vc4->bo_cache.size_list[page_index], 3668c2ecf20Sopenharmony_ci struct vc4_bo, size_head); 3678c2ecf20Sopenharmony_ci vc4_bo_remove_from_cache(bo); 3688c2ecf20Sopenharmony_ci kref_init(&bo->base.base.refcount); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciout: 3718c2ecf20Sopenharmony_ci if (bo) 3728c2ecf20Sopenharmony_ci vc4_bo_set_label(&bo->base.base, type); 3738c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 3748c2ecf20Sopenharmony_ci return bo; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/** 3788c2ecf20Sopenharmony_ci * vc4_gem_create_object - Implementation of driver->gem_create_object. 3798c2ecf20Sopenharmony_ci * @dev: DRM device 3808c2ecf20Sopenharmony_ci * @size: Size in bytes of the memory the object will reference 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * This lets the CMA helpers allocate object structs for us, and keep 3838c2ecf20Sopenharmony_ci * our BO stats correct. 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_cistruct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 3888c2ecf20Sopenharmony_ci struct vc4_bo *bo; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci bo = kzalloc(sizeof(*bo), GFP_KERNEL); 3918c2ecf20Sopenharmony_ci if (!bo) 3928c2ecf20Sopenharmony_ci return NULL; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 3958c2ecf20Sopenharmony_ci refcount_set(&bo->usecnt, 0); 3968c2ecf20Sopenharmony_ci mutex_init(&bo->madv_lock); 3978c2ecf20Sopenharmony_ci mutex_lock(&vc4->bo_lock); 3988c2ecf20Sopenharmony_ci bo->label = VC4_BO_TYPE_KERNEL; 3998c2ecf20Sopenharmony_ci vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++; 4008c2ecf20Sopenharmony_ci vc4->bo_labels[VC4_BO_TYPE_KERNEL].size_allocated += size; 4018c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return &bo->base.base; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistruct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, 4078c2ecf20Sopenharmony_ci bool allow_unzeroed, enum vc4_kernel_bo_type type) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci size_t size = roundup(unaligned_size, PAGE_SIZE); 4108c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 4118c2ecf20Sopenharmony_ci struct drm_gem_cma_object *cma_obj; 4128c2ecf20Sopenharmony_ci struct vc4_bo *bo; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (size == 0) 4158c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* First, try to get a vc4_bo from the kernel BO cache. */ 4188c2ecf20Sopenharmony_ci bo = vc4_bo_get_from_cache(dev, size, type); 4198c2ecf20Sopenharmony_ci if (bo) { 4208c2ecf20Sopenharmony_ci if (!allow_unzeroed) 4218c2ecf20Sopenharmony_ci memset(bo->base.vaddr, 0, bo->base.base.size); 4228c2ecf20Sopenharmony_ci return bo; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci cma_obj = drm_gem_cma_create(dev, size); 4268c2ecf20Sopenharmony_ci if (IS_ERR(cma_obj)) { 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * If we've run out of CMA memory, kill the cache of 4298c2ecf20Sopenharmony_ci * CMA allocations we've got laying around and try again. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci vc4_bo_cache_purge(dev); 4328c2ecf20Sopenharmony_ci cma_obj = drm_gem_cma_create(dev, size); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (IS_ERR(cma_obj)) { 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * Still not enough CMA memory, purge the userspace BO 4388c2ecf20Sopenharmony_ci * cache and retry. 4398c2ecf20Sopenharmony_ci * This is sub-optimal since we purge the whole userspace 4408c2ecf20Sopenharmony_ci * BO cache which forces user that want to re-use the BO to 4418c2ecf20Sopenharmony_ci * restore its initial content. 4428c2ecf20Sopenharmony_ci * Ideally, we should purge entries one by one and retry 4438c2ecf20Sopenharmony_ci * after each to see if CMA allocation succeeds. Or even 4448c2ecf20Sopenharmony_ci * better, try to find an entry with at least the same 4458c2ecf20Sopenharmony_ci * size. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci vc4_bo_userspace_cache_purge(dev); 4488c2ecf20Sopenharmony_ci cma_obj = drm_gem_cma_create(dev, size); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (IS_ERR(cma_obj)) { 4528c2ecf20Sopenharmony_ci struct drm_printer p = drm_info_printer(vc4->base.dev); 4538c2ecf20Sopenharmony_ci DRM_ERROR("Failed to allocate from CMA:\n"); 4548c2ecf20Sopenharmony_ci vc4_bo_stats_print(&p, vc4); 4558c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci bo = to_vc4_bo(&cma_obj->base); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* By default, BOs do not support the MADV ioctl. This will be enabled 4608c2ecf20Sopenharmony_ci * only on BOs that are exposed to userspace (V3D, V3D_SHADER and DUMB 4618c2ecf20Sopenharmony_ci * BOs). 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci bo->madv = __VC4_MADV_NOTSUPP; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci mutex_lock(&vc4->bo_lock); 4668c2ecf20Sopenharmony_ci vc4_bo_set_label(&cma_obj->base, type); 4678c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return bo; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ciint vc4_dumb_create(struct drm_file *file_priv, 4738c2ecf20Sopenharmony_ci struct drm_device *dev, 4748c2ecf20Sopenharmony_ci struct drm_mode_create_dumb *args) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 4778c2ecf20Sopenharmony_ci struct vc4_bo *bo = NULL; 4788c2ecf20Sopenharmony_ci int ret; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (args->pitch < min_pitch) 4818c2ecf20Sopenharmony_ci args->pitch = min_pitch; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (args->size < args->pitch * args->height) 4848c2ecf20Sopenharmony_ci args->size = args->pitch * args->height; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci bo = vc4_bo_create(dev, args->size, false, VC4_BO_TYPE_DUMB); 4878c2ecf20Sopenharmony_ci if (IS_ERR(bo)) 4888c2ecf20Sopenharmony_ci return PTR_ERR(bo); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); 4938c2ecf20Sopenharmony_ci drm_gem_object_put(&bo->base.base); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return ret; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void vc4_bo_cache_free_old(struct drm_device *dev) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 5018c2ecf20Sopenharmony_ci unsigned long expire_time = jiffies - msecs_to_jiffies(1000); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci lockdep_assert_held(&vc4->bo_lock); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci while (!list_empty(&vc4->bo_cache.time_list)) { 5068c2ecf20Sopenharmony_ci struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, 5078c2ecf20Sopenharmony_ci struct vc4_bo, unref_head); 5088c2ecf20Sopenharmony_ci if (time_before(expire_time, bo->free_time)) { 5098c2ecf20Sopenharmony_ci mod_timer(&vc4->bo_cache.time_timer, 5108c2ecf20Sopenharmony_ci round_jiffies_up(jiffies + 5118c2ecf20Sopenharmony_ci msecs_to_jiffies(1000))); 5128c2ecf20Sopenharmony_ci return; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci vc4_bo_remove_from_cache(bo); 5168c2ecf20Sopenharmony_ci vc4_bo_destroy(bo); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci/* Called on the last userspace/kernel unreference of the BO. Returns 5218c2ecf20Sopenharmony_ci * it to the BO cache if possible, otherwise frees it. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_civoid vc4_free_object(struct drm_gem_object *gem_bo) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct drm_device *dev = gem_bo->dev; 5268c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 5278c2ecf20Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(gem_bo); 5288c2ecf20Sopenharmony_ci struct list_head *cache_list; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Remove the BO from the purgeable list. */ 5318c2ecf20Sopenharmony_ci mutex_lock(&bo->madv_lock); 5328c2ecf20Sopenharmony_ci if (bo->madv == VC4_MADV_DONTNEED && !refcount_read(&bo->usecnt)) 5338c2ecf20Sopenharmony_ci vc4_bo_remove_from_purgeable_pool(bo); 5348c2ecf20Sopenharmony_ci mutex_unlock(&bo->madv_lock); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci mutex_lock(&vc4->bo_lock); 5378c2ecf20Sopenharmony_ci /* If the object references someone else's memory, we can't cache it. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci if (gem_bo->import_attach) { 5408c2ecf20Sopenharmony_ci vc4_bo_destroy(bo); 5418c2ecf20Sopenharmony_ci goto out; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Don't cache if it was publicly named. */ 5458c2ecf20Sopenharmony_ci if (gem_bo->name) { 5468c2ecf20Sopenharmony_ci vc4_bo_destroy(bo); 5478c2ecf20Sopenharmony_ci goto out; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* If this object was partially constructed but CMA allocation 5518c2ecf20Sopenharmony_ci * had failed, just free it. Can also happen when the BO has been 5528c2ecf20Sopenharmony_ci * purged. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci if (!bo->base.vaddr) { 5558c2ecf20Sopenharmony_ci vc4_bo_destroy(bo); 5568c2ecf20Sopenharmony_ci goto out; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); 5608c2ecf20Sopenharmony_ci if (!cache_list) { 5618c2ecf20Sopenharmony_ci vc4_bo_destroy(bo); 5628c2ecf20Sopenharmony_ci goto out; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (bo->validated_shader) { 5668c2ecf20Sopenharmony_ci kfree(bo->validated_shader->uniform_addr_offsets); 5678c2ecf20Sopenharmony_ci kfree(bo->validated_shader->texture_samples); 5688c2ecf20Sopenharmony_ci kfree(bo->validated_shader); 5698c2ecf20Sopenharmony_ci bo->validated_shader = NULL; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Reset madv and usecnt before adding the BO to the cache. */ 5738c2ecf20Sopenharmony_ci bo->madv = __VC4_MADV_NOTSUPP; 5748c2ecf20Sopenharmony_ci refcount_set(&bo->usecnt, 0); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci bo->t_format = false; 5778c2ecf20Sopenharmony_ci bo->free_time = jiffies; 5788c2ecf20Sopenharmony_ci list_add(&bo->size_head, cache_list); 5798c2ecf20Sopenharmony_ci list_add(&bo->unref_head, &vc4->bo_cache.time_list); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci vc4_bo_set_label(&bo->base.base, VC4_BO_TYPE_KERNEL_CACHE); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci vc4_bo_cache_free_old(dev); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ciout: 5868c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic void vc4_bo_cache_time_work(struct work_struct *work) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = 5928c2ecf20Sopenharmony_ci container_of(work, struct vc4_dev, bo_cache.time_work); 5938c2ecf20Sopenharmony_ci struct drm_device *dev = &vc4->base; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci mutex_lock(&vc4->bo_lock); 5968c2ecf20Sopenharmony_ci vc4_bo_cache_free_old(dev); 5978c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ciint vc4_bo_inc_usecnt(struct vc4_bo *bo) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci int ret; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* Fast path: if the BO is already retained by someone, no need to 6058c2ecf20Sopenharmony_ci * check the madv status. 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_ci if (refcount_inc_not_zero(&bo->usecnt)) 6088c2ecf20Sopenharmony_ci return 0; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci mutex_lock(&bo->madv_lock); 6118c2ecf20Sopenharmony_ci switch (bo->madv) { 6128c2ecf20Sopenharmony_ci case VC4_MADV_WILLNEED: 6138c2ecf20Sopenharmony_ci if (!refcount_inc_not_zero(&bo->usecnt)) 6148c2ecf20Sopenharmony_ci refcount_set(&bo->usecnt, 1); 6158c2ecf20Sopenharmony_ci ret = 0; 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case VC4_MADV_DONTNEED: 6188c2ecf20Sopenharmony_ci /* We shouldn't use a BO marked as purgeable if at least 6198c2ecf20Sopenharmony_ci * someone else retained its content by incrementing usecnt. 6208c2ecf20Sopenharmony_ci * Luckily the BO hasn't been purged yet, but something wrong 6218c2ecf20Sopenharmony_ci * is happening here. Just throw an error instead of 6228c2ecf20Sopenharmony_ci * authorizing this use case. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci case __VC4_MADV_PURGED: 6258c2ecf20Sopenharmony_ci /* We can't use a purged BO. */ 6268c2ecf20Sopenharmony_ci default: 6278c2ecf20Sopenharmony_ci /* Invalid madv value. */ 6288c2ecf20Sopenharmony_ci ret = -EINVAL; 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci mutex_unlock(&bo->madv_lock); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return ret; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_civoid vc4_bo_dec_usecnt(struct vc4_bo *bo) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci /* Fast path: if the BO is still retained by someone, no need to test 6398c2ecf20Sopenharmony_ci * the madv value. 6408c2ecf20Sopenharmony_ci */ 6418c2ecf20Sopenharmony_ci if (refcount_dec_not_one(&bo->usecnt)) 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci mutex_lock(&bo->madv_lock); 6458c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&bo->usecnt) && 6468c2ecf20Sopenharmony_ci bo->madv == VC4_MADV_DONTNEED) 6478c2ecf20Sopenharmony_ci vc4_bo_add_to_purgeable_pool(bo); 6488c2ecf20Sopenharmony_ci mutex_unlock(&bo->madv_lock); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic void vc4_bo_cache_time_timer(struct timer_list *t) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = from_timer(vc4, t, bo_cache.time_timer); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci schedule_work(&vc4->bo_cache.time_work); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistruct dma_buf * vc4_prime_export(struct drm_gem_object *obj, int flags) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 6618c2ecf20Sopenharmony_ci struct dma_buf *dmabuf; 6628c2ecf20Sopenharmony_ci int ret; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (bo->validated_shader) { 6658c2ecf20Sopenharmony_ci DRM_DEBUG("Attempting to export shader BO\n"); 6668c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* Note: as soon as the BO is exported it becomes unpurgeable, because 6708c2ecf20Sopenharmony_ci * noone ever decrements the usecnt even if the reference held by the 6718c2ecf20Sopenharmony_ci * exported BO is released. This shouldn't be a problem since we don't 6728c2ecf20Sopenharmony_ci * expect exported BOs to be marked as purgeable. 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_ci ret = vc4_bo_inc_usecnt(bo); 6758c2ecf20Sopenharmony_ci if (ret) { 6768c2ecf20Sopenharmony_ci DRM_ERROR("Failed to increment BO usecnt\n"); 6778c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dmabuf = drm_gem_prime_export(obj, flags); 6818c2ecf20Sopenharmony_ci if (IS_ERR(dmabuf)) 6828c2ecf20Sopenharmony_ci vc4_bo_dec_usecnt(bo); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci return dmabuf; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_civm_fault_t vc4_fault(struct vm_fault *vmf) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 6908c2ecf20Sopenharmony_ci struct drm_gem_object *obj = vma->vm_private_data; 6918c2ecf20Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* The only reason we would end up here is when user-space accesses 6948c2ecf20Sopenharmony_ci * BO's memory after it's been purged. 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci mutex_lock(&bo->madv_lock); 6978c2ecf20Sopenharmony_ci WARN_ON(bo->madv != __VC4_MADV_PURGED); 6988c2ecf20Sopenharmony_ci mutex_unlock(&bo->madv_lock); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ciint vc4_mmap(struct file *filp, struct vm_area_struct *vma) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct drm_gem_object *gem_obj; 7068c2ecf20Sopenharmony_ci unsigned long vm_pgoff; 7078c2ecf20Sopenharmony_ci struct vc4_bo *bo; 7088c2ecf20Sopenharmony_ci int ret; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci ret = drm_gem_mmap(filp, vma); 7118c2ecf20Sopenharmony_ci if (ret) 7128c2ecf20Sopenharmony_ci return ret; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci gem_obj = vma->vm_private_data; 7158c2ecf20Sopenharmony_ci bo = to_vc4_bo(gem_obj); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { 7188c2ecf20Sopenharmony_ci DRM_DEBUG("mmaping of shader BOs for writing not allowed.\n"); 7198c2ecf20Sopenharmony_ci return -EINVAL; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (bo->madv != VC4_MADV_WILLNEED) { 7238c2ecf20Sopenharmony_ci DRM_DEBUG("mmaping of %s BO not allowed\n", 7248c2ecf20Sopenharmony_ci bo->madv == VC4_MADV_DONTNEED ? 7258c2ecf20Sopenharmony_ci "purgeable" : "purged"); 7268c2ecf20Sopenharmony_ci return -EINVAL; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* 7308c2ecf20Sopenharmony_ci * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the 7318c2ecf20Sopenharmony_ci * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map 7328c2ecf20Sopenharmony_ci * the whole buffer. 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_ci vma->vm_flags &= ~VM_PFNMAP; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* This ->vm_pgoff dance is needed to make all parties happy: 7378c2ecf20Sopenharmony_ci * - dma_mmap_wc() uses ->vm_pgoff as an offset within the allocated 7388c2ecf20Sopenharmony_ci * mem-region, hence the need to set it to zero (the value set by 7398c2ecf20Sopenharmony_ci * the DRM core is a virtual offset encoding the GEM object-id) 7408c2ecf20Sopenharmony_ci * - the mmap() core logic needs ->vm_pgoff to be restored to its 7418c2ecf20Sopenharmony_ci * initial value before returning from this function because it 7428c2ecf20Sopenharmony_ci * encodes the offset of this GEM in the dev->anon_inode pseudo-file 7438c2ecf20Sopenharmony_ci * and this information will be used when we invalidate userspace 7448c2ecf20Sopenharmony_ci * mappings with drm_vma_node_unmap() (called from vc4_gem_purge()). 7458c2ecf20Sopenharmony_ci */ 7468c2ecf20Sopenharmony_ci vm_pgoff = vma->vm_pgoff; 7478c2ecf20Sopenharmony_ci vma->vm_pgoff = 0; 7488c2ecf20Sopenharmony_ci ret = dma_mmap_wc(bo->base.base.dev->dev, vma, bo->base.vaddr, 7498c2ecf20Sopenharmony_ci bo->base.paddr, vma->vm_end - vma->vm_start); 7508c2ecf20Sopenharmony_ci vma->vm_pgoff = vm_pgoff; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (ret) 7538c2ecf20Sopenharmony_ci drm_gem_vm_close(vma); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci return ret; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ciint vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { 7638c2ecf20Sopenharmony_ci DRM_DEBUG("mmaping of shader BOs for writing not allowed.\n"); 7648c2ecf20Sopenharmony_ci return -EINVAL; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return drm_gem_cma_prime_mmap(obj, vma); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_civoid *vc4_prime_vmap(struct drm_gem_object *obj) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct vc4_bo *bo = to_vc4_bo(obj); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (bo->validated_shader) { 7758c2ecf20Sopenharmony_ci DRM_DEBUG("mmaping of shader BOs not allowed.\n"); 7768c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return drm_gem_cma_prime_vmap(obj); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistruct drm_gem_object * 7838c2ecf20Sopenharmony_civc4_prime_import_sg_table(struct drm_device *dev, 7848c2ecf20Sopenharmony_ci struct dma_buf_attachment *attach, 7858c2ecf20Sopenharmony_ci struct sg_table *sgt) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt); 7908c2ecf20Sopenharmony_ci if (IS_ERR(obj)) 7918c2ecf20Sopenharmony_ci return obj; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci return obj; 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic int vc4_grab_bin_bo(struct vc4_dev *vc4, struct vc4_file *vc4file) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci int ret; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (!vc4->v3d) 8018c2ecf20Sopenharmony_ci return -ENODEV; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (vc4file->bin_bo_used) 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci ret = vc4_v3d_bin_bo_get(vc4, &vc4file->bin_bo_used); 8078c2ecf20Sopenharmony_ci if (ret) 8088c2ecf20Sopenharmony_ci return ret; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ciint vc4_create_bo_ioctl(struct drm_device *dev, void *data, 8148c2ecf20Sopenharmony_ci struct drm_file *file_priv) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct drm_vc4_create_bo *args = data; 8178c2ecf20Sopenharmony_ci struct vc4_file *vc4file = file_priv->driver_priv; 8188c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 8198c2ecf20Sopenharmony_ci struct vc4_bo *bo = NULL; 8208c2ecf20Sopenharmony_ci int ret; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci ret = vc4_grab_bin_bo(vc4, vc4file); 8238c2ecf20Sopenharmony_ci if (ret) 8248c2ecf20Sopenharmony_ci return ret; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* 8278c2ecf20Sopenharmony_ci * We can't allocate from the BO cache, because the BOs don't 8288c2ecf20Sopenharmony_ci * get zeroed, and that might leak data between users. 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_ci bo = vc4_bo_create(dev, args->size, false, VC4_BO_TYPE_V3D); 8318c2ecf20Sopenharmony_ci if (IS_ERR(bo)) 8328c2ecf20Sopenharmony_ci return PTR_ERR(bo); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); 8378c2ecf20Sopenharmony_ci drm_gem_object_put(&bo->base.base); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci return ret; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciint vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, 8438c2ecf20Sopenharmony_ci struct drm_file *file_priv) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci struct drm_vc4_mmap_bo *args = data; 8468c2ecf20Sopenharmony_ci struct drm_gem_object *gem_obj; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 8498c2ecf20Sopenharmony_ci if (!gem_obj) { 8508c2ecf20Sopenharmony_ci DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); 8518c2ecf20Sopenharmony_ci return -EINVAL; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* The mmap offset was set up at BO allocation time. */ 8558c2ecf20Sopenharmony_ci args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci drm_gem_object_put(gem_obj); 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ciint 8628c2ecf20Sopenharmony_civc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, 8638c2ecf20Sopenharmony_ci struct drm_file *file_priv) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct drm_vc4_create_shader_bo *args = data; 8668c2ecf20Sopenharmony_ci struct vc4_file *vc4file = file_priv->driver_priv; 8678c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 8688c2ecf20Sopenharmony_ci struct vc4_bo *bo = NULL; 8698c2ecf20Sopenharmony_ci int ret; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (args->size == 0) 8728c2ecf20Sopenharmony_ci return -EINVAL; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (args->size % sizeof(u64) != 0) 8758c2ecf20Sopenharmony_ci return -EINVAL; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (args->flags != 0) { 8788c2ecf20Sopenharmony_ci DRM_INFO("Unknown flags set: 0x%08x\n", args->flags); 8798c2ecf20Sopenharmony_ci return -EINVAL; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (args->pad != 0) { 8838c2ecf20Sopenharmony_ci DRM_INFO("Pad set: 0x%08x\n", args->pad); 8848c2ecf20Sopenharmony_ci return -EINVAL; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ret = vc4_grab_bin_bo(vc4, vc4file); 8888c2ecf20Sopenharmony_ci if (ret) 8898c2ecf20Sopenharmony_ci return ret; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci bo = vc4_bo_create(dev, args->size, true, VC4_BO_TYPE_V3D_SHADER); 8928c2ecf20Sopenharmony_ci if (IS_ERR(bo)) 8938c2ecf20Sopenharmony_ci return PTR_ERR(bo); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci bo->madv = VC4_MADV_WILLNEED; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (copy_from_user(bo->base.vaddr, 8988c2ecf20Sopenharmony_ci (void __user *)(uintptr_t)args->data, 8998c2ecf20Sopenharmony_ci args->size)) { 9008c2ecf20Sopenharmony_ci ret = -EFAULT; 9018c2ecf20Sopenharmony_ci goto fail; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci /* Clear the rest of the memory from allocating from the BO 9048c2ecf20Sopenharmony_ci * cache. 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_ci memset(bo->base.vaddr + args->size, 0, 9078c2ecf20Sopenharmony_ci bo->base.base.size - args->size); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci bo->validated_shader = vc4_validate_shader(&bo->base); 9108c2ecf20Sopenharmony_ci if (!bo->validated_shader) { 9118c2ecf20Sopenharmony_ci ret = -EINVAL; 9128c2ecf20Sopenharmony_ci goto fail; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* We have to create the handle after validation, to avoid 9168c2ecf20Sopenharmony_ci * races for users to do doing things like mmap the shader BO. 9178c2ecf20Sopenharmony_ci */ 9188c2ecf20Sopenharmony_ci ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cifail: 9218c2ecf20Sopenharmony_ci drm_gem_object_put(&bo->base.base); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci return ret; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci/** 9278c2ecf20Sopenharmony_ci * vc4_set_tiling_ioctl() - Sets the tiling modifier for a BO. 9288c2ecf20Sopenharmony_ci * @dev: DRM device 9298c2ecf20Sopenharmony_ci * @data: ioctl argument 9308c2ecf20Sopenharmony_ci * @file_priv: DRM file for this fd 9318c2ecf20Sopenharmony_ci * 9328c2ecf20Sopenharmony_ci * The tiling state of the BO decides the default modifier of an fb if 9338c2ecf20Sopenharmony_ci * no specific modifier was set by userspace, and the return value of 9348c2ecf20Sopenharmony_ci * vc4_get_tiling_ioctl() (so that userspace can treat a BO it 9358c2ecf20Sopenharmony_ci * received from dmabuf as the same tiling format as the producer 9368c2ecf20Sopenharmony_ci * used). 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_ciint vc4_set_tiling_ioctl(struct drm_device *dev, void *data, 9398c2ecf20Sopenharmony_ci struct drm_file *file_priv) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct drm_vc4_set_tiling *args = data; 9428c2ecf20Sopenharmony_ci struct drm_gem_object *gem_obj; 9438c2ecf20Sopenharmony_ci struct vc4_bo *bo; 9448c2ecf20Sopenharmony_ci bool t_format; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (args->flags != 0) 9478c2ecf20Sopenharmony_ci return -EINVAL; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci switch (args->modifier) { 9508c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NONE: 9518c2ecf20Sopenharmony_ci t_format = false; 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: 9548c2ecf20Sopenharmony_ci t_format = true; 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci default: 9578c2ecf20Sopenharmony_ci return -EINVAL; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 9618c2ecf20Sopenharmony_ci if (!gem_obj) { 9628c2ecf20Sopenharmony_ci DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); 9638c2ecf20Sopenharmony_ci return -ENOENT; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci bo = to_vc4_bo(gem_obj); 9668c2ecf20Sopenharmony_ci bo->t_format = t_format; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci drm_gem_object_put(gem_obj); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci return 0; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci/** 9748c2ecf20Sopenharmony_ci * vc4_get_tiling_ioctl() - Gets the tiling modifier for a BO. 9758c2ecf20Sopenharmony_ci * @dev: DRM device 9768c2ecf20Sopenharmony_ci * @data: ioctl argument 9778c2ecf20Sopenharmony_ci * @file_priv: DRM file for this fd 9788c2ecf20Sopenharmony_ci * 9798c2ecf20Sopenharmony_ci * Returns the tiling modifier for a BO as set by vc4_set_tiling_ioctl(). 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_ciint vc4_get_tiling_ioctl(struct drm_device *dev, void *data, 9828c2ecf20Sopenharmony_ci struct drm_file *file_priv) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci struct drm_vc4_get_tiling *args = data; 9858c2ecf20Sopenharmony_ci struct drm_gem_object *gem_obj; 9868c2ecf20Sopenharmony_ci struct vc4_bo *bo; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (args->flags != 0 || args->modifier != 0) 9898c2ecf20Sopenharmony_ci return -EINVAL; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 9928c2ecf20Sopenharmony_ci if (!gem_obj) { 9938c2ecf20Sopenharmony_ci DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); 9948c2ecf20Sopenharmony_ci return -ENOENT; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci bo = to_vc4_bo(gem_obj); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (bo->t_format) 9998c2ecf20Sopenharmony_ci args->modifier = DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 10008c2ecf20Sopenharmony_ci else 10018c2ecf20Sopenharmony_ci args->modifier = DRM_FORMAT_MOD_NONE; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci drm_gem_object_put(gem_obj); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci return 0; 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic void vc4_bo_cache_destroy(struct drm_device *dev, void *unused); 10098c2ecf20Sopenharmony_ciint vc4_bo_cache_init(struct drm_device *dev) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 10128c2ecf20Sopenharmony_ci int i; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* Create the initial set of BO labels that the kernel will 10158c2ecf20Sopenharmony_ci * use. This lets us avoid a bunch of string reallocation in 10168c2ecf20Sopenharmony_ci * the kernel's draw and BO allocation paths. 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_ci vc4->bo_labels = kcalloc(VC4_BO_TYPE_COUNT, sizeof(*vc4->bo_labels), 10198c2ecf20Sopenharmony_ci GFP_KERNEL); 10208c2ecf20Sopenharmony_ci if (!vc4->bo_labels) 10218c2ecf20Sopenharmony_ci return -ENOMEM; 10228c2ecf20Sopenharmony_ci vc4->num_labels = VC4_BO_TYPE_COUNT; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(bo_type_names) != VC4_BO_TYPE_COUNT); 10258c2ecf20Sopenharmony_ci for (i = 0; i < VC4_BO_TYPE_COUNT; i++) 10268c2ecf20Sopenharmony_ci vc4->bo_labels[i].name = bo_type_names[i]; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci mutex_init(&vc4->bo_lock); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci vc4_debugfs_add_file(dev, "bo_stats", vc4_bo_stats_debugfs, NULL); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vc4->bo_cache.time_list); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); 10358c2ecf20Sopenharmony_ci timer_setup(&vc4->bo_cache.time_timer, vc4_bo_cache_time_timer, 0); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci return drmm_add_action_or_reset(dev, vc4_bo_cache_destroy, NULL); 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic void vc4_bo_cache_destroy(struct drm_device *dev, void *unused) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 10438c2ecf20Sopenharmony_ci int i; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci del_timer(&vc4->bo_cache.time_timer); 10468c2ecf20Sopenharmony_ci cancel_work_sync(&vc4->bo_cache.time_work); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci vc4_bo_cache_purge(dev); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci for (i = 0; i < vc4->num_labels; i++) { 10518c2ecf20Sopenharmony_ci if (vc4->bo_labels[i].num_allocated) { 10528c2ecf20Sopenharmony_ci DRM_ERROR("Destroying BO cache with %d %s " 10538c2ecf20Sopenharmony_ci "BOs still allocated\n", 10548c2ecf20Sopenharmony_ci vc4->bo_labels[i].num_allocated, 10558c2ecf20Sopenharmony_ci vc4->bo_labels[i].name); 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (is_user_label(i)) 10598c2ecf20Sopenharmony_ci kfree(vc4->bo_labels[i].name); 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci kfree(vc4->bo_labels); 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ciint vc4_label_bo_ioctl(struct drm_device *dev, void *data, 10658c2ecf20Sopenharmony_ci struct drm_file *file_priv) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 10688c2ecf20Sopenharmony_ci struct drm_vc4_label_bo *args = data; 10698c2ecf20Sopenharmony_ci char *name; 10708c2ecf20Sopenharmony_ci struct drm_gem_object *gem_obj; 10718c2ecf20Sopenharmony_ci int ret = 0, label; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (!args->len) 10748c2ecf20Sopenharmony_ci return -EINVAL; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci name = strndup_user(u64_to_user_ptr(args->name), args->len + 1); 10778c2ecf20Sopenharmony_ci if (IS_ERR(name)) 10788c2ecf20Sopenharmony_ci return PTR_ERR(name); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, args->handle); 10818c2ecf20Sopenharmony_ci if (!gem_obj) { 10828c2ecf20Sopenharmony_ci DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); 10838c2ecf20Sopenharmony_ci kfree(name); 10848c2ecf20Sopenharmony_ci return -ENOENT; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci mutex_lock(&vc4->bo_lock); 10888c2ecf20Sopenharmony_ci label = vc4_get_user_label(vc4, name); 10898c2ecf20Sopenharmony_ci if (label != -1) 10908c2ecf20Sopenharmony_ci vc4_bo_set_label(gem_obj, label); 10918c2ecf20Sopenharmony_ci else 10928c2ecf20Sopenharmony_ci ret = -ENOMEM; 10938c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bo_lock); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci drm_gem_object_put(gem_obj); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return ret; 10988c2ecf20Sopenharmony_ci} 1099