diff options
Diffstat (limited to 'tools/testing/selftests/rcutorture/bin')
25 files changed, 2560 insertions, 0 deletions
diff --git a/tools/testing/selftests/rcutorture/bin/configNR_CPUS.sh b/tools/testing/selftests/rcutorture/bin/configNR_CPUS.sh new file mode 100755 index 000000000..2deea2169 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/configNR_CPUS.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Extract the number of CPUs expected from the specified Kconfig-file +# fragment by checking CONFIG_SMP and CONFIG_NR_CPUS. If the specified +# file gives no clue, base the number on the number of idle CPUs on +# the system. +# +# Usage: configNR_CPUS.sh config-frag +# +# Copyright (C) IBM Corporation, 2013 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +cf=$1 +if test ! -r $cf +then + echo Unreadable config fragment $cf 1>&2 + exit -1 +fi +if grep -q '^CONFIG_SMP=n$' $cf +then + echo 1 + exit 0 +fi +if grep -q '^CONFIG_NR_CPUS=' $cf +then + grep '^CONFIG_NR_CPUS=' $cf | + sed -e 's/^CONFIG_NR_CPUS=\([0-9]*\).*$/\1/' + exit 0 +fi +cpus2use.sh diff --git a/tools/testing/selftests/rcutorture/bin/config_override.sh b/tools/testing/selftests/rcutorture/bin/config_override.sh new file mode 100755 index 000000000..90016c359 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/config_override.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# config_override.sh base override +# +# Combines base and override, removing any Kconfig options from base +# that conflict with any in override, concatenating what remains and +# sending the result to standard output. +# +# Copyright (C) IBM Corporation, 2017 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +base=$1 +if test -r $base +then + : +else + echo Base file $base unreadable!!! + exit 1 +fi + +override=$2 +if test -r $override +then + : +else + echo Override file $override unreadable!!! + exit 1 +fi + +T=${TMPDIR-/tmp}/config_override.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +sed < $override -e 's/^/grep -v "/' -e 's/=.*$/="/' | + awk ' + { + if (last) + print last " |"; + last = $0; + } + END { + if (last) + print last; + }' > $T/script +sh $T/script < $base +cat $override diff --git a/tools/testing/selftests/rcutorture/bin/configcheck.sh b/tools/testing/selftests/rcutorture/bin/configcheck.sh new file mode 100755 index 000000000..31584cee8 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/configcheck.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Usage: configcheck.sh .config .config-template +# +# Copyright (C) IBM Corporation, 2011 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +T=${TMPDIR-/tmp}/abat-chk-config.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +cat $1 > $T/.config + +cat $2 | sed -e 's/\(.*\)=n/# \1 is not set/' -e 's/^#CHECK#//' | +grep -v '^CONFIG_INITRAMFS_SOURCE' | +awk ' +{ + print "if grep -q \"" $0 "\" < '"$T/.config"'"; + print "then"; + print "\t:"; + print "else"; + if ($1 == "#") { + print "\tif grep -q \"" $2 "\" < '"$T/.config"'"; + print "\tthen"; + print "\t\tif test \"$firsttime\" = \"\"" + print "\t\tthen" + print "\t\t\tfirsttime=1" + print "\t\tfi" + print "\t\techo \":" $2 ": improperly set\""; + print "\telse"; + print "\t\t:"; + print "\tfi"; + } else { + print "\tif test \"$firsttime\" = \"\"" + print "\tthen" + print "\t\tfirsttime=1" + print "\tfi" + print "\techo \":" $0 ": improperly set\""; + } + print "fi"; + }' | sh diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh new file mode 100755 index 000000000..d6e5ce084 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/configinit.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Usage: configinit.sh config-spec-file results-dir +# +# Create a .config file from the spec file. Run from the kernel source tree. +# Exits with 0 if all went well, with 1 if all went well but the config +# did not match, and some other number for other failures. +# +# The first argument is the .config specification file, which contains +# desired settings, for example, "CONFIG_NO_HZ=y". For best results, +# this should be a full pathname. +# +# Copyright (C) IBM Corporation, 2013 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +T=${TMPDIR-/tmp}/configinit.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +# Capture config spec file. + +c=$1 +resdir=$2 + +sed -e 's/^\(CONFIG[0-9A-Z_]*\)=.*$/grep -v "^# \1" |/' < $c > $T/u.sh +sed -e 's/^\(CONFIG[0-9A-Z_]*=\).*$/grep -v \1 |/' < $c >> $T/u.sh +grep '^grep' < $T/u.sh > $T/upd.sh +echo "cat - $c" >> $T/upd.sh +if test -z "$TORTURE_TRUST_MAKE" +then + make clean > $resdir/Make.clean 2>&1 +fi +make $TORTURE_KMAKE_ARG $TORTURE_DEFCONFIG > $resdir/Make.defconfig.out 2>&1 +mv .config .config.sav +sh $T/upd.sh < .config.sav > .config +cp .config .config.new +yes '' | make $TORTURE_KMAKE_ARG oldconfig > $resdir/Make.oldconfig.out 2> $resdir/Make.oldconfig.err + +# verify new config matches specification. +configcheck.sh .config $c + +exit 0 diff --git a/tools/testing/selftests/rcutorture/bin/console-badness.sh b/tools/testing/selftests/rcutorture/bin/console-badness.sh new file mode 100755 index 000000000..80ae7f08b --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/console-badness.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Scan standard input for error messages, dumping any found to standard +# output. +# +# Usage: console-badness.sh +# +# Copyright (C) 2020 Facebook, Inc. +# +# Authors: Paul E. McKenney <paulmck@kernel.org> + +egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:|detected stalls on CPUs/tasks:|self-detected stall on CPU|Stall ended before state dump start|\?\?\? Writer stall state|rcu_.*kthread starved for|!!!' | +grep -v 'ODEBUG: ' | +grep -v 'This means that this is a DEBUG kernel and it is' | +grep -v 'Warning: unable to open an initial console' | +grep -v 'NOHZ tick-stop error: Non-RCU local softirq work is pending, handler' diff --git a/tools/testing/selftests/rcutorture/bin/cpus2use.sh b/tools/testing/selftests/rcutorture/bin/cpus2use.sh new file mode 100755 index 000000000..1dbfb6256 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/cpus2use.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Get an estimate of how CPU-hoggy to be. +# +# Usage: cpus2use.sh +# +# Copyright (C) IBM Corporation, 2013 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +if test -n "$TORTURE_ALLOTED_CPUS" +then + echo $TORTURE_ALLOTED_CPUS + exit 0 +fi +ncpus=`grep '^processor' /proc/cpuinfo | wc -l` +if mpstat -V > /dev/null 2>&1 +then + idlecpus=`mpstat | tail -1 | \ + awk -v ncpus=$ncpus '{ print ncpus * ($7 + $NF) / 100 }'` +else + # No mpstat command, so use all available CPUs. + echo The mpstat command is not available, so greedily using all CPUs. + idlecpus=$ncpus +fi +awk -v ncpus=$ncpus -v idlecpus=$idlecpus < /dev/null ' +BEGIN { + cpus2use = idlecpus; + if (cpus2use < 1) + cpus2use = 1; + if (cpus2use < ncpus / 10) + cpus2use = ncpus / 10; + if (cpus2use == int(cpus2use)) + cpus2use = int(cpus2use) + else + cpus2use = int(cpus2use) + 1 + print cpus2use; +}' + diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh new file mode 100644 index 000000000..51f3464b9 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/functions.sh @@ -0,0 +1,292 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Shell functions for the rest of the scripts. +# +# Copyright (C) IBM Corporation, 2013 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +# bootparam_hotplug_cpu bootparam-string +# +# Returns 1 if the specified boot-parameter string tells rcutorture to +# test CPU-hotplug operations. +bootparam_hotplug_cpu () { + echo "$1" | grep -q "torture\.onoff_" +} + +# checkarg --argname argtype $# arg mustmatch cannotmatch +# +# Checks the specified argument "arg" against the mustmatch and cannotmatch +# patterns. +checkarg () { + if test $3 -le 1 + then + echo $1 needs argument $2 matching \"$5\" + usage + fi + if echo "$4" | grep -q -e "$5" + then + : + else + echo $1 $2 \"$4\" must match \"$5\" + usage + fi + if echo "$4" | grep -q -e "$6" + then + echo $1 $2 \"$4\" must not match \"$6\" + usage + fi +} + +# configfrag_boot_params bootparam-string config-fragment-file +# +# Adds boot parameters from the .boot file, if any. +configfrag_boot_params () { + if test -r "$2.boot" + then + echo $1 `grep -v '^#' "$2.boot" | tr '\012' ' '` + else + echo $1 + fi +} + +# configfrag_boot_cpus bootparam-string config-fragment-file config-cpus +# +# Decreases number of CPUs based on any nr_cpus= boot parameters specified. +configfrag_boot_cpus () { + local bootargs="`configfrag_boot_params "$1" "$2"`" + local nr_cpus + if echo "${bootargs}" | grep -q 'nr_cpus=[0-9]' + then + nr_cpus="`echo "${bootargs}" | sed -e 's/^.*nr_cpus=\([0-9]*\).*$/\1/'`" + if test "$3" -gt "$nr_cpus" + then + echo $nr_cpus + else + echo $3 + fi + else + echo $3 + fi +} + +# configfrag_boot_maxcpus bootparam-string config-fragment-file config-cpus +# +# Decreases number of CPUs based on any maxcpus= boot parameters specified. +# This allows tests where additional CPUs come online later during the +# test run. However, the torture parameters will be set based on the +# number of CPUs initially present, so the scripting should schedule +# test runs based on the maxcpus= boot parameter controlling the initial +# number of CPUs instead of on the ultimate number of CPUs. +configfrag_boot_maxcpus () { + local bootargs="`configfrag_boot_params "$1" "$2"`" + local maxcpus + if echo "${bootargs}" | grep -q 'maxcpus=[0-9]' + then + maxcpus="`echo "${bootargs}" | sed -e 's/^.*maxcpus=\([0-9]*\).*$/\1/'`" + if test "$3" -gt "$maxcpus" + then + echo $maxcpus + else + echo $3 + fi + else + echo $3 + fi +} + +# configfrag_hotplug_cpu config-fragment-file +# +# Returns 1 if the config fragment specifies hotplug CPU. +configfrag_hotplug_cpu () { + if test ! -r "$1" + then + echo Unreadable config fragment "$1" 1>&2 + exit -1 + fi + grep -q '^CONFIG_HOTPLUG_CPU=y$' "$1" +} + +# identify_boot_image qemu-cmd +# +# Returns the relative path to the kernel build image. This will be +# arch/<arch>/boot/bzImage or vmlinux if bzImage is not a target for the +# architecture, unless overridden with the TORTURE_BOOT_IMAGE environment +# variable. +identify_boot_image () { + if test -n "$TORTURE_BOOT_IMAGE" + then + echo $TORTURE_BOOT_IMAGE + else + case "$1" in + qemu-system-x86_64|qemu-system-i386) + echo arch/x86/boot/bzImage + ;; + qemu-system-aarch64) + echo arch/arm64/boot/Image + ;; + *) + echo vmlinux + ;; + esac + fi +} + +# identify_qemu builddir +# +# Returns our best guess as to which qemu command is appropriate for +# the kernel at hand. Override with the TORTURE_QEMU_CMD environment variable. +identify_qemu () { + local u="`file "$1"`" + if test -n "$TORTURE_QEMU_CMD" + then + echo $TORTURE_QEMU_CMD + elif echo $u | grep -q x86-64 + then + echo qemu-system-x86_64 + elif echo $u | grep -q "Intel 80386" + then + echo qemu-system-i386 + elif echo $u | grep -q aarch64 + then + echo qemu-system-aarch64 + elif uname -a | grep -q ppc64 + then + echo qemu-system-ppc64 + else + echo Cannot figure out what qemu command to use! 1>&2 + echo file $1 output: $u + # Usually this will be one of /usr/bin/qemu-system-* + # Use TORTURE_QEMU_CMD environment variable or appropriate + # argument to top-level script. + exit 1 + fi +} + +# identify_qemu_append qemu-cmd +# +# Output arguments for the qemu "-append" string based on CPU type +# and the TORTURE_QEMU_INTERACTIVE environment variable. +identify_qemu_append () { + local console=ttyS0 + case "$1" in + qemu-system-x86_64|qemu-system-i386) + echo selinux=0 initcall_debug debug + ;; + qemu-system-aarch64) + console=ttyAMA0 + ;; + esac + if test -n "$TORTURE_QEMU_INTERACTIVE" + then + echo root=/dev/sda + else + echo console=$console + fi +} + +# identify_qemu_args qemu-cmd serial-file +# +# Output arguments for qemu arguments based on the TORTURE_QEMU_MAC +# and TORTURE_QEMU_INTERACTIVE environment variables. +identify_qemu_args () { + local KVM_CPU="" + case "$1" in + qemu-system-x86_64) + KVM_CPU=kvm64 + ;; + qemu-system-i386) + KVM_CPU=kvm32 + ;; + esac + case "$1" in + qemu-system-x86_64|qemu-system-i386) + echo -machine q35,accel=kvm + echo -cpu ${KVM_CPU} + ;; + qemu-system-aarch64) + echo -machine virt,gic-version=host -cpu host + ;; + qemu-system-ppc64) + echo -enable-kvm -M pseries -nodefaults + echo -device spapr-vscsi + if test -n "$TORTURE_QEMU_INTERACTIVE" -a -n "$TORTURE_QEMU_MAC" + then + echo -device spapr-vlan,netdev=net0,mac=$TORTURE_QEMU_MAC + echo -netdev bridge,br=br0,id=net0 + fi + ;; + esac + if test -n "$TORTURE_QEMU_INTERACTIVE" + then + echo -monitor stdio -serial pty -S + else + echo -serial file:$2 + fi +} + +# identify_qemu_vcpus +# +# Returns the number of virtual CPUs available to the aggregate of the +# guest OSes. +identify_qemu_vcpus () { + lscpu | grep '^CPU(s):' | sed -e 's/CPU(s)://' -e 's/[ ]*//g' +} + +# print_bug +# +# Prints "BUG: " in red followed by remaining arguments +print_bug () { + printf '\033[031mBUG: \033[m' + echo $* +} + +# print_warning +# +# Prints "WARNING: " in yellow followed by remaining arguments +print_warning () { + printf '\033[033mWARNING: \033[m' + echo $* +} + +# specify_qemu_cpus qemu-cmd qemu-args #cpus +# +# Appends a string containing "-smp XXX" to qemu-args, unless the incoming +# qemu-args already contains "-smp". +specify_qemu_cpus () { + local nt; + + if echo $2 | grep -q -e -smp + then + echo $2 + else + case "$1" in + qemu-system-x86_64|qemu-system-i386|qemu-system-aarch64) + echo $2 -smp $3 + ;; + qemu-system-ppc64) + nt="`lscpu | grep '^NUMA node0' | sed -e 's/^[^,]*,\([0-9]*\),.*$/\1/'`" + echo $2 -smp cores=`expr \( $3 + $nt - 1 \) / $nt`,threads=$nt + ;; + esac + fi +} + +# specify_qemu_net qemu-args +# +# Appends a string containing "-net none" to qemu-args, unless the incoming +# qemu-args already contains "-smp" or unless the TORTURE_QEMU_INTERACTIVE +# environment variable is set, in which case the string that is be added is +# instead "-net nic -net user". +specify_qemu_net () { + if echo $1 | grep -q -e -net + then + echo $1 + elif test -n "$TORTURE_QEMU_INTERACTIVE" + then + echo $1 -net nic -net user + else + echo $1 -net none + fi +} diff --git a/tools/testing/selftests/rcutorture/bin/jitter.sh b/tools/testing/selftests/rcutorture/bin/jitter.sh new file mode 100755 index 000000000..188b864bc --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/jitter.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Alternate sleeping and spinning on randomly selected CPUs. The purpose +# of this script is to inflict random OS jitter on a concurrently running +# test. +# +# Usage: jitter.sh me duration [ sleepmax [ spinmax ] ] +# +# me: Random-number-generator seed salt. +# duration: Time to run in seconds. +# sleepmax: Maximum microseconds to sleep, defaults to one second. +# spinmax: Maximum microseconds to spin, defaults to one millisecond. +# +# Copyright (C) IBM Corporation, 2016 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +me=$(($1 * 1000)) +duration=$2 +sleepmax=${3-1000000} +spinmax=${4-1000} + +n=1 + +starttime=`gawk 'BEGIN { print systime(); }' < /dev/null` + +nohotplugcpus= +for i in /sys/devices/system/cpu/cpu[0-9]* +do + if test -f $i/online + then + : + else + curcpu=`echo $i | sed -e 's/^[^0-9]*//'` + nohotplugcpus="$nohotplugcpus $curcpu" + fi +done + +while : +do + # Check for done. + t=`gawk -v s=$starttime 'BEGIN { print systime() - s; }' < /dev/null` + if test "$t" -gt "$duration" + then + exit 0; + fi + + # Check for stop request. + if test -f "$TORTURE_STOPFILE" + then + exit 1; + fi + + # Set affinity to randomly selected online CPU + if cpus=`grep 1 /sys/devices/system/cpu/*/online 2>&1 | + sed -e 's,/[^/]*$,,' -e 's/^[^0-9]*//'` + then + : + else + cpus= + fi + # Do not leave out non-hot-pluggable CPUs + cpus="$cpus $nohotplugcpus" + + cpumask=`awk -v cpus="$cpus" -v me=$me -v n=$n 'BEGIN { + srand(n + me + systime()); + ncpus = split(cpus, ca); + curcpu = ca[int(rand() * ncpus + 1)]; + mask = lshift(1, curcpu); + if (mask + 0 <= 0) + mask = 1; + printf("%#x\n", mask); + }' < /dev/null` + n=$(($n+1)) + if ! taskset -p $cpumask $$ > /dev/null 2>&1 + then + echo taskset failure: '"taskset -p ' $cpumask $$ '"' + exit 1 + fi + + # Sleep a random duration + sleeptime=`awk -v me=$me -v n=$n -v sleepmax=$sleepmax 'BEGIN { + srand(n + me + systime()); + printf("%06d", int(rand() * sleepmax)); + }' < /dev/null` + n=$(($n+1)) + sleep .$sleeptime + + # Spin a random duration + limit=`awk -v me=$me -v n=$n -v spinmax=$spinmax 'BEGIN { + srand(n + me + systime()); + printf("%06d", int(rand() * spinmax)); + }' < /dev/null` + n=$(($n+1)) + for i in {1..$limit} + do + echo > /dev/null + done +done + +exit 1 diff --git a/tools/testing/selftests/rcutorture/bin/kcsan-collapse.sh b/tools/testing/selftests/rcutorture/bin/kcsan-collapse.sh new file mode 100755 index 000000000..e5cc6b2f1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kcsan-collapse.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# If this was a KCSAN run, collapse the reports in the various console.log +# files onto pairs of functions. +# +# Usage: kcsan-collapse.sh resultsdir +# +# Copyright (C) 2020 Facebook, Inc. +# +# Authors: Paul E. McKenney <paulmck@kernel.org> + +if test -z "$TORTURE_KCONFIG_KCSAN_ARG" +then + exit 0 +fi +cat $1/*/console.log | + grep "BUG: KCSAN: " | + sed -e 's/^\[[^]]*] //' | + sort | + uniq -c | + sort -k1nr > $1/kcsan.sum diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh new file mode 100755 index 000000000..115e1822b --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Build a kvm-ready Linux kernel from the tree in the current directory. +# +# Usage: kvm-build.sh config-template resdir +# +# Copyright (C) IBM Corporation, 2011 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +if test -f "$TORTURE_STOPFILE" +then + echo "kvm-build.sh early exit due to run STOP request" + exit 1 +fi + +config_template=${1} +if test -z "$config_template" -o ! -f "$config_template" -o ! -r "$config_template" +then + echo "kvm-build.sh :$config_template: Not a readable file" + exit 1 +fi +resdir=${2} + +T=${TMPDIR-/tmp}/test-linux.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +cp ${config_template} $T/config +cat << ___EOF___ >> $T/config +CONFIG_INITRAMFS_SOURCE="$TORTURE_INITRD" +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_CONSOLE=y +___EOF___ + +configinit.sh $T/config $resdir +retval=$? +if test $retval -gt 1 +then + exit 2 +fi +ncpus=`cpus2use.sh` +make -j$ncpus $TORTURE_KMAKE_ARG > $resdir/Make.out 2>&1 +retval=$? +if test $retval -ne 0 || grep "rcu[^/]*": < $resdir/Make.out | egrep -q "Stop|Error|error:|warning:" || egrep -q "Stop|Error|error:" < $resdir/Make.out +then + echo Kernel build error + egrep "Stop|Error|error:|warning:" < $resdir/Make.out + echo Run aborted. + exit 3 +fi diff --git a/tools/testing/selftests/rcutorture/bin/kvm-check-branches.sh b/tools/testing/selftests/rcutorture/bin/kvm-check-branches.sh new file mode 100755 index 000000000..6e65c134e --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-check-branches.sh @@ -0,0 +1,108 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# Run a group of kvm.sh tests on the specified commits. This currently +# unconditionally does three-minute runs on each scenario in CFLIST, +# taking advantage of all available CPUs and trusting the "make" utility. +# In the short term, adjustments can be made by editing this script and +# CFLIST. If some adjustments appear to have ongoing value, this script +# might grow some command-line arguments. +# +# Usage: kvm-check-branches.sh commit1 commit2..commit3 commit4 ... +# +# This script considers its arguments one at a time. If more elaborate +# specification of commits is needed, please use "git rev-list" to +# produce something that this simple script can understand. The reason +# for retaining the simplicity is that it allows the user to more easily +# see which commit came from which branch. +# +# This script creates a yyyy.mm.dd-hh.mm.ss-group entry in the "res" +# directory. The calls to kvm.sh create the usual entries, but this script +# moves them under the yyyy.mm.dd-hh.mm.ss-group entry, each in its own +# directory numbered in run order, that is, "0001", "0002", and so on. +# For successful runs, the large build artifacts are removed. Doing this +# reduces the disk space required by about two orders of magnitude for +# successful runs. +# +# Copyright (C) Facebook, 2020 +# +# Authors: Paul E. McKenney <paulmck@kernel.org> + +if ! git status > /dev/null 2>&1 +then + echo '!!!' This script needs to run in a git archive. 1>&2 + echo '!!!' Giving up. 1>&2 + exit 1 +fi + +# Remember where we started so that we can get back and the end. +curcommit="`git status | head -1 | awk '{ print $NF }'`" + +nfail=0 +ntry=0 +resdir="tools/testing/selftests/rcutorture/res" +ds="`date +%Y.%m.%d-%H.%M.%S`-group" +if ! test -e $resdir +then + mkdir $resdir || : +fi +mkdir $resdir/$ds +echo Results directory: $resdir/$ds + +KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM +PATH=${KVM}/bin:$PATH; export PATH +. functions.sh +cpus="`identify_qemu_vcpus`" +echo Using up to $cpus CPUs. + +# Each pass through this loop does one command-line argument. +for gitbr in $@ +do + echo ' --- git branch ' $gitbr + + # Each pass through this loop tests one commit. + for i in `git rev-list "$gitbr"` + do + ntry=`expr $ntry + 1` + idir=`awk -v ntry="$ntry" 'END { printf "%04d", ntry; }' < /dev/null` + echo ' --- commit ' $i from branch $gitbr + date + mkdir $resdir/$ds/$idir + echo $gitbr > $resdir/$ds/$idir/gitbr + echo $i >> $resdir/$ds/$idir/gitbr + + # Test the specified commit. + git checkout $i > $resdir/$ds/$idir/git-checkout.out 2>&1 + echo git checkout return code: $? "(Commit $ntry: $i)" + kvm.sh --cpus $cpus --duration 3 --trust-make > $resdir/$ds/$idir/kvm.sh.out 2>&1 + ret=$? + echo kvm.sh return code $ret for commit $i from branch $gitbr + + # Move the build products to their resting place. + runresdir="`grep -m 1 '^Results directory:' < $resdir/$ds/$idir/kvm.sh.out | sed -e 's/^Results directory://'`" + mv $runresdir $resdir/$ds/$idir + rrd="`echo $runresdir | sed -e 's,^.*/,,'`" + echo Run results: $resdir/$ds/$idir/$rrd + if test "$ret" -ne 0 + then + # Failure, so leave all evidence intact. + nfail=`expr $nfail + 1` + else + # Success, so remove large files to save about 1GB. + ( cd $resdir/$ds/$idir/$rrd; rm -f */vmlinux */bzImage */System.map */Module.symvers ) + fi + done +done +date + +# Go back to the original commit. +git checkout "$curcommit" + +if test $nfail -ne 0 +then + echo '!!! ' $nfail failures in $ntry 'runs!!!' + exit 1 +else + echo No failures in $ntry runs. + exit 0 +fi diff --git a/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh new file mode 100755 index 000000000..6f50722f2 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# Invoke a text editor on all console.log files for all runs with diagnostics, +# that is, on all such files having a console.log.diags counterpart. +# Note that both console.log.diags and console.log are passed to the +# editor (currently defaulting to "vi"), allowing the user to get an +# idea of what to search for in the console.log file. +# +# Usage: kvm-find-errors.sh directory +# +# The "directory" above should end with the date/time directory, for example, +# "tools/testing/selftests/rcutorture/res/2018.02.25-14:27:27". +# Returns error status reflecting the success (or not) of the specified run. +# +# Copyright (C) IBM Corporation, 2018 +# +# Author: Paul E. McKenney <paulmck@linux.ibm.com> + +rundir="${1}" +if test -z "$rundir" -o ! -d "$rundir" +then + echo Directory "$rundir" not found. + echo Usage: $0 directory + exit 1 +fi +editor=${EDITOR-vi} + +# Find builds with errors +files= +for i in ${rundir}/*/Make.out +do + if egrep -q "error:|warning:" < $i + then + egrep "error:|warning:" < $i > $i.diags + files="$files $i.diags $i" + fi +done +if test -n "$files" +then + $editor $files +else + echo No build errors. +fi +if grep -q -e "--buildonly" < ${rundir}/log +then + echo Build-only run, no console logs to check. +fi + +# Find console logs with errors +files= +for i in ${rundir}/*/console.log +do + if test -r $i.diags + then + files="$files $i.diags $i" + fi +done +if test -n "$files" +then + $editor $files + exit 1 +else + echo No errors in console logs. + exit 0 +fi diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh new file mode 100755 index 000000000..f3a7a5e2b --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Analyze a given results directory for locktorture progress. +# +# Usage: kvm-recheck-lock.sh resdir +# +# Copyright (C) IBM Corporation, 2014 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +i="$1" +if test -d "$i" -a -r "$i" +then + : +else + echo Unreadable results directory: $i + exit 1 +fi + +configfile=`echo $i | sed -e 's/^.*\///'` +ncs=`grep "Writes: Total:" $i/console.log 2> /dev/null | tail -1 | sed -e 's/^.* Total: //' -e 's/ .*$//'` +if test -z "$ncs" +then + echo "$configfile -------" +else + title="$configfile ------- $ncs acquisitions/releases" + dur=`sed -e 's/^.* locktorture.shutdown_secs=//' -e 's/ .*$//' < $i/qemu-cmd 2> /dev/null` + if test -z "$dur" + then + : + else + ncsps=`awk -v ncs=$ncs -v dur=$dur ' + BEGIN { print ncs / dur }' < /dev/null` + title="$title ($ncsps per second)" + fi + echo $title +fi diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh new file mode 100755 index 000000000..1706cd446 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Analyze a given results directory for rcutorture progress. +# +# Usage: kvm-recheck-rcu.sh resdir +# +# Copyright (C) IBM Corporation, 2014 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +i="$1" +if test -d "$i" -a -r "$i" +then + : +else + echo Unreadable results directory: $i + exit 1 +fi +. functions.sh + +configfile=`echo $i | sed -e 's/^.*\///'` +ngps=`grep ver: $i/console.log 2> /dev/null | tail -1 | sed -e 's/^.* ver: //' -e 's/ .*$//'` +stopstate="`grep 'End-test grace-period state: g' $i/console.log 2> /dev/null | + tail -1 | sed -e 's/^\[[ 0-9.]*] //' | + awk '{ print \"[\" $1 \" \" $5 \" \" $6 \" \" $7 \"]\"; }' | + tr -d '\012\015'`" +fwdprog="`grep 'rcu_torture_fwd_prog_cr Duration' $i/console.log 2> /dev/null | sed -e 's/^\[[^]]*] //' | sort -k15nr | head -1 | awk '{ print $14 " " $15 }'`" +if test -z "$ngps" +then + echo "$configfile ------- " $stopstate +else + title="$configfile ------- $ngps GPs" + dur=`sed -e 's/^.* rcutorture.shutdown_secs=//' -e 's/ .*$//' < $i/qemu-cmd 2> /dev/null` + if test -z "$dur" + then + : + else + ngpsps=`awk -v ngps=$ngps -v dur=$dur ' + BEGIN { print ngps / dur }' < /dev/null` + title="$title ($ngpsps/s)" + fi + echo $title $stopstate $fwdprog + nclosecalls=`grep --binary-files=text 'torture: Reader Batch' $i/console.log | tail -1 | \ + awk -v sum=0 ' + { + for (i = 0; i <= NF; i++) { + sum += $i; + if ($i ~ /Batch:/) { + sum = 0; + i = i + 2; + } + } + } + + END { + print sum + }'` + if test -z "$nclosecalls" + then + exit 0 + fi + if test "$nclosecalls" -eq 0 + then + exit 0 + fi + # Compute number of close calls per tenth of an hour + nclosecalls10=`awk -v nclosecalls=$nclosecalls -v dur=$dur 'BEGIN { print int(nclosecalls * 36000 / dur) }' < /dev/null` + if test $nclosecalls10 -gt 5 -a $nclosecalls -gt 1 + then + print_bug $nclosecalls "Reader Batch close calls in" $(($dur/60)) minute run: $i + else + print_warning $nclosecalls "Reader Batch close calls in" $(($dur/60)) minute run: $i + fi + echo $nclosecalls "Reader Batch close calls in" $(($dur/60)) minute run: $i > $i/console.log.rcu.diags +fi diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuscale-ftrace.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuscale-ftrace.sh new file mode 100755 index 000000000..d4bec5380 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuscale-ftrace.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Analyze a given results directory for rcuscale performance measurements, +# looking for ftrace data. Exits with 0 if data was found, analyzed, and +# printed. Intended to be invoked from kvm-recheck-rcuscale.sh after +# argument checking. +# +# Usage: kvm-recheck-rcuscale-ftrace.sh resdir +# +# Copyright (C) IBM Corporation, 2016 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +i="$1" +. functions.sh + +if test "`grep -c 'rcu_exp_grace_period.*start' < $i/console.log`" -lt 100 +then + exit 10 +fi + +sed -e 's/^\[[^]]*]//' < $i/console.log | +grep 'us : rcu_exp_grace_period' | +sed -e 's/us : / : /' | +tr -d '\015' | +awk ' +$8 == "start" { + if (startseq != "") + nlost++; + starttask = $1; + starttime = $3; + startseq = $7; + seqtask[startseq] = starttask; +} + +$8 == "end" { + if (startseq == $7) { + curgpdur = $3 - starttime; + gptimes[++n] = curgpdur; + gptaskcnt[starttask]++; + sum += curgpdur; + if (curgpdur > 1000) + print "Long GP " starttime "us to " $3 "us (" curgpdur "us)"; + startseq = ""; + } else { + # Lost a message or some such, reset. + startseq = ""; + nlost++; + } +} + +$8 == "done" && seqtask[$7] != $1 { + piggybackcnt[$1]++; +} + +END { + newNR = asort(gptimes); + if (newNR <= 0) { + print "No ftrace records found???" + exit 10; + } + pct50 = int(newNR * 50 / 100); + if (pct50 < 1) + pct50 = 1; + pct90 = int(newNR * 90 / 100); + if (pct90 < 1) + pct90 = 1; + pct99 = int(newNR * 99 / 100); + if (pct99 < 1) + pct99 = 1; + div = 10 ** int(log(gptimes[pct90]) / log(10) + .5) / 100; + print "Histogram bucket size: " div; + last = gptimes[1] - 10; + count = 0; + for (i = 1; i <= newNR; i++) { + current = div * int(gptimes[i] / div); + if (last == current) { + count++; + } else { + if (count > 0) + print last, count; + count = 1; + last = current; + } + } + if (count > 0) + print last, count; + print "Distribution of grace periods across tasks:"; + for (i in gptaskcnt) { + print "\t" i, gptaskcnt[i]; + nbatches += gptaskcnt[i]; + } + ngps = nbatches; + print "Distribution of piggybacking across tasks:"; + for (i in piggybackcnt) { + print "\t" i, piggybackcnt[i]; + ngps += piggybackcnt[i]; + } + print "Average grace-period duration: " sum / newNR " microseconds"; + print "Minimum grace-period duration: " gptimes[1]; + print "50th percentile grace-period duration: " gptimes[pct50]; + print "90th percentile grace-period duration: " gptimes[pct90]; + print "99th percentile grace-period duration: " gptimes[pct99]; + print "Maximum grace-period duration: " gptimes[newNR]; + print "Grace periods: " ngps + 0 " Batches: " nbatches + 0 " Ratio: " ngps / nbatches " Lost: " nlost + 0; + print "Computed from ftrace data."; +}' +exit 0 diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuscale.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuscale.sh new file mode 100755 index 000000000..aa745152a --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcuscale.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Analyze a given results directory for rcuscale scalability measurements. +# +# Usage: kvm-recheck-rcuscale.sh resdir +# +# Copyright (C) IBM Corporation, 2016 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +i="$1" +if test -d "$i" -a -r "$i" +then + : +else + echo Unreadable results directory: $i + exit 1 +fi +PATH=`pwd`/tools/testing/selftests/rcutorture/bin:$PATH; export PATH +. functions.sh + +if kvm-recheck-rcuscale-ftrace.sh $i +then + # ftrace data was successfully analyzed, call it good! + exit 0 +fi + +configfile=`echo $i | sed -e 's/^.*\///'` + +sed -e 's/^\[[^]]*]//' < $i/console.log | +awk ' +/-scale: .* gps: .* batches:/ { + ngps = $9; + nbatches = $11; +} + +/-scale: .*writer-duration/ { + gptimes[++n] = $5 / 1000.; + sum += $5 / 1000.; +} + +END { + newNR = asort(gptimes); + if (newNR <= 0) { + print "No rcuscale records found???" + exit; + } + pct50 = int(newNR * 50 / 100); + if (pct50 < 1) + pct50 = 1; + pct90 = int(newNR * 90 / 100); + if (pct90 < 1) + pct90 = 1; + pct99 = int(newNR * 99 / 100); + if (pct99 < 1) + pct99 = 1; + div = 10 ** int(log(gptimes[pct90]) / log(10) + .5) / 100; + print "Histogram bucket size: " div; + last = gptimes[1] - 10; + count = 0; + for (i = 1; i <= newNR; i++) { + current = div * int(gptimes[i] / div); + if (last == current) { + count++; + } else { + if (count > 0) + print last, count; + count = 1; + last = current; + } + } + if (count > 0) + print last, count; + print "Average grace-period duration: " sum / newNR " microseconds"; + print "Minimum grace-period duration: " gptimes[1]; + print "50th percentile grace-period duration: " gptimes[pct50]; + print "90th percentile grace-period duration: " gptimes[pct90]; + print "99th percentile grace-period duration: " gptimes[pct99]; + print "Maximum grace-period duration: " gptimes[newNR]; + print "Grace periods: " ngps + 0 " Batches: " nbatches + 0 " Ratio: " ngps / nbatches; + print "Computed from rcuscale printk output."; +}' diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-refscale.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-refscale.sh new file mode 100755 index 000000000..35a463ddd --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-refscale.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Analyze a given results directory for refscale performance measurements. +# +# Usage: kvm-recheck-refscale.sh resdir +# +# Copyright (C) IBM Corporation, 2016 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +i="$1" +if test -d "$i" -a -r "$i" +then + : +else + echo Unreadable results directory: $i + exit 1 +fi +PATH=`pwd`/tools/testing/selftests/rcutorture/bin:$PATH; export PATH +. functions.sh + +configfile=`echo $i | sed -e 's/^.*\///'` + +sed -e 's/^\[[^]]*]//' < $i/console.log | tr -d '\015' | +awk -v configfile="$configfile" ' +/^[ ]*Runs Time\(ns\) *$/ { + if (dataphase + 0 == 0) { + dataphase = 1; + # print configfile, $0; + } + next; +} + +/[^ ]*[0-9][0-9]* [0-9][0-9]*\.[0-9][0-9]*$/ { + if (dataphase == 1) { + # print $0; + readertimes[++n] = $2; + sum += $2; + } + next; +} + +{ + if (dataphase == 1) + dataphase == 2; + next; +} + +END { + print configfile " results:"; + newNR = asort(readertimes); + if (newNR <= 0) { + print "No refscale records found???" + exit; + } + medianidx = int(newNR / 2); + if (newNR == medianidx * 2) + medianvalue = (readertimes[medianidx - 1] + readertimes[medianidx]) / 2; + else + medianvalue = readertimes[medianidx]; + points = "Points:"; + for (i = 1; i <= newNR; i++) + points = points " " readertimes[i]; + print points; + print "Average reader duration: " sum / newNR " nanoseconds"; + print "Minimum reader duration: " readertimes[1]; + print "Median reader duration: " medianvalue; + print "Maximum reader duration: " readertimes[newNR]; + print "Computed from refscale printk output."; +}' diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-scf.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-scf.sh new file mode 100755 index 000000000..671bfee4f --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-scf.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Analyze a given results directory for rcutorture progress. +# +# Usage: kvm-recheck-rcu.sh resdir +# +# Copyright (C) Facebook, 2020 +# +# Authors: Paul E. McKenney <paulmck@kernel.org> + +i="$1" +if test -d "$i" -a -r "$i" +then + : +else + echo Unreadable results directory: $i + exit 1 +fi +. functions.sh + +configfile=`echo $i | sed -e 's/^.*\///'` +nscfs="`grep 'scf_invoked_count ver:' $i/console.log 2> /dev/null | tail -1 | sed -e 's/^.* scf_invoked_count ver: //' -e 's/ .*$//' | tr -d '\015'`" +if test -z "$nscfs" +then + echo "$configfile ------- " +else + dur="`sed -e 's/^.* scftorture.shutdown_secs=//' -e 's/ .*$//' < $i/qemu-cmd 2> /dev/null`" + if test -z "$dur" + then + rate="" + else + nscfss=`awk -v nscfs=$nscfs -v dur=$dur ' + BEGIN { print nscfs / dur }' < /dev/null` + rate=" ($nscfss/s)" + fi + echo "${configfile} ------- ${nscfs} SCF handler invocations$rate" +fi diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh new file mode 100755 index 000000000..840a4679a --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Given the results directories for previous KVM-based torture runs, +# check the build and console output for errors. Given a directory +# containing results directories, this recursively checks them all. +# +# Usage: kvm-recheck.sh resdir ... +# +# Returns status reflecting the success or not of the last run specified. +# +# Copyright (C) IBM Corporation, 2011 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +T=/tmp/kvm-recheck.sh.$$ +trap 'rm -f $T' 0 2 + +PATH=`pwd`/tools/testing/selftests/rcutorture/bin:$PATH; export PATH +. functions.sh +for rd in "$@" +do + firsttime=1 + dirs=`find $rd -name Make.defconfig.out -print | sort | sed -e 's,/[^/]*$,,' | sort -u` + for i in $dirs + do + if test -n "$firsttime" + then + firsttime="" + resdir=`echo $i | sed -e 's,/$,,' -e 's,/[^/]*$,,'` + head -1 $resdir/log + fi + TORTURE_SUITE="`cat $i/../TORTURE_SUITE`" + configfile=`echo $i | sed -e 's,^.*/,,'` + rm -f $i/console.log.*.diags + kvm-recheck-${TORTURE_SUITE}.sh $i + if test -f "$i/qemu-retval" && test "`cat $i/qemu-retval`" -ne 0 && test "`cat $i/qemu-retval`" -ne 137 + then + echo QEMU error, output: + cat $i/qemu-output + elif test -f "$i/console.log" + then + if test -f "$i/qemu-retval" && test "`cat $i/qemu-retval`" -eq 137 + then + echo QEMU killed + fi + configcheck.sh $i/.config $i/ConfigFragment > $T 2>&1 + cat $T + if test -r $i/Make.oldconfig.err + then + cat $i/Make.oldconfig.err + fi + parse-build.sh $i/Make.out $configfile + parse-console.sh $i/console.log $configfile + if test -r $i/Warnings + then + cat $i/Warnings + fi + else + if test -f "$i/buildonly" + then + echo Build-only run, no boot/test + configcheck.sh $i/.config $i/ConfigFragment + parse-build.sh $i/Make.out $configfile + elif test -f "$i/qemu-cmd" + then + print_bug qemu failed + echo " $i" + else + print_bug Build failed + echo " $i" + fi + fi + done + if test -f "$rd/kcsan.sum" + then + if grep -q CONFIG_KCSAN=y $T + then + echo "Compiler or architecture does not support KCSAN!" + echo Did you forget to switch your compiler with '--kmake-arg CC=<cc-that-supports-kcsan>'? + elif test -s "$rd/kcsan.sum" + then + echo KCSAN summary in $rd/kcsan.sum + else + echo Clean KCSAN run in $rd + fi + fi +done +EDITOR=echo kvm-find-errors.sh "${@: -1}" > $T 2>&1 +ret=$? +builderrors="`tr ' ' '\012' < $T | grep -c '/Make.out.diags'`" +if test "$builderrors" -gt 0 +then + echo $builderrors runs with build errors. +fi +runerrors="`tr ' ' '\012' < $T | grep -c '/console.log.diags'`" +if test "$runerrors" -gt 0 +then + echo $runerrors runs with runtime errors. +fi +exit $ret diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh new file mode 100755 index 000000000..6dc2b49b8 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh @@ -0,0 +1,287 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Run a kvm-based test of the specified tree on the specified configs. +# Fully automated run and error checking, no graphics console. +# +# Execute this in the source tree. Do not run it as a background task +# because qemu does not seem to like that much. +# +# Usage: kvm-test-1-run.sh config builddir resdir seconds qemu-args boot_args +# +# qemu-args defaults to "-enable-kvm -nographic", along with arguments +# specifying the number of CPUs and other options +# generated from the underlying CPU architecture. +# boot_args defaults to value returned by the per_version_boot_params +# shell function. +# +# Anything you specify for either qemu-args or boot_args is appended to +# the default values. The "-smp" value is deduced from the contents of +# the config fragment. +# +# More sophisticated argument parsing is clearly needed. +# +# Copyright (C) IBM Corporation, 2011 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +T=${TMPDIR-/tmp}/kvm-test-1-run.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +. functions.sh +. $CONFIGFRAG/ver_functions.sh + +config_template=${1} +config_dir=`echo $config_template | sed -e 's,/[^/]*$,,'` +title=`echo $config_template | sed -e 's/^.*\///'` +builddir=${2} +resdir=${3} +if test -z "$resdir" -o ! -d "$resdir" -o ! -w "$resdir" +then + echo "kvm-test-1-run.sh :$resdir: Not a writable directory, cannot store results into it" + exit 1 +fi +echo ' ---' `date`: Starting build +echo ' ---' Kconfig fragment at: $config_template >> $resdir/log +touch $resdir/ConfigFragment.input + +# Combine additional Kconfig options into an existing set such that +# newer options win. The first argument is the Kconfig source ID, the +# second the to-be-updated file within $T, and the third and final the +# list of additional Kconfig options. Note that a $2.tmp file is +# created when doing the update. +config_override_param () { + if test -n "$3" + then + echo $3 | sed -e 's/^ *//' -e 's/ *$//' | tr -s " " "\012" > $T/Kconfig_args + echo " --- $1" >> $resdir/ConfigFragment.input + cat $T/Kconfig_args >> $resdir/ConfigFragment.input + config_override.sh $T/$2 $T/Kconfig_args > $T/$2.tmp + mv $T/$2.tmp $T/$2 + # Note that "#CHECK#" is not permitted on commandline. + fi +} + +echo > $T/KcList +config_override_param "$config_dir/CFcommon" KcList "`cat $config_dir/CFcommon 2> /dev/null`" +config_override_param "$config_template" KcList "`cat $config_template 2> /dev/null`" +config_override_param "--gdb options" KcList "$TORTURE_KCONFIG_GDB_ARG" +config_override_param "--kasan options" KcList "$TORTURE_KCONFIG_KASAN_ARG" +config_override_param "--kcsan options" KcList "$TORTURE_KCONFIG_KCSAN_ARG" +config_override_param "--kconfig argument" KcList "$TORTURE_KCONFIG_ARG" +cp $T/KcList $resdir/ConfigFragment + +base_resdir=`echo $resdir | sed -e 's/\.[0-9]\+$//'` +if test "$base_resdir" != "$resdir" -a -f $base_resdir/bzImage -a -f $base_resdir/vmlinux +then + # Rerunning previous test, so use that test's kernel. + QEMU="`identify_qemu $base_resdir/vmlinux`" + BOOT_IMAGE="`identify_boot_image $QEMU`" + KERNEL=$base_resdir/${BOOT_IMAGE##*/} # use the last component of ${BOOT_IMAGE} + ln -s $base_resdir/Make*.out $resdir # for kvm-recheck.sh + ln -s $base_resdir/.config $resdir # for kvm-recheck.sh + # Arch-independent indicator + touch $resdir/builtkernel +elif kvm-build.sh $T/KcList $resdir +then + # Had to build a kernel for this test. + QEMU="`identify_qemu vmlinux`" + BOOT_IMAGE="`identify_boot_image $QEMU`" + cp vmlinux $resdir + cp .config $resdir + cp Module.symvers $resdir > /dev/null || : + cp System.map $resdir > /dev/null || : + if test -n "$BOOT_IMAGE" + then + cp $BOOT_IMAGE $resdir + KERNEL=$resdir/${BOOT_IMAGE##*/} + # Arch-independent indicator + touch $resdir/builtkernel + else + echo No identifiable boot image, not running KVM, see $resdir. + echo Do the torture scripts know about your architecture? + fi + parse-build.sh $resdir/Make.out $title +else + # Build failed. + cp .config $resdir || : + echo Build failed, not running KVM, see $resdir. + if test -f $builddir.wait + then + mv $builddir.wait $builddir.ready + fi + exit 1 +fi +if test -f $builddir.wait +then + mv $builddir.wait $builddir.ready +fi +while test -f $builddir.ready +do + sleep 1 +done +seconds=$4 +qemu_args=$5 +boot_args=$6 + +kstarttime=`gawk 'BEGIN { print systime() }' < /dev/null` +if test -z "$TORTURE_BUILDONLY" +then + echo ' ---' `date`: Starting kernel +fi + +# Generate -smp qemu argument. +qemu_args="-enable-kvm -nographic $qemu_args" +cpu_count=`configNR_CPUS.sh $resdir/ConfigFragment` +cpu_count=`configfrag_boot_cpus "$boot_args" "$config_template" "$cpu_count"` +if test "$cpu_count" -gt "$TORTURE_ALLOTED_CPUS" +then + echo CPU count limited from $cpu_count to $TORTURE_ALLOTED_CPUS | tee -a $resdir/Warnings + cpu_count=$TORTURE_ALLOTED_CPUS +fi +qemu_args="`specify_qemu_cpus "$QEMU" "$qemu_args" "$cpu_count"`" +qemu_args="`specify_qemu_net "$qemu_args"`" + +# Generate architecture-specific and interaction-specific qemu arguments +qemu_args="$qemu_args `identify_qemu_args "$QEMU" "$resdir/console.log"`" + +# Generate qemu -append arguments +qemu_append="`identify_qemu_append "$QEMU"`" + +# Pull in Kconfig-fragment boot parameters +boot_args="`configfrag_boot_params "$boot_args" "$config_template"`" +# Generate kernel-version-specific boot parameters +boot_args="`per_version_boot_params "$boot_args" $resdir/.config $seconds`" +if test -n "$TORTURE_BOOT_GDB_ARG" +then + boot_args="$boot_args $TORTURE_BOOT_GDB_ARG" +fi +echo $QEMU $qemu_args -m $TORTURE_QEMU_MEM -kernel $KERNEL -append \"$qemu_append $boot_args\" $TORTURE_QEMU_GDB_ARG > $resdir/qemu-cmd + +if test -n "$TORTURE_BUILDONLY" +then + echo Build-only run specified, boot/test omitted. + touch $resdir/buildonly + exit 0 +fi + +# Decorate qemu-cmd with redirection, backgrounding, and PID capture +sed -e 's/$/ 2>\&1 \&/' < $resdir/qemu-cmd > $T/qemu-cmd +echo 'echo $! > $resdir/qemu_pid' >> $T/qemu-cmd + +# In case qemu refuses to run... +echo "NOTE: $QEMU either did not run or was interactive" > $resdir/console.log + +# Attempt to run qemu +( . $T/qemu-cmd; wait `cat $resdir/qemu_pid`; echo $? > $resdir/qemu-retval ) & +commandcompleted=0 +if test -z "$TORTURE_KCONFIG_GDB_ARG" +then + sleep 10 # Give qemu's pid a chance to reach the file + if test -s "$resdir/qemu_pid" + then + qemu_pid=`cat "$resdir/qemu_pid"` + echo Monitoring qemu job at pid $qemu_pid + else + qemu_pid="" + echo Monitoring qemu job at yet-as-unknown pid + fi +fi +if test -n "$TORTURE_KCONFIG_GDB_ARG" +then + echo Waiting for you to attach a debug session, for example: > /dev/tty + echo " gdb $base_resdir/vmlinux" > /dev/tty + echo 'After symbols load and the "(gdb)" prompt appears:' > /dev/tty + echo " target remote :1234" > /dev/tty + echo " continue" > /dev/tty + kstarttime=`gawk 'BEGIN { print systime() }' < /dev/null` +fi +while : +do + if test -z "$qemu_pid" -a -s "$resdir/qemu_pid" + then + qemu_pid=`cat "$resdir/qemu_pid"` + fi + kruntime=`gawk 'BEGIN { print systime() - '"$kstarttime"' }' < /dev/null` + if test -z "$qemu_pid" || kill -0 "$qemu_pid" > /dev/null 2>&1 + then + if test $kruntime -ge $seconds -o -f "$TORTURE_STOPFILE" + then + break; + fi + sleep 1 + else + commandcompleted=1 + if test $kruntime -lt $seconds + then + echo Completed in $kruntime vs. $seconds >> $resdir/Warnings 2>&1 + grep "^(qemu) qemu:" $resdir/kvm-test-1-run.sh.out >> $resdir/Warnings 2>&1 + killpid="`sed -n "s/^(qemu) qemu: terminating on signal [0-9]* from pid \([0-9]*\).*$/\1/p" $resdir/Warnings`" + if test -n "$killpid" + then + echo "ps -fp $killpid" >> $resdir/Warnings 2>&1 + ps -fp $killpid >> $resdir/Warnings 2>&1 + fi + else + echo ' ---' `date`: "Kernel done" + fi + break + fi +done +if test -z "$qemu_pid" -a -s "$resdir/qemu_pid" +then + qemu_pid=`cat "$resdir/qemu_pid"` +fi +if test $commandcompleted -eq 0 -a -n "$qemu_pid" +then + if ! test -f "$TORTURE_STOPFILE" + then + echo Grace period for qemu job at pid $qemu_pid + fi + oldline="`tail $resdir/console.log`" + while : + do + if test -f "$TORTURE_STOPFILE" + then + echo "PID $qemu_pid killed due to run STOP request" >> $resdir/Warnings 2>&1 + kill -KILL $qemu_pid + break + fi + kruntime=`gawk 'BEGIN { print systime() - '"$kstarttime"' }' < /dev/null` + if kill -0 $qemu_pid > /dev/null 2>&1 + then + : + else + break + fi + must_continue=no + newline="`tail $resdir/console.log`" + if test "$newline" != "$oldline" && echo $newline | grep -q ' [0-9]\+us : ' + then + must_continue=yes + fi + last_ts="`tail $resdir/console.log | grep '^\[ *[0-9]\+\.[0-9]\+]' | tail -1 | sed -e 's/^\[ *//' -e 's/\..*$//'`" + if test -z "$last_ts" + then + last_ts=0 + fi + if test "$newline" != "$oldline" -a "$last_ts" -lt $((seconds + $TORTURE_SHUTDOWN_GRACE)) + then + must_continue=yes + fi + if test $must_continue = no -a $kruntime -ge $((seconds + $TORTURE_SHUTDOWN_GRACE)) + then + echo "!!! PID $qemu_pid hung at $kruntime vs. $seconds seconds" >> $resdir/Warnings 2>&1 + kill -KILL $qemu_pid + break + fi + oldline=$newline + sleep 10 + done +elif test -z "$qemu_pid" +then + echo Unknown PID, cannot kill qemu command +fi + +parse-console.sh $resdir/console.log $title diff --git a/tools/testing/selftests/rcutorture/bin/kvm-transform.sh b/tools/testing/selftests/rcutorture/bin/kvm-transform.sh new file mode 100755 index 000000000..c45a953ef --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm-transform.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Transform a qemu-cmd file to allow reuse. +# +# Usage: kvm-transform.sh bzImage console.log < qemu-cmd-in > qemu-cmd-out +# +# bzImage: Kernel and initrd from the same prior kvm.sh run. +# console.log: File into which to place console output. +# +# The original qemu-cmd file is provided on standard input. +# The transformed qemu-cmd file is on standard output. +# The transformation assumes that the qemu command is confined to a +# single line. It also assumes no whitespace in filenames. +# +# Copyright (C) 2020 Facebook, Inc. +# +# Authors: Paul E. McKenney <paulmck@kernel.org> + +image="$1" +if test -z "$image" +then + echo Need kernel image file. + exit 1 +fi +consolelog="$2" +if test -z "$consolelog" +then + echo "Need console log file name." + exit 1 +fi + +awk -v image="$image" -v consolelog="$consolelog" ' +{ + line = ""; + for (i = 1; i <= NF; i++) { + if (line == "") + line = $i; + else + line = line " " $i; + if ($i == "-serial") { + i++; + line = line " file:" consolelog; + } + if ($i == "-kernel") { + i++; + line = line " " image; + } + } + print line; +}' diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh new file mode 100755 index 000000000..6eb1d3f65 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh @@ -0,0 +1,536 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Run a series of tests under KVM. By default, this series is specified +# by the relevant CFLIST file, but can be overridden by the --configs +# command-line argument. +# +# Usage: kvm.sh [ options ] +# +# Copyright (C) IBM Corporation, 2011 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +scriptname=$0 +args="$*" + +T=${TMPDIR-/tmp}/kvm.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +cd `dirname $scriptname`/../../../../../ + +dur=$((30*60)) +dryrun="" +KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM +PATH=${KVM}/bin:$PATH; export PATH +. functions.sh + +TORTURE_ALLOTED_CPUS="`identify_qemu_vcpus`" +TORTURE_DEFCONFIG=defconfig +TORTURE_BOOT_IMAGE="" +TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD +TORTURE_KCONFIG_ARG="" +TORTURE_KCONFIG_GDB_ARG="" +TORTURE_BOOT_GDB_ARG="" +TORTURE_QEMU_GDB_ARG="" +TORTURE_KCONFIG_KASAN_ARG="" +TORTURE_KCONFIG_KCSAN_ARG="" +TORTURE_KMAKE_ARG="" +TORTURE_QEMU_MEM=512 +TORTURE_SHUTDOWN_GRACE=180 +TORTURE_SUITE=rcu +TORTURE_TRUST_MAKE="" +resdir="" +configs="" +cpus=0 +ds=`date +%Y.%m.%d-%H.%M.%S` +jitter="-1" + +usage () { + echo "Usage: $scriptname optional arguments:" + echo " --allcpus" + echo " --bootargs kernel-boot-arguments" + echo " --bootimage relative-path-to-kernel-boot-image" + echo " --buildonly" + echo " --configs \"config-file list w/ repeat factor (3*TINY01)\"" + echo " --cpus N" + echo " --datestamp string" + echo " --defconfig string" + echo " --dryrun sched|script" + echo " --duration minutes" + echo " --gdb" + echo " --help" + echo " --interactive" + echo " --jitter N [ maxsleep (us) [ maxspin (us) ] ]" + echo " --kconfig Kconfig-options" + echo " --kmake-arg kernel-make-arguments" + echo " --mac nn:nn:nn:nn:nn:nn" + echo " --memory megabytes|nnnG" + echo " --no-initrd" + echo " --qemu-args qemu-arguments" + echo " --qemu-cmd qemu-system-..." + echo " --results absolute-pathname" + echo " --torture lock|rcu|rcuscale|refscale|scf" + echo " --trust-make" + exit 1 +} + +while test $# -gt 0 +do + case "$1" in + --allcpus) + cpus=$TORTURE_ALLOTED_CPUS + max_cpus=$TORTURE_ALLOTED_CPUS + ;; + --bootargs|--bootarg) + checkarg --bootargs "(list of kernel boot arguments)" "$#" "$2" '.*' '^--' + TORTURE_BOOTARGS="$2" + shift + ;; + --bootimage) + checkarg --bootimage "(relative path to kernel boot image)" "$#" "$2" '[a-zA-Z0-9][a-zA-Z0-9_]*' '^--' + TORTURE_BOOT_IMAGE="$2" + shift + ;; + --buildonly) + TORTURE_BUILDONLY=1 + ;; + --configs|--config) + checkarg --configs "(list of config files)" "$#" "$2" '^[^/]*$' '^--' + configs="$2" + shift + ;; + --cpus) + checkarg --cpus "(number)" "$#" "$2" '^[0-9]*$' '^--' + cpus=$2 + TORTURE_ALLOTED_CPUS="$2" + max_cpus="`identify_qemu_vcpus`" + if test "$TORTURE_ALLOTED_CPUS" -gt "$max_cpus" + then + TORTURE_ALLOTED_CPUS=$max_cpus + fi + shift + ;; + --datestamp) + checkarg --datestamp "(relative pathname)" "$#" "$2" '^[^/]*$' '^--' + ds=$2 + shift + ;; + --defconfig) + checkarg --defconfig "defconfigtype" "$#" "$2" '^[^/][^/]*$' '^--' + TORTURE_DEFCONFIG=$2 + shift + ;; + --dryrun) + checkarg --dryrun "sched|script" $# "$2" 'sched\|script' '^--' + dryrun=$2 + shift + ;; + --duration) + checkarg --duration "(minutes)" $# "$2" '^[0-9]*$' '^error' + dur=$(($2*60)) + shift + ;; + --gdb) + TORTURE_KCONFIG_GDB_ARG="CONFIG_DEBUG_INFO=y"; export TORTURE_KCONFIG_GDB_ARG + TORTURE_BOOT_GDB_ARG="nokaslr"; export TORTURE_BOOT_GDB_ARG + TORTURE_QEMU_GDB_ARG="-s -S"; export TORTURE_QEMU_GDB_ARG + ;; + --help|-h) + usage + ;; + --interactive) + TORTURE_QEMU_INTERACTIVE=1; export TORTURE_QEMU_INTERACTIVE + ;; + --jitter) + checkarg --jitter "(# threads [ sleep [ spin ] ])" $# "$2" '^-\{,1\}[0-9]\+\( \+[0-9]\+\)\{,2\} *$' '^error$' + jitter="$2" + shift + ;; + --kconfig) + checkarg --kconfig "(Kconfig options)" $# "$2" '^CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\( CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\)*$' '^error$' + TORTURE_KCONFIG_ARG="$2" + shift + ;; + --kasan) + TORTURE_KCONFIG_KASAN_ARG="CONFIG_DEBUG_INFO=y CONFIG_KASAN=y"; export TORTURE_KCONFIG_KASAN_ARG + ;; + --kcsan) + TORTURE_KCONFIG_KCSAN_ARG="CONFIG_DEBUG_INFO=y CONFIG_KCSAN=y CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=n CONFIG_KCSAN_REPORT_ONCE_IN_MS=100000 CONFIG_KCSAN_VERBOSE=y CONFIG_KCSAN_INTERRUPT_WATCHER=y"; export TORTURE_KCONFIG_KCSAN_ARG + ;; + --kmake-arg) + checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$' + TORTURE_KMAKE_ARG="$2" + shift + ;; + --mac) + checkarg --mac "(MAC address)" $# "$2" '^\([0-9a-fA-F]\{2\}:\)\{5\}[0-9a-fA-F]\{2\}$' error + TORTURE_QEMU_MAC=$2 + shift + ;; + --memory) + checkarg --memory "(memory size)" $# "$2" '^[0-9]\+[MG]\?$' error + TORTURE_QEMU_MEM=$2 + shift + ;; + --no-initrd) + TORTURE_INITRD=""; export TORTURE_INITRD + ;; + --qemu-args|--qemu-arg) + checkarg --qemu-args "(qemu arguments)" $# "$2" '^-' '^error' + TORTURE_QEMU_ARG="$2" + shift + ;; + --qemu-cmd) + checkarg --qemu-cmd "(qemu-system-...)" $# "$2" 'qemu-system-' '^--' + TORTURE_QEMU_CMD="$2" + shift + ;; + --results) + checkarg --results "(absolute pathname)" "$#" "$2" '^/' '^error' + resdir=$2 + shift + ;; + --shutdown-grace) + checkarg --shutdown-grace "(seconds)" "$#" "$2" '^[0-9]*$' '^error' + TORTURE_SHUTDOWN_GRACE=$2 + shift + ;; + --torture) + checkarg --torture "(suite name)" "$#" "$2" '^\(lock\|rcu\|rcuscale\|refscale\|scf\)$' '^--' + TORTURE_SUITE=$2 + shift + if test "$TORTURE_SUITE" = rcuscale || test "$TORTURE_SUITE" = refscale + then + # If you really want jitter for refscale or + # rcuscale, specify it after specifying the rcuscale + # or the refscale. (But why jitter in these cases?) + jitter=0 + fi + ;; + --trust-make) + TORTURE_TRUST_MAKE="y" + ;; + *) + echo Unknown argument $1 + usage + ;; + esac + shift +done + +if test -z "$TORTURE_INITRD" || tools/testing/selftests/rcutorture/bin/mkinitrd.sh +then + : +else + echo No initrd and unable to create one, aborting test >&2 + exit 1 +fi + +CONFIGFRAG=${KVM}/configs/${TORTURE_SUITE}; export CONFIGFRAG + +defaultconfigs="`tr '\012' ' ' < $CONFIGFRAG/CFLIST`" +if test -z "$configs" +then + configs=$defaultconfigs +fi + +if test -z "$resdir" +then + resdir=$KVM/res +fi + +# Create a file of test-name/#cpus pairs, sorted by decreasing #cpus. +configs_derep= +for CF in $configs +do + case $CF in + [0-9]\**|[0-9][0-9]\**|[0-9][0-9][0-9]\**) + config_reps=`echo $CF | sed -e 's/\*.*$//'` + CF1=`echo $CF | sed -e 's/^[^*]*\*//'` + ;; + *) + config_reps=1 + CF1=$CF + ;; + esac + for ((cur_rep=0;cur_rep<$config_reps;cur_rep++)) + do + configs_derep="$configs_derep $CF1" + done +done +touch $T/cfgcpu +configs_derep="`echo $configs_derep | sed -e "s/\<CFLIST\>/$defaultconfigs/g"`" +if test -n "$TORTURE_KCONFIG_GDB_ARG" +then + if test "`echo $configs_derep | wc -w`" -gt 1 + then + echo "The --config list is: $configs_derep." + echo "Only one --config permitted with --gdb, terminating." + exit 1 + fi +fi +for CF1 in $configs_derep +do + if test -f "$CONFIGFRAG/$CF1" + then + cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$CF1` + cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"` + cpu_count=`configfrag_boot_maxcpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"` + echo $CF1 $cpu_count >> $T/cfgcpu + else + echo "The --configs file $CF1 does not exist, terminating." + exit 1 + fi +done +sort -k2nr $T/cfgcpu -T="$T" > $T/cfgcpu.sort + +# Use a greedy bin-packing algorithm, sorting the list accordingly. +awk < $T/cfgcpu.sort > $T/cfgcpu.pack -v ncpus=$cpus ' +BEGIN { + njobs = 0; +} + +{ + # Read file of tests and corresponding required numbers of CPUs. + cf[njobs] = $1; + cpus[njobs] = $2; + njobs++; +} + +END { + batch = 0; + nc = -1; + + # Each pass through the following loop creates on test batch + # that can be executed concurrently given ncpus. Note that a + # given test that requires more than the available CPUs will run in + # their own batch. Such tests just have to make do with what + # is available. + while (nc != ncpus) { + batch++; + nc = ncpus; + + # Each pass through the following loop considers one + # test for inclusion in the current batch. + for (i = 0; i < njobs; i++) { + if (done[i]) + continue; # Already part of a batch. + if (nc >= cpus[i] || nc == ncpus) { + + # This test fits into the current batch. + done[i] = batch; + nc -= cpus[i]; + if (nc <= 0) + break; # Too-big test in its own batch. + } + } + } + + # Dump out the tests in batch order. + for (b = 1; b <= batch; b++) + for (i = 0; i < njobs; i++) + if (done[i] == b) + print cf[i], cpus[i]; +}' + +# Generate a script to execute the tests in appropriate batches. +cat << ___EOF___ > $T/script +CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG +KVM="$KVM"; export KVM +PATH="$PATH"; export PATH +TORTURE_ALLOTED_CPUS="$TORTURE_ALLOTED_CPUS"; export TORTURE_ALLOTED_CPUS +TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE +TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY +TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG +TORTURE_INITRD="$TORTURE_INITRD"; export TORTURE_INITRD +TORTURE_KCONFIG_ARG="$TORTURE_KCONFIG_ARG"; export TORTURE_KCONFIG_ARG +TORTURE_KCONFIG_GDB_ARG="$TORTURE_KCONFIG_GDB_ARG"; export TORTURE_KCONFIG_GDB_ARG +TORTURE_BOOT_GDB_ARG="$TORTURE_BOOT_GDB_ARG"; export TORTURE_BOOT_GDB_ARG +TORTURE_QEMU_GDB_ARG="$TORTURE_QEMU_GDB_ARG"; export TORTURE_QEMU_GDB_ARG +TORTURE_KCONFIG_KASAN_ARG="$TORTURE_KCONFIG_KASAN_ARG"; export TORTURE_KCONFIG_KASAN_ARG +TORTURE_KCONFIG_KCSAN_ARG="$TORTURE_KCONFIG_KCSAN_ARG"; export TORTURE_KCONFIG_KCSAN_ARG +TORTURE_KMAKE_ARG="$TORTURE_KMAKE_ARG"; export TORTURE_KMAKE_ARG +TORTURE_QEMU_CMD="$TORTURE_QEMU_CMD"; export TORTURE_QEMU_CMD +TORTURE_QEMU_INTERACTIVE="$TORTURE_QEMU_INTERACTIVE"; export TORTURE_QEMU_INTERACTIVE +TORTURE_QEMU_MAC="$TORTURE_QEMU_MAC"; export TORTURE_QEMU_MAC +TORTURE_QEMU_MEM="$TORTURE_QEMU_MEM"; export TORTURE_QEMU_MEM +TORTURE_SHUTDOWN_GRACE="$TORTURE_SHUTDOWN_GRACE"; export TORTURE_SHUTDOWN_GRACE +TORTURE_SUITE="$TORTURE_SUITE"; export TORTURE_SUITE +TORTURE_TRUST_MAKE="$TORTURE_TRUST_MAKE"; export TORTURE_TRUST_MAKE +if ! test -e $resdir +then + mkdir -p "$resdir" || : +fi +mkdir $resdir/$ds +TORTURE_RESDIR="$resdir/$ds"; export TORTURE_RESDIR +TORTURE_STOPFILE="$resdir/$ds/STOP"; export TORTURE_STOPFILE +echo Results directory: $resdir/$ds +echo $scriptname $args +touch $resdir/$ds/log +echo $scriptname $args >> $resdir/$ds/log +echo ${TORTURE_SUITE} > $resdir/$ds/TORTURE_SUITE +pwd > $resdir/$ds/testid.txt +if test -d .git +then + git status >> $resdir/$ds/testid.txt + git rev-parse HEAD >> $resdir/$ds/testid.txt + git diff HEAD >> $resdir/$ds/testid.txt +fi +___EOF___ +awk < $T/cfgcpu.pack \ + -v TORTURE_BUILDONLY="$TORTURE_BUILDONLY" \ + -v CONFIGDIR="$CONFIGFRAG/" \ + -v KVM="$KVM" \ + -v ncpus=$cpus \ + -v jitter="$jitter" \ + -v rd=$resdir/$ds/ \ + -v dur=$dur \ + -v TORTURE_QEMU_ARG="$TORTURE_QEMU_ARG" \ + -v TORTURE_BOOTARGS="$TORTURE_BOOTARGS" \ +'BEGIN { + i = 0; +} + +{ + cf[i] = $1; + cpus[i] = $2; + i++; +} + +# Dump out the scripting required to run one test batch. +function dump(first, pastlast, batchnum) +{ + print "echo ----Start batch " batchnum ": `date` | tee -a " rd "log"; + print "needqemurun=" + jn=1 + for (j = first; j < pastlast; j++) { + builddir=KVM "/b" j - first + 1 + cpusr[jn] = cpus[j]; + if (cfrep[cf[j]] == "") { + cfr[jn] = cf[j]; + cfrep[cf[j]] = 1; + } else { + cfrep[cf[j]]++; + cfr[jn] = cf[j] "." cfrep[cf[j]]; + } + if (cpusr[jn] > ncpus && ncpus != 0) + ovf = "-ovf"; + else + ovf = ""; + print "echo ", cfr[jn], cpusr[jn] ovf ": Starting build. `date` | tee -a " rd "log"; + print "rm -f " builddir ".*"; + print "touch " builddir ".wait"; + print "mkdir " rd cfr[jn] " || :"; + print "kvm-test-1-run.sh " CONFIGDIR cf[j], builddir, rd cfr[jn], dur " \"" TORTURE_QEMU_ARG "\" \"" TORTURE_BOOTARGS "\" > " rd cfr[jn] "/kvm-test-1-run.sh.out 2>&1 &" + print "echo ", cfr[jn], cpusr[jn] ovf ": Waiting for build to complete. `date` | tee -a " rd "log"; + print "while test -f " builddir ".wait" + print "do" + print "\tsleep 1" + print "done" + print "echo ", cfr[jn], cpusr[jn] ovf ": Build complete. `date` | tee -a " rd "log"; + jn++; + } + for (j = 1; j < jn; j++) { + builddir=KVM "/b" j + print "rm -f " builddir ".ready" + print "if test -f \"" rd cfr[j] "/builtkernel\"" + print "then" + print "\techo ----", cfr[j], cpusr[j] ovf ": Kernel present. `date` | tee -a " rd "log"; + print "\tneedqemurun=1" + print "fi" + } + njitter = 0; + split(jitter, ja); + if (ja[1] == -1 && ncpus == 0) + njitter = 1; + else if (ja[1] == -1) + njitter = ncpus; + else + njitter = ja[1]; + if (TORTURE_BUILDONLY && njitter != 0) { + njitter = 0; + print "echo Build-only run, so suppressing jitter | tee -a " rd "log" + } + if (TORTURE_BUILDONLY) { + print "needqemurun=" + } + print "if test -n \"$needqemurun\"" + print "then" + print "\techo ---- Starting kernels. `date` | tee -a " rd "log"; + for (j = 0; j < njitter; j++) + print "\tjitter.sh " j " " dur " " ja[2] " " ja[3] "&" + print "\twait" + print "\techo ---- All kernel runs complete. `date` | tee -a " rd "log"; + print "else" + print "\twait" + print "\techo ---- No kernel runs. `date` | tee -a " rd "log"; + print "fi" + for (j = 1; j < jn; j++) { + builddir=KVM "/b" j + print "echo ----", cfr[j], cpusr[j] ovf ": Build/run results: | tee -a " rd "log"; + print "cat " rd cfr[j] "/kvm-test-1-run.sh.out | tee -a " rd "log"; + } +} + +END { + njobs = i; + nc = ncpus; + first = 0; + batchnum = 1; + + # Each pass through the following loop considers one test. + for (i = 0; i < njobs; i++) { + if (ncpus == 0) { + # Sequential test specified, each test its own batch. + dump(i, i + 1, batchnum); + first = i; + batchnum++; + } else if (nc < cpus[i] && i != 0) { + # Out of CPUs, dump out a batch. + dump(first, i, batchnum); + first = i; + nc = ncpus; + batchnum++; + } + # Account for the CPUs needed by the current test. + nc -= cpus[i]; + } + # Dump the last batch. + if (ncpus != 0) + dump(first, i, batchnum); +}' >> $T/script + +cat << ___EOF___ >> $T/script +echo +echo +echo " --- `date` Test summary:" +echo Results directory: $resdir/$ds +kcsan-collapse.sh $resdir/$ds +kvm-recheck.sh $resdir/$ds +___EOF___ + +if test "$dryrun" = script +then + cat $T/script + exit 0 +elif test "$dryrun" = sched +then + # Extract the test run schedule from the script. + egrep 'Start batch|Starting build\.' $T/script | + grep -v ">>" | + sed -e 's/:.*$//' -e 's/^echo //' + exit 0 +else + # Not a dryrun, so run the script. + sh $T/script +fi + +# Tracing: trace_event=rcu:rcu_grace_period,rcu:rcu_future_grace_period,rcu:rcu_grace_period_init,rcu:rcu_nocb_wake,rcu:rcu_preempt_task,rcu:rcu_unlock_preempted_task,rcu:rcu_quiescent_state_report,rcu:rcu_fqs,rcu:rcu_callback,rcu:rcu_kfree_callback,rcu:rcu_batch_start,rcu:rcu_invoke_callback,rcu:rcu_invoke_kfree_callback,rcu:rcu_batch_end,rcu:rcu_torture_read,rcu:rcu_barrier +# Function-graph tracing: ftrace=function_graph ftrace_graph_filter=sched_setaffinity,migration_cpu_stop +# Also --kconfig "CONFIG_FUNCTION_TRACER=y CONFIG_FUNCTION_GRAPH_TRACER=y" +# Control buffer size: --bootargs trace_buf_size=3k +# Get trace-buffer dumps on all oopses: --bootargs ftrace_dump_on_oops +# Ditto, but dump only the oopsing CPU: --bootargs ftrace_dump_on_oops=orig_cpu +# Heavy-handed way to also dump on warnings: --bootargs panic_on_warn diff --git a/tools/testing/selftests/rcutorture/bin/mkinitrd.sh b/tools/testing/selftests/rcutorture/bin/mkinitrd.sh new file mode 100755 index 000000000..38e424d23 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/mkinitrd.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Create an initrd directory if one does not already exist. +# +# Copyright (C) IBM Corporation, 2013 +# +# Author: Connor Shu <Connor.Shu@ibm.com> + +D=tools/testing/selftests/rcutorture + +# Prerequisite checks +[ -z "$D" ] && echo >&2 "No argument supplied" && exit 1 +if [ ! -d "$D" ]; then + echo >&2 "$D does not exist: Malformed kernel source tree?" + exit 1 +fi +if [ -s "$D/initrd/init" ]; then + echo "$D/initrd/init already exists, no need to create it" + exit 0 +fi + +# Create a C-language initrd/init infinite-loop program and statically +# link it. This results in a very small initrd. +echo "Creating a statically linked C-language initrd" +cd $D +mkdir -p initrd +cd initrd +cat > init.c << '___EOF___' +#ifndef NOLIBC +#include <unistd.h> +#include <sys/time.h> +#endif + +volatile unsigned long delaycount; + +int main(int argc, int argv[]) +{ + int i; + struct timeval tv; + struct timeval tvb; + + for (;;) { + sleep(1); + /* Need some userspace time. */ + if (gettimeofday(&tvb, NULL)) + continue; + do { + for (i = 0; i < 1000 * 100; i++) + delaycount = i * i; + if (gettimeofday(&tv, NULL)) + break; + tv.tv_sec -= tvb.tv_sec; + if (tv.tv_sec > 1) + break; + tv.tv_usec += tv.tv_sec * 1000 * 1000; + tv.tv_usec -= tvb.tv_usec; + } while (tv.tv_usec < 1000); + } + return 0; +} +___EOF___ + +# build using nolibc on supported archs (smaller executable) and fall +# back to regular glibc on other ones. +if echo -e "#if __x86_64__||__i386__||__i486__||__i586__||__i686__" \ + "||__ARM_EABI__||__aarch64__\nyes\n#endif" \ + | ${CROSS_COMPILE}gcc -E -nostdlib -xc - \ + | grep -q '^yes'; then + # architecture supported by nolibc + ${CROSS_COMPILE}gcc -fno-asynchronous-unwind-tables -fno-ident \ + -nostdlib -include ../../../../include/nolibc/nolibc.h \ + -lgcc -s -static -Os -o init init.c +else + ${CROSS_COMPILE}gcc -s -static -Os -o init init.c +fi + +rm init.c +echo "Done creating a statically linked C-language initrd" + +exit 0 diff --git a/tools/testing/selftests/rcutorture/bin/parse-build.sh b/tools/testing/selftests/rcutorture/bin/parse-build.sh new file mode 100755 index 000000000..09155c15e --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/parse-build.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Check the build output from an rcutorture run for goodness. +# The "file" is a pathname on the local system, and "title" is +# a text string for error-message purposes. +# +# The file must contain kernel build output. +# +# Usage: parse-build.sh file title +# +# Copyright (C) IBM Corporation, 2011 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +F=$1 +title=$2 +T=${TMPDIR-/tmp}/parse-build.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +. functions.sh + +if grep -q CC < $F || test -n "$TORTURE_TRUST_MAKE" +then + : +else + print_bug $title no build + exit 1 +fi + +if grep -q "error:" < $F +then + print_bug $title build errors: + grep "error:" < $F + exit 2 +fi + +grep warning: < $F > $T/warnings +grep "include/linux/*rcu*\.h:" $T/warnings > $T/hwarnings +grep "kernel/rcu/[^/]*:" $T/warnings > $T/cwarnings +cat $T/hwarnings $T/cwarnings > $T/rcuwarnings +if test -s $T/rcuwarnings +then + print_warning $title build errors: + cat $T/rcuwarnings + exit 2 +fi +exit 0 diff --git a/tools/testing/selftests/rcutorture/bin/parse-console.sh b/tools/testing/selftests/rcutorture/bin/parse-console.sh new file mode 100755 index 000000000..e03338091 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/parse-console.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Check the console output from an rcutorture run for oopses. +# The "file" is a pathname on the local system, and "title" is +# a text string for error-message purposes. +# +# Usage: parse-console.sh file title +# +# Copyright (C) IBM Corporation, 2011 +# +# Authors: Paul E. McKenney <paulmck@linux.ibm.com> + +T=${TMPDIR-/tmp}/parse-console.sh.$$ +file="$1" +title="$2" + +trap 'rm -f $T.seq $T.diags' 0 + +. functions.sh + +# Check for presence and readability of console output file +if test -f "$file" -a -r "$file" +then + : +else + echo $title unreadable console output file: $file + exit 1 +fi +if grep -Pq '\x00' < $file +then + print_warning Console output contains nul bytes, old qemu still running? +fi +cat /dev/null > $file.diags + +# Check for proper termination, except for rcuscale and refscale. +if test "$TORTURE_SUITE" != rcuscale && test "$TORTURE_SUITE" != refscale +then + # check for abject failure + + if grep -q FAILURE $file || grep -q -e '-torture.*!!!' $file + then + nerrs=`grep --binary-files=text '!!!' $file | + tail -1 | + awk ' + { + normalexit = 1; + for (i=NF-8;i<=NF;i++) { + if (i <= 0 || i !~ /^[0-9]*$/) { + bangstring = $0; + gsub(/^\[[^]]*] /, "", bangstring); + print bangstring; + normalexit = 0; + exit 0; + } + sum+=$i; + } + } + END { + if (normalexit) + print sum " instances" + }'` + print_bug $title FAILURE, $nerrs + exit + fi + + grep --binary-files=text 'torture:.*ver:' $file | + egrep --binary-files=text -v '\(null\)|rtc: 000000000* ' | + sed -e 's/^(initramfs)[^]]*] //' -e 's/^\[[^]]*] //' | + sed -e 's/^.*ver: //' | + awk ' + BEGIN { + ver = 0; + badseq = 0; + } + + { + if (!badseq && ($1 + 0 != $1 || $1 <= ver)) { + badseqno1 = ver; + badseqno2 = $1; + badseqnr = NR; + badseq = 1; + } + ver = $1 + } + + END { + if (badseq) { + if (badseqno1 == badseqno2 && badseqno2 == ver) + print "GP HANG at " ver " torture stat " badseqnr; + else + print "BAD SEQ " badseqno1 ":" badseqno2 " last:" ver " version " badseqnr; + } + }' > $T.seq + + if grep -q SUCCESS $file + then + if test -s $T.seq + then + print_warning $title `cat $T.seq` + echo " " $file + exit 2 + fi + else + if grep -q "_HOTPLUG:" $file + then + print_warning HOTPLUG FAILURES $title `cat $T.seq` + echo " " $file + exit 3 + fi + echo $title no success message, `grep --binary-files=text 'ver:' $file | wc -l` successful version messages + if test -s $T.seq + then + print_warning $title `cat $T.seq` + fi + exit 2 + fi +fi | tee -a $file.diags + +console-badness.sh < $file > $T.diags +if test -s $T.diags +then + print_warning "Assertion failure in $file $title" + # cat $T.diags + summary="" + n_badness=`grep -c Badness $file` + if test "$n_badness" -ne 0 + then + summary="$summary Badness: $n_badness" + fi + n_warn=`grep -v 'Warning: unable to open an initial console' $file | egrep -c 'WARNING:|Warn'` + if test "$n_warn" -ne 0 + then + summary="$summary Warnings: $n_warn" + fi + n_bugs=`egrep -c 'BUG|Oops:' $file` + if test "$n_bugs" -ne 0 + then + summary="$summary Bugs: $n_bugs" + fi + n_calltrace=`grep -c 'Call Trace:' $file` + if test "$n_calltrace" -ne 0 + then + summary="$summary Call Traces: $n_calltrace" + fi + n_lockdep=`grep -c =========== $file` + if test "$n_badness" -ne 0 + then + summary="$summary lockdep: $n_badness" + fi + n_stalls=`egrep -c 'detected stalls on CPUs/tasks:|self-detected stall on CPU|Stall ended before state dump start|\?\?\? Writer stall state' $file` + if test "$n_stalls" -ne 0 + then + summary="$summary Stalls: $n_stalls" + fi + n_starves=`grep -c 'rcu_.*kthread starved for' $file` + if test "$n_starves" -ne 0 + then + summary="$summary Starves: $n_starves" + fi + print_warning Summary: $summary + cat $T.diags >> $file.diags +fi +for i in $file.*.diags +do + if test -f "$i" + then + cat $i >> $file.diags + fi +done +if ! test -s $file.diags +then + rm -f $file.diags +fi |