]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
xilinx_emacps: Handle NAPI "rotten packets"
authorThomas Betker <thomas.betker@rohde-schwarz.com>
Sat, 11 May 2013 09:10:40 +0000 (11:10 +0200)
committerMichal Simek <michal.simek@xilinx.com>
Wed, 5 Jun 2013 08:48:44 +0000 (10:48 +0200)
When xemacps_rx_poll() has processed the Rx BD ring until it is empty,
another packet may come in before we have unmasked the Rx interrupts.
No interrupt is signalled for it, and it is left to rot in the BD ring
until the next packet is received.

This is what NAPI calls a "rotting packet", and it actually seems to
occur quite often in heavy traffic (about 30 times per second),
sometimes leading to Rx stalls.

The solution is standard: Check the Rx BD ring again after unmasking
the Rx interrupts, and reschedule NAPI (and remask the interrupts) when
a new packet is detected.

Note that we no longer need an inner loop around xemacps_rx(); the
latter returns when the BD list is empty or the budget is reached, so
we are usually done. [Previously, xemacps_rx() would also return when
XEMACPS_RECV_BD_CNT was reached, so we had to loop over it.]

Signed-off-by: Thomas Betker <thomas.betker@rohde-schwarz.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/net/ethernet/xilinx/xilinx_emacps.c

index 7f967a643c0230fabd48edae773515b6855153cc..20dc13acc89edf5ae3cf1d0e8398371ac22c9ad8 100644 (file)
@@ -1196,32 +1196,38 @@ static int xemacps_rx_poll(struct napi_struct *napi, int budget)
 {
        struct net_local *lp = container_of(napi, struct net_local, napi);
        int work_done = 0;
-       int temp_work_done;
        u32 regval;
 
        spin_lock(&lp->rx_lock);
-       while (work_done < budget) {
+       while (1) {
                regval = xemacps_read(lp->baseaddr, XEMACPS_RXSR_OFFSET);
                xemacps_write(lp->baseaddr, XEMACPS_RXSR_OFFSET, regval);
                if (regval & XEMACPS_RXSR_HRESPNOK_MASK)
                        dev_err(&lp->pdev->dev, "RX error 0x%x\n", regval);
-               temp_work_done = xemacps_rx(lp, budget - work_done);
-               work_done += temp_work_done;
-               if (temp_work_done <= 0)
+
+               work_done += xemacps_rx(lp, budget - work_done);
+               if (work_done >= budget)
                        break;
-       }
 
-       if (work_done >= budget) {
-               spin_unlock(&lp->rx_lock);
-               return work_done;
-       }
+               napi_complete(napi);
+               /* We disabled RX interrupts in interrupt service
+                * routine, now it is time to enable it back.
+                */
+               xemacps_write(lp->baseaddr,
+                       XEMACPS_IER_OFFSET, XEMACPS_IXR_FRAMERX_MASK);
 
-       napi_complete(napi);
-       /* We disabled RX interrupts in interrupt service
-        * routine, now it is time to enable it back.
-        */
-       xemacps_write(lp->baseaddr,
-               XEMACPS_IER_OFFSET, XEMACPS_IXR_FRAMERX_MASK);
+               /* If a packet has come in between the last check of the BD
+                * list and unmasking the interrupts, we may have missed the
+                * interrupt, so reschedule here.
+                */
+               if ((lp->rx_bd[lp->rx_bd_ci].addr & XEMACPS_RXBUF_NEW_MASK)
+               &&  napi_reschedule(napi)) {
+                       xemacps_write(lp->baseaddr,
+                               XEMACPS_IDR_OFFSET, XEMACPS_IXR_FRAMERX_MASK);
+                       continue;
+               }
+               break;
+       }
        spin_unlock(&lp->rx_lock);
        return work_done;
 }