Skip to content

Commit a4c8501

Browse files
committed
RAM usage much improved
Adds a loop that communicates with QEMU via a socket to command the balloon driver, which was previously doing nothing. Now the VM tries to maintain a set level of free RAM for Linux by limiting amount of RAM available to Windows, evaluating the situation once a second. This adds a nice speed boost, because beforehand the way this was working when literally anything else was running on the desktop, was due to ZRAM. So this also fixes the "Killed" message some non-ZRAM-users were getting. New dependency added: socat to communicate with qemu And new config file option: free_ram_goal Now allocate 6GB on 8GB, and 14GB on 16GB devices, instead of 7GB and 15GB as before. Helps prevent oomkiller incidents.
1 parent 6c116c2 commit a4c8501

2 files changed

Lines changed: 77 additions & 10 deletions

File tree

‎bvm‎

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,20 @@ get_codename() { #get debian/ubuntu codename
439439
fi
440440
}
441441

442+
package_installed() { #exit 0 if $1 package is installed, otherwise exit 1
443+
#from pi-apps
444+
local package="$1"
445+
[ -z "$package" ] && error "package_installed(): no package specified!"
446+
#find the package listed in /var/lib/dpkg/status
447+
#package_info "$package"
448+
449+
#directly search /var/lib/dpkg/status
450+
grep -x "Package: $package" /var/lib/dpkg/status -A 2 | grep -qxF 'Status: install ok installed'
451+
}
452+
442453
install_dependencies() { #try to install everything BVM needs
443-
required_commands="git jq wget mkisofs qemu-img qemu-system-aarch64 remmina nmap wget yad uuidgen wiminfo"
444-
apt_packages="git jq wget genisoimage qemu-utils qemu-system-arm qemu-system-gui qemu-efi-aarch64 remmina remmina-plugin-rdp nmap wget yad uuid-runtime seabios ipxe-qemu wimtools"
454+
required_commands="git jq wget mkisofs qemu-img qemu-system-aarch64 remmina nmap wget yad uuidgen wiminfo socat"
455+
apt_packages="git jq wget genisoimage qemu-utils qemu-system-arm qemu-system-gui qemu-efi-aarch64 remmina remmina-plugin-rdp nmap wget yad uuid-runtime seabios ipxe-qemu wimtools socat"
445456

446457
#using freerdp version 2 instead of 3 because it does not freeze on login and has more reliable clipboard sync
447458
if [ "$XDG_SESSION_TYPE" == wayland ];then
@@ -487,7 +498,15 @@ install_dependencies() { #try to install everything BVM needs
487498
if [ "$installneeded" == 1 ] && command -v apt >/dev/null;then
488499
status "Installing dependencies, please wait..."
489500
#don't change the flags here in this apt command without updating the apt-detecting code on the pi-apps install script for BVM
490-
sudo apt install -y $apt_packages || error "APT failed to install required dependencies"
501+
IFS=' '
502+
will_install=''
503+
for package in $apt_packages ;do
504+
if ! package_installed "$package" ;then
505+
will_install+=" $package"
506+
fi
507+
done
508+
509+
sudo apt install -y $will_install || error "APT failed to install required dependencies"
491510

492511
#upgrade qemu to version from bookworm-backports
493512
if [ "$(get_codename)" == bookworm ];then
@@ -634,19 +653,32 @@ fi
634653
[ -z "$download_language" ] && download_language="English (United States)"
635654
[ -z "$debloat" ] && debloat=true
636655
[ -z "$disksize" ] && disksize=40
656+
[ -z "$free_ram_goal" ] && free_ram_goal=100
637657
if [ -z "$vm_mem" ];then
638658
#choose RAM to allocate to VM - 1GB less than total RAM
639659
vm_mem="$(($(awk '/MemTotal/ {print $2}' /proc/meminfo)/1024/1024))"
640-
#edge case for 2GB Pies: allocate 2GB and hope for the best (2GB is the minimum it seems)
641-
[ "$vm_mem" == 1 ] && vm_mem=2
642-
[ "$mode" == firstboot ] && [ "$vm_mem" -gt 4 ] && vm_mem=4 #more than 4GB has no benefit for firstinstall
660+
#Force 2GB on <=2GB devices
661+
[ "$vm_mem" -le 1 ] && vm_mem=2
662+
#Take off 1GB on >=5GB devices
663+
[ "$vm_mem" -ge 4 ] && vm_mem=$((vm_mem-1))
664+
#so for boot mode, RAM allocation works out like this:
665+
#Pi VM
666+
#1GB -> 2GB (likely fails)
667+
#2GB -> 2GB
668+
#4GB -> 3GB
669+
#8GB -> 6GB
670+
#16GB -> 14GB
671+
672+
#more than 4GB has no benefit for firstinstall
673+
[ "$mode" == firstboot ] && [ "$vm_mem" -gt 4 ] && vm_mem=4
643674
fi
644675

645676
debug "DIRECTORY: $DIRECTORY
646677
mode: $mode
647678
vmdir: $vmdir
648679
vm_username: $vm_username
649680
vm_password: $vm_password
681+
vm_mem: $vm_mem
650682
rdp_port: $rdp_port
651683
download_language: $download_language"
652684

@@ -1117,7 +1149,7 @@ To get a fresh VM up and running, use a sequence like this:
11171149

11181150
#forward guest's port 3389 to localhost port of our choice (handled by config file now)
11191151
#network_flags=(-netdev user,id=nic,hostfwd=tcp:127.0.0.1:${rdp_port}-:3389 -device virtio-net-pci,netdev=nic)
1120-
1152+
rm -f "$vmdir/qemu.pid"
11211153
#all QEMU flags are combined together here
11221154
full_qemu_flags=(-M virt,accel=kvm -cpu host -m ${vm_mem}G -smp $num_cores \
11231155
-name BVM,process=bvm \
@@ -1128,6 +1160,7 @@ To get a fresh VM up and running, use a sequence like this:
11281160
-device qemu-xhci \
11291161
-device usb-kbd \
11301162
-device usb-tablet \
1163+
-monitor unix:"$vmdir/qemu.sock",server,nowait \
11311164
"${usb_forwarding_flags[@]}" \
11321165
"${audio_flags[@]}" \
11331166
-rtc base=localtime,clock=host,driftfix=none \
@@ -1138,14 +1171,43 @@ To get a fresh VM up and running, use a sequence like this:
11381171

11391172
debug "full_qemu_flags: " "${full_qemu_flags[@]}"
11401173

1174+
#always try to pick optimal VM RAM limit by sending repeated balloon requests
1175+
total_ram=$( grep MemTotal /proc/meminfo | awk '{print int($2/1024)}')
1176+
1177+
#try to keep free_ram_goal MB free RAM on Linux at all times by setting the balloon size of the VM
1178+
#Windows will eat as much RAM as possible for caches, so it can be reallocated as needed.
1179+
#This "balloon_popper" process is what's needed to make the virtio-balloon driver actually do something.
1180+
while true;do
1181+
sleep 1
1182+
qemu_pid="$(cat "$vmdir/qemu.pid")"
1183+
[ -z "$qemu_pid" ] && continue
1184+
1185+
free_ram=$(grep MemFree /proc/meminfo | awk '{print int($2/1024)}')
1186+
qemu_used_ram=$(grep VmRSS /proc/$qemu_pid/status | awk '{print $2/1024}' | sed 's/\..*//g')
1187+
other_tasks_ram=$((total_ram - (qemu_used_ram + free_ram)))
1188+
#echo "other taks use $other_tasks_ram"
1189+
balloon_size=$((total_ram - other_tasks_ram - free_ram_goal))
1190+
#echo "qemu using $qemu_used_ram, free $free_ram, balloon_size: $balloon_size"
1191+
1192+
echo "balloon $balloon_size" | socat - UNIX-CONNECT:"$vmdir/qemu.sock" &>/dev/null
1193+
done &
1194+
balloon_popper=$!
1195+
11411196
#run qemu with these flags
11421197
if [ "$use_taskset" == false ];then
1143-
qemu-system-aarch64 "${full_qemu_flags[@]}" || error "QEMU did not exit successfully."
1198+
qemu-system-aarch64 "${full_qemu_flags[@]}"
11441199
else
11451200
#For rockchip (untested but based on https://gist.github.com/Vogtinator/293c4f90c5e92838f7e72610725905fd?permalink_comment_id=5378278#gistcomment-5378278)
1146-
taskset -c "$(echo "$a76_cores" | tr '\n' ',' | sed 's/,$//g')" qemu-system-aarch64 "${full_qemu_flags[@]}" || error "QEMU did not exit successfully."
1201+
taskset -c "$(echo "$a76_cores" | tr '\n' ',' | sed 's/,$//g')" qemu-system-aarch64 "${full_qemu_flags[@]}"
1202+
fi
1203+
qemu_exit_code=$?
1204+
kill $balloon_popper
1205+
1206+
if [ $qemu_exit_code == 0 ];then
1207+
status "QEMU closed."
1208+
else
1209+
error "QEMU did not exit successfully."
11471210
fi
1148-
status "QEMU closed."
11491211
;;
11501212
connect*)
11511213
#make sure the qemu process is running

‎resources/bvm-config‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ debloat=true
3030
#An excellent ZRAM implementation can be found on Pi-Apps called 'More RAM'
3131
#You can install it directly with: wget -qO- https://raw.githubusercontent.com/Botspot/pi-apps/master/apps/More%20RAM/install | bash
3232

33+
#Leave this much free RAM for Linux at all times. Default is 100 (MB)
34+
#free_ram_goal=100
35+
#Every second, the VM will adjust its RAM allocation to make sure this much is freely available for Linux.
36+
#If linux tasks begin to use more, the VM will compensate and use less.
37+
3338
#Uncomment this to change size of the installation drive image in gigabytes. Default 40
3439
#disksize=40
3540
#You can reduce this somewhat if you encounter the "Insufficient free disk space" error. The lower limit might be somewhere around 15 GB (untested)

0 commit comments

Comments
 (0)