Commit 1f62cfa1 authored by Luo bin's avatar Luo bin Committed by David S. Miller

hinic: add net_device_ops associated with vf

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: default avatarLuo bin <luobin9@huawei.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7dd29ee1
......@@ -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);
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;
......
......@@ -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));
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment