Skip to content

KVM: Assign explicit PCI slot when hot-plugging NIC for sequential naming#12826

Open
jmsperu wants to merge 1 commit intoapache:4.20from
jmsperu:fix/nic-hotplug-pci-slot-assignment
Open

KVM: Assign explicit PCI slot when hot-plugging NIC for sequential naming#12826
jmsperu wants to merge 1 commit intoapache:4.20from
jmsperu:fix/nic-hotplug-pci-slot-assignment

Conversation

@jmsperu
Copy link

@jmsperu jmsperu commented Mar 17, 2026

Description

Fixes #12825

When hot-plugging a NIC to a running KVM VM, libvirt auto-assigns the next free PCI slot. Since non-NIC PCI devices (virtio-serial controller, virtio-disk, memballoon, watchdog) occupy slots immediately after existing NICs (slots 0x05-0x08), the hot-plugged NIC gets a much higher slot number (e.g., 0x09), causing the guest to see ens9 instead of the expected sequential ens5. The NIC shows as DOWN with no IP.

Changes

Modified LibvirtPlugNicCommandWrapper.java to explicitly assign a PCI slot when hot-plugging:

  1. Query the domain XML to find all PCI slots currently in use
  2. Find the highest PCI slot used by existing NICs
  3. Assign the next free slot after the last NIC slot

This matches the approach already used by LibvirtReplugNicCommandWrapper which preserves PCI slots during re-plug operations (line 70: interfaceDef.setSlot(oldPluggedNic.getSlot())).

Falls back gracefully to libvirt auto-assignment if:

  • No free PCI slots are available (slots 0x01-0x1f exhausted)
  • Domain XML cannot be read

Evidence

Before fix — hot-plugged NIC at non-sequential PCI slot 0x09:

Guest interfaces: ens3 (slot 3), ens4 (slot 4), ens9 (slot 9) ← non-sequential
NIC state: DOWN, no IP assigned

After relocating non-NIC devices to high slots (proving the concept):

Guest interfaces: ens3 (slot 3), ens4 (slot 4) ← sequential

Note: For complete sequential naming on existing VMs, a follow-up change is needed to assign non-NIC PCI devices to higher slot numbers during VM creation in createDevicesDef().

Test plan

  • Hot-plug a NIC to a running KVM VM and verify the new NIC gets the next PCI slot after existing NICs
  • Verify guest OS sees sequential interface naming (e.g., ens5 after ens3, ens4)
  • Verify fallback to libvirt auto-assignment when domain XML cannot be read
  • Verify re-plug (ReplugNicCommand) still works correctly
  • Verify VM with maximum NICs still works (PCI slot exhaustion graceful handling)

🤖 Generated with Claude Code

…ial naming

When hot-plugging a NIC to a running VM, libvirt auto-assigns the next
free PCI slot. Since non-NIC devices (virtio-serial, disk, balloon,
watchdog) occupy slots immediately after existing NICs, the hot-plugged
NIC gets a much higher slot number (e.g. 0x09 instead of 0x05), causing
the guest to see non-sequential interface names (ens9 instead of ens5).

This fix queries the domain XML to find all used PCI slots and assigns
the next free slot after the highest existing NIC slot. This matches
the approach already used by LibvirtReplugNicCommandWrapper which
preserves PCI slots during re-plug operations.

Fixes apache#12825

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@DaanHoogland DaanHoogland added this to the 4.20.4 milestone Mar 17, 2026
@DaanHoogland DaanHoogland requested review from Copilot and weizhouapache and removed request for Copilot March 17, 2026 09:17
@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 4.15%. Comparing base (61afb4c) to head (c572b98).

❗ There is a different number of reports uploaded between BASE (61afb4c) and HEAD (c572b98). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (61afb4c) HEAD (c572b98)
unittests 1 0
Additional details and impacted files
@@              Coverage Diff              @@
##               4.20   #12826       +/-   ##
=============================================
- Coverage     16.24%    4.15%   -12.10%     
=============================================
  Files          5664      404     -5260     
  Lines        500463    32966   -467497     
  Branches      60779     5893    -54886     
=============================================
- Hits          81308     1370    -79938     
+ Misses       410059    31420   -378639     
+ Partials       9096      176     -8920     
Flag Coverage Δ
uitests 4.15% <ø> (ø)
unittests ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to make KVM NIC hot-plug behavior more deterministic by explicitly setting a PCI slot for the newly attached NIC, with the goal of preserving predictable/sequential interface naming inside the guest.

Changes:

  • Adds logic in LibvirtPlugNicCommandWrapper to compute the next available PCI slot from the domain XML.
  • Sets the computed PCI slot on the InterfaceDef before calling Domain.attachDevice().
  • Introduces fallback behavior to let libvirt auto-assign the slot when XML cannot be read or when no slots appear available.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +131
String domXml = vm.getXMLDesc(0);

// Parse all PCI slot numbers currently in use
Set<Integer> usedSlots = new HashSet<>();
Pattern slotPattern = Pattern.compile("slot='0x([0-9a-fA-F]+)'");
Matcher matcher = slotPattern.matcher(domXml);
while (matcher.find()) {
usedSlots.add(Integer.parseInt(matcher.group(1), 16));
}
Comment on lines +115 to +154
/**
* Finds the next available PCI slot for a hot-plugged NIC by examining
* all PCI slots currently in use by the domain. This ensures the new NIC
* gets a sequential PCI address relative to existing NICs, resulting in
* predictable interface naming in the guest OS (e.g. ens5 instead of ens9).
*/
private Integer findNextAvailablePciSlot(final Domain vm, final List<InterfaceDef> pluggedNics) {
try {
String domXml = vm.getXMLDesc(0);

// Parse all PCI slot numbers currently in use
Set<Integer> usedSlots = new HashSet<>();
Pattern slotPattern = Pattern.compile("slot='0x([0-9a-fA-F]+)'");
Matcher matcher = slotPattern.matcher(domXml);
while (matcher.find()) {
usedSlots.add(Integer.parseInt(matcher.group(1), 16));
}

// Find the highest PCI slot used by existing NICs
int maxNicSlot = 0;
for (InterfaceDef pluggedNic : pluggedNics) {
if (pluggedNic.getSlot() != null && pluggedNic.getSlot() > maxNicSlot) {
maxNicSlot = pluggedNic.getSlot();
}
}

// Find next free slot starting from maxNicSlot + 1
// PCI slots range from 0x01 to 0x1f (slot 0 is reserved for host bridge)
for (int slot = maxNicSlot + 1; slot <= 0x1f; slot++) {
if (!usedSlots.contains(slot)) {
return slot;
}
}

logger.warn("No free PCI slots available, letting libvirt auto-assign");
return null;
} catch (LibvirtException e) {
logger.warn("Failed to get domain XML for PCI slot calculation, letting libvirt auto-assign", e);
return null;
}
Comment on lines +73 to +81
// Explicitly assign PCI slot to ensure sequential NIC naming in the guest.
// Without this, libvirt auto-assigns the next free PCI slot which may be
// non-sequential with existing NICs (e.g. ens9 instead of ens5), causing
// guest network configuration to fail.
Integer nextSlot = findNextAvailablePciSlot(vm, pluggedNics);
if (nextSlot != null) {
interfaceDef.setSlot(nextSlot);
logger.debug("Assigning PCI slot 0x" + String.format("%02x", nextSlot) + " to hot-plugged NIC");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KVM: Hot-plugged NIC gets non-sequential PCI slot causing unpredictable interface naming in guest

3 participants