GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / staging / media / atomisp / pci / atomisp2 / hmm / hmm_dynamic_pool.c
1 /*
2  * Support for Medifield PNW Camera Imaging ISP subsystem.
3  *
4  * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
5  *
6  * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version
10  * 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301, USA.
21  *
22  */
23 /*
24  * This file contains functions for dynamic memory pool management
25  */
26 #include <linux/kernel.h>
27 #include <linux/types.h>
28 #include <linux/mm.h>
29
30 #include <asm/set_memory.h>
31
32 #include "atomisp_internal.h"
33
34 #include "hmm/hmm_pool.h"
35
36 /*
37  * dynamic memory pool ops.
38  */
39 static unsigned int get_pages_from_dynamic_pool(void *pool,
40                                         struct hmm_page_object *page_obj,
41                                         unsigned int size, bool cached)
42 {
43         struct hmm_page *hmm_page;
44         unsigned long flags;
45         unsigned int i = 0;
46         struct hmm_dynamic_pool_info *dypool_info = pool;
47
48         if (!dypool_info)
49                 return 0;
50
51         spin_lock_irqsave(&dypool_info->list_lock, flags);
52         if (dypool_info->initialized) {
53                 while (!list_empty(&dypool_info->pages_list)) {
54                         hmm_page = list_entry(dypool_info->pages_list.next,
55                                                 struct hmm_page, list);
56
57                         list_del(&hmm_page->list);
58                         dypool_info->pgnr--;
59                         spin_unlock_irqrestore(&dypool_info->list_lock, flags);
60
61                         page_obj[i].page = hmm_page->page;
62                         page_obj[i++].type = HMM_PAGE_TYPE_DYNAMIC;
63                         kmem_cache_free(dypool_info->pgptr_cache, hmm_page);
64
65                         if (i == size)
66                                 return i;
67
68                         spin_lock_irqsave(&dypool_info->list_lock, flags);
69                 }
70         }
71         spin_unlock_irqrestore(&dypool_info->list_lock, flags);
72
73         return i;
74 }
75
76 static void free_pages_to_dynamic_pool(void *pool,
77                                         struct hmm_page_object *page_obj)
78 {
79         struct hmm_page *hmm_page;
80         unsigned long flags;
81         int ret;
82         struct hmm_dynamic_pool_info *dypool_info = pool;
83
84         if (!dypool_info)
85                 return;
86
87         spin_lock_irqsave(&dypool_info->list_lock, flags);
88         if (!dypool_info->initialized) {
89                 spin_unlock_irqrestore(&dypool_info->list_lock, flags);
90                 return;
91         }
92         spin_unlock_irqrestore(&dypool_info->list_lock, flags);
93
94         if (page_obj->type == HMM_PAGE_TYPE_RESERVED)
95                 return;
96
97         if (dypool_info->pgnr >= dypool_info->pool_size) {
98                 /* free page directly back to system */
99                 ret = set_pages_wb(page_obj->page, 1);
100                 if (ret)
101                         dev_err(atomisp_dev,
102                                 "set page to WB err ...ret=%d\n", ret);
103                 /*
104                 W/A: set_pages_wb seldom return value = -EFAULT
105                 indicate that address of page is not in valid
106                 range(0xffff880000000000~0xffffc7ffffffffff)
107                 then, _free_pages would panic; Do not know why page
108                 address be valid, it maybe memory corruption by lowmemory
109                 */
110                 if (!ret) {
111                         __free_pages(page_obj->page, 0);
112                         hmm_mem_stat.sys_size--;
113                 }
114                 return;
115         }
116         hmm_page = kmem_cache_zalloc(dypool_info->pgptr_cache,
117                                                 GFP_KERNEL);
118         if (!hmm_page) {
119                 dev_err(atomisp_dev, "out of memory for hmm_page.\n");
120
121                 /* free page directly */
122                 ret = set_pages_wb(page_obj->page, 1);
123                 if (ret)
124                         dev_err(atomisp_dev,
125                                 "set page to WB err ...ret=%d\n", ret);
126                 if (!ret) {
127                         __free_pages(page_obj->page, 0);
128                         hmm_mem_stat.sys_size--;
129                 }
130                 return;
131         }
132
133         hmm_page->page = page_obj->page;
134
135         /*
136          * add to pages_list of pages_pool
137          */
138         spin_lock_irqsave(&dypool_info->list_lock, flags);
139         list_add_tail(&hmm_page->list, &dypool_info->pages_list);
140         dypool_info->pgnr++;
141         spin_unlock_irqrestore(&dypool_info->list_lock, flags);
142         hmm_mem_stat.dyc_size++;
143 }
144
145 static int hmm_dynamic_pool_init(void **pool, unsigned int pool_size)
146 {
147         struct hmm_dynamic_pool_info *dypool_info;
148
149         if (pool_size == 0)
150                 return 0;
151
152         dypool_info = kmalloc(sizeof(struct hmm_dynamic_pool_info),
153                 GFP_KERNEL);
154         if (unlikely(!dypool_info)) {
155                 dev_err(atomisp_dev, "out of memory for repool_info.\n");
156                 return -ENOMEM;
157         }
158
159         dypool_info->pgptr_cache = kmem_cache_create("pgptr_cache",
160                                                 sizeof(struct hmm_page), 0,
161                                                 SLAB_HWCACHE_ALIGN, NULL);
162         if (!dypool_info->pgptr_cache) {
163                 kfree(dypool_info);
164                 return -ENOMEM;
165         }
166
167         INIT_LIST_HEAD(&dypool_info->pages_list);
168         spin_lock_init(&dypool_info->list_lock);
169         dypool_info->initialized = true;
170         dypool_info->pool_size = pool_size;
171         dypool_info->pgnr = 0;
172
173         *pool = dypool_info;
174
175         return 0;
176 }
177
178 static void hmm_dynamic_pool_exit(void **pool)
179 {
180         struct hmm_dynamic_pool_info *dypool_info = *pool;
181         struct hmm_page *hmm_page;
182         unsigned long flags;
183         int ret;
184
185         if (!dypool_info)
186                 return;
187
188         spin_lock_irqsave(&dypool_info->list_lock, flags);
189         if (!dypool_info->initialized) {
190                 spin_unlock_irqrestore(&dypool_info->list_lock, flags);
191                 return;
192         }
193         dypool_info->initialized = false;
194
195         while (!list_empty(&dypool_info->pages_list)) {
196                 hmm_page = list_entry(dypool_info->pages_list.next,
197                                         struct hmm_page, list);
198
199                 list_del(&hmm_page->list);
200                 spin_unlock_irqrestore(&dypool_info->list_lock, flags);
201
202                 /* can cause thread sleep, so cannot be put into spin_lock */
203                 ret = set_pages_wb(hmm_page->page, 1);
204                 if (ret)
205                         dev_err(atomisp_dev,
206                                 "set page to WB err...ret=%d\n", ret);
207                 if (!ret) {
208                         __free_pages(hmm_page->page, 0);
209                         hmm_mem_stat.dyc_size--;
210                         hmm_mem_stat.sys_size--;
211                 }
212                 kmem_cache_free(dypool_info->pgptr_cache, hmm_page);
213                 spin_lock_irqsave(&dypool_info->list_lock, flags);
214         }
215
216         spin_unlock_irqrestore(&dypool_info->list_lock, flags);
217
218         kmem_cache_destroy(dypool_info->pgptr_cache);
219
220         kfree(dypool_info);
221
222         *pool = NULL;
223 }
224
225 static int hmm_dynamic_pool_inited(void *pool)
226 {
227         struct hmm_dynamic_pool_info *dypool_info = pool;
228
229         if (!dypool_info)
230                 return 0;
231
232         return dypool_info->initialized;
233 }
234
235 struct hmm_pool_ops dynamic_pops = {
236         .pool_init              = hmm_dynamic_pool_init,
237         .pool_exit              = hmm_dynamic_pool_exit,
238         .pool_alloc_pages       = get_pages_from_dynamic_pool,
239         .pool_free_pages        = free_pages_to_dynamic_pool,
240         .pool_inited            = hmm_dynamic_pool_inited,
241 };