The Pedigree Project 0.1
MemoryMappedFile-resize.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "pedigree/kernel/LockGuard.h"
9#include "pedigree/kernel/TargetInfo.h"
10#include "pedigree/kernel/utilities/assert.h"
11
12#include "File.h"
13#include "MemoryMappedFile.h"
14
16 struct Mapping {
18 MemoryMappedFile* object;
19 };
20 UniqueArray<Mapping> mappings;
22 size_t mappingCount = 0;
23 size_t loanCount = 0;
24 size_t totalLoans = 0;
25 size_t newSize = 0;
26 bool committed = false;
27};
28
29MemoryMapManager::PreparedFileResize::PreparedFileResize(Data* data)
30 : m_Data(UniquePointer<Data>::adopt(data)) {}
31
32MemoryMapManager::PreparedFileResize::~PreparedFileResize() = default;
33
34const Cache::DiscardReference* MemoryMapManager::PreparedFileResize::loans() const {
35 return m_Data.get()->loans.get();
36}
37
38size_t MemoryMapManager::PreparedFileResize::loanCount() const {
39 return m_Data.get()->loanCount;
40}
41
42size_t MemoryMapManager::PreparedFileResize::totalLoans() const {
43 return m_Data.get()->totalLoans;
44}
45
46void MemoryMapManager::PreparedFileResize::commit() {
47 Data& data = *m_Data.get();
48 assert(!data.committed);
49 for (size_t i = 0; i < data.mappingCount; ++i) {
50 const auto& mapping = data.mappings.get()[i];
51 mapping.object->discardFilePages(*mapping.space, data.newSize);
52 }
53 data.committed = true;
54}
55
56MemoryMapManager::ResizeStatus MemoryMapManager::prepareFileResize(
57 File* file, size_t oldSize, size_t newSize, UniquePointer<PreparedFileResize>& result) {
58 result.reset();
59 if (!file || newSize >= oldSize || file->getSize() != oldSize) {
60 return ResizeStatus::Invalid;
61 }
62 OperationGuard operation(*this);
63 constexpr size_t MaximumMappings = 4096;
64 constexpr size_t MaximumTrackedPages = 65536;
65 const uintptr_t identity = file->futexIdentity();
66 size_t mappingCount = 0;
67 size_t trackedCount = 0;
68 for (auto spaces = m_MmObjectLists.count() ? m_MmObjectLists.begin() : m_MmObjectLists.end();
69 spaces != m_MmObjectLists.end(); ++spaces) {
70 for (auto objects = spaces.value()->begin(); objects != spaces.value()->end(); ++objects) {
71 if (!(*objects)->usesBacking(identity)) {
72 continue;
73 }
74#if !X64 && !HOSTED
75 return ResizeStatus::Unsupported;
76#endif
77 auto* object = static_cast<MemoryMappedFile*>(*objects);
78 LockGuard<Mutex> guard(object->m_Lock);
79 if (++mappingCount > MaximumMappings ||
80 object->m_Mappings.count() > MaximumTrackedPages - trackedCount) {
81 return ResizeStatus::NoMemory;
82 }
83 trackedCount += object->m_Mappings.count();
84 }
85 }
86
87 auto* data = new PreparedFileResize::Data;
88 if (!data) {
89 return ResizeStatus::NoMemory;
90 }
92 if (!plan) {
93 delete data;
94 return ResizeStatus::NoMemory;
95 }
96 if (mappingCount) {
98 if (!data->mappings) {
99 return ResizeStatus::NoMemory;
100 }
101 }
102 data->newSize = newSize;
103
104 Tree<uintptr_t, size_t> loanCounts;
105 for (auto spaces = m_MmObjectLists.count() ? m_MmObjectLists.begin() : m_MmObjectLists.end();
106 spaces != m_MmObjectLists.end(); ++spaces) {
107 for (auto objects = spaces.value()->begin(); objects != spaces.value()->end(); ++objects) {
108 if (!(*objects)->usesBacking(identity)) {
109 continue;
110 }
111 auto* object = static_cast<MemoryMappedFile*>(*objects);
112 data->mappings.get()[data->mappingCount++] = {spaces.key(), object};
113 LockGuard<Mutex> guard(object->m_Lock);
114 uintptr_t cursor = object->m_Address;
115 uintptr_t address = 0;
116 physical_uintptr_t tracked = 0;
117 while (object->m_Mappings.lowerBound(cursor, address, tracked)) {
118 if (address == ~uintptr_t(0)) {
119 return ResizeStatus::Invalid;
120 }
121 cursor = address + 1;
122 if (tracked != ~static_cast<physical_uintptr_t>(0)) {
123 continue;
124 }
125 if (address < object->m_Address || address - object->m_Address >= object->m_Length ||
126 address - object->m_Address > ~size_t(0) - object->m_Offset) {
127 return ResizeStatus::Invalid;
128 }
129 const uintptr_t offset = (object->m_Offset + (address - object->m_Address)) &
130 ~(static_cast<uintptr_t>(TargetInfo::getPageSize()) - 1);
131 // Generic CoW may replace a PTE while its original cache loan remains
132 // tracked here. Count that loan, independently of the current PTE.
133 const size_t count = loanCounts.lookup(offset);
134 if (!loanCounts.tryInsert(offset, count + 1)) {
135 return ResizeStatus::NoMemory;
136 }
137 ++data->totalLoans;
138 }
139 }
140 }
141 data->loanCount = loanCounts.count();
142 if (data->loanCount) {
143 data->loans = UniqueArray<Cache::DiscardReference>::allocate(data->loanCount);
144 if (!data->loans) {
145 return ResizeStatus::NoMemory;
146 }
147 size_t i = 0;
148 for (auto loan = loanCounts.begin(); loan != loanCounts.end(); ++loan) {
149 data->loans.get()[i++] = {loan.key(), loan.value()};
150 }
151 }
152 result = pedigree_std::move(plan);
153 return ResizeStatus::Ready;
154}
Memory-mapped file interface.
Definition File.h:74
virtual uintptr_t futexIdentity()
Definition File.cc:74
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
ResizeStatus prepareFileResize(File *file, size_t oldSize, size_t newSize, UniquePointer< PreparedFileResize > &result)
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
A key/value dictionary.
Definition Tree.h:33
bool tryInsert(const K &key, const E &value)
Definition Tree.h:169
Iterator begin()
Definition Tree.h:402
E lookup(const K &key) const
Definition Tree.h:193
Iterator end()
Definition Tree.h:427
size_t count() const
Definition Tree.h:142
static UniquePointer< T > adopt(T *pointer)
Definition Pointers.h:101