diff options
Diffstat (limited to 'tools')
23 files changed, 1347 insertions, 28 deletions
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 80159e6811c2..d9836c5eb694 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c | |||
@@ -3351,12 +3351,18 @@ int main(int argc, char *argv[]) | |||
3351 | /* Boot protocol version: 2.07 supports the fields for lguest. */ | 3351 | /* Boot protocol version: 2.07 supports the fields for lguest. */ |
3352 | boot->hdr.version = 0x207; | 3352 | boot->hdr.version = 0x207; |
3353 | 3353 | ||
3354 | /* The hardware_subarch value of "1" tells the Guest it's an lguest. */ | 3354 | /* X86_SUBARCH_LGUEST tells the Guest it's an lguest. */ |
3355 | boot->hdr.hardware_subarch = 1; | 3355 | boot->hdr.hardware_subarch = X86_SUBARCH_LGUEST; |
3356 | 3356 | ||
3357 | /* Tell the entry path not to try to reload segment registers. */ | 3357 | /* Tell the entry path not to try to reload segment registers. */ |
3358 | boot->hdr.loadflags |= KEEP_SEGMENTS; | 3358 | boot->hdr.loadflags |= KEEP_SEGMENTS; |
3359 | 3359 | ||
3360 | /* We don't support tboot: */ | ||
3361 | boot->tboot_addr = 0; | ||
3362 | |||
3363 | /* Ensure this is 0 to prevent APM from loading: */ | ||
3364 | boot->apm_bios_info.version = 0; | ||
3365 | |||
3360 | /* We tell the kernel to initialize the Guest. */ | 3366 | /* We tell the kernel to initialize the Guest. */ |
3361 | tell_kernel(start); | 3367 | tell_kernel(start); |
3362 | 3368 | ||
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 0144b3d1bb77..88cccea3ca99 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c | |||
@@ -1164,11 +1164,11 @@ process_filter(struct event_format *event, struct filter_arg **parg, | |||
1164 | current_op = current_exp; | 1164 | current_op = current_exp; |
1165 | 1165 | ||
1166 | ret = collapse_tree(current_op, parg, error_str); | 1166 | ret = collapse_tree(current_op, parg, error_str); |
1167 | /* collapse_tree() may free current_op, and updates parg accordingly */ | ||
1168 | current_op = NULL; | ||
1167 | if (ret < 0) | 1169 | if (ret < 0) |
1168 | goto fail; | 1170 | goto fail; |
1169 | 1171 | ||
1170 | *parg = current_op; | ||
1171 | |||
1172 | free(token); | 1172 | free(token); |
1173 | return 0; | 1173 | return 0; |
1174 | 1174 | ||
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index efdd23221c16..ee7ada78d86f 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -537,6 +537,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
537 | perf_evlist__set_leader(evsel_list); | 537 | perf_evlist__set_leader(evsel_list); |
538 | 538 | ||
539 | evlist__for_each(evsel_list, counter) { | 539 | evlist__for_each(evsel_list, counter) { |
540 | try_again: | ||
540 | if (create_perf_stat_counter(counter) < 0) { | 541 | if (create_perf_stat_counter(counter) < 0) { |
541 | /* | 542 | /* |
542 | * PPC returns ENXIO for HW counters until 2.6.37 | 543 | * PPC returns ENXIO for HW counters until 2.6.37 |
@@ -553,7 +554,11 @@ static int __run_perf_stat(int argc, const char **argv) | |||
553 | if ((counter->leader != counter) || | 554 | if ((counter->leader != counter) || |
554 | !(counter->leader->nr_members > 1)) | 555 | !(counter->leader->nr_members > 1)) |
555 | continue; | 556 | continue; |
556 | } | 557 | } else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { |
558 | if (verbose) | ||
559 | ui__warning("%s\n", msg); | ||
560 | goto try_again; | ||
561 | } | ||
557 | 562 | ||
558 | perf_evsel__open_strerror(counter, &target, | 563 | perf_evsel__open_strerror(counter, &target, |
559 | errno, msg, sizeof(msg)); | 564 | errno, msg, sizeof(msg)); |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index a23f54793e51..964c7c3602c0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -2274,6 +2274,8 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | |||
2274 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | 2274 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, |
2275 | char *msg, size_t msgsize) | 2275 | char *msg, size_t msgsize) |
2276 | { | 2276 | { |
2277 | int paranoid; | ||
2278 | |||
2277 | if ((err == ENOENT || err == ENXIO || err == ENODEV) && | 2279 | if ((err == ENOENT || err == ENXIO || err == ENODEV) && |
2278 | evsel->attr.type == PERF_TYPE_HARDWARE && | 2280 | evsel->attr.type == PERF_TYPE_HARDWARE && |
2279 | evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) { | 2281 | evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) { |
@@ -2293,6 +2295,22 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | |||
2293 | 2295 | ||
2294 | zfree(&evsel->name); | 2296 | zfree(&evsel->name); |
2295 | return true; | 2297 | return true; |
2298 | } else if (err == EACCES && !evsel->attr.exclude_kernel && | ||
2299 | (paranoid = perf_event_paranoid()) > 1) { | ||
2300 | const char *name = perf_evsel__name(evsel); | ||
2301 | char *new_name; | ||
2302 | |||
2303 | if (asprintf(&new_name, "%s%su", name, strchr(name, ':') ? "" : ":") < 0) | ||
2304 | return false; | ||
2305 | |||
2306 | if (evsel->name) | ||
2307 | free(evsel->name); | ||
2308 | evsel->name = new_name; | ||
2309 | scnprintf(msg, msgsize, | ||
2310 | "kernel.perf_event_paranoid=%d, trying to fall back to excluding kernel samples", paranoid); | ||
2311 | evsel->attr.exclude_kernel = 1; | ||
2312 | |||
2313 | return true; | ||
2296 | } | 2314 | } |
2297 | 2315 | ||
2298 | return false; | 2316 | return false; |
@@ -2311,12 +2329,13 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | |||
2311 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n" | 2329 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n" |
2312 | "which controls use of the performance events system by\n" | 2330 | "which controls use of the performance events system by\n" |
2313 | "unprivileged users (without CAP_SYS_ADMIN).\n\n" | 2331 | "unprivileged users (without CAP_SYS_ADMIN).\n\n" |
2314 | "The default value is 1:\n\n" | 2332 | "The current value is %d:\n\n" |
2315 | " -1: Allow use of (almost) all events by all users\n" | 2333 | " -1: Allow use of (almost) all events by all users\n" |
2316 | ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n" | 2334 | ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n" |
2317 | ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n" | 2335 | ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n" |
2318 | ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN", | 2336 | ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN", |
2319 | target->system_wide ? "system-wide " : ""); | 2337 | target->system_wide ? "system-wide " : "", |
2338 | perf_event_paranoid()); | ||
2320 | case ENOENT: | 2339 | case ENOENT: |
2321 | return scnprintf(msg, size, "The %s event is not supported.", | 2340 | return scnprintf(msg, size, "The %s event is not supported.", |
2322 | perf_evsel__name(evsel)); | 2341 | perf_evsel__name(evsel)); |
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index b04afc3295df..ff9e5f20a5a7 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
@@ -19,6 +19,7 @@ TARGETS += powerpc | |||
19 | TARGETS += pstore | 19 | TARGETS += pstore |
20 | TARGETS += ptrace | 20 | TARGETS += ptrace |
21 | TARGETS += seccomp | 21 | TARGETS += seccomp |
22 | TARGETS += sigaltstack | ||
22 | TARGETS += size | 23 | TARGETS += size |
23 | TARGETS += static_keys | 24 | TARGETS += static_keys |
24 | TARGETS += sysctl | 25 | TARGETS += sysctl |
diff --git a/tools/testing/selftests/rcutorture/bin/jitter.sh b/tools/testing/selftests/rcutorture/bin/jitter.sh new file mode 100755 index 000000000000..3633828375e3 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/jitter.sh | |||
@@ -0,0 +1,90 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # Alternate sleeping and spinning on randomly selected CPUs. The purpose | ||
4 | # of this script is to inflict random OS jitter on a concurrently running | ||
5 | # test. | ||
6 | # | ||
7 | # Usage: jitter.sh me duration [ sleepmax [ spinmax ] ] | ||
8 | # | ||
9 | # me: Random-number-generator seed salt. | ||
10 | # duration: Time to run in seconds. | ||
11 | # sleepmax: Maximum microseconds to sleep, defaults to one second. | ||
12 | # spinmax: Maximum microseconds to spin, defaults to one millisecond. | ||
13 | # | ||
14 | # This program is free software; you can redistribute it and/or modify | ||
15 | # it under the terms of the GNU General Public License as published by | ||
16 | # the Free Software Foundation; either version 2 of the License, or | ||
17 | # (at your option) any later version. | ||
18 | # | ||
19 | # This program is distributed in the hope that it will be useful, | ||
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | # GNU General Public License for more details. | ||
23 | # | ||
24 | # You should have received a copy of the GNU General Public License | ||
25 | # along with this program; if not, you can access it online at | ||
26 | # http://www.gnu.org/licenses/gpl-2.0.html. | ||
27 | # | ||
28 | # Copyright (C) IBM Corporation, 2016 | ||
29 | # | ||
30 | # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> | ||
31 | |||
32 | me=$(($1 * 1000)) | ||
33 | duration=$2 | ||
34 | sleepmax=${3-1000000} | ||
35 | spinmax=${4-1000} | ||
36 | |||
37 | n=1 | ||
38 | |||
39 | starttime=`awk 'BEGIN { print systime(); }' < /dev/null` | ||
40 | |||
41 | while : | ||
42 | do | ||
43 | # Check for done. | ||
44 | t=`awk -v s=$starttime 'BEGIN { print systime() - s; }' < /dev/null` | ||
45 | if test "$t" -gt "$duration" | ||
46 | then | ||
47 | exit 0; | ||
48 | fi | ||
49 | |||
50 | # Set affinity to randomly selected CPU | ||
51 | cpus=`ls /sys/devices/system/cpu/*/online | | ||
52 | sed -e 's,/[^/]*$,,' -e 's/^[^0-9]*//' | | ||
53 | grep -v '^0*$'` | ||
54 | cpumask=`awk -v cpus="$cpus" -v me=$me -v n=$n 'BEGIN { | ||
55 | srand(n + me + systime()); | ||
56 | ncpus = split(cpus, ca); | ||
57 | curcpu = ca[int(rand() * ncpus + 1)]; | ||
58 | mask = lshift(1, curcpu); | ||
59 | if (mask + 0 <= 0) | ||
60 | mask = 1; | ||
61 | printf("%#x\n", mask); | ||
62 | }' < /dev/null` | ||
63 | n=$(($n+1)) | ||
64 | if ! taskset -p $cpumask $$ > /dev/null 2>&1 | ||
65 | then | ||
66 | echo taskset failure: '"taskset -p ' $cpumask $$ '"' | ||
67 | exit 1 | ||
68 | fi | ||
69 | |||
70 | # Sleep a random duration | ||
71 | sleeptime=`awk -v me=$me -v n=$n -v sleepmax=$sleepmax 'BEGIN { | ||
72 | srand(n + me + systime()); | ||
73 | printf("%06d", int(rand() * sleepmax)); | ||
74 | }' < /dev/null` | ||
75 | n=$(($n+1)) | ||
76 | sleep .$sleeptime | ||
77 | |||
78 | # Spin a random duration | ||
79 | limit=`awk -v me=$me -v n=$n -v spinmax=$spinmax 'BEGIN { | ||
80 | srand(n + me + systime()); | ||
81 | printf("%06d", int(rand() * spinmax)); | ||
82 | }' < /dev/null` | ||
83 | n=$(($n+1)) | ||
84 | for i in {1..$limit} | ||
85 | do | ||
86 | echo > /dev/null | ||
87 | done | ||
88 | done | ||
89 | |||
90 | exit 1 | ||
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuperf-ftrace.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuperf-ftrace.sh new file mode 100755 index 000000000000..f79b0e9e84fc --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuperf-ftrace.sh | |||
@@ -0,0 +1,121 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # Analyze a given results directory for rcuperf performance measurements, | ||
4 | # looking for ftrace data. Exits with 0 if data was found, analyzed, and | ||
5 | # printed. Intended to be invoked from kvm-recheck-rcuperf.sh after | ||
6 | # argument checking. | ||
7 | # | ||
8 | # Usage: kvm-recheck-rcuperf-ftrace.sh resdir | ||
9 | # | ||
10 | # This program is free software; you can redistribute it and/or modify | ||
11 | # it under the terms of the GNU General Public License as published by | ||
12 | # the Free Software Foundation; either version 2 of the License, or | ||
13 | # (at your option) any later version. | ||
14 | # | ||
15 | # This program is distributed in the hope that it will be useful, | ||
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | # GNU General Public License for more details. | ||
19 | # | ||
20 | # You should have received a copy of the GNU General Public License | ||
21 | # along with this program; if not, you can access it online at | ||
22 | # http://www.gnu.org/licenses/gpl-2.0.html. | ||
23 | # | ||
24 | # Copyright (C) IBM Corporation, 2016 | ||
25 | # | ||
26 | # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> | ||
27 | |||
28 | i="$1" | ||
29 | . tools/testing/selftests/rcutorture/bin/functions.sh | ||
30 | |||
31 | if test "`grep -c 'rcu_exp_grace_period.*start' < $i/console.log`" -lt 100 | ||
32 | then | ||
33 | exit 10 | ||
34 | fi | ||
35 | |||
36 | sed -e 's/^\[[^]]*]//' < $i/console.log | | ||
37 | grep 'us : rcu_exp_grace_period' | | ||
38 | sed -e 's/us : / : /' | | ||
39 | tr -d '\015' | | ||
40 | awk ' | ||
41 | $8 == "start" { | ||
42 | if (starttask != "") | ||
43 | nlost++; | ||
44 | starttask = $1; | ||
45 | starttime = $3; | ||
46 | startseq = $7; | ||
47 | } | ||
48 | |||
49 | $8 == "end" { | ||
50 | if (starttask == $1 && startseq == $7) { | ||
51 | curgpdur = $3 - starttime; | ||
52 | gptimes[++n] = curgpdur; | ||
53 | gptaskcnt[starttask]++; | ||
54 | sum += curgpdur; | ||
55 | if (curgpdur > 1000) | ||
56 | print "Long GP " starttime "us to " $3 "us (" curgpdur "us)"; | ||
57 | starttask = ""; | ||
58 | } else { | ||
59 | # Lost a message or some such, reset. | ||
60 | starttask = ""; | ||
61 | nlost++; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | $8 == "done" { | ||
66 | piggybackcnt[$1]++; | ||
67 | } | ||
68 | |||
69 | END { | ||
70 | newNR = asort(gptimes); | ||
71 | if (newNR <= 0) { | ||
72 | print "No ftrace records found???" | ||
73 | exit 10; | ||
74 | } | ||
75 | pct50 = int(newNR * 50 / 100); | ||
76 | if (pct50 < 1) | ||
77 | pct50 = 1; | ||
78 | pct90 = int(newNR * 90 / 100); | ||
79 | if (pct90 < 1) | ||
80 | pct90 = 1; | ||
81 | pct99 = int(newNR * 99 / 100); | ||
82 | if (pct99 < 1) | ||
83 | pct99 = 1; | ||
84 | div = 10 ** int(log(gptimes[pct90]) / log(10) + .5) / 100; | ||
85 | print "Histogram bucket size: " div; | ||
86 | last = gptimes[1] - 10; | ||
87 | count = 0; | ||
88 | for (i = 1; i <= newNR; i++) { | ||
89 | current = div * int(gptimes[i] / div); | ||
90 | if (last == current) { | ||
91 | count++; | ||
92 | } else { | ||
93 | if (count > 0) | ||
94 | print last, count; | ||
95 | count = 1; | ||
96 | last = current; | ||
97 | } | ||
98 | } | ||
99 | if (count > 0) | ||
100 | print last, count; | ||
101 | print "Distribution of grace periods across tasks:"; | ||
102 | for (i in gptaskcnt) { | ||
103 | print "\t" i, gptaskcnt[i]; | ||
104 | nbatches += gptaskcnt[i]; | ||
105 | } | ||
106 | ngps = nbatches; | ||
107 | print "Distribution of piggybacking across tasks:"; | ||
108 | for (i in piggybackcnt) { | ||
109 | print "\t" i, piggybackcnt[i]; | ||
110 | ngps += piggybackcnt[i]; | ||
111 | } | ||
112 | print "Average grace-period duration: " sum / newNR " microseconds"; | ||
113 | print "Minimum grace-period duration: " gptimes[1]; | ||
114 | print "50th percentile grace-period duration: " gptimes[pct50]; | ||
115 | print "90th percentile grace-period duration: " gptimes[pct90]; | ||
116 | print "99th percentile grace-period duration: " gptimes[pct99]; | ||
117 | print "Maximum grace-period duration: " gptimes[newNR]; | ||
118 | print "Grace periods: " ngps + 0 " Batches: " nbatches + 0 " Ratio: " ngps / nbatches " Lost: " nlost + 0; | ||
119 | print "Computed from ftrace data."; | ||
120 | }' | ||
121 | exit 0 | ||
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuperf.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuperf.sh new file mode 100755 index 000000000000..8f3121afc716 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuperf.sh | |||
@@ -0,0 +1,96 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # Analyze a given results directory for rcuperf performance measurements. | ||
4 | # | ||
5 | # Usage: kvm-recheck-rcuperf.sh resdir | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License as published by | ||
9 | # the Free Software Foundation; either version 2 of the License, or | ||
10 | # (at your option) any later version. | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, | ||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | # GNU General Public License for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License | ||
18 | # along with this program; if not, you can access it online at | ||
19 | # http://www.gnu.org/licenses/gpl-2.0.html. | ||
20 | # | ||
21 | # Copyright (C) IBM Corporation, 2016 | ||
22 | # | ||
23 | # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> | ||
24 | |||
25 | i="$1" | ||
26 | if test -d $i | ||
27 | then | ||
28 | : | ||
29 | else | ||
30 | echo Unreadable results directory: $i | ||
31 | exit 1 | ||
32 | fi | ||
33 | PATH=`pwd`/tools/testing/selftests/rcutorture/bin:$PATH; export PATH | ||
34 | . tools/testing/selftests/rcutorture/bin/functions.sh | ||
35 | |||
36 | if kvm-recheck-rcuperf-ftrace.sh $i | ||
37 | then | ||
38 | # ftrace data was successfully analyzed, call it good! | ||
39 | exit 0 | ||
40 | fi | ||
41 | |||
42 | configfile=`echo $i | sed -e 's/^.*\///'` | ||
43 | |||
44 | sed -e 's/^\[[^]]*]//' < $i/console.log | | ||
45 | awk ' | ||
46 | /-perf: .* gps: .* batches:/ { | ||
47 | ngps = $9; | ||
48 | nbatches = $11; | ||
49 | } | ||
50 | |||
51 | /-perf: .*writer-duration/ { | ||
52 | gptimes[++n] = $5 / 1000.; | ||
53 | sum += $5 / 1000.; | ||
54 | } | ||
55 | |||
56 | END { | ||
57 | newNR = asort(gptimes); | ||
58 | if (newNR <= 0) { | ||
59 | print "No rcuperf records found???" | ||
60 | exit; | ||
61 | } | ||
62 | pct50 = int(newNR * 50 / 100); | ||
63 | if (pct50 < 1) | ||
64 | pct50 = 1; | ||
65 | pct90 = int(newNR * 90 / 100); | ||
66 | if (pct90 < 1) | ||
67 | pct90 = 1; | ||
68 | pct99 = int(newNR * 99 / 100); | ||
69 | if (pct99 < 1) | ||
70 | pct99 = 1; | ||
71 | div = 10 ** int(log(gptimes[pct90]) / log(10) + .5) / 100; | ||
72 | print "Histogram bucket size: " div; | ||
73 | last = gptimes[1] - 10; | ||
74 | count = 0; | ||
75 | for (i = 1; i <= newNR; i++) { | ||
76 | current = div * int(gptimes[i] / div); | ||
77 | if (last == current) { | ||
78 | count++; | ||
79 | } else { | ||
80 | if (count > 0) | ||
81 | print last, count; | ||
82 | count = 1; | ||
83 | last = current; | ||
84 | } | ||
85 | } | ||
86 | if (count > 0) | ||
87 | print last, count; | ||
88 | print "Average grace-period duration: " sum / newNR " microseconds"; | ||
89 | print "Minimum grace-period duration: " gptimes[1]; | ||
90 | print "50th percentile grace-period duration: " gptimes[pct50]; | ||
91 | print "90th percentile grace-period duration: " gptimes[pct90]; | ||
92 | print "99th percentile grace-period duration: " gptimes[pct99]; | ||
93 | print "Maximum grace-period duration: " gptimes[newNR]; | ||
94 | print "Grace periods: " ngps + 0 " Batches: " nbatches + 0 " Ratio: " ngps / nbatches; | ||
95 | print "Computed from rcuperf printk output."; | ||
96 | }' | ||
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh index d86bdd6b6cc2..f659346d3358 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh | |||
@@ -48,7 +48,10 @@ do | |||
48 | cat $i/Make.oldconfig.err | 48 | cat $i/Make.oldconfig.err |
49 | fi | 49 | fi |
50 | parse-build.sh $i/Make.out $configfile | 50 | parse-build.sh $i/Make.out $configfile |
51 | parse-torture.sh $i/console.log $configfile | 51 | if test "$TORTURE_SUITE" != rcuperf |
52 | then | ||
53 | parse-torture.sh $i/console.log $configfile | ||
54 | fi | ||
52 | parse-console.sh $i/console.log $configfile | 55 | parse-console.sh $i/console.log $configfile |
53 | if test -r $i/Warnings | 56 | if test -r $i/Warnings |
54 | then | 57 | then |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh index 0f80eefb0bfd..4109f306d855 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh | |||
@@ -6,7 +6,7 @@ | |||
6 | # Execute this in the source tree. Do not run it as a background task | 6 | # Execute this in the source tree. Do not run it as a background task |
7 | # because qemu does not seem to like that much. | 7 | # because qemu does not seem to like that much. |
8 | # | 8 | # |
9 | # Usage: kvm-test-1-run.sh config builddir resdir minutes qemu-args boot_args | 9 | # Usage: kvm-test-1-run.sh config builddir resdir seconds qemu-args boot_args |
10 | # | 10 | # |
11 | # qemu-args defaults to "-enable-kvm -soundhw pcspk -nographic", along with | 11 | # qemu-args defaults to "-enable-kvm -soundhw pcspk -nographic", along with |
12 | # arguments specifying the number of CPUs and other | 12 | # arguments specifying the number of CPUs and other |
@@ -91,25 +91,33 @@ fi | |||
91 | # CONFIG_PCMCIA=n | 91 | # CONFIG_PCMCIA=n |
92 | # CONFIG_CARDBUS=n | 92 | # CONFIG_CARDBUS=n |
93 | # CONFIG_YENTA=n | 93 | # CONFIG_YENTA=n |
94 | if kvm-build.sh $config_template $builddir $T | 94 | base_resdir=`echo $resdir | sed -e 's/\.[0-9]\+$//'` |
95 | if test "$base_resdir" != "$resdir" -a -f $base_resdir/bzImage -a -f $base_resdir/vmlinux | ||
95 | then | 96 | then |
97 | # Rerunning previous test, so use that test's kernel. | ||
98 | QEMU="`identify_qemu $base_resdir/vmlinux`" | ||
99 | KERNEL=$base_resdir/bzImage | ||
100 | ln -s $base_resdir/Make*.out $resdir # for kvm-recheck.sh | ||
101 | ln -s $base_resdir/.config $resdir # for kvm-recheck.sh | ||
102 | elif kvm-build.sh $config_template $builddir $T | ||
103 | then | ||
104 | # Had to build a kernel for this test. | ||
96 | QEMU="`identify_qemu $builddir/vmlinux`" | 105 | QEMU="`identify_qemu $builddir/vmlinux`" |
97 | BOOT_IMAGE="`identify_boot_image $QEMU`" | 106 | BOOT_IMAGE="`identify_boot_image $QEMU`" |
98 | cp $builddir/Make*.out $resdir | 107 | cp $builddir/Make*.out $resdir |
108 | cp $builddir/vmlinux $resdir | ||
99 | cp $builddir/.config $resdir | 109 | cp $builddir/.config $resdir |
100 | if test -n "$BOOT_IMAGE" | 110 | if test -n "$BOOT_IMAGE" |
101 | then | 111 | then |
102 | cp $builddir/$BOOT_IMAGE $resdir | 112 | cp $builddir/$BOOT_IMAGE $resdir |
113 | KERNEL=$resdir/bzImage | ||
103 | else | 114 | else |
104 | echo No identifiable boot image, not running KVM, see $resdir. | 115 | echo No identifiable boot image, not running KVM, see $resdir. |
105 | echo Do the torture scripts know about your architecture? | 116 | echo Do the torture scripts know about your architecture? |
106 | fi | 117 | fi |
107 | parse-build.sh $resdir/Make.out $title | 118 | parse-build.sh $resdir/Make.out $title |
108 | if test -f $builddir.wait | ||
109 | then | ||
110 | mv $builddir.wait $builddir.ready | ||
111 | fi | ||
112 | else | 119 | else |
120 | # Build failed. | ||
113 | cp $builddir/Make*.out $resdir | 121 | cp $builddir/Make*.out $resdir |
114 | cp $builddir/.config $resdir || : | 122 | cp $builddir/.config $resdir || : |
115 | echo Build failed, not running KVM, see $resdir. | 123 | echo Build failed, not running KVM, see $resdir. |
@@ -119,12 +127,15 @@ else | |||
119 | fi | 127 | fi |
120 | exit 1 | 128 | exit 1 |
121 | fi | 129 | fi |
130 | if test -f $builddir.wait | ||
131 | then | ||
132 | mv $builddir.wait $builddir.ready | ||
133 | fi | ||
122 | while test -f $builddir.ready | 134 | while test -f $builddir.ready |
123 | do | 135 | do |
124 | sleep 1 | 136 | sleep 1 |
125 | done | 137 | done |
126 | minutes=$4 | 138 | seconds=$4 |
127 | seconds=$(($minutes * 60)) | ||
128 | qemu_args=$5 | 139 | qemu_args=$5 |
129 | boot_args=$6 | 140 | boot_args=$6 |
130 | 141 | ||
@@ -167,15 +178,26 @@ then | |||
167 | exit 0 | 178 | exit 0 |
168 | fi | 179 | fi |
169 | echo "NOTE: $QEMU either did not run or was interactive" > $resdir/console.log | 180 | echo "NOTE: $QEMU either did not run or was interactive" > $resdir/console.log |
170 | echo $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd | 181 | echo $QEMU $qemu_args -m 512 -kernel $KERNEL -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd |
171 | ( $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append "$qemu_append $boot_args"; echo $? > $resdir/qemu-retval ) & | 182 | ( $QEMU $qemu_args -m 512 -kernel $KERNEL -append "$qemu_append $boot_args"& echo $! > $resdir/qemu_pid; wait `cat $resdir/qemu_pid`; echo $? > $resdir/qemu-retval ) & |
172 | qemu_pid=$! | ||
173 | commandcompleted=0 | 183 | commandcompleted=0 |
174 | echo Monitoring qemu job at pid $qemu_pid | 184 | sleep 10 # Give qemu's pid a chance to reach the file |
185 | if test -s "$resdir/qemu_pid" | ||
186 | then | ||
187 | qemu_pid=`cat "$resdir/qemu_pid"` | ||
188 | echo Monitoring qemu job at pid $qemu_pid | ||
189 | else | ||
190 | qemu_pid="" | ||
191 | echo Monitoring qemu job at yet-as-unknown pid | ||
192 | fi | ||
175 | while : | 193 | while : |
176 | do | 194 | do |
195 | if test -z "$qemu_pid" -a -s "$resdir/qemu_pid" | ||
196 | then | ||
197 | qemu_pid=`cat "$resdir/qemu_pid"` | ||
198 | fi | ||
177 | kruntime=`awk 'BEGIN { print systime() - '"$kstarttime"' }' < /dev/null` | 199 | kruntime=`awk 'BEGIN { print systime() - '"$kstarttime"' }' < /dev/null` |
178 | if kill -0 $qemu_pid > /dev/null 2>&1 | 200 | if test -z "$qemu_pid" || kill -0 "$qemu_pid" > /dev/null 2>&1 |
179 | then | 201 | then |
180 | if test $kruntime -ge $seconds | 202 | if test $kruntime -ge $seconds |
181 | then | 203 | then |
@@ -195,12 +217,16 @@ do | |||
195 | ps -fp $killpid >> $resdir/Warnings 2>&1 | 217 | ps -fp $killpid >> $resdir/Warnings 2>&1 |
196 | fi | 218 | fi |
197 | else | 219 | else |
198 | echo ' ---' `date`: Kernel done | 220 | echo ' ---' `date`: "Kernel done" |
199 | fi | 221 | fi |
200 | break | 222 | break |
201 | fi | 223 | fi |
202 | done | 224 | done |
203 | if test $commandcompleted -eq 0 | 225 | if test -z "$qemu_pid" -a -s "$resdir/qemu_pid" |
226 | then | ||
227 | qemu_pid=`cat "$resdir/qemu_pid"` | ||
228 | fi | ||
229 | if test $commandcompleted -eq 0 -a -n "$qemu_pid" | ||
204 | then | 230 | then |
205 | echo Grace period for qemu job at pid $qemu_pid | 231 | echo Grace period for qemu job at pid $qemu_pid |
206 | while : | 232 | while : |
@@ -220,6 +246,9 @@ then | |||
220 | fi | 246 | fi |
221 | sleep 1 | 247 | sleep 1 |
222 | done | 248 | done |
249 | elif test -z "$qemu_pid" | ||
250 | then | ||
251 | echo Unknown PID, cannot kill qemu command | ||
223 | fi | 252 | fi |
224 | 253 | ||
225 | parse-torture.sh $resdir/console.log $title | 254 | parse-torture.sh $resdir/console.log $title |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index 4a431767f77a..0d598145873e 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh | |||
@@ -34,7 +34,7 @@ T=/tmp/kvm.sh.$$ | |||
34 | trap 'rm -rf $T' 0 | 34 | trap 'rm -rf $T' 0 |
35 | mkdir $T | 35 | mkdir $T |
36 | 36 | ||
37 | dur=30 | 37 | dur=$((30*60)) |
38 | dryrun="" | 38 | dryrun="" |
39 | KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM | 39 | KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM |
40 | PATH=${KVM}/bin:$PATH; export PATH | 40 | PATH=${KVM}/bin:$PATH; export PATH |
@@ -48,6 +48,7 @@ resdir="" | |||
48 | configs="" | 48 | configs="" |
49 | cpus=0 | 49 | cpus=0 |
50 | ds=`date +%Y.%m.%d-%H:%M:%S` | 50 | ds=`date +%Y.%m.%d-%H:%M:%S` |
51 | jitter=0 | ||
51 | 52 | ||
52 | . functions.sh | 53 | . functions.sh |
53 | 54 | ||
@@ -63,6 +64,7 @@ usage () { | |||
63 | echo " --dryrun sched|script" | 64 | echo " --dryrun sched|script" |
64 | echo " --duration minutes" | 65 | echo " --duration minutes" |
65 | echo " --interactive" | 66 | echo " --interactive" |
67 | echo " --jitter N [ maxsleep (us) [ maxspin (us) ] ]" | ||
66 | echo " --kmake-arg kernel-make-arguments" | 68 | echo " --kmake-arg kernel-make-arguments" |
67 | echo " --mac nn:nn:nn:nn:nn:nn" | 69 | echo " --mac nn:nn:nn:nn:nn:nn" |
68 | echo " --no-initrd" | 70 | echo " --no-initrd" |
@@ -116,12 +118,17 @@ do | |||
116 | ;; | 118 | ;; |
117 | --duration) | 119 | --duration) |
118 | checkarg --duration "(minutes)" $# "$2" '^[0-9]*$' '^error' | 120 | checkarg --duration "(minutes)" $# "$2" '^[0-9]*$' '^error' |
119 | dur=$2 | 121 | dur=$(($2*60)) |
120 | shift | 122 | shift |
121 | ;; | 123 | ;; |
122 | --interactive) | 124 | --interactive) |
123 | TORTURE_QEMU_INTERACTIVE=1; export TORTURE_QEMU_INTERACTIVE | 125 | TORTURE_QEMU_INTERACTIVE=1; export TORTURE_QEMU_INTERACTIVE |
124 | ;; | 126 | ;; |
127 | --jitter) | ||
128 | checkarg --jitter "(# threads [ sleep [ spin ] ])" $# "$2" '^-\{,1\}[0-9]\+\( \+[0-9]\+\)\{,2\} *$' '^error$' | ||
129 | jitter="$2" | ||
130 | shift | ||
131 | ;; | ||
125 | --kmake-arg) | 132 | --kmake-arg) |
126 | checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$' | 133 | checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$' |
127 | TORTURE_KMAKE_ARG="$2" | 134 | TORTURE_KMAKE_ARG="$2" |
@@ -156,7 +163,7 @@ do | |||
156 | shift | 163 | shift |
157 | ;; | 164 | ;; |
158 | --torture) | 165 | --torture) |
159 | checkarg --torture "(suite name)" "$#" "$2" '^\(lock\|rcu\)$' '^--' | 166 | checkarg --torture "(suite name)" "$#" "$2" '^\(lock\|rcu\|rcuperf\)$' '^--' |
160 | TORTURE_SUITE=$2 | 167 | TORTURE_SUITE=$2 |
161 | shift | 168 | shift |
162 | ;; | 169 | ;; |
@@ -299,6 +306,7 @@ awk < $T/cfgcpu.pack \ | |||
299 | -v CONFIGDIR="$CONFIGFRAG/" \ | 306 | -v CONFIGDIR="$CONFIGFRAG/" \ |
300 | -v KVM="$KVM" \ | 307 | -v KVM="$KVM" \ |
301 | -v ncpus=$cpus \ | 308 | -v ncpus=$cpus \ |
309 | -v jitter="$jitter" \ | ||
302 | -v rd=$resdir/$ds/ \ | 310 | -v rd=$resdir/$ds/ \ |
303 | -v dur=$dur \ | 311 | -v dur=$dur \ |
304 | -v TORTURE_QEMU_ARG="$TORTURE_QEMU_ARG" \ | 312 | -v TORTURE_QEMU_ARG="$TORTURE_QEMU_ARG" \ |
@@ -359,6 +367,16 @@ function dump(first, pastlast, batchnum) | |||
359 | print "\techo ----", cfr[j], cpusr[j] ovf ": Starting kernel. `date` >> " rd "/log"; | 367 | print "\techo ----", cfr[j], cpusr[j] ovf ": Starting kernel. `date` >> " rd "/log"; |
360 | print "fi" | 368 | print "fi" |
361 | } | 369 | } |
370 | njitter = 0; | ||
371 | split(jitter, ja); | ||
372 | if (ja[1] == -1 && ncpus == 0) | ||
373 | njitter = 1; | ||
374 | else if (ja[1] == -1) | ||
375 | njitter = ncpus; | ||
376 | else | ||
377 | njitter = ja[1]; | ||
378 | for (j = 0; j < njitter; j++) | ||
379 | print "jitter.sh " j " " dur " " ja[2] " " ja[3] "&" | ||
362 | print "wait" | 380 | print "wait" |
363 | print "if test -z \"$TORTURE_BUILDONLY\"" | 381 | print "if test -z \"$TORTURE_BUILDONLY\"" |
364 | print "then" | 382 | print "then" |
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 b/tools/testing/selftests/rcutorture/configs/rcu/TREE04 index 39a2c6d7d7ec..17cbe098b115 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE04 | |||
@@ -14,7 +14,7 @@ CONFIG_HOTPLUG_CPU=n | |||
14 | CONFIG_SUSPEND=n | 14 | CONFIG_SUSPEND=n |
15 | CONFIG_HIBERNATION=n | 15 | CONFIG_HIBERNATION=n |
16 | CONFIG_RCU_FANOUT=4 | 16 | CONFIG_RCU_FANOUT=4 |
17 | CONFIG_RCU_FANOUT_LEAF=4 | 17 | CONFIG_RCU_FANOUT_LEAF=3 |
18 | CONFIG_RCU_NOCB_CPU=n | 18 | CONFIG_RCU_NOCB_CPU=n |
19 | CONFIG_DEBUG_LOCK_ALLOC=n | 19 | CONFIG_DEBUG_LOCK_ALLOC=n |
20 | CONFIG_DEBUG_OBJECTS_RCU_HEAD=n | 20 | CONFIG_DEBUG_OBJECTS_RCU_HEAD=n |
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE04.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE04.boot index 0fc8a3428938..e34c33430447 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE04.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE04.boot | |||
@@ -1 +1 @@ | |||
rcutorture.torture_type=rcu_bh | rcutorture.torture_type=rcu_bh rcutree.rcu_fanout_leaf=4 | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcuperf/CFLIST b/tools/testing/selftests/rcutorture/configs/rcuperf/CFLIST new file mode 100644 index 000000000000..c9f56cf20775 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/CFLIST | |||
@@ -0,0 +1 @@ | |||
TREE | |||
diff --git a/tools/testing/selftests/rcutorture/configs/rcuperf/CFcommon b/tools/testing/selftests/rcutorture/configs/rcuperf/CFcommon new file mode 100644 index 000000000000..a09816b8c0f3 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/CFcommon | |||
@@ -0,0 +1,2 @@ | |||
1 | CONFIG_RCU_PERF_TEST=y | ||
2 | CONFIG_PRINTK_TIME=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcuperf/TREE b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE new file mode 100644 index 000000000000..a312f671a29a --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE | |||
@@ -0,0 +1,20 @@ | |||
1 | CONFIG_SMP=y | ||
2 | CONFIG_PREEMPT_NONE=n | ||
3 | CONFIG_PREEMPT_VOLUNTARY=n | ||
4 | CONFIG_PREEMPT=y | ||
5 | #CHECK#CONFIG_PREEMPT_RCU=y | ||
6 | CONFIG_HZ_PERIODIC=n | ||
7 | CONFIG_NO_HZ_IDLE=y | ||
8 | CONFIG_NO_HZ_FULL=n | ||
9 | CONFIG_RCU_FAST_NO_HZ=n | ||
10 | CONFIG_RCU_TRACE=n | ||
11 | CONFIG_HOTPLUG_CPU=n | ||
12 | CONFIG_SUSPEND=n | ||
13 | CONFIG_HIBERNATION=n | ||
14 | CONFIG_RCU_NOCB_CPU=n | ||
15 | CONFIG_DEBUG_LOCK_ALLOC=n | ||
16 | CONFIG_PROVE_LOCKING=n | ||
17 | CONFIG_RCU_BOOST=n | ||
18 | CONFIG_DEBUG_OBJECTS_RCU_HEAD=n | ||
19 | CONFIG_RCU_EXPERT=y | ||
20 | CONFIG_RCU_TRACE=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcuperf/TREE54 b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE54 new file mode 100644 index 000000000000..985fb170d13c --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE54 | |||
@@ -0,0 +1,23 @@ | |||
1 | CONFIG_SMP=y | ||
2 | CONFIG_NR_CPUS=54 | ||
3 | CONFIG_PREEMPT_NONE=n | ||
4 | CONFIG_PREEMPT_VOLUNTARY=n | ||
5 | CONFIG_PREEMPT=y | ||
6 | #CHECK#CONFIG_PREEMPT_RCU=y | ||
7 | CONFIG_HZ_PERIODIC=n | ||
8 | CONFIG_NO_HZ_IDLE=y | ||
9 | CONFIG_NO_HZ_FULL=n | ||
10 | CONFIG_RCU_FAST_NO_HZ=n | ||
11 | CONFIG_RCU_TRACE=n | ||
12 | CONFIG_HOTPLUG_CPU=n | ||
13 | CONFIG_SUSPEND=n | ||
14 | CONFIG_HIBERNATION=n | ||
15 | CONFIG_RCU_FANOUT=3 | ||
16 | CONFIG_RCU_FANOUT_LEAF=2 | ||
17 | CONFIG_RCU_NOCB_CPU=n | ||
18 | CONFIG_DEBUG_LOCK_ALLOC=n | ||
19 | CONFIG_PROVE_LOCKING=n | ||
20 | CONFIG_RCU_BOOST=n | ||
21 | CONFIG_DEBUG_OBJECTS_RCU_HEAD=n | ||
22 | CONFIG_RCU_EXPERT=y | ||
23 | CONFIG_RCU_TRACE=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcuperf/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/rcuperf/ver_functions.sh new file mode 100644 index 000000000000..34f2a1b35ee5 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/ver_functions.sh | |||
@@ -0,0 +1,52 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # Torture-suite-dependent shell functions for the rest of the scripts. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify | ||
6 | # it under the terms of the GNU General Public License as published by | ||
7 | # the Free Software Foundation; either version 2 of the License, or | ||
8 | # (at your option) any later version. | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, | ||
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | # GNU General Public License for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License | ||
16 | # along with this program; if not, you can access it online at | ||
17 | # http://www.gnu.org/licenses/gpl-2.0.html. | ||
18 | # | ||
19 | # Copyright (C) IBM Corporation, 2015 | ||
20 | # | ||
21 | # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> | ||
22 | |||
23 | # rcuperf_param_nreaders bootparam-string | ||
24 | # | ||
25 | # Adds nreaders rcuperf module parameter if not already specified. | ||
26 | rcuperf_param_nreaders () { | ||
27 | if ! echo "$1" | grep -q "rcuperf.nreaders" | ||
28 | then | ||
29 | echo rcuperf.nreaders=-1 | ||
30 | fi | ||
31 | } | ||
32 | |||
33 | # rcuperf_param_nwriters bootparam-string | ||
34 | # | ||
35 | # Adds nwriters rcuperf module parameter if not already specified. | ||
36 | rcuperf_param_nwriters () { | ||
37 | if ! echo "$1" | grep -q "rcuperf.nwriters" | ||
38 | then | ||
39 | echo rcuperf.nwriters=-1 | ||
40 | fi | ||
41 | } | ||
42 | |||
43 | # per_version_boot_params bootparam-string config-file seconds | ||
44 | # | ||
45 | # Adds per-version torture-module parameters to kernels supporting them. | ||
46 | per_version_boot_params () { | ||
47 | echo $1 `rcuperf_param_nreaders "$1"` \ | ||
48 | `rcuperf_param_nwriters "$1"` \ | ||
49 | rcuperf.perf_runnable=1 \ | ||
50 | rcuperf.shutdown=1 \ | ||
51 | rcuperf.verbose=1 | ||
52 | } | ||
diff --git a/tools/testing/selftests/sigaltstack/Makefile b/tools/testing/selftests/sigaltstack/Makefile new file mode 100644 index 000000000000..56af56eda6fa --- /dev/null +++ b/tools/testing/selftests/sigaltstack/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | CFLAGS = -Wall | ||
2 | BINARIES = sas | ||
3 | all: $(BINARIES) | ||
4 | |||
5 | include ../lib.mk | ||
6 | |||
7 | clean: | ||
8 | rm -rf $(BINARIES) | ||
diff --git a/tools/testing/selftests/sigaltstack/sas.c b/tools/testing/selftests/sigaltstack/sas.c new file mode 100644 index 000000000000..1bb01258e559 --- /dev/null +++ b/tools/testing/selftests/sigaltstack/sas.c | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * Stas Sergeev <stsp@users.sourceforge.net> | ||
3 | * | ||
4 | * test sigaltstack(SS_ONSTACK | SS_AUTODISARM) | ||
5 | * If that succeeds, then swapcontext() can be used inside sighandler safely. | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #define _GNU_SOURCE | ||
10 | #include <signal.h> | ||
11 | #include <stdio.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <sys/mman.h> | ||
14 | #include <ucontext.h> | ||
15 | #include <alloca.h> | ||
16 | #include <string.h> | ||
17 | #include <assert.h> | ||
18 | #include <errno.h> | ||
19 | |||
20 | #ifndef SS_AUTODISARM | ||
21 | #define SS_AUTODISARM (1U << 31) | ||
22 | #endif | ||
23 | |||
24 | static void *sstack, *ustack; | ||
25 | static ucontext_t uc, sc; | ||
26 | static const char *msg = "[OK]\tStack preserved"; | ||
27 | static const char *msg2 = "[FAIL]\tStack corrupted"; | ||
28 | struct stk_data { | ||
29 | char msg[128]; | ||
30 | int flag; | ||
31 | }; | ||
32 | |||
33 | void my_usr1(int sig, siginfo_t *si, void *u) | ||
34 | { | ||
35 | char *aa; | ||
36 | int err; | ||
37 | stack_t stk; | ||
38 | struct stk_data *p; | ||
39 | |||
40 | register unsigned long sp asm("sp"); | ||
41 | |||
42 | if (sp < (unsigned long)sstack || | ||
43 | sp >= (unsigned long)sstack + SIGSTKSZ) { | ||
44 | printf("[FAIL]\tSP is not on sigaltstack\n"); | ||
45 | exit(EXIT_FAILURE); | ||
46 | } | ||
47 | /* put some data on stack. other sighandler will try to overwrite it */ | ||
48 | aa = alloca(1024); | ||
49 | assert(aa); | ||
50 | p = (struct stk_data *)(aa + 512); | ||
51 | strcpy(p->msg, msg); | ||
52 | p->flag = 1; | ||
53 | printf("[RUN]\tsignal USR1\n"); | ||
54 | err = sigaltstack(NULL, &stk); | ||
55 | if (err) { | ||
56 | perror("[FAIL]\tsigaltstack()"); | ||
57 | exit(EXIT_FAILURE); | ||
58 | } | ||
59 | if (stk.ss_flags != SS_DISABLE) | ||
60 | printf("[FAIL]\tss_flags=%i, should be SS_DISABLE\n", | ||
61 | stk.ss_flags); | ||
62 | else | ||
63 | printf("[OK]\tsigaltstack is disabled in sighandler\n"); | ||
64 | swapcontext(&sc, &uc); | ||
65 | printf("%s\n", p->msg); | ||
66 | if (!p->flag) { | ||
67 | printf("[RUN]\tAborting\n"); | ||
68 | exit(EXIT_FAILURE); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | void my_usr2(int sig, siginfo_t *si, void *u) | ||
73 | { | ||
74 | char *aa; | ||
75 | struct stk_data *p; | ||
76 | |||
77 | printf("[RUN]\tsignal USR2\n"); | ||
78 | aa = alloca(1024); | ||
79 | /* dont run valgrind on this */ | ||
80 | /* try to find the data stored by previous sighandler */ | ||
81 | p = memmem(aa, 1024, msg, strlen(msg)); | ||
82 | if (p) { | ||
83 | printf("[FAIL]\tsigaltstack re-used\n"); | ||
84 | /* corrupt the data */ | ||
85 | strcpy(p->msg, msg2); | ||
86 | /* tell other sighandler that his data is corrupted */ | ||
87 | p->flag = 0; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static void switch_fn(void) | ||
92 | { | ||
93 | printf("[RUN]\tswitched to user ctx\n"); | ||
94 | raise(SIGUSR2); | ||
95 | setcontext(&sc); | ||
96 | } | ||
97 | |||
98 | int main(void) | ||
99 | { | ||
100 | struct sigaction act; | ||
101 | stack_t stk; | ||
102 | int err; | ||
103 | |||
104 | sigemptyset(&act.sa_mask); | ||
105 | act.sa_flags = SA_ONSTACK | SA_SIGINFO; | ||
106 | act.sa_sigaction = my_usr1; | ||
107 | sigaction(SIGUSR1, &act, NULL); | ||
108 | act.sa_sigaction = my_usr2; | ||
109 | sigaction(SIGUSR2, &act, NULL); | ||
110 | sstack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, | ||
111 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); | ||
112 | if (sstack == MAP_FAILED) { | ||
113 | perror("mmap()"); | ||
114 | return EXIT_FAILURE; | ||
115 | } | ||
116 | |||
117 | err = sigaltstack(NULL, &stk); | ||
118 | if (err) { | ||
119 | perror("[FAIL]\tsigaltstack()"); | ||
120 | exit(EXIT_FAILURE); | ||
121 | } | ||
122 | if (stk.ss_flags == SS_DISABLE) { | ||
123 | printf("[OK]\tInitial sigaltstack state was SS_DISABLE\n"); | ||
124 | } else { | ||
125 | printf("[FAIL]\tInitial sigaltstack state was %i; should have been SS_DISABLE\n", stk.ss_flags); | ||
126 | return EXIT_FAILURE; | ||
127 | } | ||
128 | |||
129 | stk.ss_sp = sstack; | ||
130 | stk.ss_size = SIGSTKSZ; | ||
131 | stk.ss_flags = SS_ONSTACK | SS_AUTODISARM; | ||
132 | err = sigaltstack(&stk, NULL); | ||
133 | if (err) { | ||
134 | if (errno == EINVAL) { | ||
135 | printf("[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n"); | ||
136 | /* | ||
137 | * If test cases for the !SS_AUTODISARM variant were | ||
138 | * added, we could still run them. We don't have any | ||
139 | * test cases like that yet, so just exit and report | ||
140 | * success. | ||
141 | */ | ||
142 | return 0; | ||
143 | } else { | ||
144 | perror("[FAIL]\tsigaltstack(SS_ONSTACK | SS_AUTODISARM)"); | ||
145 | return EXIT_FAILURE; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | ustack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, | ||
150 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); | ||
151 | if (ustack == MAP_FAILED) { | ||
152 | perror("mmap()"); | ||
153 | return EXIT_FAILURE; | ||
154 | } | ||
155 | getcontext(&uc); | ||
156 | uc.uc_link = NULL; | ||
157 | uc.uc_stack.ss_sp = ustack; | ||
158 | uc.uc_stack.ss_size = SIGSTKSZ; | ||
159 | makecontext(&uc, switch_fn, 0); | ||
160 | raise(SIGUSR1); | ||
161 | |||
162 | err = sigaltstack(NULL, &stk); | ||
163 | if (err) { | ||
164 | perror("[FAIL]\tsigaltstack()"); | ||
165 | exit(EXIT_FAILURE); | ||
166 | } | ||
167 | if (stk.ss_flags != SS_AUTODISARM) { | ||
168 | printf("[FAIL]\tss_flags=%i, should be SS_AUTODISARM\n", | ||
169 | stk.ss_flags); | ||
170 | exit(EXIT_FAILURE); | ||
171 | } | ||
172 | printf("[OK]\tsigaltstack is still SS_AUTODISARM after signal\n"); | ||
173 | |||
174 | printf("[OK]\tTest passed\n"); | ||
175 | return 0; | ||
176 | } | ||
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index b47ebd170690..c73425de3cfe 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile | |||
@@ -9,6 +9,7 @@ TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_sysc | |||
9 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ | 9 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ |
10 | test_FCMOV test_FCOMI test_FISTTP \ | 10 | test_FCMOV test_FCOMI test_FISTTP \ |
11 | vdso_restorer | 11 | vdso_restorer |
12 | TARGETS_C_64BIT_ONLY := fsgsbase | ||
12 | 13 | ||
13 | TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) | 14 | TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) |
14 | TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY) | 15 | TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY) |
diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c new file mode 100644 index 000000000000..5b2b4b3c634c --- /dev/null +++ b/tools/testing/selftests/x86/fsgsbase.c | |||
@@ -0,0 +1,398 @@ | |||
1 | /* | ||
2 | * fsgsbase.c, an fsgsbase test | ||
3 | * Copyright (c) 2014-2016 Andy Lutomirski | ||
4 | * GPL v2 | ||
5 | */ | ||
6 | |||
7 | #define _GNU_SOURCE | ||
8 | #include <stdio.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <stdbool.h> | ||
11 | #include <string.h> | ||
12 | #include <sys/syscall.h> | ||
13 | #include <unistd.h> | ||
14 | #include <err.h> | ||
15 | #include <sys/user.h> | ||
16 | #include <asm/prctl.h> | ||
17 | #include <sys/prctl.h> | ||
18 | #include <signal.h> | ||
19 | #include <limits.h> | ||
20 | #include <sys/ucontext.h> | ||
21 | #include <sched.h> | ||
22 | #include <linux/futex.h> | ||
23 | #include <pthread.h> | ||
24 | #include <asm/ldt.h> | ||
25 | #include <sys/mman.h> | ||
26 | |||
27 | #ifndef __x86_64__ | ||
28 | # error This test is 64-bit only | ||
29 | #endif | ||
30 | |||
31 | static volatile sig_atomic_t want_segv; | ||
32 | static volatile unsigned long segv_addr; | ||
33 | |||
34 | static int nerrs; | ||
35 | |||
36 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | ||
37 | int flags) | ||
38 | { | ||
39 | struct sigaction sa; | ||
40 | memset(&sa, 0, sizeof(sa)); | ||
41 | sa.sa_sigaction = handler; | ||
42 | sa.sa_flags = SA_SIGINFO | flags; | ||
43 | sigemptyset(&sa.sa_mask); | ||
44 | if (sigaction(sig, &sa, 0)) | ||
45 | err(1, "sigaction"); | ||
46 | } | ||
47 | |||
48 | static void clearhandler(int sig) | ||
49 | { | ||
50 | struct sigaction sa; | ||
51 | memset(&sa, 0, sizeof(sa)); | ||
52 | sa.sa_handler = SIG_DFL; | ||
53 | sigemptyset(&sa.sa_mask); | ||
54 | if (sigaction(sig, &sa, 0)) | ||
55 | err(1, "sigaction"); | ||
56 | } | ||
57 | |||
58 | static void sigsegv(int sig, siginfo_t *si, void *ctx_void) | ||
59 | { | ||
60 | ucontext_t *ctx = (ucontext_t*)ctx_void; | ||
61 | |||
62 | if (!want_segv) { | ||
63 | clearhandler(SIGSEGV); | ||
64 | return; /* Crash cleanly. */ | ||
65 | } | ||
66 | |||
67 | want_segv = false; | ||
68 | segv_addr = (unsigned long)si->si_addr; | ||
69 | |||
70 | ctx->uc_mcontext.gregs[REG_RIP] += 4; /* Skip the faulting mov */ | ||
71 | |||
72 | } | ||
73 | |||
74 | enum which_base { FS, GS }; | ||
75 | |||
76 | static unsigned long read_base(enum which_base which) | ||
77 | { | ||
78 | unsigned long offset; | ||
79 | /* | ||
80 | * Unless we have FSGSBASE, there's no direct way to do this from | ||
81 | * user mode. We can get at it indirectly using signals, though. | ||
82 | */ | ||
83 | |||
84 | want_segv = true; | ||
85 | |||
86 | offset = 0; | ||
87 | if (which == FS) { | ||
88 | /* Use a constant-length instruction here. */ | ||
89 | asm volatile ("mov %%fs:(%%rcx), %%rax" : : "c" (offset) : "rax"); | ||
90 | } else { | ||
91 | asm volatile ("mov %%gs:(%%rcx), %%rax" : : "c" (offset) : "rax"); | ||
92 | } | ||
93 | if (!want_segv) | ||
94 | return segv_addr + offset; | ||
95 | |||
96 | /* | ||
97 | * If that didn't segfault, try the other end of the address space. | ||
98 | * Unless we get really unlucky and run into the vsyscall page, this | ||
99 | * is guaranteed to segfault. | ||
100 | */ | ||
101 | |||
102 | offset = (ULONG_MAX >> 1) + 1; | ||
103 | if (which == FS) { | ||
104 | asm volatile ("mov %%fs:(%%rcx), %%rax" | ||
105 | : : "c" (offset) : "rax"); | ||
106 | } else { | ||
107 | asm volatile ("mov %%gs:(%%rcx), %%rax" | ||
108 | : : "c" (offset) : "rax"); | ||
109 | } | ||
110 | if (!want_segv) | ||
111 | return segv_addr + offset; | ||
112 | |||
113 | abort(); | ||
114 | } | ||
115 | |||
116 | static void check_gs_value(unsigned long value) | ||
117 | { | ||
118 | unsigned long base; | ||
119 | unsigned short sel; | ||
120 | |||
121 | printf("[RUN]\tARCH_SET_GS to 0x%lx\n", value); | ||
122 | if (syscall(SYS_arch_prctl, ARCH_SET_GS, value) != 0) | ||
123 | err(1, "ARCH_SET_GS"); | ||
124 | |||
125 | asm volatile ("mov %%gs, %0" : "=rm" (sel)); | ||
126 | base = read_base(GS); | ||
127 | if (base == value) { | ||
128 | printf("[OK]\tGSBASE was set as expected (selector 0x%hx)\n", | ||
129 | sel); | ||
130 | } else { | ||
131 | nerrs++; | ||
132 | printf("[FAIL]\tGSBASE was not as expected: got 0x%lx (selector 0x%hx)\n", | ||
133 | base, sel); | ||
134 | } | ||
135 | |||
136 | if (syscall(SYS_arch_prctl, ARCH_GET_GS, &base) != 0) | ||
137 | err(1, "ARCH_GET_GS"); | ||
138 | if (base == value) { | ||
139 | printf("[OK]\tARCH_GET_GS worked as expected (selector 0x%hx)\n", | ||
140 | sel); | ||
141 | } else { | ||
142 | nerrs++; | ||
143 | printf("[FAIL]\tARCH_GET_GS was not as expected: got 0x%lx (selector 0x%hx)\n", | ||
144 | base, sel); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | static void mov_0_gs(unsigned long initial_base, bool schedule) | ||
149 | { | ||
150 | unsigned long base, arch_base; | ||
151 | |||
152 | printf("[RUN]\tARCH_SET_GS to 0x%lx then mov 0 to %%gs%s\n", initial_base, schedule ? " and schedule " : ""); | ||
153 | if (syscall(SYS_arch_prctl, ARCH_SET_GS, initial_base) != 0) | ||
154 | err(1, "ARCH_SET_GS"); | ||
155 | |||
156 | if (schedule) | ||
157 | usleep(10); | ||
158 | |||
159 | asm volatile ("mov %0, %%gs" : : "rm" (0)); | ||
160 | base = read_base(GS); | ||
161 | if (syscall(SYS_arch_prctl, ARCH_GET_GS, &arch_base) != 0) | ||
162 | err(1, "ARCH_GET_GS"); | ||
163 | if (base == arch_base) { | ||
164 | printf("[OK]\tGSBASE is 0x%lx\n", base); | ||
165 | } else { | ||
166 | nerrs++; | ||
167 | printf("[FAIL]\tGSBASE changed to 0x%lx but kernel reports 0x%lx\n", base, arch_base); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static volatile unsigned long remote_base; | ||
172 | static volatile bool remote_hard_zero; | ||
173 | static volatile unsigned int ftx; | ||
174 | |||
175 | /* | ||
176 | * ARCH_SET_FS/GS(0) may or may not program a selector of zero. HARD_ZERO | ||
177 | * means to force the selector to zero to improve test coverage. | ||
178 | */ | ||
179 | #define HARD_ZERO 0xa1fa5f343cb85fa4 | ||
180 | |||
181 | static void do_remote_base() | ||
182 | { | ||
183 | unsigned long to_set = remote_base; | ||
184 | bool hard_zero = false; | ||
185 | if (to_set == HARD_ZERO) { | ||
186 | to_set = 0; | ||
187 | hard_zero = true; | ||
188 | } | ||
189 | |||
190 | if (syscall(SYS_arch_prctl, ARCH_SET_GS, to_set) != 0) | ||
191 | err(1, "ARCH_SET_GS"); | ||
192 | |||
193 | if (hard_zero) | ||
194 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); | ||
195 | |||
196 | unsigned short sel; | ||
197 | asm volatile ("mov %%gs, %0" : "=rm" (sel)); | ||
198 | printf("\tother thread: ARCH_SET_GS(0x%lx)%s -- sel is 0x%hx\n", | ||
199 | to_set, hard_zero ? " and clear gs" : "", sel); | ||
200 | } | ||
201 | |||
202 | void do_unexpected_base(void) | ||
203 | { | ||
204 | /* | ||
205 | * The goal here is to try to arrange for GS == 0, GSBASE != | ||
206 | * 0, and for the the kernel the think that GSBASE == 0. | ||
207 | * | ||
208 | * To make the test as reliable as possible, this uses | ||
209 | * explicit descriptorss. (This is not the only way. This | ||
210 | * could use ARCH_SET_GS with a low, nonzero base, but the | ||
211 | * relevant side effect of ARCH_SET_GS could change.) | ||
212 | */ | ||
213 | |||
214 | /* Step 1: tell the kernel that we have GSBASE == 0. */ | ||
215 | if (syscall(SYS_arch_prctl, ARCH_SET_GS, 0) != 0) | ||
216 | err(1, "ARCH_SET_GS"); | ||
217 | |||
218 | /* Step 2: change GSBASE without telling the kernel. */ | ||
219 | struct user_desc desc = { | ||
220 | .entry_number = 0, | ||
221 | .base_addr = 0xBAADF00D, | ||
222 | .limit = 0xfffff, | ||
223 | .seg_32bit = 1, | ||
224 | .contents = 0, /* Data, grow-up */ | ||
225 | .read_exec_only = 0, | ||
226 | .limit_in_pages = 1, | ||
227 | .seg_not_present = 0, | ||
228 | .useable = 0 | ||
229 | }; | ||
230 | if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) == 0) { | ||
231 | printf("\tother thread: using LDT slot 0\n"); | ||
232 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0x7)); | ||
233 | } else { | ||
234 | /* No modify_ldt for us (configured out, perhaps) */ | ||
235 | |||
236 | struct user_desc *low_desc = mmap( | ||
237 | NULL, sizeof(desc), | ||
238 | PROT_READ | PROT_WRITE, | ||
239 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); | ||
240 | memcpy(low_desc, &desc, sizeof(desc)); | ||
241 | |||
242 | low_desc->entry_number = -1; | ||
243 | |||
244 | /* 32-bit set_thread_area */ | ||
245 | long ret; | ||
246 | asm volatile ("int $0x80" | ||
247 | : "=a" (ret) : "a" (243), "b" (low_desc) | ||
248 | : "flags"); | ||
249 | memcpy(&desc, low_desc, sizeof(desc)); | ||
250 | munmap(low_desc, sizeof(desc)); | ||
251 | |||
252 | if (ret != 0) { | ||
253 | printf("[NOTE]\tcould not create a segment -- test won't do anything\n"); | ||
254 | return; | ||
255 | } | ||
256 | printf("\tother thread: using GDT slot %d\n", desc.entry_number); | ||
257 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)((desc.entry_number << 3) | 0x3))); | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * Step 3: set the selector back to zero. On AMD chips, this will | ||
262 | * preserve GSBASE. | ||
263 | */ | ||
264 | |||
265 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); | ||
266 | } | ||
267 | |||
268 | static void *threadproc(void *ctx) | ||
269 | { | ||
270 | while (1) { | ||
271 | while (ftx == 0) | ||
272 | syscall(SYS_futex, &ftx, FUTEX_WAIT, 0, NULL, NULL, 0); | ||
273 | if (ftx == 3) | ||
274 | return NULL; | ||
275 | |||
276 | if (ftx == 1) | ||
277 | do_remote_base(); | ||
278 | else if (ftx == 2) | ||
279 | do_unexpected_base(); | ||
280 | else | ||
281 | errx(1, "helper thread got bad command"); | ||
282 | |||
283 | ftx = 0; | ||
284 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static void set_gs_and_switch_to(unsigned long local, unsigned long remote) | ||
289 | { | ||
290 | unsigned long base; | ||
291 | |||
292 | bool hard_zero = false; | ||
293 | if (local == HARD_ZERO) { | ||
294 | hard_zero = true; | ||
295 | local = 0; | ||
296 | } | ||
297 | |||
298 | printf("[RUN]\tARCH_SET_GS(0x%lx)%s, then schedule to 0x%lx\n", | ||
299 | local, hard_zero ? " and clear gs" : "", remote); | ||
300 | if (syscall(SYS_arch_prctl, ARCH_SET_GS, local) != 0) | ||
301 | err(1, "ARCH_SET_GS"); | ||
302 | if (hard_zero) | ||
303 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); | ||
304 | |||
305 | if (read_base(GS) != local) { | ||
306 | nerrs++; | ||
307 | printf("[FAIL]\tGSBASE wasn't set as expected\n"); | ||
308 | } | ||
309 | |||
310 | remote_base = remote; | ||
311 | ftx = 1; | ||
312 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); | ||
313 | while (ftx != 0) | ||
314 | syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0); | ||
315 | |||
316 | base = read_base(GS); | ||
317 | if (base == local) { | ||
318 | printf("[OK]\tGSBASE remained 0x%lx\n", local); | ||
319 | } else { | ||
320 | nerrs++; | ||
321 | printf("[FAIL]\tGSBASE changed to 0x%lx\n", base); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | static void test_unexpected_base(void) | ||
326 | { | ||
327 | unsigned long base; | ||
328 | |||
329 | printf("[RUN]\tARCH_SET_GS(0), clear gs, then manipulate GSBASE in a different thread\n"); | ||
330 | if (syscall(SYS_arch_prctl, ARCH_SET_GS, 0) != 0) | ||
331 | err(1, "ARCH_SET_GS"); | ||
332 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); | ||
333 | |||
334 | ftx = 2; | ||
335 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); | ||
336 | while (ftx != 0) | ||
337 | syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0); | ||
338 | |||
339 | base = read_base(GS); | ||
340 | if (base == 0) { | ||
341 | printf("[OK]\tGSBASE remained 0\n"); | ||
342 | } else { | ||
343 | nerrs++; | ||
344 | printf("[FAIL]\tGSBASE changed to 0x%lx\n", base); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | int main() | ||
349 | { | ||
350 | pthread_t thread; | ||
351 | |||
352 | sethandler(SIGSEGV, sigsegv, 0); | ||
353 | |||
354 | check_gs_value(0); | ||
355 | check_gs_value(1); | ||
356 | check_gs_value(0x200000000); | ||
357 | check_gs_value(0); | ||
358 | check_gs_value(0x200000000); | ||
359 | check_gs_value(1); | ||
360 | |||
361 | for (int sched = 0; sched < 2; sched++) { | ||
362 | mov_0_gs(0, !!sched); | ||
363 | mov_0_gs(1, !!sched); | ||
364 | mov_0_gs(0x200000000, !!sched); | ||
365 | } | ||
366 | |||
367 | /* Set up for multithreading. */ | ||
368 | |||
369 | cpu_set_t cpuset; | ||
370 | CPU_ZERO(&cpuset); | ||
371 | CPU_SET(0, &cpuset); | ||
372 | if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) | ||
373 | err(1, "sched_setaffinity to CPU 0"); /* should never fail */ | ||
374 | |||
375 | if (pthread_create(&thread, 0, threadproc, 0) != 0) | ||
376 | err(1, "pthread_create"); | ||
377 | |||
378 | static unsigned long bases_with_hard_zero[] = { | ||
379 | 0, HARD_ZERO, 1, 0x200000000, | ||
380 | }; | ||
381 | |||
382 | for (int local = 0; local < 4; local++) { | ||
383 | for (int remote = 0; remote < 4; remote++) { | ||
384 | set_gs_and_switch_to(bases_with_hard_zero[local], | ||
385 | bases_with_hard_zero[remote]); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | test_unexpected_base(); | ||
390 | |||
391 | ftx = 3; /* Kill the thread. */ | ||
392 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); | ||
393 | |||
394 | if (pthread_join(thread, NULL) != 0) | ||
395 | err(1, "pthread_join"); | ||
396 | |||
397 | return nerrs == 0 ? 0 : 1; | ||
398 | } | ||
diff --git a/tools/testing/selftests/x86/ldt_gdt.c b/tools/testing/selftests/x86/ldt_gdt.c index 31a3035cd4eb..4af47079cf04 100644 --- a/tools/testing/selftests/x86/ldt_gdt.c +++ b/tools/testing/selftests/x86/ldt_gdt.c | |||
@@ -21,6 +21,9 @@ | |||
21 | #include <pthread.h> | 21 | #include <pthread.h> |
22 | #include <sched.h> | 22 | #include <sched.h> |
23 | #include <linux/futex.h> | 23 | #include <linux/futex.h> |
24 | #include <sys/mman.h> | ||
25 | #include <asm/prctl.h> | ||
26 | #include <sys/prctl.h> | ||
24 | 27 | ||
25 | #define AR_ACCESSED (1<<8) | 28 | #define AR_ACCESSED (1<<8) |
26 | 29 | ||
@@ -44,6 +47,12 @@ | |||
44 | 47 | ||
45 | static int nerrs; | 48 | static int nerrs; |
46 | 49 | ||
50 | /* Points to an array of 1024 ints, each holding its own index. */ | ||
51 | static const unsigned int *counter_page; | ||
52 | static struct user_desc *low_user_desc; | ||
53 | static struct user_desc *low_user_desc_clear; /* Use to delete GDT entry */ | ||
54 | static int gdt_entry_num; | ||
55 | |||
47 | static void check_invalid_segment(uint16_t index, int ldt) | 56 | static void check_invalid_segment(uint16_t index, int ldt) |
48 | { | 57 | { |
49 | uint32_t has_limit = 0, has_ar = 0, limit, ar; | 58 | uint32_t has_limit = 0, has_ar = 0, limit, ar; |
@@ -561,16 +570,257 @@ static void do_exec_test(void) | |||
561 | } | 570 | } |
562 | } | 571 | } |
563 | 572 | ||
573 | static void setup_counter_page(void) | ||
574 | { | ||
575 | unsigned int *page = mmap(NULL, 4096, PROT_READ | PROT_WRITE, | ||
576 | MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0); | ||
577 | if (page == MAP_FAILED) | ||
578 | err(1, "mmap"); | ||
579 | |||
580 | for (int i = 0; i < 1024; i++) | ||
581 | page[i] = i; | ||
582 | counter_page = page; | ||
583 | } | ||
584 | |||
585 | static int invoke_set_thread_area(void) | ||
586 | { | ||
587 | int ret; | ||
588 | asm volatile ("int $0x80" | ||
589 | : "=a" (ret), "+m" (low_user_desc) : | ||
590 | "a" (243), "b" (low_user_desc) | ||
591 | : "flags"); | ||
592 | return ret; | ||
593 | } | ||
594 | |||
595 | static void setup_low_user_desc(void) | ||
596 | { | ||
597 | low_user_desc = mmap(NULL, 2 * sizeof(struct user_desc), | ||
598 | PROT_READ | PROT_WRITE, | ||
599 | MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0); | ||
600 | if (low_user_desc == MAP_FAILED) | ||
601 | err(1, "mmap"); | ||
602 | |||
603 | low_user_desc->entry_number = -1; | ||
604 | low_user_desc->base_addr = (unsigned long)&counter_page[1]; | ||
605 | low_user_desc->limit = 0xfffff; | ||
606 | low_user_desc->seg_32bit = 1; | ||
607 | low_user_desc->contents = 0; /* Data, grow-up*/ | ||
608 | low_user_desc->read_exec_only = 0; | ||
609 | low_user_desc->limit_in_pages = 1; | ||
610 | low_user_desc->seg_not_present = 0; | ||
611 | low_user_desc->useable = 0; | ||
612 | |||
613 | if (invoke_set_thread_area() == 0) { | ||
614 | gdt_entry_num = low_user_desc->entry_number; | ||
615 | printf("[NOTE]\tset_thread_area is available; will use GDT index %d\n", gdt_entry_num); | ||
616 | } else { | ||
617 | printf("[NOTE]\tset_thread_area is unavailable\n"); | ||
618 | } | ||
619 | |||
620 | low_user_desc_clear = low_user_desc + 1; | ||
621 | low_user_desc_clear->entry_number = gdt_entry_num; | ||
622 | low_user_desc_clear->read_exec_only = 1; | ||
623 | low_user_desc_clear->seg_not_present = 1; | ||
624 | } | ||
625 | |||
626 | static void test_gdt_invalidation(void) | ||
627 | { | ||
628 | if (!gdt_entry_num) | ||
629 | return; /* 64-bit only system -- we can't use set_thread_area */ | ||
630 | |||
631 | unsigned short prev_sel; | ||
632 | unsigned short sel; | ||
633 | unsigned int eax; | ||
634 | const char *result; | ||
635 | #ifdef __x86_64__ | ||
636 | unsigned long saved_base; | ||
637 | unsigned long new_base; | ||
638 | #endif | ||
639 | |||
640 | /* Test DS */ | ||
641 | invoke_set_thread_area(); | ||
642 | eax = 243; | ||
643 | sel = (gdt_entry_num << 3) | 3; | ||
644 | asm volatile ("movw %%ds, %[prev_sel]\n\t" | ||
645 | "movw %[sel], %%ds\n\t" | ||
646 | #ifdef __i386__ | ||
647 | "pushl %%ebx\n\t" | ||
648 | #endif | ||
649 | "movl %[arg1], %%ebx\n\t" | ||
650 | "int $0x80\n\t" /* Should invalidate ds */ | ||
651 | #ifdef __i386__ | ||
652 | "popl %%ebx\n\t" | ||
653 | #endif | ||
654 | "movw %%ds, %[sel]\n\t" | ||
655 | "movw %[prev_sel], %%ds" | ||
656 | : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), | ||
657 | "+a" (eax) | ||
658 | : "m" (low_user_desc_clear), | ||
659 | [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) | ||
660 | : "flags"); | ||
661 | |||
662 | if (sel != 0) { | ||
663 | result = "FAIL"; | ||
664 | nerrs++; | ||
665 | } else { | ||
666 | result = "OK"; | ||
667 | } | ||
668 | printf("[%s]\tInvalidate DS with set_thread_area: new DS = 0x%hx\n", | ||
669 | result, sel); | ||
670 | |||
671 | /* Test ES */ | ||
672 | invoke_set_thread_area(); | ||
673 | eax = 243; | ||
674 | sel = (gdt_entry_num << 3) | 3; | ||
675 | asm volatile ("movw %%es, %[prev_sel]\n\t" | ||
676 | "movw %[sel], %%es\n\t" | ||
677 | #ifdef __i386__ | ||
678 | "pushl %%ebx\n\t" | ||
679 | #endif | ||
680 | "movl %[arg1], %%ebx\n\t" | ||
681 | "int $0x80\n\t" /* Should invalidate es */ | ||
682 | #ifdef __i386__ | ||
683 | "popl %%ebx\n\t" | ||
684 | #endif | ||
685 | "movw %%es, %[sel]\n\t" | ||
686 | "movw %[prev_sel], %%es" | ||
687 | : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), | ||
688 | "+a" (eax) | ||
689 | : "m" (low_user_desc_clear), | ||
690 | [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) | ||
691 | : "flags"); | ||
692 | |||
693 | if (sel != 0) { | ||
694 | result = "FAIL"; | ||
695 | nerrs++; | ||
696 | } else { | ||
697 | result = "OK"; | ||
698 | } | ||
699 | printf("[%s]\tInvalidate ES with set_thread_area: new ES = 0x%hx\n", | ||
700 | result, sel); | ||
701 | |||
702 | /* Test FS */ | ||
703 | invoke_set_thread_area(); | ||
704 | eax = 243; | ||
705 | sel = (gdt_entry_num << 3) | 3; | ||
706 | #ifdef __x86_64__ | ||
707 | syscall(SYS_arch_prctl, ARCH_GET_FS, &saved_base); | ||
708 | #endif | ||
709 | asm volatile ("movw %%fs, %[prev_sel]\n\t" | ||
710 | "movw %[sel], %%fs\n\t" | ||
711 | #ifdef __i386__ | ||
712 | "pushl %%ebx\n\t" | ||
713 | #endif | ||
714 | "movl %[arg1], %%ebx\n\t" | ||
715 | "int $0x80\n\t" /* Should invalidate fs */ | ||
716 | #ifdef __i386__ | ||
717 | "popl %%ebx\n\t" | ||
718 | #endif | ||
719 | "movw %%fs, %[sel]\n\t" | ||
720 | : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), | ||
721 | "+a" (eax) | ||
722 | : "m" (low_user_desc_clear), | ||
723 | [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) | ||
724 | : "flags"); | ||
725 | |||
726 | #ifdef __x86_64__ | ||
727 | syscall(SYS_arch_prctl, ARCH_GET_FS, &new_base); | ||
728 | #endif | ||
729 | |||
730 | /* Restore FS/BASE for glibc */ | ||
731 | asm volatile ("movw %[prev_sel], %%fs" : : [prev_sel] "rm" (prev_sel)); | ||
732 | #ifdef __x86_64__ | ||
733 | if (saved_base) | ||
734 | syscall(SYS_arch_prctl, ARCH_SET_FS, saved_base); | ||
735 | #endif | ||
736 | |||
737 | if (sel != 0) { | ||
738 | result = "FAIL"; | ||
739 | nerrs++; | ||
740 | } else { | ||
741 | result = "OK"; | ||
742 | } | ||
743 | printf("[%s]\tInvalidate FS with set_thread_area: new FS = 0x%hx\n", | ||
744 | result, sel); | ||
745 | |||
746 | #ifdef __x86_64__ | ||
747 | if (sel == 0 && new_base != 0) { | ||
748 | nerrs++; | ||
749 | printf("[FAIL]\tNew FSBASE was 0x%lx\n", new_base); | ||
750 | } else { | ||
751 | printf("[OK]\tNew FSBASE was zero\n"); | ||
752 | } | ||
753 | #endif | ||
754 | |||
755 | /* Test GS */ | ||
756 | invoke_set_thread_area(); | ||
757 | eax = 243; | ||
758 | sel = (gdt_entry_num << 3) | 3; | ||
759 | #ifdef __x86_64__ | ||
760 | syscall(SYS_arch_prctl, ARCH_GET_GS, &saved_base); | ||
761 | #endif | ||
762 | asm volatile ("movw %%gs, %[prev_sel]\n\t" | ||
763 | "movw %[sel], %%gs\n\t" | ||
764 | #ifdef __i386__ | ||
765 | "pushl %%ebx\n\t" | ||
766 | #endif | ||
767 | "movl %[arg1], %%ebx\n\t" | ||
768 | "int $0x80\n\t" /* Should invalidate gs */ | ||
769 | #ifdef __i386__ | ||
770 | "popl %%ebx\n\t" | ||
771 | #endif | ||
772 | "movw %%gs, %[sel]\n\t" | ||
773 | : [prev_sel] "=&r" (prev_sel), [sel] "+r" (sel), | ||
774 | "+a" (eax) | ||
775 | : "m" (low_user_desc_clear), | ||
776 | [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) | ||
777 | : "flags"); | ||
778 | |||
779 | #ifdef __x86_64__ | ||
780 | syscall(SYS_arch_prctl, ARCH_GET_GS, &new_base); | ||
781 | #endif | ||
782 | |||
783 | /* Restore GS/BASE for glibc */ | ||
784 | asm volatile ("movw %[prev_sel], %%gs" : : [prev_sel] "rm" (prev_sel)); | ||
785 | #ifdef __x86_64__ | ||
786 | if (saved_base) | ||
787 | syscall(SYS_arch_prctl, ARCH_SET_GS, saved_base); | ||
788 | #endif | ||
789 | |||
790 | if (sel != 0) { | ||
791 | result = "FAIL"; | ||
792 | nerrs++; | ||
793 | } else { | ||
794 | result = "OK"; | ||
795 | } | ||
796 | printf("[%s]\tInvalidate GS with set_thread_area: new GS = 0x%hx\n", | ||
797 | result, sel); | ||
798 | |||
799 | #ifdef __x86_64__ | ||
800 | if (sel == 0 && new_base != 0) { | ||
801 | nerrs++; | ||
802 | printf("[FAIL]\tNew GSBASE was 0x%lx\n", new_base); | ||
803 | } else { | ||
804 | printf("[OK]\tNew GSBASE was zero\n"); | ||
805 | } | ||
806 | #endif | ||
807 | } | ||
808 | |||
564 | int main(int argc, char **argv) | 809 | int main(int argc, char **argv) |
565 | { | 810 | { |
566 | if (argc == 1 && !strcmp(argv[0], "ldt_gdt_test_exec")) | 811 | if (argc == 1 && !strcmp(argv[0], "ldt_gdt_test_exec")) |
567 | return finish_exec_test(); | 812 | return finish_exec_test(); |
568 | 813 | ||
814 | setup_counter_page(); | ||
815 | setup_low_user_desc(); | ||
816 | |||
569 | do_simple_tests(); | 817 | do_simple_tests(); |
570 | 818 | ||
571 | do_multicpu_tests(); | 819 | do_multicpu_tests(); |
572 | 820 | ||
573 | do_exec_test(); | 821 | do_exec_test(); |
574 | 822 | ||
823 | test_gdt_invalidation(); | ||
824 | |||
575 | return nerrs ? 1 : 0; | 825 | return nerrs ? 1 : 0; |
576 | } | 826 | } |