Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/arch/loongarch64/ipi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,17 @@ pub const SMP_RESCHEDULE: usize = 0x2;
pub const SMP_CALL_FUNCTION: usize = 0x4;
// customized actions :), since there is no docs on this yet
/// Dedicated physical IPI bit used only as the hvisor event-queue doorbell.
/// Linux SMP actions use bits 0..=2, so sharing those bits can drop guest IPI work.
/// Keep this separate from the guest virtual-IPI doorbell below.
pub const HVISOR_EVENT_DOORBELL: usize = 0x8;
/// Dedicated physical IPI bit used only to notify a target CPU that its
/// guest virtual-IPI status changed. This must not enter the generic event
/// queue path.
pub const HVISOR_VIPI_DOORBELL: usize = 0x10;

#[inline]
pub fn arch_send_virtual_ipi(cpu_id: usize) {
arch_send_event(cpu_id as u64, HVISOR_VIPI_DOORBELL as u64);
}

fn iocsr_mbuf_send_box_lo(a: usize) -> usize {
a << 1
Expand Down
43 changes: 31 additions & 12 deletions src/arch/loongarch64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::consts::MAX_CPU_NUM;
use crate::cpu_data::this_cpu_data;
use crate::device::irqchip::inject_irq;
use crate::device::irqchip::ls7a2000::chip::*;
use crate::event::{dump_events, handle_next_event};
use crate::event::{dump_events, handle_next_event, has_pending_events};
use crate::hypercall::{SGI_IPI_ID, *};
use crate::memory::{addr, mmio_handle_access, MMIOAccess};
use crate::zone::Zone;
Expand Down Expand Up @@ -1271,6 +1271,14 @@ const HWI7: usize = 1 << 9;

/// handle loongarch64 interrupts here
fn handle_interrupt(is: usize) {
let timer_pending = is & TIMER_BIT != 0;
if timer_pending {
// Clear the timer before processing IPI work. A combined IPI+timer
// exception must not let the IPI path starve the timer indefinitely.
loongArch64::register::ticlr::clear_timer_interrupt();
debug!("Timer interrupt received");
}

// Handle IPI interrupts
if is & IPI_BIT != 0 {
let cpu_id = this_cpu_id();
Expand All @@ -1280,31 +1288,42 @@ fn handle_interrupt(is: usize) {
cpu_id, ipi_status
);

let hvisor_mask = SGI_IPI_ID as u32;
if ipi_status & hvisor_mask != 0 {
let event_doorbell = SGI_IPI_ID as u32;
let virtual_ipi_doorbell = HVISOR_VIPI_DOORBELL as u32;

// Virtual IPI status is posted state, not a generic event. Its
// dedicated physical doorbell must never cause the event FIFO to be
// inspected or drained.
if ipi_status & virtual_ipi_doorbell != 0 {
clear_ipi_bits(virtual_ipi_doorbell);
if crate::arch::loongarch64::zone::virtual_ipi_pending(cpu_id) {
crate::arch::loongarch64::zone::sync_virtual_ipi_line();
}
}

if ipi_status & event_doorbell != 0 {
// Clear before each fetch. If the fetch observes an empty queue while a
// producer enqueues concurrently, its doorbell remains pending and
// re-fires. Clearing after the fetch could lose that coalesced wakeup.
clear_ipi_bits(hvisor_mask);
while handle_next_event() {
clear_ipi_bits(hvisor_mask);
clear_ipi_bits(event_doorbell);
if has_pending_events(cpu_id) {
while handle_next_event() {
clear_ipi_bits(event_doorbell);
}
}
}

let unhandled = ipi_status & !hvisor_mask;
let handled_mask = event_doorbell | virtual_ipi_doorbell;
let unhandled = ipi_status & !handled_mask;
if unhandled != 0 {
error!(
"CPU {} has unhandled physical IPI status {:#x}; preserving those bits",
cpu_id, unhandled
);
}
return;
}

// Handle timer interrupts
if is & TIMER_BIT != 0 {
debug!("Timer interrupt received");
loongArch64::register::ticlr::clear_timer_interrupt();
if is & (IPI_BIT | TIMER_BIT) != 0 {
return;
}

Expand Down
11 changes: 9 additions & 2 deletions src/arch/loongarch64/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
Stage2PageTable,
},
config::*,
consts::{IPI_EVENT_SEND_IPI, MAX_CPU_NUM, PAGE_SIZE},
consts::{MAX_CPU_NUM, PAGE_SIZE},
cpu_data::{get_cpu_data, this_cpu_data, VcpuState},
device::virtio_trampoline::mmio_virtio_handler,
error::{HvError, HvResult},
Expand Down Expand Up @@ -658,11 +658,18 @@ pub fn sync_virtual_ipi_line() {
}
}

pub fn virtual_ipi_pending(cpu: usize) -> bool {
cpu < MAX_CPU_NUM && VIRTUAL_IPI_STATUS[cpu].load(Ordering::Acquire) != 0
}

fn sync_or_notify_virtual_ipi_line(cpu: usize) {
if cpu == this_cpu_id() {
sync_virtual_ipi_line();
} else {
send_event(cpu, SGI_IPI_ID as usize, IPI_EVENT_SEND_IPI);
// The virtual IPI status is already the pending state. Use the
// dedicated physical doorbell directly instead of allocating a
// one-item generic event just to call sync_virtual_ipi_line().
crate::arch::ipi::arch_send_virtual_ipi(cpu);
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ pub fn fetch_event(cpu: usize) -> Option<usize> {
get_percpu_events(cpu).lock().pop_front()
}

pub fn has_pending_events(cpu: usize) -> bool {
if cpu >= MAX_CPU_NUM {
return false;
}
!get_percpu_events(cpu).lock().is_empty()
}

pub fn dump_events() {
for cpu in 0..MAX_CPU_NUM {
let events = get_percpu_events(cpu).lock();
Expand Down
Loading