18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci/* Copyright (C) 2019 Arm Ltd. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Based on msm_gem_freedreno.c: 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat 68c2ecf20Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_gem_shmem_helper.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "panfrost_device.h" 158c2ecf20Sopenharmony_ci#include "panfrost_gem.h" 168c2ecf20Sopenharmony_ci#include "panfrost_mmu.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic unsigned long 198c2ecf20Sopenharmony_cipanfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci struct panfrost_device *pfdev = 228c2ecf20Sopenharmony_ci container_of(shrinker, struct panfrost_device, shrinker); 238c2ecf20Sopenharmony_ci struct drm_gem_shmem_object *shmem; 248c2ecf20Sopenharmony_ci unsigned long count = 0; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (!mutex_trylock(&pfdev->shrinker_lock)) 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci list_for_each_entry(shmem, &pfdev->shrinker_list, madv_list) { 308c2ecf20Sopenharmony_ci if (drm_gem_shmem_is_purgeable(shmem)) 318c2ecf20Sopenharmony_ci count += shmem->base.size >> PAGE_SHIFT; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci mutex_unlock(&pfdev->shrinker_lock); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return count; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic bool panfrost_gem_purge(struct drm_gem_object *obj) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); 428c2ecf20Sopenharmony_ci struct panfrost_gem_object *bo = to_panfrost_bo(obj); 438c2ecf20Sopenharmony_ci bool ret = false; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (atomic_read(&bo->gpu_usecount)) 468c2ecf20Sopenharmony_ci return false; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (!mutex_trylock(&bo->mappings.lock)) 498c2ecf20Sopenharmony_ci return false; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!mutex_trylock(&shmem->pages_lock)) 528c2ecf20Sopenharmony_ci goto unlock_mappings; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci panfrost_gem_teardown_mappings_locked(bo); 558c2ecf20Sopenharmony_ci drm_gem_shmem_purge_locked(obj); 568c2ecf20Sopenharmony_ci ret = true; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci mutex_unlock(&shmem->pages_lock); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciunlock_mappings: 618c2ecf20Sopenharmony_ci mutex_unlock(&bo->mappings.lock); 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic unsigned long 668c2ecf20Sopenharmony_cipanfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct panfrost_device *pfdev = 698c2ecf20Sopenharmony_ci container_of(shrinker, struct panfrost_device, shrinker); 708c2ecf20Sopenharmony_ci struct drm_gem_shmem_object *shmem, *tmp; 718c2ecf20Sopenharmony_ci unsigned long freed = 0; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!mutex_trylock(&pfdev->shrinker_lock)) 748c2ecf20Sopenharmony_ci return SHRINK_STOP; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci list_for_each_entry_safe(shmem, tmp, &pfdev->shrinker_list, madv_list) { 778c2ecf20Sopenharmony_ci if (freed >= sc->nr_to_scan) 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci if (drm_gem_shmem_is_purgeable(shmem) && 808c2ecf20Sopenharmony_ci panfrost_gem_purge(&shmem->base)) { 818c2ecf20Sopenharmony_ci freed += shmem->base.size >> PAGE_SHIFT; 828c2ecf20Sopenharmony_ci list_del_init(&shmem->madv_list); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci mutex_unlock(&pfdev->shrinker_lock); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (freed > 0) 898c2ecf20Sopenharmony_ci pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return freed; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/** 958c2ecf20Sopenharmony_ci * panfrost_gem_shrinker_init - Initialize panfrost shrinker 968c2ecf20Sopenharmony_ci * @dev: DRM device 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * This function registers and sets up the panfrost shrinker. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_civoid panfrost_gem_shrinker_init(struct drm_device *dev) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct panfrost_device *pfdev = dev->dev_private; 1038c2ecf20Sopenharmony_ci pfdev->shrinker.count_objects = panfrost_gem_shrinker_count; 1048c2ecf20Sopenharmony_ci pfdev->shrinker.scan_objects = panfrost_gem_shrinker_scan; 1058c2ecf20Sopenharmony_ci pfdev->shrinker.seeks = DEFAULT_SEEKS; 1068c2ecf20Sopenharmony_ci WARN_ON(register_shrinker(&pfdev->shrinker)); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * panfrost_gem_shrinker_cleanup - Clean up panfrost shrinker 1118c2ecf20Sopenharmony_ci * @dev: DRM device 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * This function unregisters the panfrost shrinker. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_civoid panfrost_gem_shrinker_cleanup(struct drm_device *dev) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct panfrost_device *pfdev = dev->dev_private; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (pfdev->shrinker.nr_deferred) { 1208c2ecf20Sopenharmony_ci unregister_shrinker(&pfdev->shrinker); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci} 123