diff --git a/src/context/context.rs b/src/context/context.rs
index 6d723f498f..bc2a76c620 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -100,6 +100,10 @@ pub struct Context {
pub switch_time: u128,
/// Amount of CPU time used
pub cpu_time: u128,
+ /// User-mode CPU time used
+ pub utime: u128,
+ /// Kernel-mode CPU time used
+ pub stime: u128,
/// Scheduler CPU affinity. If set, [`cpu_id`] can except [`None`] never be anything else than
/// this value.
pub sched_affinity: LogicalCpuSet,
@@ -140,6 +144,12 @@ pub struct Context {
pub fmap_ret: Option,
/// Priority
pub prio: usize,
+ /// Process group ID.
+ pub pgrp: usize,
+ /// Session ID.
+ pub session: usize,
+ /// Context start time in nanoseconds since boot.
+ pub start_time: u128,
// TODO: id can reappear after wraparound?
pub owner_proc_id: Option,
@@ -186,6 +196,8 @@ impl Context {
cpu_id: None,
switch_time: 0,
cpu_time: 0,
+ utime: 0,
+ stime: 0,
sched_affinity: LogicalCpuSet::all(),
inside_syscall: false,
syscall_head: SyscallFrame::Free(RaiiFrame::allocate()?),
@@ -207,6 +219,9 @@ impl Context {
egid: 0,
pid: 0,
groups: Vec::new(),
+ pgrp: 0,
+ session: 0,
+ start_time: 0,
#[cfg(feature = "syscall_debug")]
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
diff --git a/src/context/mod.rs b/src/context/mod.rs
index 37c73f5a37..7e125f0e2a 100644
--- a/src/context/mod.rs
+++ b/src/context/mod.rs
@@ -131,6 +131,7 @@ pub fn init(token: &mut CleanLockToken) {
context.status = Status::Runnable;
context.running = true;
context.cpu_id = Some(crate::cpu_id());
+ context.start_time = crate::time::monotonic(token);
let context_lock = Arc::new(ContextLock::new(context));
@@ -223,6 +224,9 @@ pub fn spawn(
let stack = Kstack::new()?;
let mut context = Context::new(owner_proc_id)?;
+ context.start_time = crate::time::monotonic(token);
+ context.pgrp = context.pid;
+ context.session = context.pid;
let _ = context.set_addr_space(Some(AddrSpaceWrapper::new()?), token.downgrade());
context
diff --git a/src/context/switch.rs b/src/context/switch.rs
index 86684c8f4c..39fa424237 100644
--- a/src/context/switch.rs
+++ b/src/context/switch.rs
@@ -221,7 +221,13 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
// Update times
if !was_idle {
- prev_context.cpu_time += switch_time.saturating_sub(prev_context.switch_time);
+ let delta = switch_time.saturating_sub(prev_context.switch_time);
+ prev_context.cpu_time += delta;
+ if prev_context.userspace {
+ prev_context.utime += delta;
+ } else {
+ prev_context.stime += delta;
+ }
}
next_context.switch_time = switch_time;
if next_context.userspace {
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
index e133da4960..ba95f34ed7 100644
--- a/src/scheme/proc.rs
+++ b/src/scheme/proc.rs
@@ -89,7 +89,9 @@ fn try_stop_context(
let (context, token) = context.token_split();
let ret = callback(context, token);
- context.status = prev_status;
+ if matches!(context.status, Status::HardBlocked { reason: HardBlockedReason::NotYetStarted }) {
+ context.status = prev_status;
+ }
ret
}
@@ -112,7 +114,9 @@ enum ContextHandle {
privileged: bool,
}, // can write ContextVerb
+ /// Deprecated compatibility handles; prefer `ctx` for atomic save/restore.
Regs(RegsKind),
+ Ctx,
Sighandler,
Start,
NewFiletable {
@@ -237,6 +241,54 @@ fn proc_vsize(addr_space: &Option>, token: &mut CleanLockT
.unwrap_or(0)
}
+fn proc_rss(addr_space: &Option>, token: &mut CleanLockToken) -> usize {
+ addr_space
+ .as_ref()
+ .map(|addrspace| {
+ let guard = addrspace.acquire_read(token.downgrade());
+ guard
+ .grants
+ .iter()
+ .map(|(_, info)| {
+ use crate::context::memory::Provider;
+
+ match info.provider {
+ Provider::Allocated { .. }
+ | Provider::AllocatedShared { .. }
+ | Provider::PhysBorrowed { .. }
+ | Provider::External { .. }
+ | Provider::FmapBorrowed { .. } => info.page_count() * PAGE_SIZE,
+ }
+ })
+ .sum()
+ })
+ .unwrap_or(0)
+}
+
+fn require_zero_offset(offset: u64) -> Result<()> {
+ if offset == 0 {
+ Ok(())
+ } else {
+ Err(Error::new(EINVAL))
+ }
+}
+
+fn validate_kfmap_flags(flags: MapFlags, consume: bool) -> Result<()> {
+ let flags = MapFlags::from_bits(flags.bits()).ok_or(Error::new(EINVAL))?;
+ let shared = flags.contains(MapFlags::MAP_SHARED);
+ let private = flags.contains(MapFlags::MAP_PRIVATE);
+ if shared == private {
+ return Err(Error::new(EINVAL));
+ }
+ if flags.contains(MapFlags::MAP_FIXED) && flags.contains(MapFlags::MAP_FIXED_NOREPLACE) {
+ return Err(Error::new(EINVAL));
+ }
+ if consume && !shared {
+ return Err(Error::new(EINVAL));
+ }
+ Ok(())
+}
+
fn proc_context(pid: usize, token: &mut CleanLockToken) -> Option> {
let mut contexts = context::contexts(token.downgrade());
let (contexts, mut token2) = contexts.token_split();
@@ -254,7 +306,14 @@ fn proc_stat_line(
ppid: usize,
priority: usize,
utime: u128,
+ stime: u128,
+ pgrp: usize,
+ session: usize,
+ nice: i32,
+ num_threads: usize,
+ starttime: u128,
vsize: usize,
+ rss: usize,
) -> String {
format!(
"{pid} ({comm}) {state} {ppid} {pgrp} {session} {tty_nr} {tpgid} {flags} {minflt} {cminflt} {majflt} {cmajflt} {utime} {stime} {cutime} {cstime} {priority} {nice} {num_threads} {itrealvalue} {starttime} {vsize} {rss} {rsslim}\n",
@@ -262,8 +321,8 @@ fn proc_stat_line(
comm = comm,
state = state,
ppid = ppid,
- pgrp = ppid,
- session = ppid,
+ pgrp = pgrp,
+ session = session,
tty_nr = 0,
tpgid = 0,
flags = 0,
@@ -272,32 +331,41 @@ fn proc_stat_line(
majflt = 0,
cmajflt = 0,
utime = utime,
- stime = 0,
+ stime = stime,
cutime = 0,
cstime = 0,
priority = priority,
- nice = 0,
- num_threads = 1,
+ nice = nice,
+ num_threads = num_threads,
itrealvalue = 0,
- starttime = 0,
+ starttime = starttime,
vsize = vsize,
- rss = 0,
+ rss = rss / PAGE_SIZE,
rsslim = 0,
)
}
-fn proc_status_text(context: &Context) -> String {
+fn proc_status_text(context: &Context, num_threads: usize, vsize: usize, rss: usize) -> String {
let comm = context.name.as_str();
let state = proc_status_char(&context.status);
let ppid = context.owner_proc_id.map_or(0, NonZeroUsize::get);
format!(
- "Name:\t{}\nState:\t{}\nPid:\t{}\nPPid:\t{}\nUid:\t{}\nGid:\t{}\nThreads:\t1\nCpus_allowed_list:\t{}\n",
+ "Name:\t{}\nState:\t{}\nPid:\t{}\nPPid:\t{}\nUid:\t{}\t{}\t{}\t{}\nGid:\t{}\t{}\t{}\t{}\nVmSize:\t{} kB\nVmRSS:\t{} kB\nThreads:\t{}\nCpus_allowed_list:\t{}\n",
comm,
state,
context.pid,
ppid,
context.euid,
+ context.euid,
+ context.euid,
+ context.euid,
context.egid,
+ context.egid,
+ context.egid,
+ context.egid,
+ vsize / 1024,
+ rss / 1024,
+ num_threads,
context.sched_affinity
)
}
@@ -361,6 +429,8 @@ impl ProcScheme {
),
"current-addrspace" => (ContextHandle::CurrentAddrSpace, false),
"current-filetable" => (ContextHandle::CurrentFiletable, false),
+ "ctx" => (ContextHandle::Ctx, false),
+ // Deprecated compatibility handles; prefer `ctx` for atomic save/restore.
"regs/float" => (ContextHandle::Regs(RegsKind::Float), false),
"regs/int" => (ContextHandle::Regs(RegsKind::Int), false),
"regs/env" => (ContextHandle::Regs(RegsKind::Env), false),
@@ -667,7 +737,7 @@ impl KernelScheme for ProcScheme {
let mut notify_files = Vec::new();
- // TODO: Validate flags
+ validate_kfmap_flags(map.flags, consume)?;
let result_base = if consume {
dst_addr_space.r#move(
Some((addrspace, &mut *src_addr_space)),
@@ -802,7 +872,7 @@ impl KernelScheme for ProcScheme {
&self,
id: usize,
buf: UserSliceRo,
- _offset: u64,
+ offset: u64,
_fcntl_flags: u32,
_stored_flags: u32,
token: &mut CleanLockToken,
@@ -817,7 +887,7 @@ impl KernelScheme for ProcScheme {
};
let Handle { context, kind } = handle;
- kind.kwriteoff(id, context, buf, token)
+ kind.kwriteoff(id, context, buf, offset, token)
}
fn kfpath(&self, _id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result {
@@ -1091,12 +1161,21 @@ impl ContextHandle {
id: usize,
context: Arc,
buf: UserSliceRo,
+ offset: u64,
token: &mut CleanLockToken,
) -> Result {
match self {
Self::AddrSpace { addrspace } => {
let mut chunks = buf.usizes();
let mut words_read = 0;
+ let word_size = size_of::() as u64;
+ if offset % word_size != 0 {
+ return Err(Error::new(EINVAL));
+ }
+ let offset_words = usize::try_from(offset / word_size).map_err(|_| Error::new(EINVAL))?;
+ for _ in 0..offset_words {
+ chunks.next().ok_or(Error::new(EINVAL))??;
+ }
let mut next = || {
words_read += 1;
chunks.next().ok_or(Error::new(EINVAL))
@@ -1164,8 +1243,24 @@ impl ContextHandle {
}
Ok(words_read * size_of::())
}
+ ContextHandle::Ctx => {
+ require_zero_offset(offset)?;
+ let regs = unsafe { buf.read_exact::()? };
+ try_stop_context(context, token, |context, _| {
+ context.set_fx_regs(regs.float);
+ match context.regs_mut() {
+ None => Err(Error::new(ENOTRECOVERABLE)),
+ Some(stack) => {
+ stack.load(®s.int);
+ context.write_env_regs(regs.env)
+ }
+ }
+ })?;
+ Ok(size_of::())
+ }
ContextHandle::Regs(kind) => match kind {
RegsKind::Float => {
+ require_zero_offset(offset)?;
let regs = unsafe { buf.read_exact::()? };
try_stop_context(context, token, |context, _| {
@@ -1179,6 +1274,7 @@ impl ContextHandle {
})
}
RegsKind::Int => {
+ require_zero_offset(offset)?;
let regs = unsafe { buf.read_exact::()? };
try_stop_context(context, token, |context, _| match context.regs_mut() {
@@ -1198,12 +1294,14 @@ impl ContextHandle {
})
}
RegsKind::Env => {
+ require_zero_offset(offset)?;
let regs = unsafe { buf.read_exact::()? };
write_env_regs(context, regs, token)?;
Ok(size_of::())
}
},
ContextHandle::Sighandler => {
+ require_zero_offset(offset)?;
let data = unsafe { buf.read_exact::()? };
if data.user_handler >= crate::USER_END_OFFSET
@@ -1259,6 +1357,7 @@ impl ContextHandle {
Ok(size_of::())
}
ContextHandle::Start => match context.write(token.token()).status {
+ _ if offset != 0 => Err(Error::new(EINVAL)),
ref mut status @ Status::HardBlocked {
reason: HardBlockedReason::NotYetStarted,
} => {
@@ -1272,6 +1371,7 @@ impl ContextHandle {
}
ContextHandle::CurrentFiletable => {
+ require_zero_offset(offset)?;
let filetable_fd = buf.read_usize()?;
let (hopefully_this_scheme, number) = extract_scheme_number(filetable_fd, token)?;
verify_scheme(hopefully_this_scheme)?;
@@ -1317,6 +1417,7 @@ impl ContextHandle {
Ok(size_of::())
}
ContextHandle::CurrentAddrSpace => {
+ require_zero_offset(offset)?;
let mut iter = buf.usizes();
let addrspace_fd = iter.next().ok_or(Error::new(EINVAL))??;
let sp = iter.next().ok_or(Error::new(EINVAL))??;
@@ -1354,6 +1455,7 @@ impl ContextHandle {
Ok(written)
}
Self::MmapMinAddr(ref addrspace) => {
+ require_zero_offset(offset)?;
let val = buf.read_usize()?;
if val % PAGE_SIZE != 0 || val > crate::USER_END_OFFSET {
return Err(Error::new(EINVAL));
@@ -1363,6 +1465,7 @@ impl ContextHandle {
Ok(size_of::())
}
Self::SchedAffinity => {
+ require_zero_offset(offset)?;
let mask = unsafe { buf.read_exact::()? };
context
@@ -1398,7 +1501,16 @@ impl ContextHandle {
guard.status = Status::HardBlocked {
reason: HardBlockedReason::Stopped,
};
- // TODO: wait for context to be switched away from, and/or IPI?
+ drop(guard);
+
+ let mut spins = 10_000usize;
+ while context.read(token.token()).running {
+ if spins == 0 {
+ return Err(Error::new(EAGAIN));
+ }
+ spins -= 1;
+ context::switch(token);
+ }
Ok(size_of::())
}
ContextVerb::Unstop => {
@@ -1458,6 +1570,7 @@ impl ContextHandle {
}
}
ContextHandle::Attr => {
+ require_zero_offset(offset)?;
let info = unsafe { buf.read_exact::()? };
let mut guard = context.write(token.token());
@@ -1476,9 +1589,16 @@ impl ContextHandle {
guard.euid = info.euid;
guard.egid = info.egid;
guard.prio = (info.prio as usize).min(39);
+ if guard.pgrp == 0 {
+ guard.pgrp = guard.pid;
+ }
+ if guard.session == 0 {
+ guard.session = guard.pid;
+ }
Ok(size_of::())
}
Self::Groups => {
+ require_zero_offset(offset)?;
const NGROUPS_MAX: usize = 65536;
if buf.len() % size_of::() != 0 {
return Err(Error::new(EINVAL));
@@ -1512,6 +1632,7 @@ impl ContextHandle {
Ok(count * size_of::())
}
ContextHandle::OpenViaDup => {
+ require_zero_offset(offset)?;
let mut args = buf.usizes();
let user_data = args.next().ok_or(Error::new(EINVAL))??;
@@ -1566,6 +1687,35 @@ impl ContextHandle {
token: &mut CleanLockToken,
) -> Result {
match self {
+ ContextHandle::Ctx => {
+ require_zero_offset(offset)?;
+ let mut regs = syscall::FullContextRegs::default();
+ try_stop_context(context, token, |context, _| {
+ match context.regs() {
+ None => {
+ assert!(!context.running, "try_stop_context is broken, clearly");
+ println!(
+ "{}:{}: Couldn't read registers from stopped process",
+ file!(),
+ line!()
+ );
+ Err(Error::new(ENOTRECOVERABLE))
+ }
+ Some(stack) => {
+ stack.save(&mut regs.int);
+ regs.float = context.get_fx_regs();
+ regs.env = context.read_env_regs()?;
+ Ok(())
+ }
+ }
+ })?;
+ buf.copy_common_bytes_from_slice(unsafe {
+ slice::from_raw_parts(
+ ®s as *const syscall::FullContextRegs as *const u8,
+ size_of::(),
+ )
+ })
+ }
ContextHandle::Regs(kind) => {
union Output {
float: FloatRegisters,
@@ -1658,7 +1808,7 @@ impl ContextHandle {
ContextHandle::Filetable { data, .. } => read_from(buf, data, offset),
ContextHandle::ProcStat { pid } => {
let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?;
- let (pid, comm, state, ppid, priority, utime, addr_space) = {
+ let (pid, comm, state, ppid, priority, utime, stime, pgrp, session, starttime, owner_proc_id, addr_space) = {
let context = context.read(token.token());
(
context.pid,
@@ -1666,14 +1816,45 @@ impl ContextHandle {
proc_status_char(&context.status),
context.owner_proc_id.map_or(0, NonZeroUsize::get),
context.prio,
- context.cpu_time / crate::time::NANOS_PER_SEC,
+ context.utime / crate::time::NANOS_PER_SEC,
+ context.stime / crate::time::NANOS_PER_SEC,
+ context.pgrp,
+ context.session,
+ context.start_time / (crate::time::NANOS_PER_SEC / 100),
+ context.owner_proc_id,
context.addr_space.clone(),
)
};
+ let num_threads = {
+ let mut contexts = context::contexts(token.downgrade());
+ let (contexts, mut token2) = contexts.token_split();
+ contexts
+ .iter()
+ .filter(|ctx| ctx.read(token2.token()).owner_proc_id == owner_proc_id)
+ .count()
+ };
let vsize = proc_vsize(&addr_space, token);
+ let rss = proc_rss(&addr_space, token);
+ let nice = (priority as i32 / 2) - 20;
read_from(
buf,
- proc_stat_line(pid, &comm, state, ppid, priority, utime, vsize).as_bytes(),
+ proc_stat_line(
+ pid,
+ &comm,
+ state,
+ ppid,
+ priority,
+ utime,
+ stime,
+ pgrp,
+ session,
+ nice,
+ num_threads,
+ starttime,
+ vsize,
+ rss,
+ )
+ .as_bytes(),
offset,
)
}
@@ -1691,8 +1872,26 @@ impl ContextHandle {
}
ContextHandle::ProcStatus { pid } => {
let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?;
+ let owner_proc_id = context.read(token.token()).owner_proc_id;
+ let num_threads = {
+ let mut contexts = context::contexts(token.downgrade());
+ let (contexts, mut token2) = contexts.token_split();
+ contexts
+ .iter()
+ .filter(|ctx| ctx.read(token2.token()).owner_proc_id == owner_proc_id)
+ .count()
+ };
+ let (vsize, rss) = {
+ let addr_space = {
+ let context = context.read(token.token());
+ context.addr_space.clone()
+ };
+ let vsize = proc_vsize(&addr_space, token);
+ let rss = proc_rss(&addr_space, token);
+ (vsize, rss)
+ };
let context = context.read(token.token());
- read_from(buf, proc_status_text(&context).as_bytes(), offset)
+ read_from(buf, proc_status_text(&context, num_threads, vsize, rss).as_bytes(), offset)
}
ContextHandle::ProcRoot | ContextHandle::ProcDir { .. } => Err(Error::new(EBADF)),
ContextHandle::MmapMinAddr(addrspace) => {