summaryrefslogtreecommitdiff
path: root/kernel/trace/tracelevel.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace/tracelevel.c')
-rw-r--r--kernel/trace/tracelevel.c142
1 files changed, 142 insertions, 0 deletions
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);