[orbis-kernel] implement kevent proc filter

fix pipe deadlock
This commit is contained in:
DH 2023-11-13 21:36:25 +03:00
parent 5e9074d391
commit 1a9b0ed0b5
8 changed files with 204 additions and 144 deletions

View file

@ -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

View file

@ -0,0 +1,117 @@
#pragma once
#include "file.hpp"
#include "orbis-config.hpp"
#include "utils/SharedCV.hpp"
#include "utils/SharedMutex.hpp"
#include <list>
#include <mutex>
#include <set>
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<void> udata;
};
struct KQueue;
struct KNote {
shared_mutex mutex;
Ref<KQueue> 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<KNote, kallocator<KNote>> notes;
};
struct EventEmitter {
shared_mutex mutex;
std::set<KNote *, std::less<>, kallocator<KNote *>> 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

View file

@ -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;

View file

@ -188,11 +188,11 @@ std::tuple<UmtxChain &, UmtxKey, std::unique_lock<shared_mutex>>
KernelContext::getUmtxChainIndexed(int i, Thread *t, uint32_t flags,
void *ptr) {
auto pid = t->tproc->pid;
auto p = reinterpret_cast<std::uintptr_t>(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<std::uintptr_t>(ptr);
auto n = p + pid;
if (flags & 1)
n %= 0x4000;

View file

@ -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<Process *>(linked);
std::lock_guard lock(proc->event.mutex);
proc->event.notes.erase(this);
}
}

View file

@ -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<orbis::Pipe *>(file);
std::lock_guard lock(pipe->mtx);
for (auto vec : std::span(uio->iov, uio->iovcnt)) {
auto offset = pipe->data.size();

View file

@ -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) {

View file

@ -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 <chrono>
#include <list>
#include <span>
#include <thread>
namespace orbis {
struct KEvent {
uintptr_t ident;
sshort filter;
ushort flags;
uint fflags;
intptr_t data;
ptr<void> udata;
};
struct KNote {
KEvent event;
bool enabled;
};
struct KQueue : orbis::File {
std::list<KNote, kallocator<KNote>> 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<KQueue>();
@ -127,16 +52,28 @@ static SysResult keventChange(KQueue *kq, KEvent &change) {
nodeIt = kq->notes.end();
}
std::unique_lock<shared_mutex> noteLock;
if (change.flags & kEvAdd) {
if (nodeIt == kq->notes.end()) {
KNote note{
.event = change,
.enabled = true,
};
auto &note = 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<const timespec> timeout) {
// ORBIS_LOG_TODO(__FUNCTION__, fd, changelist, nchanges, eventlist, nevents,
// timeout);
Ref<File> kqf = thread->tproc->fileDescriptors.get(fd);
if (kqf == nullptr) {
return orbis::ErrorCode::BADF;
}
auto kq = dynamic_cast<KQueue *>(kqf.get());
auto kq = thread->tproc->fileDescriptors.get(fd).cast<KQueue>();
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<KEvent> 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 &note = *it;
auto &note = *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<std::chrono::microseconds>(
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 {};
}