Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
12static unsigned long
13msm_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
35static unsigned long
36msm_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 */
69static const int vmap_shrink_limit = 15;
70
71static unsigned
72vmap_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
93static int
94msm_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 */
131void 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 */
149void 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}