ixgbe: Retain VLAN filtering in promiscuous + VT mode
authorGreg Rose <gregory.v.rose@intel.com>
Fri, 22 Feb 2013 02:14:39 +0000 (02:14 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Wed, 31 Jul 2013 00:07:12 +0000 (17:07 -0700)
When using the new bridge FDB interface to allow SR-IOV virtual function
network devices to communicate with SW bridged network devices the
physical function is placed into promiscuous mode and hardware VLAN
filtering is disabled.  This defeats the ability to use VLAN tagging
to isolate user networks.  When the device is in promiscuous mode and
VT mode simultaneously ensure that VLAN hardware filtering remains
enabled.

Signed-off-by: Greg Rose <gregory.v.rose@intel.com>
Tested-by: Stephen Ko <stephen.s.ko@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c

index bad8f14b194107b7d77ac5f89f194998c4c0f514..d837f81b4d97d3cd31fc2ddddf6e69262c506553 100644 (file)
@@ -3724,8 +3724,15 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
                hw->addr_ctrl.user_set_promisc = true;
                fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
                vmolr |= (IXGBE_VMOLR_ROPE | IXGBE_VMOLR_MPE);
-               /* don't hardware filter vlans in promisc mode */
-               ixgbe_vlan_filter_disable(adapter);
+               /* Only disable hardware filter vlans in promiscuous mode
+                * if SR-IOV and VMDQ are disabled - otherwise ensure
+                * that hardware VLAN filters remain enabled.
+                */
+               if (!(adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
+                                       IXGBE_FLAG_SRIOV_ENABLED)))
+                       ixgbe_vlan_filter_disable(adapter);
+               else
+                       ixgbe_vlan_filter_enable(adapter);
        } else {
                if (netdev->flags & IFF_ALLMULTI) {
                        fctrl |= IXGBE_FCTRL_MPE;
index 1e7d587c4e572f9efdd3f876b98e906cb3963561..6c624c9d4c0fcb77ccfbfae7d5ba3f9ae38dfe4d 100644 (file)
@@ -768,6 +768,29 @@ static int ixgbe_set_vf_mac_addr(struct ixgbe_adapter *adapter,
        return ixgbe_set_vf_mac(adapter, vf, new_mac) < 0;
 }
 
+static int ixgbe_find_vlvf_entry(struct ixgbe_hw *hw, u32 vlan)
+{
+       u32 vlvf;
+       s32 regindex;
+
+       /* short cut the special case */
+       if (vlan == 0)
+               return 0;
+
+       /* Search for the vlan id in the VLVF entries */
+       for (regindex = 1; regindex < IXGBE_VLVF_ENTRIES; regindex++) {
+               vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(regindex));
+               if ((vlvf & VLAN_VID_MASK) == vlan)
+                       break;
+       }
+
+       /* Return a negative value if not found */
+       if (regindex >= IXGBE_VLVF_ENTRIES)
+               regindex = -1;
+
+       return regindex;
+}
+
 static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter,
                                 u32 *msgbuf, u32 vf)
 {
@@ -775,6 +798,9 @@ static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter,
        int add = (msgbuf[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT;
        int vid = (msgbuf[1] & IXGBE_VLVF_VLANID_MASK);
        int err;
+       s32 reg_ndx;
+       u32 vlvf;
+       u32 bits;
        u8 tcs = netdev_get_num_tc(adapter->netdev);
 
        if (adapter->vfinfo[vf].pf_vlan || tcs) {
@@ -790,10 +816,50 @@ static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter,
        else if (adapter->vfinfo[vf].vlan_count)
                adapter->vfinfo[vf].vlan_count--;
 
+       /* in case of promiscuous mode any VLAN filter set for a VF must
+        * also have the PF pool added to it.
+        */
+       if (add && adapter->netdev->flags & IFF_PROMISC)
+               err = ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0));
+
        err = ixgbe_set_vf_vlan(adapter, add, vid, vf);
        if (!err && adapter->vfinfo[vf].spoofchk_enabled)
                hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf);
 
+       /* Go through all the checks to see if the VLAN filter should
+        * be wiped completely.
+        */
+       if (!add && adapter->netdev->flags & IFF_PROMISC) {
+               reg_ndx = ixgbe_find_vlvf_entry(hw, vid);
+               if (reg_ndx < 0)
+                       goto out;
+               vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(reg_ndx));
+               /* See if any other pools are set for this VLAN filter
+                * entry other than the PF.
+                */
+               if (VMDQ_P(0) < 32) {
+                       bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2));
+                       bits &= ~(1 << VMDQ_P(0));
+                       bits |= IXGBE_READ_REG(hw,
+                                              IXGBE_VLVFB(reg_ndx * 2) + 1);
+               } else {
+                       bits = IXGBE_READ_REG(hw,
+                                             IXGBE_VLVFB(reg_ndx * 2) + 1);
+                       bits &= ~(1 << (VMDQ_P(0) - 32));
+                       bits |= IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2));
+               }
+
+               /* If the filter was removed then ensure PF pool bit
+                * is cleared if the PF only added itself to the pool
+                * because the PF is in promiscuous mode.
+                */
+               if ((vlvf & VLAN_VID_MASK) == vid &&
+                   !test_bit(vid, adapter->active_vlans) && !bits)
+                       ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0));
+       }
+
+out:
+
        return err;
 }