mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-18 03:56:42 +00:00
Add the inter-processor-call facilities
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
63364813a8
commit
425027677b
@ -153,3 +153,27 @@ impl Drop for IrqCallbackHandle {
|
||||
CALLBACK_ID_ALLOCATOR.get().unwrap().lock().free(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a general inter-processor interrupt (IPI) to the specified CPU.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the CPU ID and the interrupt number corresponds
|
||||
/// to a safe function to call.
|
||||
pub(crate) unsafe fn send_ipi(cpu_id: u32, irq_num: u8) {
|
||||
use crate::arch::kernel::apic::{self, Icr};
|
||||
|
||||
let icr = Icr::new(
|
||||
apic::ApicId::from(cpu_id),
|
||||
apic::DestinationShorthand::NoShorthand,
|
||||
apic::TriggerMode::Edge,
|
||||
apic::Level::Assert,
|
||||
apic::DeliveryStatus::Idle,
|
||||
apic::DestinationMode::Physical,
|
||||
apic::DeliveryMode::Fixed,
|
||||
irq_num,
|
||||
);
|
||||
apic::borrow(|apic| {
|
||||
apic.send_ipi(icr);
|
||||
});
|
||||
}
|
||||
|
@ -238,7 +238,6 @@ impl From<u32> for ApicId {
|
||||
/// in the system excluding the sender.
|
||||
#[repr(u64)]
|
||||
pub enum DestinationShorthand {
|
||||
#[allow(dead_code)]
|
||||
NoShorthand = 0b00,
|
||||
#[allow(dead_code)]
|
||||
MySelf = 0b01,
|
||||
@ -278,7 +277,6 @@ pub enum DestinationMode {
|
||||
#[repr(u64)]
|
||||
pub enum DeliveryMode {
|
||||
/// Delivers the interrupt specified in the vector field to the target processor or processors.
|
||||
#[allow(dead_code)]
|
||||
Fixed = 0b000,
|
||||
/// Same as fixed mode, except that the interrupt is delivered to the processor executing at
|
||||
/// the lowest priority among the set of processors specified in the destination field. The
|
||||
|
@ -39,6 +39,7 @@ pub mod logger;
|
||||
pub mod mm;
|
||||
pub mod panicking;
|
||||
pub mod prelude;
|
||||
pub mod smp;
|
||||
pub mod sync;
|
||||
pub mod task;
|
||||
pub mod trap;
|
||||
@ -90,6 +91,8 @@ pub unsafe fn init() {
|
||||
unsafe { trap::softirq::init() };
|
||||
arch::init_on_bsp();
|
||||
|
||||
smp::init();
|
||||
|
||||
bus::init();
|
||||
|
||||
// SAFETY: This function is called only once on the BSP.
|
||||
|
87
ostd/src/smp.rs
Normal file
87
ostd/src/smp.rs
Normal file
@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Symmetric Multi-Processing (SMP) support.
|
||||
//!
|
||||
//! This module provides a way to execute code on other processors via inter-
|
||||
//! processor interrupts.
|
||||
|
||||
use alloc::collections::VecDeque;
|
||||
|
||||
use spin::Once;
|
||||
|
||||
use crate::{
|
||||
cpu::{CpuSet, PinCurrentCpu},
|
||||
cpu_local,
|
||||
sync::SpinLock,
|
||||
trap::{self, IrqLine, TrapFrame},
|
||||
};
|
||||
|
||||
/// Execute a function on other processors.
|
||||
///
|
||||
/// The provided function `f` will be executed on all target processors
|
||||
/// specified by `targets`. It can also be executed on the current processor.
|
||||
/// The function should be short and non-blocking, as it will be executed in
|
||||
/// interrupt context with interrupts disabled.
|
||||
///
|
||||
/// This function does not block until all the target processors acknowledges
|
||||
/// the interrupt. So if any of the target processors disables IRQs for too
|
||||
/// long that the controller cannot queue them, the function will not be
|
||||
/// executed.
|
||||
///
|
||||
/// The function `f` will be executed asynchronously on the target processors.
|
||||
/// However if called on the current processor, it will be synchronous.
|
||||
pub fn inter_processor_call(targets: &CpuSet, f: fn()) {
|
||||
let irq_guard = trap::disable_local();
|
||||
let this_cpu_id = irq_guard.current_cpu() as usize;
|
||||
let irq_num = INTER_PROCESSOR_CALL_IRQ.get().unwrap().num();
|
||||
|
||||
let mut call_on_self = false;
|
||||
for cpu_id in targets.iter() {
|
||||
if cpu_id == this_cpu_id {
|
||||
call_on_self = true;
|
||||
continue;
|
||||
}
|
||||
CALL_QUEUES.get_on_cpu(cpu_id as u32).lock().push_back(f);
|
||||
}
|
||||
for cpu_id in targets.iter() {
|
||||
if cpu_id == this_cpu_id {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: It is safe to send inter processor call IPI to other CPUs.
|
||||
unsafe {
|
||||
crate::arch::irq::send_ipi(cpu_id as u32, irq_num);
|
||||
}
|
||||
}
|
||||
if call_on_self {
|
||||
// Execute the function synchronously.
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
static INTER_PROCESSOR_CALL_IRQ: Once<IrqLine> = Once::new();
|
||||
|
||||
cpu_local! {
|
||||
static CALL_QUEUES: SpinLock<VecDeque<fn()>> = SpinLock::new(VecDeque::new());
|
||||
}
|
||||
|
||||
fn do_inter_processor_call(_trapframe: &TrapFrame) {
|
||||
// TODO: in interrupt context, disabling interrupts is not necessary.
|
||||
let preempt_guard = trap::disable_local();
|
||||
let cur_cpu = preempt_guard.current_cpu();
|
||||
|
||||
let mut queue = CALL_QUEUES.get_on_cpu(cur_cpu).lock();
|
||||
while let Some(f) = queue.pop_front() {
|
||||
log::trace!(
|
||||
"Performing inter-processor call to {:#?} on CPU {}",
|
||||
f,
|
||||
cur_cpu
|
||||
);
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn init() {
|
||||
let mut irq = IrqLine::alloc().unwrap();
|
||||
irq.on_active(do_inter_processor_call);
|
||||
INTER_PROCESSOR_CALL_IRQ.call_once(|| irq);
|
||||
}
|
Reference in New Issue
Block a user