Skip to content

Changes for managarm cancelable syscalls #884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions sysdeps/managarm/generic/entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace {
thread_local unsigned __mlibc_gsf_nesting;
thread_local void *__mlibc_cached_thread_page;
thread_local HelHandle *cachedFileTable;
thread_local HelHandle *cancelEvent;

// This construction is a bit weird: Even though the variables above
// are thread_local we still protect their initialization with a pthread_once_t
Expand All @@ -45,6 +46,7 @@ void actuallyCacheInfos() {
__mlibc_cached_thread_page = data.threadPage;
cachedFileTable = data.fileTable;
__mlibc_clk_tracker_page = data.clockTrackerPage;
cancelEvent = data.cancelRequestEvent;
}
} // namespace

Expand Down Expand Up @@ -104,6 +106,11 @@ HelHandle getHandleForFd(int fd) {

void clearCachedInfos() { has_cached_infos = PTHREAD_ONCE_INIT; }

void setCurrentRequestEvent(HelHandle event) {
pthread_once(&has_cached_infos, &actuallyCacheInfos);
__atomic_store_n(cancelEvent, event, __ATOMIC_RELEASE);
}

extern char **environ;

extern "C" void
Expand Down
32 changes: 19 additions & 13 deletions sysdeps/managarm/generic/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1748,6 +1748,10 @@ int sys_read(int fd, void *data, size_t max_size, ssize_t *bytes_read) {
if (!handle)
return EBADF;

HelHandle cancel_handle;
HEL_CHECK(helCreateOneshotEvent(&cancel_handle));
helix::UniqueDescriptor cancel_event{cancel_handle};

managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_req_type(managarm::fs::CntReqType::READ);
req.set_fd(fd);
Expand All @@ -1756,29 +1760,31 @@ int sys_read(int fd, void *data, size_t max_size, ssize_t *bytes_read) {
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);

auto [offer, send_req, imbue_creds, recv_resp, recv_data] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::imbueCredentials(),
helix_ng::recvInline(),
helix_ng::recvBuffer(data, max_size)
)
);
auto [offer, push_req, send_req, imbue_creds, recv_resp, recv_data] =
exchangeMsgsSyncCancellable(
handle,
cancel_handle,
helix_ng::offer(
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::pushDescriptor(cancel_event),
helix_ng::imbueCredentials(),
helix_ng::recvInline(),
helix_ng::recvBuffer(data, max_size)
)
);

HEL_CHECK(offer.error());
HEL_CHECK(push_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(imbue_creds.error());
HEL_CHECK(recv_resp.error());

managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::fs::Errors::SUCCESS)
return resp.error() | toErrno;

HEL_CHECK(recv_data.error());

*bytes_read = recv_data.actualLength();
return 0;
return resp.error() | toErrno;
}

int sys_readv(int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_read) {
Expand Down
17 changes: 15 additions & 2 deletions sysdeps/managarm/generic/fork-exec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,33 @@ int sys_futex_wake(int *pointer) {
int sys_waitpid(pid_t pid, int *status, int flags, struct rusage *ru, pid_t *ret_pid) {
SignalGuard sguard;

if (ru) {
mlibc::infoLogger() << "mlibc: struct rusage in sys_waitpid is unsupported" << frg::endlog;
return ENOSYS;
}

HelHandle cancel_handle;
HEL_CHECK(helCreateOneshotEvent(&cancel_handle));
helix::UniqueDescriptor cancel_event{cancel_handle};

managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_request_type(managarm::posix::CntReqType::WAIT);
req.set_pid(pid);
req.set_flags(flags);

auto [offer, send_head, recv_resp] = exchangeMsgsSync(
auto [offer, send_head, push_descriptor, recv_resp] = exchangeMsgsSyncCancellable(
getPosixLane(),
cancel_handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::pushDescriptor(cancel_event),
helix_ng::recvInline()
)
);

HEL_CHECK(offer.error());
HEL_CHECK(send_head.error());
HEL_CHECK(push_descriptor.error());
HEL_CHECK(recv_resp.error());

managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
Expand Down
4 changes: 4 additions & 0 deletions sysdeps/managarm/include/bits/errors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ inline int operator|(managarm::fs::Errors e, ToErrno) {
return ENOTSOCK;
case managarm::fs::Errors::INTERNAL_ERROR:
return EIO;
case managarm::fs::Errors::INTERRUPTED:
return EINTR;
}

mlibc::panicLogger() << "unhandled managarm::fs::Errors " << static_cast<int32_t>(e)
Expand Down Expand Up @@ -145,6 +147,8 @@ inline int operator|(managarm::posix::Errors e, ToErrno) {
return EISCONN;
case managarm::posix::Errors::UNSUPPORTED_SOCKET_TYPE:
return ESOCKTNOSUPPORT;
case managarm::posix::Errors::INTERRUPTED:
return EINTR;
}

mlibc::panicLogger() << "unhandled managarm::posix::Errors " << static_cast<int32_t>(e)
Expand Down
76 changes: 60 additions & 16 deletions sysdeps/managarm/include/mlibc/posix-pipe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
#define MLIBC_POSIX_PIPE

#include <cstddef>
#include <signal.h>
#include <stdint.h>
#include <string.h>

#include <hel-syscalls.h>
#include <hel.h>

#include <bits/ensure.h>
#include <frg/optional.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>

struct SignalGuard {
SignalGuard();

Expand Down Expand Up @@ -121,24 +124,26 @@ struct Queue {

void trim() {}

ElementHandle dequeueSingle() {
frg::optional<ElementHandle> dequeueSingleUnlessCancelled() {
while (true) {
__ensure(_retrieveIndex != _nextIndex);

bool done;
_waitProgressFutex(&done);
auto progress = _waitProgressFutex();

auto n = _numberOf(_retrieveIndex);
__ensure(_refCount[n]);

if (done) {
if (progress == FutexProgress::DONE) {
retire(n);

_lastProgress = 0;
_retrieveIndex = ((_retrieveIndex + 1) & kHelHeadMask);
continue;
}

if (progress == FutexProgress::CANCELLED)
return frg::null_opt;

// Dequeue the next element.
auto ptr = (char *)_chunks[n] + sizeof(HelChunk) + _lastProgress;
auto element = reinterpret_cast<HelElement *>(ptr);
Expand All @@ -148,6 +153,14 @@ struct Queue {
}
}

ElementHandle dequeueSingle() {
while (true) {
auto result = dequeueSingleUnlessCancelled();
if (result)
return *result;
}
}

void retire(int n) {
__ensure(_refCount[n]);
if (_refCount[n]-- > 1)
Expand Down Expand Up @@ -175,18 +188,21 @@ struct Queue {
HEL_CHECK(helFutexWake(&_queue->headFutex));
}

void _waitProgressFutex(bool *done) {
enum class FutexProgress {
DONE,
PROGRESS,
CANCELLED,
};

FutexProgress _waitProgressFutex() {
while (true) {
auto futex = __atomic_load_n(&_retrieveChunk()->progressFutex, __ATOMIC_ACQUIRE);
__ensure(!(futex & ~(kHelProgressMask | kHelProgressWaiters | kHelProgressDone)));
do {
if (_lastProgress != (futex & kHelProgressMask)) {
*done = false;
return;
} else if (futex & kHelProgressDone) {
*done = true;
return;
}
if (_lastProgress != (futex & kHelProgressMask))
return FutexProgress::PROGRESS;
else if (futex & kHelProgressDone)
return FutexProgress::DONE;

if (futex & kHelProgressWaiters)
break; // Waiters bit is already set (in a previous iteration).
Expand All @@ -199,9 +215,14 @@ struct Queue {
__ATOMIC_ACQUIRE
));

HEL_CHECK(helFutexWait(
int err = helFutexWait(
&_retrieveChunk()->progressFutex, _lastProgress | kHelProgressWaiters, -1
));
);

if (err == kHelErrCancelled)
return FutexProgress::CANCELLED;

HEL_CHECK(err);
}
}

Expand Down Expand Up @@ -261,6 +282,7 @@ inline HelHandleResult *parseHandle(ElementHandle &element) {
HelHandle getPosixLane();
HelHandle *cacheFileTable();
HelHandle getHandleForFd(int fd);
void setCurrentRequestEvent(HelHandle event);
void clearCachedInfos();

extern thread_local Queue globalQueue;
Expand All @@ -287,4 +309,26 @@ auto exchangeMsgsSync(HelHandle descriptor, Args &&...args) {
return results;
}

template <typename... Args>
auto exchangeMsgsSyncCancellable(HelHandle descriptor, HelHandle event, Args &&...args) {
auto results = helix_ng::createResultsTuple(args...);
auto actions = helix_ng::chainActionArrays(args...);

setCurrentRequestEvent(event);
HEL_CHECK(
helSubmitAsync(descriptor, actions.data(), actions.size(), globalQueue.getQueue(), 0, 0)
);

auto element = globalQueue.dequeueSingle();
void *ptr = element.data();

[&]<size_t... p>(std::index_sequence<p...>) {
(results.template get<p>().parse(ptr, element), ...);
}(std::make_index_sequence<std::tuple_size_v<decltype(results)>>{});

setCurrentRequestEvent(kHelNullHandle);

return results;
}

#endif // MLIBC_POSIX_PIPE
32 changes: 21 additions & 11 deletions sysdeps/managarm/rtld-generic/support.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,10 @@ struct Queue {
__ATOMIC_ACQUIRE
));

HEL_CHECK(helFutexWait(&_chunk->progressFutex, _lastProgress | kHelProgressWaiters, -1)
);
int err = helFutexWait(&_chunk->progressFutex, _lastProgress | kHelProgressWaiters, -1);
if (err == kHelErrCancelled)
continue;
HEL_CHECK(err);
}
}

Expand Down Expand Up @@ -302,7 +304,10 @@ int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) {
int sys_read(int fd, void *data, size_t length, ssize_t *bytes_read) {
cacheFileTable();
auto lane = fileTable[fd];
HelAction actions[5];
HelAction actions[6];

HelHandle cancel_handle;
HEL_CHECK(helCreateOneshotEvent(&cancel_handle));

managarm::fs::CntRequest<MemoryAllocator> req(getAllocator());
req.set_req_type(managarm::fs::CntReqType::READ);
Expand All @@ -319,25 +324,30 @@ int sys_read(int fd, void *data, size_t length, ssize_t *bytes_read) {
actions[1].flags = kHelItemChain;
actions[1].buffer = ser.data();
actions[1].length = ser.size();
actions[2].type = kHelActionImbueCredentials;
actions[2].handle = kHelThisThread;
actions[2].type = kHelActionPushDescriptor;
actions[2].handle = cancel_handle;
actions[2].flags = kHelItemChain;
actions[3].type = kHelActionRecvInline;
actions[3].type = kHelActionImbueCredentials;
actions[3].handle = kHelThisThread;
actions[3].flags = kHelItemChain;
actions[4].type = kHelActionRecvToBuffer;
actions[4].flags = 0;
actions[4].buffer = data;
actions[4].length = length;
HEL_CHECK(helSubmitAsync(lane, actions, 5, globalQueue->getHandle(), 0, 0));
actions[4].type = kHelActionRecvInline;
actions[4].flags = kHelItemChain;
actions[5].type = kHelActionRecvToBuffer;
actions[5].flags = 0;
actions[5].buffer = data;
actions[5].length = length;
HEL_CHECK(helSubmitAsync(lane, actions, 6, globalQueue->getHandle(), 0, 0));

auto element = globalQueue->dequeueSingle();
auto offer = parseHandle(element);
auto send_req = parseSimple(element);
auto push_desc = parseSimple(element);
auto imbue_creds = parseSimple(element);
auto recv_resp = parseInline(element);
auto recv_data = parseLength(element);
HEL_CHECK(offer->error);
HEL_CHECK(send_req->error);
HEL_CHECK(push_desc->error);
HEL_CHECK(imbue_creds->error);
HEL_CHECK(recv_resp->error);
HEL_CHECK(recv_data->error);
Expand Down