]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - drivers/staging/android/ion/ion_page_pool.c
Merge branches 'pm-cpu', 'pm-cpuidle' and 'pm-domains'
[mirror_ubuntu-zesty-kernel.git] / drivers / staging / android / ion / ion_page_pool.c
CommitLineData
0214c7f2
RSZ
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
797a95c4 17#include <linux/debugfs.h>
0214c7f2
RSZ
18#include <linux/dma-mapping.h>
19#include <linux/err.h>
797a95c4 20#include <linux/fs.h>
0214c7f2 21#include <linux/list.h>
797a95c4 22#include <linux/module.h>
0214c7f2 23#include <linux/slab.h>
0cd2dc4d 24#include <linux/swap.h>
0214c7f2
RSZ
25#include "ion_priv.h"
26
0214c7f2
RSZ
27static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
28{
29 struct page *page = alloc_pages(pool->gfp_mask, pool->order);
30
31 if (!page)
32 return NULL;
e946b209
CC
33 ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order,
34 DMA_BIDIRECTIONAL);
0214c7f2
RSZ
35 return page;
36}
37
38static void ion_page_pool_free_pages(struct ion_page_pool *pool,
39 struct page *page)
40{
41 __free_pages(page, pool->order);
42}
43
44static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
45{
efee5a0c 46 mutex_lock(&pool->mutex);
0fb9b815 47 if (PageHighMem(page)) {
38c003b1 48 list_add_tail(&page->lru, &pool->high_items);
0fb9b815
RSZ
49 pool->high_count++;
50 } else {
38c003b1 51 list_add_tail(&page->lru, &pool->low_items);
0fb9b815
RSZ
52 pool->low_count++;
53 }
efee5a0c 54 mutex_unlock(&pool->mutex);
0214c7f2
RSZ
55 return 0;
56}
57
0fb9b815 58static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
0214c7f2 59{
0214c7f2
RSZ
60 struct page *page;
61
0fb9b815
RSZ
62 if (high) {
63 BUG_ON(!pool->high_count);
38c003b1 64 page = list_first_entry(&pool->high_items, struct page, lru);
0fb9b815
RSZ
65 pool->high_count--;
66 } else {
67 BUG_ON(!pool->low_count);
38c003b1 68 page = list_first_entry(&pool->low_items, struct page, lru);
0fb9b815
RSZ
69 pool->low_count--;
70 }
0214c7f2 71
38c003b1 72 list_del(&page->lru);
0214c7f2
RSZ
73 return page;
74}
75
79240748 76struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
0214c7f2
RSZ
77{
78 struct page *page = NULL;
79
80 BUG_ON(!pool);
81
82 mutex_lock(&pool->mutex);
0fb9b815
RSZ
83 if (pool->high_count)
84 page = ion_page_pool_remove(pool, true);
85 else if (pool->low_count)
86 page = ion_page_pool_remove(pool, false);
0214c7f2
RSZ
87 mutex_unlock(&pool->mutex);
88
0fb9b815
RSZ
89 if (!page)
90 page = ion_page_pool_alloc_pages(pool);
91
0214c7f2
RSZ
92 return page;
93}
94
e1d855b0 95void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
0214c7f2
RSZ
96{
97 int ret;
98
bdeb9f1c
HS
99 BUG_ON(pool->order != compound_order(page));
100
0214c7f2
RSZ
101 ret = ion_page_pool_add(pool, page);
102 if (ret)
103 ion_page_pool_free_pages(pool, page);
0214c7f2
RSZ
104}
105
ea313b5f 106static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
797a95c4 107{
80cb77dc 108 int count = pool->low_count;
797a95c4 109
80cb77dc
HS
110 if (high)
111 count += pool->high_count;
112
113 return count << pool->order;
797a95c4
RSZ
114}
115
ea313b5f
RSZ
116int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
117 int nr_to_scan)
0214c7f2 118{
b44d9ce3 119 int freed = 0;
0fb9b815
RSZ
120 bool high;
121
0cd2dc4d 122 if (current_is_kswapd())
17fbab1e 123 high = true;
0cd2dc4d
HS
124 else
125 high = !!(gfp_mask & __GFP_HIGHMEM);
0214c7f2 126
797a95c4 127 if (nr_to_scan == 0)
ea313b5f
RSZ
128 return ion_page_pool_total(pool, high);
129
b44d9ce3 130 while (freed < nr_to_scan) {
ea313b5f
RSZ
131 struct page *page;
132
133 mutex_lock(&pool->mutex);
ce3d1093 134 if (pool->low_count) {
ea313b5f 135 page = ion_page_pool_remove(pool, false);
ce3d1093
CC
136 } else if (high && pool->high_count) {
137 page = ion_page_pool_remove(pool, true);
ea313b5f 138 } else {
0fb9b815 139 mutex_unlock(&pool->mutex);
ea313b5f 140 break;
0214c7f2 141 }
ea313b5f
RSZ
142 mutex_unlock(&pool->mutex);
143 ion_page_pool_free_pages(pool, page);
b44d9ce3 144 freed += (1 << pool->order);
0214c7f2 145 }
0214c7f2 146
b9daf0b6 147 return freed;
0214c7f2
RSZ
148}
149
150struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
151{
152 struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool),
153 GFP_KERNEL);
154 if (!pool)
155 return NULL;
0fb9b815
RSZ
156 pool->high_count = 0;
157 pool->low_count = 0;
158 INIT_LIST_HEAD(&pool->low_items);
159 INIT_LIST_HEAD(&pool->high_items);
bdeb9f1c 160 pool->gfp_mask = gfp_mask | __GFP_COMP;
0214c7f2
RSZ
161 pool->order = order;
162 mutex_init(&pool->mutex);
797a95c4 163 plist_node_init(&pool->list, order);
0214c7f2
RSZ
164
165 return pool;
166}
167
168void ion_page_pool_destroy(struct ion_page_pool *pool)
169{
0214c7f2
RSZ
170 kfree(pool);
171}
172
797a95c4
RSZ
173static int __init ion_page_pool_init(void)
174{
797a95c4
RSZ
175 return 0;
176}
177
178static void __exit ion_page_pool_exit(void)
179{
797a95c4
RSZ
180}
181
182module_init(ion_page_pool_init);
183module_exit(ion_page_pool_exit);