Commit 8169bd91 authored by Eugene Surovegin's avatar Eugene Surovegin Committed by Jeff Garzik

[PATCH] ibm_emac: fix graceful stop timeout handling

This patch fixes graceful stop timeout handling in PPC4xx EMAC driver.

Currently, when we stop TX/RX channels we just do some number of loops
without relying on actual spent time. This has finally bitten me on
one of our systems (heavy network traffic during start up, RX channel
is stopped several times to configure multicast list).

Graceful channel stop can take up to 1 frame time, so I've added
device specific timeout counter which depends on current link speed
and calls to udelay() to really wait required amount of time before
giving up.
Signed-off-by: default avatarEugene Surovegin <ebs@ebshome.net>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent be0df20c
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
*/ */
#define DRV_NAME "emac" #define DRV_NAME "emac"
#define DRV_VERSION "3.53" #define DRV_VERSION "3.54"
#define DRV_DESC "PPC 4xx OCP EMAC driver" #define DRV_DESC "PPC 4xx OCP EMAC driver"
MODULE_DESCRIPTION(DRV_DESC); MODULE_DESCRIPTION(DRV_DESC);
...@@ -158,6 +158,14 @@ static inline void emac_report_timeout_error(struct ocp_enet_private *dev, ...@@ -158,6 +158,14 @@ static inline void emac_report_timeout_error(struct ocp_enet_private *dev,
#define PHY_POLL_LINK_ON HZ #define PHY_POLL_LINK_ON HZ
#define PHY_POLL_LINK_OFF (HZ / 5) #define PHY_POLL_LINK_OFF (HZ / 5)
/* Graceful stop timeouts in us.
* We should allow up to 1 frame time (full-duplex, ignoring collisions)
*/
#define STOP_TIMEOUT_10 1230
#define STOP_TIMEOUT_100 124
#define STOP_TIMEOUT_1000 13
#define STOP_TIMEOUT_1000_JUMBO 73
/* Please, keep in sync with struct ibm_emac_stats/ibm_emac_error_stats */ /* Please, keep in sync with struct ibm_emac_stats/ibm_emac_error_stats */
static const char emac_stats_keys[EMAC_ETHTOOL_STATS_COUNT][ETH_GSTRING_LEN] = { static const char emac_stats_keys[EMAC_ETHTOOL_STATS_COUNT][ETH_GSTRING_LEN] = {
"rx_packets", "rx_bytes", "tx_packets", "tx_bytes", "rx_packets_csum", "rx_packets", "rx_bytes", "tx_packets", "tx_bytes", "rx_packets_csum",
...@@ -222,10 +230,12 @@ static void emac_tx_disable(struct ocp_enet_private *dev) ...@@ -222,10 +230,12 @@ static void emac_tx_disable(struct ocp_enet_private *dev)
r = in_be32(&p->mr0); r = in_be32(&p->mr0);
if (r & EMAC_MR0_TXE) { if (r & EMAC_MR0_TXE) {
int n = 300; int n = dev->stop_timeout;
out_be32(&p->mr0, r & ~EMAC_MR0_TXE); out_be32(&p->mr0, r & ~EMAC_MR0_TXE);
while (!(in_be32(&p->mr0) & EMAC_MR0_TXI) && n) while (!(in_be32(&p->mr0) & EMAC_MR0_TXI) && n) {
udelay(1);
--n; --n;
}
if (unlikely(!n)) if (unlikely(!n))
emac_report_timeout_error(dev, "TX disable timeout"); emac_report_timeout_error(dev, "TX disable timeout");
} }
...@@ -248,9 +258,11 @@ static void emac_rx_enable(struct ocp_enet_private *dev) ...@@ -248,9 +258,11 @@ static void emac_rx_enable(struct ocp_enet_private *dev)
if (!(r & EMAC_MR0_RXE)) { if (!(r & EMAC_MR0_RXE)) {
if (unlikely(!(r & EMAC_MR0_RXI))) { if (unlikely(!(r & EMAC_MR0_RXI))) {
/* Wait if previous async disable is still in progress */ /* Wait if previous async disable is still in progress */
int n = 100; int n = dev->stop_timeout;
while (!(r = in_be32(&p->mr0) & EMAC_MR0_RXI) && n) while (!(r = in_be32(&p->mr0) & EMAC_MR0_RXI) && n) {
udelay(1);
--n; --n;
}
if (unlikely(!n)) if (unlikely(!n))
emac_report_timeout_error(dev, emac_report_timeout_error(dev,
"RX disable timeout"); "RX disable timeout");
...@@ -273,10 +285,12 @@ static void emac_rx_disable(struct ocp_enet_private *dev) ...@@ -273,10 +285,12 @@ static void emac_rx_disable(struct ocp_enet_private *dev)
r = in_be32(&p->mr0); r = in_be32(&p->mr0);
if (r & EMAC_MR0_RXE) { if (r & EMAC_MR0_RXE) {
int n = 300; int n = dev->stop_timeout;
out_be32(&p->mr0, r & ~EMAC_MR0_RXE); out_be32(&p->mr0, r & ~EMAC_MR0_RXE);
while (!(in_be32(&p->mr0) & EMAC_MR0_RXI) && n) while (!(in_be32(&p->mr0) & EMAC_MR0_RXI) && n) {
udelay(1);
--n; --n;
}
if (unlikely(!n)) if (unlikely(!n))
emac_report_timeout_error(dev, "RX disable timeout"); emac_report_timeout_error(dev, "RX disable timeout");
} }
...@@ -395,6 +409,7 @@ static int emac_configure(struct ocp_enet_private *dev) ...@@ -395,6 +409,7 @@ static int emac_configure(struct ocp_enet_private *dev)
r = EMAC_MR1_BASE(emac_opb_mhz()) | EMAC_MR1_VLE | EMAC_MR1_IST; r = EMAC_MR1_BASE(emac_opb_mhz()) | EMAC_MR1_VLE | EMAC_MR1_IST;
if (dev->phy.duplex == DUPLEX_FULL) if (dev->phy.duplex == DUPLEX_FULL)
r |= EMAC_MR1_FDE; r |= EMAC_MR1_FDE;
dev->stop_timeout = STOP_TIMEOUT_10;
switch (dev->phy.speed) { switch (dev->phy.speed) {
case SPEED_1000: case SPEED_1000:
if (emac_phy_gpcs(dev->phy.mode)) { if (emac_phy_gpcs(dev->phy.mode)) {
...@@ -409,12 +424,16 @@ static int emac_configure(struct ocp_enet_private *dev) ...@@ -409,12 +424,16 @@ static int emac_configure(struct ocp_enet_private *dev)
r |= EMAC_MR1_MF_1000; r |= EMAC_MR1_MF_1000;
r |= EMAC_MR1_RFS_16K; r |= EMAC_MR1_RFS_16K;
gige = 1; gige = 1;
if (dev->ndev->mtu > ETH_DATA_LEN) if (dev->ndev->mtu > ETH_DATA_LEN) {
r |= EMAC_MR1_JPSM; r |= EMAC_MR1_JPSM;
dev->stop_timeout = STOP_TIMEOUT_1000_JUMBO;
} else
dev->stop_timeout = STOP_TIMEOUT_1000;
break; break;
case SPEED_100: case SPEED_100:
r |= EMAC_MR1_MF_100; r |= EMAC_MR1_MF_100;
dev->stop_timeout = STOP_TIMEOUT_100;
/* Fall through */ /* Fall through */
default: default:
r |= EMAC_MR1_RFS_4K; r |= EMAC_MR1_RFS_4K;
...@@ -2048,6 +2067,7 @@ static int __init emac_probe(struct ocp_device *ocpdev) ...@@ -2048,6 +2067,7 @@ static int __init emac_probe(struct ocp_device *ocpdev)
dev->phy.duplex = DUPLEX_FULL; dev->phy.duplex = DUPLEX_FULL;
dev->phy.autoneg = AUTONEG_DISABLE; dev->phy.autoneg = AUTONEG_DISABLE;
dev->phy.pause = dev->phy.asym_pause = 0; dev->phy.pause = dev->phy.asym_pause = 0;
dev->stop_timeout = STOP_TIMEOUT_100;
init_timer(&dev->link_timer); init_timer(&dev->link_timer);
dev->link_timer.function = emac_link_timer; dev->link_timer.function = emac_link_timer;
dev->link_timer.data = (unsigned long)dev; dev->link_timer.data = (unsigned long)dev;
......
...@@ -189,6 +189,8 @@ struct ocp_enet_private { ...@@ -189,6 +189,8 @@ struct ocp_enet_private {
struct timer_list link_timer; struct timer_list link_timer;
int reset_failed; int reset_failed;
int stop_timeout; /* in us */
struct ibm_emac_error_stats estats; struct ibm_emac_error_stats estats;
struct net_device_stats nstats; struct net_device_stats nstats;
......
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