Skip to content

Instantly share code, notes, and snippets.

@cppcooper
Last active November 28, 2022 04:55
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cppcooper/aac3c41f0a3270cb2640f0ac3565056e to your computer and use it in GitHub Desktop.
Save cppcooper/aac3c41f0a3270cb2640f0ac3565056e to your computer and use it in GitHub Desktop.
libvirt cpu isolation
#!/bin/bash
# To use, put in the file:
# /etc/libvirt/hooks/qemu
# remember to chmod +x
echo $1 $2
cores=$(grep -c ^processor /proc/cpuinfo)
last_core=$((cores-1))
host_cpuset="0-$last_core"
function setcores() {
printf "allowed cpuset=%s\n" "$1"
#exit 0
if
systemctl set-property --runtime -- user.slice AllowedCPUs=$1 &&
systemctl set-property --runtime -- system.slice AllowedCPUs=$1 &&
systemctl set-property --runtime -- init.scope AllowedCPUs=$1
then
exit 0
fi
exit 1
}
function getright() {
echo $1 | sed 's/.*-//'
}
function getleft() {
echo $1 | sed 's/-.*//'
}
function removefrom() {
local -n list=$1
local element=$2
for (( i=0; i<${#list[@]}; i++ )); do
if [[ ${list[i]} == "$element" ]]; then
list=( "${list[@]:0:$i}" "${list[@]:$((i + 1))}" )
i=$((i - 1))
fi
done
}
function invertset() {
local -n output=$1
local input=("${output[@]}")
output=($host_cpuset)
local new_entry_L
local new_entry_R
local OL
local OR
local OE
for i in ${!input[*]}; do
local element=${input[$i]}
# check if it is a range
if [[ "$element" == *"-"* ]]; then
# first we separate the range values from each other
left=$(getleft $element)
right=$(getright $element)
# now we need to find the output element containing the range
OE=""
removed_range=false
for j in ${!output[*]}; do
OE=${output[$j]}
OL=""
OR=""
# check if the output element is a range
if [[ "$OE" == *"-"* ]]; then
OL=$(getleft $OE)
OR=$(getright $OE)
# we can simply check if one of the values is within this range
if (( OL <= left && left <= OR )); then
# we found where the values are contained in the output, now remove that element from output
removed_range=true
removefrom output "$OE"
break
fi
# the output element is not a range, so we may need to remove multiple
# if the output element is within the input element's range we remove it
elif (( OE >= left && OE <= right )); then
removefrom output "$OE"
# note we don't break in this one, so we can check for more solo values within the range
fi
done
if ((removed_range)); then
# we removed a range so we need to calculate what to add back
# example: 0-Inner_left, removed_lower-removed_upper, Inner_right-last_core
new_entry_L=""
new_entry_R=""
inner_left=$((left - 1))
inner_right=$((right + 1))
if (( inner_left >= OL )); then
if (( inner_left == OL )); then
new_entry_L="$OL"
else
new_entry_L="$OL-$inner_left"
fi
fi
if (( inner_right <= OR )); then
if (( inner_right == OR )); then
new_entry_R="$OR"
else
new_entry_R="$inner_right-$OR"
fi
fi
if [ -n "$new_entry_L" ]; then
output+=("$new_entry_L")
fi
if [ -n "$new_entry_R" ]; then
output+=("$new_entry_R")
fi
else
: # if all we did was remove solo values, there is nothing to stitch together
fi
else
# we are dealing with a solo value to remove from the output
OE=""
# we simply need to find the output range containing the value to remove (or the matching solo value)
for j in ${!output[*]}; do
OE=${output[$j]}
OL=""
OR=""
if [[ "$OE" == *"-"* ]]; then
OL=$(getleft $OE)
OR=$(getright $OE)
# is the value inside this range?
if (( OL <= element && element <= OR )); then
removefrom output "$OE"
new_entry_L=""
new_entry_R=""
# time to figure out what to add back
inner_left=$((element - 1))
inner_right=$((element + 1))
if (( inner_left >= OL )); then
if (( inner_left == OL )); then
new_entry_L="$OL"
else
new_entry_L="$OL-$inner_left"
fi
fi
if (( inner_right <= OR )); then
if (( inner_right == OR )); then
new_entry_R="$OR"
else
new_entry_R="$inner_right-$OR"
fi
fi
if [ -n "$new_entry_L" ]; then
output+=("$new_entry_L")
fi
if [ -n "$new_entry_R" ]; then
output+=("$new_entry_R")
fi
break
fi
# is this a value matching the value we're looking for
elif (( OE == element )); then
# luckily all we need to do for this edge case is remove it
removefrom output "$OE"
break
fi
done
fi
done
}
function isolate_cores() {
local cpuset=($1)
invertset cpuset
delim=""
for i in ${!cpuset[*]}; do
new_host_cpuset="$new_host_cpuset$delim${cpuset[$i]}"
delim=","
done
setcores "$new_host_cpuset"
}
if [[ "$2" == "start" ]]; then
xml_file="/etc/libvirt/qemu/$1.xml"
if [ -f $xml_file ]; then
guest_cpuset_arr=("$(grep -oP "(?<=cpuset=')[^']+" "$xml_file")")
if [ -n "${guest_cpuset_arr[*]}" ]; then
isolate_cores "${guest_cpuset_arr[*]}"
fi
fi
elif [[ "$2" == "started" ]]; then
sleep 60s && renice --priority -20 --pid $(pgrep qemu) &
elif [[ "$2" == "release" ]]; then
setcores $host_cpuset
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment