diff options
Diffstat (limited to 'tools/testing')
40 files changed, 1507 insertions, 156 deletions
diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index 38b00ecb2ed5..a34bfd0c8928 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -9,6 +9,8 @@ ldflags-y += --wrap=memunmap ldflags-y += --wrap=__devm_request_region ldflags-y += --wrap=__request_region ldflags-y += --wrap=__release_region +ldflags-y += --wrap=devm_memremap_pages +ldflags-y += --wrap=phys_to_pfn_t DRIVERS := ../../../drivers NVDIMM_SRC := $(DRIVERS)/nvdimm diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c index b7251314bbc0..7ec7df9e7fc7 100644 --- a/tools/testing/nvdimm/test/iomap.c +++ b/tools/testing/nvdimm/test/iomap.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/io.h> +#include <linux/mm.h> #include "nfit_test.h" static LIST_HEAD(iomap_head); @@ -41,7 +42,7 @@ void nfit_test_teardown(void) } EXPORT_SYMBOL(nfit_test_teardown); -static struct nfit_test_resource *get_nfit_res(resource_size_t resource) +static struct nfit_test_resource *__get_nfit_res(resource_size_t resource) { struct iomap_ops *ops; @@ -51,14 +52,22 @@ static struct nfit_test_resource *get_nfit_res(resource_size_t resource) return NULL; } -void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size, - void __iomem *(*fallback_fn)(resource_size_t, unsigned long)) +static struct nfit_test_resource *get_nfit_res(resource_size_t resource) { - struct nfit_test_resource *nfit_res; + struct nfit_test_resource *res; rcu_read_lock(); - nfit_res = get_nfit_res(offset); + res = __get_nfit_res(resource); rcu_read_unlock(); + + return res; +} + +void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size, + void __iomem *(*fallback_fn)(resource_size_t, unsigned long)) +{ + struct nfit_test_resource *nfit_res = get_nfit_res(offset); + if (nfit_res) return (void __iomem *) nfit_res->buf + offset - nfit_res->res->start; @@ -68,11 +77,8 @@ void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size, void __iomem *__wrap_devm_ioremap_nocache(struct device *dev, resource_size_t offset, unsigned long size) { - struct nfit_test_resource *nfit_res; + struct nfit_test_resource *nfit_res = get_nfit_res(offset); - rcu_read_lock(); - nfit_res = get_nfit_res(offset); - rcu_read_unlock(); if (nfit_res) return (void __iomem *) nfit_res->buf + offset - nfit_res->res->start; @@ -83,25 +89,58 @@ EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); void *__wrap_devm_memremap(struct device *dev, resource_size_t offset, size_t size, unsigned long flags) { - struct nfit_test_resource *nfit_res; + struct nfit_test_resource *nfit_res = get_nfit_res(offset); - rcu_read_lock(); - nfit_res = get_nfit_res(offset); - rcu_read_unlock(); if (nfit_res) return nfit_res->buf + offset - nfit_res->res->start; return devm_memremap(dev, offset, size, flags); } EXPORT_SYMBOL(__wrap_devm_memremap); +#ifdef __HAVE_ARCH_PTE_DEVMAP +#include <linux/memremap.h> +#include <linux/pfn_t.h> + +void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res, + struct percpu_ref *ref, struct vmem_altmap *altmap) +{ + resource_size_t offset = res->start; + struct nfit_test_resource *nfit_res = get_nfit_res(offset); + + if (nfit_res) + return nfit_res->buf + offset - nfit_res->res->start; + return devm_memremap_pages(dev, res, ref, altmap); +} +EXPORT_SYMBOL(__wrap_devm_memremap_pages); + +pfn_t __wrap_phys_to_pfn_t(dma_addr_t addr, unsigned long flags) +{ + struct nfit_test_resource *nfit_res = get_nfit_res(addr); + + if (nfit_res) + flags &= ~PFN_MAP; + return phys_to_pfn_t(addr, flags); +} +EXPORT_SYMBOL(__wrap_phys_to_pfn_t); +#else +/* to be removed post 4.5-rc1 */ +void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res) +{ + resource_size_t offset = res->start; + struct nfit_test_resource *nfit_res = get_nfit_res(offset); + + if (nfit_res) + return nfit_res->buf + offset - nfit_res->res->start; + return devm_memremap_pages(dev, res); +} +EXPORT_SYMBOL(__wrap_devm_memremap_pages); +#endif + void *__wrap_memremap(resource_size_t offset, size_t size, unsigned long flags) { - struct nfit_test_resource *nfit_res; + struct nfit_test_resource *nfit_res = get_nfit_res(offset); - rcu_read_lock(); - nfit_res = get_nfit_res(offset); - rcu_read_unlock(); if (nfit_res) return nfit_res->buf + offset - nfit_res->res->start; return memremap(offset, size, flags); @@ -110,11 +149,8 @@ EXPORT_SYMBOL(__wrap_memremap); void __wrap_devm_memunmap(struct device *dev, void *addr) { - struct nfit_test_resource *nfit_res; + struct nfit_test_resource *nfit_res = get_nfit_res((long) addr); - rcu_read_lock(); - nfit_res = get_nfit_res((unsigned long) addr); - rcu_read_unlock(); if (nfit_res) return; return devm_memunmap(dev, addr); @@ -135,11 +171,7 @@ EXPORT_SYMBOL(__wrap_ioremap_wc); void __wrap_iounmap(volatile void __iomem *addr) { - struct nfit_test_resource *nfit_res; - - rcu_read_lock(); - nfit_res = get_nfit_res((unsigned long) addr); - rcu_read_unlock(); + struct nfit_test_resource *nfit_res = get_nfit_res((long) addr); if (nfit_res) return; return iounmap(addr); @@ -148,11 +180,8 @@ EXPORT_SYMBOL(__wrap_iounmap); void __wrap_memunmap(void *addr) { - struct nfit_test_resource *nfit_res; + struct nfit_test_resource *nfit_res = get_nfit_res((long) addr); - rcu_read_lock(); - nfit_res = get_nfit_res((unsigned long) addr); - rcu_read_unlock(); if (nfit_res) return; return memunmap(addr); @@ -166,9 +195,7 @@ static struct resource *nfit_test_request_region(struct device *dev, struct nfit_test_resource *nfit_res; if (parent == &iomem_resource) { - rcu_read_lock(); nfit_res = get_nfit_res(start); - rcu_read_unlock(); if (nfit_res) { struct resource *res = nfit_res->res + 1; @@ -218,9 +245,7 @@ void __wrap___release_region(struct resource *parent, resource_size_t start, struct nfit_test_resource *nfit_res; if (parent == &iomem_resource) { - rcu_read_lock(); nfit_res = get_nfit_res(start); - rcu_read_unlock(); if (nfit_res) { struct resource *res = nfit_res->res + 1; diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 51cf8256c6cd..90bd2ea41032 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -248,6 +248,8 @@ static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd, nd_cmd->out_length = 256; nd_cmd->num_records = 0; + nd_cmd->address = 0; + nd_cmd->length = -1ULL; nd_cmd->status = 0; return 0; @@ -1088,6 +1090,8 @@ static void nfit_test1_setup(struct nfit_test *t) struct acpi_nfit_memory_map *memdev; struct acpi_nfit_control_region *dcr; struct acpi_nfit_system_address *spa; + struct nvdimm_bus_descriptor *nd_desc; + struct acpi_nfit_desc *acpi_desc; offset = 0; /* spa0 (flat range with no bdw aliasing) */ @@ -1135,6 +1139,13 @@ static void nfit_test1_setup(struct nfit_test *t) dcr->command_size = 0; dcr->status_offset = 0; dcr->status_size = 0; + + acpi_desc = &t->acpi_desc; + set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); + set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); + set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); + nd_desc = &acpi_desc->nd_desc; + nd_desc->ndctl = nfit_test_ctl; } static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa, diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index c8edff6803d1..b04afc3295df 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,10 +1,12 @@ TARGETS = breakpoints +TARGETS += capabilities TARGETS += cpu-hotplug TARGETS += efivarfs TARGETS += exec TARGETS += firmware TARGETS += ftrace TARGETS += futex +TARGETS += ipc TARGETS += kcmp TARGETS += lib TARGETS += membarrier diff --git a/tools/testing/selftests/breakpoints/.gitignore b/tools/testing/selftests/breakpoints/.gitignore new file mode 100644 index 000000000000..9b3193d06608 --- /dev/null +++ b/tools/testing/selftests/breakpoints/.gitignore @@ -0,0 +1 @@ +breakpoint_test diff --git a/tools/testing/selftests/capabilities/Makefile b/tools/testing/selftests/capabilities/Makefile index 8c8f0c1f0889..008602aed920 100644 --- a/tools/testing/selftests/capabilities/Makefile +++ b/tools/testing/selftests/capabilities/Makefile @@ -1,18 +1,15 @@ -all: - -include ../lib.mk - -.PHONY: all clean - -TARGETS := validate_cap test_execve +TEST_FILES := validate_cap TEST_PROGS := test_execve -CFLAGS := -O2 -g -std=gnu99 -Wall -lcap-ng +BINARIES := $(TEST_FILES) $(TEST_PROGS) -all: $(TARGETS) +CFLAGS += -O2 -g -std=gnu99 -Wall +LDLIBS += -lcap-ng -lrt -ldl + +all: $(BINARIES) clean: - $(RM) $(TARGETS) + $(RM) $(BINARIES) + +include ../lib.mk -$(TARGETS): %: %.c - $(CC) -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index c4366dc74e01..5c495ad7958a 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -48,8 +48,21 @@ echo "ABCD0123" >"$FW" NAME=$(basename "$FW") +if printf '\000' >"$DIR"/trigger_request; then + echo "$0: empty filename should not succeed" >&2 + exit 1 +fi + +if printf '\000' >"$DIR"/trigger_async_request; then + echo "$0: empty filename should not succeed (async)" >&2 + exit 1 +fi + # Request a firmware that doesn't exist, it should fail. -echo -n "nope-$NAME" >"$DIR"/trigger_request +if echo -n "nope-$NAME" >"$DIR"/trigger_request; then + echo "$0: firmware shouldn't have loaded" >&2 + exit 1 +fi if diff -q "$FW" /dev/test_firmware >/dev/null ; then echo "$0: firmware was not expected to match" >&2 exit 1 @@ -74,4 +87,18 @@ else echo "$0: filesystem loading works" fi +# Try the asynchronous version too +if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then + echo "$0: could not trigger async request" >&2 + exit 1 +fi + +# Verify the contents are what we expect. +if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded (async)" >&2 + exit 1 +else + echo "$0: async filesystem loading works" +fi + exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance.tc b/tools/testing/selftests/ftrace/test.d/instances/instance.tc new file mode 100644 index 000000000000..773e276ff90b --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/instances/instance.tc @@ -0,0 +1,90 @@ +#!/bin/sh +# description: Test creation and deletion of trace instances + +if [ ! -d instances ] ; then + echo "no instance directory with this kernel" + exit_unsupported; +fi + +fail() { # mesg + rmdir x y z 2>/dev/null + echo $1 + set -e + exit $FAIL +} + +cd instances + +# we don't want to fail on error +set +e + +mkdir x +rmdir x +result=$? + +if [ $result -ne 0 ]; then + echo "instance rmdir not supported" + exit_unsupported +fi + +instance_slam() { + while :; do + mkdir x + mkdir y + mkdir z + rmdir x + rmdir y + rmdir z + done 2>/dev/null +} + +instance_slam & +x=`jobs -l` +p1=`echo $x | cut -d' ' -f2` +echo $p1 + +instance_slam & +x=`jobs -l | tail -1` +p2=`echo $x | cut -d' ' -f2` +echo $p2 + +instance_slam & +x=`jobs -l | tail -1` +p3=`echo $x | cut -d' ' -f2` +echo $p3 + +instance_slam & +x=`jobs -l | tail -1` +p4=`echo $x | cut -d' ' -f2` +echo $p4 + +instance_slam & +x=`jobs -l | tail -1` +p5=`echo $x | cut -d' ' -f2` +echo $p5 + +ls -lR >/dev/null +sleep 1 + +kill -1 $p1 +kill -1 $p2 +kill -1 $p3 +kill -1 $p4 +kill -1 $p5 + +echo "Wait for processes to finish" +wait $p1 $p2 $p3 $p4 $p5 +echo "all processes finished, wait for cleanup" + +mkdir x y z +ls x y z +rmdir x y z +for d in x y z; do + if [ -d $d ]; then + fail "instance $d still exists" + fi +done + +set -e + +exit 0 diff --git a/tools/testing/selftests/intel_pstate/Makefile b/tools/testing/selftests/intel_pstate/Makefile new file mode 100644 index 000000000000..f5f1a28715ff --- /dev/null +++ b/tools/testing/selftests/intel_pstate/Makefile @@ -0,0 +1,15 @@ +CC := $(CROSS_COMPILE)gcc +CFLAGS := $(CFLAGS) -Wall -D_GNU_SOURCE +LDFLAGS := $(LDFLAGS) -lm + +TARGETS := msr aperf + +TEST_PROGS := $(TARGETS) run.sh + +.PHONY: all clean +all: $(TARGETS) + +$(TARGETS): $(HEADERS) + +clean: + rm -f $(TARGETS) diff --git a/tools/testing/selftests/intel_pstate/aperf.c b/tools/testing/selftests/intel_pstate/aperf.c new file mode 100644 index 000000000000..6046e183f4ad --- /dev/null +++ b/tools/testing/selftests/intel_pstate/aperf.c @@ -0,0 +1,80 @@ +#include <math.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/timeb.h> +#include <sched.h> +#include <errno.h> + +void usage(char *name) { + printf ("Usage: %s cpunum\n", name); +} + +int main(int argc, char **argv) { + int i, cpu, fd; + char msr_file_name[64]; + long long tsc, old_tsc, new_tsc; + long long aperf, old_aperf, new_aperf; + long long mperf, old_mperf, new_mperf; + struct timeb before, after; + long long int start, finish, total; + cpu_set_t cpuset; + + if (argc != 2) { + usage(argv[0]); + return 1; + } + + errno = 0; + cpu = strtol(argv[1], (char **) NULL, 10); + + if (errno) { + usage(argv[0]); + return 1; + } + + sprintf(msr_file_name, "/dev/cpu/%d/msr", cpu); + fd = open(msr_file_name, O_RDONLY); + + if (fd == -1) { + perror("Failed to open"); + return 1; + } + + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset)) { + perror("Failed to set cpu affinity"); + return 1; + } + + ftime(&before); + pread(fd, &old_tsc, sizeof(old_tsc), 0x10); + pread(fd, &old_aperf, sizeof(old_mperf), 0xe7); + pread(fd, &old_mperf, sizeof(old_aperf), 0xe8); + + for (i=0; i<0x8fffffff; i++) { + sqrt(i); + } + + ftime(&after); + pread(fd, &new_tsc, sizeof(new_tsc), 0x10); + pread(fd, &new_aperf, sizeof(new_mperf), 0xe7); + pread(fd, &new_mperf, sizeof(new_aperf), 0xe8); + + tsc = new_tsc-old_tsc; + aperf = new_aperf-old_aperf; + mperf = new_mperf-old_mperf; + + start = before.time*1000 + before.millitm; + finish = after.time*1000 + after.millitm; + total = finish - start; + + printf("runTime: %4.2f\n", 1.0*total/1000); + printf("freq: %7.0f\n", tsc / (1.0*aperf / (1.0 * mperf)) / total); + return 0; +} diff --git a/tools/testing/selftests/intel_pstate/msr.c b/tools/testing/selftests/intel_pstate/msr.c new file mode 100644 index 000000000000..abbbfc84d359 --- /dev/null +++ b/tools/testing/selftests/intel_pstate/msr.c @@ -0,0 +1,39 @@ +#include <math.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/timeb.h> +#include <sched.h> +#include <errno.h> + + +int main(int argc, char **argv) { + int cpu, fd; + long long msr; + char msr_file_name[64]; + + if (argc != 2) + return 1; + + errno = 0; + cpu = strtol(argv[1], (char **) NULL, 10); + + if (errno) + return 1; + + sprintf(msr_file_name, "/dev/cpu/%d/msr", cpu); + fd = open(msr_file_name, O_RDONLY); + + if (fd == -1) { + perror("Failed to open"); + return 1; + } + + pread(fd, &msr, sizeof(msr), 0x199); + + printf("msr 0x199: 0x%llx\n", msr); + return 0; +} diff --git a/tools/testing/selftests/intel_pstate/run.sh b/tools/testing/selftests/intel_pstate/run.sh new file mode 100755 index 000000000000..bdaf37e92684 --- /dev/null +++ b/tools/testing/selftests/intel_pstate/run.sh @@ -0,0 +1,113 @@ +#!/bin/bash +# +# This test runs on Intel x86 based hardware which support the intel_pstate +# driver. The test checks the frequency settings from the maximum turbo +# state to the minimum supported frequency, in decrements of 100MHz. The +# test runs the aperf.c program to put load on each processor. +# +# The results are displayed in a table which indicate the "Target" state, +# or the requested frequency in MHz, the Actual frequency, as read from +# /proc/cpuinfo, the difference between the Target and Actual frequencies, +# and the value of MSR 0x199 (MSR_IA32_PERF_CTL) which indicates what +# pstate the cpu is in, and the value of +# /sys/devices/system/cpu/intel_pstate/max_perf_pct X maximum turbo state +# +# Notes: In some cases several frequency values may be placed in the +# /tmp/result.X files. This is done on purpose in order to catch cases +# where the pstate driver may not be working at all. There is the case +# where, for example, several "similar" frequencies are in the file: +# +# +#/tmp/result.3100:1:cpu MHz : 2899.980 +#/tmp/result.3100:2:cpu MHz : 2900.000 +#/tmp/result.3100:3:msr 0x199: 0x1e00 +#/tmp/result.3100:4:max_perf_pct 94 +# +# and the test will error out in those cases. The result.X file can be checked +# for consistency and modified to remove the extra MHz values. The result.X +# files can be re-evaluated by setting EVALUATE_ONLY to 1 below. + +EVALUATE_ONLY=0 + +max_cpus=$(($(nproc)-1)) + +# compile programs +gcc -o aperf aperf.c -lm +[ $? -ne 0 ] && echo "Problem compiling aperf.c." && exit 1 +gcc -o msr msr.c -lm +[ $? -ne 0 ] && echo "Problem compiling msr.c." && exit 1 + +function run_test () { + + file_ext=$1 + for cpu in `seq 0 $max_cpus` + do + echo "launching aperf load on $cpu" + ./aperf $cpu & + done + + echo "sleeping for 5 seconds" + sleep 5 + num_freqs=$(cat /proc/cpuinfo | grep MHz | sort -u | wc -l) + if [ $num_freqs -le 2 ]; then + cat /proc/cpuinfo | grep MHz | sort -u | tail -1 > /tmp/result.$1 + else + cat /proc/cpuinfo | grep MHz | sort -u > /tmp/result.$1 + fi + ./msr 0 >> /tmp/result.$1 + + max_perf_pct=$(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct) + echo "max_perf_pct $max_perf_pct" >> /tmp/result.$1 + + for job in `jobs -p` + do + echo "waiting for job id $job" + wait $job + done +} + +# +# MAIN (ALL UNITS IN MHZ) +# + +# Get the marketing frequency +_mkt_freq=$(cat /proc/cpuinfo | grep -m 1 "model name" | awk '{print $NF}') +_mkt_freq=$(echo $_mkt_freq | tr -d [:alpha:][:punct:]) +mkt_freq=${_mkt_freq}0 + +# Get the ranges from cpupower +_min_freq=$(cpupower frequency-info -l | tail -1 | awk ' { print $1 } ') +min_freq=$(($_min_freq / 1000)) +_max_freq=$(cpupower frequency-info -l | tail -1 | awk ' { print $2 } ') +max_freq=$(($_max_freq / 1000)) + + +for freq in `seq $max_freq -100 $min_freq` +do + echo "Setting maximum frequency to $freq" + cpupower frequency-set -g powersave --max=${freq}MHz >& /dev/null + [ $EVALUATE_ONLY -eq 0 ] && run_test $freq +done + +echo "==============================================================================" + +echo "The marketing frequency of the cpu is $mkt_freq MHz" +echo "The maximum frequency of the cpu is $max_freq MHz" +echo "The minimum frequency of the cpu is $min_freq MHz" + +cpupower frequency-set -g powersave --max=${max_freq}MHz >& /dev/null + +# make a pretty table +echo "Target Actual Difference MSR(0x199) max_perf_pct" +for freq in `seq $max_freq -100 $min_freq` +do + result_freq=$(cat /tmp/result.${freq} | grep "cpu MHz" | awk ' { print $4 } ' | awk -F "." ' { print $1 } ') + msr=$(cat /tmp/result.${freq} | grep "msr" | awk ' { print $3 } ') + max_perf_pct=$(cat /tmp/result.${freq} | grep "max_perf_pct" | awk ' { print $2 } ' ) + if [ $result_freq -eq $freq ]; then + echo " $freq $result_freq 0 $msr $(($max_perf_pct*3300))" + else + echo " $freq $result_freq $(($result_freq-$freq)) $msr $(($max_perf_pct*$max_freq))" + fi +done +exit 0 diff --git a/tools/testing/selftests/powerpc/benchmarks/.gitignore b/tools/testing/selftests/powerpc/benchmarks/.gitignore index b4709ea588c1..6fa673316ac2 100644 --- a/tools/testing/selftests/powerpc/benchmarks/.gitignore +++ b/tools/testing/selftests/powerpc/benchmarks/.gitignore @@ -1 +1,2 @@ gettimeofday +context_switch diff --git a/tools/testing/selftests/powerpc/benchmarks/Makefile b/tools/testing/selftests/powerpc/benchmarks/Makefile index 5fa48702070d..912445ff7ce7 100644 --- a/tools/testing/selftests/powerpc/benchmarks/Makefile +++ b/tools/testing/selftests/powerpc/benchmarks/Makefile @@ -1,4 +1,4 @@ -TEST_PROGS := gettimeofday +TEST_PROGS := gettimeofday context_switch CFLAGS += -O2 @@ -6,6 +6,9 @@ all: $(TEST_PROGS) $(TEST_PROGS): ../harness.c +context_switch: ../utils.c +context_switch: LDLIBS += -lpthread + include ../../lib.mk clean: diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c new file mode 100644 index 000000000000..7b785941adec --- /dev/null +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -0,0 +1,466 @@ +/* + * Context switch microbenchmark. + * + * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define _GNU_SOURCE +#include <sched.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <getopt.h> +#include <signal.h> +#include <assert.h> +#include <pthread.h> +#include <limits.h> +#include <sys/time.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/shm.h> +#include <linux/futex.h> + +#include "../utils.h" + +static unsigned int timeout = 30; + +static int touch_vdso; +struct timeval tv; + +static int touch_fp = 1; +double fp; + +static int touch_vector = 1; +typedef int v4si __attribute__ ((vector_size (16))); +v4si a, b, c; + +#ifdef __powerpc__ +static int touch_altivec = 1; + +static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void) +{ + c = a + b; +} +#endif + +static void touch(void) +{ + if (touch_vdso) + gettimeofday(&tv, NULL); + + if (touch_fp) + fp += 0.1; + +#ifdef __powerpc__ + if (touch_altivec) + altivec_touch_fn(); +#endif + + if (touch_vector) + c = a + b; + + asm volatile("# %0 %1 %2": : "r"(&tv), "r"(&fp), "r"(&c)); +} + +static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu) +{ + pthread_t tid; + cpu_set_t cpuset; + pthread_attr_t attr; + + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + pthread_attr_init(&attr); + + if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset)) { + perror("pthread_attr_setaffinity_np"); + exit(1); + } + + if (pthread_create(&tid, &attr, fn, arg)) { + perror("pthread_create"); + exit(1); + } +} + +static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu) +{ + int pid; + cpu_set_t cpuset; + + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(1); + } + + if (pid) + return; + + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) { + perror("sched_setaffinity"); + exit(1); + } + + fn(arg); + + exit(0); +} + +static unsigned long iterations; +static unsigned long iterations_prev; + +static void sigalrm_handler(int junk) +{ + unsigned long i = iterations; + + printf("%ld\n", i - iterations_prev); + iterations_prev = i; + + if (--timeout == 0) + kill(0, SIGUSR1); + + alarm(1); +} + +static void sigusr1_handler(int junk) +{ + exit(0); +} + +struct actions { + void (*setup)(int, int); + void *(*thread1)(void *); + void *(*thread2)(void *); +}; + +#define READ 0 +#define WRITE 1 + +static int pipe_fd1[2]; +static int pipe_fd2[2]; + +static void pipe_setup(int cpu1, int cpu2) +{ + if (pipe(pipe_fd1) || pipe(pipe_fd2)) + exit(1); +} + +static void *pipe_thread1(void *arg) +{ + signal(SIGALRM, sigalrm_handler); + alarm(1); + + while (1) { + assert(read(pipe_fd1[READ], &c, 1) == 1); + touch(); + + assert(write(pipe_fd2[WRITE], &c, 1) == 1); + touch(); + + iterations += 2; + } + + return NULL; +} + +static void *pipe_thread2(void *arg) +{ + while (1) { + assert(write(pipe_fd1[WRITE], &c, 1) == 1); + touch(); + + assert(read(pipe_fd2[READ], &c, 1) == 1); + touch(); + } + + return NULL; +} + +static struct actions pipe_actions = { + .setup = pipe_setup, + .thread1 = pipe_thread1, + .thread2 = pipe_thread2, +}; + +static void yield_setup(int cpu1, int cpu2) +{ + if (cpu1 != cpu2) { + fprintf(stderr, "Both threads must be on the same CPU for yield test\n"); + exit(1); + } +} + +static void *yield_thread1(void *arg) +{ + signal(SIGALRM, sigalrm_handler); + alarm(1); + + while (1) { + sched_yield(); + touch(); + + iterations += 2; + } + + return NULL; +} + +static void *yield_thread2(void *arg) +{ + while (1) { + sched_yield(); + touch(); + } + + return NULL; +} + +static struct actions yield_actions = { + .setup = yield_setup, + .thread1 = yield_thread1, + .thread2 = yield_thread2, +}; + +static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, + void *addr2, int val3) +{ + return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); +} + +static unsigned long cmpxchg(unsigned long *p, unsigned long expected, + unsigned long desired) +{ + unsigned long exp = expected; + + __atomic_compare_exchange_n(p, &exp, desired, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return exp; +} + +static unsigned long xchg(unsigned long *p, unsigned long val) +{ + return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST); +} + +static int mutex_lock(unsigned long *m) +{ + int c; + + c = cmpxchg(m, 0, 1); + if (!c) + return 0; + + if (c == 1) + c = xchg(m, 2); + + while (c) { + sys_futex(m, FUTEX_WAIT, 2, NULL, NULL, 0); + c = xchg(m, 2); + } + + return 0; +} + +static int mutex_unlock(unsigned long *m) +{ + if (*m == 2) + *m = 0; + else if (xchg(m, 0) == 1) + return 0; + + sys_futex(m, FUTEX_WAKE, 1, NULL, NULL, 0); + + return 0; +} + +static unsigned long *m1, *m2; + +static void futex_setup(int cpu1, int cpu2) +{ + int shmid; + void *shmaddr; + + shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W); + if (shmid < 0) { + perror("shmget"); + exit(1); + } + + shmaddr = shmat(shmid, NULL, 0); + if (shmaddr == (char *)-1) { + perror("shmat"); + shmctl(shmid, IPC_RMID, NULL); + exit(1); + } + + shmctl(shmid, IPC_RMID, NULL); + + m1 = shmaddr; + m2 = shmaddr + sizeof(*m1); + + *m1 = 0; + *m2 = 0; + + mutex_lock(m1); + mutex_lock(m2); +} + +static void *futex_thread1(void *arg) +{ + signal(SIGALRM, sigalrm_handler); + alarm(1); + + while (1) { + mutex_lock(m2); + mutex_unlock(m1); + + iterations += 2; + } + + return NULL; +} + +static void *futex_thread2(void *arg) +{ + while (1) { + mutex_unlock(m2); + mutex_lock(m1); + } + + return NULL; +} + +static struct actions futex_actions = { + .setup = futex_setup, + .thread1 = futex_thread1, + .thread2 = futex_thread2, +}; + +static int processes; + +static struct option options[] = { + { "test", required_argument, 0, 't' }, + { "process", no_argument, &processes, 1 }, + { "timeout", required_argument, 0, 's' }, + { "vdso", no_argument, &touch_vdso, 1 }, + { "no-fp", no_argument, &touch_fp, 0 }, +#ifdef __powerpc__ + { "no-altivec", no_argument, &touch_altivec, 0 }, +#endif + { "no-vector", no_argument, &touch_vector, 0 }, + { 0, }, +}; + +static void usage(void) +{ + fprintf(stderr, "Usage: context_switch2 <options> CPU1 CPU2\n\n"); + fprintf(stderr, "\t\t--test=X\tpipe, futex or yield (default)\n"); + fprintf(stderr, "\t\t--process\tUse processes (default threads)\n"); + fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n"); + fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n"); + fprintf(stderr, "\t\t--fp\t\ttouch FP\n"); +#ifdef __powerpc__ + fprintf(stderr, "\t\t--altivec\ttouch altivec\n"); +#endif + fprintf(stderr, "\t\t--vector\ttouch vector\n"); +} + +int main(int argc, char *argv[]) +{ + signed char c; + struct actions *actions = &yield_actions; + int cpu1; + int cpu2; + static void (*start_fn)(void *(*fn)(void *), void *arg, unsigned long cpu); + + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "", options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 0: + if (options[option_index].flag != 0) + break; + + usage(); + exit(1); + break; + + case 't': + if (!strcmp(optarg, "pipe")) { + actions = &pipe_actions; + } else if (!strcmp(optarg, "yield")) { + actions = &yield_actions; + } else if (!strcmp(optarg, "futex")) { + actions = &futex_actions; + } else { + usage(); + exit(1); + } + break; + + case 's': + timeout = atoi(optarg); + break; + + default: + usage(); + exit(1); + } + } + + if (processes) + start_fn = start_process_on; + else + start_fn = start_thread_on; + + if (((argc - optind) != 2)) { + cpu1 = cpu2 = pick_online_cpu(); + } else { + cpu1 = atoi(argv[optind++]); + cpu2 = atoi(argv[optind++]); + } + + printf("Using %s with ", processes ? "processes" : "threads"); + + if (actions == &pipe_actions) + printf("pipe"); + else if (actions == &yield_actions) + printf("yield"); + else + printf("futex"); + + printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n", + cpu1, cpu2, touch_fp ? "yes" : "no", touch_altivec ? "yes" : "no", + touch_vector ? "yes" : "no", touch_vdso ? "yes" : "no"); + + /* Create a new process group so we can signal everyone for exit */ + setpgid(getpid(), getpid()); + + signal(SIGUSR1, sigusr1_handler); + + actions->setup(cpu1, cpu2); + + start_fn(actions->thread1, NULL, cpu1); + start_fn(actions->thread2, NULL, cpu2); + + while (1) + sleep(3600); + + return 0; +} diff --git a/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c b/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c index 8265504de571..08a8b95e3bc1 100644 --- a/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c +++ b/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c @@ -60,14 +60,6 @@ int dscr_inherit_exec(void) else set_dscr(dscr); - /* - * XXX: Force a context switch out so that DSCR - * current value is copied into the thread struct - * which is required for the child to inherit the - * changed value. - */ - sleep(1); - pid = fork(); if (pid == -1) { perror("fork() failed"); diff --git a/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c b/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c index 4e414caf7f40..3e5a6d195e9a 100644 --- a/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c +++ b/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c @@ -40,14 +40,6 @@ int dscr_inherit(void) else set_dscr(dscr); - /* - * XXX: Force a context switch out so that DSCR - * current value is copied into the thread struct - * which is required for the child to inherit the - * changed value. - */ - sleep(1); - pid = fork(); if (pid == -1) { perror("fork() failed"); diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c index f7997affd143..52f9be7f61f0 100644 --- a/tools/testing/selftests/powerpc/harness.c +++ b/tools/testing/selftests/powerpc/harness.c @@ -116,46 +116,3 @@ int test_harness(int (test_function)(void), char *name) return rc; } - -static char auxv[4096]; - -void *get_auxv_entry(int type) -{ - ElfW(auxv_t) *p; - void *result; - ssize_t num; - int fd; - - fd = open("/proc/self/auxv", O_RDONLY); - if (fd == -1) { - perror("open"); - return NULL; - } - - result = NULL; - - num = read(fd, auxv, sizeof(auxv)); - if (num < 0) { - perror("read"); - goto out; - } - - if (num > sizeof(auxv)) { - printf("Overflowed auxv buffer\n"); - goto out; - } - - p = (ElfW(auxv_t) *)auxv; - - while (p->a_type != AT_NULL) { - if (p->a_type == type) { - result = (void *)p->a_un.a_val; - break; - } - - p++; - } -out: - close(fd); - return result; -} diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile index a9099d9f8f39..ac41a7177f2e 100644 --- a/tools/testing/selftests/powerpc/pmu/Makefile +++ b/tools/testing/selftests/powerpc/pmu/Makefile @@ -2,7 +2,7 @@ noarg: $(MAKE) -C ../ TEST_PROGS := count_instructions l3_bank_test per_event_excludes -EXTRA_SOURCES := ../harness.c event.c lib.c +EXTRA_SOURCES := ../harness.c event.c lib.c ../utils.c all: $(TEST_PROGS) ebb @@ -12,6 +12,8 @@ $(TEST_PROGS): $(EXTRA_SOURCES) count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES) $(CC) $(CFLAGS) -m64 -o $@ $^ +per_event_excludes: ../utils.c + include ../../lib.mk DEFAULT_RUN_TESTS := $(RUN_TESTS) diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile index 5cdc9dbf2b27..8d2279c4bb4b 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/Makefile +++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile @@ -18,7 +18,8 @@ TEST_PROGS := reg_access_test event_attributes_test cycles_test \ all: $(TEST_PROGS) -$(TEST_PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c busy_loop.S +$(TEST_PROGS): ../../harness.c ../../utils.c ../event.c ../lib.c \ + ebb.c ebb_handler.S trace.c busy_loop.S instruction_count_test: ../loop.S diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c index 9729d9f90218..e67452f1bcff 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c @@ -13,7 +13,6 @@ #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> -#include <linux/auxvec.h> #include "trace.h" #include "reg.h" @@ -324,7 +323,7 @@ bool ebb_is_supported(void) { #ifdef PPC_FEATURE2_EBB /* EBB requires at least POWER8 */ - return ((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_EBB); + return have_hwcap2(PPC_FEATURE2_EBB); #else return false; #endif diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c index a07104c2afe6..a361ad3334ce 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.c +++ b/tools/testing/selftests/powerpc/pmu/lib.c @@ -15,32 +15,6 @@ #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; diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h index ca5d72ae3be6..0213af4ff332 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.h +++ b/tools/testing/selftests/powerpc/pmu/lib.h @@ -19,7 +19,6 @@ union pipe { int fds[2]; }; -extern int pick_online_cpu(void); extern int bind_to_cpu(int cpu); extern int kill_child_and_wait(pid_t child_pid); extern int wait_for_child(pid_t child_pid); diff --git a/tools/testing/selftests/powerpc/scripts/hmi.sh b/tools/testing/selftests/powerpc/scripts/hmi.sh new file mode 100755 index 000000000000..83fb253ae3bd --- /dev/null +++ b/tools/testing/selftests/powerpc/scripts/hmi.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# +# Copyright 2015, Daniel Axtens, IBM Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + + +# do we have ./getscom, ./putscom? +if [ -x ./getscom ] && [ -x ./putscom ]; then + GETSCOM=./getscom + PUTSCOM=./putscom +elif which getscom > /dev/null; then + GETSCOM=$(which getscom) + PUTSCOM=$(which putscom) +else + cat <<EOF +Can't find getscom/putscom in . or \$PATH. +See https://github.com/open-power/skiboot. +The tool is in external/xscom-utils +EOF + exit 1 +fi + +# We will get 8 HMI events per injection +# todo: deal with things being offline +expected_hmis=8 +COUNT_HMIS() { + dmesg | grep -c 'Harmless Hypervisor Maintenance interrupt' +} + +# massively expand snooze delay, allowing injection on all cores +ppc64_cpu --smt-snooze-delay=1000000000 + +# when we exit, restore it +trap "ppc64_cpu --smt-snooze-delay=100" 0 1 + +# for each chip+core combination +# todo - less fragile parsing +egrep -o 'OCC: Chip [0-9a-f]+ Core [0-9a-f]' < /sys/firmware/opal/msglog | +while read chipcore; do + chip=$(echo "$chipcore"|awk '{print $3}') + core=$(echo "$chipcore"|awk '{print $5}') + fir="0x1${core}013100" + + # verify that Core FIR is zero as expected + if [ "$($GETSCOM -c 0x${chip} $fir)" != 0 ]; then + echo "FIR was not zero before injection for chip $chip, core $core. Aborting!" + echo "Result of $GETSCOM -c 0x${chip} $fir:" + $GETSCOM -c 0x${chip} $fir + echo "If you get a -5 error, the core may be in idle state. Try stress-ng." + echo "Otherwise, try $PUTSCOM -c 0x${chip} $fir 0" + exit 1 + fi + + # keep track of the number of HMIs handled + old_hmis=$(COUNT_HMIS) + + # do injection, adding a marker to dmesg for clarity + echo "Injecting HMI on core $core, chip $chip" | tee /dev/kmsg + # inject a RegFile recoverable error + if ! $PUTSCOM -c 0x${chip} $fir 2000000000000000 > /dev/null; then + echo "Error injecting. Aborting!" + exit 1 + fi + + # now we want to wait for all the HMIs to be processed + # we expect one per thread on the core + i=0; + new_hmis=$(COUNT_HMIS) + while [ $new_hmis -lt $((old_hmis + expected_hmis)) ] && [ $i -lt 12 ]; do + echo "Seen $((new_hmis - old_hmis)) HMI(s) out of $expected_hmis expected, sleeping" + sleep 5; + i=$((i + 1)) + new_hmis=$(COUNT_HMIS) + done + if [ $i = 12 ]; then + echo "Haven't seen expected $expected_hmis recoveries after 1 min. Aborting." + exit 1 + fi + echo "Processed $expected_hmis events; presumed success. Check dmesg." + echo "" +done diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore index 2699635d2cd9..7d0f14b8cb2e 100644 --- a/tools/testing/selftests/powerpc/tm/.gitignore +++ b/tools/testing/selftests/powerpc/tm/.gitignore @@ -1,2 +1,5 @@ tm-resched-dscr tm-syscall +tm-signal-msr-resv +tm-signal-stack +tm-vmxcopy diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index 4bea62a319dc..737f72c964e6 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -1,8 +1,8 @@ -TEST_PROGS := tm-resched-dscr tm-syscall +TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack tm-vmxcopy all: $(TEST_PROGS) -$(TEST_PROGS): ../harness.c +$(TEST_PROGS): ../harness.c ../utils.c tm-syscall: tm-syscall-asm.S tm-syscall: CFLAGS += -mhtm -I../../../../../usr/include diff --git a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c index 42d4c8caad81..8fde93d6021f 100644 --- a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c +++ b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c @@ -29,6 +29,7 @@ #include <asm/tm.h> #include "utils.h" +#include "tm.h" #define TBEGIN ".long 0x7C00051D ;" #define TEND ".long 0x7C00055D ;" @@ -42,6 +43,8 @@ int test_body(void) { uint64_t rv, dscr1 = 1, dscr2, texasr; + SKIP_IF(!have_htm()); + printf("Check DSCR TM context switch: "); fflush(stdout); for (;;) { diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c b/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c new file mode 100644 index 000000000000..d86653f282b1 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c @@ -0,0 +1,74 @@ +/* + * Copyright 2015, Michael Neuling, IBM Corp. + * Licensed under GPLv2. + * + * Test the kernel's signal return code to ensure that it doesn't + * crash when both the transactional and suspend MSR bits are set in + * the signal context. + * + * For this test, we send ourselves a SIGUSR1. In the SIGUSR1 handler + * we modify the signal context to set both MSR TM S and T bits (which + * is "reserved" by the PowerISA). When we return from the signal + * handler (implicit sigreturn), the kernel should detect reserved MSR + * value and send us with a SIGSEGV. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <unistd.h> + +#include "utils.h" +#include "tm.h" + +int segv_expected = 0; + +void signal_segv(int signum) +{ + if (segv_expected && (signum == SIGSEGV)) + _exit(0); + _exit(1); +} + +void signal_usr1(int signum, siginfo_t *info, void *uc) +{ + ucontext_t *ucp = uc; + + /* Link tm checkpointed context to normal context */ + ucp->uc_link = ucp; + /* Set all TM bits so that the context is now invalid */ +#ifdef __powerpc64__ + ucp->uc_mcontext.gp_regs[PT_MSR] |= (7ULL << 32); +#else + ucp->uc_mcontext.regs->gpr[PT_MSR] |= (7ULL); +#endif + /* Should segv on return becuase of invalid context */ + segv_expected = 1; +} + +int tm_signal_msr_resv() +{ + struct sigaction act; + + SKIP_IF(!have_htm()); + + act.sa_sigaction = signal_usr1; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGUSR1, &act, NULL) < 0) { + perror("sigaction sigusr1"); + exit(1); + } + if (signal(SIGSEGV, signal_segv) == SIG_ERR) + exit(1); + + raise(SIGUSR1); + + /* We shouldn't get here as we exit in the segv handler */ + return 1; +} + +int main(void) +{ + return test_harness(tm_signal_msr_resv, "tm_signal_msr_resv"); +} diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-stack.c b/tools/testing/selftests/powerpc/tm/tm-signal-stack.c new file mode 100644 index 000000000000..e44a238c1d77 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-signal-stack.c @@ -0,0 +1,76 @@ +/* + * Copyright 2015, Michael Neuling, IBM Corp. + * Licensed under GPLv2. + * + * Test the kernel's signal delievery code to ensure that we don't + * trelaim twice in the kernel signal delivery code. This can happen + * if we trigger a signal when in a transaction and the stack pointer + * is bogus. + * + * This test case registers a SEGV handler, sets the stack pointer + * (r1) to NULL, starts a transaction and then generates a SEGV. The + * SEGV should be handled but we exit here as the stack pointer is + * invalid and hance we can't sigreturn. We only need to check that + * this flow doesn't crash the kernel. + */ + +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> + +#include "utils.h" +#include "tm.h" + +void signal_segv(int signum) +{ + /* This should never actually run since stack is foobar */ + exit(1); +} + +int tm_signal_stack() +{ + int pid; + + SKIP_IF(!have_htm()); + + pid = fork(); + if (pid < 0) + exit(1); + + if (pid) { /* Parent */ + /* + * It's likely the whole machine will crash here so if + * the child ever exits, we are good. + */ + wait(NULL); + return 0; + } + + /* + * The flow here is: + * 1) register a signal handler (so signal delievery occurs) + * 2) make stack pointer (r1) = NULL + * 3) start transaction + * 4) cause segv + */ + if (signal(SIGSEGV, signal_segv) == SIG_ERR) + exit(1); + asm volatile("li 1, 0 ;" /* stack ptr == NULL */ + "1:" + ".long 0x7C00051D ;" /* tbegin */ + "beq 1b ;" /* retry forever */ + ".long 0x7C0005DD ; ;" /* tsuspend */ + "ld 2, 0(1) ;" /* trigger segv" */ + : : : "memory"); + + /* This should never get here due to above segv */ + return 1; +} + +int main(void) +{ + return test_harness(tm_signal_stack, "tm_signal_stack"); +} diff --git a/tools/testing/selftests/powerpc/tm/tm-syscall.c b/tools/testing/selftests/powerpc/tm/tm-syscall.c index e835bf7ec7ae..60560cb20e38 100644 --- a/tools/testing/selftests/powerpc/tm/tm-syscall.c +++ b/tools/testing/selftests/powerpc/tm/tm-syscall.c @@ -13,12 +13,11 @@ #include <unistd.h> #include <sys/syscall.h> #include <asm/tm.h> -#include <asm/cputable.h> -#include <linux/auxvec.h> #include <sys/time.h> #include <stdlib.h> #include "utils.h" +#include "tm.h" extern int getppid_tm_active(void); extern int getppid_tm_suspended(void); @@ -77,16 +76,6 @@ pid_t getppid_tm(bool suspend) exit(-1); } -static inline bool have_htm_nosc(void) -{ -#ifdef PPC_FEATURE2_HTM_NOSC - return ((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_HTM_NOSC); -#else - printf("PPC_FEATURE2_HTM_NOSC not defined, can't check AT_HWCAP2\n"); - return false; -#endif -} - int tm_syscall(void) { unsigned count = 0; diff --git a/tools/testing/selftests/powerpc/tm/tm-vmxcopy.c b/tools/testing/selftests/powerpc/tm/tm-vmxcopy.c new file mode 100644 index 000000000000..0274de7b11f3 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-vmxcopy.c @@ -0,0 +1,103 @@ +/* + * Copyright 2015, Michael Neuling, IBM Corp. + * Licensed under GPLv2. + * + * Original: Michael Neuling 4/12/2013 + * Edited: Rashmica Gupta 4/12/2015 + * + * See if the altivec state is leaked out of an aborted transaction due to + * kernel vmx copy loops. + * + * When the transaction aborts, VSR values should rollback to the values + * they held before the transaction commenced. Using VSRs while transaction + * is suspended should not affect the checkpointed values. + * + * (1) write A to a VSR + * (2) start transaction + * (3) suspend transaction + * (4) change the VSR to B + * (5) trigger kernel vmx copy loop + * (6) abort transaction + * (7) check that the VSR value is A + */ + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> +#include <string.h> +#include <assert.h> + +#include "tm.h" +#include "utils.h" + +int test_vmxcopy() +{ + long double vecin = 1.3; + long double vecout; + unsigned long pgsize = getpagesize(); + int i; + int fd; + int size = pgsize*16; + char tmpfile[] = "/tmp/page_faultXXXXXX"; + char buf[pgsize]; + char *a; + uint64_t aborted = 0; + + SKIP_IF(!have_htm()); + + fd = mkstemp(tmpfile); + assert(fd >= 0); + + memset(buf, 0, pgsize); + for (i = 0; i < size; i += pgsize) + assert(write(fd, buf, pgsize) == pgsize); + + unlink(tmpfile); + + a = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + assert(a != MAP_FAILED); + + asm __volatile__( + "lxvd2x 40,0,%[vecinptr];" /* set 40 to initial value*/ + "tbegin.;" + "beq 3f;" + "tsuspend.;" + "xxlxor 40,40,40;" /* set 40 to 0 */ + "std 5, 0(%[map]);" /* cause kernel vmx copy page */ + "tabort. 0;" + "tresume.;" + "tend.;" + "li %[res], 0;" + "b 5f;" + + /* Abort handler */ + "3:;" + "li %[res], 1;" + + "5:;" + "stxvd2x 40,0,%[vecoutptr];" + : [res]"=r"(aborted) + : [vecinptr]"r"(&vecin), + [vecoutptr]"r"(&vecout), + [map]"r"(a) + : "memory", "r0", "r3", "r4", "r5", "r6", "r7"); + + if (aborted && (vecin != vecout)){ + printf("FAILED: vector state leaked on abort %f != %f\n", + (double)vecin, (double)vecout); + return 1; + } + + munmap(a, size); + + close(fd); + + return 0; +} + +int main(void) +{ + return test_harness(test_vmxcopy, "tm_vmxcopy"); +} diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h new file mode 100644 index 000000000000..24144b25772c --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#ifndef _SELFTESTS_POWERPC_TM_TM_H +#define _SELFTESTS_POWERPC_TM_TM_H + +#include <stdbool.h> +#include <asm/cputable.h> + +#include "../utils.h" + +static inline bool have_htm(void) +{ +#ifdef PPC_FEATURE2_HTM + return have_hwcap2(PPC_FEATURE2_HTM); +#else + printf("PPC_FEATURE2_HTM not defined, can't check AT_HWCAP2\n"); + return false; +#endif +} + +static inline bool have_htm_nosc(void) +{ +#ifdef PPC_FEATURE2_HTM_NOSC + return have_hwcap2(PPC_FEATURE2_HTM_NOSC); +#else + printf("PPC_FEATURE2_HTM_NOSC not defined, can't check AT_HWCAP2\n"); + return false; +#endif +} + +#endif /* _SELFTESTS_POWERPC_TM_TM_H */ diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c new file mode 100644 index 000000000000..dcf74184bfd0 --- /dev/null +++ b/tools/testing/selftests/powerpc/utils.c @@ -0,0 +1,87 @@ +/* + * Copyright 2013-2015, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#define _GNU_SOURCE /* For CPU_ZERO etc. */ + +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <link.h> +#include <sched.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "utils.h" + +static char auxv[4096]; + +void *get_auxv_entry(int type) +{ + ElfW(auxv_t) *p; + void *result; + ssize_t num; + int fd; + + fd = open("/proc/self/auxv", O_RDONLY); + if (fd == -1) { + perror("open"); + return NULL; + } + + result = NULL; + + num = read(fd, auxv, sizeof(auxv)); + if (num < 0) { + perror("read"); + goto out; + } + + if (num > sizeof(auxv)) { + printf("Overflowed auxv buffer\n"); + goto out; + } + + p = (ElfW(auxv_t) *)auxv; + + while (p->a_type != AT_NULL) { + if (p->a_type == type) { + result = (void *)p->a_un.a_val; + break; + } + + p++; + } +out: + close(fd); + return result; +} + +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; +} diff --git a/tools/testing/selftests/powerpc/utils.h b/tools/testing/selftests/powerpc/utils.h index b7d41086bb0a..175ac6ad10dd 100644 --- a/tools/testing/selftests/powerpc/utils.h +++ b/tools/testing/selftests/powerpc/utils.h @@ -8,6 +8,7 @@ #include <stdint.h> #include <stdbool.h> +#include <linux/auxvec.h> /* Avoid headaches with PRI?64 - just use %ll? always */ typedef unsigned long long u64; @@ -21,6 +22,12 @@ typedef uint8_t u8; int test_harness(int (test_function)(void), char *name); extern void *get_auxv_entry(int type); +int pick_online_cpu(void); + +static inline bool have_hwcap2(unsigned long ftr2) +{ + return ((unsigned long)get_auxv_entry(AT_HWCAP2) & ftr2) == ftr2; +} /* Yes, this is evil */ #define FAIL_IF(x) \ diff --git a/tools/testing/selftests/ptrace/.gitignore b/tools/testing/selftests/ptrace/.gitignore new file mode 100644 index 000000000000..b3e59d41fd82 --- /dev/null +++ b/tools/testing/selftests/ptrace/.gitignore @@ -0,0 +1 @@ +peeksiginfo diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h index fb2841601f2f..a786c69c7584 100644 --- a/tools/testing/selftests/seccomp/test_harness.h +++ b/tools/testing/selftests/seccomp/test_harness.h @@ -42,6 +42,7 @@ #define TEST_HARNESS_H_ #define _GNU_SOURCE +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -370,8 +371,8 @@ __typeof__(_expected) __exp = (_expected); \ __typeof__(_seen) __seen = (_seen); \ if (!(__exp _t __seen)) { \ - unsigned long long __exp_print = (unsigned long long)__exp; \ - unsigned long long __seen_print = (unsigned long long)__seen; \ + unsigned long long __exp_print = (uintptr_t)__exp; \ + unsigned long long __seen_print = (uintptr_t)__seen; \ __TH_LOG("Expected %s (%llu) %s %s (%llu)", \ #_expected, __exp_print, #_t, \ #_seen, __seen_print); \ diff --git a/tools/testing/selftests/timers/.gitignore b/tools/testing/selftests/timers/.gitignore index ced998151bc4..68f3fc71ac44 100644 --- a/tools/testing/selftests/timers/.gitignore +++ b/tools/testing/selftests/timers/.gitignore @@ -16,3 +16,4 @@ set-timer-lat skew_consistency threadtest valid-adjtimex +adjtick diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore index ff1bb16cec4f..a937a9d26b60 100644 --- a/tools/testing/selftests/vm/.gitignore +++ b/tools/testing/selftests/vm/.gitignore @@ -2,3 +2,8 @@ hugepage-mmap hugepage-shm map_hugetlb thuge-gen +compaction_test +mlock2-tests +on-fault-limit +transhuge-stress +userfaultfd diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index eabcff411984..d0c473f65850 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -4,9 +4,11 @@ include ../lib.mk .PHONY: all all_32 all_64 warn_32bit_failure clean -TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs ldt_gdt syscall_nt ptrace_syscall +TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn test_syscall_vdso unwind_vdso \ - test_FCMOV test_FCOMI test_FISTTP + test_FCMOV test_FCOMI test_FISTTP \ + ldt_gdt \ + vdso_restorer TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) diff --git a/tools/testing/selftests/x86/vdso_restorer.c b/tools/testing/selftests/x86/vdso_restorer.c new file mode 100644 index 000000000000..cb038424a403 --- /dev/null +++ b/tools/testing/selftests/x86/vdso_restorer.c @@ -0,0 +1,88 @@ +/* + * vdso_restorer.c - tests vDSO-based signal restore + * Copyright (c) 2015 Andrew Lutomirski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * This makes sure that sa_restorer == NULL keeps working on 32-bit + * configurations. Modern glibc doesn't use it under any circumstances, + * so it's easy to overlook breakage. + * + * 64-bit userspace has never supported sa_restorer == NULL, so this is + * 32-bit only. + */ + +#define _GNU_SOURCE + +#include <err.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <syscall.h> +#include <sys/syscall.h> + +/* Open-code this -- the headers are too messy to easily use them. */ +struct real_sigaction { + void *handler; + unsigned long flags; + void *restorer; + unsigned int mask[2]; +}; + +static volatile sig_atomic_t handler_called; + +static void handler_with_siginfo(int sig, siginfo_t *info, void *ctx_void) +{ + handler_called = 1; +} + +static void handler_without_siginfo(int sig) +{ + handler_called = 1; +} + +int main() +{ + int nerrs = 0; + struct real_sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.handler = handler_with_siginfo; + sa.flags = SA_SIGINFO; + sa.restorer = NULL; /* request kernel-provided restorer */ + + if (syscall(SYS_rt_sigaction, SIGUSR1, &sa, NULL, 8) != 0) + err(1, "raw rt_sigaction syscall"); + + raise(SIGUSR1); + + if (handler_called) { + printf("[OK]\tSA_SIGINFO handler returned successfully\n"); + } else { + printf("[FAIL]\tSA_SIGINFO handler was not called\n"); + nerrs++; + } + + sa.flags = 0; + sa.handler = handler_without_siginfo; + if (syscall(SYS_sigaction, SIGUSR1, &sa, 0) != 0) + err(1, "raw sigaction syscall"); + handler_called = 0; + + raise(SIGUSR1); + + if (handler_called) { + printf("[OK]\t!SA_SIGINFO handler returned successfully\n"); + } else { + printf("[FAIL]\t!SA_SIGINFO handler was not called\n"); + nerrs++; + } +} |