[RFC PATCH 2/7] VF driver changes to enable hooks to get kernel notifications

From: Satha Koteswara Rao
Date: Wed Dec 21 2016 - 04:23:04 EST


---
drivers/net/ethernet/cavium/thunder/nicvf_main.c | 579 ++++++++++++++++++++++-
1 file changed, 565 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 8a37012..8f00bc7 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -52,6 +52,11 @@
MODULE_VERSION(DRV_VERSION);
MODULE_DEVICE_TABLE(pci, nicvf_id_table);

+static int veb_enabled;
+
+int uc_mc_list;
+module_param(uc_mc_list, int, 0644);
+
static int debug = 0x00;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug message level bitmap");
@@ -61,6 +66,132 @@
MODULE_PARM_DESC(cpi_alg,
"PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)");

+/* Initialize the Shadow List */
+void nicvf_shadow_list_init(struct netdev_hw_addr_list *list)
+{
+ INIT_LIST_HEAD(&list->list);
+ list->count = 0;
+}
+
+/*Set the sync it of the addr structure */
+void nicvf_shadow_list_setsync(struct netdev_hw_addr_list *list, int sync)
+{
+ struct netdev_hw_addr *ha, *tmp;
+
+ list_for_each_entry_safe(ha, tmp, &list->list, list) {
+ ha->synced = sync;
+ }
+}
+
+/*Flush the entire list */
+void nicvf_shadow_list_flush(struct netdev_hw_addr_list *list)
+{
+ struct netdev_hw_addr *ha, *tmp;
+
+ list_for_each_entry_safe(ha, tmp, &list->list, list) {
+ list_del(&ha->list);
+ kfree(ha);
+ }
+ list->count = 0;
+}
+
+/*Return the number of items in the list */
+int nicvf_shadow_list_count(struct netdev_hw_addr_list *list)
+{
+ return list->count;
+}
+
+/*Check if the list is empty */
+int nicvf_shadow_list_empty(struct netdev_hw_addr_list *list)
+{
+ return (list->count == 0);
+}
+
+/* Add item to list */
+int nicvf_shadow_list_add(struct netdev_hw_addr_list *list, unsigned char *addr)
+{
+ struct netdev_hw_addr *ha;
+ int alloc_size;
+
+ alloc_size = sizeof(*ha);
+ ha = kmalloc(alloc_size, GFP_ATOMIC);
+ if (!ha)
+ return -ENOMEM;
+ ether_addr_copy(ha->addr, addr);
+ ha->synced = 0;
+ list_add_tail(&ha->list, &list->list);
+ list->count++;
+ return 0;
+}
+
+/* Delete item in the list given the address */
+void nicvf_shadow_list_del_ha(struct netdev_hw_addr_list *list,
+ struct netdev_hw_addr *ha)
+{
+ list_del(&ha->list);
+ kfree(ha);
+ list->count--;
+}
+
+/* Delete item in list by address */
+int nicvf_shadow_list_del(struct netdev_hw_addr_list *list, unsigned char *addr)
+{
+ struct netdev_hw_addr *ha, *tmp;
+
+ list_for_each_entry_safe(ha, tmp, &list->list, list)
+ if (ether_addr_equal(ha->addr, addr))
+ nicvf_shadow_list_del_ha(list, ha);
+
+ return -ENOENT;
+}
+
+/* Delete the addresses that are not in the netdev list and send delete
+ * notification
+ */
+int nicvf_shadow_list_delsync(struct netdev_hw_addr_list *list,
+ struct nicvf *nic, int addr_type)
+{
+ int is_modified = 0;
+ union nic_mbx mbx = {};
+ struct netdev_hw_addr *ha, *tmp;
+
+ list_for_each_entry_safe(ha, tmp, &list->list, list) {
+ if (ha->synced == 1) {
+ if (!uc_mc_list) {
+ mbx.msg.msg = NIC_MBOX_MSG_UC_MC;
+ mbx.uc_mc_cfg.vf_id = nic->vf_id;
+ mbx.uc_mc_cfg.addr_type = addr_type;
+ mbx.uc_mc_cfg.is_flush = 0;
+ mbx.uc_mc_cfg.is_add = 0;
+ ether_addr_copy(mbx.uc_mc_cfg.mac_addr,
+ ha->addr);
+ if (nicvf_send_msg_to_pf(nic, &mbx)) {
+ netdev_err(nic->netdev,
+ "PF not respond to MSG_UC_MC\n");
+ }
+ }
+ is_modified = 1;
+ nicvf_shadow_list_del_ha(list, ha);
+ }
+ }
+ return is_modified;
+}
+
+/*Check if an entry with the mac address exits in the list */
+int nicvf_shadow_list_find(struct netdev_hw_addr_list *list,
+ unsigned char *addr)
+{
+ struct netdev_hw_addr *ha;
+
+ list_for_each_entry(ha, &list->list, list) {
+ if (ether_addr_equal(ha->addr, addr)) {
+ ha->synced = 0;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
static inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx)
{
if (nic->sqs_mode)
@@ -113,22 +244,198 @@ static void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx)
nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]);
}

+bool pf_ack_required(struct nicvf *nic, union nic_mbx *mbx)
+{
+ if (mbx->msg.msg == NIC_MBOX_MSG_PROMISC ||
+ !nic->wait_for_ack)
+ return false;
+
+ return true;
+}
+
+void submit_uc_mc_mbox_msg(struct nicvf *nic, int vf, int flush, int addr_type,
+ u8 *mac)
+{
+ union nic_mbx mbx = {};
+
+ mbx.msg.msg = NIC_MBOX_MSG_UC_MC;
+ mbx.uc_mc_cfg.vf_id = vf;
+ mbx.uc_mc_cfg.addr_type = addr_type;
+ mbx.uc_mc_cfg.is_flush = flush;
+ mbx.uc_mc_cfg.is_add = !flush;
+ if (mac)
+ ether_addr_copy(mbx.uc_mc_cfg.mac_addr, mac);
+
+ if (nicvf_send_msg_to_pf(nic, &mbx) == -EBUSY) {
+ netdev_err(nic->netdev,
+ "PF didn't respond to MSG_UC_MC flush\n");
+ }
+}
+
+void send_uc_mc_msg(struct work_struct *work)
+{
+ struct nicvf *nic = container_of(work, struct nicvf, dwork.work);
+ struct net_device *netdev = nic->netdev;
+ union nic_mbx mbx = {};
+ int is_modified1 = 0;
+ int is_modified2 = 0;
+
+ if (nic->send_op_link_status) {
+ mbx.msg.msg = nic->link_up ? NIC_MBOX_MSG_OP_UP :
+ NIC_MBOX_MSG_OP_DOWN;
+ if (nicvf_send_msg_to_pf(nic, &mbx)) {
+ netdev_err(nic->netdev,
+ "PF not respond to msg %d\n", mbx.msg.msg);
+ }
+ nic->send_op_link_status = false;
+ return;
+ }
+
+ /* If the netdev list is empty */
+ if (netdev_uc_empty(netdev)) {
+ /* If shadow list is not empty */
+ if (!nicvf_shadow_list_empty(&nic->uc_shadow)) {
+ /* send uc flush notifcation */
+ nicvf_shadow_list_flush(&nic->uc_shadow);
+ submit_uc_mc_mbox_msg(nic, nic->vf_id, 1, 0, NULL);
+ }
+ } else {
+ /* If shadow list is empty add all and notify */
+ if (nicvf_shadow_list_empty(&nic->uc_shadow)) {
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_uc_addr(ha, netdev) {
+ nicvf_shadow_list_add(&nic->uc_shadow,
+ ha->addr);
+ submit_uc_mc_mbox_msg(nic, nic->vf_id, 0, 0,
+ ha->addr);
+ }
+ } else {
+ struct netdev_hw_addr *ha;
+
+ nicvf_shadow_list_setsync(&nic->uc_shadow, 1);
+ /* ADD the entries which are present in netdev list
+ * and not present in shadow list
+ */
+ netdev_for_each_uc_addr(ha, netdev) {
+ if (nicvf_shadow_list_find(&nic->uc_shadow,
+ ha->addr)) {
+ is_modified1 = 1;
+ nicvf_shadow_list_add(&nic->uc_shadow,
+ ha->addr);
+ if (uc_mc_list)
+ continue;
+ submit_uc_mc_mbox_msg(nic, nic->vf_id,
+ 0, 0, ha->addr);
+ }
+ }
+ /* Delete items that are not present in netdev list and
+ * present in shadow list
+ */
+ is_modified2 = nicvf_shadow_list_delsync(
+ &nic->uc_shadow, nic, 0);
+ if (uc_mc_list && (is_modified1 || is_modified2)) {
+ /* Now the shadow list is updated,
+ * send the entire list
+ */
+ netdev_for_each_uc_addr(ha, netdev)
+ submit_uc_mc_mbox_msg(nic, nic->vf_id,
+ 0, 0, ha->addr);
+ }
+ }
+ }
+
+ is_modified1 = 0;
+ is_modified2 = 0;
+ if (netdev_mc_empty(netdev)) { // If the netdev list is empty
+ /* If shadow list is not empty */
+ if (!nicvf_shadow_list_empty(&nic->mc_shadow)) {
+ // send uc flush notifcation
+ nicvf_shadow_list_flush(&nic->mc_shadow);
+ submit_uc_mc_mbox_msg(nic, nic->vf_id, 1, 1, NULL);
+ }
+ } else {
+ /* If shadow list is empty add all and notfy */
+ if (nicvf_shadow_list_empty(&nic->mc_shadow)) {
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, netdev) {
+ nicvf_shadow_list_add(&nic->mc_shadow,
+ ha->addr);
+ submit_uc_mc_mbox_msg(nic, nic->vf_id, 0, 1,
+ ha->addr);
+ }
+ } else {
+ struct netdev_hw_addr *ha;
+
+ nicvf_shadow_list_setsync(&nic->mc_shadow, 1);
+ /* ADD the entries which are present in netdev list and
+ * not present in shadow list
+ */
+ netdev_for_each_mc_addr(ha, netdev) {
+ if (nicvf_shadow_list_find(&nic->mc_shadow,
+ ha->addr)) {
+ is_modified1 = 1;
+ nicvf_shadow_list_add(&nic->mc_shadow,
+ ha->addr);
+ if (!uc_mc_list)
+ submit_uc_mc_mbox_msg(
+ nic, nic->vf_id, 0, 1,
+ ha->addr);
+ }
+ }
+ /* Delete items that are not present in netdev list and
+ * present in shadow list
+ */
+ is_modified2 = nicvf_shadow_list_delsync(
+ &nic->mc_shadow, nic, 1);
+ if (uc_mc_list && (is_modified1 || is_modified2)) {
+ /* Now the shadow list is updated, send the
+ * entire list
+ */
+ netdev_for_each_mc_addr(ha, netdev)
+ submit_uc_mc_mbox_msg(nic, nic->vf_id,
+ 0, 1, ha->addr);
+ }
+ }
+ }
+}
+
int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx)
{
int timeout = NIC_MBOX_MSG_TIMEOUT;
int sleep = 10;

+ if (nic->pf_ack_waiting) {
+ timeout += 20;
+ while (nic->pf_ack_waiting) {
+ msleep(sleep);
+ if (!timeout)
+ break;
+ timeout -= sleep;
+ }
+ timeout = NIC_MBOX_MSG_TIMEOUT;
+ }
nic->pf_acked = false;
nic->pf_nacked = false;
+ nic->pf_ack_waiting = true;

nicvf_write_to_mbx(nic, mbx);

+ if (!pf_ack_required(nic, mbx)) {
+ nic->pf_ack_waiting = false;
+ nic->pf_acked = true;
+ nic->pf_nacked = true;
+ return 0;
+ }
/* Wait for previous message to be acked, timeout 2sec */
while (!nic->pf_acked) {
if (nic->pf_nacked) {
- netdev_err(nic->netdev,
- "PF NACK to mbox msg 0x%02x from VF%d\n",
- (mbx->msg.msg & 0xFF), nic->vf_id);
+ if (mbx->msg.msg != NIC_MBOX_MSG_READY)
+ netdev_info(nic->netdev,
+ "PF NACK to mbox msg 0x%02x from VF%d\n",
+ (mbx->msg.msg & 0xFF), nic->vf_id);
+ nic->pf_ack_waiting = false;
return -EINVAL;
}
msleep(sleep);
@@ -139,9 +446,11 @@ int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx)
netdev_err(nic->netdev,
"PF didn't ACK to mbox msg 0x%02x from VF%d\n",
(mbx->msg.msg & 0xFF), nic->vf_id);
+ nic->pf_ack_waiting = false;
return -EBUSY;
}
}
+ nic->pf_ack_waiting = false;
return 0;
}

@@ -151,9 +460,14 @@ int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx)
static int nicvf_check_pf_ready(struct nicvf *nic)
{
union nic_mbx mbx = {};
+ int ret = 0;

mbx.msg.msg = NIC_MBOX_MSG_READY;
- if (nicvf_send_msg_to_pf(nic, &mbx)) {
+ ret = nicvf_send_msg_to_pf(nic, &mbx);
+ if (ret == -EINVAL) {
+ /* VF disabled through module parameter */
+ return 0;
+ } else if (ret) {
netdev_err(nic->netdev,
"PF didn't respond to READY msg\n");
return 0;
@@ -193,12 +507,22 @@ static void nicvf_handle_mbx_intr(struct nicvf *nic)
nic->vf_id = mbx.nic_cfg.vf_id & 0x7F;
nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F;
nic->node = mbx.nic_cfg.node_id;
+ nic->true_vf = mbx.nic_cfg.is_pf;
+ if (!veb_enabled)
+ veb_enabled = mbx.nic_cfg.veb_enabled;
+ if (veb_enabled)
+ snprintf(nic->phys_port_name, IFNAMSIZ, "%d %d %d %d",
+ nic->node, mbx.nic_cfg.bgx_id,
+ mbx.nic_cfg.lmac, mbx.nic_cfg.chan);
if (!nic->set_mac_pending)
ether_addr_copy(nic->netdev->dev_addr,
mbx.nic_cfg.mac_addr);
nic->sqs_mode = mbx.nic_cfg.sqs_mode;
nic->loopback_supported = mbx.nic_cfg.loopback_supported;
- nic->link_up = false;
+ if (veb_enabled)
+ nic->link_up = mbx.nic_cfg.pf_up;
+ else
+ nic->link_up = false;
nic->duplex = 0;
nic->speed = 0;
break;
@@ -208,6 +532,12 @@ static void nicvf_handle_mbx_intr(struct nicvf *nic)
case NIC_MBOX_MSG_NACK:
nic->pf_nacked = true;
break;
+ case NIC_MBOX_MSG_ADMIN_VLAN:
+ if (mbx.vlan_cfg.vlan_add && nic->admin_vlan_id == -1)
+ nic->admin_vlan_id = mbx.vlan_cfg.vlan_id;
+ else if (!mbx.vlan_cfg.vlan_add)
+ nic->admin_vlan_id = -1;
+ break;
case NIC_MBOX_MSG_RSS_SIZE:
nic->rss_info.rss_size = mbx.rss_size.ind_tbl_size;
nic->pf_acked = true;
@@ -216,16 +546,21 @@ static void nicvf_handle_mbx_intr(struct nicvf *nic)
nicvf_read_bgx_stats(nic, &mbx.bgx_stats);
nic->pf_acked = true;
break;
- case NIC_MBOX_MSG_BGX_LINK_CHANGE:
+ case NIC_MBOX_MSG_CFG_DONE:
nic->pf_acked = true;
nic->link_up = mbx.link_status.link_up;
nic->duplex = mbx.link_status.duplex;
nic->speed = mbx.link_status.speed;
+ break;
+ case NIC_MBOX_MSG_BGX_LINK_CHANGE:
+ nic->link_up = mbx.link_status.link_up;
+ nic->duplex = mbx.link_status.duplex;
+ nic->speed = mbx.link_status.speed;
if (nic->link_up) {
netdev_info(nic->netdev, "%s: Link is Up %d Mbps %s\n",
nic->netdev->name, nic->speed,
nic->duplex == DUPLEX_FULL ?
- "Full duplex" : "Half duplex");
+ "Full duplex" : "Half duplex");
netif_carrier_on(nic->netdev);
netif_tx_start_all_queues(nic->netdev);
} else {
@@ -563,6 +898,14 @@ static inline void nicvf_set_rxhash(struct net_device *netdev,
skb_set_hash(skb, hash, hash_type);
}

+static inline bool is_vf_vlan(struct nicvf *nic, u16 vid)
+{
+ if (veb_enabled && ((nic->admin_vlan_id & 0xFFF) == vid))
+ return false;
+
+ return true;
+}
+
static void nicvf_rcv_pkt_handler(struct net_device *netdev,
struct napi_struct *napi,
struct cqe_rx_t *cqe_rx)
@@ -617,7 +960,8 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev,
skb->protocol = eth_type_trans(skb, netdev);

/* Check for stripped VLAN */
- if (cqe_rx->vlan_found && cqe_rx->vlan_stripped)
+ if (cqe_rx->vlan_found && cqe_rx->vlan_stripped &&
+ is_vf_vlan(nic, (ntohs(cqe_rx->vlan_tci) & 0xFFF)))
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
ntohs((__force __be16)cqe_rx->vlan_tci));

@@ -1151,6 +1495,8 @@ int nicvf_stop(struct net_device *netdev)
/* disable mailbox interrupt */
nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);

+ //MBOX interrupts disabled, don't expect any ACK's from PF
+ nic->wait_for_ack = false;
nicvf_unregister_interrupts(nic);

nicvf_free_cq_poll(nic);
@@ -1182,6 +1528,8 @@ int nicvf_open(struct net_device *netdev)

netif_carrier_off(netdev);

+ //MBOX interrupts enabled, so wait for ACK from PF
+ nic->wait_for_ack = true;
err = nicvf_register_misc_interrupt(nic);
if (err)
return err;
@@ -1202,7 +1550,8 @@ int nicvf_open(struct net_device *netdev)
}

/* Check if we got MAC address from PF or else generate a radom MAC */
- if (!nic->sqs_mode && is_zero_ether_addr(netdev->dev_addr)) {
+ if ((veb_enabled || !nic->sqs_mode) &&
+ is_zero_ether_addr(netdev->dev_addr)) {
eth_hw_addr_random(netdev);
nicvf_hw_set_mac_addr(nic, netdev);
}
@@ -1268,7 +1617,17 @@ int nicvf_open(struct net_device *netdev)

/* Send VF config done msg to PF */
mbx.msg.msg = NIC_MBOX_MSG_CFG_DONE;
- nicvf_write_to_mbx(nic, &mbx);
+ if (veb_enabled)
+ nicvf_send_msg_to_pf(nic, &mbx);
+ else
+ nicvf_write_to_mbx(nic, &mbx);
+
+ if (veb_enabled && nic->link_up) {
+ nic->send_op_link_status = true;
+ queue_delayed_work(nic->uc_mc_msg, &nic->dwork, 0);
+ netif_carrier_on(netdev);
+ netif_tx_start_all_queues(netdev);
+ }

return 0;
cleanup:
@@ -1299,6 +1658,8 @@ static int nicvf_change_mtu(struct net_device *netdev, int new_mtu)
return -EINVAL;

netdev->mtu = new_mtu;
+ if (!nic->link_up)
+ return 0;

if (!netif_running(netdev))
return 0;
@@ -1508,6 +1869,142 @@ static int nicvf_set_features(struct net_device *netdev,
return 0;
}

+static int nicvf_vlan_rx_add_vid(struct net_device *netdev,
+ __always_unused __be16 proto, u16 vid)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+ union nic_mbx mbx = {};
+ int ret = 0;
+
+ if (!veb_enabled)
+ return 0;
+
+ if (nic->admin_vlan_id != -1) {
+ netdev_err(nic->netdev,
+ "VF %d could not add VLAN %d\n", nic->vf_id, vid);
+ return -1;
+ }
+ mbx.msg.msg = NIC_MBOX_MSG_VLAN;
+ mbx.vlan_cfg.vf_id = nic->vf_id;
+ mbx.vlan_cfg.vlan_id = vid;
+ mbx.vlan_cfg.vlan_add = 1;
+ ret = nicvf_send_msg_to_pf(nic, &mbx);
+ if (ret == -EINVAL) {
+ netdev_err(nic->netdev, "VF %d could not add VLAN %d\n",
+ nic->vf_id, vid);
+ } else if (ret == -EBUSY) {
+ netdev_err(nic->netdev,
+ "PF didn't respond to VLAN msg VLAN ID: %d VF: %d\n",
+ vid, nic->vf_id);
+ }
+ return ret;
+}
+
+static int nicvf_vlan_rx_kill_vid(struct net_device *netdev,
+ __always_unused __be16 proto, u16 vid)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+ union nic_mbx mbx = {};
+
+ if (!veb_enabled)
+ return 0;
+
+ mbx.msg.msg = NIC_MBOX_MSG_VLAN;
+ mbx.vlan_cfg.vf_id = nic->vf_id;
+ mbx.vlan_cfg.vlan_id = vid;
+ mbx.vlan_cfg.vlan_add = 0;
+ if (nicvf_send_msg_to_pf(nic, &mbx)) {
+ netdev_err(nic->netdev,
+ "PF didn't respond to VLAN msg VLAN ID: %d VF: %d\n",
+ vid, nic->vf_id);
+ return -1;
+ }
+ return 0;
+}
+
+void nicvf_set_rx_mode(struct net_device *netdev)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+
+ if (!veb_enabled)
+ return;
+
+ queue_delayed_work(nic->uc_mc_msg, &nic->dwork, 0);
+}
+
+void nicvf_change_rx_flags(struct net_device *netdev, int flags)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+ union nic_mbx mbx = {};
+
+ if (!veb_enabled)
+ return;
+
+ mbx.msg.msg = NIC_MBOX_MSG_PROMISC;
+ mbx.promisc_cfg.vf_id = nic->vf_id;
+ mbx.promisc_cfg.on = netdev->flags & IFF_PROMISC;
+ if (nicvf_send_msg_to_pf(nic, &mbx)) {
+ netdev_err(nic->netdev,
+ "PF didn't respond to PROMISC Mode\n");
+ return;
+ }
+}
+
+int nicvf_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos,
+ __be16 vlan_proto)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+ int is_add = (vlan | qos);
+ union nic_mbx mbx = {};
+ int ret = 0;
+
+ if (!veb_enabled)
+ return 0;
+
+ mbx.msg.msg = NIC_MBOX_MSG_ADMIN_VLAN;
+ mbx.vlan_cfg.vf_id = vf;
+ mbx.vlan_cfg.vlan_add = is_add;
+ mbx.vlan_cfg.vlan_id = vlan;
+
+ ret = nicvf_send_msg_to_pf(nic, &mbx);
+ if (ret == -EINVAL) {
+ netdev_err(nic->netdev, "ADMIN VLAN %s failed For Vf %d\n",
+ is_add ? "Add" : "Delete", vf);
+ } else if (ret == -EBUSY) {
+ netdev_err(nic->netdev,
+ "PF didn't respond to ADMIN VLAN UPDATE msg\n");
+ }
+ return ret;
+}
+
+static int nicvf_get_phys_port_name(struct net_device *netdev, char *name,
+ size_t len)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+ int plen;
+
+ plen = snprintf(name, len, "%s", nic->phys_port_name);
+
+ if (plen >= len)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int nicvf_get_phys_port_id(struct net_device *netdev,
+ struct netdev_phys_item_id *ppid)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+
+ if (veb_enabled && !nic->true_vf)
+ return -EOPNOTSUPP;
+
+ ppid->id_len = min_t(int, sizeof(netdev->dev_addr), sizeof(ppid->id));
+ memcpy(ppid->id, netdev->dev_addr, ppid->id_len);
+
+ return 0;
+}
+
static const struct net_device_ops nicvf_netdev_ops = {
.ndo_open = nicvf_open,
.ndo_stop = nicvf_stop,
@@ -1518,6 +2015,13 @@ static int nicvf_set_features(struct net_device *netdev,
.ndo_tx_timeout = nicvf_tx_timeout,
.ndo_fix_features = nicvf_fix_features,
.ndo_set_features = nicvf_set_features,
+ .ndo_vlan_rx_add_vid = nicvf_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = nicvf_vlan_rx_kill_vid,
+ .ndo_set_rx_mode = nicvf_set_rx_mode,
+ .ndo_change_rx_flags = nicvf_change_rx_flags,
+ .ndo_set_vf_vlan = nicvf_set_vf_vlan,
+ .ndo_get_phys_port_name = nicvf_get_phys_port_name,
+ .ndo_get_phys_port_id = nicvf_get_phys_port_id,
};

static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -1576,6 +2080,7 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
nic->pdev = pdev;
nic->pnicvf = nic;
nic->max_queues = qcount;
+ nic->pf_ack_waiting = false;

/* MAP VF's configuration registers */
nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
@@ -1595,6 +2100,8 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_free_netdev;

+ //MBOX interrupts enabled, so wait for ACK from PF
+ nic->wait_for_ack = true;
/* Check if PF is alive and get MAC address for this VF */
err = nicvf_register_misc_interrupt(nic);
if (err)
@@ -1619,12 +2126,13 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG |
NETIF_F_TSO | NETIF_F_GRO |
- NETIF_F_HW_VLAN_CTAG_RX);
-
- netdev->hw_features |= NETIF_F_RXHASH;
+ NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXHASH);

netdev->features |= netdev->hw_features;
- netdev->hw_features |= NETIF_F_LOOPBACK;
+ if (veb_enabled)
+ netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ else
+ netdev->hw_features |= NETIF_F_LOOPBACK;

netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;

@@ -1642,6 +2150,37 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
nic->msg_enable = debug;

nicvf_set_ethtool_ops(netdev);
+ if (veb_enabled) {
+ int bgx, lmac, chan, node, ret;
+
+ ret = sscanf(nic->phys_port_name, "%d %d %d %d", &node, &bgx,
+ &lmac, &chan);
+ if (nic->true_vf) {
+ dev_info(dev,
+ "interface %s enabled with node %d VF %d channel %d directly attached to physical port n%d-bgx-%d-%d\n",
+ netdev->name, node, nic->vf_id, chan, node,
+ bgx, lmac);
+ } else {
+ dev_info(dev,
+ "interface %s enabled with node %d VF %d channel %d attached to physical port n%d-bgx-%d-%d\n",
+ netdev->name, node, nic->vf_id, chan, node,
+ bgx, lmac);
+ }
+ snprintf(nic->phys_port_name, IFNAMSIZ, "n%d-bgx-%d-%d",
+ node, bgx, lmac);
+ nicvf_shadow_list_init(&nic->uc_shadow);
+ nicvf_shadow_list_init(&nic->mc_shadow);
+
+ nic->admin_vlan_id = -1;
+ nic->send_op_link_status = false;
+ nic->uc_mc_msg = alloc_workqueue("uc_mc_msg", WQ_UNBOUND |
+ WQ_MEM_RECLAIM, 1);
+ if (!nic->uc_mc_msg)
+ return -ENOMEM;
+ INIT_DELAYED_WORK(&nic->dwork, send_uc_mc_msg);
+ } else {
+ strlcpy(nic->phys_port_name, netdev->name, IFNAMSIZ);
+ }

return 0;

@@ -1669,6 +2208,12 @@ static void nicvf_remove(struct pci_dev *pdev)
return;

nic = netdev_priv(netdev);
+ if (veb_enabled) {
+ if (nicvf_shadow_list_count(&nic->uc_shadow))
+ nicvf_shadow_list_flush(&nic->uc_shadow);
+ if (nicvf_shadow_list_count(&nic->mc_shadow))
+ nicvf_shadow_list_flush(&nic->mc_shadow);
+ }
pnetdev = nic->pnicvf->netdev;

/* Check if this Qset is assigned to different VF.
@@ -1678,6 +2223,12 @@ static void nicvf_remove(struct pci_dev *pdev)
unregister_netdev(pnetdev);
nicvf_unregister_interrupts(nic);
pci_set_drvdata(pdev, NULL);
+ if (veb_enabled) {
+ if (nic->uc_mc_msg) {
+ cancel_delayed_work_sync(&nic->dwork);
+ destroy_workqueue(nic->uc_mc_msg);
+ }
+ }
if (nic->drv_stats)
free_percpu(nic->drv_stats);
free_netdev(netdev);
--
1.8.3.1