hinic: add net_device_ops associated with vf
authorLuo bin <luobin9@huawei.com>
Sat, 25 Apr 2020 01:21:11 +0000 (01:21 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 26 Apr 2020 03:46:28 +0000 (20:46 -0700)
adds ndo_set_vf_mac/ndo_set_vf_vlan/ndo_get_vf_config and
ndo_set_vf_trust to configure netdev of virtual function

Signed-off-by: Luo bin <luobin9@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/huawei/hinic/hinic_main.c
drivers/net/ethernet/huawei/hinic/hinic_sriov.c
drivers/net/ethernet/huawei/hinic/hinic_sriov.h

index cd71249f9b1c7ccfb486c8b3e1c541408d9086d0..b66bb86cff96f511a771672ef197123b30b01e08 100644 (file)
@@ -779,8 +779,26 @@ static void hinic_set_rx_mode(struct net_device *netdev)
 static void hinic_tx_timeout(struct net_device *netdev, unsigned int txqueue)
 {
        struct hinic_dev *nic_dev = netdev_priv(netdev);
+       u16 sw_pi, hw_ci, sw_ci;
+       struct hinic_sq *sq;
+       u16 num_sqs, q_id;
+
+       num_sqs = hinic_hwdev_num_qps(nic_dev->hwdev);
 
        netif_err(nic_dev, drv, netdev, "Tx timeout\n");
+
+       for (q_id = 0; q_id < num_sqs; q_id++) {
+               if (!netif_xmit_stopped(netdev_get_tx_queue(netdev, q_id)))
+                       continue;
+
+               sq = hinic_hwdev_get_sq(nic_dev->hwdev, q_id);
+               sw_pi = atomic_read(&sq->wq->prod_idx) & sq->wq->mask;
+               hw_ci = be16_to_cpu(*(u16 *)(sq->hw_ci_addr)) & sq->wq->mask;
+               sw_ci = atomic_read(&sq->wq->cons_idx) & sq->wq->mask;
+               netif_err(nic_dev, drv, netdev, "Txq%d: sw_pi: %d, hw_ci: %d, sw_ci: %d, napi->state: 0x%lx\n",
+                         q_id, sw_pi, hw_ci, sw_ci,
+                         nic_dev->txqs[q_id].napi.state);
+       }
 }
 
 static void hinic_get_stats64(struct net_device *netdev,
@@ -846,6 +864,26 @@ static const struct net_device_ops hinic_netdev_ops = {
        .ndo_get_stats64 = hinic_get_stats64,
        .ndo_fix_features = hinic_fix_features,
        .ndo_set_features = hinic_set_features,
+       .ndo_set_vf_mac = hinic_ndo_set_vf_mac,
+       .ndo_set_vf_vlan = hinic_ndo_set_vf_vlan,
+       .ndo_get_vf_config = hinic_ndo_get_vf_config,
+       .ndo_set_vf_trust = hinic_ndo_set_vf_trust,
+};
+
+static const struct net_device_ops hinicvf_netdev_ops = {
+       .ndo_open = hinic_open,
+       .ndo_stop = hinic_close,
+       .ndo_change_mtu = hinic_change_mtu,
+       .ndo_set_mac_address = hinic_set_mac_addr,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid,
+       .ndo_set_rx_mode = hinic_set_rx_mode,
+       .ndo_start_xmit = hinic_xmit_frame,
+       .ndo_tx_timeout = hinic_tx_timeout,
+       .ndo_get_stats64 = hinic_get_stats64,
+       .ndo_fix_features = hinic_fix_features,
+       .ndo_set_features = hinic_set_features,
 };
 
 static void netdev_features_init(struct net_device *netdev)
@@ -983,7 +1021,10 @@ static int nic_dev_init(struct pci_dev *pdev)
 
        hinic_set_ethtool_ops(netdev);
 
-       netdev->netdev_ops = &hinic_netdev_ops;
+       if (!HINIC_IS_VF(hwdev->hwif))
+               netdev->netdev_ops = &hinic_netdev_ops;
+       else
+               netdev->netdev_ops = &hinicvf_netdev_ops;
 
        netdev->max_mtu = ETH_MAX_MTU;
 
index d1c4e1428b38486ca0bb0536b2db23a20792da2b..b24788e9733cff921315e46703ef876d30b05f74 100644 (file)
@@ -359,6 +359,168 @@ struct hinic_sriov_info *hinic_get_sriov_info_by_pcidev(struct pci_dev *pdev)
        return &nic_dev->sriov_info;
 }
 
+static int hinic_check_mac_info(u8 status, u16 vlan_id)
+{
+       if ((status && status != HINIC_MGMT_STATUS_EXIST &&
+            status != HINIC_PF_SET_VF_ALREADY) ||
+           (vlan_id & CHECK_IPSU_15BIT &&
+            status == HINIC_MGMT_STATUS_EXIST))
+               return -EINVAL;
+
+       return 0;
+}
+
+#define HINIC_VLAN_ID_MASK     0x7FFF
+
+int hinic_update_mac(struct hinic_hwdev *hwdev, u8 *old_mac, u8 *new_mac,
+                    u16 vlan_id, u16 func_id)
+{
+       struct hinic_port_mac_update mac_info = {0};
+       u16 out_size = sizeof(mac_info);
+       int err;
+
+       if (!hwdev || !old_mac || !new_mac)
+               return -EINVAL;
+
+       if ((vlan_id & HINIC_VLAN_ID_MASK) >= VLAN_N_VID) {
+               dev_err(&hwdev->hwif->pdev->dev, "Invalid VLAN number: %d\n",
+                       (vlan_id & HINIC_VLAN_ID_MASK));
+               return -EINVAL;
+       }
+
+       mac_info.func_id = func_id;
+       mac_info.vlan_id = vlan_id;
+       memcpy(mac_info.old_mac, old_mac, ETH_ALEN);
+       memcpy(mac_info.new_mac, new_mac, ETH_ALEN);
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_UPDATE_MAC, &mac_info,
+                                sizeof(mac_info), &mac_info, &out_size);
+
+       if (err || !out_size ||
+           hinic_check_mac_info(mac_info.status, mac_info.vlan_id)) {
+               dev_err(&hwdev->hwif->pdev->dev,
+                       "Failed to update MAC, err: %d, status: 0x%x, out size: 0x%x\n",
+                       err, mac_info.status, out_size);
+               return -EINVAL;
+       }
+
+       if (mac_info.status == HINIC_PF_SET_VF_ALREADY) {
+               dev_warn(&hwdev->hwif->pdev->dev,
+                        "PF has already set VF MAC. Ignore update operation\n");
+               return HINIC_PF_SET_VF_ALREADY;
+       }
+
+       if (mac_info.status == HINIC_MGMT_STATUS_EXIST)
+               dev_warn(&hwdev->hwif->pdev->dev, "MAC is repeated. Ignore update operation\n");
+
+       return 0;
+}
+
+void hinic_get_vf_config(struct hinic_hwdev *hwdev, u16 vf_id,
+                        struct ifla_vf_info *ivi)
+{
+       struct vf_data_storage *vfinfo;
+
+       vfinfo = hwdev->func_to_io.vf_infos + HW_VF_ID_TO_OS(vf_id);
+
+       ivi->vf = HW_VF_ID_TO_OS(vf_id);
+       memcpy(ivi->mac, vfinfo->vf_mac_addr, ETH_ALEN);
+       ivi->vlan = vfinfo->pf_vlan;
+       ivi->qos = vfinfo->pf_qos;
+       ivi->spoofchk = vfinfo->spoofchk;
+       ivi->trusted = vfinfo->trust;
+       ivi->max_tx_rate = vfinfo->max_rate;
+       ivi->min_tx_rate = vfinfo->min_rate;
+
+       if (!vfinfo->link_forced)
+               ivi->linkstate = IFLA_VF_LINK_STATE_AUTO;
+       else if (vfinfo->link_up)
+               ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE;
+       else
+               ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE;
+}
+
+int hinic_ndo_get_vf_config(struct net_device *netdev,
+                           int vf, struct ifla_vf_info *ivi)
+{
+       struct hinic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+
+       sriov_info = &nic_dev->sriov_info;
+       if (vf >= sriov_info->num_vfs)
+               return -EINVAL;
+
+       hinic_get_vf_config(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), ivi);
+
+       return 0;
+}
+
+int hinic_set_vf_mac(struct hinic_hwdev *hwdev, int vf, unsigned char *mac_addr)
+{
+       struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
+       struct vf_data_storage *vf_info;
+       u16 func_id;
+       int err;
+
+       vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf);
+
+       /* duplicate request, so just return success */
+       if (vf_info->pf_set_mac &&
+           !memcmp(vf_info->vf_mac_addr, mac_addr, ETH_ALEN))
+               return 0;
+
+       vf_info->pf_set_mac = true;
+
+       func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf;
+       err = hinic_update_mac(hwdev, vf_info->vf_mac_addr,
+                              mac_addr, 0, func_id);
+       if (err) {
+               vf_info->pf_set_mac = false;
+               return err;
+       }
+
+       memcpy(vf_info->vf_mac_addr, mac_addr, ETH_ALEN);
+
+       return 0;
+}
+
+int hinic_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
+{
+       struct hinic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+       int err;
+
+       sriov_info = &nic_dev->sriov_info;
+       if (!is_valid_ether_addr(mac) || vf >= sriov_info->num_vfs)
+               return -EINVAL;
+
+       err = hinic_set_vf_mac(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), mac);
+       if (err)
+               return err;
+
+       netif_info(nic_dev, drv, netdev, "Setting MAC %pM on VF %d\n", mac, vf);
+       netif_info(nic_dev, drv, netdev, "Reload the VF driver to make this change effective.");
+
+       return 0;
+}
+
+int hinic_add_vf_vlan(struct hinic_hwdev *hwdev, int vf_id, u16 vlan, u8 qos)
+{
+       struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
+       int err;
+
+       err = hinic_set_vf_vlan(hwdev, true, vlan, qos, vf_id);
+       if (err)
+               return err;
+
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan = vlan;
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos = qos;
+
+       dev_info(&hwdev->hwif->pdev->dev, "Setting VLAN %d, QOS 0x%x on VF %d\n",
+                vlan, qos, HW_VF_ID_TO_OS(vf_id));
+       return 0;
+}
+
 int hinic_kill_vf_vlan(struct hinic_hwdev *hwdev, int vf_id)
 {
        struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
@@ -381,6 +543,159 @@ int hinic_kill_vf_vlan(struct hinic_hwdev *hwdev, int vf_id)
        return 0;
 }
 
+int hinic_update_mac_vlan(struct hinic_dev *nic_dev, u16 old_vlan, u16 new_vlan,
+                         int vf_id)
+{
+       struct vf_data_storage *vf_info;
+       u16 vlan_id;
+       int err;
+
+       if (!nic_dev || old_vlan >= VLAN_N_VID || new_vlan >= VLAN_N_VID)
+               return -EINVAL;
+
+       vf_info = nic_dev->hwdev->func_to_io.vf_infos + HW_VF_ID_TO_OS(vf_id);
+       if (!vf_info->pf_set_mac)
+               return 0;
+
+       vlan_id = old_vlan;
+       if (vlan_id)
+               vlan_id |= HINIC_ADD_VLAN_IN_MAC;
+
+       err = hinic_port_del_mac(nic_dev, vf_info->vf_mac_addr, vlan_id);
+       if (err) {
+               dev_err(&nic_dev->hwdev->hwif->pdev->dev, "Failed to delete VF %d MAC %pM vlan %d\n",
+                       HW_VF_ID_TO_OS(vf_id), vf_info->vf_mac_addr, old_vlan);
+               return err;
+       }
+
+       vlan_id = new_vlan;
+       if (vlan_id)
+               vlan_id |= HINIC_ADD_VLAN_IN_MAC;
+
+       err = hinic_port_add_mac(nic_dev, vf_info->vf_mac_addr, vlan_id);
+       if (err) {
+               dev_err(&nic_dev->hwdev->hwif->pdev->dev, "Failed to add VF %d MAC %pM vlan %d\n",
+                       HW_VF_ID_TO_OS(vf_id), vf_info->vf_mac_addr, new_vlan);
+               goto out;
+       }
+
+       return 0;
+
+out:
+       vlan_id = old_vlan;
+       if (vlan_id)
+               vlan_id |= HINIC_ADD_VLAN_IN_MAC;
+       hinic_port_add_mac(nic_dev, vf_info->vf_mac_addr, vlan_id);
+
+       return err;
+}
+
+static int set_hw_vf_vlan(struct hinic_dev *nic_dev,
+                         u16 cur_vlanprio, int vf, u16 vlan, u8 qos)
+{
+       u16 old_vlan = cur_vlanprio & VLAN_VID_MASK;
+       int err = 0;
+
+       if (vlan || qos) {
+               if (cur_vlanprio) {
+                       err = hinic_kill_vf_vlan(nic_dev->hwdev,
+                                                OS_VF_ID_TO_HW(vf));
+                       if (err) {
+                               dev_err(&nic_dev->sriov_info.pdev->dev, "Failed to delete vf %d old vlan %d\n",
+                                       vf, old_vlan);
+                               goto out;
+                       }
+               }
+               err = hinic_add_vf_vlan(nic_dev->hwdev,
+                                       OS_VF_ID_TO_HW(vf), vlan, qos);
+               if (err) {
+                       dev_err(&nic_dev->sriov_info.pdev->dev, "Failed to add vf %d new vlan %d\n",
+                               vf, vlan);
+                       goto out;
+               }
+       } else {
+               err = hinic_kill_vf_vlan(nic_dev->hwdev, OS_VF_ID_TO_HW(vf));
+               if (err) {
+                       dev_err(&nic_dev->sriov_info.pdev->dev, "Failed to delete vf %d vlan %d\n",
+                               vf, old_vlan);
+                       goto out;
+               }
+       }
+
+       err = hinic_update_mac_vlan(nic_dev, old_vlan, vlan,
+                                   OS_VF_ID_TO_HW(vf));
+
+out:
+       return err;
+}
+
+int hinic_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos,
+                         __be16 vlan_proto)
+{
+       struct hinic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+       u16 vlanprio, cur_vlanprio;
+
+       sriov_info = &nic_dev->sriov_info;
+       if (vf >= sriov_info->num_vfs || vlan > 4095 || qos > 7)
+               return -EINVAL;
+       if (vlan_proto != htons(ETH_P_8021Q))
+               return -EPROTONOSUPPORT;
+       vlanprio = vlan | qos << HINIC_VLAN_PRIORITY_SHIFT;
+       cur_vlanprio = hinic_vf_info_vlanprio(nic_dev->hwdev,
+                                             OS_VF_ID_TO_HW(vf));
+       /* duplicate request, so just return success */
+       if (vlanprio == cur_vlanprio)
+               return 0;
+
+       return set_hw_vf_vlan(nic_dev, cur_vlanprio, vf, vlan, qos);
+}
+
+int hinic_set_vf_trust(struct hinic_hwdev *hwdev, u16 vf_id, bool trust)
+{
+       struct vf_data_storage *vf_infos;
+       struct hinic_func_to_io *nic_io;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       nic_io = &hwdev->func_to_io;
+       vf_infos = nic_io->vf_infos;
+       vf_infos[vf_id].trust = trust;
+
+       return 0;
+}
+
+int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting)
+{
+       struct hinic_dev *adapter = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+       struct hinic_func_to_io *nic_io;
+       bool cur_trust;
+       int err;
+
+       sriov_info = &adapter->sriov_info;
+       nic_io = &adapter->hwdev->func_to_io;
+
+       if (vf >= sriov_info->num_vfs)
+               return -EINVAL;
+
+       cur_trust = nic_io->vf_infos[vf].trust;
+       /* same request, so just return success */
+       if ((setting && cur_trust) || (!setting && !cur_trust))
+               return 0;
+
+       err = hinic_set_vf_trust(adapter->hwdev, vf, setting);
+       if (!err)
+               dev_info(&sriov_info->pdev->dev, "Set VF %d trusted %s succeed\n",
+                        vf, setting ? "on" : "off");
+       else
+               dev_err(&sriov_info->pdev->dev, "Failed set VF %d trusted %s\n",
+                       vf, setting ? "on" : "off");
+
+       return err;
+}
+
 /* pf receive message from vf */
 int nic_pf_mbox_handler(void *hwdev, u16 vf_id, u8 cmd, void *buf_in,
                        u16 in_size, void *buf_out, u16 *out_size)
@@ -484,6 +799,9 @@ void hinic_clear_vf_infos(struct hinic_dev *nic_dev, u16 vf_id)
        if (hinic_vf_info_vlanprio(nic_dev->hwdev, vf_id))
                hinic_kill_vf_vlan(nic_dev->hwdev, vf_id);
 
+       if (vf_infos->trust)
+               hinic_set_vf_trust(nic_dev->hwdev, vf_id, false);
+
        memset(vf_infos, 0, sizeof(*vf_infos));
        /* set vf_infos to default */
        hinic_init_vf_infos(&nic_dev->hwdev->func_to_io, HW_VF_ID_TO_OS(vf_id));
index 4889eabe7b7ce1fb76092e109aef49d47b101862..64affc7474b554dc7654fcf7a9a30cfb73e7fa7c 100644 (file)
@@ -52,6 +52,19 @@ struct hinic_register_vf {
        u8      rsvd0[6];
 };
 
+struct hinic_port_mac_update {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u16     func_id;
+       u16     vlan_id;
+       u16     rsvd1;
+       u8      old_mac[ETH_ALEN];
+       u16     rsvd2;
+       u8      new_mac[ETH_ALEN];
+};
+
 struct hinic_vf_vlan_config {
        u8 status;
        u8 version;
@@ -63,6 +76,16 @@ struct hinic_vf_vlan_config {
        u8  rsvd1[7];
 };
 
+int hinic_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac);
+
+int hinic_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos,
+                         __be16 vlan_proto);
+
+int hinic_ndo_get_vf_config(struct net_device *netdev,
+                           int vf, struct ifla_vf_info *ivi);
+
+int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting);
+
 void hinic_notify_all_vfs_link_changed(struct hinic_hwdev *hwdev,
                                       u8 link_status);