9#include "epoll-syscalls.h"
10#include "pedigree/kernel/Atomic.h"
11#include "pedigree/kernel/LockGuard.h"
12#include "pedigree/kernel/process/Mutex.h"
13#include "pedigree/kernel/process/Semaphore.h"
14#include "pedigree/kernel/process/Thread.h"
15#include "pedigree/kernel/syscallError.h"
16#include "pedigree/kernel/time/Time.h"
17#include "pedigree/kernel/utilities/List.h"
18#include "pedigree/kernel/utilities/SharedPointer.h"
19#include "pedigree/kernel/utilities/assert.h"
26#include "modules/subsys/posix/FileDescriptor.h"
27#include "modules/subsys/posix/PosixSubsystem.h"
28#include "modules/subsys/posix/eventfd-syscalls.h"
29#include "modules/subsys/posix/fanotify-syscalls.h"
30#include "modules/subsys/posix/inotify-syscalls.h"
31#include "modules/subsys/posix/mqueue-syscalls.h"
32#include "modules/subsys/posix/net-syscalls.h"
33#include "modules/system/vfs/File.h"
34#include "signalfd-syscalls.h"
35#include "timerfd-syscalls.h"
38constexpr int MaximumEpollBatch = 16384;
39constexpr int LinuxMaximumEpollEvents = INT_MAX /
static_cast<int>(
sizeof(
LinuxEpollEvent));
40constexpr size_t LinuxKernelSigsetSize =
sizeof(uint64_t);
41constexpr uint64_t UnblockableSignals =
42 (
static_cast<uint64_t
>(1) << (SIGKILL - 1)) | (
static_cast<uint64_t
>(1) << (SIGSTOP - 1));
44constexpr uint32_t ReadEvents = LinuxEpoll::In | LinuxEpoll::ReadNormal | LinuxEpoll::ReadBand;
45constexpr uint32_t WriteEvents = LinuxEpoll::Out | LinuxEpoll::WriteNormal | LinuxEpoll::WriteBand;
46constexpr uint32_t RequestedEvents =
47 ReadEvents | WriteEvents | LinuxEpoll::Priority | LinuxEpoll::ReadHangup;
48constexpr uint32_t AlwaysReturnedEvents = LinuxEpoll::Error | LinuxEpoll::Hangup;
49constexpr uint32_t SupportedEvents =
50 RequestedEvents | AlwaysReturnedEvents | LinuxEpoll::OneShot | LinuxEpoll::EdgeTriggered;
51constexpr uint32_t UnsupportedModes =
52 LinuxEpoll::Exclusive | LinuxEpoll::Wakeup | LinuxEpoll::Message;
65 description(openFile),
67 network(watchedNetwork),
68 eventFd(watchedEventFd),
69 inotify(watchedInotify),
70 fanotify(watchedFanotify),
71 mqueue(watchedMqueue),
72 timerFd(watchedTimerFd),
73 signalView(watchedSignalView),
81 observedWriteGeneration(watchedEventFd ? watchedEventFd->writeGeneration() : 0),
82 observedGenerations(),
100 uint32_t observedEvents;
101 uint32_t pendingEvents;
102 uint64_t observedWriteGeneration;
109 return watch.timerFd.get();
111 if (watch.signalView) {
112 return watch.signalView.get();
118 return watch.network.get();
121 return watch.inotify.get();
123 if (watch.fanotify) {
124 return watch.fanotify->readinessSource();
127 return watch.mqueue.get();
129 return watch.eventFd.get();
132uint32_t eventsFor(ReadyMask ready, uint32_t requested) {
134 if (ready & ReadyRead) {
135 result |= requested & (LinuxEpoll::In | LinuxEpoll::ReadNormal);
137 if (ready & ReadyPriority) {
138 result |= requested & (LinuxEpoll::Priority | LinuxEpoll::ReadBand);
140 if (ready & ReadyWrite) {
141 result |= requested & WriteEvents;
143 if (ready & ReadyError) {
144 result |= LinuxEpoll::Error;
146 if (ready & ReadyHangup) {
147 result |= LinuxEpoll::Hangup;
149 if ((ready & ReadyReadHangup) && (requested & LinuxEpoll::ReadHangup)) {
150 result |= LinuxEpoll::ReadHangup;
152 if (ready & ReadyInvalid) {
153 result |= LinuxEpoll::Error | LinuxEpoll::Hangup;
158ReadyMask queryWatch(
const EpollWatch& watch) {
160 return watch.timerFd->queryReady();
162 if (watch.signalView) {
163 return watch.signalView->queryCallerReady();
165 const bool reading = watch.canRead && (watch.events & (ReadEvents | LinuxEpoll::ReadHangup));
166 const bool writing = watch.canWrite && (watch.events & WriteEvents);
168 return watch.description->queryFileReady(reading, writing);
171 return watch.network->queryReady(reading, writing);
174 return watch.eventFd->queryReady();
177 return watch.inotify->queryReady();
179 if (watch.fanotify) {
180 return watch.fanotify->queryReady();
183 return watch.mqueue->queryReady();
188uint32_t sampleWatch(EpollWatch& watch) {
189 const uint32_t readyEvents = eventsFor(queryWatch(watch), watch.events);
190 if (watch.events & LinuxEpoll::EdgeTriggered) {
194 ? watch.signalView->callerReadinessGenerations()
195 : (watch.file ? watch.description->fileReadinessGenerations()
197 const uint64_t writeGeneration = watch.eventFd ? watch.eventFd->writeGeneration() : 0;
202 watch.pendingEvents &= readyEvents;
203 watch.pendingEvents |= readyEvents & ~watch.observedEvents;
204 if (generations.read != watch.observedGenerations.read) {
205 watch.pendingEvents |= readyEvents & (LinuxEpoll::In | LinuxEpoll::ReadNormal);
207 if (generations.write != watch.observedGenerations.write) {
208 watch.pendingEvents |= readyEvents & WriteEvents;
210 if (generations.priority != watch.observedGenerations.priority) {
211 watch.pendingEvents |= readyEvents & (LinuxEpoll::Priority | LinuxEpoll::ReadBand);
213 if (generations.error != watch.observedGenerations.error) {
214 watch.pendingEvents |= readyEvents & LinuxEpoll::Error;
216 if (generations.hangup != watch.observedGenerations.hangup) {
217 watch.pendingEvents |= readyEvents & LinuxEpoll::Hangup;
219 if (generations.readHangup != watch.observedGenerations.readHangup) {
220 watch.pendingEvents |= readyEvents & LinuxEpoll::ReadHangup;
222 if (watch.eventFd && writeGeneration != watch.observedWriteGeneration) {
226 watch.pendingEvents |= readyEvents & ReadEvents;
228 watch.observedEvents = readyEvents;
229 watch.observedWriteGeneration = writeGeneration;
230 watch.observedGenerations = generations;
235uint32_t reportableEvents(
const EpollWatch& watch, uint32_t readyEvents) {
236 if (watch.events & LinuxEpoll::EdgeTriggered) {
237 return watch.pendingEvents;
242void retireWatch(EpollWatch* watch) {
246 watch->subscription.reset();
251 while (watches.
count()) {
258 if (!acquireDescriptor(fd, descriptor)) {
259 SYSCALL_ERROR(BadFileDescriptor);
265 SYSCALL_ERROR(InvalidArgument);
277 m_Instance->sourceReadinessChanged(mask);
296 assert(!watches.
count());
308EpollInstance::~EpollInstance() {
312 while (m_State->watches.
count()) {
320 retireWatches(retiring);
321 m_State->observer.
reset();
327void EpollInstance::wakeWaiter() {
330 if (m_State->wakePending.compareAndSwap(
false,
true)) {
335void EpollInstance::sourceReadinessChanged(ReadyMask) {
336 bool reportable =
false;
339 for (EpollWatch* watch : m_State->watches) {
340 if (!watch->description->descriptorOwnerCount()) {
344 if (watch->signalView) {
347 reportable |= watch->armed;
351 const uint32_t readyEvents = sampleWatch(*watch);
352 if (watch->armed && reportableEvents(*watch, readyEvents)) {
368 if (operation != LinuxEpoll::ControlAdd && operation != LinuxEpoll::ControlDelete &&
369 operation != LinuxEpoll::ControlModify) {
370 SYSCALL_ERROR(InvalidArgument);
374 if ((operation == LinuxEpoll::ControlAdd || operation == LinuxEpoll::ControlModify) && !event) {
375 SYSCALL_ERROR(BadAddress);
380 if (event->events & UnsupportedModes) {
381 SYSCALL_ERROR(OperationNotSupported);
384 if (event->events & ~SupportedEvents) {
385 SYSCALL_ERROR(InvalidArgument);
391 if (!acquireDescriptor(targetFd, descriptor)) {
392 SYSCALL_ERROR(BadFileDescriptor);
399 SYSCALL_ERROR(InvalidArgument);
404 File* file = description->getFile();
408 auto fanotify = description->getFanotifyImpl();
410 auto timerFd = description->getTimerFdImpl();
411 auto signalFd = description->getSignalFdImpl();
412 if (!file && !network && !eventFd && !inotify && !fanotify && !mqueue && !timerFd && !signalFd) {
413 SYSCALL_ERROR(NotEnoughPermissions);
417 if ((operation == LinuxEpoll::ControlAdd || operation == LinuxEpoll::ControlModify) && file &&
422 SYSCALL_ERROR(NotEnoughPermissions);
426 if (operation == LinuxEpoll::ControlDelete) {
427 EpollWatch* retiring =
nullptr;
430 for (
auto it = m_State->watches.
begin(); it != m_State->watches.
end(); ++it) {
431 EpollWatch* watch = *it;
432 if (watch->fd == targetFd && watch->description.get() == description.
get()) {
434 m_State->watches.
erase(it);
441 SYSCALL_ERROR(DoesNotExist);
444 retireWatch(retiring);
448 if (operation == LinuxEpoll::ControlModify) {
452 for (EpollWatch* watch : m_State->watches) {
453 if (watch->fd == targetFd && watch->description.get() == description.
get()) {
454 watch->events =
event->events;
455 watch->data =
event->data;
459 watch->observedEvents = 0;
460 watch->pendingEvents = 0;
461 watch->observedWriteGeneration = watch->eventFd ? watch->eventFd->writeGeneration() : 0;
463 watch->observedGenerations =
465 ? watch->signalView->callerReadinessGenerations()
467 ? watch->description->fileReadinessGenerations()
476 SYSCALL_ERROR(DoesNotExist);
481 sourceReadinessChanged(ReadyAll);
487 signalView = signalFd->bindCaller();
489 SYSCALL_ERROR(BadFileDescriptor);
496 network || eventFd || inotify || fanotify || mqueue || accessMode != O_WRONLY;
497 const bool canWrite = network || eventFd || mqueue || accessMode != O_RDONLY;
499 new EpollWatch(targetFd, description, file, network, eventFd, inotify, fanotify, mqueue,
500 timerFd, signalView, canRead, canWrite, *event);
509 SYSCALL_ERROR(NotEnoughPermissions);
513 bool duplicate =
false;
516 for (EpollWatch* existing : m_State->watches) {
517 if (existing->fd == targetFd && existing->description.get() == description.
get()) {
529 SYSCALL_ERROR(FileExists);
534 sourceReadinessChanged(ReadyAll);
538int EpollInstance::collectEvents(
LinuxEpollEvent* events,
int maxEvents,
bool consumeOneShot) {
543 const size_t candidates = m_State->watches.
count();
544 size_t inspected = 0;
548 while (inspected < candidates && count < maxEvents) {
549 EpollWatch* watch = m_State->watches.
popFront();
552 if (!watch->description->descriptorOwnerCount()) {
562 const uint32_t readyEvents = sampleWatch(*watch);
563 if (!watch->description->descriptorOwnerCount()) {
568 const uint32_t returnedEvents = reportableEvents(*watch, readyEvents);
569 if (returnedEvents) {
571 events[count].events = returnedEvents;
572 events[count].data = watch->data;
575 if (consumeOneShot) {
576 if (watch->events & LinuxEpoll::EdgeTriggered) {
577 watch->pendingEvents &= ~returnedEvents;
579 if (watch->events & LinuxEpoll::OneShot) {
580 watch->armed =
false;
591 retireWatches(retiring);
596 return collectEvents(
nullptr, 1,
false) ? ReadyRead : ReadyNone;
600 const bool hasTimeout = timeoutMilliseconds >= 0;
601 const Time::Timestamp deadline =
602 timeoutMilliseconds > 0
604 static_cast<Time::Timestamp
>(timeoutMilliseconds) * Time::Multiplier::Millisecond
608 return collectEvents(events, maxEvents,
true);
614 int ready = collectEvents(events, maxEvents,
true);
623 if (timeoutMilliseconds == 0) {
631 m_State->wakePending =
false;
635 size_t waitSeconds = 0;
636 size_t waitMicroseconds = 0;
638 const Time::Timestamp now = Time::getTicks();
639 if (now >= deadline) {
643 const Time::Timestamp remaining = deadline - now;
644 waitSeconds = remaining / Time::Multiplier::Second;
646 (remaining % Time::Multiplier::Second + Time::Multiplier::Microsecond - 1) /
647 Time::Multiplier::Microsecond;
648 if (waitMicroseconds >= 1000000) {
650 waitMicroseconds = 0;
654 Semaphore::SemaphoreError error = Semaphore::NoError;
655 const bool acquired =
658 m_State->wakePending =
false;
665 ready = collectEvents(events, maxEvents,
true);
669 if (error == Semaphore::TimedOut) {
673 SYSCALL_ERROR(Interrupted);
679int posix_epoll_create1(
int flags) {
680 if (flags & ~LinuxEpoll::CloseOnExec) {
681 SYSCALL_ERROR(InvalidArgument);
685 const size_t fd = getAvailableDescriptor();
686 const int descriptorFlags = flags & LinuxEpoll::CloseOnExec ? FD_CLOEXEC : 0;
689 addDescriptor(
static_cast<int>(fd), descriptor);
690 return static_cast<int>(fd);
693int posix_epoll_create(
int size) {
695 SYSCALL_ERROR(InvalidArgument);
698 return posix_epoll_create1(0);
701int posix_epoll_ctl(
int epollFd,
int operation,
int targetFd,
const LinuxEpollEvent* event) {
703 if (!acquireEpoll(epollFd, instance)) {
709 if (operation == LinuxEpoll::ControlAdd || operation == LinuxEpoll::ControlModify) {
711 SYSCALL_ERROR(BadAddress);
714 kernelEvent = &snapshot;
716 return instance->control(operation, targetFd, kernelEvent);
720int epollWait(
int epollFd,
LinuxEpollEvent* events,
int maxEvents,
int timeoutMilliseconds,
721 const uint64_t* temporarySignalMask) {
722 if (maxEvents <= 0 || maxEvents > LinuxMaximumEpollEvents) {
723 SYSCALL_ERROR(InvalidArgument);
730 const int eventCapacity = maxEvents < MaximumEpollBatch ? maxEvents : MaximumEpollBatch;
733 if (!acquireEpoll(epollFd, instance)) {
741 SYSCALL_ERROR(BadAddress);
747 bool signalInterrupted =
false;
748 if (temporarySignalMask) {
751 FATAL(
"epoll_pwait has no current Thread.");
755 result = instance->wait(snapshot, eventCapacity, timeoutMilliseconds);
756 signalInterrupted = signalWait.finish();
758 result = instance->wait(snapshot, eventCapacity, timeoutMilliseconds);
761 if (!result && signalInterrupted) {
762 SYSCALL_ERROR(Interrupted);
773 SYSCALL_ERROR(BadAddress);
780int posix_epoll_wait(
int epollFd,
LinuxEpollEvent* events,
int maxEvents,
int timeoutMilliseconds) {
781 return epollWait(epollFd, events, maxEvents, timeoutMilliseconds,
nullptr);
784int posix_epoll_pwait(
int epollFd,
LinuxEpollEvent* events,
int maxEvents,
int timeoutMilliseconds,
785 const void* signalMask,
size_t signalMaskSize) {
787 return epollWait(epollFd, events, maxEvents, timeoutMilliseconds,
nullptr);
789 if (signalMaskSize != LinuxKernelSigsetSize) {
790 SYSCALL_ERROR(InvalidArgument);
794 uint64_t temporarySignalMask = 0;
796 SYSCALL_ERROR(BadAddress);
799 temporarySignalMask &= ~UnblockableSignals;
800 return epollWait(epollFd, events, maxEvents, timeoutMilliseconds, &temporarySignalMask);
int control(int operation, int targetFd, const LinuxEpollEvent *event)
int wait(LinuxEpollEvent *events, int maxEvents, int timeoutMilliseconds)
void readinessChanged(ReadyMask mask) override
SharedPointer< EpollInstance > epollImpl
Epoll implementation for this descriptor (if it is an epoll object).
OpenFileDescriptionLease acquireOpenFileDescription() const
int getStatusFlags() const
Get current status flags.
virtual bool supportsReadinessNotifications() const
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool checkUserBuffer(uintptr_t addr, size_t count, size_t elementSize, size_t flags, size_t *extent=nullptr)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()
void notifyReadiness(ReadyMask mask)
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
MUST_USE_RESULT bool subscribeReadiness(ReadyMask interest, const SharedPointer< ReadinessObserver > &observer, ReadinessSubscription &subscription)
virtual ReadinessGenerations readinessGenerations()
MUST_USE_RESULT bool acquireWithError(size_t n, size_t timeoutSecs, size_t timeoutUsecs, SemaphoreError &error)
bool tryAcquire(size_t n=1)
Iterator erase(Iterator &Iter)
void pushBack(const T &value)