---
 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

Reply via email to