[ltt-dev] [PATCH 3/3] In-kernel filter module

Zhaolei zhaolei at cn.fujitsu.com
Sun Mar 29 04:44:48 EDT 2009


LTTng's in-kernel filter is used to filter event by its arguments.
User can config every event's filter option by echo to filts in
debugfs/ltt/filter

Signed-off-by: Zhao Lei <zhaolei at cn.fujitsu.com>
---
 ltt/Kconfig      |    9 +
 ltt/Makefile     |    1 +
 ltt/ltt-filter.c | 1831 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1841 insertions(+), 0 deletions(-)
 create mode 100644 ltt/ltt-filter.c

diff --git a/ltt/Kconfig b/ltt/Kconfig
index 3c520f5..2cfb479 100644
--- a/ltt/Kconfig
+++ b/ltt/Kconfig
@@ -214,4 +214,13 @@ config LTT_ASCII
 	  Output trace data in a text-formatted ascii file, presented in
 	  /mnt/debugfs/ltt/ascii/<trace name>.
 
+config LTT_FILTER
+	tristate "Linux Trace Toolkit Filter"
+	depends on LTT_TRACER
+	depends on LTT_SERIALIZE
+	default n
+	help
+	  Filter for LTTng to control which type of event to be traced.
+	  Config filter rule by debugfs/ltt/filter
+
 endif # LTT
diff --git a/ltt/Makefile b/ltt/Makefile
index 5ea5cca..29ab6e4 100644
--- a/ltt/Makefile
+++ b/ltt/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_LTT_KPROBES)		+= ltt-kprobes.o
 obj-$(CONFIG_LTT_TRACEPROBES)		+= probes/
 obj-$(CONFIG_LTT_FTRACE)		+= ltt-ftrace.o
 obj-$(CONFIG_LTT_ASCII)			+= ltt-ascii.o
+obj-$(CONFIG_LTT_FILTER)		+= ltt-filter.o
diff --git a/ltt/ltt-filter.c b/ltt/ltt-filter.c
new file mode 100644
index 0000000..bf2760a
--- /dev/null
+++ b/ltt/ltt-filter.c
@@ -0,0 +1,1831 @@
+/*
+ * LTT filter module.
+ *
+ * Copyright 2009 -
+ *	Zhaolei <zhaolei at cn.fujitsu.com>
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/ltt-core.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/marker.h>
+#include <linux/ltt-tracer.h>
+#include <asm/atomic.h>
+
+#define LTT_FILTER_DIR "filter"
+
+struct dentry *ltt_filter_dir;
+
+/*
+ * Marker format operations
+ *
+ * Maintenance a marker_format list sync with module add and release
+ * Each data-type in format are paresed and stored in marker_format struct
+ * Impl a notifier for respond add/remove marker format event
+ */
+
+enum format_item_type {
+	SIGNED,
+	UNSIGNED,
+	STRING,
+	CHAR,
+};
+const char *format_item_type_string[] = {
+	"signed int",
+	"unsigned int",
+	"string",
+	"char",
+};
+
+struct format_item {
+	struct list_head list;
+	char name[PATH_MAX];
+	enum format_item_type type;
+	size_t size;
+};
+
+struct marker_format {
+	atomic_t refcnt;
+	struct list_head list;
+	const char *name;
+	const char *format;
+	struct format_item **items;
+	size_t format_item_cnt;
+};
+
+static LIST_HEAD(marker_format_head);
+static DEFINE_MUTEX(format_lock);
+
+BLOCKING_NOTIFIER_HEAD(format_notifier_list);
+enum FORMAT_EVENT {
+	ADD_FORMAT,
+	DEL_FORMAT,
+};
+
+static struct format_item *new_format_item(const char *name_head,
+		const char *name_tail, int type_type, int type_size)
+{
+	struct format_item *format_item;
+	const char *p;
+	int i, name_len;
+
+	format_item = kmalloc(sizeof(struct format_item), GFP_KERNEL);
+	if (!format_item) {
+		printk(KERN_ERR "%s: alloc format_item failed\n", __func__);
+		goto err_alloc_format_item;
+	}
+
+	INIT_LIST_HEAD(&format_item->list);
+
+	/* trim itemname */
+	while (name_head < name_tail
+		&& *name_head == ' ')
+		name_head++;
+	while (name_tail > name_head
+		&& *(name_tail - 1) == ' ')
+		name_tail--;
+
+	name_len = name_tail - name_head;
+	if (name_len) {
+		for (i = 0, p = name_head; i < PATH_MAX - 1 && p < name_tail;
+			i++, p++) {
+			if ((*p == '%' && p + 1 < name_tail && *(p + 1) == '%')
+				|| (*p == '#' &&
+				p + 1 < name_tail && *(p + 1) == '#')) {
+				p++;
+			}
+			format_item->name[i] = isalnum(*p) ? *p : '_';
+		}
+		format_item->name[i] = 0;
+	} else {
+		format_item->name[0] = 0;
+	}
+
+	format_item->type = type_type;
+	format_item->size = type_size;
+
+	return format_item;
+
+err_alloc_format_item:
+	return NULL;
+}
+
+static size_t parse_format(const char *format, struct format_item ***items)
+{
+	int ret = 0; /* number of types in format string, or -ERRNO on fail */
+	int state; /* 0: in name, 1: in type, 2: in # */
+	const char *p; /* format string's iter */
+	/* name for current type, init with 0 to avoid compile warning */
+	const char *name_head, *name_tail = 0;
+	enum format_item_type type_type;
+	/*
+	 * size(in bytes) of current type
+	 * init with 0 to avoid compile warning
+	 */
+	int type_size = 0;
+
+	LIST_HEAD(format_item_list);
+	struct format_item *format_item, *format_item_next;
+
+	for (state = 0, name_head = p = format; *p; p++) {
+		switch (state) {
+		case 0:
+			switch (*p) {
+			case '%':
+				if (*(p+1) != '%') {
+					name_tail = p;
+					type_size = sizeof(int);
+					state = 1;
+				} else {
+					p++;
+				}
+				break;
+			case '#':
+				if (*(p+1) != '#') {
+					name_tail = p;
+					state = 2;
+				} else {
+					p++;
+				}
+				break;
+			default:
+				break;
+			}
+			break;
+		case 1:
+			switch (*p) {
+			case 'l':
+				if (*(p+1) == 'l') {
+					type_size = sizeof(long long);
+					p++;
+				} else {
+					type_size = sizeof(long);
+				}
+				break;
+			case 'L':
+				type_size = sizeof(long long);
+				break;
+			case 'z':
+			case 'Z':
+				type_size = sizeof(size_t);
+				break;
+			case 't':
+				type_size = sizeof(ptrdiff_t);
+				break;
+			case 'h':
+				type_size = sizeof(short);
+				break;
+			case 'd':
+			case 'i':
+				type_type = SIGNED;
+				goto type_finish;
+				break;
+			case 'u':
+			case 'o':
+			case 'x':
+			case 'X':
+				type_type = UNSIGNED;
+				goto type_finish;
+				break;
+			case 'p':
+				type_type = UNSIGNED;
+				type_size = sizeof(void *);
+				goto type_finish;
+				break;
+			case 's':
+				type_type = STRING;
+				type_size = sizeof(void *);
+				goto type_finish;
+				break;
+			case 'c':
+				type_type = CHAR;
+				type_size = sizeof(char);
+				goto type_finish;
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				break;
+			default:
+				printk(KERN_ERR
+					"%s: unknown char after %%: %c\n",
+					__func__, *p);
+				break;
+			}
+			break;
+		case 2:
+			switch (*p) {
+			case 'l':
+			case 'L':
+			case 'Z':
+			case 'z':
+			case 't':
+			case 'h':
+			case 'd':
+			case 'i':
+			case 'u':
+			case 'o':
+			case 'x':
+			case 'X':
+			case 'p':
+			case 's':
+			case 'c':
+			case '1':
+			case '2':
+			case '4':
+			case '8':
+				break;
+			case '%':
+				type_size = sizeof(int);
+				state = 1;
+				break;
+			default:
+				printk(KERN_ERR "%s: unknown char after #: %c\n",
+					__func__, *p);
+				break;
+			}
+			break;
+		default:
+			/* should not run to here */
+			ret = -EINVAL;
+			goto err_unknown_state;
+		}
+		continue;
+type_finish:
+		format_item = new_format_item(name_head, name_tail, type_type,
+			type_size);
+		if (!format_item) {
+			printk(KERN_ERR "%s: add_format_item failed\n",
+				__func__);
+			ret = -ENOMEM;
+			goto err_new_format_item;
+		}
+
+		list_add_tail(&format_item->list, &format_item_list);
+		ret++;
+		name_head = p + 1;
+		state = 0;
+		continue;
+	}
+
+	/*
+	 * We have parsed types in format, and build linked_list to save
+	 * there information.
+	 * Now we will build a point array to save each format_item for
+	 * easy access.
+	 */
+
+	*items = kmalloc(ret * sizeof(struct format_item *), GFP_KERNEL);
+	if (!*items) {
+		printk(KERN_ERR "%s: alloc format_item index failed\n",
+			__func__);
+		ret = -ENOMEM;
+		goto err_alloc_format_item;
+	}
+
+	/* we reuse state as a iter for (*items)[] */
+	state = 0;
+	list_for_each_entry(format_item, &format_item_list, list)
+		(*items)[state++] = format_item;
+
+	return ret;
+
+err_alloc_format_item:
+err_unknown_state:
+err_new_format_item:
+	list_for_each_entry_safe(format_item, format_item_next,
+		&format_item_list, list)
+		kfree(format_item);
+	return ret;
+}
+
+/* need have format_lock */
+static struct marker_format *_find_marker_format(const char *name)
+{
+	struct marker_format *format;
+	list_for_each_entry(format, &marker_format_head, list) {
+		if (!strcmp(name, format->name))
+			return format;
+	}
+	return NULL;
+}
+
+/*
+ * For add_marker_format internal use
+ * need have format_lock
+ */
+static struct marker_format *_new_marker_format(struct marker *marker)
+{
+	struct marker_format *format;
+	char *tmp;
+
+	format = kmalloc(sizeof(struct marker_format), GFP_KERNEL);
+	if (!format)
+		goto err_alloc_format;
+
+	atomic_set(&format->refcnt, 1);
+
+	INIT_LIST_HEAD(&format->list);
+
+	tmp = kmalloc(strlen(marker->name) + 1, GFP_KERNEL);
+	if (!tmp)
+		goto err_alloc_name;
+	strcpy(tmp, marker->name);
+	format->name = tmp;
+
+	tmp = kmalloc(strlen(marker->format) + 1, GFP_KERNEL);
+	if (!tmp)
+		goto err_alloc_formatstr;
+	strcpy(tmp, marker->format);
+	format->format = tmp;
+
+	format->format_item_cnt = parse_format(marker->format,
+		&format->items);
+	if (IS_ERR_VALUE(format->format_item_cnt)) {
+		printk(KERN_ERR "%s: parse format failed: %s\n", __func__,
+			marker->format);
+		goto err_parse_file;
+	}
+
+	list_add(&format->list, &marker_format_head);
+
+	blocking_notifier_call_chain(&format_notifier_list, ADD_FORMAT, format);
+
+	return format;
+
+err_parse_file:
+	kfree(format->format);
+err_alloc_formatstr:
+	kfree(format->name);
+err_alloc_name:
+	kfree(format);
+err_alloc_format:
+	return NULL;
+}
+
+/* need have format_lock */
+static void _destroy_marker_format(struct marker_format *format)
+{
+	int i;
+
+	blocking_notifier_call_chain(&format_notifier_list, DEL_FORMAT, format);
+
+	list_del(&format->list);
+
+	for (i = 0; i < format->format_item_cnt; i++)
+		kfree(format->items[i]);
+	kfree(format->items);
+	kfree(format->format);
+	kfree(format->name);
+	kfree(format);
+
+	return;
+}
+
+static struct marker_format *add_marker_format(struct marker *marker)
+{
+	struct marker_format *format;
+
+	mutex_lock(&format_lock);
+
+	format = _find_marker_format(marker->name);
+	if (!format) {
+		format = _new_marker_format(marker);
+		if (!format) {
+			printk(KERN_ERR
+				"%s: new format struct for %s failed\n",
+				__func__, marker->name);
+			goto err_alloc_format;
+		}
+	} else {
+		atomic_inc(&format->refcnt);
+	}
+
+	mutex_unlock(&format_lock);
+	return format;
+
+err_alloc_format:
+	mutex_unlock(&format_lock);
+	return NULL;
+}
+
+static int put_marker_format(const char *name)
+{
+	struct marker_format *format;
+	int err = 0;
+
+	mutex_lock(&format_lock);
+
+	format = _find_marker_format(name);
+	if (!format) {
+		printk(KERN_ERR "%s: can't find format item for %s\n",
+			__func__, name);
+		err = -ENOENT;
+		goto err_find_marker_format;
+	}
+
+	if (atomic_dec_and_test(&format->refcnt))
+		_destroy_marker_format(format);
+
+	mutex_unlock(&format_lock);
+	return 0;
+
+err_find_marker_format:
+	mutex_unlock(&format_lock);
+	return err;
+}
+
+static void destroy_all_format(void)
+{
+	struct marker_format *format, *next;
+
+	mutex_lock(&format_lock);
+
+	list_for_each_entry_safe(format, next, &marker_format_head, list) {
+		_destroy_marker_format(format);
+	}
+
+	mutex_unlock(&format_lock);
+
+	return;
+}
+
+static int bulid_exist_format(void)
+{
+	struct marker_iter iter;
+	struct marker_format *format;
+	int err = 0;
+
+	if (!list_empty(&marker_format_head)) {
+		err = -EEXIST;
+		goto err_not_empty;
+	}
+
+	marker_iter_reset(&iter);
+	marker_iter_start(&iter);
+	for (; iter.marker != NULL; marker_iter_next(&iter)) {
+		format = add_marker_format(iter.marker);
+		if (!format) {
+			err = -EIO;
+			goto err_build_fail;
+		}
+	}
+	marker_iter_stop(&iter);
+	return 0;
+
+err_build_fail:
+	destroy_all_format();
+err_not_empty:
+	return err;
+}
+
+static int module_notify(struct notifier_block *self,
+		unsigned long val, void *data)
+{
+	struct module *mod = data;
+	struct marker *iter;
+
+	switch (val) {
+	case MODULE_STATE_COMING:
+		for (iter = mod->markers;
+			iter < mod->markers + mod->num_markers; iter++)
+			add_marker_format(iter);
+		break;
+	case MODULE_STATE_GOING:
+		for (iter = mod->markers;
+			iter < mod->markers + mod->num_markers; iter++)
+			put_marker_format(iter->name);
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block module_nb = {
+	.notifier_call = module_notify,
+};
+
+static int marker_format_init(void)
+{
+	int err = 0;
+
+	err = bulid_exist_format();
+	if (IS_ERR_VALUE(err)) {
+		printk(KERN_ERR "%s: bulid_exist_format failed\n", __func__);
+		goto err_add_exist_filter;
+	}
+
+	/* Todo:
+	 * If a module is insert/delete here(between
+	 * bulid_exist_format() and register_module_notifier()),
+	 * marker_format struct is not uptodate.
+	 */
+
+	err = register_module_notifier(&module_nb);
+	if (IS_ERR_VALUE(err)) {
+		printk(KERN_ERR "%s register module notifier failed\n",
+			__func__);
+		goto err_register_module_notifier;
+	}
+
+	return 0;
+
+err_register_module_notifier:
+	destroy_all_format();
+err_add_exist_filter:
+	return err;
+}
+
+static void marker_format_destroy(void)
+{
+	unregister_module_notifier(&module_nb);
+	destroy_all_format();
+}
+
+static int register_format_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&format_notifier_list, nb);
+}
+
+static int unregister_format_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&format_notifier_list, nb);
+}
+
+
+/*
+ * Control operations
+ *
+ * Maintenance a dir struct and datastruct for control filter
+ * dir struct will dynamical updated when marker_format got changed
+ *
+ * Todo:
+ *   Control per-trace filter
+ *
+ */
+
+enum compare_op {
+	gt,
+	ge,
+	eq,
+	le,
+	lt,
+};
+const char *compare_op_string[] = {
+	">",
+	">=",
+	"=",
+	"<=",
+	"<",
+};
+
+struct marker_filter;
+struct filter_item {
+	int enable;
+	union {
+		struct {
+			enum compare_op op;
+			long long value;
+		} signed_type;
+		struct {
+			enum compare_op op;
+			unsigned long long value;
+		} unsigned_type;
+		struct {
+			enum compare_op op;
+			char value[PATH_MAX]; /* Todo: Make it dynamic */
+		} string_type;
+		struct {
+			enum compare_op op;
+			unsigned char value;
+		} char_type;
+	} setting;
+	struct marker_filter *parent;
+	struct dentry *den[2]; /* [0]: __argX, [1]: symbol link to __argX */
+};
+
+struct marker_filter {
+	struct list_head list;
+	struct marker_format *format;
+	struct filter_item *items;
+	struct dentry *rootden;
+	struct dentry *formatden;
+	/*
+	 * is any of items[] are enabled
+	 * for bypass marker which haven't any filter fast
+	 */
+	int enable;
+};
+static inline unsigned int filter_item_no(struct filter_item *filter_item)
+{
+	return filter_item - filter_item->parent->items;
+}
+static inline struct format_item *filter_item_format(
+		struct filter_item *filter_item)
+{
+	return filter_item->parent->format->items[filter_item_no(filter_item)];
+}
+
+static LIST_HEAD(marker_filter_head);
+
+/*
+ * Use spinlock to lock filter struct because we can't use mutex in filter
+ * callback
+ */
+static DEFINE_SPINLOCK(filter_lock);
+
+/*
+ * We need call some kernel function in ltt-filter function, but many kernel
+ * function have tp/markers which cause cpu jump to ltt, then jump to
+ * ltt-filter from ltt code.
+ * Even if we don't call any kernel function in filter code, interrupt will
+ * do similar thing.
+ * To make it easy, we avoid this type of nesting by lock/unlock_filter.
+ */
+DEFINE_PER_CPU(atomic_t, filter_nesting) = ATOMIC_INIT(0);
+static inline int lock_filter(void)
+{
+	preempt_disable();
+	if (atomic_xchg(&__get_cpu_var(filter_nesting), 1) > 0) {
+		/* Already in lock */
+		preempt_enable();
+		return 1;
+	}
+	return 0;
+}
+static inline void unlock_filter(void)
+{
+	atomic_set(&__get_cpu_var(filter_nesting), 0);
+	preempt_enable();
+}
+
+struct marker_filter *_find_marker_filter(const char *name)
+{
+	struct marker_filter *filter;
+	list_for_each_entry(filter, &marker_filter_head, list) {
+		if (!strcmp(name, filter->format->name))
+			return filter;
+	}
+	return NULL;
+}
+
+static ssize_t format_read(struct file *filp, char __user *ubuf, size_t cnt,
+		loff_t *ppos)
+{
+	size_t ret;
+	struct marker_filter *filter;
+
+	lock_filter();
+	spin_lock(&filter_lock);
+	filter = filp->f_dentry->d_inode->i_private;
+	if (filter) {
+		ret = simple_read_from_buffer(ubuf, cnt, ppos,
+			filter->format->format, strlen(filter->format->format));
+	} else {
+		ret = -EIO;
+	}
+	spin_unlock(&filter_lock);
+	unlock_filter();
+
+	return ret;
+}
+
+static const struct file_operations format_fops = {
+	.read = format_read,
+};
+
+static int signed_type_read(char *buf, size_t bufsize,
+		struct filter_item *filter_item)
+{
+	int ret;
+	int pos = 0;
+
+	ret = scnprintf(buf + pos, bufsize - pos, "%s",
+		compare_op_string[filter_item->setting.signed_type.op]);
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	ret = scnprintf(buf + pos, bufsize - pos, " %lld",
+		filter_item->setting.signed_type.value);
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	return pos;
+}
+
+static int unsigned_type_read(char *buf, size_t bufsize,
+	struct filter_item *filter_item)
+{
+	int ret;
+	int pos = 0;
+
+	ret = scnprintf(buf + pos, bufsize - pos, "%s",
+		compare_op_string[filter_item->setting.unsigned_type.op]);
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	ret = scnprintf(buf + pos, bufsize - pos, " %llu",
+		filter_item->setting.unsigned_type.value);
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	return pos;
+}
+
+static int string_type_read(char *buf, size_t bufsize,
+	struct filter_item *filter_item)
+{
+	int ret;
+	int pos = 0;
+
+	ret = scnprintf(buf + pos, bufsize - pos, "%s",
+		compare_op_string[filter_item->setting.string_type.op]);
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	ret = scnprintf(buf + pos, bufsize - pos, " %s",
+		filter_item->setting.string_type.value);
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	return pos;
+}
+
+static int char_type_read(char *buf, size_t bufsize,
+	struct filter_item *filter_item)
+{
+	int ret;
+	int pos = 0;
+
+	ret = scnprintf(buf + pos, bufsize - pos, "%s",
+		compare_op_string[filter_item->setting.char_type.op]);
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	switch (filter_item->setting.char_type.value) {
+	case '\t':
+		ret = scnprintf(buf + pos, bufsize - pos, " \\t");
+		break;
+	case ' ':
+		ret = scnprintf(buf + pos, bufsize - pos, " blank");
+		break;
+	case '\n':
+		ret = scnprintf(buf + pos, bufsize - pos, " \\n");
+		break;
+	default:
+		if (isgraph(filter_item->setting.char_type.value)) {
+			ret = scnprintf(buf + pos, bufsize - pos, " %c",
+				filter_item->setting.char_type.value);
+		} else {
+			ret = scnprintf(buf + pos, bufsize - pos,
+				" \\%u", (unsigned int)
+				filter_item->setting.char_type.value);
+		}
+		break;
+	}
+	if (!ret)
+		return 0;
+	pos += ret;
+
+	return pos;
+}
+
+static ssize_t type_read(struct file *filp, char __user *ubuf, size_t cnt,
+	loff_t *ppos)
+{
+	size_t ret;
+	struct filter_item *filter_item;
+
+	lock_filter();
+	spin_lock(&filter_lock);
+	filter_item = filp->f_dentry->d_inode->i_private;
+	if (filter_item) {
+		struct format_item *format_item;
+		char buf[128];
+		int pos = 0;
+
+		format_item = filter_item_format(filter_item);
+
+		ret = scnprintf(buf + pos, sizeof(buf) - pos,
+			"type: %u byte(s) %s\n",
+			format_item->size,
+			format_item_type_string[format_item->type]);
+		if (!ret) {
+			ret = -EINVAL;
+			goto err_format_string;
+		}
+		pos += ret;
+
+		ret = scnprintf(buf + pos, sizeof(buf) - pos, "setting: ");
+		if (!ret) {
+			ret = -EINVAL;
+			goto err_format_string;
+		}
+		pos += ret;
+
+		if (!filter_item->enable) {
+			ret = scnprintf(buf + pos, sizeof(buf) - pos, "*");
+			if (!ret) {
+				ret = -EINVAL;
+				goto err_format_string;
+			}
+			pos += ret;
+			goto format_finish;
+		}
+
+		switch (format_item->type) {
+		case SIGNED:
+			ret = signed_type_read(buf + pos, sizeof(buf) - pos,
+				filter_item);
+			break;
+		case UNSIGNED:
+			ret = unsigned_type_read(buf + pos, sizeof(buf) - pos,
+				filter_item);
+			break;
+		case STRING:
+			ret = string_type_read(buf + pos, sizeof(buf) - pos,
+				filter_item);
+			break;
+		case CHAR:
+			ret = char_type_read(buf + pos, sizeof(buf) - pos,
+				filter_item);
+			break;
+		default:
+			/* should not run to here */
+			printk(KERN_ERR "%s: unknown type %d\n",
+				__func__, format_item->type);
+			ret = -EIO;
+			goto err_unknown_type;
+		}
+		if (!ret) {
+			ret = -EINVAL;
+			goto err_format_string;
+		}
+		pos += ret;
+
+format_finish:
+		ret = scnprintf(buf + pos, sizeof(buf) - pos, "\n");
+		if (!ret) {
+			ret = -EINVAL;
+			goto err_format_string;
+		}
+		pos += ret;
+
+		ret = simple_read_from_buffer(ubuf, cnt, ppos, buf,
+			strlen(buf));
+	} else {
+		ret = -EIO;
+	}
+
+err_format_string:
+err_unknown_type:
+	spin_unlock(&filter_lock);
+	unlock_filter();
+
+	return ret;
+}
+
+/*
+ * Check op info in a input string
+ * return length of op part on success, *op is set.
+ * return 0 on fail
+ */
+static size_t scan_op(const char *str, enum compare_op *op)
+{
+	/* Must reverse sort with strlen */
+	static struct {
+		const char *str;
+		enum compare_op op;
+	} str_op[] = {
+		{"gt",	gt},
+		{"ge",	ge},
+		{"eq",	eq},
+		{"le",	le},
+		{"lt",	lt},
+		{">=",	ge},
+		{"<=",	le},
+		{">",	gt},
+		{"=",	eq},
+		{"<",	lt},
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(str_op); i++) {
+		if (!strncmp(str, str_op[i].str, strlen(str_op[i].str))) {
+			*op = str_op[i].op;
+			return strlen(str_op[i].str);
+		}
+	}
+
+	return 0;
+}
+
+static int signed_type_write(const char *buf, struct format_item *format_item,
+		struct filter_item *filter_item)
+{
+	int oplen;
+	enum compare_op op;
+	long long value = 0;
+
+	oplen = scan_op(buf, &op);
+	if (!oplen)
+		return -EINVAL;
+
+	switch (format_item->size) {
+	case 1:
+		{
+			char tmp;
+			if (sscanf(buf + oplen, "%hhd", &tmp) != 1)
+				return -EINVAL;
+			value = tmp;
+		}
+		break;
+	case 2:
+		{
+			short int tmp;
+			if (sscanf(buf + oplen, "%hd", &tmp) != 1)
+				return -EINVAL;
+			value = tmp;
+		}
+		break;
+	case 4:
+		{
+			int tmp;
+			if (sscanf(buf + oplen, "%d", &tmp) != 1)
+				return -EINVAL;
+			value = tmp;
+		}
+		break;
+	case 8:
+		if (sscanf(buf + oplen, "%lld", &value) != 1)
+			return -EINVAL;
+		break;
+	default:
+		/* should not run to here */
+		printk(KERN_ERR "%s: unknown size %zu\n", __func__,
+			format_item->size);
+		return -EIO;
+	}
+
+	filter_item->setting.signed_type.op = op;
+	filter_item->setting.signed_type.value = value;
+
+	return 0;
+}
+
+static int unsigned_type_write(const char *buf, struct format_item *format_item,
+		struct filter_item *filter_item)
+{
+	int oplen;
+	enum compare_op op;
+	long long value = 0;
+
+	oplen = scan_op(buf, &op);
+	if (!oplen)
+		return -EINVAL;
+
+	switch (format_item->size) {
+	case 1:
+		{
+			unsigned char tmp;
+			if (sscanf(buf + oplen, "%hhu", &tmp) != 1)
+				return -EINVAL;
+			value = tmp;
+		}
+		break;
+	case 2:
+		{
+			unsigned short int tmp;
+			if (sscanf(buf + oplen, "%hu", &tmp) != 1)
+				return -EINVAL;
+			value = tmp;
+		}
+		break;
+	case 4:
+		{
+			unsigned int tmp;
+			if (sscanf(buf + oplen, "%u", &tmp) != 1)
+				return -EINVAL;
+			value = tmp;
+		}
+		break;
+	case 8:
+		if (sscanf(buf + oplen, "%llu", &value) != 1)
+			return -EINVAL;
+		break;
+	default:
+		/* should not run to here */
+		printk(KERN_ERR "%s: unknown size %zu\n", __func__,
+			format_item->size);
+		return -EIO;
+	}
+
+	filter_item->setting.unsigned_type.op = op;
+	filter_item->setting.unsigned_type.value = value;
+
+	return 0;
+}
+
+static int string_type_write(const char *buf, struct format_item *format_item,
+		struct filter_item *filter_item)
+{
+	int oplen;
+	enum compare_op op;
+	int val_len;
+
+	oplen = scan_op(buf, &op);
+	if (!oplen)
+		return -EINVAL;
+
+	/* End string on \n */
+	for (val_len = 0; *(buf + oplen + val_len); val_len++) {
+		if (*(buf + oplen + val_len) == '\n')
+			break;
+	}
+
+	if (val_len > ARRAY_SIZE(filter_item->setting.string_type.value) - 1)
+		return -EINVAL;
+
+	filter_item->setting.string_type.op = op;
+	memcpy(filter_item->setting.string_type.value, buf + oplen, val_len);
+	filter_item->setting.string_type.value[val_len] = 0;
+
+	return 0;
+}
+
+static int char_type_write(const char *buf, struct format_item *format_item,
+		struct filter_item *filter_item)
+{
+	int oplen;
+	enum compare_op op;
+	unsigned char value;
+	int val_start;
+
+	oplen = scan_op(buf, &op);
+	if (!oplen)
+		return -EINVAL;
+
+	/* bypass blanks */
+	for (val_start = 0; *(buf + oplen + val_start) == ' ' ||
+		*(buf + oplen + val_start) == '\t'; val_start++)
+		;
+
+	/*
+	 * parse escape string, following escape string are supported:
+	 *   "\n"
+	 *   "\[num]"
+	 *   "\[char expect \n and \[num]]", as "\\", "\ ", ...
+	 */
+	switch (*(buf + oplen + val_start)) {
+	case '\0':
+	case '\n':
+		return -EINVAL;
+	case '\\':
+		switch (*(buf + oplen + val_start + 1)) {
+		case '\0':
+		case '\n':
+			return -EINVAL;
+		case 'n':
+			value = '\n';
+			break;
+		default:
+			if (isdigit(*(buf + oplen + val_start + 1))) {
+				if (sscanf(buf + oplen + val_start + 1, "%hhu",
+					&value) != 1)
+					return -EINVAL;
+			} else {
+				value =
+					(*(buf + oplen + val_start + 1));
+			}
+			break;
+		}
+		break;
+	default:
+		value = *(buf + oplen + val_start);
+		break;
+	}
+
+	filter_item->setting.char_type.op = op;
+	filter_item->setting.char_type.value = value;
+
+	return 0;
+}
+
+static ssize_t type_write(struct file *filp, const char __user *ubuf,
+	size_t cnt, loff_t *ppos)
+{
+	size_t ret;
+	struct filter_item *filter_item;
+
+	lock_filter();
+	spin_lock(&filter_lock);
+	filter_item = filp->f_dentry->d_inode->i_private;
+	if (filter_item) {
+		struct format_item *format_item;
+		char buf[NAME_MAX];
+		size_t buf_size;
+		int val_start;
+
+		format_item = filter_item_format(filter_item);
+
+		buf_size = min(cnt, sizeof(buf) - 1);
+		if (copy_from_user(buf, ubuf, buf_size)) {
+			ret = -EFAULT;
+			goto err_copy_from_user;
+		}
+		buf[buf_size] = 0;
+
+		/* Check is to disable filter */
+		for (val_start = 0; *(buf + val_start) == ' ' ||
+			*(buf + val_start) == '\t'; val_start++)
+			;
+		if (!strncmp(buf + val_start, "*", strlen("*"))
+			|| !strncmp(buf + val_start, "off", strlen("off"))) {
+			struct marker_filter *filter;
+			int i;
+
+			filter_item->enable = 0;
+
+			/*
+			 * rescan all items in this marker to
+			 * update marker_filter->enable
+			 */
+			filter = filter_item->parent;
+			filter->enable = 0;
+			for (i = 0; i < filter->format->format_item_cnt; i++) {
+				if (filter->items[i].enable) {
+					filter->enable = 1;
+					break;
+				}
+			}
+
+			goto scan_finish;
+		}
+
+		switch (format_item->type) {
+		case SIGNED:
+			ret = signed_type_write(buf, format_item, filter_item);
+			break;
+		case UNSIGNED:
+			ret = unsigned_type_write(buf, format_item,
+				filter_item);
+			break;
+		case STRING:
+			ret = string_type_write(buf, format_item, filter_item);
+			break;
+		case CHAR:
+			ret = char_type_write(buf, format_item, filter_item);
+			break;
+		default:
+			/* should not run to here */
+			printk(KERN_ERR "%s: unknown type %d\n",
+				__func__, format_item->type);
+			ret = -EIO;
+			goto err_unknown_type;
+		}
+		if (IS_ERR_VALUE(ret))
+			goto err_scan_string;
+
+		filter_item->enable = 1;
+		filter_item->parent->enable = 1;
+
+scan_finish:
+		ret = cnt;
+	} else {
+		ret = -EIO;
+	}
+
+err_scan_string:
+err_unknown_type:
+err_copy_from_user:
+	spin_unlock(&filter_lock);
+	unlock_filter();
+
+	return ret;
+}
+
+static const struct file_operations type_fops = {
+	.read = type_read,
+	.write = type_write,
+};
+
+static int init_filter_items(struct marker_filter *filter,
+		struct marker_format *format)
+{
+	int err = 0;
+	int i;
+
+	for (i = 0; i < format->format_item_cnt; i++) {
+		char fname[16];
+
+		filter->items[i].parent = filter;
+		filter->items[i].enable = 0;
+
+		/* Create type file as __arg1, __arg2 */
+		sprintf(fname, "__arg%d", i + 1);
+		filter->items[i].den[0] = debugfs_create_file(fname, S_IRUGO,
+			filter->rootden, &filter->items[i], &type_fops);
+		if (IS_ERR(filter->items[i].den[0])
+			|| !filter->items[i].den[0]) {
+			printk(KERN_ERR
+				"%s: create type file of %s for %s failed\n",
+				__func__, fname, filter->format->name);
+			err = -EIO;
+			goto err_build_typefile;
+		}
+
+		/* Create symbol link to type file */
+		if (filter->format->items[i]->name[0]) {
+
+			filter->items[i].den[1] = debugfs_create_symlink(
+				filter->format->items[i]->name,
+				filter->rootden, fname);
+			if (IS_ERR(filter->items[i].den[1])
+				|| !filter->items[i].den[1]) {
+				/*
+				 * same-name type is allowed, but it will cause
+				 * create symlink fail.
+				 * we ignore this type of error.
+				 */
+				filter->items[i].den[1] = NULL;
+			}
+		}
+	}
+
+	return 0;
+
+err_build_typefile:
+	for (i--; i >= 0; i--) {
+		/* see comments in _destroy_marker_filter() for why set NULL */
+		filter->items[i].den[0]->d_inode->i_private = NULL;
+		debugfs_remove(filter->items[i].den[0]);
+		debugfs_remove(filter->items[i].den[1]);
+	}
+	return err;
+}
+
+static int add_marker_filter(struct marker_format *format)
+{
+	int err = 0;
+	struct marker_filter *filter;
+
+	lock_filter();
+	spin_lock(&filter_lock);
+
+#ifdef _DEBUG
+	if (_find_marker_filter(format->name)) {
+		printk(KERN_ERR "%s: filter %s already exist\n", __func__,
+			format->name);
+		err = -EIO;
+		goto err_already_exist;
+	}
+#endif /* _DEBUG */
+
+	filter = kmalloc(sizeof(struct marker_filter), GFP_KERNEL);
+	if (!filter) {
+		printk(KERN_ERR "%s: alloc struct marker_filter failed\n",
+			__func__);
+		err = -ENOMEM;
+		goto err_alloc_marker_filter;
+	}
+
+	INIT_LIST_HEAD(&filter->list);
+
+	filter->format = format;
+
+	filter->rootden = debugfs_create_dir(format->name, ltt_filter_dir);
+	if (IS_ERR(filter->rootden) || !filter->rootden) {
+		printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
+			format->name);
+		err = -EIO;
+		goto err_create_dir;
+	}
+
+	filter->formatden = debugfs_create_file("__arg0", S_IRUGO,
+		filter->rootden, filter, &format_fops);
+	if (IS_ERR(filter->formatden) || !filter->formatden) {
+		printk(KERN_ERR
+			"%s: create typefile failed\n", __func__);
+		err = -EIO;
+		goto err_build_typefile;
+	}
+
+	if (format->format_item_cnt) {
+		filter->items = kmalloc(sizeof(struct filter_item)
+			* format->format_item_cnt, GFP_KERNEL);
+		if (!filter->items) {
+			printk(KERN_ERR
+				"%s: alloc filter's items failed\n", __func__);
+			err = -ENOMEM;
+			goto err_alloc_types;
+		}
+		err = init_filter_items(filter, format);
+		if (IS_ERR_VALUE(err)) {
+			printk(KERN_ERR
+				"%s: init filter's items failed\n", __func__);
+			goto err_init_types;
+		}
+	} else {
+		filter->items = NULL;
+	}
+
+	filter->enable = 0;
+
+	list_add(&filter->list, &marker_filter_head);
+
+	spin_unlock(&filter_lock);
+	unlock_filter();
+
+	return 0;
+
+err_init_types:
+	kfree(filter->items);
+err_alloc_types:
+	/* see comments in _destroy_marker_filter() for why set NULL */
+	filter->formatden->d_inode->i_private = NULL;
+	debugfs_remove(filter->formatden);
+err_build_typefile:
+	debugfs_remove(filter->rootden);
+err_create_dir:
+	kfree(filter);
+err_alloc_marker_filter:
+#ifdef _DEBUG
+err_already_exist:
+#endif /* _DEBUG */
+	spin_unlock(&filter_lock);
+	unlock_filter();
+	return err;
+}
+
+static void _destroy_marker_filter(struct marker_filter *filter)
+{
+	int i;
+
+	list_del(&filter->list);
+
+	for (i = 0; i < filter->format->format_item_cnt; i++) {
+		filter->items[i].den[0]->d_inode->i_private = NULL;
+		debugfs_remove(filter->items[i].den[0]);
+		debugfs_remove(filter->items[i].den[1]);
+	}
+	kfree(filter->items);
+
+	/*
+	 * Set i_private to NULL, so that if some fileop in process,
+	 * it can avoid illegal access using i_private in fileop.
+	 * (we must get filter_lock in fileop to ensure no other change
+	 *  in filter struct)
+	 */
+	filter->formatden->d_inode->i_private = NULL;
+	debugfs_remove(filter->formatden);
+
+	debugfs_remove(filter->rootden);
+
+	kfree(filter);
+
+	return;
+}
+
+static void destroy_all_filter(void)
+{
+	struct marker_filter *filter, *next;
+
+	lock_filter();
+	spin_lock(&filter_lock);
+	list_for_each_entry_safe(filter, next, &marker_filter_head, list) {
+		_destroy_marker_filter(filter);
+	}
+	spin_unlock(&filter_lock);
+	unlock_filter();
+}
+
+static int bulid_exist_filter(void)
+{
+	int err;
+	struct marker_format *format;
+
+	if (!list_empty(&marker_filter_head)) {
+		err = -EEXIST;
+		goto err_not_empty;
+	}
+
+	mutex_lock(&format_lock);
+	list_for_each_entry(format, &marker_format_head, list) {
+		err = add_marker_filter(format);
+		if (IS_ERR_VALUE(err)) {
+			printk("%s: add_marker_filter failed\n", __func__);
+			goto err_add_filter;
+		}
+	}
+	mutex_unlock(&format_lock);
+
+	return 0;
+
+err_add_filter:
+	destroy_all_filter();
+	mutex_unlock(&format_lock);
+err_not_empty:
+	return err;
+}
+
+static int format_notify(struct notifier_block *self, unsigned long val,
+		void *data)
+{
+	struct marker_format *format = data;
+	struct marker_filter *filter, *next;
+
+	switch (val) {
+	case ADD_FORMAT:
+		add_marker_filter(format);
+		break;
+	case DEL_FORMAT:
+		lock_filter();
+		spin_lock(&filter_lock);
+		list_for_each_entry_safe(filter, next, &marker_filter_head,
+			list) {
+			if (filter->format == format)
+				_destroy_marker_filter(filter);
+		}
+		spin_unlock(&filter_lock);
+		unlock_filter();
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block format_nb = {
+	.notifier_call = format_notify,
+};
+
+static int marker_filter_init(void)
+{
+	int err = 0;
+
+	err = bulid_exist_filter();
+	if (IS_ERR_VALUE(err)) {
+		printk(KERN_ERR "%s: bulid_exist_filter failed\n", __func__);
+		goto err_add_exist_filter;
+	}
+
+	err = register_format_notifier(&format_nb);
+	if (IS_ERR_VALUE(err)) {
+		printk(KERN_ERR "%s register format notifier failed\n",
+			__func__);
+		goto err_register_format_notifier;
+	}
+
+	return 0;
+
+err_register_format_notifier:
+	destroy_all_filter();
+err_add_exist_filter:
+	return err;
+}
+
+static void marker_filter_destroy(void)
+{
+	unregister_format_notifier(&format_nb);
+	destroy_all_filter();
+}
+
+
+/*
+ * Filter operations
+ *
+ * define filter callback for lttng to determine if to output a event
+ * by user's setting.
+ *
+ */
+
+/*
+ * Ret:
+ *   0:  filted
+ *   1:  passed
+ *   <0: err
+ */
+static int signed_filter(struct format_item *format_item,
+		struct filter_item *filter_item, va_list *args)
+{
+	long long int val;
+
+	switch (format_item->size) {
+	case 1:
+		val = (char)va_arg(*args, int);
+		break;
+	case 2:
+		val = (short int)va_arg(*args, int);
+		break;
+	case 4:
+		val = va_arg(*args, int);
+		break;
+	case 8:
+		val = va_arg(*args, long long int);
+		break;
+	default:
+		/* should not run to here */
+		printk(KERN_ERR "%s: unknown size %d\n", __func__,
+			format_item->size);
+		return -1;
+	}
+
+	/*
+	 * We judge filter_item->enable here, because even if filter is not
+	 * enabled, we still need to bypass this data in va_list
+	 */
+	if (!filter_item->enable)
+		return 1;
+
+	switch (filter_item->setting.signed_type.op) {
+	case gt:
+		return val > filter_item->setting.signed_type.value;
+	case ge:
+		return val >= filter_item->setting.signed_type.value;
+	case eq:
+		return val == filter_item->setting.signed_type.value;
+	case le:
+		return val <= filter_item->setting.signed_type.value;
+	case lt:
+		return val < filter_item->setting.signed_type.value;
+	}
+
+	/* should not run to here */
+	return -1;
+}
+
+static int unsigned_filter(struct format_item *format_item,
+		struct filter_item *filter_item, va_list *args)
+{
+	unsigned long long int val;
+
+	switch (format_item->size) {
+	case 1:
+		val = (unsigned char)va_arg(*args, unsigned int);
+		break;
+	case 2:
+		val = (short int)va_arg(*args, unsigned int);
+		break;
+	case 4:
+		val = va_arg(*args, unsigned int);
+		break;
+	case 8:
+		val = va_arg(*args, unsigned long long int);
+		break;
+	default:
+		/* should not run to here */
+		printk(KERN_ERR "%s: unknown size %d\n", __func__,
+			format_item->size);
+		return -1;
+	}
+
+	/*
+	 * We judge filter_item->enable here, because even if filter is not
+	 * enabled, we still need to bypass this data in va_list
+	 */
+	if (!filter_item->enable)
+		return 1;
+
+	switch (filter_item->setting.unsigned_type.op) {
+	case gt:
+		return val > filter_item->setting.unsigned_type.value;
+	case ge:
+		return val >= filter_item->setting.unsigned_type.value;
+	case eq:
+		return val == filter_item->setting.unsigned_type.value;
+	case le:
+		return val <= filter_item->setting.unsigned_type.value;
+	case lt:
+		return val < filter_item->setting.unsigned_type.value;
+	}
+
+	/* should not run to here */
+	return -1;
+}
+
+static int string_filter(struct format_item *format_item,
+		struct filter_item *filter_item, va_list *args)
+{
+	const char *val;
+
+	val = va_arg(*args, const char *);
+
+	/*
+	 * We judge filter_item->enable here, because even if filter is not
+	 * enabled, we still need to bypass this data in va_list
+	 */
+	if (!filter_item->enable)
+		return 1;
+
+	switch (filter_item->setting.string_type.op) {
+	case gt:
+		return strcmp(val, filter_item->setting.string_type.value) > 0;
+	case ge:
+		return strcmp(val, filter_item->setting.string_type.value) >= 0;
+	case eq:
+		return strcmp(val, filter_item->setting.string_type.value) == 0;
+	case le:
+		return strcmp(val, filter_item->setting.string_type.value) <= 0;
+	case lt:
+		return strcmp(val, filter_item->setting.string_type.value) < 0;
+	}
+
+	/* should not run to here */
+	return -1;
+}
+
+static int char_filter(struct format_item *format_item,
+		struct filter_item *filter_item, va_list *args)
+{
+	unsigned char val;
+
+	val = (unsigned char)va_arg(*args, unsigned int);
+
+	/*
+	 * We judge filter_item->enable here, because even if filter is not
+	 * enabled, we still need to bypass this data in va_list
+	 */
+	if (!filter_item->enable)
+		return 1;
+
+	switch (filter_item->setting.char_type.op) {
+	case gt:
+		return val > filter_item->setting.char_type.value;
+	case ge:
+		return val >= filter_item->setting.char_type.value;
+	case eq:
+		return val == filter_item->setting.char_type.value;
+	case le:
+		return val <= filter_item->setting.char_type.value;
+	case lt:
+		return val < filter_item->setting.char_type.value;
+	}
+
+	/* should not run to here */
+	return -1;
+}
+
+static int ltt_filter(struct ltt_trace_struct *trace,
+		const struct marker *mdata, va_list *args)
+{
+	int ret;
+	struct marker_filter *filter;
+	struct marker_format *format;
+	int i;
+
+	if (lock_filter()) {
+		/* Don't output events caused by ltt-filter */
+		return 0;
+	}
+
+	spin_lock(&filter_lock);
+
+	filter = _find_marker_filter(mdata->name);
+	if (!filter) {
+		printk(KERN_ERR "%s: unknown marker %s\n", __func__,
+			mdata->name);
+		ret = 1;
+		goto done;
+	}
+
+	/* Bypass if no filter setting on this marker */
+	if (!filter->enable) {
+		ret = 1;
+		goto done;
+	}
+
+	format = filter->format;
+
+	for (i = 0; i < format->format_item_cnt; i++) {
+		int result;
+
+		switch (format->items[i]->type) {
+		case SIGNED:
+			result = signed_filter(format->items[i],
+				filter->items + i, args);
+			break;
+		case UNSIGNED:
+			result = unsigned_filter(format->items[i],
+				filter->items + i, args);
+			break;
+		case STRING:
+			result = string_filter(format->items[i],
+				filter->items + i, args);
+			break;
+		case CHAR:
+			result = char_filter(format->items[i],
+				filter->items + i, args);
+			break;
+		default:
+			/* should not run to here */
+			printk(KERN_ERR "%s: unknown type %d\n", __func__,
+				format->items[i]->type);
+			ret = 1;
+			goto done;
+		}
+		if (result < 0) {
+			ret = 1;
+			goto done;
+		}
+		if (!result) {
+			ret = 0;
+			goto done;
+		}
+	}
+
+	ret = 1;
+
+done:
+	spin_unlock(&filter_lock);
+	unlock_filter();
+	return ret;
+}
+
+
+/*
+ * Init/exit option of module
+ *
+ * Init marker_format and marker_filter operations
+ * register filter callback in ltt
+ *
+ */
+
+static int __init ltt_filter_init(void)
+{
+	int err = 0;
+	struct dentry *ltt_root_dentry;
+
+	ltt_root_dentry = get_ltt_root();
+	if (!ltt_root_dentry) {
+		err = -ENOENT;
+		goto err_no_root;
+	}
+
+	ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR, ltt_root_dentry);
+	if (IS_ERR(ltt_filter_dir) || !ltt_filter_dir) {
+		printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
+			LTT_FILTER_DIR);
+		err = -ENOMEM;
+		goto err_create_filter_dir;
+	}
+
+	err = marker_format_init();
+	if (IS_ERR_VALUE(err)) {
+		printk(KERN_ERR "%s: init marker format failed\n", __func__);
+		goto err_marker_format_init;
+	}
+
+	err = marker_filter_init();
+	if (IS_ERR_VALUE(err)) {
+		printk(KERN_ERR "%s: init marker filter failed\n", __func__);
+		goto err_marker_filter_init;
+	}
+
+	err = ltt_module_register(LTT_FUNCTION_RUN_FILTER, ltt_filter,
+		THIS_MODULE);
+	if (IS_ERR_VALUE(err)) {
+		printk(KERN_ERR "%s: register ltt filter failed\n", __func__);
+		goto err_filter_register;
+	}
+
+	return 0;
+
+err_filter_register:
+	marker_filter_destroy();
+err_marker_filter_init:
+	marker_format_destroy();
+err_marker_format_init:
+	debugfs_remove(ltt_filter_dir);
+err_create_filter_dir:
+err_no_root:
+	return err;
+}
+
+static void __exit ltt_filter_exit(void)
+{
+	ltt_module_unregister(LTT_FUNCTION_RUN_FILTER);
+	marker_filter_destroy();
+	marker_format_destroy();
+
+	debugfs_remove(ltt_filter_dir);
+}
+
+module_init(ltt_filter_init);
+module_exit(ltt_filter_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhao Lei <zhaolei at cn.fujitsu.com>");
+MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
-- 
1.5.5.3






More information about the lttng-dev mailing list