gpio: adp5520: rename common defines and typos
[safe/jmp/linux-2.6] / drivers / net / macvlan.c
index 1e7faf9..21a9c9a 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/if_link.h>
 #include <linux/if_macvlan.h>
 #include <net/rtnetlink.h>
+#include <net/xfrm.h>
 
 #define MACVLAN_HASH_SIZE      (1 << BITS_PER_BYTE)
 
@@ -59,6 +60,7 @@ struct macvlan_dev {
        struct macvlan_port     *port;
        struct net_device       *lowerdev;
        struct macvlan_rx_stats *rx_stats;
+       enum macvlan_mode       mode;
 };
 
 
@@ -134,11 +136,14 @@ static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
 }
 
 static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev,
-                                const struct ethhdr *eth)
+                                const struct ethhdr *eth, bool local)
 {
        if (!skb)
                return NET_RX_DROP;
 
+       if (local)
+               return dev_forward_skb(dev, skb);
+
        skb->dev = dev;
        if (!compare_ether_addr_64bits(eth->h_dest,
                                       dev->broadcast))
@@ -150,7 +155,9 @@ static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev,
 }
 
 static void macvlan_broadcast(struct sk_buff *skb,
-                             const struct macvlan_port *port)
+                             const struct macvlan_port *port,
+                             struct net_device *src,
+                             enum macvlan_mode mode)
 {
        const struct ethhdr *eth = eth_hdr(skb);
        const struct macvlan_dev *vlan;
@@ -164,8 +171,12 @@ static void macvlan_broadcast(struct sk_buff *skb,
 
        for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
                hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) {
+                       if (vlan->dev == src || !(vlan->mode & mode))
+                               continue;
+
                        nskb = skb_clone(skb, GFP_ATOMIC);
-                       err = macvlan_broadcast_one(nskb, vlan->dev, eth);
+                       err = macvlan_broadcast_one(nskb, vlan->dev, eth,
+                                        mode == MACVLAN_MODE_BRIDGE);
                        macvlan_count_rx(vlan, skb->len + ETH_HLEN,
                                         err == NET_RX_SUCCESS, 1);
                }
@@ -178,6 +189,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
        const struct ethhdr *eth = eth_hdr(skb);
        const struct macvlan_port *port;
        const struct macvlan_dev *vlan;
+       const struct macvlan_dev *src;
        struct net_device *dev;
        unsigned int len;
 
@@ -186,7 +198,25 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
                return skb;
 
        if (is_multicast_ether_addr(eth->h_dest)) {
-               macvlan_broadcast(skb, port);
+               src = macvlan_hash_lookup(port, eth->h_source);
+               if (!src)
+                       /* frame comes from an external address */
+                       macvlan_broadcast(skb, port, NULL,
+                                         MACVLAN_MODE_PRIVATE |
+                                         MACVLAN_MODE_VEPA    |
+                                         MACVLAN_MODE_BRIDGE);
+               else if (src->mode == MACVLAN_MODE_VEPA)
+                       /* flood to everyone except source */
+                       macvlan_broadcast(skb, port, src->dev,
+                                         MACVLAN_MODE_VEPA |
+                                         MACVLAN_MODE_BRIDGE);
+               else if (src->mode == MACVLAN_MODE_BRIDGE)
+                       /*
+                        * flood only to VEPA ports, bridge ports
+                        * already saw the frame on the way out.
+                        */
+                       macvlan_broadcast(skb, port, src->dev,
+                                         MACVLAN_MODE_VEPA);
                return skb;
        }
 
@@ -212,18 +242,46 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
        return NULL;
 }
 
+static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       const struct macvlan_dev *vlan = netdev_priv(dev);
+       const struct macvlan_port *port = vlan->port;
+       const struct macvlan_dev *dest;
+
+       if (vlan->mode == MACVLAN_MODE_BRIDGE) {
+               const struct ethhdr *eth = (void *)skb->data;
+
+               /* send to other bridge ports directly */
+               if (is_multicast_ether_addr(eth->h_dest)) {
+                       macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE);
+                       goto xmit_world;
+               }
+
+               dest = macvlan_hash_lookup(port, eth->h_dest);
+               if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {
+                       unsigned int length = skb->len + ETH_HLEN;
+                       int ret = dev_forward_skb(dest->dev, skb);
+                       macvlan_count_rx(dest, length,
+                                        ret == NET_RX_SUCCESS, 0);
+
+                       return NET_XMIT_SUCCESS;
+               }
+       }
+
+xmit_world:
+       skb->dev = vlan->lowerdev;
+       return dev_queue_xmit(skb);
+}
+
 static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
 {
        int i = skb_get_queue_mapping(skb);
        struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
-       const struct macvlan_dev *vlan = netdev_priv(dev);
        unsigned int len = skb->len;
        int ret;
 
-       skb->dev = vlan->lowerdev;
-       ret = dev_queue_xmit(skb);
-
+       ret = macvlan_queue_xmit(skb, dev);
        if (likely(ret == NET_XMIT_SUCCESS)) {
                txq->tx_packets++;
                txq->tx_bytes += len;
@@ -388,6 +446,7 @@ static int macvlan_init(struct net_device *dev)
        dev->state              = (dev->state & ~MACVLAN_STATE_MASK) |
                                  (lowerdev->state & MACVLAN_STATE_MASK);
        dev->features           = lowerdev->features & MACVLAN_FEATURES;
+       dev->gso_max_size       = lowerdev->gso_max_size;
        dev->iflink             = lowerdev->ifindex;
        dev->hard_header_len    = lowerdev->hard_header_len;
 
@@ -523,25 +582,6 @@ static void macvlan_port_destroy(struct net_device *dev)
        kfree(port);
 }
 
-static void macvlan_transfer_operstate(struct net_device *dev)
-{
-       struct macvlan_dev *vlan = netdev_priv(dev);
-       const struct net_device *lowerdev = vlan->lowerdev;
-
-       if (lowerdev->operstate == IF_OPER_DORMANT)
-               netif_dormant_on(dev);
-       else
-               netif_dormant_off(dev);
-
-       if (netif_carrier_ok(lowerdev)) {
-               if (!netif_carrier_ok(dev))
-                       netif_carrier_on(dev);
-       } else {
-               if (netif_carrier_ok(dev))
-                       netif_carrier_off(dev);
-       }
-}
-
 static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
 {
        if (tb[IFLA_ADDRESS]) {
@@ -550,6 +590,17 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
                if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
                        return -EADDRNOTAVAIL;
        }
+
+       if (data && data[IFLA_MACVLAN_MODE]) {
+               switch (nla_get_u32(data[IFLA_MACVLAN_MODE])) {
+               case MACVLAN_MODE_PRIVATE:
+               case MACVLAN_MODE_VEPA:
+               case MACVLAN_MODE_BRIDGE:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
        return 0;
 }
 
@@ -614,12 +665,16 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev,
        vlan->dev      = dev;
        vlan->port     = port;
 
+       vlan->mode     = MACVLAN_MODE_VEPA;
+       if (data && data[IFLA_MACVLAN_MODE])
+               vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
+
        err = register_netdevice(dev);
        if (err < 0)
                return err;
 
        list_add_tail(&vlan->list, &port->vlans);
-       macvlan_transfer_operstate(dev);
+       netif_stacked_transfer_operstate(lowerdev, dev);
        return 0;
 }
 
@@ -635,6 +690,36 @@ static void macvlan_dellink(struct net_device *dev, struct list_head *head)
                macvlan_port_destroy(port->dev);
 }
 
+static int macvlan_changelink(struct net_device *dev,
+               struct nlattr *tb[], struct nlattr *data[])
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       if (data && data[IFLA_MACVLAN_MODE])
+               vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
+       return 0;
+}
+
+static size_t macvlan_get_size(const struct net_device *dev)
+{
+       return nla_total_size(4);
+}
+
+static int macvlan_fill_info(struct sk_buff *skb,
+                               const struct net_device *dev)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+
+       NLA_PUT_U32(skb, IFLA_MACVLAN_MODE, vlan->mode);
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = {
+       [IFLA_MACVLAN_MODE] = { .type = NLA_U32 },
+};
+
 static struct rtnl_link_ops macvlan_link_ops __read_mostly = {
        .kind           = "macvlan",
        .priv_size      = sizeof(struct macvlan_dev),
@@ -643,6 +728,11 @@ static struct rtnl_link_ops macvlan_link_ops __read_mostly = {
        .validate       = macvlan_validate,
        .newlink        = macvlan_newlink,
        .dellink        = macvlan_dellink,
+       .maxtype        = IFLA_MACVLAN_MAX,
+       .policy         = macvlan_policy,
+       .changelink     = macvlan_changelink,
+       .get_size       = macvlan_get_size,
+       .fill_info      = macvlan_fill_info,
 };
 
 static int macvlan_device_event(struct notifier_block *unused,
@@ -659,11 +749,13 @@ static int macvlan_device_event(struct notifier_block *unused,
        switch (event) {
        case NETDEV_CHANGE:
                list_for_each_entry(vlan, &port->vlans, list)
-                       macvlan_transfer_operstate(vlan->dev);
+                       netif_stacked_transfer_operstate(vlan->lowerdev,
+                                                        vlan->dev);
                break;
        case NETDEV_FEAT_CHANGE:
                list_for_each_entry(vlan, &port->vlans, list) {
                        vlan->dev->features = dev->features & MACVLAN_FEATURES;
+                       vlan->dev->gso_max_size = dev->gso_max_size;
                        netdev_features_change(vlan->dev);
                }
                break;