Skip to content

refactor(aarch64): migrate virtual IPIs off PERCPU_EVENTS - #407

Open
peikachu wants to merge 6 commits into
syswonder:devfrom
peikachu:refactor/remove-percpu-events
Open

peikachu wants to merge 6 commits into
syswonder:devfrom
peikachu:refactor/remove-percpu-events

Conversation

@peikachu

Copy link
Copy Markdown

Summary

This pull request starts the staged removal of PERCPU_EVENTS by migrating AArch64 virtual IPIs to per-vCPU interrupt state.

  • Add a common GuestCpuIrqState pending bitmap and architecture-private ArchVcpu state.
  • Post GICv2 and GICv3 guest SGIs to the target vCPU, then flush them locally into list registers.
  • Preserve GICv2 SGI source information and virtualize the relevant pending-register behavior.
  • Flush posted interrupts from host-doorbell, maintenance, and guest-entry paths while the legacy event FIFO remains available for unmigrated event classes.
  • Reserve physical SGI 7 for the host doorbell and stop treating other physical SGIs as guest virtual IPIs.
  • Resolve the existing virtio-pci event ID collisions by moving CONFIG, DATA, and DONE to IDs 10-12.

Motivation

Virtual IPIs describe guest interrupt state and do not require FIFO payload semantics. Routing them through PERCPU_EVENTS adds locking, possible queue allocation, and unnecessary coupling between guest interrupt delivery and host CPU event dispatch.

The new path publishes pending state before kicking the target CPU. Only the target CPU installs the interrupt into its local GIC list registers. This keeps interrupt state with the guest CPU, supports the current pinned pCPU:vCPU mapping, and provides a foundation for removing additional event categories without introducing a scheduler in this change.

The event ID update also removes collisions between virtio-pci events, vCPU suspend/resume events, and DWC MSI injection.

Verification

  • On AArch64 QEMU with the qemu-gicv3 configuration, zone1 booted successfully and reached its shell.

Risks and Limitations

  • Only AArch64 virtual IPIs are migrated. PPI, SPI, LPI, DWC MSI, virtio IRQ, and other architecture event classes still use their existing paths.
  • The legacy PERCPU_EVENTS FIFO and GIC PENDING_VIRQS queue remain for unmigrated producers.
  • GICv2 QEMU reached a running zone1 with both assigned CPUs started, but the zone1 virtio console did not produce output, so a complete GICv2 zone1 shell regression is still pending.
  • Cross-CPU pending clears remove software pending state but do not retract an interrupt that has already been claimed or installed in a remote CPU's list register.
  • The implementation retains the current pinned pCPU:vCPU mapping and does not add vCPU scheduling or migration.

@li041
li041 requested review from agicy, dallasxy and li041 October 14, 2026 06:57
Comment thread src/cpu_data.rs Outdated
continue;
}
let lr = gich.get_lr(index) as usize;
let lr_source = (lr >> GICV2_GICH_LR_CPUID_SHIFT) & 0x7;

@agicy agicy Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using bitvec or bitflags to achieve better readability.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, use bit_field. I forget the name of this crate. bit_field would help reducing lots of MASK definition and bit-level operation, and it's already introduced in this repo (in Cargo.toml).

bit_field = "0.10"


const VINTID_MASK: usize = 0x3ff;

fn try_install_software_sgi(irq_id: usize, source_cpu: usize) -> bool {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try_install_software_sgi() matches existing LRs by (vINTID, source), so when the same SGI is pending from two different source CPUs, each source gets its own LR.

The GICv2 architecture requires every valid interrupt in the List registers to have a unique VirtualID for a virtual CPU interface; violating this makes the LR set UNPREDICTABLE. KVM had exactly this bug in its GICv2 SGI injection and fixed it in commit 16ca6a607d84 ("KVM: arm/arm64: vgic: Don't populate multiple LRs with the same vintid").

let width_mask = if mmio.size == 4 {
u32::MAX
} else {
((1u32 << (mmio.size * 8)) - 1) << shift

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can mmio.size be 8 here? If can, an overflow would happen here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants