diff options
author | Alexander Aksenov <a.aksenov@samsung.com> | 2014-04-24 16:05:48 +0400 |
---|---|---|
committer | Alexander Aksenov <a.aksenov@samsung.com> | 2014-04-29 18:45:08 +0400 |
commit | 7aa5d1c3653e83743b71493420e18b07f5fe6175 (patch) | |
tree | 19136bc920666914e10707bbd098a3d324d3236b | |
parent | cffc6e626ea2375c840523d9de053feb12e6b1d5 (diff) | |
download | swap-modules-7aa5d1c3653e83743b71493420e18b07f5fe6175.tar.gz swap-modules-7aa5d1c3653e83743b71493420e18b07f5fe6175.tar.bz2 swap-modules-7aa5d1c3653e83743b71493420e18b07f5fe6175.zip |
[FEATURE] Dlog: dlog suspend module implement
Change-Id: Idd16fa1b40b128179d606b5eb0b3a16867520659
Signed-off-by: Alexander Aksenov <a.aksenov@samsung.com>
-rw-r--r-- | Kbuild | 7 | ||||
-rw-r--r-- | dlog_suspend/dentry.c | 45 | ||||
-rw-r--r-- | dlog_suspend/dentry.h | 7 | ||||
-rw-r--r-- | dlog_suspend/elf_parser.c | 269 | ||||
-rw-r--r-- | dlog_suspend/elf_parser.h | 7 | ||||
-rw-r--r-- | dlog_suspend/file_ops.c | 57 | ||||
-rw-r--r-- | dlog_suspend/file_ops.h | 15 | ||||
-rw-r--r-- | dlog_suspend/get_task.c | 198 | ||||
-rw-r--r-- | dlog_suspend/get_task.h | 14 | ||||
-rw-r--r-- | dlog_suspend/lib_handler.c | 102 | ||||
-rw-r--r-- | dlog_suspend/lib_handler.h | 10 | ||||
-rw-r--r-- | dlog_suspend/swap_dlog_suspend.c | 246 | ||||
-rw-r--r-- | dlog_suspend/swap_dlog_suspend.h | 9 | ||||
-rw-r--r-- | dlog_suspend/tasks_list.c | 78 | ||||
-rw-r--r-- | dlog_suspend/tasks_list.h | 14 | ||||
-rw-r--r-- | swap_module.c | 13 |
16 files changed, 1090 insertions, 1 deletions
@@ -46,6 +46,13 @@ swap-y := buffer/swap_buffer_module.o \ parser/msg_cmd.o \ parser/features.o \ parser/us_inst.o \ + dlog_suspend/swap_dlog_suspend.o \ + dlog_suspend/elf_parser.o \ + dlog_suspend/file_ops.o \ + dlog_suspend/get_task.o \ + dlog_suspend/tasks_list.o \ + dlog_suspend/dentry.o \ + dlog_suspend/lib_handler.o \ swap_module.o ifeq ($(CONFIG_KALLSYMS),y) diff --git a/dlog_suspend/dentry.c b/dlog_suspend/dentry.c new file mode 100644 index 00000000..73d1b282 --- /dev/null +++ b/dlog_suspend/dentry.c @@ -0,0 +1,45 @@ +#include <linux/namei.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <us_manager/sspt/sspt.h> + +#include "dentry.h" + +int check_dentry(struct task_struct * const task, struct dentry * const dentry) +{ + struct vm_area_struct *vma; + struct mm_struct *mm = task->mm; + + if (mm == NULL) { + return 0; + } + + down_read(&mm->mmap_sem); + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (check_vma(vma) && vma->vm_file->f_dentry == dentry) { + up_read(&mm->mmap_sem); + return 1; + } + } + up_read(&mm->mmap_sem); + + return 0; +} + +int get_dentry(char * const path, struct dentry **dentry) +{ + struct path st_path; + + *dentry = NULL; + if (kern_path(path, LOOKUP_FOLLOW, &st_path) != 0) { + printk("failed to lookup dentry for path %s!\n", path); + return -EINVAL; + } + + *dentry = st_path.dentry; + path_put(&st_path); + + return 0; +} + diff --git a/dlog_suspend/dentry.h b/dlog_suspend/dentry.h new file mode 100644 index 00000000..dff9172f --- /dev/null +++ b/dlog_suspend/dentry.h @@ -0,0 +1,7 @@ +#ifndef __DENTRY_H__ +#define __DENTRY_H__ + +int check_dentry(struct task_struct * const task, struct dentry * const dentry); +int get_dentry(char * const path, struct dentry **dentry); + +#endif /* __DENTRY_H__ */ diff --git a/dlog_suspend/elf_parser.c b/dlog_suspend/elf_parser.c new file mode 100644 index 00000000..3612c8cd --- /dev/null +++ b/dlog_suspend/elf_parser.c @@ -0,0 +1,269 @@ +#include <linux/string.h> +#include <linux/slab.h> + +#include "elf_parser.h" +#include "file_ops.h" + +#define EI_NIDENT 16 + +typedef unsigned int Elf32_Addr; +typedef unsigned int Elf32_Off; +typedef unsigned int Elf32_Sword; +typedef unsigned int Elf32_Word; +typedef unsigned short Elf32_Half; + + +struct Elf32_Ehdr { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +}; + +struct Elf32_Shdr { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +}; + +struct Elf32_Sym { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +}; + + +static Elf32_Off get_sections_header_offset(struct file *bin_p) +{ + Elf32_Off ret = 0; + loff_t off; + + off = file_seek(bin_p, offsetof(struct Elf32_Ehdr, e_shoff), SEEK_SET); + file_read(bin_p, (char *)&ret, sizeof(ret), off); + + return ret; +} + +static Elf32_Half get_sections_count(struct file *bin_p) +{ + Elf32_Half ret = 0; + loff_t off; + + off = file_seek(bin_p, offsetof(struct Elf32_Ehdr, e_shnum), SEEK_SET); + file_read(bin_p, (char *)&ret, sizeof(ret), off); + + return ret; +} + +static Elf32_Half get_sections_header_size(struct file *bin_p) +{ + Elf32_Half ret = 0; + loff_t off; + + off = file_seek(bin_p, offsetof(struct Elf32_Ehdr, e_shentsize), SEEK_SET); + file_read(bin_p, (char *)&ret, sizeof(ret), off); + + return ret; +} + +static Elf32_Half get_sections(struct Elf32_Shdr **sections, struct file *bin_p) +{ + Elf32_Off sec_off; + Elf32_Half sec_count; + Elf32_Half sec_size; + loff_t off; + + sec_off = get_sections_header_offset(bin_p); + sec_count = get_sections_count(bin_p); + sec_size = get_sections_header_size(bin_p); + + *sections = kmalloc(sec_size * sec_count, GFP_KERNEL); + if (*sections == NULL) { + printk("ERROR: Not enough memory!\n"); + return 0; + } + + off = file_seek(bin_p, sec_off, SEEK_SET); + file_read(bin_p, (char *)*sections, sec_size * sec_count, off); + + return sec_count; +} + +static Elf32_Word find_section_offset(struct Elf32_Shdr *sections, + Elf32_Half count, Elf32_Word type, + Elf32_Off *offset) +{ + unsigned short i; + + + for (i = 0; i < count; i++) + if (sections[i].sh_type == type) { + *offset = sections[i].sh_offset; + return sections[i].sh_size; + } + return 0; +} + +static Elf32_Half get_symbols(struct Elf32_Sym **syms, Elf32_Word size, + Elf32_Word offset, struct file *bin_p) +{ + Elf32_Word i_size = 0; + Elf32_Half syms_count; + loff_t off; + + *syms = kmalloc(size, GFP_KERNEL); + if (*syms == NULL) { + printk("ERROR: Not enough memory!\n"); + return 0; + } + off = file_seek(bin_p, offset, SEEK_SET); + file_read(bin_p, (char *)*syms, size, off); + + for (syms_count = 0; ; syms_count++) { + if (i_size >= size) + return syms_count; + i_size += sizeof(struct Elf32_Sym); + } + + /* Never get there */ + return 0; +} + +static Elf32_Addr find_addr_by_name_offset(struct Elf32_Sym *syms, + Elf32_Half syms_count, + Elf32_Word name_offset) +{ + Elf32_Half i; + + for (i = 0; i < syms_count; i++) + if (syms[i].st_name == name_offset) + return syms[i].st_value; + + /* No symbol with such name offset */ + + return 0; + +} + +static int compare_names(char *name, char **names, int names_cnt) +{ + int i; + size_t name_size = strlen(name); + + for (i = 0; i < names_cnt; i++) + if (strncmp(name, names[i], name_size) == 0) + return i; + + return -1; +} + +static unsigned int find_symbols_by_names(struct Elf32_Sym *syms, + Elf32_Half syms_count, + Elf32_Off str_offset, + Elf32_Word str_size, struct file *bin_p, + char **name, int name_cnt, + Elf32_Addr **addrs) +{ + Elf32_Word i = 0; + Elf32_Addr addr = 0; + Elf32_Addr *addrs_arr = NULL; + loff_t off; + unsigned int ret = 0; + char *names = NULL; + + names = kmalloc(str_size, GFP_KERNEL); + if (names == NULL) { + printk("ERROR: Not enough memory!\n"); + return 0; + } + + addrs_arr = kmalloc(sizeof(Elf32_Addr) * name_cnt, GFP_KERNEL); + if (addrs_arr == NULL) { + printk("ERROR: Not enough memory!\n"); + goto exit_find_syms; + } + + off = file_seek(bin_p, str_offset, SEEK_SET); + file_read(bin_p, names, str_size, off); + + for (i = 1; i <= str_size; ) { + if (compare_names(names + i, name, name_cnt) >= 0) { + addr = find_addr_by_name_offset(syms, syms_count, i); + /* If we've found addr - add it to addrs list. Filter for thumb */ + if (addr) { + addrs_arr[ret] = addr & 0xfffffffe ; + ret++; + } + } + i += strlen(names + i) + 1; + } + + *addrs = addrs_arr; + +exit_find_syms: + kfree(names); + + return ret; +} + +/** + * get_offset_from_bin - get specified symbols offset in binary + * @bin_path: path to binary + * @names: symbols names + * @syms_cnt: symbols count + * @addrs: pointer to array where result is stored + * + */ +int get_offset_from_bin(char *bin_path, char **names, + unsigned int syms_cnt, unsigned long **addrs) +{ + struct file *filp = NULL; + Elf32_Half sec_count, sym_count; + Elf32_Off syms_offset = 0; + Elf32_Word syms_size; + Elf32_Off str_offset = 0; + Elf32_Word str_size; + unsigned int found_addrs = 0; + struct Elf32_Sym *syms = NULL; + struct Elf32_Shdr *sections = NULL; + + filp = file_open(bin_path, O_RDONLY, 0); + if (filp == NULL) + return -1; + + sec_count = get_sections(§ions, filp); + + syms_size = find_section_offset(sections, sec_count, 0xb, &syms_offset); + sym_count = get_symbols(&syms, syms_size, syms_offset, filp); + + str_size = find_section_offset(sections, sec_count, 0x3, &str_offset); + + found_addrs = find_symbols_by_names(syms, sym_count, str_offset, str_size, filp, names, syms_cnt, (Elf32_Addr **)addrs); + + kfree(syms); + kfree(sections); + + file_close(filp); + + return found_addrs; +} diff --git a/dlog_suspend/elf_parser.h b/dlog_suspend/elf_parser.h new file mode 100644 index 00000000..aec47f24 --- /dev/null +++ b/dlog_suspend/elf_parser.h @@ -0,0 +1,7 @@ +#ifndef __ELF_PARSER_H__ +#define __ELF_PARSER_H__ + +int get_offset_from_bin(char *bin_path, char **names, + unsigned int syms_cnt, unsigned long **addrs); + +#endif /* __ELF_PARSER_H__ */ diff --git a/dlog_suspend/file_ops.c b/dlog_suspend/file_ops.c new file mode 100644 index 00000000..ab51763c --- /dev/null +++ b/dlog_suspend/file_ops.c @@ -0,0 +1,57 @@ +#include <linux/string.h> +#include <asm/uaccess.h> + +#include "file_ops.h" + +struct file *file_open(const char *bin, int flags, int rights) +{ + struct file *filp = NULL; + mm_segment_t oldfs; + + oldfs = get_fs(); + set_fs(get_ds()); + filp = filp_open(bin, flags, rights); + set_fs(oldfs); + if (IS_ERR(filp)) { + printk("ERROR: Cannot open file %s\n", bin); + return NULL; + } + + return filp; +} + +void file_close(struct file *filp) +{ + filp_close(filp, NULL); +} + +int file_read(struct file *filp, unsigned char *data, unsigned int size, + loff_t offset) +{ + mm_segment_t oldfs; + int ret; + + oldfs = get_fs(); + set_fs(get_ds()); + + ret = vfs_read(filp, data, size, &offset); + + set_fs(oldfs); + + return ret; +} + +loff_t file_seek(struct file *filp, loff_t offset, int whence) +{ + mm_segment_t oldfs; + loff_t ret; + + oldfs = get_fs(); + set_fs(get_ds()); + + ret = vfs_llseek(filp, offset, whence); + + set_fs(oldfs); + + return ret; +} diff --git a/dlog_suspend/file_ops.h b/dlog_suspend/file_ops.h new file mode 100644 index 00000000..0e829bab --- /dev/null +++ b/dlog_suspend/file_ops.h @@ -0,0 +1,15 @@ +#ifndef __FILE_OPS_H__ +#define __FILE_OPS_H__ + +#include <linux/fs.h> + +#define SEEK_SET 0 + + +struct file *file_open(const char *bin, int flags, int rights); +void file_close(struct file *filp); +int file_read(struct file *filp, unsigned char *data, unsigned int size, + loff_t offset); +loff_t file_seek(struct file *filp, loff_t offset, int whence); + +#endif /* __FILE_OPS_H__ */ diff --git a/dlog_suspend/get_task.c b/dlog_suspend/get_task.c new file mode 100644 index 00000000..29507d95 --- /dev/null +++ b/dlog_suspend/get_task.c @@ -0,0 +1,198 @@ +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/rwlock.h> +#include <linux/stop_machine.h> + +#include <us_manager/sspt/sspt.h> + +#include "get_task.h" +#include "lib_handler.h" +#include "tasks_list.h" +#include "dentry.h" + +/* libdlog.so dentry */ +struct dentry *lib_dentry = NULL; + +/* Offsets in libdlog.so */ +static unsigned long *addrs = NULL; + +/* Default log level for apps. + * -1 - logs disabled + */ +static int main_log_level = 4; /* According to libdlog sources */ + +struct for_each_task_info_t { + int (*filter_func)(struct task_struct *, void *); + void *filter_data; + int (*for_each_task_func)(struct task_struct *, void *); + void *for_each_task_data; +}; + +/* 1 - equal + * 0 - not equal + */ +static int pid_filter(struct task_struct *task, void *desired_pid) +{ + if (task->pid == *(unsigned long *)desired_pid) + return 1; + + return 0; +} + +/* 1 - equal + * 0 - not equal + */ +static int dentry_filter(struct task_struct *task, void *desired_dentry) +{ + struct vm_area_struct *vma; + struct mm_struct *mm = task->mm; + + if (mm == NULL) + return 0; + + down_read(&mm->mmap_sem); + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (check_vma(vma) && vma->vm_file->f_dentry == + *(struct dentry **)desired_dentry) { + up_read(&mm->mmap_sem); + return 1; + } + } + up_read(&mm->mmap_sem); + + return 0; +} + +/* Always equal */ +static int dumb_filter(struct task_struct *task, void *data) +{ + return 1; +} + +static int patch_process(struct task_struct * const task, void * const data) +{ + int log_level = *(int *)data; + struct vm_area_struct *vma = NULL; + + if (addrs == NULL) + if (get_offsets(&addrs) == 0) + return -1; + + if (lib_dentry == NULL) + lib_dentry = get_lib_dentry(); + + if (task->mm == NULL) + return 0; + + down_read(&task->mm->mmap_sem); + for (vma = task->mm->mmap; vma; vma = vma->vm_next) { + if (check_vma(vma) && vma->vm_file->f_dentry == lib_dentry) { + patch_dlog(task, vma->vm_start + addrs[0], log_level); + patch_dlog(task, vma->vm_start + addrs[1], log_level); + up_read(&task->mm->mmap_sem); + return 1; + } + } + up_read(&task->mm->mmap_sem); + + return 0; + +} + +static int find_library(struct task_struct *task, void *data) +{ + int ret; + + ret = patch_process(task, data); + if (ret == 0) + return -1; + else + return ret; +} + +static int __look_through_all_tasks(void *data) +{ + int res, ret = 0; + struct task_struct *task; + struct for_each_task_info_t *feti_p = (struct for_each_task_info_t *)data; + + if (feti_p == NULL) + return -1; + + for_each_process(task) { + if (feti_p->filter_func && feti_p->filter_func(task, feti_p->filter_data)) { + if (feti_p->for_each_task_func) { + res = feti_p->for_each_task_func(task, feti_p->for_each_task_data); + if (res < 0) { + ret = res; + goto look_exit; + } + else if (res > 0) { + ret = res; + } + } + } + } + +look_exit: + return ret; +} + + +static int patch_lli(struct task_struct * const task, + struct dentry * const dentry, int log_level) +{ + if (check_dentry(current, dentry) == 0) + return 0; + + return patch_process(task, &log_level); +} + +int page_fault_callback(void) +{ + return for_each_log_level_item(current, patch_lli); +} + +int patch_process_by_pid(pid_t pid, int *log_level_p) +{ + struct for_each_task_info_t feti = { + .filter_func = pid_filter, + .filter_data = (void *)&pid, + .for_each_task_func = find_library, + .for_each_task_data = log_level_p + }; + + return stop_machine(__look_through_all_tasks, &feti, NULL); +} + +int patch_process_by_dentry(struct dentry *dentry, int *log_level_p) +{ + struct for_each_task_info_t feti = { + .filter_func = dentry_filter, + .filter_data = (void *)&dentry, + .for_each_task_func = find_library, + .for_each_task_data = log_level_p + }; + + return stop_machine(__look_through_all_tasks, &feti, NULL); +} + +int patch_all_processes(void) +{ + struct for_each_task_info_t feti = { + .filter_func = dumb_filter, + .filter_data = NULL, + .for_each_task_func = patch_process, + .for_each_task_data = &main_log_level + }; + + return stop_machine(__look_through_all_tasks, &feti, NULL); +} + +void set_main_log_level(const int log_level) +{ + if ((log_level >= -1) && (log_level <= 9)) + main_log_level = log_level; + else + printk("ERROR: Wrong log_level!\n"); +} diff --git a/dlog_suspend/get_task.h b/dlog_suspend/get_task.h new file mode 100644 index 00000000..f9b01b76 --- /dev/null +++ b/dlog_suspend/get_task.h @@ -0,0 +1,14 @@ +#ifndef __GET_TASK_H__ +#define __GET_TASK_H__ + +#include <linux/types.h> + +struct dentry; + +int patch_process_by_pid(pid_t pid, int *log_level); +int patch_process_by_dentry(struct dentry *dentry, int *log_level); +int patch_all_processes(void); +int page_fault_callback(void); +void set_main_log_level(const int log_level); + +#endif /* __GET_TASK_H__ */ diff --git a/dlog_suspend/lib_handler.c b/dlog_suspend/lib_handler.c new file mode 100644 index 00000000..4d32ea80 --- /dev/null +++ b/dlog_suspend/lib_handler.c @@ -0,0 +1,102 @@ +#include <linux/sched.h> + +#include <kprobe/dbi_kprobes_deps.h> + +#include "elf_parser.h" +#include "lib_handler.h" +#include "dentry.h" + +#define LIBDLOG_PATH "/usr/lib/libdlog.so.0.0.0" +#define LIBDLOG_NAME "libdlog.so.0.0.0" + +struct patch_insns { + unsigned short insn_1; + unsigned short insn_2; + unsigned short insn_3; +} __attribute__((packed)); + + +static int patch_dlog_enable(struct task_struct *task, unsigned long addr, + unsigned int log_level) +{ + int ret = 0; + struct patch_insns enable_insns = { + .insn_1 = 0xbf00, /* nop */ + .insn_2 = 0xbf00, /* nop */ + .insn_3 = 0x2900 /* cmp r1, #0 */ + }; + + enable_insns.insn_3 += log_level; /* cmp r1, #(0+log_level) */ + + ret = write_proc_vm_atomic(task, addr, &enable_insns, sizeof(enable_insns)); + if (ret == 0) { + printk("ERROR: Cannot write memory! PID: %d\n", task->pid); + return -EINVAL; + } + + return ret; +} + +static int patch_dlog_disable(struct task_struct *task, unsigned long addr) +{ + int ret = 0; + + struct patch_insns disable_insns = { + .insn_1 = 0xf04f, + .insn_2 = 0x0000, /* mov r0, #0 */ + .insn_3 = 0x4770 /* bx lr */ + }; + + + ret = write_proc_vm_atomic(task, addr, &disable_insns, sizeof(disable_insns)); + if (ret == 0) { + printk("ERROR: Cannot write memory! PID: %d\n", task->pid); + return -EINVAL; + } + + return ret; +} + +int patch_dlog(struct task_struct *task, unsigned long addr, int log_level) +{ + if (log_level == -1) { + return patch_dlog_disable(task, addr); + } else if (log_level >= 0) { + return patch_dlog_enable(task, addr, log_level); + } else { + printk("ERROR: Wrong log level!\n"); + return -1; + } +} + +int get_offsets(unsigned long **addrs) +{ + char **names; + char *name_1 = "__dlog_print"; + char *name_2 = "__dlog_vprint"; + int found_addrs; + + names = kmalloc(sizeof(name_1) * 2, GFP_KERNEL); + + names[0] = name_1; + names[1] = name_2; + + found_addrs = get_offset_from_bin(LIBDLOG_PATH, names, 2, addrs); + + kfree(names); + + return found_addrs; +} + +struct dentry *get_lib_dentry(void) +{ + struct dentry *dentry; + + if (get_dentry(LIBDLOG_PATH, &dentry) == 0) + return dentry; + else + return NULL; +} + +#undef LIBDLOG_NAME +#undef LIBDLOG_PATH diff --git a/dlog_suspend/lib_handler.h b/dlog_suspend/lib_handler.h new file mode 100644 index 00000000..b9e0632c --- /dev/null +++ b/dlog_suspend/lib_handler.h @@ -0,0 +1,10 @@ +#ifndef __LIB_HANDLER_H__ +#define __LIB_HANDLER_H__ + +struct task_struct; + +int get_offsets(unsigned long **addrs); +int patch_dlog(struct task_struct *task, unsigned long addr, int log_level); +struct dentry *get_lib_dentry(void); + +#endif /* __LIB_HANDLER_H__ */ diff --git a/dlog_suspend/swap_dlog_suspend.c b/dlog_suspend/swap_dlog_suspend.c new file mode 100644 index 00000000..717f1efb --- /dev/null +++ b/dlog_suspend/swap_dlog_suspend.c @@ -0,0 +1,246 @@ +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/namei.h> +#include <asm/uaccess.h> + +#include <driver/swap_debugfs.h> +#include <us_manager/sspt/sspt.h> +#include <us_manager/callbacks.h> +#include <us_manager/helper.h> + +#include "swap_dlog_suspend.h" +#include "dentry.h" +#include "lib_handler.h" +#include "tasks_list.h" +#include "get_task.h" + + + +static char *common_buf = NULL; +enum message_type_t { + PID = 0, + PATH, + ALL +}; + + +/* ============================================================================ + * = BUFFER = + * ============================================================================ + */ + +enum { subbuf_size = 8*1024 }; +enum { common_buf_size = subbuf_size * NR_CPUS }; + +static int init_buffer(void) +{ + common_buf = vmalloc(common_buf_size); + + return common_buf ? 0 : -ENOMEM; +} + +static void exit_buffer(void) +{ + vfree(common_buf); + common_buf = NULL; +} + +static void *get_current_buf(void) +{ + return common_buf + subbuf_size * get_cpu(); +} + +static void put_current_buf(void) +{ + put_cpu(); +} + + +/* ============================================================================ + * = PARSER = + * ============================================================================ + */ + +static int check_action(char * const action) +{ + ssize_t action_size = strnlen(action, subbuf_size); + + if ((action_size != 2) || + (strncmp(action, "d", 1) && + strncmp(action, "0", 1) && + strncmp(action, "1", 1) && + strncmp(action, "2", 1) && + strncmp(action, "3", 1) && + strncmp(action, "4", 1) && + strncmp(action, "5", 1) && + strncmp(action, "6", 1) && + strncmp(action, "7", 1) && + strncmp(action, "8", 1) && + strncmp(action, "9", 1))) + return -EINVAL; + + return 0; +} + +static ssize_t parse_string(char *buf) +{ + size_t str_size = strnlen(buf, subbuf_size); + char *app_s = NULL; + pid_t pid = 0; + struct dentry *app_dentry = NULL; + char *action_s = NULL; + int ret = 0; + int log_level; + enum message_type_t msg_type; + + + /* Parse app */ + app_s = strsep(&buf, " "); + if (app_s == NULL) + return -EINVAL; + + /* Check action */ + ret = check_action(buf); + if (ret) + return -EINVAL; + + /* Here we absolutely sure that action_s is 'd' or in between '0' - '8' */ + + /* Parse log level */ + if (buf[0] == 'd') { + log_level = -1; + } else { + ret = kstrtouint(buf, 10, &log_level); + if (ret) { + printk("ERROR: Wrong log level: %s\n", action_s); + return -EINVAL; + } + } + + /* If log level for all apps */ + if (strnlen(app_s, 3) == 1 && app_s[0] == '*') { + msg_type = ALL; + set_main_log_level(log_level); + patch_all_processes(); + } else if (get_dentry(app_s, &app_dentry) == 0) { + msg_type = PATH; + + ret = patch_process_by_dentry(app_dentry, &log_level); + if (ret <= 0) { + /* If task not found - add to list */ + ret = add_to_logs_list(app_dentry, log_level); + if (ret) { + printk("ERROR: Task not found!\n"); + return -EINVAL; + } + return str_size; + } + } else if (kstrtouint(app_s, 10, &pid) == 0) { + msg_type = PID; + ret = patch_process_by_pid(pid, &log_level); + if (ret <= 0) { + printk("ERROR: Task not found! Specified PID: %d\n", pid); + return -EINVAL; + } + } else { + printk("ERROR: Cannot identify application!\n"); + return -EINVAL; + } + + return str_size; +} + + +/* ============================================================================ + * = FILE OPERATIONS = + * ============================================================================ + */ + +static ssize_t write_dlog(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + void *buf; + + if (count > subbuf_size) + return -EINVAL; + + buf = get_current_buf(); + + memset(buf, 0, subbuf_size); + + if (copy_from_user(buf, user_buf, count)) { + ret = -EFAULT; + goto put_buf; + } + + ret = parse_string(buf); + +put_buf: + put_current_buf(); + return ret; +} + + +static const struct file_operations fops_dlog = { + .owner = THIS_MODULE, + .write = write_dlog +}; + + +/* ============================================================================ + * = INIT/EXIT = + * ============================================================================ + */ + +static struct dentry *swap_dlog_dentry = NULL; + +void swap_dlog_exit_debugfs(void) +{ + unregister_helper_top(); + unregister_helper_bottom(); + + free_logs_list(); + + if (swap_dlog_dentry) + debugfs_remove(swap_dlog_dentry); + + swap_dlog_dentry = NULL; + + exit_buffer(); +} + +int swap_dlog_init_debugfs(void) +{ + struct dentry *swap_dir; + int ret; + + ret = init_buffer(); + if (ret) + return ret; + + swap_dir = get_swap_debugfs_dir(); + if (swap_dir == NULL) + return -ENOENT; + + swap_dlog_dentry = debugfs_create_file(SWAP_DLOG, 0600, swap_dir, NULL, + &fops_dlog); + if (swap_dlog_dentry == NULL) + goto fail; + + register_pf_cb(page_fault_callback); + + if (register_helper()) { + printk("ERROR: Cannot install probes\n"); + goto fail; + } + + return 0; + +fail: + swap_dlog_exit_debugfs(); + return -ENOMEM; +} diff --git a/dlog_suspend/swap_dlog_suspend.h b/dlog_suspend/swap_dlog_suspend.h new file mode 100644 index 00000000..69342d74 --- /dev/null +++ b/dlog_suspend/swap_dlog_suspend.h @@ -0,0 +1,9 @@ +#ifndef __SWAP_DLOG_SUSPEND_H__ +#define __SWAP_DLOG_SUSPEND_H__ + +#define SWAP_DLOG "dlog" + +int swap_dlog_init_debugfs(void); +void swap_dlog_exit_debugfs(void); + +#endif /* __SWAP_DLOG_SUSPEND_H__ */ diff --git a/dlog_suspend/tasks_list.c b/dlog_suspend/tasks_list.c new file mode 100644 index 00000000..394c7b93 --- /dev/null +++ b/dlog_suspend/tasks_list.c @@ -0,0 +1,78 @@ +#include <linux/list.h> +#include <linux/slab.h> + +#include "tasks_list.h" + +struct log_level_item { + struct list_head list; + + struct dentry *bin; + int log_level; +}; + +static LIST_HEAD(logs_list); + +static struct log_level_item *create_log_level_item(struct dentry * const dentry, + const int log_level) +{ + struct log_level_item *lli = NULL; + + lli = kmalloc(sizeof(*lli), GFP_KERNEL); + if (lli == NULL) + return NULL; + + lli->bin = dentry; + lli->log_level = log_level; + INIT_LIST_HEAD(&lli->list); + + return lli; +} + +static void free_log_level_item(struct log_level_item *lli) +{ + kfree(lli); +} + +int add_to_logs_list(struct dentry * const dentry, const int log_level) +{ + struct log_level_item *lli = NULL; + + lli = create_log_level_item(dentry, log_level); + if (lli == NULL) { + printk("ERROR: Not enough memory!\n"); + return -ENOMEM; + } + + list_add_tail(&lli->list, &logs_list); + + return 0; +} + +int for_each_log_level_item(struct task_struct *task, + int (*lli_cb)(struct task_struct *, + struct dentry *, + int)) +{ + struct log_level_item *lli, *n; + int ret; + + list_for_each_entry_safe(lli, n, &logs_list, list) { + ret = lli_cb(task, lli->bin, lli->log_level); + if (ret == 1) { + list_del(&lli->list); + free_log_level_item(lli); + } + } + + return ret; +} + +void free_logs_list(void) +{ + struct log_level_item *lli, *n; + + list_for_each_entry_safe(lli, n, &logs_list, list) { + list_del(&lli->list); + free_log_level_item(lli); + } +} diff --git a/dlog_suspend/tasks_list.h b/dlog_suspend/tasks_list.h new file mode 100644 index 00000000..7d04eafd --- /dev/null +++ b/dlog_suspend/tasks_list.h @@ -0,0 +1,14 @@ +#ifndef __TASKS_LIST_H__ +#define __TASKS_LIST_H__ + +struct dentry; +struct task_struct; + +int add_to_logs_list(struct dentry * const dentry, const int log_level); +void free_logs_list(void); +int for_each_log_level_item(struct task_struct *task, + int (*lli_cb)(struct task_struct *, + struct dentry *, + int)); + +#endif /* __TASKS_LIST_H__ */ diff --git a/swap_module.c b/swap_module.c index 788776c5..26522900 100644 --- a/swap_module.c +++ b/swap_module.c @@ -12,6 +12,8 @@ #include <us_manager/us_manager.h> #include <writer/swap_writer_module.h> +#include <dlog_suspend/swap_dlog_suspend.h> + typedef enum { FULL_REMOVE, BUFFER_FAIL, @@ -24,13 +26,16 @@ typedef enum { KS_FEATURE_FAIL, SAMPLER_FAIL, ENERGY_FAIL, - PARSER_FAIL + PARSER_FAIL, + DLOG_FAIL } exit_modules_t; static void uninit_modules(exit_modules_t exit_m) { switch (exit_m) { case FULL_REMOVE: + swap_dlog_exit_debugfs(); + case DLOG_FAIL: swap_parser_exit(); case PARSER_FAIL: swap_energy_exit(); @@ -127,6 +132,12 @@ static int __init swap_init(void) return ret; } + ret = swap_dlog_init_debugfs(); + if (ret) { + uninit_modules(DLOG_FAIL); + return ret; + } + return ret; } |