From patchwork Thu Feb 21 08:04:06 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Staale.Aakermann@kongsberg.com X-Patchwork-Id: 222196 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [IPv6:::1]) by ozlabs.org (Postfix) with ESMTP id C73952C03AA for ; Thu, 21 Feb 2013 19:09:54 +1100 (EST) X-Greylist: delayed 313 seconds by postgrey-1.34 at bilbo; Thu, 21 Feb 2013 19:09:25 EST Received: from ukgl-edge-p01.kongsberg.com (ukgl-edge-p01.kongsberg.com [193.71.180.98]) by ozlabs.org (Postfix) with ESMTP id AEBD22C007E for ; Thu, 21 Feb 2013 19:09:25 +1100 (EST) Received: from KDA-MAILGW-02.mail.kongsberg.com (kda-mailgw-02.mail.kongsberg.com [193.71.180.105]) by ukgl-edge-p01.kongsberg.com (8.14.5/8.14.5) with ESMTP id r1L847RZ011173 for ; Thu, 21 Feb 2013 09:04:07 +0100 Received: from UKGTW-EXM-P03.kda.kongsberg.com (unverified [10.50.100.64]) by KDA-MAILGW-02.mail.kongsberg.com (Clearswift SMTPRS 5.5.0) with ESMTP id for ; Thu, 21 Feb 2013 09:03:30 +0100 Received: from UKGAW-EXM-P03.kda.kongsberg.com ([fe80::6d9e:de0f:aafa:cc4]) by UKGTW-EXM-P03.kda.kongsberg.com ([fe80::3c3d:7a3a:5c6:f2de%22]) with mapi id 14.01.0438.000; Thu, 21 Feb 2013 09:04:06 +0100 From: To: Subject: PATCH [1/1] GIANFAR infiinate loop on reception of PAUSE FRAME Thread-Topic: PATCH [1/1] GIANFAR infiinate loop on reception of PAUSE FRAME Thread-Index: Ac4QCH7E6udYj/nKRbG5xK0XTtbcXg== Date: Thu, 21 Feb 2013 08:04:06 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.63.2.241] MIME-Version: 1.0 X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" 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. --- 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(®s->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(®s->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(®s->dmactrl, tempval); do { - ret = spin_event_timeout(((gfar_read(®s->ievent) & - (IEVENT_GRSC | IEVENT_GTSC)) == - (IEVENT_GRSC | IEVENT_GTSC)), 1000000, 0); + + ret = spin_event_timeout(((gfar_read(®s->ievent) & event_value) == event_value), 1000000, 0); + if (!ret && !(gfar_read(®s->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(®s->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); }