diff options
-rw-r--r-- | Documentation/trace/tracelevel.txt | 42 | ||||
-rw-r--r-- | include/linux/tracelevel.h | 42 | ||||
-rw-r--r-- | kernel/trace/Kconfig | 9 | ||||
-rw-r--r-- | kernel/trace/Makefile | 1 | ||||
-rw-r--r-- | kernel/trace/tracelevel.c | 142 |
5 files changed, 236 insertions, 0 deletions
diff --git a/Documentation/trace/tracelevel.txt b/Documentation/trace/tracelevel.txt new file mode 100644 index 000000000000..b282dd2b329b --- /dev/null +++ b/Documentation/trace/tracelevel.txt @@ -0,0 +1,42 @@ + Tracelevel + + Documentation by Alon Farchy + +1. Overview +=========== + +Tracelevel allows subsystem authors to add trace priorities to +their tracing events. High priority traces will be enabled +automatically at boot time. + +This module is configured with CONFIG_TRACELEVEL. + +2. Usage +========= + +To give an event a priority, use the function tracelevel_register +at any time. + + tracelevel_register(my_event, level); + +my_event corresponds directly to the event name as defined in the +event header file. Available levels are: + + TRACELEVEL_ERR 3 + TRACELEVEL_WARN 2 + TRACELEVEL_INFO 1 + TRACELEVEL_DEBUG 0 + +Any event registered at boot time as TRACELEVEL_ERR will be enabled +by default. The header also exposes the function tracelevel_set_level +to change the trace level at runtime. Any trace event registered with the +specified level or higher will be enabled with this call. + +A userspace handle to tracelevel_set_level is available via the module +parameter 'level'. For example, + + echo 1 > /sys/module/tracelevel/parameters/level + +Is logically equivalent to: + + tracelevel_set_level(TRACELEVEL_INFO); diff --git a/include/linux/tracelevel.h b/include/linux/tracelevel.h new file mode 100644 index 000000000000..ac3351c6ed85 --- /dev/null +++ b/include/linux/tracelevel.h @@ -0,0 +1,42 @@ +/* + * include/linux/tracelevel.c + * + * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _TRACELEVEL_H +#define _TRACELEVEL_H + +/* tracelevel allows a subsystem author to add priorities to + * trace_events. For usage details, see tracelevel.txt. + */ + +#define TRACELEVEL_ERR 3 +#define TRACELEVEL_WARN 2 +#define TRACELEVEL_INFO 1 +#define TRACELEVEL_DEBUG 0 + +#define TRACELEVEL_MAX TRACELEVEL_ERR +#define TRACELEVEL_DEFAULT TRACELEVEL_ERR + +int __tracelevel_register(char *name, unsigned int level); +int tracelevel_set_level(int level); + +#define tracelevel_register(name, level) \ + __tracelevel_register(#name, level) + +#endif /* _TRACELEVEL_H */ diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 015f85aaca08..1c8ed74dcaf0 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -573,6 +573,15 @@ config RING_BUFFER_STARTUP_TEST If unsure, say N +config TRACELEVEL + bool "Add capability to prioritize traces" + depends on EVENT_TRACING + help + This option allows subsystem programmers to add priorities to trace + events by calling to tracelevel_register. Traces of high priority + will automatically be enabled on kernel boot, and users can change + the the trace level in a kernel parameter. + endif # FTRACE endif # TRACING_SUPPORT diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index d7e2068e4b71..45ef52cfb0b9 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -58,6 +58,7 @@ endif ifeq ($(CONFIG_TRACING),y) obj-$(CONFIG_KGDB_KDB) += trace_kdb.o endif +obj-$(CONFIG_TRACELEVEL) += tracelevel.o obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o diff --git a/kernel/trace/tracelevel.c b/kernel/trace/tracelevel.c new file mode 100644 index 000000000000..9f8b8eedbb58 --- /dev/null +++ b/kernel/trace/tracelevel.c @@ -0,0 +1,142 @@ +/* + * kernel/trace/tracelevel.c + * + * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/ftrace_event.h> +#include <linux/list.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/tracelevel.h> +#include <linux/vmalloc.h> + +#include "trace.h" + +#define TAG KERN_ERR "tracelevel: " + +struct tracelevel_record { + struct list_head list; + char *name; + int level; +}; + +static LIST_HEAD(tracelevel_list); + +static bool started; +static unsigned int tracelevel_level = TRACELEVEL_DEFAULT; + +static DEFINE_MUTEX(tracelevel_record_lock); + +/* tracelevel_set_event sets a single event if set = 1, or + * clears an event if set = 0. + */ +static int tracelevel_set_event(struct tracelevel_record *evt, bool set) +{ + if (trace_set_clr_event(NULL, evt->name, set) < 0) { + printk(TAG "failed to set event %s\n", evt->name); + return -EINVAL; + } + return 0; +} + +/* Registers an event. If possible, it also sets it. + * If not, we'll set it in tracelevel_init. + */ +int __tracelevel_register(char *name, unsigned int level) +{ + struct tracelevel_record *evt = (struct tracelevel_record *) + vmalloc(sizeof(struct tracelevel_record)); + if (!evt) { + printk(TAG "failed to allocate tracelevel_record for %s\n", + name); + return -ENOMEM; + } + + evt->name = name; + evt->level = level; + + mutex_lock(&tracelevel_record_lock); + list_add(&evt->list, &tracelevel_list); + mutex_unlock(&tracelevel_record_lock); + + if (level >= tracelevel_level && started) + tracelevel_set_event(evt, 1); + return 0; +} + +/* tracelevel_set_level sets the global level, clears events + * lower than that level, and enables events greater or equal. + */ +int tracelevel_set_level(int level) +{ + struct tracelevel_record *evt = NULL; + + if (level < 0 || level > TRACELEVEL_MAX) + return -EINVAL; + tracelevel_level = level; + + mutex_lock(&tracelevel_record_lock); + list_for_each_entry(evt, &tracelevel_list, list) { + if (evt->level >= level) + tracelevel_set_event(evt, 1); + else + tracelevel_set_event(evt, 0); + } + mutex_unlock(&tracelevel_record_lock); + return 0; +} + +static int param_set_level(const char *val, const struct kernel_param *kp) +{ + int level, ret; + ret = strict_strtol(val, 0, &level); + if (ret < 0) + return ret; + return tracelevel_set_level(level); +} + +static int param_get_level(char *buffer, const struct kernel_param *kp) +{ + return param_get_int(buffer, kp); +} + +static struct kernel_param_ops tracelevel_level_ops = { + .set = param_set_level, + .get = param_get_level +}; + +module_param_cb(level, &tracelevel_level_ops, &tracelevel_level, 0644); + +/* Turn on the tracing that has been registered thus far. */ +static int __init tracelevel_init(void) +{ + int ret; + started = true; + + /* Ring buffer is initialize to 1 page until the user sets a tracer. + * Since we're doing this manually, we need to ask for expanded buffer. + */ + ret = tracing_update_buffers(); + if (ret < 0) + return ret; + + return tracelevel_set_level(tracelevel_level); +} + +/* Tracing mechanism is set up during fs_initcall. */ +fs_initcall_sync(tracelevel_init); |