Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 146112
b: refs/heads/master
c: 8a327f6
h: refs/heads/master
v: v3
  • Loading branch information
Markus Metzger authored and Ingo Molnar committed Mar 13, 2009
1 parent 228623d commit 50a3f76
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: bc44fb5f7d3e764ed7698c835a1a0f35aba2eb3d
refs/heads/master: 8a327f6d1b05f5ce16572b4413a5df1d0e872283
9 changes: 9 additions & 0 deletions trunk/arch/x86/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ config IOMMU_LEAK
Add a simple leak tracer to the IOMMU code. This is useful when you
are debugging a buggy device driver that leaks IOMMU mappings.

config X86_DS_SELFTEST
bool "DS selftest"
default y
depends on DEBUG_KERNEL
depends on X86_DS
---help---
Perform Debug Store selftests at boot time.
If in doubt, say "N".

config HAVE_MMIOTRACE_SUPPORT
def_bool y

Expand Down
1 change: 1 addition & 0 deletions trunk/arch/x86/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ obj-y += process.o
obj-y += i387.o xsave.o
obj-y += ptrace.o
obj-$(CONFIG_X86_DS) += ds.o
obj-$(CONFIG_X86_DS_SELFTEST) += ds_selftest.o
obj-$(CONFIG_X86_32) += tls.o
obj-$(CONFIG_IA32_EMULATION) += tls.o
obj-y += step.o
Expand Down
21 changes: 21 additions & 0 deletions trunk/arch/x86/kernel/ds.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <linux/mm.h>
#include <linux/kernel.h>

#include "ds_selftest.h"

/*
* The configuration for a particular DS hardware implementation.
Expand Down Expand Up @@ -940,6 +941,26 @@ ds_configure(const struct ds_configuration *cfg,
printk(KERN_INFO "[ds] pebs not available\n");
}

if (ds_cfg.sizeof_rec[ds_bts]) {
int error;

error = ds_selftest_bts();
if (error) {
WARN(1, "[ds] selftest failed. disabling bts.\n");
ds_cfg.sizeof_rec[ds_bts] = 0;
}
}

if (ds_cfg.sizeof_rec[ds_pebs]) {
int error;

error = ds_selftest_pebs();
if (error) {
WARN(1, "[ds] selftest failed. disabling pebs.\n");
ds_cfg.sizeof_rec[ds_pebs] = 0;
}
}

printk(KERN_INFO "[ds] sizes: address: %u bit, ",
8 * ds_cfg.sizeof_ptr_field);
printk("bts/pebs record: %u/%u bytes\n",
Expand Down
241 changes: 241 additions & 0 deletions trunk/arch/x86/kernel/ds_selftest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* Debug Store support - selftest
*
*
* Copyright (C) 2009 Intel Corporation.
* Markus Metzger <markus.t.metzger@intel.com>, 2009
*/

#include "ds_selftest.h"

#include <linux/kernel.h>
#include <linux/string.h>

#include <asm/ds.h>


#define DS_SELFTEST_BUFFER_SIZE 1021 /* Intentionally chose an odd size. */


static int ds_selftest_bts_consistency(const struct bts_trace *trace)
{
int error = 0;

if (!trace) {
printk(KERN_CONT "failed to access trace...");
/* Bail out. Other tests are pointless. */
return -1;
}

if (!trace->read) {
printk(KERN_CONT "bts read not available...");
error = -1;
}

/* Do some sanity checks on the trace configuration. */
if (!trace->ds.n) {
printk(KERN_CONT "empty bts buffer...");
error = -1;
}
if (!trace->ds.size) {
printk(KERN_CONT "bad bts trace setup...");
error = -1;
}
if (trace->ds.end !=
(char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) {
printk(KERN_CONT "bad bts buffer setup...");
error = -1;
}
if ((trace->ds.top < trace->ds.begin) ||
(trace->ds.end <= trace->ds.top)) {
printk(KERN_CONT "bts top out of bounds...");
error = -1;
}

return error;
}

static int ds_selftest_bts_read(struct bts_tracer *tracer,
const struct bts_trace *trace,
const void *from, const void *to)
{
const unsigned char *at;

/*
* Check a few things which do not belong to this test.
* They should be covered by other tests.
*/
if (!trace)
return -1;

if (!trace->read)
return -1;

if (to < from)
return -1;

if (from < trace->ds.begin)
return -1;

if (trace->ds.end < to)
return -1;

if (!trace->ds.size)
return -1;

/* Now to the test itself. */
for (at = from; (void *)at < to; at += trace->ds.size) {
struct bts_struct bts;
size_t index;
int error;

if (((void *)at - trace->ds.begin) % trace->ds.size) {
printk(KERN_CONT
"read from non-integer index...");
return -1;
}
index = ((void *)at - trace->ds.begin) / trace->ds.size;

memset(&bts, 0, sizeof(bts));
error = trace->read(tracer, at, &bts);
if (error < 0) {
printk(KERN_CONT
"error reading bts trace at [%lu] (0x%p)...",
index, at);
return error;
}

switch (bts.qualifier) {
case BTS_BRANCH:
break;
default:
printk(KERN_CONT
"unexpected bts entry %llu at [%lu] (0x%p)...",
bts.qualifier, index, at);
return -1;
}
}

return 0;
}

int ds_selftest_bts(void)
{
const struct bts_trace *trace;
struct bts_tracer *tracer;
int error = 0;
void *top;
unsigned char buffer[DS_SELFTEST_BUFFER_SIZE];

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

tracer = ds_request_bts(NULL, buffer, DS_SELFTEST_BUFFER_SIZE,
NULL, (size_t)-1, BTS_KERNEL);
if (IS_ERR(tracer)) {
error = PTR_ERR(tracer);
tracer = NULL;

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

/* The return should already give us enough trace. */
ds_suspend_bts(tracer);

/* Let's see if we can access the trace. */
trace = ds_read_bts(tracer);

error = ds_selftest_bts_consistency(trace);
if (error < 0)
goto out;

/* If everything went well, we should have a few trace entries. */
if (trace->ds.top == trace->ds.begin) {
/*
* It is possible but highly unlikely that we got a
* buffer overflow and end up at exactly the same
* position we started from.
* Let's issue a warning, but continue.
*/
printk(KERN_CONT "no trace/overflow...");
}

/* Let's try to read the trace we collected. */
error = ds_selftest_bts_read(tracer, trace,
trace->ds.begin, trace->ds.top);
if (error < 0)
goto out;

/*
* 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;

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

/* Let's collect some more trace - see if resume is working. */
ds_resume_bts(tracer);
ds_suspend_bts(tracer);

trace = ds_read_bts(tracer);

error = ds_selftest_bts_consistency(trace);
if (error < 0)
goto out;

if (trace->ds.top == top) {
/*
* It is possible but highly unlikely that we got a
* buffer overflow and end up at exactly the same
* position we started from.
* Let's issue a warning and check the full trace.
*/
printk(KERN_CONT
"no resume progress/overflow...");

error = ds_selftest_bts_read(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,
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);
}
if (error < 0)
goto out;

error = 0;

/* The final test: release the tracer while tracing is suspended. */
out:
ds_release_bts(tracer);

printk(KERN_CONT "%s.\n", (error ? "failed" : "passed"));

return error;
}

int ds_selftest_pebs(void)
{
return 0;
}
15 changes: 15 additions & 0 deletions trunk/arch/x86/kernel/ds_selftest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Debug Store support - selftest
*
*
* Copyright (C) 2009 Intel Corporation.
* Markus Metzger <markus.t.metzger@intel.com>, 2009
*/

#ifdef CONFIG_X86_DS_SELFTEST
extern int ds_selftest_bts(void);
extern int ds_selftest_pebs(void);
#else
static inline int ds_selftest_bts(void) { return 0; }
static inline int ds_selftest_pebs(void) { return 0; }
#endif /* CONFIG_X86_DS_SELFTEST */

0 comments on commit 50a3f76

Please sign in to comment.