]> git.proxmox.com Git - mirror_ubuntu-disco-kernel.git/commitdiff
e1000: remove races when changing mtu
authorJesse Brandeburg <jesse.brandeburg@intel.com>
Fri, 25 Sep 2009 12:19:02 +0000 (12:19 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 27 Sep 2009 03:15:47 +0000 (20:15 -0700)
this patch fixes a bug that occurs when routing packets and simultaneously
changing the mtu.  the rx_buffer_len variable is used during the rx cleanup
and if that changes on the fly without stopping traffic bad things happen

Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: Don Skidmore <donald.c.skidmore@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/e1000/e1000_main.c

index cdbf4fb8150daec41c62f458e66663fd089e986d..2178e0d39c012c505a3e0b5b4a634c12c6de4f83 100644 (file)
@@ -3141,6 +3141,13 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
                break;
        }
 
+       while (test_and_set_bit(__E1000_RESETTING, &adapter->flags))
+               msleep(1);
+       /* e1000_down has a dependency on max_frame_size */
+       hw->max_frame_size = max_frame;
+       if (netif_running(netdev))
+               e1000_down(adapter);
+
        /* NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN
         * means we reserve 2 more, this pushes us to allocate from the next
         * larger slab size.
@@ -3169,11 +3176,16 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
             (max_frame == MAXIMUM_ETHERNET_VLAN_SIZE)))
                adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
 
+       printk(KERN_INFO "e1000: %s changing MTU from %d to %d\n",
+              netdev->name, netdev->mtu, new_mtu);
        netdev->mtu = new_mtu;
-       hw->max_frame_size = max_frame;
 
        if (netif_running(netdev))
-               e1000_reinit_locked(adapter);
+               e1000_up(adapter);
+       else
+               e1000_reset(adapter);
+
+       clear_bit(__E1000_RESETTING, &adapter->flags);
 
        return 0;
 }