GNU Linux-libre 4.14.332-gnu1
[releases.git] / drivers / staging / android / ion / ion_page_pool.c
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/init.h>
23 #include <linux/slab.h>
24 #include <linux/swap.h>
25
26 #include "ion.h"
27
28 static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
29 {
30         struct page *page = alloc_pages(pool->gfp_mask, pool->order);
31
32         if (!page)
33                 return NULL;
34         return page;
35 }
36
37 static void ion_page_pool_free_pages(struct ion_page_pool *pool,
38                                      struct page *page)
39 {
40         __free_pages(page, pool->order);
41 }
42
43 static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
44 {
45         mutex_lock(&pool->mutex);
46         if (PageHighMem(page)) {
47                 list_add_tail(&page->lru, &pool->high_items);
48                 pool->high_count++;
49         } else {
50                 list_add_tail(&page->lru, &pool->low_items);
51                 pool->low_count++;
52         }
53         mutex_unlock(&pool->mutex);
54         return 0;
55 }
56
57 static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
58 {
59         struct page *page;
60
61         if (high) {
62                 BUG_ON(!pool->high_count);
63                 page = list_first_entry(&pool->high_items, struct page, lru);
64                 pool->high_count--;
65         } else {
66                 BUG_ON(!pool->low_count);
67                 page = list_first_entry(&pool->low_items, struct page, lru);
68                 pool->low_count--;
69         }
70
71         list_del(&page->lru);
72         return page;
73 }
74
75 struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
76 {
77         struct page *page = NULL;
78
79         BUG_ON(!pool);
80
81         mutex_lock(&pool->mutex);
82         if (pool->high_count)
83                 page = ion_page_pool_remove(pool, true);
84         else if (pool->low_count)
85                 page = ion_page_pool_remove(pool, false);
86         mutex_unlock(&pool->mutex);
87
88         if (!page)
89                 page = ion_page_pool_alloc_pages(pool);
90
91         return page;
92 }
93
94 void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
95 {
96         int ret;
97
98         BUG_ON(pool->order != compound_order(page));
99
100         ret = ion_page_pool_add(pool, page);
101         if (ret)
102                 ion_page_pool_free_pages(pool, page);
103 }
104
105 static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
106 {
107         int count = pool->low_count;
108
109         if (high)
110                 count += pool->high_count;
111
112         return count << pool->order;
113 }
114
115 int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
116                          int nr_to_scan)
117 {
118         int freed = 0;
119         bool high;
120
121         if (current_is_kswapd())
122                 high = true;
123         else
124                 high = !!(gfp_mask & __GFP_HIGHMEM);
125
126         if (nr_to_scan == 0)
127                 return ion_page_pool_total(pool, high);
128
129         while (freed < nr_to_scan) {
130                 struct page *page;
131
132                 mutex_lock(&pool->mutex);
133                 if (pool->low_count) {
134                         page = ion_page_pool_remove(pool, false);
135                 } else if (high && pool->high_count) {
136                         page = ion_page_pool_remove(pool, true);
137                 } else {
138                         mutex_unlock(&pool->mutex);
139                         break;
140                 }
141                 mutex_unlock(&pool->mutex);
142                 ion_page_pool_free_pages(pool, page);
143                 freed += (1 << pool->order);
144         }
145
146         return freed;
147 }
148
149 struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
150                                            bool cached)
151 {
152         struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
153
154         if (!pool)
155                 return NULL;
156         pool->high_count = 0;
157         pool->low_count = 0;
158         INIT_LIST_HEAD(&pool->low_items);
159         INIT_LIST_HEAD(&pool->high_items);
160         pool->gfp_mask = gfp_mask | __GFP_COMP;
161         pool->order = order;
162         mutex_init(&pool->mutex);
163         plist_node_init(&pool->list, order);
164         if (cached)
165                 pool->cached = true;
166
167         return pool;
168 }
169
170 void ion_page_pool_destroy(struct ion_page_pool *pool)
171 {
172         kfree(pool);
173 }
174
175 static int __init ion_page_pool_init(void)
176 {
177         return 0;
178 }
179 device_initcall(ion_page_pool_init);