Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6.git] / arch / x86 / kernel / ds_selftest.c
1 /*
2  * Debug Store support - selftest
3  *
4  *
5  * Copyright (C) 2009 Intel Corporation.
6  * Markus Metzger <markus.t.metzger@intel.com>, 2009
7  */
8
9 #include "ds_selftest.h"
10
11 #include <linux/kernel.h>
12 #include <linux/string.h>
13 #include <linux/smp.h>
14 #include <linux/cpu.h>
15
16 #include <asm/ds.h>
17
18
19 #define BUFFER_SIZE             521     /* Intentionally chose an odd size. */
20 #define SMALL_BUFFER_SIZE       24      /* A single bts entry. */
21
22 struct ds_selftest_bts_conf {
23         struct bts_tracer *tracer;
24         int error;
25         int (*suspend)(struct bts_tracer *);
26         int (*resume)(struct bts_tracer *);
27 };
28
29 static int ds_selftest_bts_consistency(const struct bts_trace *trace)
30 {
31         int error = 0;
32
33         if (!trace) {
34                 printk(KERN_CONT "failed to access trace...");
35                 /* Bail out. Other tests are pointless. */
36                 return -1;
37         }
38
39         if (!trace->read) {
40                 printk(KERN_CONT "bts read not available...");
41                 error = -1;
42         }
43
44         /* Do some sanity checks on the trace configuration. */
45         if (!trace->ds.n) {
46                 printk(KERN_CONT "empty bts buffer...");
47                 error = -1;
48         }
49         if (!trace->ds.size) {
50                 printk(KERN_CONT "bad bts trace setup...");
51                 error = -1;
52         }
53         if (trace->ds.end !=
54             (char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) {
55                 printk(KERN_CONT "bad bts buffer setup...");
56                 error = -1;
57         }
58         /*
59          * We allow top in [begin; end], since its not clear when the
60          * overflow adjustment happens: after the increment or before the
61          * write.
62          */
63         if ((trace->ds.top < trace->ds.begin) ||
64             (trace->ds.end < trace->ds.top)) {
65                 printk(KERN_CONT "bts top out of bounds...");
66                 error = -1;
67         }
68
69         return error;
70 }
71
72 static int ds_selftest_bts_read(struct bts_tracer *tracer,
73                                 const struct bts_trace *trace,
74                                 const void *from, const void *to)
75 {
76         const unsigned char *at;
77
78         /*
79          * Check a few things which do not belong to this test.
80          * They should be covered by other tests.
81          */
82         if (!trace)
83                 return -1;
84
85         if (!trace->read)
86                 return -1;
87
88         if (to < from)
89                 return -1;
90
91         if (from < trace->ds.begin)
92                 return -1;
93
94         if (trace->ds.end < to)
95                 return -1;
96
97         if (!trace->ds.size)
98                 return -1;
99
100         /* Now to the test itself. */
101         for (at = from; (void *)at < to; at += trace->ds.size) {
102                 struct bts_struct bts;
103                 unsigned long index;
104                 int error;
105
106                 if (((void *)at - trace->ds.begin) % trace->ds.size) {
107                         printk(KERN_CONT
108                                "read from non-integer index...");
109                         return -1;
110                 }
111                 index = ((void *)at - trace->ds.begin) / trace->ds.size;
112
113                 memset(&bts, 0, sizeof(bts));
114                 error = trace->read(tracer, at, &bts);
115                 if (error < 0) {
116                         printk(KERN_CONT
117                                "error reading bts trace at [%lu] (0x%p)...",
118                                index, at);
119                         return error;
120                 }
121
122                 switch (bts.qualifier) {
123                 case BTS_BRANCH:
124                         break;
125                 default:
126                         printk(KERN_CONT
127                                "unexpected bts entry %llu at [%lu] (0x%p)...",
128                                bts.qualifier, index, at);
129                         return -1;
130                 }
131         }
132
133         return 0;
134 }
135
136 static void ds_selftest_bts_cpu(void *arg)
137 {
138         struct ds_selftest_bts_conf *conf = arg;
139         const struct bts_trace *trace;
140         void *top;
141
142         if (IS_ERR(conf->tracer)) {
143                 conf->error = PTR_ERR(conf->tracer);
144                 conf->tracer = NULL;
145
146                 printk(KERN_CONT
147                        "initialization failed (err: %d)...", conf->error);
148                 return;
149         }
150
151         /* We should meanwhile have enough trace. */
152         conf->error = conf->suspend(conf->tracer);
153         if (conf->error < 0)
154                 return;
155
156         /* Let's see if we can access the trace. */
157         trace = ds_read_bts(conf->tracer);
158
159         conf->error = ds_selftest_bts_consistency(trace);
160         if (conf->error < 0)
161                 return;
162
163         /* If everything went well, we should have a few trace entries. */
164         if (trace->ds.top == trace->ds.begin) {
165                 /*
166                  * It is possible but highly unlikely that we got a
167                  * buffer overflow and end up at exactly the same
168                  * position we started from.
169                  * Let's issue a warning, but continue.
170                  */
171                 printk(KERN_CONT "no trace/overflow...");
172         }
173
174         /* Let's try to read the trace we collected. */
175         conf->error =
176                 ds_selftest_bts_read(conf->tracer, trace,
177                                      trace->ds.begin, trace->ds.top);
178         if (conf->error < 0)
179                 return;
180
181         /*
182          * Let's read the trace again.
183          * Since we suspended tracing, we should get the same result.
184          */
185         top = trace->ds.top;
186
187         trace = ds_read_bts(conf->tracer);
188         conf->error = ds_selftest_bts_consistency(trace);
189         if (conf->error < 0)
190                 return;
191
192         if (top != trace->ds.top) {
193                 printk(KERN_CONT "suspend not working...");
194                 conf->error = -1;
195                 return;
196         }
197
198         /* Let's collect some more trace - see if resume is working. */
199         conf->error = conf->resume(conf->tracer);
200         if (conf->error < 0)
201                 return;
202
203         conf->error = conf->suspend(conf->tracer);
204         if (conf->error < 0)
205                 return;
206
207         trace = ds_read_bts(conf->tracer);
208
209         conf->error = ds_selftest_bts_consistency(trace);
210         if (conf->error < 0)
211                 return;
212
213         if (trace->ds.top == top) {
214                 /*
215                  * It is possible but highly unlikely that we got a
216                  * buffer overflow and end up at exactly the same
217                  * position we started from.
218                  * Let's issue a warning and check the full trace.
219                  */
220                 printk(KERN_CONT
221                        "no resume progress/overflow...");
222
223                 conf->error =
224                         ds_selftest_bts_read(conf->tracer, trace,
225                                              trace->ds.begin, trace->ds.end);
226         } else if (trace->ds.top < top) {
227                 /*
228                  * We had a buffer overflow - the entire buffer should
229                  * contain trace records.
230                  */
231                 conf->error =
232                         ds_selftest_bts_read(conf->tracer, trace,
233                                              trace->ds.begin, trace->ds.end);
234         } else {
235                 /*
236                  * It is quite likely that the buffer did not overflow.
237                  * Let's just check the delta trace.
238                  */
239                 conf->error =
240                         ds_selftest_bts_read(conf->tracer, trace, top,
241                                              trace->ds.top);
242         }
243         if (conf->error < 0)
244                 return;
245
246         conf->error = 0;
247 }
248
249 static int ds_suspend_bts_wrap(struct bts_tracer *tracer)
250 {
251         ds_suspend_bts(tracer);
252         return 0;
253 }
254
255 static int ds_resume_bts_wrap(struct bts_tracer *tracer)
256 {
257         ds_resume_bts(tracer);
258         return 0;
259 }
260
261 static void ds_release_bts_noirq_wrap(void *tracer)
262 {
263         (void)ds_release_bts_noirq(tracer);
264 }
265
266 static int ds_selftest_bts_bad_release_noirq(int cpu,
267                                              struct bts_tracer *tracer)
268 {
269         int error = -EPERM;
270
271         /* Try to release the tracer on the wrong cpu. */
272         get_cpu();
273         if (cpu != smp_processor_id()) {
274                 error = ds_release_bts_noirq(tracer);
275                 if (error != -EPERM)
276                         printk(KERN_CONT "release on wrong cpu...");
277         }
278         put_cpu();
279
280         return error ? 0 : -1;
281 }
282
283 static int ds_selftest_bts_bad_request_cpu(int cpu, void *buffer)
284 {
285         struct bts_tracer *tracer;
286         int error;
287
288         /* Try to request cpu tracing while task tracing is active. */
289         tracer = ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, NULL,
290                                     (size_t)-1, BTS_KERNEL);
291         error = PTR_ERR(tracer);
292         if (!IS_ERR(tracer)) {
293                 ds_release_bts(tracer);
294                 error = 0;
295         }
296
297         if (error != -EPERM)
298                 printk(KERN_CONT "cpu/task tracing overlap...");
299
300         return error ? 0 : -1;
301 }
302
303 static int ds_selftest_bts_bad_request_task(void *buffer)
304 {
305         struct bts_tracer *tracer;
306         int error;
307
308         /* Try to request cpu tracing while task tracing is active. */
309         tracer = ds_request_bts_task(current, buffer, BUFFER_SIZE, NULL,
310                                     (size_t)-1, BTS_KERNEL);
311         error = PTR_ERR(tracer);
312         if (!IS_ERR(tracer)) {
313                 error = 0;
314                 ds_release_bts(tracer);
315         }
316
317         if (error != -EPERM)
318                 printk(KERN_CONT "task/cpu tracing overlap...");
319
320         return error ? 0 : -1;
321 }
322
323 int ds_selftest_bts(void)
324 {
325         struct ds_selftest_bts_conf conf;
326         unsigned char buffer[BUFFER_SIZE], *small_buffer;
327         unsigned long irq;
328         int cpu;
329
330         printk(KERN_INFO "[ds] bts selftest...");
331         conf.error = 0;
332
333         small_buffer = (unsigned char *)ALIGN((unsigned long)buffer, 8) + 8;
334
335         get_online_cpus();
336         for_each_online_cpu(cpu) {
337                 conf.suspend = ds_suspend_bts_wrap;
338                 conf.resume = ds_resume_bts_wrap;
339                 conf.tracer =
340                         ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
341                                            NULL, (size_t)-1, BTS_KERNEL);
342                 ds_selftest_bts_cpu(&conf);
343                 if (conf.error >= 0)
344                         conf.error = ds_selftest_bts_bad_request_task(buffer);
345                 ds_release_bts(conf.tracer);
346                 if (conf.error < 0)
347                         goto out;
348
349                 conf.suspend = ds_suspend_bts_noirq;
350                 conf.resume = ds_resume_bts_noirq;
351                 conf.tracer =
352                         ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
353                                            NULL, (size_t)-1, BTS_KERNEL);
354                 smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1);
355                 if (conf.error >= 0) {
356                         conf.error =
357                                 ds_selftest_bts_bad_release_noirq(cpu,
358                                                                   conf.tracer);
359                         /* We must not release the tracer twice. */
360                         if (conf.error < 0)
361                                 conf.tracer = NULL;
362                 }
363                 if (conf.error >= 0)
364                         conf.error = ds_selftest_bts_bad_request_task(buffer);
365                 smp_call_function_single(cpu, ds_release_bts_noirq_wrap,
366                                          conf.tracer, 1);
367                 if (conf.error < 0)
368                         goto out;
369         }
370
371         conf.suspend = ds_suspend_bts_wrap;
372         conf.resume = ds_resume_bts_wrap;
373         conf.tracer =
374                 ds_request_bts_task(current, buffer, BUFFER_SIZE,
375                                     NULL, (size_t)-1, BTS_KERNEL);
376         ds_selftest_bts_cpu(&conf);
377         if (conf.error >= 0)
378                 conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
379         ds_release_bts(conf.tracer);
380         if (conf.error < 0)
381                 goto out;
382
383         conf.suspend = ds_suspend_bts_noirq;
384         conf.resume = ds_resume_bts_noirq;
385         conf.tracer =
386                 ds_request_bts_task(current, small_buffer, SMALL_BUFFER_SIZE,
387                                    NULL, (size_t)-1, BTS_KERNEL);
388         local_irq_save(irq);
389         ds_selftest_bts_cpu(&conf);
390         if (conf.error >= 0)
391                 conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
392         ds_release_bts_noirq(conf.tracer);
393         local_irq_restore(irq);
394         if (conf.error < 0)
395                 goto out;
396
397         conf.error = 0;
398  out:
399         put_online_cpus();
400         printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed"));
401
402         return conf.error;
403 }
404
405 int ds_selftest_pebs(void)
406 {
407         return 0;
408 }