diff options
Diffstat (limited to 'arch/x86/kernel/ds_selftest.c')
| -rw-r--r-- | arch/x86/kernel/ds_selftest.c | 408 | 
1 files changed, 0 insertions, 408 deletions
diff --git a/arch/x86/kernel/ds_selftest.c b/arch/x86/kernel/ds_selftest.c deleted file mode 100644 index 6bc7c199ab99..000000000000 --- a/arch/x86/kernel/ds_selftest.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Debug Store support - selftest - * - * - * Copyright (C) 2009 Intel Corporation. - * Markus Metzger <[email protected]>, 2009 - */ - -#include "ds_selftest.h" - -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/smp.h> -#include <linux/cpu.h> - -#include <asm/ds.h> - - -#define BUFFER_SIZE		521	/* Intentionally chose an odd size. */ -#define SMALL_BUFFER_SIZE	24	/* A single bts entry. */ - -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; - -	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; -	} -	/* -	 * We allow top in [begin; end], since its not clear when the -	 * overflow adjustment happens: after the increment or before the -	 * write. -	 */ -	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; -		unsigned long 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; -} - -static void ds_selftest_bts_cpu(void *arg) -{ -	struct ds_selftest_bts_conf *conf = arg; -	const struct bts_trace *trace; -	void *top; - -	if (IS_ERR(conf->tracer)) { -		conf->error = PTR_ERR(conf->tracer); -		conf->tracer = NULL; - -		printk(KERN_CONT -		       "initialization failed (err: %d)...", conf->error); -		return; -	} - -	/* 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(conf->tracer); - -	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) { -		/* -		 * 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. */ -	conf->error = -		ds_selftest_bts_read(conf->tracer, trace, -				     trace->ds.begin, trace->ds.top); -	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(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..."); -		conf->error = -1; -		return; -	} - -	/* Let's collect some more trace - see if resume is working. */ -	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(conf->tracer); - -	conf->error = ds_selftest_bts_consistency(trace); -	if (conf->error < 0) -		return; - -	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..."); - -		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. -		 */ -		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. -		 */ -		conf->error = -			ds_selftest_bts_read(conf->tracer, trace, top, -					     trace->ds.top); -	} -	if (conf->error < 0) -		return; - -	conf->error = 0; -} - -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; -} - -static int ds_selftest_bts_bad_request_cpu(int cpu, void *buffer) -{ -	struct bts_tracer *tracer; -	int error; - -	/* Try to request cpu tracing while task tracing is active. */ -	tracer = ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, NULL, -				    (size_t)-1, BTS_KERNEL); -	error = PTR_ERR(tracer); -	if (!IS_ERR(tracer)) { -		ds_release_bts(tracer); -		error = 0; -	} - -	if (error != -EPERM) -		printk(KERN_CONT "cpu/task tracing overlap..."); - -	return error ? 0 : -1; -} - -static int ds_selftest_bts_bad_request_task(void *buffer) -{ -	struct bts_tracer *tracer; -	int error; - -	/* Try to request cpu tracing while task tracing is active. */ -	tracer = ds_request_bts_task(current, buffer, BUFFER_SIZE, NULL, -				    (size_t)-1, BTS_KERNEL); -	error = PTR_ERR(tracer); -	if (!IS_ERR(tracer)) { -		error = 0; -		ds_release_bts(tracer); -	} - -	if (error != -EPERM) -		printk(KERN_CONT "task/cpu tracing overlap..."); - -	return error ? 0 : -1; -} - -int ds_selftest_bts(void) -{ -	struct ds_selftest_bts_conf conf; -	unsigned char buffer[BUFFER_SIZE], *small_buffer; -	unsigned long irq; -	int cpu; - -	printk(KERN_INFO "[ds] bts selftest..."); -	conf.error = 0; - -	small_buffer = (unsigned char *)ALIGN((unsigned long)buffer, 8) + 8; - -	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); -		if (conf.error >= 0) -			conf.error = ds_selftest_bts_bad_request_task(buffer); -		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; -		} -		if (conf.error >= 0) -			conf.error = ds_selftest_bts_bad_request_task(buffer); -		smp_call_function_single(cpu, ds_release_bts_noirq_wrap, -					 conf.tracer, 1); -		if (conf.error < 0) -			goto out; -	} - -	conf.suspend = ds_suspend_bts_wrap; -	conf.resume = ds_resume_bts_wrap; -	conf.tracer = -		ds_request_bts_task(current, buffer, BUFFER_SIZE, -				    NULL, (size_t)-1, BTS_KERNEL); -	ds_selftest_bts_cpu(&conf); -	if (conf.error >= 0) -		conf.error = ds_selftest_bts_bad_request_cpu(0, buffer); -	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_task(current, small_buffer, SMALL_BUFFER_SIZE, -				   NULL, (size_t)-1, BTS_KERNEL); -	local_irq_save(irq); -	ds_selftest_bts_cpu(&conf); -	if (conf.error >= 0) -		conf.error = ds_selftest_bts_bad_request_cpu(0, buffer); -	ds_release_bts_noirq(conf.tracer); -	local_irq_restore(irq); -	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) -{ -	return 0; -}  |