aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet
diff options
context:
space:
mode:
authorAndreas Herrmann <andreas.herrmann@calxeda.com>2013-11-07 06:07:56 -0500
committerDavid S. Miller <davem@davemloft.net>2013-11-07 19:25:53 -0500
commitb5ad795e52dae6e9f88b193a5e779b70005d005c (patch)
tree520907ecada6e8f2d3ed49e08d7483b98a6115eb /drivers/net/ethernet
parente21dd863acec8d3bc5166fb2a0c680a9982b37db (diff)
net: calxedaxgmac: Fix panic caused by MTU change of active interface
Changing MTU size of an xgmac network interface while it is active can cause a panic like skbuff: skb_over_panic: text:c03bc62c len:1090 put:1090 head:edfb6900 data:edfb6942 tail:0xedfb6d84 end:0xedfb6bc0 dev:eth0 ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:126! Internal error: Oops - BUG: 0 [#1] SMP ARM Modules linked in: CPU: 0 PID: 762 Comm: python Tainted: G W 3.10.0-00015-g3e33cd7 #309 task: edcfe000 ti: ed67e000 task.ti: ed67e000 PC is at skb_panic+0x64/0x70 LR is at wake_up_klogd+0x5c/0x68 This happens because xgmac_change_mtu modifies dev->mtu before the network interface is quiesced. And thus there still might be buffers in use which have a buffer size based on the old MTU. To fix this I moved the change of dev->mtu after the call to xgmac_stop. Another modification is required (in xgmac_stop) to ensure that xgmac_xmit is really not called anymore (xgmac_tx_complete might wake up the queue again). I've tested the fix by switching MTU size every second between 600 and 1500 while network traffic was going on. The test box survived a test of several hours (until I've stopped it) whereas w/o this fix above panic occurs after several minutes (at most). Change since v1: - remove call to netif_stop_queue at beginning of xgmac_stop - use netif_tx_disable instead of locking+netif_stop_queue Signed-off-by: Andreas Herrmann <andreas.herrmann@calxeda.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r--drivers/net/ethernet/calxeda/xgmac.c10
1 files changed, 4 insertions, 6 deletions
diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
index 48f52882a22b..4fc5c8ef5121 100644
--- a/drivers/net/ethernet/calxeda/xgmac.c
+++ b/drivers/net/ethernet/calxeda/xgmac.c
@@ -1060,13 +1060,13 @@ static int xgmac_stop(struct net_device *dev)
1060{ 1060{
1061 struct xgmac_priv *priv = netdev_priv(dev); 1061 struct xgmac_priv *priv = netdev_priv(dev);
1062 1062
1063 netif_stop_queue(dev);
1064
1065 if (readl(priv->base + XGMAC_DMA_INTR_ENA)) 1063 if (readl(priv->base + XGMAC_DMA_INTR_ENA))
1066 napi_disable(&priv->napi); 1064 napi_disable(&priv->napi);
1067 1065
1068 writel(0, priv->base + XGMAC_DMA_INTR_ENA); 1066 writel(0, priv->base + XGMAC_DMA_INTR_ENA);
1069 1067
1068 netif_tx_disable(dev);
1069
1070 /* Disable the MAC core */ 1070 /* Disable the MAC core */
1071 xgmac_mac_disable(priv->base); 1071 xgmac_mac_disable(priv->base);
1072 1072
@@ -1370,11 +1370,8 @@ static int xgmac_change_mtu(struct net_device *dev, int new_mtu)
1370 } 1370 }
1371 1371
1372 old_mtu = dev->mtu; 1372 old_mtu = dev->mtu;
1373 dev->mtu = new_mtu;
1374 1373
1375 /* return early if the buffer sizes will not change */ 1374 /* return early if the buffer sizes will not change */
1376 if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN)
1377 return 0;
1378 if (old_mtu == new_mtu) 1375 if (old_mtu == new_mtu)
1379 return 0; 1376 return 0;
1380 1377
@@ -1382,8 +1379,9 @@ static int xgmac_change_mtu(struct net_device *dev, int new_mtu)
1382 if (!netif_running(dev)) 1379 if (!netif_running(dev))
1383 return 0; 1380 return 0;
1384 1381
1385 /* Bring the interface down and then back up */ 1382 /* Bring interface down, change mtu and bring interface back up */
1386 xgmac_stop(dev); 1383 xgmac_stop(dev);
1384 dev->mtu = new_mtu;
1387 return xgmac_open(dev); 1385 return xgmac_open(dev);
1388} 1386}
1389 1387