]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/net/macvlan.c
macvlan: Introduce 'passthru' mode to takeover the underlying device
[net-next-2.6.git] / drivers / net / macvlan.c
index 0fc9dc7f20db02da7ac784c193b940453b7eccd7..6ed577b065df136a18af5ee483810773ca4f43f8 100644 (file)
@@ -38,6 +38,7 @@ struct macvlan_port {
        struct hlist_head       vlan_hash[MACVLAN_HASH_SIZE];
        struct list_head        vlans;
        struct rcu_head         rcu;
+       bool                    passthru;
 };
 
 #define macvlan_port_get_rcu(dev) \
@@ -169,6 +170,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
                        macvlan_broadcast(skb, port, NULL,
                                          MACVLAN_MODE_PRIVATE |
                                          MACVLAN_MODE_VEPA    |
+                                         MACVLAN_MODE_PASSTHRU|
                                          MACVLAN_MODE_BRIDGE);
                else if (src->mode == MACVLAN_MODE_VEPA)
                        /* flood to everyone except source */
@@ -185,7 +187,10 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
                return skb;
        }
 
-       vlan = macvlan_hash_lookup(port, eth->h_dest);
+       if (port->passthru)
+               vlan = list_first_entry(&port->vlans, struct macvlan_dev, list);
+       else
+               vlan = macvlan_hash_lookup(port, eth->h_dest);
        if (vlan == NULL)
                return skb;
 
@@ -243,18 +248,22 @@ xmit_world:
 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);
        unsigned int len = skb->len;
        int ret;
+       const struct macvlan_dev *vlan = netdev_priv(dev);
 
        ret = macvlan_queue_xmit(skb, dev);
        if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
-               txq->tx_packets++;
-               txq->tx_bytes += len;
-       } else
-               txq->tx_dropped++;
+               struct macvlan_pcpu_stats *pcpu_stats;
 
+               pcpu_stats = this_cpu_ptr(vlan->pcpu_stats);
+               u64_stats_update_begin(&pcpu_stats->syncp);
+               pcpu_stats->tx_packets++;
+               pcpu_stats->tx_bytes += len;
+               u64_stats_update_end(&pcpu_stats->syncp);
+       } else {
+               this_cpu_inc(vlan->pcpu_stats->tx_dropped);
+       }
        return ret;
 }
 EXPORT_SYMBOL_GPL(macvlan_start_xmit);
@@ -284,6 +293,11 @@ static int macvlan_open(struct net_device *dev)
        struct net_device *lowerdev = vlan->lowerdev;
        int err;
 
+       if (vlan->port->passthru) {
+               dev_set_promiscuity(lowerdev, 1);
+               goto hash_add;
+       }
+
        err = -EBUSY;
        if (macvlan_addr_busy(vlan->port, dev->dev_addr))
                goto out;
@@ -296,6 +310,8 @@ static int macvlan_open(struct net_device *dev)
                if (err < 0)
                        goto del_unicast;
        }
+
+hash_add:
        macvlan_hash_add(vlan);
        return 0;
 
@@ -310,12 +326,18 @@ static int macvlan_stop(struct net_device *dev)
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct net_device *lowerdev = vlan->lowerdev;
 
+       if (vlan->port->passthru) {
+               dev_set_promiscuity(lowerdev, -1);
+               goto hash_del;
+       }
+
        dev_mc_unsync(lowerdev, dev);
        if (dev->flags & IFF_ALLMULTI)
                dev_set_allmulti(lowerdev, -1);
 
        dev_uc_del(lowerdev, dev->dev_addr);
 
+hash_del:
        macvlan_hash_del(vlan);
        return 0;
 }
@@ -414,14 +436,15 @@ 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->features           |= NETIF_F_LLTX;
        dev->gso_max_size       = lowerdev->gso_max_size;
        dev->iflink             = lowerdev->ifindex;
        dev->hard_header_len    = lowerdev->hard_header_len;
 
        macvlan_set_lockdep_class(dev);
 
-       vlan->rx_stats = alloc_percpu(struct macvlan_rx_stats);
-       if (!vlan->rx_stats)
+       vlan->pcpu_stats = alloc_percpu(struct macvlan_pcpu_stats);
+       if (!vlan->pcpu_stats)
                return -ENOMEM;
 
        return 0;
@@ -431,7 +454,7 @@ static void macvlan_uninit(struct net_device *dev)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
 
-       free_percpu(vlan->rx_stats);
+       free_percpu(vlan->pcpu_stats);
 }
 
 static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev,
@@ -439,33 +462,38 @@ static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev,
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
 
-       dev_txq_stats_fold(dev, stats);
-
-       if (vlan->rx_stats) {
-               struct macvlan_rx_stats *p, accum = {0};
-               u64 rx_packets, rx_bytes, rx_multicast;
+       if (vlan->pcpu_stats) {
+               struct macvlan_pcpu_stats *p;
+               u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
+               u32 rx_errors = 0, tx_dropped = 0;
                unsigned int start;
                int i;
 
                for_each_possible_cpu(i) {
-                       p = per_cpu_ptr(vlan->rx_stats, i);
+                       p = per_cpu_ptr(vlan->pcpu_stats, i);
                        do {
                                start = u64_stats_fetch_begin_bh(&p->syncp);
                                rx_packets      = p->rx_packets;
                                rx_bytes        = p->rx_bytes;
                                rx_multicast    = p->rx_multicast;
+                               tx_packets      = p->tx_packets;
+                               tx_bytes        = p->tx_bytes;
                        } while (u64_stats_fetch_retry_bh(&p->syncp, start));
-                       accum.rx_packets        += rx_packets;
-                       accum.rx_bytes          += rx_bytes;
-                       accum.rx_multicast      += rx_multicast;
-                       /* rx_errors is an ulong, updated without syncp protection */
-                       accum.rx_errors         += p->rx_errors;
+
+                       stats->rx_packets       += rx_packets;
+                       stats->rx_bytes         += rx_bytes;
+                       stats->multicast        += rx_multicast;
+                       stats->tx_packets       += tx_packets;
+                       stats->tx_bytes         += tx_bytes;
+                       /* rx_errors & tx_dropped are u32, updated
+                        * without syncp protection.
+                        */
+                       rx_errors       += p->rx_errors;
+                       tx_dropped      += p->tx_dropped;
                }
-               stats->rx_packets = accum.rx_packets;
-               stats->rx_bytes   = accum.rx_bytes;
-               stats->rx_errors  = accum.rx_errors;
-               stats->rx_dropped = accum.rx_errors;
-               stats->multicast  = accum.rx_multicast;
+               stats->rx_errors        = rx_errors;
+               stats->rx_dropped       = rx_errors;
+               stats->tx_dropped       = tx_dropped;
        }
        return stats;
 }
@@ -549,6 +577,7 @@ static int macvlan_port_create(struct net_device *dev)
        if (port == NULL)
                return -ENOMEM;
 
+       port->passthru = false;
        port->dev = dev;
        INIT_LIST_HEAD(&port->vlans);
        for (i = 0; i < MACVLAN_HASH_SIZE; i++)
@@ -593,6 +622,7 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
                case MACVLAN_MODE_PRIVATE:
                case MACVLAN_MODE_VEPA:
                case MACVLAN_MODE_BRIDGE:
+               case MACVLAN_MODE_PASSTHRU:
                        break;
                default:
                        return -EINVAL;
@@ -601,25 +631,6 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
        return 0;
 }
 
-static int macvlan_get_tx_queues(struct net *net,
-                                struct nlattr *tb[],
-                                unsigned int *num_tx_queues,
-                                unsigned int *real_num_tx_queues)
-{
-       struct net_device *real_dev;
-
-       if (!tb[IFLA_LINK])
-               return -EINVAL;
-
-       real_dev = __dev_get_by_index(net, nla_get_u32(tb[IFLA_LINK]));
-       if (!real_dev)
-               return -ENODEV;
-
-       *num_tx_queues      = real_dev->num_tx_queues;
-       *real_num_tx_queues = real_dev->real_num_tx_queues;
-       return 0;
-}
-
 int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
                           struct nlattr *tb[], struct nlattr *data[],
                           int (*receive)(struct sk_buff *skb),
@@ -661,6 +672,10 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
        }
        port = macvlan_port_get(lowerdev);
 
+       /* Only 1 macvlan device can be created in passthru mode */
+       if (port->passthru)
+               return -EINVAL;
+
        vlan->lowerdev = lowerdev;
        vlan->dev      = dev;
        vlan->port     = port;
@@ -671,6 +686,13 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
        if (data && data[IFLA_MACVLAN_MODE])
                vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
 
+       if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
+               if (!list_empty(&port->vlans))
+                       return -EINVAL;
+               port->passthru = true;
+               memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN);
+       }
+
        err = register_netdevice(dev);
        if (err < 0)
                goto destroy_port;
@@ -743,7 +765,6 @@ int macvlan_link_register(struct rtnl_link_ops *ops)
 {
        /* common fields */
        ops->priv_size          = sizeof(struct macvlan_dev);
-       ops->get_tx_queues      = macvlan_get_tx_queues;
        ops->validate           = macvlan_validate;
        ops->maxtype            = IFLA_MACVLAN_MAX;
        ops->policy             = macvlan_policy;