Skip to content

Commit

Permalink
e1000e: Add delays after writing to registers
Browse files Browse the repository at this point in the history
There is a noticeable impact on determinism when a large number of
writes are flushed. Writes to the hardware registers are sent across
the PCI bus and take a significant amount of time to complete after
a flush, which causes high priority tasks (including interrupts) to
be delayed. This problem stems from an issue with the PCI bus where
a fabric lock is held during I/O buffer drain. This process can be
detrimental to real-time systems and is seen whenever a large number
of MMIO writes are issued. In the case of the e1000 drivers, when a
device is reset several tables (MTA, VLAN, etc) are rewritten,
generating enough MMIO writes over PCI for the latency issues to be
seen.

Adding a delay after long series of writes gives them time to
complete (drain the I/O buffer), and for higher priority tasks to
run unimpeded.

Signed-off-by: Jonathan David <jonathan.david@ni.com>
Signed-off-by: Brad Mouring <brad.mouring@ni.com>
[bstreiff: remove unused 'rar_count']
Signed-off-by: Brandon Streiff <brandon.streiff@ni.com>
[gratian: update Kconfig '---help---' to 'help']
Signed-off-by: Gratian Crisan <gratian.crisan@ni.com>
  • Loading branch information
Jonathan David authored and gratian committed Dec 1, 2020
1 parent b4c1810 commit a555b1a
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 0 deletions.
9 changes: 9 additions & 0 deletions drivers/net/ethernet/intel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ config E1000E_HWTS
devices. The cross-timestamp is available through the PTP clock
driver precise cross-timestamp ioctl (PTP_SYS_OFFSET_PRECISE).

config E1000_DELAY
bool "Add delays to e1000x drivers"
default n
depends on (E1000E || E1000) && PREEMPT_RT_FULL
help
Enable delays after large numbers of MMIO writes to registers.
The delays aid in preventing noticeable impact on real-time
performance when a connection is interrupted.

config IGB
tristate "Intel(R) 82575/82576 PCI-Express Gigabit Ethernet support"
depends on PCI
Expand Down
7 changes: 7 additions & 0 deletions drivers/net/ethernet/intel/e1000/e1000.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ struct e1000_rx_ring {
#define E1000_TX_DESC(R, i) E1000_GET_DESC(R, i, e1000_tx_desc)
#define E1000_CONTEXT_DESC(R, i) E1000_GET_DESC(R, i, e1000_context_desc)

/* Time to wait after writing large amount of data to registers */
#ifdef CONFIG_E1000_DELAY
#define E1000_WR_DELAY() usleep_range(50, 100)
#else
#define E1000_WR_DELAY()
#endif

/* board specific private data structure */

struct e1000_adapter {
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/ethernet/intel/e1000/e1000_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2158,6 +2158,7 @@ static void e1000_enter_82542_rst(struct e1000_adapter *adapter)
rctl = er32(RCTL);
rctl |= E1000_RCTL_RST;
ew32(RCTL, rctl);
E1000_WR_DELAY();
E1000_WRITE_FLUSH();
mdelay(5);

Expand All @@ -2174,6 +2175,7 @@ static void e1000_leave_82542_rst(struct e1000_adapter *adapter)
rctl = er32(RCTL);
rctl &= ~E1000_RCTL_RST;
ew32(RCTL, rctl);
E1000_WR_DELAY();
E1000_WRITE_FLUSH();
mdelay(5);

Expand Down Expand Up @@ -2322,6 +2324,9 @@ static void e1000_set_rx_mode(struct net_device *netdev)
*/
E1000_WRITE_REG_ARRAY(hw, MTA, i, mcarray[i]);
}

E1000_WR_DELAY();

E1000_WRITE_FLUSH();

if (hw->mac_type == e1000_82542_rev2_0)
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/ethernet/intel/e1000e/82571.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw)
{
u32 swsm;

E1000_WR_DELAY();

swsm = er32(SWSM);
swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI);
ew32(SWSM, swsm);
Expand Down Expand Up @@ -545,6 +547,7 @@ static void e1000_put_hw_semaphore_82573(struct e1000_hw *hw)
{
u32 extcnf_ctrl;

E1000_WR_DELAY();
extcnf_ctrl = er32(EXTCNF_CTRL);
extcnf_ctrl &= ~E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
ew32(EXTCNF_CTRL, extcnf_ctrl);
Expand Down Expand Up @@ -1094,6 +1097,8 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
for (i = 0; i < mac->mta_reg_count; i++)
E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);

E1000_WR_DELAY();

/* Setup link and flow control */
ret_val = mac->ops.setup_link(hw);

Expand Down
7 changes: 7 additions & 0 deletions drivers/net/ethernet/intel/e1000e/e1000.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ struct e1000_info;
/* Time to wait before putting the device into D3 if there's no link (in ms). */
#define LINK_TIMEOUT 100

/* Time to wait after writing large amount of data to registers */
#ifdef CONFIG_E1000_DELAY
#define E1000_WR_DELAY() usleep_range(50, 100)
#else
#define E1000_WR_DELAY()
#endif

/* Count for polling __E1000_RESET condition every 10-20msec.
* Experimentation has shown the reset can take approximately 210msec.
*/
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/intel/e1000e/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw,
/* replace the entire MTA table */
for (i = hw->mac.mta_reg_count - 1; i >= 0; i--)
E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, hw->mac.mta_shadow[i]);
E1000_WR_DELAY();
e1e_flush();
}

Expand Down
7 changes: 7 additions & 0 deletions drivers/net/ethernet/intel/e1000e/netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2980,6 +2980,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter)
e1000e_update_tdt_wa(tx_ring, 0);
else
writel(0, tx_ring->tail);
E1000_WR_DELAY();

/* Set the Tx Interrupt Delay register */
ew32(TIDV, adapter->tx_int_delay);
Expand Down Expand Up @@ -3257,6 +3258,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
rctl = er32(RCTL);
if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
ew32(RCTL, rctl & ~E1000_RCTL_EN);
E1000_WR_DELAY();
e1e_flush();
usleep_range(10000, 11000);

Expand Down Expand Up @@ -3286,6 +3288,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
ctrl_ext |= E1000_CTRL_EXT_IAME;
ew32(IAM, 0xffffffff);
ew32(CTRL_EXT, ctrl_ext);
E1000_WR_DELAY();
e1e_flush();

/* Setup the HW Rx Head and Tail Descriptor Pointers and
Expand All @@ -3305,6 +3308,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
e1000e_update_rdt_wa(rx_ring, 0);
else
writel(0, rx_ring->tail);
E1000_WR_DELAY();

/* Enable Receive Checksum Offload for TCP and UDP */
rxcsum = er32(RXCSUM);
Expand Down Expand Up @@ -3430,6 +3434,7 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev)
ew32(RAH(rar_entries), 0);
ew32(RAL(rar_entries), 0);
}
E1000_WR_DELAY();
e1e_flush();

return count;
Expand Down Expand Up @@ -3505,10 +3510,12 @@ static void e1000e_setup_rss_hash(struct e1000_adapter *adapter)
netdev_rss_key_fill(rss_key, sizeof(rss_key));
for (i = 0; i < 10; i++)
ew32(RSSRK(i), rss_key[i]);
E1000_WR_DELAY();

/* Direct all traffic to queue 0 */
for (i = 0; i < 32; i++)
ew32(RETA(i), 0);
E1000_WR_DELAY();

/* Disable raw packet checksumming so that RSS hash is placed in
* descriptor on writeback.
Expand Down

0 comments on commit a555b1a

Please sign in to comment.