diff options
-rw-r--r-- | include/trace/boot.h | 31 | ||||
-rw-r--r-- | init/main.c | 32 | ||||
-rw-r--r-- | kernel/trace/trace.h | 17 | ||||
-rw-r--r-- | kernel/trace/trace_boot.c | 123 |
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 | */ |
9 | struct boot_trace { | 10 | struct 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 | */ | ||
19 | struct 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 */ |
20 | extern void trace_boot(struct boot_trace *it, initcall_t fn); | 27 | extern void trace_boot_call(struct boot_trace_call *bt, initcall_t fn); |
28 | extern 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 | */ |
35 | extern void disable_boot_trace(void); | 43 | extern void disable_boot_trace(void); |
36 | #else | 44 | #else |
37 | static inline void trace_boot(struct boot_trace *it, initcall_t fn) { } | 45 | static inline |
46 | void trace_boot_call(struct boot_trace_call *bt, initcall_t fn) { } | ||
47 | |||
48 | static inline | ||
49 | void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn) { } | ||
50 | |||
38 | static inline void start_boot_trace(void) { } | 51 | static inline void start_boot_trace(void) { } |
39 | static inline void enable_boot_trace(void) { } | 52 | static inline void enable_boot_trace(void) { } |
40 | static inline void disable_boot_trace(void) { } | 53 | static 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); | |||
704 | int do_one_initcall(initcall_t fn) | 704 | int 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 | ||
126 | struct trace_boot { | 127 | struct 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 | |||
132 | struct 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 | ||
61 | static enum print_line_t initcall_print_line(struct trace_iterator *iter) | 61 | static enum print_line_t |
62 | initcall_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 | |||
86 | static enum print_line_t | ||
87 | initcall_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 | |||
114 | static 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 | ||
92 | struct tracer boot_tracer __read_mostly = | 128 | struct 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 | ||
100 | void trace_boot(struct boot_trace *it, initcall_t fn) | 136 | void 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 | |||
168 | void 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(); |