From 06e7fc9deea669ef25eb27352b7681ef262bf906 Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 20 Mar 2020 00:44:42 +0200 Subject: [PATCH] Kernel: Limit IRQ rate within E1000 network adapter This is not a complete fix, since spurious IRQs under heavy loads can still occur. However, this fix limits the amount of spurious IRQs. It is encouraged to provide a better fix in the future, probably something that takes into account handling of PCI level-triggered interrupts. --- Kernel/Net/E1000NetworkAdapter.cpp | 40 +++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/Kernel/Net/E1000NetworkAdapter.cpp b/Kernel/Net/E1000NetworkAdapter.cpp index ead4b6bdce..bfcdd8cf4c 100644 --- a/Kernel/Net/E1000NetworkAdapter.cpp +++ b/Kernel/Net/E1000NetworkAdapter.cpp @@ -36,7 +36,10 @@ namespace Kernel { #define REG_STATUS 0x0008 #define REG_EEPROM 0x0014 #define REG_CTRL_EXT 0x0018 -#define REG_IMASK 0x00D0 +#define REG_INTERRUPT_CAUSE_READ 0x00C0 +#define REG_INTERRUPT_RATE 0x00C4 +#define REG_INTERRUPT_MASK_SET 0x00D0 +#define REG_INTERRUPT_MASK_CLEAR 0x00D8 #define REG_RCTRL 0x0100 #define REG_RXDESCLO 0x2800 #define REG_RXDESCHI 0x2804 @@ -121,6 +124,21 @@ namespace Kernel { #define STATUS_SPEED_1000MB1 0x80 #define STATUS_SPEED_1000MB2 0xC0 +// Interrupt Masks + +#define INTERRUPT_TXDW (1 << 0) +#define INTERRUPT_TXQE (1 << 1) +#define INTERRUPT_LSC (1 << 2) +#define INTERRUPT_RXSEQ (1 << 3) +#define INTERRUPT_RXDMT0 (1 << 4) +#define INTERRUPT_RXO (1 << 6) +#define INTERRUPT_RXT0 (1 << 7) +#define INTERRUPT_MDAC (1 << 9) +#define INTERRUPT_RXCFG (1 << 10) +#define INTERRUPT_PHYINT (1 << 12) +#define INTERRUPT_TXD_LOW (1 << 15) +#define INTERRUPT_SRPD (1 << 16) + void E1000NetworkAdapter::detect(const PCI::Address& address) { if (address.is_null()) @@ -163,12 +181,14 @@ E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address address, u8 irq) u32 flags = in32(REG_CTRL); out32(REG_CTRL, flags | ECTRL_SLU); + out16(REG_INTERRUPT_RATE, 6000); // Interrupt rate of 1.536 milliseconds + initialize_rx_descriptors(); initialize_tx_descriptors(); - out32(REG_IMASK, 0x1f6dc); - out32(REG_IMASK, 0xff & ~4); - in32(0xc0); + out32(REG_INTERRUPT_MASK_SET, 0x1f6dc); + out32(REG_INTERRUPT_MASK_SET, INTERRUPT_LSC | INTERRUPT_RXT0); + in32(REG_INTERRUPT_CAUSE_READ); enable_irq(); } @@ -179,21 +199,23 @@ E1000NetworkAdapter::~E1000NetworkAdapter() void E1000NetworkAdapter::handle_irq(const RegisterState&) { - out32(REG_IMASK, 0x1); + out32(REG_INTERRUPT_MASK_CLEAR, 0xffffffff); - u32 status = in32(0xc0); + u32 status = in32(REG_INTERRUPT_CAUSE_READ); if (status & 4) { u32 flags = in32(REG_CTRL); out32(REG_CTRL, flags | ECTRL_SLU); } - if (status & 0x10) { - // Threshold OK? - } if (status & 0x80) { receive(); } + if (status & 0x10) { + // Threshold OK? + } m_wait_queue.wake_all(); + + out32(REG_INTERRUPT_MASK_SET, INTERRUPT_LSC | INTERRUPT_RXT0 | INTERRUPT_RXO); } void E1000NetworkAdapter::detect_eeprom()