#1111337 libvirt: After upgrading to trixie, some kvm guests never successfully boot

#1111337#5
Date:
2025-08-17 01:04:13 UTC
From:
To:
I have a hypervisor (sol) with 4 guests (mercury, venus, mars, and
jupiter).  The hypervisor and first three guests run Debian.  The 4th
guest runs Home Assistant.  The Debian guests have been running in
roughly their current configuration since 2016 (wheezy) and have been
upgraded since then for each Debian release.

I have been working to upgrade all of the Debian guests to trixie. I
followed the same process as usual: I upgraded each guest one at a time,
rebooted, and tested.  Once all guests were working, I upgraded the
hypervisor.  After rebooting the hypervisor, none of the guests were
running, and neither was the virtual network.  I started the network
manually, but I haven't managed to get any of the guests working.

The initial problem with the guests was that their machine type
pc-i440fx-2.1 was no longer supported.  I adjusted this to
pc-i440fx-2.4, and virsh accepted that change. Now, I can start the
guests, and they report as running, but they never do anything useful.

As far as I can tell, nothing is ever even written to the console.  The
guests definitely never come up far enough to respond to a ping or
accept an SSH connection.  If I try to cleanly shut down a guest, it
never stops.  I have to use destroy instead of shutdown.  When I attempt
to start a guest, I get a new kvm process that immediately uses 100% of
a CPU and stays like that for at least 30 minutes (which is as long as
I've been willing to wait).

Oddly, the jupiter guest (Home Assistant) boots without problems and
seems to be working fine.  So, I assume there must be something
wrong/different about the Debian guests that conflicts with the new
libvirt version in trixie, but can't figure out what that might be.

I compared the current state of each guest against my pre-upgrade backup
of /etc/libvirt, and none of the changes are surprising.  Besides
the change to the machine name, there are some minor changes to some XML
stanzas which libvirt seems to make on its own. (I removed them and they
came back.)  All of the other changes within /etc/livirt came with as
part of the upgrade to trixie.

The main difference I see is that jupiter uses <os firmware='efi'>, and
its only disk is a .qcow2 file.  The other guests do not use EFI and
their disks are raw block devices instead.

I hesitate to attach configuration here, because I'm not sure what will
be useful to you.  Let me me know what questions you have, and I can
attach configuration or run other diagnostics.

Thanks,

Ken

#1111337#14
Date:
2025-08-17 13:22:46 UTC
From:
To:
This is a very sensible way to go about upgrading an hypervisor host,
well done :)

The 2.1 machine types have been dropped from QEMU, so switching to
newer ones is the right thing to do. That said, I would probably pick
the most recent machine type (10.0) instead of the oldest one (2.4),
as that would give you the most runway. The 2.4 machine types are
almost certainly going to be absent from the QEMU version that will
end up in forky, and adopting them today pretty much guarantees that
you'll be forced to go through this process again in two years' time.

They're not using EFI "indirectly" through a legacy configuration
with a <loader> element either, right? And I assume you've confirmed
that the raw block devices are still present at the expected paths.

If libvirt is able to start the guests at all, that rules out most of
the common failure modes (e.g. failing to access the disk files or
rejecting an invalid configuration). It seems more likely than the
issue would be on the firmware/guest OS side, though of course it
could also be that libvirt is not setting things up in a way that
allows the firmware/guest OS to do anything useful.

You could start by collecting some debug logs[1] and checking that
there is nothing suspicious in the journal, such as AppArmor denials.

Another useful attempt would be to eschew the existing configuration
for one of the guest and import it from scratch using virt-install.
Assuming the needs are not too specific, that should result in a
working configuration, which we could then compare with the previous
one to try and identify the issue.


[1] https://libvirt.org/kbase/debuglogs.html

#1111337#19
Date:
2025-08-17 19:50:19 UTC
From:
To:
I enabled debug logs. I don't see any AppArmor errors. However, I ran
across this in the logs when starting the mars guest:

   error : virStorageFileBackendFileRead:106 : Failed to open file
   '/dev/vg_ssd/lv_mars_root': Permission denied error :
   virStorageFileBackendFileRead:106 : Failed to open file
   '/dev/vg_spin/lv_mars_data': Permission denied error :
   virStorageFileBackendFileRead:106 : Failed to open file
   '/dev/vg_ssd/lv_mars_fast': Permission denied

To work around that, I edited /etc/libvirt/qemu.conf, adjusted the
security driver, and restarted the service:

   #security_driver = "selinux"
   security_driver = "none"

That resolved the permission denied error, but made no difference to the
behavior of the guest once it started.  I still see nothing in the
console, and it never becomes available for use.

I also followed your advice and tried importing the existing filesystem
into a new guest. I had saved off the original command used to create
this guest back in 2016, so I adjusted it to use --import and --osinfo
rather than one of install options.

   virt-install \
          --import \
          --osinfo debiantrixie \
          --check path_in_use=off \
          --check mac_in_use=off \
          --connect qemu:///system \
          --virt-type kvm \
          --name mars.2 \
          --memory 22528 \
          --vcpus 4 \
          --graphics none \
          --machine "pc-i440fx-10.0" \
          --disk /dev/vg_ssd/lv_mars_root \
          --disk /dev/vg_spin/lv_mars_data \
          --disk /dev/vg_ssd/lv_mars_fast \
          --network network=virtual,mac=52:54:00:00:00:03

This behaves the same way as the original mars guest.  It starts, but
nothing is ever written to the console and it never becomes available.
There are slight differences in the generated XML.  There are different
PCI ids assigned to the disks, and virt-install chooses host-passthrough
for the CPU instead of a specific CPU type as in the old guest.
However, there's nothing else that looks particularly significant.

Besides the original permission denied errors that I mentioned above,
there aren't any messages that contain 'error', 'exception', 'fail',
'failure', etc. in the libvirt debug logs, regardless of whether I'm
booting the original mars guest or the imported mars.2 guest.

Just as confirmation, I grabbed the contents of kern.log and syslog from
the filesystem using virt-cat:

   virt-cat -d mars /var/log/kern.log > kern.log

There has been nothing written to those logs since the last time the
guest was shut down (when rebooting the hypervisor after upgrading it),
which I think tends to support my theory that the guest isn't really
booting up, even though virsh reports it as started.

I've also experimented with:

- using only the root --disk
- using --cpu host-passthrough,secure=off
- using --boot hd,uefi=off
- using --boot bootmenu.enable=on,bios.useserial=on

None of these made any difference in the final behavior of the VM.

However, the bootmenu change did yield some interesting results.  With
this option in use, the BIOS bootmenu flashes onto the screen about
every 10 seconds.  If I manage to hit ESC before it goes away, I can
capture this:

   SeaBIOS (version 1.16.3-debian-1.16.3-2)
   Machine UUID 4763922d-20fa-4efa-8261-621281bee979

   iPXE (https://ipxe.org) 00:02.0 C000 PCI2.10 PnP
   PMM+BEFC6C80+BEF06C80 C000

   Press ESC for boot menu.

   Select boot device:

   1. Virtio disk PCI:00:05.0
   2. Legacy option rom
   3. iPXE (PCI 00:02.0)

For option 1, "PCI:00:05.0" matches up with the slot assigned to the
root disk in its <address>, so at least that makes sense.

If I pick option 1, I get the following, which disappears in less than a
second:

   Booting from Hard Disk...
   GRUB loading..
   Welcome to GRUB!

After this is printed, the screen blanks, and I'm eventually sent back
to the BIOS boot menu again.  I suspect this tight loop explains the
100% CPU utilization I see in top, since utilization drops if I hit ESC
and sit at the BIOS menu without making a choice.

I haven't had any success in coaxing GRUB to tell me what's going on. I
manually changed the timeout in /boot/grub/grub.cfg, and that seems to
change the behavior of the BIOS menu loop. This makes me think that GRUB
renders the menu and waits for the timeout before selecting the default,
and then something crashes after that... but for some reason the menu
and whatever else happens after the crash aren't available to the
console.

Do you have any other suggestions about things that I should explore, or
things that might cause the old kernel or init to no longer work with
the new libvirt?

I haven't attached the libvirt debug logs, since they're fairly large.
Let me know if you want them.

Thanks,

Ken

#1111337#24
Date:
2025-08-17 22:22:20 UTC
From:
To:
I have a fix.  If I adjust /etc/default/grub to set one of these values
and then run update-grub, then the guest works:

   GRUB_TERMINAL="console"
   GRUB_TERMINAL="serial"
   GRUB_TERMINAL="console serial"

Initially, since I couldn't log into the guest, I prototyped this on the
hypervisor and then made changes manually in /boot/grub/grub.cfg for
each of the guests using virt-edit. I started with a bunch of other
changes (for instance to enable the console TTYs when booting the
kernel, etc.), but in the end this was the minimal necessary fix.

The GRUB_TERMINAL change has two effects in /boot/grub/grub.cfg:
1) it removes the conditional logic that sets up for "terminal_output
gfxterm", and 2) it removes conditional logic in the Debian theme
section, leaving just two menu color lines.

From manual testing, I'm pretty confident that the first change for
gfxterm is the important one, but I still don't understand why it's
necessary.  It's not clear whether the change in behavior is due to
libvirt, grub, or the kernel package.  But at least it works, which is
quite a relief. :)

There's an inline patch below showing the complete set of differences
when using GRUB_TERMINAL="console". Changes are similar when using the
other working values.

Ken
--- grub.orig.cfg 2025-08-17 21:36:02.347698273 +0000 +++ grub.works.cfg 2025-08-17 21:36:15.979841505 +0000 @@ -55,30 +55,8 @@ fi } -if [ x$feature_default_font_path = xy ] ; then - font=unicode -else -insmod part_msdos -insmod lvm -insmod ext2 -set root='lvmid/fA9B2E-GJRf-H7XG-7HFA-1Wqq-3ZwA-kglN18/3KwGYi-I3t1-2jvP-ZsLj-fiDr-e21m-WMQ6dU' -if [ x$feature_platform_search_hint = xy ]; then - search --no-floppy --fs-uuid --set=root --hint='lvmid/fA9B2E-GJRf-H7XG-7HFA-1Wqq-3ZwA-kglN18/3KwGYi-I3t1-2jvP-ZsLj-fiDr-e21m-WMQ6dU' 537ffb3e-02da-4ed6-aa3b-8c84319f72a0 -else - search --no-floppy --fs-uuid --set=root 537ffb3e-02da-4ed6-aa3b-8c84319f72a0 -fi - font="/usr/share/grub/unicode.pf2" -fi - -if loadfont $font ; then - set gfxmode=auto - load_video - insmod gfxterm - set locale_dir=$prefix/locale - set lang=en_US - insmod gettext -fi -terminal_output gfxterm +terminal_input console +terminal_output console if [ "${recordfail}" = 1 ] ; then set timeout=30 else @@ -94,23 +72,8 @@ ### END /etc/grub.d/00_header ### ### BEGIN /etc/grub.d/05_debian_theme ### -insmod part_msdos -insmod lvm -insmod ext2 -set root='lvmid/fA9B2E-GJRf-H7XG-7HFA-1Wqq-3ZwA-kglN18/3KwGYi-I3t1-2jvP-ZsLj-fiDr-e21m-WMQ6dU' -if [ x$feature_platform_search_hint = xy ]; then - search --no-floppy --fs-uuid --set=root --hint='lvmid/fA9B2E-GJRf-H7XG-7HFA-1Wqq-3ZwA-kglN18/3KwGYi-I3t1-2jvP-ZsLj-fiDr-e21m-WMQ6dU' 537ffb3e-02da-4ed6-aa3b-8c84319f72a0 -else - search --no-floppy --fs-uuid --set=root 537ffb3e-02da-4ed6-aa3b-8c84319f72a0 -fi -insmod png -if background_image /usr/share/desktop-base/ceratopsian-theme/grub/grub-4x3.png; then - set color_normal=white/black - set color_highlight=black/white -else - set menu_color_normal=cyan/blue - set menu_color_highlight=white/blue -fi +set menu_color_normal=cyan/blue +set menu_color_highlight=white/blue ### END /etc/grub.d/05_debian_theme ### ### BEGIN /etc/grub.d/10_linux ###