diff options
Diffstat (limited to 'tools/testing/selftests/powerpc/pmu/lib.c')
| -rw-r--r-- | tools/testing/selftests/powerpc/pmu/lib.c | 252 | 
1 files changed, 252 insertions, 0 deletions
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c new file mode 100644 index 00000000000..0f6a4731d54 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/lib.c @@ -0,0 +1,252 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#define _GNU_SOURCE	/* For CPU_ZERO etc. */ + +#include <errno.h> +#include <sched.h> +#include <setjmp.h> +#include <stdlib.h> +#include <sys/wait.h> + +#include "utils.h" +#include "lib.h" + + +int pick_online_cpu(void) +{ +	cpu_set_t mask; +	int cpu; + +	CPU_ZERO(&mask); + +	if (sched_getaffinity(0, sizeof(mask), &mask)) { +		perror("sched_getaffinity"); +		return -1; +	} + +	/* We prefer a primary thread, but skip 0 */ +	for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8) +		if (CPU_ISSET(cpu, &mask)) +			return cpu; + +	/* Search for anything, but in reverse */ +	for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--) +		if (CPU_ISSET(cpu, &mask)) +			return cpu; + +	printf("No cpus in affinity mask?!\n"); +	return -1; +} + +int bind_to_cpu(int cpu) +{ +	cpu_set_t mask; + +	printf("Binding to cpu %d\n", cpu); + +	CPU_ZERO(&mask); +	CPU_SET(cpu, &mask); + +	return sched_setaffinity(0, sizeof(mask), &mask); +} + +#define PARENT_TOKEN	0xAA +#define CHILD_TOKEN	0x55 + +int sync_with_child(union pipe read_pipe, union pipe write_pipe) +{ +	char c = PARENT_TOKEN; + +	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); +	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); +	if (c != CHILD_TOKEN) /* sometimes expected */ +		return 1; + +	return 0; +} + +int wait_for_parent(union pipe read_pipe) +{ +	char c; + +	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); +	FAIL_IF(c != PARENT_TOKEN); + +	return 0; +} + +int notify_parent(union pipe write_pipe) +{ +	char c = CHILD_TOKEN; + +	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); + +	return 0; +} + +int notify_parent_of_error(union pipe write_pipe) +{ +	char c = ~CHILD_TOKEN; + +	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); + +	return 0; +} + +int wait_for_child(pid_t child_pid) +{ +	int rc; + +	if (waitpid(child_pid, &rc, 0) == -1) { +		perror("waitpid"); +		return 1; +	} + +	if (WIFEXITED(rc)) +		rc = WEXITSTATUS(rc); +	else +		rc = 1; /* Signal or other */ + +	return rc; +} + +int kill_child_and_wait(pid_t child_pid) +{ +	kill(child_pid, SIGTERM); + +	return wait_for_child(child_pid); +} + +static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe) +{ +	volatile int i = 0; + +	/* +	 * We are just here to eat cpu and die. So make sure we can be killed, +	 * and also don't do any custom SIGTERM handling. +	 */ +	signal(SIGTERM, SIG_DFL); + +	notify_parent(write_pipe); +	wait_for_parent(read_pipe); + +	/* Soak up cpu forever */ +	while (1) i++; + +	return 0; +} + +pid_t eat_cpu(int (test_function)(void)) +{ +	union pipe read_pipe, write_pipe; +	int cpu, rc; +	pid_t pid; + +	cpu = pick_online_cpu(); +	FAIL_IF(cpu < 0); +	FAIL_IF(bind_to_cpu(cpu)); + +	if (pipe(read_pipe.fds) == -1) +		return -1; + +	if (pipe(write_pipe.fds) == -1) +		return -1; + +	pid = fork(); +	if (pid == 0) +		exit(eat_cpu_child(write_pipe, read_pipe)); + +	if (sync_with_child(read_pipe, write_pipe)) { +		rc = -1; +		goto out; +	} + +	printf("main test running as pid %d\n", getpid()); + +	rc = test_function(); +out: +	kill(pid, SIGKILL); + +	return rc; +} + +struct addr_range libc, vdso; + +int parse_proc_maps(void) +{ +	char execute, name[128]; +	uint64_t start, end; +	FILE *f; +	int rc; + +	f = fopen("/proc/self/maps", "r"); +	if (!f) { +		perror("fopen"); +		return -1; +	} + +	do { +		/* This skips line with no executable which is what we want */ +		rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n", +			    &start, &end, &execute, name); +		if (rc <= 0) +			break; + +		if (execute != 'x') +			continue; + +		if (strstr(name, "libc")) { +			libc.first = start; +			libc.last = end - 1; +		} else if (strstr(name, "[vdso]")) { +			vdso.first = start; +			vdso.last = end - 1; +		} +	} while(1); + +	fclose(f); + +	return 0; +} + +#define PARANOID_PATH	"/proc/sys/kernel/perf_event_paranoid" + +bool require_paranoia_below(int level) +{ +	unsigned long current; +	char *end, buf[16]; +	FILE *f; +	int rc; + +	rc = -1; + +	f = fopen(PARANOID_PATH, "r"); +	if (!f) { +		perror("fopen"); +		goto out; +	} + +	if (!fgets(buf, sizeof(buf), f)) { +		printf("Couldn't read " PARANOID_PATH "?\n"); +		goto out_close; +	} + +	current = strtoul(buf, &end, 10); + +	if (end == buf) { +		printf("Couldn't parse " PARANOID_PATH "?\n"); +		goto out_close; +	} + +	if (current >= level) +		goto out; + +	rc = 0; +out_close: +	fclose(f); +out: +	return rc; +}  | 
