obj-m := swap_writer.o
swap_writer-y := swap_writer_module.o \
kernel_operations.o \
- debugfs_writer.o
+ debugfs_writer.o \
+ event_filter.o
#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"
/* ============================================================================
/* ============================================================================
+ * === 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 ===
* ============================================================================
*/
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;
}
--- /dev/null
+/*
+ * 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);;
+}
--- /dev/null
+#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 */
#include "swap_writer_errors.h"
#include "kernel_operations.h"
#include "debugfs_writer.h"
+#include "event_filter.h"
enum MSG_ID {
{
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);
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);
{
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);
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);
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);