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