diff options
author | Anton Vorontsov <avorontsov@ru.mvista.com> | 2008-12-18 03:23:26 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-12-19 01:48:55 -0500 |
commit | 1762a29ae5ebdd974eb2ba0c36b56ab6f7a9c16d (patch) | |
tree | d78e6058cf6ba22d92ac593c64c0dcfaa9951577 /drivers/net/ucc_geth.c | |
parent | b3431c647662a3647f3500a12ec85d65e3622759 (diff) |
ucc_geth: Fix TX watchdog timeout handling
The timeout handling code is currently broken in several ways:
- It calls stop() (which frees all the memory and IRQ), and then
calls startup() (which won't re-request IRQ, neither it will
re-init the Fast UCC structure).
- It calls these routines from the softirq context, which is wrong,
since stop() calls free_irq() (which might sleep) and startup()
allocates things with GFP_KERNEL.
- It won't soft-reset the PHY. We need the PHY reset for at least
MPC8360E-MDS boards with Marvell 88E1111 PHY, the PHY won't recover
from timeouts w/o the reset.
So the patch fixes these problems by implementing the workqueue for the
timeout handling, and there we fully re-open the device via close() and
open() calls. The close/open paths do the right things, and I can see
that the driver actually survive the timeouts.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ucc_geth.c')
-rw-r--r-- | drivers/net/ucc_geth.c | 37 |
1 files changed, 29 insertions, 8 deletions
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index fa25dc1fcdf1..6ebefe951b95 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c | |||
@@ -3355,13 +3355,17 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) | |||
3355 | return 0; | 3355 | return 0; |
3356 | } | 3356 | } |
3357 | 3357 | ||
3358 | /* ucc_geth_timeout gets called when a packet has not been | 3358 | static int ucc_geth_close(struct net_device *dev); |
3359 | * transmitted after a set amount of time. | 3359 | static int ucc_geth_open(struct net_device *dev); |
3360 | * For now, assume that clearing out all the structures, and | 3360 | |
3361 | * starting over will fix the problem. */ | 3361 | /* Reopen device. This will reset the MAC and PHY. */ |
3362 | static void ucc_geth_timeout(struct net_device *dev) | 3362 | static void ucc_geth_timeout_work(struct work_struct *work) |
3363 | { | 3363 | { |
3364 | struct ucc_geth_private *ugeth = netdev_priv(dev); | 3364 | struct ucc_geth_private *ugeth; |
3365 | struct net_device *dev; | ||
3366 | |||
3367 | ugeth = container_of(work, struct ucc_geth_private, timeout_work); | ||
3368 | dev = ugeth->dev; | ||
3365 | 3369 | ||
3366 | ugeth_vdbg("%s: IN", __func__); | 3370 | ugeth_vdbg("%s: IN", __func__); |
3367 | 3371 | ||
@@ -3370,13 +3374,29 @@ static void ucc_geth_timeout(struct net_device *dev) | |||
3370 | ugeth_dump_regs(ugeth); | 3374 | ugeth_dump_regs(ugeth); |
3371 | 3375 | ||
3372 | if (dev->flags & IFF_UP) { | 3376 | if (dev->flags & IFF_UP) { |
3373 | ucc_geth_stop(ugeth); | 3377 | /* |
3374 | ucc_geth_startup(ugeth); | 3378 | * Must reset MAC *and* PHY. This is done by reopening |
3379 | * the device. | ||
3380 | */ | ||
3381 | ucc_geth_close(dev); | ||
3382 | ucc_geth_open(dev); | ||
3375 | } | 3383 | } |
3376 | 3384 | ||
3377 | netif_tx_schedule_all(dev); | 3385 | netif_tx_schedule_all(dev); |
3378 | } | 3386 | } |
3379 | 3387 | ||
3388 | /* | ||
3389 | * ucc_geth_timeout gets called when a packet has not been | ||
3390 | * transmitted after a set amount of time. | ||
3391 | */ | ||
3392 | static void ucc_geth_timeout(struct net_device *dev) | ||
3393 | { | ||
3394 | struct ucc_geth_private *ugeth = netdev_priv(dev); | ||
3395 | |||
3396 | netif_carrier_off(dev); | ||
3397 | schedule_work(&ugeth->timeout_work); | ||
3398 | } | ||
3399 | |||
3380 | /* This is called by the kernel when a frame is ready for transmission. */ | 3400 | /* This is called by the kernel when a frame is ready for transmission. */ |
3381 | /* It is pointed to by the dev->hard_start_xmit function pointer */ | 3401 | /* It is pointed to by the dev->hard_start_xmit function pointer */ |
3382 | static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) | 3402 | static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) |
@@ -4027,6 +4047,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma | |||
4027 | dev->hard_start_xmit = ucc_geth_start_xmit; | 4047 | dev->hard_start_xmit = ucc_geth_start_xmit; |
4028 | dev->tx_timeout = ucc_geth_timeout; | 4048 | dev->tx_timeout = ucc_geth_timeout; |
4029 | dev->watchdog_timeo = TX_TIMEOUT; | 4049 | dev->watchdog_timeo = TX_TIMEOUT; |
4050 | INIT_WORK(&ugeth->timeout_work, ucc_geth_timeout_work); | ||
4030 | netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, UCC_GETH_DEV_WEIGHT); | 4051 | netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, UCC_GETH_DEV_WEIGHT); |
4031 | #ifdef CONFIG_NET_POLL_CONTROLLER | 4052 | #ifdef CONFIG_NET_POLL_CONTROLLER |
4032 | dev->poll_controller = ucc_netpoll; | 4053 | dev->poll_controller = ucc_netpoll; |