summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/ftrace.h4
-rw-r--r--kernel/trace/ftrace.c51
2 files changed, 55 insertions, 0 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index a42390c1d6e1..2c1670c65236 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -54,6 +54,8 @@ struct dyn_ftrace {
unsigned long flags;
};
+int ftrace_force_update(void);
+
/* defined in arch */
extern int ftrace_ip_converted(unsigned long ip);
extern unsigned char *ftrace_nop_replace(void);
@@ -66,6 +68,8 @@ extern int ftrace_update_ftrace_func(ftrace_func_t func);
extern void ftrace_caller(void);
extern void ftrace_call(void);
extern void mcount_call(void);
+#else
+# define ftrace_force_update() do { } while (0)
#endif
static inline void tracer_disable(void)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 97d5cb7b7e75..4facf5ceeb86 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -146,6 +146,10 @@ static int notrace __unregister_ftrace_function(struct ftrace_ops *ops)
#ifdef CONFIG_DYNAMIC_FTRACE
+static struct task_struct *ftraced_task;
+static DECLARE_WAIT_QUEUE_HEAD(ftraced_waiters);
+static unsigned long ftraced_iteration_counter;
+
enum {
FTRACE_ENABLE_CALLS = (1 << 0),
FTRACE_DISABLE_CALLS = (1 << 1),
@@ -590,9 +594,12 @@ static int notrace ftraced(void *ignore)
ftraced_trigger = 0;
ftrace_record_suspend--;
}
+ ftraced_iteration_counter++;
mutex_unlock(&ftraced_lock);
mutex_unlock(&ftrace_sysctl_lock);
+ wake_up_interruptible(&ftraced_waiters);
+
ftrace_shutdown_replenish();
set_current_state(TASK_INTERRUPTIBLE);
@@ -1050,6 +1057,49 @@ static struct file_operations ftrace_filter_fops = {
.release = ftrace_filter_release,
};
+/**
+ * ftrace_force_update - force an update to all recording ftrace functions
+ *
+ * The ftrace dynamic update daemon only wakes up once a second.
+ * There may be cases where an update needs to be done immediately
+ * for tests or internal kernel tracing to begin. This function
+ * wakes the daemon to do an update and will not return until the
+ * update is complete.
+ */
+int ftrace_force_update(void)
+{
+ unsigned long last_counter;
+ DECLARE_WAITQUEUE(wait, current);
+ int ret = 0;
+
+ if (!ftraced_task)
+ return -ENODEV;
+
+ mutex_lock(&ftraced_lock);
+ last_counter = ftraced_iteration_counter;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&ftraced_waiters, &wait);
+
+ do {
+ mutex_unlock(&ftraced_lock);
+ wake_up_process(ftraced_task);
+ schedule();
+ mutex_lock(&ftraced_lock);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ } while (last_counter == ftraced_iteration_counter);
+
+ mutex_unlock(&ftraced_lock);
+ remove_wait_queue(&ftraced_waiters, &wait);
+ set_current_state(TASK_RUNNING);
+
+ return ret;
+}
+
static __init int ftrace_init_debugfs(void)
{
struct dentry *d_tracer;
@@ -1095,6 +1145,7 @@ static int __init notrace ftrace_dynamic_init(void)
return -1;
last_ftrace_enabled = ftrace_enabled = 1;
+ ftraced_task = p;
return 0;
}