Introduce a syscall restart mechanism

This commit is contained in:
Zejun Zhao 2024-12-16 08:34:32 +08:00 committed by Tate, Hongliang Tian
parent b70c4784ed
commit ced0023d6b
9 changed files with 45 additions and 15 deletions

View File

@ -22,6 +22,10 @@ impl LinuxAbi for UserContext {
self.rax()
}
fn set_syscall_num(&mut self, num: usize) {
self.set_rax(num);
}
fn set_syscall_ret(&mut self, ret: usize) {
self.set_rax(ret);
}

View File

@ -7,6 +7,9 @@ pub trait LinuxAbi {
/// Get return value of syscall
fn syscall_ret(&self) -> usize;
/// Set number of syscall
fn set_syscall_num(&mut self, num: usize);
/// Set return value of syscall
fn set_syscall_ret(&mut self, ret: usize);

View File

@ -150,6 +150,8 @@ pub enum Errno {
ERFKILL = 132, /* Operation not possible due to RF-kill */
EHWPOISON = 133, /* Memory page has hardware error */
ERESTARTSYS = 512, /* Restart of an interrupted system call. For kernel internal use only. */
}
/// error used in this crate

View File

@ -10,6 +10,7 @@ use ostd::sync::Waker;
use super::{
kill::SignalSenderIds,
signal::{
sig_action::SigAction,
sig_mask::{AtomicSigMask, SigMask, SigSet},
sig_num::SigNum,
sig_queues::SigQueues,
@ -220,8 +221,11 @@ impl PosixThread {
/// Enqueues a thread-directed signal. This method should only be used for enqueue kernel
/// signal and fault signal.
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
let signal_number = signal.num();
self.sig_queues.enqueue(signal);
if let Some(waker) = &*self.signalled_waker.lock() {
if self.process().sig_dispositions().lock().get(signal_number) != SigAction::Ign
&& let Some(waker) = &*self.signalled_waker.lock()
{
waker.wake_up();
}
}

View File

@ -28,6 +28,7 @@ pub use sig_stack::{SigStack, SigStackFlags};
use super::posix_thread::PosixThread;
use crate::{
cpu::LinuxAbi,
current_userspace,
prelude::*,
process::{do_exit_group, TermStatus},
@ -41,7 +42,11 @@ pub trait SignalContext {
// TODO: This interface of this method is error prone.
// The method takes an argument for the current thread to optimize its efficiency.
/// Handle pending signal for current process.
pub fn handle_pending_signal(user_ctx: &mut UserContext, ctx: &Context) -> Result<()> {
pub fn handle_pending_signal(
user_ctx: &mut UserContext,
ctx: &Context,
syscall_number: Option<usize>,
) -> Result<()> {
// We first deal with signal in current thread, then signal in current process.
let posix_thread = ctx.posix_thread;
let signal = {
@ -69,6 +74,17 @@ pub fn handle_pending_signal(user_ctx: &mut UserContext, ctx: &Context) -> Resul
restorer_addr,
mask,
} => {
if let Some(syscall_number) = syscall_number
&& user_ctx.syscall_ret() == -(Errno::ERESTARTSYS as i32) as usize
{
if flags.contains(SigActionFlags::SA_RESTART) {
user_ctx.set_syscall_num(syscall_number);
user_ctx.set_instruction_pointer(user_ctx.instruction_pointer() - 2);
} else {
user_ctx.set_syscall_ret(-(Errno::EINTR as i32) as usize);
}
}
if flags.contains(SigActionFlags::SA_RESETHAND) {
// In Linux, SA_RESETHAND corresponds to SA_ONESHOT,
// which means the user handler will be executed only once and then reset to the default.

View File

@ -90,9 +90,6 @@ impl TryFrom<u32> for SigActionFlags {
fn try_from(bits: u32) -> Result<Self> {
let flags = SigActionFlags::from_bits(bits)
.ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid sig action flag"))?;
if flags.contains(SigActionFlags::SA_RESTART) {
warn!("SA_RESTART is not supported");
}
Ok(flags)
}
}

View File

@ -64,22 +64,26 @@ pub fn create_new_user_task(user_space: Arc<UserSpace>, thread_ref: Arc<Thread>)
loop {
let return_reason = user_mode.execute(has_kernel_event_fn);
let user_ctx = user_mode.context_mut();
let mut syscall_number = None;
// handle user event:
match return_reason {
ReturnReason::UserException => handle_exception(&ctx, user_ctx),
ReturnReason::UserSyscall => handle_syscall(&ctx, user_ctx),
ReturnReason::UserSyscall => {
syscall_number = Some(user_ctx.syscall_num());
handle_syscall(&ctx, user_ctx);
}
ReturnReason::KernelEvent => {}
};
if current_thread.is_exited() {
break;
}
handle_pending_signal(user_ctx, &ctx).unwrap();
handle_pending_signal(user_ctx, &ctx, syscall_number).unwrap();
// If current is suspended, wait for a signal to wake up self
while current_thread.is_stopped() {
Thread::yield_now();
debug!("{} is suspended.", current_posix_thread.tid());
handle_pending_signal(user_ctx, &ctx).unwrap();
handle_pending_signal(user_ctx, &ctx, None).unwrap();
}
if current_thread.is_exited() {
debug!("exit due to signal");

View File

@ -18,7 +18,7 @@
.code64
.text
# extern "sysv64" fn syscall_return(&mut GeneralRegs)
# extern "sysv64" fn syscall_return(&mut UserContext)
.global syscall_return
syscall_return:
# disable interrupt
@ -34,7 +34,7 @@ syscall_return:
push rdi # keep rsp 16 bytes align
mov gs:4, rsp # store kernel rsp -> TSS.sp0
mov rsp, rdi # set rsp -> GeneralRegs
mov rsp, rdi # set rsp -> UserContext
# restore user gsbase
swapgs
@ -99,8 +99,8 @@ syscall_entry:
swapgs # swap in kernel gs
mov gs:12, rsp # store user rsp -> scratch at TSS.sp1
mov rsp, gs:4 # load kernel rsp <- TSS.sp0
pop rsp # load rsp -> GeneralRegs
add rsp, 21*8 # rsp -> error code of GeneralRegs
pop rsp # load rsp <- UserContext
add rsp, 21*8 # rsp -> error code of UserContext
push 0x100 # push trap_num
sub rsp, 16 # skip fsbase, gsbase

View File

@ -66,7 +66,7 @@ trap_common:
__from_user:
/*
kernel stack:
- ptr to GeneralRegs
- ptr to UserContext
- ss
- rsp
- rflags
@ -80,8 +80,8 @@ __from_user:
mov rax, [rsp + 6*8] # rax = user rsp
mov gs:12, rax # store user rsp -> scratch at TSS.sp1
mov rsp, [rsp + 8*8] # load rsp -> GeneralRegs
add rsp, 22*8 # rsp -> top of GeneralRegs
mov rsp, [rsp + 8*8] # load rsp <- UserContext
add rsp, 22*8 # rsp -> top of UserContext
mov rax, gs:4 # rax = kernel stack
# push trap_num, error_code