aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2016-05-17 16:19:32 -0400
committerRob Clark <robdclark@gmail.com>2016-07-16 10:09:06 -0400
commit68209390f116034449fa6a3ae03f7b100b3d894a (patch)
tree00fae111275f1785a910792c21261b6d52876321
parent4fe5f65e66823dcb212a0404af47389b2b1c58f0 (diff)
drm/msm: shrinker support
For a first step, only purge obj->madv==DONTNEED objects. We could be more agressive and next try unpinning inactive objects.. but that is only useful if you have swap. Signed-off-by: Rob Clark <robdclark@gmail.com>
-rw-r--r--drivers/gpu/drm/msm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c5
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h8
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c32
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h6
-rw-r--r--drivers/gpu/drm/msm/msm_gem_shrinker.c128
6 files changed, 180 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 4727d045f179..4e2806cf778c 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -46,6 +46,7 @@ msm-y := \
46 msm_fence.o \ 46 msm_fence.o \
47 msm_gem.o \ 47 msm_gem.o \
48 msm_gem_prime.o \ 48 msm_gem_prime.o \
49 msm_gem_shrinker.o \
49 msm_gem_submit.o \ 50 msm_gem_submit.o \
50 msm_gpu.o \ 51 msm_gpu.o \
51 msm_iommu.o \ 52 msm_iommu.o \
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 00881f3ed32e..f3b8f69ea9ae 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -195,6 +195,8 @@ static int msm_drm_uninit(struct device *dev)
195 kfree(vbl_ev); 195 kfree(vbl_ev);
196 } 196 }
197 197
198 msm_gem_shrinker_cleanup(ddev);
199
198 drm_kms_helper_poll_fini(ddev); 200 drm_kms_helper_poll_fini(ddev);
199 201
200 drm_dev_unregister(ddev); 202 drm_dev_unregister(ddev);
@@ -350,6 +352,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
350 } 352 }
351 353
352 ddev->dev_private = priv; 354 ddev->dev_private = priv;
355 priv->dev = ddev;
353 356
354 ret = msm_mdss_init(ddev); 357 ret = msm_mdss_init(ddev);
355 if (ret) { 358 if (ret) {
@@ -382,6 +385,8 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
382 if (ret) 385 if (ret)
383 goto fail; 386 goto fail;
384 387
388 msm_gem_shrinker_init(ddev);
389
385 switch (get_mdp_ver(pdev)) { 390 switch (get_mdp_ver(pdev)) {
386 case 4: 391 case 4:
387 kms = mdp4_kms_init(ddev); 392 kms = mdp4_kms_init(ddev);
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index a49d7fd67ec3..4755894d9229 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -78,6 +78,8 @@ struct msm_vblank_ctrl {
78 78
79struct msm_drm_private { 79struct msm_drm_private {
80 80
81 struct drm_device *dev;
82
81 struct msm_kms *kms; 83 struct msm_kms *kms;
82 84
83 /* subordinate devices, if present: */ 85 /* subordinate devices, if present: */
@@ -151,6 +153,8 @@ struct msm_drm_private {
151 struct drm_mm mm; 153 struct drm_mm mm;
152 } vram; 154 } vram;
153 155
156 struct shrinker shrinker;
157
154 struct msm_vblank_ctrl vblank_ctrl; 158 struct msm_vblank_ctrl vblank_ctrl;
155}; 159};
156 160
@@ -169,6 +173,9 @@ void msm_gem_submit_free(struct msm_gem_submit *submit);
169int msm_ioctl_gem_submit(struct drm_device *dev, void *data, 173int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
170 struct drm_file *file); 174 struct drm_file *file);
171 175
176void msm_gem_shrinker_init(struct drm_device *dev);
177void msm_gem_shrinker_cleanup(struct drm_device *dev);
178
172int msm_gem_mmap_obj(struct drm_gem_object *obj, 179int msm_gem_mmap_obj(struct drm_gem_object *obj,
173 struct vm_area_struct *vma); 180 struct vm_area_struct *vma);
174int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); 181int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
@@ -196,6 +203,7 @@ void msm_gem_prime_unpin(struct drm_gem_object *obj);
196void *msm_gem_vaddr_locked(struct drm_gem_object *obj); 203void *msm_gem_vaddr_locked(struct drm_gem_object *obj);
197void *msm_gem_vaddr(struct drm_gem_object *obj); 204void *msm_gem_vaddr(struct drm_gem_object *obj);
198int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv); 205int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv);
206void msm_gem_purge(struct drm_gem_object *obj);
199int msm_gem_sync_object(struct drm_gem_object *obj, 207int msm_gem_sync_object(struct drm_gem_object *obj,
200 struct msm_fence_context *fctx, bool exclusive); 208 struct msm_fence_context *fctx, bool exclusive);
201void msm_gem_move_to_active(struct drm_gem_object *obj, 209void msm_gem_move_to_active(struct drm_gem_object *obj,
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 2636c279d504..444d0b5680f5 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -448,6 +448,38 @@ int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv)
448 return (msm_obj->madv != __MSM_MADV_PURGED); 448 return (msm_obj->madv != __MSM_MADV_PURGED);
449} 449}
450 450
451void msm_gem_purge(struct drm_gem_object *obj)
452{
453 struct drm_device *dev = obj->dev;
454 struct msm_gem_object *msm_obj = to_msm_bo(obj);
455
456 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
457 WARN_ON(!is_purgeable(msm_obj));
458 WARN_ON(obj->import_attach);
459
460 put_iova(obj);
461
462 vunmap(msm_obj->vaddr);
463 msm_obj->vaddr = NULL;
464
465 put_pages(obj);
466
467 msm_obj->madv = __MSM_MADV_PURGED;
468
469 drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
470 drm_gem_free_mmap_offset(obj);
471
472 /* Our goal here is to return as much of the memory as
473 * is possible back to the system as we are called from OOM.
474 * To do this we must instruct the shmfs to drop all of its
475 * backing pages, *now*.
476 */
477 shmem_truncate_range(file_inode(obj->filp), 0, (loff_t)-1);
478
479 invalidate_mapping_pages(file_inode(obj->filp)->i_mapping,
480 0, (loff_t)-1);
481}
482
451/* must be called before _move_to_active().. */ 483/* must be called before _move_to_active().. */
452int msm_gem_sync_object(struct drm_gem_object *obj, 484int msm_gem_sync_object(struct drm_gem_object *obj,
453 struct msm_fence_context *fctx, bool exclusive) 485 struct msm_fence_context *fctx, bool exclusive)
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index fa8e1f16f18e..631dab56b8fd 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -77,6 +77,12 @@ static inline bool is_active(struct msm_gem_object *msm_obj)
77 return msm_obj->gpu != NULL; 77 return msm_obj->gpu != NULL;
78} 78}
79 79
80static inline bool is_purgeable(struct msm_gem_object *msm_obj)
81{
82 return (msm_obj->madv == MSM_MADV_DONTNEED) && msm_obj->sgt &&
83 !msm_obj->base.dma_buf && !msm_obj->base.import_attach;
84}
85
80#define MAX_CMDS 4 86#define MAX_CMDS 4
81 87
82/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, 88/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c
new file mode 100644
index 000000000000..70fba9baddde
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c
@@ -0,0 +1,128 @@
1/*
2 * Copyright (C) 2016 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19#include "msm_gem.h"
20
21static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
22{
23 if (!mutex_is_locked(mutex))
24 return false;
25
26#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES)
27 return mutex->owner == task;
28#else
29 /* Since UP may be pre-empted, we cannot assume that we own the lock */
30 return false;
31#endif
32}
33
34static bool msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
35{
36 if (!mutex_trylock(&dev->struct_mutex)) {
37 if (!mutex_is_locked_by(&dev->struct_mutex, current))
38 return false;
39 *unlock = false;
40 } else {
41 *unlock = true;
42 }
43
44 return true;
45}
46
47
48static unsigned long
49msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
50{
51 struct msm_drm_private *priv =
52 container_of(shrinker, struct msm_drm_private, shrinker);
53 struct drm_device *dev = priv->dev;
54 struct msm_gem_object *msm_obj;
55 unsigned long count = 0;
56 bool unlock;
57
58 if (!msm_gem_shrinker_lock(dev, &unlock))
59 return 0;
60
61 list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) {
62 if (is_purgeable(msm_obj))
63 count += msm_obj->base.size >> PAGE_SHIFT;
64 }
65
66 if (unlock)
67 mutex_unlock(&dev->struct_mutex);
68
69 return count;
70}
71
72static unsigned long
73msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
74{
75 struct msm_drm_private *priv =
76 container_of(shrinker, struct msm_drm_private, shrinker);
77 struct drm_device *dev = priv->dev;
78 struct msm_gem_object *msm_obj;
79 unsigned long freed = 0;
80 bool unlock;
81
82 if (!msm_gem_shrinker_lock(dev, &unlock))
83 return SHRINK_STOP;
84
85 list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) {
86 if (freed >= sc->nr_to_scan)
87 break;
88 if (is_purgeable(msm_obj)) {
89 msm_gem_purge(&msm_obj->base);
90 freed += msm_obj->base.size >> PAGE_SHIFT;
91 }
92 }
93
94 if (unlock)
95 mutex_unlock(&dev->struct_mutex);
96
97 if (freed > 0)
98 pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT);
99
100 return freed;
101}
102
103/**
104 * msm_gem_shrinker_init - Initialize msm shrinker
105 * @dev_priv: msm device
106 *
107 * This function registers and sets up the msm shrinker.
108 */
109void msm_gem_shrinker_init(struct drm_device *dev)
110{
111 struct msm_drm_private *priv = dev->dev_private;
112 priv->shrinker.count_objects = msm_gem_shrinker_count;
113 priv->shrinker.scan_objects = msm_gem_shrinker_scan;
114 priv->shrinker.seeks = DEFAULT_SEEKS;
115 WARN_ON(register_shrinker(&priv->shrinker));
116}
117
118/**
119 * msm_gem_shrinker_cleanup - Clean up msm shrinker
120 * @dev_priv: msm device
121 *
122 * This function unregisters the msm shrinker.
123 */
124void msm_gem_shrinker_cleanup(struct drm_device *dev)
125{
126 struct msm_drm_private *priv = dev->dev_private;
127 unregister_shrinker(&priv->shrinker);
128}