[v6,09/26] net/spnic: support MAC and link event handling

Message ID 683cdaab3c9602409662f77862b83f2c345855c5.1640838702.git.songyl@ramaxel.com (mailing list archive)
State Changes Requested, archived
Delegated to: Ferruh Yigit
Headers
Series Net/SPNIC: support SPNIC into DPDK 22.03 |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Yanling Song Dec. 30, 2021, 6:08 a.m. UTC
  This commit adds interfaces to add/remove MAC addresses
and registers related ops to struct eth_dev_ops. Furthermore,
this commit adds callback to handle link events.

Signed-off-by: Yanling Song <songyl@ramaxel.com>
---
 drivers/net/spnic/base/meson.build       |   3 +-
 drivers/net/spnic/base/spnic_hw_cfg.c    |  12 +
 drivers/net/spnic/base/spnic_hw_cfg.h    |   2 +
 drivers/net/spnic/base/spnic_nic_cfg.c   | 291 +++++++++++++++++++
 drivers/net/spnic/base/spnic_nic_cfg.h   | 180 ++++++++++++
 drivers/net/spnic/base/spnic_nic_event.c |  27 +-
 drivers/net/spnic/base/spnic_nic_event.h |   9 +-
 drivers/net/spnic/spnic_ethdev.c         | 348 ++++++++++++++++++++++-
 8 files changed, 861 insertions(+), 11 deletions(-)
 create mode 100644 drivers/net/spnic/base/spnic_nic_cfg.c
 create mode 100644 drivers/net/spnic/base/spnic_nic_cfg.h
  

Comments

Ferruh Yigit Jan. 19, 2022, 5:26 p.m. UTC | #1
On 12/30/2021 6:08 AM, Yanling Song wrote:
> This commit adds interfaces to add/remove MAC addresses
> and registers related ops to struct eth_dev_ops. Furthermore,
> this commit adds callback to handle link events.
> 

The patch also adds the VF dev_ops.

It would be more clear to support PF first and add mbox support and VF later.
But VF support is crept into the code from early patches, I assume that is
because the driver is already complete and spliting it is hard at this stage..

Similarly the primary/secondary support seems spread through the patches,
hard to separate the feature.


Above are sign of the patches are not split logically which makes harder
to review them and detect any issues, and future fixes references won't be
clear.

If you can clarify the split more, that would be great but I can see it
is hard with an existing driver.

> Signed-off-by: Yanling Song <songyl@ramaxel.com>
  
Yanling Song Jan. 21, 2022, 9:36 a.m. UTC | #2
On Wed, 19 Jan 2022 17:26:47 +0000
Ferruh Yigit <ferruh.yigit@intel.com> wrote:

> On 12/30/2021 6:08 AM, Yanling Song wrote:
> > This commit adds interfaces to add/remove MAC addresses
> > and registers related ops to struct eth_dev_ops. Furthermore,
> > this commit adds callback to handle link events.
> >   
> 
> The patch also adds the VF dev_ops.
> 
> It would be more clear to support PF first and add mbox support and
> VF later. But VF support is crept into the code from early patches, I
> assume that is because the driver is already complete and spliting it
> is hard at this stage..
> 
Yes. it is.

> Similarly the primary/secondary support seems spread through the
> patches, hard to separate the feature.
> 
> 
> Above are sign of the patches are not split logically which makes
> harder to review them and detect any issues, and future fixes
> references won't be clear.
> 
> If you can clarify the split more, that would be great but I can see
> it is hard with an existing driver.
> 
Sorry for the inconvenient. Will split the patches clearly in the next
version. 

> > Signed-off-by: Yanling Song <songyl@ramaxel.com>  
>
  

Patch

diff --git a/drivers/net/spnic/base/meson.build b/drivers/net/spnic/base/meson.build
index 77a56ca41e..f4bb4469ae 100644
--- a/drivers/net/spnic/base/meson.build
+++ b/drivers/net/spnic/base/meson.build
@@ -11,7 +11,8 @@  sources = [
 	'spnic_cmdq.c',
 	'spnic_hw_comm.c',
 	'spnic_wq.c',
-	'spnic_hw_cfg.c'
+	'spnic_hw_cfg.c',
+	'spnic_nic_cfg.c'
 ]
 
 extra_flags = []
diff --git a/drivers/net/spnic/base/spnic_hw_cfg.c b/drivers/net/spnic/base/spnic_hw_cfg.c
index ac76b2632e..83a8e98408 100644
--- a/drivers/net/spnic/base/spnic_hw_cfg.c
+++ b/drivers/net/spnic/base/spnic_hw_cfg.c
@@ -155,3 +155,15 @@  void spnic_free_capability(void *dev)
 {
 	rte_free(((struct spnic_hwdev *)dev)->cfg_mgmt);
 }
+
+u8 spnic_physical_port_id(void *hwdev)
+{
+	struct spnic_hwdev *dev = hwdev;
+
+	if (!dev) {
+		PMD_DRV_LOG(INFO, "Hwdev is NULL for getting physical port id");
+		return 0;
+	}
+
+	return dev->cfg_mgmt->svc_cap.port_id;
+}
diff --git a/drivers/net/spnic/base/spnic_hw_cfg.h b/drivers/net/spnic/base/spnic_hw_cfg.h
index 5003d048e6..df59d7874f 100644
--- a/drivers/net/spnic/base/spnic_hw_cfg.h
+++ b/drivers/net/spnic/base/spnic_hw_cfg.h
@@ -112,6 +112,8 @@  struct spnic_cfg_cmd_dev_cap {
 int spnic_init_capability(void *dev);
 void spnic_free_capability(void *dev);
 
+u8 spnic_physical_port_id(void *hwdev);
+
 int spnic_cfg_mbx_vf_proc_msg(void *hwdev, void *pri_handle, u16 cmd, void *buf_in,
 			u16 in_size, void *buf_out, u16 *out_size);
 #endif /* _SPNIC_HW_CFG_H_ */
diff --git a/drivers/net/spnic/base/spnic_nic_cfg.c b/drivers/net/spnic/base/spnic_nic_cfg.c
new file mode 100644
index 0000000000..2d4ba70bf0
--- /dev/null
+++ b/drivers/net/spnic/base/spnic_nic_cfg.c
@@ -0,0 +1,291 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Ramaxel Memory Technology, Ltd
+ */
+
+#include <rte_ether.h>
+#include "spnic_compat.h"
+#include "spnic_cmd.h"
+#include "spnic_mgmt.h"
+#include "spnic_hwif.h"
+#include "spnic_mbox.h"
+#include "spnic_hwdev.h"
+#include "spnic_wq.h"
+#include "spnic_cmdq.h"
+#include "spnic_nic_cfg.h"
+#include "spnic_hw_cfg.h"
+
+struct vf_msg_handler {
+	u16 cmd;
+};
+
+const struct vf_msg_handler vf_cmd_handler[] = {
+	{
+		.cmd = SPNIC_CMD_VF_REGISTER,
+	},
+
+	{
+		.cmd = SPNIC_CMD_GET_MAC,
+	},
+
+	{
+		.cmd = SPNIC_CMD_SET_MAC,
+	},
+
+	{
+		.cmd = SPNIC_CMD_DEL_MAC,
+	},
+
+	{
+		.cmd = SPNIC_CMD_UPDATE_MAC,
+	},
+
+	{
+		.cmd = SPNIC_CMD_VF_COS,
+	},
+};
+
+static const struct vf_msg_handler vf_mag_cmd_handler[] = {
+	{
+		.cmd = MAG_CMD_GET_LINK_STATUS,
+	},
+};
+
+static int mag_msg_to_mgmt_sync(void *hwdev, u16 cmd, void *buf_in, u16 in_size,
+				void *buf_out, u16 *out_size);
+
+int spnic_l2nic_msg_to_mgmt_sync(void *hwdev, u16 cmd, void *buf_in, u16 in_size,
+			   void *buf_out, u16 *out_size)
+{
+	u32 i, cmd_cnt = ARRAY_LEN(vf_cmd_handler);
+	bool cmd_to_pf = false;
+
+	if (spnic_func_type(hwdev) == TYPE_VF) {
+		for (i = 0; i < cmd_cnt; i++) {
+			if (cmd == vf_cmd_handler[i].cmd)
+				cmd_to_pf = true;
+		}
+	}
+
+	if (cmd_to_pf) {
+		return spnic_mbox_to_pf(hwdev, SPNIC_MOD_L2NIC, cmd, buf_in,
+					in_size, buf_out, out_size, 0);
+	}
+
+	return spnic_msg_to_mgmt_sync(hwdev, SPNIC_MOD_L2NIC, cmd, buf_in,
+				      in_size, buf_out, out_size, 0);
+}
+
+static int spnic_check_mac_info(u8 status, u16 vlan_id)
+{
+	if ((status && status != SPNIC_MGMT_STATUS_EXIST &&
+	     status != SPNIC_PF_SET_VF_ALREADY) ||
+	    (vlan_id & CHECK_IPSU_15BIT &&
+	     status == SPNIC_MGMT_STATUS_EXIST))
+		return -EINVAL;
+
+	return 0;
+}
+
+#define VLAN_N_VID		4096
+
+int spnic_set_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id)
+{
+	struct spnic_port_mac_set mac_info;
+	u16 out_size = sizeof(mac_info);
+	int err;
+
+	if (!hwdev || !mac_addr)
+		return -EINVAL;
+
+	memset(&mac_info, 0, sizeof(mac_info));
+
+	if (vlan_id >= VLAN_N_VID) {
+		PMD_DRV_LOG(ERR, "Invalid VLAN number: %d", vlan_id);
+		return -EINVAL;
+	}
+
+	mac_info.func_id = func_id;
+	mac_info.vlan_id = vlan_id;
+	memmove(mac_info.mac, mac_addr, ETH_ALEN);
+
+	err = spnic_l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_SET_MAC, &mac_info,
+				     sizeof(mac_info), &mac_info, &out_size);
+	if (err || !out_size ||
+	    spnic_check_mac_info(mac_info.msg_head.status, mac_info.vlan_id)) {
+		PMD_DRV_LOG(ERR, "Update MAC failed, err: %d, status: 0x%x, out size: 0x%x",
+			    err, mac_info.msg_head.status, out_size);
+		return -EINVAL;
+	}
+
+	if (mac_info.msg_head.status == SPNIC_PF_SET_VF_ALREADY) {
+		PMD_DRV_LOG(WARNING, "PF has already set VF mac, Ignore set operation");
+		return SPNIC_PF_SET_VF_ALREADY;
+	}
+
+	if (mac_info.msg_head.status == SPNIC_MGMT_STATUS_EXIST) {
+		PMD_DRV_LOG(WARNING, "MAC is repeated. Ignore update operation");
+		return 0;
+	}
+
+	return 0;
+}
+
+int spnic_del_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id)
+{
+	struct spnic_port_mac_set mac_info;
+	u16 out_size = sizeof(mac_info);
+	int err;
+
+	if (!hwdev || !mac_addr)
+		return -EINVAL;
+
+	if (vlan_id >= VLAN_N_VID) {
+		PMD_DRV_LOG(ERR, "Invalid VLAN number: %d", vlan_id);
+		return -EINVAL;
+	}
+
+	memset(&mac_info, 0, sizeof(mac_info));
+	mac_info.func_id = func_id;
+	mac_info.vlan_id = vlan_id;
+	memmove(mac_info.mac, mac_addr, ETH_ALEN);
+
+	err = spnic_l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_DEL_MAC, &mac_info,
+				     sizeof(mac_info), &mac_info, &out_size);
+	if (err || !out_size || (mac_info.msg_head.status &&
+	    mac_info.msg_head.status != SPNIC_PF_SET_VF_ALREADY)) {
+		PMD_DRV_LOG(ERR, "Delete MAC failed, err: %d, status: 0x%x, out size: 0x%x",
+			    err, mac_info.msg_head.status, out_size);
+		return -EINVAL;
+	}
+
+	if (mac_info.msg_head.status == SPNIC_PF_SET_VF_ALREADY) {
+		PMD_DRV_LOG(WARNING, "PF has already set VF mac, Ignore delete operation");
+		return SPNIC_PF_SET_VF_ALREADY;
+	}
+
+	return 0;
+}
+
+int spnic_update_mac(void *hwdev, u8 *old_mac, u8 *new_mac, u16 vlan_id,
+		     u16 func_id)
+{
+	struct spnic_port_mac_update mac_info;
+	u16 out_size = sizeof(mac_info);
+	int err;
+
+	if (!hwdev || !old_mac || !new_mac)
+		return -EINVAL;
+
+	if (vlan_id >= VLAN_N_VID) {
+		PMD_DRV_LOG(ERR, "Invalid VLAN number: %d", vlan_id);
+		return -EINVAL;
+	}
+
+	memset(&mac_info, 0, sizeof(mac_info));
+	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 = spnic_l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_UPDATE_MAC, &mac_info,
+				     sizeof(mac_info), &mac_info, &out_size);
+	if (err || !out_size ||
+	    spnic_check_mac_info(mac_info.msg_head.status, mac_info.vlan_id)) {
+		PMD_DRV_LOG(ERR, "Update MAC failed, err: %d, status: 0x%x, out size: 0x%x",
+			    err, mac_info.msg_head.status, out_size);
+		return -EINVAL;
+	}
+
+	if (mac_info.msg_head.status == SPNIC_PF_SET_VF_ALREADY) {
+		PMD_DRV_LOG(WARNING, "PF has already set VF MAC. Ignore update operation");
+		return SPNIC_PF_SET_VF_ALREADY;
+	}
+
+	if (mac_info.msg_head.status == SPNIC_MGMT_STATUS_EXIST) {
+		PMD_DRV_LOG(INFO, "MAC is repeated. Ignore update operation");
+		return 0;
+	}
+
+	return 0;
+}
+
+int spnic_get_default_mac(void *hwdev, u8 *mac_addr, int ether_len)
+{
+	struct spnic_port_mac_set mac_info;
+	u16 out_size = sizeof(mac_info);
+	int err;
+
+	if (!hwdev || !mac_addr)
+		return -EINVAL;
+
+	memset(&mac_info, 0, sizeof(mac_info));
+	mac_info.func_id = spnic_global_func_id(hwdev);
+
+	err = spnic_l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_GET_MAC,
+				     &mac_info, sizeof(mac_info),
+		&mac_info, &out_size);
+	if (err || !out_size || mac_info.msg_head.status) {
+		PMD_DRV_LOG(ERR, "Get MAC failed, err: %d, status: 0x%x, out size: 0x%x",
+			    err, mac_info.msg_head.status, out_size);
+		return -EINVAL;
+	}
+
+	memmove(mac_addr, mac_info.mac, ether_len);
+
+	return 0;
+}
+
+int spnic_get_port_info(void *hwdev, struct nic_port_info *port_info)
+{
+	struct spnic_cmd_port_info port_msg;
+	u16 out_size = sizeof(port_msg);
+	int err;
+
+	if (!hwdev || !port_info)
+		return -EINVAL;
+
+	memset(&port_msg, 0, sizeof(port_msg));
+	port_msg.port_id = spnic_physical_port_id(hwdev);
+
+	err = mag_msg_to_mgmt_sync(hwdev, MAG_CMD_GET_PORT_INFO, &port_msg,
+				   sizeof(port_msg), &port_msg, &out_size);
+	if (err || !out_size || port_msg.msg_head.status) {
+		PMD_DRV_LOG(ERR, "Get port info failed, err: %d, status: 0x%x, out size: 0x%x",
+			    err, port_msg.msg_head.status, out_size);
+		return -EINVAL;
+	}
+
+	port_info->autoneg_cap = port_msg.autoneg_cap;
+	port_info->autoneg_state = port_msg.autoneg_state;
+	port_info->duplex = port_msg.duplex;
+	port_info->port_type = port_msg.port_type;
+	port_info->speed = port_msg.speed;
+	port_info->fec = port_msg.fec;
+
+	return 0;
+}
+
+static int _mag_msg_to_mgmt_sync(void *hwdev, u16 cmd, void *buf_in,
+				 u16 in_size, void *buf_out, u16 *out_size)
+{
+	u32 i, cmd_cnt = ARRAY_LEN(vf_mag_cmd_handler);
+
+	if (spnic_func_type(hwdev) == TYPE_VF) {
+		for (i = 0; i < cmd_cnt; i++) {
+			if (cmd == vf_mag_cmd_handler[i].cmd)
+				return spnic_mbox_to_pf(hwdev, SPNIC_MOD_HILINK,
+							cmd, buf_in, in_size,
+							buf_out, out_size, 0);
+		}
+	}
+
+	return spnic_msg_to_mgmt_sync(hwdev, SPNIC_MOD_HILINK, cmd, buf_in,
+				      in_size, buf_out, out_size, 0);
+}
+
+static int mag_msg_to_mgmt_sync(void *hwdev, u16 cmd, void *buf_in, u16 in_size,
+				void *buf_out, u16 *out_size)
+{
+	return _mag_msg_to_mgmt_sync(hwdev, cmd, buf_in, in_size, buf_out,
+				     out_size);
+}
diff --git a/drivers/net/spnic/base/spnic_nic_cfg.h b/drivers/net/spnic/base/spnic_nic_cfg.h
new file mode 100644
index 0000000000..c60b28a93f
--- /dev/null
+++ b/drivers/net/spnic/base/spnic_nic_cfg.h
@@ -0,0 +1,180 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Ramaxel Memory Technology, Ltd
+ */
+
+#ifndef _SPNIC_NIC_CFG_H_
+#define _SPNIC_NIC_CFG_H_
+
+#ifndef ETH_ALEN
+#define ETH_ALEN			6
+#endif
+
+#define OS_VF_ID_TO_HW(os_vf_id) ((os_vf_id) + 1)
+#define HW_VF_ID_TO_OS(hw_vf_id) ((hw_vf_id) - 1)
+
+#define SPNIC_PF_SET_VF_ALREADY		0x4
+#define SPNIC_MGMT_STATUS_EXIST		0x6
+#define CHECK_IPSU_15BIT		0x8000
+
+/* Structures for port info */
+struct nic_port_info {
+	u8 port_type;
+	u8 autoneg_cap;
+	u8 autoneg_state;
+	u8 duplex;
+	u8 speed;
+	u8 fec;
+};
+
+enum spnic_link_status {
+	SPNIC_LINK_DOWN = 0,
+	SPNIC_LINK_UP
+};
+
+enum nic_media_type {
+	MEDIA_UNKNOWN = -1,
+	MEDIA_FIBRE = 0,
+	MEDIA_COPPER,
+	MEDIA_BACKPLANE
+};
+
+enum nic_speed_level {
+	LINK_SPEED_10MB = 0,
+	LINK_SPEED_100MB,
+	LINK_SPEED_1GB,
+	LINK_SPEED_10GB,
+	LINK_SPEED_25GB,
+	LINK_SPEED_40GB,
+	LINK_SPEED_100GB,
+	LINK_SPEED_LEVELS,
+};
+
+struct spnic_port_mac_set {
+	struct mgmt_msg_head msg_head;
+
+	u16 func_id;
+	u16 vlan_id;
+	u16 rsvd1;
+	u8 mac[ETH_ALEN];
+};
+
+struct spnic_port_mac_update {
+	struct mgmt_msg_head msg_head;
+
+	u16 func_id;
+	u16 vlan_id;
+	u16 rsvd1;
+	u8 old_mac[ETH_ALEN];
+	u16 rsvd2;
+	u8 new_mac[ETH_ALEN];
+};
+
+struct spnic_cmd_port_info {
+	struct mgmt_msg_head msg_head;
+
+	u8 port_id;
+	u8 rsvd1[3];
+	u8 port_type;
+	u8 autoneg_cap;
+	u8 autoneg_state;
+	u8 duplex;
+	u8 speed;
+	u8 fec;
+	u16 rsvd2;
+	u32 rsvd3[4];
+};
+
+struct spnic_cmd_link_state {
+	struct mgmt_msg_head msg_head;
+
+	u8 port_id;
+	u8 state;
+	u16 rsvd1;
+};
+
+int spnic_l2nic_msg_to_mgmt_sync(void *hwdev, u16 cmd, void *buf_in, u16 in_size,
+			   void *buf_out, u16 *out_size);
+
+/**
+ * Update MAC address to hardware
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] old_mac
+ *   Old MAC addr to delete
+ * @param[in] new_mac
+ *   New MAC addr to update
+ * @param[in] vlan_id
+ *   Vlan id
+ * @param func_id
+ *   Function index
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_update_mac(void *hwdev, u8 *old_mac, u8 *new_mac, u16 vlan_id,
+		     u16 func_id);
+
+/**
+ * Get the default mac address
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] mac_addr
+ *   Mac address from hardware
+ * @param[in] ether_len
+ *   The length of mac address
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_get_default_mac(void *hwdev, u8 *mac_addr, int ether_len);
+
+/**
+ * Set mac address
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] mac_addr
+ *   Mac address from hardware
+ * @param[in] vlan_id
+ *   Vlan id
+ * @param[in] func_id
+ *   Function index
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_set_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id);
+
+/**
+ * Delete MAC address
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] mac_addr
+ *   MAC address from hardware
+ * @param[in] vlan_id
+ *   Vlan id
+ * @param[in] func_id
+ *   Function index
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_del_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id);
+
+/**
+ * Get port info
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[out] port_info
+ *   Port info, including autoneg, port type, duplex, speed and fec mode
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_get_port_info(void *hwdev, struct nic_port_info *port_info);
+
+#endif /* _SPNIC_NIC_CFG_H_ */
diff --git a/drivers/net/spnic/base/spnic_nic_event.c b/drivers/net/spnic/base/spnic_nic_event.c
index b8dbe65646..1a8f723a77 100644
--- a/drivers/net/spnic/base/spnic_nic_event.c
+++ b/drivers/net/spnic/base/spnic_nic_event.c
@@ -9,16 +9,39 @@ 
 #include "spnic_hwif.h"
 #include "spnic_hwdev.h"
 #include "spnic_mgmt.h"
+#include "spnic_nic_cfg.h"
 #include "spnic_hwdev.h"
 #include "spnic_nic_event.h"
 
-static void spnic_get_port_link_info(u8 link_state, struct rte_eth_link *link)
+void spnic_get_port_link_info(struct spnic_hwdev *hwdev, u8 link_state,
+		   struct rte_eth_link *link)
 {
+	uint32_t port_speed[LINK_SPEED_LEVELS] = {ETH_SPEED_NUM_10M,
+					ETH_SPEED_NUM_100M, ETH_SPEED_NUM_1G,
+					ETH_SPEED_NUM_10G, ETH_SPEED_NUM_25G,
+					ETH_SPEED_NUM_40G, ETH_SPEED_NUM_100G};
+	struct nic_port_info port_info = {0};
+	int err;
+
 	if (!link_state) {
 		link->link_status = ETH_LINK_DOWN;
 		link->link_speed = ETH_SPEED_NUM_NONE;
 		link->link_duplex = ETH_LINK_HALF_DUPLEX;
 		link->link_autoneg = ETH_LINK_FIXED;
+	} else {
+		link->link_status = ETH_LINK_UP;
+
+		err = spnic_get_port_info(hwdev, &port_info);
+		if (err) {
+			link->link_speed = ETH_SPEED_NUM_NONE;
+			link->link_duplex = ETH_LINK_FULL_DUPLEX;
+			link->link_autoneg = ETH_LINK_FIXED;
+		} else {
+			link->link_speed = port_speed[port_info.speed %
+						LINK_SPEED_LEVELS];
+			link->link_duplex = port_info.duplex;
+			link->link_autoneg = port_info.autoneg_state;
+		}
 	}
 }
 
@@ -51,7 +74,7 @@  static void link_status_event_handler(void *hwdev, void *buf_in,
 	spnic_link_event_stats(hwdev, link_status->state);
 
 	/* Link event reported only after set vport enable */
-	spnic_get_port_link_info(link_status->state, &link);
+	spnic_get_port_link_info(dev, link_status->state, &link);
 	err = rte_eth_linkstatus_set((struct rte_eth_dev *)(dev->eth_dev),
 				     &link);
 	if (!err)
diff --git a/drivers/net/spnic/base/spnic_nic_event.h b/drivers/net/spnic/base/spnic_nic_event.h
index bf1c15941e..89d09a2b24 100644
--- a/drivers/net/spnic/base/spnic_nic_event.h
+++ b/drivers/net/spnic/base/spnic_nic_event.h
@@ -5,13 +5,8 @@ 
 #ifndef _SPNIC_NIC_EVENT_H_
 #define _SPNIC_NIC_EVENT_H_
 
-struct spnic_cmd_link_state {
-	struct mgmt_msg_head msg_head;
-
-	u8 port_id;
-	u8 state;
-	u16 rsvd1;
-};
+void spnic_get_port_link_info(struct spnic_hwdev *hwdev, u8 link_state,
+		   struct rte_eth_link *link);
 
 void spnic_pf_event_handler(void *hwdev, __rte_unused void *pri_handle,
 			    u16 cmd, void *buf_in, u16 in_size,
diff --git a/drivers/net/spnic/spnic_ethdev.c b/drivers/net/spnic/spnic_ethdev.c
index 36d0495ac8..5e26cbe041 100644
--- a/drivers/net/spnic/spnic_ethdev.c
+++ b/drivers/net/spnic/spnic_ethdev.c
@@ -5,14 +5,23 @@ 
 #include <rte_pci.h>
 #include <rte_bus_pci.h>
 #include <ethdev_pci.h>
+#include <rte_malloc.h>
 #include <rte_errno.h>
 #include <rte_ether.h>
 
 #include "base/spnic_compat.h"
+#include "base/spnic_cmd.h"
 #include "base/spnic_csr.h"
+#include "base/spnic_wq.h"
+#include "base/spnic_eqs.h"
+#include "base/spnic_mgmt.h"
+#include "base/spnic_cmdq.h"
 #include "base/spnic_hwdev.h"
 #include "base/spnic_hwif.h"
-
+#include "base/spnic_hw_cfg.h"
+#include "base/spnic_hw_comm.h"
+#include "base/spnic_nic_cfg.h"
+#include "base/spnic_nic_event.h"
 #include "spnic_ethdev.h"
 
 /* Driver-specific log messages type */
@@ -21,6 +30,58 @@  int spnic_logtype;
 #define SPNIC_MAX_UC_MAC_ADDRS		128
 #define SPNIC_MAX_MC_MAC_ADDRS		128
 
+static void spnic_delete_mc_addr_list(struct spnic_nic_dev *nic_dev)
+{
+	u16 func_id;
+	u32 i;
+
+	func_id = spnic_global_func_id(nic_dev->hwdev);
+
+	for (i = 0; i < SPNIC_MAX_MC_MAC_ADDRS; i++) {
+		if (rte_is_zero_ether_addr(&nic_dev->mc_list[i]))
+			break;
+
+		spnic_del_mac(nic_dev->hwdev, nic_dev->mc_list[i].addr_bytes,
+			      0, func_id);
+		memset(&nic_dev->mc_list[i], 0, sizeof(struct rte_ether_addr));
+	}
+}
+
+/**
+ * Deinit mac_vlan table in hardware.
+ *
+ * @param[in] eth_dev
+ *   Pointer to ethernet device structure.
+ */
+static void spnic_deinit_mac_addr(struct rte_eth_dev *eth_dev)
+{
+	struct spnic_nic_dev *nic_dev =
+				SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
+	u16 func_id = 0;
+	int err;
+	int i;
+
+	func_id = spnic_global_func_id(nic_dev->hwdev);
+
+	for (i = 0; i < SPNIC_MAX_UC_MAC_ADDRS; i++) {
+		if (rte_is_zero_ether_addr(&eth_dev->data->mac_addrs[i]))
+			continue;
+
+		err = spnic_del_mac(nic_dev->hwdev,
+				    eth_dev->data->mac_addrs[i].addr_bytes,
+				    0, func_id);
+		if (err && err != SPNIC_PF_SET_VF_ALREADY)
+			PMD_DRV_LOG(ERR, "Delete mac table failed, dev_name: %s",
+				    eth_dev->data->name);
+
+		memset(&eth_dev->data->mac_addrs[i], 0,
+		       sizeof(struct rte_ether_addr));
+	}
+
+	/* Delete multicast mac addrs */
+	spnic_delete_mc_addr_list(nic_dev);
+}
+
 /**
  * Close the device.
  *
@@ -38,13 +99,247 @@  static int spnic_dev_close(struct rte_eth_dev *eth_dev)
 		return 0;
 	}
 
+	spnic_deinit_mac_addr(eth_dev);
+	rte_free(nic_dev->mc_list);
+
+	rte_bit_relaxed_clear32(SPNIC_DEV_INTR_EN, &nic_dev->dev_status);
+
 	spnic_free_hwdev(nic_dev->hwdev);
 
+	eth_dev->dev_ops = NULL;
+
 	rte_free(nic_dev->hwdev);
 	nic_dev->hwdev = NULL;
 
 	return 0;
 }
+/**
+ * Update MAC address
+ *
+ * @param[in] dev
+ *   Pointer to ethernet device structure.
+ * @param[in] addr
+ *   Pointer to MAC address
+ *
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+static int spnic_set_mac_addr(struct rte_eth_dev *dev,
+			      struct rte_ether_addr *addr)
+{
+	struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+	char mac_addr[RTE_ETHER_ADDR_FMT_SIZE];
+	u16 func_id;
+	int err;
+
+	if (!rte_is_valid_assigned_ether_addr(addr)) {
+		rte_ether_format_addr(mac_addr, RTE_ETHER_ADDR_FMT_SIZE, addr);
+		PMD_DRV_LOG(ERR, "Set invalid MAC address %s", mac_addr);
+		return -EINVAL;
+	}
+
+	func_id = spnic_global_func_id(nic_dev->hwdev);
+	err = spnic_update_mac(nic_dev->hwdev,
+				nic_dev->default_addr.addr_bytes,
+				addr->addr_bytes, 0, func_id);
+	if (err)
+		return err;
+
+	rte_ether_addr_copy(addr, &nic_dev->default_addr);
+	rte_ether_format_addr(mac_addr, RTE_ETHER_ADDR_FMT_SIZE,
+			      &nic_dev->default_addr);
+
+	PMD_DRV_LOG(INFO, "Set new MAC address %s", mac_addr);
+
+	return 0;
+}
+
+/**
+ * Remove a MAC address.
+ *
+ * @param[in] dev
+ *   Pointer to ethernet device structure.
+ * @param[in] index
+ *   MAC address index.
+ */
+static void spnic_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
+{
+	struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+	u16 func_id;
+	int err;
+
+	if (index >= SPNIC_MAX_UC_MAC_ADDRS) {
+		PMD_DRV_LOG(INFO, "Remove MAC index(%u) is out of range",
+			    index);
+		return;
+	}
+
+	func_id = spnic_global_func_id(nic_dev->hwdev);
+	err = spnic_del_mac(nic_dev->hwdev,
+			     dev->data->mac_addrs[index].addr_bytes,
+			     0, func_id);
+	if (err)
+		PMD_DRV_LOG(ERR, "Remove MAC index(%u) failed", index);
+}
+
+/**
+ * Add a MAC address.
+ *
+ * @param[in] dev
+ *   Pointer to ethernet device structure.
+ * @param[in] mac_addr
+ *   MAC address to register.
+ * @param[in] index
+ *   MAC address index.
+ * @param[in] vmdq
+ *   VMDq pool index to associate address with (unused_).
+ *
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+static int spnic_mac_addr_add(struct rte_eth_dev *dev,
+			      struct rte_ether_addr *mac_addr, uint32_t index,
+			      __rte_unused uint32_t vmdq)
+{
+	struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+	unsigned int i;
+	u16 func_id;
+	int err;
+
+	if (!rte_is_valid_assigned_ether_addr(mac_addr)) {
+		PMD_DRV_LOG(ERR, "Add invalid MAC address");
+		return -EINVAL;
+	}
+
+	if (index >= SPNIC_MAX_UC_MAC_ADDRS) {
+		PMD_DRV_LOG(ERR, "Add MAC index(%u) is out of range", index);
+		return -EINVAL;
+	}
+
+	/* Make sure this address doesn't already be configured */
+	for (i = 0; i < SPNIC_MAX_UC_MAC_ADDRS; i++) {
+		if (rte_is_same_ether_addr(mac_addr,
+			&dev->data->mac_addrs[i])) {
+			PMD_DRV_LOG(ERR, "MAC address is already configured");
+			return -EADDRINUSE;
+		}
+	}
+
+	func_id = spnic_global_func_id(nic_dev->hwdev);
+	err = spnic_set_mac(nic_dev->hwdev, mac_addr->addr_bytes, 0, func_id);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/**
+ * Set multicast MAC address
+ *
+ * @param[in] dev
+ *   Pointer to ethernet device structure.
+ * @param[in] mc_addr_set
+ *   Pointer to multicast MAC address
+ * @param[in] nb_mc_addr
+ *   The number of multicast MAC address to set
+ *
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+static int spnic_set_mc_addr_list(struct rte_eth_dev *dev,
+				  struct rte_ether_addr *mc_addr_set,
+				  uint32_t nb_mc_addr)
+{
+	struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+	char mac_addr[RTE_ETHER_ADDR_FMT_SIZE];
+	u16 func_id;
+	int err;
+	u32 i;
+
+	func_id = spnic_global_func_id(nic_dev->hwdev);
+
+	/* Delete old multi_cast addrs firstly */
+	spnic_delete_mc_addr_list(nic_dev);
+
+	if (nb_mc_addr > SPNIC_MAX_MC_MAC_ADDRS)
+		return -EINVAL;
+
+	for (i = 0; i < nb_mc_addr; i++) {
+		if (!rte_is_multicast_ether_addr(&mc_addr_set[i])) {
+			rte_ether_format_addr(mac_addr, RTE_ETHER_ADDR_FMT_SIZE,
+					      &mc_addr_set[i]);
+			PMD_DRV_LOG(ERR, "Set mc MAC addr failed, addr(%s) invalid",
+				    mac_addr);
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < nb_mc_addr; i++) {
+		err = spnic_set_mac(nic_dev->hwdev, mc_addr_set[i].addr_bytes,
+				    0, func_id);
+		if (err) {
+			spnic_delete_mc_addr_list(nic_dev);
+			return err;
+		}
+
+		rte_ether_addr_copy(&mc_addr_set[i], &nic_dev->mc_list[i]);
+	}
+
+	return 0;
+}
+static const struct eth_dev_ops spnic_pmd_ops = {
+	.mac_addr_set                  = spnic_set_mac_addr,
+	.mac_addr_remove               = spnic_mac_addr_remove,
+	.mac_addr_add                  = spnic_mac_addr_add,
+	.set_mc_addr_list              = spnic_set_mc_addr_list,
+};
+
+static const struct eth_dev_ops spnic_pmd_vf_ops = {
+	.mac_addr_set                  = spnic_set_mac_addr,
+	.mac_addr_remove               = spnic_mac_addr_remove,
+	.mac_addr_add                  = spnic_mac_addr_add,
+	.set_mc_addr_list              = spnic_set_mc_addr_list,
+};
+
+/**
+ * Init mac_vlan table in hardwares.
+ *
+ * @param[in] eth_dev
+ *   Pointer to ethernet device structure.
+ *
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+static int spnic_init_mac_table(struct rte_eth_dev *eth_dev)
+{
+	struct spnic_nic_dev *nic_dev =
+		SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
+	u8 addr_bytes[RTE_ETHER_ADDR_LEN];
+	u16 func_id = 0;
+	int err = 0;
+
+	err = spnic_get_default_mac(nic_dev->hwdev, addr_bytes,
+				     RTE_ETHER_ADDR_LEN);
+	if (err)
+		return err;
+
+	rte_ether_addr_copy((struct rte_ether_addr *)addr_bytes,
+			    &eth_dev->data->mac_addrs[0]);
+	if (rte_is_zero_ether_addr(&eth_dev->data->mac_addrs[0]))
+		rte_eth_random_addr(eth_dev->data->mac_addrs[0].addr_bytes);
+
+	func_id = spnic_global_func_id(nic_dev->hwdev);
+	err = spnic_set_mac(nic_dev->hwdev,
+			    eth_dev->data->mac_addrs[0].addr_bytes,
+			    0, func_id);
+	if (err && err != SPNIC_PF_SET_VF_ALREADY)
+		return err;
+
+	rte_ether_addr_copy(&eth_dev->data->mac_addrs[0],
+			    &nic_dev->default_addr);
+
+	return 0;
+}
 
 static int spnic_func_init(struct rte_eth_dev *eth_dev)
 {
@@ -63,11 +358,37 @@  static int spnic_func_init(struct rte_eth_dev *eth_dev)
 	}
 
 	nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
+	memset(nic_dev, 0, sizeof(*nic_dev));
 	snprintf(nic_dev->dev_name, sizeof(nic_dev->dev_name),
 		 "spnic-%.4x:%.2x:%.2x.%x",
 		 pci_dev->addr.domain, pci_dev->addr.bus,
 		 pci_dev->addr.devid, pci_dev->addr.function);
 
+	/* Alloc mac_addrs */
+	eth_dev->data->mac_addrs = rte_zmalloc("spnic_mac",
+		SPNIC_MAX_UC_MAC_ADDRS * sizeof(struct rte_ether_addr), 0);
+	if (!eth_dev->data->mac_addrs) {
+		PMD_DRV_LOG(ERR, "Allocate %zx bytes to store MAC addresses "
+			    "failed, dev_name: %s",
+			    SPNIC_MAX_UC_MAC_ADDRS *
+			    sizeof(struct rte_ether_addr),
+			    eth_dev->data->name);
+		err = -ENOMEM;
+		goto alloc_eth_addr_fail;
+	}
+
+	nic_dev->mc_list = rte_zmalloc("spnic_mc",
+		SPNIC_MAX_MC_MAC_ADDRS * sizeof(struct rte_ether_addr), 0);
+	if (!nic_dev->mc_list) {
+		PMD_DRV_LOG(ERR, "Allocate %zx bytes to store multicast "
+			    "addresses failed, dev_name: %s",
+			    SPNIC_MAX_MC_MAC_ADDRS *
+			    sizeof(struct rte_ether_addr),
+			    eth_dev->data->name);
+		err = -ENOMEM;
+		goto alloc_mc_list_fail;
+	}
+
 	/* Create hardware device */
 	nic_dev->hwdev = rte_zmalloc("spnic_hwdev", sizeof(*nic_dev->hwdev),
 				     RTE_CACHE_LINE_SIZE);
@@ -89,17 +410,42 @@  static int spnic_func_init(struct rte_eth_dev *eth_dev)
 		goto init_hwdev_fail;
 	}
 
+	if (SPNIC_FUNC_TYPE(nic_dev->hwdev) == TYPE_VF)
+		eth_dev->dev_ops = &spnic_pmd_vf_ops;
+	else
+		eth_dev->dev_ops = &spnic_pmd_ops;
+	err = spnic_init_mac_table(eth_dev);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Init mac table failed, dev_name: %s",
+			    eth_dev->data->name);
+		goto init_mac_table_fail;
+	}
+
+	rte_bit_relaxed_set32(SPNIC_DEV_INTR_EN, &nic_dev->dev_status);
+
 	rte_bit_relaxed_set32(SPNIC_DEV_INIT, &nic_dev->dev_status);
 	PMD_DRV_LOG(INFO, "Initialize %s in primary succeed",
 		    eth_dev->data->name);
 
 	return 0;
 
+init_mac_table_fail:
+	spnic_free_hwdev(nic_dev->hwdev);
+	eth_dev->dev_ops = NULL;
+
 init_hwdev_fail:
 	rte_free(nic_dev->hwdev);
 	nic_dev->hwdev = NULL;
 
 alloc_hwdev_mem_fail:
+	rte_free(nic_dev->mc_list);
+	nic_dev->mc_list = NULL;
+
+alloc_mc_list_fail:
+	rte_free(eth_dev->data->mac_addrs);
+	eth_dev->data->mac_addrs = NULL;
+
+alloc_eth_addr_fail:
 	PMD_DRV_LOG(ERR, "Initialize %s in primary failed",
 		    eth_dev->data->name);
 	return err;