Commit 9444981
Naman Jain
Drivers: hv: mshv_vtl: restore 2M VTL0 low mappings to bound PageTables
Under sustained VTL0 I/O the paravisor's /proc/meminfo PageTables grew
from ~660 kB into the tens of MB, steadily eating into VTL2 memory. The
low VTL0 mapping (/dev/mshv_vtl_low) was installing a 4K PTE for every
page it touched instead of a 2M PMD, so each touched 2M region cost a
full extra page table.
The regression came from gating the huge-fault path on a registered
struct page. Underhill registers lower-VTL memory with the kernel
lazily - only for ranges handed to a device for DMA (expose_va) - because
a small VTL2 cannot afford struct pages for all of guest RAM. The CPU
relay path, however, touches far more memory than is ever DMA'd, so most
huge faults hit not-yet-registered pfns, failed the gate, and fell back
to 4K.
Fix it by mapping huge VTL0 faults by raw pfn again, via
vmf_insert_pfn_pmd() with no registration gate (the pre-v6.15 behaviour).
vmf_insert_pfn_pmd() dereferences no struct page, so a 2M map is valid
even for an unregistered pfn and PageTables stays flat.
Going back to the raw-pfn path re-exposes issues that the recent
folio-based rework had addressed; handle each without giving up 2M:
- rmap/RSS drift: vmf_insert_folio_pmd() adds a file rmap and RSS that
zap_huge_pmd() never reverses on this VM_MIXEDMAP, non-DAX VMA
(vma_is_special_huge() true, vma_is_dax() false), leaking a folio
reference and tripping a "Bad rss-counter state" BUG. The pfn
inserter carries no such state, so this drift simply goes away.
- GUP refcount race: a huge pfn PMD holds no folio reference, so a
zap racing pin_user_pages() could drop the refcount to 0 and warn
in try_grab_folio(). Take a permanent reference on each pgmap folio
in add_vtl0_mem() instead; VTL0 memory lives for the partition's
lifetime, so the count never reaches 0.
- GUP over smaller folios: add_vtl0_mem() derives the folio order
from the range's alignment, so a sub-2M-aligned edge yields folios
smaller than a PMD. Slow GUP would then batch a whole 2M span's
references onto one base folio and corrupt its neighbours. Record
such ranges on a normally-empty list - before the range itself is
published - and fall back to 4K for any 2M window that overlaps one,
so the mapping order never exceeds the folio order; only tiny
RAM-edge tails lose 2M.
- GUP into a memmap-less range: GUP on a huge pfn PMD walks the struct
page (follow_huge_pmd -> pmd_page -> try_grab_folio) and would oops
for a range whose devm_memremap_pages() failed. Track such failed
ranges and fall back to 4K (pte_special) for any 2M window that
overlaps one, so GUP fails gracefully with -EFAULT while all normal
memory stays 2M.
Also make add_vtl0_mem() idempotent (an already-registered range returns
success, zapping any stale 4K PTEs so they refault as 2M) so
re-registration across servicing never reports a spurious failure or
leaves memory on 4K. Keep the failed-range bookkeeping consistent under
concurrent registration: re-check registration when recording a failure,
clear stale markers after the range is published so a transient -EBUSY
never strands valid memory on 4K, coalesce failed ranges so repeated
failures cannot grow the list without bound, and report success when a
concurrent add has already registered the range.
Fixes: 775741a ("Drivers: hv: mshv_vtl: use folio-aware inserters for huge VTL0 mappings")
Signed-off-by: Naman Jain <[email protected]>1 parent ee40c82 commit 9444981
1 file changed
Lines changed: 323 additions & 42 deletions
0 commit comments