ixgbevf: Support Windows hosts (Hyper-V)
authorKY Srinivasan <kys@microsoft.com>
Wed, 20 Apr 2016 02:17:57 +0000 (19:17 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Wed, 4 May 2016 07:24:29 +0000 (00:24 -0700)
On Hyper-V, the VF/PF communication is a via software mediated path
as opposed to the hardware mailbox. Make the necessary
adjustments to support Hyper-V.

Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/intel/ixgbevf/mbx.c
drivers/net/ethernet/intel/ixgbevf/vf.c
drivers/net/ethernet/intel/ixgbevf/vf.h

index aa28c4fb1a43dc7ee7d9f02b0c7a3a442804cfb4..f2bafa6cb395a650e9927e4680323c5e93d0d758 100644 (file)
@@ -450,9 +450,13 @@ enum ixbgevf_state_t {
 
 enum ixgbevf_boards {
        board_82599_vf,
+       board_82599_vf_hv,
        board_X540_vf,
+       board_X540_vf_hv,
        board_X550_vf,
+       board_X550_vf_hv,
        board_X550EM_x_vf,
+       board_X550EM_x_vf_hv,
 };
 
 enum ixgbevf_xcast_modes {
@@ -467,6 +471,12 @@ extern const struct ixgbevf_info ixgbevf_X550_vf_info;
 extern const struct ixgbevf_info ixgbevf_X550EM_x_vf_info;
 extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops;
 
+extern const struct ixgbevf_info ixgbevf_82599_vf_hv_info;
+extern const struct ixgbevf_info ixgbevf_X540_vf_hv_info;
+extern const struct ixgbevf_info ixgbevf_X550_vf_hv_info;
+extern const struct ixgbevf_info ixgbevf_X550EM_x_vf_hv_info;
+extern const struct ixgbe_mbx_operations ixgbevf_hv_mbx_ops;
+
 /* needed by ethtool.c */
 extern const char ixgbevf_driver_name[];
 extern const char ixgbevf_driver_version[];
@@ -484,6 +494,7 @@ void ixgbevf_free_rx_resources(struct ixgbevf_ring *);
 void ixgbevf_free_tx_resources(struct ixgbevf_ring *);
 void ixgbevf_update_stats(struct ixgbevf_adapter *adapter);
 int ethtool_ioctl(struct ifreq *ifr);
+bool ixgbevf_on_hyperv(struct ixgbe_hw *hw);
 
 extern void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector);
 
index 319e25f298830786629646fe8f8f43dd703b8532..f39c8cb35839c173314a02d068cf59851e2a3eff 100644 (file)
@@ -62,10 +62,14 @@ static char ixgbevf_copyright[] =
        "Copyright (c) 2009 - 2015 Intel Corporation.";
 
 static const struct ixgbevf_info *ixgbevf_info_tbl[] = {
-       [board_82599_vf] = &ixgbevf_82599_vf_info,
-       [board_X540_vf]  = &ixgbevf_X540_vf_info,
-       [board_X550_vf]  = &ixgbevf_X550_vf_info,
-       [board_X550EM_x_vf] = &ixgbevf_X550EM_x_vf_info,
+       [board_82599_vf]        = &ixgbevf_82599_vf_info,
+       [board_82599_vf_hv]     = &ixgbevf_82599_vf_hv_info,
+       [board_X540_vf]         = &ixgbevf_X540_vf_info,
+       [board_X540_vf_hv]      = &ixgbevf_X540_vf_hv_info,
+       [board_X550_vf]         = &ixgbevf_X550_vf_info,
+       [board_X550_vf_hv]      = &ixgbevf_X550_vf_hv_info,
+       [board_X550EM_x_vf]     = &ixgbevf_X550EM_x_vf_info,
+       [board_X550EM_x_vf_hv]  = &ixgbevf_X550EM_x_vf_hv_info,
 };
 
 /* ixgbevf_pci_tbl - PCI Device ID Table
@@ -78,9 +82,13 @@ static const struct ixgbevf_info *ixgbevf_info_tbl[] = {
  */
 static const struct pci_device_id ixgbevf_pci_tbl[] = {
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF), board_82599_vf },
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF_HV), board_82599_vf_hv },
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF), board_X540_vf },
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF_HV), board_X540_vf_hv },
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF), board_X550_vf },
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF_HV), board_X550_vf_hv },
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF), board_X550EM_x_vf },
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF_HV), board_X550EM_x_vf_hv},
        /* required last entry */
        {0, }
 };
@@ -1795,7 +1803,10 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter)
                ixgbevf_setup_vfmrqc(adapter);
 
        /* notify the PF of our intent to use this size of frame */
-       ixgbevf_rlpml_set_vf(hw, netdev->mtu + ETH_HLEN + ETH_FCS_LEN);
+       if (!ixgbevf_on_hyperv(hw))
+               ixgbevf_rlpml_set_vf(hw, netdev->mtu + ETH_HLEN + ETH_FCS_LEN);
+       else
+               ixgbevf_hv_rlpml_set_vf(hw, netdev->mtu + ETH_HLEN + ETH_FCS_LEN);
 
        /* Setup the HW Rx Head and Tail Descriptor Pointers and
         * the Base and Length of the Rx Descriptor Ring
@@ -2056,7 +2067,10 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter)
        spin_lock_bh(&adapter->mbx_lock);
 
        while (api[idx] != ixgbe_mbox_api_unknown) {
-               err = hw->mac.ops.negotiate_api_version(hw, api[idx]);
+               if (!ixgbevf_on_hyperv(hw))
+                       err = hw->mac.ops.negotiate_api_version(hw, api[idx]);
+               else
+                       err = ixgbevf_hv_negotiate_api_version(hw, api[idx]);
                if (!err)
                        break;
                idx++;
@@ -3740,7 +3754,10 @@ static int ixgbevf_change_mtu(struct net_device *netdev, int new_mtu)
        netdev->mtu = new_mtu;
 
        /* notify the PF of our intent to use this size of frame */
-       ixgbevf_rlpml_set_vf(hw, max_frame);
+       if (!ixgbevf_on_hyperv(hw))
+               ixgbevf_rlpml_set_vf(hw, max_frame);
+       else
+               ixgbevf_hv_rlpml_set_vf(hw, max_frame);
 
        return 0;
 }
index dc68fea4894b4dabab0f900f116d182c7a68c0e3..61a80da8b6f0dec4323f094a245fc3fcbb6bcab0 100644 (file)
@@ -346,3 +346,14 @@ const struct ixgbe_mbx_operations ixgbevf_mbx_ops = {
        .check_for_rst  = ixgbevf_check_for_rst_vf,
 };
 
+/* Mailbox operations when running on Hyper-V.
+ * On Hyper-V, PF/VF communication is not through the
+ * hardware mailbox; this communication is through
+ * a software mediated path.
+ * Most mail box operations are noop while running on
+ * Hyper-V.
+ */
+const struct ixgbe_mbx_operations ixgbevf_hv_mbx_ops = {
+       .init_params    = ixgbevf_init_mbx_params_vf,
+       .check_for_rst  = ixgbevf_check_for_rst_vf,
+};
index 987ad69d4918a5b2e40a9cc318ab80934a9212a6..8a4eb08a3d19dfef4c007593031928cc67fd6feb 100644 (file)
 #include "vf.h"
 #include "ixgbevf.h"
 
+/* On Hyper-V, to reset, we need to read from this offset
+ * from the PCI config space. This is the mechanism used on
+ * Hyper-V to support PF/VF communication.
+ */
+#define IXGBE_HV_RESET_OFFSET           0x201
+
 /**
  *  ixgbevf_start_hw_vf - Prepare hardware for Tx/Rx
  *  @hw: pointer to hardware structure
@@ -125,6 +131,27 @@ static s32 ixgbevf_reset_hw_vf(struct ixgbe_hw *hw)
        return 0;
 }
 
+/**
+ * Hyper-V variant; the VF/PF communication is through the PCI
+ * config space.
+ */
+static s32 ixgbevf_hv_reset_hw_vf(struct ixgbe_hw *hw)
+{
+#if IS_ENABLED(CONFIG_PCI_MMCONFIG)
+       struct ixgbevf_adapter *adapter = hw->back;
+       int i;
+
+       for (i = 0; i < 6; i++)
+               pci_read_config_byte(adapter->pdev,
+                                    (i + IXGBE_HV_RESET_OFFSET),
+                                    &hw->mac.perm_addr[i]);
+       return 0;
+#else
+       pr_err("PCI_MMCONFIG needs to be enabled for Hyper-V\n");
+       return -EOPNOTSUPP;
+#endif
+}
+
 /**
  *  ixgbevf_stop_hw_vf - Generic stop Tx/Rx units
  *  @hw: pointer to hardware structure
@@ -258,6 +285,11 @@ static s32 ixgbevf_set_uc_addr_vf(struct ixgbe_hw *hw, u32 index, u8 *addr)
        return ret_val;
 }
 
+static s32 ixgbevf_hv_set_uc_addr_vf(struct ixgbe_hw *hw, u32 index, u8 *addr)
+{
+       return -EOPNOTSUPP;
+}
+
 /**
  * ixgbevf_get_reta_locked - get the RSS redirection table (RETA) contents.
  * @adapter: pointer to the port handle
@@ -416,6 +448,26 @@ static s32 ixgbevf_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr,
        return ret_val;
 }
 
+/**
+ *  ixgbevf_hv_set_rar_vf - set device MAC address Hyper-V variant
+ *  @hw: pointer to hardware structure
+ *  @index: Receive address register to write
+ *  @addr: Address to put into receive address register
+ *  @vmdq: Unused in this implementation
+ *
+ * We don't really allow setting the device MAC address. However,
+ * if the address being set is the permanent MAC address we will
+ * permit that.
+ **/
+static s32 ixgbevf_hv_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr,
+                                u32 vmdq)
+{
+       if (ether_addr_equal(addr, hw->mac.perm_addr))
+               return 0;
+
+       return -EOPNOTSUPP;
+}
+
 static void ixgbevf_write_msg_read_ack(struct ixgbe_hw *hw,
                                       u32 *msg, u16 size)
 {
@@ -472,6 +524,15 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw,
        return 0;
 }
 
+/**
+ * Hyper-V variant - just a stub.
+ */
+static s32 ixgbevf_hv_update_mc_addr_list_vf(struct ixgbe_hw *hw,
+                                            struct net_device *netdev)
+{
+       return -EOPNOTSUPP;
+}
+
 /**
  *  ixgbevf_update_xcast_mode - Update Multicast mode
  *  @hw: pointer to the HW structure
@@ -512,6 +573,16 @@ static s32 ixgbevf_update_xcast_mode(struct ixgbe_hw *hw,
        return 0;
 }
 
+/**
+ * Hyper-V variant - just a stub.
+ */
+static s32 ixgbevf_hv_update_xcast_mode(struct ixgbe_hw *hw,
+                                       struct net_device *netdev,
+                                       int xcast_mode)
+{
+       return -EOPNOTSUPP;
+}
+
 /**
  *  ixgbevf_set_vfta_vf - Set/Unset VLAN filter table address
  *  @hw: pointer to the HW structure
@@ -550,6 +621,15 @@ mbx_err:
        return err;
 }
 
+/**
+ * Hyper-V variant - just a stub.
+ */
+static s32 ixgbevf_hv_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind,
+                                 bool vlan_on)
+{
+       return -EOPNOTSUPP;
+}
+
 /**
  *  ixgbevf_setup_mac_link_vf - Setup MAC link settings
  *  @hw: pointer to hardware structure
@@ -655,6 +735,67 @@ out:
        return ret_val;
 }
 
+/**
+ * Hyper-V variant; there is no mailbox communication.
+ */
+static s32 ixgbevf_hv_check_mac_link_vf(struct ixgbe_hw *hw,
+                                       ixgbe_link_speed *speed,
+                                       bool *link_up,
+                                       bool autoneg_wait_to_complete)
+{
+       struct ixgbe_mbx_info *mbx = &hw->mbx;
+       struct ixgbe_mac_info *mac = &hw->mac;
+       u32 links_reg;
+
+       /* If we were hit with a reset drop the link */
+       if (!mbx->ops.check_for_rst(hw) || !mbx->timeout)
+               mac->get_link_status = true;
+
+       if (!mac->get_link_status)
+               goto out;
+
+       /* if link status is down no point in checking to see if pf is up */
+       links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS);
+       if (!(links_reg & IXGBE_LINKS_UP))
+               goto out;
+
+       /* for SFP+ modules and DA cables on 82599 it can take up to 500usecs
+        * before the link status is correct
+        */
+       if (mac->type == ixgbe_mac_82599_vf) {
+               int i;
+
+               for (i = 0; i < 5; i++) {
+                       udelay(100);
+                       links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS);
+
+                       if (!(links_reg & IXGBE_LINKS_UP))
+                               goto out;
+               }
+       }
+
+       switch (links_reg & IXGBE_LINKS_SPEED_82599) {
+       case IXGBE_LINKS_SPEED_10G_82599:
+               *speed = IXGBE_LINK_SPEED_10GB_FULL;
+               break;
+       case IXGBE_LINKS_SPEED_1G_82599:
+               *speed = IXGBE_LINK_SPEED_1GB_FULL;
+               break;
+       case IXGBE_LINKS_SPEED_100_82599:
+               *speed = IXGBE_LINK_SPEED_100_FULL;
+               break;
+       }
+
+       /* if we passed all the tests above then the link is up and we no
+        * longer need to check for link
+        */
+       mac->get_link_status = false;
+
+out:
+       *link_up = !mac->get_link_status;
+       return 0;
+}
+
 /**
  *  ixgbevf_rlpml_set_vf - Set the maximum receive packet length
  *  @hw: pointer to the HW structure
@@ -669,6 +810,25 @@ void ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size)
        ixgbevf_write_msg_read_ack(hw, msgbuf, 2);
 }
 
+/**
+ * ixgbevf_hv_rlpml_set_vf - Set the maximum receive packet length
+ * @hw: pointer to the HW structure
+ * @max_size: value to assign to max frame size
+ * Hyper-V variant.
+ **/
+void ixgbevf_hv_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size)
+{
+       u32 reg;
+
+       /* If we are on Hyper-V, we implement this functionality
+        * differently.
+        */
+       reg =  IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(0));
+       /* CRC == 4 */
+       reg |= ((max_size + 4) | IXGBE_RXDCTL_RLPML_EN);
+       IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(0), reg);
+}
+
 /**
  *  ixgbevf_negotiate_api_version_vf - Negotiate supported API version
  *  @hw: pointer to the HW structure
@@ -703,6 +863,21 @@ static int ixgbevf_negotiate_api_version_vf(struct ixgbe_hw *hw, int api)
        return err;
 }
 
+/**
+ *  ixgbevf_hv_negotiate_api_version - Negotiate supported API version
+ *  @hw: pointer to the HW structure
+ *  @api: integer containing requested API version
+ *  Hyper-V version - only ixgbe_mbox_api_10 supported.
+ **/
+int ixgbevf_hv_negotiate_api_version(struct ixgbe_hw *hw, int api)
+{
+       /* Hyper-V only supports api version ixgbe_mbox_api_10 */
+       if (api != ixgbe_mbox_api_10)
+               return IXGBE_ERR_INVALID_ARGUMENT;
+
+       return 0;
+}
+
 int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs,
                       unsigned int *default_tc)
 {
@@ -777,22 +952,62 @@ static const struct ixgbe_mac_operations ixgbevf_mac_ops = {
        .set_vfta               = ixgbevf_set_vfta_vf,
 };
 
+static const struct ixgbe_mac_operations ixgbevf_hv_mac_ops = {
+       .init_hw                = ixgbevf_init_hw_vf,
+       .reset_hw               = ixgbevf_hv_reset_hw_vf,
+       .start_hw               = ixgbevf_start_hw_vf,
+       .get_mac_addr           = ixgbevf_get_mac_addr_vf,
+       .stop_adapter           = ixgbevf_stop_hw_vf,
+       .setup_link             = ixgbevf_setup_mac_link_vf,
+       .check_link             = ixgbevf_hv_check_mac_link_vf,
+       .set_rar                = ixgbevf_hv_set_rar_vf,
+       .update_mc_addr_list    = ixgbevf_hv_update_mc_addr_list_vf,
+       .update_xcast_mode      = ixgbevf_hv_update_xcast_mode,
+       .set_uc_addr            = ixgbevf_hv_set_uc_addr_vf,
+       .set_vfta               = ixgbevf_hv_set_vfta_vf,
+};
+
 const struct ixgbevf_info ixgbevf_82599_vf_info = {
        .mac = ixgbe_mac_82599_vf,
        .mac_ops = &ixgbevf_mac_ops,
 };
 
+const struct ixgbevf_info ixgbevf_82599_vf_hv_info = {
+       .mac = ixgbe_mac_82599_vf,
+       .mac_ops = &ixgbevf_hv_mac_ops,
+};
+
 const struct ixgbevf_info ixgbevf_X540_vf_info = {
        .mac = ixgbe_mac_X540_vf,
        .mac_ops = &ixgbevf_mac_ops,
 };
 
+const struct ixgbevf_info ixgbevf_X540_vf_hv_info = {
+       .mac = ixgbe_mac_X540_vf,
+       .mac_ops = &ixgbevf_hv_mac_ops,
+};
+
 const struct ixgbevf_info ixgbevf_X550_vf_info = {
        .mac = ixgbe_mac_X550_vf,
        .mac_ops = &ixgbevf_mac_ops,
 };
 
+const struct ixgbevf_info ixgbevf_X550_vf_hv_info = {
+       .mac = ixgbe_mac_X550_vf,
+       .mac_ops = &ixgbevf_hv_mac_ops,
+};
+
 const struct ixgbevf_info ixgbevf_X550EM_x_vf_info = {
        .mac = ixgbe_mac_X550EM_x_vf,
        .mac_ops = &ixgbevf_mac_ops,
 };
+
+const struct ixgbevf_info ixgbevf_X550EM_x_vf_hv_info = {
+       .mac = ixgbe_mac_X550EM_x_vf,
+       .mac_ops = &ixgbevf_hv_mac_ops,
+};
+
+bool ixgbevf_on_hyperv(struct ixgbe_hw *hw)
+{
+       return hw->mbx.ops.check_for_msg == NULL;
+}
index 8e623f9327aeca676d79aa1590a0cf5e23c8a9db..f7c017058f83468e2ab992ddf758ed797b0af5ed 100644 (file)
@@ -209,6 +209,8 @@ static inline u32 ixgbe_read_reg_array(struct ixgbe_hw *hw, u32 reg,
 #define IXGBE_READ_REG_ARRAY(h, r, o) ixgbe_read_reg_array(h, r, o)
 
 void ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size);
+void ixgbevf_hv_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size);
+int ixgbevf_hv_negotiate_api_version(struct ixgbe_hw *hw, int api);
 int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs,
                       unsigned int *default_tc);
 int ixgbevf_get_reta_locked(struct ixgbe_hw *hw, u32 *reta, int num_rx_queues);