Skip to content

Commit

Permalink
x86, ds: selftest each cpu
Browse files Browse the repository at this point in the history
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 <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
markus-metzger authored and Ingo Molnar committed Apr 7, 2009
1 parent 84f2011 commit 01f6569
Showing 1 changed file with 135 additions and 47 deletions.
182 changes: 135 additions & 47 deletions arch/x86/kernel/ds_selftest.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/smp.h>
#include <linux/cpu.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)
{
int error = 0;
Expand Down Expand Up @@ -125,36 +133,32 @@ static int ds_selftest_bts_read(struct bts_tracer *tracer,
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;
struct bts_tracer *tracer;
int error = 0;
void *top;
unsigned char buffer[BUFFER_SIZE];

printk(KERN_INFO "[ds] bts selftest...");

tracer = ds_request_bts_cpu(smp_processor_id(), buffer, BUFFER_SIZE,
NULL, (size_t)-1, BTS_KERNEL);
if (IS_ERR(tracer)) {
error = PTR_ERR(tracer);
tracer = NULL;
if (IS_ERR(conf->tracer)) {
conf->error = PTR_ERR(conf->tracer);
conf->tracer = NULL;

printk(KERN_CONT
"initialization failed (err: %d)...", error);
goto out;
"initialization failed (err: %d)...", conf->error);
return;
}

/* The return should already give us enough trace. */
ds_suspend_bts(tracer);
/* We should meanwhile have enough trace. */
conf->error = conf->suspend(conf->tracer);
if (conf->error < 0)
return;

/* 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);
if (error < 0)
goto out;
conf->error = ds_selftest_bts_consistency(trace);
if (conf->error < 0)
return;

/* If everything went well, we should have a few trace entries. */
if (trace->ds.top == trace->ds.begin) {
Expand All @@ -168,37 +172,43 @@ int ds_selftest_bts(void)
}

/* 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);
if (error < 0)
goto out;
if (conf->error < 0)
return;

/*
* Let's read the trace again.
* Since we suspended tracing, we should get the same result.
*/
top = trace->ds.top;

trace = ds_read_bts(tracer);
error = ds_selftest_bts_consistency(trace);
if (error < 0)
goto out;
trace = ds_read_bts(conf->tracer);
conf->error = ds_selftest_bts_consistency(trace);
if (conf->error < 0)
return;

if (top != trace->ds.top) {
printk(KERN_CONT "suspend not working...");
error = -1;
goto out;
conf->error = -1;
return;
}

/* Let's collect some more trace - see if resume is working. */
ds_resume_bts(tracer);
ds_suspend_bts(tracer);
conf->error = conf->resume(conf->tracer);
if (conf->error < 0)
return;

conf->error = conf->suspend(conf->tracer);
if (conf->error < 0)
return;

trace = ds_read_bts(tracer);
trace = ds_read_bts(conf->tracer);

error = ds_selftest_bts_consistency(trace);
if (error < 0)
goto out;
conf->error = ds_selftest_bts_consistency(trace);
if (conf->error < 0)
return;

if (trace->ds.top == top) {
/*
Expand All @@ -210,35 +220,113 @@ int ds_selftest_bts(void)
printk(KERN_CONT
"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);
} else if (trace->ds.top < top) {
/*
* We had a buffer overflow - the entire buffer should
* 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);
} else {
/*
* It is quite likely that the buffer did not overflow.
* Let's just check the delta trace.
*/
error = ds_selftest_bts_read(tracer, trace,
top, trace->ds.top);
conf->error =
ds_selftest_bts_read(conf->tracer, trace, top,
trace->ds.top);
}
if (error < 0)
goto out;
if (conf->error < 0)
return;

error = 0;
conf->error = 0;
}

/* The final test: release the tracer while tracing is suspended. */
out:
ds_release_bts(tracer);
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;
}

printk(KERN_CONT "%s.\n", (error ? "failed" : "passed"));
static void ds_release_bts_noirq_wrap(void *tracer)
{
(void)ds_release_bts_noirq(tracer);
}

return error;
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:
put_online_cpus();
printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed"));

return conf.error;
}

int ds_selftest_pebs(void)
Expand Down

0 comments on commit 01f6569

Please sign in to comment.