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