Wine-NSPA – Memory, Sections, Large Pages, and Working-Set Support

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.

Table of Contents

  1. Overview
  2. Coverage
  3. Client-side sections
  4. Large-page allocation and mapping
  5. Working-set reporting and quota semantics
  6. Shared-memory backing choices
  7. Validation and observed effect
  8. Implementation map
  9. Related docs

1. Overview

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.


2. Coverage

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.

3. Client-side sections

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:

This 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.


4. Large-page allocation and mapping

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.

Large-page allocation and mapping paths Windows-visible entrypoints `GetLargePageMinimum()` `VirtualAlloc(MEM_LARGE_PAGES)` `NtAllocateVirtualMemoryEx(...)` `CreateFileMapping(SEC_LARGE_PAGES)` Wine userspace kernelbase reads `KUSER_SHARED_DATA` `virtual.c` validates size + alignment large-page views keep `SEC_LARGE_PAGES` tag working-set reporting can later expose `LargePage` anonymous path stays in-process; section path crosses wineserver once Kernel and wineserver backing hugepage inventory scanned at startup `LargePageMinimum` published to shared data anonymous large pages use hugepage-aware `mmap` section path uses `memfd_create(... MFD_HUGETLB ...)` `SEC_LARGE_PAGES` requires `SeLockMemoryPrivilege` Anonymous path `VirtualAlloc(MEM_LARGE_PAGES)` or `NtAllocateVirtualMemoryEx(... MEM_LARGE_PAGES ...)` 2 MiB large pages today; 1 GiB huge-page request shape also accepted when configured Section-backed path `CreateFileMapping(SEC_LARGE_PAGES)` wineserver mapping object + hugetlb-capable memfd map-view path enforces large-page size alignment before the view is installed

The large-page contract is:

That 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.

4.1 Automatic hugetlb promotion safety rules

Transparent promotion is an optimization, not a new Windows API. That means the fast path also needs a clear “stay honest” rule set:

Automatic hugetlb promotion: promote when safe, demote or fall back when not eligible alloc anonymous `RESERVE|COMMIT` RW or RWX, RT gate enabled, size and alignment fit promotion checks free-pool watermark: refuse below 10% pool exhaust on auto-promote: retry on regular pages explicit large-page callers still keep fail-fast semantics steady state hugetlb-backed view or regular-page fallback if transparent promotion was refused partial-op guard sub-hugepage decommit / release / protect demote auto-promoted view before the NT operation runs reclaim guard `MEM_RESET` under `mlockall()` `munlock + MADV_DONTNEED` so the page can really leave RAM

5. Working-set reporting and quota semantics

Working-set support is deliberately split into two levels:

What 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.

Working-set reporting vs quota bookkeeping Reporting path `QueryWorkingSetEx()` `NtQueryVirtualMemory(... MemoryWorkingSetExInformation ...)` current-process pages only preferred probe: `PAGEMAP_SCAN` fallback probe: `/proc/self/pagemap` returns residency bits plus `LargePage` this is the path the public `large-pages` harness uses to confirm large-page-backed views Quota path `SetProcessWorkingSetSize(Ex)` `NtSetInformationProcess(ProcessQuotaLimits)` values stored in process bookkeeping `GetProcessWorkingSetSize(Ex)` returns the stored values no kernel-side trimmer, no forced sweep, no fake reclaim story Windows-visible quota surface is present without fake enforcement keeps compatibility and diagnostics honest without claiming a memory manager Wine does not have same overall memory surface, different semantics

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.


6. Shared-memory backing choices

The tree uses several shared-memory backends, and they are chosen per subsystem instead of by one global rule.

Shared-memory backing choices by subsystem Request / reply payload per-thread `request_shm` server-call payload bytes reply payload bytes used by gamma and by the canonical wineserver path Per-queue `memfd` msg-ring send / reply slots redraw push ring paint-cache metadata queue-local, fd-passed, mapped by the client peers that need it Shared `memfd` tables local-file inode arbitration shared `(dev, inode)` state bucket PI mutex words one published region, reused by every process that participates in the local-file path Large-page mappings hugepage-backed anonymous views or hugetlb-capable section memfd not part of msg-ring or inode-table plumbing separate goal: page size and locking semantics, not peer-to-peer bypass state

This is why a single “memfd page” would be misleading. memfd is important, but it is not the whole story:

The docs should keep those roles separate, because the correctness rules and performance goals are different.


7. Validation and observed effect

The public large-pages PE harness covers more than one call shape:

That 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:


8. Implementation map

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