Merge tag 'master-2014-09-23' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil...
[firefly-linux-kernel-4.4.55.git] / drivers / net / macvlan.c
index ef8a5c20236a007b976f98e4c787b5520f9affd8..726edabff26b710d06b622782a71092430b184b4 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/netpoll.h>
 
 #define MACVLAN_HASH_SIZE      (1 << BITS_PER_BYTE)
+#define MACVLAN_BC_QUEUE_LEN   1000
 
 struct macvlan_port {
        struct net_device       *dev;
@@ -45,10 +46,9 @@ struct macvlan_port {
        struct sk_buff_head     bc_queue;
        struct work_struct      bc_work;
        bool                    passthru;
+       int                     count;
 };
 
-#define MACVLAN_PORT_IS_EMPTY(port)    list_empty(&port->vlans)
-
 struct macvlan_skb_cb {
        const struct macvlan_dev *src;
 };
@@ -249,7 +249,7 @@ static void macvlan_broadcast_enqueue(struct macvlan_port *port,
                goto err;
 
        spin_lock(&port->bc_queue.lock);
-       if (skb_queue_len(&port->bc_queue) < skb->dev->tx_queue_len) {
+       if (skb_queue_len(&port->bc_queue) < MACVLAN_BC_QUEUE_LEN) {
                __skb_queue_tail(&port->bc_queue, nskb);
                err = 0;
        }
@@ -667,7 +667,8 @@ static void macvlan_uninit(struct net_device *dev)
 
        free_percpu(vlan->pcpu_stats);
 
-       if (MACVLAN_PORT_IS_EMPTY(port))
+       port->count -= 1;
+       if (!port->count)
                macvlan_port_destroy(port->dev);
 }
 
@@ -739,7 +740,10 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        struct macvlan_dev *vlan = netdev_priv(dev);
        int err = -EINVAL;
 
-       if (!vlan->port->passthru)
+       /* Support unicast filter only on passthru devices.
+        * Multicast filter should be allowed on all devices.
+        */
+       if (!vlan->port->passthru && is_unicast_ether_addr(addr))
                return -EOPNOTSUPP;
 
        if (flags & NLM_F_REPLACE)
@@ -760,7 +764,10 @@ static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
        struct macvlan_dev *vlan = netdev_priv(dev);
        int err = -EINVAL;
 
-       if (!vlan->port->passthru)
+       /* Support unicast filter only on passthru devices.
+        * Multicast filter should be allowed on all devices.
+        */
+       if (!vlan->port->passthru && is_unicast_ether_addr(addr))
                return -EOPNOTSUPP;
 
        if (is_unicast_ether_addr(addr))
@@ -800,6 +807,7 @@ static netdev_features_t macvlan_fix_features(struct net_device *dev,
                                             features,
                                             mask);
        features |= ALWAYS_ON_FEATURES;
+       features &= ~NETIF_F_NETNS_LOCAL;
 
        return features;
 }
@@ -1020,12 +1028,13 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
                vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
 
        if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
-               if (!MACVLAN_PORT_IS_EMPTY(port))
+               if (port->count)
                        return -EINVAL;
                port->passthru = true;
                eth_hw_addr_inherit(dev, lowerdev);
        }
 
+       port->count += 1;
        err = register_netdevice(dev);
        if (err < 0)
                goto destroy_port;
@@ -1043,7 +1052,8 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
 unregister_netdev:
        unregister_netdevice(dev);
 destroy_port:
-       if (MACVLAN_PORT_IS_EMPTY(port))
+       port->count -= 1;
+       if (!port->count)
                macvlan_port_destroy(lowerdev);
 
        return err;