Skip to content

Commit 5d2442d

Browse files
committed
rt: Add upcall_call_shim_on_rust_stack
1 parent 79b1563 commit 5d2442d

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
lines changed

src/rt/rust_task.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ rust_task::rust_task(rust_task_thread *thread, rust_task_list *state,
9090
cc_counter(0),
9191
total_stack_sz(0),
9292
c_stack(NULL),
93-
next_c_sp(0)
93+
next_c_sp(0),
94+
next_rust_sp(0)
9495
{
9596
LOGPTR(thread, "new task", (uintptr_t)this);
9697
DLOG(thread, task, "sizeof(task) = %d (0x%x)", sizeof *this, sizeof *this);
@@ -658,6 +659,7 @@ rust_task::prev_stack() {
658659

659660
void
660661
rust_task::record_stack_limit() {
662+
I(thread, stk);
661663
// The function prolog compares the amount of stack needed to the end of
662664
// the stack. As an optimization, when the frame size is less than 256
663665
// bytes, it will simply compare %esp to to the stack limit instead of
@@ -732,18 +734,40 @@ rust_task::config_notify(chan_handle chan) {
732734

733735
extern "C" void __morestack(void *args, void *fn_ptr, uintptr_t stack_ptr);
734736

737+
static uintptr_t
738+
sanitize_next_sp(uintptr_t next_sp) {
739+
740+
// Since I'm not precisely sure where the next stack pointer sits in
741+
// relation to where the context switch actually happened, nor in relation
742+
// to the amount of stack needed for calling __morestack I've added some
743+
// extra bytes here.
744+
745+
// FIXME: On the rust stack this potentially puts is quite far into the
746+
// red zone. Might want to just allocate a new rust stack every time we
747+
// switch back to rust.
748+
const uintptr_t padding = 16;
749+
750+
return align_down(next_sp - padding);
751+
}
752+
735753
void
736754
rust_task::call_on_c_stack(void *args, void *fn_ptr) {
737755
I(thread, on_rust_stack());
738756

757+
next_rust_sp = get_sp();
758+
739759
bool borrowed_a_c_stack = false;
760+
uintptr_t sp;
740761
if (c_stack == NULL) {
741762
c_stack = thread->borrow_c_stack();
742763
next_c_sp = align_down(c_stack->end);
764+
sp = next_c_sp;
743765
borrowed_a_c_stack = true;
766+
} else {
767+
sp = sanitize_next_sp(next_c_sp);
744768
}
745769

746-
__morestack(args, fn_ptr, next_c_sp);
770+
__morestack(args, fn_ptr, sp);
747771

748772
// Note that we may not actually get here if we threw an exception,
749773
// in which case we will return the c stack when the exception is caught.
@@ -752,6 +776,18 @@ rust_task::call_on_c_stack(void *args, void *fn_ptr) {
752776
}
753777
}
754778

779+
void
780+
rust_task::call_on_rust_stack(void *args, void *fn_ptr) {
781+
I(thread, !on_rust_stack());
782+
I(thread, next_rust_sp);
783+
784+
next_c_sp = get_sp();
785+
786+
uintptr_t sp = sanitize_next_sp(next_rust_sp);
787+
788+
__morestack(args, fn_ptr, sp);
789+
}
790+
755791
void
756792
rust_task::return_c_stack() {
757793
I(thread, on_rust_stack());

src/rt/rust_task.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond
110110
// The stack used for running C code, borrowed from the scheduler thread
111111
stk_seg *c_stack;
112112
uintptr_t next_c_sp;
113+
uintptr_t next_rust_sp;
113114

114115
// Called when the atomic refcount reaches zero
115116
void delete_this();
@@ -194,6 +195,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond
194195
void config_notify(chan_handle chan);
195196

196197
void call_on_c_stack(void *args, void *fn_ptr);
198+
void call_on_rust_stack(void *args, void *fn_ptr);
197199
};
198200

199201
//

src/rt/rust_upcall.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,38 @@ upcall_call_shim_on_c_stack(void *args, void *fn_ptr) {
7171
try {
7272
task->call_on_c_stack(args, fn_ptr);
7373
} catch (...) {
74-
A(task->thread, false, "Native code threw an exception");
74+
LOG_ERR(task, task, "Native code threw an exception");
75+
abort();
7576
}
7677

7778
task = rust_task_thread::get_task();
7879
task->record_stack_limit();
7980
}
8081

82+
/*
83+
* The opposite of above. Starts on a C stack and switches to the Rust
84+
* stack. This is the only upcall that runs from the C stack.
85+
*/
86+
extern "C" CDECL void
87+
upcall_call_shim_on_rust_stack(void *args, void *fn_ptr) {
88+
rust_task *task = rust_task_thread::get_task();
89+
90+
// FIXME: Because of the hack in the other function that disables the
91+
// stack limit when entering the C stack, here we restore the stack limit
92+
// again.
93+
task->record_stack_limit();
94+
95+
try {
96+
task->call_on_rust_stack(args, fn_ptr);
97+
} catch (...) {
98+
// We can't count on being able to unwind through arbitrary
99+
// code. Our best option is to just fail hard.
100+
LOG_ERR(task, task,
101+
"Rust task failed after reentering the Rust stack");
102+
abort();
103+
}
104+
}
105+
81106
/**********************************************************************/
82107

83108
struct s_fail_args {

src/rt/rustrt.def.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ upcall_shared_free
7575
upcall_vec_grow
7676
upcall_vec_push
7777
upcall_call_shim_on_c_stack
78+
upcall_call_shim_on_rust_stack
7879
upcall_new_stack
7980
upcall_del_stack
8081
upcall_reset_stack_limit

0 commit comments

Comments
 (0)