mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-02-03 22:34:19 +01:00
[orbis-kernel] implement kevent proc filter
fix pipe deadlock
This commit is contained in:
parent
5e9074d391
commit
1a9b0ed0b5
|
|
@ -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
|
||||
|
|
|
|||
117
orbis-kernel/include/orbis/event.hpp
Normal file
117
orbis-kernel/include/orbis/event.hpp
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
15
orbis-kernel/src/event.cpp
Normal file
15
orbis-kernel/src/event.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 ¬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<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 ¬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<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 {};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue