1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0+ 3# 4# Run a series of tests under KVM. By default, this series is specified 5# by the relevant CFLIST file, but can be overridden by the --configs 6# command-line argument. 7# 8# Usage: kvm.sh [ options ] 9# 10# Copyright (C) IBM Corporation, 2011 11# 12# Authors: Paul E. McKenney <paulmck@linux.ibm.com> 13 14scriptname=$0 15args="$*" 16 17T=${TMPDIR-/tmp}/kvm.sh.$$ 18trap 'rm -rf $T' 0 19mkdir $T 20 21cd `dirname $scriptname`/../../../../../ 22 23dur=$((30*60)) 24dryrun="" 25KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM 26PATH=${KVM}/bin:$PATH; export PATH 27TORTURE_DEFCONFIG=defconfig 28TORTURE_BOOT_IMAGE="" 29TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD 30TORTURE_KCONFIG_ARG="" 31TORTURE_KMAKE_ARG="" 32TORTURE_QEMU_MEM=512 33TORTURE_SHUTDOWN_GRACE=180 34TORTURE_SUITE=rcu 35resdir="" 36configs="" 37cpus=0 38ds=`date +%Y.%m.%d-%H:%M:%S` 39jitter="-1" 40 41. functions.sh 42 43usage () { 44 echo "Usage: $scriptname optional arguments:" 45 echo " --bootargs kernel-boot-arguments" 46 echo " --bootimage relative-path-to-kernel-boot-image" 47 echo " --buildonly" 48 echo " --configs \"config-file list w/ repeat factor (3*TINY01)\"" 49 echo " --cpus N" 50 echo " --datestamp string" 51 echo " --defconfig string" 52 echo " --dryrun sched|script" 53 echo " --duration minutes" 54 echo " --interactive" 55 echo " --jitter N [ maxsleep (us) [ maxspin (us) ] ]" 56 echo " --kconfig Kconfig-options" 57 echo " --kmake-arg kernel-make-arguments" 58 echo " --mac nn:nn:nn:nn:nn:nn" 59 echo " --memory megabytes | nnnG" 60 echo " --no-initrd" 61 echo " --qemu-args qemu-arguments" 62 echo " --qemu-cmd qemu-system-..." 63 echo " --results absolute-pathname" 64 echo " --torture rcu" 65 exit 1 66} 67 68while test $# -gt 0 69do 70 case "$1" in 71 --bootargs|--bootarg) 72 checkarg --bootargs "(list of kernel boot arguments)" "$#" "$2" '.*' '^--' 73 TORTURE_BOOTARGS="$2" 74 shift 75 ;; 76 --bootimage) 77 checkarg --bootimage "(relative path to kernel boot image)" "$#" "$2" '[a-zA-Z0-9][a-zA-Z0-9_]*' '^--' 78 TORTURE_BOOT_IMAGE="$2" 79 shift 80 ;; 81 --buildonly) 82 TORTURE_BUILDONLY=1 83 ;; 84 --configs|--config) 85 checkarg --configs "(list of config files)" "$#" "$2" '^[^/]*$' '^--' 86 configs="$2" 87 shift 88 ;; 89 --cpus) 90 checkarg --cpus "(number)" "$#" "$2" '^[0-9]*$' '^--' 91 cpus=$2 92 shift 93 ;; 94 --datestamp) 95 checkarg --datestamp "(relative pathname)" "$#" "$2" '^[^/]*$' '^--' 96 ds=$2 97 shift 98 ;; 99 --defconfig) 100 checkarg --defconfig "defconfigtype" "$#" "$2" '^[^/][^/]*$' '^--' 101 TORTURE_DEFCONFIG=$2 102 shift 103 ;; 104 --dryrun) 105 checkarg --dryrun "sched|script" $# "$2" 'sched\|script' '^--' 106 dryrun=$2 107 shift 108 ;; 109 --duration) 110 checkarg --duration "(minutes)" $# "$2" '^[0-9]*$' '^error' 111 dur=$(($2*60)) 112 shift 113 ;; 114 --interactive) 115 TORTURE_QEMU_INTERACTIVE=1; export TORTURE_QEMU_INTERACTIVE 116 ;; 117 --jitter) 118 checkarg --jitter "(# threads [ sleep [ spin ] ])" $# "$2" '^-\{,1\}[0-9]\+\( \+[0-9]\+\)\{,2\} *$' '^error$' 119 jitter="$2" 120 shift 121 ;; 122 --kconfig) 123 checkarg --kconfig "(Kconfig options)" $# "$2" '^CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\( CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\)*$' '^error$' 124 TORTURE_KCONFIG_ARG="$2" 125 shift 126 ;; 127 --kmake-arg) 128 checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$' 129 TORTURE_KMAKE_ARG="$2" 130 shift 131 ;; 132 --mac) 133 checkarg --mac "(MAC address)" $# "$2" '^\([0-9a-fA-F]\{2\}:\)\{5\}[0-9a-fA-F]\{2\}$' error 134 TORTURE_QEMU_MAC=$2 135 shift 136 ;; 137 --memory) 138 checkarg --memory "(memory size)" $# "$2" '^[0-9]\+[MG]\?$' error 139 TORTURE_QEMU_MEM=$2 140 shift 141 ;; 142 --no-initrd) 143 TORTURE_INITRD=""; export TORTURE_INITRD 144 ;; 145 --qemu-args|--qemu-arg) 146 checkarg --qemu-args "(qemu arguments)" $# "$2" '^-' '^error' 147 TORTURE_QEMU_ARG="$2" 148 shift 149 ;; 150 --qemu-cmd) 151 checkarg --qemu-cmd "(qemu-system-...)" $# "$2" 'qemu-system-' '^--' 152 TORTURE_QEMU_CMD="$2" 153 shift 154 ;; 155 --results) 156 checkarg --results "(absolute pathname)" "$#" "$2" '^/' '^error' 157 resdir=$2 158 shift 159 ;; 160 --shutdown-grace) 161 checkarg --shutdown-grace "(seconds)" "$#" "$2" '^[0-9]*$' '^error' 162 TORTURE_SHUTDOWN_GRACE=$2 163 shift 164 ;; 165 --torture) 166 checkarg --torture "(suite name)" "$#" "$2" '^\(lock\|rcu\|rcuperf\)$' '^--' 167 TORTURE_SUITE=$2 168 shift 169 if test "$TORTURE_SUITE" = rcuperf 170 then 171 # If you really want jitter for rcuperf, specify 172 # it after specifying rcuperf. (But why?) 173 jitter=0 174 fi 175 ;; 176 *) 177 echo Unknown argument $1 178 usage 179 ;; 180 esac 181 shift 182done 183 184if test -z "$TORTURE_INITRD" || tools/testing/selftests/rcutorture/bin/mkinitrd.sh 185then 186 : 187else 188 echo No initrd and unable to create one, aborting test >&2 189 exit 1 190fi 191 192CONFIGFRAG=${KVM}/configs/${TORTURE_SUITE}; export CONFIGFRAG 193 194if test -z "$configs" 195then 196 configs="`cat $CONFIGFRAG/CFLIST`" 197fi 198 199if test -z "$resdir" 200then 201 resdir=$KVM/res 202fi 203 204# Create a file of test-name/#cpus pairs, sorted by decreasing #cpus. 205touch $T/cfgcpu 206for CF in $configs 207do 208 case $CF in 209 [0-9]\**|[0-9][0-9]\**|[0-9][0-9][0-9]\**) 210 config_reps=`echo $CF | sed -e 's/\*.*$//'` 211 CF1=`echo $CF | sed -e 's/^[^*]*\*//'` 212 ;; 213 *) 214 config_reps=1 215 CF1=$CF 216 ;; 217 esac 218 if test -f "$CONFIGFRAG/$CF1" 219 then 220 cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$CF1` 221 cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"` 222 cpu_count=`configfrag_boot_maxcpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"` 223 for ((cur_rep=0;cur_rep<$config_reps;cur_rep++)) 224 do 225 echo $CF1 $cpu_count >> $T/cfgcpu 226 done 227 else 228 echo "The --configs file $CF1 does not exist, terminating." 229 exit 1 230 fi 231done 232sort -k2nr $T/cfgcpu -T="$T" > $T/cfgcpu.sort 233 234# Use a greedy bin-packing algorithm, sorting the list accordingly. 235awk < $T/cfgcpu.sort > $T/cfgcpu.pack -v ncpus=$cpus ' 236BEGIN { 237 njobs = 0; 238} 239 240{ 241 # Read file of tests and corresponding required numbers of CPUs. 242 cf[njobs] = $1; 243 cpus[njobs] = $2; 244 njobs++; 245} 246 247END { 248 batch = 0; 249 nc = -1; 250 251 # Each pass through the following loop creates on test batch 252 # that can be executed concurrently given ncpus. Note that a 253 # given test that requires more than the available CPUs will run in 254 # their own batch. Such tests just have to make do with what 255 # is available. 256 while (nc != ncpus) { 257 batch++; 258 nc = ncpus; 259 260 # Each pass through the following loop considers one 261 # test for inclusion in the current batch. 262 for (i = 0; i < njobs; i++) { 263 if (done[i]) 264 continue; # Already part of a batch. 265 if (nc >= cpus[i] || nc == ncpus) { 266 267 # This test fits into the current batch. 268 done[i] = batch; 269 nc -= cpus[i]; 270 if (nc <= 0) 271 break; # Too-big test in its own batch. 272 } 273 } 274 } 275 276 # Dump out the tests in batch order. 277 for (b = 1; b <= batch; b++) 278 for (i = 0; i < njobs; i++) 279 if (done[i] == b) 280 print cf[i], cpus[i]; 281}' 282 283# Generate a script to execute the tests in appropriate batches. 284cat << ___EOF___ > $T/script 285CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG 286KVM="$KVM"; export KVM 287PATH="$PATH"; export PATH 288TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE 289TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY 290TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG 291TORTURE_INITRD="$TORTURE_INITRD"; export TORTURE_INITRD 292TORTURE_KCONFIG_ARG="$TORTURE_KCONFIG_ARG"; export TORTURE_KCONFIG_ARG 293TORTURE_KMAKE_ARG="$TORTURE_KMAKE_ARG"; export TORTURE_KMAKE_ARG 294TORTURE_QEMU_CMD="$TORTURE_QEMU_CMD"; export TORTURE_QEMU_CMD 295TORTURE_QEMU_INTERACTIVE="$TORTURE_QEMU_INTERACTIVE"; export TORTURE_QEMU_INTERACTIVE 296TORTURE_QEMU_MAC="$TORTURE_QEMU_MAC"; export TORTURE_QEMU_MAC 297TORTURE_QEMU_MEM="$TORTURE_QEMU_MEM"; export TORTURE_QEMU_MEM 298TORTURE_SHUTDOWN_GRACE="$TORTURE_SHUTDOWN_GRACE"; export TORTURE_SHUTDOWN_GRACE 299TORTURE_SUITE="$TORTURE_SUITE"; export TORTURE_SUITE 300if ! test -e $resdir 301then 302 mkdir -p "$resdir" || : 303fi 304mkdir $resdir/$ds 305echo Results directory: $resdir/$ds 306echo $scriptname $args 307touch $resdir/$ds/log 308echo $scriptname $args >> $resdir/$ds/log 309echo ${TORTURE_SUITE} > $resdir/$ds/TORTURE_SUITE 310pwd > $resdir/$ds/testid.txt 311if test -d .git 312then 313 git status >> $resdir/$ds/testid.txt 314 git rev-parse HEAD >> $resdir/$ds/testid.txt 315 git diff HEAD >> $resdir/$ds/testid.txt 316fi 317___EOF___ 318awk < $T/cfgcpu.pack \ 319 -v TORTURE_BUILDONLY="$TORTURE_BUILDONLY" \ 320 -v CONFIGDIR="$CONFIGFRAG/" \ 321 -v KVM="$KVM" \ 322 -v ncpus=$cpus \ 323 -v jitter="$jitter" \ 324 -v rd=$resdir/$ds/ \ 325 -v dur=$dur \ 326 -v TORTURE_QEMU_ARG="$TORTURE_QEMU_ARG" \ 327 -v TORTURE_BOOTARGS="$TORTURE_BOOTARGS" \ 328'BEGIN { 329 i = 0; 330} 331 332{ 333 cf[i] = $1; 334 cpus[i] = $2; 335 i++; 336} 337 338# Dump out the scripting required to run one test batch. 339function dump(first, pastlast, batchnum) 340{ 341 print "echo ----Start batch " batchnum ": `date` | tee -a " rd "log"; 342 print "needqemurun=" 343 jn=1 344 for (j = first; j < pastlast; j++) { 345 builddir=KVM "/b1" 346 cpusr[jn] = cpus[j]; 347 if (cfrep[cf[j]] == "") { 348 cfr[jn] = cf[j]; 349 cfrep[cf[j]] = 1; 350 } else { 351 cfrep[cf[j]]++; 352 cfr[jn] = cf[j] "." cfrep[cf[j]]; 353 } 354 if (cpusr[jn] > ncpus && ncpus != 0) 355 ovf = "-ovf"; 356 else 357 ovf = ""; 358 print "echo ", cfr[jn], cpusr[jn] ovf ": Starting build. `date` | tee -a " rd "log"; 359 print "rm -f " builddir ".*"; 360 print "touch " builddir ".wait"; 361 print "mkdir " builddir " > /dev/null 2>&1 || :"; 362 print "mkdir " rd cfr[jn] " || :"; 363 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 &" 364 print "echo ", cfr[jn], cpusr[jn] ovf ": Waiting for build to complete. `date` | tee -a " rd "log"; 365 print "while test -f " builddir ".wait" 366 print "do" 367 print "\tsleep 1" 368 print "done" 369 print "echo ", cfr[jn], cpusr[jn] ovf ": Build complete. `date` | tee -a " rd "log"; 370 jn++; 371 } 372 for (j = 1; j < jn; j++) { 373 builddir=KVM "/b" j 374 print "rm -f " builddir ".ready" 375 print "if test -f \"" rd cfr[j] "/builtkernel\"" 376 print "then" 377 print "\techo ----", cfr[j], cpusr[j] ovf ": Kernel present. `date` | tee -a " rd "log"; 378 print "\tneedqemurun=1" 379 print "fi" 380 } 381 njitter = 0; 382 split(jitter, ja); 383 if (ja[1] == -1 && ncpus == 0) 384 njitter = 1; 385 else if (ja[1] == -1) 386 njitter = ncpus; 387 else 388 njitter = ja[1]; 389 if (TORTURE_BUILDONLY && njitter != 0) { 390 njitter = 0; 391 print "echo Build-only run, so suppressing jitter | tee -a " rd "log" 392 } 393 if (TORTURE_BUILDONLY) { 394 print "needqemurun=" 395 } 396 print "if test -n \"$needqemurun\"" 397 print "then" 398 print "\techo ---- Starting kernels. `date` | tee -a " rd "log"; 399 for (j = 0; j < njitter; j++) 400 print "\tjitter.sh " j " " dur " " ja[2] " " ja[3] "&" 401 print "\twait" 402 print "\techo ---- All kernel runs complete. `date` | tee -a " rd "log"; 403 print "else" 404 print "\twait" 405 print "\techo ---- No kernel runs. `date` | tee -a " rd "log"; 406 print "fi" 407 for (j = 1; j < jn; j++) { 408 builddir=KVM "/b" j 409 print "echo ----", cfr[j], cpusr[j] ovf ": Build/run results: | tee -a " rd "log"; 410 print "cat " rd cfr[j] "/kvm-test-1-run.sh.out | tee -a " rd "log"; 411 } 412} 413 414END { 415 njobs = i; 416 nc = ncpus; 417 first = 0; 418 batchnum = 1; 419 420 # Each pass through the following loop considers one test. 421 for (i = 0; i < njobs; i++) { 422 if (ncpus == 0) { 423 # Sequential test specified, each test its own batch. 424 dump(i, i + 1, batchnum); 425 first = i; 426 batchnum++; 427 } else if (nc < cpus[i] && i != 0) { 428 # Out of CPUs, dump out a batch. 429 dump(first, i, batchnum); 430 first = i; 431 nc = ncpus; 432 batchnum++; 433 } 434 # Account for the CPUs needed by the current test. 435 nc -= cpus[i]; 436 } 437 # Dump the last batch. 438 if (ncpus != 0) 439 dump(first, i, batchnum); 440}' >> $T/script 441 442cat << ___EOF___ >> $T/script 443echo 444echo 445echo " --- `date` Test summary:" 446echo Results directory: $resdir/$ds 447kvm-recheck.sh $resdir/$ds 448___EOF___ 449 450if test "$dryrun" = script 451then 452 cat $T/script 453 exit 0 454elif test "$dryrun" = sched 455then 456 # Extract the test run schedule from the script. 457 egrep 'Start batch|Starting build\.' $T/script | 458 grep -v ">>" | 459 sed -e 's/:.*$//' -e 's/^echo //' 460 exit 0 461else 462 # Not a dryrun, so run the script. 463 sh $T/script 464fi 465 466# 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 467