The Pedigree Project 0.1
timerfd-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/machine/Machine.h"
4#include "pedigree/kernel/machine/Timer.h"
5#include "pedigree/kernel/machine/TimerHandler.h"
6#include "pedigree/kernel/panic.h"
7#include "pedigree/kernel/process/TerminationDeferral.h"
8#include "pedigree/kernel/processor/Processor.h"
9#include "pedigree/kernel/syscallError.h"
10#include "pedigree/kernel/utilities/assert.h"
11
12#include <fcntl.h>
13
14#include "FileDescriptor.h"
15#include "PosixSubsystem.h"
16#include "clock-syscalls.h"
17#include "timerfd-syscalls.h"
18
19namespace {
20constexpr int ClockRealtime = 0, ClockMonotonic = 1;
21
22uint64_t addExpirations(uint64_t counter, uint64_t added) {
23 return added > ~uint64_t(0) - counter ? ~uint64_t(0) : counter + added;
24}
25
26bool copyRead(void* context, const void* value, size_t count) {
27 return PosixSubsystem::copyToUser(context, value, count);
28}
29} // namespace
30
31class TimerFdService final : public TimerHandler {
32 public:
33 ~TimerFdService() override {
34 if (m_Source && !m_Source->unregisterHandler(this))
35 panic("timerfd callback could not be drained");
36 }
37
38 bool add(const SharedPointer<TimerFd>& timer) {
39 LockGuard<Mutex> guard(m_Lock);
40 size_t slot = LinuxTimerFd::MaximumObjects;
41 for (size_t i = 0; i < LinuxTimerFd::MaximumObjects; ++i)
42 if (!m_Timers[i]) {
43 slot = i;
44 break;
45 }
46 if (slot == LinuxTimerFd::MaximumObjects) {
47 SYSCALL_ERROR(TooManyOpenFiles);
48 return false;
49 }
50 if (!m_Source) {
51 Timer* source = Machine::instance().getTimer();
52 if (!source || !source->registerHandler(this)) {
53 SYSCALL_ERROR(OutOfMemory);
54 return false;
55 }
56 m_Source = source;
57 }
58 m_Timers[slot] = timer;
59 if (slot >= m_End)
60 m_End = slot + 1;
61 return true;
62 }
63
64 void remove(TimerFd* timer) {
66 {
67 LockGuard<Mutex> guard(m_Lock);
68 for (auto& entry : m_Timers)
69 if (entry.get() == timer) {
70 retired = pedigree_std::move(entry);
71 break;
72 }
73 while (m_End && !m_Timers[m_End - 1])
74 --m_End;
75 }
76 }
77
78 void refresh(uint64_t generation) {
79 TerminationDeferral lifetime;
80 for (size_t i = 0; i < LinuxTimerFd::MaximumObjects; ++i) {
82 if (generation)
83 m_Lock.acquire();
84 else if (!m_Lock.tryAcquire())
85 return;
86 if (i >= m_End) {
87 m_Lock.release();
88 return;
89 }
90 timer = m_Timers[i];
91 m_Lock.release();
92 // A callback pin outlives final descriptor close, but cannot keep the
93 // descriptor admission open. Never notify observers under the registry.
94 if (timer)
95 timer->service(generation);
96 }
97 }
98
99 void timer(uint64_t) override {
101 refresh(0);
102 }
103
104 private:
105 Mutex m_Lock;
106 Timer* m_Source = nullptr;
107 size_t m_End = 0;
108 SharedPointer<TimerFd> m_Timers[LinuxTimerFd::MaximumObjects];
109};
110
111namespace {
112TimerFdService timerService;
113} // namespace
114
115TimerFd::TimerFd(int clock)
116 : m_Lock(),
117 m_Readers(),
118 m_State(),
119 m_Generations(),
120 m_Counter(0),
121 m_ClockGeneration(posix_clock_change_generation()),
122 m_DescriptorOwners(0),
123 m_Clock(clock),
124 m_Expired(false),
125 m_CancelOnSet(false),
126 m_CancelPending(false),
127 m_CancelWake(false),
128 m_AdmissionOpen(true) {}
129
130TimerFd::~TimerFd() {
131 assert(!m_DescriptorOwners);
133}
134
135bool TimerFd::readable() const {
136 return m_Counter || m_CancelWake;
137}
138
139Time::Timestamp TimerFd::now(const PosixClockSnapshot& clock) const {
140 return m_State.realtime ? clock.realtime : clock.monotonic;
141}
142
143void TimerFd::update(const PosixClockSnapshot& clock) {
144 if (clock.generation > m_ClockGeneration) {
145 m_ClockGeneration = clock.generation;
146 if (m_CancelOnSet) {
147 m_CancelPending = true;
148 m_CancelWake = true;
149 ++m_Generations.read;
150 }
151 }
152 if (m_State.armed && now(clock) >= m_State.deadline) {
153 m_State.armed = false;
154 m_Expired = true;
155 m_Counter = addExpirations(m_Counter, 1);
156 ++m_Generations.read;
157 }
158}
159
160uint64_t TimerFd::forward(PosixTimerState::State& state, Time::Timestamp current) const {
161 if (!m_Expired || !state.interval)
162 return m_Counter;
163 assert(m_Counter);
164 state.armed = true;
165 const uint64_t expired = PosixTimerState::advance(state, current);
166 // The service counted the first expiration. A backward realtime change
167 // can put that deadline in the future again, yielding a zero-byte read.
168 return addExpirations(m_Counter - 1, expired);
169}
170
171void TimerFd::changed() {
172 m_Readers.broadcast();
173 notifyReadiness(ReadyRead);
174}
175
176void TimerFd::service(uint64_t generation) {
177 // A read copy may fault or block. The timer worker must remain available
178 // to drive unrelated waits while that copy owns the object mutex.
179 if (generation)
180 m_Lock.acquire();
181 else if (!m_Lock.tryAcquire())
182 return;
183 if (!m_AdmissionOpen || (generation && generation <= m_ClockGeneration)) {
184 m_Lock.release();
185 return;
186 }
187 const uint64_t oldGeneration = m_Generations.read;
188 update(posix_clock_snapshot());
189 const bool notify = oldGeneration != m_Generations.read;
190 m_Lock.release();
191 if (notify)
192 changed();
193}
194
195int TimerFd::readToUser(void* buffer, size_t count, bool canBlock) {
196 return readWithCopy(count, canBlock, copyRead, buffer);
197}
198
199int TimerFd::readWithCopy(size_t count, bool canBlock, PosixDescriptorReadCopy copy,
200 void* context) {
201 if (count < sizeof(uint64_t)) {
202 SYSCALL_ERROR(InvalidArgument);
203 return -1;
204 }
205 TerminationDeferral lifetime;
206 m_Lock.acquire();
207 PosixClockSnapshot clock = {};
208 for (;;) {
209 if (!m_AdmissionOpen) {
210 m_Lock.release();
211 SYSCALL_ERROR(BadFileDescriptor);
212 return -1;
213 }
214 clock = posix_clock_snapshot();
215 update(clock);
216 if (readable() || (!canBlock && m_CancelOnSet && m_CancelPending))
217 break;
218 if (!canBlock) {
219 m_Lock.release();
220 SYSCALL_ERROR(NoMoreProcesses);
221 return -1;
222 }
223 ConditionVariable::Error error = ConditionVariable::NoError;
224 if (!m_Readers.wait(m_Lock, error)) {
226 m_Lock.release();
227 if (error == ConditionVariable::Interrupted ||
228 error == ConditionVariable::TerminationDeferred)
229 SYSCALL_ERROR(Interrupted);
230 else
231 SYSCALL_ERROR(BadFileDescriptor);
232 return -1;
233 }
234 }
235 if (m_CancelOnSet && m_CancelPending) {
236 m_CancelPending = false;
237 m_CancelWake = false;
238 m_Counter = 0;
239 m_Expired = false;
240 m_Lock.release();
241 changed();
242 SYSCALL_ERROR(Cancelled);
243 return -1;
244 }
245
246 PosixTimerState::State next = m_State;
247 const uint64_t value = forward(next, now(clock));
248 if (value && !copy(context, &value, sizeof(value))) {
249 m_Lock.release();
250 changed();
251 SYSCALL_ERROR(BadAddress);
252 return -1;
253 }
254 m_State = next;
255 m_Counter = 0;
256 m_Expired = false;
257 m_Lock.release();
258 changed();
259 return value ? sizeof(value) : 0;
260}
261
262int TimerFd::configure(int flags, Time::Timestamp value, Time::Timestamp interval, void* previous) {
263 TerminationDeferral lifetime;
264 m_Lock.acquire();
265 if (!m_AdmissionOpen) {
266 m_Lock.release();
267 SYSCALL_ERROR(BadFileDescriptor);
268 return -1;
269 }
270 const PosixClockSnapshot clock = posix_clock_snapshot();
271 update(clock);
272 if (previous) {
273 PosixTimerState::State old = m_State;
274 forward(old, now(clock));
275 const PosixTimerState::Setting setting = PosixTimerState::snapshot(old, now(clock));
276 if (!PosixSubsystem::copyToUser(previous, &setting, sizeof(setting))) {
277 m_Lock.release();
278 changed();
279 SYSCALL_ERROR(BadAddress);
280 return -1;
281 }
282 }
283 m_CancelOnSet = m_Clock == ClockRealtime && (flags & LinuxTimerFd::Absolute) &&
284 (flags & LinuxTimerFd::CancelOnSet);
285 const bool cancelled = value && m_CancelOnSet && m_CancelPending;
286 if (cancelled)
287 m_CancelPending = false;
288 m_CancelWake = false;
289 m_ClockGeneration = clock.generation;
290 m_Counter = 0;
291 m_Expired = false;
292 m_State.realtime = m_Clock == ClockRealtime && (flags & LinuxTimerFd::Absolute);
293 m_State.interval = interval;
294 m_State.deadline =
295 flags & LinuxTimerFd::Absolute ? value : PosixTimerState::add(now(clock), value);
296 m_State.armed = value != 0;
297 update(clock);
298 m_Lock.release();
299 changed();
300 if (cancelled) {
301 SYSCALL_ERROR(Cancelled);
302 return -1;
303 }
304 return 0;
305}
306
307int TimerFd::getTime(void* value) {
308 TerminationDeferral lifetime;
309 m_Lock.acquire();
310 if (!m_AdmissionOpen) {
311 m_Lock.release();
312 SYSCALL_ERROR(BadFileDescriptor);
313 return -1;
314 }
315 const PosixClockSnapshot clock = posix_clock_snapshot();
316 update(clock);
317 PosixTimerState::State next = m_State;
318 const uint64_t counter = forward(next, now(clock));
319 const PosixTimerState::Setting setting = PosixTimerState::snapshot(next, now(clock));
320 if (!PosixSubsystem::copyToUser(value, &setting, sizeof(setting))) {
321 m_Lock.release();
322 changed();
323 SYSCALL_ERROR(BadAddress);
324 return -1;
325 }
326 m_State = next;
327 m_Counter = counter;
328 m_Expired = false;
329 m_Lock.release();
330 changed();
331 return 0;
332}
333
334ReadyMask TimerFd::queryReady() {
335 LockGuard<Mutex> guard(m_Lock);
336 if (!m_AdmissionOpen)
337 return ReadyInvalid | ReadyHangup;
338 return readable() ? ReadyRead : ReadyNone;
339}
340
342 LockGuard<Mutex> guard(m_Lock);
343 return m_Generations;
344}
345
346bool TimerFd::addDescriptorOwner() {
347 TerminationDeferral lifetime;
348 LockGuard<Mutex> guard(m_Lock);
349 if (!m_AdmissionOpen)
350 return false;
351 ++m_DescriptorOwners;
352 return true;
353}
354
355void TimerFd::removeDescriptorOwner() {
356 TerminationDeferral lifetime;
357 m_Lock.acquire();
358 assert(m_DescriptorOwners);
359 const bool last = !--m_DescriptorOwners;
360 if (last) {
361 m_AdmissionOpen = false;
362 m_State.armed = false;
363 ++m_Generations.hangup;
364 }
365 m_Lock.release();
366 if (last) {
367 timerService.remove(this);
368 m_Readers.broadcast();
370 }
371}
372
373int posix_timerfd_create(int clock, int flags) {
374 if ((clock != ClockRealtime && clock != ClockMonotonic) ||
375 (flags & ~(LinuxTimerFd::NonBlock | LinuxTimerFd::CloseOnExec))) {
376 SYSCALL_ERROR(InvalidArgument);
377 return -1;
378 }
379 TerminationDeferral lifetime;
380 SharedPointer<TimerFd> timer(new TimerFd(clock));
381 if (!timerService.add(timer))
382 return -1;
383 const size_t fd = getAvailableDescriptor();
384 const int descriptorFlags = flags & LinuxTimerFd::CloseOnExec ? FD_CLOEXEC : 0;
385 const int statusFlags = O_RDWR | (flags & LinuxTimerFd::NonBlock ? O_NONBLOCK : 0);
386 auto* descriptor = new FileDescriptor(nullptr, 0, fd, descriptorFlags, statusFlags);
387 descriptor->setTimerFdImpl(timer);
388 addDescriptor(static_cast<int>(fd), descriptor);
389 return static_cast<int>(fd);
390}
391
392int posix_timerfd_settime(int fd, int flags, const void* newValue, void* oldValue) {
393 PosixTimerState::Setting requested;
394 if (!PosixSubsystem::copyFromUser(&requested, newValue, sizeof(requested))) {
395 SYSCALL_ERROR(BadAddress);
396 return -1;
397 }
398 Time::Timestamp value, interval;
399 if ((flags & ~(LinuxTimerFd::Absolute | LinuxTimerFd::CancelOnSet)) ||
400 !PosixTimerState::decode(requested.value, value) ||
401 !PosixTimerState::decode(requested.interval, interval)) {
402 SYSCALL_ERROR(InvalidArgument);
403 return -1;
404 }
405 DescriptorLease descriptor;
406 if (!acquireDescriptor(fd, descriptor)) {
407 SYSCALL_ERROR(BadFileDescriptor);
408 return -1;
409 }
410 SharedPointer<TimerFd> timer = descriptor->getTimerFdImpl();
411 if (!timer) {
412 SYSCALL_ERROR(InvalidArgument);
413 return -1;
414 }
415 return timer->configure(flags, value, interval, oldValue);
416}
417
418int posix_timerfd_gettime(int fd, void* value) {
419 DescriptorLease descriptor;
420 if (!acquireDescriptor(fd, descriptor)) {
421 SYSCALL_ERROR(BadFileDescriptor);
422 return -1;
423 }
424 SharedPointer<TimerFd> timer = descriptor->getTimerFdImpl();
425 if (!timer) {
426 SYSCALL_ERROR(InvalidArgument);
427 return -1;
428 }
429 return timer->getTime(value);
430}
431
432void posix_timerfd_clock_changed(uint64_t generation) {
433 timerService.refresh(generation);
434}
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
static bool mutexAcquired(Error error)
virtual Timer * getTimer()=0
Definition Mutex.h:56
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool inDeviceHardIrq()
Definition Processor.h:559
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
Definition Readiness.cc:208
void release(size_t n=1)
Definition Semaphore.cc:546
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:481
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:352
void timer(uint64_t) override
ReadinessGenerations readinessGenerations() override
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117