Skip to content

Commit

Permalink
Merge branch 'nps_enet-fixes'
Browse files Browse the repository at this point in the history
Elad Kanfi says:

====================
nps_enet: Net driver bugs fix

v3:
tx_packet_sent flag is not necessary, use socket buffer pointer
instead.
Use wmb() instead of smp_wmb().

v2:
Remove code style commit for now.
Code style commit will be added after the bugs fix will be approved.

Summary:
 1. Bug description: TX done interrupts that arrives while interrupts
    are masked, during NAPI poll, will not trigger an interrupt handling.
    Since TX interrupt is of level edge we will lose the TX done interrupt.
    As a result all pending tx frames will get no service.

    Solution: Check if there is a pending tx request after unmasking the
    interrupt and if answer is yes then re-add ourselves to
    the NAPI poll list.

 2. Bug description: CPU-A before sending a frame will set a variable
    to true. CPU-B that executes the tx done interrupt service routine
    might read a non valid value of that variable.

    Solution: Use the socket buffer pointer instead of the variable,
    and add a write memory barrier at the tx sending function after
    the pointer is set.
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed May 10, 2016
2 parents d99079e + 05c00d8 commit 3b0d190
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 8 deletions.
30 changes: 24 additions & 6 deletions drivers/net/ethernet/ezchip/nps_enet.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ static void nps_enet_tx_handler(struct net_device *ndev)
u32 tx_ctrl_nt = (tx_ctrl_value & TX_CTL_NT_MASK) >> TX_CTL_NT_SHIFT;

/* Check if we got TX */
if (!priv->tx_packet_sent || tx_ctrl_ct)
if (!priv->tx_skb || tx_ctrl_ct)
return;

/* Ack Tx ctrl register */
Expand All @@ -160,7 +160,7 @@ static void nps_enet_tx_handler(struct net_device *ndev)
}

dev_kfree_skb(priv->tx_skb);
priv->tx_packet_sent = false;
priv->tx_skb = NULL;

if (netif_queue_stopped(ndev))
netif_wake_queue(ndev);
Expand All @@ -183,6 +183,9 @@ static int nps_enet_poll(struct napi_struct *napi, int budget)
work_done = nps_enet_rx_handler(ndev);
if (work_done < budget) {
u32 buf_int_enable_value = 0;
u32 tx_ctrl_value = nps_enet_reg_get(priv, NPS_ENET_REG_TX_CTL);
u32 tx_ctrl_ct =
(tx_ctrl_value & TX_CTL_CT_MASK) >> TX_CTL_CT_SHIFT;

napi_complete(napi);

Expand All @@ -192,6 +195,18 @@ static int nps_enet_poll(struct napi_struct *napi, int budget)

nps_enet_reg_set(priv, NPS_ENET_REG_BUF_INT_ENABLE,
buf_int_enable_value);

/* in case we will get a tx interrupt while interrupts
* are masked, we will lose it since the tx is edge interrupt.
* specifically, while executing the code section above,
* between nps_enet_tx_handler and the interrupts enable, all
* tx requests will be stuck until we will get an rx interrupt.
* the two code lines below will solve this situation by
* re-adding ourselves to the poll list.
*/

if (priv->tx_skb && !tx_ctrl_ct)
napi_reschedule(napi);
}

return work_done;
Expand All @@ -217,7 +232,7 @@ static irqreturn_t nps_enet_irq_handler(s32 irq, void *dev_instance)
u32 tx_ctrl_ct = (tx_ctrl_value & TX_CTL_CT_MASK) >> TX_CTL_CT_SHIFT;
u32 rx_ctrl_cr = (rx_ctrl_value & RX_CTL_CR_MASK) >> RX_CTL_CR_SHIFT;

if ((!tx_ctrl_ct && priv->tx_packet_sent) || rx_ctrl_cr)
if ((!tx_ctrl_ct && priv->tx_skb) || rx_ctrl_cr)
if (likely(napi_schedule_prep(&priv->napi))) {
nps_enet_reg_set(priv, NPS_ENET_REG_BUF_INT_ENABLE, 0);
__napi_schedule(&priv->napi);
Expand Down Expand Up @@ -387,8 +402,6 @@ static void nps_enet_send_frame(struct net_device *ndev,
/* Write the length of the Frame */
tx_ctrl_value |= length << TX_CTL_NT_SHIFT;

/* Indicate SW is done */
priv->tx_packet_sent = true;
tx_ctrl_value |= NPS_ENET_ENABLE << TX_CTL_CT_SHIFT;
/* Send Frame */
nps_enet_reg_set(priv, NPS_ENET_REG_TX_CTL, tx_ctrl_value);
Expand Down Expand Up @@ -465,7 +478,7 @@ static s32 nps_enet_open(struct net_device *ndev)
s32 err;

/* Reset private variables */
priv->tx_packet_sent = false;
priv->tx_skb = NULL;
priv->ge_mac_cfg_2_value = 0;
priv->ge_mac_cfg_3_value = 0;

Expand Down Expand Up @@ -534,6 +547,11 @@ static netdev_tx_t nps_enet_start_xmit(struct sk_buff *skb,

priv->tx_skb = skb;

/* make sure tx_skb is actually written to the memory
* before the HW is informed and the IRQ is fired.
*/
wmb();

nps_enet_send_frame(ndev, skb);

return NETDEV_TX_OK;
Expand Down
2 changes: 0 additions & 2 deletions drivers/net/ethernet/ezchip/nps_enet.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,12 @@
* struct nps_enet_priv - Storage of ENET's private information.
* @regs_base: Base address of ENET memory-mapped control registers.
* @irq: For RX/TX IRQ number.
* @tx_packet_sent: SW indication if frame is being sent.
* @tx_skb: socket buffer of sent frame.
* @napi: Structure for NAPI.
*/
struct nps_enet_priv {
void __iomem *regs_base;
s32 irq;
bool tx_packet_sent;
struct sk_buff *tx_skb;
struct napi_struct napi;
u32 ge_mac_cfg_2_value;
Expand Down

0 comments on commit 3b0d190

Please sign in to comment.