Skip to content

Report hugepage-backed allocatable memory as hypervisor capacity - #103

Merged
PhilippMatthes merged 1 commit into
mainfrom
report-hugepage-memory-capacity
Oct 29, 2026
Merged

PhilippMatthes merged 1 commit into
mainfrom
report-hugepage-memory-capacity

Conversation

@PhilippMatthes

Copy link
Copy Markdown
Member

Problem

In internal/libvirt/libvirt.go, addAllocationCapacity derives the hypervisor's memory capacity (Status.Capacity[memory] and per-cell Cell.Capacity[memory]) from each NUMA cell's total physical memory reported by libvirt capabilities.

That physical value fluctuates between otherwise-identical hypervisors by a few hundred MB, because linux reserves/frees varying amounts during boot:

[    1.371895] efi: Freeing EFI boot services memory: 327072K
[   95.725123] Freeing initrd memory: 450684K
...

This makes capacity reporting non-deterministic across the fleet.

Fix

All KVM OpenStack Nova flavors use the extra spec hw:mem_page_size=large, i.e. every VM runs on hugepages. The memory that can actually be allocated to VMs is therefore the hugepage-backed memory, which is explicitly configured (like HugePages_Total in /proc/meminfo) and does not fluctuate between hosts.

We now report that stable value as the memory capacity.

The libvirt capabilities XML already exposes per-cell hugepage counts (<pages size='2048'>N</pages>), and the Go structs already parse them (CapabilitiesHostTopologyCell.Pages) — so no CRD or XML-schema change is needed. We only change which value we write into the existing ResourceMemory capacity.

Changes

  • New helper HugepageMemoryToResource(pages) in utils.go — sums all non-base (hugepage) page sizes to bytes. The base page is the smallest reported size and is excluded; both 2 MiB and 1 GiB hugepages count.
  • addAllocationCapacity uses the helper instead of cell.Memory. addEffectiveCapacity and all CPU/allocation logic are unchanged (effective capacity is generic over the capacity map).
  • Fallback: if a cell reports no hugepages (helper returns zero), fall back to the cell's total physical memory and log it, so we never report a spurious zero capacity.

Notes for reviewers

  • Status.Capabilities.HostMemory is intentionally left as the physical total for information; it will now exceed Status.Capacity[memory] — the gap is host-reserved + non-hugepage memory.
  • A host that has not allocated hugepages reverts (per cell) to the fluctuating physical value via the fallback, with an info log naming the cell. Given the stated invariant that all KVM hosts use hugepages, this should only fire during misconfiguration or a pre-boot window.

Tests

  • Added TestHugepageMemoryToResource (2 MiB, 1 GiB, mixed, empty, base-only, base-not-first, bad-unit).
  • Added TestAddAllocationCapacity_FallsBackToPhysicalMemoryWithoutHugepages.
  • Updated the two existing allocation-capacity tests to populate Pages.

Verification: go build ./..., go vet ./internal/libvirt/..., go test ./internal/libvirt/... all pass.

@PhilippMatthes
PhilippMatthes force-pushed the report-hugepage-memory-capacity branch from 2167d0a to e7d59ae Compare October 29, 2026 07:57
Memory capacity was derived from each NUMA cell's total physical memory
reported by libvirt. That value fluctuates between otherwise-identical
hosts by a few hundred MB because linux reserves/frees varying amounts
during boot, making capacity reporting non-deterministic.

All KVM nova flavors use hw:mem_page_size=large, so the memory that can
actually be allocated to VMs is the hugepage-backed memory, which is
explicitly configured and therefore stable. Report that as the memory
capacity instead.

The libvirt capabilities XML already exposes per-cell hugepage counts
and the Go structs already parse them, so no CRD or schema change is
needed. A new HugepageMemoryToResource helper sums all non-base page
sizes. If a cell reports no hugepages, fall back to its total physical
memory so we never report a spurious zero capacity.

Status.Capabilities.HostMemory is left as the physical total for
information; it will now intentionally exceed Status.Capacity[memory].

Signed-off-by: Philipp Matthes <p.matthes@sap.com>
@PhilippMatthes
PhilippMatthes force-pushed the report-hugepage-memory-capacity branch from e7d59ae to 95083dc Compare October 29, 2026 08:04
@github-actions

Copy link
Copy Markdown

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt 45.13% (+2.24%) 👍
Coverage details

Compared to commit c320213 (run #39742049513)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt/libvirt.go 73.91% (+0.14%) 230 (+5) 170 (+4) 60 (+1) 👍
github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt/utils.go 55.56% (+23.41%) 45 (+17) 25 (+16) 20 (+1) 🌟

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files:

  • github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt/libvirt_test.go
  • github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt/utils_test.go

@fwiesel fwiesel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd say it is better than before, but we should be aware that this query the host capabilities.
Currently, in practice, all huge-pages are only for VMs, but that doesn't have to be.

Technically, we could (and probably will) limit the resources for VMs.
For faster network operations for example, we need hugepages just for the networking stack. That is not schedulable memory for the VMs.

Ideally, we would query the cgroupv2 for the machine.slice directly. But then I am not 100% sure in which slice these hugepages for networking will land either.
So, that may be wasted work then.

@PhilippMatthes

Copy link
Copy Markdown
Member Author

Thanks for the added context!

@PhilippMatthes
PhilippMatthes merged commit b247708 into main Oct 29, 2026
7 checks passed
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.

2 participants