The Pedigree Project 0.1
DiskPaging.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#include "pedigree/kernel/machine/Disk.h"
8#include "pedigree/kernel/machine/DiskPaging.h"
9#include "pedigree/kernel/panic.h"
10#include "pedigree/kernel/process/TerminationDeferral.h"
11#include "pedigree/kernel/process/Thread.h"
12#include "pedigree/kernel/process/WaitQueue.h"
13#include "pedigree/kernel/processor/Processor.h"
14#include "pedigree/kernel/processor/ProcessorInformation.h"
15
17 Disk* disk = nullptr;
18 uint32_t id = 0;
19 size_t users = 0;
20 size_t transfers = 0;
21 bool published = false;
22 bool retiring = false;
23 bool removal = false;
24 bool preparing = false;
25 bool closing = false;
26 PagingChannel* channel = nullptr;
27 PagingTransport* transport = nullptr;
28 uint64_t bytes = 0;
29};
30
31namespace {
32DiskEndpoint endpoints[DiskEndpoints::Capacity];
33WaitQueue endpointWaiters;
34uint32_t nextEndpointId = 1;
35
36bool canWait() {
37#if THREADS
38 return Processor::getInterrupts() && Processor::information().getCurrentThread();
39#else
40 return true;
41#endif
42}
43} // namespace
44
45DiskUse::DiskUse() : m_Endpoint(nullptr) {}
46DiskUse::DiskUse(DiskUse&& other) noexcept : m_Endpoint(other.m_Endpoint) {
47 other.m_Endpoint = nullptr;
48}
49DiskUse::~DiskUse() {
50 reset();
51}
52DiskUse& DiskUse::operator=(DiskUse&& other) noexcept {
53 if (this != &other) {
54 reset();
55 m_Endpoint = other.m_Endpoint;
56 other.m_Endpoint = nullptr;
57 }
58 return *this;
59}
60void DiskUse::reset() {
61 if (!m_Endpoint)
62 return;
63 auto guard = endpointWaiters.acquire();
64 DiskEndpoint* endpoint = m_Endpoint;
65 m_Endpoint = nullptr;
66 if (!endpoint->users)
67 panic("Disk endpoint admission underflow");
68 --endpoint->users;
69 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(endpoint));
70}
71Disk* DiskUse::get() const {
72 return m_Endpoint ? m_Endpoint->disk : nullptr;
73}
74DiskUse::operator bool() const {
75 return m_Endpoint != nullptr;
76}
77
78DiskEndpoint* DiskEndpoints::reserve(Disk* disk) {
79 auto guard = endpointWaiters.acquire();
80 // The public device encoding reserves twenty bits for the minor number.
81 if (!disk || nextEndpointId > 0xfffff)
82 return nullptr;
83 for (auto& endpoint : endpoints) {
84 if (!endpoint.disk) {
85 endpoint = DiskEndpoint();
86 endpoint.disk = disk;
87 endpoint.id = nextEndpointId++;
88 return &endpoint;
89 }
90 }
91 return nullptr;
92}
93void DiskEndpoints::publish(DiskEndpoint* endpoint) {
94 if (!endpoint)
95 return;
96 auto guard = endpointWaiters.acquire();
97 if (!endpoint->retiring) {
98 endpoint->bytes = endpoint->disk->getSize();
99 endpoint->published = true;
100 }
101}
102bool DiskEndpoints::acquire(DiskEndpoint* endpoint, Disk* disk, DiskUse& use) {
103 use.reset();
104 auto guard = endpointWaiters.acquire();
105 if (!endpoint || endpoint->disk != disk || endpoint->retiring || endpoint->removal ||
106 endpoint->preparing || endpoint->channel || endpoint->users == ~static_cast<size_t>(0))
107 return false;
108 ++endpoint->users;
109 use.m_Endpoint = endpoint;
110 return true;
111}
112bool DiskEndpoints::acquire(uint32_t id, DiskUse& use) {
113 use.reset();
114 auto guard = endpointWaiters.acquire();
115 for (auto& endpoint : endpoints) {
116 if (endpoint.id != id || !endpoint.disk || !endpoint.published || endpoint.retiring)
117 continue;
118 if (endpoint.removal || endpoint.preparing || endpoint.channel ||
119 endpoint.users == ~static_cast<size_t>(0))
120 return false;
121 ++endpoint.users;
122 use.m_Endpoint = &endpoint;
123 return true;
124 }
125 return false;
126}
127uint32_t DiskEndpoints::id(DiskEndpoint* endpoint, Disk* disk) {
128 auto guard = endpointWaiters.acquire();
129 return endpoint && endpoint->disk == disk && !endpoint->retiring ? endpoint->id : 0;
130}
131bool DiskEndpoints::describe(uint32_t id, uint64_t& bytes) {
132 auto guard = endpointWaiters.acquire();
133 for (const auto& endpoint : endpoints) {
134 if (endpoint.id == id && endpoint.disk && endpoint.published && !endpoint.retiring) {
135 bytes = endpoint.bytes;
136 return true;
137 }
138 }
139 bytes = 0;
140 return false;
141}
142size_t DiskEndpoints::snapshot(uint32_t* ids, size_t capacity) {
143 if (!ids)
144 return 0;
145 auto guard = endpointWaiters.acquire();
146 size_t count = 0;
147 for (const auto& endpoint : endpoints) {
148 if (endpoint.disk && endpoint.published && !endpoint.retiring && count < capacity)
149 ids[count++] = endpoint.id;
150 }
151 return count;
152}
153bool DiskEndpoints::tryClose(DiskEndpoint* endpoint, Disk* disk) {
154 auto guard = endpointWaiters.acquire();
155 if (!endpoint || endpoint->disk != disk || endpoint->retiring || endpoint->users ||
156 endpoint->preparing || endpoint->channel)
157 return false;
158 endpoint->removal = true;
159 return true;
160}
161void DiskEndpoints::reopen(DiskEndpoint* endpoint, Disk* disk) {
162 auto guard = endpointWaiters.acquire();
163 if (endpoint && endpoint->disk == disk && !endpoint->retiring)
164 endpoint->removal = false;
165}
166void DiskEndpoints::retire(DiskEndpoint* endpoint, Disk* disk) {
167 if (!endpoint)
168 return;
169 TerminationDeferral lifetime;
170 while (true) {
171 auto guard = endpointWaiters.acquire();
172 if (endpoint->disk != disk)
173 return;
174 endpoint->published = false;
175 endpoint->retiring = true;
176 if (!endpoint->users && !endpoint->preparing && !endpoint->channel) {
177 endpoint->disk = nullptr;
178 return;
179 }
180 const auto reason =
181 guard.waitForCompletion(WaitQueue::Channel(endpoint), Thread::CallbackDrain);
182 (void)reason;
183 }
184}
185
186PagingChannel::PagingChannel() : m_Endpoint(nullptr) {}
187PagingChannel::~PagingChannel() {
188 reset();
189}
190PagingStatus DiskEndpoints::prepare(uint32_t id, PagingChannel& channel) {
191 if (!canWait())
192 return PagingStatus::Busy;
193 TerminationDeferral lifetime;
194 DiskEndpoint* selected = nullptr;
195 {
196 auto guard = endpointWaiters.acquire();
197 if (channel.m_Endpoint)
198 return PagingStatus::Busy;
199 for (auto& endpoint : endpoints) {
200 if (endpoint.id != id || !endpoint.disk || !endpoint.published || endpoint.retiring)
201 continue;
202 if (endpoint.removal || endpoint.users || endpoint.preparing || endpoint.channel)
203 return PagingStatus::Busy;
204 endpoint.preparing = true;
205 selected = &endpoint;
206 break;
207 }
208 }
209 if (!selected)
210 return PagingStatus::Closed;
211 PagingTransport* transport = nullptr;
212 PagingStatus result = selected->disk->preparePagingTransport(transport);
213 {
214 auto guard = endpointWaiters.acquire();
215 if (result == PagingStatus::Success && transport && !selected->retiring) {
216 selected->transport = transport;
217 selected->channel = &channel;
218 channel.m_Endpoint = selected;
219 selected->preparing = false;
220 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(selected));
221 return PagingStatus::Success;
222 }
223 }
224 // Preparation owns the endpoint until backend rollback releases controller
225 // admission. Hardware teardown cannot consume the backend in this interval.
226 if (transport)
227 transport->release();
228 {
229 auto guard = endpointWaiters.acquire();
230 selected->preparing = false;
231 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(selected));
232 }
233 return result == PagingStatus::Success ? PagingStatus::Closed : result;
234}
235PagingStatus PagingChannel::transfer(PagingOperation operation, uint64_t offset, void* page) {
236 if (!canWait())
237 return PagingStatus::Busy;
238 TerminationDeferral lifetime;
239 DiskEndpoint* endpoint = nullptr;
240 PagingTransport* transport = nullptr;
241 {
242 auto guard = endpointWaiters.acquire();
243 endpoint = m_Endpoint;
244 if (!endpoint || endpoint->channel != this || endpoint->closing)
245 return PagingStatus::Closed;
246 if (operation != PagingOperation::Flush &&
247 (!page || (offset % PageBytes) || offset > endpoint->bytes ||
248 PageBytes > endpoint->bytes - offset))
249 return PagingStatus::Invalid;
250 if (endpoint->transfers == ~static_cast<size_t>(0))
251 return PagingStatus::Busy;
252 ++endpoint->transfers;
253 transport = endpoint->transport;
254 }
255 const PagingStatus result = transport->transfer(operation, offset, page);
256 {
257 auto guard = endpointWaiters.acquire();
258 --endpoint->transfers;
259 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(endpoint));
260 }
261 return result;
262}
263PagingStatus PagingChannel::readPage(uint64_t offset, void* page) {
264 return transfer(PagingOperation::Read, offset, page);
265}
266PagingStatus PagingChannel::writePage(uint64_t offset, const void* page) {
267 return transfer(PagingOperation::Write, offset, const_cast<void*>(page));
268}
269PagingStatus PagingChannel::flush() {
270 return transfer(PagingOperation::Flush, 0, nullptr);
271}
272void PagingChannel::reset() {
273 TerminationDeferral lifetime;
274 DiskEndpoint* endpoint = nullptr;
275 PagingTransport* transport = nullptr;
276 bool ownsClosure = false;
277 while (true) {
278 auto guard = endpointWaiters.acquire();
279 endpoint = m_Endpoint;
280 if (!endpoint)
281 return;
282 if (!endpoint->closing) {
283 endpoint->closing = true;
284 ownsClosure = true;
285 }
286 if (ownsClosure && !endpoint->transfers) {
287 transport = endpoint->transport;
288 break;
289 }
290 const auto reason =
291 guard.waitForCompletion(WaitQueue::Channel(endpoint), Thread::CallbackDrain);
292 (void)reason;
293 }
294 transport->release();
295 auto guard = endpointWaiters.acquire();
296 endpoint->transport = nullptr;
297 endpoint->channel = nullptr;
298 endpoint->closing = false;
299 m_Endpoint = nullptr;
300 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(endpoint));
301}
302uint32_t PagingChannel::endpointId() const {
303 auto guard = endpointWaiters.acquire();
304 return m_Endpoint ? m_Endpoint->id : 0;
305}
306uint64_t PagingChannel::size() const {
307 auto guard = endpointWaiters.acquire();
308 return m_Endpoint ? m_Endpoint->bytes : 0;
309}
310PagingChannel::operator bool() const {
311 auto guard = endpointWaiters.acquire();
312 return m_Endpoint != nullptr;
313}
Definition Disk.h:35
virtual size_t getSize() const
Gets the size of the disk.
Definition Disk.cc:344
virtual PagingStatus preparePagingTransport(PagingTransport *&transport)
Definition Disk.cc:142
static bool getInterrupts()
static ProcessorInformation & information()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117