diff options
author | Rob Clark <robdclark@gmail.com> | 2016-05-17 16:19:32 -0400 |
---|---|---|
committer | Rob Clark <robdclark@gmail.com> | 2016-07-16 10:09:06 -0400 |
commit | 68209390f116034449fa6a3ae03f7b100b3d894a (patch) | |
tree | 00fae111275f1785a910792c21261b6d52876321 | |
parent | 4fe5f65e66823dcb212a0404af47389b2b1c58f0 (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/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.c | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem_shrinker.c | 128 |
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 | ||
79 | struct msm_drm_private { | 79 | struct 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); | |||
169 | int msm_ioctl_gem_submit(struct drm_device *dev, void *data, | 173 | int msm_ioctl_gem_submit(struct drm_device *dev, void *data, |
170 | struct drm_file *file); | 174 | struct drm_file *file); |
171 | 175 | ||
176 | void msm_gem_shrinker_init(struct drm_device *dev); | ||
177 | void msm_gem_shrinker_cleanup(struct drm_device *dev); | ||
178 | |||
172 | int msm_gem_mmap_obj(struct drm_gem_object *obj, | 179 | int msm_gem_mmap_obj(struct drm_gem_object *obj, |
173 | struct vm_area_struct *vma); | 180 | struct vm_area_struct *vma); |
174 | int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | 181 | int 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); | |||
196 | void *msm_gem_vaddr_locked(struct drm_gem_object *obj); | 203 | void *msm_gem_vaddr_locked(struct drm_gem_object *obj); |
197 | void *msm_gem_vaddr(struct drm_gem_object *obj); | 204 | void *msm_gem_vaddr(struct drm_gem_object *obj); |
198 | int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv); | 205 | int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv); |
206 | void msm_gem_purge(struct drm_gem_object *obj); | ||
199 | int msm_gem_sync_object(struct drm_gem_object *obj, | 207 | int 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); |
201 | void msm_gem_move_to_active(struct drm_gem_object *obj, | 209 | void 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 | ||
451 | void 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().. */ |
452 | int msm_gem_sync_object(struct drm_gem_object *obj, | 484 | int 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 | ||
80 | static 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 | |||
21 | static 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 | |||
34 | static 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 | |||
48 | static unsigned long | ||
49 | msm_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 | |||
72 | static unsigned long | ||
73 | msm_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 | */ | ||
109 | void 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 | */ | ||
124 | void msm_gem_shrinker_cleanup(struct drm_device *dev) | ||
125 | { | ||
126 | struct msm_drm_private *priv = dev->dev_private; | ||
127 | unregister_shrinker(&priv->shrinker); | ||
128 | } | ||