1
2
3
4
5
6
7
8#include <linux/list.h>
9#include <linux/slab.h>
10#include <linux/swap.h>
11
12#include "ion.h"
13
14static inline struct page *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
15{
16 return alloc_pages(pool->gfp_mask, pool->order);
17}
18
19static void ion_page_pool_free_pages(struct ion_page_pool *pool,
20 struct page *page)
21{
22 __free_pages(page, pool->order);
23}
24
25static void ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
26{
27 mutex_lock(&pool->mutex);
28 if (PageHighMem(page)) {
29 list_add_tail(&page->lru, &pool->high_items);
30 pool->high_count++;
31 } else {
32 list_add_tail(&page->lru, &pool->low_items);
33 pool->low_count++;
34 }
35 mutex_unlock(&pool->mutex);
36}
37
38static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
39{
40 struct page *page;
41
42 if (high) {
43 BUG_ON(!pool->high_count);
44 page = list_first_entry(&pool->high_items, struct page, lru);
45 pool->high_count--;
46 } else {
47 BUG_ON(!pool->low_count);
48 page = list_first_entry(&pool->low_items, struct page, lru);
49 pool->low_count--;
50 }
51
52 list_del(&page->lru);
53 return page;
54}
55
56struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
57{
58 struct page *page = NULL;
59
60 BUG_ON(!pool);
61
62 mutex_lock(&pool->mutex);
63 if (pool->high_count)
64 page = ion_page_pool_remove(pool, true);
65 else if (pool->low_count)
66 page = ion_page_pool_remove(pool, false);
67 mutex_unlock(&pool->mutex);
68
69 if (!page)
70 page = ion_page_pool_alloc_pages(pool);
71
72 return page;
73}
74
75void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
76{
77 BUG_ON(pool->order != compound_order(page));
78
79 ion_page_pool_add(pool, page);
80}
81
82static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
83{
84 int count = pool->low_count;
85
86 if (high)
87 count += pool->high_count;
88
89 return count << pool->order;
90}
91
92int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
93 int nr_to_scan)
94{
95 int freed = 0;
96 bool high;
97
98 if (current_is_kswapd())
99 high = true;
100 else
101 high = !!(gfp_mask & __GFP_HIGHMEM);
102
103 if (nr_to_scan == 0)
104 return ion_page_pool_total(pool, high);
105
106 while (freed < nr_to_scan) {
107 struct page *page;
108
109 mutex_lock(&pool->mutex);
110 if (pool->low_count) {
111 page = ion_page_pool_remove(pool, false);
112 } else if (high && pool->high_count) {
113 page = ion_page_pool_remove(pool, true);
114 } else {
115 mutex_unlock(&pool->mutex);
116 break;
117 }
118 mutex_unlock(&pool->mutex);
119 ion_page_pool_free_pages(pool, page);
120 freed += (1 << pool->order);
121 }
122
123 return freed;
124}
125
126struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
127{
128 struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
129
130 if (!pool)
131 return NULL;
132 pool->high_count = 0;
133 pool->low_count = 0;
134 INIT_LIST_HEAD(&pool->low_items);
135 INIT_LIST_HEAD(&pool->high_items);
136 pool->gfp_mask = gfp_mask | __GFP_COMP;
137 pool->order = order;
138 mutex_init(&pool->mutex);
139 plist_node_init(&pool->list, order);
140
141 return pool;
142}
143
144void ion_page_pool_destroy(struct ion_page_pool *pool)
145{
146 kfree(pool);
147}
148