]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
68209390 RC |
2 | /* |
3 | * Copyright (C) 2016 Red Hat | |
4 | * Author: Rob Clark <robdclark@gmail.com> | |
68209390 RC |
5 | */ |
6 | ||
7 | #include "msm_drv.h" | |
8 | #include "msm_gem.h" | |
9 | ||
68209390 RC |
10 | static bool msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock) |
11 | { | |
0e08270a SS |
12 | /* NOTE: we are *closer* to being able to get rid of |
13 | * mutex_trylock_recursive().. the msm_gem code itself does | |
14 | * not need struct_mutex, although codepaths that can trigger | |
15 | * shrinker are still called in code-paths that hold the | |
16 | * struct_mutex. | |
17 | * | |
18 | * Also, msm_obj->madv is protected by struct_mutex. | |
19 | * | |
20 | * The next step is probably split out a seperate lock for | |
21 | * protecting inactive_list, so that shrinker does not need | |
22 | * struct_mutex. | |
23 | */ | |
0f5225b0 PZ |
24 | switch (mutex_trylock_recursive(&dev->struct_mutex)) { |
25 | case MUTEX_TRYLOCK_FAILED: | |
3ab7c086 | 26 | return false; |
68209390 | 27 | |
0f5225b0 PZ |
28 | case MUTEX_TRYLOCK_SUCCESS: |
29 | *unlock = true; | |
30 | return true; | |
31 | ||
32 | case MUTEX_TRYLOCK_RECURSIVE: | |
33 | *unlock = false; | |
34 | return true; | |
35 | } | |
68209390 | 36 | |
0f5225b0 PZ |
37 | BUG(); |
38 | } | |
68209390 RC |
39 | |
40 | static unsigned long | |
41 | msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) | |
42 | { | |
43 | struct msm_drm_private *priv = | |
44 | container_of(shrinker, struct msm_drm_private, shrinker); | |
45 | struct drm_device *dev = priv->dev; | |
46 | struct msm_gem_object *msm_obj; | |
47 | unsigned long count = 0; | |
48 | bool unlock; | |
49 | ||
50 | if (!msm_gem_shrinker_lock(dev, &unlock)) | |
51 | return 0; | |
52 | ||
53 | list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { | |
54 | if (is_purgeable(msm_obj)) | |
55 | count += msm_obj->base.size >> PAGE_SHIFT; | |
56 | } | |
57 | ||
58 | if (unlock) | |
59 | mutex_unlock(&dev->struct_mutex); | |
60 | ||
61 | return count; | |
62 | } | |
63 | ||
64 | static unsigned long | |
65 | msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) | |
66 | { | |
67 | struct msm_drm_private *priv = | |
68 | container_of(shrinker, struct msm_drm_private, shrinker); | |
69 | struct drm_device *dev = priv->dev; | |
70 | struct msm_gem_object *msm_obj; | |
71 | unsigned long freed = 0; | |
72 | bool unlock; | |
73 | ||
74 | if (!msm_gem_shrinker_lock(dev, &unlock)) | |
75 | return SHRINK_STOP; | |
76 | ||
77 | list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { | |
78 | if (freed >= sc->nr_to_scan) | |
79 | break; | |
80 | if (is_purgeable(msm_obj)) { | |
0e08270a | 81 | msm_gem_purge(&msm_obj->base, OBJ_LOCK_SHRINKER); |
68209390 RC |
82 | freed += msm_obj->base.size >> PAGE_SHIFT; |
83 | } | |
84 | } | |
85 | ||
86 | if (unlock) | |
87 | mutex_unlock(&dev->struct_mutex); | |
88 | ||
89 | if (freed > 0) | |
90 | pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT); | |
91 | ||
92 | return freed; | |
93 | } | |
94 | ||
e1e9db2c RC |
95 | static int |
96 | msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr) | |
97 | { | |
98 | struct msm_drm_private *priv = | |
99 | container_of(nb, struct msm_drm_private, vmap_notifier); | |
100 | struct drm_device *dev = priv->dev; | |
101 | struct msm_gem_object *msm_obj; | |
102 | unsigned unmapped = 0; | |
103 | bool unlock; | |
104 | ||
105 | if (!msm_gem_shrinker_lock(dev, &unlock)) | |
106 | return NOTIFY_DONE; | |
107 | ||
108 | list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { | |
109 | if (is_vunmapable(msm_obj)) { | |
0e08270a | 110 | msm_gem_vunmap(&msm_obj->base, OBJ_LOCK_SHRINKER); |
e1e9db2c RC |
111 | /* since we don't know any better, lets bail after a few |
112 | * and if necessary the shrinker will be invoked again. | |
113 | * Seems better than unmapping *everything* | |
114 | */ | |
115 | if (++unmapped >= 15) | |
116 | break; | |
117 | } | |
118 | } | |
119 | ||
120 | if (unlock) | |
121 | mutex_unlock(&dev->struct_mutex); | |
122 | ||
123 | *(unsigned long *)ptr += unmapped; | |
124 | ||
125 | if (unmapped > 0) | |
126 | pr_info_ratelimited("Purging %u vmaps\n", unmapped); | |
127 | ||
128 | return NOTIFY_DONE; | |
129 | } | |
130 | ||
68209390 RC |
131 | /** |
132 | * msm_gem_shrinker_init - Initialize msm shrinker | |
133 | * @dev_priv: msm device | |
134 | * | |
135 | * This function registers and sets up the msm shrinker. | |
136 | */ | |
137 | void msm_gem_shrinker_init(struct drm_device *dev) | |
138 | { | |
139 | struct msm_drm_private *priv = dev->dev_private; | |
140 | priv->shrinker.count_objects = msm_gem_shrinker_count; | |
141 | priv->shrinker.scan_objects = msm_gem_shrinker_scan; | |
142 | priv->shrinker.seeks = DEFAULT_SEEKS; | |
143 | WARN_ON(register_shrinker(&priv->shrinker)); | |
e1e9db2c RC |
144 | |
145 | priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap; | |
146 | WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier)); | |
68209390 RC |
147 | } |
148 | ||
149 | /** | |
150 | * msm_gem_shrinker_cleanup - Clean up msm shrinker | |
151 | * @dev_priv: msm device | |
152 | * | |
153 | * This function unregisters the msm shrinker. | |
154 | */ | |
155 | void msm_gem_shrinker_cleanup(struct drm_device *dev) | |
156 | { | |
157 | struct msm_drm_private *priv = dev->dev_private; | |
16976085 AT |
158 | |
159 | if (priv->shrinker.nr_deferred) { | |
160 | WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier)); | |
161 | unregister_shrinker(&priv->shrinker); | |
162 | } | |
68209390 | 163 | } |