From ced0023d6b5d15cda1fb4ebfdb185f24561a7397 Mon Sep 17 00:00:00 2001 From: Zejun Zhao Date: Mon, 16 Dec 2024 08:34:32 +0800 Subject: [PATCH] Introduce a syscall restart mechanism --- kernel/src/arch/x86/cpu.rs | 4 ++++ kernel/src/cpu.rs | 3 +++ kernel/src/error.rs | 2 ++ kernel/src/process/posix_thread/mod.rs | 6 +++++- kernel/src/process/signal/mod.rs | 18 +++++++++++++++++- kernel/src/process/signal/sig_action.rs | 3 --- kernel/src/thread/task.rs | 10 +++++++--- ostd/src/arch/x86/trap/syscall.S | 8 ++++---- ostd/src/arch/x86/trap/trap.S | 6 +++--- 9 files changed, 45 insertions(+), 15 deletions(-) diff --git a/kernel/src/arch/x86/cpu.rs b/kernel/src/arch/x86/cpu.rs index 2a42d8a1..8963dd8d 100644 --- a/kernel/src/arch/x86/cpu.rs +++ b/kernel/src/arch/x86/cpu.rs @@ -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); } diff --git a/kernel/src/cpu.rs b/kernel/src/cpu.rs index 24d8d0c1..c61332b6 100644 --- a/kernel/src/cpu.rs +++ b/kernel/src/cpu.rs @@ -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); diff --git a/kernel/src/error.rs b/kernel/src/error.rs index a475ebd3..0a8949f1 100644 --- a/kernel/src/error.rs +++ b/kernel/src/error.rs @@ -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 diff --git a/kernel/src/process/posix_thread/mod.rs b/kernel/src/process/posix_thread/mod.rs index dfcfb758..1f8dde20 100644 --- a/kernel/src/process/posix_thread/mod.rs +++ b/kernel/src/process/posix_thread/mod.rs @@ -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) { + 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(); } } diff --git a/kernel/src/process/signal/mod.rs b/kernel/src/process/signal/mod.rs index 87e905df..74647e22 100644 --- a/kernel/src/process/signal/mod.rs +++ b/kernel/src/process/signal/mod.rs @@ -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, +) -> 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. diff --git a/kernel/src/process/signal/sig_action.rs b/kernel/src/process/signal/sig_action.rs index 73711b23..2704ac7e 100644 --- a/kernel/src/process/signal/sig_action.rs +++ b/kernel/src/process/signal/sig_action.rs @@ -90,9 +90,6 @@ impl TryFrom for SigActionFlags { fn try_from(bits: u32) -> Result { 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) } } diff --git a/kernel/src/thread/task.rs b/kernel/src/thread/task.rs index f9be7132..df5a2187 100644 --- a/kernel/src/thread/task.rs +++ b/kernel/src/thread/task.rs @@ -64,22 +64,26 @@ pub fn create_new_user_task(user_space: Arc, thread_ref: Arc) 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"); diff --git a/ostd/src/arch/x86/trap/syscall.S b/ostd/src/arch/x86/trap/syscall.S index 51eb8720..7f90bebd 100644 --- a/ostd/src/arch/x86/trap/syscall.S +++ b/ostd/src/arch/x86/trap/syscall.S @@ -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 diff --git a/ostd/src/arch/x86/trap/trap.S b/ostd/src/arch/x86/trap/trap.S index 3a308643..7d472573 100644 --- a/ostd/src/arch/x86/trap/trap.S +++ b/ostd/src/arch/x86/trap/trap.S @@ -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