Re: [PATCH v10 00/12] sched, steal_governor: Introduce preferred CPUs and steal-driven vCPU backoff

Shrikanth Hegde <[email protected]>
Newsgroups dev.linux.lists.virtualization,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Hi Yury.

On 8/22/26 3:57 AM, Yury Norov wrote:
>> ========================================
>> Performance Numbers (powerpc, x86, s390)
>> ========================================
>>
>> PowerPC:
>> ===================
>> VM1: 60VP/30EC and VM2: 30VP/20EC
>> Shared physical CPU pool size: 50 Cores. Each core is SMT8.
>> (VP - Virtual Core, EC - Entitles Core) -  PowerVM terminologies of SPLPAR[2]
>>
>> Default parameter values: 1000ms, 200 low threshold, 500 high threshold
>> Both the VMs are running the same workload. Total throughput/time of VM1+VM2
>> is being mentioned in all cases.
>>
>> Hackbench
>>                baseline    steal_governor        steal_governor
>>                               disabled               enabled
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>
>> 10 groups        5.20   |    5.40 (-3.85%)  |     4.65 (+10.58%)
>> 20 groups       11.39   |   12.01 (-5.44%)  |     7.09 (+37.75%)
>> 40 groups       20.32   |   19.80 (+2.56%)  |    11.31 (+44.34%)
>> 10 groups(-p)    2.37   |    2.26 (+4.64%)  |     2.06 (+13.08%)
>> 20 groups(-p)    3.34   |    3.28 (+1.80%)  |     3.20 (+4.19%)
>> 40 groups(-p)    4.46   |    4.83 (-8.30%)  |     4.26 (+4.48%)
>> Remarks: Net improvement with steal_governor specially high load points.
>>
>> schbench ( -L -n 0 -r 30 -s 0)
>>                baseline    steal_governor          steal_governor
>>                               disabled                 enabled
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> -m 1 -t 128     2475162 |    2621246 (+5.90%)  |      2527299 (+2.11%)
>> -m 1 -t 256     1467350 |    1470032 (+0.18%)  |      1492372 (+1.71%)
>> -m 1 -t 512     1408813 |    1454687 (+3.26%)  |      1437605 (+2.04%)
>> Remarks: Effectively means no-improvements or regressions
>>
>> kernbench	baseline    steal_governor     steal_governor
>> (elapsed time)	               disabled            enabled
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> -j nr_cpus	231      |      235 (-1.7%) |    199 (+14%)
>> Remarks: Net improvement in elapsed time.
>>
>> Daytrader - A real life work which is a proxy for trading based
>> on db2[3]
>>                baseline      steal_governor   steal_governor
>>                                disabled          enabled
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> Load@30%	1x	|	0.96x	|	 1.53x			
>> Load@60%	1x	|	0.94x	|	 1.41x
>> Remarks: Good improvement seen at different load points.
>>
>> When there is no steal time (such as dedicated LPAR, or only VM2
>> is running) throughput was same with steal_governor enabled/disabled
>> which indicates minimal overhead of steal_governor.
>>
>> I have run v10 also on a smaller powerpc LPAR system and it shows
>> good improvements.
>>
>> =======================================================================
>>
>> Data from x86,s390 KVM which Ilya Leoshkevich carried out during OSPM26
>> time. *This was based on v2*. Idea is still the name, numbers are
>> expected to be better in v10 as some of the overhead has been removed.
>> Note: Other variations of the benchmark shows no observable
>> difference.
>>
>> x86:
>> ====
>> cascade-lake: 32 threads = 16 cores
>> Benchmark      #VMs    #CPUs/VM  ΔRPS     (%std)
>> ===============================================
>> hackbench         8          16  90.73% ± 9.97%
>> hackbench         4          24  52.67% ± 7.43%
>> hackbench         4          16  37.96% ± 11.19%
>> hackbench         4          32  37.82% ± 4.38%
>> hackbench        12           8  36.90% ± 4.74%
>> hackbench         8           8  35.30% ± 3.61%
>> pgbench          16           4  31.77% ± 2.44%
>> hackbench         2          24  25.85% ± 8.63%
>> hackbench        16           8  24.87% ± 3.46%
>> pgbench          16           8  21.83% ± 2.20%
>> pgbench          12           8  21.35% ± 2.15%
>> pgbench           8           8  18.46% ± 1.01%
>> hackbench         2          32  15.56% ± 4.53%
>> pgbench          12           4  14.28% ± 2.04%
>> hackbench        16           4  14.07% ± 2.90%
>> hackbench        12           4  9.60% ± 3.49%
>> [...]
>> pgbench           4           8  -1.16% ± 3.60%
>> hackbench         4           4  -1.80% ± 9.55%
>> sysbench         12           4  -2.19% ± 0.78%
>> pgbench           4          24  -2.43% ± 4.38%
>> pgbench           4          32  -3.21% ± 0.79%
>> sysbench         16           4  -3.22% ± 1.09%
>>
>> S390:
>> =====
>> z16: 16 threads = 8 cores (SMT-2)
>> Benchmark      #VMs    #CPUs/VM  ΔRPS    (std%)
>> ===============================================
>> pgbench           2           8  73.50% ± 35.91%
>> pgbench          16           4  61.30% ± 4.09%
>> hackbench        16           4  54.11% ± 4.38%
>> hackbench        12           4  36.34% ± 4.63%
>> pgbench          12           4  34.83% ± 2.57%
>> hackbench         8           4  29.75% ± 5.86%
>> hackbench         8           8  25.98% ± 5.09%
>> pgbench           2           4  23.31% ± 33.44%
>> pgbench           2          16  19.95% ± 17.12%
>> hackbench         4           8  19.43% ± 9.33%
>> pgbench           8           4  19.32% ± 4.50%
>> [...]
>> schbench          8           8  -0.79% ± 0.33%
>> sysbench          8           8  -0.81% ± 0.39%
>> hackbench         4          16  -1.11% ± 5.82%
>> sysbench          8           4  -1.62% ± 0.49%
>> sysbench         16           4  -2.70% ± 0.58%
>> schbench         16           4  -2.73% ± 0.91%
>> sysbench         12           4  -2.91% ± 0.61%
>> hackbench         2          24  -4.99% ± 3.31%
>>
>> Summary:
>> - Many improvement across archs specially with real life workloads.
>> - No major regressions observed.
>> - Overhead of steal_governor looks minimal when there is no steal time.
>> - Overhead when STEAL_GOVERNOR=n is negligible.
> 
> OK, I gave it some testing on my laptop.
> 

Hi Yury, thanks for trying.

> The results are pretty consistent: the steal ratio is converged to a
> number withing the threshold, but the overall performance is 3-5% worse
> comparing to baseline. I tried 2-5% and 1.5-15% boundaries.
> 
> It's 4 VMs, each running 8 vCPUs on 8 pCPU machine, the payload is
> running for 2 minutes.
> 
>    Steal governor off:
>    VM         THROUGHPUT       AVG STEAL%
>    -------- ------------ ----------------
>    0               13802            74.59
>    1               11929            76.35
>    2               13470            74.61
>    3               10259            78.67
>    -------- ------------ ----------------
>    TOTAL           49460                -
>    
>    Steal governor on:
>    VM         THROUGHPUT       AVG STEAL%
>    -------- ------------ ----------------
>    0               12921             5.72
>    1               12762             6.11
>    2               10160             5.99
>    3               12098             6.00
>    -------- ------------ ----------------
>    TOTAL           47941                -
> 
> 
> The test is attached below. The results are quite differ from the numbers
> above, so maybe I misconfigured something? I didn't use hackbench or
> similar benchmarks, just a basic math.
> 

If cputime is all that matters to workload, i.e. if workload is just math, or
without any locks or critical sections you may not see gains.
I have seen similar observation with stress-ng --cpu --metrics IIRC. Not much different.

The reason being, previously it was vCPU but thet vCPU is preempted 75% time.
But now, it is on vCPU but it is sharing that vCPU with 3 more tasks. Effectively
it is preempted 75% time still. Plus context switch overhead will show up.
That likley accounts for your 3-5% regression.

Can you give a try with hackbench, or pgbench, etc if possible?

> Shrikanth, can you please check my test and results? Is there something
> that I have missed?
> 
> I think this series should include some testing. The scripts below look
> bulky and they depend on virtme, but they allow to build the proper
> kernel and run tests with a single command.
> 

Ok, I will check the scripts you have attached. If they are just math, they may not be
the right benchmark to see gains for the reason explained above.


> Thanks,
> Yury
> 
>  From 27e548d65bbe0ec01308d3329017619e165d0488 Mon Sep 17 00:00:00 2001
> From: Yury Norov <[email protected]>
> Date: Wed, 19 Aug 2026 16:31:33 -0400
> Subject: [PATCH] steal governor: add testing harness based on vng
> 
> Signed-off-by: Yury Norov <[email protected]>
> ---
>   .../virtme-steal-payload.sh                   | 220 ++++++++++++
>   .../steal_governor_test/virtme-steal-time.sh  | 321 ++++++++++++++++++
>   2 files changed, 541 insertions(+)
>   create mode 100755 drivers/virt/steal_governor_test/virtme-steal-payload.sh
>   create mode 100755 drivers/virt/steal_governor_test/virtme-steal-time.sh
> 
> diff --git a/drivers/virt/steal_governor_test/virtme-steal-payload.sh b/drivers/virt/steal_governor_test/virtme-steal-payload.sh
> new file mode 100755
> index 000000000000..02a245a900b8
> --- /dev/null
> +++ b/drivers/virt/steal_governor_test/virtme-steal-payload.sh
> @@ -0,0 +1,220 @@
> +#!/bin/sh
> +# Guest-side measurable workload for the steal governor test.
> +
> +set -eu
> +
> +case $# in
> +2|3|5) ;;
> +*)
> +	echo "usage: ${0##*/} VM_ID DURATION_SECONDS [DRIVER [LOW_THRESHOLD HIGH_THRESHOLD]]" >&2
> +	exit 2
> +	;;
> +esac
> +
> +vm_id=$1
> +duration=$2
> +driver=${3-}
> +low_threshold=${4-}
> +high_threshold=${5-}
> +
> +verify_parameter()
> +{
> +	parameter=$1
> +	expected=$2
> +	parameter_file=/sys/module/$driver_sysfs/parameters/$parameter
> +
> +	if [ ! -r "$parameter_file" ]; then
> +		echo "error: driver $driver has no readable $parameter parameter" >&2
> +		exit 1
> +	fi
> +	actual=$(cat "$parameter_file")
> +	if [ "$actual" != "$expected" ]; then
> +		echo "error: driver $driver $parameter is $actual, expected $expected" >&2
> +		exit 1
> +	fi
> +}
> +
> +case $vm_id in
> +*[!0-9]*|'')
> +	echo "error: VM_ID must be a non-negative integer" >&2
> +	exit 2
> +	;;
> +esac
> +case $duration in
> +*[!0-9]*|'')
> +	echo "error: DURATION_SECONDS must be a positive integer" >&2
> +	exit 2
> +	;;
> +esac
> +if [ "$duration" -eq 0 ]; then
> +	echo "error: DURATION_SECONDS must be greater than zero" >&2
> +	exit 2
> +fi
> +if [ -n "$driver" ]; then
> +	case $driver in
> +	*[!A-Za-z0-9_.-]*)
> +		echo "error: invalid driver module name: $driver" >&2
> +		exit 2
> +		;;
> +	esac
> +	if [ -n "$low_threshold" ]; then
> +		case $low_threshold:$high_threshold in
> +		*[!0-9:]*|:*|*:)
> +			echo "error: thresholds must be non-negative integers in percent * 100" >&2
> +			exit 2
> +			;;
> +		esac
> +		if [ "$low_threshold" -ge "$high_threshold" ] ||
> +		   [ "$high_threshold" -ge 10000 ]; then
> +			echo "error: thresholds must satisfy 0 <= LOW < HIGH < 10000" >&2
> +			exit 2
> +		fi
> +	fi
> +	if ! command -v modprobe >/dev/null; then
> +		echo "error: modprobe is required to load driver $driver" >&2
> +		exit 1
> +	fi
> +	driver_sysfs=$(printf '%s\n' "$driver" | tr '-' '_')
> +	if [ ! -d "/sys/module/$driver_sysfs" ]; then
> +		if [ -n "$low_threshold" ]; then
> +			modprobe "$driver" \
> +				low_threshold="$low_threshold" \
> +				high_threshold="$high_threshold"
> +		else
> +			modprobe "$driver"
> +		fi
> +	fi
> +	if [ ! -d "/sys/module/$driver_sysfs" ]; then
> +		echo "error: driver $driver has no /sys/module/$driver_sysfs entry after modprobe" >&2
> +		exit 1
> +	fi
> +	if [ -n "$low_threshold" ]; then
> +		verify_parameter low_threshold "$low_threshold"
> +		verify_parameter high_threshold "$high_threshold"
> +	fi
> +fi
> +
> +tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/virtme-steal-payload.XXXXXXXX")
> +before=$tmpdir/before
> +after=$tmpdir/after
> +pids=
> +
> +stop_workers()
> +{
> +	for pid in $pids; do
> +		kill "$pid" 2>/dev/null || :
> +	done
> +	for pid in $pids; do
> +		wait "$pid" 2>/dev/null || :
> +	done
> +	pids=
> +}
> +
> +cleanup()
> +{
> +	stop_workers
> +	rm -rf -- "$tmpdir"
> +}
> +trap cleanup EXIT
> +trap 'exit 130' INT
> +trap 'exit 143' TERM
> +
> +awk '$1 ~ /^cpu[0-9]+$/ { print $1, $9 }' /proc/stat >"$before"
> +start_time=$(awk '{ print $1 }' /proc/uptime)
> +deadline=$(awk -v start="$start_time" -v seconds="$duration" \
> +	'BEGIN { printf "%.2f", start + seconds }')
> +workers=$(awk '$1 ~ /^cpu[0-9]+$/ { n++ } END { print n }' /proc/stat)
> +
> +i=0
> +while [ "$i" -lt "$workers" ]; do
> +	result=$tmpdir/work-$i
> +	awk -v deadline="$deadline" -v result="$result" '
> +		function uptime(    line, fields) {
> +			getline line < "/proc/uptime"
> +			close("/proc/uptime")
> +			split(line, fields)
> +			return fields[1]
> +		}
> +		BEGIN {
> +			batch = 1000
> +			units = 0
> +			value = 1
> +			while (uptime() < deadline) {
> +				for (iteration = 0; iteration < batch; iteration++)
> +					value = (value * 1103515245 + 12345) % 2147483647
> +				units++
> +			}
> +			printf "%.0f %.0f\n", units, units * batch > result
> +		}
> +	' </dev/null &
> +	pids="$pids $!"
> +	i=$((i + 1))
> +done
> +
> +worker_failed=0
> +for pid in $pids; do
> +	wait "$pid" || worker_failed=1
> +done
> +pids=
> +if [ "$worker_failed" -ne 0 ]; then
> +	echo "error: one or more workload processes failed" >&2
> +	exit 1
> +fi
> +
> +awk '$1 ~ /^cpu[0-9]+$/ { print $1, $9 }' /proc/stat >"$after"
> +end_time=$(awk '{ print $1 }' /proc/uptime)
> +elapsed=$(awk -v start="$start_time" -v end="$end_time" \
> +	'BEGIN { printf "%.2f", end - start }')
> +clk_tck=$(getconf CLK_TCK 2>/dev/null || echo 100)
> +
> +echo VIRTME_STEAL_REPORT_BEGIN
> +driver_status=none
> +[ -n "$driver" ] && driver_status=$driver:loaded
> +[ -n "$low_threshold" ] && \
> +	driver_status="$driver_status,thresholds=$low_threshold..$high_threshold"
> +echo "VM $vm_id  kernel=$(uname -r)  vCPUs=$workers  sample=${elapsed}s  driver=$driver_status"
> +echo "Workload (unpinned workers)"
> +printf '%-10s %12s %16s %16s\n' \
> +	WORKER WORK_UNITS ITERATIONS ITERATIONS/s
> +printf '%-10s %12s %16s %16s\n' \
> +	---------- ------------ ---------------- ----------------
> +total_units=0
> +total_iterations=0
> +i=0
> +while [ "$i" -lt "$workers" ]; do
> +	read -r units iterations <"$tmpdir/work-$i"
> +	rate=$(awk -v iterations="$iterations" -v elapsed="$elapsed" \
> +		'BEGIN { printf "%.0f", iterations / elapsed }')
> +	printf '%-10s %12s %16s %16s\n' \
> +		"worker$i" "$units" "$iterations" "$rate"
> +	total_units=$((total_units + units))
> +	total_iterations=$((total_iterations + iterations))
> +	i=$((i + 1))
> +done
> +total_rate=$(awk -v iterations="$total_iterations" -v elapsed="$elapsed" \
> +	'BEGIN { printf "%.0f", iterations / elapsed }')
> +throughput=$(awk -v units="$total_units" -v elapsed="$elapsed" \
> +	'BEGIN { printf "%.0f", units / elapsed }')
> +printf '%-10s %12s %16s %16s\n' \
> +	TOTAL "$total_units" "$total_iterations" "$total_rate"
> +
> +echo "Steal time by guest CPU"
> +printf '%-8s %12s %12s %10s\n' CPU STEAL_TICKS STEAL_s STEAL_%
> +printf '%-8s %12s %12s %10s\n' -------- ------------ ------------ ----------
> +average_file=$tmpdir/average-steal
> +awk -v hz="$clk_tck" -v elapsed="$elapsed" -v cpus="$workers" \
> +	-v average_file="$average_file" '
> +	NR == FNR { before[$1] = $2; next }
> +	{
> +		delta = $2 - before[$1]
> +		total += delta
> +		printf "%-8s %12d %12.2f %10.2f\n", $1, delta,
> +		       delta / hz, 100 * delta / hz / elapsed
> +	}
> +	END {
> +		printf "%.2f\n", 100 * total / hz / elapsed / cpus > average_file
> +	}
> +' "$before" "$after"
> +average_steal=$(cat "$average_file")
> +echo "VIRTME_STEAL_SUMMARY $vm_id $throughput $average_steal"
> +echo VIRTME_STEAL_REPORT_END
> diff --git a/drivers/virt/steal_governor_test/virtme-steal-time.sh b/drivers/virt/steal_governor_test/virtme-steal-time.sh
> new file mode 100755
> index 000000000000..a41d362c7888
> --- /dev/null
> +++ b/drivers/virt/steal_governor_test/virtme-steal-time.sh
> @@ -0,0 +1,321 @@
> +#!/usr/bin/env bash
> +# Build a minimal KVM guest kernel and measure per-vCPU steal time under load.
> +
> +set -euo pipefail
> +
> +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
> +kernel_dir=$(CDPATH= cd -- "$script_dir/../../.." && pwd)
> +vng=${VNG:-vng}
> +build_dir=${BUILD_DIR:-"$kernel_dir/.virtme-steal"}
> +payload=${PAYLOAD:-"$script_dir/virtme-steal-payload.sh"}
> +vm_count=4
> +vcpu_count=$(nproc)
> +duration=20
> +memory=512M
> +driver=
> +low_threshold=
> +high_threshold=
> +verbose=0
> +declare -a config_items=()
> +
> +usage()
> +{
> +	cat <<EOF
> +Usage: ${0##*/} [options] [O=DIR]
> +
> +Build a virtme-ng minimal kernel with paravirtual steal-time accounting,
> +start multiple CPU-bound VMs, and report the steal time of every guest CPU.
> +
> +Options:
> +  -n VMS       number of VMs to run (default: $vm_count)
> +  -p VCPUS     vCPUs per VM (default: $vcpu_count)
> +  -d SECONDS   workload duration (default: $duration)
> +  -m MEMORY    memory per VM (default: $memory)
> +  --configitem CONFIG[=VALUE]
> +                enable or set a kernel config option (repeatable)
> +  --driver MODULE
> +                load and verify this module before running the payload
> +  -lo VALUE     low steal threshold in percent * 100 (for example, 150 = 1.5%)
> +  -hi VALUE     high steal threshold in percent * 100 (for example, 550 = 5.5%)
> +  -O DIR       kernel build directory (default: $build_dir)
> +  -v, --verbose show per-worker and per-CPU result tables
> +  -h            show this help
> +
> +Environment equivalents: VNG, BUILD_DIR, and PAYLOAD. A custom payload is
> +called inside each VM as: PAYLOAD VM_ID DURATION_SECONDS
> +[DRIVER [LOW_THRESHOLD HIGH_THRESHOLD]].
> +
> +Local virtme-ng checkout example:
> +  VNG=../virtme-ng/vng ${0##*/}
> +
> +The positional O=DIR form is equivalent to -O DIR, for example:
> +  ${0##*/} -n 4 O=../build-linux-virtme-steal
> +
> +Config examples:
> +  ${0##*/} --configitem CONFIG_SCHEDSTATS --configitem CONFIG_HZ_1000=y
> +
> +Threshold example:
> +  ${0##*/} --driver steal_governor -lo 150 -hi 550
> +EOF
> +}
> +
> +while (($#)); do
> +	case $1 in
> +	-n|-p|-d|-m|-O|-lo|-hi|--configitem|--driver)
> +		if (($# < 2)); then
> +			echo "error: $1 requires an argument" >&2
> +			exit 2
> +		fi
> +		case $1 in
> +		-n) vm_count=$2 ;;
> +		-p) vcpu_count=$2 ;;
> +		-d) duration=$2 ;;
> +		-m) memory=$2 ;;
> +		-O) build_dir=$2 ;;
> +		-lo) low_threshold=$2 ;;
> +		-hi) high_threshold=$2 ;;
> +		--configitem) config_items+=("$2") ;;
> +		--driver) driver=$2 ;;
> +		esac
> +		shift 2
> +		;;
> +	--configitem=?*)
> +		config_items+=("${1#*=}")
> +		shift
> +		;;
> +	--driver=?*)
> +		driver=${1#*=}
> +		shift
> +		;;
> +	-v|--verbose)
> +		verbose=1
> +		shift
> +		;;
> +	-h|--help)
> +		usage
> +		exit 0
> +		;;
> +	O=?*)
> +		build_dir=${1#O=}
> +		shift
> +		;;
> +	O=|--configitem=|--driver=)
> +		echo "error: ${1%%=*}= requires a non-empty argument" >&2
> +		exit 2
> +		;;
> +	*)
> +		echo "error: unexpected argument: $1" >&2
> +		usage >&2
> +		exit 2
> +		;;
> +	esac
> +done
> +
> +require_positive_integer()
> +{
> +	local name=$1 value=$2
> +
> +	if [[ ! $value =~ ^[1-9][0-9]*$ ]]; then
> +		echo "error: $name must be a positive integer (got '$value')" >&2
> +		exit 2
> +	fi
> +}
> +
> +require_positive_integer "VM count" "$vm_count"
> +require_positive_integer "vCPU count" "$vcpu_count"
> +require_positive_integer "duration" "$duration"
> +
> +if [[ -n $low_threshold || -n $high_threshold ]]; then
> +	if [[ -z $low_threshold || -z $high_threshold ]]; then
> +		echo "error: -lo and -hi must be specified together" >&2
> +		exit 2
> +	fi
> +	if [[ ! $low_threshold =~ ^[0-9]+$ || ! $high_threshold =~ ^[0-9]+$ ]]; then
> +		echo "error: -lo and -hi must be non-negative integers in percent * 100" >&2
> +		exit 2
> +	fi
> +	low_threshold=$((10#$low_threshold))
> +	high_threshold=$((10#$high_threshold))
> +	if ((low_threshold >= high_threshold)); then
> +		echo "error: -lo must be less than -hi" >&2
> +		exit 2
> +	fi
> +	if ((high_threshold >= 10000)); then
> +		echo "error: -hi must be less than 10000 (100%)" >&2
> +		exit 2
> +	fi
> +	if [[ -z $driver ]]; then
> +		echo "error: -lo and -hi require --driver MODULE" >&2
> +		exit 2
> +	fi
> +fi
> +
> +if [[ -n $driver && ! $driver =~ ^[A-Za-z0-9_.-]+$ ]]; then
> +	echo "error: invalid driver module name: $driver" >&2
> +	exit 2
> +fi
> +
> +for index in "${!config_items[@]}"; do
> +	if [[ ! ${config_items[index]} =~ ^CONFIG_[A-Z0-9_]+(=.*)?$ ]]; then
> +		echo "error: invalid kernel config item: ${config_items[index]}" >&2
> +		exit 2
> +	fi
> +	if [[ ${config_items[index]} != *=* ]]; then
> +		config_items[index]="${config_items[index]}=y"
> +	fi
> +done
> +
> +if [[ ! -x $payload ]]; then
> +	echo "error: guest payload is not executable: $payload" >&2
> +	exit 1
> +fi
> +if [[ ! -r /dev/kvm || ! -w /dev/kvm ]]; then
> +	echo "error: /dev/kvm is not accessible; KVM is required for steal-time accounting" >&2
> +	exit 1
> +fi
> +
> +case $build_dir in
> +/*) ;;
> +*) build_dir=$PWD/$build_dir ;;
> +esac
> +
> +mkdir -p "$build_dir"
> +cd "$kernel_dir"
> +
> +echo "==> Configuring paravirtual kernel in $build_dir"
> +if [[ ! -f $build_dir/.config ]]; then
> +	config_args=()
> +	for config_item in "${config_items[@]}"; do
> +		config_args+=(--configitem "$config_item")
> +	done
> +	"$vng" --kconfig "${config_args[@]}" \
> +		--configitem CONFIG_HYPERVISOR_GUEST=y \
> +		--configitem CONFIG_PARAVIRT=y \
> +		--configitem CONFIG_KVM_GUEST=y \
> +		--configitem CONFIG_PARAVIRT_TIME_ACCOUNTING=y \
> +		-- "O=$build_dir"
> +else
> +	# Preserve an existing local minimal config and add the options needed by
> +	# this test. olddefconfig resolves their dependencies for the current tree.
> +	config_args=(--file "$build_dir/.config")
> +	for config_item in "${config_items[@]}"; do
> +		config_name=${config_item%%=*}
> +		config_value=${config_item#*=}
> +		config_args+=(--set-val "$config_name" "$config_value")
> +	done
> +	"$kernel_dir/scripts/config" "${config_args[@]}" \
> +		-e HYPERVISOR_GUEST \
> +		-e PARAVIRT \
> +		-e KVM_GUEST \
> +		-e PARAVIRT_TIME_ACCOUNTING
> +	make -s O="$build_dir" olddefconfig
> +fi
> +
> +echo "==> Building kernel"
> +declare -a module_args=()
> +if [[ -z $driver ]]; then
> +	module_args+=(--skip-modules)
> +fi
> +"$vng" --build "${module_args[@]}" -- "O=$build_dir"
> +
> +for option in HYPERVISOR_GUEST PARAVIRT KVM_GUEST PARAVIRT_TIME_ACCOUNTING; do
> +	if ! grep -qx "CONFIG_${option}=y" "$build_dir/.config"; then
> +		echo "error: CONFIG_${option}=y is required but is absent after configuration" >&2
> +		exit 1
> +	fi
> +done
> +
> +tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/virtme-steal.XXXXXXXX")
> +declare -a vm_pids=()
> +
> +cleanup()
> +{
> +	local pid
> +
> +	for pid in "${vm_pids[@]}"; do
> +		kill "$pid" 2>/dev/null || true
> +	done
> +	wait 2>/dev/null || true
> +	rm -rf -- "$tmpdir"
> +}
> +trap cleanup EXIT
> +trap 'exit 130' INT
> +trap 'exit 143' TERM
> +
> +echo "==> Starting $vm_count VMs ($vcpu_count vCPUs each)"
> +for ((vm = 0; vm < vm_count; vm++)); do
> +	printf -v guest_script '%q %q %q' "$payload" "$vm" "$duration"
> +	if [[ -n $driver ]]; then
> +		printf -v driver_arg ' %q' "$driver"
> +		guest_script+=$driver_arg
> +		if [[ -n $low_threshold ]]; then
> +			printf -v threshold_args ' %q %q' \
> +				"$low_threshold" "$high_threshold"
> +			guest_script+=$threshold_args
> +		fi
> +	fi
> +
> +	log=$tmpdir/vm-$vm.log
> +	"$vng" \
> +		"${module_args[@]}" \
> +		--name "steal-vm-$vm" \
> +		--cpus "$vcpu_count" \
> +		--memory "$memory" \
> +		--exec "$guest_script" \
> +		-- "O=$build_dir" >"$log" 2>&1 &
> +	vm_pids+=("$!")
> +done
> +
> +status=0
> +total_throughput=0
> +if (( ! verbose )); then
> +	printf '%-8s %12s %16s\n' VM THROUGHPUT 'AVG STEAL%'
> +	printf '%-8s %12s %16s\n' -------- ------------ ----------------
> +fi
> +for ((vm = 0; vm < vm_count; vm++)); do
> +	if ! wait "${vm_pids[vm]}"; then
> +		echo "error: VM $vm failed; its complete log follows" >&2
> +		status=1
> +		echo "--- VM $vm log ---"
> +		cat "$tmpdir/vm-$vm.log"
> +		continue
> +	fi
> +	if ! grep -qx VIRTME_STEAL_REPORT_BEGIN "$tmpdir/vm-$vm.log" ||
> +	   ! grep -qx VIRTME_STEAL_REPORT_END "$tmpdir/vm-$vm.log"; then
> +		echo "error: VM $vm exited without a steal-time report; its complete log follows" >&2
> +		cat "$tmpdir/vm-$vm.log"
> +		status=1
> +		continue
> +	fi
> +
> +	summary=$(awk '/^VIRTME_STEAL_SUMMARY / { print $2, $3, $4 }' \
> +		"$tmpdir/vm-$vm.log")
> +	if [[ -z $summary ]]; then
> +		echo "error: VM $vm exited without a summary; its complete log follows" >&2
> +		cat "$tmpdir/vm-$vm.log"
> +		status=1
> +		continue
> +	fi
> +
> +	if (( verbose )); then
> +		echo "--- VM $vm results ---"
> +		awk '
> +			/^VIRTME_STEAL_REPORT_BEGIN$/ { report = 1; next }
> +			/^VIRTME_STEAL_REPORT_END$/ { report = 0 }
> +			/^VIRTME_STEAL_SUMMARY / { next }
> +			report
> +		' "$tmpdir/vm-$vm.log"
> +	else
> +		read -r summary_vm summary_throughput summary_steal <<<"$summary"
> +		printf '%-8s %12s %16s\n' \
> +			"$summary_vm" "$summary_throughput" "$summary_steal"
> +		total_throughput=$((total_throughput + summary_throughput))
> +	fi
> +done
> +
> +if (( ! verbose )); then
> +	printf '%-8s %12s %16s\n' -------- ------------ ----------------
> +	printf '%-8s %12s %16s\n' TOTAL "$total_throughput" -
> +fi
> +
> +exit "$status"
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.