xref: /linux/tools/testing/selftests/rcutorture/bin/kvm.sh (revision 8fc4e4aa2bfca8d32e8bc2a01526ea2da450e6cb)
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
27. functions.sh
28
29TORTURE_ALLOTED_CPUS="`identify_qemu_vcpus`"
30TORTURE_DEFCONFIG=defconfig
31TORTURE_BOOT_IMAGE=""
32TORTURE_BUILDONLY=
33TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD
34TORTURE_KCONFIG_ARG=""
35TORTURE_KCONFIG_GDB_ARG=""
36TORTURE_BOOT_GDB_ARG=""
37TORTURE_QEMU_GDB_ARG=""
38TORTURE_JITTER_START=""
39TORTURE_JITTER_STOP=""
40TORTURE_KCONFIG_KASAN_ARG=""
41TORTURE_KCONFIG_KCSAN_ARG=""
42TORTURE_KMAKE_ARG=""
43TORTURE_QEMU_MEM=512
44TORTURE_SHUTDOWN_GRACE=180
45TORTURE_SUITE=rcu
46TORTURE_MOD=rcutorture
47TORTURE_TRUST_MAKE=""
48resdir=""
49configs=""
50cpus=0
51ds=`date +%Y.%m.%d-%H.%M.%S`
52jitter="-1"
53
54startdate="`date`"
55starttime="`get_starttime`"
56
57usage () {
58	echo "Usage: $scriptname optional arguments:"
59	echo "       --allcpus"
60	echo "       --bootargs kernel-boot-arguments"
61	echo "       --bootimage relative-path-to-kernel-boot-image"
62	echo "       --buildonly"
63	echo "       --configs \"config-file list w/ repeat factor (3*TINY01)\""
64	echo "       --cpus N"
65	echo "       --datestamp string"
66	echo "       --defconfig string"
67	echo "       --dryrun batches|sched|script"
68	echo "       --duration minutes | <seconds>s | <hours>h | <days>d"
69	echo "       --gdb"
70	echo "       --help"
71	echo "       --interactive"
72	echo "       --jitter N [ maxsleep (us) [ maxspin (us) ] ]"
73	echo "       --kconfig Kconfig-options"
74	echo "       --kmake-arg kernel-make-arguments"
75	echo "       --mac nn:nn:nn:nn:nn:nn"
76	echo "       --memory megabytes|nnnG"
77	echo "       --no-initrd"
78	echo "       --qemu-args qemu-arguments"
79	echo "       --qemu-cmd qemu-system-..."
80	echo "       --results absolute-pathname"
81	echo "       --torture lock|rcu|rcuscale|refscale|scf"
82	echo "       --trust-make"
83	exit 1
84}
85
86while test $# -gt 0
87do
88	case "$1" in
89	--allcpus)
90		cpus=$TORTURE_ALLOTED_CPUS
91		max_cpus=$TORTURE_ALLOTED_CPUS
92		;;
93	--bootargs|--bootarg)
94		checkarg --bootargs "(list of kernel boot arguments)" "$#" "$2" '.*' '^--'
95		TORTURE_BOOTARGS="$TORTURE_BOOTARGS $2"
96		shift
97		;;
98	--bootimage)
99		checkarg --bootimage "(relative path to kernel boot image)" "$#" "$2" '[a-zA-Z0-9][a-zA-Z0-9_]*' '^--'
100		TORTURE_BOOT_IMAGE="$2"
101		shift
102		;;
103	--buildonly|--build-only)
104		TORTURE_BUILDONLY=1
105		;;
106	--configs|--config)
107		checkarg --configs "(list of config files)" "$#" "$2" '^[^/.a-z]\+$' '^--'
108		configs="$configs $2"
109		shift
110		;;
111	--cpus)
112		checkarg --cpus "(number)" "$#" "$2" '^[0-9]*$' '^--'
113		cpus=$2
114		TORTURE_ALLOTED_CPUS="$2"
115		max_cpus="`identify_qemu_vcpus`"
116		if test "$TORTURE_ALLOTED_CPUS" -gt "$max_cpus"
117		then
118			TORTURE_ALLOTED_CPUS=$max_cpus
119		fi
120		shift
121		;;
122	--datestamp)
123		checkarg --datestamp "(relative pathname)" "$#" "$2" '^[a-zA-Z0-9._/-]*$' '^--'
124		ds=$2
125		shift
126		;;
127	--defconfig)
128		checkarg --defconfig "defconfigtype" "$#" "$2" '^[^/][^/]*$' '^--'
129		TORTURE_DEFCONFIG=$2
130		shift
131		;;
132	--dryrun)
133		checkarg --dryrun "batches|sched|script" $# "$2" 'batches\|sched\|script' '^--'
134		dryrun=$2
135		shift
136		;;
137	--duration)
138		checkarg --duration "(minutes)" $# "$2" '^[0-9][0-9]*\(s\|m\|h\|d\|\)$' '^error'
139		mult=60
140		if echo "$2" | grep -q 's$'
141		then
142			mult=1
143		elif echo "$2" | grep -q 'h$'
144		then
145			mult=3600
146		elif echo "$2" | grep -q 'd$'
147		then
148			mult=86400
149		fi
150		ts=`echo $2 | sed -e 's/[smhd]$//'`
151		dur=$(($ts*mult))
152		shift
153		;;
154	--gdb)
155		TORTURE_KCONFIG_GDB_ARG="CONFIG_DEBUG_INFO=y"; export TORTURE_KCONFIG_GDB_ARG
156		TORTURE_BOOT_GDB_ARG="nokaslr"; export TORTURE_BOOT_GDB_ARG
157		TORTURE_QEMU_GDB_ARG="-s -S"; export TORTURE_QEMU_GDB_ARG
158		;;
159	--help|-h)
160		usage
161		;;
162	--interactive)
163		TORTURE_QEMU_INTERACTIVE=1; export TORTURE_QEMU_INTERACTIVE
164		;;
165	--jitter)
166		checkarg --jitter "(# threads [ sleep [ spin ] ])" $# "$2" '^-\{,1\}[0-9]\+\( \+[0-9]\+\)\{,2\} *$' '^error$'
167		jitter="$2"
168		shift
169		;;
170	--kconfig|--kconfigs)
171		checkarg --kconfig "(Kconfig options)" $# "$2" '^CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\( CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\)*$' '^error$'
172		TORTURE_KCONFIG_ARG="`echo "$TORTURE_KCONFIG_ARG $2" | sed -e 's/^ *//' -e 's/ *$//'`"
173		shift
174		;;
175	--kasan)
176		TORTURE_KCONFIG_KASAN_ARG="CONFIG_DEBUG_INFO=y CONFIG_KASAN=y"; export TORTURE_KCONFIG_KASAN_ARG
177		;;
178	--kcsan)
179		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_INTERRUPT_WATCHER=y CONFIG_KCSAN_VERBOSE=y CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=y"; export TORTURE_KCONFIG_KCSAN_ARG
180		;;
181	--kmake-arg|--kmake-args)
182		checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$'
183		TORTURE_KMAKE_ARG="`echo "$TORTURE_KMAKE_ARG $2" | sed -e 's/^ *//' -e 's/ *$//'`"
184		shift
185		;;
186	--mac)
187		checkarg --mac "(MAC address)" $# "$2" '^\([0-9a-fA-F]\{2\}:\)\{5\}[0-9a-fA-F]\{2\}$' error
188		TORTURE_QEMU_MAC=$2
189		shift
190		;;
191	--memory)
192		checkarg --memory "(memory size)" $# "$2" '^[0-9]\+[MG]\?$' error
193		TORTURE_QEMU_MEM=$2
194		shift
195		;;
196	--no-initrd)
197		TORTURE_INITRD=""; export TORTURE_INITRD
198		;;
199	--qemu-args|--qemu-arg)
200		checkarg --qemu-args "(qemu arguments)" $# "$2" '^-' '^error'
201		TORTURE_QEMU_ARG="`echo "$TORTURE_QEMU_ARG $2" | sed -e 's/^ *//' -e 's/ *$//'`"
202		shift
203		;;
204	--qemu-cmd)
205		checkarg --qemu-cmd "(qemu-system-...)" $# "$2" 'qemu-system-' '^--'
206		TORTURE_QEMU_CMD="$2"
207		shift
208		;;
209	--results)
210		checkarg --results "(absolute pathname)" "$#" "$2" '^/' '^error'
211		resdir=$2
212		shift
213		;;
214	--shutdown-grace)
215		checkarg --shutdown-grace "(seconds)" "$#" "$2" '^[0-9]*$' '^error'
216		TORTURE_SHUTDOWN_GRACE=$2
217		shift
218		;;
219	--torture)
220		checkarg --torture "(suite name)" "$#" "$2" '^\(lock\|rcu\|rcuscale\|refscale\|scf\)$' '^--'
221		TORTURE_SUITE=$2
222		TORTURE_MOD="`echo $TORTURE_SUITE | sed -e 's/^\(lock\|rcu\|scf\)$/\1torture/'`"
223		shift
224		if test "$TORTURE_SUITE" = rcuscale || test "$TORTURE_SUITE" = refscale
225		then
226			# If you really want jitter for refscale or
227			# rcuscale, specify it after specifying the rcuscale
228			# or the refscale.  (But why jitter in these cases?)
229			jitter=0
230		fi
231		;;
232	--trust-make)
233		TORTURE_TRUST_MAKE="y"
234		;;
235	*)
236		echo Unknown argument $1
237		usage
238		;;
239	esac
240	shift
241done
242
243if test -n "$dryrun" || test -z "$TORTURE_INITRD" || tools/testing/selftests/rcutorture/bin/mkinitrd.sh
244then
245	:
246else
247	echo No initrd and unable to create one, aborting test >&2
248	exit 1
249fi
250
251CONFIGFRAG=${KVM}/configs/${TORTURE_SUITE}; export CONFIGFRAG
252
253defaultconfigs="`tr '\012' ' ' < $CONFIGFRAG/CFLIST`"
254if test -z "$configs"
255then
256	configs=$defaultconfigs
257fi
258
259if test -z "$resdir"
260then
261	resdir=$KVM/res
262fi
263
264# Create a file of test-name/#cpus pairs, sorted by decreasing #cpus.
265configs_derep=
266for CF in $configs
267do
268	case $CF in
269	[0-9]\**|[0-9][0-9]\**|[0-9][0-9][0-9]\**)
270		config_reps=`echo $CF | sed -e 's/\*.*$//'`
271		CF1=`echo $CF | sed -e 's/^[^*]*\*//'`
272		;;
273	*)
274		config_reps=1
275		CF1=$CF
276		;;
277	esac
278	for ((cur_rep=0;cur_rep<$config_reps;cur_rep++))
279	do
280		configs_derep="$configs_derep $CF1"
281	done
282done
283touch $T/cfgcpu
284configs_derep="`echo $configs_derep | sed -e "s/\<CFLIST\>/$defaultconfigs/g"`"
285if test -n "$TORTURE_KCONFIG_GDB_ARG"
286then
287	if test "`echo $configs_derep | wc -w`" -gt 1
288	then
289		echo "The --config list is: $configs_derep."
290		echo "Only one --config permitted with --gdb, terminating."
291		exit 1
292	fi
293fi
294echo 'BEGIN {' > $T/cfgcpu.awk
295for CF1 in `echo $configs_derep | tr -s ' ' '\012' | sort -u`
296do
297	if test -f "$CONFIGFRAG/$CF1"
298	then
299		if echo "$TORTURE_KCONFIG_ARG" | grep -q '\<CONFIG_NR_CPUS='
300		then
301			echo "$TORTURE_KCONFIG_ARG" | tr -s ' ' | tr ' ' '\012' > $T/KCONFIG_ARG
302			cpu_count=`configNR_CPUS.sh $T/KCONFIG_ARG`
303		else
304			cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$CF1`
305		fi
306		cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"`
307		cpu_count=`configfrag_boot_maxcpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"`
308		echo 'scenariocpu["'"$CF1"'"] = '"$cpu_count"';' >> $T/cfgcpu.awk
309	else
310		echo "The --configs file $CF1 does not exist, terminating."
311		exit 1
312	fi
313done
314cat << '___EOF___' >> $T/cfgcpu.awk
315}
316{
317	for (i = 1; i <= NF; i++)
318		print $i, scenariocpu[$i];
319}
320___EOF___
321echo $configs_derep | awk -f $T/cfgcpu.awk > $T/cfgcpu
322sort -k2nr $T/cfgcpu -T="$T" > $T/cfgcpu.sort
323
324# Use a greedy bin-packing algorithm, sorting the list accordingly.
325awk < $T/cfgcpu.sort > $T/cfgcpu.pack -v ncpus=$cpus '
326BEGIN {
327	njobs = 0;
328}
329
330{
331	# Read file of tests and corresponding required numbers of CPUs.
332	cf[njobs] = $1;
333	cpus[njobs] = $2;
334	njobs++;
335}
336
337END {
338	batch = 0;
339	nc = -1;
340
341	# Each pass through the following loop creates on test batch that
342	# can be executed concurrently given ncpus.  Note that a given test
343	# that requires more than the available CPUs will run in its own
344	# batch.  Such tests just have to make do with what is available.
345	while (nc != ncpus) {
346		batch++;
347		nc = ncpus;
348
349		# Each pass through the following loop considers one
350		# test for inclusion in the current batch.
351		for (i = 0; i < njobs; i++) {
352			if (done[i])
353				continue; # Already part of a batch.
354			if (nc >= cpus[i] || nc == ncpus) {
355
356				# This test fits into the current batch.
357				done[i] = batch;
358				nc -= cpus[i];
359				if (nc <= 0)
360					break; # Too-big test in its own batch.
361			}
362		}
363	}
364
365	# Dump out the tests in batch order.
366	for (b = 1; b <= batch; b++)
367		for (i = 0; i < njobs; i++)
368			if (done[i] == b)
369				print cf[i], cpus[i];
370}'
371
372# Generate a script to execute the tests in appropriate batches.
373cat << ___EOF___ > $T/script
374CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG
375KVM="$KVM"; export KVM
376PATH="$PATH"; export PATH
377TORTURE_ALLOTED_CPUS="$TORTURE_ALLOTED_CPUS"; export TORTURE_ALLOTED_CPUS
378TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE
379TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY
380TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG
381TORTURE_INITRD="$TORTURE_INITRD"; export TORTURE_INITRD
382TORTURE_KCONFIG_ARG="$TORTURE_KCONFIG_ARG"; export TORTURE_KCONFIG_ARG
383TORTURE_KCONFIG_GDB_ARG="$TORTURE_KCONFIG_GDB_ARG"; export TORTURE_KCONFIG_GDB_ARG
384TORTURE_BOOT_GDB_ARG="$TORTURE_BOOT_GDB_ARG"; export TORTURE_BOOT_GDB_ARG
385TORTURE_QEMU_GDB_ARG="$TORTURE_QEMU_GDB_ARG"; export TORTURE_QEMU_GDB_ARG
386TORTURE_KCONFIG_KASAN_ARG="$TORTURE_KCONFIG_KASAN_ARG"; export TORTURE_KCONFIG_KASAN_ARG
387TORTURE_KCONFIG_KCSAN_ARG="$TORTURE_KCONFIG_KCSAN_ARG"; export TORTURE_KCONFIG_KCSAN_ARG
388TORTURE_KMAKE_ARG="$TORTURE_KMAKE_ARG"; export TORTURE_KMAKE_ARG
389TORTURE_MOD="$TORTURE_MOD"; export TORTURE_MOD
390TORTURE_QEMU_CMD="$TORTURE_QEMU_CMD"; export TORTURE_QEMU_CMD
391TORTURE_QEMU_INTERACTIVE="$TORTURE_QEMU_INTERACTIVE"; export TORTURE_QEMU_INTERACTIVE
392TORTURE_QEMU_MAC="$TORTURE_QEMU_MAC"; export TORTURE_QEMU_MAC
393TORTURE_QEMU_MEM="$TORTURE_QEMU_MEM"; export TORTURE_QEMU_MEM
394TORTURE_SHUTDOWN_GRACE="$TORTURE_SHUTDOWN_GRACE"; export TORTURE_SHUTDOWN_GRACE
395TORTURE_SUITE="$TORTURE_SUITE"; export TORTURE_SUITE
396TORTURE_TRUST_MAKE="$TORTURE_TRUST_MAKE"; export TORTURE_TRUST_MAKE
397if ! test -e $resdir
398then
399	mkdir -p "$resdir" || :
400fi
401mkdir -p $resdir/$ds
402TORTURE_RESDIR="$resdir/$ds"; export TORTURE_RESDIR
403TORTURE_STOPFILE="$resdir/$ds/STOP.1"; export TORTURE_STOPFILE
404echo Results directory: $resdir/$ds
405echo $scriptname $args
406touch $resdir/$ds/log
407echo $scriptname $args >> $resdir/$ds/log
408echo ${TORTURE_SUITE} > $resdir/$ds/torture_suite
409echo Build directory: `pwd` > $resdir/$ds/testid.txt
410if test -d .git
411then
412	echo Current commit: `git rev-parse HEAD` >> $resdir/$ds/testid.txt
413	echo >> $resdir/$ds/testid.txt
414	echo ' ---' Output of "'"git status"'": >> $resdir/$ds/testid.txt
415	git status >> $resdir/$ds/testid.txt
416	echo >> $resdir/$ds/testid.txt
417	echo >> $resdir/$ds/testid.txt
418	echo ' ---' Output of "'"git diff HEAD"'": >> $resdir/$ds/testid.txt
419	git diff HEAD >> $resdir/$ds/testid.txt
420fi
421___EOF___
422awk < $T/cfgcpu.pack \
423	-v TORTURE_BUILDONLY="$TORTURE_BUILDONLY" \
424	-v CONFIGDIR="$CONFIGFRAG/" \
425	-v KVM="$KVM" \
426	-v ncpus=$cpus \
427	-v jitter="$jitter" \
428	-v rd=$resdir/$ds/ \
429	-v dur=$dur \
430	-v TORTURE_QEMU_ARG="$TORTURE_QEMU_ARG" \
431	-v TORTURE_BOOTARGS="$TORTURE_BOOTARGS" \
432'BEGIN {
433	i = 0;
434}
435
436{
437	cf[i] = $1;
438	cpus[i] = $2;
439	i++;
440}
441
442# Dump out the scripting required to run one test batch.
443function dump(first, pastlast, batchnum)
444{
445	print "echo ----Start batch " batchnum ": `date` | tee -a " rd "log";
446	print "needqemurun="
447	jn=1
448	njitter = 0;
449	split(jitter, ja);
450	if (ja[1] == -1 && ncpus == 0)
451		njitter = 1;
452	else if (ja[1] == -1)
453		njitter = ncpus;
454	else
455		njitter = ja[1];
456	print "TORTURE_JITTER_START=\". jitterstart.sh " njitter " " rd " " dur " " ja[2] " " ja[3] "\"; export TORTURE_JITTER_START";
457	print "TORTURE_JITTER_STOP=\". jitterstop.sh " rd " \"; export TORTURE_JITTER_STOP"
458	for (j = first; j < pastlast; j++) {
459		cpusr[jn] = cpus[j];
460		if (cfrep[cf[j]] == "") {
461			cfr[jn] = cf[j];
462			cfrep[cf[j]] = 1;
463		} else {
464			cfrep[cf[j]]++;
465			cfr[jn] = cf[j] "." cfrep[cf[j]];
466		}
467		builddir=rd cfr[jn] "/build";
468		if (cpusr[jn] > ncpus && ncpus != 0)
469			ovf = "-ovf";
470		else
471			ovf = "";
472		print "echo ", cfr[jn], cpusr[jn] ovf ": Starting build. `date` | tee -a " rd "log";
473		print "mkdir " rd cfr[jn] " || :";
474		print "touch " builddir ".wait";
475		print "kvm-test-1-run.sh " CONFIGDIR cf[j], rd cfr[jn], dur " \"" TORTURE_QEMU_ARG "\" \"" TORTURE_BOOTARGS "\" > " rd cfr[jn]  "/kvm-test-1-run.sh.out 2>&1 &"
476		print "echo ", cfr[jn], cpusr[jn] ovf ": Waiting for build to complete. `date` | tee -a " rd "log";
477		print "while test -f " builddir ".wait"
478		print "do"
479		print "\tsleep 1"
480		print "done"
481		print "echo ", cfr[jn], cpusr[jn] ovf ": Build complete. `date` | tee -a " rd "log";
482		jn++;
483	}
484	print "runfiles="
485	for (j = 1; j < jn; j++) {
486		builddir=rd cfr[j] "/build";
487		if (TORTURE_BUILDONLY)
488			print "rm -f " builddir ".ready"
489		else
490			print "mv " builddir ".ready " builddir ".run"
491			print "runfiles=\"$runfiles " builddir ".run\""
492		fi
493		print "if test -f \"" rd cfr[j] "/builtkernel\""
494		print "then"
495		print "\techo ----", cfr[j], cpusr[j] ovf ": Kernel present. `date` | tee -a " rd "log";
496		print "\tneedqemurun=1"
497		print "fi"
498	}
499	if (TORTURE_BUILDONLY && njitter != 0) {
500		njitter = 0;
501		print "echo Build-only run, so suppressing jitter | tee -a " rd "log"
502	}
503	if (TORTURE_BUILDONLY) {
504		print "needqemurun="
505	}
506	print "if test -n \"$needqemurun\""
507	print "then"
508	print "\techo ---- Starting kernels. `date` | tee -a " rd "log";
509	print "\t$TORTURE_JITTER_START";
510	print "\twhile ls $runfiles > /dev/null 2>&1"
511	print "\tdo"
512	print "\t\t:"
513	print "\tdone"
514	print "\t$TORTURE_JITTER_STOP";
515	print "\techo ---- All kernel runs complete. `date` | tee -a " rd "log";
516	print "else"
517	print "\twait"
518	print "\techo ---- No kernel runs. `date` | tee -a " rd "log";
519	print "fi"
520	for (j = 1; j < jn; j++) {
521		print "echo ----", cfr[j], cpusr[j] ovf ": Build/run results: | tee -a " rd "log";
522		print "cat " rd cfr[j]  "/kvm-test-1-run.sh.out | tee -a " rd "log";
523	}
524}
525
526END {
527	njobs = i;
528	nc = ncpus;
529	first = 0;
530	batchnum = 1;
531
532	# Each pass through the following loop considers one test.
533	for (i = 0; i < njobs; i++) {
534		if (ncpus == 0) {
535			# Sequential test specified, each test its own batch.
536			dump(i, i + 1, batchnum);
537			first = i;
538			batchnum++;
539		} else if (nc < cpus[i] && i != 0) {
540			# Out of CPUs, dump out a batch.
541			dump(first, i, batchnum);
542			first = i;
543			nc = ncpus;
544			batchnum++;
545		}
546		# Account for the CPUs needed by the current test.
547		nc -= cpus[i];
548	}
549	# Dump the last batch.
550	if (ncpus != 0)
551		dump(first, i, batchnum);
552}' >> $T/script
553
554cat << '___EOF___' >> $T/script
555echo | tee -a $TORTURE_RESDIR/log
556echo | tee -a $TORTURE_RESDIR/log
557echo " --- `date` Test summary:" | tee -a $TORTURE_RESDIR/log
558___EOF___
559cat << ___EOF___ >> $T/script
560echo Results directory: $resdir/$ds | tee -a $resdir/$ds/log
561kcsan-collapse.sh $resdir/$ds | tee -a $resdir/$ds/log
562kvm-recheck.sh $resdir/$ds > $T/kvm-recheck.sh.out 2>&1
563___EOF___
564echo 'ret=$?' >> $T/script
565echo "cat $T/kvm-recheck.sh.out | tee -a $resdir/$ds/log" >> $T/script
566echo 'exit $ret' >> $T/script
567
568# Extract the tests and their batches from the script.
569egrep 'Start batch|Starting build\.' $T/script | grep -v ">>" |
570	sed -e 's/:.*$//' -e 's/^echo //' -e 's/-ovf//' |
571	awk '
572	/^----Start/ {
573		batchno = $3;
574		next;
575	}
576	{
577		print batchno, $1, $2
578	}' > $T/batches
579
580if test "$dryrun" = script
581then
582	cat $T/script
583	exit 0
584elif test "$dryrun" = sched
585then
586	# Extract the test run schedule from the script.
587	egrep 'Start batch|Starting build\.' $T/script | grep -v ">>" |
588		sed -e 's/:.*$//' -e 's/^echo //'
589	nbuilds="`grep 'Starting build\.' $T/script |
590		  grep -v ">>" | sed -e 's/:.*$//' -e 's/^echo //' |
591		  awk '{ print $1 }' | grep -v '\.' | wc -l`"
592	echo Total number of builds: $nbuilds
593	nbatches="`grep 'Start batch' $T/script | grep -v ">>" | wc -l`"
594	echo Total number of batches: $nbatches
595	exit 0
596elif test "$dryrun" = batches
597then
598	cat $T/batches
599	exit 0
600else
601	# Not a dryrun.  Record the batches and the number of CPUs, then run the script.
602	bash $T/script
603	ret=$?
604	cp $T/batches $resdir/$ds/batches
605	echo '#' cpus=$cpus >> $resdir/$ds/batches
606	echo " --- Done at `date` (`get_starttime_duration $starttime`) exitcode $ret" | tee -a $resdir/$ds/log
607	exit $ret
608fi
609
610# 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
611# Function-graph tracing: ftrace=function_graph ftrace_graph_filter=sched_setaffinity,migration_cpu_stop
612# Also --kconfig "CONFIG_FUNCTION_TRACER=y CONFIG_FUNCTION_GRAPH_TRACER=y"
613# Control buffer size: --bootargs trace_buf_size=3k
614# Get trace-buffer dumps on all oopses: --bootargs ftrace_dump_on_oops
615# Ditto, but dump only the oopsing CPU: --bootargs ftrace_dump_on_oops=orig_cpu
616# Heavy-handed way to also dump on warnings: --bootargs panic_on_warn
617