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 constexpr size_t MaximumObjects = 4096;
245 if (objects->count() > MaximumObjects)
247#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
248 process->recordBenchmarkVmCounter(Process::VmPublishObjectCount, objects->count());
250 const uintptr_t requested = address;
251 const bool directPlacement = requested == 0 && placement == Placement::Hint;
252 for (
size_t attempt = 0; attempt < 32; ++attempt) {
254 uintptr_t destination = requested;
255 Process::UserRegion directRegion = Process::UserRegion::Normal;
256 DirectReservation directReservation;
258 if (directPlacement) {
259 direct = allocateDirect(*process, space, length, mask, destination, directRegion);
262 directReservation.arm(process, directRegion, destination, length);
264 if (!process->snapshotUserReservations(snapshot))
266 auto placementStatus = place(snapshot, space, destination, length, placement, mask);
267 if (placementStatus != MapStatus::Success) {
269 *status = placementStatus;
273 if (space.runtimeMappingPages(destination, length)) {
275 *status = MapStatus::PolicyDenied;
280 if (rawStatus != MemoryLockStatus::Success)
282 bool overlaps =
false;
283#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
284 size_t objectVisits = 0;
287 for (
auto*
object : *objects) {
288#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
291 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
292 if (destination < end && object->address() < destination + length) {
297#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
298 process->recordBenchmarkVmCounter(Process::VmPublishOverlapProbeVisits, objectVisits);
300 process->recordBenchmarkVmCounter(Process::VmPublishOverlapHits);
303 if (!plan.staged.tryReserve(overlaps ? objects->count() * 2 + 1 : 1))
305 size_t removedPages = 0;
307 if (!plan.retired.tryReserve(objects->count()))
309 plan.replacement =
new MmObjectList;
310 if (!plan.replacement)
312 for (
auto*
object : *objects) {
313 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
314 if (destination >= end || object->address() >= destination + length) {
315 if (!plan.replacement->tryPushBack(
object))
319 if (placement != Placement::FixedReplace)
321 const uintptr_t first =
object->address() > destination ?
object->address() : destination;
322 const uintptr_t last = end < destination + length ? end : destination + length;
323 if (object->m_LockMode != MemoryLockMode::None)
324 removedPages += (last - first) / pageSize;
325 plan.retired.pushBack(
object);
326 if (!plan.appendSlice(
object, object->address(), first) ||
327 !plan.appendSlice(
object, last, end))
333 const size_t rawRemovedPages = raw ? raw.get()->removedPages() : 0;
334 assert(removedPages <= charge.managedPages && rawRemovedPages <= charge.rawPages);
335 charge.managedPages -= removedPages;
336 charge.rawPages -= rawRemovedPages;
337 if (mode != MemoryLockMode::None) {
338 const size_t added = length / pageSize;
339 if (added > ~
size_t(0) - charge.managedPages)
341 charge.managedPages += added;
342 if (charge.rawPages > ~
size_t(0) - charge.managedPages ||
343 !account->permitsTotalPages(charge.managedPages + charge.rawPages,
344 process->getEffectiveUserId() == 0)) {
346 *status = MapStatus::LockLimit;
353 new MemoryMappedFile(destination, actualLength, offset, file, copyOnWrite, perms,
354 maximumPerms, attachment, origin))
359 plan.inserted->m_OwnsMappings =
false;
360 plan.inserted->m_LockMode = mode;
361 plan.staged.pushBack(plan.inserted);
362 if (file && !
static_cast<MemoryMappedFile*
>(plan.inserted)->m_UseAdmitted) {
364 *status = MapStatus::TextBusy;
367 auto* publication = overlaps ? plan.replacement : objects;
368 if (publication->count() >= MaximumObjects || !publication->tryPushBack(plan.inserted))
371#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
372 process->recordBenchmarkVmCounter(Process::VmPublishCommitRetries);
378 [[maybe_unused]]
auto* removed = objects->popBack();
379 assert(removed == plan.inserted);
383 directReservation.commit();
386 for (
auto*
object : plan.retired) {
387 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
388 const uintptr_t first =
object->address() > destination ?
object->address() : destination;
389 const uintptr_t last = end < destination + length ? end : destination + length;
390 object->discardRange(space, first, last - first);
391 object->m_OwnsMappings =
false;
395 for (
auto*
object : plan.staged)
396 object->m_OwnsMappings = true;
401 plan.replacement =
nullptr;
402 plan.committed =
true;
406 account->publish(charge, account->futureMode());
407 address = destination;
409 *status = MapStatus::Success;
410 if (mode == MemoryLockMode::Eager)
411 for (uintptr_t page = destination; page < destination + length; page += pageSize)
412 plan.inserted->populatePage(space, page);
413 return plan.inserted;
418size_t MemoryMapManager::removeInternal(uintptr_t base,
size_t length,
bool releaseReservations,
420 OperationGuard operation(*
this);
422 *status = VmStatus::InvalidRange;
424 if (!length || (base & mask) || length > ~
size_t(0) - mask)
426 length = (length + mask) & ~mask;
427 if (length > ~uintptr_t(0) - base)
433 *status = VmStatus::Success;
437 *status = VmStatus::NoMemory;
438 if (objects->count() > 4096)
441#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
442 process->recordBenchmarkVmCounter(Process::VmRemoveCalls);
443 process->recordBenchmarkVmCounter(Process::VmRemoveObjectCount, objects->count());
444 size_t objectVisits = 0;
450 for (
auto it = objects->rbegin(); it != objects->rend(); ++it) {
452#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
455 const uintptr_t objectEnd = (
object->address() +
object->length() + mask) & ~mask;
456 if (object->address() != base || objectEnd != base + length)
459 const size_t removedPages =
460 object->m_LockMode == MemoryLockMode::None ? 0 : (objectEnd - base) / pageSize;
461 object->discardRange(space, base, length);
462 object->m_OwnsMappings =
false;
463 if (releaseReservations)
464 releaseReservation(process, space, base, length);
467 retireLockedPages(space, removedPages);
468#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
469 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
470 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, 1);
473 *status = VmStatus::Success;
477#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
480 bool needsSlices =
false;
481 for (
auto*
object : *objects) {
482#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
485 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
486 if (object->address() < base + length && base < end &&
487 (
object->address() < base || end > base + length)) {
492#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
493 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
496 size_t affected = 0, removedPages = 0;
497#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
500 for (
auto it = objects->begin(); it != objects->end();) {
501#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
505 const uintptr_t first =
object->address();
506 const uintptr_t end = (first +
object->length() + mask) & ~mask;
507 if (first >= base + length || end <= base) {
511 if (object->m_LockMode != MemoryLockMode::None)
512 removedPages += (end - first) / pageSize;
513 object->discardRange(space, first, end - first);
514 object->m_OwnsMappings =
false;
515 if (releaseReservations)
516 releaseReservation(process, space, first, end - first);
517 it = objects->erase(it);
521#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
522 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
523 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, affected);
525 retireLockedPages(space, removedPages);
527 *status = VmStatus::Success;
531 if (!plan.staged.tryReserve(objects->count() * 2) || !plan.retired.tryReserve(objects->count()))
533 plan.replacement =
new MmObjectList;
534 if (!plan.replacement)
536 size_t removedPages = 0;
537#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
540 for (
auto*
object : *objects) {
541#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
544 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
545 const uintptr_t first =
object->address() > base ?
object->address() : base;
546 const uintptr_t last = end < base + length ? end : base + length;
548 if (!plan.replacement->tryPushBack(
object))
552 if (object->m_LockMode != MemoryLockMode::None)
553 removedPages += (last - first) / pageSize;
554 plan.retired.pushBack(
object);
555 if (!plan.appendSlice(
object, object->address(), first) || !plan.appendSlice(
object, last, end))
558#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
559 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
560 process->recordBenchmarkVmCounter(Process::VmRemoveSliceCalls);
561 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, plan.retired.count());
563 if (plan.replacement->count() > 4096)
565 for (
auto*
object : plan.retired) {
566 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
567 const uintptr_t first =
object->address() > base ?
object->address() : base;
568 const uintptr_t last = end < base + length ? end : base + length;
569 object->discardRange(space, first, last - first);
570 object->m_OwnsMappings =
false;
571 if (releaseReservations)
572 releaseReservation(process, space, first, last - first);
574 for (
auto*
object : plan.staged)
575 object->m_OwnsMappings = true;
580 plan.replacement =
nullptr;
581 plan.committed =
true;
583 retireLockedPages(space, removedPages);
585 *status = VmStatus::Success;
586 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)