[FEATURE] add event_filter in swap_writer module
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Thu, 3 Oct 2013 12:55:02 +0000 (16:55 +0400)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Fri, 4 Oct 2013 09:14:11 +0000 (13:14 +0400)
Change-Id: I9f02dbf02dab36264f4a1f8e2e6f29d702ef7ea9
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
writer/Kbuild
writer/debugfs_writer.c
writer/event_filter.c [new file with mode: 0644]
writer/event_filter.h [new file with mode: 0644]
writer/swap_writer_module.c

index 72d7ef3..8f15bde 100644 (file)
@@ -3,4 +3,5 @@ EXTRA_CFLAGS := $(extra_cflags)
 obj-m := swap_writer.o
 swap_writer-y := swap_writer_module.o \
                  kernel_operations.o \
-                 debugfs_writer.o
+                 debugfs_writer.o \
+                 event_filter.o
index 9986a54..21c221b 100644 (file)
 #include <linux/module.h>
 #include <linux/debugfs.h>
 #include <linux/vmalloc.h>
+#include <linux/slab.h>
 #include <asm/uaccess.h>
 #include <driver/swap_debugfs.h>
 #include "swap_writer_module.h"
+#include "event_filter.h"
 
 
 /* ============================================================================
@@ -81,6 +83,119 @@ static const struct file_operations fops_raw = {
 
 
 /* ============================================================================
+ * ===                        FOPS_AVAILABLE_FILTERS                        ===
+ * ============================================================================
+ */
+struct read_buf {
+       char *begin;
+       char *ptr;
+       char *end;
+};
+
+static void func_for_read(struct ev_filter *f, void *data)
+{
+       struct read_buf *rbuf = (struct read_buf *)data;
+       int len = strlen(f->name);
+
+       if (rbuf->end - rbuf->ptr < len + 2)
+               return;
+
+       if (rbuf->ptr != rbuf->begin) {
+               *rbuf->ptr = ' ';
+               ++rbuf->ptr;
+       }
+
+       memcpy(rbuf->ptr, f->name, len);
+       rbuf->ptr += len;
+}
+
+static ssize_t read_af(struct file *file, char __user *user_buf,
+                      size_t count, loff_t *ppos)
+{
+       char buf[512];
+       struct read_buf rbuf = {
+               .begin = buf,
+               .ptr = buf,
+               .end = buf + sizeof(buf)
+       };
+
+       event_filter_on_each(func_for_read, (void *)&rbuf);
+
+       *rbuf.ptr = '\n';
+       ++rbuf.ptr;
+
+       return simple_read_from_buffer(user_buf, count, ppos,
+                                      rbuf.begin, rbuf.ptr - rbuf.begin);
+}
+
+static const struct file_operations fops_available_filters = {
+       .read =         read_af,
+       .llseek =       default_llseek
+};
+
+
+
+
+
+/* ============================================================================
+ * ===                              FOPS_FILTER                             ===
+ * ============================================================================
+ */
+static ssize_t read_filter(struct file *file, char __user *user_buf,
+                          size_t count, loff_t *ppos)
+{
+       const char *name = event_filter_get();
+       int len = strlen(name);
+       char *buf;
+       ssize_t ret;
+
+       buf = kmalloc(len + 2, GFP_KERNEL);
+       memcpy(buf, name, len);
+
+       buf[len] = '\0';
+       buf[len + 1] = '\n';
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, len + 2);
+       kfree(buf);
+
+       return ret;
+}
+
+static ssize_t write_filter(struct file *file, const char __user *user_buf,
+                       size_t count, loff_t *ppos)
+{
+       enum { len = 32 };
+       char buf[len], name[len];
+       size_t buf_size;
+       ssize_t ret;
+
+       buf_size = min(count, (size_t)(len - 1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[len - 1] = '\0';
+       ret = sscanf(buf, "%31s", name);
+       if (ret != 1)
+               return -EINVAL;
+
+       ret = event_filter_set(name);
+       if (ret)
+               return -EINVAL;
+
+       return count;
+}
+
+static const struct file_operations fops_filter = {
+       .read =         read_filter,
+       .write =        write_filter,
+       .llseek =       default_llseek
+};
+
+
+
+
+
+/* ============================================================================
  * ===                              INIT/EXIT                               ===
  * ============================================================================
  */
@@ -114,10 +229,20 @@ int init_debugfs_writer(void)
                return -ENOMEM;
 
        dentry = debugfs_create_file("raw", 0600, writer_dir, NULL, &fops_raw);
-       if (dentry == NULL) {
-               exit_debugfs_writer();
-               return -ENOMEM;
-       }
+       if (dentry == NULL)
+               goto fail;
+
+       dentry = debugfs_create_file("available_filters", 0600, writer_dir, NULL, &fops_available_filters);
+       if (dentry == NULL)
+               goto fail;
+
+       dentry = debugfs_create_file("filter", 0600, writer_dir, NULL, &fops_filter);
+       if (dentry == NULL)
+               goto fail;
 
        return 0;
+
+fail:
+       exit_debugfs_writer();
+       return -ENOMEM;
 }
diff --git a/writer/event_filter.c b/writer/event_filter.c
new file mode 100644 (file)
index 0000000..fdddd51
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *  SWAP kernel features
+ *  writer/event_filter.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         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include "event_filter.h"
+
+
+static LIST_HEAD(filter_list);
+
+static int func_none(struct task_struct *task)
+{
+       return 1;
+}
+
+static struct ev_filter filter_none = {
+       .name = "all",
+       .filter = func_none
+};
+
+static struct ev_filter *filter_current = &filter_none;
+
+int check_event(struct task_struct *task)
+{
+       return filter_current->filter(task);
+}
+
+static struct ev_filter *event_filter_find(const char *name)
+{
+       struct ev_filter *f, *tmp;
+
+       list_for_each_entry_safe(f, tmp, &filter_list, list) {
+               if (strcmp(f->name, name) == 0)
+                       return f;
+       }
+
+       return NULL;
+}
+
+int event_filter_register(struct ev_filter *f)
+{
+       if (event_filter_find(f->name))
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&f->list);
+       list_add(&f->list, &filter_list);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(event_filter_register);
+
+void event_filter_unregister(struct ev_filter *f)
+{
+       struct ev_filter *filter, *tmp;
+
+       if (filter_current == f)
+               filter_current = &filter_none;
+
+       list_for_each_entry_safe(filter, tmp, &filter_list, list) {
+               if (filter == f) {
+                       list_del(&filter->list);
+                       break;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(event_filter_unregister);
+
+int event_filter_set(const char *name)
+{
+       struct ev_filter *f;
+
+       f = event_filter_find(name);
+       if (f == NULL)
+               return -EINVAL;
+
+       filter_current = f;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(event_filter_set);
+
+const char *event_filter_get(void)
+{
+       return filter_current->name;
+}
+
+void event_filter_on_each(void (*func)(struct ev_filter *, void *),
+                         void *data)
+{
+       struct ev_filter *f, *tmp;
+
+       list_for_each_entry_safe(f, tmp, &filter_list, list)
+               func(f, data);
+}
+
+int event_filter_init(void)
+{
+       return event_filter_register(&filter_none);
+}
+
+void event_filter_exit(void)
+{
+       event_filter_unregister(&filter_none);;
+}
diff --git a/writer/event_filter.h b/writer/event_filter.h
new file mode 100644 (file)
index 0000000..3adcaae
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _EVENT_FILTER_H
+#define _EVENT_FILTER_H
+
+/*
+ *  SWAP kernel features
+ *  writer/event_filter.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         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/list.h>
+
+struct task_struct;
+
+struct ev_filter {
+       struct list_head list;
+       char *name;
+       int (*filter)(struct task_struct *);
+};
+
+
+int check_event(struct task_struct *task);
+
+int event_filter_register(struct ev_filter *f);
+void event_filter_unregister(struct ev_filter *f);
+int event_filter_set(const char *name);
+const char *event_filter_get(void);
+
+void event_filter_on_each(void (*func)(struct ev_filter *, void *),
+                         void *data);
+
+int event_filter_init(void);
+void event_filter_exit(void);
+
+#endif /* _EVENT_FILTER_H */
index 5748d16..9b294a1 100644 (file)
@@ -42,6 +42,7 @@
 #include "swap_writer_errors.h"
 #include "kernel_operations.h"
 #include "debugfs_writer.h"
+#include "event_filter.h"
 
 
 enum MSG_ID {
@@ -357,6 +358,9 @@ int sample_msg(struct pt_regs *regs)
 {
        char *buf, *payload, *buf_end;
 
+       if (!check_event(current))
+               return 0;
+
        buf = get_current_buf();
        payload = pack_basic_msg_fmt(buf, MSG_SAMPLE);
        buf_end = pack_sample(payload, regs);
@@ -503,6 +507,9 @@ int entry_event(const char *fmt, struct pt_regs *regs,
        char *buf, *payload, *args, *buf_end;
        int ret;
 
+       if (pt == PT_KS && !check_event(current))
+               return 0;
+
        buf = get_current_buf();
        payload = pack_basic_msg_fmt(buf, MSG_FUNCTION_ENTRY);
        args = pack_msg_func_entry(payload, fmt, regs, pt, sub_type);
@@ -557,6 +564,9 @@ int exit_event(struct pt_regs *regs, unsigned long func_addr)
 {
        char *buf, *payload, *buf_end;
 
+       if (!check_event(current))
+               return 0;
+
        buf = get_current_buf();
        payload = pack_basic_msg_fmt(buf, MSG_FUNCTION_EXIT);
        buf_end = pack_msg_func_exit(payload, regs, func_addr);
@@ -609,12 +619,18 @@ static int context_switch(struct pt_regs *regs, enum MSG_ID id)
 
 int switch_entry(struct pt_regs *regs)
 {
+       if (!check_event(current))
+               return 0;
+
        return context_switch(regs, MSG_CONTEXT_SWITCH_ENTRY);
 }
 EXPORT_SYMBOL_GPL(switch_entry);
 
 int switch_exit(struct pt_regs *regs)
 {
+       if (!check_event(current))
+               return 0;
+
        return context_switch(regs, MSG_CONTEXT_SWITCH_EXIT);
 }
 EXPORT_SYMBOL_GPL(switch_exit);
@@ -688,17 +704,25 @@ int raw_msg(char *buf, size_t len)
 
 static int __init swap_writer_module_init(void)
 {
-       return init_debugfs_writer();
+       int ret;
+
+       ret = event_filter_init();
+       if (ret)
+               return ret;
+
+       ret = init_debugfs_writer();
+       if (ret)
+               event_filter_exit();
+
+       return ret;
 }
 
 static void __exit swap_writer_module_exit(void)
 {
        exit_debugfs_writer();
+       event_filter_exit();
 }
 
-
-
-
 module_init(swap_writer_module_init);
 module_exit(swap_writer_module_exit);