diff --git a/orbis-kernel/CMakeLists.txt b/orbis-kernel/CMakeLists.txt index d494b7e51..23af45654 100644 --- a/orbis-kernel/CMakeLists.txt +++ b/orbis-kernel/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(obj.orbis-kernel OBJECT src/module.cpp src/pipe.cpp src/sysvec.cpp + src/event.cpp src/evf.cpp src/ipmi.cpp src/KernelContext.cpp diff --git a/orbis-kernel/include/orbis/event.hpp b/orbis-kernel/include/orbis/event.hpp new file mode 100644 index 000000000..686e89802 --- /dev/null +++ b/orbis-kernel/include/orbis/event.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include "file.hpp" +#include "orbis-config.hpp" +#include "utils/SharedCV.hpp" +#include "utils/SharedMutex.hpp" +#include +#include +#include + +namespace orbis { +static constexpr auto kEvFiltRead = -1; +static constexpr auto kEvFiltWrite = -2; +static constexpr auto kEvFiltAio = -3; +static constexpr auto kEvFiltVnode = -4; +static constexpr auto kEvFiltProc = -5; +static constexpr auto kEvFiltSignal = -6; +static constexpr auto kEvFiltTimer = -7; +static constexpr auto kEvFiltFs = -9; +static constexpr auto kEvFiltLio = -10; +static constexpr auto kEvFiltUser = -11; +static constexpr auto kEvFiltPolling = -12; +static constexpr auto kEvFiltDisplay = -13; +static constexpr auto kEvFiltGraphicsCore = -14; +static constexpr auto kEvFiltHrTimer = -15; +static constexpr auto kEvFiltUvdTrap = -16; +static constexpr auto kEvFiltVceTrap = -17; +static constexpr auto kEvFiltSdmaTrap = -18; +static constexpr auto kEvFiltRegEv = -19; +static constexpr auto kEvFiltGpuException = -20; +static constexpr auto kEvFiltGpuSystemException = -21; +static constexpr auto kEvFiltGpuDbgGcEv = -22; +static constexpr auto kEvFiltSysCount = 22; + +// actions +static constexpr auto kEvAdd = 0x0001; +static constexpr auto kEvDelete = 0x0002; +static constexpr auto kEvEnable = 0x0004; +static constexpr auto kEvDisable = 0x0008; + +// flags +static constexpr auto kEvOneshot = 0x0010; +static constexpr auto kEvClear = 0x0020; +static constexpr auto kEvReceipt = 0x0040; +static constexpr auto kEvDispatch = 0x0080; +static constexpr auto kEvSysFlags = 0xf000; +static constexpr auto kEvFlag1 = 0x2000; + +static constexpr auto kEvEof = 0x8000; +static constexpr auto kEvError = 0x4000; + +// kEvFiltUser +static constexpr auto kNoteFFNop = 0x00000000; +static constexpr auto kNoteFFAnd = 0x40000000; +static constexpr auto kNoteFFOr = 0x80000000; +static constexpr auto kNoteFFCopy = 0xc0000000; +static constexpr auto kNoteFFCtrlMask = 0xc0000000; +static constexpr auto kNoteFFlagsMask = 0x00ffffff; +static constexpr auto kNoteTrigger = 0x01000000; + +// kEvFiltProc +static constexpr auto kNoteExit = 0x80000000; +static constexpr auto kNoteFork = 0x40000000; +static constexpr auto kNoteExec = 0x20000000; + +struct KEvent { + uintptr_t ident; + sshort filter; + ushort flags; + uint fflags; + intptr_t data; + ptr udata; +}; + +struct KQueue; + +struct KNote { + shared_mutex mutex; + Ref queue; + KEvent event{}; + bool enabled = true; + bool triggered = false; + void *linked = nullptr; // TODO: use Ref<> + + ~KNote(); +}; + +struct KQueue : orbis::File { + shared_cv cv; + std::list> notes; +}; + +struct EventEmitter { + shared_mutex mutex; + std::set, kallocator> notes; + + void emit(uint noteId, intptr_t data = 0) { + std::lock_guard lock(mutex); + + for (auto note : notes) { + if ((note->event.fflags & noteId) == 0) { + continue; + } + + std::lock_guard lock(note->mutex); + + if (note->triggered) { + continue; + } + + note->triggered = true; + note->event.data = data; + note->queue->cv.notify_all(note->queue->mtx); + } + } +}; +} // namespace orbis diff --git a/orbis-kernel/include/orbis/thread/Process.hpp b/orbis-kernel/include/orbis/thread/Process.hpp index 2ebf556ed..55412d2e2 100644 --- a/orbis-kernel/include/orbis/thread/Process.hpp +++ b/orbis-kernel/include/orbis/thread/Process.hpp @@ -1,6 +1,7 @@ #pragma once #include "orbis-config.hpp" +#include "../event.hpp" #include "../evf.hpp" #include "../ipmi.hpp" #include "../osem.hpp" @@ -62,6 +63,7 @@ struct Process final { sint memoryContainer{1}; sint budgetId{1}; bool isInSandbox = false; + EventEmitter event; std::uint64_t nextTlsSlot = 1; std::uint64_t lastTlsOffset = 0; diff --git a/orbis-kernel/src/KernelContext.cpp b/orbis-kernel/src/KernelContext.cpp index dea01d77e..dfe54691b 100644 --- a/orbis-kernel/src/KernelContext.cpp +++ b/orbis-kernel/src/KernelContext.cpp @@ -188,11 +188,11 @@ std::tuple> KernelContext::getUmtxChainIndexed(int i, Thread *t, uint32_t flags, void *ptr) { auto pid = t->tproc->pid; + auto p = reinterpret_cast(ptr); if (flags & 1) { pid = 0; // Process shared (TODO) - ORBIS_LOG_WARNING("Using process-shared umtx", t->tid, ptr); + ORBIS_LOG_WARNING("Using process-shared umtx", t->tid, ptr, (p % 0x4000)); } - auto p = reinterpret_cast(ptr); auto n = p + pid; if (flags & 1) n %= 0x4000; diff --git a/orbis-kernel/src/event.cpp b/orbis-kernel/src/event.cpp new file mode 100644 index 000000000..d528b2653 --- /dev/null +++ b/orbis-kernel/src/event.cpp @@ -0,0 +1,15 @@ +#include "event.hpp" +#include "thread/Process.hpp" + +orbis::KNote::~KNote() { + if (linked == nullptr) { + return; + } + + if (event.filter == kEvFiltProc) { + auto proc = static_cast(linked); + + std::lock_guard lock(proc->event.mutex); + proc->event.notes.erase(this); + } +} diff --git a/orbis-kernel/src/pipe.cpp b/orbis-kernel/src/pipe.cpp index bdc4edc85..de325df9f 100644 --- a/orbis-kernel/src/pipe.cpp +++ b/orbis-kernel/src/pipe.cpp @@ -13,8 +13,6 @@ static orbis::ErrorCode pipe_read(orbis::File *file, orbis::Uio *uio, std::this_thread::sleep_for(std::chrono::seconds(1)); } - std::lock_guard lock(pipe->mtx); - if (pipe->data.empty()) { continue; } @@ -41,7 +39,6 @@ static orbis::ErrorCode pipe_read(orbis::File *file, orbis::Uio *uio, static orbis::ErrorCode pipe_write(orbis::File *file, orbis::Uio *uio, orbis::Thread *thread) { auto pipe = static_cast(file); - std::lock_guard lock(pipe->mtx); for (auto vec : std::span(uio->iov, uio->iovcnt)) { auto offset = pipe->data.size(); diff --git a/orbis-kernel/src/sys/sys_descrip.cpp b/orbis-kernel/src/sys/sys_descrip.cpp index d4288620b..6156ba7de 100644 --- a/orbis-kernel/src/sys/sys_descrip.cpp +++ b/orbis-kernel/src/sys/sys_descrip.cpp @@ -27,6 +27,10 @@ orbis::SysResult orbis::sys_dup(Thread *thread, uint fd) { orbis::SysResult orbis::sys_fcntl(Thread *thread, sint fd, sint cmd, slong arg) { ORBIS_LOG_TODO(__FUNCTION__, fd, cmd, arg); + if (cmd == 0xb) { + // TODO: hack + std::this_thread::sleep_for(std::chrono::seconds(10)); + } return {}; } orbis::SysResult orbis::sys_close(Thread *thread, sint fd) { diff --git a/orbis-kernel/src/sys/sys_event.cpp b/orbis-kernel/src/sys/sys_event.cpp index d2a6ddcc2..d630ba918 100644 --- a/orbis-kernel/src/sys/sys_event.cpp +++ b/orbis-kernel/src/sys/sys_event.cpp @@ -1,87 +1,12 @@ #include "KernelAllocator.hpp" +#include "KernelContext.hpp" #include "sys/sysproto.hpp" #include "thread/Process.hpp" #include "utils/Logs.hpp" -#include "utils/SharedMutex.hpp" #include #include #include -#include - -namespace orbis { -struct KEvent { - uintptr_t ident; - sshort filter; - ushort flags; - uint fflags; - intptr_t data; - ptr udata; -}; - -struct KNote { - KEvent event; - bool enabled; -}; - -struct KQueue : orbis::File { - std::list> notes; -}; - -static constexpr auto kEvFiltRead = -1; -static constexpr auto kEvFiltWrite = -2; -static constexpr auto kEvFiltAio = -3; -static constexpr auto kEvFiltVnode = -4; -static constexpr auto kEvFiltProc = -5; -static constexpr auto kEvFiltSignal = -6; -static constexpr auto kEvFiltTimer = -7; -static constexpr auto kEvFiltFs = -9; -static constexpr auto kEvFiltLio = -10; -static constexpr auto kEvFiltUser = -11; -static constexpr auto kEvFiltPolling = -12; -static constexpr auto kEvFiltDisplay = -13; -static constexpr auto kEvFiltGraphicsCore = -14; -static constexpr auto kEvFiltHrTimer = -15; -static constexpr auto kEvFiltUvdTrap = -16; -static constexpr auto kEvFiltVceTrap = -17; -static constexpr auto kEvFiltSdmaTrap = -18; -static constexpr auto kEvFiltRegEv = -19; -static constexpr auto kEvFiltGpuException = -20; -static constexpr auto kEvFiltGpuSystemException = -21; -static constexpr auto kEvFiltGpuDbgGcEv = -22; -static constexpr auto kEvFiltSysCount = 22; - -// actions -static constexpr auto kEvAdd = 0x0001; -static constexpr auto kEvDelete = 0x0002; -static constexpr auto kEvEnable = 0x0004; -static constexpr auto kEvDisable = 0x0008; - -// flags -static constexpr auto kEvOneshot = 0x0010; -static constexpr auto kEvClear = 0x0020; -static constexpr auto kEvReceipt = 0x0040; -static constexpr auto kEvDispatch = 0x0080; -static constexpr auto kEvSysFlags = 0xf000; -static constexpr auto kEvFlag1 = 0x2000; - -static constexpr auto kEvEof = 0x8000; -static constexpr auto kEvError = 0x4000; - -// kEvFiltUser -static constexpr auto kNoteFFNop = 0x00000000; -static constexpr auto kNoteFFAnd = 0x40000000; -static constexpr auto kNoteFFOr = 0x80000000; -static constexpr auto kNoteFFCopy = 0xc0000000; -static constexpr auto kNoteFFCtrlMask = 0xc0000000; -static constexpr auto kNoteFFlagsMask = 0x00ffffff; -static constexpr auto kNoteTrigger = 0x01000000; - -// kEvFiltProc -static constexpr auto kNoteExit = 0x80000000; -static constexpr auto kNoteFork = 0x40000000; -static constexpr auto kNoteExec = 0x20000000; -} // namespace orbis orbis::SysResult orbis::sys_kqueue(Thread *thread) { auto queue = knew(); @@ -127,16 +52,28 @@ static SysResult keventChange(KQueue *kq, KEvent &change) { nodeIt = kq->notes.end(); } + std::unique_lock noteLock; if (change.flags & kEvAdd) { if (nodeIt == kq->notes.end()) { - KNote note{ - .event = change, - .enabled = true, - }; - + auto ¬e = kq->notes.emplace_front(); note.event.flags &= ~(kEvAdd | kEvDelete | kEvDisable | kEvEnable); - kq->notes.push_front(note); + note.queue = kq; + note.event = change; + note.enabled = true; nodeIt = kq->notes.begin(); + + if (change.filter == kEvFiltProc) { + auto process = g_context.findProcessById(change.ident); + if (process == nullptr) { + return ErrorCode::SRCH; + } + + noteLock = std::unique_lock(nodeIt->mutex); + + std::unique_lock lock(process->event.mutex); + process->event.notes.insert(&*nodeIt); + nodeIt->linked = process; + } } } @@ -148,12 +85,19 @@ static SysResult keventChange(KQueue *kq, KEvent &change) { return orbis::ErrorCode::NOENT; } + if (!noteLock.owns_lock()) { + noteLock = std::unique_lock(nodeIt->mutex); + } + if (change.flags & kEvDisable) { nodeIt->enabled = false; } if (change.flags & kEvEnable) { nodeIt->enabled = true; } + if (change.flags & kEvClear) { + nodeIt->triggered = false; + } if (change.filter == kEvFiltUser) { auto fflags = 0; @@ -173,12 +117,13 @@ static SysResult keventChange(KQueue *kq, KEvent &change) { (nodeIt->event.fflags & ~kNoteFFlagsMask) | (fflags & kNoteFFlagsMask); if (change.fflags & kNoteTrigger) { - nodeIt->event.fflags |= kNoteTrigger; - } - - if (change.flags & kEvClear) { - nodeIt->event.fflags &= ~kNoteTrigger; + nodeIt->triggered = true; + kq->cv.notify_all(kq->mtx); } + } else if (change.filter == kEvFiltGraphicsCore || + change.filter == kEvFiltDisplay) { + nodeIt->triggered = true; + kq->cv.notify_all(kq->mtx); } return {}; @@ -203,26 +148,22 @@ orbis::SysResult orbis::sys_kevent(Thread *thread, sint fd, ptr timeout) { // ORBIS_LOG_TODO(__FUNCTION__, fd, changelist, nchanges, eventlist, nevents, // timeout); - Ref kqf = thread->tproc->fileDescriptors.get(fd); - if (kqf == nullptr) { - return orbis::ErrorCode::BADF; - } - - auto kq = dynamic_cast(kqf.get()); - + auto kq = thread->tproc->fileDescriptors.get(fd).cast(); if (kq == nullptr) { return orbis::ErrorCode::BADF; } { - std::lock_guard lock(kqf->mtx); + std::lock_guard lock(kq->mtx); if (nchanges != 0) { - for (auto change : std::span(changelist, nchanges)) { + for (auto &changePtr : std::span(changelist, nchanges)) { + KEvent change; + ORBIS_RET_ON_ERROR(uread(change, &changePtr)); ORBIS_LOG_TODO(__FUNCTION__, change.ident, change.filter, change.flags, change.fflags, change.data, change.udata); - if (auto result = keventChange(kq, change); result.value() != 0) { + if (auto result = keventChange(kq.get(), change); result.value() != 0) { return result; } } @@ -259,51 +200,25 @@ orbis::SysResult orbis::sys_kevent(Thread *thread, sint fd, std::vector result; result.reserve(nevents); - while (result.empty()) { - { - std::lock_guard lock(kqf->mtx); - for (auto it = kq->notes.begin(); it != kq->notes.end();) { - if (result.size() >= nevents) { - break; - } + while (true) { + std::lock_guard lock(kq->mtx); + for (auto it = kq->notes.begin(); it != kq->notes.end();) { + if (result.size() >= nevents) { + break; + } - auto ¬e = *it; + auto ¬e = *it; - if (!note.enabled) { - ++it; - continue; - } - - if (note.event.filter == kEvFiltUser) { - if ((note.event.fflags & kNoteTrigger) == 0) { - ++it; - continue; - } - - auto event = note.event; - event.fflags &= kNoteFFlagsMask; - result.push_back(event); - } else if (note.event.filter == kEvFiltDisplay || - note.event.filter == kEvFiltGraphicsCore) { - result.push_back(note.event); - } else if (note.event.filter == kEvFiltProc) { - // TODO - if (note.event.fflags & kNoteExec) { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - note.event.data = 0; - result.push_back(note.event); - } - } else { - ++it; - continue; - } + if (note.enabled && note.triggered) { + result.push_back(note.event); if (note.event.flags & kEvOneshot) { it = kq->notes.erase(it); - } else { - ++it; + continue; } } + + ++it; } if (!result.empty()) { @@ -311,15 +226,24 @@ orbis::SysResult orbis::sys_kevent(Thread *thread, sint fd, } if (timeoutPoint != clock::time_point::max()) { - if (clock::now() >= timeoutPoint) { + auto now = clock::now(); + + if (now >= timeoutPoint) { break; } - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + auto waitTimeout = std::chrono::duration_cast( + timeoutPoint - now); + kq->cv.wait(kq->mtx, waitTimeout.count()); + } else { + kq->cv.wait(kq->mtx); + } } - std::memcpy(eventlist, result.data(), result.size() * sizeof(KEvent)); + // ORBIS_LOG_TODO(__FUNCTION__, "kevent wakeup", fd); + + ORBIS_RET_ON_ERROR( + uwriteRaw(eventlist, result.data(), result.size() * sizeof(KEvent))); thread->retval[0] = result.size(); return {}; }