summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ftrace.h6
-rw-r--r--kernel/sysctl.c11
-rw-r--r--kernel/trace/ftrace.c125
3 files changed, 124 insertions, 18 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 90dbc0ee204..ccd8537dbdb 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -5,6 +5,12 @@
#include <linux/linkage.h>
+extern int ftrace_enabled;
+extern int
+ftrace_enable_sysctl(struct ctl_table *table, int write,
+ struct file *filp, void __user *buffer, size_t *lenp,
+ loff_t *ppos);
+
typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip);
struct ftrace_ops {
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 29116652dca..efaf7c5500e 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -46,6 +46,7 @@
#include <linux/nfs_fs.h>
#include <linux/acpi.h>
#include <linux/reboot.h>
+#include <linux/ftrace.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
@@ -455,6 +456,16 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+#ifdef CONFIG_FTRACE
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "ftrace_enabled",
+ .data = &ftrace_enabled,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &ftrace_enable_sysctl,
+ },
+#endif
#ifdef CONFIG_KMOD
{
.ctl_name = KERN_MODPROBE,
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index d1ae2ba2527..d3de37299ba 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -20,12 +20,24 @@
#include <linux/hardirq.h>
#include <linux/ftrace.h>
#include <linux/module.h>
+#include <linux/sysctl.h>
#include <linux/hash.h>
#include <linux/list.h>
#include "trace.h"
+#ifdef CONFIG_DYNAMIC_FTRACE
+# define FTRACE_ENABLED_INIT 1
+#else
+# define FTRACE_ENABLED_INIT 0
+#endif
+
+int ftrace_enabled = FTRACE_ENABLED_INIT;
+static int last_ftrace_enabled = FTRACE_ENABLED_INIT;
+
static DEFINE_SPINLOCK(ftrace_lock);
+static DEFINE_MUTEX(ftrace_sysctl_lock);
+
static struct ftrace_ops ftrace_list_end __read_mostly =
{
.func = ftrace_stub,
@@ -78,14 +90,16 @@ static int notrace __register_ftrace_function(struct ftrace_ops *ops)
smp_wmb();
ftrace_list = ops;
- /*
- * For one func, simply call it directly.
- * For more than one func, call the chain.
- */
- if (ops->next == &ftrace_list_end)
- ftrace_trace_function = ops->func;
- else
- ftrace_trace_function = ftrace_list_func;
+ if (ftrace_enabled) {
+ /*
+ * For one func, simply call it directly.
+ * For more than one func, call the chain.
+ */
+ if (ops->next == &ftrace_list_end)
+ ftrace_trace_function = ops->func;
+ else
+ ftrace_trace_function = ftrace_list_func;
+ }
spin_unlock(&ftrace_lock);
@@ -120,10 +134,12 @@ static int notrace __unregister_ftrace_function(struct ftrace_ops *ops)
*p = (*p)->next;
- /* If we only have one func left, then call that directly */
- if (ftrace_list == &ftrace_list_end ||
- ftrace_list->next == &ftrace_list_end)
- ftrace_trace_function = ftrace_list->func;
+ if (ftrace_enabled) {
+ /* If we only have one func left, then call that directly */
+ if (ftrace_list == &ftrace_list_end ||
+ ftrace_list->next == &ftrace_list_end)
+ ftrace_trace_function = ftrace_list->func;
+ }
out:
spin_unlock(&ftrace_lock);
@@ -263,7 +279,8 @@ static void notrace ftrace_startup(void)
goto out;
__unregister_ftrace_function(&ftrace_shutdown_ops);
- ftrace_run_startup_code();
+ if (ftrace_enabled)
+ ftrace_run_startup_code();
out:
mutex_unlock(&ftraced_lock);
}
@@ -275,13 +292,32 @@ static void notrace ftrace_shutdown(void)
if (ftraced_suspend)
goto out;
- ftrace_run_shutdown_code();
+ if (ftrace_enabled)
+ ftrace_run_shutdown_code();
__register_ftrace_function(&ftrace_shutdown_ops);
out:
mutex_unlock(&ftraced_lock);
}
+static void notrace ftrace_startup_sysctl(void)
+{
+ mutex_lock(&ftraced_lock);
+ /* ftraced_suspend is true if we want ftrace running */
+ if (ftraced_suspend)
+ ftrace_run_startup_code();
+ mutex_unlock(&ftraced_lock);
+}
+
+static void notrace ftrace_shutdown_sysctl(void)
+{
+ mutex_lock(&ftraced_lock);
+ /* ftraced_suspend is true if ftrace is running */
+ if (ftraced_suspend)
+ ftrace_run_shutdown_code();
+ mutex_unlock(&ftraced_lock);
+}
+
static cycle_t ftrace_update_time;
static unsigned long ftrace_update_cnt;
unsigned long ftrace_update_tot_cnt;
@@ -341,8 +377,9 @@ static int notrace ftraced(void *ignore)
/* check once a second */
schedule_timeout(HZ);
+ mutex_lock(&ftrace_sysctl_lock);
mutex_lock(&ftraced_lock);
- if (ftraced_trigger && !ftraced_suspend) {
+ if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) {
ftrace_record_suspend++;
ftrace_update_code();
usecs = nsecs_to_usecs(ftrace_update_time);
@@ -360,6 +397,7 @@ static int notrace ftraced(void *ignore)
ftrace_record_suspend--;
}
mutex_unlock(&ftraced_lock);
+ mutex_unlock(&ftrace_sysctl_lock);
ftrace_shutdown_replenish();
@@ -389,8 +427,10 @@ static int __init notrace ftrace_shutdown_init(void)
core_initcall(ftrace_shutdown_init);
#else
-# define ftrace_startup() do { } while (0)
-# define ftrace_shutdown() do { } while (0)
+# define ftrace_startup() do { } while (0)
+# define ftrace_shutdown() do { } while (0)
+# define ftrace_startup_sysctl() do { } while (0)
+# define ftrace_shutdown_sysctl() do { } while (0)
#endif /* CONFIG_DYNAMIC_FTRACE */
/**
@@ -406,9 +446,15 @@ core_initcall(ftrace_shutdown_init);
*/
int register_ftrace_function(struct ftrace_ops *ops)
{
+ int ret;
+
+ mutex_lock(&ftrace_sysctl_lock);
ftrace_startup();
- return __register_ftrace_function(ops);
+ ret = __register_ftrace_function(ops);
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ return ret;
}
/**
@@ -421,10 +467,53 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
{
int ret;
+ mutex_lock(&ftrace_sysctl_lock);
ret = __unregister_ftrace_function(ops);
if (ftrace_list == &ftrace_list_end)
ftrace_shutdown();
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ return ret;
+}
+
+notrace int
+ftrace_enable_sysctl(struct ctl_table *table, int write,
+ struct file *filp, void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret;
+
+ mutex_lock(&ftrace_sysctl_lock);
+
+ ret = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+
+ if (ret || !write || (last_ftrace_enabled == ftrace_enabled))
+ goto out;
+
+ last_ftrace_enabled = ftrace_enabled;
+
+ if (ftrace_enabled) {
+
+ ftrace_startup_sysctl();
+
+ /* we are starting ftrace again */
+ if (ftrace_list != &ftrace_list_end) {
+ if (ftrace_list->next == &ftrace_list_end)
+ ftrace_trace_function = ftrace_list->func;
+ else
+ ftrace_trace_function = ftrace_list_func;
+ }
+
+ } else {
+ /* stopping ftrace calls (just send to ftrace_stub) */
+ ftrace_trace_function = ftrace_stub;
+
+ ftrace_shutdown_sysctl();
+ }
+
+ out:
+ mutex_unlock(&ftrace_sysctl_lock);
return ret;
}