[ltt-dev] [PATCH 3/3] In-kernel filter module
Mathieu Desnoyers
compudj at krystal.dyndns.org
Mon Apr 6 13:52:35 EDT 2009
* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> 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
>
Hi Zhaolei,
That's a good start, but locking will have to be rethought. Please see
comments below,
> 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
paresed -> parsed
> + * Impl a notifier for respond add/remove marker format event
Notifier which responds to 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];
this rather large "name" field should go at the end of the struct.
> + 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();
> +}
Hrm, this lock_filter seems broken. If you have an interrupt nesting
over lock_filter, and takes lock_filter again, the interrupt will set
the filter_nesting to 0, which creates a race.
Basically, what you want is to disallow filter modification used by an
active tracing session _or_, if you want to allow filter modification
while a tracing session is active. But to do it properly, you want to
use RCU data structures synchronized with call_rcu_sched and
synchronize_sched and use the RCU read lock (which is _already_ taken by
LTTng around probe execution anyway) to synchronize.
The filter_lock spinlock should probably become a simple mutex too.
Mathieu
> +
> +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()) {
Hrm do you allow session filter modification when tracing is active for
this session ?
I think the lock_filter approach you are taking here is wrong.
> + /* 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
>
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
More information about the lttng-dev
mailing list