]> Git Repo - linux.git/blob - drivers/gpu/drm/msm/msm_gem_shrinker.c
Merge tag 'for-linus' of git://git.armlinux.org.uk/~rmk/linux-arm
[linux.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 <[email protected]>
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         struct msm_gem_object *msm_obj;
18         unsigned long count = 0;
19
20         mutex_lock(&priv->mm_lock);
21
22         list_for_each_entry(msm_obj, &priv->inactive_dontneed, mm_list) {
23                 if (!msm_gem_trylock(&msm_obj->base))
24                         continue;
25                 if (is_purgeable(msm_obj))
26                         count += msm_obj->base.size >> PAGE_SHIFT;
27                 msm_gem_unlock(&msm_obj->base);
28         }
29
30         mutex_unlock(&priv->mm_lock);
31
32         return count;
33 }
34
35 static unsigned long
36 msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
37 {
38         struct msm_drm_private *priv =
39                 container_of(shrinker, struct msm_drm_private, shrinker);
40         struct msm_gem_object *msm_obj;
41         unsigned long freed = 0;
42
43         mutex_lock(&priv->mm_lock);
44
45         list_for_each_entry(msm_obj, &priv->inactive_dontneed, mm_list) {
46                 if (freed >= sc->nr_to_scan)
47                         break;
48                 if (!msm_gem_trylock(&msm_obj->base))
49                         continue;
50                 if (is_purgeable(msm_obj)) {
51                         msm_gem_purge(&msm_obj->base);
52                         freed += msm_obj->base.size >> PAGE_SHIFT;
53                 }
54                 msm_gem_unlock(&msm_obj->base);
55         }
56
57         mutex_unlock(&priv->mm_lock);
58
59         if (freed > 0)
60                 trace_msm_gem_purge(freed << PAGE_SHIFT);
61
62         return freed;
63 }
64
65 /* since we don't know any better, lets bail after a few
66  * and if necessary the shrinker will be invoked again.
67  * Seems better than unmapping *everything*
68  */
69 static const int vmap_shrink_limit = 15;
70
71 static unsigned
72 vmap_shrink(struct list_head *mm_list)
73 {
74         struct msm_gem_object *msm_obj;
75         unsigned unmapped = 0;
76
77         list_for_each_entry(msm_obj, mm_list, mm_list) {
78                 if (!msm_gem_trylock(&msm_obj->base))
79                         continue;
80                 if (is_vunmapable(msm_obj)) {
81                         msm_gem_vunmap(&msm_obj->base);
82                         unmapped++;
83                 }
84                 msm_gem_unlock(&msm_obj->base);
85
86                 if (++unmapped >= vmap_shrink_limit)
87                         break;
88         }
89
90         return unmapped;
91 }
92
93 static int
94 msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr)
95 {
96         struct msm_drm_private *priv =
97                 container_of(nb, struct msm_drm_private, vmap_notifier);
98         struct list_head *mm_lists[] = {
99                 &priv->inactive_dontneed,
100                 &priv->inactive_willneed,
101                 priv->gpu ? &priv->gpu->active_list : NULL,
102                 NULL,
103         };
104         unsigned idx, unmapped = 0;
105
106         mutex_lock(&priv->mm_lock);
107
108         for (idx = 0; mm_lists[idx]; idx++) {
109                 unmapped += vmap_shrink(mm_lists[idx]);
110
111                 if (unmapped >= vmap_shrink_limit)
112                         break;
113         }
114
115         mutex_unlock(&priv->mm_lock);
116
117         *(unsigned long *)ptr += unmapped;
118
119         if (unmapped > 0)
120                 trace_msm_gem_purge_vmaps(unmapped);
121
122         return NOTIFY_DONE;
123 }
124
125 /**
126  * msm_gem_shrinker_init - Initialize msm shrinker
127  * @dev: drm device
128  *
129  * This function registers and sets up the msm shrinker.
130  */
131 void msm_gem_shrinker_init(struct drm_device *dev)
132 {
133         struct msm_drm_private *priv = dev->dev_private;
134         priv->shrinker.count_objects = msm_gem_shrinker_count;
135         priv->shrinker.scan_objects = msm_gem_shrinker_scan;
136         priv->shrinker.seeks = DEFAULT_SEEKS;
137         WARN_ON(register_shrinker(&priv->shrinker));
138
139         priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap;
140         WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier));
141 }
142
143 /**
144  * msm_gem_shrinker_cleanup - Clean up msm shrinker
145  * @dev: drm device
146  *
147  * This function unregisters the msm shrinker.
148  */
149 void msm_gem_shrinker_cleanup(struct drm_device *dev)
150 {
151         struct msm_drm_private *priv = dev->dev_private;
152
153         if (priv->shrinker.nr_deferred) {
154                 WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier));
155                 unregister_shrinker(&priv->shrinker);
156         }
157 }
This page took 0.043026 seconds and 4 git commands to generate.