Last active
November 28, 2022 04:55
-
-
Save cppcooper/aac3c41f0a3270cb2640f0ac3565056e to your computer and use it in GitHub Desktop.
libvirt cpu isolation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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