mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-01-22 16:40:57 +01:00
orbis-kernel: unblock signals only on wait operations
implement sys_cpuset_getaffinity, sys_cpuset_setaffinity, sys_rtprio_thread fix hang on sys_select simplify sys_thr_*_ucontext
This commit is contained in:
parent
c19c70eb77
commit
9fbe1846c0
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -87,5 +87,8 @@ struct ProcessOps {
|
|||
SysResult (*registerEhFrames)(Thread *thread);
|
||||
|
||||
void (*where)(Thread *);
|
||||
|
||||
void (*unblock)(Thread *);
|
||||
void (*block)(Thread *);
|
||||
};
|
||||
} // namespace orbis
|
||||
|
|
|
|||
|
|
@ -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 <atomic>
|
||||
#include <thread>
|
||||
|
||||
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<void *> altStack;
|
||||
ptr<void> stackStart;
|
||||
ptr<void> 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<int> signalQueue;
|
||||
kvector<UContext> sigReturns;
|
||||
std::atomic<unsigned> suspended{0};
|
||||
kvector<SigInfo> blockedSignals;
|
||||
kvector<SigInfo> 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<std::uint32_t> {
|
|||
using atomic::operator=;
|
||||
|
||||
template <typename Clock, typename Dur>
|
||||
[[nodiscard]] std::errc wait(std::uint32_t oldValue,
|
||||
std::chrono::time_point<Clock, Dur> timeout) {
|
||||
std::errc wait(std::uint32_t oldValue,
|
||||
std::chrono::time_point<Clock, Dur> timeout) {
|
||||
if (try_spin_wait(
|
||||
[&] { return load(std::memory_order::acquire) != oldValue; })) {
|
||||
return {};
|
||||
|
|
@ -76,12 +78,12 @@ struct shared_atomic32 : std::atomic<std::uint32_t> {
|
|||
std::chrono::duration_cast<std::chrono::microseconds>(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 {};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "orbis/thread/Process.hpp"
|
||||
#include "orbis/thread/ProcessOps.hpp"
|
||||
#include "orbis/utils/Logs.hpp"
|
||||
#include "utils/SharedAtomic.hpp"
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
|
|
@ -314,16 +315,42 @@ void log_class_string<kstring>::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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "ipmi.hpp"
|
||||
#include "KernelContext.hpp"
|
||||
#include "thread/Process.hpp"
|
||||
#include "thread/Thread.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
#include <chrono>
|
||||
#include <span>
|
||||
|
|
@ -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<std::chrono::microseconds>(
|
||||
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<uint> 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<uint> 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));
|
||||
|
|
|
|||
|
|
@ -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 <bit>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
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<cpusetid_t> 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<cpuset> 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<const cpuset> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,7 +450,15 @@ orbis::SysResult orbis::sys_select(Thread *thread, sint nd,
|
|||
ptr<struct fd_set_t> out,
|
||||
ptr<struct fd_set_t> ex,
|
||||
ptr<struct timeval> 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<struct pollfd> fds,
|
||||
uint nfds, sint timeout) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "sys/sysproto.hpp"
|
||||
#include "thread/Process.hpp"
|
||||
#include "thread/Thread.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
#include <sched.h>
|
||||
|
||||
namespace orbis {
|
||||
struct rlimit {
|
||||
|
|
@ -21,9 +23,54 @@ orbis::SysResult orbis::sys_rtprio_thread(Thread *thread, sint function,
|
|||
ptr<struct rtprio> 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 {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
#include "uio.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
#include <fcntl.h>
|
||||
#include <ranges>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
|
||||
orbis::SysResult orbis::sys_netcontrol(Thread *thread, sint fd, uint op,
|
||||
ptr<void> 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<UContext> 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 {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ orbis::SysResult orbis::sys_sigaction(Thread *thread, sint sig,
|
|||
|
||||
orbis::SysResult orbis::sys_sigprocmask(Thread *thread, sint how,
|
||||
ptr<SigSet> set, ptr<SigSet> 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 {};
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ orbis::ErrorCode orbis::umtx_wait(Thread *thread, ptr<void> 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<void> 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<umutex> 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<ucond> 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<ucond> 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<urwlock> 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<urwlock> 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<urwlock> 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<urwlock> 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<usem> 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<usem> 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)
|
||||
|
|
|
|||
|
|
@ -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<std::errc>(errno);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,17 +17,23 @@ std::errc shared_cv::impl_wait(shared_mutex &mutex, unsigned _val,
|
|||
|
||||
std::errc result = {};
|
||||
|
||||
bool useTimeout = usec_timeout != static_cast<std::uint64_t>(-1);
|
||||
|
||||
while (true) {
|
||||
result = m_value.wait(_val, usec_timeout == static_cast<std::uint64_t>(-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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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, {});
|
||||
|
||||
|
|
|
|||
112
rpcsx/ops.cpp
112
rpcsx/ops.cpp
|
|
@ -540,6 +540,7 @@ SysResult thr_new(orbis::Thread *thread, orbis::ptr<thr_param> 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<thr_param> param,
|
|||
ORBIS_RET_ON_ERROR(uread(_rtp, _param.rtp));
|
||||
ORBIS_LOG_NOTICE(" rtp: ", _rtp.type, _rtp.prio);
|
||||
}
|
||||
auto stdthr = std::thread{[=, childThread = Ref<Thread>(childThread)] {
|
||||
static_cast<void>(
|
||||
uwrite(_param.child_tid, slong(childThread->tid))); // TODO: verify
|
||||
auto context = new ucontext_t{};
|
||||
childThread->handle =
|
||||
std::thread{[=, childThread = Ref<Thread>(childThread)] {
|
||||
static_cast<void>(
|
||||
uwrite(_param.child_tid, slong(childThread->tid))); // TODO: verify
|
||||
auto context = new ucontext_t{};
|
||||
|
||||
context->uc_mcontext.gregs[REG_RDI] =
|
||||
reinterpret_cast<std::uintptr_t>(_param.arg);
|
||||
context->uc_mcontext.gregs[REG_RSI] =
|
||||
reinterpret_cast<std::uintptr_t>(_param.arg);
|
||||
context->uc_mcontext.gregs[REG_RSP] =
|
||||
reinterpret_cast<std::uintptr_t>(childThread->stackEnd);
|
||||
context->uc_mcontext.gregs[REG_RIP] =
|
||||
reinterpret_cast<std::uintptr_t>(_param.start_func);
|
||||
context->uc_mcontext.gregs[REG_RDI] =
|
||||
reinterpret_cast<std::uintptr_t>(_param.arg);
|
||||
context->uc_mcontext.gregs[REG_RSI] =
|
||||
reinterpret_cast<std::uintptr_t>(_param.arg);
|
||||
context->uc_mcontext.gregs[REG_RSP] =
|
||||
reinterpret_cast<std::uintptr_t>(childThread->stackEnd);
|
||||
context->uc_mcontext.gregs[REG_RIP] =
|
||||
reinterpret_cast<std::uintptr_t>(_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<orbis::slong> 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<char> fname, ptr<ptr<char>> argv,
|
||||
ptr<ptr<char>> envv) {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, fname);
|
||||
|
|
@ -906,7 +912,13 @@ SysResult execve(Thread *thread, ptr<char> fname, ptr<ptr<char>> 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<ucontext_t *>(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,
|
||||
};
|
||||
|
|
|
|||
233
rpcsx/thread.cpp
233
rpcsx/thread.cpp
|
|
@ -10,18 +10,13 @@
|
|||
#include <immintrin.h>
|
||||
#include <link.h>
|
||||
#include <linux/prctl.h>
|
||||
#include <print>
|
||||
#include <rx/align.hpp>
|
||||
#include <sys/prctl.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
static std::size_t getSigStackSize() {
|
||||
static auto sigStackSize = std::max<std::size_t>(
|
||||
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<void (*)(const mcontext_t &)>();
|
||||
}();
|
||||
|
||||
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_t *>(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_t *>(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<std::size_t>(
|
||||
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_t *>(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_t *>(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_t *>(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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue