tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / kernel / swap / buffer / swap_buffer_module.c
1 /**
2  * buffer/swap_buffer_module.c
3  * @author Alexander Aksenov <a.aksenov@samsung.com>
4  *
5  * @section LICENSE
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * @section COPYRIGHT
22  *
23  * Copyright (C) Samsung Electronics, 2013
24  *
25  * @section DESCRIPTION
26  *
27  * SWAP Buffer interface implementation.
28  */
29
30 #include "swap_buffer_module.h"
31 #include "buffer_queue.h"
32 #include "buffer_description.h"
33 #include "swap_buffer_errors.h"
34 #include "kernel_operations.h"
35
36 /**
37  * @enum _swap_buffer_status_mask
38  * @brief Bitwise mask for buffer status.
39  */
40 enum _swap_buffer_status_mask {
41         BUFFER_FREE = 0,                /**< 000 - memory free. */
42         BUFFER_ALLOC = 1,               /**< 001 - memory allocated. */
43         BUFFER_PAUSE = 2,               /**< 010 - buffer overflow. */
44         BUFFER_WORK = 4                 /**< @brief 100 - buffer work. */
45 };
46
47 static unsigned char swap_buffer_status = BUFFER_FREE;
48
49 /**
50  * @brief Subbuffer callback type.
51  */
52 typedef int(*subbuffer_callback_type)(bool wakeup);
53
54 /* Callback that is called when full subbuffer appears */
55 static subbuffer_callback_type subbuffer_callback;
56
57 /* One subbuffer size */
58 static size_t subbuffers_size;
59
60 /* Subbuffers count */
61 static unsigned int subbuffers_num;
62
63 static unsigned int enough_writable_bufs;
64 static unsigned int min_writable_bufs;
65 static int (*low_mem_cb)(void);
66 static int (*enough_mem_cb)(void);
67
68
69 static inline int areas_overlap(const void *area1,
70                                 const void *area2,
71                                 size_t size)
72 {
73         int i;
74
75         for (i = 0; i < size; i++)
76                 if ((area1 + i == area2) || (area2 + i == area1))
77                         return 1;
78
79         return 0;
80 }
81
82 static inline unsigned int percent_to_count(unsigned char percent,
83                                              unsigned int cnt)
84 {
85         return (percent * cnt) / 100;
86 }
87
88 /**
89  * @brief Initializes SWAP buffer and allocates memory.
90  *
91  * @param buf_init Pointer to the buffer_init_t structure which contains
92  * information about subbuffers count, subbuffers size and subbuffer-full-
93  * callback.
94  * @return 0 on success, negative error code otherwise.
95  */
96 int swap_buffer_init(struct buffer_init_t *buf_init)
97 {
98         int result = -1;
99
100         swap_buffer_status &= ~BUFFER_WORK;
101         print_debug("status buffer stop = %d\n", swap_buffer_status);
102
103         if ((buf_init->top_threshold > 100) ||
104             (buf_init->lower_threshold > 100) ||
105             (buf_init->top_threshold < buf_init->lower_threshold))
106                 return -E_SB_WRONG_THRESHOLD;
107
108         min_writable_bufs = percent_to_count(buf_init->lower_threshold,
109                                              buf_init->nr_subbuffers);
110
111         enough_writable_bufs = percent_to_count(buf_init->top_threshold,
112                                                 buf_init->nr_subbuffers);
113
114         low_mem_cb = buf_init->low_mem_cb;
115         enough_mem_cb = buf_init->enough_mem_cb;
116
117         if ((swap_buffer_status & BUFFER_ALLOC) &&
118                 (subbuffers_size == buf_init->subbuffer_size) &&
119                 (subbuffers_num == buf_init->nr_subbuffers) &&
120                 ((subbuffer_callback_type)subbuffer_callback ==
121                                   buf_init->subbuffer_full_cb)) {
122                 result = buffer_queue_reset();
123                 goto swap_buffer_init_work;
124         }
125
126         subbuffer_callback = buf_init->subbuffer_full_cb;
127         subbuffers_size = buf_init->subbuffer_size;
128         subbuffers_num = buf_init->nr_subbuffers;
129
130         result = buffer_queue_allocation(subbuffers_size, subbuffers_num);
131         if (result < 0)
132                 return result;
133
134         result = get_pages_count_in_subbuffer();
135
136         swap_buffer_status |= BUFFER_ALLOC;
137         print_debug("status buffer alloc = %d\n", swap_buffer_status);
138
139 swap_buffer_init_work:
140         swap_buffer_status |= BUFFER_WORK;
141         print_debug("status buffer work = %d\n", swap_buffer_status);
142
143         return result;
144 }
145 EXPORT_SYMBOL_GPL(swap_buffer_init);
146
147 /**
148  * @brief Uninitializes SWAP buffer, releases allocated memory.
149  *
150  * @return 0 on success, negative error code otherwise.
151  */
152 int swap_buffer_uninit(void)
153 {
154         /* Check whether buffer is allocated */
155         if (!(swap_buffer_status & BUFFER_ALLOC))
156                 return -E_SB_NOT_ALLOC;
157
158         /* Stop buffer */
159         swap_buffer_status &= ~BUFFER_WORK;
160         print_debug("status buffer stop = %d\n", swap_buffer_status);
161
162         /* Check whether all buffers are released */
163         if (get_busy_buffers_count())
164                 return -E_SB_UNRELEASED_BUFFERS;
165
166         /* Free */
167         buffer_queue_free();
168
169         subbuffer_callback = NULL;
170         subbuffers_size = 0;
171         subbuffers_num = 0;
172         min_writable_bufs = 0;
173         enough_writable_bufs = 0;
174         low_mem_cb = NULL;
175         enough_mem_cb = NULL;
176
177         swap_buffer_status &= ~BUFFER_ALLOC;
178         print_debug("status buffer dealloc = %d\n", swap_buffer_status);
179
180         return E_SB_SUCCESS;
181 }
182 EXPORT_SYMBOL_GPL(swap_buffer_uninit);
183
184 /**
185  * @brief Writes data to SWAP buffer.
186  *
187  * @param data Pointer to a data for writing.
188  * @param size Size of a data for writing.
189  * @return Size of written data on success, negative error code otherwise.
190  */
191 ssize_t swap_buffer_write(void *data, size_t size, bool wakeup)
192 {
193         int result = E_SB_SUCCESS;
194         struct swap_subbuffer *buffer_to_write = NULL;
195         void *ptr_to_write = NULL;
196         unsigned long flags = 0;
197
198         /* Size sanitization */
199         if ((size > subbuffers_size) || (size == 0))
200                 return -E_SB_WRONG_DATA_SIZE;
201
202         /* Check buffer status */
203         if (!(swap_buffer_status & BUFFER_WORK))
204                 return -E_SB_IS_STOPPED;
205
206         /* We're going to look for writable buffer, so disable irqs */
207         swap_irq_disable(&flags);
208
209         /* Get next write buffer and occupying semaphore */
210         buffer_to_write = get_from_write_list(size, &ptr_to_write, wakeup);
211         if (!buffer_to_write) {
212                 swap_irq_enable(&flags);
213                 return -E_SB_NO_WRITABLE_BUFFERS;
214         }
215
216         /* Check for overlapping */
217         if (areas_overlap(ptr_to_write, data, size)) {
218                 result = -E_SB_OVERLAP;
219                 goto buf_write_sem_post;
220         }
221
222         /* Copy data to buffer */
223         /* XXX Think of using memmove instead - useless, anyway overlapping
224          * means that something went wrong. */
225         memcpy(ptr_to_write, data, size);
226
227         result = size;
228
229         if ((get_writable_buf_cnt() < min_writable_bufs) &&
230             !(swap_buffer_status & BUFFER_PAUSE)) {
231                 swap_buffer_status |= BUFFER_PAUSE;
232                 if (low_mem_cb != NULL)
233                         low_mem_cb();
234         }
235
236         /* Unlock sync (Locked in get_from_write_list()) and enable irqs */
237 buf_write_sem_post:
238         sync_unlock_no_flags(&buffer_to_write->buffer_sync);
239         swap_irq_enable(&flags);
240
241         return result;
242 }
243 EXPORT_SYMBOL_GPL(swap_buffer_write);
244
245 /**
246  * @brief Gets pointer to subbuffer for reading.
247  *
248  * @param[out] subbuffer Pointer to a variable which points on target subbuffer.
249  * @return 0 on success, negative error code otherwise.
250  */
251 int swap_buffer_get(struct swap_subbuffer **subbuffer)
252 {
253         int result = 0;
254         struct swap_subbuffer *buffer_to_read = NULL;
255
256         /* Check buffer status */
257         if (!(swap_buffer_status & BUFFER_WORK))
258                 return -E_SB_IS_STOPPED;
259
260         /* Get next read buffer */
261         buffer_to_read = get_from_read_list();
262         if (!buffer_to_read)
263                 return -E_SB_NO_READABLE_BUFFERS;
264
265         /* Add to busy list */
266         buffer_to_read->next_in_queue = NULL;
267         add_to_busy_list(buffer_to_read);
268
269         *subbuffer = buffer_to_read;
270
271         result = get_pages_count_in_subbuffer();
272
273         return result;
274 }
275 EXPORT_SYMBOL_GPL(swap_buffer_get);
276
277 /**
278  * @brief Releases subbuffer after reading.
279  *
280  * @param subbuffer Subbuffer that should be released.
281  * @return 0 on success, negative error code otherwise.
282  */
283 int swap_buffer_release(struct swap_subbuffer **subbuffer)
284 {
285         int result;
286
287         /* Remove from busy list (includes sanitization) */
288         result = remove_from_busy_list(*subbuffer);
289         if (result < 0)
290                 return result;
291
292         /* Add to write list */
293         add_to_write_list(*subbuffer);
294
295         if ((swap_buffer_status & BUFFER_PAUSE) &&
296             (get_writable_buf_cnt() >= enough_writable_bufs)) {
297                 swap_buffer_status &= ~BUFFER_PAUSE;
298                 if (enough_mem_cb != NULL)
299                         enough_mem_cb();
300         }
301
302         return E_SB_SUCCESS;
303 }
304 EXPORT_SYMBOL_GPL(swap_buffer_release);
305
306 /**
307  * @brief Sets all subbuffers for reading.
308  *
309  * @return Count of subbeffers for reading.
310  */
311 unsigned int swap_buffer_flush(void)
312 {
313         unsigned int result;
314
315         /* Set all non-empty write buffers to read list */
316         buffer_queue_flush();
317
318         /* Get count of all full buffers */
319         result = get_readable_buf_cnt();
320
321         return result;
322 }
323 EXPORT_SYMBOL_GPL(swap_buffer_flush);
324
325 /**
326  * @brief Executes subbuffer-full-callback.
327  *
328  * @param buffer Pointer to the full subbuffer.
329  * @return -E_SB_NO_CALLBACK if no callback is registered or callbacks ret
330  * value otherwise.
331  */
332 int swap_buffer_callback(void *buffer, bool wakeup)
333 {
334         int result;
335
336         if (!subbuffer_callback)
337                 return -E_SB_NO_CALLBACK;
338
339         result = subbuffer_callback(wakeup);
340         if (result < 0)
341                 print_err("Callback error! Error code: %d\n", result);
342
343         return result;
344 }
345
346 static int __init swap_buffer_module_init(void)
347 {
348         printk(KERN_NOTICE "SWAP_BUFFER : Buffer module initialized\n");
349         return E_SB_SUCCESS;
350 }
351
352 static void __exit swap_buffer_module_exit(void)
353 {
354         if (swap_buffer_status & BUFFER_ALLOC)
355                 swap_buffer_uninit();
356         printk(KERN_NOTICE "SWAP_BUFFER : Buffer module unintialized\n");
357 }
358
359 module_init(swap_buffer_module_init);
360 module_exit(swap_buffer_module_exit);
361
362 MODULE_LICENSE("GPL");
363 MODULE_DESCRIPTION("SWAP buffer module");
364 MODULE_AUTHOR("Aksenov A.S.");