net: Fix an RCU warning in dev_pick_tx()
[safe/jmp/linux-2.6] / net / core / dev.c
index d1cf53d..f769098 100644 (file)
@@ -80,6 +80,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/hash.h>
+#include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/mutex.h>
 #include <linux/string.h>
@@ -1113,19 +1114,7 @@ void dev_load(struct net *net, const char *name)
 }
 EXPORT_SYMBOL(dev_load);
 
-/**
- *     dev_open        - prepare an interface for use.
- *     @dev:   device to open
- *
- *     Takes a device from down to up state. The device's private open
- *     function is invoked and then the multicast lists are loaded. Finally
- *     the device is moved into the up state and a %NETDEV_UP message is
- *     sent to the netdev notifier chain.
- *
- *     Calling this function on an active interface is a nop. On a failure
- *     a negative errno code is returned.
- */
-int dev_open(struct net_device *dev)
+static int __dev_open(struct net_device *dev)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
        int ret;
@@ -1133,13 +1122,6 @@ int dev_open(struct net_device *dev)
        ASSERT_RTNL();
 
        /*
-        *      Is it already up?
-        */
-
-       if (dev->flags & IFF_UP)
-               return 0;
-
-       /*
         *      Is it even present?
         */
        if (!netif_device_present(dev))
@@ -1187,36 +1169,57 @@ int dev_open(struct net_device *dev)
                 *      Wakeup transmit queue engine
                 */
                dev_activate(dev);
-
-               /*
-                *      ... and announce new interface.
-                */
-               call_netdevice_notifiers(NETDEV_UP, dev);
        }
 
        return ret;
 }
-EXPORT_SYMBOL(dev_open);
 
 /**
- *     dev_close - shutdown an interface.
- *     @dev: device to shutdown
+ *     dev_open        - prepare an interface for use.
+ *     @dev:   device to open
  *
- *     This function moves an active device into down state. A
- *     %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device
- *     is then deactivated and finally a %NETDEV_DOWN is sent to the notifier
- *     chain.
+ *     Takes a device from down to up state. The device's private open
+ *     function is invoked and then the multicast lists are loaded. Finally
+ *     the device is moved into the up state and a %NETDEV_UP message is
+ *     sent to the netdev notifier chain.
+ *
+ *     Calling this function on an active interface is a nop. On a failure
+ *     a negative errno code is returned.
  */
-int dev_close(struct net_device *dev)
+int dev_open(struct net_device *dev)
+{
+       int ret;
+
+       /*
+        *      Is it already up?
+        */
+       if (dev->flags & IFF_UP)
+               return 0;
+
+       /*
+        *      Open device
+        */
+       ret = __dev_open(dev);
+       if (ret < 0)
+               return ret;
+
+       /*
+        *      ... and announce new interface.
+        */
+       rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
+       call_netdevice_notifiers(NETDEV_UP, dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(dev_open);
+
+static int __dev_close(struct net_device *dev)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
-       ASSERT_RTNL();
 
+       ASSERT_RTNL();
        might_sleep();
 
-       if (!(dev->flags & IFF_UP))
-               return 0;
-
        /*
         *      Tell people we are going down, so that they can
         *      prepare to death, when device is still operating.
@@ -1252,14 +1255,34 @@ int dev_close(struct net_device *dev)
        dev->flags &= ~IFF_UP;
 
        /*
-        * Tell people we are down
+        *      Shutdown NET_DMA
         */
-       call_netdevice_notifiers(NETDEV_DOWN, dev);
+       net_dmaengine_put();
+
+       return 0;
+}
+
+/**
+ *     dev_close - shutdown an interface.
+ *     @dev: device to shutdown
+ *
+ *     This function moves an active device into down state. A
+ *     %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device
+ *     is then deactivated and finally a %NETDEV_DOWN is sent to the notifier
+ *     chain.
+ */
+int dev_close(struct net_device *dev)
+{
+       if (!(dev->flags & IFF_UP))
+               return 0;
+
+       __dev_close(dev);
 
        /*
-        *      Shutdown NET_DMA
+        * Tell people we are down
         */
-       net_dmaengine_put();
+       rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
+       call_netdevice_notifiers(NETDEV_DOWN, dev);
 
        return 0;
 }
@@ -1966,8 +1989,12 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev,
                        if (dev->real_num_tx_queues > 1)
                                queue_index = skb_tx_hash(dev, skb);
 
-                       if (sk && sk->sk_dst_cache)
-                               sk_tx_queue_set(sk, queue_index);
+                       if (sk) {
+                               struct dst_entry *dst = rcu_dereference_bh(sk->sk_dst_cache);
+
+                               if (dst && skb_dst(skb) == dst)
+                                       sk_tx_queue_set(sk, queue_index);
+                       }
                }
        }
 
@@ -2081,7 +2108,7 @@ gso:
        rcu_read_lock_bh();
 
        txq = dev_pick_tx(dev, skb);
-       q = rcu_dereference(txq->qdisc);
+       q = rcu_dereference_bh(txq->qdisc);
 
 #ifdef CONFIG_NET_CLS_ACT
        skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
@@ -2461,6 +2488,7 @@ int netif_receive_skb(struct sk_buff *skb)
 {
        struct packet_type *ptype, *pt_prev;
        struct net_device *orig_dev;
+       struct net_device *master;
        struct net_device *null_or_orig;
        struct net_device *null_or_bond;
        int ret = NET_RX_DROP;
@@ -2481,11 +2509,12 @@ int netif_receive_skb(struct sk_buff *skb)
 
        null_or_orig = NULL;
        orig_dev = skb->dev;
-       if (orig_dev->master) {
-               if (skb_bond_should_drop(skb))
+       master = ACCESS_ONCE(orig_dev->master);
+       if (master) {
+               if (skb_bond_should_drop(skb, master))
                        null_or_orig = orig_dev; /* deliver only exact match */
                else
-                       skb->dev = orig_dev->master;
+                       skb->dev = master;
        }
 
        __get_cpu_var(netdev_rx_stat).total++;
@@ -2813,7 +2842,7 @@ gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb,
        switch (ret) {
        case GRO_NORMAL:
        case GRO_HELD:
-               skb->protocol = eth_type_trans(skb, napi->dev);
+               skb->protocol = eth_type_trans(skb, skb->dev);
 
                if (ret == GRO_HELD)
                        skb_gro_pull(skb, -ETH_HLEN);
@@ -3018,7 +3047,7 @@ static void net_rx_action(struct softirq_action *h)
                 * entries to the tail of this list, and only ->poll()
                 * calls can remove this head entry from the list.
                 */
-               n = list_entry(list->next, struct napi_struct, poll_list);
+               n = list_first_entry(list, struct napi_struct, poll_list);
 
                have = netpoll_poll_lock(n);
 
@@ -4299,18 +4328,10 @@ unsigned dev_get_flags(const struct net_device *dev)
 }
 EXPORT_SYMBOL(dev_get_flags);
 
-/**
- *     dev_change_flags - change device settings
- *     @dev: device
- *     @flags: device state flags
- *
- *     Change settings on device based state flags. The flags are
- *     in the userspace exported format.
- */
-int dev_change_flags(struct net_device *dev, unsigned flags)
+int __dev_change_flags(struct net_device *dev, unsigned int flags)
 {
-       int ret, changes;
        int old_flags = dev->flags;
+       int ret;
 
        ASSERT_RTNL();
 
@@ -4341,17 +4362,12 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
 
        ret = 0;
        if ((old_flags ^ flags) & IFF_UP) {     /* Bit is different  ? */
-               ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
+               ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev);
 
                if (!ret)
                        dev_set_rx_mode(dev);
        }
 
-       if (dev->flags & IFF_UP &&
-           ((old_flags ^ dev->flags) & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
-                                         IFF_VOLATILE)))
-               call_netdevice_notifiers(NETDEV_CHANGE, dev);
-
        if ((flags ^ dev->gflags) & IFF_PROMISC) {
                int inc = (flags & IFF_PROMISC) ? 1 : -1;
 
@@ -4370,11 +4386,47 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
                dev_set_allmulti(dev, inc);
        }
 
-       /* Exclude state transition flags, already notified */
-       changes = (old_flags ^ dev->flags) & ~(IFF_UP | IFF_RUNNING);
+       return ret;
+}
+
+void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
+{
+       unsigned int changes = dev->flags ^ old_flags;
+
+       if (changes & IFF_UP) {
+               if (dev->flags & IFF_UP)
+                       call_netdevice_notifiers(NETDEV_UP, dev);
+               else
+                       call_netdevice_notifiers(NETDEV_DOWN, dev);
+       }
+
+       if (dev->flags & IFF_UP &&
+           (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE)))
+               call_netdevice_notifiers(NETDEV_CHANGE, dev);
+}
+
+/**
+ *     dev_change_flags - change device settings
+ *     @dev: device
+ *     @flags: device state flags
+ *
+ *     Change settings on device based state flags. The flags are
+ *     in the userspace exported format.
+ */
+int dev_change_flags(struct net_device *dev, unsigned flags)
+{
+       int ret, changes;
+       int old_flags = dev->flags;
+
+       ret = __dev_change_flags(dev, flags);
+       if (ret < 0)
+               return ret;
+
+       changes = old_flags ^ dev->flags;
        if (changes)
                rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
 
+       __dev_notify_flags(dev, old_flags);
        return ret;
 }
 EXPORT_SYMBOL(dev_change_flags);
@@ -4865,6 +4917,10 @@ static void rollback_registered_many(struct list_head *head)
                */
                call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
 
+               if (!dev->rtnl_link_ops ||
+                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
+                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
+
                /*
                 *      Flush the unicast and multicast chains
                 */
@@ -4882,7 +4938,7 @@ static void rollback_registered_many(struct list_head *head)
        }
 
        /* Process any work delayed until the end of the batch */
-       dev = list_entry(head->next, struct net_device, unreg_list);
+       dev = list_first_entry(head, struct net_device, unreg_list);
        call_netdevice_notifiers(NETDEV_UNREGISTER_BATCH, dev);
 
        synchronize_net();
@@ -5091,7 +5147,9 @@ int register_netdevice(struct net_device *dev)
         *      Prevent userspace races by waiting until the network
         *      device is fully setup before sending notifications.
         */
-       rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
+       if (!dev->rtnl_link_ops ||
+           dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
+               rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
 
 out:
        return ret;
@@ -5268,7 +5326,7 @@ void netdev_run_todo(void)
 
        while (!list_empty(&list)) {
                struct net_device *dev
-                       = list_entry(list.next, struct net_device, todo_list);
+                       = list_first_entry(&list, struct net_device, todo_list);
                list_del(&dev->todo_list);
 
                if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) {