integ/virt/kvm-timer-advance/files/setup_kvm_timer_advance.sh

116 lines
3.7 KiB
Bash

#!/bin/bash
#
# SPDX-License-Identifier: GPLv2
#
# The qemu command details and the 98-102% range is taken from
# find-lapictscdeadline-optimal.sh and script.sh
# from the tuned package available at
# https://github.com/redhat-performance/tuned/tree/master/profiles/realtime-virtual-host
#
# The tuned package is GPLv2 therefore this component is GPLv2
#
# Copyright(c) 2019 Wind River Systems, Inc. All rights reserved.
#
QEMU=/usr/libexec/qemu-kvm
ADVANCE_FILE="/sys/module/kvm/parameters/lapic_timer_advance_ns"
ADVANCE_CALIB="/etc/kvm-timer-advance/calibrated_lapic_timer_advance_ns"
function log {
logger -p local1.info -t $0 $@
echo $0: "$@"
}
# This is a check for a virtualbox machine where kvm modules are not loaded
if [ ! -f $ADVANCE_FILE ]; then
exit 1
fi
# Use previous calibrated advance result
if [ -f $ADVANCE_CALIB ]; then
read -r advance < $ADVANCE_CALIB
if [[ "$advance" =~ ^[0-9]+$ ]]; then
echo $advance > $ADVANCE_FILE
log "using previously calibrated advance value of" $(cat $ADVANCE_FILE)
exit 0
fi
fi
# Use the application cpus calculated by puppet. This will ensure that
# we run on a CPU that isn't being used by management or vswitch.
VCPU_PIN_STR=$(grep vcpu_pin_set /etc/kvm-timer-advance/kvm-timer-advance.conf)
VCPU_PIN_STR=${VCPU_PIN_STR//\"/}
FLOAT_CPUS=${VCPU_PIN_STR##*=}
if [ -z "${FLOAT_CPUS}" ]; then
log "skip calibration, we have not configured yet"
exit 0
fi
log "Calibrating with FLOAT_CPUS: ${FLOAT_CPUS}"
taskset --pid --cpu-list ${FLOAT_CPUS} $$ &> /dev/null
dir=$(mktemp -d)
advance=1500
latency=1000000
for i in $(seq 1500 500 7000); do
log "test advance ${i}"
echo $i > $ADVANCE_FILE
timeout --foreground --signal TERM 10s \
chrt -f 1 stdbuf -oL ${QEMU} -enable-kvm -device pc-testdev \
-device isa-debug-exit,iobase=0xf4,iosize=0x4 \
-display none -serial stdio -device pci-testdev \
-kernel /usr/share/qemu-kvm/tscdeadline_latency.flat \
-cpu host | awk 'NF==2 && /latency:/ {print $2}' > ${dir}/out0
# chomp last line since output may be incomplete
sed \$d < ${dir}/out0 > ${dir}/out
# Calculate the average of all the latency numbers output by
# the test image.
A=0
while read l; do
A=$(($A + $l))
done < $dir/out
lines=$(wc -l $dir/out | cut -f 1 -d " ")
if [ ${lines} -eq 0 ]; then
# this shouldn't happen
log "got no output from test, aborting"
break
fi
ans=$(($A/$lines))
# Get the current latency as a percentage of the previous latency
value=$((${ans}*100/${latency}))
if [ $value -ge 102 ]; then
# Latency has increased by too much, we don't want to use this
# much advance. I didn't see this in practice, this is just
# a sanity check.
advance=$((${i} - 500))
log "latency too large, reverting to advance of ${advance}"
echo $advance > $ADVANCE_FILE
break
elif [ $value -ge 98 ]; then
# If we're close to the previous latency, then use the current
# advance. The algorithm has a tendency to underestimate a bit,
# so we don't want to use the previous advance value.
break
else
# We're substantially lower than the previous latency, so store
# the current advance and latency numbers and loop through again
# to see if it improves further with a bit higher advance.
latency=$ans
advance=$i
fi
done
# Save calibrated result
cat $ADVANCE_FILE > $ADVANCE_CALIB
log "using advance value of" $(cat $ADVANCE_FILE)
rm -rf $dir
exit 0