From: Benjamin Poirier <hidden> Date: 2015-10-30 17:31:40
Hi,
For this series:
Benjamin Poirier (4):
e1000e: Remove unreachable code
e1000e: Do not read icr in Other interrupt
e1000e: Do not write lsc to ics in msi-x mode
e1000e: Fix msi-x interrupt automask
drivers/net/ethernet/intel/e1000e/defines.h | 3 +-
drivers/net/ethernet/intel/e1000e/netdev.c | 65 +++++++++++++----------------
2 files changed, 30 insertions(+), 38 deletions(-)
Changes in v2:
Address review comments from Alexander Duyck: extend cleanup of Other
interrupt handler and use tx_ring->ims_val.
The first three patches cleanup handling of Other interrupts and the
last patch fixes tx and rx interrupts. Please consider reading the
description for that patch before proceeding. I believe that the
following simple tracing statements are helpful in detecting the problem
fixed by the last patch.
@@ -1952,6 +1952,9 @@ static irqreturn_t e1000_intr_msix_rx(int __always_unused irq, void *data)structnet_device*netdev=data;structe1000_adapter*adapter=netdev_priv(netdev);structe1000_ring*rx_ring=adapter->rx_ring;+structe1000_hw*hw=&adapter->hw;++trace_printk("%s: rxq0 irq ims 0x%08x\n",netdev->name,er32(IMS));/* Write the ITR value calculated at the end of the*previousinterrupt.
@@ -2672,6 +2676,8 @@ static int e1000e_poll(struct napi_struct *napi, int weight)structnet_device*poll_dev=adapter->netdev;inttx_cleaned=1,work_done=0;+trace_printk("%s: poll starting ims 0x%08x\n",poll_dev->name,+er32(IMS));adapter=netdev_priv(poll_dev);if(!adapter->msix_entries||
@@ -2689,6 +2695,8 @@ static int e1000e_poll(struct napi_struct *napi, int weight)e1000_set_itr(adapter);napi_complete_done(napi,work_done);if(!test_bit(__E1000_DOWN,&adapter->state)){+trace_printk("%s: will enable rxq0 irq\n",+poll_dev->name);if(adapter->msix_entries)ew32(IMS,adapter->rx_ring->ims_val);else--------8<--------
With that patch but without the patches in this series we can see that rx irqs
occur at unexpected times:
<idle>-0 [000] .Ns. 1986.887517: e1000e_poll: eth1: will enable rxq0 irq
<idle>-0 [000] d.h. 1986.896654: e1000_intr_msix_rx: eth1: rxq0 irq ims 0x01500004
<idle>-0 [000] d.h. 1986.896657: e1000_intr_msix_rx: eth1: scheduling napi
<idle>-0 [000] d.H. 1986.896662: e1000_intr_msix_rx: eth1: rxq0 irq ims 0x01500004
<idle>-0 [000] ..s. 1986.896667: e1000e_poll: eth1: poll starting ims 0x01500004
Warning: many interrupts (2) before napi
<idle>-0 [000] ..s. 1986.896685: e1000e_poll: eth1: will enable rxq0 irq
<idle>-0 [000] d.h. 1990.688870: e1000_intr_msix_rx: eth1: scheduling napi
<idle>-0 [000] ..s. 1990.688875: e1000e_poll: eth1: poll starting ims 0x01500004
<idle>-0 [000] dNH. 1990.688913: e1000_intr_msix_rx: eth1: rxq0 irq ims 0x01500004
Warning: interrupt inside napi
<idle>-0 [000] .Ns. 1990.688916: e1000e_poll: eth1: will enable rxq0 irq
<idle>-0 [000] d.h. 1990.729688: e1000_intr_msix_rx: eth1: rxq0 irq ims 0x01500004
Here's a typical sequence after applying the patches in this series. Notice
that ims is changed. Another printk at the end of e1000e_poll would show it to
be 0x01500000.
<idle>-0 [000] d.h. 672874.016104: e1000_intr_msix_rx: eth1: rxq0 irq ims 0x01400000
<idle>-0 [000] d.h. 672874.016107: e1000_intr_msix_rx: eth1: scheduling napi
<idle>-0 [000] ..s. 672874.016112: e1000e_poll: eth1: poll starting ims 0x01400000
<idle>-0 [000] ..s. 672874.016126: e1000e_poll: eth1: will enable rxq0 irq
Finally, here's the script I used to generate the warnings above:
#!/usr/bin/python3
import sys
import re
import pprint
class NaE(Exception):
"Not an Event"
pass
class Event:
def __init__(self, line):
# sample events:
# <idle>-0 [000] d.h. 2025.256536: e1000_intr_msix_rx: eth1: rxq0 irq ims 0x01500004
# <idle>-0 [000] d.h. 2025.256539: e1000_intr_msix_rx: eth1: scheduling napi
# <idle>-0 [000] ..s. 2025.256544: e1000e_poll: eth1: poll starting ims 0x01500004
# <idle>-0 [000] ..s. 2025.256558: e1000e_poll: eth1: will enable rxq0 irq
retval = re.match(" +<?(?P<comm>.*)>?-(?P<pid>[0-9]+) +\[(?P<cpu>.*)\] (?P<flags>[^ ]+) +(?P<time>[0-9.]+): (?P<funcname>[^:]+): (?P<ifname>[^:]+): (?P<args>.*)", line)
if retval:
self.comm = retval.group("comm")
self.pid = retval.group("pid")
self.cpu = retval.group("cpu")
self.flags = retval.group("flags")
self.time = retval.group("time")
self.funcname = retval.group("funcname")
self.ifname = retval.group("ifname")
self.args = retval.group("args")
else:
raise NaE
class Machine:
pass
class State:
def __init__(self, machine):
self.machine = machine
def entry(self, event):
pass
def transition(self, event):
"self.machine should be considered read-only in transition"
return State(self.machine)
def run(self, event):
pass
def exit(self, event):
pass
def advance(self, event):
nextState = self.transition(event)
if (nextState != self):
self.exit(event)
nextState.entry(event)
nextState.run(event)
return nextState
# general receive processing machine
def enteringNapi(machine, event):
if event.args.startswith("poll starting"):
return True
else:
return False
def exitingNapi(machine, event):
if event.args.startswith("will enable"):
return True
else:
return False
class OutsideNapi(State):
def entry(self, event):
self.machine.intr = []
def transition(self, event):
if enteringNapi(self.machine, event):
return InsideNapi(self.machine)
else:
return self
def run(self, event):
if event.args.startswith("rxq0 irq"):
self.machine.intr.append(event)
def exit(self, event):
if len(self.machine.intr) > 1:
print("Warning: many interrupts (%d) before napi at %s" % (
len(self.machine.intr), event.time,))
class InsideNapi(State):
def transition(self, event):
if exitingNapi(self.machine, event):
return OutsideNapi(self.machine)
else:
return self
def run(self, event):
if event.args.startswith("rxq0 irq"):
print("Warning: interrupt inside napi")
class UnknownState(State):
def transition(self, event):
if enteringNapi(self.machine, event):
return InsideNapi(self.machine)
elif exitingNapi(self.machine, event):
return ExitingNapi(self.machine)
else:
return self
if __name__ == '__main__':
debug = False
state = UnknownState(Machine())
for line in sys.stdin:
print(line, end='')
try:
event = Event(line)
except NaE:
pass
else:
if debug:
pprinter.pprint(event)
state = state.advance(event)
--
2.6.0
From: Benjamin Poirier <hidden> Date: 2015-10-30 17:31:47
msi-x interrupts are not shared so there's no need to check if the
interrupt was really from this adapter.
Signed-off-by: Benjamin Poirier <redacted>
---
drivers/net/ethernet/intel/e1000e/netdev.c | 6 ------
1 file changed, 6 deletions(-)
From: Benjamin Poirier <hidden> Date: 2015-10-30 17:31:54
Using eiac instead of reading icr allows us to avoid interference with
rx and tx interrupts in the Other interrupt handler.
According to the 82574 datasheet section 10.2.4.1, interrupt causes that
trigger the Other interrupt are
1) Link Status Change.
2) Receiver Overrun.
3) MDIO Access Complete.
4) Small Receive Packet Detected.
5) Receive ACK Frame Detected.
6) Manageability Event Detected.
Causes 3, 4, 5 are related to features which are not enabled by the
driver. Always assume that cause 1 is what triggered the Other interrupt
and set get_link_status. Cause 2 and 6 should be rare enough that the
extra cost of needlessly re-reading the link status is negligible.
Signed-off-by: Benjamin Poirier <redacted>
---
drivers/net/ethernet/intel/e1000e/netdev.c | 23 ++++++++---------------
1 file changed, 8 insertions(+), 15 deletions(-)
@@ -1905,24 +1905,16 @@ static irqreturn_t e1000_msix_other(int __always_unused irq, void *data)structnet_device*netdev=data;structe1000_adapter*adapter=netdev_priv(netdev);structe1000_hw*hw=&adapter->hw;-u32icr=er32(ICR);-if(icr&adapter->eiac_mask)-ew32(ICS,(icr&adapter->eiac_mask));+/* Assume that the Other interrupt was triggered by LSC */+hw->mac.get_link_status=true;-if(icr&E1000_ICR_OTHER){-if(!(icr&E1000_ICR_LSC))-gotono_link_interrupt;-hw->mac.get_link_status=true;-/* guard against interrupt when we're going down */-if(!test_bit(__E1000_DOWN,&adapter->state))-mod_timer(&adapter->watchdog_timer,jiffies+1);+/* guard against interrupt when we're going down */+if(!test_bit(__E1000_DOWN,&adapter->state)){+mod_timer(&adapter->watchdog_timer,jiffies+1);+ew32(IMS,E1000_IMS_OTHER);}-no_link_interrupt:-if(!test_bit(__E1000_DOWN,&adapter->state))-ew32(IMS,E1000_IMS_LSC|E1000_IMS_OTHER);-returnIRQ_HANDLED;}
@@ -2019,6 +2011,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)hw->hw_addr+E1000_EITR_82574(vector));elsewritel(1,hw->hw_addr+E1000_EITR_82574(vector));+adapter->eiac_mask|=E1000_IMS_OTHER;/* Cause Tx interrupts on every write back */ivar|=(1<<31);
From: Benjamin Poirier <hidden> Date: 2015-10-30 17:32:01
In msi-x mode, there is no handler for the lsc interrupt so there is no
point in writing that to ics now that we always assume Other interrupts
are caused by lsc.
Reviewed-by: Jasna Hodzic <redacted>
Signed-off-by: Benjamin Poirier <redacted>
---
drivers/net/ethernet/intel/e1000e/defines.h | 3 ++-
drivers/net/ethernet/intel/e1000e/netdev.c | 27 ++++++++++++++++-----------
2 files changed, 18 insertions(+), 12 deletions(-)
@@ -4131,10 +4131,23 @@ void e1000e_reset(struct e1000_adapter *adapter)}-inte1000e_up(structe1000_adapter*adapter)+/**+*e1000e_trigger_lsc-triggeralscinterrupt+*+*Firealinkstatuschangeinterrupttostartthewatchdog.+**/+staticvoide1000e_trigger_lsc(structe1000_adapter*adapter){structe1000_hw*hw=&adapter->hw;+if(adapter->msix_entries)+ew32(ICS,E1000_ICS_OTHER);+else+ew32(ICS,E1000_ICS_LSC);+}++inte1000e_up(structe1000_adapter*adapter)+{/* hardware has been reset, we need to reload some things */e1000_configure(adapter);
@@ -4146,11 +4159,7 @@ int e1000e_up(struct e1000_adapter *adapter)netif_start_queue(adapter->netdev);-/* fire a link change interrupt to start the watchdog */-if(adapter->msix_entries)-ew32(ICS,E1000_ICS_LSC|E1000_ICR_OTHER);-else-ew32(ICS,E1000_ICS_LSC);+e1000e_trigger_lsc(adapter);return0;}
@@ -4577,11 +4586,7 @@ static int e1000_open(struct net_device *netdev)hw->mac.get_link_status=true;pm_runtime_put(&pdev->dev);-/* fire a link status change interrupt to start the watchdog */-if(adapter->msix_entries)-ew32(ICS,E1000_ICS_LSC|E1000_ICR_OTHER);-else-ew32(ICS,E1000_ICS_LSC);+e1000e_trigger_lsc(adapter);return0;
From: Benjamin Poirier <hidden> Date: 2015-10-30 17:32:08
Since the introduction of 82574 support in e1000e, the driver has worked
on the assumption that msi-x interrupt generation is automatically
disabled after each irq. As it turns out, this is not the case.
Currently, rx interrupts can fire multiple times before and during napi
processing. This can be a problem for users because frames that arrive
in a certain window (after adapter->clean_rx() but before
napi_complete_done() has cleared NAPI_STATE_SCHED) generate an interrupt
which does not lead to napi_schedule(). These frames sit in the rx queue
until another frame arrives (a tcp retransmit for example).
While the EIAC and CTRL_EXT registers are properly configured for irq
automask, the modification of IAM in e1000_configure_msix() is what
prevents automask from working as intended.
This patch removes that erroneous write and fixes interrupt rearming for
tx interrupts.
Reported-by: Frank Steiner <redacted>
Signed-off-by: Benjamin Poirier <redacted>
---
drivers/net/ethernet/intel/e1000e/netdev.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
@@ -1932,6 +1932,9 @@ static irqreturn_t e1000_intr_msix_tx(int __always_unused irq, void *data)/* Ring was not completely cleaned, so fire another interrupt */ew32(ICS,tx_ring->ims_val);+if(!test_bit(__E1000_DOWN,&adapter->state))+ew32(IMS,adapter->tx_ring->ims_val);+returnIRQ_HANDLED;}
@@ -2020,11 +2023,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)/* enable MSI-X PBA support */ctrl_ext=er32(CTRL_EXT);-ctrl_ext|=E1000_CTRL_EXT_PBA_CLR;--/* Auto-Mask Other interrupts upon ICR read */-ew32(IAM,~E1000_EIAC_MASK_82574|E1000_IMS_OTHER);-ctrl_ext|=E1000_CTRL_EXT_EIAME;+ctrl_ext|=E1000_CTRL_EXT_PBA_CLR|E1000_CTRL_EXT_EIAME;ew32(CTRL_EXT,ctrl_ext);e1e_flush();}
From: Alexander Duyck <hidden> Date: 2015-10-30 19:19:06
On 10/30/2015 10:31 AM, Benjamin Poirier wrote:
Using eiac instead of reading icr allows us to avoid interference with
rx and tx interrupts in the Other interrupt handler.
According to the 82574 datasheet section 10.2.4.1, interrupt causes that
trigger the Other interrupt are
1) Link Status Change.
2) Receiver Overrun.
3) MDIO Access Complete.
4) Small Receive Packet Detected.
5) Receive ACK Frame Detected.
6) Manageability Event Detected.
Causes 3, 4, 5 are related to features which are not enabled by the
driver. Always assume that cause 1 is what triggered the Other interrupt
and set get_link_status. Cause 2 and 6 should be rare enough that the
extra cost of needlessly re-reading the link status is negligible.
Signed-off-by: Benjamin Poirier <redacted>
You might want to instead use a write of LSC to the ICR instead of just
using auto-clear and not enabling LSC. My concern is that you might no
longer be getting link status change events at all. An easy test is to
just unplug/plug the cable a few times, or run "ethtool -r" on the link
partner if connected back to back. You should see messages appear in
the dmesg log indicating that the link state changed.
In addition you should probably clear the IAME bit in the CTRL_EXT
register so that you don't risk masking the interrupts on the ICR read
or write.
@@ -1905,24 +1905,16 @@ static irqreturn_t e1000_msix_other(int __always_unused irq, void *data)structnet_device*netdev=data;structe1000_adapter*adapter=netdev_priv(netdev);structe1000_hw*hw=&adapter->hw;-u32icr=er32(ICR);-if(icr&adapter->eiac_mask)-ew32(ICS,(icr&adapter->eiac_mask));+/* Assume that the Other interrupt was triggered by LSC */+hw->mac.get_link_status=true;-if(icr&E1000_ICR_OTHER){-if(!(icr&E1000_ICR_LSC))-gotono_link_interrupt;-hw->mac.get_link_status=true;-/* guard against interrupt when we're going down */-if(!test_bit(__E1000_DOWN,&adapter->state))-mod_timer(&adapter->watchdog_timer,jiffies+1);
You could probably just pop a write to the ICR register here to clear
the LSC bit since you are auto clearing the OTHER bit.
quoted hunk
+ /* guard against interrupt when we're going down */
+ if (!test_bit(__E1000_DOWN, &adapter->state)) {
+ mod_timer(&adapter->watchdog_timer, jiffies + 1);
+ ew32(IMS, E1000_IMS_OTHER);
}
-no_link_interrupt:
- if (!test_bit(__E1000_DOWN, &adapter->state))
- ew32(IMS, E1000_IMS_LSC | E1000_IMS_OTHER);
-
return IRQ_HANDLED;
}
@@ -2019,6 +2011,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter) hw->hw_addr + E1000_EITR_82574(vector)); else writel(1, hw->hw_addr + E1000_EITR_82574(vector));+ adapter->eiac_mask |= E1000_IMS_OTHER; /* Cause Tx interrupts on every write back */ ivar |= (1 << 31);
You might want to consider adding a write to IAM here that would limit
the auto masking to same bits you are auto clearing.
I would probably leave E1000_IMS_LSC set in the IMS. No need to
auto-mask it since the mask for other will keep it from triggering more
than once.
From: Benjamin Poirier <hidden> Date: 2015-11-04 23:19:57
On 2015/10/30 12:19, Alexander Duyck wrote:
On 10/30/2015 10:31 AM, Benjamin Poirier wrote:
quoted
Using eiac instead of reading icr allows us to avoid interference with
rx and tx interrupts in the Other interrupt handler.
According to the 82574 datasheet section 10.2.4.1, interrupt causes that
trigger the Other interrupt are
1) Link Status Change.
2) Receiver Overrun.
3) MDIO Access Complete.
4) Small Receive Packet Detected.
5) Receive ACK Frame Detected.
6) Manageability Event Detected.
Causes 3, 4, 5 are related to features which are not enabled by the
driver. Always assume that cause 1 is what triggered the Other interrupt
and set get_link_status. Cause 2 and 6 should be rare enough that the
extra cost of needlessly re-reading the link status is negligible.
Signed-off-by: Benjamin Poirier <redacted>
You might want to instead use a write of LSC to the ICR instead of just
using auto-clear and not enabling LSC. My concern is that you might no
longer be getting link status change events at all. An easy test is to just
unplug/plug the cable a few times, or run "ethtool -r" on the link partner
if connected back to back. You should see messages appear in the dmesg log
indicating that the link state changed.
In addition you should probably clear the IAME bit in the CTRL_EXT register
so that you don't risk masking the interrupts on the ICR read or write.
Thanks, your concern about not getting LSC events was right. After more
experimentation I noticed that in order for the Other interrupt to be
raised for each of these six conditions, the IMS bit for that condition
must also be set. I've restored setting LSC in IMS. OTOH, I don't see a
need to clear LSC from ICR. Even without an ICR read or write-to-clear
to clear the LSC bit, Other interrupts are raised to signal LSC events.
I'll wait for net-next to reopen and send v3.
From: Alexander Duyck <hidden> Date: 2015-11-04 23:26:26
On 11/04/2015 03:19 PM, Benjamin Poirier wrote:
On 2015/10/30 12:19, Alexander Duyck wrote:
quoted
On 10/30/2015 10:31 AM, Benjamin Poirier wrote:
quoted
Using eiac instead of reading icr allows us to avoid interference with
rx and tx interrupts in the Other interrupt handler.
According to the 82574 datasheet section 10.2.4.1, interrupt causes that
trigger the Other interrupt are
1) Link Status Change.
2) Receiver Overrun.
3) MDIO Access Complete.
4) Small Receive Packet Detected.
5) Receive ACK Frame Detected.
6) Manageability Event Detected.
Causes 3, 4, 5 are related to features which are not enabled by the
driver. Always assume that cause 1 is what triggered the Other interrupt
and set get_link_status. Cause 2 and 6 should be rare enough that the
extra cost of needlessly re-reading the link status is negligible.
Signed-off-by: Benjamin Poirier <redacted>
You might want to instead use a write of LSC to the ICR instead of just
using auto-clear and not enabling LSC. My concern is that you might no
longer be getting link status change events at all. An easy test is to just
unplug/plug the cable a few times, or run "ethtool -r" on the link partner
if connected back to back. You should see messages appear in the dmesg log
indicating that the link state changed.
In addition you should probably clear the IAME bit in the CTRL_EXT register
so that you don't risk masking the interrupts on the ICR read or write.
Thanks, your concern about not getting LSC events was right. After more
experimentation I noticed that in order for the Other interrupt to be
raised for each of these six conditions, the IMS bit for that condition
must also be set. I've restored setting LSC in IMS. OTOH, I don't see a
need to clear LSC from ICR. Even without an ICR read or write-to-clear
to clear the LSC bit, Other interrupts are raised to signal LSC events.
I'll wait for net-next to reopen and send v3.
You probably don't need to wait. The Intel-wired tree operates outside
of Dave's merge window, and it will take some time for the patches to be
validated before the Jeff can submit them to Dave.
- Alex