2 * buffer/swap_buffer_module.c
3 * @author Alexander Aksenov <a.aksenov@samsung.com>
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.
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.
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.
23 * Copyright (C) Samsung Electronics, 2013
25 * @section DESCRIPTION
27 * SWAP Buffer interface implementation.
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"
37 * @enum _swap_buffer_status_mask
38 * @brief Bitwise mask for buffer status.
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. */
47 static unsigned char swap_buffer_status = BUFFER_FREE;
50 * @brief Subbuffer callback type.
52 typedef int(*subbuffer_callback_type)(bool wakeup);
54 /* Callback that is called when full subbuffer appears */
55 static subbuffer_callback_type subbuffer_callback;
57 /* One subbuffer size */
58 static size_t subbuffers_size;
60 /* Subbuffers count */
61 static unsigned int subbuffers_num;
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);
69 static inline int areas_overlap(const void *area1,
75 for (i = 0; i < size; i++)
76 if ((area1 + i == area2) || (area2 + i == area1))
82 static inline unsigned int percent_to_count(unsigned char percent,
85 return (percent * cnt) / 100;
89 * @brief Initializes SWAP buffer and allocates memory.
91 * @param buf_init Pointer to the buffer_init_t structure which contains
92 * information about subbuffers count, subbuffers size and subbuffer-full-
94 * @return 0 on success, negative error code otherwise.
96 int swap_buffer_init(struct buffer_init_t *buf_init)
100 swap_buffer_status &= ~BUFFER_WORK;
101 print_debug("status buffer stop = %d\n", swap_buffer_status);
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;
108 min_writable_bufs = percent_to_count(buf_init->lower_threshold,
109 buf_init->nr_subbuffers);
111 enough_writable_bufs = percent_to_count(buf_init->top_threshold,
112 buf_init->nr_subbuffers);
114 low_mem_cb = buf_init->low_mem_cb;
115 enough_mem_cb = buf_init->enough_mem_cb;
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;
126 subbuffer_callback = buf_init->subbuffer_full_cb;
127 subbuffers_size = buf_init->subbuffer_size;
128 subbuffers_num = buf_init->nr_subbuffers;
130 result = buffer_queue_allocation(subbuffers_size, subbuffers_num);
134 result = get_pages_count_in_subbuffer();
136 swap_buffer_status |= BUFFER_ALLOC;
137 print_debug("status buffer alloc = %d\n", swap_buffer_status);
139 swap_buffer_init_work:
140 swap_buffer_status |= BUFFER_WORK;
141 print_debug("status buffer work = %d\n", swap_buffer_status);
145 EXPORT_SYMBOL_GPL(swap_buffer_init);
148 * @brief Uninitializes SWAP buffer, releases allocated memory.
150 * @return 0 on success, negative error code otherwise.
152 int swap_buffer_uninit(void)
154 /* Check whether buffer is allocated */
155 if (!(swap_buffer_status & BUFFER_ALLOC))
156 return -E_SB_NOT_ALLOC;
159 swap_buffer_status &= ~BUFFER_WORK;
160 print_debug("status buffer stop = %d\n", swap_buffer_status);
162 /* Check whether all buffers are released */
163 if (get_busy_buffers_count())
164 return -E_SB_UNRELEASED_BUFFERS;
169 subbuffer_callback = NULL;
172 min_writable_bufs = 0;
173 enough_writable_bufs = 0;
175 enough_mem_cb = NULL;
177 swap_buffer_status &= ~BUFFER_ALLOC;
178 print_debug("status buffer dealloc = %d\n", swap_buffer_status);
182 EXPORT_SYMBOL_GPL(swap_buffer_uninit);
185 * @brief Writes data to SWAP buffer.
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.
191 ssize_t swap_buffer_write(void *data, size_t size, bool wakeup)
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;
198 /* Size sanitization */
199 if ((size > subbuffers_size) || (size == 0))
200 return -E_SB_WRONG_DATA_SIZE;
202 /* Check buffer status */
203 if (!(swap_buffer_status & BUFFER_WORK))
204 return -E_SB_IS_STOPPED;
206 /* We're going to look for writable buffer, so disable irqs */
207 swap_irq_disable(&flags);
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;
216 /* Check for overlapping */
217 if (areas_overlap(ptr_to_write, data, size)) {
218 result = -E_SB_OVERLAP;
219 goto buf_write_sem_post;
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);
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)
236 /* Unlock sync (Locked in get_from_write_list()) and enable irqs */
238 sync_unlock_no_flags(&buffer_to_write->buffer_sync);
239 swap_irq_enable(&flags);
243 EXPORT_SYMBOL_GPL(swap_buffer_write);
246 * @brief Gets pointer to subbuffer for reading.
248 * @param[out] subbuffer Pointer to a variable which points on target subbuffer.
249 * @return 0 on success, negative error code otherwise.
251 int swap_buffer_get(struct swap_subbuffer **subbuffer)
254 struct swap_subbuffer *buffer_to_read = NULL;
256 /* Check buffer status */
257 if (!(swap_buffer_status & BUFFER_WORK))
258 return -E_SB_IS_STOPPED;
260 /* Get next read buffer */
261 buffer_to_read = get_from_read_list();
263 return -E_SB_NO_READABLE_BUFFERS;
265 /* Add to busy list */
266 buffer_to_read->next_in_queue = NULL;
267 add_to_busy_list(buffer_to_read);
269 *subbuffer = buffer_to_read;
271 result = get_pages_count_in_subbuffer();
275 EXPORT_SYMBOL_GPL(swap_buffer_get);
278 * @brief Releases subbuffer after reading.
280 * @param subbuffer Subbuffer that should be released.
281 * @return 0 on success, negative error code otherwise.
283 int swap_buffer_release(struct swap_subbuffer **subbuffer)
287 /* Remove from busy list (includes sanitization) */
288 result = remove_from_busy_list(*subbuffer);
292 /* Add to write list */
293 add_to_write_list(*subbuffer);
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)
304 EXPORT_SYMBOL_GPL(swap_buffer_release);
307 * @brief Sets all subbuffers for reading.
309 * @return Count of subbeffers for reading.
311 unsigned int swap_buffer_flush(void)
315 /* Set all non-empty write buffers to read list */
316 buffer_queue_flush();
318 /* Get count of all full buffers */
319 result = get_readable_buf_cnt();
323 EXPORT_SYMBOL_GPL(swap_buffer_flush);
326 * @brief Executes subbuffer-full-callback.
328 * @param buffer Pointer to the full subbuffer.
329 * @return -E_SB_NO_CALLBACK if no callback is registered or callbacks ret
332 int swap_buffer_callback(void *buffer, bool wakeup)
336 if (!subbuffer_callback)
337 return -E_SB_NO_CALLBACK;
339 result = subbuffer_callback(wakeup);
341 print_err("Callback error! Error code: %d\n", result);
346 static int __init swap_buffer_module_init(void)
348 printk(KERN_NOTICE "SWAP_BUFFER : Buffer module initialized\n");
352 static void __exit swap_buffer_module_exit(void)
354 if (swap_buffer_status & BUFFER_ALLOC)
355 swap_buffer_uninit();
356 printk(KERN_NOTICE "SWAP_BUFFER : Buffer module unintialized\n");
359 module_init(swap_buffer_module_init);
360 module_exit(swap_buffer_module_exit);
362 MODULE_LICENSE("GPL");
363 MODULE_DESCRIPTION("SWAP buffer module");
364 MODULE_AUTHOR("Aksenov A.S.");