ftrace: fix boot trace sched startup
[firefly-linux-kernel-4.4.55.git] / kernel / trace / trace_boot.c
index c65ef8ffd6b4ede6cc5f6d5c746162d0607d24e8..662cb9198906e2684b05742327355ff56fda3268 100644 (file)
@@ -8,22 +8,43 @@
 #include <linux/init.h>
 #include <linux/debugfs.h>
 #include <linux/ftrace.h>
+#include <linux/kallsyms.h>
 
 #include "trace.h"
 
 static struct trace_array *boot_trace;
-static int trace_boot_enabled;
+static bool pre_initcalls_finished;
 
-
-/* Should be started after do_pre_smp_initcalls() in init/main.c */
+/* Tells the boot tracer that the pre_smp_initcalls are finished.
+ * So we are ready .
+ * It doesn't enable sched events tracing however.
+ * You have to call enable_boot_trace to do so.
+ */
 void start_boot_trace(void)
 {
-       trace_boot_enabled = 1;
+       pre_initcalls_finished = true;
+}
+
+void enable_boot_trace(void)
+{
+       if (pre_initcalls_finished)
+               tracing_start_cmdline_record();
+}
+
+void disable_boot_trace(void)
+{
+       if (pre_initcalls_finished)
+               tracing_stop_cmdline_record();
 }
 
-void stop_boot_trace(struct trace_array *tr)
+static void reset_boot_trace(struct trace_array *tr)
 {
-       trace_boot_enabled = 0;
+       int cpu;
+
+       tr->time_start = ftrace_now(tr->cpu);
+
+       for_each_online_cpu(cpu)
+               tracing_reset(tr, cpu);
 }
 
 static void boot_trace_init(struct trace_array *tr)
@@ -31,71 +52,90 @@ static void boot_trace_init(struct trace_array *tr)
        int cpu;
        boot_trace = tr;
 
-       trace_boot_enabled = 0;
-
        for_each_cpu_mask(cpu, cpu_possible_map)
-               tracing_reset(tr->data[cpu]);
+               tracing_reset(tr, cpu);
+
+       tracing_cmdline_assign_trace(tr);
 }
 
 static void boot_trace_ctrl_update(struct trace_array *tr)
 {
        if (tr->ctrl)
-               start_boot_trace();
+               enable_boot_trace();
        else
-               stop_boot_trace(tr);
+               disable_boot_trace();
 }
 
-static int initcall_print_line(struct trace_iterator *iter)
+static enum print_line_t initcall_print_line(struct trace_iterator *iter)
 {
-       int ret = 1;
+       int ret;
        struct trace_entry *entry = iter->ent;
-       struct boot_trace *it = &entry->field.initcall;
+       struct trace_boot *field = (struct trace_boot *)entry;
+       struct boot_trace *it = &field->initcall;
        struct trace_seq *s = &iter->seq;
-
-       if (iter->ent->type == TRACE_BOOT)
-               ret = trace_seq_printf(s, "%pF called from %i "
-                                      "returned %d after %lld msecs\n",
-                                      it->func, it->caller, it->result,
-                                      it->duration);
-       if (ret)
-               return 1;
-       return 0;
+       struct timespec calltime = ktime_to_timespec(it->calltime);
+       struct timespec rettime = ktime_to_timespec(it->rettime);
+
+       if (entry->type == TRACE_BOOT) {
+               ret = trace_seq_printf(s, "[%5ld.%09ld] calling  %s @ %i\n",
+                                         calltime.tv_sec,
+                                         calltime.tv_nsec,
+                                         it->func, it->caller);
+               if (!ret)
+                       return TRACE_TYPE_PARTIAL_LINE;
+
+               ret = trace_seq_printf(s, "[%5ld.%09ld] initcall %s "
+                                         "returned %d after %lld msecs\n",
+                                         rettime.tv_sec,
+                                         rettime.tv_nsec,
+                                         it->func, it->result, it->duration);
+
+               if (!ret)
+                       return TRACE_TYPE_PARTIAL_LINE;
+               return TRACE_TYPE_HANDLED;
+       }
+       return TRACE_TYPE_UNHANDLED;
 }
 
 struct tracer boot_tracer __read_mostly =
 {
        .name           = "initcall",
        .init           = boot_trace_init,
-       .reset          = stop_boot_trace,
+       .reset          = reset_boot_trace,
        .ctrl_update    = boot_trace_ctrl_update,
        .print_line     = initcall_print_line,
 };
 
-
-void trace_boot(struct boot_trace *it)
+void trace_boot(struct boot_trace *it, initcall_t fn)
 {
-       struct trace_entry *entry;
+       struct ring_buffer_event *event;
+       struct trace_boot *entry;
        struct trace_array_cpu *data;
        unsigned long irq_flags;
        struct trace_array *tr = boot_trace;
 
-       if (!trace_boot_enabled)
+       if (!pre_initcalls_finished)
                return;
 
+       /* Get its name now since this function could
+        * disappear because it is in the .init section.
+        */
+       sprint_symbol(it->func, (unsigned long)fn);
        preempt_disable();
        data = tr->data[smp_processor_id()];
 
-       raw_local_irq_save(irq_flags);
-       __raw_spin_lock(&data->lock);
-
-       entry = tracing_get_trace_entry(tr, data);
-       tracing_generic_entry_update(entry, 0);
-       entry->type = TRACE_BOOT;
-       entry->field.initcall = *it;
+       event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
+                                        &irq_flags);
+       if (!event)
+               goto out;
+       entry   = ring_buffer_event_data(event);
+       tracing_generic_entry_update(&entry->ent, 0, 0);
+       entry->ent.type = TRACE_BOOT;
+       entry->initcall = *it;
+       ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
 
-       __raw_spin_unlock(&data->lock);
-       raw_local_irq_restore(irq_flags);
        trace_wake_up();
 
+ out:
        preempt_enable();
 }