]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ixgbe/ixgbe_sriov.c
ixgbe: Streamline MC filter setup for VFs
[karo-tx-linux.git] / drivers / net / ixgbe / ixgbe_sriov.c
index d4cd20f301990e576cc12ecf24f71eb1908c5371..f6cee94ec8e8522db891155fb3d131cd8df6dca4 100644 (file)
@@ -48,7 +48,11 @@ int ixgbe_set_vf_multicasts(struct ixgbe_adapter *adapter,
                            int entries, u16 *hash_list, u32 vf)
 {
        struct vf_data_storage *vfinfo = &adapter->vfinfo[vf];
+       struct ixgbe_hw *hw = &adapter->hw;
        int i;
+       u32 vector_bit;
+       u32 vector_reg;
+       u32 mta_reg;
 
        /* only so many hash values supported */
        entries = min(entries, IXGBE_MAX_VF_MC_ENTRIES);
@@ -68,8 +72,13 @@ int ixgbe_set_vf_multicasts(struct ixgbe_adapter *adapter,
                vfinfo->vf_mc_hashes[i] = hash_list[i];;
        }
 
-       /* Flush and reset the mta with the new values */
-       ixgbe_set_rx_mode(adapter->netdev);
+       for (i = 0; i < vfinfo->num_vf_mc_hashes; i++) {
+               vector_reg = (vfinfo->vf_mc_hashes[i] >> 5) & 0x7F;
+               vector_bit = vfinfo->vf_mc_hashes[i] & 0x1F;
+               mta_reg = IXGBE_READ_REG(hw, IXGBE_MTA(vector_reg));
+               mta_reg |= (1 << vector_bit);
+               IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg);
+       }
 
        return 0;
 }
@@ -98,38 +107,51 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
 
 int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid, u32 vf)
 {
-       u32 ctrl;
-
-       /* Check if global VLAN already set, if not set it */
-       ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL);
-       if (!(ctrl & IXGBE_VLNCTRL_VFE)) {
-               /* enable VLAN tag insert/strip */
-               ctrl |= IXGBE_VLNCTRL_VFE;
-               ctrl &= ~IXGBE_VLNCTRL_CFIEN;
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl);
-       }
-
        return adapter->hw.mac.ops.set_vfta(&adapter->hw, vid, vf, (bool)add);
 }
 
 
-void ixgbe_set_vmolr(struct ixgbe_hw *hw, u32 vf)
+void ixgbe_set_vmolr(struct ixgbe_hw *hw, u32 vf, bool aupe)
 {
        u32 vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf));
-       vmolr |= (IXGBE_VMOLR_AUPE |
-                 IXGBE_VMOLR_ROMPE |
+       vmolr |= (IXGBE_VMOLR_ROMPE |
                  IXGBE_VMOLR_ROPE |
                  IXGBE_VMOLR_BAM);
+       if (aupe)
+               vmolr |= IXGBE_VMOLR_AUPE;
+       else
+               vmolr &= ~IXGBE_VMOLR_AUPE;
        IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf), vmolr);
 }
 
+static void ixgbe_set_vmvir(struct ixgbe_adapter *adapter, u32 vid, u32 vf)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       if (vid)
+               IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf),
+                               (vid | IXGBE_VMVIR_VLANA_DEFAULT));
+       else
+               IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf), 0);
+}
+
 inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
 {
        struct ixgbe_hw *hw = &adapter->hw;
 
        /* reset offloads to defaults */
-       ixgbe_set_vmolr(hw, vf);
-
+       if (adapter->vfinfo[vf].pf_vlan) {
+               ixgbe_set_vf_vlan(adapter, true,
+                                 adapter->vfinfo[vf].pf_vlan, vf);
+               ixgbe_set_vmvir(adapter,
+                               (adapter->vfinfo[vf].pf_vlan |
+                                (adapter->vfinfo[vf].pf_qos <<
+                                 VLAN_PRIO_SHIFT)), vf);
+               ixgbe_set_vmolr(hw, vf, false);
+       } else {
+               ixgbe_set_vmvir(adapter, 0, vf);
+               ixgbe_set_vmolr(hw, vf, true);
+       }
 
        /* reset multicast table array for vf */
        adapter->vfinfo[vf].num_vf_mc_hashes = 0;
@@ -263,10 +285,12 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf)
        case IXGBE_VF_SET_MAC_ADDR:
                {
                        u8 *new_mac = ((u8 *)(&msgbuf[1]));
-                       if (is_valid_ether_addr(new_mac))
+                       if (is_valid_ether_addr(new_mac) &&
+                           !adapter->vfinfo[vf].pf_set_mac)
                                ixgbe_set_vf_mac(adapter, vf, new_mac);
                        else
-                               retval = -1;
+                               ixgbe_set_vf_mac(adapter,
+                                 vf, adapter->vfinfo[vf].vf_mac_addresses);
                }
                break;
        case IXGBE_VF_SET_MULTICAST:
@@ -360,3 +384,76 @@ void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter)
        }
 }
 
+int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+       if (!is_valid_ether_addr(mac) || (vf >= adapter->num_vfs))
+               return -EINVAL;
+       adapter->vfinfo[vf].pf_set_mac = true;
+       dev_info(&adapter->pdev->dev, "setting MAC %pM on VF %d\n", mac, vf);
+       dev_info(&adapter->pdev->dev, "Reload the VF driver to make this"
+                                     " change effective.");
+       if (test_bit(__IXGBE_DOWN, &adapter->state)) {
+               dev_warn(&adapter->pdev->dev, "The VF MAC address has been set,"
+                        " but the PF device is not up.\n");
+               dev_warn(&adapter->pdev->dev, "Bring the PF device up before"
+                        " attempting to use the VF device.\n");
+       }
+       return ixgbe_set_vf_mac(adapter, vf, mac);
+}
+
+int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos)
+{
+       int err = 0;
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       if ((vf >= adapter->num_vfs) || (vlan > 4095) || (qos > 7))
+               return -EINVAL;
+       if (vlan || qos) {
+               err = ixgbe_set_vf_vlan(adapter, true, vlan, vf);
+               if (err)
+                       goto out;
+               ixgbe_set_vmvir(adapter, vlan | (qos << VLAN_PRIO_SHIFT), vf);
+               ixgbe_set_vmolr(&adapter->hw, vf, false);
+               adapter->vfinfo[vf].pf_vlan = vlan;
+               adapter->vfinfo[vf].pf_qos = qos;
+               dev_info(&adapter->pdev->dev,
+                        "Setting VLAN %d, QOS 0x%x on VF %d\n", vlan, qos, vf);
+               if (test_bit(__IXGBE_DOWN, &adapter->state)) {
+                       dev_warn(&adapter->pdev->dev,
+                                "The VF VLAN has been set,"
+                                " but the PF device is not up.\n");
+                       dev_warn(&adapter->pdev->dev,
+                                "Bring the PF device up before"
+                                " attempting to use the VF device.\n");
+               }
+       } else {
+               err = ixgbe_set_vf_vlan(adapter, false,
+                                       adapter->vfinfo[vf].pf_vlan, vf);
+               ixgbe_set_vmvir(adapter, vlan, vf);
+               ixgbe_set_vmolr(&adapter->hw, vf, true);
+               adapter->vfinfo[vf].pf_vlan = 0;
+               adapter->vfinfo[vf].pf_qos = 0;
+       }
+out:
+       return err;
+}
+
+int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
+{
+       return -EOPNOTSUPP;
+}
+
+int ixgbe_ndo_get_vf_config(struct net_device *netdev,
+                           int vf, struct ifla_vf_info *ivi)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+       if (vf >= adapter->num_vfs)
+               return -EINVAL;
+       ivi->vf = vf;
+       memcpy(&ivi->mac, adapter->vfinfo[vf].vf_mac_addresses, ETH_ALEN);
+       ivi->tx_rate = 0;
+       ivi->vlan = adapter->vfinfo[vf].pf_vlan;
+       ivi->qos = adapter->vfinfo[vf].pf_qos;
+       return 0;
+}