2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Process.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/utilities/Vector.h"
15using Status = MemoryMapManager::MapStatus;
16using Placement = MemoryMapManager::Placement;
21 return &snapshot.dynamic;
23 return &snapshot.normal;
26bool reserveFree(
MemoryAllocator& allocator, uintptr_t base,
size_t length) {
29 for (
size_t i = 0; i < allocator.
size(); ++i) {
30 MemoryAllocator::Range range(0, 0);
33 const uintptr_t first = range.address > base ? range.address : base;
34 const uintptr_t rangeEnd = range.length > ~uintptr_t(0) - range.address
36 : range.address + range.length;
37 const uintptr_t last = rangeEnd < base + length ? rangeEnd : base + length;
49Status place(Snapshot& snapshot,
VirtualAddressSpace& space, uintptr_t& address,
size_t length,
50 Placement placement,
size_t mask) {
52 auto* allocator = allocatorFor(snapshot, space, address, length);
54 return Status::Success;
55 if (placement == Placement::FixedNoReplace)
56 return Status::AddressInUse;
57 if (placement == Placement::FixedReplace)
58 return reserveFree(snapshot.dynamic, address, length) &&
59 reserveFree(snapshot.normal, address, length)
64 for (
auto* allocator : allocators) {
65 uintptr_t allocation = 0;
66 if (!allocator->
allocate(length + mask, allocation))
68 address = (allocation + mask) & ~mask;
69 if (address != allocation && !allocator->tryFree(allocation, address - allocation))
70 return Status::NoMemory;
71 const uintptr_t end = allocation + length + mask;
72 if (address + length < end && !allocator->tryFree(address + length, end - address - length))
73 return Status::NoMemory;
74 return Status::Success;
76 return Status::NoMemory;
82 bool committed =
false;
84 for (
auto*
object : committed ? retired : staged)
93 auto*
object = owner->stageSlice(first, end - first, first, end - first);
97 return replacement->tryPushBack(
object);
101class DirectReservation {
104 : m_Process(nullptr),
108 m_Committed(false) {}
110 void arm(
Process* process, Process::UserRegion region, uintptr_t base,
size_t length) {
121 ~DirectReservation() {
122 if (m_Process && !m_Committed)
123 m_Process->freeUserRange(m_Region, m_Base, m_Length);
128 Process::UserRegion m_Region;
135 uintptr_t& address, Process::UserRegion& region) {
136 const size_t allocationLength = length + mask;
137 auto allocate = [&](Process::UserRegion candidate) {
138 uintptr_t allocation = 0;
139 if (!process.allocateUserRange(candidate, allocationLength, allocation))
141 if (allocation > ~uintptr_t(0) - mask) {
142 process.freeUserRange(candidate, allocation, allocationLength);
145 const uintptr_t aligned = (allocation + mask) & ~mask;
146 if (aligned > ~uintptr_t(0) - length || allocationLength > ~uintptr_t(0) - allocation) {
147 process.freeUserRange(candidate, allocation, allocationLength);
150 const uintptr_t allocationEnd = allocation + allocationLength;
151 const uintptr_t usedEnd = aligned + length;
152 if (aligned != allocation)
153 process.freeUserRange(candidate, allocation, aligned - allocation);
154 if (usedEnd != allocationEnd)
155 process.freeUserRange(candidate, usedEnd, allocationEnd - usedEnd);
163 return allocate(Process::UserRegion::Normal);
170 return mapFile(file, address, length, perms, offset, copyOnWrite, Placement::FixedReplace,
175 bool copyOnWrite, Placement placement,
179 MemoryLockMode requestedLock,
181 OperationGuard operation(*
this);
182 bool mayWrite = maximumPerms & MemoryMappedObject::Write;
183 if (!file->
allowMapping(!copyOnWrite, perms & MemoryMappedObject::Write, mayWrite)) {
185 *status = MapStatus::PolicyDenied;
190 return publishMapping(file, address, length, perms, offset, copyOnWrite, placement, status,
191 maximumPerms, attachment, requestedLock, origin);
195 return mapAnon(address, length, perms, Placement::FixedReplace,
nullptr);
199 Placement placement, MapStatus* status,
200 MemoryLockMode requestedLock) {
201 OperationGuard operation(*
this);
202 return publishMapping(
203 nullptr, address, length, perms, 0,
true, placement, status,
204 MemoryMappedObject::Read | MemoryMappedObject::Write | MemoryMappedObject::Exec,
210 size_t offset,
bool copyOnWrite, Placement placement, MapStatus* status,
215 *status = MapStatus::NoMemory;
217 const size_t actualLength = length;
218 if (!length || length > ~
size_t(0) - mask)
220 length = (length + mask) & ~mask;
221 if (length > ~
size_t(0) - mask || length > ~uintptr_t(0) - address)
225#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
226 process->recordBenchmarkVmCounter(Process::VmPublishCalls);
228 auto* account = space.memoryLockAccount();
229 const MemoryLockMode mode = requestedLock != MemoryLockMode::None ? requestedLock
230 : account ? account->futureMode()
231 : MemoryLockMode::None;
235 auto* empty =
new MmObjectList;
244#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
245 process->recordBenchmarkVmCounter(Process::VmPublishObjectCount, objects->count());
247 const uintptr_t requested = address;
248 const bool directPlacement = requested == 0 && placement == Placement::Hint;
249 for (
size_t attempt = 0; attempt < 32; ++attempt) {
251 uintptr_t destination = requested;
252 Process::UserRegion directRegion = Process::UserRegion::Normal;
253 DirectReservation directReservation;
255 if (directPlacement) {
256 direct = allocateDirect(*process, space, length, mask, destination, directRegion);
259 directReservation.arm(process, directRegion, destination, length);
261 if (!process->snapshotUserReservations(snapshot))
263 auto placementStatus = place(snapshot, space, destination, length, placement, mask);
264 if (placementStatus != MapStatus::Success) {
266 *status = placementStatus;
270 if (space.runtimeMappingPages(destination, length)) {
272 *status = MapStatus::PolicyDenied;
277 if (rawStatus != MemoryLockStatus::Success)
279 bool overlaps =
false;
280#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
281 size_t objectVisits = 0;
284 for (
auto*
object : *objects) {
285#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
288 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
289 if (destination < end && object->address() < destination + length) {
294#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
295 process->recordBenchmarkVmCounter(Process::VmPublishOverlapProbeVisits, objectVisits);
297 process->recordBenchmarkVmCounter(Process::VmPublishOverlapHits);
300 if (!plan.staged.tryReserve(overlaps ? objects->count() * 2 + 1 : 1))
302 size_t removedPages = 0;
304 if (!plan.retired.tryReserve(objects->count()))
306 plan.replacement =
new MmObjectList;
307 if (!plan.replacement)
309 for (
auto*
object : *objects) {
310 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
311 if (destination >= end || object->address() >= destination + length) {
312 if (!plan.replacement->tryPushBack(
object))
316 if (placement != Placement::FixedReplace)
318 const uintptr_t first =
object->address() > destination ?
object->address() : destination;
319 const uintptr_t last = end < destination + length ? end : destination + length;
320 if (object->m_LockMode != MemoryLockMode::None)
321 removedPages += (last - first) / pageSize;
322 plan.retired.pushBack(
object);
323 if (!plan.appendSlice(
object, object->address(), first) ||
324 !plan.appendSlice(
object, last, end))
330 const size_t rawRemovedPages = raw ? raw.get()->removedPages() : 0;
331 assert(removedPages <= charge.managedPages && rawRemovedPages <= charge.rawPages);
332 charge.managedPages -= removedPages;
333 charge.rawPages -= rawRemovedPages;
334 if (mode != MemoryLockMode::None) {
335 const size_t added = length / pageSize;
336 if (added > ~
size_t(0) - charge.managedPages)
338 charge.managedPages += added;
339 if (charge.rawPages > ~
size_t(0) - charge.managedPages ||
340 !account->permitsTotalPages(charge.managedPages + charge.rawPages,
341 process->getEffectiveUserId() == 0)) {
343 *status = MapStatus::LockLimit;
350 new MemoryMappedFile(destination, actualLength, offset, file, copyOnWrite, perms,
351 maximumPerms, attachment, origin))
356 plan.inserted->m_OwnsMappings =
false;
357 plan.inserted->m_LockMode = mode;
358 plan.staged.pushBack(plan.inserted);
359 if (file && !
static_cast<MemoryMappedFile*
>(plan.inserted)->m_MountAdmitted) {
361 *status = MapStatus::PolicyDenied;
365 if (file && !
static_cast<MemoryMappedFile*
>(plan.inserted)->m_UseAdmitted) {
367 *status = MapStatus::TextBusy;
370 auto* publication = overlaps ? plan.replacement : objects;
371 if (!publication->tryPushBack(plan.inserted))
374#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
375 process->recordBenchmarkVmCounter(Process::VmPublishCommitRetries);
381 [[maybe_unused]]
auto* removed = objects->popBack();
382 assert(removed == plan.inserted);
386 directReservation.commit();
389 for (
auto*
object : plan.retired) {
390 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
391 const uintptr_t first =
object->address() > destination ?
object->address() : destination;
392 const uintptr_t last = end < destination + length ? end : destination + length;
393 object->discardRange(space, first, last - first);
394 object->m_OwnsMappings =
false;
398 for (
auto*
object : plan.staged)
399 object->m_OwnsMappings = true;
404 plan.replacement =
nullptr;
405 plan.committed =
true;
409 account->publish(charge, account->futureMode());
410 address = destination;
412 *status = MapStatus::Success;
413 if (mode == MemoryLockMode::Eager)
414 for (uintptr_t page = destination; page < destination + length; page += pageSize)
415 plan.inserted->populatePage(space, page);
416 return plan.inserted;
421size_t MemoryMapManager::removeInternal(uintptr_t base,
size_t length,
bool releaseReservations,
423 OperationGuard operation(*
this);
425 *status = VmStatus::InvalidRange;
427 if (!length || (base & mask) || length > ~
size_t(0) - mask)
429 length = (length + mask) & ~mask;
430 if (length > ~uintptr_t(0) - base)
436 *status = VmStatus::Success;
440 *status = VmStatus::NoMemory;
442#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
443 process->recordBenchmarkVmCounter(Process::VmRemoveCalls);
444 process->recordBenchmarkVmCounter(Process::VmRemoveObjectCount, objects->count());
445 size_t objectVisits = 0;
451 for (
auto it = objects->rbegin(); it != objects->rend(); ++it) {
453#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
456 const uintptr_t objectEnd = (
object->address() +
object->length() + mask) & ~mask;
457 if (object->address() != base || objectEnd != base + length)
460 const size_t removedPages =
461 object->m_LockMode == MemoryLockMode::None ? 0 : (objectEnd - base) / pageSize;
462 object->discardRange(space, base, length);
463 object->m_OwnsMappings =
false;
464 if (releaseReservations)
465 releaseReservation(process, space, base, length);
468 retireLockedPages(space, removedPages);
469#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
470 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
471 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, 1);
474 *status = VmStatus::Success;
478#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
481 bool needsSlices =
false;
482 for (
auto*
object : *objects) {
483#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
486 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
487 if (object->address() < base + length && base < end &&
488 (
object->address() < base || end > base + length)) {
493#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
494 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
497 size_t affected = 0, removedPages = 0;
498#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
501 for (
auto it = objects->begin(); it != objects->end();) {
502#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
506 const uintptr_t first =
object->address();
507 const uintptr_t end = (first +
object->length() + mask) & ~mask;
508 if (first >= base + length || end <= base) {
512 if (object->m_LockMode != MemoryLockMode::None)
513 removedPages += (end - first) / pageSize;
514 object->discardRange(space, first, end - first);
515 object->m_OwnsMappings =
false;
516 if (releaseReservations)
517 releaseReservation(process, space, first, end - first);
518 it = objects->erase(it);
522#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
523 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
524 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, affected);
526 retireLockedPages(space, removedPages);
528 *status = VmStatus::Success;
532 if (!plan.staged.tryReserve(objects->count() * 2) || !plan.retired.tryReserve(objects->count()))
534 plan.replacement =
new MmObjectList;
535 if (!plan.replacement)
537 size_t removedPages = 0;
538#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
541 for (
auto*
object : *objects) {
542#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
545 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
546 const uintptr_t first =
object->address() > base ?
object->address() : base;
547 const uintptr_t last = end < base + length ? end : base + length;
549 if (!plan.replacement->tryPushBack(
object))
553 if (object->m_LockMode != MemoryLockMode::None)
554 removedPages += (last - first) / pageSize;
555 plan.retired.pushBack(
object);
556 if (!plan.appendSlice(
object, object->address(), first) || !plan.appendSlice(
object, last, end))
559#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
560 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
561 process->recordBenchmarkVmCounter(Process::VmRemoveSliceCalls);
562 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, plan.retired.count());
564 for (
auto*
object : plan.retired) {
565 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
566 const uintptr_t first =
object->address() > base ?
object->address() : base;
567 const uintptr_t last = end < base + length ? end : base + length;
568 object->discardRange(space, first, last - first);
569 object->m_OwnsMappings =
false;
570 if (releaseReservations)
571 releaseReservation(process, space, first, last - first);
573 for (
auto*
object : plan.staged)
574 object->m_OwnsMappings = true;
579 plan.replacement =
nullptr;
580 plan.committed =
true;
582 retireLockedPages(space, removedPages);
584 *status = VmStatus::Success;
585 return plan.retired.count();
Memory-mapped file interface.
virtual bool allowMapping(bool shared, bool writeRequested, bool &mayWrite)
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
MemoryMappedObject * mapFile(File *pFile, uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms, size_t offset=0, bool bCopyOnWrite=true)
static constexpr size_t getPageSize() PURE
Process * addressSpaceOwner()
bool commitUserReservations(uint64_t expectedGeneration, UserReservationSnapshot &replacement)
static ProcessorInformation & information()
bool getRange(size_t index, Range &range) const
bool allocate(T length, T &address)
bool allocateSpecific(T address, T length)
MemoryLockStatus prepareReplacement(uintptr_t base, size_t length, UniquePointer< PreparedMemoryLock > &result)
A vector / dynamic array.
virtual uintptr_t getUserReservedStart() const =0
virtual uintptr_t getDynamicStart() const
virtual uintptr_t getUserStart() const =0
virtual uintptr_t getDynamicEnd() const
void pushBack(const T &value)