[FEATURE] Dlog: dlog suspend module implement 42/20142/2
authorAlexander Aksenov <a.aksenov@samsung.com>
Thu, 24 Apr 2014 12:05:48 +0000 (16:05 +0400)
committerAlexander Aksenov <a.aksenov@samsung.com>
Tue, 29 Apr 2014 14:45:08 +0000 (18:45 +0400)
Change-Id: Idd16fa1b40b128179d606b5eb0b3a16867520659
Signed-off-by: Alexander Aksenov <a.aksenov@samsung.com>
16 files changed:
Kbuild
dlog_suspend/dentry.c [new file with mode: 0644]
dlog_suspend/dentry.h [new file with mode: 0644]
dlog_suspend/elf_parser.c [new file with mode: 0644]
dlog_suspend/elf_parser.h [new file with mode: 0644]
dlog_suspend/file_ops.c [new file with mode: 0644]
dlog_suspend/file_ops.h [new file with mode: 0644]
dlog_suspend/get_task.c [new file with mode: 0644]
dlog_suspend/get_task.h [new file with mode: 0644]
dlog_suspend/lib_handler.c [new file with mode: 0644]
dlog_suspend/lib_handler.h [new file with mode: 0644]
dlog_suspend/swap_dlog_suspend.c [new file with mode: 0644]
dlog_suspend/swap_dlog_suspend.h [new file with mode: 0644]
dlog_suspend/tasks_list.c [new file with mode: 0644]
dlog_suspend/tasks_list.h [new file with mode: 0644]
swap_module.c

diff --git a/Kbuild b/Kbuild
index c8ffd8a..3a5e6b2 100644 (file)
--- a/Kbuild
+++ b/Kbuild
@@ -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 (file)
index 0000000..73d1b28
--- /dev/null
@@ -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 (file)
index 0000000..dff9172
--- /dev/null
@@ -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 (file)
index 0000000..3612c8c
--- /dev/null
@@ -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(&sections, 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 (file)
index 0000000..aec47f2
--- /dev/null
@@ -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 (file)
index 0000000..ab51763
--- /dev/null
@@ -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 (file)
index 0000000..0e829ba
--- /dev/null
@@ -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 (file)
index 0000000..29507d9
--- /dev/null
@@ -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 (file)
index 0000000..f9b01b7
--- /dev/null
@@ -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 (file)
index 0000000..4d32ea8
--- /dev/null
@@ -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 (file)
index 0000000..b9e0632
--- /dev/null
@@ -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 (file)
index 0000000..717f1ef
--- /dev/null
@@ -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 (file)
index 0000000..69342d7
--- /dev/null
@@ -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 (file)
index 0000000..394c7b9
--- /dev/null
@@ -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 (file)
index 0000000..7d04eaf
--- /dev/null
@@ -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__ */
index 788776c..2652290 100644 (file)
@@ -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;
 }