Skip to content

fix(memory): use fallocate(PUNCH_HOLE) for discard_range on MAP_SHARED regions - #6168

Open
AdaAibaby wants to merge 1 commit into
firecracker-microvm:mainfrom
AdaAibaby:fix/discard-range-memfd-punch-hole
Open

fix(memory): use fallocate(PUNCH_HOLE) for discard_range on MAP_SHARED regions#6168
AdaAibaby wants to merge 1 commit into
firecracker-microvm:mainfrom
AdaAibaby:fix/discard-range-memfd-punch-hole

Conversation

@AdaAibaby

@AdaAibaby AdaAibaby commented Aug 31, 2026

Copy link
Copy Markdown

Summary

GuestRegionMmapExt::discard_range() was calling madvise(MADV_DONTNEED)
for all non-private-file-backed mappings. For anonymous regions this works
correctly, but for MAP_SHARED memfd-backed regions (used when huge pages
are enabled) the kernel ignores MADV_DONTNEED for shared pages and returns
0 — so balloon inflation never freed any host physical memory.

This PR adds a dedicated match arm for MAP_SHARED mappings that calls
fallocate(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE) instead, which
punches a hole in the backing memfd file and releases the physical frames.
Subsequent guest accesses fault in fresh zero pages, matching the expected
balloon-inflation semantics.

Changes

  • src/vmm/src/vstate/memory.rs: add MAP_SHARED arm to
    GuestRegionMmapExt::discard_range() using fallocate(PUNCH_HOLE)
  • Add test_discard_range_on_memfd that writes data, calls discard_range,
    and asserts the discarded page reads back as zeroes

Testing

The new test test_discard_range_on_memfd covers the happy path (page is
zeroed after discard), the second page is unaffected, out-of-range errors,
and the unaligned-offset error path.

Fixes #6167

Hi @Manciukic @JackThomson2 @JamesC1305 Would you mind taking a look when you have a chance? Thanks!

…D regions

madvise(MADV_DONTNEED) has no effect on MAP_SHARED file-backed mappings
(such as memfd regions used for huge-pages guest memory): the kernel
ignores it for shared pages because other mappers may still be using
them. As a result, balloon inflation never actually freed host physical
frames when huge pages were enabled.

Add a dedicated match arm in GuestRegionMmapExt::discard_range() for
MAP_SHARED mappings that uses fallocate(FALLOC_FL_PUNCH_HOLE |
FALLOC_FL_KEEP_SIZE) instead. This punches a hole in the backing file,
causing the kernel to release the physical frames while keeping the
virtual mapping intact; subsequent guest accesses fault in fresh zero
pages, matching the expected balloon-inflation semantics.

The existing MAP_PRIVATE (snapshot restore) and anonymous arms are
unchanged. Add test_discard_range_on_memfd to verify the new path
actually zeroes the discarded page.

Fixes firecracker-microvm#6167
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.

bug: discard_range() silently fails to free physical memory for memfd-backed (MAP_SHARED) guest regions

2 participants