]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/core/dev.c
Merge branch 'drm-next-3.16' of git://people.freedesktop.org/~agd5f/linux into drm...
[karo-tx-linux.git] / net / core / dev.c
index 8908a68db44921c91a4f359b90e575c4a7190468..30eedf6779138d77ae9c54ca5efa8bf57587e7ac 100644 (file)
@@ -1661,6 +1661,29 @@ bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(is_skb_forwardable);
 
+int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
+{
+       if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
+               if (skb_copy_ubufs(skb, GFP_ATOMIC)) {
+                       atomic_long_inc(&dev->rx_dropped);
+                       kfree_skb(skb);
+                       return NET_RX_DROP;
+               }
+       }
+
+       if (unlikely(!is_skb_forwardable(dev, skb))) {
+               atomic_long_inc(&dev->rx_dropped);
+               kfree_skb(skb);
+               return NET_RX_DROP;
+       }
+
+       skb_scrub_packet(skb, true);
+       skb->protocol = eth_type_trans(skb, dev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__dev_forward_skb);
+
 /**
  * dev_forward_skb - loopback an skb to another netif
  *
@@ -1681,24 +1704,7 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable);
  */
 int dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
 {
-       if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
-               if (skb_copy_ubufs(skb, GFP_ATOMIC)) {
-                       atomic_long_inc(&dev->rx_dropped);
-                       kfree_skb(skb);
-                       return NET_RX_DROP;
-               }
-       }
-
-       if (unlikely(!is_skb_forwardable(dev, skb))) {
-               atomic_long_inc(&dev->rx_dropped);
-               kfree_skb(skb);
-               return NET_RX_DROP;
-       }
-
-       skb_scrub_packet(skb, true);
-       skb->protocol = eth_type_trans(skb, dev);
-
-       return netif_rx_internal(skb);
+       return __dev_forward_skb(dev, skb) ?: netif_rx_internal(skb);
 }
 EXPORT_SYMBOL_GPL(dev_forward_skb);
 
@@ -2507,13 +2513,39 @@ static int dev_gso_segment(struct sk_buff *skb, netdev_features_t features)
        return 0;
 }
 
+/* If MPLS offload request, verify we are testing hardware MPLS features
+ * instead of standard features for the netdev.
+ */
+#ifdef CONFIG_NET_MPLS_GSO
+static netdev_features_t net_mpls_features(struct sk_buff *skb,
+                                          netdev_features_t features,
+                                          __be16 type)
+{
+       if (type == htons(ETH_P_MPLS_UC) || type == htons(ETH_P_MPLS_MC))
+               features &= skb->dev->mpls_features;
+
+       return features;
+}
+#else
+static netdev_features_t net_mpls_features(struct sk_buff *skb,
+                                          netdev_features_t features,
+                                          __be16 type)
+{
+       return features;
+}
+#endif
+
 static netdev_features_t harmonize_features(struct sk_buff *skb,
        netdev_features_t features)
 {
        int tmp;
+       __be16 type;
+
+       type = skb_network_protocol(skb, &tmp);
+       features = net_mpls_features(skb, features, type);
 
        if (skb->ip_summed != CHECKSUM_NONE &&
-           !can_checksum_protocol(features, skb_network_protocol(skb, &tmp))) {
+           !can_checksum_protocol(features, type)) {
                features &= ~NETIF_F_ALL_CSUM;
        } else if (illegal_highdma(skb->dev, skb)) {
                features &= ~NETIF_F_SG;
@@ -5689,10 +5721,6 @@ 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, GFP_KERNEL);
-
                /*
                 *      Flush the unicast and multicast chains
                 */
@@ -5702,6 +5730,10 @@ static void rollback_registered_many(struct list_head *head)
                if (dev->netdev_ops->ndo_uninit)
                        dev->netdev_ops->ndo_uninit(dev);
 
+               if (!dev->rtnl_link_ops ||
+                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
+                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
+
                /* Notifier chain MUST detach us all upper devices. */
                WARN_ON(netdev_has_any_upper_dev(dev));
 
@@ -5927,10 +5959,7 @@ static void netdev_init_one_queue(struct net_device *dev,
 
 static void netif_free_tx_queues(struct net_device *dev)
 {
-       if (is_vmalloc_addr(dev->_tx))
-               vfree(dev->_tx);
-       else
-               kfree(dev->_tx);
+       kvfree(dev->_tx);
 }
 
 static int netif_alloc_netdev_queues(struct net_device *dev)
@@ -6404,10 +6433,7 @@ void netdev_freemem(struct net_device *dev)
 {
        char *addr = (char *)dev - dev->padded;
 
-       if (is_vmalloc_addr(addr))
-               vfree(addr);
-       else
-               kfree(addr);
+       kvfree(addr);
 }
 
 /**
@@ -6512,11 +6538,6 @@ free_all:
 
 free_pcpu:
        free_percpu(dev->pcpu_refcnt);
-       netif_free_tx_queues(dev);
-#ifdef CONFIG_SYSFS
-       kfree(dev->_rx);
-#endif
-
 free_dev:
        netdev_freemem(dev);
        return NULL;
@@ -6613,6 +6634,9 @@ EXPORT_SYMBOL(unregister_netdevice_queue);
 /**
  *     unregister_netdevice_many - unregister many devices
  *     @head: list of devices
+ *
+ *  Note: As most callers use a stack allocated list_head,
+ *  we force a list_del() to make sure stack wont be corrupted later.
  */
 void unregister_netdevice_many(struct list_head *head)
 {
@@ -6622,6 +6646,7 @@ void unregister_netdevice_many(struct list_head *head)
                rollback_registered_many(head);
                list_for_each_entry(dev, head, unreg_list)
                        net_set_todo(dev);
+               list_del(head);
        }
 }
 EXPORT_SYMBOL(unregister_netdevice_many);
@@ -7077,7 +7102,6 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
                }
        }
        unregister_netdevice_many(&dev_kill_list);
-       list_del(&dev_kill_list);
        rtnl_unlock();
 }