]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
net: ethernet: xilinx: Fix lost wake-up scenario in xmit
authorKedareswara rao Appana <appanad@xilinx.com>
Mon, 18 Sep 2017 05:35:45 +0000 (11:05 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Thu, 15 Mar 2018 14:17:44 +0000 (15:17 +0100)
In ndo_start_xmit we are checking for free BD count
and incase there are no available BD's we stop
the networking stack.

If these sequence of operation are preemted then
it will lead to classic Lost Wake-Up Problem.

If interrupt comes after xmit has checked  BD
count and TX post processing clears up the BD and
issue wake-up and then later xmit goes for a wait.
There is no one to wake up xmit and as wake-up
event is lost.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/net/ethernet/xilinx/xilinx_axienet.h
drivers/net/ethernet/xilinx/xilinx_axienet_main.c

index 9363cdf57915328c31eb7fd1b393821e05dcabae..99c0ea7cf6606dadbb9754a9882f138b9ef79081 100644 (file)
@@ -398,6 +398,7 @@ struct axidma_bd {
  * @regs:      Base address for the axienet_local device address space
  * @dma_regs:  Base address for the axidma device address space
  * @dma_err_tasklet: Tasklet structure to process Axi DMA errors
+ * @tx_lock:   Spin lock for tx path
  * @tx_irq:    Axidma TX IRQ number
  * @rx_irq:    Axidma RX IRQ number
  * @phy_mode:  Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X
@@ -442,6 +443,7 @@ struct axienet_local {
        void __iomem *dma_regs;
 
        struct tasklet_struct dma_err_tasklet;
+       spinlock_t tx_lock;
        spinlock_t rx_lock;             /* Spin lock */
        struct napi_struct napi;        /* NAPI Structure */
 
index 426eded2c5c34d0720a4ee2c96fdf702133ac072..29f2f607b393422090c4892bdb483443c8b6599c 100644 (file)
@@ -669,13 +669,16 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        dma_addr_t tail_p;
        struct axienet_local *lp = netdev_priv(ndev);
        struct axidma_bd *cur_p;
+       unsigned long flags;
 
        num_frag = skb_shinfo(skb)->nr_frags;
        cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
 
+       spin_lock_irqsave(&lp->tx_lock, flags);
        if (axienet_check_tx_bd_space(lp, num_frag)) {
                if (!netif_queue_stopped(ndev))
                        netif_stop_queue(ndev);
+               spin_unlock_irqrestore(&lp->tx_lock, flags);
                return NETDEV_TX_BUSY;
        }
 
@@ -722,6 +725,8 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        ++lp->tx_bd_tail;
        lp->tx_bd_tail %= TX_BD_NUM;
 
+       spin_unlock_irqrestore(&lp->tx_lock, flags);
+
        return NETDEV_TX_OK;
 }
 
@@ -1684,6 +1689,7 @@ static int axienet_probe(struct platform_device *pdev)
                goto free_netdev;
        }
 
+       spin_lock_init(&lp->tx_lock);
        spin_lock_init(&lp->rx_lock);
 
        /* Retrieve the MAC address */