ibmveth: Fix opps during MTU change on an active device
Robert Jennings [Tue, 17 Aug 2010 09:15:45 +0000 (09:15 +0000)]
This fixes the following opps which can occur when trying to deallocate
receive buffer pools when changing the MTU of an active ibmveth device.

Oops: Kernel access of bad area, sig: 11 [#1]
NIP: d000000004db00e8 LR: d000000004db00ac CTR: 0000000000591038
REGS: c00000007fff39d0 TRAP: 0300   Not tainted  (2.6.36-rc1)
MSR: 8000000000009032 <EE,ME,IR,DR>  CR: 22248244  XER: 00000002
DAR: 0000000000000488, DSISR: 0000000042000000
TASK = c00000007c463790[6531] 'netserver' THREAD: c00000007a154000 CPU: 0
GPR00: 0000000000000000 c00000007fff3c50 d000000004dbd360 0000000000000001
GPR04: 0000000000000001 1fffffffffffffff 000000000000043c c00000007a8e9f60
GPR08: c00000007a8e9e20 0000000000000245 0000000000000488 0000000000000000
GPR12: 00000000000000c0 c000000006d70000 c00000007bfec098 c00000007bfebc2c
GPR16: c00000007a157c78 0000000000000000 0000000000000001 0000000000000000
GPR20: 0000000000000001 0000000000000010 c000000000b51180 c00000007a8e9d90
GPR24: c00000007a8e9da0 c00000007a8e9580 00000000000005ea 00000000000002ff
GPR28: 0000000000000004 0000000000000080 c000000000a946f8 c00000007a8e9d80
NIP [d000000004db00e8] .ibmveth_remove_buffer_from_pool+0xe8/0x130 [ibmveth]
LR [d000000004db00ac] .ibmveth_remove_buffer_from_pool+0xac/0x130 [ibmveth]
Call Trace:
[c00000007fff3c50] [d000000004db00ac] .ibmveth_remove_buffer_from_pool+0xac/0x130 [ibmveth] (unreliable)
[c00000007fff3cf0] [d000000004db31dc] .ibmveth_poll+0x30c/0x460 [ibmveth]
[c00000007fff3dd0] [c00000000042c4b8] .net_rx_action+0x178/0x278
[c00000007fff3eb0] [c000000000093cf0] .__do_softirq+0x118/0x1f8
[c00000007fff3f90] [c00000000002ab3c] .call_do_softirq+0x14/0x24
[c00000007a157600] [c00000000000e3e4] .do_softirq+0xec/0x110
[c00000007a1576a0] [c000000000093394] .local_bh_enable_ip+0xb4/0xe0
[c00000007a157720] [c0000000004f0bac] ._raw_spin_unlock_bh+0x3c/0x50
[c00000007a157790] [c0000000004186e0] .release_sock+0x158/0x188
[c00000007a157840] [c000000000479660] .tcp_recvmsg+0x560/0x9b8
[c00000007a157970] [c0000000004a0d78] .inet_recvmsg+0x80/0xd8
[c00000007a157a00] [c000000000413e28] .sock_recvmsg+0x128/0x178
[c00000007a157bf0] [c0000000004164ac] .SyS_recvfrom+0xb4/0x148
[c00000007a157d70] [c000000000411f3c] .SyS_socketcall+0x274/0x360
[c00000007a157e30] [c0000000000085b4] syscall_exit+0x0/0x40

Reported-by: Rafael Camarda Silva Folco <rfolco@linux.vnet.ibm.com>
Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com>
Acked-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

drivers/net/ibmveth.c

index 2602852..4734c93 100644 (file)
@@ -1113,7 +1113,8 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
        struct ibmveth_adapter *adapter = netdev_priv(dev);
        struct vio_dev *viodev = adapter->vdev;
        int new_mtu_oh = new_mtu + IBMVETH_BUFF_OH;
-       int i;
+       int i, rc;
+       int need_restart = 0;
 
        if (new_mtu < IBMVETH_MAX_MTU)
                return -EINVAL;
@@ -1127,35 +1128,32 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
 
        /* Deactivate all the buffer pools so that the next loop can activate
           only the buffer pools necessary to hold the new MTU */
-       for (i = 0; i < IbmVethNumBufferPools; i++)
-               if (adapter->rx_buff_pool[i].active) {
-                       ibmveth_free_buffer_pool(adapter,
-                                                &adapter->rx_buff_pool[i]);
-                       adapter->rx_buff_pool[i].active = 0;
-               }
+       if (netif_running(adapter->netdev)) {
+               need_restart = 1;
+               adapter->pool_config = 1;
+               ibmveth_close(adapter->netdev);
+               adapter->pool_config = 0;
+       }
 
        /* Look for an active buffer pool that can hold the new MTU */
        for(i = 0; i<IbmVethNumBufferPools; i++) {
                adapter->rx_buff_pool[i].active = 1;
 
                if (new_mtu_oh < adapter->rx_buff_pool[i].buff_size) {
-                       if (netif_running(adapter->netdev)) {
-                               adapter->pool_config = 1;
-                               ibmveth_close(adapter->netdev);
-                               adapter->pool_config = 0;
-                               dev->mtu = new_mtu;
-                               vio_cmo_set_dev_desired(viodev,
-                                               ibmveth_get_desired_dma
-                                               (viodev));
-                               return ibmveth_open(adapter->netdev);
-                       }
                        dev->mtu = new_mtu;
                        vio_cmo_set_dev_desired(viodev,
                                                ibmveth_get_desired_dma
                                                (viodev));
+                       if (need_restart) {
+                               return ibmveth_open(adapter->netdev);
+                       }
                        return 0;
                }
        }
+
+       if (need_restart && (rc = ibmveth_open(adapter->netdev)))
+               return rc;
+
        return -EINVAL;
 }