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

Submitted by Staale.Aakermann@kongsberg.com on Feb. 21, 2013, 8:04 a.m.

Details

Message ID BDD883B26EE1104B8A2B30223B09A48C1AC98A96@UKGAW-EXM-P03.kda.kongsberg.com
State Not Applicable
Headers show

Commit Message

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 hide | download patch | download mbox

--- 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);
 }