summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/trace/tracelevel.txt42
-rw-r--r--include/linux/tracelevel.h42
-rw-r--r--kernel/trace/Kconfig9
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/tracelevel.c142
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);