f3e948af01c58fd9d68766166230a7b4245f051b
[sfrench/cifs-2.6.git] / drivers / gpu / drm / msm / msm_gem_shrinker.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2016 Red Hat
4  * Author: Rob Clark <robdclark@gmail.com>
5  */
6
7 #include "msm_drv.h"
8 #include "msm_gem.h"
9 #include "msm_gpu.h"
10 #include "msm_gpu_trace.h"
11
12 static unsigned long
13 msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
14 {
15         struct msm_drm_private *priv =
16                 container_of(shrinker, struct msm_drm_private, shrinker);
17         return priv->shrinkable_count;
18 }
19
20 static unsigned long
21 msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
22 {
23         struct msm_drm_private *priv =
24                 container_of(shrinker, struct msm_drm_private, shrinker);
25         struct msm_gem_object *msm_obj;
26         unsigned long freed = 0;
27
28         mutex_lock(&priv->mm_lock);
29
30         list_for_each_entry(msm_obj, &priv->inactive_dontneed, mm_list) {
31                 if (freed >= sc->nr_to_scan)
32                         break;
33                 /* Use trylock, because we cannot block on a obj that
34                  * might be trying to acquire mm_lock
35                  */
36                 if (!msm_gem_trylock(&msm_obj->base))
37                         continue;
38                 if (is_purgeable(msm_obj)) {
39                         msm_gem_purge(&msm_obj->base);
40                         freed += msm_obj->base.size >> PAGE_SHIFT;
41                 }
42                 msm_gem_unlock(&msm_obj->base);
43         }
44
45         mutex_unlock(&priv->mm_lock);
46
47         if (freed > 0) {
48                 trace_msm_gem_purge(freed << PAGE_SHIFT);
49         } else {
50                 return SHRINK_STOP;
51         }
52
53         return freed;
54 }
55
56 /* since we don't know any better, lets bail after a few
57  * and if necessary the shrinker will be invoked again.
58  * Seems better than unmapping *everything*
59  */
60 static const int vmap_shrink_limit = 15;
61
62 static unsigned
63 vmap_shrink(struct list_head *mm_list)
64 {
65         struct msm_gem_object *msm_obj;
66         unsigned unmapped = 0;
67
68         list_for_each_entry(msm_obj, mm_list, mm_list) {
69                 /* Use trylock, because we cannot block on a obj that
70                  * might be trying to acquire mm_lock
71                  */
72                 if (!msm_gem_trylock(&msm_obj->base))
73                         continue;
74                 if (is_vunmapable(msm_obj)) {
75                         msm_gem_vunmap(&msm_obj->base);
76                         unmapped++;
77                 }
78                 msm_gem_unlock(&msm_obj->base);
79
80                 if (++unmapped >= vmap_shrink_limit)
81                         break;
82         }
83
84         return unmapped;
85 }
86
87 static int
88 msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr)
89 {
90         struct msm_drm_private *priv =
91                 container_of(nb, struct msm_drm_private, vmap_notifier);
92         struct list_head *mm_lists[] = {
93                 &priv->inactive_dontneed,
94                 &priv->inactive_willneed,
95                 priv->gpu ? &priv->gpu->active_list : NULL,
96                 NULL,
97         };
98         unsigned idx, unmapped = 0;
99
100         mutex_lock(&priv->mm_lock);
101
102         for (idx = 0; mm_lists[idx]; idx++) {
103                 unmapped += vmap_shrink(mm_lists[idx]);
104
105                 if (unmapped >= vmap_shrink_limit)
106                         break;
107         }
108
109         mutex_unlock(&priv->mm_lock);
110
111         *(unsigned long *)ptr += unmapped;
112
113         if (unmapped > 0)
114                 trace_msm_gem_purge_vmaps(unmapped);
115
116         return NOTIFY_DONE;
117 }
118
119 /**
120  * msm_gem_shrinker_init - Initialize msm shrinker
121  * @dev: drm device
122  *
123  * This function registers and sets up the msm shrinker.
124  */
125 void msm_gem_shrinker_init(struct drm_device *dev)
126 {
127         struct msm_drm_private *priv = dev->dev_private;
128         priv->shrinker.count_objects = msm_gem_shrinker_count;
129         priv->shrinker.scan_objects = msm_gem_shrinker_scan;
130         priv->shrinker.seeks = DEFAULT_SEEKS;
131         WARN_ON(register_shrinker(&priv->shrinker));
132
133         priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap;
134         WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier));
135 }
136
137 /**
138  * msm_gem_shrinker_cleanup - Clean up msm shrinker
139  * @dev: drm device
140  *
141  * This function unregisters the msm shrinker.
142  */
143 void msm_gem_shrinker_cleanup(struct drm_device *dev)
144 {
145         struct msm_drm_private *priv = dev->dev_private;
146
147         if (priv->shrinker.nr_deferred) {
148                 WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier));
149                 unregister_shrinker(&priv->shrinker);
150         }
151 }