128 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (C) 2019 Arm Ltd.3 *4 * Based on msm_gem_freedreno.c:5 * Copyright (C) 2016 Red Hat6 * Author: Rob Clark <robdclark@gmail.com>7 */8 9#include <linux/list.h>10 11#include <drm/drm_device.h>12#include <drm/drm_gem_shmem_helper.h>13 14#include "panfrost_device.h"15#include "panfrost_gem.h"16#include "panfrost_mmu.h"17 18static unsigned long19panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)20{21 struct panfrost_device *pfdev = shrinker->private_data;22 struct drm_gem_shmem_object *shmem;23 unsigned long count = 0;24 25 if (!mutex_trylock(&pfdev->shrinker_lock))26 return 0;27 28 list_for_each_entry(shmem, &pfdev->shrinker_list, madv_list) {29 if (drm_gem_shmem_is_purgeable(shmem))30 count += shmem->base.size >> PAGE_SHIFT;31 }32 33 mutex_unlock(&pfdev->shrinker_lock);34 35 return count;36}37 38static bool panfrost_gem_purge(struct drm_gem_object *obj)39{40 struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);41 struct panfrost_gem_object *bo = to_panfrost_bo(obj);42 bool ret = false;43 44 if (atomic_read(&bo->gpu_usecount))45 return false;46 47 if (!mutex_trylock(&bo->mappings.lock))48 return false;49 50 if (!dma_resv_trylock(shmem->base.resv))51 goto unlock_mappings;52 53 panfrost_gem_teardown_mappings_locked(bo);54 drm_gem_shmem_purge(&bo->base);55 ret = true;56 57 dma_resv_unlock(shmem->base.resv);58 59unlock_mappings:60 mutex_unlock(&bo->mappings.lock);61 return ret;62}63 64static unsigned long65panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)66{67 struct panfrost_device *pfdev = shrinker->private_data;68 struct drm_gem_shmem_object *shmem, *tmp;69 unsigned long freed = 0;70 71 if (!mutex_trylock(&pfdev->shrinker_lock))72 return SHRINK_STOP;73 74 list_for_each_entry_safe(shmem, tmp, &pfdev->shrinker_list, madv_list) {75 if (freed >= sc->nr_to_scan)76 break;77 if (drm_gem_shmem_is_purgeable(shmem) &&78 panfrost_gem_purge(&shmem->base)) {79 freed += shmem->base.size >> PAGE_SHIFT;80 list_del_init(&shmem->madv_list);81 }82 }83 84 mutex_unlock(&pfdev->shrinker_lock);85 86 if (freed > 0)87 pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT);88 89 return freed;90}91 92/**93 * panfrost_gem_shrinker_init - Initialize panfrost shrinker94 * @dev: DRM device95 *96 * This function registers and sets up the panfrost shrinker.97 */98int panfrost_gem_shrinker_init(struct drm_device *dev)99{100 struct panfrost_device *pfdev = dev->dev_private;101 102 pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");103 if (!pfdev->shrinker)104 return -ENOMEM;105 106 pfdev->shrinker->count_objects = panfrost_gem_shrinker_count;107 pfdev->shrinker->scan_objects = panfrost_gem_shrinker_scan;108 pfdev->shrinker->private_data = pfdev;109 110 shrinker_register(pfdev->shrinker);111 112 return 0;113}114 115/**116 * panfrost_gem_shrinker_cleanup - Clean up panfrost shrinker117 * @dev: DRM device118 *119 * This function unregisters the panfrost shrinker.120 */121void panfrost_gem_shrinker_cleanup(struct drm_device *dev)122{123 struct panfrost_device *pfdev = dev->dev_private;124 125 if (pfdev->shrinker)126 shrinker_free(pfdev->shrinker);127}128