ahcid: always re-arm the IRQ line, even for foreign interrupts

The kernel masks an IRQ line when it delivers the interrupt to userspace
(trigger() -> pic_mask()/ioapic_mask()) and only re-enables it when the
driver writes the count back (kwrite -> acknowledge() -> pic_unmask()/
ioapic_unmask()). That write-back is therefore mandatory, not optional.

ahcid only performed it inside the `is > 0` branch. IRQ lines are shared
(on q35 the AHCI controller sits on IRQ 10 with other devices), so an
interrupt raised by a different device reaches the handler with the HBA
interrupt status register reading 0. In that case ahcid returned without
writing back, leaving the line masked permanently: every subsequent AHCI
completion was lost and all disk I/O blocked forever.

Move the write-back out of the branch so the line is re-armed whether or
not the interrupt turned out to be ours, and keep scheme.tick() gated on
`is > 0` since there is no completion to service otherwise. Device-level
status is still cleared before unmasking, so this cannot cause a storm.
This commit is contained in:
Red Bear OS
2026-07-20 14:09:40 +09:00
parent fb421083e0
commit ad40fffd80
+14 -3
View File
@@ -91,11 +91,22 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
}
}
hba_mem.is.write(is);
}
irq_file
.write(&irq)
.expect("ahcid: failed to write irq file");
// The kernel masks the IRQ line when it delivers the interrupt to us and
// only re-enables it once we write the count back (that write maps to
// acknowledge() -> pic_unmask()/ioapic_unmask()). IRQ lines are shared, so
// an interrupt raised by a *different* device arrives here with is == 0.
// Acking only inside the `is > 0` branch meant one foreign interrupt left
// the line masked forever: every later AHCI completion was lost and all
// disk I/O blocked permanently. Always re-arm the line, whether or not the
// interrupt turned out to be ours. Device-level status is cleared above
// before we unmask, so this cannot re-trigger a storm.
irq_file
.write(&irq)
.expect("ahcid: failed to write irq file");
if is > 0 {
FuturesExecutor.block_on(scheme.tick()).unwrap();
}
}