ion_page_pool.c revision 80cb77dc6310a72002f6cb8255316c5aaea78807
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 "ion_priv.h" 25 26struct ion_page_pool_item { 27 struct page *page; 28 struct list_head list; 29}; 30 31static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) 32{ 33 struct page *page = alloc_pages(pool->gfp_mask, pool->order); 34 35 if (!page) 36 return NULL; 37 ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order, 38 DMA_BIDIRECTIONAL); 39 return page; 40} 41 42static void ion_page_pool_free_pages(struct ion_page_pool *pool, 43 struct page *page) 44{ 45 __free_pages(page, pool->order); 46} 47 48static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) 49{ 50 struct ion_page_pool_item *item; 51 52 item = kmalloc(sizeof(struct ion_page_pool_item), GFP_KERNEL); 53 if (!item) 54 return -ENOMEM; 55 56 mutex_lock(&pool->mutex); 57 item->page = page; 58 if (PageHighMem(page)) { 59 list_add_tail(&item->list, &pool->high_items); 60 pool->high_count++; 61 } else { 62 list_add_tail(&item->list, &pool->low_items); 63 pool->low_count++; 64 } 65 mutex_unlock(&pool->mutex); 66 return 0; 67} 68 69static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) 70{ 71 struct ion_page_pool_item *item; 72 struct page *page; 73 74 if (high) { 75 BUG_ON(!pool->high_count); 76 item = list_first_entry(&pool->high_items, 77 struct ion_page_pool_item, list); 78 pool->high_count--; 79 } else { 80 BUG_ON(!pool->low_count); 81 item = list_first_entry(&pool->low_items, 82 struct ion_page_pool_item, list); 83 pool->low_count--; 84 } 85 86 list_del(&item->list); 87 page = item->page; 88 kfree(item); 89 return page; 90} 91 92struct page *ion_page_pool_alloc(struct ion_page_pool *pool) 93{ 94 struct page *page = NULL; 95 96 BUG_ON(!pool); 97 98 mutex_lock(&pool->mutex); 99 if (pool->high_count) 100 page = ion_page_pool_remove(pool, true); 101 else if (pool->low_count) 102 page = ion_page_pool_remove(pool, false); 103 mutex_unlock(&pool->mutex); 104 105 if (!page) 106 page = ion_page_pool_alloc_pages(pool); 107 108 return page; 109} 110 111void ion_page_pool_free(struct ion_page_pool *pool, struct page *page) 112{ 113 int ret; 114 115 ret = ion_page_pool_add(pool, page); 116 if (ret) 117 ion_page_pool_free_pages(pool, page); 118} 119 120static int ion_page_pool_total(struct ion_page_pool *pool, bool high) 121{ 122 int count = pool->low_count; 123 124 if (high) 125 count += pool->high_count; 126 127 return count << pool->order; 128} 129 130int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, 131 int nr_to_scan) 132{ 133 int freed; 134 bool high; 135 136 high = !!(gfp_mask & __GFP_HIGHMEM); 137 138 if (nr_to_scan == 0) 139 return ion_page_pool_total(pool, high); 140 141 for (freed = 0; freed < nr_to_scan; freed++) { 142 struct page *page; 143 144 mutex_lock(&pool->mutex); 145 if (pool->low_count) { 146 page = ion_page_pool_remove(pool, false); 147 } else if (high && pool->high_count) { 148 page = ion_page_pool_remove(pool, true); 149 } else { 150 mutex_unlock(&pool->mutex); 151 break; 152 } 153 mutex_unlock(&pool->mutex); 154 ion_page_pool_free_pages(pool, page); 155 } 156 157 return freed; 158} 159 160struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order) 161{ 162 struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool), 163 GFP_KERNEL); 164 if (!pool) 165 return NULL; 166 pool->high_count = 0; 167 pool->low_count = 0; 168 INIT_LIST_HEAD(&pool->low_items); 169 INIT_LIST_HEAD(&pool->high_items); 170 pool->gfp_mask = gfp_mask; 171 pool->order = order; 172 mutex_init(&pool->mutex); 173 plist_node_init(&pool->list, order); 174 175 return pool; 176} 177 178void ion_page_pool_destroy(struct ion_page_pool *pool) 179{ 180 kfree(pool); 181} 182 183static int __init ion_page_pool_init(void) 184{ 185 return 0; 186} 187 188static void __exit ion_page_pool_exit(void) 189{ 190} 191 192module_init(ion_page_pool_init); 193module_exit(ion_page_pool_exit); 194