]>
Commit | Line | Data |
---|---|---|
bdcb7be6 | 1 | // SPDX-License-Identifier: GPL-2.0 |
0214c7f2 RSZ |
2 | /* |
3 | * drivers/staging/android/ion/ion_mem_pool.c | |
4 | * | |
5 | * Copyright (C) 2011 Google, Inc. | |
0214c7f2 RSZ |
6 | */ |
7 | ||
0214c7f2 RSZ |
8 | #include <linux/list.h> |
9 | #include <linux/slab.h> | |
0cd2dc4d | 10 | #include <linux/swap.h> |
eb9751db LA |
11 | |
12 | #include "ion.h" | |
0214c7f2 | 13 | |
acb42f8e | 14 | static inline struct page *ion_page_pool_alloc_pages(struct ion_page_pool *pool) |
0214c7f2 | 15 | { |
acb42f8e | 16 | return alloc_pages(pool->gfp_mask, pool->order); |
0214c7f2 RSZ |
17 | } |
18 | ||
19 | static void ion_page_pool_free_pages(struct ion_page_pool *pool, | |
20 | struct page *page) | |
21 | { | |
22 | __free_pages(page, pool->order); | |
23 | } | |
24 | ||
70ea629b | 25 | static void ion_page_pool_add(struct ion_page_pool *pool, struct page *page) |
0214c7f2 | 26 | { |
efee5a0c | 27 | mutex_lock(&pool->mutex); |
0fb9b815 | 28 | if (PageHighMem(page)) { |
38c003b1 | 29 | list_add_tail(&page->lru, &pool->high_items); |
0fb9b815 RSZ |
30 | pool->high_count++; |
31 | } else { | |
38c003b1 | 32 | list_add_tail(&page->lru, &pool->low_items); |
0fb9b815 RSZ |
33 | pool->low_count++; |
34 | } | |
548cbba1 | 35 | |
b29940c1 VB |
36 | mod_node_page_state(page_pgdat(page), NR_KERNEL_MISC_RECLAIMABLE, |
37 | 1 << pool->order); | |
efee5a0c | 38 | mutex_unlock(&pool->mutex); |
0214c7f2 RSZ |
39 | } |
40 | ||
0fb9b815 | 41 | static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) |
0214c7f2 | 42 | { |
0214c7f2 RSZ |
43 | struct page *page; |
44 | ||
0fb9b815 RSZ |
45 | if (high) { |
46 | BUG_ON(!pool->high_count); | |
38c003b1 | 47 | page = list_first_entry(&pool->high_items, struct page, lru); |
0fb9b815 RSZ |
48 | pool->high_count--; |
49 | } else { | |
50 | BUG_ON(!pool->low_count); | |
38c003b1 | 51 | page = list_first_entry(&pool->low_items, struct page, lru); |
0fb9b815 RSZ |
52 | pool->low_count--; |
53 | } | |
0214c7f2 | 54 | |
38c003b1 | 55 | list_del(&page->lru); |
b29940c1 VB |
56 | mod_node_page_state(page_pgdat(page), NR_KERNEL_MISC_RECLAIMABLE, |
57 | -(1 << pool->order)); | |
0214c7f2 RSZ |
58 | return page; |
59 | } | |
60 | ||
79240748 | 61 | struct page *ion_page_pool_alloc(struct ion_page_pool *pool) |
0214c7f2 RSZ |
62 | { |
63 | struct page *page = NULL; | |
64 | ||
65 | BUG_ON(!pool); | |
66 | ||
67 | mutex_lock(&pool->mutex); | |
0fb9b815 RSZ |
68 | if (pool->high_count) |
69 | page = ion_page_pool_remove(pool, true); | |
70 | else if (pool->low_count) | |
71 | page = ion_page_pool_remove(pool, false); | |
0214c7f2 RSZ |
72 | mutex_unlock(&pool->mutex); |
73 | ||
0fb9b815 RSZ |
74 | if (!page) |
75 | page = ion_page_pool_alloc_pages(pool); | |
76 | ||
0214c7f2 RSZ |
77 | return page; |
78 | } | |
79 | ||
e1d855b0 | 80 | void ion_page_pool_free(struct ion_page_pool *pool, struct page *page) |
0214c7f2 | 81 | { |
bdeb9f1c HS |
82 | BUG_ON(pool->order != compound_order(page)); |
83 | ||
a3f75c43 | 84 | ion_page_pool_add(pool, page); |
0214c7f2 RSZ |
85 | } |
86 | ||
ea313b5f | 87 | static int ion_page_pool_total(struct ion_page_pool *pool, bool high) |
797a95c4 | 88 | { |
80cb77dc | 89 | int count = pool->low_count; |
797a95c4 | 90 | |
80cb77dc HS |
91 | if (high) |
92 | count += pool->high_count; | |
93 | ||
94 | return count << pool->order; | |
797a95c4 RSZ |
95 | } |
96 | ||
ea313b5f | 97 | int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, |
679011bd | 98 | int nr_to_scan) |
0214c7f2 | 99 | { |
b44d9ce3 | 100 | int freed = 0; |
0fb9b815 RSZ |
101 | bool high; |
102 | ||
0cd2dc4d | 103 | if (current_is_kswapd()) |
17fbab1e | 104 | high = true; |
0cd2dc4d HS |
105 | else |
106 | high = !!(gfp_mask & __GFP_HIGHMEM); | |
0214c7f2 | 107 | |
797a95c4 | 108 | if (nr_to_scan == 0) |
ea313b5f RSZ |
109 | return ion_page_pool_total(pool, high); |
110 | ||
b44d9ce3 | 111 | while (freed < nr_to_scan) { |
ea313b5f RSZ |
112 | struct page *page; |
113 | ||
114 | mutex_lock(&pool->mutex); | |
ce3d1093 | 115 | if (pool->low_count) { |
ea313b5f | 116 | page = ion_page_pool_remove(pool, false); |
ce3d1093 CC |
117 | } else if (high && pool->high_count) { |
118 | page = ion_page_pool_remove(pool, true); | |
ea313b5f | 119 | } else { |
0fb9b815 | 120 | mutex_unlock(&pool->mutex); |
ea313b5f | 121 | break; |
0214c7f2 | 122 | } |
ea313b5f RSZ |
123 | mutex_unlock(&pool->mutex); |
124 | ion_page_pool_free_pages(pool, page); | |
b44d9ce3 | 125 | freed += (1 << pool->order); |
0214c7f2 | 126 | } |
0214c7f2 | 127 | |
b9daf0b6 | 128 | return freed; |
0214c7f2 RSZ |
129 | } |
130 | ||
d92a1fab | 131 | struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order) |
0214c7f2 | 132 | { |
8fb78ad6 BM |
133 | struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL); |
134 | ||
0214c7f2 RSZ |
135 | if (!pool) |
136 | return NULL; | |
0fb9b815 RSZ |
137 | pool->high_count = 0; |
138 | pool->low_count = 0; | |
139 | INIT_LIST_HEAD(&pool->low_items); | |
140 | INIT_LIST_HEAD(&pool->high_items); | |
bdeb9f1c | 141 | pool->gfp_mask = gfp_mask | __GFP_COMP; |
0214c7f2 RSZ |
142 | pool->order = order; |
143 | mutex_init(&pool->mutex); | |
797a95c4 | 144 | plist_node_init(&pool->list, order); |
0214c7f2 RSZ |
145 | |
146 | return pool; | |
147 | } | |
148 | ||
149 | void ion_page_pool_destroy(struct ion_page_pool *pool) | |
150 | { | |
0214c7f2 RSZ |
151 | kfree(pool); |
152 | } |