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 rearm();
77 }
78
79 void rearm() {
80 LockGuard<Mutex> guard(m_Lock);
81 if (!m_Source || !m_Source->supportsDeadlines())
82 return;
83 const PosixClockSnapshot clock = posix_clock_snapshot();
84 Time::Timestamp next = 0;
85 for (size_t i = 0; i < m_End; ++i) {
86 TimerFd* timer = m_Timers[i].get();
87 if (!timer)
88 continue;
89 Time::Timestamp deadline = 0;
90 if (!timer->m_Lock.tryAcquire()) {
91 deadline = PosixTimerState::add(clock.monotonic, Time::Multiplier::Millisecond);
92 } else {
93 if (timer->m_AdmissionOpen)
94 deadline =
95 PosixTimerState::monotonicDeadline(timer->m_State, clock.realtime, clock.monotonic);
96 timer->m_Lock.release();
97 }
98 if (deadline && (!next || deadline < next))
99 next = deadline;
100 }
101 if (!m_Source->armHandler(this, next))
102 panic("timerfd service could not arm its callback");
103 }
104
105 void refresh(uint64_t generation) {
106 TerminationDeferral lifetime;
107 for (size_t i = 0; i < LinuxTimerFd::MaximumObjects; ++i) {
109 Timer* source = Machine::instance().getTimer();
110 if (generation || (source && source->supportsDeadlines()))
111 m_Lock.acquire();
112 else if (!m_Lock.tryAcquire())
113 return;
114 if (i >= m_End) {
115 m_Lock.release();
116 break;
117 }
118 timer = m_Timers[i];
119 m_Lock.release();
120 // A callback pin outlives final descriptor close, but cannot keep the
121 // descriptor admission open. Never notify observers under the registry.
122 if (timer)
123 timer->service(generation);
124 }
125 rearm();
126 }
127
128 void timer(uint64_t) override {
130 refresh(0);
131 }
132
133 private:
134 Mutex m_Lock;
135 Timer* m_Source = nullptr;
136 size_t m_End = 0;
137 SharedPointer<TimerFd> m_Timers[LinuxTimerFd::MaximumObjects];
138};
139
140namespace {
141TimerFdService timerService;
142} // namespace
143
144TimerFd::TimerFd(int clock)
145 : m_Lock(),
146 m_Readers(),
147 m_State(),
148 m_Generations(),
149 m_Counter(0),
150 m_ClockGeneration(posix_clock_change_generation()),
151 m_DescriptorOwners(0),
152 m_Clock(clock),
153 m_Expired(false),
154 m_CancelOnSet(false),
155 m_CancelPending(false),
156 m_CancelWake(false),
157 m_AdmissionOpen(true) {}
158
159TimerFd::~TimerFd() {
160 assert(!m_DescriptorOwners);
162}
163
164bool TimerFd::readable() const {
165 return m_Counter || m_CancelWake;
166}
167
168Time::Timestamp TimerFd::now(const PosixClockSnapshot& clock) const {
169 return m_State.realtime ? clock.realtime : clock.monotonic;
170}
171
172void TimerFd::update(const PosixClockSnapshot& clock) {
173 if (clock.generation > m_ClockGeneration) {
174 m_ClockGeneration = clock.generation;
175 if (m_CancelOnSet) {
176 m_CancelPending = true;
177 m_CancelWake = true;
178 ++m_Generations.read;
179 }
180 }
181 if (m_State.armed && now(clock) >= m_State.deadline) {
182 m_State.armed = false;
183 m_Expired = true;
184 m_Counter = addExpirations(m_Counter, 1);
185 ++m_Generations.read;
186 }
187}
188
189uint64_t TimerFd::forward(PosixTimerState::State& state, Time::Timestamp current) const {
190 if (!m_Expired || !state.interval)
191 return m_Counter;
192 assert(m_Counter);
193 state.armed = true;
194 const uint64_t expired = PosixTimerState::advance(state, current);
195 // The service counted the first expiration. A backward realtime change
196 // can put that deadline in the future again, yielding a zero-byte read.
197 return addExpirations(m_Counter - 1, expired);
198}
199
200void TimerFd::changed() {
201 m_Readers.broadcast();
202 notifyReadiness(ReadyRead);
203}
204
205void TimerFd::service(uint64_t generation) {
206 // A read copy may fault or block. The timer worker must remain available
207 // to drive unrelated waits while that copy owns the object mutex.
208 if (generation)
209 m_Lock.acquire();
210 else if (!m_Lock.tryAcquire())
211 return;
212 if (!m_AdmissionOpen || (generation && generation <= m_ClockGeneration)) {
213 m_Lock.release();
214 return;
215 }
216 const uint64_t oldGeneration = m_Generations.read;
217 update(posix_clock_snapshot());
218 const bool notify = oldGeneration != m_Generations.read;
219 m_Lock.release();
220 if (notify)
221 changed();
222}
223
224int TimerFd::readToUser(void* buffer, size_t count, bool canBlock) {
225 return readWithCopy(count, canBlock, copyRead, buffer);
226}
227
228int TimerFd::readWithCopy(size_t count, bool canBlock, PosixDescriptorReadCopy copy,
229 void* context) {
230 if (count < sizeof(uint64_t)) {
231 SYSCALL_ERROR(InvalidArgument);
232 return -1;
233 }
234 TerminationDeferral lifetime;
235 m_Lock.acquire();
236 PosixClockSnapshot clock = {};
237 for (;;) {
238 if (!m_AdmissionOpen) {
239 m_Lock.release();
240 SYSCALL_ERROR(BadFileDescriptor);
241 return -1;
242 }
243 clock = posix_clock_snapshot();
244 update(clock);
245 if (readable() || (!canBlock && m_CancelOnSet && m_CancelPending))
246 break;
247 if (!canBlock) {
248 m_Lock.release();
249 SYSCALL_ERROR(NoMoreProcesses);
250 return -1;
251 }
252 ConditionVariable::Error error = ConditionVariable::NoError;
253 if (!m_Readers.wait(m_Lock, error)) {
255 m_Lock.release();
256 if (error == ConditionVariable::Interrupted ||
257 error == ConditionVariable::TerminationDeferred)
258 SYSCALL_ERROR(Interrupted);
259 else
260 SYSCALL_ERROR(BadFileDescriptor);
261 return -1;
262 }
263 }
264 if (m_CancelOnSet && m_CancelPending) {
265 m_CancelPending = false;
266 m_CancelWake = false;
267 m_Counter = 0;
268 m_Expired = false;
269 m_Lock.release();
270 changed();
271 SYSCALL_ERROR(Cancelled);
272 return -1;
273 }
274
275 PosixTimerState::State next = m_State;
276 const uint64_t value = forward(next, now(clock));
277 if (value && !copy(context, &value, sizeof(value))) {
278 m_Lock.release();
279 changed();
280 SYSCALL_ERROR(BadAddress);
281 return -1;
282 }
283 m_State = next;
284 m_Counter = 0;
285 m_Expired = false;
286 m_Lock.release();
287 timerService.rearm();
288 changed();
289 return value ? sizeof(value) : 0;
290}
291
292int TimerFd::configure(int flags, Time::Timestamp value, Time::Timestamp interval, void* previous) {
293 TerminationDeferral lifetime;
294 m_Lock.acquire();
295 if (!m_AdmissionOpen) {
296 m_Lock.release();
297 SYSCALL_ERROR(BadFileDescriptor);
298 return -1;
299 }
300 const PosixClockSnapshot clock = posix_clock_snapshot();
301 update(clock);
302 if (previous) {
303 PosixTimerState::State old = m_State;
304 forward(old, now(clock));
305 const PosixTimerState::Setting setting = PosixTimerState::snapshot(old, now(clock));
306 if (!PosixSubsystem::copyToUser(previous, &setting, sizeof(setting))) {
307 m_Lock.release();
308 changed();
309 SYSCALL_ERROR(BadAddress);
310 return -1;
311 }
312 }
313 m_CancelOnSet = m_Clock == ClockRealtime && (flags & LinuxTimerFd::Absolute) &&
314 (flags & LinuxTimerFd::CancelOnSet);
315 const bool cancelled = value && m_CancelOnSet && m_CancelPending;
316 if (cancelled)
317 m_CancelPending = false;
318 m_CancelWake = false;
319 m_ClockGeneration = clock.generation;
320 m_Counter = 0;
321 m_Expired = false;
322 m_State.realtime = m_Clock == ClockRealtime && (flags & LinuxTimerFd::Absolute);
323 m_State.interval = interval;
324 m_State.deadline =
325 flags & LinuxTimerFd::Absolute ? value : PosixTimerState::add(now(clock), value);
326 m_State.armed = value != 0;
327 update(clock);
328 m_Lock.release();
329 timerService.rearm();
330 changed();
331 if (cancelled) {
332 SYSCALL_ERROR(Cancelled);
333 return -1;
334 }
335 return 0;
336}
337
338int TimerFd::getTime(void* value) {
339 TerminationDeferral lifetime;
340 m_Lock.acquire();
341 if (!m_AdmissionOpen) {
342 m_Lock.release();
343 SYSCALL_ERROR(BadFileDescriptor);
344 return -1;
345 }
346 const PosixClockSnapshot clock = posix_clock_snapshot();
347 update(clock);
348 PosixTimerState::State next = m_State;
349 const uint64_t counter = forward(next, now(clock));
350 const PosixTimerState::Setting setting = PosixTimerState::snapshot(next, now(clock));
351 if (!PosixSubsystem::copyToUser(value, &setting, sizeof(setting))) {
352 m_Lock.release();
353 changed();
354 SYSCALL_ERROR(BadAddress);
355 return -1;
356 }
357 m_State = next;
358 m_Counter = counter;
359 m_Expired = false;
360 m_Lock.release();
361 timerService.rearm();
362 changed();
363 return 0;
364}
365
366ReadyMask TimerFd::queryReady() {
367 LockGuard<Mutex> guard(m_Lock);
368 if (!m_AdmissionOpen)
369 return ReadyInvalid | ReadyHangup;
370 return readable() ? ReadyRead : ReadyNone;
371}
372
374 LockGuard<Mutex> guard(m_Lock);
375 return m_Generations;
376}
377
378bool TimerFd::addDescriptorOwner() {
379 TerminationDeferral lifetime;
380 LockGuard<Mutex> guard(m_Lock);
381 if (!m_AdmissionOpen)
382 return false;
383 ++m_DescriptorOwners;
384 return true;
385}
386
387void TimerFd::removeDescriptorOwner() {
388 TerminationDeferral lifetime;
389 m_Lock.acquire();
390 assert(m_DescriptorOwners);
391 const bool last = !--m_DescriptorOwners;
392 if (last) {
393 m_AdmissionOpen = false;
394 m_State.armed = false;
395 ++m_Generations.hangup;
396 }
397 m_Lock.release();
398 if (last) {
399 timerService.remove(this);
400 m_Readers.broadcast();
402 }
403}
404
405int posix_timerfd_create(int clock, int flags) {
406 if ((clock != ClockRealtime && clock != ClockMonotonic) ||
407 (flags & ~(LinuxTimerFd::NonBlock | LinuxTimerFd::CloseOnExec))) {
408 SYSCALL_ERROR(InvalidArgument);
409 return -1;
410 }
411 TerminationDeferral lifetime;
412 SharedPointer<TimerFd> timer(new TimerFd(clock));
413 if (!timerService.add(timer))
414 return -1;
415 const size_t fd = getAvailableDescriptor();
416 const int descriptorFlags = flags & LinuxTimerFd::CloseOnExec ? FD_CLOEXEC : 0;
417 const int statusFlags = O_RDWR | (flags & LinuxTimerFd::NonBlock ? O_NONBLOCK : 0);
418 auto* descriptor = new FileDescriptor(nullptr, 0, fd, descriptorFlags, statusFlags);
419 descriptor->setTimerFdImpl(timer);
420 addDescriptor(static_cast<int>(fd), descriptor);
421 return static_cast<int>(fd);
422}
423
424int posix_timerfd_settime(int fd, int flags, const void* newValue, void* oldValue) {
425 PosixTimerState::Setting requested;
426 if (!PosixSubsystem::copyFromUser(&requested, newValue, sizeof(requested))) {
427 SYSCALL_ERROR(BadAddress);
428 return -1;
429 }
430 Time::Timestamp value, interval;
431 if ((flags & ~(LinuxTimerFd::Absolute | LinuxTimerFd::CancelOnSet)) ||
432 !PosixTimerState::decode(requested.value, value) ||
433 !PosixTimerState::decode(requested.interval, interval)) {
434 SYSCALL_ERROR(InvalidArgument);
435 return -1;
436 }
437 DescriptorLease descriptor;
438 if (!acquireDescriptor(fd, descriptor)) {
439 SYSCALL_ERROR(BadFileDescriptor);
440 return -1;
441 }
442 SharedPointer<TimerFd> timer = descriptor->getTimerFdImpl();
443 if (!timer) {
444 SYSCALL_ERROR(InvalidArgument);
445 return -1;
446 }
447 return timer->configure(flags, value, interval, oldValue);
448}
449
450int posix_timerfd_gettime(int fd, void* value) {
451 DescriptorLease descriptor;
452 if (!acquireDescriptor(fd, descriptor)) {
453 SYSCALL_ERROR(BadFileDescriptor);
454 return -1;
455 }
456 SharedPointer<TimerFd> timer = descriptor->getTimerFdImpl();
457 if (!timer) {
458 SYSCALL_ERROR(InvalidArgument);
459 return -1;
460 }
461 return timer->getTime(value);
462}
463
464void posix_timerfd_clock_changed(uint64_t generation) {
465 timerService.refresh(generation);
466}
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:581
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:549
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:484
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:355
T * get() const
void timer(uint64_t) override
ReadinessGenerations readinessGenerations() override
virtual bool supportsDeadlines() const
virtual bool armHandler(TimerHandler *, uint64_t)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118