]>
Commit | Line | Data |
---|---|---|
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> |
0214c7f2 RSZ |
24 | #include "ion_priv.h" |
25 | ||
26 | struct ion_page_pool_item { | |
27 | struct page *page; | |
28 | struct list_head list; | |
29 | }; | |
30 | ||
31 | static 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; | |
e946b209 CC |
37 | ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order, |
38 | DMA_BIDIRECTIONAL); | |
0214c7f2 RSZ |
39 | return page; |
40 | } | |
41 | ||
42 | static void ion_page_pool_free_pages(struct ion_page_pool *pool, | |
43 | struct page *page) | |
44 | { | |
45 | __free_pages(page, pool->order); | |
46 | } | |
47 | ||
48 | static 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; | |
efee5a0c RSZ |
55 | |
56 | mutex_lock(&pool->mutex); | |
0214c7f2 | 57 | item->page = page; |
0fb9b815 RSZ |
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 | } | |
efee5a0c | 65 | mutex_unlock(&pool->mutex); |
0214c7f2 RSZ |
66 | return 0; |
67 | } | |
68 | ||
0fb9b815 | 69 | static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) |
0214c7f2 RSZ |
70 | { |
71 | struct ion_page_pool_item *item; | |
72 | struct page *page; | |
73 | ||
0fb9b815 RSZ |
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 | } | |
0214c7f2 | 85 | |
0214c7f2 RSZ |
86 | list_del(&item->list); |
87 | page = item->page; | |
88 | kfree(item); | |
0214c7f2 RSZ |
89 | return page; |
90 | } | |
91 | ||
92 | void *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); | |
0fb9b815 RSZ |
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); | |
0214c7f2 RSZ |
103 | mutex_unlock(&pool->mutex); |
104 | ||
0fb9b815 RSZ |
105 | if (!page) |
106 | page = ion_page_pool_alloc_pages(pool); | |
107 | ||
0214c7f2 RSZ |
108 | return page; |
109 | } | |
110 | ||
111 | void ion_page_pool_free(struct ion_page_pool *pool, struct page* page) | |
112 | { | |
113 | int ret; | |
114 | ||
0214c7f2 RSZ |
115 | ret = ion_page_pool_add(pool, page); |
116 | if (ret) | |
117 | ion_page_pool_free_pages(pool, page); | |
0214c7f2 RSZ |
118 | } |
119 | ||
ea313b5f | 120 | static int ion_page_pool_total(struct ion_page_pool *pool, bool high) |
797a95c4 | 121 | { |
797a95c4 RSZ |
122 | int total = 0; |
123 | ||
ea313b5f RSZ |
124 | total += high ? (pool->high_count + pool->low_count) * |
125 | (1 << pool->order) : | |
797a95c4 | 126 | pool->low_count * (1 << pool->order); |
797a95c4 RSZ |
127 | return total; |
128 | } | |
129 | ||
ea313b5f RSZ |
130 | int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, |
131 | int nr_to_scan) | |
0214c7f2 | 132 | { |
0214c7f2 RSZ |
133 | int nr_freed = 0; |
134 | int i; | |
0fb9b815 RSZ |
135 | bool high; |
136 | ||
ea313b5f | 137 | high = gfp_mask & __GFP_HIGHMEM; |
0214c7f2 | 138 | |
797a95c4 | 139 | if (nr_to_scan == 0) |
ea313b5f RSZ |
140 | return ion_page_pool_total(pool, high); |
141 | ||
142 | for (i = 0; i < nr_to_scan; i++) { | |
143 | struct page *page; | |
144 | ||
145 | mutex_lock(&pool->mutex); | |
146 | if (high && pool->high_count) { | |
147 | page = ion_page_pool_remove(pool, true); | |
148 | } else if (pool->low_count) { | |
149 | page = ion_page_pool_remove(pool, false); | |
150 | } else { | |
0fb9b815 | 151 | mutex_unlock(&pool->mutex); |
ea313b5f | 152 | break; |
0214c7f2 | 153 | } |
ea313b5f RSZ |
154 | mutex_unlock(&pool->mutex); |
155 | ion_page_pool_free_pages(pool, page); | |
156 | nr_freed += (1 << pool->order); | |
0214c7f2 | 157 | } |
0214c7f2 | 158 | |
ea313b5f | 159 | return nr_freed; |
0214c7f2 RSZ |
160 | } |
161 | ||
162 | struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order) | |
163 | { | |
164 | struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool), | |
165 | GFP_KERNEL); | |
166 | if (!pool) | |
167 | return NULL; | |
0fb9b815 RSZ |
168 | pool->high_count = 0; |
169 | pool->low_count = 0; | |
170 | INIT_LIST_HEAD(&pool->low_items); | |
171 | INIT_LIST_HEAD(&pool->high_items); | |
0214c7f2 RSZ |
172 | pool->gfp_mask = gfp_mask; |
173 | pool->order = order; | |
174 | mutex_init(&pool->mutex); | |
797a95c4 | 175 | plist_node_init(&pool->list, order); |
0214c7f2 RSZ |
176 | |
177 | return pool; | |
178 | } | |
179 | ||
180 | void ion_page_pool_destroy(struct ion_page_pool *pool) | |
181 | { | |
0214c7f2 RSZ |
182 | kfree(pool); |
183 | } | |
184 | ||
797a95c4 RSZ |
185 | static int __init ion_page_pool_init(void) |
186 | { | |
797a95c4 RSZ |
187 | return 0; |
188 | } | |
189 | ||
190 | static void __exit ion_page_pool_exit(void) | |
191 | { | |
797a95c4 RSZ |
192 | } |
193 | ||
194 | module_init(ion_page_pool_init); | |
195 | module_exit(ion_page_pool_exit); |