WIP: Separate process status from context status.
This commit is contained in:
@@ -16,7 +16,7 @@ use crate::{
|
||||
memory::{allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame},
|
||||
paging::{RmmA, RmmArch},
|
||||
percpu::PercpuBlock,
|
||||
scheme::FileHandle,
|
||||
scheme::FileHandle, sync::WaitCondition,
|
||||
};
|
||||
|
||||
use crate::syscall::error::{Error, Result, EAGAIN, ESRCH};
|
||||
@@ -43,9 +43,7 @@ pub enum Status {
|
||||
HardBlocked {
|
||||
reason: HardBlockedReason,
|
||||
},
|
||||
|
||||
Stopped(usize),
|
||||
Exited(usize),
|
||||
Exited { user_data: usize },
|
||||
}
|
||||
|
||||
impl Status {
|
||||
@@ -183,6 +181,7 @@ pub struct Context {
|
||||
pub userspace: bool,
|
||||
pub being_sigkilled: bool,
|
||||
pub fmap_ret: Option<Frame>,
|
||||
pub status_cond: Arc<WaitCondition>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -228,6 +227,7 @@ impl Context {
|
||||
userspace: false,
|
||||
fmap_ret: None,
|
||||
being_sigkilled: false,
|
||||
status_cond: Arc::new(WaitCondition::new()),
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
|
||||
@@ -85,6 +85,7 @@ pub fn init() {
|
||||
info: ProcessInfo::default(),
|
||||
waitpid: Arc::new(WaitMap::new()),
|
||||
threads: Vec::new(),
|
||||
status: process::ProcessStatus::PossiblyRunnable,
|
||||
}))
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ pub struct Process {
|
||||
pub info: ProcessInfo,
|
||||
/// Context is being waited on
|
||||
pub waitpid: Arc<WaitMap<WaitpidKey, (ProcessId, usize)>>,
|
||||
pub status: ProcessStatus,
|
||||
/// Threads of process
|
||||
pub threads: Vec<Weak<RwSpinlock<Context>>>,
|
||||
}
|
||||
@@ -69,6 +70,12 @@ impl DerefMut for Process {
|
||||
&mut self.info
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum ProcessStatus {
|
||||
PossiblyRunnable,
|
||||
Stopped(usize),
|
||||
Exited(usize),
|
||||
}
|
||||
|
||||
pub const INIT: ProcessId = ProcessId::new(1);
|
||||
static NEXT_PID: AtomicProcessId = AtomicProcessId::new(INIT);
|
||||
@@ -110,6 +117,7 @@ pub fn new_process(info: impl FnOnce(ProcessId) -> ProcessInfo) -> Result<Arc<Rw
|
||||
let proc = Arc::try_new(RwLock::new(Process {
|
||||
waitpid: Arc::try_new(WaitMap::new()).map_err(|_| Error::new(ENOMEM))?,
|
||||
threads: Vec::new(),
|
||||
status: ProcessStatus::PossiblyRunnable,
|
||||
info: info(pid),
|
||||
}))
|
||||
.map_err(|_| Error::new(ENOMEM))?;
|
||||
|
||||
@@ -114,6 +114,8 @@ enum ProcHandle {
|
||||
}
|
||||
#[derive(Clone)]
|
||||
enum ContextHandle {
|
||||
Status,
|
||||
|
||||
Regs(RegsKind),
|
||||
Name,
|
||||
Sighandler,
|
||||
@@ -1410,6 +1412,16 @@ impl ContextHandle {
|
||||
|
||||
Ok(mem::size_of_val(&mask))
|
||||
}
|
||||
Self::Status => {
|
||||
let user_data = buf.read_usize()?;
|
||||
let mut context = context.write();
|
||||
|
||||
// TODO: Handle Status::HardBlocked differently?
|
||||
context.status = Status::Exited { user_data };
|
||||
context.status_cond.notify();
|
||||
|
||||
Ok(mem::size_of::<usize>())
|
||||
}
|
||||
Self::OpenViaDup
|
||||
| Self::AwaitingAddrSpaceChange { .. }
|
||||
| Self::AwaitingFiletableChange { .. } => Err(Error::new(EBADF)),
|
||||
@@ -1520,6 +1532,26 @@ impl ContextHandle {
|
||||
buf.copy_exactly(crate::cpu_set::mask_as_bytes(&mask))?;
|
||||
Ok(mem::size_of_val(&mask))
|
||||
} // TODO: Replace write() with SYS_DUP_FORWARD.
|
||||
|
||||
ContextHandle::Status => {
|
||||
// Writing to the status explicitly exits the current thread.
|
||||
let cond = Arc::clone(&context.read().status_cond);
|
||||
let user_data = loop {
|
||||
let mut context = context.write();
|
||||
|
||||
if let Status::Exited { user_data } = context.status {
|
||||
break user_data;
|
||||
}
|
||||
|
||||
if !cond.wait(context, "waiting for thread") {
|
||||
return Err(Error::new(EINTR));
|
||||
}
|
||||
};
|
||||
|
||||
buf.write_usize(user_data)?;
|
||||
Ok(mem::size_of::<usize>())
|
||||
}
|
||||
|
||||
// TODO: Find a better way to switch address spaces, since they also require switching
|
||||
// the instruction and stack pointer. Maybe remove `<pid>/regs` altogether and replace it
|
||||
// with `<pid>/ctx`
|
||||
|
||||
@@ -53,10 +53,7 @@ pub fn resource() -> Result<Vec<u8>> {
|
||||
stat_string.push('B');
|
||||
}
|
||||
}
|
||||
context::Status::Stopped(_sig) => {
|
||||
stat_string.push('T');
|
||||
}
|
||||
context::Status::Exited(_status) => {
|
||||
context::Status::Exited { .. } => {
|
||||
stat_string.push('Z');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ impl WaitCondition {
|
||||
}
|
||||
|
||||
// Wait until notified. Unlocks guard when blocking is ready. Returns false if resumed by a signal or the notify_signal function
|
||||
pub fn wait<T>(&self, guard: MutexGuard<T>, reason: &'static str) -> bool {
|
||||
pub fn wait<T>(&self, guard: T, reason: &'static str) -> bool {
|
||||
let current_context_ref = context::current();
|
||||
{
|
||||
{
|
||||
|
||||
+43
-39
@@ -1,4 +1,5 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use spinning_top::RwSpinlock;
|
||||
use core::{mem, num::NonZeroUsize, sync::atomic::Ordering};
|
||||
use syscall::{sig_bit, SIGCHLD, SIGKILL, SIGSTOP, SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU};
|
||||
|
||||
@@ -6,9 +7,7 @@ use rmm::Arch;
|
||||
use spin::RwLock;
|
||||
|
||||
use crate::context::{
|
||||
memory::{AddrSpace, Grant, PageSpan},
|
||||
process::{self, ProcessId, ProcessInfo},
|
||||
WaitpidKey,
|
||||
memory::{AddrSpace, Grant, PageSpan}, process::{self, Process, ProcessId, ProcessInfo, ProcessStatus}, Context, WaitpidKey
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -78,7 +77,7 @@ pub fn exit(status: usize) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
context_lock.write().status = context::Status::Exited(status);
|
||||
process_lock.write().status = ProcessStatus::Exited(status);
|
||||
|
||||
let children = process_lock.write().waitpid.receive_all();
|
||||
|
||||
@@ -174,11 +173,11 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result<usize> {
|
||||
},
|
||||
}
|
||||
|
||||
let mut send = |context: &mut context::Context, proc: &ProcessInfo| -> SendResult {
|
||||
let is_self = context.is_current_context();
|
||||
let mut send = |context_lock: &Arc<RwSpinlock<Context>>, process_lock: &Arc<RwLock<Process>>, proc_info: &ProcessInfo| -> SendResult {
|
||||
let is_self = context::is_current(context_lock);
|
||||
|
||||
// Non-root users cannot kill arbitrarily.
|
||||
if euid != 0 && euid != proc.ruid && ruid != proc.ruid {
|
||||
if euid != 0 && euid != proc_info.ruid && ruid != proc_info.ruid {
|
||||
return SendResult::Forbidden;
|
||||
}
|
||||
// If sig = 0, test that process exists and can be signalled, but don't send any
|
||||
@@ -187,15 +186,18 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result<usize> {
|
||||
return SendResult::Succeeded;
|
||||
}
|
||||
|
||||
let mut process_guard = process_lock.write();
|
||||
|
||||
if sig == SIGCONT
|
||||
&& let context::Status::Stopped(_sig) = context.status
|
||||
&& let ProcessStatus::Stopped(_sig) = process_guard.status
|
||||
{
|
||||
// Convert stopped processes to blocked if sending SIGCONT, regardless of whether
|
||||
// SIGCONT is blocked or ignored. It can however be controlled whether the process
|
||||
// will additionally ignore, defer, or handle that signal.
|
||||
context.status = context::Status::Runnable;
|
||||
process_guard.status = ProcessStatus::PossiblyRunnable;
|
||||
drop(process_guard);
|
||||
|
||||
if let Some((tctl, pctl, _st)) = context.sigcontrol() {
|
||||
if let Some((tctl, pctl, _st)) = context_lock.write().sigcontrol() {
|
||||
if !pctl.signal_will_ign(SIGCONT, false) {
|
||||
tctl.word[0].fetch_or(sig_bit(SIGCONT), Ordering::Relaxed);
|
||||
}
|
||||
@@ -205,39 +207,47 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result<usize> {
|
||||
}
|
||||
}
|
||||
// POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs.
|
||||
SendResult::SucceededSigcont {
|
||||
ppid: proc.ppid,
|
||||
pgid: proc.pgid,
|
||||
}
|
||||
} else if sig == SIGSTOP
|
||||
return SendResult::SucceededSigcont {
|
||||
ppid: proc_info.ppid,
|
||||
pgid: proc_info.pgid,
|
||||
};
|
||||
}
|
||||
let mut context_guard = context_lock.write();
|
||||
if sig == SIGSTOP
|
||||
|| (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP)
|
||||
&& context
|
||||
&& context_guard
|
||||
.sigcontrol()
|
||||
.map_or(false, |(_, proc, _)| proc.signal_will_stop(sig)))
|
||||
{
|
||||
context.status = context::Status::Stopped(sig);
|
||||
context_guard.status = context::Status::Blocked;
|
||||
// TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise.
|
||||
if let Some((ctl, _, _)) = context.sigcontrol() {
|
||||
if let Some((ctl, _, _)) = context_guard.sigcontrol() {
|
||||
ctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed);
|
||||
}
|
||||
SendResult::SucceededSigchld {
|
||||
ppid: proc.ppid,
|
||||
pgid: proc.pgid,
|
||||
drop(context_guard);
|
||||
process_lock.write().status = ProcessStatus::Stopped(sig);
|
||||
return SendResult::SucceededSigchld {
|
||||
ppid: proc_info.ppid,
|
||||
pgid: proc_info.pgid,
|
||||
orig_signal: sig,
|
||||
}
|
||||
} else if sig == SIGKILL {
|
||||
context.being_sigkilled = true;
|
||||
context.unblock();
|
||||
};
|
||||
}
|
||||
if sig == SIGKILL {
|
||||
context_guard.being_sigkilled = true;
|
||||
context_guard.unblock();
|
||||
drop(context_guard);
|
||||
process_lock.write().status = ProcessStatus::Exited(SIGKILL);
|
||||
killed_self |= is_self;
|
||||
|
||||
// exit() will signal the parent, rather than immediately in kill()
|
||||
SendResult::Succeeded
|
||||
} else if let Some((tctl, pctl, _st)) = context.sigcontrol()
|
||||
return SendResult::Succeeded;
|
||||
}
|
||||
if let Some((tctl, pctl, _st)) = context_guard.sigcontrol()
|
||||
&& !pctl.signal_will_ign(sig, parent_sigchld)
|
||||
{
|
||||
let _was_new = tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Relaxed);
|
||||
if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 {
|
||||
context.unblock();
|
||||
context_guard.unblock();
|
||||
killed_self |= is_self;
|
||||
}
|
||||
SendResult::Succeeded
|
||||
@@ -313,8 +323,7 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result<usize> {
|
||||
process.info,
|
||||
)
|
||||
};
|
||||
let mut context = context_lock.write();
|
||||
let result = send(&mut *context, &info);
|
||||
let result = send(&context_lock, process_lock, &info);
|
||||
handle_send(pid, result)?;
|
||||
}
|
||||
} else if pid.get() == 1_usize.wrapping_neg() {
|
||||
@@ -333,14 +342,11 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result<usize> {
|
||||
)
|
||||
};
|
||||
|
||||
if info.pid.get() <= 2 {
|
||||
if info.pid.get() <= 1 {
|
||||
continue;
|
||||
}
|
||||
found += 1;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
let result = send(&mut *context, &info);
|
||||
drop(context);
|
||||
let result = send(&context_lock, process_lock, &info);
|
||||
handle_send(*pid, result)?;
|
||||
}
|
||||
} else {
|
||||
@@ -370,9 +376,7 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result<usize> {
|
||||
}
|
||||
found += 1;
|
||||
|
||||
let mut context = context_lock.write();
|
||||
let result = send(&mut *context, &info);
|
||||
drop(context);
|
||||
let result = send(&context_lock, process_lock, &info);
|
||||
|
||||
handle_send(*pid, result)?;
|
||||
}
|
||||
@@ -600,7 +604,7 @@ pub fn waitpid(
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(context::Status::Exited(status)) = hack_status {
|
||||
if let Some(ProcessStatus::Exited(_status)) = hack_status {
|
||||
/*let _ = waitpid.receive_nonblock(&WaitpidKey {
|
||||
pid: Some(pid),
|
||||
pgid: None,
|
||||
|
||||
Reference in New Issue
Block a user