macvlan: Use software path for offloaded local, broadcast, and multicast traffic
authorAlexander Duyck <alexander.h.duyck@intel.com>
Tue, 3 Apr 2018 21:16:09 +0000 (17:16 -0400)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Wed, 25 Apr 2018 15:26:19 +0000 (08:26 -0700)
This change makes it so that we use a software path for packets that are
going to be locally switched between two macvlan interfaces on the same
device. In addition we resort to software replication of broadcast and
multicast packets instead of offloading that to hardware.

The general idea is that using the device for east/west traffic local to
the system is extremely inefficient. We can only support up to whatever the
PCIe limit is for any given device so this caps us at somewhere around 20G
for devices supported by ixgbe. This is compounded even further when you
take broadcast and multicast into account as a single 10G port can come to
a crawl as a packet is replicated up to 60+ times in some cases. In order
to get away from that I am implementing changes so that we handle
broadcast/multicast replication and east/west local traffic all in
software.

Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/macvlan.c

index 45793491d4ba34dea2ffa632579ead116d4e9add..ee645bacfe93f4f8dc000d4398271a56216ebc7f 100644 (file)
@@ -1254,7 +1254,7 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface)
                        glort = l2_accel->dglort + 1 + i;
 
                        hw->mac.ops.update_xcast_mode(hw, glort,
-                                                     FM10K_XCAST_MODE_MULTI);
+                                                     FM10K_XCAST_MODE_NONE);
                        fm10k_queue_mac_request(interface, glort,
                                                sdev->dev_addr,
                                                hw->mac.default_vid, true);
@@ -1515,7 +1515,7 @@ static void *fm10k_dfwd_add_station(struct net_device *dev,
 
        if (fm10k_host_mbx_ready(interface)) {
                hw->mac.ops.update_xcast_mode(hw, glort,
-                                             FM10K_XCAST_MODE_MULTI);
+                                             FM10K_XCAST_MODE_NONE);
                fm10k_queue_mac_request(interface, glort, sdev->dev_addr,
                                        hw->mac.default_vid, true);
        }
index 0fbeb2f4871173baedf84b6d09835119e1348f85..4d4fa46a1e7c4b7cc8740c810015b8c8ca0b9a15 100644 (file)
@@ -4219,7 +4219,8 @@ static void ixgbe_setup_psrtype(struct ixgbe_adapter *adapter)
 static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
-       u32 reg_offset, vf_shift;
+       u16 pool = adapter->num_rx_pools;
+       u32 reg_offset, vf_shift, vmolr;
        u32 gcr_ext, vmdctl;
        int i;
 
@@ -4233,6 +4234,13 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
        vmdctl |= IXGBE_VT_CTL_REPLEN;
        IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vmdctl);
 
+       /* accept untagged packets until a vlan tag is
+        * specifically set for the VMDQ queue/pool
+        */
+       vmolr = IXGBE_VMOLR_AUPE;
+       while (pool--)
+               IXGBE_WRITE_REG(hw, IXGBE_VMOLR(VMDQ_P(pool)), vmolr);
+
        vf_shift = VMDQ_P(0) % 32;
        reg_offset = (VMDQ_P(0) >= 32) ? 1 : 0;
 
@@ -5279,28 +5287,6 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter)
        spin_unlock(&adapter->fdir_perfect_lock);
 }
 
-static void ixgbe_macvlan_set_rx_mode(struct net_device *dev, unsigned int pool,
-                                     struct ixgbe_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       u32 vmolr;
-
-       /* No unicast promiscuous support for VMDQ devices. */
-       vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(pool));
-       vmolr |= (IXGBE_VMOLR_ROMPE | IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE);
-
-       /* clear the affected bit */
-       vmolr &= ~IXGBE_VMOLR_MPE;
-
-       if (dev->flags & IFF_ALLMULTI) {
-               vmolr |= IXGBE_VMOLR_MPE;
-       } else {
-               vmolr |= IXGBE_VMOLR_ROMPE;
-               hw->mac.ops.update_mc_addr_list(hw, dev);
-       }
-       IXGBE_WRITE_REG(hw, IXGBE_VMOLR(pool), vmolr);
-}
-
 /**
  * ixgbe_clean_rx_ring - Free Rx Buffers per Queue
  * @rx_ring: ring to free buffers from
@@ -5384,10 +5370,8 @@ static int ixgbe_fwd_ring_up(struct net_device *vdev,
         */
        err = ixgbe_add_mac_filter(adapter, vdev->dev_addr,
                                   VMDQ_P(accel->pool));
-       if (err >= 0) {
-               ixgbe_macvlan_set_rx_mode(vdev, accel->pool, adapter);
+       if (err >= 0)
                return 0;
-       }
 
        for (i = 0; i < adapter->num_rx_queues_per_pool; i++)
                adapter->rx_ring[baseq + i]->netdev = NULL;
@@ -9832,9 +9816,6 @@ static void ixgbe_fwd_del(struct net_device *pdev, void *priv)
        ixgbe_del_mac_filter(adapter, accel->netdev->dev_addr,
                             VMDQ_P(accel->pool));
 
-       /* disable ability to receive packets for this pool */
-       IXGBE_WRITE_REG(&adapter->hw, IXGBE_VMOLR(accel->pool), 0);
-
        /* Allow remaining Rx packets to get flushed out of the
         * Rx FIFO before we drop the netdev for the ring.
         */
index 7ddc94ff4109c689e79fcbf4f106d0ffd7f2e6fd..adde8fc45588ba12c82bd79cf830e3ed99b907e3 100644 (file)
@@ -514,6 +514,7 @@ 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;
+       void *accel_priv = NULL;
 
        if (vlan->mode == MACVLAN_MODE_BRIDGE) {
                const struct ethhdr *eth = (void *)skb->data;
@@ -533,9 +534,14 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
                }
        }
 
+       /* For packets that are non-multicast and not bridged we will pass
+        * the necessary information so that the lowerdev can distinguish
+        * the source of the packets via the accel_priv value.
+        */
+       accel_priv = vlan->accel_priv;
 xmit_world:
        skb->dev = vlan->lowerdev;
-       return dev_queue_xmit(skb);
+       return dev_queue_xmit_accel(skb, accel_priv);
 }
 
 static inline netdev_tx_t macvlan_netpoll_send_skb(struct macvlan_dev *vlan, struct sk_buff *skb)
@@ -552,19 +558,14 @@ static inline netdev_tx_t macvlan_netpoll_send_skb(struct macvlan_dev *vlan, str
 static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
 {
+       struct macvlan_dev *vlan = netdev_priv(dev);
        unsigned int len = skb->len;
        int ret;
-       struct macvlan_dev *vlan = netdev_priv(dev);
 
        if (unlikely(netpoll_tx_running(dev)))
                return macvlan_netpoll_send_skb(vlan, skb);
 
-       if (vlan->accel_priv) {
-               skb->dev = vlan->lowerdev;
-               ret = dev_queue_xmit_accel(skb, vlan->accel_priv);
-       } else {
-               ret = macvlan_queue_xmit(skb, dev);
-       }
+       ret = macvlan_queue_xmit(skb, dev);
 
        if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
                struct vlan_pcpu_stats *pcpu_stats;
@@ -620,26 +621,20 @@ static int macvlan_open(struct net_device *dev)
        /* Attempt to populate accel_priv which is used to offload the L2
         * forwarding requests for unicast packets.
         */
-       if (lowerdev->features & NETIF_F_HW_L2FW_DOFFLOAD) {
+       if (lowerdev->features & NETIF_F_HW_L2FW_DOFFLOAD)
                vlan->accel_priv =
                      lowerdev->netdev_ops->ndo_dfwd_add_station(lowerdev, dev);
 
-               /* If we get a NULL pointer back, or if we get an error
-                * then we should just fall through to the non accelerated path
-                */
-               if (IS_ERR_OR_NULL(vlan->accel_priv))
-                       vlan->accel_priv = NULL;
-               else
-                       return 0;
+       /* If earlier attempt to offload failed, or accel_priv is not
+        * populated we must add the unicast address to the lower device.
+        */
+       if (IS_ERR_OR_NULL(vlan->accel_priv)) {
+               vlan->accel_priv = NULL;
+               err = dev_uc_add(lowerdev, dev->dev_addr);
+               if (err < 0)
+                       goto out;
        }
 
-       err = -EBUSY;
-       if (macvlan_addr_busy(vlan->port, dev->dev_addr))
-               goto out;
-
-       err = dev_uc_add(lowerdev, dev->dev_addr);
-       if (err < 0)
-               goto out;
        if (dev->flags & IFF_ALLMULTI) {
                err = dev_set_allmulti(lowerdev, 1);
                if (err < 0)
@@ -660,13 +655,14 @@ clear_multi:
        if (dev->flags & IFF_ALLMULTI)
                dev_set_allmulti(lowerdev, -1);
 del_unicast:
-       dev_uc_del(lowerdev, dev->dev_addr);
-out:
        if (vlan->accel_priv) {
                lowerdev->netdev_ops->ndo_dfwd_del_station(lowerdev,
                                                           vlan->accel_priv);
                vlan->accel_priv = NULL;
+       } else {
+               dev_uc_del(lowerdev, dev->dev_addr);
        }
+out:
        return err;
 }
 
@@ -679,7 +675,6 @@ static int macvlan_stop(struct net_device *dev)
                lowerdev->netdev_ops->ndo_dfwd_del_station(lowerdev,
                                                           vlan->accel_priv);
                vlan->accel_priv = NULL;
-               return 0;
        }
 
        dev_uc_unsync(lowerdev, dev);