From c10650b661b6c43c82c8e91b1d0b9b6adcf7f7dc Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 17 Feb 2014 12:53:18 +0200 Subject: gianfar: Add missing graceful reset steps and fixes gfar_halt() and gfar_start() are responsible for stopping and starting the DMA and the Rx/Tx hw rings. They implement the support for the "graceful Rx/Tx stop/start" hw procedure, and also disable/enable eTSEC's hw interrupts in the process. The GRS/GTS procedure requires however to have the RQUEUE/TQUEUE registers cleared first and to wait for a period of time for the current frame to pass through the interface (around ~10ms for a jumbo frame). Only then may the GTS and GRS bits from DMACTRL be set to shut down the DMA, and finally the Tx_EN and Rx_EN bits in MACCFG1 may be cleared to disable the Tx/Rx blocks. The same register programming order applies to start the Rx/Tx: enabling the RQUEUE/TQUEUE *before* clearing the GRS/GTS bits. This is a HW recommendation in order to avoid a possible controller "lock up" during graceful reset. Cleanup the gfar_halt()/start() prototypes, to take priv instead of ndev as their purpose is to operate on HW. Enabling the RQUEUE/TQUEUE in the hw_init() is not needed anymore since that's the job of gfar_start(). Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ethtool.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net/ethernet/freescale/gianfar_ethtool.c') diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 63d234419cc1..69fab72b8a8d 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -44,7 +44,6 @@ #include "gianfar.h" -extern void gfar_start(struct net_device *dev); extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit); @@ -504,7 +503,7 @@ static int gfar_sringparam(struct net_device *dev, lock_tx_qs(priv); lock_rx_qs(priv); - gfar_halt(dev); + gfar_halt(priv); unlock_rx_qs(priv); unlock_tx_qs(priv); @@ -627,7 +626,7 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features) lock_tx_qs(priv); lock_rx_qs(priv); - gfar_halt(dev); + gfar_halt(priv); unlock_tx_qs(priv); unlock_rx_qs(priv); -- cgit v1.2.3 From 7cca336ae1b27909526987e076388a388f668fe0 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 17 Feb 2014 12:53:19 +0200 Subject: gianfar: Remove clean_rx_ring race from gfar_ethtool gfar_clean_rx_ring() was designed to be called from napi (rx softirq) context to do the Rx processing. Calling it from a process context like this is a bug as it will clearly race with the napi Rx processing. There's also no point in initializing num_txbdfree since startup_gfar() already does that, when bringing the device up again (after reset). Changing num_txbdfree "on-the-fly" like this is also subject to race conditions. num_txbdfree is handled by the Tx processing path and the device reset procedure. Also, don't assume that num_rx_queues is always equal to num_tx_queues. Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ethtool.c | 63 +++--------------------- 1 file changed, 8 insertions(+), 55 deletions(-) (limited to 'drivers/net/ethernet/freescale/gianfar_ethtool.c') diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 69fab72b8a8d..19557ec31f33 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -44,9 +44,6 @@ #include "gianfar.h" -extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, - int rx_work_limit); - #define GFAR_MAX_COAL_USECS 0xffff #define GFAR_MAX_COAL_FRAMES 0xff static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, @@ -466,15 +463,13 @@ static void gfar_gringparam(struct net_device *dev, } /* Change the current ring parameters, stopping the controller if - * necessary so that we don't mess things up while we're in - * motion. We wait for the ring to be clean before reallocating - * the rings. + * necessary so that we don't mess things up while we're in motion. */ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) { struct gfar_private *priv = netdev_priv(dev); - int err = 0, i = 0; + int err = 0, i; if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE) return -EINVAL; @@ -492,38 +487,15 @@ static int gfar_sringparam(struct net_device *dev, return -EINVAL; } - - if (dev->flags & IFF_UP) { - unsigned long flags; - - /* Halt TX and RX, and process the frames which - * have already been received - */ - local_irq_save(flags); - lock_tx_qs(priv); - lock_rx_qs(priv); - - gfar_halt(priv); - - unlock_rx_qs(priv); - unlock_tx_qs(priv); - local_irq_restore(flags); - - for (i = 0; i < priv->num_rx_queues; i++) - gfar_clean_rx_ring(priv->rx_queue[i], - priv->rx_queue[i]->rx_ring_size); - - /* Now we take down the rings to rebuild them */ + if (dev->flags & IFF_UP) stop_gfar(dev); - } - /* Change the size */ - for (i = 0; i < priv->num_rx_queues; i++) { + /* Change the sizes */ + for (i = 0; i < priv->num_rx_queues; i++) priv->rx_queue[i]->rx_ring_size = rvals->rx_pending; + + for (i = 0; i < priv->num_tx_queues; i++) priv->tx_queue[i]->tx_ring_size = rvals->tx_pending; - priv->tx_queue[i]->num_txbdfree = - priv->tx_queue[i]->tx_ring_size; - } /* Rebuild the rings with the new size */ if (dev->flags & IFF_UP) { @@ -607,10 +579,8 @@ static int gfar_spauseparam(struct net_device *dev, int gfar_set_features(struct net_device *dev, netdev_features_t features) { - struct gfar_private *priv = netdev_priv(dev); - unsigned long flags; - int err = 0, i = 0; netdev_features_t changed = dev->features ^ features; + int err = 0; if (changed & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX)) gfar_vlan_mode(dev, features); @@ -619,23 +589,6 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features) return 0; if (dev->flags & IFF_UP) { - /* Halt TX and RX, and process the frames which - * have already been received - */ - local_irq_save(flags); - lock_tx_qs(priv); - lock_rx_qs(priv); - - gfar_halt(priv); - - unlock_tx_qs(priv); - unlock_rx_qs(priv); - local_irq_restore(flags); - - for (i = 0; i < priv->num_rx_queues; i++) - gfar_clean_rx_ring(priv->rx_queue[i], - priv->rx_queue[i]->rx_ring_size); - /* Now we take down the rings to rebuild them */ stop_gfar(dev); -- cgit v1.2.3 From 88302648be8c45af09694363be1b165680489137 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 24 Feb 2014 12:13:43 +0200 Subject: gianfar: Fix on-the-fly vlan and mtu updates The RCTRL and TCTRL registers should not be changed on-the-fly, while the controller is running, otherwise unexpected behaviour occurs. But that's exactly what gfar_vlan_mode() does, updating the VLAN acceleration bits inside RCTRL/TCTRL. The attempt to lock these operations doesn't help, but only adds to the confusion. There's also a dependency for Rx FCB insertion (activating /de-activating the TOE offload block on Rx) which might change the required rx buffer size. This makes matters worse as gfar_vlan_mode() ends up calling gfar_change_mtu(), though the MTU size remains the same. Note that there are other situations that may affect the required rx buffer size, like changing RXCSUM or rx hw timestamping, but errorneously the rx buffer size is not recomputed/ updated in the process. To fix this, do the vlan updates properly inside the MAC reset and reconfiguration procedure, which takes care of the rx buffer size dependecy and the rx TOE block (PRSDEP) activation/deactivation as well (in the correct order). As a consequence, MTU/ rx buff size updates are done now by the same MAC reset and reconfig procedure, so that out of context updates to MAXFRM, MRBLR, and MACCFG inside change_mtu() are no longer needed. The rx buffer size dependecy to Rx FCB is now handled for the other cases too (RXCSUM and rx hw timestamping). Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ethtool.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/net/ethernet/freescale/gianfar_ethtool.c') diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 19557ec31f33..dd7ccec506f1 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -582,18 +582,15 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features) netdev_features_t changed = dev->features ^ features; int err = 0; - if (changed & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX)) - gfar_vlan_mode(dev, features); - - if (!(changed & NETIF_F_RXCSUM)) + if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_RXCSUM))) return 0; + dev->features = features; + if (dev->flags & IFF_UP) { /* Now we take down the rings to rebuild them */ stop_gfar(dev); - - dev->features = features; - err = startup_gfar(dev); netif_tx_wake_all_queues(dev); } -- cgit v1.2.3 From 0851133bb5ad9d95fceccac9fc67b798041b73e2 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 24 Feb 2014 12:13:45 +0200 Subject: gianfar: Fix device reset races (oops) for Tx The device reset procedure, stop_gfar()/startup_gfar(), has concurrency issues. "Kernel access of bad area" oopses show up during Tx timeout device reset or other reset cases (like changing MTU) that happen while the interface still has traffic. The oopses happen in start_xmit and clean_tx_ring when accessing tx_queue-> tx_skbuff which is NULL. The race comes from de-allocating the tx_skbuff while transmission and napi processing are still active. Though the Tx queues get temoprarily stopped when Tx timeout occurs, they get re-enabled as a result of Tx congestion handling inside the napi context (see clean_tx_ring()). Not disabling the napi during reset is also a bug, because clean_tx_ring() will try to access tx_skbuff while it is being de-alloc'ed and re-alloc'ed. To fix this, stop_gfar() needs to disable napi processing after stopping the Tx queues. However, in order to prevent clean_tx_ring() to re-enable the Tx queue before the napi gets disabled, the device state DOWN has been introduced. It prevents the Tx congestion management from re-enabling the de-congested Tx queue while the device is brought down. An additional locking state, RESETTING, has been introduced to prevent simultaneous resets or to prevent configuring the device while it is resetting. The bogus 'rxlock's (for each Rx queue) have been removed since their purpose is not justified, as they don't prevent nor are suited to prevent device reset/reconfig races (such as this one). Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ethtool.c | 28 ++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers/net/ethernet/freescale/gianfar_ethtool.c') diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index dd7ccec506f1..45219d4d09b4 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -487,6 +487,9 @@ static int gfar_sringparam(struct net_device *dev, return -EINVAL; } + while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) + cpu_relax(); + if (dev->flags & IFF_UP) stop_gfar(dev); @@ -498,10 +501,11 @@ static int gfar_sringparam(struct net_device *dev, priv->tx_queue[i]->tx_ring_size = rvals->tx_pending; /* Rebuild the rings with the new size */ - if (dev->flags & IFF_UP) { + if (dev->flags & IFF_UP) err = startup_gfar(dev); - netif_tx_wake_all_queues(dev); - } + + clear_bit_unlock(GFAR_RESETTING, &priv->state); + return err; } @@ -580,20 +584,28 @@ static int gfar_spauseparam(struct net_device *dev, int gfar_set_features(struct net_device *dev, netdev_features_t features) { netdev_features_t changed = dev->features ^ features; + struct gfar_private *priv = netdev_priv(dev); int err = 0; if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM))) return 0; + while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) + cpu_relax(); + dev->features = features; if (dev->flags & IFF_UP) { /* Now we take down the rings to rebuild them */ stop_gfar(dev); err = startup_gfar(dev); - netif_tx_wake_all_queues(dev); + } else { + gfar_mac_reset(priv); } + + clear_bit_unlock(GFAR_RESETTING, &priv->state); + return err; } @@ -1559,9 +1571,6 @@ static int gfar_write_filer_table(struct gfar_private *priv, if (tab->index > MAX_FILER_IDX - 1) return -EBUSY; - /* Avoid inconsistent filer table to be processed */ - lock_rx_qs(priv); - /* Fill regular entries */ for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl); i++) @@ -1574,8 +1583,6 @@ static int gfar_write_filer_table(struct gfar_private *priv, */ gfar_write_filer(priv, i, 0x20, 0x0); - unlock_rx_qs(priv); - return 0; } @@ -1780,6 +1787,9 @@ static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) struct gfar_private *priv = netdev_priv(dev); int ret = 0; + if (test_bit(GFAR_RESETTING, &priv->state)) + return -EBUSY; + mutex_lock(&priv->rx_queue_access); switch (cmd->cmd) { -- cgit v1.2.3 From f19015baa23b9130acbf290e1d65c70193e34ff1 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 24 Feb 2014 12:13:46 +0200 Subject: gianfar: Fix Tx int miss, dont write IC on-the-fly Programming the interrupt coalescing (IC) registers while the controller/DMA is on may incur the loss of one Tx confirmation interrupt, under certain conditions. This is a subtle hw race because it does not occur during a burst of Tx packets. It has been observed on p2020 devices that, if just one packet is being xmit'ed, the Tx confirmation doesn't trigger and BQL evetually blocks the Tx queues, followed by Tx timeout and an un-responsive device. This issue was not apparent prior to introducing BQL support, as a late Tx confirmation was not an issue back then and the next burst of Tx frames would have triggered the Tx confirmation/ Tx ring cleanup anyway. Bottom line, the hw specifications state that the IC registers should not be programmed while the Rx/Tx blocks (the DMA) are enabled. Further more, these registers are currently re-written with the same values on the processing path, over and over again. To fix this, rewriting the IC registers has been removed from the processing path (napi poll). A complete MAC reset procedure has been implemented for the ethtool -c option instead, to reliably update these registers while the controller is stopped. Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ethtool.c | 66 +++++++++++++----------- 1 file changed, 36 insertions(+), 30 deletions(-) (limited to 'drivers/net/ethernet/freescale/gianfar_ethtool.c') diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 45219d4d09b4..891dbee6e6c1 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -360,25 +360,11 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); - int i = 0; + int i, err = 0; if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) return -EOPNOTSUPP; - /* Set up rx coalescing */ - /* As of now, we will enable/disable coalescing for all - * queues together in case of eTSEC2, this will be modified - * along with the ethtool interface - */ - if ((cvals->rx_coalesce_usecs == 0) || - (cvals->rx_max_coalesced_frames == 0)) { - for (i = 0; i < priv->num_rx_queues; i++) - priv->rx_queue[i]->rxcoalescing = 0; - } else { - for (i = 0; i < priv->num_rx_queues; i++) - priv->rx_queue[i]->rxcoalescing = 1; - } - if (NULL == priv->phydev) return -ENODEV; @@ -395,6 +381,32 @@ static int gfar_scoalesce(struct net_device *dev, return -EINVAL; } + /* Check the bounds of the values */ + if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) { + netdev_info(dev, "Coalescing is limited to %d microseconds\n", + GFAR_MAX_COAL_USECS); + return -EINVAL; + } + + if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) { + netdev_info(dev, "Coalescing is limited to %d frames\n", + GFAR_MAX_COAL_FRAMES); + return -EINVAL; + } + + while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) + cpu_relax(); + + /* Set up rx coalescing */ + if ((cvals->rx_coalesce_usecs == 0) || + (cvals->rx_max_coalesced_frames == 0)) { + for (i = 0; i < priv->num_rx_queues; i++) + priv->rx_queue[i]->rxcoalescing = 0; + } else { + for (i = 0; i < priv->num_rx_queues; i++) + priv->rx_queue[i]->rxcoalescing = 1; + } + for (i = 0; i < priv->num_rx_queues; i++) { priv->rx_queue[i]->rxic = mk_ic_value( cvals->rx_max_coalesced_frames, @@ -411,28 +423,22 @@ static int gfar_scoalesce(struct net_device *dev, priv->tx_queue[i]->txcoalescing = 1; } - /* Check the bounds of the values */ - if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) { - netdev_info(dev, "Coalescing is limited to %d microseconds\n", - GFAR_MAX_COAL_USECS); - return -EINVAL; - } - - if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) { - netdev_info(dev, "Coalescing is limited to %d frames\n", - GFAR_MAX_COAL_FRAMES); - return -EINVAL; - } - for (i = 0; i < priv->num_tx_queues; i++) { priv->tx_queue[i]->txic = mk_ic_value( cvals->tx_max_coalesced_frames, gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs)); } - gfar_configure_coalescing_all(priv); + if (dev->flags & IFF_UP) { + stop_gfar(dev); + err = startup_gfar(dev); + } else { + gfar_mac_reset(priv); + } - return 0; + clear_bit_unlock(GFAR_RESETTING, &priv->state); + + return err; } /* Fills in rvals with the current ring parameters. Currently, -- cgit v1.2.3