Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzi...
[safe/jmp/linux-2.6] / kernel / trace / ftrace.c
index 57592a9..9a236ff 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/clocksource.h>
 #include <linux/kallsyms.h>
 #include <linux/seq_file.h>
+#include <linux/suspend.h>
 #include <linux/debugfs.h>
 #include <linux/hardirq.h>
 #include <linux/kthread.h>
@@ -48,7 +49,8 @@ int ftrace_enabled __read_mostly;
 static int last_ftrace_enabled;
 
 /* set when tracing only a pid */
-int ftrace_pid_trace = -1;
+struct pid *ftrace_pid_trace;
+static struct pid * const ftrace_swapper_pid = &init_struct_pid;
 
 /* Quick disabling of function tracer. */
 int function_trace_stop;
@@ -153,7 +155,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
                else
                        func = ftrace_list_func;
 
-               if (ftrace_pid_trace >= 0) {
+               if (ftrace_pid_trace) {
                        set_ftrace_pid_function(func);
                        func = ftrace_pid_func;
                }
@@ -209,7 +211,7 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
                if (ftrace_list->next == &ftrace_list_end) {
                        ftrace_func_t func = ftrace_list->func;
 
-                       if (ftrace_pid_trace >= 0) {
+                       if (ftrace_pid_trace) {
                                set_ftrace_pid_function(func);
                                func = ftrace_pid_func;
                        }
@@ -239,7 +241,7 @@ static void ftrace_update_pid_func(void)
 
        func = ftrace_trace_function;
 
-       if (ftrace_pid_trace >= 0) {
+       if (ftrace_pid_trace) {
                set_ftrace_pid_function(func);
                func = ftrace_pid_func;
        } else {
@@ -1046,6 +1048,13 @@ ftrace_match(unsigned char *buff, int len, int enable)
        int type = MATCH_FULL;
        unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
        unsigned i, match = 0, search_len = 0;
+       int not = 0;
+
+       if (buff[0] == '!') {
+               not = 1;
+               buff++;
+               len--;
+       }
 
        for (i = 0; i < len; i++) {
                if (buff[i] == '*') {
@@ -1099,8 +1108,12 @@ ftrace_match(unsigned char *buff, int len, int enable)
                                        matched = 1;
                                break;
                        }
-                       if (matched)
-                               rec->flags |= flag;
+                       if (matched) {
+                               if (not)
+                                       rec->flags &= ~flag;
+                               else
+                                       rec->flags |= flag;
+                       }
                }
                pg = pg->next;
        }
@@ -1424,7 +1437,7 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer)
        struct dyn_ftrace *rec;
        struct ftrace_page *pg;
        int found = 0;
-       int i;
+       int i, j;
 
        if (ftrace_disabled)
                return -ENODEV;
@@ -1442,7 +1455,13 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer)
                        kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
                        if (strcmp(str, buffer) == 0) {
                                found = 1;
-                               array[idx] = rec->ip;
+                               for (j = 0; j < idx; j++)
+                                       if (array[j] == rec->ip) {
+                                               found = 0;
+                                               break;
+                                       }
+                               if (found)
+                                       array[idx] = rec->ip;
                                break;
                        }
                }
@@ -1678,18 +1697,89 @@ ftrace_pid_read(struct file *file, char __user *ubuf,
        char buf[64];
        int r;
 
-       if (ftrace_pid_trace >= 0)
-               r = sprintf(buf, "%u\n", ftrace_pid_trace);
+       if (ftrace_pid_trace == ftrace_swapper_pid)
+               r = sprintf(buf, "swapper tasks\n");
+       else if (ftrace_pid_trace)
+               r = sprintf(buf, "%u\n", pid_nr(ftrace_pid_trace));
        else
                r = sprintf(buf, "no pid\n");
 
        return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
 }
 
+static void clear_ftrace_swapper(void)
+{
+       struct task_struct *p;
+       int cpu;
+
+       get_online_cpus();
+       for_each_online_cpu(cpu) {
+               p = idle_task(cpu);
+               clear_tsk_trace_trace(p);
+       }
+       put_online_cpus();
+}
+
+static void set_ftrace_swapper(void)
+{
+       struct task_struct *p;
+       int cpu;
+
+       get_online_cpus();
+       for_each_online_cpu(cpu) {
+               p = idle_task(cpu);
+               set_tsk_trace_trace(p);
+       }
+       put_online_cpus();
+}
+
+static void clear_ftrace_pid(struct pid *pid)
+{
+       struct task_struct *p;
+
+       rcu_read_lock();
+       do_each_pid_task(pid, PIDTYPE_PID, p) {
+               clear_tsk_trace_trace(p);
+       } while_each_pid_task(pid, PIDTYPE_PID, p);
+       rcu_read_unlock();
+
+       put_pid(pid);
+}
+
+static void set_ftrace_pid(struct pid *pid)
+{
+       struct task_struct *p;
+
+       rcu_read_lock();
+       do_each_pid_task(pid, PIDTYPE_PID, p) {
+               set_tsk_trace_trace(p);
+       } while_each_pid_task(pid, PIDTYPE_PID, p);
+       rcu_read_unlock();
+}
+
+static void clear_ftrace_pid_task(struct pid **pid)
+{
+       if (*pid == ftrace_swapper_pid)
+               clear_ftrace_swapper();
+       else
+               clear_ftrace_pid(*pid);
+
+       *pid = NULL;
+}
+
+static void set_ftrace_pid_task(struct pid *pid)
+{
+       if (pid == ftrace_swapper_pid)
+               set_ftrace_swapper();
+       else
+               set_ftrace_pid(pid);
+}
+
 static ssize_t
 ftrace_pid_write(struct file *filp, const char __user *ubuf,
                   size_t cnt, loff_t *ppos)
 {
+       struct pid *pid;
        char buf[64];
        long val;
        int ret;
@@ -1707,40 +1797,37 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
                return ret;
 
        mutex_lock(&ftrace_start_lock);
-       if (ret < 0) {
+       if (val < 0) {
                /* disable pid tracing */
-               if (ftrace_pid_trace < 0)
+               if (!ftrace_pid_trace)
                        goto out;
-               ftrace_pid_trace = -1;
+
+               clear_ftrace_pid_task(&ftrace_pid_trace);
 
        } else {
-               struct task_struct *p;
-               int found = 0;
+               /* swapper task is special */
+               if (!val) {
+                       pid = ftrace_swapper_pid;
+                       if (pid == ftrace_pid_trace)
+                               goto out;
+               } else {
+                       pid = find_get_pid(val);
 
-               if (ftrace_pid_trace == val)
+                       if (pid == ftrace_pid_trace) {
+                               put_pid(pid);
+                               goto out;
+                       }
+               }
+
+               if (ftrace_pid_trace)
+                       clear_ftrace_pid_task(&ftrace_pid_trace);
+
+               if (!pid)
                        goto out;
 
-               /*
-                * Find the task that matches this pid.
-                * TODO: use pid namespaces instead.
-                */
-               rcu_read_lock();
-               for_each_process(p) {
-                       if (p->pid == val) {
-                               found = 1;
-                               set_tsk_trace_trace(p);
-                       } else if (test_tsk_trace_trace(p))
-                               clear_tsk_trace_trace(p);
-               }
-               rcu_read_unlock();
+               ftrace_pid_trace = pid;
 
-               if (found)
-                       ftrace_pid_trace = val;
-               else {
-                       if (ftrace_pid_trace < 0)
-                               goto out;
-                       ftrace_pid_trace = -1;
-               }
+               set_ftrace_pid_task(ftrace_pid_trace);
        }
 
        /* update the function call */
@@ -1884,6 +1971,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 
 static atomic_t ftrace_graph_active;
+static struct notifier_block ftrace_suspend_notifier;
 
 int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
 {
@@ -1928,6 +2016,7 @@ static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list)
                        /* Make sure IRQs see the -1 first: */
                        barrier();
                        t->ret_stack = ret_stack_list[start++];
+                       atomic_set(&t->tracing_graph_pause, 0);
                        atomic_set(&t->trace_overrun, 0);
                }
        } while_each_thread(g, t);
@@ -1961,6 +2050,27 @@ static int start_graph_tracing(void)
        return ret;
 }
 
+/*
+ * Hibernation protection.
+ * The state of the current task is too much unstable during
+ * suspend/restore to disk. We want to protect against that.
+ */
+static int
+ftrace_suspend_notifier_call(struct notifier_block *bl, unsigned long state,
+                                                       void *unused)
+{
+       switch (state) {
+       case PM_HIBERNATION_PREPARE:
+               pause_graph_tracing();
+               break;
+
+       case PM_POST_HIBERNATION:
+               unpause_graph_tracing();
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
 int register_ftrace_graph(trace_func_graph_ret_t retfunc,
                        trace_func_graph_ent_t entryfunc)
 {
@@ -1968,6 +2078,9 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,
 
        mutex_lock(&ftrace_sysctl_lock);
 
+       ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call;
+       register_pm_notifier(&ftrace_suspend_notifier);
+
        atomic_inc(&ftrace_graph_active);
        ret = start_graph_tracing();
        if (ret) {
@@ -1993,6 +2106,7 @@ void unregister_ftrace_graph(void)
        ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
        ftrace_graph_entry = ftrace_graph_entry_stub;
        ftrace_shutdown(FTRACE_STOP_FUNC_RET);
+       unregister_pm_notifier(&ftrace_suspend_notifier);
 
        mutex_unlock(&ftrace_sysctl_lock);
 }
@@ -2007,6 +2121,7 @@ void ftrace_graph_init_task(struct task_struct *t)
                if (!t->ret_stack)
                        return;
                t->curr_ret_stack = -1;
+               atomic_set(&t->tracing_graph_pause, 0);
                atomic_set(&t->trace_overrun, 0);
        } else
                t->ret_stack = NULL;