Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andreas Herrmann and committed by
David S. Miller
b5ad795e e21dd863

+4 -6
+4 -6
drivers/net/ethernet/calxeda/xgmac.c
··· 1060 1060 { 1061 1061 struct xgmac_priv *priv = netdev_priv(dev); 1062 1062 1063 - netif_stop_queue(dev); 1064 - 1065 1063 if (readl(priv->base + XGMAC_DMA_INTR_ENA)) 1066 1064 napi_disable(&priv->napi); 1067 1065 1068 1066 writel(0, priv->base + XGMAC_DMA_INTR_ENA); 1067 + 1068 + netif_tx_disable(dev); 1069 1069 1070 1070 /* Disable the MAC core */ 1071 1071 xgmac_mac_disable(priv->base); ··· 1370 1370 } 1371 1371 1372 1372 old_mtu = dev->mtu; 1373 - dev->mtu = new_mtu; 1374 1373 1375 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 1375 if (old_mtu == new_mtu) 1379 1376 return 0; 1380 1377 ··· 1379 1382 if (!netif_running(dev)) 1380 1383 return 0; 1381 1384 1382 - /* Bring the interface down and then back up */ 1385 + /* Bring interface down, change mtu and bring interface back up */ 1383 1386 xgmac_stop(dev); 1387 + dev->mtu = new_mtu; 1384 1388 return xgmac_open(dev); 1385 1389 } 1386 1390