Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[cascardo/linux.git] / net / core / dev.c
index ed49da5..472676d 100644 (file)
@@ -90,6 +90,7 @@
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 #include <linux/notifier.h>
 #include <linux/skbuff.h>
 #include <net/net_namespace.h>
 #include <linux/err.h>
 #include <linux/ctype.h>
 #include <linux/if_arp.h>
+#include <linux/if_vlan.h>
 
 #include "net-sysfs.h"
 
@@ -162,7 +164,7 @@ struct net_dma {
        struct dma_client client;
        spinlock_t lock;
        cpumask_t channel_mask;
-       struct dma_chan *channels[NR_CPUS];
+       struct dma_chan **channels;
 };
 
 static enum dma_state_client
@@ -903,7 +905,11 @@ int dev_change_name(struct net_device *dev, char *newname)
                strlcpy(dev->name, newname, IFNAMSIZ);
 
 rollback:
-       device_rename(&dev->dev, dev->name);
+       err = device_rename(&dev->dev, dev->name);
+       if (err) {
+               memcpy(dev->name, oldname, IFNAMSIZ);
+               return err;
+       }
 
        write_lock_bh(&dev_base_lock);
        hlist_del(&dev->name_hlist);
@@ -956,6 +962,12 @@ void netdev_state_change(struct net_device *dev)
        }
 }
 
+void netdev_bonding_change(struct net_device *dev)
+{
+       call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, dev);
+}
+EXPORT_SYMBOL(netdev_bonding_change);
+
 /**
  *     dev_load        - load a network module
  *     @net: the applicable net namespace
@@ -994,6 +1006,8 @@ int dev_open(struct net_device *dev)
 {
        int ret = 0;
 
+       ASSERT_RTNL();
+
        /*
         *      Is it already up?
         */
@@ -1060,6 +1074,8 @@ int dev_open(struct net_device *dev)
  */
 int dev_close(struct net_device *dev)
 {
+       ASSERT_RTNL();
+
        might_sleep();
 
        if (!(dev->flags & IFF_UP))
@@ -1108,6 +1124,29 @@ int dev_close(struct net_device *dev)
 }
 
 
+/**
+ *     dev_disable_lro - disable Large Receive Offload on a device
+ *     @dev: device
+ *
+ *     Disable Large Receive Offload (LRO) on a net device.  Must be
+ *     called under RTNL.  This is needed if received packets may be
+ *     forwarded to another interface.
+ */
+void dev_disable_lro(struct net_device *dev)
+{
+       if (dev->ethtool_ops && dev->ethtool_ops->get_flags &&
+           dev->ethtool_ops->set_flags) {
+               u32 flags = dev->ethtool_ops->get_flags(dev);
+               if (flags & ETH_FLAG_LRO) {
+                       flags &= ~ETH_FLAG_LRO;
+                       dev->ethtool_ops->set_flags(dev, flags);
+               }
+       }
+       WARN_ON(dev->features & NETIF_F_LRO);
+}
+EXPORT_SYMBOL(dev_disable_lro);
+
+
 static int dev_boot_phase = 1;
 
 /*
@@ -1354,6 +1393,29 @@ void netif_device_attach(struct net_device *dev)
 }
 EXPORT_SYMBOL(netif_device_attach);
 
+static bool can_checksum_protocol(unsigned long features, __be16 protocol)
+{
+       return ((features & NETIF_F_GEN_CSUM) ||
+               ((features & NETIF_F_IP_CSUM) &&
+                protocol == htons(ETH_P_IP)) ||
+               ((features & NETIF_F_IPV6_CSUM) &&
+                protocol == htons(ETH_P_IPV6)));
+}
+
+static bool dev_can_checksum(struct net_device *dev, struct sk_buff *skb)
+{
+       if (can_checksum_protocol(dev->features, skb->protocol))
+               return true;
+
+       if (skb->protocol == htons(ETH_P_8021Q)) {
+               struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
+               if (can_checksum_protocol(dev->features & dev->vlan_features,
+                                         veh->h_vlan_encapsulated_proto))
+                       return true;
+       }
+
+       return false;
+}
 
 /*
  * Invalidate hardware checksum when packet is to be mangled, and
@@ -1632,14 +1694,8 @@ int dev_queue_xmit(struct sk_buff *skb)
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
                skb_set_transport_header(skb, skb->csum_start -
                                              skb_headroom(skb));
-
-               if (!(dev->features & NETIF_F_GEN_CSUM) &&
-                   !((dev->features & NETIF_F_IP_CSUM) &&
-                     skb->protocol == htons(ETH_P_IP)) &&
-                   !((dev->features & NETIF_F_IPV6_CSUM) &&
-                     skb->protocol == htons(ETH_P_IPV6)))
-                       if (skb_checksum_help(skb))
-                               goto out_kfree_skb;
+               if (!dev_can_checksum(dev, skb) && skb_checksum_help(skb))
+                       goto out_kfree_skb;
        }
 
 gso:
@@ -2051,6 +2107,10 @@ int netif_receive_skb(struct sk_buff *skb)
 
        rcu_read_lock();
 
+       /* Don't receive packets in an exiting network namespace */
+       if (!net_alive(dev_net(skb->dev)))
+               goto out;
+
 #ifdef CONFIG_NET_CLS_ACT
        if (skb->tc_verd & TC_NCLS) {
                skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
@@ -2444,7 +2504,7 @@ static struct netif_rx_stats *softnet_get_online(loff_t *pos)
 {
        struct netif_rx_stats *rc = NULL;
 
-       while (*pos < NR_CPUS)
+       while (*pos < nr_cpu_ids)
                if (cpu_online(*pos)) {
                        rc = &per_cpu(netdev_rx_stat, *pos);
                        break;
@@ -2739,16 +2799,29 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
        return 0;
 }
 
-static void __dev_set_promiscuity(struct net_device *dev, int inc)
+static int __dev_set_promiscuity(struct net_device *dev, int inc)
 {
        unsigned short old_flags = dev->flags;
 
        ASSERT_RTNL();
 
-       if ((dev->promiscuity += inc) == 0)
-               dev->flags &= ~IFF_PROMISC;
-       else
-               dev->flags |= IFF_PROMISC;
+       dev->flags |= IFF_PROMISC;
+       dev->promiscuity += inc;
+       if (dev->promiscuity == 0) {
+               /*
+                * Avoid overflow.
+                * If inc causes overflow, untouch promisc and return error.
+                */
+               if (inc < 0)
+                       dev->flags &= ~IFF_PROMISC;
+               else {
+                       dev->promiscuity -= inc;
+                       printk(KERN_WARNING "%s: promiscuity touches roof, "
+                               "set promiscuity failed, promiscuity feature "
+                               "of device might be broken.\n", dev->name);
+                       return -EOVERFLOW;
+               }
+       }
        if (dev->flags != old_flags) {
                printk(KERN_INFO "device %s %s promiscuous mode\n",
                       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
@@ -2766,6 +2839,7 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
                if (dev->change_rx_flags)
                        dev->change_rx_flags(dev, IFF_PROMISC);
        }
+       return 0;
 }
 
 /**
@@ -2777,14 +2851,19 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
  *     remains above zero the interface remains promiscuous. Once it hits zero
  *     the device reverts back to normal filtering operation. A negative inc
  *     value is used to drop promiscuity on the device.
+ *     Return 0 if successful or a negative errno code on error.
  */
-void dev_set_promiscuity(struct net_device *dev, int inc)
+int dev_set_promiscuity(struct net_device *dev, int inc)
 {
        unsigned short old_flags = dev->flags;
+       int err;
 
-       __dev_set_promiscuity(dev, inc);
+       err = __dev_set_promiscuity(dev, inc);
+       if (!err)
+               return err;
        if (dev->flags != old_flags)
                dev_set_rx_mode(dev);
+       return err;
 }
 
 /**
@@ -2797,22 +2876,38 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
  *     to all interfaces. Once it hits zero the device reverts back to normal
  *     filtering operation. A negative @inc value is used to drop the counter
  *     when releasing a resource needing all multicasts.
+ *     Return 0 if successful or a negative errno code on error.
  */
 
-void dev_set_allmulti(struct net_device *dev, int inc)
+int dev_set_allmulti(struct net_device *dev, int inc)
 {
        unsigned short old_flags = dev->flags;
 
        ASSERT_RTNL();
 
        dev->flags |= IFF_ALLMULTI;
-       if ((dev->allmulti += inc) == 0)
-               dev->flags &= ~IFF_ALLMULTI;
+       dev->allmulti += inc;
+       if (dev->allmulti == 0) {
+               /*
+                * Avoid overflow.
+                * If inc causes overflow, untouch allmulti and return error.
+                */
+               if (inc < 0)
+                       dev->flags &= ~IFF_ALLMULTI;
+               else {
+                       dev->allmulti -= inc;
+                       printk(KERN_WARNING "%s: allmulti touches roof, "
+                               "set allmulti failed, allmulti feature of "
+                               "device might be broken.\n", dev->name);
+                       return -EOVERFLOW;
+               }
+       }
        if (dev->flags ^ old_flags) {
                if (dev->change_rx_flags)
                        dev->change_rx_flags(dev, IFF_ALLMULTI);
                dev_set_rx_mode(dev);
        }
+       return 0;
 }
 
 /*
@@ -2943,7 +3038,7 @@ EXPORT_SYMBOL(dev_unicast_delete);
 /**
  *     dev_unicast_add         - add a secondary unicast address
  *     @dev: device
- *     @addr: address to delete
+ *     @addr: address to add
  *     @alen: length of @addr
  *
  *     Add a secondary unicast address to the device or increase
@@ -3133,7 +3228,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
         *      Load in the correct multicast list now the flags have changed.
         */
 
-       if (dev->change_rx_flags && (dev->flags ^ flags) & IFF_MULTICAST)
+       if (dev->change_rx_flags && (old_flags ^ flags) & IFF_MULTICAST)
                dev->change_rx_flags(dev, IFF_MULTICAST);
 
        dev_set_rx_mode(dev);
@@ -3776,6 +3871,7 @@ int register_netdevice(struct net_device *dev)
                }
        }
 
+       netdev_initialize_kobject(dev);
        ret = netdev_register_kobject(dev);
        if (ret)
                goto err_uninit;
@@ -4208,7 +4304,8 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
        }
 
        /* Fixup kobjects */
-       err = device_rename(&dev->dev, dev->name);
+       netdev_unregister_kobject(dev);
+       err = netdev_register_kobject(dev);
        WARN_ON(err);
 
        /* Add the device back in the hashes */
@@ -4324,7 +4421,7 @@ netdev_dma_event(struct dma_client *client, struct dma_chan *chan,
        spin_lock(&net_dma->lock);
        switch (state) {
        case DMA_RESOURCE_AVAILABLE:
-               for (i = 0; i < NR_CPUS; i++)
+               for (i = 0; i < nr_cpu_ids; i++)
                        if (net_dma->channels[i] == chan) {
                                found = 1;
                                break;
@@ -4339,7 +4436,7 @@ netdev_dma_event(struct dma_client *client, struct dma_chan *chan,
                }
                break;
        case DMA_RESOURCE_REMOVED:
-               for (i = 0; i < NR_CPUS; i++)
+               for (i = 0; i < nr_cpu_ids; i++)
                        if (net_dma->channels[i] == chan) {
                                found = 1;
                                pos = i;
@@ -4366,6 +4463,13 @@ netdev_dma_event(struct dma_client *client, struct dma_chan *chan,
  */
 static int __init netdev_dma_register(void)
 {
+       net_dma.channels = kzalloc(nr_cpu_ids * sizeof(struct net_dma),
+                                                               GFP_KERNEL);
+       if (unlikely(!net_dma.channels)) {
+               printk(KERN_NOTICE
+                               "netdev_dma: no memory for net_dma.channels\n");
+               return -ENOMEM;
+       }
        spin_lock_init(&net_dma.lock);
        dma_cap_set(DMA_MEMCPY, net_dma.client.cap_mask);
        dma_async_client_register(&net_dma.client);
@@ -4471,17 +4575,19 @@ static void __net_exit default_device_exit(struct net *net)
        rtnl_lock();
        for_each_netdev_safe(net, dev, next) {
                int err;
+               char fb_name[IFNAMSIZ];
 
                /* Ignore unmoveable devices (i.e. loopback) */
                if (dev->features & NETIF_F_NETNS_LOCAL)
                        continue;
 
                /* Push remaing network devices to init_net */
-               err = dev_change_net_namespace(dev, &init_net, "dev%d");
+               snprintf(fb_name, IFNAMSIZ, "dev%d", dev->ifindex);
+               err = dev_change_net_namespace(dev, &init_net, fb_name);
                if (err) {
-                       printk(KERN_WARNING "%s: failed to move %s to init_net: %d\n",
+                       printk(KERN_EMERG "%s: failed to move %s to init_net: %d\n",
                                __func__, dev->name, err);
-                       unregister_netdevice(dev);
+                       BUG();
                }
        }
        rtnl_unlock();