mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-20 13:06:33 +00:00
Optimize the latency of lmbench-signal-prot
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
7b58d97aa2
commit
a72c7dadf3
@ -168,7 +168,7 @@ fn init_thread() {
|
||||
}
|
||||
|
||||
// TODO: exit via qemu isa debug device should not be the only way.
|
||||
let exit_code = if initproc.exit_code().unwrap() == 0 {
|
||||
let exit_code = if initproc.exit_code() == 0 {
|
||||
QemuExitCode::Success
|
||||
} else {
|
||||
QemuExitCode::Failed
|
||||
|
@ -3,7 +3,11 @@
|
||||
use super::{
|
||||
posix_thread::PosixThreadExt,
|
||||
process_table,
|
||||
signal::signals::{user::UserSignal, Signal},
|
||||
signal::{
|
||||
constants::SIGCONT,
|
||||
sig_num::SigNum,
|
||||
signals::{user::UserSignal, Signal},
|
||||
},
|
||||
Pgid, Pid, Process, Sid, Uid,
|
||||
};
|
||||
use crate::{
|
||||
@ -18,11 +22,27 @@ use crate::{
|
||||
///
|
||||
/// If `signal` is `None`, this method will only check permission without sending
|
||||
/// any signal.
|
||||
pub fn kill(pid: Pid, signal: Option<UserSignal>) -> Result<()> {
|
||||
pub fn kill(pid: Pid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
|
||||
// Fast path: If the signal is sent to self, we can skip most check.
|
||||
if pid == ctx.process.pid() {
|
||||
let Some(signal) = signal else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if !ctx.posix_thread.has_signal_blocked(signal.num()) {
|
||||
ctx.posix_thread.enqueue_signal(Box::new(signal));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
return kill_process(ctx.process, Some(signal), ctx);
|
||||
}
|
||||
|
||||
// Slow path
|
||||
|
||||
let process = process_table::get_process(pid)
|
||||
.ok_or_else(|| Error::with_message(Errno::ESRCH, "the target process does not exist"))?;
|
||||
|
||||
kill_process(&process, signal)
|
||||
kill_process(&process, signal, ctx)
|
||||
}
|
||||
|
||||
/// Sends a signal to all processes in a group, using the current process
|
||||
@ -33,13 +53,13 @@ pub fn kill(pid: Pid, signal: Option<UserSignal>) -> Result<()> {
|
||||
///
|
||||
/// If `signal` is `None`, this method will only check permission without sending
|
||||
/// any signal.
|
||||
pub fn kill_group(pgid: Pgid, signal: Option<UserSignal>) -> Result<()> {
|
||||
pub fn kill_group(pgid: Pgid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
|
||||
let process_group = process_table::get_process_group(&pgid)
|
||||
.ok_or_else(|| Error::with_message(Errno::ESRCH, "target group does not exist"))?;
|
||||
|
||||
let inner = process_group.inner.lock();
|
||||
for process in inner.processes.values() {
|
||||
kill_process(process, signal)?;
|
||||
kill_process(process, signal, ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -50,7 +70,7 @@ pub fn kill_group(pgid: Pgid, signal: Option<UserSignal>) -> Result<()> {
|
||||
///
|
||||
/// If `signal` is `None`, this method will only check permission without sending
|
||||
/// any signal.
|
||||
pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>) -> Result<()> {
|
||||
pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
|
||||
let thread = thread_table::get_thread(tid)
|
||||
.ok_or_else(|| Error::with_message(Errno::ESRCH, "target thread does not exist"))?;
|
||||
|
||||
@ -71,7 +91,7 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>) -> Result<()> {
|
||||
|
||||
// Check permission
|
||||
let signum = signal.map(|signal| signal.num());
|
||||
let sender = current_thread_sender_ids();
|
||||
let sender = current_thread_sender_ids(signum.as_ref(), ctx);
|
||||
posix_thread.check_signal_perm(signum.as_ref(), &sender)?;
|
||||
|
||||
if let Some(signal) = signal {
|
||||
@ -86,66 +106,74 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>) -> Result<()> {
|
||||
///
|
||||
/// The credentials of the current process will be checked to determine
|
||||
/// if it is authorized to send the signal to the target group.
|
||||
pub fn kill_all(signal: Option<UserSignal>) -> Result<()> {
|
||||
pub fn kill_all(signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
|
||||
let current = current!();
|
||||
for process in process_table::process_table().iter() {
|
||||
if Arc::ptr_eq(¤t, process) || process.is_init_process() {
|
||||
continue;
|
||||
}
|
||||
|
||||
kill_process(process, signal)?;
|
||||
kill_process(process, signal, ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn kill_process(process: &Process, signal: Option<UserSignal>) -> Result<()> {
|
||||
fn kill_process(process: &Process, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
|
||||
let threads = process.threads().lock();
|
||||
let posix_threads = threads
|
||||
.iter()
|
||||
.map(|thread| thread.as_posix_thread().unwrap());
|
||||
|
||||
// First check permission
|
||||
let signum = signal.map(|signal| signal.num());
|
||||
let sender_ids = current_thread_sender_ids();
|
||||
let mut permitted_threads = {
|
||||
posix_threads.clone().filter(|posix_thread| {
|
||||
posix_thread
|
||||
.check_signal_perm(signum.as_ref(), &sender_ids)
|
||||
.is_ok()
|
||||
})
|
||||
};
|
||||
let sender_ids = current_thread_sender_ids(signum.as_ref(), ctx);
|
||||
|
||||
if permitted_threads.clone().count() == 0 {
|
||||
return_errno_with_message!(Errno::EPERM, "cannot send signal to the target process");
|
||||
}
|
||||
let mut permitted_thread = None;
|
||||
for thread in threads.iter() {
|
||||
let posix_thread = thread.as_posix_thread().unwrap();
|
||||
|
||||
let Some(signal) = signal else { return Ok(()) };
|
||||
// First check permission
|
||||
if posix_thread
|
||||
.check_signal_perm(signum.as_ref(), &sender_ids)
|
||||
.is_ok()
|
||||
{
|
||||
let Some(ref signum) = signum else {
|
||||
// If signal is None, only permission check is required
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Send signal to any thread that does not blocks the signal.
|
||||
for thread in permitted_threads.clone() {
|
||||
if !thread.has_signal_blocked(&signal) {
|
||||
thread.enqueue_signal(Box::new(signal));
|
||||
return Ok(());
|
||||
if !posix_thread.has_signal_blocked(*signum) {
|
||||
// Send signal to any thread that does not blocks the signal.
|
||||
let signal = signal.unwrap();
|
||||
posix_thread.enqueue_signal(Box::new(signal));
|
||||
return Ok(());
|
||||
} else if permitted_thread.is_none() {
|
||||
permitted_thread = Some(posix_thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(permitted_thread) = permitted_thread else {
|
||||
return_errno_with_message!(Errno::EPERM, "cannot send signal to the target process");
|
||||
};
|
||||
|
||||
// If signal is None, only permission check is required
|
||||
let Some(signal) = signal else { return Ok(()) };
|
||||
|
||||
// If all threads block the signal, send signal to the first thread.
|
||||
let first_thread = permitted_threads.next().unwrap();
|
||||
first_thread.enqueue_signal(Box::new(signal));
|
||||
permitted_thread.enqueue_signal(Box::new(signal));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn current_thread_sender_ids() -> SignalSenderIds {
|
||||
let current_thread = current_thread!();
|
||||
let current_posix_thread = current_thread.as_posix_thread().unwrap();
|
||||
let current_process = current_posix_thread.process();
|
||||
|
||||
let credentials = current_posix_thread.credentials();
|
||||
fn current_thread_sender_ids(signum: Option<&SigNum>, ctx: &Context) -> SignalSenderIds {
|
||||
let credentials = ctx.posix_thread.credentials();
|
||||
let ruid = credentials.ruid();
|
||||
let euid = credentials.euid();
|
||||
let sid = current_process.session().unwrap().sid();
|
||||
let sid = signum.and_then(|signum| {
|
||||
if *signum == SIGCONT {
|
||||
Some(ctx.process.session().unwrap().sid())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
SignalSenderIds::new(ruid, euid, sid)
|
||||
}
|
||||
@ -156,11 +184,11 @@ fn current_thread_sender_ids() -> SignalSenderIds {
|
||||
pub(super) struct SignalSenderIds {
|
||||
ruid: Uid,
|
||||
euid: Uid,
|
||||
sid: Sid,
|
||||
sid: Option<Sid>,
|
||||
}
|
||||
|
||||
impl SignalSenderIds {
|
||||
fn new(ruid: Uid, euid: Uid, sid: Sid) -> Self {
|
||||
fn new(ruid: Uid, euid: Uid, sid: Option<Sid>) -> Self {
|
||||
Self { ruid, euid, sid }
|
||||
}
|
||||
|
||||
@ -172,7 +200,7 @@ impl SignalSenderIds {
|
||||
self.euid
|
||||
}
|
||||
|
||||
pub(super) fn sid(&self) -> Sid {
|
||||
pub(super) fn sid(&self) -> Option<Sid> {
|
||||
self.sid
|
||||
}
|
||||
}
|
||||
|
@ -117,8 +117,9 @@ impl PosixThread {
|
||||
}
|
||||
|
||||
/// Returns whether the signal is blocked by the thread.
|
||||
pub(in crate::process) fn has_signal_blocked(&self, signal: &dyn Signal) -> bool {
|
||||
self.sig_mask.contains(signal.num(), Ordering::Relaxed)
|
||||
pub(in crate::process) fn has_signal_blocked(&self, signum: SigNum) -> bool {
|
||||
// FIMXE: Some signals cannot be blocked, even set in sig_mask.
|
||||
self.sig_mask.contains(signum, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Checks whether the signal can be delivered to the thread.
|
||||
@ -141,7 +142,7 @@ impl PosixThread {
|
||||
&& *signum == SIGCONT
|
||||
{
|
||||
let receiver_sid = self.process().session().unwrap().sid();
|
||||
if receiver_sid == sender.sid() {
|
||||
if receiver_sid == sender.sid().unwrap() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ pub struct Process {
|
||||
/// The threads
|
||||
threads: Mutex<Vec<Arc<Thread>>>,
|
||||
/// Process status
|
||||
status: Mutex<ProcessStatus>,
|
||||
status: ProcessStatus,
|
||||
/// Parent process
|
||||
pub(super) parent: Mutex<Weak<Process>>,
|
||||
/// Children processes
|
||||
@ -140,7 +140,7 @@ impl Process {
|
||||
executable_path: RwLock::new(executable_path),
|
||||
process_vm,
|
||||
children_pauser,
|
||||
status: Mutex::new(ProcessStatus::Uninit),
|
||||
status: ProcessStatus::new_uninit(),
|
||||
parent: Mutex::new(parent),
|
||||
children: Mutex::new(BTreeMap::new()),
|
||||
process_group: Mutex::new(Weak::new()),
|
||||
@ -589,7 +589,7 @@ impl Process {
|
||||
let threads = self.threads.lock();
|
||||
for thread in threads.iter() {
|
||||
let posix_thread = thread.as_posix_thread().unwrap();
|
||||
if !posix_thread.has_signal_blocked(&signal) {
|
||||
if !posix_thread.has_signal_blocked(signal.num()) {
|
||||
posix_thread.enqueue_signal(Box::new(signal));
|
||||
return;
|
||||
}
|
||||
@ -622,26 +622,23 @@ impl Process {
|
||||
// ******************* Status ********************
|
||||
|
||||
fn set_runnable(&self) {
|
||||
self.status.lock().set_runnable();
|
||||
self.status.set_runnable();
|
||||
}
|
||||
|
||||
fn is_runnable(&self) -> bool {
|
||||
self.status.lock().is_runnable()
|
||||
self.status.is_runnable()
|
||||
}
|
||||
|
||||
pub fn is_zombie(&self) -> bool {
|
||||
self.status.lock().is_zombie()
|
||||
self.status.is_zombie()
|
||||
}
|
||||
|
||||
pub fn set_zombie(&self, term_status: TermStatus) {
|
||||
*self.status.lock() = ProcessStatus::Zombie(term_status);
|
||||
self.status.set_zombie(term_status);
|
||||
}
|
||||
|
||||
pub fn exit_code(&self) -> Option<ExitCode> {
|
||||
match &*self.status.lock() {
|
||||
ProcessStatus::Runnable | ProcessStatus::Uninit => None,
|
||||
ProcessStatus::Zombie(term_status) => Some(term_status.as_u32()),
|
||||
}
|
||||
pub fn exit_code(&self) -> ExitCode {
|
||||
self.status.exit_code()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,32 +4,56 @@
|
||||
|
||||
//! The process status
|
||||
|
||||
use super::TermStatus;
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProcessStatus {
|
||||
// Not ready to run
|
||||
Uninit,
|
||||
/// Can be scheduled to run
|
||||
Runnable,
|
||||
/// Exit while not reaped by parent
|
||||
Zombie(TermStatus),
|
||||
use super::{ExitCode, TermStatus};
|
||||
|
||||
/// The status of process.
|
||||
///
|
||||
/// The `ProcessStatus` can be viewed as two parts,
|
||||
/// the highest 32 bits is the value of `TermStatus`, if any,
|
||||
/// the lowest 32 bits is the value of status.
|
||||
#[derive(Debug)]
|
||||
pub struct ProcessStatus(AtomicU64);
|
||||
|
||||
#[repr(u8)]
|
||||
enum Status {
|
||||
Uninit = 0,
|
||||
Runnable = 1,
|
||||
Zombie = 2,
|
||||
}
|
||||
|
||||
impl ProcessStatus {
|
||||
pub fn set_zombie(&mut self, term_status: TermStatus) {
|
||||
*self = ProcessStatus::Zombie(term_status);
|
||||
const LOW_MASK: u64 = 0xffff_ffff;
|
||||
|
||||
pub fn new_uninit() -> Self {
|
||||
Self(AtomicU64::new(Status::Uninit as u64))
|
||||
}
|
||||
|
||||
pub fn set_zombie(&self, term_status: TermStatus) {
|
||||
let new_val = (term_status.as_u32() as u64) << 32 | Status::Zombie as u64;
|
||||
self.0.store(new_val, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn is_zombie(&self) -> bool {
|
||||
matches!(self, ProcessStatus::Zombie(_))
|
||||
self.0.load(Ordering::Relaxed) & Self::LOW_MASK == Status::Zombie as u64
|
||||
}
|
||||
|
||||
pub fn set_runnable(&mut self) {
|
||||
*self = ProcessStatus::Runnable;
|
||||
pub fn set_runnable(&self) {
|
||||
let new_val = Status::Runnable as u64;
|
||||
self.0.store(new_val, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn is_runnable(&self) -> bool {
|
||||
*self == ProcessStatus::Runnable
|
||||
self.0.load(Ordering::Relaxed) & Self::LOW_MASK == Status::Runnable as u64
|
||||
}
|
||||
|
||||
/// Returns the exit code.
|
||||
///
|
||||
/// If the process is not exited, the exit code is zero.
|
||||
/// But if exit code is zero, the process may or may not exit.
|
||||
pub fn exit_code(&self) -> ExitCode {
|
||||
let val = self.0.load(Ordering::Relaxed);
|
||||
(val >> 32 & Self::LOW_MASK) as ExitCode
|
||||
}
|
||||
}
|
||||
|
@ -109,5 +109,5 @@ fn reap_zombie_child(process: &Process, pid: Pid) -> ExitCode {
|
||||
}
|
||||
|
||||
process_table_mut.remove(&child_process.pid());
|
||||
child_process.exit_code().unwrap()
|
||||
child_process.exit_code()
|
||||
}
|
||||
|
@ -36,9 +36,9 @@ pub fn do_sys_kill(filter: ProcessFilter, sig_num: Option<SigNum>, ctx: &Context
|
||||
});
|
||||
|
||||
match filter {
|
||||
ProcessFilter::Any => kill_all(signal)?,
|
||||
ProcessFilter::WithPid(pid) => kill(pid, signal)?,
|
||||
ProcessFilter::WithPgid(pgid) => kill_group(pgid, signal)?,
|
||||
ProcessFilter::Any => kill_all(signal, ctx)?,
|
||||
ProcessFilter::WithPid(pid) => kill(pid, signal, ctx)?,
|
||||
ProcessFilter::WithPgid(pgid) => kill_group(pgid, signal, ctx)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -28,6 +28,6 @@ pub fn sys_tgkill(tgid: Pid, tid: Tid, sig_num: u8, ctx: &Context) -> Result<Sys
|
||||
let uid = ctx.posix_thread.credentials().ruid();
|
||||
UserSignal::new(sig_num, UserSignalKind::Tkill, pid, uid)
|
||||
});
|
||||
tgkill(tid, tgid, signal)?;
|
||||
tgkill(tid, tgid, signal, ctx)?;
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ pub fn sys_wait4(
|
||||
return Ok(SyscallReturn::Return(0 as _));
|
||||
};
|
||||
|
||||
let (return_pid, exit_code) = (process.pid(), process.exit_code().unwrap());
|
||||
let (return_pid, exit_code) = (process.pid(), process.exit_code());
|
||||
if exit_status_ptr != 0 {
|
||||
ctx.get_user_space()
|
||||
.write_val(exit_status_ptr as _, &exit_code)?;
|
||||
|
@ -19,12 +19,12 @@ pub fn handle_exception(ctx: &Context, context: &UserContext) {
|
||||
match *exception {
|
||||
PAGE_FAULT => {
|
||||
if handle_page_fault(root_vmar.vm_space(), trap_info).is_err() {
|
||||
generate_fault_signal(trap_info);
|
||||
generate_fault_signal(trap_info, ctx);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// We current do nothing about other exceptions
|
||||
generate_fault_signal(trap_info);
|
||||
generate_fault_signal(trap_info, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,13 +42,14 @@ pub(crate) fn handle_page_fault(
|
||||
trap_info.error_code,
|
||||
page_fault_addr
|
||||
);
|
||||
|
||||
let not_present = trap_info.error_code & PAGE_NOT_PRESENT_ERROR_MASK == 0;
|
||||
let write = trap_info.error_code & WRITE_ACCESS_MASK != 0;
|
||||
if not_present || write {
|
||||
// If page is not present or due to write access, we should ask the vmar try to commit this page
|
||||
let current = current!();
|
||||
let root_vmar = current.root_vmar();
|
||||
|
||||
// If page is not present or due to write access, we should ask the vmar try to commit this page
|
||||
debug_assert_eq!(
|
||||
Arc::as_ptr(root_vmar.vm_space()),
|
||||
vm_space as *const VmSpace
|
||||
@ -69,10 +70,9 @@ pub(crate) fn handle_page_fault(
|
||||
}
|
||||
|
||||
/// generate a fault signal for current process.
|
||||
fn generate_fault_signal(trap_info: &CpuExceptionInfo) {
|
||||
let current = current!();
|
||||
fn generate_fault_signal(trap_info: &CpuExceptionInfo, ctx: &Context) {
|
||||
let signal = FaultSignal::new(trap_info);
|
||||
current.enqueue_signal(signal);
|
||||
ctx.posix_thread.enqueue_signal(Box::new(signal));
|
||||
}
|
||||
|
||||
macro_rules! log_trap_common {
|
||||
|
@ -7,4 +7,4 @@ set -e
|
||||
echo "*** Running the LMbench protection fault latency test ***"
|
||||
|
||||
dd if=/dev/zero of=/ext2/test_file bs=1M count=256
|
||||
/benchmark/bin/lmbench/lat_sig prot /ext2/test_file
|
||||
/benchmark/bin/lmbench/lat_sig -N 100 prot /ext2/test_file
|
Reference in New Issue
Block a user