Skip to content

fix(x86_64): model virtual IOAPIC interrupt lines - #410

Draft
Solicey wants to merge 1 commit into
syswonder:devfrom
Solicey:fix_x86_ioapic
Draft

Solicey wants to merge 1 commit into
syswonder:devfrom
Solicey:fix_x86_ioapic

Conversation

@Solicey

@Solicey Solicey commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Linux briefly changes a level-triggered IOAPIC redirection table entry (RTE) to masked + edge while handling EOI. If a virtio completion arrived during that window, hvisor dropped it according to the temporary RTE value, even though the device's interrupt line remained asserted. Zone1 could then hang waiting for I/O, as reported in #409.

Changes

  • Keep each IOAPIC pin's asserted line state separately from its RTE. An x86 virtio completion asserts the pin; the existing deassert hypercall now lowers it when the backend reports that the guest has acknowledged the interrupt. The companion hvisor-tool change supplies that report.
  • Apply the RTE's trigger mode to the line state. A rising edge is delivered only if its RTE is unmasked and has a valid vector. A level request remains asserted while masked, including during Linux's temporary masked + edge EOI sequence, and is delivered when the RTE is restored to unmasked + level.
  • Track the vector awaiting EOI for each level-triggered pin and expose its Remote IRR bit on RTE reads. EOI clears that state and permits another delivery only while the pin is still asserted. The virtual LAPIC also tracks the injected vector in its ISR and TMR registers so Linux reads the correct trigger mode during acknowledgement.
  • Block further pin deliveries when a zone starts shutting down, then reset its IOAPIC state before the zone is reused.
  • Replace the x86 QEMU zone1 smoke check with one iteration that waits for a zone1 shell, performs a 100 MiB direct write and read on its root disk, and checks the SHA-256 digest. Archive the console logs in Jenkins for failed-run diagnosis.

@Solicey Solicey added bug Something isn't working x86_64 labels Oct 26, 2026
@github-actions github-actions Bot added x86_64 and removed x86_64 labels Oct 26, 2026
@Solicey
Solicey marked this pull request as ready for review October 28, 2026 02:39
@Solicey
Solicey marked this pull request as draft November 1, 2026 02:58
@Solicey
Solicey force-pushed the fix_x86_ioapic branch 2 times, most recently from 3bd36f7 to 9a1a0d7 Compare November 1, 2026 03:57
@Solicey Solicey changed the title fix(x86_64): replay pending IOAPIC interrupts fix(x86_64): model virtual IOAPIC interrupt lines Nov 1, 2026
@peikachu

peikachu commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

This PR seems to break riscv64. In src/hypercall/mod.rs, the old not(target_arch = "loongarch64") also covered riscv64, but it’s now aarch64 / any(aarch64, loongarch64). As a result, riscv64 fails to build. Even after fixing the build, hv_virtio_inject_irq never advances res_agent and loops forever without injecting any IRQ, and hv_zone_shutdown no longer takes the VIRTIO_IRQS lock or clears the zone’s irq_list.

Comment thread src/hypercall/mod.rs
zone.arch_irqchip_reset();

#[cfg(target_arch = "x86_64")]
crate::device::irqchip::pic::ioapic::ioapic_reset(zone_id as usize);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Race on x86: ioapic_reset() clears the stopping flag before remove_zone(). A virtio injection from another root CPU in between still passes find_zone() and sets the asserted bit on the reset state. If this zone is started again, it will lose its first edge IRQ or get a spurious level IRQ on unmask.

Comment thread src/arch/x86_64/trap.rs
IdtVector::VIRT_IPI_VECTOR if !is_timer => {
ipi::handle_virt_ipi();
}
IdtVector::I8042_KEYBOARD_VECTOR if !is_timer => {}

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.

While using Linux 7.2 kernel for testing, I found a problem here.

Linux assigns MSI-X vectors from 0x20 up, so a passed-through device can own 0x21. Filtering it here swallows that device's completion interrupts.

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

Labels

bug Something isn't working x86_64

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants