aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/trace/boot.h31
-rw-r--r--init/main.c32
-rw-r--r--kernel/trace/trace.h17
-rw-r--r--kernel/trace/trace_boot.c123
4 files changed, 144 insertions, 59 deletions
diff --git a/include/trace/boot.h b/include/trace/boot.h
index 4cbe64e46cdc..6b54537eab02 100644
--- a/include/trace/boot.h
+++ b/include/trace/boot.h
@@ -2,22 +2,30 @@
2#define _LINUX_TRACE_BOOT_H 2#define _LINUX_TRACE_BOOT_H
3 3
4/* 4/*
5 * Structure which defines the trace of an initcall. 5 * Structure which defines the trace of an initcall
6 * while it is called.
6 * You don't have to fill the func field since it is 7 * You don't have to fill the func field since it is
7 * only used internally by the tracer. 8 * only used internally by the tracer.
8 */ 9 */
9struct boot_trace { 10struct boot_trace_call {
10 pid_t caller; 11 pid_t caller;
11 char func[KSYM_NAME_LEN]; 12 char func[KSYM_NAME_LEN];
12 int result; 13};
13 unsigned long long duration; /* usecs */ 14
14 ktime_t calltime; 15/*
15 ktime_t rettime; 16 * Structure which defines the trace of an initcall
17 * while it returns.
18 */
19struct boot_trace_ret {
20 char func[KSYM_NAME_LEN];
21 int result;
22 unsigned long long duration; /* nsecs */
16}; 23};
17 24
18#ifdef CONFIG_BOOT_TRACER 25#ifdef CONFIG_BOOT_TRACER
19/* Append the trace on the ring-buffer */ 26/* Append the traces on the ring-buffer */
20extern void trace_boot(struct boot_trace *it, initcall_t fn); 27extern void trace_boot_call(struct boot_trace_call *bt, initcall_t fn);
28extern void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn);
21 29
22/* Tells the tracer that smp_pre_initcall is finished. 30/* Tells the tracer that smp_pre_initcall is finished.
23 * So we can start the tracing 31 * So we can start the tracing
@@ -34,7 +42,12 @@ extern void enable_boot_trace(void);
34 */ 42 */
35extern void disable_boot_trace(void); 43extern void disable_boot_trace(void);
36#else 44#else
37static inline void trace_boot(struct boot_trace *it, initcall_t fn) { } 45static inline
46void trace_boot_call(struct boot_trace_call *bt, initcall_t fn) { }
47
48static inline
49void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn) { }
50
38static inline void start_boot_trace(void) { } 51static inline void start_boot_trace(void) { }
39static inline void enable_boot_trace(void) { } 52static inline void enable_boot_trace(void) { }
40static inline void disable_boot_trace(void) { } 53static inline void disable_boot_trace(void) { }
diff --git a/init/main.c b/init/main.c
index 16ca1ee071c4..e810196bf2f2 100644
--- a/init/main.c
+++ b/init/main.c
@@ -704,33 +704,35 @@ core_param(initcall_debug, initcall_debug, bool, 0644);
704int do_one_initcall(initcall_t fn) 704int do_one_initcall(initcall_t fn)
705{ 705{
706 int count = preempt_count(); 706 int count = preempt_count();
707 ktime_t delta; 707 ktime_t calltime, delta, rettime;
708 char msgbuf[64]; 708 char msgbuf[64];
709 struct boot_trace it; 709 struct boot_trace_call call;
710 struct boot_trace_ret ret;
710 711
711 if (initcall_debug) { 712 if (initcall_debug) {
712 it.caller = task_pid_nr(current); 713 call.caller = task_pid_nr(current);
713 printk("calling %pF @ %i\n", fn, it.caller); 714 printk("calling %pF @ %i\n", fn, call.caller);
714 it.calltime = ktime_get(); 715 calltime = ktime_get();
716 trace_boot_call(&call, fn);
715 enable_boot_trace(); 717 enable_boot_trace();
716 } 718 }
717 719
718 it.result = fn(); 720 ret.result = fn();
719 721
720 if (initcall_debug) { 722 if (initcall_debug) {
721 it.rettime = ktime_get();
722 delta = ktime_sub(it.rettime, it.calltime);
723 it.duration = (unsigned long long) delta.tv64 >> 10;
724 printk("initcall %pF returned %d after %Ld usecs\n", fn,
725 it.result, it.duration);
726 trace_boot(&it, fn);
727 disable_boot_trace(); 723 disable_boot_trace();
724 rettime = ktime_get();
725 delta = ktime_sub(rettime, calltime);
726 ret.duration = (unsigned long long) delta.tv64 >> 10;
727 trace_boot_ret(&ret, fn);
728 printk("initcall %pF returned %d after %Ld usecs\n", fn,
729 ret.result, ret.duration);
728 } 730 }
729 731
730 msgbuf[0] = 0; 732 msgbuf[0] = 0;
731 733
732 if (it.result && it.result != -ENODEV && initcall_debug) 734 if (ret.result && ret.result != -ENODEV && initcall_debug)
733 sprintf(msgbuf, "error code %d ", it.result); 735 sprintf(msgbuf, "error code %d ", ret.result);
734 736
735 if (preempt_count() != count) { 737 if (preempt_count() != count) {
736 strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf)); 738 strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf));
@@ -744,7 +746,7 @@ int do_one_initcall(initcall_t fn)
744 printk("initcall %pF returned with %s\n", fn, msgbuf); 746 printk("initcall %pF returned with %s\n", fn, msgbuf);
745 } 747 }
746 748
747 return it.result; 749 return ret.result;
748} 750}
749 751
750 752
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index f69a5199596b..b5f91f198fd4 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -22,7 +22,8 @@ enum trace_type {
22 TRACE_SPECIAL, 22 TRACE_SPECIAL,
23 TRACE_MMIO_RW, 23 TRACE_MMIO_RW,
24 TRACE_MMIO_MAP, 24 TRACE_MMIO_MAP,
25 TRACE_BOOT, 25 TRACE_BOOT_CALL,
26 TRACE_BOOT_RET,
26 TRACE_FN_RET, 27 TRACE_FN_RET,
27 28
28 __TRACE_LAST_TYPE 29 __TRACE_LAST_TYPE
@@ -123,9 +124,14 @@ struct trace_mmiotrace_map {
123 struct mmiotrace_map map; 124 struct mmiotrace_map map;
124}; 125};
125 126
126struct trace_boot { 127struct trace_boot_call {
127 struct trace_entry ent; 128 struct trace_entry ent;
128 struct boot_trace initcall; 129 struct boot_trace_call boot_call;
130};
131
132struct trace_boot_ret {
133 struct trace_entry ent;
134 struct boot_trace_ret boot_ret;
129}; 135};
130 136
131/* 137/*
@@ -228,8 +234,9 @@ extern void __ftrace_bad_type(void);
228 TRACE_MMIO_RW); \ 234 TRACE_MMIO_RW); \
229 IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \ 235 IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \
230 TRACE_MMIO_MAP); \ 236 TRACE_MMIO_MAP); \
231 IF_ASSIGN(var, ent, struct trace_boot, TRACE_BOOT); \ 237 IF_ASSIGN(var, ent, struct trace_boot_call, TRACE_BOOT_CALL);\
232 IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET); \ 238 IF_ASSIGN(var, ent, struct trace_boot_ret, TRACE_BOOT_RET);\
239 IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET);\
233 __ftrace_bad_type(); \ 240 __ftrace_bad_type(); \
234 } while (0) 241 } while (0)
235 242
diff --git a/kernel/trace/trace_boot.c b/kernel/trace/trace_boot.c
index 8f71915e8bb4..cb333b7fd113 100644
--- a/kernel/trace/trace_boot.c
+++ b/kernel/trace/trace_boot.c
@@ -58,35 +58,71 @@ static void boot_trace_init(struct trace_array *tr)
58 tracing_sched_switch_assign_trace(tr); 58 tracing_sched_switch_assign_trace(tr);
59} 59}
60 60
61static enum print_line_t initcall_print_line(struct trace_iterator *iter) 61static enum print_line_t
62initcall_call_print_line(struct trace_iterator *iter)
62{ 63{
64 struct trace_entry *entry = iter->ent;
65 struct trace_seq *s = &iter->seq;
66 struct trace_boot_call *field;
67 struct boot_trace_call *call;
68 u64 ts;
69 unsigned long nsec_rem;
63 int ret; 70 int ret;
71
72 trace_assign_type(field, entry);
73 call = &field->boot_call;
74 ts = iter->ts;
75 nsec_rem = do_div(ts, 1000000000);
76
77 ret = trace_seq_printf(s, "[%5ld.%09ld] calling %s @ %i\n",
78 (unsigned long)ts, nsec_rem, call->func, call->caller);
79
80 if (!ret)
81 return TRACE_TYPE_PARTIAL_LINE;
82 else
83 return TRACE_TYPE_HANDLED;
84}
85
86static enum print_line_t
87initcall_ret_print_line(struct trace_iterator *iter)
88{
64 struct trace_entry *entry = iter->ent; 89 struct trace_entry *entry = iter->ent;
65 struct trace_boot *field = (struct trace_boot *)entry;
66 struct boot_trace *it = &field->initcall;
67 struct trace_seq *s = &iter->seq; 90 struct trace_seq *s = &iter->seq;
68 struct timespec calltime = ktime_to_timespec(it->calltime); 91 struct trace_boot_ret *field;
69 struct timespec rettime = ktime_to_timespec(it->rettime); 92 struct boot_trace_ret *init_ret;
70 93 u64 ts;
71 if (entry->type == TRACE_BOOT) { 94 unsigned long nsec_rem;
72 ret = trace_seq_printf(s, "[%5ld.%09ld] calling %s @ %i\n", 95 int ret;
73 calltime.tv_sec, 96
74 calltime.tv_nsec, 97 trace_assign_type(field, entry);
75 it->func, it->caller); 98 init_ret = &field->boot_ret;
76 if (!ret) 99 ts = iter->ts;
77 return TRACE_TYPE_PARTIAL_LINE; 100 nsec_rem = do_div(ts, 1000000000);
78 101
79 ret = trace_seq_printf(s, "[%5ld.%09ld] initcall %s " 102 ret = trace_seq_printf(s, "[%5ld.%09ld] initcall %s "
80 "returned %d after %lld msecs\n", 103 "returned %d after %llu msecs\n",
81 rettime.tv_sec, 104 (unsigned long) ts,
82 rettime.tv_nsec, 105 nsec_rem,
83 it->func, it->result, it->duration); 106 init_ret->func, init_ret->result, init_ret->duration);
84 107
85 if (!ret) 108 if (!ret)
86 return TRACE_TYPE_PARTIAL_LINE; 109 return TRACE_TYPE_PARTIAL_LINE;
110 else
87 return TRACE_TYPE_HANDLED; 111 return TRACE_TYPE_HANDLED;
112}
113
114static enum print_line_t initcall_print_line(struct trace_iterator *iter)
115{
116 struct trace_entry *entry = iter->ent;
117
118 switch (entry->type) {
119 case TRACE_BOOT_CALL:
120 return initcall_call_print_line(iter);
121 case TRACE_BOOT_RET:
122 return initcall_ret_print_line(iter);
123 default:
124 return TRACE_TYPE_UNHANDLED;
88 } 125 }
89 return TRACE_TYPE_UNHANDLED;
90} 126}
91 127
92struct tracer boot_tracer __read_mostly = 128struct tracer boot_tracer __read_mostly =
@@ -97,11 +133,10 @@ struct tracer boot_tracer __read_mostly =
97 .print_line = initcall_print_line, 133 .print_line = initcall_print_line,
98}; 134};
99 135
100void trace_boot(struct boot_trace *it, initcall_t fn) 136void trace_boot_call(struct boot_trace_call *bt, initcall_t fn)
101{ 137{
102 struct ring_buffer_event *event; 138 struct ring_buffer_event *event;
103 struct trace_boot *entry; 139 struct trace_boot_call *entry;
104 struct trace_array_cpu *data;
105 unsigned long irq_flags; 140 unsigned long irq_flags;
106 struct trace_array *tr = boot_trace; 141 struct trace_array *tr = boot_trace;
107 142
@@ -111,9 +146,37 @@ void trace_boot(struct boot_trace *it, initcall_t fn)
111 /* Get its name now since this function could 146 /* Get its name now since this function could
112 * disappear because it is in the .init section. 147 * disappear because it is in the .init section.
113 */ 148 */
114 sprint_symbol(it->func, (unsigned long)fn); 149 sprint_symbol(bt->func, (unsigned long)fn);
150 preempt_disable();
151
152 event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
153 &irq_flags);
154 if (!event)
155 goto out;
156 entry = ring_buffer_event_data(event);
157 tracing_generic_entry_update(&entry->ent, 0, 0);
158 entry->ent.type = TRACE_BOOT_CALL;
159 entry->boot_call = *bt;
160 ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
161
162 trace_wake_up();
163
164 out:
165 preempt_enable();
166}
167
168void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn)
169{
170 struct ring_buffer_event *event;
171 struct trace_boot_ret *entry;
172 unsigned long irq_flags;
173 struct trace_array *tr = boot_trace;
174
175 if (!pre_initcalls_finished)
176 return;
177
178 sprint_symbol(bt->func, (unsigned long)fn);
115 preempt_disable(); 179 preempt_disable();
116 data = tr->data[smp_processor_id()];
117 180
118 event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), 181 event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
119 &irq_flags); 182 &irq_flags);
@@ -121,8 +184,8 @@ void trace_boot(struct boot_trace *it, initcall_t fn)
121 goto out; 184 goto out;
122 entry = ring_buffer_event_data(event); 185 entry = ring_buffer_event_data(event);
123 tracing_generic_entry_update(&entry->ent, 0, 0); 186 tracing_generic_entry_update(&entry->ent, 0, 0);
124 entry->ent.type = TRACE_BOOT; 187 entry->ent.type = TRACE_BOOT_RET;
125 entry->initcall = *it; 188 entry->boot_ret = *bt;
126 ring_buffer_unlock_commit(tr->buffer, event, irq_flags); 189 ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
127 190
128 trace_wake_up(); 191 trace_wake_up();