ARM: tizen_tm1_defconfig: Enable missing features related with CGROUPS
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / kernel / swap / master / swap_initializer.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  *
16  * Copyright (C) Samsung Electronics, 2015
17  *
18  * 2015         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
19  *
20  */
21
22
23 #include <linux/slab.h>
24 #include <linux/list.h>
25 #include <linux/mutex.h>
26 #include <linux/types.h>
27 #include <linux/spinlock.h>
28 #include "swap_initializer.h"
29
30
31 enum init_level {
32         IL_CORE,
33         IL_FS
34 };
35
36 static swap_init_t sis_get_fn_init(struct swap_init_struct *init,
37                                    enum init_level level)
38 {
39         switch (level) {
40         case IL_CORE:
41                 return init->core_init;
42         case IL_FS:
43                 return init->fs_init;
44         default:
45                 return NULL;
46         }
47 }
48
49 static swap_uninit_t sis_get_fn_uninit(struct swap_init_struct *init,
50                                        enum init_level level)
51 {
52         switch (level) {
53         case IL_CORE:
54                 return init->core_uninit;
55         case IL_FS:
56                 return init->fs_uninit;
57         }
58
59         return NULL;
60 }
61
62 static void sis_set_flag(struct swap_init_struct *init,
63                          enum init_level level, bool val)
64 {
65         switch (level) {
66         case IL_CORE:
67                 init->core_flag = val;
68                 break;
69         case IL_FS:
70                 init->fs_flag = val;
71                 break;
72         }
73 }
74
75 static bool sis_get_flag(struct swap_init_struct *init, enum init_level level)
76 {
77         switch (level) {
78         case IL_CORE:
79                 return init->core_flag;
80         case IL_FS:
81                 return init->fs_flag;
82         }
83
84         return false;
85 }
86
87 static int sis_once(struct swap_init_struct *init)
88 {
89         swap_init_t once;
90
91         once = init->once;
92         if (!init->once_flag && once) {
93                 int ret;
94
95                 ret = once();
96                 if (ret)
97                         return ret;
98
99                 init->once_flag = true;
100         }
101
102         return 0;
103 }
104
105 static int sis_init_level(struct swap_init_struct *init, enum init_level level)
106 {
107         int ret;
108         swap_init_t fn;
109
110         if (sis_get_flag(init, level))
111                 return -EPERM;
112
113         fn = sis_get_fn_init(init, level);
114         if (fn) {
115                 ret = fn();
116                 if (ret)
117                         return ret;
118         }
119
120         sis_set_flag(init, level, true);
121         return 0;
122 }
123
124 static void sis_uninit_level(struct swap_init_struct *init,
125                              enum init_level level)
126 {
127         if (sis_get_flag(init, level)) {
128                 swap_uninit_t fn = sis_get_fn_uninit(init, level);
129                 if (fn)
130                         fn();
131                 sis_set_flag(init, level, false);
132         }
133 }
134
135 static int sis_init(struct swap_init_struct *init)
136 {
137         int ret;
138
139         ret = sis_once(init);
140         if (ret)
141                 return ret;
142
143         ret = sis_init_level(init, IL_CORE);
144         if (ret)
145                 return ret;
146
147         ret = sis_init_level(init, IL_FS);
148         if (ret)
149                 sis_uninit_level(init, IL_CORE);
150
151         return ret;
152 }
153
154 static void sis_uninit(struct swap_init_struct *init)
155 {
156         sis_uninit_level(init, IL_FS);
157         sis_uninit_level(init, IL_CORE);
158 }
159
160 static LIST_HEAD(init_list);
161 static DEFINE_MUTEX(inst_mutex);
162 static unsigned init_flag;
163
164 static int do_once(void)
165 {
166         int ret;
167         struct swap_init_struct *init;
168
169         list_for_each_entry(init, &init_list, list) {
170                 ret = sis_once(init);
171                 if (ret)
172                         return ret;
173         }
174
175         return 0;
176 }
177
178 static void do_uninit_level(enum init_level level)
179 {
180         struct swap_init_struct *init;
181
182         list_for_each_entry_reverse(init, &init_list, list)
183                 sis_uninit_level(init, level);
184 }
185
186 static int do_init_level(enum init_level level)
187 {
188         int ret;
189         struct swap_init_struct *init;
190
191         list_for_each_entry(init, &init_list, list) {
192                 ret = sis_init_level(init, level);
193                 if (ret) {
194                         do_uninit_level(level);
195                         return ret;
196                 }
197         }
198
199         return 0;
200 }
201
202 static int do_init(void)
203 {
204         int ret;
205
206         ret = do_once();
207         if (ret)
208                 return ret;
209
210         ret = do_init_level(IL_CORE);
211         if (ret)
212                 return ret;
213
214         ret = do_init_level(IL_FS);
215         if (ret)
216                 do_uninit_level(IL_CORE);
217
218         init_flag = 1;
219
220         return 0;
221 }
222
223 static void do_uninit(void)
224 {
225         do_uninit_level(IL_FS);
226         do_uninit_level(IL_CORE);
227
228         init_flag = 0;
229 }
230
231
232 static atomic_t init_use = ATOMIC_INIT(0);
233
234 enum init_stat_t {
235         IS_OFF,
236         IS_SWITCHING,
237         IS_ON,
238 };
239
240 static enum init_stat_t init_stat;
241 static DEFINE_SPINLOCK(init_stat_lock);
242
243
244 static bool swap_init_try_get(void)
245 {
246         spin_lock(&init_stat_lock);
247         if (init_stat != IS_ON) {
248                 spin_unlock(&init_stat_lock);
249                 return false;
250         }
251         spin_unlock(&init_stat_lock);
252
253         atomic_inc(&init_use);
254
255         return true;
256 }
257
258 static void swap_init_put(void)
259 {
260         atomic_dec(&init_use);
261 }
262
263 int swap_init_simple_open(struct inode *inode, struct file *file)
264 {
265         if (swap_init_try_get() == false)
266                 return -EBUSY;
267
268         return 0;
269 }
270 EXPORT_SYMBOL_GPL(swap_init_simple_open);
271
272 int swap_init_simple_release(struct inode *inode, struct file *file)
273 {
274         swap_init_put();
275         return 0;
276 }
277 EXPORT_SYMBOL_GPL(swap_init_simple_release);
278
279 int swap_init_init(void)
280 {
281         int ret;
282
283         spin_lock(&init_stat_lock);
284         init_stat = IS_SWITCHING;
285         spin_unlock(&init_stat_lock);
286
287         ret = do_init();
288
289         spin_lock(&init_stat_lock);
290         init_stat = ret ? IS_OFF : IS_ON;
291         spin_unlock(&init_stat_lock);
292
293         return ret;
294 }
295
296 int swap_init_uninit(void)
297 {
298         spin_lock(&init_stat_lock);
299         init_stat = IS_SWITCHING;
300         if (atomic_read(&init_use)) {
301                 init_stat = IS_ON;
302                 spin_unlock(&init_stat_lock);
303                 return -EBUSY;
304         }
305         spin_unlock(&init_stat_lock);
306
307         do_uninit();
308
309         spin_lock(&init_stat_lock);
310         init_stat = IS_OFF;
311         spin_unlock(&init_stat_lock);
312
313         return 0;
314 }
315
316
317 int swap_init_stat_get(void)
318 {
319         mutex_lock(&inst_mutex);
320
321         return init_flag;
322 }
323
324 void swap_init_stat_put(void)
325 {
326         mutex_unlock(&inst_mutex);
327 }
328
329 int swap_init_register(struct swap_init_struct *init)
330 {
331         int ret = 0;
332
333         mutex_lock(&inst_mutex);
334         if (init_flag)
335                 ret = sis_init(init);
336
337         if (ret == 0)
338                 list_add_tail(&init->list, &init_list);
339         mutex_unlock(&inst_mutex);
340
341         return ret;
342 }
343 EXPORT_SYMBOL_GPL(swap_init_register);
344
345 void swap_init_unregister(struct swap_init_struct *init)
346 {
347         mutex_lock(&inst_mutex);
348         list_del(&init->list);
349         sis_uninit(init);
350         mutex_unlock(&inst_mutex);
351 }
352 EXPORT_SYMBOL_GPL(swap_init_unregister);