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 msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock) 22 { 23 /* NOTE: we are *closer* to being able to get rid of 24 * mutex_trylock_recursive().. the msm_gem code itself does 25 * not need struct_mutex, although codepaths that can trigger 26 * shrinker are still called in code-paths that hold the 27 * struct_mutex. 28 * 29 * Also, msm_obj->madv is protected by struct_mutex. 30 * 31 * The next step is probably split out a seperate lock for 32 * protecting inactive_list, so that shrinker does not need 33 * struct_mutex. 34 */ 35 switch (mutex_trylock_recursive(&dev->struct_mutex)) { 36 case MUTEX_TRYLOCK_FAILED: 37 return false; 38 39 case MUTEX_TRYLOCK_SUCCESS: 40 *unlock = true; 41 return true; 42 43 case MUTEX_TRYLOCK_RECURSIVE: 44 *unlock = false; 45 return true; 46 } 47 48 BUG(); 49 } 50 51 static unsigned long 52 msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) 53 { 54 struct msm_drm_private *priv = 55 container_of(shrinker, struct msm_drm_private, shrinker); 56 struct drm_device *dev = priv->dev; 57 struct msm_gem_object *msm_obj; 58 unsigned long count = 0; 59 bool unlock; 60 61 if (!msm_gem_shrinker_lock(dev, &unlock)) 62 return 0; 63 64 list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { 65 if (is_purgeable(msm_obj)) 66 count += msm_obj->base.size >> PAGE_SHIFT; 67 } 68 69 if (unlock) 70 mutex_unlock(&dev->struct_mutex); 71 72 return count; 73 } 74 75 static unsigned long 76 msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) 77 { 78 struct msm_drm_private *priv = 79 container_of(shrinker, struct msm_drm_private, shrinker); 80 struct drm_device *dev = priv->dev; 81 struct msm_gem_object *msm_obj; 82 unsigned long freed = 0; 83 bool unlock; 84 85 if (!msm_gem_shrinker_lock(dev, &unlock)) 86 return SHRINK_STOP; 87 88 list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { 89 if (freed >= sc->nr_to_scan) 90 break; 91 if (is_purgeable(msm_obj)) { 92 msm_gem_purge(&msm_obj->base, OBJ_LOCK_SHRINKER); 93 freed += msm_obj->base.size >> PAGE_SHIFT; 94 } 95 } 96 97 if (unlock) 98 mutex_unlock(&dev->struct_mutex); 99 100 if (freed > 0) 101 pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT); 102 103 return freed; 104 } 105 106 static int 107 msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr) 108 { 109 struct msm_drm_private *priv = 110 container_of(nb, struct msm_drm_private, vmap_notifier); 111 struct drm_device *dev = priv->dev; 112 struct msm_gem_object *msm_obj; 113 unsigned unmapped = 0; 114 bool unlock; 115 116 if (!msm_gem_shrinker_lock(dev, &unlock)) 117 return NOTIFY_DONE; 118 119 list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { 120 if (is_vunmapable(msm_obj)) { 121 msm_gem_vunmap(&msm_obj->base, OBJ_LOCK_SHRINKER); 122 /* since we don't know any better, lets bail after a few 123 * and if necessary the shrinker will be invoked again. 124 * Seems better than unmapping *everything* 125 */ 126 if (++unmapped >= 15) 127 break; 128 } 129 } 130 131 if (unlock) 132 mutex_unlock(&dev->struct_mutex); 133 134 *(unsigned long *)ptr += unmapped; 135 136 if (unmapped > 0) 137 pr_info_ratelimited("Purging %u vmaps\n", unmapped); 138 139 return NOTIFY_DONE; 140 } 141 142 /** 143 * msm_gem_shrinker_init - Initialize msm shrinker 144 * @dev_priv: msm device 145 * 146 * This function registers and sets up the msm shrinker. 147 */ 148 void msm_gem_shrinker_init(struct drm_device *dev) 149 { 150 struct msm_drm_private *priv = dev->dev_private; 151 priv->shrinker.count_objects = msm_gem_shrinker_count; 152 priv->shrinker.scan_objects = msm_gem_shrinker_scan; 153 priv->shrinker.seeks = DEFAULT_SEEKS; 154 WARN_ON(register_shrinker(&priv->shrinker)); 155 156 priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap; 157 WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier)); 158 } 159 160 /** 161 * msm_gem_shrinker_cleanup - Clean up msm shrinker 162 * @dev_priv: msm device 163 * 164 * This function unregisters the msm shrinker. 165 */ 166 void msm_gem_shrinker_cleanup(struct drm_device *dev) 167 { 168 struct msm_drm_private *priv = dev->dev_private; 169 170 if (priv->shrinker.nr_deferred) { 171 WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier)); 172 unregister_shrinker(&priv->shrinker); 173 } 174 } 175