ion_page_pool.c revision 797a95c48c48d5b83bf1a7893602161d2e2a4654
1/* 2 * drivers/staging/android/ion/ion_mem_pool.c 3 * 4 * Copyright (C) 2011 Google, Inc. 5 * 6 * This software is licensed under the terms of the GNU General Public 7 * License version 2, as published by the Free Software Foundation, and 8 * may be copied, distributed, and modified under those terms. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 */ 16 17#include <linux/debugfs.h> 18#include <linux/dma-mapping.h> 19#include <linux/err.h> 20#include <linux/fs.h> 21#include <linux/list.h> 22#include <linux/module.h> 23#include <linux/slab.h> 24#include <linux/shrinker.h> 25#include "ion_priv.h" 26 27/* #define DEBUG_PAGE_POOL_SHRINKER */ 28 29static struct plist_head pools = PLIST_HEAD_INIT(pools); 30static struct shrinker shrinker; 31 32struct ion_page_pool_item { 33 struct page *page; 34 struct list_head list; 35}; 36 37static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) 38{ 39 struct page *page = alloc_pages(pool->gfp_mask, pool->order); 40 41 if (!page) 42 return NULL; 43 /* this is only being used to flush the page for dma, 44 this api is not really suitable for calling from a driver 45 but no better way to flush a page for dma exist at this time */ 46 __dma_page_cpu_to_dev(page, 0, PAGE_SIZE << pool->order, 47 DMA_BIDIRECTIONAL); 48 return page; 49} 50 51static void ion_page_pool_free_pages(struct ion_page_pool *pool, 52 struct page *page) 53{ 54 __free_pages(page, pool->order); 55} 56 57static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) 58{ 59 struct ion_page_pool_item *item; 60 61 item = kmalloc(sizeof(struct ion_page_pool_item), GFP_KERNEL); 62 if (!item) 63 return -ENOMEM; 64 65 mutex_lock(&pool->mutex); 66 item->page = page; 67 if (PageHighMem(page)) { 68 list_add_tail(&item->list, &pool->high_items); 69 pool->high_count++; 70 } else { 71 list_add_tail(&item->list, &pool->low_items); 72 pool->low_count++; 73 } 74 mutex_unlock(&pool->mutex); 75 return 0; 76} 77 78static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) 79{ 80 struct ion_page_pool_item *item; 81 struct page *page; 82 83 if (high) { 84 BUG_ON(!pool->high_count); 85 item = list_first_entry(&pool->high_items, 86 struct ion_page_pool_item, list); 87 pool->high_count--; 88 } else { 89 BUG_ON(!pool->low_count); 90 item = list_first_entry(&pool->low_items, 91 struct ion_page_pool_item, list); 92 pool->low_count--; 93 } 94 95 list_del(&item->list); 96 page = item->page; 97 kfree(item); 98 return page; 99} 100 101void *ion_page_pool_alloc(struct ion_page_pool *pool) 102{ 103 struct page *page = NULL; 104 105 BUG_ON(!pool); 106 107 mutex_lock(&pool->mutex); 108 if (pool->high_count) 109 page = ion_page_pool_remove(pool, true); 110 else if (pool->low_count) 111 page = ion_page_pool_remove(pool, false); 112 mutex_unlock(&pool->mutex); 113 114 if (!page) 115 page = ion_page_pool_alloc_pages(pool); 116 117 return page; 118} 119 120void ion_page_pool_free(struct ion_page_pool *pool, struct page* page) 121{ 122 int ret; 123 124 ret = ion_page_pool_add(pool, page); 125 if (ret) 126 ion_page_pool_free_pages(pool, page); 127} 128 129#ifdef DEBUG_PAGE_POOL_SHRINKER 130static int debug_drop_pools_set(void *data, u64 val) 131{ 132 struct shrink_control sc; 133 int objs; 134 135 sc.gfp_mask = -1; 136 sc.nr_to_scan = 0; 137 138 if (!val) 139 return 0; 140 141 objs = shrinker.shrink(&shrinker, &sc); 142 sc.nr_to_scan = objs; 143 144 shrinker.shrink(&shrinker, &sc); 145 return 0; 146} 147 148static int debug_drop_pools_get(void *data, u64 *val) 149{ 150 struct shrink_control sc; 151 int objs; 152 153 sc.gfp_mask = -1; 154 sc.nr_to_scan = 0; 155 156 objs = shrinker.shrink(&shrinker, &sc); 157 *val = objs; 158 return 0; 159} 160 161DEFINE_SIMPLE_ATTRIBUTE(debug_drop_pools_fops, debug_drop_pools_get, 162 debug_drop_pools_set, "%llu\n"); 163 164static int debug_grow_pools_set(void *data, u64 val) 165{ 166 struct ion_page_pool *pool; 167 struct page *page; 168 169 plist_for_each_entry(pool, &pools, list) { 170 if (val != pool->list.prio) 171 continue; 172 page = ion_page_pool_alloc_pages(pool); 173 if (page) 174 ion_page_pool_add(pool, page); 175 } 176 177 return 0; 178} 179 180DEFINE_SIMPLE_ATTRIBUTE(debug_grow_pools_fops, debug_drop_pools_get, 181 debug_grow_pools_set, "%llu\n"); 182#endif 183 184static int ion_page_pool_total(bool high) 185{ 186 struct ion_page_pool *pool; 187 int total = 0; 188 189 plist_for_each_entry(pool, &pools, list) { 190 total += high ? (pool->high_count + pool->low_count) * 191 (1 << pool->order) : 192 pool->low_count * (1 << pool->order); 193 } 194 return total; 195} 196 197static int ion_page_pool_shrink(struct shrinker *shrinker, 198 struct shrink_control *sc) 199{ 200 struct ion_page_pool *pool; 201 int nr_freed = 0; 202 int i; 203 bool high; 204 int nr_to_scan = sc->nr_to_scan; 205 206 if (sc->gfp_mask & __GFP_HIGHMEM) 207 high = true; 208 209 if (nr_to_scan == 0) 210 return ion_page_pool_total(high); 211 212 plist_for_each_entry(pool, &pools, list) { 213 for (i = 0; i < nr_to_scan; i++) { 214 struct page *page; 215 216 mutex_lock(&pool->mutex); 217 if (high && pool->high_count) { 218 page = ion_page_pool_remove(pool, true); 219 } else if (pool->low_count) { 220 page = ion_page_pool_remove(pool, false); 221 } else { 222 mutex_unlock(&pool->mutex); 223 break; 224 } 225 mutex_unlock(&pool->mutex); 226 ion_page_pool_free_pages(pool, page); 227 nr_freed += (1 << pool->order); 228 } 229 nr_to_scan -= i; 230 } 231 232 return ion_page_pool_total(high); 233} 234 235struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order) 236{ 237 struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool), 238 GFP_KERNEL); 239 if (!pool) 240 return NULL; 241 pool->high_count = 0; 242 pool->low_count = 0; 243 INIT_LIST_HEAD(&pool->low_items); 244 INIT_LIST_HEAD(&pool->high_items); 245 pool->gfp_mask = gfp_mask; 246 pool->order = order; 247 mutex_init(&pool->mutex); 248 plist_node_init(&pool->list, order); 249 plist_add(&pool->list, &pools); 250 251 return pool; 252} 253 254void ion_page_pool_destroy(struct ion_page_pool *pool) 255{ 256 plist_del(&pool->list, &pools); 257 kfree(pool); 258} 259 260static int __init ion_page_pool_init(void) 261{ 262 shrinker.shrink = ion_page_pool_shrink; 263 shrinker.seeks = DEFAULT_SEEKS; 264 shrinker.batch = 0; 265 register_shrinker(&shrinker); 266#ifdef DEBUG_PAGE_POOL_SHRINKER 267 debugfs_create_file("ion_pools_shrink", 0644, NULL, NULL, 268 &debug_drop_pools_fops); 269 debugfs_create_file("ion_pools_grow", 0644, NULL, NULL, 270 &debug_grow_pools_fops); 271#endif 272 return 0; 273} 274 275static void __exit ion_page_pool_exit(void) 276{ 277 unregister_shrinker(&shrinker); 278} 279 280module_init(ion_page_pool_init); 281module_exit(ion_page_pool_exit); 282