Log::remap_settled maps each settled 4 MiB page with its own mmap and a null address hint, so the kernel picks an address for each one. Nothing ever unmaps a page above the compaction floor: evict_behind steps over a mapped victim by design since #759, and the only unmap paths are reclaim_to, which clears the slots under a punched range, and the drop.
So with map_settled on, the number of mappings the process holds rises with the size of the log and never comes down.
Measured on server1 rather than argued from the manual page. A small C program that maps a file a page at a time the way remap_settled does, counting the lines of /proc/self/maps either side:
64 pages mapped one at a time: 23 map lines before, 87 after, 64 added
512 pages mapped one at a time: 23 map lines before, 535 after, 512 added
One VMA a page, no merging. The merge does not happen because the kernel hands out addresses top down while the log maps ascending file offsets, so the addresses run the opposite way to the offsets and two neighbours in the file are never neighbours in the address space.
Two costs, and the second is the one that matters:
The fix is to reserve the span once and put the pages into it, so the addresses run the way the offsets do. Measured the same way:
512 pages into a reservation: 23 before, 24 reserved, 24 filled, 535 with every other one dropped
One anonymous PROT_NONE MAP_NORESERVE reservation, then each page mmaped over it with MAP_FIXED, and 512 mappings collapse into one VMA. MAP_NORESERVE and PROT_NONE so the reservation is address space and not memory, which is free on a 64 bit host.
The last number in that line is the honest caveat and it is why this needs care rather than a patch. Drop every other page back to PROT_NONE and the single VMA splits into 512 again, so the collapse holds only while the mapped region is a contiguous run. That happens to be what remap_settled produces today, since remap_from is a monotonic cursor and eviction steps over mapped pages rather than taking them, but it is a property of the current policy rather than of the design, and anything that starts unmapping in the middle gets the old behaviour back without a test noticing.
So this issue is three pieces of work:
- Reserve
max_pages of address space at open when map_settled is on, and map into it with MAP_FIXED.
- A test that asserts the mapping count, which on Linux means reading
/proc/self/maps and on macOS means skipping. It is the only way this stays fixed.
- Decide what a page below the mapped run means.
reclaim_to already drops slots under the punched range, and those are at the bottom of the run rather than in the middle of it, so the run stays contiguous; that wants stating in the code rather than being true by accident.
Windows has no map_read at all, so none of this applies there.
Log::remap_settledmaps each settled 4 MiB page with its ownmmapand a null address hint, so the kernel picks an address for each one. Nothing ever unmaps a page above the compaction floor:evict_behindsteps over a mapped victim by design since #759, and the only unmap paths arereclaim_to, which clears the slots under a punched range, and the drop.So with
map_settledon, the number of mappings the process holds rises with the size of the log and never comes down.Measured on server1 rather than argued from the manual page. A small C program that maps a file a page at a time the way
remap_settleddoes, counting the lines of/proc/self/mapseither side:One VMA a page, no merging. The merge does not happen because the kernel hands out addresses top down while the log maps ascending file offsets, so the addresses run the opposite way to the offsets and two neighbours in the file are never neighbours in the address space.
Two costs, and the second is the one that matters:
preadfor, so it is exactly the path the option is supposed to make cheaper.vm.max_map_countis 65530 by default, which is 256 GiB of log. Past thatmmaprefuses, and it refuses for everything else in the process too, not only for this. server1 has it at 1048576, so no run of record has hit it, and a host with the default would.The fix is to reserve the span once and put the pages into it, so the addresses run the way the offsets do. Measured the same way:
One anonymous
PROT_NONEMAP_NORESERVEreservation, then each pagemmaped over it withMAP_FIXED, and 512 mappings collapse into one VMA.MAP_NORESERVEandPROT_NONEso the reservation is address space and not memory, which is free on a 64 bit host.The last number in that line is the honest caveat and it is why this needs care rather than a patch. Drop every other page back to
PROT_NONEand the single VMA splits into 512 again, so the collapse holds only while the mapped region is a contiguous run. That happens to be whatremap_settledproduces today, sinceremap_fromis a monotonic cursor and eviction steps over mapped pages rather than taking them, but it is a property of the current policy rather than of the design, and anything that starts unmapping in the middle gets the old behaviour back without a test noticing.So this issue is three pieces of work:
max_pagesof address space at open whenmap_settledis on, and map into it withMAP_FIXED./proc/self/mapsand on macOS means skipping. It is the only way this stays fixed.reclaim_toalready drops slots under the punched range, and those are at the bottom of the run rather than in the middle of it, so the run stays contiguous; that wants stating in the code rather than being true by accident.Windows has no
map_readat all, so none of this applies there.