irq: MSI first, and poll the INTx lines that never arrive
PCI drivers took their interrupt from INTERRUPT_LINE. On real hardware that is the 8259's number, not the IOAPIC pin the device is wired to (that is in ACPI _PRT, which needs AML), so nothing ever arrived. xHCI is entirely interrupt-driven, so the USB keyboard and a USB root would have hung even on a clean boot. Under QEMU the two numbers agree, which is why it never showed. - pci_irq_attach()/pci_irq_detach(): MSI, then MSI-X if the driver allows it, then INTx. The handler is registered before MSI is enabled, so no edge is lost. xHCI, e1000, r8169 and rtl8139 use it. - Lines 24-29 are MSI vectors. The LAPIC error vector moves from 19, which is a PCI pin on Intel chipsets, to 30. Nothing is placed at or above vector 64 (T_SYSCALL). - A storming line is masked and its handlers are polled instead of being left to eat the CPU. A level line that has had a handler for 2 s and never fired is polled every tick by a kernel thread. Tested in QEMU q35 with the root on a USB stick: xHCI and e1000e on MSI; then MSI disabled and INTx deliberately misrouted, where xHCI got zero interrupts and the root still mounted through the poller. i440fx INTx and the rtl8139 hand-over are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HH7xukt4aaiGj1nQEAoQf1
This commit is contained in:
parent
94c936ed98
commit
7e8d5e2821
11 changed files with 407 additions and 35 deletions
|
|
@ -1169,7 +1169,7 @@ xhci_dump(void)
|
|||
rd32(x->rt, XHCI_IR0 + IR_IMAN), rd32(x->rt, XHCI_IR0 + IR_IMOD),
|
||||
rd32(x->rt, XHCI_IR0 + IR_ERSTSZ),
|
||||
rd64(x->rt, XHCI_IR0 + IR_ERDP), x->ev.deq, x->ev.cycle);
|
||||
cprintf(" pci irq %d\n", x->pci->irq);
|
||||
cprintf(" pci irq %d (%s)\n", x->pci->irq_line, pci_irq_kind(x->pci));
|
||||
|
||||
for(p = 1; p <= x->maxports; p++){
|
||||
uint32 sc = rd32(x->op, XHCI_PORTSC(p));
|
||||
|
|
@ -1328,17 +1328,26 @@ xhci_probe(struct pci_dev *d)
|
|||
if(x->ndevices == 0)
|
||||
cprintf("xhci: no devices attached\n");
|
||||
|
||||
// Probing is over: from here a bulk transfer has a process behind it and
|
||||
// can sleep rather than spin.
|
||||
x->can_sleep = 1;
|
||||
|
||||
// Now, and only now, turn interrupts on. The order is: claim the line
|
||||
// (which unmasks it and sets level/active-low), clear anything the
|
||||
// controller already considers pending so that it drives INTx low, and then
|
||||
// let it interrupt. The next event is a genuine low-to-high transition,
|
||||
// which is the only thing an interrupt controller can act on.
|
||||
if(d->irq != 0xFF){
|
||||
irq_register(d->irq, xhci_irq, x, "xhci", 1); // PCI: level, active low
|
||||
//
|
||||
// MSI or MSI-X when the controller has either, which every xHCI does. On
|
||||
// real hardware that is not a refinement: the INTx line the config space
|
||||
// names is the 8259's number, the IOAPIC pin the controller is really on
|
||||
// is a different one, and an xHCI waiting on INTx waited for ever -- the
|
||||
// USB keyboard dead and a USB root never mounted.
|
||||
//
|
||||
// Probing is over: from here a bulk transfer has a process behind it and
|
||||
// can sleep rather than spin -- but only if something will wake it. With
|
||||
// no interrupt at all, transfers go on spinning as they did during probe.
|
||||
if(pci_irq_attach(d, xhci_irq, x, "xhci", PCI_IRQF_MSIX) < 0){
|
||||
cprintf("xhci: no interrupt; transfers will be polled\n");
|
||||
} else {
|
||||
x->can_sleep = 1;
|
||||
|
||||
wr32(x->op, XHCI_USBSTS, STS_EINT);
|
||||
wr32(x->rt, XHCI_IR0 + IR_IMAN, IMAN_IP | IMAN_IE);
|
||||
|
|
|
|||
|
|
@ -359,19 +359,20 @@ e1000_probe(struct pci_dev *d)
|
|||
wr(e, E1000_RCTL, E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SECRC |
|
||||
E1000_RCTL_SZ_2048 | E1000_RCTL_LBM_NONE);
|
||||
|
||||
if (d->irq != 0xFF)
|
||||
// level_low = 1: this is a PCI interrupt, which is level-triggered and
|
||||
// active low. Registering it as edge-triggered makes it fire once and then
|
||||
// wedge, because the device holds the line asserted and no further edge
|
||||
// ever arrives. See the comment on irq_register in include/irq.h.
|
||||
irq_register(d->irq, e1000_intr, e, "e1000", 1);
|
||||
// MSI where the part has it -- the 82574 and the rest of the PCIe
|
||||
// generation do -- and INTx otherwise. MSI-X is not offered: the 82574 in
|
||||
// MSI-X mode routes its causes through IVAR, which this driver does not
|
||||
// program, and its interrupts would go nowhere. If neither works the
|
||||
// network thread still collects frames on its tick.
|
||||
pci_irq_attach(d, e1000_intr, e, "e1000", 0);
|
||||
wr(e, E1000_IMS, E1000_ICR_RXT0 | E1000_ICR_RXDMT0 | E1000_ICR_LSC);
|
||||
|
||||
e->ready = 1;
|
||||
|
||||
kinfo("e1000: %x:%x:%x:%x:%x:%x, link %s, irq %d\n",
|
||||
kinfo("e1000: %x:%x:%x:%x:%x:%x, link %s, irq %d (%s)\n",
|
||||
e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
|
||||
(rd(e, E1000_STATUS) & E1000_STATUS_LU) ? "up" : "down", d->irq);
|
||||
(rd(e, E1000_STATUS) & E1000_STATUS_LU) ? "up" : "down",
|
||||
d->irq_line, pci_irq_kind(d));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -458,8 +459,7 @@ e1000_release(struct pci_dev *d)
|
|||
wr(e, E1000_TCTL, 0); // stop transmitting
|
||||
rd(e, E1000_STATUS); // post the writes
|
||||
|
||||
if (d->irq != 0xFF)
|
||||
irq_unregister(d->irq, e1000_intr, e);
|
||||
pci_irq_detach(d, e1000_intr, e);
|
||||
|
||||
// And take the interface out of the stack.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -468,9 +468,9 @@ r8169_probe(struct pci_dev *d)
|
|||
wr16(r, R_IMR, ISR_ROK | ISR_RER | ISR_TOK | ISR_TER |
|
||||
ISR_RDU | ISR_LINKCHG);
|
||||
|
||||
if (d->irq != 0xFF && d->irq < 24)
|
||||
irq_register(d->irq, r8169_intr, r, "r8169", 1);
|
||||
else
|
||||
// MSI first: every 8111 has it, and on the boards these are soldered to it
|
||||
// is the only interrupt that is sure to arrive. See pci_irq_attach().
|
||||
if (pci_irq_attach(d, r8169_intr, r, "r8169", 0) < 0)
|
||||
cprintf("r8169: no usable interrupt line; the link will be polled\n");
|
||||
|
||||
r->ready = 1;
|
||||
|
|
@ -487,10 +487,10 @@ r8169_probe(struct pci_dev *d)
|
|||
uint32 xid = (rd32(r, R_TCR) >> 20) & 0xFCF;
|
||||
int bmsr = mii_read(r, 1);
|
||||
|
||||
cprintf("r8169: xid %x, %x:%x:%x:%x:%x:%x, irq %d, %s (bmsr %x)\n",
|
||||
cprintf("r8169: xid %x, %x:%x:%x:%x:%x:%x, irq %d (%s), %s (bmsr %x)\n",
|
||||
xid,
|
||||
r->mac[0], r->mac[1], r->mac[2], r->mac[3], r->mac[4], r->mac[5],
|
||||
d->irq,
|
||||
d->irq_line, pci_irq_kind(d),
|
||||
(rd8(r, R_PHYSTATUS) & PHYSTATUS_LINK) ? "link up" : "link down",
|
||||
bmsr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,8 +360,7 @@ rtl8139_probe(struct pci_dev *d)
|
|||
wr16(r, R_IMR, ISR_ROK | ISR_RER | ISR_TOK | ISR_TER |
|
||||
ISR_RXOVW | ISR_FOVW | ISR_LINKCHG);
|
||||
|
||||
if (d->irq != 0xFF && d->irq < 24)
|
||||
irq_register(d->irq, rtl8139_intr, r, "rtl8139", 1);
|
||||
pci_irq_attach(d, rtl8139_intr, r, "rtl8139", 0); // no MSI on this part
|
||||
|
||||
r->ready = 1;
|
||||
kinfo("rtl8139: %x:%x:%x:%x:%x:%x, %s, irq %d\n",
|
||||
|
|
@ -405,8 +404,7 @@ rtl8139_release(struct pci_dev *d)
|
|||
wr8(r, R_CR, 0); // receiver and transmitter off
|
||||
release(&r->lock);
|
||||
|
||||
if (d->irq != 0xFF && d->irq < 24)
|
||||
irq_unregister(d->irq, rtl8139_intr, r);
|
||||
pci_irq_detach(d, rtl8139_intr, r);
|
||||
|
||||
// Reset, and wait for it, so the chip has truly stopped before its buffers
|
||||
// are reused. The reset bit clears itself when the chip is done.
|
||||
|
|
|
|||
Loading…
Reference in a new issue