diff --git a/orbis-kernel/include/orbis/thread/Process.hpp b/orbis-kernel/include/orbis/thread/Process.hpp index a6c949239..54c689d1f 100644 --- a/orbis-kernel/include/orbis/thread/Process.hpp +++ b/orbis-kernel/include/orbis/thread/Process.hpp @@ -8,6 +8,7 @@ #include "../thread/Thread.hpp" #include "../thread/types.hpp" #include "ProcessState.hpp" +#include "cpuset.hpp" #include "orbis/AppInfo.hpp" #include "orbis/AuthInfo.hpp" #include "orbis/file.hpp" @@ -64,6 +65,7 @@ struct Process final { AuthInfo authInfo{}; kstring cwd; kstring root = "/"; + cpuset affinity{(1 << 7) - 1}; sint memoryContainer{1}; sint budgetId{1}; bool isInSandbox = false; diff --git a/orbis-kernel/include/orbis/thread/ProcessOps.hpp b/orbis-kernel/include/orbis/thread/ProcessOps.hpp index 71508bf27..ecb36a8ac 100644 --- a/orbis-kernel/include/orbis/thread/ProcessOps.hpp +++ b/orbis-kernel/include/orbis/thread/ProcessOps.hpp @@ -87,5 +87,8 @@ struct ProcessOps { SysResult (*registerEhFrames)(Thread *thread); void (*where)(Thread *); + + void (*unblock)(Thread *); + void (*block)(Thread *); }; } // namespace orbis diff --git a/orbis-kernel/include/orbis/thread/Thread.hpp b/orbis-kernel/include/orbis/thread/Thread.hpp index 84418cfb6..097037cb3 100644 --- a/orbis-kernel/include/orbis/thread/Thread.hpp +++ b/orbis-kernel/include/orbis/thread/Thread.hpp @@ -1,53 +1,76 @@ #pragma once #include "ThreadState.hpp" +#include "cpuset.hpp" #include "orbis-config.hpp" #include "types.hpp" #include "../KernelAllocator.hpp" #include "../ucontext.hpp" +#include "../utils/SharedAtomic.hpp" #include "../utils/SharedCV.hpp" #include "../utils/SharedMutex.hpp" -#include #include namespace orbis { struct Process; +static constexpr std::uint32_t kThreadSuspendFlag = 1 << 31; struct Thread { utils::shared_mutex mtx; Process *tproc = nullptr; uint64_t retval[2]{}; void *context{}; + kvector altStack; ptr stackStart; ptr stackEnd; uint64_t fsBase{}; uint64_t gsBase{}; char name[32]{}; + cpuset affinity{~0u}; SigSet sigMask = {0x7fff'ffff, ~0u, ~0u, ~0u}; + rtprio prio{ + .type = 2, + .prio = 10, + }; utils::shared_mutex suspend_mtx; utils::shared_cv suspend_cv; - kdeque signalQueue; kvector sigReturns; - std::atomic suspended{0}; + kvector blockedSignals; + kvector queuedSignals; + shared_atomic32 suspendFlags{0}; std::int64_t hostTid = -1; lwpid_t tid = -1; + unsigned unblocked = 0; ThreadState state = ThreadState::INACTIVE; std::thread handle; + std::thread::native_handle_type nativeHandle; // Used to wake up thread in sleep queue utils::shared_cv sync_cv; uint64_t evfResultPattern; uint64_t evfIsCancelled; + [[nodiscard]] std::thread::native_handle_type getNativeHandle() { + if (handle.joinable()) { + return handle.native_handle(); + } + + return nativeHandle; + } + // Print backtrace void where(); + void unblock(); + void block(); + void suspend(); void resume(); void sendSignal(int signo); + void notifyUnblockedSignal(int signo); // FIXME: implement thread destruction void incRef() {} @@ -55,4 +78,31 @@ struct Thread { }; extern thread_local Thread *g_currentThread; + +struct scoped_unblock { + scoped_unblock(); + ~scoped_unblock(); + + scoped_unblock(const scoped_unblock &) = delete; +}; + +class scoped_unblock_now { + bool unblocked = false; + +public: + scoped_unblock_now() { + if (g_currentThread && g_currentThread->context) { + g_currentThread->unblock(); + unblocked = true; + } + } + + ~scoped_unblock_now() { + if (unblocked) { + g_currentThread->block(); + } + } + + scoped_unblock_now(const scoped_unblock_now &) = delete; +}; } // namespace orbis diff --git a/orbis-kernel/include/orbis/thread/cpuset.hpp b/orbis-kernel/include/orbis/thread/cpuset.hpp index 4e417ce92..1821b04f3 100644 --- a/orbis-kernel/include/orbis/thread/cpuset.hpp +++ b/orbis-kernel/include/orbis/thread/cpuset.hpp @@ -3,10 +3,7 @@ #include "orbis-config.hpp" namespace orbis { -static constexpr auto NCPUBITS = sizeof(slong) * 8; -static constexpr auto NCPUWORDS = 128 / NCPUBITS; - struct cpuset { - slong bits[NCPUWORDS]; + uint bits; }; } // namespace orbis diff --git a/orbis-kernel/include/orbis/utils/SharedAtomic.hpp b/orbis-kernel/include/orbis/utils/SharedAtomic.hpp index 326c206cd..890b0936d 100644 --- a/orbis-kernel/include/orbis/utils/SharedAtomic.hpp +++ b/orbis-kernel/include/orbis/utils/SharedAtomic.hpp @@ -23,6 +23,8 @@ static constexpr auto kRelaxSpinCount = 12; static constexpr auto kSpinCount = 16; inline namespace utils { +inline thread_local void (*g_scopedUnblock)(bool) = nullptr; + bool try_spin_wait(auto &&pred) { for (std::size_t i = 0; i < kSpinCount; ++i) { if (pred()) { @@ -58,8 +60,8 @@ struct shared_atomic32 : std::atomic { using atomic::operator=; template - [[nodiscard]] std::errc wait(std::uint32_t oldValue, - std::chrono::time_point timeout) { + std::errc wait(std::uint32_t oldValue, + std::chrono::time_point timeout) { if (try_spin_wait( [&] { return load(std::memory_order::acquire) != oldValue; })) { return {}; @@ -76,12 +78,12 @@ struct shared_atomic32 : std::atomic { std::chrono::duration_cast(timeout - now)); } - [[nodiscard]] std::errc wait(std::uint32_t oldValue, - std::chrono::microseconds usec_timeout) { + std::errc wait(std::uint32_t oldValue, + std::chrono::microseconds usec_timeout) { return wait_impl(oldValue, usec_timeout); } - [[nodiscard]] std::errc wait(std::uint32_t oldValue) { + std::errc wait(std::uint32_t oldValue) { if (try_spin_wait( [&] { return load(std::memory_order::acquire) != oldValue; })) { return {}; diff --git a/orbis-kernel/src/KernelContext.cpp b/orbis-kernel/src/KernelContext.cpp index 89262bd83..7e030fe76 100644 --- a/orbis-kernel/src/KernelContext.cpp +++ b/orbis-kernel/src/KernelContext.cpp @@ -2,6 +2,7 @@ #include "orbis/thread/Process.hpp" #include "orbis/thread/ProcessOps.hpp" #include "orbis/utils/Logs.hpp" +#include "utils/SharedAtomic.hpp" #include #include #include @@ -314,16 +315,42 @@ void log_class_string::format(std::string &out, const void *arg) { } // namespace logs void Thread::suspend() { sendSignal(-1); } - void Thread::resume() { sendSignal(-2); } void Thread::sendSignal(int signo) { - std::lock_guard lock(mtx); - signalQueue.push_back(signo); - if (::tgkill(tproc->hostPid, hostTid, SIGUSR1) < 0) { - perror("tgkill"); + if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) { + perror("pthread_sigqueue"); + } +} + +void Thread::notifyUnblockedSignal(int signo) { + for (std::size_t i = 0; i < blockedSignals.size();) { + if (blockedSignals[i].signo != signo) { + ++i; + continue; + } + + queuedSignals.push_back(blockedSignals[i]); + blockedSignals.erase(blockedSignals.begin() + i); } } void Thread::where() { tproc->ops->where(this); } + +void Thread::unblock() { tproc->ops->unblock(this); } +void Thread::block() { tproc->ops->block(this); } + +scoped_unblock::scoped_unblock() { + if (g_currentThread && g_currentThread->context) { + g_scopedUnblock = [](bool unblock) { + if (unblock) { + g_currentThread->unblock(); + } else { + g_currentThread->block(); + } + }; + } +} + +scoped_unblock::~scoped_unblock() { g_scopedUnblock = nullptr; } } // namespace orbis diff --git a/orbis-kernel/src/evf.cpp b/orbis-kernel/src/evf.cpp index 83cd57209..2612078c1 100644 --- a/orbis-kernel/src/evf.cpp +++ b/orbis-kernel/src/evf.cpp @@ -77,11 +77,14 @@ orbis::ErrorCode orbis::EventFlag::wait(Thread *thread, std::uint8_t waitMode, waitingThreads.emplace_back(waitingThread); - if (timeout) { - result = toErrorCode(thread->sync_cv.wait(queueMtx, *timeout)); - update_timeout(); - } else { - result = toErrorCode(thread->sync_cv.wait(queueMtx)); + { + orbis::scoped_unblock unblock; + if (timeout) { + result = toErrorCode(thread->sync_cv.wait(queueMtx, *timeout)); + update_timeout(); + } else { + result = toErrorCode(thread->sync_cv.wait(queueMtx)); + } } if (thread->evfIsCancelled == UINT64_MAX) { diff --git a/orbis-kernel/src/ipmi.cpp b/orbis-kernel/src/ipmi.cpp index 88dae98ff..3271f1dba 100644 --- a/orbis-kernel/src/ipmi.cpp +++ b/orbis-kernel/src/ipmi.cpp @@ -1,6 +1,7 @@ #include "ipmi.hpp" #include "KernelContext.hpp" #include "thread/Process.hpp" +#include "thread/Thread.hpp" #include "utils/Logs.hpp" #include #include @@ -243,6 +244,7 @@ orbis::SysResult orbis::sysIpmiServerReceivePacket(Thread *thread, { std::lock_guard lock(server->mutex); while (server->packets.empty()) { + orbis::scoped_unblock unblock; server->receiveCv.wait(server->mutex); } @@ -621,6 +623,7 @@ orbis::SysResult orbis::sysIpmiClientTryGetResult(Thread *thread, return uwrite(result, 0u); } + orbis::scoped_unblock unblock; client->asyncResponseCv.wait(client->mutex); } @@ -684,7 +687,11 @@ orbis::SysResult orbis::sysIpmiClientGetMessage(Thread *thread, auto waitTime = std::chrono::duration_cast( timeoutPoint - now); - queue.messageCv.wait(client->mutex, waitTime.count()); + + { + orbis::scoped_unblock unblock; + queue.messageCv.wait(client->mutex, waitTime.count()); + } if (!queue.messages.empty()) { now = clock::now(); @@ -704,6 +711,7 @@ orbis::SysResult orbis::sysIpmiClientGetMessage(Thread *thread, } } else { while (queue.messages.empty()) { + orbis::scoped_unblock unblock; queue.messageCv.wait(client->mutex); } } @@ -966,7 +974,10 @@ orbis::sysIpmiClientInvokeSyncMethod(Thread *thread, ptr result, uint kid, IpmiSession::SyncResponse response; while (true) { - session->responseCv.wait(session->mutex); + { + orbis::scoped_unblock unblock; + session->responseCv.wait(session->mutex); + } bool found = false; for (auto it = session->syncResponses.begin(); @@ -1131,10 +1142,12 @@ orbis::SysResult orbis::sysIpmiClientConnect(Thread *thread, ptr result, } while (client->session == nullptr && !client->connectionStatus) { + orbis::scoped_unblock unblock; client->sessionCv.wait(client->mutex); } while (!client->connectionStatus) { + orbis::scoped_unblock unblock; client->connectCv.wait(client->mutex); } @@ -1287,8 +1300,13 @@ orbis::SysResult orbis::sysIpmiClientWaitEventFlag(Thread *thread, } auto &evf = client->eventFlags[_params.index]; - auto waitResult = evf.wait(thread, _params.mode, _params.patternSet, - _params.pTimeout != 0 ? &resultTimeout : nullptr); + ErrorCode waitResult; + + { + orbis::scoped_unblock unblock; + waitResult = evf.wait(thread, _params.mode, _params.patternSet, + _params.pTimeout != 0 ? &resultTimeout : nullptr); + } if (_params.pPatternSet != nullptr) { ORBIS_RET_ON_ERROR(uwrite(_params.pPatternSet, thread->evfResultPattern)); diff --git a/orbis-kernel/src/sys/sys_cpuset.cpp b/orbis-kernel/src/sys/sys_cpuset.cpp index bbb8cbfb1..d651bb5f1 100644 --- a/orbis-kernel/src/sys/sys_cpuset.cpp +++ b/orbis-kernel/src/sys/sys_cpuset.cpp @@ -1,4 +1,50 @@ +#include "KernelContext.hpp" #include "sys/sysproto.hpp" +#include "thread/Process.hpp" +#include "thread/Thread.hpp" +#include "thread/cpuset.hpp" +#include "utils/Logs.hpp" + +#include +#include +#include +#include + +enum class CpuLevel { + Root = 1, + CpuSet = 2, + Which = 3, +}; + +enum class CpuWhich { + Tid = 1, + Pid = 2, + CpuSet = 3, + Irq = 4, + Jail = 5, +}; + +static cpu_set_t toHostCpuSet(orbis::cpuset cpuSet) { + const int procCount = get_nprocs(); + cpu_set_t result{}; + + for (unsigned cpu = std::countr_zero(cpuSet.bits); + cpu < sizeof(cpuSet.bits) * 8; + cpu = std::countr_zero(cpuSet.bits >> (cpu + 1)) + cpu + 1) { + unsigned hostCpu = cpu; + if (procCount < 8) { + hostCpu = cpu % procCount; + } else if (procCount >= 8 * 2) { + hostCpu = cpu * 2; + } + + ORBIS_LOG_ERROR(__FUNCTION__, cpu, hostCpu); + CPU_SET(hostCpu, &result); + } + + ORBIS_LOG_ERROR(__FUNCTION__, procCount, result.__bits[0], cpuSet.bits); + return result; +} orbis::SysResult orbis::sys_cpuset(Thread *thread, ptr setid) { return ErrorCode::NOSYS; @@ -16,11 +62,133 @@ orbis::SysResult orbis::sys_cpuset_getaffinity(Thread *thread, cpulevel_t level, cpuwhich_t which, id_t id, size_t cpusetsize, ptr mask) { - return {}; + if (cpusetsize != sizeof(cpuset)) { + return ErrorCode::INVAL; + } + + std::lock_guard lock(thread->mtx); + std::lock_guard lockProc(thread->tproc->mtx); + + switch (CpuLevel{level}) { + case CpuLevel::Root: + case CpuLevel::CpuSet: + ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize); + return ErrorCode::INVAL; + + case CpuLevel::Which: + switch (CpuWhich(which)) { + case CpuWhich::Tid: { + Thread *whichThread = nullptr; + if (id == ~id_t(0) || thread->tid == id) { + whichThread = thread; + } else { + whichThread = thread->tproc->threadsMap.get(id - thread->tproc->pid); + if (whichThread == nullptr) { + ORBIS_LOG_ERROR(__FUNCTION__, "thread not found", level, which, id, + cpusetsize); + return ErrorCode::SRCH; + } + } + + return uwrite(mask, whichThread->affinity); + } + case CpuWhich::Pid: { + Process *whichProcess = nullptr; + if (id == ~id_t(0) || id == thread->tproc->pid) { + whichProcess = thread->tproc; + } else { + whichProcess = g_context.findProcessById(id); + + if (whichProcess == nullptr) { + return ErrorCode::SRCH; + } + } + + return uwrite(mask, whichProcess->affinity); + } + case CpuWhich::CpuSet: + case CpuWhich::Irq: + case CpuWhich::Jail: + ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize); + return ErrorCode::INVAL; + } + break; + } + + return ErrorCode::INVAL; } orbis::SysResult orbis::sys_cpuset_setaffinity(Thread *thread, cpulevel_t level, cpuwhich_t which, id_t id, size_t cpusetsize, ptr mask) { - return {}; + std::lock_guard lock(thread->mtx); + std::lock_guard lockProc(thread->tproc->mtx); + switch (CpuLevel{level}) { + case CpuLevel::Root: + case CpuLevel::CpuSet: + ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize); + return ErrorCode::INVAL; + + case CpuLevel::Which: + switch (CpuWhich(which)) { + case CpuWhich::Tid: { + Thread *whichThread = nullptr; + if (id == ~id_t(0) || thread->tid == id) { + whichThread = thread; + } else { + whichThread = thread->tproc->threadsMap.get(id - thread->tproc->pid); + if (whichThread == nullptr) { + ORBIS_LOG_ERROR(__FUNCTION__, "thread not found", level, which, id, + cpusetsize); + return ErrorCode::SRCH; + } + } + + ORBIS_RET_ON_ERROR(uread(whichThread->affinity, mask)); + auto threadHandle = whichThread->getNativeHandle(); + auto hostCpuSet = toHostCpuSet(whichThread->affinity); + ORBIS_LOG_ERROR(__FUNCTION__, threadHandle, thread->tid, id); + if (pthread_setaffinity_np(threadHandle, sizeof(hostCpuSet), &hostCpuSet)) { + ORBIS_LOG_ERROR(__FUNCTION__, + "failed to set affinity mask for host thread", + whichThread->hostTid, whichThread->affinity.bits); + } + return {}; + } + case CpuWhich::Pid: { + Process *whichProcess = nullptr; + if (id == ~id_t(0) || id == thread->tproc->pid) { + whichProcess = thread->tproc; + } else { + ORBIS_LOG_ERROR(__FUNCTION__, "process not found", level, which, id, + cpusetsize); + whichProcess = g_context.findProcessById(id); + + if (whichProcess == nullptr) { + return ErrorCode::SRCH; + } + } + + ORBIS_RET_ON_ERROR(uread(whichProcess->affinity, mask)); + auto hostCpuSet = toHostCpuSet(whichProcess->affinity); + + if (sched_setaffinity(whichProcess->hostPid, sizeof(hostCpuSet), + &hostCpuSet)) { + ORBIS_LOG_ERROR(__FUNCTION__, + "failed to set affinity mask for host process", + whichProcess->hostPid, whichProcess->affinity.bits); + } + + return {}; + } + case CpuWhich::CpuSet: + case CpuWhich::Irq: + case CpuWhich::Jail: + ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize); + return ErrorCode::INVAL; + } + break; + } + + return ErrorCode::INVAL; } diff --git a/orbis-kernel/src/sys/sys_generic.cpp b/orbis-kernel/src/sys/sys_generic.cpp index cc8a81895..cd0c44676 100644 --- a/orbis-kernel/src/sys/sys_generic.cpp +++ b/orbis-kernel/src/sys/sys_generic.cpp @@ -450,7 +450,15 @@ orbis::SysResult orbis::sys_select(Thread *thread, sint nd, ptr out, ptr ex, ptr tv) { - return ErrorCode::NOSYS; + if (tv == nullptr) { + orbis::scoped_unblock_now unblock; + std::this_thread::sleep_for(std::chrono::days(1)); + } else { + std::this_thread::sleep_for(std::chrono::seconds(tv->tv_sec)); + return orbis::ErrorCode::TIMEDOUT; + } + + return {}; } orbis::SysResult orbis::sys_poll(Thread *thread, ptr fds, uint nfds, sint timeout) { diff --git a/orbis-kernel/src/sys/sys_resource.cpp b/orbis-kernel/src/sys/sys_resource.cpp index 5bb10ba9c..b59bbef0b 100644 --- a/orbis-kernel/src/sys/sys_resource.cpp +++ b/orbis-kernel/src/sys/sys_resource.cpp @@ -1,6 +1,8 @@ #include "sys/sysproto.hpp" +#include "thread/Process.hpp" #include "thread/Thread.hpp" #include "utils/Logs.hpp" +#include namespace orbis { struct rlimit { @@ -21,9 +23,54 @@ orbis::SysResult orbis::sys_rtprio_thread(Thread *thread, sint function, ptr rtp) { ORBIS_LOG_ERROR(__FUNCTION__, function, lwpid, rtp->prio, rtp->type); thread->where(); + + Thread *targetThread; + if (lwpid == thread->tid || lwpid == -1) { + targetThread = thread; + } else { + targetThread = thread->tproc->threadsMap.get(lwpid - thread->tproc->pid); + if (targetThread == nullptr) { + return ErrorCode::SRCH; + } + } if (function == 0) { - rtp->type = 2; - rtp->prio = 10; + return orbis::uwrite(rtp, targetThread->prio); + } else if (function == 1) { + ORBIS_RET_ON_ERROR(orbis::uread(targetThread->prio, rtp)); + + int hostPolicy = SCHED_RR; + auto prioMin = sched_get_priority_min(hostPolicy); + auto prioMax = sched_get_priority_max(hostPolicy); + auto hostPriority = + (targetThread->prio.prio * (prioMax - prioMin + 1)) / 1000 - prioMin; + ::sched_param hostParam{}; + hostParam.sched_priority = hostPriority; + if (pthread_setschedparam(targetThread->getNativeHandle(), hostPolicy, + &hostParam)) { + + auto normPrio = targetThread->prio.prio / 1000.f; + hostParam.sched_priority = 0; + + if (normPrio < 0.3f) { + hostPolicy = SCHED_BATCH; + } else if (normPrio < 0.7f) { + hostPolicy = SCHED_OTHER; + } else { + hostPolicy = SCHED_IDLE; + } + + if (pthread_setschedparam(targetThread->getNativeHandle(), + hostPolicy, &hostParam)) { + ORBIS_LOG_ERROR(__FUNCTION__, "failed to set host priority", + hostPriority, targetThread->prio.prio, + targetThread->prio.type, errno, + targetThread->getNativeHandle(), prioMin, prioMax, errno); + } + } else { + ORBIS_LOG_ERROR(__FUNCTION__, "set host priority", hostPriority, + targetThread->tid, targetThread->prio.prio, + targetThread->prio.type, prioMin, prioMax); + } } return {}; } diff --git a/orbis-kernel/src/sys/sys_sce.cpp b/orbis-kernel/src/sys/sys_sce.cpp index c4be2ecc3..f2e4cb8b3 100644 --- a/orbis-kernel/src/sys/sys_sce.cpp +++ b/orbis-kernel/src/sys/sys_sce.cpp @@ -15,9 +15,9 @@ #include "uio.hpp" #include "utils/Logs.hpp" #include +#include #include #include -#include orbis::SysResult orbis::sys_netcontrol(Thread *thread, sint fd, uint op, ptr buf, uint nbuf) { @@ -576,6 +576,8 @@ orbis::SysResult orbis::sys_osem_wait(Thread *thread, sint id, sint need, break; } } + + orbis::scoped_unblock unblock; sem->cond.wait(sem->mtx, ut); } @@ -1158,10 +1160,17 @@ orbis::SysResult orbis::sys_get_resident_fmem_count(Thread *thread, pid_t pid) { orbis::SysResult orbis::sys_thr_get_name(Thread *thread, lwpid_t lwpid, char *buf, size_t buflen) { Thread *searchThread; - if (thread->tid == lwpid) { + if (thread->tid == lwpid || lwpid == -1) { searchThread = thread; } else { - searchThread = thread->tproc->threadsMap.get(lwpid); + searchThread = thread->tproc->threadsMap.get(lwpid - thread->tproc->pid); + + if (searchThread == nullptr) { + if (auto process = g_context.findProcessById(lwpid)) { + searchThread = process->threadsMap.get(lwpid - process->pid); + } + } + if (searchThread == nullptr) { return ErrorCode::SRCH; } @@ -1169,11 +1178,13 @@ orbis::SysResult orbis::sys_thr_get_name(Thread *thread, lwpid_t lwpid, auto namelen = std::strlen(searchThread->name); - if (namelen >= buflen) { - return ErrorCode::INVAL; + auto writeLen = std::min(namelen + 1, buflen); + if (writeLen > 0) { + ORBIS_RET_ON_ERROR(uwriteRaw(buf, searchThread->name, writeLen - 1)); + buf[writeLen] = 0; } - ORBIS_RET_ON_ERROR(uwriteRaw(buf, searchThread->name, namelen)); + thread->retval[0] = writeLen > 0 ? writeLen : namelen + 1; return {}; } orbis::SysResult orbis::sys_set_gpo(Thread *thread /* TODO */) { @@ -1318,78 +1329,77 @@ orbis::SysResult orbis::sys_resume_internal_hdd(Thread *thread /* TODO */) { return ErrorCode::NOSYS; } orbis::SysResult orbis::sys_thr_suspend_ucontext(Thread *thread, lwpid_t tid) { - auto t = tid == thread->tid ? thread - : thread->tproc->threadsMap.get(tid % 10000 - 1); + auto t = tid == thread->tid + ? thread + : thread->tproc->threadsMap.get(tid - thread->tproc->pid); if (t == nullptr) { return ErrorCode::SRCH; } - while (true) { - unsigned prevSuspend = 0; - if (t->suspended.compare_exchange_strong(prevSuspend, 1)) { - t->suspended.fetch_sub(1); - t->suspend(); + ORBIS_LOG_NOTICE(__FUNCTION__, tid); - while (t->suspended == 0) { - std::this_thread::yield(); - } + auto prevValue = t->suspendFlags.fetch_add(1, std::memory_order::relaxed); - break; - } - - if (t->suspended.compare_exchange_strong(prevSuspend, prevSuspend + 1)) { - break; - } + while ((prevValue & kThreadSuspendFlag) == 0) { + t->suspend(); + t->suspendFlags.wait(prevValue); + prevValue = t->suspendFlags.load(std::memory_order::relaxed); } return {}; } orbis::SysResult orbis::sys_thr_resume_ucontext(Thread *thread, lwpid_t tid) { - auto t = tid == thread->tid ? thread - : thread->tproc->threadsMap.get(tid % 10000 - 1); + auto t = tid == thread->tid + ? thread + : thread->tproc->threadsMap.get(tid - thread->tproc->pid); if (t == nullptr) { return ErrorCode::SRCH; } - while (true) { - unsigned prevSuspend = t->suspended.load(); - if (t->suspended == prevSuspend) { - t->resume(); - break; - } + ORBIS_LOG_NOTICE(__FUNCTION__, tid); - if (prevSuspend == 0) { + auto result = t->suspendFlags.op([](unsigned &value) -> ErrorCode { + if ((value & kThreadSuspendFlag) == 0) { return ErrorCode::INVAL; } - if (t->suspended.compare_exchange_strong(prevSuspend, prevSuspend - 1)) { - break; + if ((value & ~kThreadSuspendFlag) == 0) { + return ErrorCode::INVAL; } + + --value; + return {}; + }); + + if (result == ErrorCode{}) { + t->suspendFlags.notify_all(); } - return {}; + + return result; } orbis::SysResult orbis::sys_thr_get_ucontext(Thread *thread, lwpid_t tid, ptr context) { - auto t = tid == thread->tid ? thread - : thread->tproc->threadsMap.get(tid % 10000 - 1); + auto t = tid == thread->tid + ? thread + : thread->tproc->threadsMap.get(tid - thread->tproc->pid); if (t == nullptr) { return ErrorCode::SRCH; } std::lock_guard lock(t->mtx); - if (t->suspended == 0) { + if ((t->suspendFlags.load() & kThreadSuspendFlag) == 0) { return ErrorCode::INVAL; } - for (auto it = t->sigReturns.rbegin(); it != t->sigReturns.rend(); ++it) { - auto &savedContext = *it; - if (savedContext.mcontext.rip < 0x100'0000'0000) { + for (auto &savedContext : std::ranges::reverse_view(t->sigReturns)) { + if (savedContext.mcontext.rip < orbis::kMaxAddress) { return uwrite(context, savedContext); } } ORBIS_LOG_FATAL(__FUNCTION__, tid, "not found guest context"); + std::abort(); *context = {}; return {}; } diff --git a/orbis-kernel/src/sys/sys_sig.cpp b/orbis-kernel/src/sys/sys_sig.cpp index fac5550e1..7cdee71cb 100644 --- a/orbis-kernel/src/sys/sys_sig.cpp +++ b/orbis-kernel/src/sys/sys_sig.cpp @@ -32,6 +32,8 @@ orbis::SysResult orbis::sys_sigaction(Thread *thread, sint sig, orbis::SysResult orbis::sys_sigprocmask(Thread *thread, sint how, ptr set, ptr oset) { + std::lock_guard lock(thread->mtx); + if (oset) { ORBIS_RET_ON_ERROR(uwrite(oset, thread->sigMask)); } @@ -40,20 +42,23 @@ orbis::SysResult orbis::sys_sigprocmask(Thread *thread, sint how, SigSet _set; ORBIS_RET_ON_ERROR(uread(_set, set)); + auto newSigMask = thread->sigMask; + auto oldSigMask = newSigMask; + switch (how) { case 1: // block for (std::size_t i = 0; i < 4; ++i) { - thread->sigMask.bits[i] |= _set.bits[i]; + newSigMask.bits[i] |= _set.bits[i]; } break; case 2: // unblock for (std::size_t i = 0; i < 4; ++i) { - thread->sigMask.bits[i] &= ~_set.bits[i]; + newSigMask.bits[i] &= ~_set.bits[i]; } break; case 3: // set - thread->sigMask = _set; + newSigMask = _set; break; default: @@ -62,8 +67,20 @@ orbis::SysResult orbis::sys_sigprocmask(Thread *thread, sint how, return ErrorCode::INVAL; } - thread->sigMask.clear(kSigKill); - thread->sigMask.clear(kSigStop); + newSigMask.clear(kSigKill); + newSigMask.clear(kSigStop); + + thread->sigMask = newSigMask; + + for (std::size_t word = 0; word < std::size(newSigMask.bits); ++word) { + auto unblockedBits = ~oldSigMask.bits[word] & newSigMask.bits[word]; + std::uint32_t offset = word * 32 + 1; + + for (std::uint32_t i = std::countr_zero(unblockedBits); i < 32; + i += std::countr_zero(unblockedBits >> (i + 1)) + 1) { + thread->notifyUnblockedSignal(offset + i); + } + } } return {}; diff --git a/orbis-kernel/src/umtx.cpp b/orbis-kernel/src/umtx.cpp index bad5ccae4..5f9ffa3e5 100644 --- a/orbis-kernel/src/umtx.cpp +++ b/orbis-kernel/src/umtx.cpp @@ -91,6 +91,7 @@ orbis::ErrorCode orbis::umtx_wait(Thread *thread, ptr addr, ulong id, if (val == id) { if (ut + 1 == 0) { while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx)); if (result != ErrorCode{} || node->second.thr != thread) break; @@ -99,6 +100,7 @@ orbis::ErrorCode orbis::umtx_wait(Thread *thread, ptr addr, ulong id, auto start = std::chrono::steady_clock::now(); std::uint64_t udiff = 0; while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff)); if (node->second.thr != thread) @@ -191,6 +193,7 @@ static ErrorCode do_lock_normal(Thread *thread, ptr m, uint flags, auto [chain, key, lock] = g_context.getUmtxChain1(thread, flags, m); auto node = chain.enqueue(key, thread); if (m->owner.compare_exchange_strong(owner, owner | kUmutexContested)) { + orbis::scoped_unblock unblock; error = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut)); if (error == ErrorCode{} && node->second.thr == thread) { error = ErrorCode::TIMEDOUT; @@ -350,6 +353,7 @@ orbis::ErrorCode orbis::umtx_cv_wait(Thread *thread, ptr cv, if (result == ErrorCode{}) { if (ut + 1 == 0) { while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut)); if (result != ErrorCode{} || node->second.thr != thread) { break; @@ -359,6 +363,7 @@ orbis::ErrorCode orbis::umtx_cv_wait(Thread *thread, ptr cv, auto start = std::chrono::steady_clock::now(); std::uint64_t udiff = 0; while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff)); if (node->second.thr != thread) { @@ -455,6 +460,7 @@ orbis::ErrorCode orbis::umtx_rw_rdlock(Thread *thread, ptr rwlock, if (ut + 1 == 0) { while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut)); if (result != ErrorCode{} || node->second.thr != thread) { break; @@ -464,6 +470,7 @@ orbis::ErrorCode orbis::umtx_rw_rdlock(Thread *thread, ptr rwlock, auto start = std::chrono::steady_clock::now(); std::uint64_t udiff = 0; while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff)); if (node->second.thr != thread) @@ -556,6 +563,7 @@ orbis::ErrorCode orbis::umtx_rw_wrlock(Thread *thread, ptr rwlock, if (ut + 1 == 0) { while (true) { + orbis::scoped_unblock unblock; error = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut)); if (error != ErrorCode{} || node->second.thr != thread) { break; @@ -565,6 +573,7 @@ orbis::ErrorCode orbis::umtx_rw_wrlock(Thread *thread, ptr rwlock, auto start = std::chrono::steady_clock::now(); std::uint64_t udiff = 0; while (true) { + orbis::scoped_unblock unblock; error = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff)); if (node->second.thr != thread) @@ -729,6 +738,7 @@ orbis::ErrorCode orbis::umtx_sem_wait(Thread *thread, ptr sem, if (!sem->count) { if (ut + 1 == 0) { while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut)); if (result != ErrorCode{} || node->second.thr != thread) break; @@ -737,6 +747,7 @@ orbis::ErrorCode orbis::umtx_sem_wait(Thread *thread, ptr sem, auto start = std::chrono::steady_clock::now(); std::uint64_t udiff = 0; while (true) { + orbis::scoped_unblock unblock; result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff)); if (node->second.thr != thread) diff --git a/orbis-kernel/src/utils/SharedAtomic.cpp b/orbis-kernel/src/utils/SharedAtomic.cpp index b02c1875c..3f069d7ec 100644 --- a/orbis-kernel/src/utils/SharedAtomic.cpp +++ b/orbis-kernel/src/utils/SharedAtomic.cpp @@ -15,9 +15,20 @@ std::errc shared_atomic32::wait_impl(std::uint32_t oldValue, timeout.tv_sec = (usec_timeout_count / 1000'000); } + bool unblock = (!useTimeout || usec_timeout.count() > 1000) && + g_scopedUnblock != nullptr; + + if (unblock) { + g_scopedUnblock(true); + } + int result = syscall(SYS_futex, this, FUTEX_WAIT, oldValue, useTimeout ? &timeout : nullptr); + if (unblock) { + g_scopedUnblock(false); + } + if (result < 0) { return static_cast(errno); } diff --git a/orbis-kernel/src/utils/SharedCV.cpp b/orbis-kernel/src/utils/SharedCV.cpp index f76032df7..0749e6624 100644 --- a/orbis-kernel/src/utils/SharedCV.cpp +++ b/orbis-kernel/src/utils/SharedCV.cpp @@ -17,17 +17,23 @@ std::errc shared_cv::impl_wait(shared_mutex &mutex, unsigned _val, std::errc result = {}; + bool useTimeout = usec_timeout != static_cast(-1); + while (true) { - result = m_value.wait(_val, usec_timeout == static_cast(-1) - ? std::chrono::microseconds::max() - : std::chrono::microseconds(usec_timeout)); + result = + m_value.wait(_val, useTimeout ? std::chrono::microseconds(usec_timeout) + : std::chrono::microseconds::max()); + bool spurious = result == std::errc::resource_unavailable_try_again || + result == std::errc::interrupted; // Cleanup const auto old = m_value.fetch_op([&](unsigned &value) { // Remove waiter if no signals - if (!(value & ~c_waiter_mask) && - result != std::errc::resource_unavailable_try_again) { - value -= 1; + if ((value & ~c_waiter_mask) == 0) { + + if (useTimeout || !spurious) { + value -= 1; + } } // Try to remove signal @@ -60,7 +66,7 @@ std::errc shared_cv::impl_wait(shared_mutex &mutex, unsigned _val, #endif // Possibly spurious wakeup - if (result != std::errc::resource_unavailable_try_again) { + if (useTimeout || !spurious) { break; } diff --git a/rpcsx/main.cpp b/rpcsx/main.cpp index 5d2e9be56..79767c8ca 100644 --- a/rpcsx/main.cpp +++ b/rpcsx/main.cpp @@ -801,11 +801,15 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path, thread->tproc->event.emit(orbis::kEvFiltProc, orbis::kNoteExec); - std::thread([&] { + thread->handle = std::thread([&] { + thread->hostTid = ::gettid(); + rx::thread::initialize(); rx::thread::setupSignalStack(); rx::thread::setupThisThread(); ps4Exec(thread, executableModule, argv, envv); - }).join(); + }); + + thread->handle.join(); std::abort(); } @@ -1038,7 +1042,6 @@ int main(int argc, const char *argv[]) { mainThread->tproc = initProcess; mainThread->tid = initProcess->pid + baseId; mainThread->state = orbis::ThreadState::RUNNING; - mainThread->hostTid = ::gettid(); orbis::g_currentThread = mainThread; if (!isSystem && !vfs::exists(guestArgv[0], mainThread) && @@ -1191,6 +1194,12 @@ int main(int argc, const char *argv[]) { } } + mainThread->hostTid = ::gettid(); + mainThread->nativeHandle = pthread_self(); + orbis::g_currentThread = mainThread; + rx::thread::setupSignalStack(); + rx::thread::setupThisThread(); + status = ps4Exec(mainThread, execEnv, std::move(executableModule), guestArgv, {}); diff --git a/rpcsx/ops.cpp b/rpcsx/ops.cpp index 9ea5e5e5c..daf71a08d 100644 --- a/rpcsx/ops.cpp +++ b/rpcsx/ops.cpp @@ -540,6 +540,7 @@ SysResult thr_new(orbis::Thread *thread, orbis::ptr param, auto proc = thread->tproc; std::lock_guard lock(proc->mtx); auto [baseId, childThread] = proc->threadsMap.emplace(); + std::lock_guard lockThr(childThread->mtx); childThread->tproc = proc; childThread->tid = proc->pid + baseId; childThread->state = orbis::ThreadState::RUNQ; @@ -569,35 +570,43 @@ SysResult thr_new(orbis::Thread *thread, orbis::ptr param, ORBIS_RET_ON_ERROR(uread(_rtp, _param.rtp)); ORBIS_LOG_NOTICE(" rtp: ", _rtp.type, _rtp.prio); } - auto stdthr = std::thread{[=, childThread = Ref(childThread)] { - static_cast( - uwrite(_param.child_tid, slong(childThread->tid))); // TODO: verify - auto context = new ucontext_t{}; + childThread->handle = + std::thread{[=, childThread = Ref(childThread)] { + static_cast( + uwrite(_param.child_tid, slong(childThread->tid))); // TODO: verify + auto context = new ucontext_t{}; - context->uc_mcontext.gregs[REG_RDI] = - reinterpret_cast(_param.arg); - context->uc_mcontext.gregs[REG_RSI] = - reinterpret_cast(_param.arg); - context->uc_mcontext.gregs[REG_RSP] = - reinterpret_cast(childThread->stackEnd); - context->uc_mcontext.gregs[REG_RIP] = - reinterpret_cast(_param.start_func); + context->uc_mcontext.gregs[REG_RDI] = + reinterpret_cast(_param.arg); + context->uc_mcontext.gregs[REG_RSI] = + reinterpret_cast(_param.arg); + context->uc_mcontext.gregs[REG_RSP] = + reinterpret_cast(childThread->stackEnd); + context->uc_mcontext.gregs[REG_RIP] = + reinterpret_cast(_param.start_func); - childThread->hostTid = ::gettid(); - childThread->context = context; - childThread->state = orbis::ThreadState::RUNNING; + childThread->hostTid = ::gettid(); + childThread->context = context; + childThread->state = orbis::ThreadState::RUNNING; - rx::thread::setupSignalStack(); - rx::thread::setupThisThread(); - rx::thread::invoke(childThread.get()); - }}; + rx::thread::setupSignalStack(); + rx::thread::setupThisThread(); + rx::thread::invoke(childThread.get()); + }}; - if (pthread_setname_np(stdthr.native_handle(), - std::to_string(childThread->tid).c_str())) { + std::string name = std::to_string(childThread->tid); + if (childThread->name[0] != '0') { + name += '-'; + name += childThread->name; + } + + if (name.size() > 15) { + name.resize(15); + } + if (pthread_setname_np(childThread->getNativeHandle(), name.c_str())) { perror("pthread_setname_np"); } - childThread->handle = std::move(stdthr); return {}; } SysResult thr_exit(orbis::Thread *thread, orbis::ptr state) { @@ -623,9 +632,7 @@ SysResult thr_kill(orbis::Thread *thread, orbis::slong id, orbis::sint sig) { } ORBIS_LOG_FATAL(__FUNCTION__, id, sig, t->hostTid); - std::lock_guard lock(t->tproc->mtx); - t->signalQueue.push_back(sig); - ::tgkill(t->tproc->hostPid, t->hostTid, SIGUSR1); + t->sendSignal(sig); return {}; } @@ -811,6 +818,7 @@ SysResult fork(Thread *thread, slong flags) { orbis::g_currentThread = newThread; newThread->retval[0] = 0; newThread->retval[1] = 1; + newThread->nativeHandle = pthread_self(); thread = orbis::g_currentThread; @@ -830,8 +838,6 @@ SysResult fork(Thread *thread, slong flags) { return {}; } -volatile bool debuggerPresent = false; - SysResult execve(Thread *thread, ptr fname, ptr> argv, ptr> envv) { ORBIS_LOG_ERROR(__FUNCTION__, fname); @@ -906,7 +912,13 @@ SysResult execve(Thread *thread, ptr fname, ptr> argv, ORBIS_LOG_ERROR(__FUNCTION__, "done"); std::thread([&] { rx::thread::setupSignalStack(); + rx::thread::initialize(); rx::thread::setupThisThread(); + + thread->hostTid = ::gettid(); + thread->nativeHandle = pthread_self(); + orbis::g_currentThread = thread; + ps4Exec(thread, executableModule, _argv, _envv); }).join(); std::abort(); @@ -925,6 +937,50 @@ SysResult registerEhFrames(Thread *thread) { void where(Thread *thread) { rx::printStackTrace((ucontext_t *)thread->context, thread, 2); } + +void block(Thread *thread) { + if (--thread->unblocked != 0) { + return; + } + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + sigaddset(&set, SIGSYS); + if (pthread_sigmask(SIG_BLOCK, &set, nullptr)) { + perror("pthread_sigmask block"); + std::abort(); + } + // ORBIS_LOG_ERROR("blocking thread", thread->tid, + // orbis::g_currentThread->tid); + + std::free(thread->altStack.back()); + thread->altStack.pop_back(); + thread->sigReturns.pop_back(); +} + +void unblock(Thread *thread) { + if (thread->unblocked++ != 0) { + return; + } + + // ORBIS_LOG_ERROR("unblocking thread", thread->tid, + // orbis::g_currentThread->tid); + rx::thread::copyContext(thread, thread->sigReturns.emplace_back(), + *reinterpret_cast(thread->context)); + + auto altStack = malloc(rx::thread::getSigAltStackSize()); + thread->altStack.push_back(altStack); + rx::thread::setupSignalStack(altStack); + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + sigaddset(&set, SIGSYS); + if (pthread_sigmask(SIG_UNBLOCK, &set, nullptr)) { + perror("pthread_sigmask unblock"); + std::abort(); + } +} } // namespace ProcessOps rx::procOpsTable = { @@ -975,4 +1031,6 @@ ProcessOps rx::procOpsTable = { .processNeeded = processNeeded, .registerEhFrames = registerEhFrames, .where = where, + .unblock = unblock, + .block = block, }; diff --git a/rpcsx/thread.cpp b/rpcsx/thread.cpp index 7947ca894..c9a65011b 100644 --- a/rpcsx/thread.cpp +++ b/rpcsx/thread.cpp @@ -10,18 +10,13 @@ #include #include #include +#include #include #include #include #include #include -static std::size_t getSigStackSize() { - static auto sigStackSize = std::max( - SIGSTKSZ, ::rx::alignUp(64 * 1024 * 1024, rx::mem::pageSize)); - return sigStackSize; -} - static auto setContext = [] { struct SetContext : Xbyak::CodeGenerator { SetContext() { @@ -42,7 +37,108 @@ static auto setContext = [] { return setContextStorage.getCode(); }(); -static void copy(orbis::MContext &dst, const mcontext_t &src) { +static __attribute__((no_stack_protector)) void +handleSigSys(int sig, siginfo_t *info, void *ucontext) { + if (auto hostFs = _readgsbase_u64()) { + _writefsbase_u64(hostFs); + } + + // rx::printStackTrace(reinterpret_cast(ucontext), + // rx::thread::g_current, 1); + auto thread = orbis::g_currentThread; + auto prevContext = std::exchange(thread->context, ucontext); + + if ((std::uint64_t)&thread < orbis::kMaxAddress) { + ORBIS_LOG_ERROR("unexpected sigsys signal stack", thread->tid, sig, + (std::uint64_t)&thread); + std::abort(); + } + + orbis::syscall_entry(thread); + + thread = orbis::g_currentThread; + thread->context = prevContext; + _writefsbase_u64(thread->fsBase); +} + +__attribute__((no_stack_protector)) static void +handleSigUser(int sig, siginfo_t *info, void *ucontext) { + if (auto hostFs = _readgsbase_u64()) { + _writefsbase_u64(hostFs); + } + + auto context = reinterpret_cast(ucontext); + bool inGuestCode = context->uc_mcontext.gregs[REG_RIP] < orbis::kMaxAddress; + auto thread = orbis::g_currentThread; + + if ((std::uint64_t)&context < orbis::kMaxAddress) { + ORBIS_LOG_ERROR("unexpected sigusr signal stack", thread->tid, sig, + inGuestCode, (std::uint64_t)&context); + std::abort(); + } + + int guestSignal = info->si_value.sival_int; + + if (guestSignal == -1) { + // ORBIS_LOG_ERROR("suspending thread", thread->tid, inGuestCode); + + if (inGuestCode) { + thread->unblock(); + } + + auto [value, locked] = thread->suspendFlags.fetch_op([](unsigned &value) { + if ((value & ~orbis::kThreadSuspendFlag) != 0) { + value |= orbis::kThreadSuspendFlag; + return true; + } + return false; + }); + + if (locked) { + thread->suspendFlags.notify_all(); + value |= orbis::kThreadSuspendFlag; + + while (true) { + while ((value & ~orbis::kThreadSuspendFlag) != 0) { + thread->suspendFlags.wait(value); + value = thread->suspendFlags.load(std::memory_order::relaxed); + } + + bool unlocked = thread->suspendFlags.op([](unsigned &value) { + if ((value & ~orbis::kThreadSuspendFlag) == 0) { + value &= ~orbis::kThreadSuspendFlag; + return true; + } + + return false; + }); + + if (unlocked) { + thread->suspendFlags.notify_all(); + break; + } + } + } + + if (inGuestCode) { + thread->block(); + } + + // ORBIS_LOG_ERROR("thread wake", thread->tid); + } + + if (inGuestCode) { + _writefsbase_u64(thread->fsBase); + } +} + +std::size_t rx::thread::getSigAltStackSize() { + static auto sigStackSize = std::max( + SIGSTKSZ, ::rx::alignUp(64 * 1024 * 1024, rx::mem::pageSize)); + return sigStackSize; +} + +void rx::thread::copyContext(orbis::MContext &dst, const mcontext_t &src) { // dst.onstack = src.gregs[REG_ONSTACK]; dst.rdi = src.gregs[REG_RDI]; dst.rsi = src.gregs[REG_RSI]; @@ -86,123 +182,14 @@ static void copy(orbis::MContext &dst, const mcontext_t &src) { // dst.xfpustate_len = src.gregs[REG_XFPUSTATE_LEN]; } -static void copy(orbis::Thread *thread, orbis::UContext &dst, - const ucontext_t &src) { +void rx::thread::copyContext(orbis::Thread *thread, orbis::UContext &dst, + const ucontext_t &src) { dst = {}; dst.stack.sp = thread->stackStart; dst.stack.size = (char *)thread->stackEnd - (char *)thread->stackStart; dst.stack.align = 0x10000; dst.sigmask = thread->sigMask; - copy(dst.mcontext, src.uc_mcontext); -} - -static __attribute__((no_stack_protector)) void -handleSigSys(int sig, siginfo_t *info, void *ucontext) { - if (auto hostFs = _readgsbase_u64()) { - _writefsbase_u64(hostFs); - } - - // rx::printStackTrace(reinterpret_cast(ucontext), - // rx::thread::g_current, 1); - auto thread = orbis::g_currentThread; - auto prevContext = std::exchange(thread->context, ucontext); - { - std::lock_guard lock(thread->mtx); - copy(thread, thread->sigReturns.emplace_back(), - *reinterpret_cast(ucontext)); - } - if ((std::uint64_t)&thread < 0x100'0000'0000) { - ORBIS_LOG_ERROR("unexpected sigsys signal stack", thread->tid, sig, - (std::uint64_t)&thread); - std::abort(); - } - - auto altStack = malloc(getSigStackSize()); - rx::thread::setupSignalStack(altStack); - - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - sigaddset(&set, SIGSYS); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - orbis::syscall_entry(thread); - - pthread_sigmask(SIG_BLOCK, &set, NULL); - - std::free(altStack); - if (thread == orbis::g_currentThread) { - std::lock_guard lock(thread->mtx); - thread->sigReturns.pop_back(); - } - thread = orbis::g_currentThread; - thread->context = prevContext; - _writefsbase_u64(thread->fsBase); -} - -__attribute__((no_stack_protector)) static void -handleSigUser(int sig, siginfo_t *info, void *ucontext) { - if (auto hostFs = _readgsbase_u64()) { - _writefsbase_u64(hostFs); - } - - auto context = reinterpret_cast(ucontext); - bool inGuestCode = context->uc_mcontext.gregs[REG_RIP] < 0x100'0000'0000; - auto thread = orbis::g_currentThread; - - if ((std::uint64_t)&context < 0x100'0000'0000) { - ORBIS_LOG_ERROR("unexpected sigusr signal stack", thread->tid, sig, - inGuestCode, (std::uint64_t)&context); - std::abort(); - } - - int guestSignal = -3; - { - std::lock_guard lock(thread->mtx); - if (thread->signalQueue.empty()) { - ORBIS_LOG_ERROR("unexpected user signal", thread->tid, sig, inGuestCode); - return; - } - - guestSignal = thread->signalQueue.front(); - thread->signalQueue.pop_front(); - - copy(thread, thread->sigReturns.emplace_back(), *context); - } - - if (guestSignal == -1) { - auto altStack = malloc(getSigStackSize()); - rx::thread::setupSignalStack(altStack); - - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - sigaddset(&set, SIGSYS); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - thread->suspended++; - // ORBIS_LOG_ERROR("suspending thread", thread->tid); - while (thread->suspended > 0) { - ::sleep(1); - } - - pthread_sigmask(SIG_BLOCK, &set, NULL); - free(altStack); - - // ORBIS_LOG_ERROR("thread wake", thread->tid); - } - - if (guestSignal == -2) { - // ORBIS_LOG_ERROR("thread resume signal", thread->tid); - - std::lock_guard lock(thread->mtx); - thread->sigReturns.pop_back(); - --thread->suspended; - } - - if (inGuestCode) { - _writefsbase_u64(thread->fsBase); - } + copyContext(dst.mcontext, src.uc_mcontext); } void rx::thread::initialize() { @@ -233,12 +220,12 @@ void *rx::thread::setupSignalStack(void *address) { if (address == NULL) { std::fprintf(stderr, "attempt to set null signal stack, %p - %zx\n", - address, getSigStackSize()); + address, getSigAltStackSize()); std::exit(EXIT_FAILURE); } ss.ss_sp = address; - ss.ss_size = getSigStackSize(); + ss.ss_size = getSigAltStackSize(); ss.ss_flags = 1 << 31; if (sigaltstack(&ss, &oss) == -1) { @@ -250,9 +237,9 @@ void *rx::thread::setupSignalStack(void *address) { } void *rx::thread::setupSignalStack() { - auto data = malloc(getSigStackSize()); + auto data = malloc(getSigAltStackSize()); if (data == nullptr) { - std::fprintf(stderr, "malloc produces null, %zx\n", getSigStackSize()); + std::println(stderr, "malloc produces null, {:x}", getSigAltStackSize()); std::exit(EXIT_FAILURE); } return setupSignalStack(data); diff --git a/rpcsx/thread.hpp b/rpcsx/thread.hpp index 3fb415637..f36523111 100644 --- a/rpcsx/thread.hpp +++ b/rpcsx/thread.hpp @@ -3,11 +3,15 @@ #include "orbis/thread/Thread.hpp" namespace rx::thread { +std::size_t getSigAltStackSize(); void initialize(); void deinitialize(); void *setupSignalStack(); void *setupSignalStack(void *address); void setupThisThread(); +void copyContext(orbis::MContext &dst, const mcontext_t &src); +void copyContext(orbis::Thread *thread, orbis::UContext &dst, + const ucontext_t &src); void invoke(orbis::Thread *thread); } // namespace rx::thread diff --git a/rpcsx/vm.cpp b/rpcsx/vm.cpp index 58e0b6831..c4db109e7 100644 --- a/rpcsx/vm.cpp +++ b/rpcsx/vm.cpp @@ -933,6 +933,7 @@ void *vm::map(void *addr, std::uint64_t len, std::int32_t prot, } if (auto thr = orbis::g_currentThread) { + std::lock_guard lock(orbis::g_context.gpuDeviceMtx); if (auto gpu = amdgpu::DeviceCtl{orbis::g_context.gpuDevice}) { gpu.submitMapMemory(thr->tproc->pid, address, len, -1, -1, prot, address - kMinAddress);