Skip to content

Instantly share code, notes, and snippets.

@ayufan
Last active January 4, 2024 10:56
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save ayufan/37be5c0b8fd26113a8be to your computer and use it in GitHub Desktop.
Save ayufan/37be5c0b8fd26113a8be to your computer and use it in GitHub Desktop.
KVM CPU pinning for Proxmox VE
#!/bin/bash
set -eo pipefail
VMID=200
cpu_tasks() {
expect <<EOF | sed -n 's/^.* CPU .*thread_id=\(.*\)$/\1/p' | tr -d '\r' || true
spawn qm monitor $VMID
expect ">"
send "info cpus\r"
expect ">"
EOF
}
VCPUS=($(cpu_tasks))
VCPU_COUNT="${#VCPUS[@]}"
if [[ $VCPU_COUNT -eq 0 ]]; then
echo "* No VCPUS for VM$VMID"
exit 1
fi
echo "* Detected ${#VCPUS[@]} assigned to VM$VMID..."
echo "* Resetting cpu shield..."
for CPU_INDEX in "${!VCPUS[@]}"
do
CPU_TASK="${VCPUS[$CPU_INDEX]}"
echo "* Assigning $CPU_INDEX to $CPU_TASK..."
taskset -pc "$CPU_INDEX" "$CPU_TASK"
done
@GuyNoIRQ
Copy link

GuyNoIRQ commented Mar 26, 2021

Hacked up something based on this for PCs with 2 NUMA nodes. Hope this helps someone. I'm hoping it helps with the quite poor performance I'm getting in some games with GPU passthrough. I'll report back here if it does anything.
`root@ProxMox01:~# cat PinVM.sh
#!/bin/bash

set -eo pipefail

VMID=$1
OffSet=$2

cpu_tasks() {
expect <<EOF | sed -n 's/^.* CPU .thread_id=(.)$/\1/p' | tr -d '\r' || true
spawn qm monitor $VMID
expect ">"
send "info cpus\r"
expect ">"
EOF
}

VCPUs=($(cpu_tasks));

if ! [ ${VCPUs} ]; then
echo "* No VCPUS for VM$VMID";
exit 1;
fi

if ! (( ${2} % 2 )); then
NumaCPUs=($(lscpu | grep "NUMA node[0-9]" | awk '{print $4}' | head -n1 | sed 's/,/ /g'));
else
NumaCPUs=($(lscpu | grep "NUMA node[0-9]" | awk '{print $4}' | tail -n1 | sed 's/,/ /g'));
fi

echo -e "\nVCPU PIDs: ${VCPUs[@]}\n";
lscpu | grep "NUMA node. CPU"
echo -e "\nCores per Numa Boundary: ${#NumaCPUs[@]}";
echo "Number of VCPUs: ${#VCPUs[@]}";
echo "Defined Core offset: $2";

if [[ $(( ${#NumaCPUs[@]} + 1 )) < $(( $2 + ${#VCPUs[@]} )) ]]; then
echo -e "\nOffset too great for number of CPU cores. Exiting\n";
exit 1;
fi;

VCPUCount="${#VCPUS[@]}";
VCPUCount=$(( ${VCPUCount} - 1 ));

if [[ $2 = 0 ]]; then
Index=0;
else
Index=$(( $2 - 1 ));
fi;

for VCPU in ${VCPUs[@]}; do
NumaCPU="${NumaCPUs[$CPUIndex]}";
echo "VCPU PID: $VCPU assigned to CORE: ${NumaCPUs[$Index]}";
taskset -pc "${NumaCPUs[$Index]}" "${VCPU}" 2>&1 > /dev/null;
Index=$(( $Index + 1 ));
done`

Executed like:
./PinVM.sh

Sample output:
`root@ProxMox01:~# ./PinVM.sh 103 0

VCPU PIDs: 35681 35682 35683 35684 35685 35686 35687 35688 35689 35690 35691 35692

NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30
NUMA node1 CPU(s): 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31

Cores per Numa Boundary: 16
Number of VCPUs: 12
Defined Core offset: 0
VCPU PID: 35681 assigned to CORE: 0
VCPU PID: 35682 assigned to CORE: 2
VCPU PID: 35683 assigned to CORE: 4
VCPU PID: 35684 assigned to CORE: 6
VCPU PID: 35685 assigned to CORE: 8
VCPU PID: 35686 assigned to CORE: 10
VCPU PID: 35687 assigned to CORE: 12
VCPU PID: 35688 assigned to CORE: 14
VCPU PID: 35689 assigned to CORE: 16
VCPU PID: 35690 assigned to CORE: 18
VCPU PID: 35691 assigned to CORE: 20
VCPU PID: 35692 assigned to CORE: 22`

And if you try to set too large an offset you get an error:
`root@ProxMox01:~# ./PinVM.sh 103 7

VCPU PIDs: 35681 35682 35683 35684 35685 35686 35687 35688 35689 35690 35691 35692

NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30
NUMA node1 CPU(s): 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31

Cores per Numa Boundary: 16
Number of VCPUs: 12
Defined Core offset: 7

Offset too great for number of CPU cores. Exiting`

@docop
Copy link

docop commented Jun 7, 2023

Is it giving a notable speed increased ? And was it only noticable in game .. ? thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment