diff --git a/src/context/context.rs b/src/context/context.rs index 23332a62c0..0d797ef474 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -242,8 +242,7 @@ impl Context { ens: SchemeNamespace::from(0), sig: SignalState { pending: 0, - procmask: 0, - + procmask: !0, handler: None, }, umask: 0o022, diff --git a/src/context/signal.rs b/src/context/signal.rs index c6ce99e258..729aa3d686 100644 --- a/src/context/signal.rs +++ b/src/context/signal.rs @@ -1,5 +1,5 @@ use alloc::sync::Arc; -use core::mem::{self, size_of}; +use core::mem::size_of; use syscall::{ flag::{ PTRACE_FLAG_IGNORE, PTRACE_STOP_SIGNAL, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, @@ -14,12 +14,7 @@ use crate::{ syscall::usercopy::UserSlice, }; -pub fn is_user_handled(handler: Option) -> bool { - let handler = handler.map(|ptr| ptr as usize).unwrap_or(0); - handler != SIG_DFL && handler != SIG_IGN -} - -// TODO: Move everything but SIGKILL to userspace. SIGCONT and SIGSTOP does not necessarily need to +// TODO: Move everything but SIGKILL to userspace. SIGCONT and SIGSTOP do not necessarily need to // be done from this current context. pub fn signal_handler() { let (action, sig) = { @@ -29,6 +24,9 @@ pub fn signal_handler() { // Lowest-numbered signal first. // TODO: randomly? + if context.sig.deliverable() == 0 { + return; + } let selected = context.sig.deliverable().trailing_zeros() as usize + 1; context.sig.pending &= !(1 << (selected - 1)); @@ -46,8 +44,7 @@ pub fn signal_handler() { if sig != SIGKILL && thumbs_down.unwrap_or(false) { // If signal can be and was ignored - crate::syscall::sigreturn().unwrap(); - unreachable!(); + return; } if handler == SIG_DFL { @@ -166,7 +163,7 @@ pub fn signal_handler() { context.sig.procmask |= action.sa_mask; if !action.sa_flags.contains(SigActionFlags::SA_NODEFER) { - context.sig.procmask &= !(1 << (sig - 1)); + context.sig.procmask |= 1 << (sig - 1); } let Some(regs) = context.regs_mut() else { diff --git a/src/context/switch.rs b/src/context/switch.rs index e6c41a6577..8f7bd71bc3 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -12,30 +12,37 @@ use crate::{ use super::{ContextId, Status}; -unsafe fn update_runnable(context: &mut Context, cpu_id: LogicalCpuId) -> bool { +enum UpdateResult { + CanSwitch { signal: bool }, + Skip, +} + +unsafe fn update_runnable(context: &mut Context, cpu_id: LogicalCpuId) -> UpdateResult { // Ignore already running contexts if context.running { - return false; + return UpdateResult::Skip; } // Ignore contexts stopped by ptrace if context.ptrace_stop { - return false; + return UpdateResult::Skip; } // Ignore contexts assigned to other CPUs if !context.sched_affinity.contains(cpu_id) { - return false; + return UpdateResult::Skip; } //TODO: HACK TO WORKAROUND HANGS BY PINNING TO ONE CPU if !context.cpu_id.map_or(true, |x| x == cpu_id) { - return false; + return UpdateResult::Skip; } + let signal = context.sig.deliverable() != 0; + // Unblock when there are pending nonmasked signals. - if matches!(context.status, Status::Blocked) && context.sig.deliverable() != 0 { - context.unblock(); + if matches!(context.status, Status::Blocked) && signal { + context.unblock_no_ipi(); } // Wake from sleep @@ -50,7 +57,11 @@ unsafe fn update_runnable(context: &mut Context, cpu_id: LogicalCpuId) -> bool { } // Switch to context if it needs to run - context.status.is_runnable() + if context.status.is_runnable() { + UpdateResult::CanSwitch { signal } + } else { + UpdateResult::Skip + } } struct SwitchResult { @@ -144,9 +155,10 @@ pub unsafe fn switch() -> bool { let mut next_context_guard = next_context_lock.write_arc(); // Update state of next context and check if runnable - if update_runnable(&mut *next_context_guard, cpu_id) { + if let UpdateResult::CanSwitch { signal } = update_runnable(&mut *next_context_guard, cpu_id) { // Store locks for previous and next context switch_context_opt = Some((prev_context_guard, next_context_guard)); + percpu.switch_internals.switch_signal.set(signal); break; } else { continue; @@ -186,6 +198,10 @@ pub unsafe fn switch() -> bool { arch::switch_to(prev_context, next_context); + if percpu.switch_internals.switch_signal.replace(false) { + crate::context::signal::signal_handler(); + } + // NOTE: After switch_to is called, the return address can even be different from the // current return address, meaning that we cannot use local variables here, and that we // need to use the `switch_finish_hook` to be able to release the locks. @@ -209,6 +225,7 @@ pub struct ContextSwitchPercpu { // The ID of the idle process idle_id: Cell, + switch_signal: Cell, } impl ContextSwitchPercpu { pub fn context_id(&self) -> ContextId { diff --git a/src/ptrace.rs b/src/ptrace.rs index d9f95318cb..13073e4627 100644 --- a/src/ptrace.rs +++ b/src/ptrace.rs @@ -10,7 +10,7 @@ use crate::{ syscall::{data::PtraceEvent, error::*, flag::*, ptrace_event}, }; -use alloc::{boxed::Box, collections::VecDeque, sync::Arc}; +use alloc::{collections::VecDeque, sync::Arc}; use core::cmp; use hashbrown::hash_map::{Entry, HashMap}; use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 4f0d82ac17..68bfa8f331 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -11,7 +11,7 @@ use crate::{ scheme::{self, FileHandle, KernelScheme}, syscall::{ self, - data::{GrantDesc, Map, PtraceEvent, SigAction, Stat}, + data::{GrantDesc, Map, PtraceEvent, SigAction, SetSighandlerData, Stat}, error::*, flag::*, usercopy::{UserSliceRo, UserSliceWo}, @@ -934,15 +934,17 @@ impl KernelScheme for ProcScheme { &mut 0, ), - // TODO: Struct Operation::Sighandler => { let handler = context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().sig.handler; - let [entry, altstack_base, altstack_len] = buf.in_exact_chunks(mem::size_of::()).next_chunk().map_err(|_| Error::new(EINVAL))?; - entry.write_usize(handler.map_or(0, |h| h.handler.get()))?; let altstack = handler.and_then(|h| h.altstack); - altstack_base.write_usize(altstack.map_or(0, |a| a.base.get()))?; - altstack_len.write_usize(altstack.map_or(0, |a| a.len.get()))?; - Ok(3 * mem::size_of::()) + let data = SetSighandlerData { + entry: handler.map_or(0, |h| h.handler.get()), + altstack_base: altstack.map_or(0, |a| a.base.get()), + altstack_len: altstack.map_or(0, |a| a.len.get()), + }; + buf.copy_exactly(&data)?; + + Ok(mem::size_of::()) } Operation::Sigprocmask => { let procmask = context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().sig.procmask; @@ -1190,14 +1192,13 @@ impl KernelScheme for ProcScheme { Ok(buf.len()) } - // TODO: Struct Operation::Sighandler => { - let [handler, altstack_base, altstack_len] = buf.usizes().next_chunk().map_err(|_| Error::new(EINVAL))?; + let data = unsafe { buf.read_exact::()? }; - let new_handler = match NonZeroUsize::new(handler?) { + let new_handler = match NonZeroUsize::new(data.entry) { Some(handler) => Some(SignalHandler { handler, - altstack: match (NonZeroUsize::new(altstack_base?), NonZeroUsize::new(altstack_len?)) { + altstack: match (NonZeroUsize::new(data.altstack_base), NonZeroUsize::new(data.altstack_len)) { (Some(base), Some(len)) => Some(Altstack { base, len }), _ => None, } @@ -1207,7 +1208,7 @@ impl KernelScheme for ProcScheme { context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.write().sig.handler = new_handler; - Ok(3 * mem::size_of::()) + Ok(mem::size_of::()) } Operation::Sigprocmask => { let new_procmask = buf.read_u64()?; @@ -1535,6 +1536,8 @@ fn inherit_context() -> Result { let current_context_lock = Arc::clone(context::contexts().current().ok_or(Error::new(ESRCH))?); let new_context_lock = Arc::clone(context::contexts_mut().spawn(true, clone_handler)?); + // (Starts with "all signals blocked".) + let current_context = current_context_lock.read(); let mut new_context = new_context_lock.write(); @@ -1554,13 +1557,6 @@ fn inherit_context() -> Result { new_context.session_id = current_context.session_id; new_context.umask = current_context.umask; - // Start with "all signals blocked". - new_context.sig = SignalState { - pending: 0, - procmask: !0, - handler: None, - }; - new_context.id }; diff --git a/syscall b/syscall index 0652a057f9..f1719f57df 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 0652a057f9d28ed556dbcd1c2a9ae4a72d697298 +Subproject commit f1719f57dffd1edf35aa3564dad45caf26b5d374