]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/macvlan.c
Revert "net: phy: Set the driver when registering an MDIO bus device"
[karo-tx-linux.git] / drivers / net / macvlan.c
index d53e299ae1d97ca39c4f3af511b97c5dacb32827..958df383068a534d01bf9632463a22689184ea50 100644 (file)
 #include <linux/if_link.h>
 #include <linux/if_macvlan.h>
 #include <linux/hash.h>
+#include <linux/workqueue.h>
 #include <net/rtnetlink.h>
 #include <net/xfrm.h>
+#include <linux/netpoll.h>
 
 #define MACVLAN_HASH_SIZE      (1 << BITS_PER_BYTE)
 
@@ -40,10 +42,19 @@ struct macvlan_port {
        struct hlist_head       vlan_hash[MACVLAN_HASH_SIZE];
        struct list_head        vlans;
        struct rcu_head         rcu;
+       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;
+};
+
+#define MACVLAN_SKB_CB(__skb) ((struct macvlan_skb_cb *)&((__skb)->cb[0]))
+
 static void macvlan_port_destroy(struct net_device *dev);
 
 static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev)
@@ -120,7 +131,7 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
        struct net_device *dev = vlan->dev;
 
        if (local)
-               return dev_forward_skb(dev, skb);
+               return __dev_forward_skb(dev, skb);
 
        skb->dev = dev;
        if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
@@ -128,7 +139,7 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
        else
                skb->pkt_type = PACKET_MULTICAST;
 
-       return netif_rx(skb);
+       return 0;
 }
 
 static u32 macvlan_hash_mix(const struct macvlan_dev *vlan)
@@ -175,32 +186,32 @@ static void macvlan_broadcast(struct sk_buff *skb,
                        if (likely(nskb))
                                err = macvlan_broadcast_one(
                                        nskb, vlan, eth,
-                                       mode == MACVLAN_MODE_BRIDGE);
+                                       mode == MACVLAN_MODE_BRIDGE) ?:
+                                     netif_rx_ni(nskb);
                        macvlan_count_rx(vlan, skb->len + ETH_HLEN,
                                         err == NET_RX_SUCCESS, 1);
                }
        }
 }
 
-/* called under rcu_read_lock() from netif_receive_skb */
-static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
+static void macvlan_process_broadcast(struct work_struct *w)
 {
-       struct macvlan_port *port;
-       struct sk_buff *skb = *pskb;
-       const struct ethhdr *eth = eth_hdr(skb);
-       const struct macvlan_dev *vlan;
-       const struct macvlan_dev *src;
-       struct net_device *dev;
-       unsigned int len = 0;
-       int ret = NET_RX_DROP;
+       struct macvlan_port *port = container_of(w, struct macvlan_port,
+                                                bc_work);
+       struct sk_buff *skb;
+       struct sk_buff_head list;
+
+       skb_queue_head_init(&list);
+
+       spin_lock_bh(&port->bc_queue.lock);
+       skb_queue_splice_tail_init(&port->bc_queue, &list);
+       spin_unlock_bh(&port->bc_queue.lock);
+
+       while ((skb = __skb_dequeue(&list))) {
+               const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src;
+
+               rcu_read_lock();
 
-       port = macvlan_port_get_rcu(skb->dev);
-       if (is_multicast_ether_addr(eth->h_dest)) {
-               skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
-               if (!skb)
-                       return RX_HANDLER_CONSUMED;
-               eth = eth_hdr(skb);
-               src = macvlan_hash_lookup(port, eth->h_source);
                if (!src)
                        /* frame comes from an external address */
                        macvlan_broadcast(skb, port, NULL,
@@ -213,20 +224,80 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
                        macvlan_broadcast(skb, port, src->dev,
                                          MACVLAN_MODE_VEPA |
                                          MACVLAN_MODE_BRIDGE);
-               else if (src->mode == MACVLAN_MODE_BRIDGE)
+               else
                        /*
                         * flood only to VEPA ports, bridge ports
                         * already saw the frame on the way out.
                         */
                        macvlan_broadcast(skb, port, src->dev,
                                          MACVLAN_MODE_VEPA);
-               else {
+
+               rcu_read_unlock();
+
+               kfree_skb(skb);
+       }
+}
+
+static void macvlan_broadcast_enqueue(struct macvlan_port *port,
+                                     struct sk_buff *skb)
+{
+       struct sk_buff *nskb;
+       int err = -ENOMEM;
+
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       if (!nskb)
+               goto err;
+
+       spin_lock(&port->bc_queue.lock);
+       if (skb_queue_len(&port->bc_queue) < skb->dev->tx_queue_len) {
+               __skb_queue_tail(&port->bc_queue, nskb);
+               err = 0;
+       }
+       spin_unlock(&port->bc_queue.lock);
+
+       if (err)
+               goto free_nskb;
+
+       schedule_work(&port->bc_work);
+       return;
+
+free_nskb:
+       kfree_skb(nskb);
+err:
+       atomic_long_inc(&skb->dev->rx_dropped);
+}
+
+/* called under rcu_read_lock() from netif_receive_skb */
+static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
+{
+       struct macvlan_port *port;
+       struct sk_buff *skb = *pskb;
+       const struct ethhdr *eth = eth_hdr(skb);
+       const struct macvlan_dev *vlan;
+       const struct macvlan_dev *src;
+       struct net_device *dev;
+       unsigned int len = 0;
+       int ret = NET_RX_DROP;
+
+       port = macvlan_port_get_rcu(skb->dev);
+       if (is_multicast_ether_addr(eth->h_dest)) {
+               skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
+               if (!skb)
+                       return RX_HANDLER_CONSUMED;
+               eth = eth_hdr(skb);
+               src = macvlan_hash_lookup(port, eth->h_source);
+               if (src && src->mode != MACVLAN_MODE_VEPA &&
+                   src->mode != MACVLAN_MODE_BRIDGE) {
                        /* forward to original port. */
                        vlan = src;
-                       ret = macvlan_broadcast_one(skb, vlan, eth, 0);
+                       ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?:
+                             netif_rx(skb);
                        goto out;
                }
 
+               MACVLAN_SKB_CB(skb)->src = src;
+               macvlan_broadcast_enqueue(port, skb);
+
                return RX_HANDLER_PASS;
        }
 
@@ -287,12 +358,26 @@ xmit_world:
        return dev_queue_xmit(skb);
 }
 
+static inline netdev_tx_t macvlan_netpoll_send_skb(struct macvlan_dev *vlan, struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       if (vlan->netpoll)
+               netpoll_send_skb(vlan->netpoll, skb);
+#else
+       BUG();
+#endif
+       return NETDEV_TX_OK;
+}
+
 static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
 {
        unsigned int len = skb->len;
        int ret;
-       const struct macvlan_dev *vlan = netdev_priv(dev);
+       struct macvlan_dev *vlan = netdev_priv(dev);
+
+       if (unlikely(netpoll_tx_running(dev)))
+               return macvlan_netpoll_send_skb(vlan, skb);
 
        if (vlan->fwd_priv) {
                skb->dev = vlan->lowerdev;
@@ -424,35 +509,49 @@ hash_del:
        return 0;
 }
 
-static int macvlan_set_mac_address(struct net_device *dev, void *p)
+static int macvlan_sync_address(struct net_device *dev, unsigned char *addr)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct net_device *lowerdev = vlan->lowerdev;
-       struct sockaddr *addr = p;
        int err;
 
-       if (!is_valid_ether_addr(addr->sa_data))
-               return -EADDRNOTAVAIL;
-
        if (!(dev->flags & IFF_UP)) {
                /* Just copy in the new address */
-               memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+               ether_addr_copy(dev->dev_addr, addr);
        } else {
                /* Rehash and update the device filters */
-               if (macvlan_addr_busy(vlan->port, addr->sa_data))
+               if (macvlan_addr_busy(vlan->port, addr))
                        return -EBUSY;
 
-               err = dev_uc_add(lowerdev, addr->sa_data);
-               if (err)
-                       return err;
+               if (!vlan->port->passthru) {
+                       err = dev_uc_add(lowerdev, addr);
+                       if (err)
+                               return err;
 
-               dev_uc_del(lowerdev, dev->dev_addr);
+                       dev_uc_del(lowerdev, dev->dev_addr);
+               }
 
-               macvlan_hash_change_addr(vlan, addr->sa_data);
+               macvlan_hash_change_addr(vlan, addr);
        }
        return 0;
 }
 
+static int macvlan_set_mac_address(struct net_device *dev, void *p)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
+               dev_set_mac_address(vlan->lowerdev, addr);
+               return 0;
+       }
+
+       return macvlan_sync_address(dev, addr->sa_data);
+}
+
 static void macvlan_change_rx_flags(struct net_device *dev, int change)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
@@ -567,8 +666,7 @@ static void macvlan_uninit(struct net_device *dev)
 
        free_percpu(vlan->pcpu_stats);
 
-       port->count -= 1;
-       if (!port->count)
+       if (MACVLAN_PORT_IS_EMPTY(port))
                macvlan_port_destroy(port->dev);
 }
 
@@ -705,6 +803,50 @@ static netdev_features_t macvlan_fix_features(struct net_device *dev,
        return features;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void macvlan_dev_poll_controller(struct net_device *dev)
+{
+       return;
+}
+
+static int macvlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct net_device *real_dev = vlan->lowerdev;
+       struct netpoll *netpoll;
+       int err = 0;
+
+       netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
+       err = -ENOMEM;
+       if (!netpoll)
+               goto out;
+
+       err = __netpoll_setup(netpoll, real_dev);
+       if (err) {
+               kfree(netpoll);
+               goto out;
+       }
+
+       vlan->netpoll = netpoll;
+
+out:
+       return err;
+}
+
+static void macvlan_dev_netpoll_cleanup(struct net_device *dev)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct netpoll *netpoll = vlan->netpoll;
+
+       if (!netpoll)
+               return;
+
+       vlan->netpoll = NULL;
+
+       __netpoll_free_async(netpoll);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
 static const struct ethtool_ops macvlan_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_settings           = macvlan_ethtool_get_settings,
@@ -730,6 +872,11 @@ static const struct net_device_ops macvlan_netdev_ops = {
        .ndo_fdb_del            = macvlan_fdb_del,
        .ndo_fdb_dump           = ndo_dflt_fdb_dump,
        .ndo_get_lock_subclass  = macvlan_get_nest_level,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = macvlan_dev_poll_controller,
+       .ndo_netpoll_setup      = macvlan_dev_netpoll_setup,
+       .ndo_netpoll_cleanup    = macvlan_dev_netpoll_cleanup,
+#endif
 };
 
 void macvlan_common_setup(struct net_device *dev)
@@ -770,6 +917,9 @@ static int macvlan_port_create(struct net_device *dev)
        for (i = 0; i < MACVLAN_HASH_SIZE; i++)
                INIT_HLIST_HEAD(&port->vlan_hash[i]);
 
+       skb_queue_head_init(&port->bc_queue);
+       INIT_WORK(&port->bc_work, macvlan_process_broadcast);
+
        err = netdev_rx_handler_register(dev, macvlan_handle_frame, port);
        if (err)
                kfree(port);
@@ -782,6 +932,7 @@ static void macvlan_port_destroy(struct net_device *dev)
 {
        struct macvlan_port *port = macvlan_port_get_rtnl(dev);
 
+       cancel_work_sync(&port->bc_work);
        dev->priv_flags &= ~IFF_MACVLAN_PORT;
        netdev_rx_handler_unregister(dev);
        kfree_rcu(port, rcu);
@@ -868,13 +1019,12 @@ 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 (port->count)
+               if (!MACVLAN_PORT_IS_EMPTY(port))
                        return -EINVAL;
                port->passthru = true;
                eth_hw_addr_inherit(dev, lowerdev);
        }
 
-       port->count += 1;
        err = register_netdevice(dev);
        if (err < 0)
                goto destroy_port;
@@ -892,8 +1042,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
 unregister_netdev:
        unregister_netdevice(dev);
 destroy_port:
-       port->count -= 1;
-       if (!port->count)
+       if (MACVLAN_PORT_IS_EMPTY(port))
                macvlan_port_destroy(lowerdev);
 
        return err;
@@ -1027,6 +1176,25 @@ static int macvlan_device_event(struct notifier_block *unused,
                        vlan->dev->gso_max_size = dev->gso_max_size;
                        netdev_update_features(vlan->dev);
                }
+               break;
+       case NETDEV_CHANGEMTU:
+               list_for_each_entry(vlan, &port->vlans, list) {
+                       if (vlan->dev->mtu <= dev->mtu)
+                               continue;
+                       dev_set_mtu(vlan->dev, dev->mtu);
+               }
+               break;
+       case NETDEV_CHANGEADDR:
+               if (!port->passthru)
+                       return NOTIFY_DONE;
+
+               vlan = list_first_entry_or_null(&port->vlans,
+                                               struct macvlan_dev,
+                                               list);
+
+               if (macvlan_sync_address(vlan->dev, dev->dev_addr))
+                       return NOTIFY_BAD;
+
                break;
        case NETDEV_UNREGISTER:
                /* twiddle thumbs on netns device moves */
@@ -1036,11 +1204,17 @@ static int macvlan_device_event(struct notifier_block *unused,
                list_for_each_entry_safe(vlan, next, &port->vlans, list)
                        vlan->dev->rtnl_link_ops->dellink(vlan->dev, &list_kill);
                unregister_netdevice_many(&list_kill);
-               list_del(&list_kill);
                break;
        case NETDEV_PRE_TYPE_CHANGE:
                /* Forbid underlaying device to change its type. */
                return NOTIFY_BAD;
+
+       case NETDEV_NOTIFY_PEERS:
+       case NETDEV_BONDING_FAILOVER:
+       case NETDEV_RESEND_IGMP:
+               /* Propagate to all vlans */
+               list_for_each_entry(vlan, &port->vlans, list)
+                       call_netdevice_notifiers(event, vlan->dev);
        }
        return NOTIFY_DONE;
 }