mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
mm: restore file data after partial huge-page MADV_DONTNEED
Private file mappings can acquire owned huge PMAs after a write breaks copy-on-write. MemoryManager.Decommit treats those PMAs like private anonymous memory when MADV_DONTNEED covers only part of a huge page. It decommits the MemoryFile range in place, replacing discarded file data with zeroes. Linux instead discards the private copy so later faults reload the mapped file. Limit the partial huge-page decommit optimization to anonymous VMAs. Let file-backed private PMAs follow the normal invalidation path, which drops the copied pages and restores file contents on the next fault. Add a syscall test that modifies a huge private file mapping and applies MADV_DONTNEED to an interior page.
This commit is contained in:
@@ -1132,9 +1132,10 @@ func (mm *MemoryManager) Decommit(addr hostarch.Addr, length uint64) error {
|
||||
// - If at least one byte in ar is not covered by a vma, decommit the rest
|
||||
// but return ENOMEM.
|
||||
//
|
||||
// - If we would invalidate only part of a huge page that we own (is not
|
||||
// copy-on-write), use MemoryFile.Decommit() instead to keep the allocated
|
||||
// huge page intact for future use.
|
||||
// - If we would invalidate only part of a huge page backing a private
|
||||
// anonymous mapping that we own (is not copy-on-write), use
|
||||
// MemoryFile.Decommit() instead to keep the allocated huge page intact for
|
||||
// future use.
|
||||
didUnmapAS := false
|
||||
pseg := mm.pmas.LowerBoundSegment(ar.Start)
|
||||
vseg := mm.vmas.LowerBoundSegment(ar.Start)
|
||||
@@ -1157,7 +1158,7 @@ func (mm *MemoryManager) Decommit(addr hostarch.Addr, length uint64) error {
|
||||
}
|
||||
for pseg.Ok() && pseg.Start() < vsegAR.End {
|
||||
pma := pseg.ValuePtr()
|
||||
if pma.huge && !mm.isPMACopyOnWriteLocked(vseg, pseg) {
|
||||
if vma.mappable == nil && pma.huge && !mm.isPMACopyOnWriteLocked(vseg, pseg) {
|
||||
psegAR := pseg.Range().Intersect(vsegAR)
|
||||
if !psegAR.IsHugePageAligned() {
|
||||
firstHugeStart := psegAR.Start.HugeRoundDown()
|
||||
|
||||
@@ -115,6 +115,32 @@ TEST(MadviseDontneedTest, CleansPrivateFilePage) {
|
||||
ExpectAllMappingBytes(m, 4);
|
||||
}
|
||||
|
||||
TEST(MadviseDontneedTest, CleansPrivateFileHugePageSubrange) {
|
||||
constexpr char kFileValue = 4;
|
||||
constexpr char kPrivateValue = 5;
|
||||
TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
|
||||
/* parent = */ GetAbsoluteTestTmpdir(),
|
||||
/* content = */ std::string(kHugePageSize, kFileValue),
|
||||
TempPath::kDefaultFileMode));
|
||||
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR));
|
||||
|
||||
Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mmap(nullptr, kHugePageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||||
fd.get(), 0));
|
||||
memset(m.ptr(), kPrivateValue, m.len());
|
||||
ExpectAllMappingBytes(m, kPrivateValue);
|
||||
|
||||
char* const advised = static_cast<char*>(m.ptr()) + kPageSize;
|
||||
ASSERT_THAT(madvise(advised, kPageSize, MADV_DONTNEED), SyscallSucceeds());
|
||||
|
||||
auto const v = m.view();
|
||||
for (size_t i = 0; i < v.size(); ++i) {
|
||||
EXPECT_EQ(v[i], i >= kPageSize && i < 2 * kPageSize ? kFileValue
|
||||
: kPrivateValue)
|
||||
<< "at offset " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MadviseDontneedTest, DoesNotModifySharedFilePage) {
|
||||
TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
|
||||
/* parent = */ GetAbsoluteTestTmpdir(),
|
||||
|
||||
Reference in New Issue
Block a user