Skip to content

Give a system VM one NIC queue per CPU - #14136

Open
bhouse-nexthop wants to merge 2 commits into
apache:4.22from
bhouse-nexthop:vr-nic-multiqueue-422
Open

Give a system VM one NIC queue per CPU#14136
bhouse-nexthop wants to merge 2 commits into
apache:4.22from
bhouse-nexthop:vr-nic-multiqueue-422

Conversation

@bhouse-nexthop

Copy link
Copy Markdown
Collaborator

Description

A system VM gets one NIC queue no matter how many CPUs it has, so every packet interrupt lands on
CPU0. Adding CPUs to a router therefore does not move more packets, which is the only reason to add
them.

The queue count is already a VM detail, nic.multiqueue.number, and the agent already turns it into
<driver queues='N'/> on the interface. A user VM can set it three ways:

how since
deployVirtualMachine nicmultiqueuenumber=N 4.18
updateVirtualMachine details[0].nic.multiqueue.number=N
the Settings tab - listDetailOptions offers the key for KVM

A system VM goes through none of them. It is not created by deployVirtualMachine, it is not a
UserVm so updateVirtualMachine does not apply, and it has no details editor. There is no way
to set it at all
, which is why this is a fix rather than a new setting.

So a system VM now gets one queue per CPU when nothing is set:

CPUs emitted
1 nothing, identical to today
4 queues='4'
512 queues='256'

The 256 is the tap device ceiling in the host kernel, which refuses the interface rather than
trimming to fit. Above the CPU count it would make no difference anyway - the guest driver uses
min(CPUs, queues) and pushes that at probe, so no guest side change is needed.

A queue number already set on the VM still wins.

The default offering is a single CPU, so a default install emits the same domain XML as before.
Only a system VM someone has already resized differs, and it differs in the direction the resize
asked for.

Worth calling out

  • There is no way to turn this off. There is no way to turn it on today either, so it is
    symmetric, but it is worth being explicit about on a release branch.
  • The cost scales with NIC count, not just queue count. A VPC router has one NIC per tier, so a
    4 CPU router fronting 8 tiers goes from 8 vhost threads to 32. Idle queues are cheap, but the
    thread count is not nothing on a dense host.

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial

How Has This Been Tested?

KVMGuruTest, Tests run: 35, Failures: 0. Six new cases:

  • a 4 CPU system VM gets queues='4'
  • a 512 CPU one stops at the tap ceiling of 256
  • a single CPU one is left alone, so no attribute is emitted
  • other details on the VM are preserved
  • a queue number already on the VM wins
  • a user VM is never touched

The guest side was checked against the driver source the system VM runs, Linux 6.1
drivers/net/virtio_net.c:

	/* Enable multiqueue by default */
	if (num_online_cpus() >= max_queue_pairs)
		vi->curr_queue_pairs = max_queue_pairs;
	else
		vi->curr_queue_pairs = num_online_cpus();

followed by virtnet_set_queues(vi, vi->curr_queue_pairs) at probe, so the queues come up without
ethtool -L. The 256 comes from MAX_TAP_QUEUES in drivers/net/tun.c.

A system VM gets a single NIC queue however many CPUs it has, so every
packet interrupt lands on CPU0. Adding CPUs to a router does not move
more packets, which is the only reason to add them.

Queue count is already a VM detail, nic.multiqueue.number, and the agent
already turns it into <driver queues='N'/>. A user VM sets it three ways:

  deployVirtualMachine nicmultiqueuenumber=N
  updateVirtualMachine details[0].nic.multiqueue.number=N
  the Settings tab, listDetailOptions offers the key

A system VM goes through none of them, so there is no way to set it at
all. This gives one queue per CPU when nothing is set.

  1 CPU    no queues attribute, unchanged
  4 CPU    queues='4'
  512 CPU  queues='256', a tap device goes no higher

The default offering is a single CPU, so a default install is unchanged.
Only a resized system VM differs, and it differs the way the resize
asked for. A queue number set on the VM still wins.

Signed-off-by: Brad House <bhouse@nexthop.ai>
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 17.93%. Comparing base (d0d49bc) to head (0d63a76).
⚠️ Report is 1 commits behind head on 4.22.

Files with missing lines Patch % Lines
.../java/com/cloud/hypervisor/HypervisorGuruBase.java 85.71% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##               4.22   #14136   +/-   ##
=========================================
  Coverage     17.93%   17.93%           
- Complexity    16143    16150    +7     
=========================================
  Files          5928     5928           
  Lines        535174   535188   +14     
  Branches      65494    65497    +3     
=========================================
+ Hits          95981    96007   +26     
+ Misses       428266   428254   -12     
  Partials      10927    10927           
Flag Coverage Δ
uitests 4.02% <ø> (ø)
unittests 19.00% <85.71%> (+<0.01%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 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.

MAX_TAP_QUEUES was declared between the @Inject and
networkOfferingDetailsDao, so the annotation bound to the constant and
the DAO was never injected. getNicDetails() dereferences it for every
NIC, so every VM deploy failed with an NPE behind a 530 "Internal Server
Error", which is what the simulator CI run was reporting.

Move the constant above the injected fields and give the DAO its
annotation back.

Signed-off-by: Brad House <bhouse@nexthop.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant