fix: enable timely delivery of POSIX signals while busy-looping

This commit is contained in:
jellllly420
2024-06-01 16:10:51 +08:00
committed by Tate, Hongliang Tian
parent 5a23de1932
commit e1480f94ee
7 changed files with 83 additions and 43 deletions

View File

@ -55,7 +55,7 @@ use alloc::vec;
use aster_frame::cpu::UserContext; use aster_frame::cpu::UserContext;
use aster_frame::prelude::*; use aster_frame::prelude::*;
use aster_frame::task::{Task, TaskOptions}; use aster_frame::task::{Task, TaskOptions};
use aster_frame::user::{UserEvent, UserMode, UserSpace}; use aster_frame::user::{ReturnReason, UserMode, UserSpace};
use aster_frame::vm::{PageFlags, PAGE_SIZE, Vaddr, VmAllocOptions, VmIo, VmMapOptions, VmSpace}; use aster_frame::vm::{PageFlags, PAGE_SIZE, Vaddr, VmAllocOptions, VmIo, VmMapOptions, VmSpace};
/// The kernel's boot and initialization process is managed by Asterinas Framework. /// The kernel's boot and initialization process is managed by Asterinas Framework.
@ -116,13 +116,15 @@ fn create_user_task(user_space: Arc<UserSpace>) -> Arc<Task> {
loop { loop {
// The execute method returns when system // The execute method returns when system
// calls or CPU exceptions occur. // calls or CPU exceptions occur or some
let user_event = user_mode.execute(); // events specified by the kernel occur.
let return_reason = user_mode.execute(|| false);
// The CPU registers of the user space // The CPU registers of the user space
// can be accessed and manipulated via // can be accessed and manipulated via
// the `UserContext` abstraction. // the `UserContext` abstraction.
let user_context = user_mode.context_mut(); let user_context = user_mode.context_mut();
if UserEvent::Syscall == user_event { if ReturnReason::UserSyscall == return_reason {
handle_syscall(user_context, current.user_space().unwrap()); handle_syscall(user_context, current.user_space().unwrap());
} }
} }

View File

@ -23,7 +23,7 @@ use x86_64::registers::rflags::RFlags;
use crate::arch::tdx_guest::{handle_virtual_exception, TdxTrapFrame}; use crate::arch::tdx_guest::{handle_virtual_exception, TdxTrapFrame};
use crate::{ use crate::{
trap::call_irq_callback_functions, trap::call_irq_callback_functions,
user::{UserContextApi, UserContextApiInternal, UserEvent}, user::{ReturnReason, UserContextApi, UserContextApiInternal},
}; };
/// Returns the number of CPUs. /// Returns the number of CPUs.
@ -257,11 +257,15 @@ impl UserContext {
} }
impl UserContextApiInternal for UserContext { impl UserContextApiInternal for UserContext {
fn execute(&mut self) -> crate::user::UserEvent { fn execute<F>(&mut self, mut has_kernel_event: F) -> ReturnReason
where
F: FnMut() -> bool,
{
// set interrupt flag so that in user mode it can receive external interrupts // set interrupt flag so that in user mode it can receive external interrupts
// set ID flag which means cpu support CPUID instruction // set ID flag which means cpu support CPUID instruction
self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize; self.user_context.general.rflags |= (RFlags::INTERRUPT_FLAG | RFlags::ID).bits() as usize;
let return_reason: ReturnReason;
const SYSCALL_TRAPNUM: u16 = 0x100; const SYSCALL_TRAPNUM: u16 = 0x100;
let mut user_preemption = UserPreemption::new(); let mut user_preemption = UserPreemption::new();
@ -281,31 +285,36 @@ impl UserContextApiInternal for UserContext {
|| exception.typ == CpuExceptionType::Fault || exception.typ == CpuExceptionType::Fault
|| exception.typ == CpuExceptionType::Trap || exception.typ == CpuExceptionType::Trap
{ {
return_reason = ReturnReason::UserException;
break; break;
} }
} }
None => { None => {
if self.user_context.trap_num as u16 == SYSCALL_TRAPNUM { if self.user_context.trap_num as u16 == SYSCALL_TRAPNUM {
return_reason = ReturnReason::UserSyscall;
break; break;
} }
} }
}; };
call_irq_callback_functions(&self.as_trap_frame()); call_irq_callback_functions(&self.as_trap_frame());
if has_kernel_event() {
return_reason = ReturnReason::KernelEvent;
break;
}
user_preemption.might_preempt(); user_preemption.might_preempt();
} }
crate::arch::irq::enable_local(); crate::arch::irq::enable_local();
if self.user_context.trap_num as u16 != SYSCALL_TRAPNUM { if return_reason == ReturnReason::UserException {
self.cpu_exception_info = CpuExceptionInfo { self.cpu_exception_info = CpuExceptionInfo {
page_fault_addr: unsafe { x86::controlregs::cr2() }, page_fault_addr: unsafe { x86::controlregs::cr2() },
id: self.user_context.trap_num, id: self.user_context.trap_num,
error_code: self.user_context.error_code, error_code: self.user_context.error_code,
}; };
UserEvent::Exception
} else {
UserEvent::Syscall
} }
return_reason
} }
fn as_trap_frame(&self) -> trapframe::TrapFrame { fn as_trap_frame(&self) -> trapframe::TrapFrame {

View File

@ -51,7 +51,9 @@ impl UserSpace {
/// Only visible in aster-frame /// Only visible in aster-frame
pub(crate) trait UserContextApiInternal { pub(crate) trait UserContextApiInternal {
/// Starts executing in the user mode. /// Starts executing in the user mode.
fn execute(&mut self) -> UserEvent; fn execute<F>(&mut self, has_kernel_event: F) -> ReturnReason
where
F: FnMut() -> bool;
/// Use the information inside CpuContext to build a trapframe /// Use the information inside CpuContext to build a trapframe
fn as_trap_frame(&self) -> TrapFrame; fn as_trap_frame(&self) -> TrapFrame;
@ -93,9 +95,9 @@ pub trait UserContextApi {
/// .expect("the current task is associated with a user space"); /// .expect("the current task is associated with a user space");
/// let mut user_mode = user_space.user_mode(); /// let mut user_mode = user_space.user_mode();
/// loop { /// loop {
/// // Execute in the user space until some interesting user event occurs /// // Execute in the user space until some interesting events occur.
/// let user_event = user_mode.execute(); /// let return_reason = user_mode.execute(|| false);
/// todo!("handle the user event, e.g., syscall"); /// todo!("handle the event, e.g., syscall");
/// } /// }
/// ``` /// ```
pub struct UserMode<'a> { pub struct UserMode<'a> {
@ -118,17 +120,22 @@ impl<'a> UserMode<'a> {
/// Starts executing in the user mode. Make sure current task is the task in `UserMode`. /// Starts executing in the user mode. Make sure current task is the task in `UserMode`.
/// ///
/// The method returns for one of three possible reasons indicated by `UserEvent`. /// The method returns for one of three possible reasons indicated by `ReturnReason`.
/// 1. The user invokes a system call; /// 1. A system call is issued by the user space;
/// 2. The user triggers an exception; /// 2. A CPU exception is triggered by the user space;
/// 3. The user triggers a fault. /// 3. A kernel event is pending, as indicated by the given closure.
/// ///
/// After handling the user event and updating the user-mode CPU context, /// After handling whatever user or kernel events that
/// cause the method to return
/// and updating the user-mode CPU context,
/// this method can be invoked again to go back to the user space. /// this method can be invoked again to go back to the user space.
pub fn execute(&mut self) -> UserEvent { pub fn execute<F>(&mut self, has_kernel_event: F) -> ReturnReason
where
F: FnMut() -> bool,
{
self.user_space.vm_space().activate(); self.user_space.vm_space().activate();
debug_assert!(Arc::ptr_eq(&self.current, &Task::current())); debug_assert!(Arc::ptr_eq(&self.current, &Task::current()));
self.context.execute() self.context.execute(has_kernel_event)
} }
/// Returns an immutable reference the user-mode CPU context. /// Returns an immutable reference the user-mode CPU context.
@ -143,14 +150,13 @@ impl<'a> UserMode<'a> {
} }
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
/// A user event is what brings back the control of the CPU back from /// A reason as to why the control of the CPU is returned from
/// the user space to the kernel space. /// the user space to the kernel.
/// pub enum ReturnReason {
/// Note that hardware interrupts are not considered user events as they /// A system call is issued by the user space.
/// are triggered by devices and not visible to user programs. UserSyscall,
/// To handle interrupts, one should register callback funtions for /// A CPU exception is triggered by the user space.
/// IRQ lines (`IrqLine`). UserException,
pub enum UserEvent { /// A kernel event is pending
Syscall, KernelEvent,
Exception,
} }

View File

@ -1,20 +1,27 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use aster_frame::{ use aster_frame::{
cpu::UserContext,
task::{preempt, Task, TaskOptions}, task::{preempt, Task, TaskOptions},
user::{UserContextApi, UserEvent, UserMode, UserSpace}, user::{ReturnReason, UserContextApi, UserMode, UserSpace},
}; };
use super::Thread; use super::Thread;
use crate::{ use crate::{
cpu::LinuxAbi, prelude::*, process::signal::handle_pending_signal, syscall::handle_syscall, cpu::LinuxAbi,
prelude::*,
process::{posix_thread::PosixThreadExt, signal::handle_pending_signal},
syscall::handle_syscall,
thread::exception::handle_exception, thread::exception::handle_exception,
}; };
/// create new task with userspace and parent process /// create new task with userspace and parent process
pub fn create_new_user_task(user_space: Arc<UserSpace>, thread_ref: Weak<Thread>) -> Arc<Task> { pub fn create_new_user_task(user_space: Arc<UserSpace>, thread_ref: Weak<Thread>) -> Arc<Task> {
fn user_task_entry() { fn user_task_entry() {
fn has_pending_signal(current_thread: &Arc<Thread>) -> bool {
let posix_thread = current_thread.as_posix_thread().unwrap();
posix_thread.has_pending_signal()
}
let current_thread = current_thread!(); let current_thread = current_thread!();
let current_task = current_thread.task(); let current_task = current_thread.task();
let user_space = current_task let user_space = current_task
@ -34,11 +41,17 @@ pub fn create_new_user_task(user_space: Arc<UserSpace>, thread_ref: Weak<Thread>
user_mode.context().syscall_ret() user_mode.context().syscall_ret()
); );
#[allow(clippy::redundant_closure)]
let has_kernel_event_fn = || has_pending_signal(&current_thread);
loop { loop {
let user_event = user_mode.execute(); let return_reason = user_mode.execute(has_kernel_event_fn);
let context = user_mode.context_mut(); let context = user_mode.context_mut();
// handle user event: // handle user event:
handle_user_event(user_event, context); match return_reason {
ReturnReason::UserException => handle_exception(context),
ReturnReason::UserSyscall => handle_syscall(context),
ReturnReason::KernelEvent => {}
};
// should be do this comparison before handle signal? // should be do this comparison before handle signal?
if current_thread.status().is_exited() { if current_thread.status().is_exited() {
break; break;
@ -68,10 +81,3 @@ pub fn create_new_user_task(user_space: Arc<UserSpace>, thread_ref: Weak<Thread>
.build() .build()
.expect("spawn task failed") .expect("spawn task failed")
} }
fn handle_user_event(user_event: UserEvent, context: &mut UserContext) {
match user_event {
UserEvent::Syscall => handle_syscall(context),
UserEvent::Exception => handle_exception(context),
}
}

View File

@ -10,6 +10,7 @@ REGRESSION_BUILD_DIR ?= $(INITRAMFS)/regression
# These test apps are sorted by name # These test apps are sorted by name
TEST_APPS := \ TEST_APPS := \
alarm \
clone3 \ clone3 \
eventfd2 \ eventfd2 \
execve \ execve \

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: MPL-2.0
include ../test_common.mk
EXTRA_C_FLAGS := -static

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
#include <unistd.h>
int main()
{
alarm(3);
while (1) {
}
return 0;
}