diff options
author | Joakim Tjernlund <joakim.tjernlund@transmode.se> | 2010-11-11 22:55:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-11-12 15:21:18 -0500 |
commit | 75e6047431872dadd0b13503b374e48ccd71a507 (patch) | |
tree | 89e698b4a01ba2debe2b16334f3e0b8baf6ccf54 /drivers/net/ucc_geth.c | |
parent | 2040bd57b5f0c52dc4503e0e960b1dcafaf30a8b (diff) |
ucc_geth: Fix deadlock
This script:
while [ 1==1 ] ; do ifconfig eth0 up; usleep 1950000 ;ifconfig eth0 down; dmesg -c ;done
causes in just a second or two:
INFO: task ifconfig:572 blocked for more than 120 seconds.
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
ifconfig D 0ff65760 0 572 369 0x00000000
Call Trace:
[c6157be0] [c6008460] 0xc6008460 (unreliable)
[c6157ca0] [c0008608] __switch_to+0x4c/0x6c
[c6157cb0] [c028fecc] schedule+0x184/0x310
[c6157ce0] [c0290e54] __mutex_lock_slowpath+0xa4/0x150
[c6157d20] [c0290c48] mutex_lock+0x44/0x48
[c6157d30] [c01aba74] phy_stop+0x20/0x70
[c6157d40] [c01aef40] ucc_geth_stop+0x30/0x98
[c6157d60] [c01b18fc] ucc_geth_close+0x9c/0xdc
[c6157d80] [c01db0cc] __dev_close+0xa0/0xd0
[c6157d90] [c01deddc] __dev_change_flags+0x8c/0x148
[c6157db0] [c01def54] dev_change_flags+0x1c/0x64
[c6157dd0] [c0237ac8] devinet_ioctl+0x678/0x784
[c6157e50] [c0239a58] inet_ioctl+0xb0/0xbc
[c6157e60] [c01cafa8] sock_ioctl+0x174/0x2a0
[c6157e80] [c009a16c] vfs_ioctl+0xcc/0xe0
[c6157ea0] [c009a998] do_vfs_ioctl+0xc4/0x79c
[c6157f10] [c009b0b0] sys_ioctl+0x40/0x74
[c6157f40] [c00117c4] ret_from_syscall+0x0/0x38
The reason appears to be ucc_geth_stop meets adjust_link as the
PHY reports PHY changes. I belive adjust_link hangs somewhere,
holding the PHY lock, because ucc_geth_stop disabled the
controller HW.
Fix is to stop the PHY before disabling the controller.
Signed-off-by: Joakim Tjernlund <Joakim.Tjernlund@transmode.se>
Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.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 | 10 |
1 files changed, 7 insertions, 3 deletions
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index cf6653f71d23..acbdab3d66ca 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c | |||
@@ -2050,12 +2050,16 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth) | |||
2050 | 2050 | ||
2051 | ugeth_vdbg("%s: IN", __func__); | 2051 | ugeth_vdbg("%s: IN", __func__); |
2052 | 2052 | ||
2053 | /* | ||
2054 | * Tell the kernel the link is down. | ||
2055 | * Must be done before disabling the controller | ||
2056 | * or deadlock may happen. | ||
2057 | */ | ||
2058 | phy_stop(phydev); | ||
2059 | |||
2053 | /* Disable the controller */ | 2060 | /* Disable the controller */ |
2054 | ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); | 2061 | ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); |
2055 | 2062 | ||
2056 | /* Tell the kernel the link is down */ | ||
2057 | phy_stop(phydev); | ||
2058 | |||
2059 | /* Mask all interrupts */ | 2063 | /* Mask all interrupts */ |
2060 | out_be32(ugeth->uccf->p_uccm, 0x00000000); | 2064 | out_be32(ugeth->uccf->p_uccm, 0x00000000); |
2061 | 2065 | ||