Patchwork PATCH [1/1] GIANFAR infiinate loop on reception of PAUSE FRAME

login
register
mail settings
Submitter Staale.Aakermann@kongsberg.com
Date Feb. 21, 2013, 8:04 a.m.
Message ID <BDD883B26EE1104B8A2B30223B09A48C1AC98A96@UKGAW-EXM-P03.kda.kongsberg.com>
Download mbox | patch
Permalink /patch/222196/
State Not Applicable
Headers show

Comments

Staale.Aakermann@kongsberg.com - Feb. 21, 2013, 8:04 a.m.
There is a undesired behavior in the GIANFAR driver that causes a infinite loop stalling the CPU when reception of PAUSE FRAMES.
I found this error during testing with a DSL modem (Westermo http://www.westermo.com). This equipment spawns PAUSE FRAMES continuously on its LAN side as long as the link is not up on the WAN side. The GIANFAR driver does not handle that it get an timeout on transmission on the first packet sent. It will try to gracefully stop all ongoing transactions, but this will fail as there aren't any, and it will loop forever.

Patch

--- drivers/net/ethernet/freescale/gianfar.c    2012-12-11 04:30:57.000000000 +0100
+++ drivers/net/ethernet/freescale/gianfar.c    2013-02-20 20:28:13.201931602 +0100
@@ -1600,8 +1600,10 @@ 
        struct gfar_private *priv = netdev_priv(dev);
        struct gfar __iomem *regs = NULL;
        u32 tempval;
-       int i;
-
+       u32 reset_value = DMACTRL_GRS;
+       u32 event_value = IEVENT_GRSC;
+       int i = 0;
+
        for (i = 0; i < priv->num_grps; i++) {
                regs = priv->gfargrp[i].regs;
                /* Mask all interrupts */
@@ -1612,22 +1614,32 @@ 
        }

        regs = priv->gfargrp[0].regs;
+
+       if (!((gfar_read(&regs->tctrl) & TCTRL_RFCPAUSE) == TCTRL_RFCPAUSE))
+       {
+               reset_value |= DMACTRL_GTS;
+               event_value |= IEVENT_GTSC;
+
+       }
+
        /* Stop the DMA, and wait for it to stop */
        tempval = gfar_read(&regs->dmactrl);
-       if ((tempval & (DMACTRL_GRS | DMACTRL_GTS)) !=
-           (DMACTRL_GRS | DMACTRL_GTS)) {
+       if ((tempval & reset_value) != reset_value) {
                int ret;

-               tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+               tempval |= reset_value;
                gfar_write(&regs->dmactrl, tempval);

                do {
-                       ret = spin_event_timeout(((gfar_read(&regs->ievent) &
-                                (IEVENT_GRSC | IEVENT_GTSC)) ==
-                                (IEVENT_GRSC | IEVENT_GTSC)), 1000000, 0);
+
+                       ret = spin_event_timeout(((gfar_read(&regs->ievent) & event_value) == event_value), 1000000, 0);
+
                        if (!ret && !(gfar_read(&regs->ievent) & IEVENT_GRSC))
+                       {
                                ret = __gfar_is_rx_idle(priv);
+                       }
                } while (!ret);
+
        }
 }

@@ -1668,9 +1682,10 @@ 
        lock_rx_qs(priv);

        gfar_halt(dev);
-
+
        unlock_rx_qs(priv);
        unlock_tx_qs(priv);
+
        local_irq_restore(flags);

        /* Free the IRQs */
@@ -2424,22 +2439,32 @@ 
        struct gfar_private *priv = container_of(work, struct gfar_private,
                                                 reset_task);
        struct net_device *dev = priv->ndev;
-
        if (dev->flags & IFF_UP) {
                netif_tx_stop_all_queues(dev);
                stop_gfar(dev);
                startup_gfar(dev);
                netif_tx_start_all_queues(dev);
        }
-
        netif_tx_schedule_all(dev);
 }

 static void gfar_timeout(struct net_device *dev)
 {
        struct gfar_private *priv = netdev_priv(dev);
+       struct gfar __iomem *regs = NULL;

-       dev->stats.tx_errors++;
+       regs = priv->gfargrp[0].regs;
+
+       if ((gfar_read(&regs->tctrl) & TCTRL_RFCPAUSE) == TCTRL_RFCPAUSE)
+       {
+               printk(KERN_DEBUG "PAUSE FRAME RECEIVED\n");
+               dev->stats.tx_dropped++;
+       }
+       else
+       {
+               dev->stats.tx_errors++;
+       }
+
        schedule_work(&priv->reset_task);
 }