[FIX] Ksyms: Fix warnings
[kernel/swap-modules.git] / buffer / swap_buffer_module.c
1 /*
2  *  SWAP Buffer Module
3  *  modules/buffer/swap_buffer_module.c
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
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  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Copyright (C) Samsung Electronics, 2013
20  *
21  * 2013  Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement
22  *
23  */
24
25 /* SWAP Buffer interface implementation */
26
27 #include "swap_buffer_module.h"
28 #include "buffer_queue.h"
29 #include "buffer_description.h"
30 #include "swap_buffer_errors.h"
31 #include "kernel_operations.h"
32
33 /* Bitwise mask for buffer status */
34 enum _swap_buffer_status_mask {
35         BUFFER_FREE = 0,
36         BUFFER_ALLOC = 1,
37         BUFFER_PAUSE = 2,
38         BUFFER_WORK = 4
39 };
40
41 /* Buffer status masks:
42  *   0 - memory free
43  *   1 - memory allocated
44  *  10 - buffer overflow
45  * 100 - buffer work
46  * */
47 static unsigned char swap_buffer_status = BUFFER_FREE;
48
49 /* Callback type */
50 typedef int(*subbuffer_callback_type)(void);
51
52 /* Callback that is called when full subbuffer appears */
53 static subbuffer_callback_type subbuffer_callback = NULL;
54
55 /* One subbuffer size */
56 static size_t subbuffers_size = 0;
57
58 /* Subbuffers count */
59 static unsigned int subbuffers_num = 0;
60
61 static unsigned int enough_writable_bufs = 0;
62 static unsigned int min_writable_bufs = 0;
63 static int (*low_mem_cb)(void) = NULL;
64 static int (*enough_mem_cb)(void) = NULL;
65
66
67 static inline int areas_overlap(const void *area1,const void *area2, size_t size)
68 {
69         int i;
70
71         for (i = 0; i < size; i++)
72                 if ((area1 + i == area2) || (area2 + i == area1))
73                         return 1;
74
75         return 0;
76 }
77
78 static inline unsigned int percent_to_count(unsigned char percent,
79                                              unsigned int cnt)
80 {
81         return (percent * cnt) / 100;
82 }
83
84 int swap_buffer_init(struct buffer_init_t *buf_init)
85 {
86         int result = -1;
87
88         swap_buffer_status &= ~BUFFER_WORK;
89         print_debug("status buffer stop = %d\n", swap_buffer_status);
90
91         if ((buf_init->top_threshold > 100) || (buf_init->lower_threshold > 100) ||
92             (buf_init->top_threshold < buf_init->lower_threshold))
93                 return -E_SB_WRONG_THRESHOLD;
94
95         min_writable_bufs = percent_to_count(buf_init->lower_threshold,
96                                              buf_init->nr_subbuffers);
97
98         enough_writable_bufs = percent_to_count(buf_init->top_threshold,
99                                                 buf_init->nr_subbuffers);
100
101         low_mem_cb = buf_init->low_mem_cb;
102         enough_mem_cb = buf_init->enough_mem_cb;
103
104         if ((swap_buffer_status & BUFFER_ALLOC) &&
105                 (subbuffers_size == buf_init->subbuffer_size) &&
106                 (subbuffers_num == buf_init->nr_subbuffers) &&
107                 ((subbuffer_callback_type)subbuffer_callback ==
108                                   buf_init->subbuffer_full_cb)) {
109                 result = buffer_queue_reset();
110                 goto swap_buffer_init_work;
111         }
112
113         subbuffer_callback = buf_init->subbuffer_full_cb;
114         subbuffers_size = buf_init->subbuffer_size;
115         subbuffers_num = buf_init->nr_subbuffers;
116
117         result = buffer_queue_allocation(subbuffers_size, subbuffers_num);
118         if (result < 0)
119                 return result;
120
121         result = get_pages_count_in_subbuffer();
122
123         swap_buffer_status |= BUFFER_ALLOC;
124         print_debug("status buffer alloc = %d\n", swap_buffer_status);
125
126 swap_buffer_init_work:
127         swap_buffer_status |= BUFFER_WORK;
128         print_debug("status buffer work = %d\n", swap_buffer_status);
129
130         return result;
131 }
132 EXPORT_SYMBOL_GPL(swap_buffer_init);
133
134
135 int swap_buffer_uninit(void)
136 {
137         /* Check whether buffer is allocated */
138         if (!(swap_buffer_status & BUFFER_ALLOC))
139                 return -E_SB_NOT_ALLOC;
140
141         /* Stop buffer */
142         swap_buffer_status &= ~BUFFER_WORK;
143         print_debug("status buffer stop = %d\n", swap_buffer_status);
144
145         /* Check whether all buffers are released */
146         if (get_busy_buffers_count())
147                 return -E_SB_UNRELEASED_BUFFERS;
148
149         /* Free */
150         buffer_queue_free();
151
152         subbuffer_callback = NULL;
153         subbuffers_size = 0;
154         subbuffers_num = 0;
155         min_writable_bufs = 0;
156         enough_writable_bufs = 0;
157         low_mem_cb = NULL;
158         enough_mem_cb = NULL;
159
160         swap_buffer_status &= ~BUFFER_ALLOC;
161         print_debug("status buffer dealloc = %d\n", swap_buffer_status);
162
163         return E_SB_SUCCESS;
164 }
165 EXPORT_SYMBOL_GPL(swap_buffer_uninit);
166
167
168 ssize_t swap_buffer_write(void *data, size_t size)
169 {
170         int result = E_SB_SUCCESS;
171         struct swap_subbuffer *buffer_to_write = NULL;
172         void *ptr_to_write = NULL;
173
174         /* Size sanitization */
175         if ((size > subbuffers_size) || (size == 0))
176                 return -E_SB_WRONG_DATA_SIZE;
177
178         /* Check buffer status */
179         if (!(swap_buffer_status & BUFFER_WORK))
180                 return -E_SB_IS_STOPPED;
181
182         /* Get next write buffer and occupying semaphore */
183         buffer_to_write = get_from_write_list(size, &ptr_to_write);
184         if (!buffer_to_write)
185                 return -E_SB_NO_WRITABLE_BUFFERS;
186
187         /* Check for overlapping */
188         if (areas_overlap(ptr_to_write, data, size)) {
189                 result = -E_SB_OVERLAP;
190                 goto buf_write_sem_post;
191         }
192
193         /* Copy data to buffer */
194         /* XXX Think of using memmove instead - useless, anyway overlapping means
195          * that something went wrong. */
196         memcpy(ptr_to_write, data, size);
197
198         result = size;
199
200         if ((get_writable_buf_cnt() < min_writable_bufs) &&
201             !(swap_buffer_status & BUFFER_PAUSE)) {
202                 swap_buffer_status |= BUFFER_PAUSE;
203                 if (low_mem_cb != NULL)
204                         low_mem_cb();
205         }
206
207         /* Unlock sync (Locked in get_from_write_list()) */
208 buf_write_sem_post:
209         sync_unlock(&buffer_to_write->buffer_sync);
210
211         return result;
212 }
213 EXPORT_SYMBOL_GPL(swap_buffer_write);
214
215
216 int swap_buffer_get(struct swap_subbuffer **subbuffer)
217 {
218         int result = 0;
219         struct swap_subbuffer *buffer_to_read = NULL;
220
221         /* Check buffer status */
222         if (!(swap_buffer_status & BUFFER_WORK))
223                 return -E_SB_IS_STOPPED;
224
225         /* Get next read buffer */
226         buffer_to_read = get_from_read_list();
227         if (!buffer_to_read)
228                 return -E_SB_NO_READABLE_BUFFERS;
229
230         /* Add to busy list */
231         buffer_to_read->next_in_queue = NULL;
232         add_to_busy_list(buffer_to_read);
233
234         *subbuffer = buffer_to_read;
235
236         result = get_pages_count_in_subbuffer();
237
238         return result;
239 }
240 EXPORT_SYMBOL_GPL(swap_buffer_get);
241
242
243 int swap_buffer_release(struct swap_subbuffer **subbuffer)
244 {
245         int result;
246
247         /* Remove from busy list (includes sanitization) */
248         result = remove_from_busy_list(*subbuffer);
249         if (result < 0)
250                 return result;
251
252         /* Add to write list */
253         add_to_write_list(*subbuffer);
254
255         if ((swap_buffer_status & BUFFER_PAUSE) &&
256             (get_writable_buf_cnt() >= enough_writable_bufs)) {
257                 swap_buffer_status &= ~BUFFER_PAUSE;
258                 if (enough_mem_cb != NULL)
259                         enough_mem_cb();
260         }
261
262         return E_SB_SUCCESS;
263 }
264 EXPORT_SYMBOL_GPL(swap_buffer_release);
265
266
267 unsigned int swap_buffer_flush(void)
268 {
269         unsigned int result;
270
271         /* Set all non-empty write buffers to read list */
272         buffer_queue_flush();
273
274         /* Get count of all full buffers */
275         result = get_readable_buf_cnt();
276
277         return result;
278 }
279 EXPORT_SYMBOL_GPL(swap_buffer_flush);
280
281
282 int swap_buffer_callback(void *buffer)
283 {
284         int result;
285
286         if (!subbuffer_callback) {
287                 return -E_SB_NO_CALLBACK;
288         }
289
290         result = subbuffer_callback();
291         if (result < 0)
292                 print_err("Callback error! Error code: %d\n", result);
293
294         return result;
295 }
296
297 static int __init swap_buffer_module_init(void)
298 {
299         printk(KERN_NOTICE "SWAP_BUFFER : Buffer module initialized\n");
300         return E_SB_SUCCESS;
301 }
302
303 static void __exit swap_buffer_module_exit(void)
304 {
305         if (swap_buffer_status & BUFFER_ALLOC)
306                 swap_buffer_uninit();
307         printk(KERN_NOTICE "SWAP_BUFFER : Buffer module unintialized\n");
308 }
309
310 module_init(swap_buffer_module_init);
311 module_exit(swap_buffer_module_exit);
312
313 MODULE_LICENSE("GPL");
314 MODULE_DESCRIPTION("SWAP buffer module");
315 MODULE_AUTHOR("Aksenov A.S.");