Merge "[REFACTOR] Buffer: move getting next queue element into separate function"
[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         /* Unlock sync (Locked in get_from_write_list()) */
201 buf_write_sem_post:
202         sync_unlock(&buffer_to_write->buffer_sync);
203
204         return result;
205 }
206 EXPORT_SYMBOL_GPL(swap_buffer_write);
207
208
209 int swap_buffer_get(struct swap_subbuffer **subbuffer)
210 {
211         int result = 0;
212         struct swap_subbuffer *buffer_to_read = NULL;
213
214         /* Check buffer status */
215         if (!(swap_buffer_status & BUFFER_WORK))
216                 return -E_SB_IS_STOPPED;
217
218         /* Get next read buffer */
219         buffer_to_read = get_from_read_list();
220         if (!buffer_to_read)
221                 return -E_SB_NO_READABLE_BUFFERS;
222
223         /* Add to busy list */
224         buffer_to_read->next_in_queue = NULL;
225         add_to_busy_list(buffer_to_read);
226
227         *subbuffer = buffer_to_read;
228
229         result = get_pages_count_in_subbuffer();
230
231         return result;
232 }
233 EXPORT_SYMBOL_GPL(swap_buffer_get);
234
235
236 int swap_buffer_release(struct swap_subbuffer **subbuffer)
237 {
238         int result;
239
240         /* Remove from busy list (includes sanitization) */
241         result = remove_from_busy_list(*subbuffer);
242         if (result < 0)
243                 return result;
244
245         /* Add to write list */
246         add_to_write_list(*subbuffer);
247
248         return E_SB_SUCCESS;
249 }
250 EXPORT_SYMBOL_GPL(swap_buffer_release);
251
252
253 unsigned int swap_buffer_flush(void)
254 {
255         unsigned int result;
256
257         /* Set all non-empty write buffers to read list */
258         buffer_queue_flush();
259
260         /* Get count of all full buffers */
261         result = get_readable_buf_cnt();
262
263         return result;
264 }
265 EXPORT_SYMBOL_GPL(swap_buffer_flush);
266
267
268 int swap_buffer_callback(void *buffer)
269 {
270         int result;
271
272         if (!subbuffer_callback) {
273                 return -E_SB_NO_CALLBACK;
274         }
275
276         result = subbuffer_callback();
277         if (result < 0)
278                 print_err("Callback error! Error code: %d\n", result);
279
280         return result;
281 }
282
283 static int __init swap_buffer_module_init(void)
284 {
285         printk(KERN_NOTICE "SWAP_BUFFER : Buffer module initialized\n");
286         return E_SB_SUCCESS;
287 }
288
289 static void __exit swap_buffer_module_exit(void)
290 {
291         if (swap_buffer_status & BUFFER_ALLOC)
292                 swap_buffer_uninit();
293         printk(KERN_NOTICE "SWAP_BUFFER : Buffer module unintialized\n");
294 }
295
296 module_init(swap_buffer_module_init);
297 module_exit(swap_buffer_module_exit);
298
299 MODULE_LICENSE("GPL");
300 MODULE_DESCRIPTION("SWAP buffer module");
301 MODULE_AUTHOR("Aksenov A.S.");