Commit | Line | Data |
---|---|---|
013b6510 RH |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* Copyright (C) 2019 Arm Ltd. | |
3 | * | |
4 | * Based on msm_gem_freedreno.c: | |
5 | * Copyright (C) 2016 Red Hat | |
6 | * 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 | ||
18 | static unsigned long | |
19 | panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) | |
20 | { | |
21 | struct panfrost_device *pfdev = | |
22 | container_of(shrinker, struct panfrost_device, shrinker); | |
23 | struct drm_gem_shmem_object *shmem; | |
24 | unsigned long count = 0; | |
25 | ||
26 | if (!mutex_trylock(&pfdev->shrinker_lock)) | |
27 | return 0; | |
28 | ||
29 | list_for_each_entry(shmem, &pfdev->shrinker_list, madv_list) { | |
30 | if (drm_gem_shmem_is_purgeable(shmem)) | |
31 | count += shmem->base.size >> PAGE_SHIFT; | |
32 | } | |
33 | ||
34 | mutex_unlock(&pfdev->shrinker_lock); | |
35 | ||
36 | return count; | |
37 | } | |
38 | ||
55b9a054 | 39 | static bool panfrost_gem_purge(struct drm_gem_object *obj) |
013b6510 RH |
40 | { |
41 | struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); | |
bdefca2d | 42 | struct panfrost_gem_object *bo = to_panfrost_bo(obj); |
55b9a054 | 43 | |
7e0cf7e9 BB |
44 | if (atomic_read(&bo->gpu_usecount)) |
45 | return false; | |
46 | ||
55b9a054 RH |
47 | if (!mutex_trylock(&shmem->pages_lock)) |
48 | return false; | |
013b6510 | 49 | |
bdefca2d | 50 | panfrost_gem_teardown_mappings(bo); |
013b6510 RH |
51 | drm_gem_shmem_purge_locked(obj); |
52 | ||
53 | mutex_unlock(&shmem->pages_lock); | |
55b9a054 | 54 | return true; |
013b6510 RH |
55 | } |
56 | ||
57 | static unsigned long | |
58 | panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) | |
59 | { | |
60 | struct panfrost_device *pfdev = | |
61 | container_of(shrinker, struct panfrost_device, shrinker); | |
62 | struct drm_gem_shmem_object *shmem, *tmp; | |
63 | unsigned long freed = 0; | |
64 | ||
65 | if (!mutex_trylock(&pfdev->shrinker_lock)) | |
66 | return SHRINK_STOP; | |
67 | ||
68 | list_for_each_entry_safe(shmem, tmp, &pfdev->shrinker_list, madv_list) { | |
69 | if (freed >= sc->nr_to_scan) | |
70 | break; | |
55b9a054 RH |
71 | if (drm_gem_shmem_is_purgeable(shmem) && |
72 | panfrost_gem_purge(&shmem->base)) { | |
013b6510 RH |
73 | freed += shmem->base.size >> PAGE_SHIFT; |
74 | list_del_init(&shmem->madv_list); | |
75 | } | |
76 | } | |
77 | ||
78 | mutex_unlock(&pfdev->shrinker_lock); | |
79 | ||
80 | if (freed > 0) | |
81 | pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT); | |
82 | ||
83 | return freed; | |
84 | } | |
85 | ||
86 | /** | |
87 | * panfrost_gem_shrinker_init - Initialize panfrost shrinker | |
88 | * @dev: DRM device | |
89 | * | |
90 | * This function registers and sets up the panfrost shrinker. | |
91 | */ | |
92 | void panfrost_gem_shrinker_init(struct drm_device *dev) | |
93 | { | |
94 | struct panfrost_device *pfdev = dev->dev_private; | |
95 | pfdev->shrinker.count_objects = panfrost_gem_shrinker_count; | |
96 | pfdev->shrinker.scan_objects = panfrost_gem_shrinker_scan; | |
97 | pfdev->shrinker.seeks = DEFAULT_SEEKS; | |
98 | WARN_ON(register_shrinker(&pfdev->shrinker)); | |
99 | } | |
100 | ||
101 | /** | |
102 | * panfrost_gem_shrinker_cleanup - Clean up panfrost shrinker | |
103 | * @dev: DRM device | |
104 | * | |
105 | * This function unregisters the panfrost shrinker. | |
106 | */ | |
107 | void panfrost_gem_shrinker_cleanup(struct drm_device *dev) | |
108 | { | |
109 | struct panfrost_device *pfdev = dev->dev_private; | |
110 | ||
111 | if (pfdev->shrinker.nr_deferred) { | |
112 | unregister_shrinker(&pfdev->shrinker); | |
113 | } | |
114 | } |