net: Introduce unregister_netdevice_many()
[cascardo/linux.git] / net / core / dev.c
index 560c8c9..04d3e30 100644 (file)
@@ -193,18 +193,15 @@ static struct list_head ptype_all __read_mostly;  /* Taps */
 DEFINE_RWLOCK(dev_base_lock);
 EXPORT_SYMBOL(dev_base_lock);
 
-#define NETDEV_HASHBITS        8
-#define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS)
-
 static inline struct hlist_head *dev_name_hash(struct net *net, const char *name)
 {
        unsigned hash = full_name_hash(name, strnlen(name, IFNAMSIZ));
-       return &net->dev_name_head[hash & ((1 << NETDEV_HASHBITS) - 1)];
+       return &net->dev_name_head[hash & (NETDEV_HASHENTRIES - 1)];
 }
 
 static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
 {
-       return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)];
+       return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)];
 }
 
 /* Device list insertion */
@@ -1791,13 +1788,25 @@ EXPORT_SYMBOL(skb_tx_hash);
 static struct netdev_queue *dev_pick_tx(struct net_device *dev,
                                        struct sk_buff *skb)
 {
-       const struct net_device_ops *ops = dev->netdev_ops;
-       u16 queue_index = 0;
+       u16 queue_index;
+       struct sock *sk = skb->sk;
 
-       if (ops->ndo_select_queue)
-               queue_index = ops->ndo_select_queue(dev, skb);
-       else if (dev->real_num_tx_queues > 1)
-               queue_index = skb_tx_hash(dev, skb);
+       if (sk_tx_queue_recorded(sk)) {
+               queue_index = sk_tx_queue_get(sk);
+       } else {
+               const struct net_device_ops *ops = dev->netdev_ops;
+
+               if (ops->ndo_select_queue) {
+                       queue_index = ops->ndo_select_queue(dev, skb);
+               } else {
+                       queue_index = 0;
+                       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);
+               }
+       }
 
        skb_set_queue_mapping(skb, queue_index);
        return netdev_get_tx_queue(dev, queue_index);
@@ -2288,16 +2297,16 @@ int netif_receive_skb(struct sk_buff *skb)
        int ret = NET_RX_DROP;
        __be16 type;
 
-       if (skb->vlan_tci && vlan_hwaccel_do_receive(skb))
+       if (!skb->tstamp.tv64)
+               net_timestamp(skb);
+
+       if (vlan_tx_tag_present(skb) && vlan_hwaccel_do_receive(skb))
                return NET_RX_SUCCESS;
 
        /* if we've gotten here through NAPI, check netpoll */
        if (netpoll_receive_skb(skb))
                return NET_RX_DROP;
 
-       if (!skb->tstamp.tv64)
-               net_timestamp(skb);
-
        if (!skb->iif)
                skb->iif = skb->dev->ifindex;
 
@@ -2604,20 +2613,13 @@ EXPORT_SYMBOL(napi_reuse_skb);
 
 struct sk_buff *napi_get_frags(struct napi_struct *napi)
 {
-       struct net_device *dev = napi->dev;
        struct sk_buff *skb = napi->skb;
 
        if (!skb) {
-               skb = netdev_alloc_skb(dev, GRO_MAX_HEAD + NET_IP_ALIGN);
-               if (!skb)
-                       goto out;
-
-               skb_reserve(skb, NET_IP_ALIGN);
-
-               napi->skb = skb;
+               skb = netdev_alloc_skb_ip_align(napi->dev, GRO_MAX_HEAD);
+               if (skb)
+                       napi->skb = skb;
        }
-
-out:
        return skb;
 }
 EXPORT_SYMBOL(napi_get_frags);
@@ -4635,59 +4637,76 @@ static void net_set_todo(struct net_device *dev)
        list_add_tail(&dev->todo_list, &net_todo_list);
 }
 
-static void rollback_registered(struct net_device *dev)
+static void rollback_registered_many(struct list_head *head)
 {
+       struct net_device *dev;
+
        BUG_ON(dev_boot_phase);
        ASSERT_RTNL();
 
-       /* Some devices call without registering for initialization unwind. */
-       if (dev->reg_state == NETREG_UNINITIALIZED) {
-               printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "
-                                 "was registered\n", dev->name, dev);
+       list_for_each_entry(dev, head, unreg_list) {
+               /* Some devices call without registering
+                * for initialization unwind.
+                */
+               if (dev->reg_state == NETREG_UNINITIALIZED) {
+                       pr_debug("unregister_netdevice: device %s/%p never "
+                                "was registered\n", dev->name, dev);
 
-               WARN_ON(1);
-               return;
-       }
+                       WARN_ON(1);
+                       return;
+               }
 
-       BUG_ON(dev->reg_state != NETREG_REGISTERED);
+               BUG_ON(dev->reg_state != NETREG_REGISTERED);
 
-       /* If device is running, close it first. */
-       dev_close(dev);
+               /* If device is running, close it first. */
+               dev_close(dev);
 
-       /* And unlink it from device chain. */
-       unlist_netdevice(dev);
+               /* And unlink it from device chain. */
+               unlist_netdevice(dev);
 
-       dev->reg_state = NETREG_UNREGISTERING;
+               dev->reg_state = NETREG_UNREGISTERING;
+       }
 
        synchronize_net();
 
-       /* Shutdown queueing discipline. */
-       dev_shutdown(dev);
+       list_for_each_entry(dev, head, unreg_list) {
+               /* Shutdown queueing discipline. */
+               dev_shutdown(dev);
 
 
-       /* Notify protocols, that we are about to destroy
-          this device. They should clean all the things.
-       */
-       call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+               /* Notify protocols, that we are about to destroy
+                  this device. They should clean all the things.
+               */
+               call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
 
-       /*
-        *      Flush the unicast and multicast chains
-        */
-       dev_unicast_flush(dev);
-       dev_addr_discard(dev);
+               /*
+                *      Flush the unicast and multicast chains
+                */
+               dev_unicast_flush(dev);
+               dev_addr_discard(dev);
 
-       if (dev->netdev_ops->ndo_uninit)
-               dev->netdev_ops->ndo_uninit(dev);
+               if (dev->netdev_ops->ndo_uninit)
+                       dev->netdev_ops->ndo_uninit(dev);
 
-       /* Notifier chain MUST detach us from master device. */
-       WARN_ON(dev->master);
+               /* Notifier chain MUST detach us from master device. */
+               WARN_ON(dev->master);
 
-       /* Remove entries from kobject tree */
-       netdev_unregister_kobject(dev);
+               /* Remove entries from kobject tree */
+               netdev_unregister_kobject(dev);
+       }
 
        synchronize_net();
 
-       dev_put(dev);
+       list_for_each_entry(dev, head, unreg_list)
+               dev_put(dev);
+}
+
+static void rollback_registered(struct net_device *dev)
+{
+       LIST_HEAD(single);
+
+       list_add(&dev->unreg_list, &single);
+       rollback_registered_many(&single);
 }
 
 static void __netdev_init_queue_locks_one(struct net_device *dev,
@@ -4836,6 +4855,12 @@ int register_netdevice(struct net_device *dev)
                dev->features |= NETIF_F_GSO;
 
        netdev_initialize_kobject(dev);
+
+       ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
+       ret = notifier_to_errno(ret);
+       if (ret)
+               goto err_uninit;
+
        ret = netdev_register_kobject(dev);
        if (ret)
                goto err_uninit;
@@ -5237,25 +5262,47 @@ void synchronize_net(void)
 EXPORT_SYMBOL(synchronize_net);
 
 /**
- *     unregister_netdevice - remove device from the kernel
+ *     unregister_netdevice_queue - remove device from the kernel
  *     @dev: device
- *
+ *     @head: list
+
  *     This function shuts down a device interface and removes it
  *     from the kernel tables.
+ *     If head not NULL, device is queued to be unregistered later.
  *
  *     Callers must hold the rtnl semaphore.  You may want
  *     unregister_netdev() instead of this.
  */
 
-void unregister_netdevice(struct net_device *dev)
+void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
 {
        ASSERT_RTNL();
 
-       rollback_registered(dev);
-       /* Finish processing unregister after unlock */
-       net_set_todo(dev);
+       if (head) {
+               list_add_tail(&dev->unreg_list, head);
+       } else {
+               rollback_registered(dev);
+               /* Finish processing unregister after unlock */
+               net_set_todo(dev);
+       }
+}
+EXPORT_SYMBOL(unregister_netdevice_queue);
+
+/**
+ *     unregister_netdevice_many - unregister many devices
+ *     @head: list of devices
+ *
+ */
+void unregister_netdevice_many(struct list_head *head)
+{
+       struct net_device *dev;
+
+       if (!list_empty(head)) {
+               rollback_registered_many(head);
+               list_for_each_entry(dev, head, unreg_list)
+                       net_set_todo(dev);
+       }
 }
-EXPORT_SYMBOL(unregister_netdevice);
 
 /**
  *     unregister_netdev - remove device from the kernel
@@ -5483,7 +5530,7 @@ unsigned long netdev_increment_features(unsigned long all, unsigned long one,
        one |= NETIF_F_ALL_CSUM;
 
        one |= all & NETIF_F_ONE_FOR_ALL;
-       all &= one | NETIF_F_LLTX | NETIF_F_GSO;
+       all &= one | NETIF_F_LLTX | NETIF_F_GSO | NETIF_F_UFO;
        all |= one & mask & NETIF_F_ONE_FOR_ALL;
 
        return all;