x86, ds: selftest each cpu
Perform debug store selftests on each cpu. Cover both the normal and the _noirq variant of the debug store interface. Signed-off-by: Markus Metzger <markus.t.metzger@intel.com> Cc: roland@redhat.com Cc: eranian@googlemail.com Cc: oleg@redhat.com Cc: juan.villacis@intel.com Cc: ak@linux.jf.intel.com LKML-Reference: <20090403144559.394583000@intel.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
committed by
Ingo Molnar
parent
84f2011392
commit
01f6569ece
@@ -11,13 +11,21 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
|
#include <linux/cpu.h>
|
||||||
|
|
||||||
#include <asm/ds.h>
|
#include <asm/ds.h>
|
||||||
|
|
||||||
|
|
||||||
#define BUFFER_SIZE 1021 /* Intentionally chose an odd size. */
|
#define BUFFER_SIZE 521 /* Intentionally chose an odd size. */
|
||||||
|
|
||||||
|
|
||||||
|
struct ds_selftest_bts_conf {
|
||||||
|
struct bts_tracer *tracer;
|
||||||
|
int error;
|
||||||
|
int (*suspend)(struct bts_tracer *);
|
||||||
|
int (*resume)(struct bts_tracer *);
|
||||||
|
};
|
||||||
|
|
||||||
static int ds_selftest_bts_consistency(const struct bts_trace *trace)
|
static int ds_selftest_bts_consistency(const struct bts_trace *trace)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
@@ -125,36 +133,32 @@ static int ds_selftest_bts_read(struct bts_tracer *tracer,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ds_selftest_bts(void)
|
static void ds_selftest_bts_cpu(void *arg)
|
||||||
{
|
{
|
||||||
|
struct ds_selftest_bts_conf *conf = arg;
|
||||||
const struct bts_trace *trace;
|
const struct bts_trace *trace;
|
||||||
struct bts_tracer *tracer;
|
|
||||||
int error = 0;
|
|
||||||
void *top;
|
void *top;
|
||||||
unsigned char buffer[BUFFER_SIZE];
|
|
||||||
|
|
||||||
printk(KERN_INFO "[ds] bts selftest...");
|
if (IS_ERR(conf->tracer)) {
|
||||||
|
conf->error = PTR_ERR(conf->tracer);
|
||||||
tracer = ds_request_bts_cpu(smp_processor_id(), buffer, BUFFER_SIZE,
|
conf->tracer = NULL;
|
||||||
NULL, (size_t)-1, BTS_KERNEL);
|
|
||||||
if (IS_ERR(tracer)) {
|
|
||||||
error = PTR_ERR(tracer);
|
|
||||||
tracer = NULL;
|
|
||||||
|
|
||||||
printk(KERN_CONT
|
printk(KERN_CONT
|
||||||
"initialization failed (err: %d)...", error);
|
"initialization failed (err: %d)...", conf->error);
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The return should already give us enough trace. */
|
/* We should meanwhile have enough trace. */
|
||||||
ds_suspend_bts(tracer);
|
conf->error = conf->suspend(conf->tracer);
|
||||||
|
if (conf->error < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Let's see if we can access the trace. */
|
/* Let's see if we can access the trace. */
|
||||||
trace = ds_read_bts(tracer);
|
trace = ds_read_bts(conf->tracer);
|
||||||
|
|
||||||
error = ds_selftest_bts_consistency(trace);
|
conf->error = ds_selftest_bts_consistency(trace);
|
||||||
if (error < 0)
|
if (conf->error < 0)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
/* If everything went well, we should have a few trace entries. */
|
/* If everything went well, we should have a few trace entries. */
|
||||||
if (trace->ds.top == trace->ds.begin) {
|
if (trace->ds.top == trace->ds.begin) {
|
||||||
@@ -168,10 +172,11 @@ int ds_selftest_bts(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Let's try to read the trace we collected. */
|
/* Let's try to read the trace we collected. */
|
||||||
error = ds_selftest_bts_read(tracer, trace,
|
conf->error =
|
||||||
|
ds_selftest_bts_read(conf->tracer, trace,
|
||||||
trace->ds.begin, trace->ds.top);
|
trace->ds.begin, trace->ds.top);
|
||||||
if (error < 0)
|
if (conf->error < 0)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let's read the trace again.
|
* Let's read the trace again.
|
||||||
@@ -179,26 +184,31 @@ int ds_selftest_bts(void)
|
|||||||
*/
|
*/
|
||||||
top = trace->ds.top;
|
top = trace->ds.top;
|
||||||
|
|
||||||
trace = ds_read_bts(tracer);
|
trace = ds_read_bts(conf->tracer);
|
||||||
error = ds_selftest_bts_consistency(trace);
|
conf->error = ds_selftest_bts_consistency(trace);
|
||||||
if (error < 0)
|
if (conf->error < 0)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
if (top != trace->ds.top) {
|
if (top != trace->ds.top) {
|
||||||
printk(KERN_CONT "suspend not working...");
|
printk(KERN_CONT "suspend not working...");
|
||||||
error = -1;
|
conf->error = -1;
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let's collect some more trace - see if resume is working. */
|
/* Let's collect some more trace - see if resume is working. */
|
||||||
ds_resume_bts(tracer);
|
conf->error = conf->resume(conf->tracer);
|
||||||
ds_suspend_bts(tracer);
|
if (conf->error < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
trace = ds_read_bts(tracer);
|
conf->error = conf->suspend(conf->tracer);
|
||||||
|
if (conf->error < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
error = ds_selftest_bts_consistency(trace);
|
trace = ds_read_bts(conf->tracer);
|
||||||
if (error < 0)
|
|
||||||
goto out;
|
conf->error = ds_selftest_bts_consistency(trace);
|
||||||
|
if (conf->error < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (trace->ds.top == top) {
|
if (trace->ds.top == top) {
|
||||||
/*
|
/*
|
||||||
@@ -210,35 +220,113 @@ int ds_selftest_bts(void)
|
|||||||
printk(KERN_CONT
|
printk(KERN_CONT
|
||||||
"no resume progress/overflow...");
|
"no resume progress/overflow...");
|
||||||
|
|
||||||
error = ds_selftest_bts_read(tracer, trace,
|
conf->error =
|
||||||
|
ds_selftest_bts_read(conf->tracer, trace,
|
||||||
trace->ds.begin, trace->ds.end);
|
trace->ds.begin, trace->ds.end);
|
||||||
} else if (trace->ds.top < top) {
|
} else if (trace->ds.top < top) {
|
||||||
/*
|
/*
|
||||||
* We had a buffer overflow - the entire buffer should
|
* We had a buffer overflow - the entire buffer should
|
||||||
* contain trace records.
|
* contain trace records.
|
||||||
*/
|
*/
|
||||||
error = ds_selftest_bts_read(tracer, trace,
|
conf->error =
|
||||||
|
ds_selftest_bts_read(conf->tracer, trace,
|
||||||
trace->ds.begin, trace->ds.end);
|
trace->ds.begin, trace->ds.end);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* It is quite likely that the buffer did not overflow.
|
* It is quite likely that the buffer did not overflow.
|
||||||
* Let's just check the delta trace.
|
* Let's just check the delta trace.
|
||||||
*/
|
*/
|
||||||
error = ds_selftest_bts_read(tracer, trace,
|
conf->error =
|
||||||
top, trace->ds.top);
|
ds_selftest_bts_read(conf->tracer, trace, top,
|
||||||
|
trace->ds.top);
|
||||||
}
|
}
|
||||||
if (error < 0)
|
if (conf->error < 0)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
error = 0;
|
conf->error = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* The final test: release the tracer while tracing is suspended. */
|
static int ds_suspend_bts_wrap(struct bts_tracer *tracer)
|
||||||
|
{
|
||||||
|
ds_suspend_bts(tracer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds_resume_bts_wrap(struct bts_tracer *tracer)
|
||||||
|
{
|
||||||
|
ds_resume_bts(tracer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ds_release_bts_noirq_wrap(void *tracer)
|
||||||
|
{
|
||||||
|
(void)ds_release_bts_noirq(tracer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds_selftest_bts_bad_release_noirq(int cpu,
|
||||||
|
struct bts_tracer *tracer)
|
||||||
|
{
|
||||||
|
int error = -EPERM;
|
||||||
|
|
||||||
|
/* Try to release the tracer on the wrong cpu. */
|
||||||
|
get_cpu();
|
||||||
|
if (cpu != smp_processor_id()) {
|
||||||
|
error = ds_release_bts_noirq(tracer);
|
||||||
|
if (error != -EPERM)
|
||||||
|
printk(KERN_CONT "release on wrong cpu...");
|
||||||
|
}
|
||||||
|
put_cpu();
|
||||||
|
|
||||||
|
return error ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ds_selftest_bts(void)
|
||||||
|
{
|
||||||
|
struct ds_selftest_bts_conf conf;
|
||||||
|
unsigned char buffer[BUFFER_SIZE];
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
printk(KERN_INFO "[ds] bts selftest...");
|
||||||
|
conf.error = 0;
|
||||||
|
|
||||||
|
get_online_cpus();
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
conf.suspend = ds_suspend_bts_wrap;
|
||||||
|
conf.resume = ds_resume_bts_wrap;
|
||||||
|
conf.tracer =
|
||||||
|
ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
|
||||||
|
NULL, (size_t)-1, BTS_KERNEL);
|
||||||
|
ds_selftest_bts_cpu(&conf);
|
||||||
|
ds_release_bts(conf.tracer);
|
||||||
|
if (conf.error < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
conf.suspend = ds_suspend_bts_noirq;
|
||||||
|
conf.resume = ds_resume_bts_noirq;
|
||||||
|
conf.tracer =
|
||||||
|
ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
|
||||||
|
NULL, (size_t)-1, BTS_KERNEL);
|
||||||
|
smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1);
|
||||||
|
if (conf.error >= 0) {
|
||||||
|
conf.error =
|
||||||
|
ds_selftest_bts_bad_release_noirq(cpu,
|
||||||
|
conf.tracer);
|
||||||
|
/* We must not release the tracer twice. */
|
||||||
|
if (conf.error < 0)
|
||||||
|
conf.tracer = NULL;
|
||||||
|
}
|
||||||
|
smp_call_function_single(cpu, ds_release_bts_noirq_wrap,
|
||||||
|
conf.tracer, 1);
|
||||||
|
if (conf.error < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.error = 0;
|
||||||
out:
|
out:
|
||||||
ds_release_bts(tracer);
|
put_online_cpus();
|
||||||
|
printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed"));
|
||||||
|
|
||||||
printk(KERN_CONT "%s.\n", (error ? "failed" : "passed"));
|
return conf.error;
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ds_selftest_pebs(void)
|
int ds_selftest_pebs(void)
|
||||||
|
Reference in New Issue
Block a user