The Pedigree Project 0.1
Process-reservations.cc
1#include "pedigree/kernel/process/Process.h"
2
3#if THREADS
4#include "pedigree/kernel/LockGuard.h"
5#include "pedigree/kernel/Log.h"
6#include "pedigree/kernel/processor/VirtualAddressSpace.h"
7#include "pedigree/kernel/utilities/Pointers.h"
8
9namespace {
10struct ReservationRange {
11 uintptr_t address;
12 size_t length;
13};
14} // namespace
15
16bool Process::snapshotUserReservations(UserReservationSnapshot& result) {
17 if (m_pVforkOwner)
18 return m_pVforkOwner->snapshotUserReservations(result);
20 size_t capacity = 0, normalCount = 0, dynamicCount = 0;
21 uint64_t generation = 0;
22#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
23 size_t storageAllocations = 0;
24#endif
25 for (;;) {
26 size_t needed = 0;
27 {
28 LockGuard<Spinlock> guard(m_UserReservationLock);
29 normalCount = m_SpaceAllocator.size();
30 dynamicCount = m_DynamicSpaceAllocator.size();
31 if (normalCount > (~size_t{0} / sizeof(ReservationRange)) ||
32 dynamicCount > (~size_t{0} / sizeof(ReservationRange)) - normalCount) {
33 return false;
34 }
35 needed = normalCount + dynamicCount;
36 if (needed <= capacity) {
37 MemoryAllocator::Range range(0, 0);
38 for (size_t i = 0; i < normalCount; ++i) {
39 m_SpaceAllocator.getRange(i, range);
40 ranges.get()[i] = {range.address, range.length};
41 }
42 for (size_t i = 0; i < dynamicCount; ++i) {
44 ranges.get()[normalCount + i] = {range.address, range.length};
45 }
46 generation = m_UserReservationGeneration;
47 break;
48 }
49 }
50 // Allocation can reclaim mappings; it must not hold the reservation lock.
52 if (!ranges) {
53 return false;
54 }
55#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
56 ++storageAllocations;
57#endif
58 capacity = needed;
59 }
60
61#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
62 recordBenchmarkVmCounter(VmReservationSnapshots);
63 recordBenchmarkVmCounter(VmReservationExtents, normalCount + dynamicCount);
64 recordBenchmarkVmCounter(VmReservationScratchAllocations, storageAllocations);
65#endif
66
67 UserReservationSnapshot snapshot;
68 for (size_t i = 0; i < normalCount; ++i) {
69 const auto& range = ranges.get()[i];
70 if (range.length && !snapshot.normal.tryFree(range.address, range.length, false)) {
71 return false;
72 }
73 }
74 for (size_t i = 0; i < dynamicCount; ++i) {
75 const auto& range = ranges.get()[normalCount + i];
76 if (range.length && !snapshot.dynamic.tryFree(range.address, range.length, false)) {
77 return false;
78 }
79 }
80 result.normal.swap(snapshot.normal);
81 result.dynamic.swap(snapshot.dynamic);
82 result.generation = generation;
83 return true;
84}
85
86bool Process::commitUserReservations(uint64_t expectedGeneration,
87 UserReservationSnapshot& replacement) {
88 if (m_pVforkOwner)
89 return m_pVforkOwner->commitUserReservations(expectedGeneration, replacement);
90 LockGuard<Spinlock> guard(m_UserReservationLock);
91 if (m_UserReservationGeneration != expectedGeneration) {
92 return false;
93 }
94 m_SpaceAllocator.swap(replacement.normal);
95 m_DynamicSpaceAllocator.swap(replacement.dynamic);
96 ++m_UserReservationGeneration;
97 replacement.generation = m_UserReservationGeneration;
98 return true;
99}
100
101bool Process::allocateUserRange(UserRegion region, size_t length, uintptr_t& address) {
102 if (m_pVforkOwner)
103 return m_pVforkOwner->allocateUserRange(region, length, address);
104 if (!length) {
105 return false;
106 }
107 for (;;) {
108 {
109 LockGuard<Spinlock> guard(m_UserReservationLock);
110 auto& allocator = region == UserRegion::Dynamic ? m_DynamicSpaceAllocator : m_SpaceAllocator;
111 if (allocator.allocateWithoutAllocation(length, address)) {
112 ++m_UserReservationGeneration;
113 return true;
114 }
115 }
116 UserReservationSnapshot snapshot;
117 if (!snapshotUserReservations(snapshot)) {
118 return false;
119 }
120 MemoryAllocator& allocator = region == UserRegion::Dynamic ? snapshot.dynamic : snapshot.normal;
121 uintptr_t candidate = 0;
122 if (!allocator.allocate(length, candidate)) {
123 return false;
124 }
125 if (commitUserReservations(snapshot.generation, snapshot)) {
126 address = candidate;
127 return true;
128 }
129 }
130}
131
132bool Process::allocateSpecificUserRange(UserRegion region, uintptr_t address, size_t length) {
133 if (m_pVforkOwner)
134 return m_pVforkOwner->allocateSpecificUserRange(region, address, length);
135 if (!length || length > ~uintptr_t{0} - address) {
136 return false;
137 }
138 for (;;) {
139 {
140 LockGuard<Spinlock> guard(m_UserReservationLock);
141 auto& allocator = region == UserRegion::Dynamic ? m_DynamicSpaceAllocator : m_SpaceAllocator;
142 if (allocator.allocateSpecificWithoutAllocation(address, length)) {
143 ++m_UserReservationGeneration;
144 return true;
145 }
146 }
147 UserReservationSnapshot snapshot;
148 if (!snapshotUserReservations(snapshot)) {
149 return false;
150 }
151 MemoryAllocator& allocator = region == UserRegion::Dynamic ? snapshot.dynamic : snapshot.normal;
152 if (!allocator.allocateSpecific(address, length)) {
153 return false;
154 }
155 if (commitUserReservations(snapshot.generation, snapshot)) {
156 return true;
157 }
158 }
159}
160
161void Process::freeUserRange(UserRegion region, uintptr_t address, size_t length) {
162 if (m_pVforkOwner) {
163 m_pVforkOwner->freeUserRange(region, address, length);
164 return;
165 }
166 if (!length) {
167 return;
168 }
169 if (length > ~uintptr_t{0} - address) {
170 FATAL("Invalid process reservation release");
171 }
172 for (;;) {
173 {
174 LockGuard<Spinlock> guard(m_UserReservationLock);
175 auto& allocator = region == UserRegion::Dynamic ? m_DynamicSpaceAllocator : m_SpaceAllocator;
176 if (allocator.freeWithoutAllocation(address, length)) {
177 ++m_UserReservationGeneration;
178 return;
179 }
180 }
181 // Only creating a new free extent needs storage. Exhausted nodes and
182 // adjacent extents above keep ordinary teardown allocation-free.
183 UserReservationSnapshot snapshot;
184 if (!snapshotUserReservations(snapshot)) {
185 FATAL("Cannot grow process reservation metadata during release");
186 }
187 MemoryAllocator& allocator = region == UserRegion::Dynamic ? snapshot.dynamic : snapshot.normal;
188 if (!allocator.tryFree(address, length)) {
189 FATAL("Cannot grow process reservation metadata during release");
190 }
191 if (commitUserReservations(snapshot.generation, snapshot)) {
192 return;
193 }
194 }
195}
196
197void Process::resetUserReservations() {
198 assert(!m_pVforkOwner);
199 for (;;) {
200 UserReservationSnapshot snapshot;
201 {
202 LockGuard<Spinlock> guard(m_UserReservationLock);
203 snapshot.generation = m_UserReservationGeneration;
204 }
205 snapshot.normal.free(
206 getAddressSpace()->getUserStart(),
207 getAddressSpace()->getUserReservedStart() - getAddressSpace()->getUserStart());
208 if (getAddressSpace()->getDynamicStart()) {
209 snapshot.dynamic.free(
210 getAddressSpace()->getDynamicStart(),
211 getAddressSpace()->getDynamicEnd() - getAddressSpace()->getDynamicStart());
212 }
213 if (commitUserReservations(snapshot.generation, snapshot)) {
214 return;
215 }
216 }
217}
218#endif
VirtualAddressSpace * getAddressSpace()
Definition Process.h:478
MemoryAllocator m_DynamicSpaceAllocator
Definition Process.h:1013
MemoryAllocator m_SpaceAllocator
Definition Process.h:1009
bool commitUserReservations(uint64_t expectedGeneration, UserReservationSnapshot &replacement)
bool getRange(size_t index, Range &range) const
Definition RangeList.h:419
bool allocate(T length, T &address)
Definition RangeList.h:318
size_t size() const
Definition RangeList.h:104
void swap(RangeList &other) noexcept
Definition RangeList.h:95
bool freeWithoutAllocation(T address, T length)
Definition RangeList.h:228
bool allocateSpecific(T address, T length)
Definition RangeList.h:363