Commit 432c5b3a authored by David S. Miller's avatar David S. Miller

Merge branch 'gianfar'

Claudiu Manoil says:

====================
gianfar: Device reset and reconfig fixes

These patches end up fixing some notable device reset & reconfig
related problems.  One issue is on-the-fly (Rx/Tx on) programming
of interrupt coalescing (IC) registers on the processing path,
against HW recommendation.  This is an old issue that became visible
after BQL introduction, as under certain conditions (low traffic)
one TX interrupt gets lost and BQL fires Tx timeout as a result.
Another notable issue is a race on the Tx path (xmit, clean_tx)
during device reset (i.e. during Tx timeout watchdog firing)
that leads to NULL access.
Fixing the problematic on-thy-fly register writes (i.e. the IC regs)
required the implementation of a MAC soft reset procedure.
The race leading to NULL access was addressed by fixing the
stop_gfar()/startup_gfar() pair (disable/enable napi a.s.o.)
and adding the device state DOWN to sync with the TX path.

v2: Refactored if() clauses from gfar_set_features(), PATCH 2.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 225a9a25 f19015ba
This diff is collapsed.
...@@ -965,7 +965,6 @@ struct rx_q_stats { ...@@ -965,7 +965,6 @@ struct rx_q_stats {
/** /**
* struct gfar_priv_rx_q - per rx queue structure * struct gfar_priv_rx_q - per rx queue structure
* @rxlock: per queue rx spin lock
* @rx_skbuff: skb pointers * @rx_skbuff: skb pointers
* @skb_currx: currently use skb pointer * @skb_currx: currently use skb pointer
* @rx_bd_base: First rx buffer descriptor * @rx_bd_base: First rx buffer descriptor
...@@ -978,8 +977,7 @@ struct rx_q_stats { ...@@ -978,8 +977,7 @@ struct rx_q_stats {
*/ */
struct gfar_priv_rx_q { struct gfar_priv_rx_q {
spinlock_t rxlock __attribute__ ((aligned (SMP_CACHE_BYTES))); struct sk_buff **rx_skbuff __aligned(SMP_CACHE_BYTES);
struct sk_buff ** rx_skbuff;
dma_addr_t rx_bd_dma_base; dma_addr_t rx_bd_dma_base;
struct rxbd8 *rx_bd_base; struct rxbd8 *rx_bd_base;
struct rxbd8 *cur_rx; struct rxbd8 *cur_rx;
...@@ -1040,6 +1038,11 @@ enum gfar_errata { ...@@ -1040,6 +1038,11 @@ enum gfar_errata {
GFAR_ERRATA_12 = 0x08, /* a.k.a errata eTSEC49 */ GFAR_ERRATA_12 = 0x08, /* a.k.a errata eTSEC49 */
}; };
enum gfar_dev_state {
GFAR_DOWN = 1,
GFAR_RESETTING
};
/* Struct stolen almost completely (and shamelessly) from the FCC enet source /* Struct stolen almost completely (and shamelessly) from the FCC enet source
* (Ok, that's not so true anymore, but there is a family resemblance) * (Ok, that's not so true anymore, but there is a family resemblance)
* The GFAR buffer descriptors track the ring buffers. The rx_bd_base * The GFAR buffer descriptors track the ring buffers. The rx_bd_base
...@@ -1068,6 +1071,7 @@ struct gfar_private { ...@@ -1068,6 +1071,7 @@ struct gfar_private {
struct gfar_priv_rx_q *rx_queue[MAX_RX_QS]; struct gfar_priv_rx_q *rx_queue[MAX_RX_QS];
struct gfar_priv_grp gfargrp[MAXGROUPS]; struct gfar_priv_grp gfargrp[MAXGROUPS];
unsigned long state;
u32 device_flags; u32 device_flags;
unsigned int mode; unsigned int mode;
...@@ -1198,21 +1202,17 @@ static inline void gfar_write_isrg(struct gfar_private *priv) ...@@ -1198,21 +1202,17 @@ static inline void gfar_write_isrg(struct gfar_private *priv)
} }
} }
void lock_rx_qs(struct gfar_private *priv);
void lock_tx_qs(struct gfar_private *priv);
void unlock_rx_qs(struct gfar_private *priv);
void unlock_tx_qs(struct gfar_private *priv);
irqreturn_t gfar_receive(int irq, void *dev_id); irqreturn_t gfar_receive(int irq, void *dev_id);
int startup_gfar(struct net_device *dev); int startup_gfar(struct net_device *dev);
void stop_gfar(struct net_device *dev); void stop_gfar(struct net_device *dev);
void reset_gfar(struct net_device *dev);
void gfar_mac_reset(struct gfar_private *priv);
void gfar_halt(struct gfar_private *priv); void gfar_halt(struct gfar_private *priv);
void gfar_start(struct gfar_private *priv); void gfar_start(struct gfar_private *priv);
void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable, void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
u32 regnum, u32 read); u32 regnum, u32 read);
void gfar_configure_coalescing_all(struct gfar_private *priv); void gfar_configure_coalescing_all(struct gfar_private *priv);
int gfar_set_features(struct net_device *dev, netdev_features_t features); int gfar_set_features(struct net_device *dev, netdev_features_t features);
void gfar_check_rx_parser_mode(struct gfar_private *priv);
void gfar_vlan_mode(struct net_device *dev, netdev_features_t features);
extern const struct ethtool_ops gfar_ethtool_ops; extern const struct ethtool_ops gfar_ethtool_ops;
......
...@@ -360,25 +360,11 @@ static int gfar_scoalesce(struct net_device *dev, ...@@ -360,25 +360,11 @@ static int gfar_scoalesce(struct net_device *dev,
struct ethtool_coalesce *cvals) struct ethtool_coalesce *cvals)
{ {
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
int i = 0; int i, err = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP; 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) if (NULL == priv->phydev)
return -ENODEV; return -ENODEV;
...@@ -395,6 +381,32 @@ static int gfar_scoalesce(struct net_device *dev, ...@@ -395,6 +381,32 @@ static int gfar_scoalesce(struct net_device *dev,
return -EINVAL; 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++) { for (i = 0; i < priv->num_rx_queues; i++) {
priv->rx_queue[i]->rxic = mk_ic_value( priv->rx_queue[i]->rxic = mk_ic_value(
cvals->rx_max_coalesced_frames, cvals->rx_max_coalesced_frames,
...@@ -411,28 +423,22 @@ static int gfar_scoalesce(struct net_device *dev, ...@@ -411,28 +423,22 @@ static int gfar_scoalesce(struct net_device *dev,
priv->tx_queue[i]->txcoalescing = 1; 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++) { for (i = 0; i < priv->num_tx_queues; i++) {
priv->tx_queue[i]->txic = mk_ic_value( priv->tx_queue[i]->txic = mk_ic_value(
cvals->tx_max_coalesced_frames, cvals->tx_max_coalesced_frames,
gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs)); 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, /* Fills in rvals with the current ring parameters. Currently,
...@@ -487,6 +493,9 @@ static int gfar_sringparam(struct net_device *dev, ...@@ -487,6 +493,9 @@ static int gfar_sringparam(struct net_device *dev,
return -EINVAL; return -EINVAL;
} }
while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state))
cpu_relax();
if (dev->flags & IFF_UP) if (dev->flags & IFF_UP)
stop_gfar(dev); stop_gfar(dev);
...@@ -498,10 +507,11 @@ static int gfar_sringparam(struct net_device *dev, ...@@ -498,10 +507,11 @@ static int gfar_sringparam(struct net_device *dev,
priv->tx_queue[i]->tx_ring_size = rvals->tx_pending; priv->tx_queue[i]->tx_ring_size = rvals->tx_pending;
/* Rebuild the rings with the new size */ /* Rebuild the rings with the new size */
if (dev->flags & IFF_UP) { if (dev->flags & IFF_UP)
err = startup_gfar(dev); err = startup_gfar(dev);
netif_tx_wake_all_queues(dev);
} clear_bit_unlock(GFAR_RESETTING, &priv->state);
return err; return err;
} }
...@@ -580,23 +590,28 @@ static int gfar_spauseparam(struct net_device *dev, ...@@ -580,23 +590,28 @@ static int gfar_spauseparam(struct net_device *dev,
int gfar_set_features(struct net_device *dev, netdev_features_t features) int gfar_set_features(struct net_device *dev, netdev_features_t features)
{ {
netdev_features_t changed = dev->features ^ features; netdev_features_t changed = dev->features ^ features;
struct gfar_private *priv = netdev_priv(dev);
int err = 0; int err = 0;
if (changed & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX)) if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
gfar_vlan_mode(dev, features); NETIF_F_RXCSUM)))
if (!(changed & NETIF_F_RXCSUM))
return 0; return 0;
while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state))
cpu_relax();
dev->features = features;
if (dev->flags & IFF_UP) { if (dev->flags & IFF_UP) {
/* Now we take down the rings to rebuild them */ /* Now we take down the rings to rebuild them */
stop_gfar(dev); stop_gfar(dev);
dev->features = features;
err = startup_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; return err;
} }
...@@ -1562,9 +1577,6 @@ static int gfar_write_filer_table(struct gfar_private *priv, ...@@ -1562,9 +1577,6 @@ static int gfar_write_filer_table(struct gfar_private *priv,
if (tab->index > MAX_FILER_IDX - 1) if (tab->index > MAX_FILER_IDX - 1)
return -EBUSY; return -EBUSY;
/* Avoid inconsistent filer table to be processed */
lock_rx_qs(priv);
/* Fill regular entries */ /* Fill regular entries */
for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl); for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl);
i++) i++)
...@@ -1577,8 +1589,6 @@ static int gfar_write_filer_table(struct gfar_private *priv, ...@@ -1577,8 +1589,6 @@ static int gfar_write_filer_table(struct gfar_private *priv,
*/ */
gfar_write_filer(priv, i, 0x20, 0x0); gfar_write_filer(priv, i, 0x20, 0x0);
unlock_rx_qs(priv);
return 0; return 0;
} }
...@@ -1783,6 +1793,9 @@ static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) ...@@ -1783,6 +1793,9 @@ static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
int ret = 0; int ret = 0;
if (test_bit(GFAR_RESETTING, &priv->state))
return -EBUSY;
mutex_lock(&priv->rx_queue_access); mutex_lock(&priv->rx_queue_access);
switch (cmd->cmd) { switch (cmd->cmd) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment