Merge commit 'v2.6.30-rc6' into tracing/core
[linux-2.6] / kernel / trace / trace_power.c
1 /*
2  * ring buffer based C-state tracer
3  *
4  * Arjan van de Ven <arjan@linux.intel.com>
5  * Copyright (C) 2008 Intel Corporation
6  *
7  * Much is borrowed from trace_boot.c which is
8  * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
9  *
10  */
11
12 #include <linux/init.h>
13 #include <linux/debugfs.h>
14 #include <trace/power.h>
15 #include <linux/kallsyms.h>
16 #include <linux/module.h>
17
18 #include "trace.h"
19 #include "trace_output.h"
20
21 static struct trace_array *power_trace;
22 static int __read_mostly trace_power_enabled;
23
24 static void probe_power_start(struct power_trace *it, unsigned int type,
25                                 unsigned int level)
26 {
27         if (!trace_power_enabled)
28                 return;
29
30         memset(it, 0, sizeof(struct power_trace));
31         it->state = level;
32         it->type = type;
33         it->stamp = ktime_get();
34 }
35
36
37 static void probe_power_end(struct power_trace *it)
38 {
39         struct ftrace_event_call *call = &event_power;
40         struct ring_buffer_event *event;
41         struct trace_power *entry;
42         struct trace_array_cpu *data;
43         struct trace_array *tr = power_trace;
44
45         if (!trace_power_enabled)
46                 return;
47
48         preempt_disable();
49         it->end = ktime_get();
50         data = tr->data[smp_processor_id()];
51
52         event = trace_buffer_lock_reserve(tr, TRACE_POWER,
53                                           sizeof(*entry), 0, 0);
54         if (!event)
55                 goto out;
56         entry   = ring_buffer_event_data(event);
57         entry->state_data = *it;
58         if (!filter_check_discard(call, entry, tr->buffer, event))
59                 trace_buffer_unlock_commit(tr, event, 0, 0);
60  out:
61         preempt_enable();
62 }
63
64 static void probe_power_mark(struct power_trace *it, unsigned int type,
65                                 unsigned int level)
66 {
67         struct ftrace_event_call *call = &event_power;
68         struct ring_buffer_event *event;
69         struct trace_power *entry;
70         struct trace_array_cpu *data;
71         struct trace_array *tr = power_trace;
72
73         if (!trace_power_enabled)
74                 return;
75
76         memset(it, 0, sizeof(struct power_trace));
77         it->state = level;
78         it->type = type;
79         it->stamp = ktime_get();
80         preempt_disable();
81         it->end = it->stamp;
82         data = tr->data[smp_processor_id()];
83
84         event = trace_buffer_lock_reserve(tr, TRACE_POWER,
85                                           sizeof(*entry), 0, 0);
86         if (!event)
87                 goto out;
88         entry   = ring_buffer_event_data(event);
89         entry->state_data = *it;
90         if (!filter_check_discard(call, entry, tr->buffer, event))
91                 trace_buffer_unlock_commit(tr, event, 0, 0);
92  out:
93         preempt_enable();
94 }
95
96 static int tracing_power_register(void)
97 {
98         int ret;
99
100         ret = register_trace_power_start(probe_power_start);
101         if (ret) {
102                 pr_info("power trace: Couldn't activate tracepoint"
103                         " probe to trace_power_start\n");
104                 return ret;
105         }
106         ret = register_trace_power_end(probe_power_end);
107         if (ret) {
108                 pr_info("power trace: Couldn't activate tracepoint"
109                         " probe to trace_power_end\n");
110                 goto fail_start;
111         }
112         ret = register_trace_power_mark(probe_power_mark);
113         if (ret) {
114                 pr_info("power trace: Couldn't activate tracepoint"
115                         " probe to trace_power_mark\n");
116                 goto fail_end;
117         }
118         return ret;
119 fail_end:
120         unregister_trace_power_end(probe_power_end);
121 fail_start:
122         unregister_trace_power_start(probe_power_start);
123         return ret;
124 }
125
126 static void start_power_trace(struct trace_array *tr)
127 {
128         trace_power_enabled = 1;
129 }
130
131 static void stop_power_trace(struct trace_array *tr)
132 {
133         trace_power_enabled = 0;
134 }
135
136 static void power_trace_reset(struct trace_array *tr)
137 {
138         trace_power_enabled = 0;
139         unregister_trace_power_start(probe_power_start);
140         unregister_trace_power_end(probe_power_end);
141         unregister_trace_power_mark(probe_power_mark);
142 }
143
144
145 static int power_trace_init(struct trace_array *tr)
146 {
147         int cpu;
148         power_trace = tr;
149
150         trace_power_enabled = 1;
151         tracing_power_register();
152
153         for_each_cpu(cpu, cpu_possible_mask)
154                 tracing_reset(tr, cpu);
155         return 0;
156 }
157
158 static enum print_line_t power_print_line(struct trace_iterator *iter)
159 {
160         int ret = 0;
161         struct trace_entry *entry = iter->ent;
162         struct trace_power *field ;
163         struct power_trace *it;
164         struct trace_seq *s = &iter->seq;
165         struct timespec stamp;
166         struct timespec duration;
167
168         trace_assign_type(field, entry);
169         it = &field->state_data;
170         stamp = ktime_to_timespec(it->stamp);
171         duration = ktime_to_timespec(ktime_sub(it->end, it->stamp));
172
173         if (entry->type == TRACE_POWER) {
174                 if (it->type == POWER_CSTATE)
175                         ret = trace_seq_printf(s, "[%5ld.%09ld] CSTATE: Going to C%i on cpu %i for %ld.%09ld\n",
176                                           stamp.tv_sec,
177                                           stamp.tv_nsec,
178                                           it->state, iter->cpu,
179                                           duration.tv_sec,
180                                           duration.tv_nsec);
181                 if (it->type == POWER_PSTATE)
182                         ret = trace_seq_printf(s, "[%5ld.%09ld] PSTATE: Going to P%i on cpu %i\n",
183                                           stamp.tv_sec,
184                                           stamp.tv_nsec,
185                                           it->state, iter->cpu);
186                 if (!ret)
187                         return TRACE_TYPE_PARTIAL_LINE;
188                 return TRACE_TYPE_HANDLED;
189         }
190         return TRACE_TYPE_UNHANDLED;
191 }
192
193 static void power_print_header(struct seq_file *s)
194 {
195         seq_puts(s, "#   TIMESTAMP      STATE  EVENT\n");
196         seq_puts(s, "#       |            |      |\n");
197 }
198
199 static struct tracer power_tracer __read_mostly =
200 {
201         .name           = "power",
202         .init           = power_trace_init,
203         .start          = start_power_trace,
204         .stop           = stop_power_trace,
205         .reset          = power_trace_reset,
206         .print_line     = power_print_line,
207         .print_header   = power_print_header,
208 };
209
210 static int init_power_trace(void)
211 {
212         return register_tracer(&power_tracer);
213 }
214 device_initcall(init_power_trace);