diff options
author | Alexander Aksenov <a.aksenov@samsung.com> | 2013-05-28 18:46:36 +0400 |
---|---|---|
committer | Alexander Aksenov <a.aksenov@samsung.com> | 2013-05-28 18:46:36 +0400 |
commit | 43e4fda67ea97afaf8204a32ad9e1d915cffdc90 (patch) | |
tree | 7ea12367d58c5fc6b0609ea5112822fbfff51286 | |
parent | 035ab7da56300cc7711b8e1813c8d13bdf11da5b (diff) | |
download | swap-modules-43e4fda67ea97afaf8204a32ad9e1d915cffdc90.tar.gz swap-modules-43e4fda67ea97afaf8204a32ad9e1d915cffdc90.tar.bz2 swap-modules-43e4fda67ea97afaf8204a32ad9e1d915cffdc90.zip |
[FEATURE] SWAP Buffer implemented
-rw-r--r-- | buffer/Kbuild | 6 | ||||
-rw-r--r-- | buffer/Makefile | 27 | ||||
-rw-r--r-- | buffer/Makefile.am | 34 | ||||
-rw-r--r-- | buffer/buffer.c | 284 | ||||
-rw-r--r-- | buffer/buffer.h | 9 | ||||
-rw-r--r-- | buffer/buffer_description.h | 40 | ||||
-rw-r--r-- | buffer/buffer_queue.c | 680 | ||||
-rw-r--r-- | buffer/buffer_queue.h | 46 | ||||
-rw-r--r-- | buffer/space_dep_operations.c | 148 | ||||
-rw-r--r-- | buffer/space_dep_operations.h | 50 | ||||
-rw-r--r-- | buffer/space_dep_types_and_def.h | 188 | ||||
-rw-r--r-- | buffer/swap_buffer_module.c | 317 | ||||
-rw-r--r-- | buffer/swap_buffer_module.h | 48 | ||||
-rw-r--r-- | buffer/swap_buffer_to_buffer_queue.h | 27 |
14 files changed, 1574 insertions, 330 deletions
diff --git a/buffer/Kbuild b/buffer/Kbuild index 34c249a9..6374b174 100644 --- a/buffer/Kbuild +++ b/buffer/Kbuild @@ -1,4 +1,4 @@ -EXTRA_CFLAGS := $(extra_cflags) - obj-m := swap_buffer.o -swap_buffer-y := buffer.o + +swap_buffer-y := swap_buffer_module.o buffer_queue.o space_dep_operations.o + diff --git a/buffer/Makefile b/buffer/Makefile new file mode 100644 index 00000000..b9d835ff --- /dev/null +++ b/buffer/Makefile @@ -0,0 +1,27 @@ +USER_OBJECTS := buffer_queue_user.o swap_buffer_user.o space_dep_operations_user.o +USER_FLAG := BUFFER_FOR_USER +KERNEL_OBJECTS := buffer_queue.o swap_buffer_module.o space_dep_operations.o +KERNEL_FILES := swap_buffer.o swap_buffer.mod.c swap_buffer.mod.o +obj-m = $(KERNEL_OBJECTS) +#CC=gcc +CC=/home/alexander/dev/u1_slp/arm-linux-gnueabi-gcc4.4.1-glibc2.11.1/bin/arm-none-linux-gnueabi-gcc +KERNEL_PATH=/home/alexander/dev/u1_slp/kernel_20121005 +#KERNEL_PATH=/home/alexander/vanilla_kernels/linux-3.8.6 + +kernel: + make ARCH=arm CROSS_COMPILE=/home/alexander/dev/u1_slp/arm-linux-gnueabi-gcc4.4.1-glibc2.11.1/bin/arm-none-linux-gnueabi- -C $(KERNEL_PATH) SUBDIRS=$(PWD) modules + +libswap_buffer.so: $(USER_OBJECTS) + $(CC) -shared -lpthread -Wl,-soname,libswap_buffer.so -o libswap_buffer.so $(USER_OBJECTS) + +swap_buffer_user.o: swap_buffer_module.c + $(CC) -c -fPIC swap_buffer_module.c -D$(USER_FLAG) -o swap_buffer_user.o + +buffer_queue_user.o: buffer_queue.c + $(CC) -c -fPIC buffer_queue.c -D$(USER_FLAG) -o buffer_queue_user.o + +space_dep_operations_user.o: space_dep_operations.c + $(CC) -c -fPIC space_dep_operations.c -D$(USER_FLAG) -o space_dep_operations_user.o + +clean: + rm $(USER_OBJECTS) $(KERNEL_OBJECTS) $(KERNEL_FILES) diff --git a/buffer/Makefile.am b/buffer/Makefile.am deleted file mode 100644 index 4e450431..00000000 --- a/buffer/Makefile.am +++ /dev/null @@ -1,34 +0,0 @@ -if DEBUG -debug_opt = -D__DEBUG -endif - -if SPARSE -sparse_output = C=2 -endif - -board_opt = -DBOARD_@BOARD@ - -target_kernel_src = @KERNEL@ -target_arch = @ARCH@ -module_dir = $(realpath $(srcdir)) -module_name = swap_buffer -cross_compiler = $(subst gcc,,$(CC)) - -inlude_opt = -I$(top_srcdir)/src/modules/ksyms -I$(top_srcdir)/src/common -I$(top_srcdir)/src/profile -extra_cflags = "$(inlude_opt) -DEC_ARCH_$(ARCH) $(debug_opt) $(board_opt)" - -#bin_SCRIPTS = patchko.sh insmod.sh - -all-local: - cp $(top_srcdir)/src/modules/kprobe/Module.symvers $(module_dir) - $(MAKE) CROSS_COMPILE=$(cross_compiler) ARCH=$(target_arch) extra_cflags=$(extra_cflags) $(AM_MAKEFLAGS) -C $(target_kernel_src) $(sparse_output) M=$(module_dir) modules - - echo "generate data for version patching <$(OBJDUMP)><$(READELF)>" - $(top_srcdir)/src/modules/driver/patchko.sh -g $(module_dir)/$(module_name).ko $(OBJDUMP) $(READELF) - -clean-local: - $(MAKE) CROSS_COMPILE=$(cross_compiler) ARCH=$(target_arch) $(AM_MAKEFLAGS) -C $(target_kernel_src) M=$(module_dir) clean - -install-exec-local: - install -m 644 $(module_dir)/$(module_name).ko $(prefix) - install -m 644 $(module_dir)/$(module_name).ko.addr $(prefix) diff --git a/buffer/buffer.c b/buffer/buffer.c deleted file mode 100644 index 52f4e389..00000000 --- a/buffer/buffer.c +++ /dev/null @@ -1,284 +0,0 @@ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/vmalloc.h> -#include <linux/cdev.h> -#include <linux/list.h> -#include <linux/fs.h> -#include <asm/uaccess.h> -#include "buffer.h" - -#define BUF_DEBUG - -#ifdef BUF_DEBUG -#define DPRINTF(format, args...) \ - do { \ - char *f = strrchr(__FILE__, '/'); \ - printk("%s[%s:%u:%s]: " format "\n", BUF_DEVICE, f ? f + 1: __FILE__, \ - __LINE__, __FUNCTION__, ##args); \ - } while (0) -#else /* !BUF_DEBUG */ -#define DPRINTF(format, args...) -#endif /* BUF_DEBUG */ - -#define EPRINTF(format, args...) \ - do { \ - printk("%s: " format "\n", BUF_DEVICE, ##args); \ - } while (0) - -struct chunk { - struct list_head list; - unsigned long size; - void *payload; -}; - -struct buffer_device { - struct list_head free_chunks; - struct list_head used_chunks; - spinlock_t lock; - struct cdev cdev; - dev_t dev; - unsigned long size; - unsigned long chunk_size; - char *buf; -}; - -static int buf_open(struct inode *, struct file *); -static int buf_release(struct inode *, struct file *); -static int buf_mmap(struct file *, struct vm_area_struct *); -static ssize_t buf_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t buf_write(struct file *, const char __user *, size_t, loff_t *); -static long buf_ioctl(struct file *, unsigned int, unsigned long); -static unsigned int buf_poll(struct file *, struct poll_table_struct *); -static ssize_t buf_splice_read(struct file *, loff_t *, - struct pipe_inode_info *, size_t, unsigned int); -static ssize_t buf_splice_write(struct pipe_inode_info *, struct file *, - loff_t *, size_t, unsigned int); - -static struct file_operations buf_fops = { - .owner = THIS_MODULE, - .open = buf_open, - .release = buf_release, - .mmap = buf_mmap, - .read = buf_read, - .write = buf_write, - .unlocked_ioctl = buf_ioctl, - .poll = buf_poll, - .splice_read = buf_splice_read, - .splice_write = buf_splice_write -}; - -static struct buffer_device bdevice = { - .free_chunks = LIST_HEAD_INIT(bdevice.free_chunks), - .used_chunks = LIST_HEAD_INIT(bdevice.used_chunks), - .lock = __SPIN_LOCK_UNLOCKED(bdevice.lock), - .dev = MKDEV(BUF_DEFAULT_MAJOR, BUF_DEFAULT_MAJOR), - .size = 0, - .chunk_size = 0, - .buf = NULL -}; - -/* --- chunks manipulation routines --- */ -static inline void *buf_get_free_chunk(void) -{ - if (!list_empty(&bdevice.free_chunks)) - return list_first_entry(&bdevice.free_chunks, struct chunk, list); - - return NULL; -} - -static inline void *buf_get_used_chunk(void) -{ - if (!list_empty(&bdevice.used_chunks)) - return list_first_entry(&bdevice.used_chunks, struct chunk, list); - - return NULL; -} - -static inline void buf_free_chunk(struct chunk *chunk) -{ - list_move_tail(&chunk->list, &bdevice.free_chunks); - chunk->size = 0; -} - -static inline void buf_use_chunk(struct chunk *chunk) -{ - list_move_tail(&chunk->list, &bdevice.used_chunks); - chunk->size = 0; -} - -static inline int buf_check_chunk_size(struct chunk *chunk, unsigned long size) -{ - return (bdevice.chunk_size - chunk->size <= size); -} - -static inline unsigned long buf_copy_to_chunk(struct chunk *chunk, - const char __user *buf, size_t length) -{ - return copy_from_user(chunk->payload, buf, length); -} - -static inline unsigned long buf_copy_from_chunk(struct chunk *chunk, - char __user *buf, size_t length) -{ - return copy_to_user(buf, chunk->payload, length); -} - -/* --- buffer manipulation routines */ -static inline void *buf_get_chunk(int i) -{ - return (((struct chunk *)bdevice.buf) + i); //TODO FIXME!!!! -} - -static int buf_init(unsigned long size, unsigned long chunk_size) -{ - int retval = 0; - - INIT_LIST_HEAD(&bdevice.free_chunks); - INIT_LIST_HEAD(&bdevice.used_chunks); - bdevice.size = 0; - bdevice.chunk_size = 0; - - bdevice.buf = vmalloc(size); - if (!bdevice.buf) { - retval = -ENOMEM; - goto out; - } - - /*for (;;) { //TODO - struct chunk *chunk = buf_get_chunk(i); //TODO - list_add_tail(&chunk->list, &bdevice.free_chunks); - }*/ - -out: - return retval; -} - -static int buf_free(void) -{ - INIT_LIST_HEAD(&bdevice.free_chunks); - INIT_LIST_HEAD(&bdevice.used_chunks); - bdevice.size = 0; - bdevice.chunk_size = 0; - - if (bdevice.buf) - vfree(bdevice.buf); - bdevice.buf = NULL; - - return 0; -} - -/* --- char dev file operations --- */ -static int buf_open(struct inode *inode, struct file *filp) -{ - /*struct buffer_device *dev = NULL; //TODO - - if (filp->f_flags & O_WRONLY) { - } - - if ((filp->f_flags & O_ACCMODE) == O_WRONLY) { - } - - if (!atomic_dec_and_test()) { - //TODO - return -EBUSY; - } - - filp->private_data = dev;*/ - DPRINTF(""); - return 0; -} - -static int buf_release(struct inode *inode, struct file *filp) -{ - DPRINTF(""); - return 0; -} - -static int buf_mmap(struct file *filp, struct vm_area_struct *vma) -{ - EPRINTF("mmap() operation is not supported"); - return -ENODEV; -} - -static ssize_t buf_read(struct file *filp, char __user *buf, size_t length, - loff_t *offset) -{ - DPRINTF(""); - return 0; -} - -static ssize_t buf_write(struct file *filp, const char __user *buf, - size_t length, loff_t *offset) -{ - DPRINTF(""); - return length; -} - -static long buf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - DPRINTF(""); - return 0; -} - -static unsigned int buf_poll(struct file *filp, struct poll_table_struct *table) -{ - DPRINTF(""); - return 0; -} - -static ssize_t buf_splice_read(struct file *filp, loff_t *offset, - struct pipe_inode_info *pipe, size_t length, unsigned int flags) -{ - DPRINTF(""); - return 0; -} - -static ssize_t buf_splice_write(struct pipe_inode_info *pipe, struct file *filp, - loff_t *offset, size_t length, unsigned int flags) -{ - DPRINTF(""); - return 0; -} - -/* --- module init/exit routines --- */ -static int __init buf_module_init(void) -{ - int retval = 0; - - bdevice.dev = MKDEV(BUF_DEFAULT_MAJOR, BUF_DEFAULT_MINOR); - - retval = alloc_chrdev_region(&bdevice.dev, BUF_DEFAULT_MINOR, - BUF_NUM_DEVICES, BUF_DEVICE); - if (retval < 0) { - EPRINTF("(%d) - alloc_chrdev_region", retval); - goto out; - } - - cdev_init(&bdevice.cdev, &buf_fops); - bdevice.cdev.owner = THIS_MODULE; - bdevice.cdev.ops = &buf_fops; - - retval = cdev_add(&bdevice.cdev, bdevice.dev, BUF_NUM_DEVICES); - if (retval < 0) { - EPRINTF("(%d) - cdev_add", retval); - goto out; - } - -out: - DPRINTF("major = %d", MAJOR(bdevice.dev)); - return retval; -} - -static void __exit buf_module_exit(void) -{ - DPRINTF("major = %d", MAJOR(bdevice.dev)); - cdev_del(&bdevice.cdev); - unregister_chrdev_region(bdevice.dev, BUF_NUM_DEVICES); -} - -module_init(buf_module_init); -module_exit(buf_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SWAP buffer module"); diff --git a/buffer/buffer.h b/buffer/buffer.h deleted file mode 100644 index 82857771..00000000 --- a/buffer/buffer.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef BUFFER_H -#define BUFFER_H - -#define BUF_DEFAULT_MAJOR 0 -#define BUF_DEFAULT_MINOR 0 -#define BUF_NUM_DEVICES 1 -#define BUF_DEVICE "swap_buffer" - -#endif /* BUFFER_H */ diff --git a/buffer/buffer_description.h b/buffer/buffer_description.h new file mode 100644 index 00000000..49b77ceb --- /dev/null +++ b/buffer/buffer_description.h @@ -0,0 +1,40 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* SWAP Buffer structure description */ + +#ifndef __BUFFER_DESCRIPTION_H__ +#define __BUFFER_DESCRIPTION_H__ + +#include "space_dep_types_and_def.h" + +struct swap_buffer { + struct swap_buffer* next_in_queue; // Next buffer in queue + size_t full_buffer_part; // Buffer length + swap_subbuffer_ptr buffer; // Points to subbuffers virt mem(user) + // or to subbuffers first page(kernel) + buffer_rw_sync_type buffer_sync; // Buffer sync primitive +}; + +#endif /* __BUFFER_DESCRIPTION_H__ */ diff --git a/buffer/buffer_queue.c b/buffer/buffer_queue.c new file mode 100644 index 00000000..7869f106 --- /dev/null +++ b/buffer/buffer_queue.c @@ -0,0 +1,680 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* SWAP buffer queues implementation */ + +/* For all memory allocation/deallocation operations, except buffer memory + * allocation/deallocation should be used + * memory_allocation(size_t memory_size) + * memory_free(void* ptr) + * defines. + * For subbuffer allocation/deallocation operations should be used + * buffer_allocation(size_t subbuffer_size) + * buffer_free(void *ptr, size_t subbuffer_size) + * To get buffer pointer for any usage, EXCEPT ALLOCATION AND DEALLOCATION + * use the following define: + * buffer_pointer(void *ptr_to_buffer_element_of_swap_buffer_structure) + * DO NOT USE SUBBUFFER PTR IN STRUCT SWAP_BUFFER WITHOUT THIS DEFINE! + * It will be ok for user space, but fail in kernel space. + * + * See space_dep_types_and_def.h for details */ + + + +#include "buffer_queue.h" +#include "buffer_description.h" +#include "swap_buffer_to_buffer_queue.h" +#include "space_dep_operations.h" + +typedef struct swap_buffer* write_start_ptr_type; +typedef struct swap_buffer* write_end_ptr_type; +typedef struct swap_buffer* read_start_ptr_type; +typedef struct swap_buffer* read_end_ptr_type; + +static write_start_ptr_type queue_write_start_ptr = NULL; //Points to the + //write queue first + //element +static write_end_ptr_type queue_write_end_ptr = NULL; //Points to the + //write queue last + //element +static read_start_ptr_type queue_read_start_ptr = NULL; //Points to the read + //queue first + //element +static read_end_ptr_type queue_read_end_ptr = NULL; //Points to the read + //queue last element +static struct swap_buffer** queue_busy = NULL; //Pointers array. Points + //to occupied buffers +static unsigned int queue_busy_last_element; //Store last occupied + //element in queue_busy +static unsigned int queue_subbuffer_count = 0; //Subbuffers count +static size_t queue_subbuffer_size = 0; //Subbuffers size +static buffer_access_sync_type buffer_read_sync; //add_to_read_list and + //get_from_read_list + //sync +static buffer_access_sync_type buffer_write_sync; //add_to_write_list and + //get_from_write_list + //sync +static buffer_access_sync_type buffer_busy_sync; //add_to_busy_list and + //remove_from_busy_list + //sync +static int pages_order_in_subbuffer = 0; //Page count in one + //subbuffer + + +int buffer_queue_allocation(const size_t subbuffer_size, + const unsigned int subbuffers_count) +{ + int result = 0; + int i; + + /* 0 - ok + * -1 - memory for queue_busy wasn't allocated + * -2 - memory for swap_buffer structure wasn't allocated + * -3 - memory for buffer wasn't allocated + * -4 - semaphore cannot be inited + * -5 - sync primitives cannot be inited + */ + + /* Static varibles initialization */ + queue_subbuffer_size = subbuffer_size; + queue_subbuffer_count = subbuffers_count; + queue_busy_last_element = 0; + + /* Set variable pages_in_subbuffer. It is used for allocation and + * deallocation memory pages and its value is returned from + * swap_buffer_get() and contains page count in one subbuffer. + * All this useful only in kernel space. In userspace it is dummy.*/ + set_pages_order_in_subbuffer(queue_subbuffer_size); + + /* Sync primitives initialization */ + if (buffer_access_init(&buffer_read_sync)) { + result = -5; + return result; + } + if (buffer_access_init(&buffer_write_sync)) { + result = -5; + return result; + } + if (buffer_access_init(&buffer_busy_sync)) { + result = -5; + return result; + } + + /* Memory allocation for queue_busy */ + queue_busy = memory_allocation(sizeof(struct swap_buffer*) * + queue_subbuffer_count); + + if (!queue_busy) { + result = -1; + return result; + } + + /* Memory allocation for swap_buffer structures */ + /* Allocation for first structure. */ + + queue_write_start_ptr = memory_allocation(sizeof(struct swap_buffer)); + + if (!queue_write_start_ptr) { + result = -2; + memory_free(queue_busy); + queue_busy = NULL; + return result; + } + queue_write_end_ptr = queue_write_start_ptr; + + queue_write_end_ptr->next_in_queue = NULL; + queue_write_end_ptr->full_buffer_part = 0; + queue_write_end_ptr->buffer = buffer_allocation(queue_subbuffer_size); + if (!queue_write_end_ptr->buffer) { + print_err("Cannot allocate memory for buffer 1\n"); + result = -3; + memory_free(queue_busy); + memory_free(queue_write_start_ptr); + queue_write_start_ptr = NULL; + queue_busy = NULL; + + return result; + } + + print_msg(" Buffer allocated = 0x%x\n", (unsigned long)queue_write_end_ptr->buffer); + + if (buffer_rw_init(&queue_write_end_ptr->buffer_sync) != 0) { + result = -4; + memory_free(queue_busy); + queue_busy = NULL; + memory_free(queue_write_start_ptr); + queue_write_start_ptr = NULL; + return result; + } + + /* Buffer initialization */ + memset(buffer_address(queue_write_end_ptr->buffer), 0, queue_subbuffer_size); + + /* Allocation for other structures. */ + for (i = 1; i < queue_subbuffer_count; i++) { + queue_write_end_ptr->next_in_queue = memory_allocation(sizeof(struct swap_buffer)); + if (!queue_write_end_ptr->next_in_queue) { + /* Free all previously allocated memory */ + int j; + struct swap_buffer *clean_tmp_struct = queue_write_start_ptr; + + result = -2; + for (j = 0; j < i; j++) { + clean_tmp_struct = queue_write_start_ptr; + if (queue_write_start_ptr != queue_write_end_ptr) { + queue_write_start_ptr = queue_write_start_ptr->next_in_queue; + } + buffer_free(clean_tmp_struct->buffer,queue_subbuffer_size); + memory_free(clean_tmp_struct); + } + queue_write_end_ptr = NULL; + queue_write_start_ptr = NULL; + memory_free(queue_busy); + queue_busy = NULL; + return result; + } + + /* Now next queue_write_end_ptr is next */ + queue_write_end_ptr = queue_write_end_ptr->next_in_queue; + + queue_write_end_ptr->next_in_queue = NULL; + queue_write_end_ptr->full_buffer_part = 0; + queue_write_end_ptr->buffer = buffer_allocation(queue_subbuffer_size); + if (!queue_write_end_ptr->buffer) { + /* Free all previously allocated memory */ + int j; + struct swap_buffer *clean_tmp_struct = queue_write_start_ptr; + + result = -3; + print_err("Cannot allocate memory for buffer %d\n", i+1); + + for (j = 0; j < i; j++) { + clean_tmp_struct = queue_write_start_ptr; + if (queue_write_start_ptr != queue_write_end_ptr) { + queue_write_start_ptr = queue_write_start_ptr->next_in_queue; + buffer_free(clean_tmp_struct->buffer, queue_subbuffer_size); + } + memory_free(clean_tmp_struct); + } + queue_write_end_ptr = NULL; + queue_write_start_ptr = NULL; + memory_free(queue_busy); + queue_busy = NULL; + return result; + } + + print_msg(" Buffer allocated = 0x%x, pages_order = %d\n", (unsigned long)queue_write_end_ptr->buffer, pages_order_in_subbuffer); + + if (buffer_rw_init(&queue_write_end_ptr->buffer_sync) != 0) { + /* Free all previously allocated memory */ + int j; + struct swap_buffer *clean_tmp_struct = queue_write_start_ptr; + + result = -4; + for (j = 0; j < i; j++) { + clean_tmp_struct = queue_write_start_ptr; + if (queue_write_start_ptr != queue_write_end_ptr) { + queue_write_start_ptr = queue_write_start_ptr->next_in_queue; + } + buffer_free(clean_tmp_struct->buffer, queue_subbuffer_size); + memory_free(clean_tmp_struct); + } + queue_write_end_ptr = NULL; + queue_write_start_ptr = NULL; + memory_free(queue_busy); + queue_busy = NULL; + return result; + } + + /* Buffer initialization */ + memset(buffer_address(queue_write_end_ptr->buffer), 0, + queue_subbuffer_size); + } + + return result; +} + +int buffer_queue_free(void) +{ + int result = 0; + struct swap_buffer* tmp = NULL; + + /* 0 - ok + * <0 - set_all_to_read_list() error + */ + + //TODO Lock read list semaphore to prevent getting subbuffer from read list + /* Set all write buffers to read list */ + result = set_all_to_read_list(); + + if (result < 0) { + return result; + } + + /* Free buffers and structures memory that are in read list */ + while (queue_read_start_ptr) { + tmp = queue_read_start_ptr; + queue_read_start_ptr = queue_read_start_ptr->next_in_queue; + buffer_free(tmp->buffer, queue_subbuffer_size); + memory_free(tmp); + } + + /* Free busy_list */ + memory_free(queue_busy); + queue_busy = NULL; + + queue_subbuffer_size = 0; + queue_subbuffer_count = 0; + queue_read_start_ptr = NULL; + queue_read_end_ptr = NULL; + queue_write_start_ptr = NULL; + queue_write_end_ptr = NULL; + + return result; +} + +static unsigned int is_buffer_enough(struct swap_buffer* subbuffer, size_t size) +{ + return ((queue_subbuffer_size-subbuffer->full_buffer_part) >= size) ? 1 : 0; +} + +/* Get first subbuffer from read list */ +struct swap_buffer* get_from_read_list(void) +{ + struct swap_buffer* result = NULL; + + /* Lock read sync primitive */ + if (buffer_access_lock(&buffer_read_sync)) { + return NULL; + } + + if (queue_read_start_ptr == NULL) { + result = NULL; + goto get_from_read_list_unlock; + } + + result = queue_read_start_ptr; + + /* If this is the last readable buffer, queue_read_start_ptr next time will + * points to NULL and that case is handled in the beginning of function + */ + if (queue_read_start_ptr == queue_read_end_ptr) { + queue_read_end_ptr = NULL; + } + queue_read_start_ptr = queue_read_start_ptr->next_in_queue; + +get_from_read_list_unlock: + /* Unlock read sync primitive */ + if (buffer_access_unlock(&buffer_read_sync)) { + return NULL; + } + + return result; +} + +/* Add subbuffer to read list */ +int add_to_read_list(struct swap_buffer* subbuffer) +{ + int result = 0; + + /* 0 - ok + * 1 - cannot lock + * 2 - cannot unlock */ + + /* Lock read sync primitive */ + if (buffer_access_lock(&buffer_read_sync)) { + result = 1; + return result; + } + + // TODO Sanitization? + if (!queue_read_start_ptr) { + queue_read_start_ptr = subbuffer; + } + + if (queue_read_end_ptr) { + queue_read_end_ptr->next_in_queue = subbuffer; + + queue_read_end_ptr = queue_read_end_ptr->next_in_queue; + } else { + queue_read_end_ptr = subbuffer; + } + queue_read_end_ptr->next_in_queue = NULL; + + /* Unlock read sync primitive */ + if (buffer_access_unlock(&buffer_read_sync)) { + result = 2; + return result; + } + + return result; +} + +/* Call add to read list and callback function from driver module */ +int add_to_read_list_with_callback(struct swap_buffer* subbuffer) +{ + int result = 0; + + result = add_to_read_list(subbuffer); + // TODO Handle ret value + swap_buffer_callback(subbuffer); + + return result; +} + +/* Get first writable subbuffer from write list */ +struct swap_buffer* get_from_write_list(size_t size) +{ + struct swap_buffer *result = NULL; + + /* Callbacks are called at the end of the function to prevent deadlocks */ + struct swap_buffer *queue_callback_start_ptr = NULL; + struct swap_buffer *queue_callback_end_ptr = NULL; + struct swap_buffer *tmp_buffer = NULL; + + /* Lock write list sync primitive */ + if (buffer_access_lock(&buffer_write_sync)) { + return NULL; + } + + while (queue_write_start_ptr) { + /* If start points to NULL => list is empty => exit */ + if (!queue_write_start_ptr) { + result = NULL; + goto get_from_write_list_unlock; + } + + /* Get semaphore value. Useful only if we want buffer to write to + * several buffers the same time + * + * We're trying to lock semaphore, and if it is successful, unlocking + * it. Otherwise, going to the next step. */ + if (buffer_rw_lock(&queue_write_start_ptr->buffer_sync) != 0) { + // TODO HOW? HOW is it possible to get there?! + result = queue_write_start_ptr; + /* If we reached end of the list */ + if (queue_write_start_ptr == queue_write_end_ptr) { + queue_write_end_ptr = NULL; + } + /* Move start write pointer */ + queue_write_start_ptr = queue_write_start_ptr->next_in_queue; + + /* Add to callback list */ + if (!queue_callback_start_ptr) { + queue_callback_start_ptr = result; + } + if (queue_callback_end_ptr) { + queue_callback_end_ptr->next_in_queue = result; + } + queue_callback_end_ptr = result; + queue_callback_end_ptr->next_in_queue = NULL; + + result = NULL; + continue; + } + buffer_rw_unlock(&queue_write_start_ptr->buffer_sync); + +// TODO Do something + + if (is_buffer_enough(queue_write_start_ptr, size)) { + result = queue_write_start_ptr; + break; + } else { + /* If size is not enough, subbuffers goes to read list */ + result = queue_write_start_ptr; + /* If we reached end of the list */ + if (queue_write_start_ptr == queue_write_end_ptr) { + queue_write_end_ptr = NULL; + } + /* Move start write pointer */ + queue_write_start_ptr = queue_write_start_ptr->next_in_queue; + + /* Add to callback list */ + if (!queue_callback_start_ptr) { + queue_callback_start_ptr = result; + } + if (queue_callback_end_ptr) { + queue_callback_end_ptr->next_in_queue = result; + } + queue_callback_end_ptr = result; + queue_callback_end_ptr->next_in_queue = NULL; + + result = NULL; + } + } + + /* Lock writing semaphore */ + if (result) { + if (buffer_rw_lock(&result->buffer_sync)) { + result = NULL; + goto get_from_write_list_unlock; + } + } + +get_from_write_list_unlock: + /* Unlock write list sync primitive */ + if (buffer_access_unlock(&buffer_write_sync)) { + if (result) { + buffer_rw_unlock(&result->buffer_sync); + } + return NULL; + } + + /* Adding buffers to read list and calling callbacks */ + for (tmp_buffer = NULL; queue_callback_start_ptr; ) { + + if (queue_callback_start_ptr == queue_callback_end_ptr) { + queue_callback_end_ptr = NULL; + } + tmp_buffer = queue_callback_start_ptr; + queue_callback_start_ptr = queue_callback_start_ptr->next_in_queue; + + add_to_read_list_with_callback(tmp_buffer); + } + + return result; +} + +/* Add subbuffer to write list */ +int add_to_write_list(struct swap_buffer* subbuffer) +{ + /* 0 - ok + * -1 - cannot lock + * -2 - cannot unlock */ + + if (buffer_access_lock(&buffer_write_sync)) { + return -1; + } + + /* Reinitialize */ + memset(buffer_address(subbuffer->buffer), 0, queue_subbuffer_size); + subbuffer->full_buffer_part = 0; + + if (!queue_write_start_ptr) { + queue_write_start_ptr = subbuffer; + } + + if (queue_write_end_ptr) { + queue_write_end_ptr->next_in_queue = subbuffer; + queue_write_end_ptr = queue_write_end_ptr->next_in_queue; + } else { + queue_write_end_ptr = subbuffer; + } + queue_write_end_ptr->next_in_queue = NULL; + + if (buffer_access_unlock(&buffer_write_sync)) { + return -2; + } + + return 0; +} + +/* Add subbuffer to busy list when it is read from out of the buffer */ +int add_to_busy_list(struct swap_buffer* subbuffer) +{ + /* 0 - ok + * -1 - cannot lock + * -2 - cannot unlock */ + + /* Lock busy sync primitive */ + if (buffer_access_lock(&buffer_busy_sync)) { + return -1; + } + + subbuffer->next_in_queue = NULL; + queue_busy[queue_busy_last_element] = subbuffer; + queue_busy_last_element += 1; + + /* Unlock busy sync primitive */ + if (buffer_access_unlock(&buffer_busy_sync)) { + return -2; + } + + return 0; +} + +/* Remove subbuffer from busy list when it is released */ +int remove_from_busy_list(struct swap_buffer* subbuffer) +{ + int result = -1; // For sanitization + int i; + + /* 0 - ok + * -1 - no such buffer in queue_busy list + * -2 - cannot lock + * -3 - cannot unlock + */ + + /* Lock busy list sync primitive */ + if (buffer_access_lock(&buffer_busy_sync)) { + result = -2; + return result; + } + + /* Sanitization and removing */ + for (i = 0; i < queue_busy_last_element; i++) { + if (queue_busy[i] == subbuffer) { + /* Last element goes here and length is down 1 */ + queue_busy[i] = queue_busy[queue_busy_last_element - 1]; + queue_busy_last_element -= 1; + result = 0; + break; + } + } + + /* Unlock busy list sync primitive */ + if (buffer_access_unlock(&buffer_busy_sync)) { + result = -3; + return result; + } + + return result; +} + +/* Get subbuffers count in read list */ +/* XXX Think about locks */ +int get_full_buffers_count(void) +{ + int result = 0; + struct swap_buffer* buffer = queue_read_start_ptr; + + /* >=0 - buffers count + */ + + while (buffer && buffer->full_buffer_part) { + result += 1; + buffer = buffer->next_in_queue; + } + + return result; +} + +/* Set all subbuffers in write list to read list */ +int set_all_to_read_list(void) +{ + int result = 0; + struct swap_buffer *buffer = queue_write_start_ptr; + + /* 0 - ok + * -1 - sem_wait() error + * -2 - sem_post() error + * -3 - problems with locking sync primitives + * -4 - problems with unlocking sync primitives + */ + + /* Locking write sync primitive */ + if (buffer_access_lock(&buffer_write_sync)) { + result = -3; + return result; + } + + while (queue_write_start_ptr) { + /* Waiting till semaphore should be posted */ + +// TODO To think: It's not bad as it is, but maybe it would be better locking +// semaphore while changing its list? (Not bad now, cause buffer should have +// already been stopped). + + if (buffer_rw_lock(&buffer->buffer_sync)) { + result = -1; + goto set_all_to_read_list_unlock; + } + + if (buffer_rw_unlock(&buffer->buffer_sync)) { + result = -2; + goto set_all_to_read_list_unlock; + } + + buffer = queue_write_start_ptr; + + /* If we reached end of the list */ + if (queue_write_start_ptr == queue_write_end_ptr) { + queue_write_end_ptr = NULL; + } + queue_write_start_ptr = queue_write_start_ptr->next_in_queue; + + add_to_read_list(buffer); + } + +set_all_to_read_list_unlock: + /* Unlocking write primitive */ + if (buffer_access_unlock(&buffer_write_sync)) { + result = -4; + } + return result; +} + +/* Get subbuffers count in busy list */ +/* XXX Think abount lock */ +int get_busy_buffers_count(void) +{ + return queue_busy_last_element; +} + +/* Get memory pages count in subbuffer */ +int get_pages_in_subbuffer(void) +{ +/* Return 1 if pages order 0, or 2 of power pages_order_in_subbuffer otherwise */ + return (pages_order_in_subbuffer) ? 2 << (pages_order_in_subbuffer - 1) : 1; +} diff --git a/buffer/buffer_queue.h b/buffer/buffer_queue.h new file mode 100644 index 00000000..daf8044f --- /dev/null +++ b/buffer/buffer_queue.h @@ -0,0 +1,46 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* SWAP Buffer queues interface */ + +#ifndef __BUFFER_QUEUE_HEADER__ +#define __BUFFER_QUEUE_HEADER__ + +#include "buffer_description.h" + +int buffer_queue_allocation(size_t subbuffer_size, unsigned int subbuffers_count); +int buffer_queue_free(void); +struct swap_buffer* get_from_write_list(size_t size); +struct swap_buffer* get_from_read_list(void); +int add_to_write_list(struct swap_buffer* subbuffer); +int add_to_read_list(struct swap_buffer* subbuffer); +int add_to_busy_list(struct swap_buffer* subbuffer); +int remove_from_busy_list(struct swap_buffer* subbuffer); +int get_full_buffers_count(void); + +int set_all_to_read_list(void); +int get_busy_buffers_count(void); +int get_pages_in_subbuffer(void); + +#endif /* __BUFFER_QUEUE_HEADER__ */ diff --git a/buffer/space_dep_operations.c b/buffer/space_dep_operations.c new file mode 100644 index 00000000..77a97cfe --- /dev/null +++ b/buffer/space_dep_operations.c @@ -0,0 +1,148 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* Space-depended operations: memory allocations and synchronizations. + * This makes swap_buffer buildable both as a kernel module and a library. */ + +#ifdef BUFFER_FOR_USER + +#include <stdio.h> +#include <string.h> +#include <stddef.h> +#include <stdlib.h> + +#else /* BUFFER_FOR_USER */ + +#include <linux/module.h> +#include <linux/slab.h> + +unsigned long flags; // Flags for spinlocks + +#endif /* BUFFER_FOR_USER */ + + +#include "space_dep_operations.h" + + + +/* For access sync primitives we use pthread_mutex for user space and + * spin_locks for kernel space */ + +inline int buffer_access_init(buffer_access_sync_type *buffer_sync) +{ +#ifdef BUFFER_FOR_USER + return pthread_mutex_init(buffer_sync, NULL); +#else /* BUFFER_FOR_USER */ + spin_lock_init(buffer_sync); + return 0; +#endif /* BUFFER_FOR_USER */ +} + +inline int buffer_access_lock(buffer_access_sync_type *buffer_sync) +{ +#ifdef BUFFER_FOR_USER + return pthread_mutex_lock(buffer_sync); +#else /* BUFFER_FOR_USER */ + spin_lock_irqsave(buffer_sync, flags); + return 0; +#endif /* BUFFER_FOR_USER */ +} + +inline int buffer_access_unlock(buffer_access_sync_type *buffer_sync) +{ +#ifdef BUFFER_FOR_USER + return pthread_mutex_unlock(buffer_sync); +#else /* BUFFER_FOR_USER */ + spin_unlock_irqrestore(buffer_sync, flags); + return 0; +#endif /* BUFFER_FOR_USER */ +} + + +/* For buffer RW sync primitives in kernel space we use spinlocks as we do it + * for access sync primitives, so, if building for kernel, buffer_access + * functions are called. */ + +inline int buffer_rw_init(buffer_rw_sync_type *buffer_rw) +{ +#ifdef BUFFER_FOR_USER + return sem_init(buffer_rw, 0, 1); +#else /* BUFFER_FOR_USER */ + return buffer_access_init(buffer_rw); +#endif /* BUFFER_FOR_USER */ +} + +inline int buffer_rw_lock(buffer_rw_sync_type *buffer_rw) +{ +#ifdef BUFFER_FOR_USER + return sem_wait(buffer_rw); +#else /* BUFFER_FOR_USER */ + return buffer_access_lock(buffer_rw); +#endif /* BUFFER_FOR_USER */ +} + +inline int buffer_rw_unlock(buffer_rw_sync_type *buffer_rw) +{ +#ifdef BUFFER_FOR_USER + return sem_post(buffer_rw); +#else /* BUFFER_FOR_USER */ + return buffer_access_unlock(buffer_rw); +#endif /* BUFFER_FOR_USER */ +} + +#ifndef BUFFER_FOR_USER + +inline unsigned int nearest_power_of_two(unsigned int number) +{ + unsigned int result = 0; + unsigned int two_to_the_power = 1; + + /* If aligned_size == PAGE_SIZE we need only one page, so return 0 */ + if (number == 1) { + return result; + } + + while (two_to_the_power < number) { + two_to_the_power <<= 1; + result++; + } + + return result; +} + +inline unsigned int get_order_for_alloc_pages(size_t memory_size) +{ + /* First evaluate remainder of the division memory_size by PAGE_SIZE. + * If memory_size is divisible by PAGE_SIZE, then remainder equals 0. */ + size_t remainder = (memory_size % PAGE_SIZE) ? + (memory_size % PAGE_SIZE) : PAGE_SIZE; + + /* Align memory_size to the PAGE_SIZE. aligned_size >= memory_size */ + size_t aligned_size = memory_size + (PAGE_SIZE - remainder); + + return nearest_power_of_two(aligned_size / PAGE_SIZE); +} + +#endif /* BUFFER_FOR_USER */ + diff --git a/buffer/space_dep_operations.h b/buffer/space_dep_operations.h new file mode 100644 index 00000000..d8d789d7 --- /dev/null +++ b/buffer/space_dep_operations.h @@ -0,0 +1,50 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* Space-depended operations file header. + * This makes swap_buffer buildable both as a kernel module and a library. */ + +#ifndef __SPACE_DEPENDED_OPERATIONS_FILE_HEADER__ +#define __SPACE_DEPENDED_OPERATIONS_FILE_HEADER__ + +#include "space_dep_types_and_def.h" + +inline int buffer_access_init(buffer_access_sync_type *buffer_sync);//Buffer + //access + //sync + //primitives + //init +inline int buffer_access_lock(buffer_access_sync_type *buffer_sync);//Lock sync + //primitive +inline int buffer_access_unlock(buffer_access_sync_type *buffer_sync);//Unlock + //sync + //primitive +inline int buffer_rw_init(buffer_rw_sync_type *buffer_rw); //Init read-write + //sync primitive +inline int buffer_rw_lock(buffer_rw_sync_type *buffer_rw); //Lock read-write + //sync primitive +inline int buffer_rw_unlock(buffer_rw_sync_type *buffer_rw); //Unlock read-write + //sync primitive + +#endif /* __SPACE_DEPENDED_OPERATIONS_FILE_HEADER__ */ diff --git a/buffer/space_dep_types_and_def.h b/buffer/space_dep_types_and_def.h new file mode 100644 index 00000000..6965bcdc --- /dev/null +++ b/buffer/space_dep_types_and_def.h @@ -0,0 +1,188 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* Space-depended types and defines. + * This makes swap_buffer buildable both as a kernel module and as a library.*/ + +#ifndef __SPACE_DEPENDEND_TYPES_AND_DEFINES_FILE_HEADER__ +#define __SPACE_DEPENDEND_TYPES_AND_DEFINES_FILE_HEADER__ + +#ifdef BUFFER_FOR_USER + +#include <semaphore.h> +#include <pthread.h> +#include <string.h> +#include <stdio.h> +#include <string.h> +#include <stddef.h> +#include <stdlib.h> + +#else /* BUFFER_FOR_USER */ + +#include <linux/semaphore.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/mm.h> + +#endif /*BUFFER_FOR_USER */ + + +/* Functions for alloc_pages */ +#ifndef BUFFER_FOR_USER + +inline unsigned int nearest_power_of_two(unsigned int number); +inline unsigned int get_order_for_alloc_pages(size_t memory_size); + +#endif /* BUFFER_FOR_USER */ + + + +/* Synchronization primitives with the same interface for different context */ +#ifdef BUFFER_FOR_USER + +typedef sem_t buffer_rw_sync_type; // Read-write sync +typedef pthread_mutex_t buffer_access_sync_type;// Pointer manipulations sync + +#else /* BUFFER_FOR_USER */ + +typedef spinlock_t buffer_rw_sync_type; // Read-write sync +typedef spinlock_t buffer_access_sync_type; // Pointer manipulations sync + +#endif /*BUFFER_FOR_USER */ + + +/* Subbuffer type */ +#ifdef BUFFER_FOR_USER + +typedef void* swap_subbuffer_ptr; + +#else /* BUFFER_FOR_USER */ + +/* If buffer supposed to work in kernel space, it's very useful to + * store buffer page struct ptr */ +typedef struct page* swap_subbuffer_ptr; + +#endif /* BUFFER_FOR_USER */ + + +/* Memory and buffer operations */ +#ifdef BUFFER_FOR_USER + +#define memory_allocation(memory_size) malloc(memory_size) +#define memory_free(ptr) free(ptr) +#define buffer_allocation(memory_size) malloc(memory_size) +#define buffer_free(ptr, subbuf_size) free(ptr) +#define buffer_address(buffer_ptr) buffer_ptr + +#else /* BUFFER_FOR_USER */ + +#define memory_allocation(memory_size) kmalloc(memory_size, GFP_KERNEL) +#define memory_free(ptr) kfree(ptr) +#define buffer_allocation(memory_size) \ + alloc_pages(GFP_KERNEL, (pages_order_in_subbuffer >= 0) ? \ + pages_order_in_subbuffer : \ + get_order_for_alloc_pages(memory_size)) +#define buffer_free(ptr, subbuf_size) \ + __free_pages(ptr, (pages_order_in_subbuffer >= 0) ? \ + pages_order_in_subbuffer : \ + get_order_for_alloc_pages(subbuf_size)) +// TODO Check whether it is correct for several pages +#define buffer_address(buffer_ptr) page_address(buffer_ptr) + +#endif /* BUFFER_FOR_USER */ + + +/* Set pages_order_in_subbuffer variable. Used only in kernel space */ +#ifdef BUFFER_FOR_USER + +#define set_pages_order_in_subbuffer(memory_size) \ + pages_order_in_subbuffer = 0 + +#else /* BUFFER_FOR_USER */ + +#define set_pages_order_in_subbuffer(memory_size) \ + pages_order_in_subbuffer = get_order_for_alloc_pages(memory_size) + +#endif /* BUFFER_FOR_USER */ + + +/* Kernel module specific functions */ +#ifdef BUFFER_FOR_USER + +#define SWAP_BUFFER_MODULE_INFORMATION + +#else /* BUFFER_FOR_USER */ + +#define SWAP_BUFFER_MODULE_INFORMATION \ +static int __init swap_buffer_module_init(void) \ +{ \ + printk(KERN_NOTICE "SWAP_BUFFER : Buffer module initialized\n"); \ + return 0; \ +} \ + \ +static void __exit swap_buffer_module_exit(void) \ +{ \ + printk(KERN_NOTICE "SWAP_BUFFER : Buffer module unintialized\n"); \ +} \ + \ +module_init(swap_buffer_module_init); \ +module_exit(swap_buffer_module_exit); + +#endif /* BUFFER_FOR_USER */ + + +/* Message printing */ +#ifdef BUFFER_FOR_USER + +#define print_debug(msg, args...) \ + printf("SWAP_BUFFER DEBUG : " msg, ##args) +#define print_msg(msg, args...) \ + printf("SWAP_BUFFER : " msg, ##args) +#define print_warn(msg, args...) \ + printf("SWAP_BUFFER WARNING : " msg, ##args) +#define print_err(msg, args...) \ + printf("SWAP_BUFFER ERROR : " msg, ##args) +#define print_crit(msg, args...) \ + printf("SWAP_BUFFER CRITICAL : " msg, ##args) + +#else /* BUFFER_FOR_USER */ + +#define print_debug(msg, args...) \ + printk(KERN_DEBUG "SWAP_BUFFER DEBUG : " msg, ##args) +#define print_msg(msg, args...) \ + printk(KERN_INFO "SWAP_BUFFER : " msg, ##args) +#define print_warn(msg, args...) \ + printk(KERN_WARNING "SWAP_BUFFER WARNING : " msg, ##args) +#define print_err(msg, args...) \ + printk(KERN_ERR "SWAP_BUFFER ERROR : " msg, ##args) +#define print_crit(msg, args...) \ + printk(KERN_CRIT "SWAP_BUFFER CRITICAL : " msg, ##args) + +#endif /* BUFFER_FOR_USER */ + +#endif /* __SPACE_DEPENDEND_TYPES_AND_DEFINES_FILE_HEADER__ */ diff --git a/buffer/swap_buffer_module.c b/buffer/swap_buffer_module.c new file mode 100644 index 00000000..539ec417 --- /dev/null +++ b/buffer/swap_buffer_module.c @@ -0,0 +1,317 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* SWAP Buffer interface implementation */ + +#include "swap_buffer_module.h" +#include "buffer_queue.h" +#include "buffer_description.h" +#include "space_dep_operations.h" + +#define BUFFER_WORK 1 +#define BUFFER_STOP 0 + +typedef int(*subbuffer_callback_type)(void*); +typedef unsigned char buffer_status; + +static subbuffer_callback_type subbuffer_callback = NULL; //Callback, called + //when new readalbe + //subbuffer appears +static size_t subbuffers_size = 0; //Subbuffers size +static unsigned int subbuffers_num = 0; //Subbuffres count +static buffer_status swap_buffer_status = BUFFER_STOP; //Buffer status + +//static buffer_access_sync_type buffer_sync; + +//TODO Swap restart + +static inline int are_two_regions_overlap(const void* region1, + const void* region2, size_t size) +{ + int i; + + for (i = 0; i < size; i++) { + if ((region1 + i == region2) || (region1 + i == region2)) { + return 1; + } + } + + return 0; +} + +int swap_buffer_init(size_t subbuffer_size, unsigned int nr_subbuffers, + int (*subbuffer_full_callback)(void)) +{ + int result = 0; + + /* >0 - mem pages in one subbuffer + */ + + // TODO Different initialization for first one and initialization after stop + // TODO Test if wrong function type + subbuffer_callback = (subbuffer_callback_type)subbuffer_full_callback; + subbuffers_size = subbuffer_size; + subbuffers_num = nr_subbuffers; + + result = buffer_queue_allocation(subbuffers_size, subbuffers_num); + if (result < 0) { + return result; + } + + swap_buffer_status = BUFFER_WORK; + result = get_pages_in_subbuffer(); + + return result; +} + +int swap_buffer_uninit(void) +{ + int result; + + /* 0 - ok + * -1 - not all buffers released + * 2 - mutex_destroy failed + */ + + /* Stop swap_buffer */ + swap_buffer_status = BUFFER_STOP; + + /* Checking whether all buffers are released */ + if (get_busy_buffers_count()) { + result = -1; + return result; + } + + /* Free */ + result = buffer_queue_free(); + + subbuffer_callback = NULL; + subbuffers_size = 0; + subbuffers_num = 0; + +//TODO +/*#ifdef BUFFER_FOR_USER + if (pthread_mutex_destroy(&buffer_sync)) { + result = 2; + return result; + } + +// TODO Think for spinlock*/ +//#endif /* BUFFER_FOR_USER */ + + return result; +} + +ssize_t swap_buffer_write(size_t size, void* data) +{ + int result = 0; + struct swap_buffer* buffer_to_write = NULL; + + /* >0 - ok, written size + * -1 - no buffers in write list + * -2 - wrong size + * -3 - swap_buffer stopped + * -4 - cannot lock semaphore + * -5 - cannot unlock semaphore + * -6 - regions are overlapping + */ + + /* Check buffer status */ + if (!(swap_buffer_status & BUFFER_WORK)) { + result = -3; + return result; + } + + /* Size sanitization */ + if ((size > subbuffers_size) || (size == 0)) { + result = -2; + return result; + } + + /* Get next write buffer and occupying semaphore */ + buffer_to_write = get_from_write_list(size); + if (!buffer_to_write) { + result = -1; + return result; + } + + /* Check for overlapping */ + if (are_two_regions_overlap(buffer_address(buffer_to_write->buffer) + + buffer_to_write->full_buffer_part, data, + size)) { + result = -6; + goto buf_write_sem_post; + } + + /* Copy data to buffer */ + /* XXX Think of using memmove instead */ + memcpy((void*)((unsigned long)(buffer_address(buffer_to_write->buffer)) + + buffer_to_write->full_buffer_part), data, size); + + /* Inc buffer full part size */ + buffer_to_write->full_buffer_part += size; + + result = size; + + /* Unlock semaphpore (Locked in get_from_write_list()) */ +buf_write_sem_post: + if (buffer_rw_unlock(&buffer_to_write->buffer_sync)) { + result = -5; + return result; + } + + return result; +} + +int swap_buffer_get(struct swap_buffer** subbuffer) +{ + int result = 0; + struct swap_buffer* buffer_to_read = NULL; + + /* >0 - page count in subbuffer (in kernel) or 0 (in user) + * -1 - no buffer for reading + * -2 - problems with add_to_busy_list + */ + + /* Get next read buffer */ + buffer_to_read = get_from_read_list(); + if (!buffer_to_read) { + result = -1; + return result; + } + + /* Add to busy list */ + buffer_to_read->next_in_queue = NULL; + if (add_to_busy_list(buffer_to_read) < 0) { + result = -2; + return result; + } + + // TODO Useless check + if (!result) { + *subbuffer = buffer_to_read; + } + + return get_pages_in_subbuffer(); +} + +int swap_buffer_release(struct swap_buffer** subbuffer) +{ + int result = 0; + + /* 0 - ok + * -1 - can't remove from busy list! + * -2 - can't add to write list + */ + + /* Remove from busy list (includes sanitization) */ + if (remove_from_busy_list(*subbuffer) < 0) { + result = -1; + return result; + } + + /* Add to write list */ + if (add_to_write_list(*subbuffer) < 0) { + result = -2; + return result; + } + + return result; +} + +int swap_buffer_flush(struct swap_buffer ***subbuffers) +{ + int result = 0; + + /* >=0 - buffers count + * <0 - set_all_to_read_list() error code + */ + + /* Stop swap_buffer */ + swap_buffer_status = BUFFER_STOP; + + /* Set all write buffers to read list */ + result = set_all_to_read_list(); + if (result < 0) { + return result; + } + + /* Get count of all full buffers */ + result = get_full_buffers_count(); + if (result <= 0) { + result = 0; + return result; + } + +// Relict code, not used now. You can just enjoy how it was some time before. +// +// /* Memory allocation for swap_buffer structures array. +// * Must be freed in module that called this one */ +// *subbuffers = memory_allocation(sizeof(struct swap_buffer*) * result); +// if (!(*subbuffers)) { +// result = -1; +// return result; +// } +// +// /* Adding all subbufers from read list to subbuffers array */ +// do { +// i++; +// (*subbuffers)[i] = get_from_read_list(); //TODO Are we need mutex? +// //Buffer stopped - nobody's +// //writing +// } while ((*subbuffers)[i]); + + return result; +} + +int swap_buffer_callback(void *buffer) +{ + int result; + + /* 0 - ok + * <0 - subbuffer_callback error + * -99 - subbuffer_callback is not registered */ + + if (!subbuffer_callback) { + return -99; + } + + result = subbuffer_callback(buffer); + if (result < 0) { + print_err("Callback error! Error code: %d\n", result); + } + + return result; +} + +#ifndef BUFFER_FOR_USER +EXPORT_SYMBOL_GPL(swap_buffer_init); +EXPORT_SYMBOL_GPL(swap_buffer_uninit); +EXPORT_SYMBOL_GPL(swap_buffer_write); +EXPORT_SYMBOL_GPL(swap_buffer_get); +EXPORT_SYMBOL_GPL(swap_buffer_release); +EXPORT_SYMBOL_GPL(swap_buffer_flush); +#endif /* BUFFER_FOR_USER */ + +SWAP_BUFFER_MODULE_INFORMATION diff --git a/buffer/swap_buffer_module.h b/buffer/swap_buffer_module.h new file mode 100644 index 00000000..38c91745 --- /dev/null +++ b/buffer/swap_buffer_module.h @@ -0,0 +1,48 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* SWAP Buffer interface description */ + +#ifndef __SWAP_BUFFER_HEADER_H__ +#define __SWAP_BUFFER_HEADER_H__ + +#include "buffer_description.h" + +int swap_buffer_init(size_t subbuffer_size, unsigned int nr_subbuffers, + int (*subbuffer_full_callback)(void)); + +int swap_buffer_uninit(void); +ssize_t swap_buffer_write(size_t size, void* data); + +int swap_buffer_get(struct swap_buffer **subbuffer); +int swap_buffer_release(struct swap_buffer **subbuffer); + +/* Takes pointer to array of subbuffers pointers. Supposed to be NULL, + * allocation occures in buf_flush. + * BE AWARE!!! Function returns: + * =<0 - IF IT FINISHED UNSUCCESSFUL + * >0 - count of readable buffers */ +int swap_buffer_flush(struct swap_buffer ***subbuffer); + +#endif /* __SWAP_BUFFER_HEADER_H__ */ diff --git a/buffer/swap_buffer_to_buffer_queue.h b/buffer/swap_buffer_to_buffer_queue.h new file mode 100644 index 00000000..91c2bbf6 --- /dev/null +++ b/buffer/swap_buffer_to_buffer_queue.h @@ -0,0 +1,27 @@ +/* + * SWAP Buffer Module + * modules/buffer/swap_buffer_module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2013 + * + * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement + * + */ + +/* SWAP Buffer interface for buffer queue */ + +void swap_buffer_callback(void* buffer); |