This page documents Wine-NSPA’s memory surface: local sections, large-page mappings, RT-keyed page locking and hugetlb, working-set support, and shared-memory backing choices.
Wine-NSPA’s memory work is not just large-page syscall support and it is not
just memfd everywhere.
Five distinct pieces are active:
The important design point is that these pieces solve different problems. Client-side sections reduce mapping RPC traffic on same-process file-backed mapping workloads. Large pages reduce TLB pressure on hot mappings. Working-set reporting makes the Windows-visible memory surface honest enough for tools and tests. Dedicated shared-memory backends keep bypass subsystems off the wineserver hot path without forcing every shared region through one mechanism.
| Surface | Current behavior |
|---|---|
GetLargePageMinimum() |
Returns the real smallest huge-page size published into KUSER_SHARED_DATA, or 0 when the host has no usable hugepage pool. |
VirtualAlloc(MEM_LARGE_PAGES) |
Accepted and mapped through the unix VM path, with large-page alignment and large-page view tagging preserved for reporting. |
NtAllocateVirtualMemoryEx(... MEM_LARGE_PAGES ...) |
Accepted on the same large-page path, including the 1 GiB huge-page request shape when the host supports it. |
CreateFileMapping(SEC_LARGE_PAGES) |
Privilege-gated, backed through large-page-capable memfd_create() on the wineserver side, and mapped with large-page alignment rules. |
RT-keyed mlockall() |
When NSPA_RT_PRIO is set, process startup issues mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) so touched pages stay locked without pretending Wine has a full working-set manager. |
| RT-keyed automatic hugetlb promotion | When NSPA_RT_PRIO is set, eligible anonymous RESERVE|COMMIT allocations auto-promote onto the existing large-page mapping path, including PAGE_EXECUTE_READWRITE JIT-style arenas. |
| RT-keyed heap arena hugetlb backing | When NSPA_RT_PRIO is set, eligible growable heap arenas round up and full-commit onto huge pages, eliminating most heap-driven mmap / mprotect / page-fault traffic from the hot path. |
| hugetlb pool pressure handling | auto-promotion stops when free hugepages drop below a 10% watermark, and auto-promoted allocations fall back to regular pages instead of failing when the pool is exhausted |
| partial-op demote | sub-hugepage MEM_DECOMMIT, partial MEM_RELEASE, or partial VirtualProtect on an auto-promoted view first demote the view back to regular pages so NT semantics stay correct |
MEM_RESET under mlockall() |
MEM_RESET uses munlock + MADV_DONTNEED, so pages can really leave RAM before their next touch |
| local file-backed sections | Eligible unnamed file-backed sections on local-file handles can stay client-side for create / map / unmap / query / close, with same-process duplicate promoted only when the call crosses a real server-handle boundary. |
QueryWorkingSetEx() |
Current-process reporting is live, including LargePage flag reporting. Preferred path is PAGEMAP_SCAN; fallback is /proc/self/pagemap. |
Get/SetProcessWorkingSetSize(Ex) |
Working-set quota values are accepted, stored, and returned through ProcessQuotaLimits. This is bookkeeping, not a working-set trimmer. |
| msg-ring shared state | Per-queue message / redraw / paint-cache regions live in dedicated memfd mappings. |
| local-file shared state | Cross-process inode sharing arbitration lives in a dedicated shared memfd table, separate from message rings and separate from large-page mappings. |
The new client-side section path sits between local-file handles and the traditional wineserver mapping-object path.
Eligible unnamed file-backed sections on local-file handles can:
FILE_MAPPING_* bits back into the local-file aggregate so later
share checks and end-of-file changes still see active mappingsThis is a memory feature as much as a file feature. It changes how Windows section objects are represented, how views are installed, and how file-mapping state is reflected back into the rest of the process.
The main boundary is still the same one the local-file path already uses: cross-process or namespace-visible semantics remain on the server side. Same-process mapping is the fast path. Cross-process duplication is not guessed at.
For the full lifecycle and ownership rules, see Local Section Bypass.
Large-page support has three allocation shapes: explicit Windows large-page APIs, RT-keyed anonymous-memory promotion, and heap-arena backing that rides on that same anonymous path. Section-backed mappings remain the one large-page shape that still crosses wineserver once for backing creation.
The large-page contract is:
QueryWorkingSetEx() can later report LargePage=1That last point matters. The feature is not just about getting mmap()
flags accepted. It is about keeping the Windows-visible memory story coherent
from allocation to reporting.
Transparent promotion is an optimization, not a new Windows API. That means the fast path also needs a clear “stay honest” rule set:
MEM_DECOMMIT, MEM_RELEASE, and VirtualProtect retain NT semanticsMEM_RESET runs under mlockall(), unlock before MADV_DONTNEED so the
kernel can actually reclaim the pagesWorking-set support is deliberately split into two levels:
QueryWorkingSetEx() tells tools and tests
what is resident and whether a page is large-page-backedGetProcessWorkingSetSize(Ex) and
SetProcessWorkingSetSize(Ex) store and return Windows-visible quota valuesWhat is not part of this surface is a Linux-side working-set trimmer that enforces those quota values by reclaiming or emptying the process working set.
That division is intentional. Reporting should be correct. Quota APIs should
behave coherently. RT-keyed mlockall() improves residency behavior for the
actual process, but the docs should not imply that Wine-NSPA has grown a full
Windows-style working-set manager or a quota-enforcing trimmer. It has not.
The tree uses several shared-memory backends, and they are chosen per subsystem instead of by one global rule.
This is why a single “memfd page” would be misleading. memfd is important,
but it is not the whole story:
memfd regionsmemfd tablesThe docs should keep those roles separate, because the correctness rules and performance goals are different.
The public large-pages PE harness covers more than one call shape:
VirtualAlloc(MEM_LARGE_PAGES)QueryWorkingSetEx() LargePage reporting on the resulting viewCreateFileMapping(SEC_LARGE_PAGES)SEC_LARGE_PAGES behaviorThat means the public test surface validates both the allocation path and
the Windows-visible reporting path. Local sections are currently validated
through the workload path rather than a dedicated PE harness:
CreateFile -> CreateFileMapping -> CloseHandle(file) -> MapViewOfFile is
clean on the local path and materially reduces mapping RPC traffic.
The relevant public harness documentation lives on nspa-rt-test.gen.html.
The newer RT-keyed memory work was validated separately on real workloads and targeted shell harnesses:
mlockall() under NSPA_RT_PRIO cut perf page faults from 561/s to
451/s, cut bpf page faults from 869/s to 629/s, and tightened max
futex wait from 94us to 49us, with VmLck around 300848kB.NSPA_RT_PRIO; the cleanup pass ended with test-huge-auto.sh 3/3 PASS.test-huge-decommit.sh validates zero-on-recommit after partial decommit,
test-huge-rwx.sh validates RWX JIT-style promotion, and pool-pressure cases
fall back instead of failing an ordinary allocation3 or 6 to
104, reduced dTLB miss / insn to 0.071%, reduced mmap rate from
33-61/s to 0.13/s, reduced mprotect rate from 56-90/s to 0.03/s,
and reduced page-faults from 753-869/s to 2.8/s.test-mlock-ws.sh 4/4, test-huge-auto.sh 3/3, and
test-heap-hugepage.sh 3/3.| File | Responsibility |
|---|---|
dlls/ntdll/unix/nspa/local_file.c |
local-file table plus local-section table, handle ranges, mapping-bit publication |
dlls/ntdll/unix/sync.c / dlls/ntdll/unix/virtual.c |
section creation, map / unmap / query hooks, and large-page / view semantics |
server/mapping.c |
hugepage inventory scan, LargePageMinimum publication, SEC_LARGE_PAGES privilege gate, large-page section backing |
dlls/kernelbase/memory.c |
GetLargePageMinimum() |
dlls/ntdll/unix/nspa/huge_auto.c |
automatic hugetlb-promotion eligibility, watermarking, and demote helper for auto-promoted views |
dlls/ntdll/unix/virtual.c |
anonymous large-page allocation, automatic hugetlb promotion, fallback and demote call sites, MEM_RESET, large-page view tracking, QueryWorkingSetEx() reporting |
dlls/ntdll/unix/process.c |
ProcessQuotaLimits working-set bookkeeping plus RT-keyed mlockall() startup |
dlls/ntdll/heap.c |
RT-keyed hugepage arena backing for eligible growable heaps |
dlls/kernelbase/process.c |
Get/SetProcessWorkingSetSize(Ex) user-facing entrypoints |
dlls/kernelbase/debug.c |
QueryWorkingSetEx() / K32QueryWorkingSetEx() front-end |
server/queue.c |
per-queue memfd regions for msg-ring / redraw / paint-cache |
server/nspa/local_file.c |
shared inode arbitration memfd table for the local-file path |