netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v05 0/5] net: hinic3: PF initialization
@ 2025-11-06 11:15 Fan Gong
  2025-11-06 11:15 ` [PATCH net-next v05 1/5] hinic3: Add PF framework Fan Gong
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Fan Gong @ 2025-11-06 11:15 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi

This is [1/3] part of hinic3 Ethernet driver second submission.
With this patch hinic3 becomes a complete Ethernet driver with
pf and vf.

The driver parts contained in this patch:
Add support for PF framework based on the VF code.
Add PF management interfaces to communicate with HW.
Add ops to configure NIC features.
Support mac filter to unicast and multicast.
Add netdev notifier.

Changes:

PATCH 01 V01: https://lore.kernel.org/netdev/cover.1760502478.git.zhuyikai1@h-partners.com/

PATCH 01 V02: https://lore.kernel.org/netdev/cover.1760685059.git.zhuyikai1@h-partners.com/
* Change the order of hinic3_netdev_event (Jakub Kicinski)
* Use netdev_hold/put instead of dev_hold/put (Jakub Kicinski)
* Remove the semicolon at the end of switch case (Jakub Kicinski)
* Remove redundant PF judgement in hinic3_rx_tx_flush (Paven Chebbi)
* change hinic3_send_mbox_to_mgmt errcode to EFAULT (Paven Chebbi)
* Optimize hinic3_set_bdf_ctxt parameters (Paven Chebbi)
* Modify main and CC recipients (Markus Elfring)

PATCH 01 V03: https://lore.kernel.org/netdev/cover.1761362580.git.zhuyikai1@h-partners.com/
* Use disable_delayed_work_sync instead of cancel_delayed_work_sync (Paolo Abeni)
* Fill in the missing hinic3_sync_time & hinic3_free_ppf_work (Paolo Abeni)
* Refactor hinic3_mac_filter_sync to implement linux coding style(err label)
  and improve readability (Paolo Abeni & Markus Elfring)

PATCH 01 V04: https://lore.kernel.org/netdev/cover.1761711549.git.zhuyikai1@h-partners.com/
* Use linux error value(EADDRINUSE) instead of custom value in set_mac (Simon Horman)
* Use "hinic3_check_pf_set_vf_already" function instead of macro (Simon Horman)

PATCH 01 V04:
* Code format fixes: wrap the code at 80 characters (Jakub Kicinski)
* Use str_up_down instead of ternary expression (Simon Horman)
* Remove needless override of error value (Simon Horman)

Fan Gong (5):
  hinic3: Add PF framework
  hinic3: Add PF management interfaces
  hinic3: Add NIC configuration ops
  hinic3: Add mac filter ops
  hinic3: Add netdev register interfaces

 drivers/net/ethernet/huawei/hinic3/Makefile   |   1 +
 .../net/ethernet/huawei/hinic3/hinic3_csr.h   |   6 +
 .../ethernet/huawei/hinic3/hinic3_filter.c    | 418 ++++++++++++++++++
 .../ethernet/huawei/hinic3/hinic3_hw_comm.c   | 115 +++++
 .../ethernet/huawei/hinic3/hinic3_hw_comm.h   |   6 +
 .../ethernet/huawei/hinic3/hinic3_hw_intf.h   |  24 +
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.c |  97 +++-
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.h |  21 +
 .../net/ethernet/huawei/hinic3/hinic3_hwif.c  |  90 +++-
 .../net/ethernet/huawei/hinic3/hinic3_hwif.h  |  23 +
 .../net/ethernet/huawei/hinic3/hinic3_irq.c   | 138 +++++-
 .../net/ethernet/huawei/hinic3/hinic3_lld.c   |  53 ++-
 .../net/ethernet/huawei/hinic3/hinic3_main.c  | 263 ++++++++++-
 .../net/ethernet/huawei/hinic3/hinic3_mbox.c  |  55 ++-
 .../net/ethernet/huawei/hinic3/hinic3_mbox.h  |   2 +
 .../net/ethernet/huawei/hinic3/hinic3_mgmt.c  | 311 ++++++++++++-
 .../net/ethernet/huawei/hinic3/hinic3_mgmt.h  |  53 +++
 .../huawei/hinic3/hinic3_mgmt_interface.h     |  69 +++
 .../huawei/hinic3/hinic3_netdev_ops.c         | 380 ++++++++++++++++
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.c   | 284 +++++++++++-
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |  47 ++
 .../ethernet/huawei/hinic3/hinic3_nic_dev.h   |  84 +++-
 .../net/ethernet/huawei/hinic3/hinic3_rx.h    |  20 +
 .../net/ethernet/huawei/hinic3/hinic3_tx.h    |  18 +
 24 files changed, 2547 insertions(+), 31 deletions(-)
 create mode 100644 drivers/net/ethernet/huawei/hinic3/hinic3_filter.c


base-commit: 16a2206354d169bfd13552ad577e07ce66e439ab
-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH net-next v05 1/5] hinic3: Add PF framework
  2025-11-06 11:15 [PATCH net-next v05 0/5] net: hinic3: PF initialization Fan Gong
@ 2025-11-06 11:15 ` Fan Gong
  2025-11-06 14:19   ` [External] : " ALOK TIWARI
  2025-11-06 11:15 ` [PATCH net-next v05 2/5] hinic3: Add PF management interfaces Fan Gong
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Fan Gong @ 2025-11-06 11:15 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi

Add support for PF framework based on the VF code.

Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
---
 .../net/ethernet/huawei/hinic3/hinic3_csr.h   |  6 ++
 .../ethernet/huawei/hinic3/hinic3_hw_comm.c   | 61 +++++++++++++
 .../ethernet/huawei/hinic3/hinic3_hw_comm.h   |  4 +
 .../ethernet/huawei/hinic3/hinic3_hw_intf.h   | 22 +++++
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.c | 44 ++++++++-
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.h |  2 +
 .../net/ethernet/huawei/hinic3/hinic3_hwif.c  | 90 ++++++++++++++++++-
 .../net/ethernet/huawei/hinic3/hinic3_hwif.h  | 23 +++++
 .../net/ethernet/huawei/hinic3/hinic3_lld.c   | 53 ++++++++++-
 .../net/ethernet/huawei/hinic3/hinic3_main.c  | 30 +++++--
 .../net/ethernet/huawei/hinic3/hinic3_mbox.c  | 42 +++++++--
 .../huawei/hinic3/hinic3_mgmt_interface.h     |  1 +
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.c   | 61 ++++++++++++-
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |  3 +
 14 files changed, 419 insertions(+), 23 deletions(-)

diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h b/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h
index e7417e8efa99..f7083a6e7df9 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h
@@ -5,6 +5,7 @@
 #define _HINIC3_CSR_H_
 
 #define HINIC3_CFG_REGS_FLAG                  0x40000000
+#define HINIC3_MGMT_REGS_FLAG                 0xC0000000
 #define HINIC3_REGS_FLAG_MASK                 0x3FFFFFFF
 
 #define HINIC3_VF_CFG_REG_OFFSET              0x2000
@@ -24,6 +25,11 @@
 #define HINIC3_FUNC_CSR_MAILBOX_RESULT_H_OFF  (HINIC3_CFG_REGS_FLAG + 0x0108)
 #define HINIC3_FUNC_CSR_MAILBOX_RESULT_L_OFF  (HINIC3_CFG_REGS_FLAG + 0x010C)
 
+#define HINIC3_HOST_CSR_BASE_ADDR             (HINIC3_MGMT_REGS_FLAG + 0x6000)
+#define HINIC3_PPF_ELECTION_OFFSET            0x0
+#define HINIC3_CSR_PPF_ELECTION_ADDR  \
+	(HINIC3_HOST_CSR_BASE_ADDR + HINIC3_PPF_ELECTION_OFFSET)
+
 #define HINIC3_CSR_DMA_ATTR_TBL_ADDR          (HINIC3_CFG_REGS_FLAG + 0x380)
 #define HINIC3_CSR_DMA_ATTR_INDIR_IDX_ADDR    (HINIC3_CFG_REGS_FLAG + 0x390)
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
index 89638813df40..09dae2ef610c 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
@@ -314,6 +314,8 @@ int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev)
 			ret = -EFAULT;
 	}
 
+	hinic3_set_pf_status(hwif, HINIC3_PF_STATUS_FLR_START_FLAG);
+
 	clr_res.func_id = hwif->attr.func_global_idx;
 	msg_params.buf_in = &clr_res;
 	msg_params.in_size = sizeof(clr_res);
@@ -337,6 +339,65 @@ int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev)
 	return ret;
 }
 
+int hinic3_set_bdf_ctxt(struct hinic3_hwdev *hwdev,
+			struct comm_cmd_bdf_info *bdf_info)
+{
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	mgmt_msg_params_init_default(&msg_params, bdf_info, sizeof(*bdf_info));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
+				       COMM_CMD_SEND_BDF_INFO, &msg_params);
+	if (err || bdf_info->head.status) {
+		dev_err(hwdev->dev,
+			"Failed to set bdf info to fw, err: %d, status: 0x%x\n",
+			err, bdf_info->head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int hinic3_sync_time(struct hinic3_hwdev *hwdev, u64 time)
+{
+	struct comm_cmd_sync_time time_info = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	time_info.mstime = time;
+
+	mgmt_msg_params_init_default(&msg_params, &time_info,
+				     sizeof(time_info));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
+				       COMM_CMD_SYNC_TIME, &msg_params);
+	if (err || time_info.head.status) {
+		dev_err(hwdev->dev,
+			"Failed to sync time to mgmt, err: %d, status: 0x%x\n",
+			err, time_info.head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev)
+{
+	struct timespec64 ts = {};
+	u64 time;
+	int err;
+
+	ktime_get_real_ts64(&ts);
+	time = (u64)(ts.tv_sec * MSEC_PER_SEC + ts.tv_nsec / NSEC_PER_MSEC);
+
+	err = hinic3_sync_time(hwdev, time);
+	if (err)
+		dev_err(hwdev->dev,
+			"Synchronize UTC time to firmware failed, errno:%d.\n",
+			err);
+}
+
 static int get_hw_rx_buf_size_idx(int rx_buf_sz, u16 *buf_sz_idx)
 {
 	/* Supported RX buffer sizes in bytes. Configured by array index. */
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
index 304f5691f0c2..c9c6b4fbcb12 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
@@ -40,6 +40,10 @@ int hinic3_set_wq_page_size(struct hinic3_hwdev *hwdev, u16 func_idx,
 			    u32 page_size);
 int hinic3_set_cmdq_depth(struct hinic3_hwdev *hwdev, u16 cmdq_depth);
 int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev);
+int hinic3_set_bdf_ctxt(struct hinic3_hwdev *hwdev,
+			struct comm_cmd_bdf_info *bdf_info);
+void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev);
+
 int hinic3_set_root_ctxt(struct hinic3_hwdev *hwdev, u32 rq_depth, u32 sq_depth,
 			 int rx_buf_sz);
 int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
index 623cf2d14cbc..a0422ec0500f 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
@@ -110,6 +110,10 @@ enum comm_cmd {
 	COMM_CMD_CFG_MSIX_CTRL_REG       = 23,
 	COMM_CMD_SET_CEQ_CTRL_REG        = 24,
 	COMM_CMD_SET_DMA_ATTR            = 25,
+
+	/* Commands for obtaining information */
+	COMM_CMD_SYNC_TIME               = 62,
+	COMM_CMD_SEND_BDF_INFO           = 64,
 };
 
 struct comm_cmd_cfg_msix_ctrl_reg {
@@ -251,6 +255,24 @@ struct comm_cmd_clear_resource {
 	u16                  rsvd1[3];
 };
 
+struct comm_cmd_sync_time {
+	struct mgmt_msg_head head;
+
+	u64                  mstime;
+	u64                  rsvd1;
+};
+
+struct comm_cmd_bdf_info {
+	struct mgmt_msg_head head;
+
+	u16                  function_idx;
+	u8                   rsvd1[2];
+	u8                   bus;
+	u8                   device;
+	u8                   function;
+	u8                   rsvd2[5];
+};
+
 /* Services supported by HW. HW uses these values when delivering events.
  * HW supports multiple services that are not yet supported by driver
  * (e.g. RoCE).
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
index 95a213133be9..2b1f1036620e 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
@@ -13,6 +13,8 @@
 #define HINIC3_PCIE_SNOOP        0
 #define HINIC3_PCIE_TPH_DISABLE  0
 
+#define HINIC3_SYNFW_TIME_PERIOD  (60 * 60 * 1000)
+
 #define HINIC3_DMA_ATTR_INDIR_IDX_MASK          GENMASK(9, 0)
 #define HINIC3_DMA_ATTR_INDIR_IDX_SET(val, member)  \
 	FIELD_PREP(HINIC3_DMA_ATTR_INDIR_##member##_MASK, val)
@@ -38,6 +40,7 @@
 #define HINIC3_WQ_MAX_REQ       10
 
 enum hinic3_hwdev_init_state {
+	HINIC3_HWDEV_MGMT_INITED = 1,
 	HINIC3_HWDEV_MBOX_INITED = 2,
 	HINIC3_HWDEV_CMDQ_INITED = 3,
 };
@@ -419,6 +422,8 @@ static int hinic3_init_comm_ch(struct hinic3_hwdev *hwdev)
 		goto err_clear_func_svc_used_state;
 	}
 
+	hinic3_set_pf_status(hwdev->hwif, HINIC3_PF_STATUS_ACTIVE_FLAG);
+
 	return 0;
 
 err_clear_func_svc_used_state:
@@ -431,11 +436,43 @@ static int hinic3_init_comm_ch(struct hinic3_hwdev *hwdev)
 
 static void hinic3_uninit_comm_ch(struct hinic3_hwdev *hwdev)
 {
+	hinic3_set_pf_status(hwdev->hwif, HINIC3_PF_STATUS_INIT);
 	hinic3_free_cmdqs_channel(hwdev);
 	hinic3_set_func_svc_used_state(hwdev, COMM_FUNC_SVC_T_COMM, 0);
 	free_base_mgmt_channel(hwdev);
 }
 
+static void hinic3_auto_sync_time_work(struct work_struct *work)
+{
+	struct delayed_work *delay = to_delayed_work(work);
+	struct hinic3_hwdev *hwdev;
+
+	hwdev = container_of(delay, struct hinic3_hwdev, sync_time_task);
+
+	hinic3_sync_time_to_fw(hwdev);
+
+	queue_delayed_work(hwdev->workq, &hwdev->sync_time_task,
+			   msecs_to_jiffies(HINIC3_SYNFW_TIME_PERIOD));
+}
+
+static void hinic3_init_ppf_work(struct hinic3_hwdev *hwdev)
+{
+	if (hinic3_ppf_idx(hwdev) != hinic3_global_func_id(hwdev))
+		return;
+
+	INIT_DELAYED_WORK(&hwdev->sync_time_task, hinic3_auto_sync_time_work);
+	queue_delayed_work(hwdev->workq, &hwdev->sync_time_task,
+			   msecs_to_jiffies(HINIC3_SYNFW_TIME_PERIOD));
+}
+
+static void hinic3_free_ppf_work(struct hinic3_hwdev *hwdev)
+{
+	if (hinic3_ppf_idx(hwdev) != hinic3_global_func_id(hwdev))
+		return;
+
+	disable_delayed_work_sync(&hwdev->sync_time_task);
+}
+
 static DEFINE_IDA(hinic3_adev_ida);
 
 static int hinic3_adev_idx_alloc(void)
@@ -498,15 +535,19 @@ int hinic3_init_hwdev(struct pci_dev *pdev)
 		goto err_uninit_comm_ch;
 	}
 
+	hinic3_init_ppf_work(hwdev);
+
 	err = hinic3_set_comm_features(hwdev, hwdev->features,
 				       COMM_MAX_FEATURE_QWORD);
 	if (err) {
 		dev_err(hwdev->dev, "Failed to set comm features\n");
-		goto err_uninit_comm_ch;
+		goto err_free_ppf_work;
 	}
 
 	return 0;
 
+err_free_ppf_work:
+	hinic3_free_ppf_work(hwdev);
 err_uninit_comm_ch:
 	hinic3_uninit_comm_ch(hwdev);
 err_free_cfg_mgmt:
@@ -528,6 +569,7 @@ void hinic3_free_hwdev(struct hinic3_hwdev *hwdev)
 	u64 drv_features[COMM_MAX_FEATURE_QWORD] = {};
 
 	hinic3_set_comm_features(hwdev, drv_features, COMM_MAX_FEATURE_QWORD);
+	hinic3_free_ppf_work(hwdev);
 	hinic3_func_rx_tx_flush(hwdev);
 	hinic3_uninit_comm_ch(hwdev);
 	hinic3_free_cfg_mgmt(hwdev);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
index 62e2745e9316..78cface6ddd7 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
@@ -28,6 +28,7 @@ struct hinic3_pcidev {
 
 	void __iomem         *cfg_reg_base;
 	void __iomem         *intr_reg_base;
+	void __iomem         *mgmt_reg_base;
 	void __iomem         *db_base;
 	u64                  db_dwqe_len;
 	u64                  db_base_phy;
@@ -48,6 +49,7 @@ struct hinic3_hwdev {
 	struct hinic3_ceqs          *ceqs;
 	struct hinic3_mbox          *mbox;
 	struct hinic3_cmdqs         *cmdqs;
+	struct delayed_work         sync_time_task;
 	struct workqueue_struct     *workq;
 	/* protect channel init and uninit */
 	spinlock_t                  channel_lock;
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c
index f76f140fb6f7..801f48e241f8 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c
@@ -31,6 +31,7 @@
 #define HINIC3_AF0_GET(val, member) \
 	FIELD_GET(HINIC3_AF0_##member##_MASK, val)
 
+#define HINIC3_AF1_PPF_IDX_MASK           GENMASK(5, 0)
 #define HINIC3_AF1_AEQS_PER_FUNC_MASK     GENMASK(9, 8)
 #define HINIC3_AF1_MGMT_INIT_STATUS_MASK  BIT(30)
 #define HINIC3_AF1_GET(val, member) \
@@ -41,6 +42,10 @@
 #define HINIC3_AF2_GET(val, member) \
 	FIELD_GET(HINIC3_AF2_##member##_MASK, val)
 
+#define HINIC3_AF3_GLOBAL_VF_ID_OF_PF_MASK  GENMASK(27, 16)
+#define HINIC3_AF3_GET(val, member) \
+	FIELD_GET(HINIC3_AF3_##member##_MASK, val)
+
 #define HINIC3_AF4_DOORBELL_CTRL_MASK  BIT(0)
 #define HINIC3_AF4_GET(val, member) \
 	FIELD_GET(HINIC3_AF4_##member##_MASK, val)
@@ -54,9 +59,17 @@
 #define HINIC3_AF6_PF_STATUS_MASK     GENMASK(15, 0)
 #define HINIC3_AF6_FUNC_MAX_SQ_MASK   GENMASK(31, 23)
 #define HINIC3_AF6_MSIX_FLEX_EN_MASK  BIT(22)
+#define HINIC3_AF6_SET(val, member) \
+	FIELD_PREP(HINIC3_AF6_##member##_MASK, val)
 #define HINIC3_AF6_GET(val, member) \
 	FIELD_GET(HINIC3_AF6_##member##_MASK, val)
 
+#define HINIC3_PPF_ELECTION_IDX_MASK  GENMASK(5, 0)
+#define HINIC3_PPF_ELECTION_SET(val, member) \
+	FIELD_PREP(HINIC3_PPF_ELECTION_##member##_MASK, val)
+#define HINIC3_PPF_ELECTION_GET(val, member) \
+	FIELD_GET(HINIC3_PPF_ELECTION_##member##_MASK, val)
+
 #define HINIC3_GET_REG_ADDR(reg)  ((reg) & (HINIC3_REGS_FLAG_MASK))
 
 static void __iomem *hinic3_reg_addr(struct hinic3_hwif *hwif, u32 reg)
@@ -105,12 +118,15 @@ static void set_hwif_attr(struct hinic3_func_attr *attr, u32 attr0, u32 attr1,
 	attr->pci_intf_idx = HINIC3_AF0_GET(attr0, PCI_INTF_IDX);
 	attr->func_type = HINIC3_AF0_GET(attr0, FUNC_TYPE);
 
+	attr->ppf_idx = HINIC3_AF1_GET(attr1, PPF_IDX);
 	attr->num_aeqs = BIT(HINIC3_AF1_GET(attr1, AEQS_PER_FUNC));
 	attr->num_ceqs = HINIC3_AF2_GET(attr2, CEQS_PER_FUNC);
 	attr->num_irqs = HINIC3_AF2_GET(attr2, IRQS_PER_FUNC);
 	if (attr->num_irqs > HINIC3_MAX_MSIX_ENTRY)
 		attr->num_irqs = HINIC3_MAX_MSIX_ENTRY;
 
+	attr->global_vf_id_of_pf = HINIC3_AF3_GET(attr3, GLOBAL_VF_ID_OF_PF);
+
 	attr->num_sq = HINIC3_AF6_GET(attr6, FUNC_MAX_SQ);
 	attr->msix_flex_en = HINIC3_AF6_GET(attr6, MSIX_FLEX_EN);
 }
@@ -187,6 +203,28 @@ void hinic3_toggle_doorbell(struct hinic3_hwif *hwif,
 	hinic3_hwif_write_reg(hwif, addr, attr4);
 }
 
+static void hinic3_set_ppf(struct hinic3_hwdev *hwdev)
+{
+	struct hinic3_hwif *hwif = hwdev->hwif;
+	struct hinic3_func_attr *attr;
+	u32 addr, val;
+
+	if (HINIC3_IS_VF(hwdev))
+		return;
+
+	/* Read Modify Write */
+	attr = &hwif->attr;
+	addr = HINIC3_CSR_PPF_ELECTION_ADDR;
+	val = hinic3_hwif_read_reg(hwif, addr);
+	val &= ~HINIC3_PPF_ELECTION_IDX_MASK;
+	val |= HINIC3_PPF_ELECTION_SET(attr->func_global_idx, IDX);
+	hinic3_hwif_write_reg(hwif, addr, val);
+
+	/* Check PPF index */
+	val = hinic3_hwif_read_reg(hwif, addr);
+	attr->ppf_idx = HINIC3_PPF_ELECTION_GET(val, IDX);
+}
+
 static int db_area_idx_init(struct hinic3_hwif *hwif, u64 db_base_phy,
 			    u8 __iomem *db_base, u64 db_dwqe_len)
 {
@@ -366,6 +404,27 @@ static int wait_until_doorbell_and_outbound_enabled(struct hinic3_hwif *hwif)
 				       USEC_PER_MSEC);
 }
 
+void hinic3_set_pf_status(struct hinic3_hwif *hwif,
+			  enum hinic3_pf_status status)
+{
+	u32 attr6 = hinic3_hwif_read_reg(hwif, HINIC3_CSR_FUNC_ATTR6_ADDR);
+
+	attr6 &= ~HINIC3_AF6_PF_STATUS_MASK;
+	attr6 |= HINIC3_AF6_SET(status, PF_STATUS);
+
+	if (hwif->attr.func_type == HINIC3_FUNC_TYPE_VF)
+		return;
+
+	hinic3_hwif_write_reg(hwif, HINIC3_CSR_FUNC_ATTR6_ADDR, attr6);
+}
+
+enum hinic3_pf_status hinic3_get_pf_status(struct hinic3_hwif *hwif)
+{
+	u32 attr6 = hinic3_hwif_read_reg(hwif, HINIC3_CSR_FUNC_ATTR6_ADDR);
+
+	return HINIC3_AF6_GET(attr6, PF_STATUS);
+}
+
 int hinic3_init_hwif(struct hinic3_hwdev *hwdev)
 {
 	struct hinic3_pcidev *pci_adapter = hwdev->adapter;
@@ -378,9 +437,15 @@ int hinic3_init_hwif(struct hinic3_hwdev *hwdev)
 		return -ENOMEM;
 
 	hwdev->hwif = hwif;
-	hwif->cfg_regs_base = (u8 __iomem *)pci_adapter->cfg_reg_base +
+	/* if function is VF, mgmt_regs_base will be NULL */
+	hwif->cfg_regs_base = pci_adapter->mgmt_reg_base ?
+			      pci_adapter->cfg_reg_base :
+			      (u8 __iomem *)pci_adapter->cfg_reg_base +
 			      HINIC3_VF_CFG_REG_OFFSET;
 
+	hwif->intr_regs_base = pci_adapter->intr_reg_base;
+	hwif->mgmt_regs_base = pci_adapter->mgmt_reg_base;
+
 	err = db_area_idx_init(hwif, pci_adapter->db_base_phy,
 			       pci_adapter->db_base,
 			       pci_adapter->db_dwqe_len);
@@ -412,7 +477,15 @@ int hinic3_init_hwif(struct hinic3_hwdev *hwdev)
 		goto err_free_db_area_idx;
 	}
 
+	hinic3_set_ppf(hwdev);
+
 	disable_all_msix(hwdev);
+	/* disable mgmt cpu from reporting any event */
+	hinic3_set_pf_status(hwdev->hwif, HINIC3_PF_STATUS_INIT);
+
+	dev_dbg(hwdev->dev, "global_func_idx: %u, func_type: %d, host_id: %u, ppf: %u\n",
+		hwif->attr.func_global_idx, hwif->attr.func_type,
+		hwif->attr.pci_intf_idx, hwif->attr.ppf_idx);
 
 	return 0;
 
@@ -434,3 +507,18 @@ u16 hinic3_global_func_id(struct hinic3_hwdev *hwdev)
 {
 	return hwdev->hwif->attr.func_global_idx;
 }
+
+u8 hinic3_pf_id_of_vf(struct hinic3_hwdev *hwdev)
+{
+	return hwdev->hwif->attr.port_to_port_idx;
+}
+
+u16 hinic3_glb_pf_vf_offset(struct hinic3_hwdev *hwdev)
+{
+	return hwdev->hwif->attr.global_vf_id_of_pf;
+}
+
+u8 hinic3_ppf_idx(struct hinic3_hwdev *hwdev)
+{
+	return hwdev->hwif->attr.ppf_idx;
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h
index c02904e861cc..445bf7fa79b4 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h
@@ -10,6 +10,7 @@
 struct hinic3_hwdev;
 
 enum hinic3_func_type {
+	HINIC3_FUNC_TYPE_PF = 0,
 	HINIC3_FUNC_TYPE_VF = 1,
 };
 
@@ -38,6 +39,8 @@ static_assert(sizeof(struct hinic3_func_attr) == 20);
 
 struct hinic3_hwif {
 	u8 __iomem              *cfg_regs_base;
+	u8 __iomem              *intr_regs_base;
+	u8 __iomem              *mgmt_regs_base;
 	u64                     db_base_phy;
 	u64                     db_dwqe_len;
 	u8 __iomem              *db_base;
@@ -50,6 +53,13 @@ enum hinic3_outbound_ctrl {
 	DISABLE_OUTBOUND = 0x1,
 };
 
+enum hinic3_pf_status {
+	HINIC3_PF_STATUS_INIT            = 0x0,
+	HINIC3_PF_STATUS_ACTIVE_FLAG     = 0x11,
+	HINIC3_PF_STATUS_FLR_START_FLAG  = 0x12,
+	HINIC3_PF_STATUS_FLR_FINISH_FLAG = 0x13,
+};
+
 enum hinic3_doorbell_ctrl {
 	ENABLE_DOORBELL  = 0,
 	DISABLE_DOORBELL = 1,
@@ -65,6 +75,12 @@ enum hinic3_msix_auto_mask {
 	HINIC3_SET_MSIX_AUTO_MASK,
 };
 
+#define HINIC3_FUNC_TYPE(hwdev)  ((hwdev)->hwif->attr.func_type)
+#define HINIC3_IS_PF(hwdev)  \
+	(HINIC3_FUNC_TYPE(hwdev) == HINIC3_FUNC_TYPE_PF)
+#define HINIC3_IS_VF(hwdev)  \
+	(HINIC3_FUNC_TYPE(hwdev) == HINIC3_FUNC_TYPE_VF)
+
 u32 hinic3_hwif_read_reg(struct hinic3_hwif *hwif, u32 reg);
 void hinic3_hwif_write_reg(struct hinic3_hwif *hwif, u32 reg, u32 val);
 
@@ -75,6 +91,10 @@ int hinic3_alloc_db_addr(struct hinic3_hwdev *hwdev, void __iomem **db_base,
 			 void __iomem **dwqe_base);
 void hinic3_free_db_addr(struct hinic3_hwdev *hwdev, const u8 __iomem *db_base);
 
+void hinic3_set_pf_status(struct hinic3_hwif *hwif,
+			  enum hinic3_pf_status status);
+enum hinic3_pf_status hinic3_get_pf_status(struct hinic3_hwif *hwif);
+
 int hinic3_init_hwif(struct hinic3_hwdev *hwdev);
 void hinic3_free_hwif(struct hinic3_hwdev *hwdev);
 
@@ -86,5 +106,8 @@ void hinic3_set_msix_auto_mask_state(struct hinic3_hwdev *hwdev, u16 msix_idx,
 				     enum hinic3_msix_auto_mask flag);
 
 u16 hinic3_global_func_id(struct hinic3_hwdev *hwdev);
+u8 hinic3_pf_id_of_vf(struct hinic3_hwdev *hwdev);
+u16 hinic3_glb_pf_vf_offset(struct hinic3_hwdev *hwdev);
+u8 hinic3_ppf_idx(struct hinic3_hwdev *hwdev);
 
 #endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c
index 3db8241a3b0c..2b77fea1e0b3 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c
@@ -5,15 +5,22 @@
 #include <linux/iopoll.h>
 
 #include "hinic3_hw_cfg.h"
+#include "hinic3_hw_comm.h"
 #include "hinic3_hwdev.h"
+#include "hinic3_hwif.h"
 #include "hinic3_lld.h"
 #include "hinic3_mgmt.h"
 #include "hinic3_pci_id_tbl.h"
 
 #define HINIC3_VF_PCI_CFG_REG_BAR  0
+#define HINIC3_PF_PCI_CFG_REG_BAR  1
 #define HINIC3_PCI_INTR_REG_BAR    2
+/* Only PF has mgmt bar */
+#define HINIC3_PCI_MGMT_REG_BAR    3
 #define HINIC3_PCI_DB_BAR          4
 
+#define HINIC3_IS_VF_DEV(pdev)     ((pdev)->device == PCI_DEV_ID_HINIC3_VF)
+
 #define HINIC3_EVENT_POLL_SLEEP_US   1000
 #define HINIC3_EVENT_POLL_TIMEOUT_US 10000000
 
@@ -181,8 +188,12 @@ void hinic3_adev_event_unregister(struct auxiliary_device *adev)
 static int hinic3_mapping_bar(struct pci_dev *pdev,
 			      struct hinic3_pcidev *pci_adapter)
 {
-	pci_adapter->cfg_reg_base = pci_ioremap_bar(pdev,
-						    HINIC3_VF_PCI_CFG_REG_BAR);
+	int cfg_bar;
+
+	cfg_bar = HINIC3_IS_VF_DEV(pdev) ?
+			HINIC3_VF_PCI_CFG_REG_BAR : HINIC3_PF_PCI_CFG_REG_BAR;
+
+	pci_adapter->cfg_reg_base = pci_ioremap_bar(pdev, cfg_bar);
 	if (!pci_adapter->cfg_reg_base) {
 		dev_err(&pdev->dev, "Failed to map configuration regs\n");
 		return -ENOMEM;
@@ -195,16 +206,28 @@ static int hinic3_mapping_bar(struct pci_dev *pdev,
 		goto err_unmap_cfg_reg_base;
 	}
 
+	if (!HINIC3_IS_VF_DEV(pdev)) {
+		pci_adapter->mgmt_reg_base =
+			pci_ioremap_bar(pdev, HINIC3_PCI_MGMT_REG_BAR);
+		if (!pci_adapter->mgmt_reg_base) {
+			dev_err(&pdev->dev, "Failed to map mgmt regs\n");
+			goto err_unmap_intr_reg_base;
+		}
+	}
+
 	pci_adapter->db_base_phy = pci_resource_start(pdev, HINIC3_PCI_DB_BAR);
 	pci_adapter->db_dwqe_len = pci_resource_len(pdev, HINIC3_PCI_DB_BAR);
 	pci_adapter->db_base = pci_ioremap_bar(pdev, HINIC3_PCI_DB_BAR);
 	if (!pci_adapter->db_base) {
 		dev_err(&pdev->dev, "Failed to map doorbell regs\n");
-		goto err_unmap_intr_reg_base;
+		goto err_unmap_mgmt_reg_base;
 	}
 
 	return 0;
 
+err_unmap_mgmt_reg_base:
+	if (!HINIC3_IS_VF_DEV(pdev))
+		iounmap(pci_adapter->mgmt_reg_base);
 err_unmap_intr_reg_base:
 	iounmap(pci_adapter->intr_reg_base);
 
@@ -217,6 +240,8 @@ static int hinic3_mapping_bar(struct pci_dev *pdev,
 static void hinic3_unmapping_bar(struct hinic3_pcidev *pci_adapter)
 {
 	iounmap(pci_adapter->db_base);
+	if (!HINIC3_IS_VF_DEV(pci_adapter->pdev))
+		iounmap(pci_adapter->mgmt_reg_base);
 	iounmap(pci_adapter->intr_reg_base);
 	iounmap(pci_adapter->cfg_reg_base);
 }
@@ -295,6 +320,9 @@ static int hinic3_func_init(struct pci_dev *pdev,
 		return err;
 	}
 
+	if (HINIC3_IS_PF(pci_adapter->hwdev))
+		hinic3_sync_time_to_fw(pci_adapter->hwdev);
+
 	err = hinic3_attach_aux_devices(pci_adapter->hwdev);
 	if (err)
 		goto err_free_hwdev;
@@ -311,6 +339,8 @@ static void hinic3_func_uninit(struct pci_dev *pdev)
 {
 	struct hinic3_pcidev *pci_adapter = pci_get_drvdata(pdev);
 
+	/* disable mgmt reporting before flushing mgmt work-queue. */
+	hinic3_set_pf_status(pci_adapter->hwdev->hwif, HINIC3_PF_STATUS_INIT);
 	hinic3_flush_mgmt_workq(pci_adapter->hwdev);
 	hinic3_detach_aux_devices(pci_adapter->hwdev);
 	hinic3_free_hwdev(pci_adapter->hwdev);
@@ -319,6 +349,7 @@ static void hinic3_func_uninit(struct pci_dev *pdev)
 static int hinic3_probe_func(struct hinic3_pcidev *pci_adapter)
 {
 	struct pci_dev *pdev = pci_adapter->pdev;
+	struct comm_cmd_bdf_info bdf_info = {};
 	int err;
 
 	err = hinic3_mapping_bar(pdev, pci_adapter);
@@ -331,8 +362,24 @@ static int hinic3_probe_func(struct hinic3_pcidev *pci_adapter)
 	if (err)
 		goto err_unmap_bar;
 
+	if (HINIC3_IS_PF(pci_adapter->hwdev)) {
+		bdf_info.function_idx =
+			hinic3_global_func_id(pci_adapter->hwdev);
+		bdf_info.bus = pdev->bus->number;
+		bdf_info.device = PCI_SLOT(pdev->devfn);
+		bdf_info.function =  PCI_FUNC(pdev->devfn);
+
+		err = hinic3_set_bdf_ctxt(pci_adapter->hwdev, &bdf_info);
+		if (err) {
+			dev_err(&pdev->dev, "Failed to set BDF info to fw\n");
+			goto err_uninit_func;
+		}
+	}
+
 	return 0;
 
+err_uninit_func:
+	hinic3_func_uninit(pdev);
 err_unmap_bar:
 	hinic3_unmapping_bar(pci_adapter);
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
index 6d87d4d895ba..a7c9c5bca53a 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
@@ -130,6 +130,7 @@ static int hinic3_sw_init(struct net_device *netdev)
 {
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
 	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+	u8 mac_addr[ETH_ALEN];
 	int err;
 
 	nic_dev->q_params.sq_depth = HINIC3_SQ_DEPTH;
@@ -137,16 +138,29 @@ static int hinic3_sw_init(struct net_device *netdev)
 
 	hinic3_try_to_enable_rss(netdev);
 
-	/* VF driver always uses random MAC address. During VM migration to a
-	 * new device, the new device should learn the VMs old MAC rather than
-	 * provide its own MAC. The product design assumes that every VF is
-	 * suspectable to migration so the device avoids offering MAC address
-	 * to VFs.
-	 */
-	eth_hw_addr_random(netdev);
+	if (HINIC3_IS_VF(hwdev)) {
+		/* VF driver always uses random MAC address. During VM migration
+		 * to a new device, the new device should learn the VMs old MAC
+		 * rather than provide its own MAC. The product design assumes
+		 * that every VF is suspectable to migration so the device
+		 * avoids offering MAC address to VFs.
+		 */
+		eth_hw_addr_random(netdev);
+	} else {
+		err = hinic3_get_default_mac(hwdev, mac_addr);
+		if (err) {
+			dev_err(hwdev->dev, "Failed to get MAC address\n");
+			goto err_clear_rss_config;
+		}
+		eth_hw_addr_set(netdev, mac_addr);
+	}
+
 	err = hinic3_set_mac(hwdev, netdev->dev_addr, 0,
 			     hinic3_global_func_id(hwdev));
-	if (err) {
+	/* Failure to set MAC is not a fatal error for VF since its MAC may have
+	 * already been set by PF
+	 */
+	if (err && err != -EADDRINUSE) {
 		dev_err(hwdev->dev, "Failed to set default MAC\n");
 		goto err_clear_rss_config;
 	}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
index cf67e26acece..b4e151e88a13 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
@@ -82,10 +82,27 @@ static struct hinic3_msg_desc *get_mbox_msg_desc(struct hinic3_mbox *mbox,
 						 enum mbox_msg_direction_type dir,
 						 u16 src_func_id)
 {
+	struct hinic3_hwdev *hwdev = mbox->hwdev;
 	struct hinic3_msg_channel *msg_ch;
-
-	msg_ch = (src_func_id == MBOX_MGMT_FUNC_ID) ?
-		&mbox->mgmt_msg : mbox->func_msg;
+	u16 id;
+
+	if (src_func_id == MBOX_MGMT_FUNC_ID) {
+		msg_ch = &mbox->mgmt_msg;
+	} else if (HINIC3_IS_VF(hwdev)) {
+		/* message from pf */
+		msg_ch = mbox->func_msg;
+		if (src_func_id != hinic3_pf_id_of_vf(hwdev) || !msg_ch)
+			return NULL;
+	} else if (src_func_id > hinic3_glb_pf_vf_offset(hwdev)) {
+		/* message from vf */
+		id = (src_func_id - 1) - hinic3_glb_pf_vf_offset(hwdev);
+		if (id >= 1)
+			return NULL;
+
+		msg_ch = &mbox->func_msg[id];
+	} else {
+		return NULL;
+	}
 
 	return (dir == MBOX_MSG_SEND) ?
 		&msg_ch->recv_msg : &msg_ch->resp_msg;
@@ -409,6 +426,13 @@ int hinic3_init_mbox(struct hinic3_hwdev *hwdev)
 	if (err)
 		goto err_destroy_workqueue;
 
+	if (HINIC3_IS_VF(hwdev)) {
+		/* VF to PF mbox message channel */
+		err = hinic3_init_func_mbox_msg_channel(hwdev);
+		if (err)
+			goto err_uninit_mgmt_msg_ch;
+	}
+
 	err = hinic3_init_func_mbox_msg_channel(hwdev);
 	if (err)
 		goto err_uninit_mgmt_msg_ch;
@@ -424,8 +448,8 @@ int hinic3_init_mbox(struct hinic3_hwdev *hwdev)
 	return 0;
 
 err_uninit_func_mbox_msg_ch:
-	hinic3_uninit_func_mbox_msg_channel(hwdev);
-
+	if (HINIC3_IS_VF(hwdev))
+		hinic3_uninit_func_mbox_msg_channel(hwdev);
 err_uninit_mgmt_msg_ch:
 	uninit_mgmt_msg_channel(mbox);
 
@@ -576,7 +600,13 @@ static void write_mbox_msg_attr(struct hinic3_mbox *mbox,
 {
 	struct hinic3_hwif *hwif = mbox->hwdev->hwif;
 	u32 mbox_int, mbox_ctrl, tx_size;
+	u16 func = dst_func;
 
+	/* VF can send non-management messages only to PF. We set DST_FUNC field
+	 * to 0 since HW will ignore it anyway.
+	 */
+	if (HINIC3_IS_VF(mbox->hwdev) && dst_func != MBOX_MGMT_FUNC_ID)
+		func = 0;
 	tx_size = ALIGN(seg_len + MBOX_HEADER_SZ, MBOX_SEG_LEN_ALIGN) >> 2;
 
 	mbox_int = MBOX_INT_SET(dst_aeqn, DST_AEQN) |
@@ -587,7 +617,7 @@ static void write_mbox_msg_attr(struct hinic3_mbox *mbox,
 
 	mbox_ctrl = MBOX_CTRL_SET(1, TX_STATUS) |
 		    MBOX_CTRL_SET(0, TRIGGER_AEQE) |
-		    MBOX_CTRL_SET(dst_func, DST_FUNC);
+		    MBOX_CTRL_SET(func, DST_FUNC);
 
 	hinic3_hwif_write_reg(hwif, HINIC3_FUNC_CSR_MAILBOX_INT_OFF, mbox_int);
 	hinic3_hwif_write_reg(hwif, HINIC3_FUNC_CSR_MAILBOX_CONTROL_OFF,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
index 6cc0345c39e4..f9a3222b1b46 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
@@ -163,6 +163,7 @@ enum l2nic_cmd {
 	L2NIC_CMD_SET_SQ_CI_ATTR      = 8,
 	L2NIC_CMD_CLEAR_QP_RESOURCE   = 11,
 	L2NIC_CMD_FEATURE_NEGO        = 15,
+	L2NIC_CMD_GET_MAC             = 20,
 	L2NIC_CMD_SET_MAC             = 21,
 	L2NIC_CMD_DEL_MAC             = 22,
 	L2NIC_CMD_UPDATE_MAC          = 23,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
index 979f47ca77f9..e784f1b04a41 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
@@ -117,17 +117,52 @@ int hinic3_set_port_mtu(struct net_device *netdev, u16 new_mtu)
 					 &func_tbl_cfg);
 }
 
+static bool hinic3_check_vf_set_by_pf(struct hinic3_hwdev *hwdev,
+				      u8 status)
+{
+	return HINIC3_IS_VF(hwdev) && status == HINIC3_PF_SET_VF_ALREADY;
+}
+
 static int hinic3_check_mac_info(struct hinic3_hwdev *hwdev, u8 status,
 				 u16 vlan_id)
 {
 	if ((status && status != MGMT_STATUS_EXIST) ||
 	    ((vlan_id & BIT(15)) && status == MGMT_STATUS_EXIST)) {
+		if (hinic3_check_vf_set_by_pf(hwdev, status))
+			return 0;
+
 		return -EINVAL;
 	}
 
 	return 0;
 }
 
+int hinic3_get_default_mac(struct hinic3_hwdev *hwdev, u8 *mac_addr)
+{
+	struct l2nic_cmd_set_mac mac_info = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	mac_info.func_id = hinic3_global_func_id(hwdev);
+
+	mgmt_msg_params_init_default(&msg_params, &mac_info, sizeof(mac_info));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+				       L2NIC_CMD_GET_MAC,
+				       &msg_params);
+
+	if (err || mac_info.msg_head.status) {
+		dev_err(hwdev->dev,
+			"Failed to get mac, err: %d, status: 0x%x\n",
+			err, mac_info.msg_head.status);
+		return -EFAULT;
+	}
+
+	ether_addr_copy(mac_addr, mac_info.mac);
+
+	return 0;
+}
+
 int hinic3_set_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
 		   u16 func_id)
 {
@@ -157,9 +192,9 @@ int hinic3_set_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
 		return -EIO;
 	}
 
-	if (mac_info.msg_head.status == MGMT_STATUS_PF_SET_VF_ALREADY) {
+	if (hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status)) {
 		dev_warn(hwdev->dev, "PF has already set VF mac, Ignore set operation\n");
-		return 0;
+		return -EADDRINUSE;
 	}
 
 	if (mac_info.msg_head.status == MGMT_STATUS_EXIST) {
@@ -191,11 +226,18 @@ int hinic3_del_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
 
 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
 				       L2NIC_CMD_DEL_MAC, &msg_params);
-	if (err) {
+	if (err ||
+	    (mac_info.msg_head.status &&
+	     !hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status))) {
 		dev_err(hwdev->dev,
 			"Failed to delete MAC, err: %d, status: 0x%x\n",
 			err, mac_info.msg_head.status);
-		return err;
+		return -EFAULT;
+	}
+
+	if (hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status)) {
+		dev_warn(hwdev->dev, "PF has already set VF mac, Ignore delete operation.\n");
+		return -EADDRINUSE;
 	}
 
 	return 0;
@@ -231,6 +273,17 @@ int hinic3_update_mac(struct hinic3_hwdev *hwdev, const u8 *old_mac,
 		return -EIO;
 	}
 
+	if (hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status)) {
+		dev_warn(hwdev->dev, "PF has already set VF MAC. Ignore update operation\n");
+		return -EADDRINUSE;
+	}
+
+	if (mac_info.msg_head.status == HINIC3_MGMT_STATUS_EXIST) {
+		dev_warn(hwdev->dev,
+			 "MAC is repeated. Ignore update operation\n");
+		return 0;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
index b83b567fa542..08bf14679bf8 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -16,6 +16,8 @@ struct hinic3_nic_dev;
 #define HINIC3_MAX_JUMBO_FRAME_SIZE  9600
 
 #define HINIC3_VLAN_ID_MASK          0x7FFF
+#define HINIC3_PF_SET_VF_ALREADY     0x4
+#define HINIC3_MGMT_STATUS_EXIST     0x6
 
 enum hinic3_nic_event_type {
 	HINIC3_NIC_EVENT_LINK_DOWN = 0,
@@ -41,6 +43,7 @@ void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap);
 int hinic3_init_function_table(struct hinic3_nic_dev *nic_dev);
 int hinic3_set_port_mtu(struct net_device *netdev, u16 new_mtu);
 
+int hinic3_get_default_mac(struct hinic3_hwdev *hwdev, u8 *mac_addr);
 int hinic3_set_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
 		   u16 func_id);
 int hinic3_del_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next v05 2/5] hinic3: Add PF management interfaces
  2025-11-06 11:15 [PATCH net-next v05 0/5] net: hinic3: PF initialization Fan Gong
  2025-11-06 11:15 ` [PATCH net-next v05 1/5] hinic3: Add PF framework Fan Gong
@ 2025-11-06 11:15 ` Fan Gong
  2025-11-06 14:33   ` [External] : " ALOK TIWARI
  2025-11-06 11:15 ` [PATCH net-next v05 3/5] hinic3: Add NIC configuration ops Fan Gong
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Fan Gong @ 2025-11-06 11:15 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi

Add management and communication pathways between PF and HW.

Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
---
 .../ethernet/huawei/hinic3/hinic3_hw_intf.h   |   2 +
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.c |  51 ++-
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.h |   1 +
 .../net/ethernet/huawei/hinic3/hinic3_main.c  |   2 +
 .../net/ethernet/huawei/hinic3/hinic3_mbox.c  |  13 +
 .../net/ethernet/huawei/hinic3/hinic3_mbox.h  |   2 +
 .../net/ethernet/huawei/hinic3/hinic3_mgmt.c  | 311 +++++++++++++++++-
 .../net/ethernet/huawei/hinic3/hinic3_mgmt.h  |  53 +++
 .../huawei/hinic3/hinic3_mgmt_interface.h     |   1 +
 .../huawei/hinic3/hinic3_netdev_ops.c         |  35 ++
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.c   |  28 ++
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |  18 +
 .../ethernet/huawei/hinic3/hinic3_nic_dev.h   |   2 +
 13 files changed, 517 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
index a0422ec0500f..329a9c464ff9 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
@@ -39,6 +39,8 @@ enum mgmt_mod_type {
 	/* Configuration module */
 	MGMT_MOD_CFGM   = 7,
 	MGMT_MOD_HILINK = 14,
+	/* hardware max module id */
+	MGMT_MOD_HW_MAX = 20,
 };
 
 static inline void mgmt_msg_params_init_default(struct mgmt_msg_params *msg_params,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
index 2b1f1036620e..25e375b20174 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
@@ -211,6 +211,36 @@ static int init_ceqs_msix_attr(struct hinic3_hwdev *hwdev)
 	return 0;
 }
 
+static int hinic3_comm_pf_to_mgmt_init(struct hinic3_hwdev *hwdev)
+{
+	int err;
+
+	if (HINIC3_IS_VF(hwdev))
+		return 0;
+
+	err = hinic3_pf_to_mgmt_init(hwdev);
+	if (err)
+		return err;
+
+	set_bit(HINIC3_HWDEV_MGMT_INITED, &hwdev->func_state);
+
+	return 0;
+}
+
+static void hinic3_comm_pf_to_mgmt_free(struct hinic3_hwdev *hwdev)
+{
+	if (HINIC3_IS_VF(hwdev))
+		return;
+
+	spin_lock_bh(&hwdev->channel_lock);
+	clear_bit(HINIC3_HWDEV_MGMT_INITED, &hwdev->func_state);
+	spin_unlock_bh(&hwdev->channel_lock);
+
+	hinic3_aeq_unregister_cb(hwdev, HINIC3_MSG_FROM_FW);
+
+	hinic3_pf_to_mgmt_free(hwdev);
+}
+
 static int init_basic_mgmt_channel(struct hinic3_hwdev *hwdev)
 {
 	int err;
@@ -412,10 +442,14 @@ static int hinic3_init_comm_ch(struct hinic3_hwdev *hwdev)
 	if (err)
 		return err;
 
-	err = init_basic_attributes(hwdev);
+	err = hinic3_comm_pf_to_mgmt_init(hwdev);
 	if (err)
 		goto err_free_basic_mgmt_ch;
 
+	err = init_basic_attributes(hwdev);
+	if (err)
+		goto err_free_comm_pf_to_mgmt;
+
 	err = init_cmdqs_channel(hwdev);
 	if (err) {
 		dev_err(hwdev->dev, "Failed to init cmdq channel\n");
@@ -428,6 +462,8 @@ static int hinic3_init_comm_ch(struct hinic3_hwdev *hwdev)
 
 err_clear_func_svc_used_state:
 	hinic3_set_func_svc_used_state(hwdev, COMM_FUNC_SVC_T_COMM, 0);
+err_free_comm_pf_to_mgmt:
+	hinic3_comm_pf_to_mgmt_free(hwdev);
 err_free_basic_mgmt_ch:
 	free_base_mgmt_channel(hwdev);
 
@@ -439,6 +475,7 @@ static void hinic3_uninit_comm_ch(struct hinic3_hwdev *hwdev)
 	hinic3_set_pf_status(hwdev->hwif, HINIC3_PF_STATUS_INIT);
 	hinic3_free_cmdqs_channel(hwdev);
 	hinic3_set_func_svc_used_state(hwdev, COMM_FUNC_SVC_T_COMM, 0);
+	hinic3_comm_pf_to_mgmt_free(hwdev);
 	free_base_mgmt_channel(hwdev);
 }
 
@@ -581,9 +618,21 @@ void hinic3_free_hwdev(struct hinic3_hwdev *hwdev)
 
 void hinic3_set_api_stop(struct hinic3_hwdev *hwdev)
 {
+	struct hinic3_recv_msg *recv_resp_msg;
 	struct hinic3_mbox *mbox;
 
 	spin_lock_bh(&hwdev->channel_lock);
+	if (HINIC3_IS_PF(hwdev) &&
+	    test_bit(HINIC3_HWDEV_MGMT_INITED, &hwdev->func_state)) {
+		recv_resp_msg = &hwdev->pf_to_mgmt->recv_resp_msg_from_mgmt;
+		spin_lock_bh(&hwdev->pf_to_mgmt->sync_event_lock);
+		if (hwdev->pf_to_mgmt->event_flag == COMM_SEND_EVENT_START) {
+			complete(&recv_resp_msg->recv_done);
+			hwdev->pf_to_mgmt->event_flag = COMM_SEND_EVENT_TIMEOUT;
+		}
+		spin_unlock_bh(&hwdev->pf_to_mgmt->sync_event_lock);
+	}
+
 	if (test_bit(HINIC3_HWDEV_MBOX_INITED, &hwdev->func_state)) {
 		mbox = hwdev->mbox;
 		spin_lock(&mbox->mbox_lock);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
index 78cface6ddd7..3c15f22973fe 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
@@ -51,6 +51,7 @@ struct hinic3_hwdev {
 	struct hinic3_cmdqs         *cmdqs;
 	struct delayed_work         sync_time_task;
 	struct workqueue_struct     *workq;
+	struct hinic3_msg_pf_to_mgmt *pf_to_mgmt;
 	/* protect channel init and uninit */
 	spinlock_t                  channel_lock;
 	u64                         features[COMM_MAX_FEATURE_QWORD];
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
index a7c9c5bca53a..e12102806791 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
@@ -133,6 +133,8 @@ static int hinic3_sw_init(struct net_device *netdev)
 	u8 mac_addr[ETH_ALEN];
 	int err;
 
+	sema_init(&nic_dev->port_state_sem, 1);
+
 	nic_dev->q_params.sq_depth = HINIC3_SQ_DEPTH;
 	nic_dev->q_params.rq_depth = HINIC3_RQ_DEPTH;
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
index b4e151e88a13..a4be5b2984cf 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
@@ -870,6 +870,19 @@ int hinic3_send_mbox_to_mgmt(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
 	return err;
 }
 
+void hinic3_response_mbox_to_mgmt(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
+				  const void *buf_in, u32 in_size, u16 msg_id)
+{
+	struct mbox_msg_info msg_info;
+
+	msg_info.msg_id = (u8)msg_id;
+	msg_info.status = 0;
+
+	send_mbox_msg(hwdev->mbox, mod, cmd, buf_in, in_size,
+		      MBOX_MGMT_FUNC_ID, MBOX_MSG_RESP,
+		      MBOX_MSG_NO_ACK, &msg_info);
+}
+
 int hinic3_send_mbox_to_mgmt_no_ack(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
 				    const struct mgmt_msg_params *msg_params)
 {
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h
index e71629e95086..e26f22d1d564 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h
@@ -135,6 +135,8 @@ void hinic3_free_mbox(struct hinic3_hwdev *hwdev);
 
 int hinic3_send_mbox_to_mgmt(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
 			     const struct mgmt_msg_params *msg_params);
+void hinic3_response_mbox_to_mgmt(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
+				  const void *buf_in, u32 in_size, u16 msg_id);
 int hinic3_send_mbox_to_mgmt_no_ack(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
 				    const struct mgmt_msg_params *msg_params);
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.c b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.c
index c38d10cd7fac..04a948cf9c63 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.c
@@ -3,19 +3,328 @@
 
 #include "hinic3_eqs.h"
 #include "hinic3_hwdev.h"
+#include "hinic3_hwif.h"
 #include "hinic3_mbox.h"
 #include "hinic3_mgmt.h"
 
+#define HINIC3_MSG_TO_MGMT_MAX_LEN  2016
+
+#define MGMT_MAX_PF_BUF_SIZE        2048UL
+#define MGMT_SEG_LEN_MAX            48
+#define MGMT_ASYNC_MSG_FLAG         0x8
+
+#define HINIC3_MGMT_WQ_NAME         "hinic3_mgmt"
+
+/* Bogus sequence ID to prevent accidental match following partial message */
+#define MGMT_BOGUS_SEQ_ID  \
+	(MGMT_MAX_PF_BUF_SIZE / MGMT_SEG_LEN_MAX + 1)
+
+static void
+hinic3_mgmt_resp_msg_handler(struct hinic3_msg_pf_to_mgmt *pf_to_mgmt,
+			     struct hinic3_recv_msg *recv_msg)
+{
+	struct device *dev = pf_to_mgmt->hwdev->dev;
+
+	/* Ignore async msg */
+	if (recv_msg->msg_id & MGMT_ASYNC_MSG_FLAG)
+		return;
+
+	spin_lock(&pf_to_mgmt->sync_event_lock);
+	if (recv_msg->msg_id != pf_to_mgmt->sync_msg_id) {
+		dev_err(dev, "msg id mismatch, send msg id: 0x%x, recv msg id: 0x%x, event state: %d\n",
+			pf_to_mgmt->sync_msg_id, recv_msg->msg_id,
+			pf_to_mgmt->event_flag);
+	} else if (pf_to_mgmt->event_flag == COMM_SEND_EVENT_START) {
+		pf_to_mgmt->event_flag = COMM_SEND_EVENT_SUCCESS;
+		complete(&recv_msg->recv_done);
+	} else {
+		dev_err(dev, "Wait timeout, send msg id: 0x%x, recv msg id: 0x%x, event state: %d\n",
+			pf_to_mgmt->sync_msg_id, recv_msg->msg_id,
+			pf_to_mgmt->event_flag);
+	}
+	spin_unlock(&pf_to_mgmt->sync_event_lock);
+}
+
+static void hinic3_recv_mgmt_msg_work_handler(struct work_struct *work)
+{
+	struct hinic3_msg_pf_to_mgmt *pf_to_mgmt;
+	struct mgmt_msg_handle_work *mgmt_work;
+	struct mgmt_msg_head *ack_cmd;
+
+	mgmt_work = container_of(work, struct mgmt_msg_handle_work, work);
+
+	/* At the moment, we do not expect any meaningful messages but if the
+	 * sender expects an ACK we still need to provide one with "unsupported"
+	 * status.
+	 */
+	if (mgmt_work->async_mgmt_to_pf)
+		goto out;
+
+	pf_to_mgmt = mgmt_work->pf_to_mgmt;
+	ack_cmd = pf_to_mgmt->mgmt_ack_buf;
+	memset(ack_cmd, 0, sizeof(*ack_cmd));
+	ack_cmd->status = MGMT_STATUS_CMD_UNSUPPORTED;
+
+	hinic3_response_mbox_to_mgmt(pf_to_mgmt->hwdev, mgmt_work->mod,
+				     mgmt_work->cmd, ack_cmd, sizeof(*ack_cmd),
+				     mgmt_work->msg_id);
+
+out:
+	kfree(mgmt_work->msg);
+	kfree(mgmt_work);
+}
+
+static int hinic3_recv_msg_add_seg(struct hinic3_recv_msg *recv_msg,
+				   __le64 msg_header, const void *seg_data,
+				   bool *is_complete)
+{
+	u8 seq_id, msg_id, seg_len, is_last;
+	char *msg_buff;
+	u32 offset;
+
+	seg_len = MBOX_MSG_HEADER_GET(msg_header, SEG_LEN);
+	is_last = MBOX_MSG_HEADER_GET(msg_header, LAST);
+	seq_id  = MBOX_MSG_HEADER_GET(msg_header, SEQID);
+	msg_id = MBOX_MSG_HEADER_GET(msg_header, MSG_ID);
+
+	if (seg_len > MGMT_SEG_LEN_MAX)
+		return -EINVAL;
+
+	/* All segments but last must be of maximal size */
+	if (seg_len != MGMT_SEG_LEN_MAX && !is_last)
+		return -EINVAL;
+
+	if (seq_id == 0) {
+		recv_msg->seq_id = seq_id;
+		recv_msg->msg_id = msg_id;
+	} else if (seq_id != recv_msg->seq_id + 1 ||
+		   msg_id != recv_msg->msg_id) {
+		return -EINVAL;
+	}
+
+	offset = seq_id * MGMT_SEG_LEN_MAX;
+	if (offset + seg_len > MGMT_MAX_PF_BUF_SIZE)
+		return -EINVAL;
+
+	msg_buff = recv_msg->msg;
+	memcpy(msg_buff + offset, seg_data, seg_len);
+	recv_msg->msg_len = offset + seg_len;
+	recv_msg->seq_id = seq_id;
+	*is_complete = !!is_last;
+
+	return 0;
+}
+
+static void hinic3_init_mgmt_msg_work(struct hinic3_msg_pf_to_mgmt *pf_to_mgmt,
+				      struct hinic3_recv_msg *recv_msg)
+{
+	struct mgmt_msg_handle_work *mgmt_work;
+
+	mgmt_work = kmalloc(sizeof(*mgmt_work), GFP_KERNEL);
+	if (!mgmt_work)
+		return;
+
+	if (recv_msg->msg_len) {
+		mgmt_work->msg = kmalloc(recv_msg->msg_len, GFP_KERNEL);
+		if (!mgmt_work->msg) {
+			kfree(mgmt_work);
+			return;
+		}
+	}
+
+	mgmt_work->pf_to_mgmt = pf_to_mgmt;
+	mgmt_work->msg_len = recv_msg->msg_len;
+	memcpy(mgmt_work->msg, recv_msg->msg, recv_msg->msg_len);
+	mgmt_work->msg_id = recv_msg->msg_id;
+	mgmt_work->mod = recv_msg->mod;
+	mgmt_work->cmd = recv_msg->cmd;
+	mgmt_work->async_mgmt_to_pf = recv_msg->async_mgmt_to_pf;
+
+	INIT_WORK(&mgmt_work->work, hinic3_recv_mgmt_msg_work_handler);
+	queue_work(pf_to_mgmt->workq, &mgmt_work->work);
+}
+
+static void
+hinic3_recv_mgmt_msg_handler(struct hinic3_msg_pf_to_mgmt *pf_to_mgmt,
+			     const u8 *header,
+			     struct hinic3_recv_msg *recv_msg)
+{
+	struct hinic3_hwdev *hwdev = pf_to_mgmt->hwdev;
+	const void *seg_data;
+	__le64 msg_header;
+	bool is_complete;
+	u8 dir, msg_id;
+	int err;
+
+	msg_header = *(__force __le64 *)header;
+	dir = MBOX_MSG_HEADER_GET(msg_header, DIRECTION);
+	msg_id = MBOX_MSG_HEADER_GET(msg_header, MSG_ID);
+	/* Don't need to get anything from hw when cmd is async */
+	if (dir == MBOX_MSG_RESP && (msg_id & MGMT_ASYNC_MSG_FLAG))
+		return;
+
+	seg_data = header + sizeof(msg_header);
+	err = hinic3_recv_msg_add_seg(recv_msg, msg_header,
+				      seg_data, &is_complete);
+	if (err) {
+		dev_err(hwdev->dev, "invalid receive segment\n");
+		/* set seq_id to invalid seq_id */
+		recv_msg->seq_id = MGMT_BOGUS_SEQ_ID;
+
+		return;
+	}
+
+	if (!is_complete)
+		return;
+
+	recv_msg->cmd = MBOX_MSG_HEADER_GET(msg_header, CMD);
+	recv_msg->mod = MBOX_MSG_HEADER_GET(msg_header, MODULE);
+	recv_msg->async_mgmt_to_pf = MBOX_MSG_HEADER_GET(msg_header, NO_ACK);
+	recv_msg->seq_id = MGMT_BOGUS_SEQ_ID;
+
+	if (dir == MBOX_MSG_RESP)
+		hinic3_mgmt_resp_msg_handler(pf_to_mgmt, recv_msg);
+	else
+		hinic3_init_mgmt_msg_work(pf_to_mgmt, recv_msg);
+}
+
+static int alloc_recv_msg(struct hinic3_recv_msg *recv_msg)
+{
+	recv_msg->seq_id = MGMT_BOGUS_SEQ_ID;
+
+	recv_msg->msg = kzalloc(MGMT_MAX_PF_BUF_SIZE, GFP_KERNEL);
+	if (!recv_msg->msg)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void free_recv_msg(struct hinic3_recv_msg *recv_msg)
+{
+	kfree(recv_msg->msg);
+}
+
+static int alloc_msg_buf(struct hinic3_msg_pf_to_mgmt *pf_to_mgmt)
+{
+	struct device *dev = pf_to_mgmt->hwdev->dev;
+	int err;
+
+	err = alloc_recv_msg(&pf_to_mgmt->recv_msg_from_mgmt);
+	if (err) {
+		dev_err(dev, "Failed to allocate recv msg\n");
+		return err;
+	}
+
+	err = alloc_recv_msg(&pf_to_mgmt->recv_resp_msg_from_mgmt);
+	if (err) {
+		dev_err(dev, "Failed to allocate resp recv msg\n");
+		goto err_free_msg_from_mgmt;
+	}
+
+	pf_to_mgmt->mgmt_ack_buf = kzalloc(MGMT_MAX_PF_BUF_SIZE, GFP_KERNEL);
+	if (!pf_to_mgmt->mgmt_ack_buf) {
+		err = -ENOMEM;
+		goto err_free_resp_msg_from_mgmt;
+	}
+
+	return 0;
+
+err_free_resp_msg_from_mgmt:
+	free_recv_msg(&pf_to_mgmt->recv_resp_msg_from_mgmt);
+err_free_msg_from_mgmt:
+	free_recv_msg(&pf_to_mgmt->recv_msg_from_mgmt);
+
+	return err;
+}
+
+static void free_msg_buf(struct hinic3_msg_pf_to_mgmt *pf_to_mgmt)
+{
+	kfree(pf_to_mgmt->mgmt_ack_buf);
+
+	free_recv_msg(&pf_to_mgmt->recv_resp_msg_from_mgmt);
+	free_recv_msg(&pf_to_mgmt->recv_msg_from_mgmt);
+}
+
+int hinic3_pf_to_mgmt_init(struct hinic3_hwdev *hwdev)
+{
+	struct hinic3_msg_pf_to_mgmt *pf_to_mgmt;
+	int err;
+
+	pf_to_mgmt = kzalloc(sizeof(*pf_to_mgmt), GFP_KERNEL);
+	if (!pf_to_mgmt)
+		return -ENOMEM;
+
+	hwdev->pf_to_mgmt = pf_to_mgmt;
+	pf_to_mgmt->hwdev = hwdev;
+	spin_lock_init(&pf_to_mgmt->sync_event_lock);
+	pf_to_mgmt->workq = create_singlethread_workqueue(HINIC3_MGMT_WQ_NAME);
+	if (!pf_to_mgmt->workq) {
+		dev_err(hwdev->dev, "Failed to initialize MGMT workqueue\n");
+		err = -ENOMEM;
+		goto err_free_pf_to_mgmt;
+	}
+
+	err = alloc_msg_buf(pf_to_mgmt);
+	if (err) {
+		dev_err(hwdev->dev, "Failed to allocate msg buffers\n");
+		goto err_destroy_workqueue;
+	}
+
+	return 0;
+
+err_destroy_workqueue:
+	destroy_workqueue(pf_to_mgmt->workq);
+err_free_pf_to_mgmt:
+	kfree(pf_to_mgmt);
+
+	return err;
+}
+
+void hinic3_pf_to_mgmt_free(struct hinic3_hwdev *hwdev)
+{
+	struct hinic3_msg_pf_to_mgmt *pf_to_mgmt = hwdev->pf_to_mgmt;
+
+	/* destroy workqueue before free related pf_to_mgmt resources in case of
+	 * illegal resource access
+	 */
+	destroy_workqueue(pf_to_mgmt->workq);
+
+	free_msg_buf(pf_to_mgmt);
+	kfree(pf_to_mgmt);
+}
+
 void hinic3_flush_mgmt_workq(struct hinic3_hwdev *hwdev)
 {
 	if (hwdev->aeqs)
 		flush_workqueue(hwdev->aeqs->workq);
+
+	if (HINIC3_IS_PF(hwdev) && hwdev->pf_to_mgmt)
+		flush_workqueue(hwdev->pf_to_mgmt->workq);
 }
 
 void hinic3_mgmt_msg_aeqe_handler(struct hinic3_hwdev *hwdev, u8 *header,
 				  u8 size)
 {
+	struct hinic3_msg_pf_to_mgmt *pf_to_mgmt;
+	struct hinic3_recv_msg *recv_msg;
+	__le64 msg_header;
+	bool is_send_dir;
+
 	if (MBOX_MSG_HEADER_GET(*(__force __le64 *)header, SOURCE) ==
-				MBOX_MSG_FROM_MBOX)
+	    MBOX_MSG_FROM_MBOX){
 		hinic3_mbox_func_aeqe_handler(hwdev, header, size);
+
+		return;
+	}
+
+	pf_to_mgmt = hwdev->pf_to_mgmt;
+	msg_header = *(__force __le64 *)header;
+
+	is_send_dir = (MBOX_MSG_HEADER_GET(msg_header, DIRECTION) ==
+		       MBOX_MSG_SEND) ? true : false;
+
+	recv_msg = is_send_dir ? &pf_to_mgmt->recv_msg_from_mgmt :
+		   &pf_to_mgmt->recv_resp_msg_from_mgmt;
+
+	hinic3_recv_mgmt_msg_handler(pf_to_mgmt, header, recv_msg);
 }
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.h
index bbef3b32a6ec..56f48d5442bc 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.h
@@ -6,8 +6,61 @@
 
 #include <linux/types.h>
 
+#include "hinic3_mbox.h"
+#include "hinic3_hw_intf.h"
+
 struct hinic3_hwdev;
 
+struct hinic3_recv_msg {
+	/* Preallocated buffer of size MAX_PF_MGMT_BUF_SIZE that accumulates
+	 * receive message, segment-by-segment.
+	 */
+	void                 *msg;
+	/* Message id for which segments are accumulated. */
+	u8                   msg_id;
+	/* Sequence id of last received segment of current message. */
+	u8                   seq_id;
+	u16                  msg_len;
+	int                  async_mgmt_to_pf;
+	enum mgmt_mod_type   mod;
+	u16                  cmd;
+	struct completion    recv_done;
+};
+
+enum comm_pf_to_mgmt_event_state {
+	COMM_SEND_EVENT_UNINIT,
+	COMM_SEND_EVENT_START,
+	COMM_SEND_EVENT_SUCCESS,
+	COMM_SEND_EVENT_TIMEOUT,
+};
+
+struct hinic3_msg_pf_to_mgmt {
+	struct hinic3_hwdev              *hwdev;
+	struct workqueue_struct          *workq;
+	void                             *mgmt_ack_buf;
+	struct hinic3_recv_msg           recv_msg_from_mgmt;
+	struct hinic3_recv_msg           recv_resp_msg_from_mgmt;
+	u16                              async_msg_id;
+	u16                              sync_msg_id;
+	void                             *async_msg_cb_data[MGMT_MOD_HW_MAX];
+	/* synchronizes message send with message receives via event queue */
+	spinlock_t                       sync_event_lock;
+	enum comm_pf_to_mgmt_event_state event_flag;
+};
+
+struct mgmt_msg_handle_work {
+	struct work_struct           work;
+	struct hinic3_msg_pf_to_mgmt *pf_to_mgmt;
+	void                         *msg;
+	u16                          msg_len;
+	enum mgmt_mod_type           mod;
+	u16                          cmd;
+	u16                          msg_id;
+	int                          async_mgmt_to_pf;
+};
+
+int hinic3_pf_to_mgmt_init(struct hinic3_hwdev *hwdev);
+void hinic3_pf_to_mgmt_free(struct hinic3_hwdev *hwdev);
 void hinic3_flush_mgmt_workq(struct hinic3_hwdev *hwdev);
 void hinic3_mgmt_msg_aeqe_handler(struct hinic3_hwdev *hwdev,
 				  u8 *header, u8 size);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
index f9a3222b1b46..3a6d3ee534d0 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
@@ -190,6 +190,7 @@ enum l2nic_ucode_cmd {
 
 /* hilink mac group command */
 enum mag_cmd {
+	MAG_CMD_SET_PORT_ENABLE = 6,
 	MAG_CMD_GET_LINK_STATUS = 7,
 };
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index 0fa3c7900225..bf199f4ce847 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -327,6 +327,31 @@ static void hinic3_close_channel(struct net_device *netdev)
 	hinic3_free_qp_ctxts(nic_dev);
 }
 
+static int hinic3_maybe_set_port_state(struct net_device *netdev, bool enable)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	int err;
+
+	down(&nic_dev->port_state_sem);
+	err = hinic3_set_port_enable(nic_dev->hwdev, enable);
+	up(&nic_dev->port_state_sem);
+
+	return err;
+}
+
+static void hinic3_print_link_message(struct net_device *netdev,
+				      bool link_status_up)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+	if (nic_dev->link_status_up == link_status_up)
+		return;
+
+	nic_dev->link_status_up = link_status_up;
+
+	netdev_dbg(netdev, "Link is %s\n", str_up_down(link_status_up));
+}
+
 static int hinic3_vport_up(struct net_device *netdev)
 {
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
@@ -341,6 +366,12 @@ static int hinic3_vport_up(struct net_device *netdev)
 		goto err_flush_qps_res;
 	}
 
+	err = hinic3_maybe_set_port_state(netdev, true);
+	if (err) {
+		netdev_err(netdev, "Failed to enable port\n");
+		goto err_disable_vport;
+	}
+
 	err = netif_set_real_num_queues(netdev, nic_dev->q_params.num_qps,
 					nic_dev->q_params.num_qps);
 	if (err) {
@@ -353,8 +384,12 @@ static int hinic3_vport_up(struct net_device *netdev)
 	if (!err && link_status_up)
 		netif_carrier_on(netdev);
 
+	hinic3_print_link_message(netdev, link_status_up);
+
 	return 0;
 
+err_disable_vport:
+	hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
 err_flush_qps_res:
 	hinic3_flush_qps_res(nic_dev->hwdev);
 	/* wait to guarantee that no packets will be sent to host */
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
index e784f1b04a41..7fec13bbe60e 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
@@ -366,6 +366,34 @@ int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev)
 	return pkt_drop.msg_head.status;
 }
 
+int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable)
+{
+	struct mag_cmd_set_port_enable en_state = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	if (HINIC3_IS_VF(hwdev))
+		return 0;
+
+	en_state.function_id = hinic3_global_func_id(hwdev);
+	en_state.state = enable ? MAG_CMD_TX_ENABLE | MAG_CMD_RX_ENABLE :
+				MAG_CMD_PORT_DISABLE;
+
+	mgmt_msg_params_init_default(&msg_params, &en_state,
+				     sizeof(en_state));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_HILINK,
+				       MAG_CMD_SET_PORT_ENABLE, &msg_params);
+
+	if (err || en_state.head.status) {
+		dev_err(hwdev->dev, "Failed to set port state, err: %d, status: 0x%x\n",
+			err, en_state.head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
 int hinic3_sync_dcb_state(struct hinic3_hwdev *hwdev, u8 op_code, u8 state)
 {
 	struct l2nic_cmd_set_dcb_state dcb_state = {};
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
index 08bf14679bf8..d4326937db48 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -34,6 +34,23 @@ struct hinic3_sq_attr {
 	u64 ci_dma_base;
 };
 
+#define MAG_CMD_PORT_DISABLE    0x0
+#define MAG_CMD_TX_ENABLE       0x1
+#define MAG_CMD_RX_ENABLE       0x2
+/* the physical port is disabled only when all pf of the port are set to down,
+ * if any pf is enabled, the port is enabled
+ */
+struct mag_cmd_set_port_enable {
+	struct mgmt_msg_head head;
+
+	u16                  function_id;
+	u16                  rsvd0;
+
+	/* bitmap bit0:tx_en bit1:rx_en */
+	u8                   state;
+	u8                   rsvd1[3];
+};
+
 int hinic3_get_nic_feature_from_hw(struct hinic3_nic_dev *nic_dev);
 int hinic3_set_nic_feature_to_hw(struct hinic3_nic_dev *nic_dev);
 bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
@@ -57,6 +74,7 @@ int hinic3_flush_qps_res(struct hinic3_hwdev *hwdev);
 int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev);
 
 int hinic3_sync_dcb_state(struct hinic3_hwdev *hwdev, u8 op_code, u8 state);
+int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable);
 int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up);
 int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
 			    bool enable);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
index 5ba83261616c..3a9f3ccdb684 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
@@ -83,6 +83,8 @@ struct hinic3_nic_dev {
 
 	struct hinic3_intr_coal_info    *intr_coalesce;
 
+	struct semaphore                port_state_sem;
+
 	bool                            link_status_up;
 };
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next v05 3/5] hinic3: Add NIC configuration ops
  2025-11-06 11:15 [PATCH net-next v05 0/5] net: hinic3: PF initialization Fan Gong
  2025-11-06 11:15 ` [PATCH net-next v05 1/5] hinic3: Add PF framework Fan Gong
  2025-11-06 11:15 ` [PATCH net-next v05 2/5] hinic3: Add PF management interfaces Fan Gong
@ 2025-11-06 11:15 ` Fan Gong
  2025-11-06 19:14   ` [External] : " ALOK TIWARI
  2025-11-06 11:15 ` [PATCH net-next v05 4/5] hinic3: Add mac filter ops Fan Gong
  2025-11-06 11:15 ` [PATCH net-next v05 5/5] hinic3: Add netdev register interfaces Fan Gong
  4 siblings, 1 reply; 11+ messages in thread
From: Fan Gong @ 2025-11-06 11:15 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi

Add ops to configure NIC feature(lro, vlan, csum...).
Add queue work to collect NIC data.

Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
---
 .../ethernet/huawei/hinic3/hinic3_hw_comm.c   |  54 +++
 .../ethernet/huawei/hinic3/hinic3_hw_comm.h   |   2 +
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.c |   2 +-
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.h |   9 +
 .../net/ethernet/huawei/hinic3/hinic3_irq.c   | 136 +++++++-
 .../net/ethernet/huawei/hinic3/hinic3_main.c  |  43 +++
 .../huawei/hinic3/hinic3_mgmt_interface.h     |  50 +++
 .../huawei/hinic3/hinic3_netdev_ops.c         | 318 ++++++++++++++++++
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.c   | 171 ++++++++++
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |   7 +
 .../ethernet/huawei/hinic3/hinic3_nic_dev.h   |  45 +++
 .../net/ethernet/huawei/hinic3/hinic3_rx.h    |  20 ++
 .../net/ethernet/huawei/hinic3/hinic3_tx.h    |  18 +
 13 files changed, 873 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
index 09dae2ef610c..554b3134b5a7 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
@@ -9,6 +9,36 @@
 #include "hinic3_hwif.h"
 #include "hinic3_mbox.h"
 
+static int hinic3_get_interrupt_cfg(struct hinic3_hwdev *hwdev,
+				    struct hinic3_interrupt_info *info)
+{
+	struct comm_cmd_cfg_msix_ctrl_reg msix_cfg = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	msix_cfg.func_id = hinic3_global_func_id(hwdev);
+	msix_cfg.msix_index = info->msix_index;
+	msix_cfg.opcode = MGMT_MSG_CMD_OP_GET;
+
+	mgmt_msg_params_init_default(&msg_params, &msix_cfg, sizeof(msix_cfg));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
+				       COMM_CMD_CFG_MSIX_CTRL_REG, &msg_params);
+	if (err || msix_cfg.head.status) {
+		dev_err(hwdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x\n",
+			err, msix_cfg.head.status);
+		return -EFAULT;
+	}
+
+	info->lli_credit_limit = msix_cfg.lli_credit_cnt;
+	info->lli_timer_cfg = msix_cfg.lli_timer_cnt;
+	info->pending_limit = msix_cfg.pending_cnt;
+	info->coalesc_timer_cfg = msix_cfg.coalesce_timer_cnt;
+	info->resend_timer_cfg = msix_cfg.resend_timer_cnt;
+
+	return 0;
+}
+
 int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
 				    const struct hinic3_interrupt_info *info)
 {
@@ -40,6 +70,30 @@ int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
 	return 0;
 }
 
+int hinic3_set_interrupt_cfg(struct hinic3_hwdev *hwdev,
+			     struct hinic3_interrupt_info info)
+{
+	struct hinic3_interrupt_info temp_info;
+	int err;
+
+	temp_info.msix_index = info.msix_index;
+
+	err = hinic3_get_interrupt_cfg(hwdev, &temp_info);
+	if (err)
+		return err;
+
+	info.lli_credit_limit = temp_info.lli_credit_limit;
+	info.lli_timer_cfg = temp_info.lli_timer_cfg;
+
+	if (!info.interrupt_coalesc_set) {
+		info.pending_limit = temp_info.pending_limit;
+		info.coalesc_timer_cfg = temp_info.coalesc_timer_cfg;
+		info.resend_timer_cfg = temp_info.resend_timer_cfg;
+	}
+
+	return hinic3_set_interrupt_cfg_direct(hwdev, &info);
+}
+
 int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag)
 {
 	struct comm_cmd_func_reset func_reset = {};
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
index c9c6b4fbcb12..8e4737c486b7 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
@@ -23,6 +23,8 @@ struct hinic3_interrupt_info {
 
 int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
 				    const struct hinic3_interrupt_info *info);
+int hinic3_set_interrupt_cfg(struct hinic3_hwdev *hwdev,
+			     struct hinic3_interrupt_info info);
 int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag);
 
 int hinic3_get_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
index 25e375b20174..4048b3302db7 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
@@ -200,7 +200,7 @@ static int init_ceqs_msix_attr(struct hinic3_hwdev *hwdev)
 	for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) {
 		eq = &ceqs->ceq[q_id];
 		info.msix_index = eq->msix_entry_idx;
-		err = hinic3_set_interrupt_cfg_direct(hwdev, &info);
+		err = hinic3_set_interrupt_cfg(hwdev, info);
 		if (err) {
 			dev_err(hwdev->dev, "Set msix attr for ceq %u failed\n",
 				q_id);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
index 3c15f22973fe..58bc561f95b3 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
@@ -17,6 +17,15 @@ enum hinic3_event_service_type {
 	HINIC3_EVENT_SRV_NIC  = 1
 };
 
+enum hinic3_fault_err_level {
+	HINIC3_FAULT_LEVEL_SERIOUS_FLR = 3,
+};
+
+enum hinic3_fault_source_type {
+	HINIC3_FAULT_SRC_HW_PHY_FAULT = 9,
+	HINIC3_FAULT_SRC_TX_TIMEOUT   = 22,
+};
+
 #define HINIC3_SRV_EVENT_TYPE(svc, type)    (((svc) << 16) | (type))
 
 /* driver-specific data of pci_dev */
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
index a69b361225e9..cb9412986c26 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
@@ -10,6 +10,9 @@
 #include "hinic3_rx.h"
 #include "hinic3_tx.h"
 
+#define HINIC3_RX_RATE_THRESH  50000
+#define HINIC3_AVG_PKT_SMALL   256U
+
 static int hinic3_poll(struct napi_struct *napi, int budget)
 {
 	struct hinic3_irq_cfg *irq_cfg =
@@ -92,7 +95,7 @@ static int hinic3_request_irq(struct hinic3_irq_cfg *irq_cfg, u16 q_id)
 	info.coalesc_timer_cfg =
 		nic_dev->intr_coalesce[q_id].coalesce_timer_cfg;
 	info.resend_timer_cfg = nic_dev->intr_coalesce[q_id].resend_timer_cfg;
-	err = hinic3_set_interrupt_cfg_direct(nic_dev->hwdev, &info);
+	err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info);
 	if (err) {
 		netdev_err(netdev, "Failed to set RX interrupt coalescing attribute.\n");
 		qp_del_napi(irq_cfg);
@@ -117,6 +120,134 @@ static void hinic3_release_irq(struct hinic3_irq_cfg *irq_cfg)
 	free_irq(irq_cfg->irq_id, irq_cfg);
 }
 
+static int hinic3_set_interrupt_moder(struct net_device *netdev, u16 q_id,
+				      u8 coalesc_timer_cfg, u8 pending_limit)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_interrupt_info info = {};
+	int err;
+
+	if (coalesc_timer_cfg == nic_dev->rxqs[q_id].last_coalesc_timer_cfg &&
+	    pending_limit == nic_dev->rxqs[q_id].last_pending_limit)
+		return 0;
+
+	if (q_id >= nic_dev->q_params.num_qps)
+		return 0;
+
+	info.interrupt_coalesc_set = 1;
+	info.coalesc_timer_cfg = coalesc_timer_cfg;
+	info.pending_limit = pending_limit;
+	info.msix_index = nic_dev->q_params.irq_cfg[q_id].msix_entry_idx;
+	info.resend_timer_cfg =
+		nic_dev->intr_coalesce[q_id].resend_timer_cfg;
+
+	err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info);
+	if (err) {
+		netdev_err(netdev,
+			   "Failed to modify moderation for Queue: %u\n", q_id);
+	} else {
+		nic_dev->rxqs[q_id].last_coalesc_timer_cfg = coalesc_timer_cfg;
+		nic_dev->rxqs[q_id].last_pending_limit = pending_limit;
+	}
+
+	return err;
+}
+
+static void hinic3_calc_coal_para(struct hinic3_intr_coal_info *q_coal,
+				  u64 rx_rate, u8 *coalesc_timer_cfg,
+				  u8 *pending_limit)
+{
+	if (rx_rate < q_coal->pkt_rate_low) {
+		*coalesc_timer_cfg = q_coal->rx_usecs_low;
+		*pending_limit = q_coal->rx_pending_limit_low;
+	} else if (rx_rate > q_coal->pkt_rate_high) {
+		*coalesc_timer_cfg = q_coal->rx_usecs_high;
+		*pending_limit = q_coal->rx_pending_limit_high;
+	} else {
+		*coalesc_timer_cfg =
+			(u8)((rx_rate - q_coal->pkt_rate_low) *
+			     (q_coal->rx_usecs_high - q_coal->rx_usecs_low) /
+			     (q_coal->pkt_rate_high - q_coal->pkt_rate_low) +
+			     q_coal->rx_usecs_low);
+
+		*pending_limit =
+			(u8)((rx_rate - q_coal->pkt_rate_low) *
+			     (q_coal->rx_pending_limit_high -
+			      q_coal->rx_pending_limit_low) /
+			     (q_coal->pkt_rate_high - q_coal->pkt_rate_low) +
+			     q_coal->rx_pending_limit_low);
+	}
+}
+
+static void hinic3_update_queue_coal(struct net_device *netdev, u16 qid,
+				     u64 rx_rate, u64 avg_pkt_size, u64 tx_rate)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_intr_coal_info *q_coal;
+	u8 coalesc_timer_cfg, pending_limit;
+
+	q_coal = &nic_dev->intr_coalesce[qid];
+
+	if (rx_rate > HINIC3_RX_RATE_THRESH &&
+	    avg_pkt_size > HINIC3_AVG_PKT_SMALL) {
+		hinic3_calc_coal_para(q_coal, rx_rate, &coalesc_timer_cfg,
+				      &pending_limit);
+	} else {
+		coalesc_timer_cfg = 3;
+		pending_limit = q_coal->rx_pending_limit_low;
+	}
+
+	hinic3_set_interrupt_moder(netdev, qid,
+				   coalesc_timer_cfg, pending_limit);
+}
+
+static void hinic3_auto_moderation_work(struct work_struct *work)
+{
+	u64 rx_packets, rx_bytes, rx_pkt_diff, rx_rate, avg_pkt_size;
+	u64 tx_packets, tx_bytes, tx_pkt_diff, tx_rate;
+	struct hinic3_nic_dev *nic_dev;
+	struct delayed_work *delay;
+	struct net_device *netdev;
+	unsigned long period;
+	u16 qid;
+
+	delay = to_delayed_work(work);
+	nic_dev = container_of(delay, struct hinic3_nic_dev, moderation_task);
+	period = (unsigned long)(jiffies - nic_dev->last_moder_jiffies);
+	netdev = nic_dev->netdev;
+
+	queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task,
+			   HINIC3_MODERATONE_DELAY);
+
+	for (qid = 0; qid < nic_dev->q_params.num_qps; qid++) {
+		rx_packets = nic_dev->rxqs[qid].rxq_stats.packets;
+		rx_bytes = nic_dev->rxqs[qid].rxq_stats.bytes;
+		tx_packets = nic_dev->txqs[qid].txq_stats.packets;
+		tx_bytes = nic_dev->txqs[qid].txq_stats.bytes;
+
+		rx_pkt_diff =
+			rx_packets - nic_dev->rxqs[qid].last_moder_packets;
+		avg_pkt_size = rx_pkt_diff ?
+			((unsigned long)(rx_bytes -
+			 nic_dev->rxqs[qid].last_moder_bytes)) /
+			 rx_pkt_diff : 0;
+
+		rx_rate = rx_pkt_diff * HZ / period;
+		tx_pkt_diff =
+			tx_packets - nic_dev->txqs[qid].last_moder_packets;
+		tx_rate = tx_pkt_diff * HZ / period;
+
+		hinic3_update_queue_coal(netdev, qid, rx_rate, avg_pkt_size,
+					 tx_rate);
+		nic_dev->rxqs[qid].last_moder_packets = rx_packets;
+		nic_dev->rxqs[qid].last_moder_bytes = rx_bytes;
+		nic_dev->txqs[qid].last_moder_packets = tx_packets;
+		nic_dev->txqs[qid].last_moder_bytes = tx_bytes;
+	}
+
+	nic_dev->last_moder_jiffies = jiffies;
+}
+
 int hinic3_qps_irq_init(struct net_device *netdev)
 {
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
@@ -157,6 +288,9 @@ int hinic3_qps_irq_init(struct net_device *netdev)
 				      HINIC3_MSIX_ENABLE);
 	}
 
+	INIT_DELAYED_WORK(&nic_dev->moderation_task,
+			  hinic3_auto_moderation_work);
+
 	return 0;
 
 err_release_irqs:
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
index e12102806791..4a47dac1c4b4 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
@@ -108,6 +108,22 @@ static void hinic3_free_txrxqs(struct net_device *netdev)
 	hinic3_free_txqs(netdev);
 }
 
+static void hinic3_periodic_work_handler(struct work_struct *work)
+{
+	struct delayed_work *delay = to_delayed_work(work);
+	struct hinic3_nic_dev *nic_dev;
+
+	nic_dev = container_of(delay, struct hinic3_nic_dev, periodic_work);
+	if (test_and_clear_bit(HINIC3_EVENT_WORK_TX_TIMEOUT,
+			       &nic_dev->event_flag))
+		dev_info(nic_dev->hwdev->dev,
+			 "Fault event report, src: %u, level: %u\n",
+			 HINIC3_FAULT_SRC_TX_TIMEOUT,
+			 HINIC3_FAULT_LEVEL_SERIOUS_FLR);
+
+	queue_delayed_work(nic_dev->workq, &nic_dev->periodic_work, HZ);
+}
+
 static int hinic3_init_nic_dev(struct net_device *netdev,
 			       struct hinic3_hwdev *hwdev)
 {
@@ -121,8 +137,23 @@ static int hinic3_init_nic_dev(struct net_device *netdev,
 
 	nic_dev->rx_buf_len = HINIC3_RX_BUF_LEN;
 	nic_dev->lro_replenish_thld = HINIC3_LRO_REPLENISH_THLD;
+	nic_dev->vlan_bitmap = kzalloc(HINIC3_VLAN_BITMAP_SIZE(nic_dev),
+				       GFP_KERNEL);
+	if (!nic_dev->vlan_bitmap)
+		return -ENOMEM;
+
 	nic_dev->nic_svc_cap = hwdev->cfg_mgmt->cap.nic_svc_cap;
 
+	nic_dev->workq = create_singlethread_workqueue(HINIC3_NIC_DEV_WQ_NAME);
+	if (!nic_dev->workq) {
+		dev_err(hwdev->dev, "Failed to initialize nic workqueue\n");
+		kfree(nic_dev->vlan_bitmap);
+		return -ENOMEM;
+	}
+
+	INIT_DELAYED_WORK(&nic_dev->periodic_work,
+			  hinic3_periodic_work_handler);
+
 	return 0;
 }
 
@@ -229,6 +260,13 @@ static int hinic3_set_default_hw_feature(struct net_device *netdev)
 		return err;
 	}
 
+	err = hinic3_set_hw_features(netdev);
+	if (err) {
+		hinic3_update_nic_feature(nic_dev, 0);
+		hinic3_set_nic_feature_to_hw(nic_dev);
+		return err;
+	}
+
 	return 0;
 }
 
@@ -329,6 +367,7 @@ static int hinic3_nic_probe(struct auxiliary_device *adev,
 	if (err)
 		goto err_uninit_sw;
 
+	queue_delayed_work(nic_dev->workq, &nic_dev->periodic_work, HZ);
 	netif_carrier_off(netdev);
 
 	err = register_netdev(netdev);
@@ -368,12 +407,16 @@ static void hinic3_nic_remove(struct auxiliary_device *adev)
 	netdev = nic_dev->netdev;
 	unregister_netdev(netdev);
 
+	disable_delayed_work_sync(&nic_dev->periodic_work);
+	destroy_workqueue(nic_dev->workq);
+
 	hinic3_update_nic_feature(nic_dev, 0);
 	hinic3_set_nic_feature_to_hw(nic_dev);
 	hinic3_sw_uninit(netdev);
 
 	hinic3_free_nic_io(nic_dev);
 
+	kfree(nic_dev->vlan_bitmap);
 	free_netdev(netdev);
 }
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
index 3a6d3ee534d0..68dfdfa1b1ba 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
@@ -56,6 +56,31 @@ struct l2nic_cmd_update_mac {
 	u8                   new_mac[ETH_ALEN];
 };
 
+struct l2nic_cmd_vlan_config {
+	struct mgmt_msg_head msg_head;
+	u16                  func_id;
+	u8                   opcode;
+	u8                   rsvd1;
+	u16                  vlan_id;
+	u16                  rsvd2;
+};
+
+struct l2nic_cmd_vlan_offload {
+	struct mgmt_msg_head msg_head;
+	u16                  func_id;
+	u8                   vlan_offload;
+	u8                   vd1[5];
+};
+
+/* set vlan filter */
+struct l2nic_cmd_set_vlan_filter {
+	struct mgmt_msg_head msg_head;
+	u16                  func_id;
+	u8                   resvd[2];
+	/* bit0:vlan filter en; bit1:broadcast_filter_en */
+	u32                  vlan_filter_ctrl;
+};
+
 struct l2nic_cmd_set_ci_attr {
 	struct mgmt_msg_head msg_head;
 	u16                  func_idx;
@@ -102,6 +127,26 @@ struct l2nic_cmd_set_dcb_state {
 	u8                   rsvd[7];
 };
 
+struct l2nic_cmd_lro_config {
+	struct mgmt_msg_head msg_head;
+	u16                  func_id;
+	u8                   opcode;
+	u8                   rsvd1;
+	u8                   lro_ipv4_en;
+	u8                   lro_ipv6_en;
+	/* unit is 1K */
+	u8                   lro_max_pkt_len;
+	u8                   resv2[13];
+};
+
+struct l2nic_cmd_lro_timer {
+	struct mgmt_msg_head msg_head;
+	/* 1: set timer value, 0: get timer value */
+	u8                   opcode;
+	u8                   rsvd[3];
+	u32                  timer;
+};
+
 #define L2NIC_RSS_TYPE_VALID_MASK         BIT(23)
 #define L2NIC_RSS_TYPE_TCP_IPV6_EXT_MASK  BIT(24)
 #define L2NIC_RSS_TYPE_IPV6_EXT_MASK      BIT(25)
@@ -162,11 +207,16 @@ enum l2nic_cmd {
 	L2NIC_CMD_SET_VPORT_ENABLE    = 6,
 	L2NIC_CMD_SET_SQ_CI_ATTR      = 8,
 	L2NIC_CMD_CLEAR_QP_RESOURCE   = 11,
+	L2NIC_CMD_CFG_RX_LRO          = 13,
+	L2NIC_CMD_CFG_LRO_TIMER       = 14,
 	L2NIC_CMD_FEATURE_NEGO        = 15,
 	L2NIC_CMD_GET_MAC             = 20,
 	L2NIC_CMD_SET_MAC             = 21,
 	L2NIC_CMD_DEL_MAC             = 22,
 	L2NIC_CMD_UPDATE_MAC          = 23,
+	L2NIC_CMD_CFG_FUNC_VLAN       = 25,
+	L2NIC_CMD_SET_VLAN_FILTER_EN  = 26,
+	L2NIC_CMD_SET_RX_VLAN_OFFLOAD = 27,
 	L2NIC_CMD_CFG_RSS             = 60,
 	L2NIC_CMD_CFG_RSS_HASH_KEY    = 63,
 	L2NIC_CMD_CFG_RSS_HASH_ENGINE = 64,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index bf199f4ce847..ad50128f3d76 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -12,6 +12,15 @@
 #include "hinic3_rx.h"
 #include "hinic3_tx.h"
 
+#define HINIC3_LRO_DEFAULT_COAL_PKT_SIZE  32
+#define HINIC3_LRO_DEFAULT_TIME_LIMIT     16
+
+#define VLAN_BITMAP_BITS_SIZE(nic_dev)    (sizeof(*(nic_dev)->vlan_bitmap) * 8)
+#define VID_LINE(nic_dev, vid)  \
+	((vid) / VLAN_BITMAP_BITS_SIZE(nic_dev))
+#define VID_COL(nic_dev, vid)  \
+	((vid) & (VLAN_BITMAP_BITS_SIZE(nic_dev) - 1))
+
 /* try to modify the number of irq to the target number,
  * and return the actual number of irq.
  */
@@ -384,6 +393,9 @@ static int hinic3_vport_up(struct net_device *netdev)
 	if (!err && link_status_up)
 		netif_carrier_on(netdev);
 
+	queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task,
+			   HINIC3_MODERATONE_DELAY);
+
 	hinic3_print_link_message(netdev, link_status_up);
 
 	return 0;
@@ -406,6 +418,8 @@ static void hinic3_vport_down(struct net_device *netdev)
 	netif_carrier_off(netdev);
 	netif_tx_disable(netdev);
 
+	disable_delayed_work_sync(&nic_dev->moderation_task);
+
 	glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
 	hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
 
@@ -476,6 +490,162 @@ static int hinic3_close(struct net_device *netdev)
 	return 0;
 }
 
+#define SET_FEATURES_OP_STR(op)  ((op) ? "Enable" : "Disable")
+
+static int hinic3_set_feature_rx_csum(struct net_device *netdev,
+				      netdev_features_t wanted_features,
+				      netdev_features_t features,
+				      netdev_features_t *failed_features)
+{
+	netdev_features_t changed = wanted_features ^ features;
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+
+	if (changed & NETIF_F_RXCSUM)
+		dev_dbg(hwdev->dev, "%s rx csum success\n",
+			SET_FEATURES_OP_STR(wanted_features & NETIF_F_RXCSUM));
+
+	return 0;
+}
+
+static int hinic3_set_feature_tso(struct net_device *netdev,
+				  netdev_features_t wanted_features,
+				  netdev_features_t features,
+				  netdev_features_t *failed_features)
+{
+	netdev_features_t changed = wanted_features ^ features;
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+
+	if (changed & NETIF_F_TSO)
+		dev_dbg(hwdev->dev, "%s tso success\n",
+			SET_FEATURES_OP_STR(wanted_features & NETIF_F_TSO));
+
+	return 0;
+}
+
+static int hinic3_set_feature_lro(struct net_device *netdev,
+				  netdev_features_t wanted_features,
+				  netdev_features_t features,
+				  netdev_features_t *failed_features)
+{
+	netdev_features_t changed = wanted_features ^ features;
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+	bool en = !!(wanted_features & NETIF_F_LRO);
+	int err;
+
+	if (!(changed & NETIF_F_LRO))
+		return 0;
+
+	err = hinic3_set_rx_lro_state(hwdev, en,
+				      HINIC3_LRO_DEFAULT_TIME_LIMIT,
+				      HINIC3_LRO_DEFAULT_COAL_PKT_SIZE);
+	if (err) {
+		dev_err(hwdev->dev, "%s lro failed\n", SET_FEATURES_OP_STR(en));
+		*failed_features |= NETIF_F_LRO;
+	}
+
+	return err;
+}
+
+static int hinic3_set_feature_rx_cvlan(struct net_device *netdev,
+				       netdev_features_t wanted_features,
+				       netdev_features_t features,
+				       netdev_features_t *failed_features)
+{
+	bool en = !!(wanted_features & NETIF_F_HW_VLAN_CTAG_RX);
+	netdev_features_t changed = wanted_features ^ features;
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+	int err;
+
+	if (!(changed & NETIF_F_HW_VLAN_CTAG_RX))
+		return 0;
+
+	err = hinic3_set_rx_vlan_offload(hwdev, en);
+	if (err) {
+		dev_err(hwdev->dev, "%s rxvlan failed\n",
+			SET_FEATURES_OP_STR(en));
+		*failed_features |= NETIF_F_HW_VLAN_CTAG_RX;
+	}
+
+	return err;
+}
+
+static int hinic3_set_feature_vlan_filter(struct net_device *netdev,
+					  netdev_features_t wanted_features,
+					  netdev_features_t features,
+					  netdev_features_t *failed_features)
+{
+	bool en = !!(wanted_features & NETIF_F_HW_VLAN_CTAG_FILTER);
+	netdev_features_t changed = wanted_features ^ features;
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+	int err;
+
+	if (!(changed & NETIF_F_HW_VLAN_CTAG_FILTER))
+		return 0;
+
+	err = hinic3_set_vlan_fliter(hwdev, en);
+	if (err) {
+		dev_err(hwdev->dev, "%s rx vlan filter failed\n",
+			SET_FEATURES_OP_STR(en));
+		*failed_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+	}
+
+	return err;
+}
+
+static int hinic3_set_features(struct net_device *netdev,
+			       netdev_features_t curr,
+			       netdev_features_t wanted)
+{
+	netdev_features_t failed = 0;
+	int err;
+
+	err = hinic3_set_feature_rx_csum(netdev, wanted, curr, &failed) |
+	      hinic3_set_feature_tso(netdev, wanted, curr, &failed) |
+	      hinic3_set_feature_lro(netdev, wanted, curr, &failed) |
+	      hinic3_set_feature_rx_cvlan(netdev, wanted, curr, &failed) |
+	      hinic3_set_feature_vlan_filter(netdev, wanted, curr, &failed);
+	if (err) {
+		netdev->features = wanted ^ failed;
+		return err;
+	}
+
+	return 0;
+}
+
+static int hinic3_ndo_set_features(struct net_device *netdev,
+				   netdev_features_t features)
+{
+	return hinic3_set_features(netdev, netdev->features, features);
+}
+
+static netdev_features_t hinic3_fix_features(struct net_device *netdev,
+					     netdev_features_t features)
+{
+	netdev_features_t features_tmp = features;
+
+	/* If Rx checksum is disabled, then LRO should also be disabled */
+	if (!(features_tmp & NETIF_F_RXCSUM))
+		features_tmp &= ~NETIF_F_LRO;
+
+	return features_tmp;
+}
+
+int hinic3_set_hw_features(struct net_device *netdev)
+{
+	netdev_features_t wanted, curr;
+
+	wanted = netdev->features;
+	/* fake current features so all wanted are enabled */
+	curr = ~wanted;
+
+	return hinic3_set_features(netdev, curr, wanted);
+}
+
 static int hinic3_change_mtu(struct net_device *netdev, int new_mtu)
 {
 	int err;
@@ -517,11 +687,159 @@ static int hinic3_set_mac_addr(struct net_device *netdev, void *addr)
 	return 0;
 }
 
+static int hinic3_vlan_rx_add_vid(struct net_device *netdev,
+				  __be16 proto, u16 vid)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	unsigned long *vlan_bitmap = nic_dev->vlan_bitmap;
+	u32 column, row;
+	u16 func_id;
+	int err;
+
+	column = VID_COL(nic_dev, vid);
+	row = VID_LINE(nic_dev, vid);
+
+	func_id = hinic3_global_func_id(nic_dev->hwdev);
+
+	err = hinic3_add_vlan(nic_dev->hwdev, vid, func_id);
+	if (err) {
+		netdev_err(netdev, "Failed to add vlan %u\n", vid);
+		goto out;
+	}
+
+	set_bit(column, &vlan_bitmap[row]);
+	netdev_dbg(netdev, "Add vlan %u\n", vid);
+
+out:
+	return err;
+}
+
+static int hinic3_vlan_rx_kill_vid(struct net_device *netdev,
+				   __be16 proto, u16 vid)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	unsigned long *vlan_bitmap = nic_dev->vlan_bitmap;
+	u32 column, row;
+	u16 func_id;
+	int err;
+
+	column  = VID_COL(nic_dev, vid);
+	row = VID_LINE(nic_dev, vid);
+
+	func_id = hinic3_global_func_id(nic_dev->hwdev);
+	err = hinic3_del_vlan(nic_dev->hwdev, vid, func_id);
+	if (err) {
+		netdev_err(netdev, "Failed to delete vlan\n");
+		goto out;
+	}
+
+	clear_bit(column, &vlan_bitmap[row]);
+	netdev_dbg(netdev, "Remove vlan %u\n", vid);
+
+out:
+	return err;
+}
+
+static void hinic3_tx_timeout(struct net_device *netdev, unsigned int txqueue)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_io_queue *sq;
+	bool hw_err = false;
+	u16 sw_pi, hw_ci;
+	u8 q_id;
+
+	HINIC3_NIC_STATS_INC(nic_dev, netdev_tx_timeout);
+	netdev_err(netdev, "Tx timeout\n");
+
+	for (q_id = 0; q_id < nic_dev->q_params.num_qps; q_id++) {
+		if (!netif_xmit_stopped(netdev_get_tx_queue(netdev, q_id)))
+			continue;
+
+		sq = nic_dev->txqs[q_id].sq;
+		sw_pi = hinic3_get_sq_local_pi(sq);
+		hw_ci = hinic3_get_sq_hw_ci(sq);
+		netdev_dbg(netdev,
+			   "txq%u: sw_pi: %u, hw_ci: %u, sw_ci: %u, napi->state: 0x%lx.\n",
+			   q_id, sw_pi, hw_ci, hinic3_get_sq_local_ci(sq),
+			   nic_dev->q_params.irq_cfg[q_id].napi.state);
+
+		if (sw_pi != hw_ci)
+			hw_err = true;
+	}
+
+	if (hw_err)
+		set_bit(HINIC3_EVENT_WORK_TX_TIMEOUT, &nic_dev->event_flag);
+}
+
+static void hinic3_get_stats64(struct net_device *netdev,
+			       struct rtnl_link_stats64 *stats)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	u64 bytes, packets, dropped, errors;
+	struct hinic3_txq_stats *txq_stats;
+	struct hinic3_rxq_stats *rxq_stats;
+	struct hinic3_txq *txq;
+	struct hinic3_rxq *rxq;
+	unsigned int start;
+	int i;
+
+	bytes = 0;
+	packets = 0;
+	dropped = 0;
+	for (i = 0; i < nic_dev->max_qps; i++) {
+		if (!nic_dev->txqs)
+			break;
+
+		txq = &nic_dev->txqs[i];
+		txq_stats = &txq->txq_stats;
+		do {
+			start = u64_stats_fetch_begin(&txq_stats->syncp);
+			bytes += txq_stats->bytes;
+			packets += txq_stats->packets;
+			dropped += txq_stats->dropped;
+		} while (u64_stats_fetch_retry(&txq_stats->syncp, start));
+	}
+	stats->tx_packets = packets;
+	stats->tx_bytes   = bytes;
+	stats->tx_dropped = dropped;
+
+	bytes = 0;
+	packets = 0;
+	errors = 0;
+	dropped = 0;
+	for (i = 0; i < nic_dev->max_qps; i++) {
+		if (!nic_dev->rxqs)
+			break;
+
+		rxq = &nic_dev->rxqs[i];
+		rxq_stats = &rxq->rxq_stats;
+		do {
+			start = u64_stats_fetch_begin(&rxq_stats->syncp);
+			bytes += rxq_stats->bytes;
+			packets += rxq_stats->packets;
+			errors += rxq_stats->csum_errors +
+				rxq_stats->other_errors;
+			dropped += rxq_stats->dropped;
+		} while (u64_stats_fetch_retry(&rxq_stats->syncp, start));
+	}
+	stats->rx_packets = packets;
+	stats->rx_bytes   = bytes;
+	stats->rx_errors  = errors;
+	stats->rx_dropped = dropped;
+}
+
 static const struct net_device_ops hinic3_netdev_ops = {
 	.ndo_open             = hinic3_open,
 	.ndo_stop             = hinic3_close,
+	.ndo_set_features     = hinic3_ndo_set_features,
+	.ndo_fix_features     = hinic3_fix_features,
 	.ndo_change_mtu       = hinic3_change_mtu,
 	.ndo_set_mac_address  = hinic3_set_mac_addr,
+	.ndo_validate_addr    = eth_validate_addr,
+	.ndo_vlan_rx_add_vid  = hinic3_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid = hinic3_vlan_rx_kill_vid,
+	.ndo_tx_timeout       = hinic3_tx_timeout,
+	.ndo_get_stats64      = hinic3_get_stats64,
 	.ndo_start_xmit       = hinic3_xmit_frame,
 };
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
index 7fec13bbe60e..72e09402841a 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
@@ -10,6 +10,9 @@
 #include "hinic3_nic_dev.h"
 #include "hinic3_nic_io.h"
 
+#define MGMT_MSG_CMD_OP_ADD  1
+#define MGMT_MSG_CMD_OP_DEL  0
+
 static int hinic3_feature_nego(struct hinic3_hwdev *hwdev, u8 opcode,
 			       u64 *s_feature, u16 size)
 {
@@ -57,6 +60,136 @@ bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
 	return (nic_dev->nic_io->feature_cap & feature_bits) == feature_bits;
 }
 
+static int hinic3_set_rx_lro(struct hinic3_hwdev *hwdev, u8 ipv4_en, u8 ipv6_en,
+			     u8 lro_max_pkt_len)
+{
+	struct l2nic_cmd_lro_config lro_cfg = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	lro_cfg.func_id = hinic3_global_func_id(hwdev);
+	lro_cfg.opcode = MGMT_MSG_CMD_OP_SET;
+	lro_cfg.lro_ipv4_en = ipv4_en;
+	lro_cfg.lro_ipv6_en = ipv6_en;
+	lro_cfg.lro_max_pkt_len = lro_max_pkt_len;
+
+	mgmt_msg_params_init_default(&msg_params, &lro_cfg,
+				     sizeof(lro_cfg));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+				       L2NIC_CMD_CFG_RX_LRO,
+				       &msg_params);
+
+	if (err || lro_cfg.msg_head.status) {
+		dev_err(hwdev->dev, "Failed to set lro offload, err: %d, status: 0x%x\n",
+			err, lro_cfg.msg_head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int hinic3_set_rx_lro_timer(struct hinic3_hwdev *hwdev, u32 timer_value)
+{
+	struct l2nic_cmd_lro_timer lro_timer = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	lro_timer.opcode = MGMT_MSG_CMD_OP_SET;
+	lro_timer.timer = timer_value;
+
+	mgmt_msg_params_init_default(&msg_params, &lro_timer,
+				     sizeof(lro_timer));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+				       L2NIC_CMD_CFG_LRO_TIMER,
+				       &msg_params);
+
+	if (err || lro_timer.msg_head.status) {
+		dev_err(hwdev->dev, "Failed to set lro timer, err: %d, status: 0x%x\n",
+			err, lro_timer.msg_head.status);
+
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int hinic3_set_rx_lro_state(struct hinic3_hwdev *hwdev, u8 lro_en,
+			    u32 lro_timer, u8 lro_max_pkt_len)
+{
+	u8 ipv4_en, ipv6_en;
+	int err;
+
+	ipv4_en = lro_en ? 1 : 0;
+	ipv6_en = lro_en ? 1 : 0;
+
+	dev_dbg(hwdev->dev, "Set LRO max coalesce packet size to %uK\n",
+		lro_max_pkt_len);
+
+	err = hinic3_set_rx_lro(hwdev, ipv4_en, ipv6_en, lro_max_pkt_len);
+	if (err)
+		return err;
+
+	/* we don't set LRO timer for VF */
+	if (HINIC3_IS_VF(hwdev))
+		return 0;
+
+	dev_dbg(hwdev->dev, "Set LRO timer to %u\n", lro_timer);
+
+	return hinic3_set_rx_lro_timer(hwdev, lro_timer);
+}
+
+int hinic3_set_rx_vlan_offload(struct hinic3_hwdev *hwdev, u8 en)
+{
+	struct l2nic_cmd_vlan_offload vlan_cfg = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	vlan_cfg.func_id = hinic3_global_func_id(hwdev);
+	vlan_cfg.vlan_offload = en;
+
+	mgmt_msg_params_init_default(&msg_params, &vlan_cfg,
+				     sizeof(vlan_cfg));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+				       L2NIC_CMD_SET_RX_VLAN_OFFLOAD,
+				       &msg_params);
+
+	if (err || vlan_cfg.msg_head.status) {
+		dev_err(hwdev->dev, "Failed to set rx vlan offload, err: %d, status: 0x%x\n",
+			err, vlan_cfg.msg_head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int hinic3_set_vlan_fliter(struct hinic3_hwdev *hwdev, u32 vlan_filter_ctrl)
+{
+	struct l2nic_cmd_set_vlan_filter vlan_filter;
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	vlan_filter.func_id = hinic3_global_func_id(hwdev);
+	vlan_filter.vlan_filter_ctrl = vlan_filter_ctrl;
+
+	mgmt_msg_params_init_default(&msg_params, &vlan_filter,
+				     sizeof(vlan_filter));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+				       L2NIC_CMD_SET_VLAN_FILTER_EN,
+				       &msg_params);
+
+	if (err || vlan_filter.msg_head.status) {
+		dev_err(hwdev->dev, "Failed to set vlan filter, err: %d, status: 0x%x\n",
+			err, vlan_filter.msg_head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
 void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap)
 {
 	nic_dev->nic_io->feature_cap = feature_cap;
@@ -366,6 +499,44 @@ int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev)
 	return pkt_drop.msg_head.status;
 }
 
+static int hinic3_config_vlan(struct hinic3_hwdev *hwdev,
+			      u8 opcode, u16 vlan_id, u16 func_id)
+{
+	struct l2nic_cmd_vlan_config vlan_info = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	vlan_info.opcode = opcode;
+	vlan_info.func_id = func_id;
+	vlan_info.vlan_id = vlan_id;
+
+	mgmt_msg_params_init_default(&msg_params, &vlan_info,
+				     sizeof(vlan_info));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+				       L2NIC_CMD_CFG_FUNC_VLAN, &msg_params);
+
+	if (err || vlan_info.msg_head.status) {
+		dev_err(hwdev->dev,
+			"Failed to %s vlan, err: %d, status: 0x%x\n",
+			opcode == MGMT_MSG_CMD_OP_ADD ? "add" : "delete",
+			err, vlan_info.msg_head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id)
+{
+	return hinic3_config_vlan(hwdev, MGMT_MSG_CMD_OP_ADD, vlan_id, func_id);
+}
+
+int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id)
+{
+	return hinic3_config_vlan(hwdev, MGMT_MSG_CMD_OP_DEL, vlan_id, func_id);
+}
+
 int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable)
 {
 	struct mag_cmd_set_port_enable en_state = {};
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
index d4326937db48..bb7c2a67dd4b 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -57,6 +57,11 @@ bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
 			 enum hinic3_nic_feature_cap feature_bits);
 void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap);
 
+int hinic3_set_rx_lro_state(struct hinic3_hwdev *hwdev, u8 lro_en,
+			    u32 lro_timer, u8 lro_max_pkt_len);
+int hinic3_set_rx_vlan_offload(struct hinic3_hwdev *hwdev, u8 en);
+int hinic3_set_vlan_fliter(struct hinic3_hwdev *hwdev, u32 vlan_filter_ctrl);
+
 int hinic3_init_function_table(struct hinic3_nic_dev *nic_dev);
 int hinic3_set_port_mtu(struct net_device *netdev, u16 new_mtu);
 
@@ -78,5 +83,7 @@ int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable);
 int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up);
 int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
 			    bool enable);
+int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
+int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
 
 #endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
index 3a9f3ccdb684..b628294b375c 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
@@ -4,15 +4,42 @@
 #ifndef _HINIC3_NIC_DEV_H_
 #define _HINIC3_NIC_DEV_H_
 
+#include <linux/if_vlan.h>
 #include <linux/netdevice.h>
 
 #include "hinic3_hw_cfg.h"
+#include "hinic3_hwdev.h"
 #include "hinic3_mgmt_interface.h"
 
+#define HINIC3_VLAN_BITMAP_BYTE_SIZE(nic_dev)  (sizeof(*(nic_dev)->vlan_bitmap))
+#define HINIC3_VLAN_BITMAP_SIZE(nic_dev)  \
+	(VLAN_N_VID / HINIC3_VLAN_BITMAP_BYTE_SIZE(nic_dev))
+#define HINIC3_MODERATONE_DELAY  HZ
+
 enum hinic3_flags {
 	HINIC3_RSS_ENABLE,
 };
 
+enum hinic3_event_work_flags {
+	HINIC3_EVENT_WORK_TX_TIMEOUT,
+};
+
+#define HINIC3_NIC_STATS_INC(nic_dev, field) \
+do { \
+	u64_stats_update_begin(&(nic_dev)->stats.syncp); \
+	(nic_dev)->stats.field++; \
+	u64_stats_update_end(&(nic_dev)->stats.syncp); \
+} while (0)
+
+struct hinic3_nic_stats {
+	u64                   netdev_tx_timeout;
+
+	/* Subdivision statistics show in private tool */
+	u64                   tx_carrier_off_drop;
+	u64                   tx_invalid_qid;
+	struct u64_stats_sync syncp;
+};
+
 enum hinic3_rss_hash_type {
 	HINIC3_RSS_HASH_ENGINE_TYPE_XOR  = 0,
 	HINIC3_RSS_HASH_ENGINE_TYPE_TOEP = 1,
@@ -55,6 +82,15 @@ struct hinic3_intr_coal_info {
 	u8 pending_limit;
 	u8 coalesce_timer_cfg;
 	u8 resend_timer_cfg;
+
+	u64 pkt_rate_low;
+	u8  rx_usecs_low;
+	u8  rx_pending_limit_low;
+	u64 pkt_rate_high;
+	u8  rx_usecs_high;
+	u8  rx_pending_limit_high;
+
+	u8  user_set_intr_coal_flag;
 };
 
 struct hinic3_nic_dev {
@@ -66,12 +102,14 @@ struct hinic3_nic_dev {
 	u16                             max_qps;
 	u16                             rx_buf_len;
 	u32                             lro_replenish_thld;
+	unsigned long                   *vlan_bitmap;
 	unsigned long                   flags;
 	struct hinic3_nic_service_cap   nic_svc_cap;
 
 	struct hinic3_dyna_txrxq_params q_params;
 	struct hinic3_txq               *txqs;
 	struct hinic3_rxq               *rxqs;
+	struct hinic3_nic_stats         stats;
 
 	enum hinic3_rss_hash_type       rss_hash_type;
 	struct hinic3_rss_type          rss_type;
@@ -82,13 +120,20 @@ struct hinic3_nic_dev {
 	struct msix_entry               *qps_msix_entries;
 
 	struct hinic3_intr_coal_info    *intr_coalesce;
+	unsigned long                   last_moder_jiffies;
 
+	struct workqueue_struct         *workq;
+	struct delayed_work             periodic_work;
+	struct delayed_work             moderation_task;
 	struct semaphore                port_state_sem;
 
+	/* flag bits defined by hinic3_event_work_flags */
+	unsigned long                   event_flag;
 	bool                            link_status_up;
 };
 
 void hinic3_set_netdev_ops(struct net_device *netdev);
+int hinic3_set_hw_features(struct net_device *netdev);
 int hinic3_qps_irq_init(struct net_device *netdev);
 void hinic3_qps_irq_uninit(struct net_device *netdev);
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
index 44ae841a3648..9ab9fa03d80b 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
@@ -25,6 +25,20 @@
 #define RQ_CQE_STATUS_GET(val, member) \
 	FIELD_GET(RQ_CQE_STATUS_##member##_MASK, val)
 
+struct hinic3_rxq_stats {
+	u64                   packets;
+	u64                   bytes;
+	u64                   errors;
+	u64                   csum_errors;
+	u64                   other_errors;
+	u64                   dropped;
+	u64                   rx_buf_empty;
+	u64                   alloc_skb_err;
+	u64                   alloc_rx_buf_err;
+	u64                   restore_drop_sge;
+	struct u64_stats_sync syncp;
+};
+
 /* RX Completion information that is provided by HW for a specific RX WQE */
 struct hinic3_rq_cqe {
 	__le32 status;
@@ -59,6 +73,7 @@ struct hinic3_rxq {
 	u16                     buf_len;
 	u32                     buf_len_shift;
 
+	struct hinic3_rxq_stats rxq_stats;
 	u32                     cons_idx;
 	u32                     delta;
 
@@ -80,6 +95,11 @@ struct hinic3_rxq {
 	struct device          *dev; /* device for DMA mapping */
 
 	dma_addr_t             cqe_start_paddr;
+
+	u64                    last_moder_packets;
+	u64                    last_moder_bytes;
+	u8                     last_coalesc_timer_cfg;
+	u8                     last_pending_limit;
 } ____cacheline_aligned;
 
 struct hinic3_dyna_rxq_res {
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
index 7e1b872ba752..7df7f3fe8061 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
@@ -100,6 +100,20 @@ struct hinic3_sq_wqe_combo {
 	u32                       task_type;
 };
 
+struct hinic3_txq_stats {
+	u64                   packets;
+	u64                   bytes;
+	u64                   busy;
+	u64                   dropped;
+	u64                   skb_pad_err;
+	u64                   frag_len_overflow;
+	u64                   offload_cow_skb_err;
+	u64                   map_frag_err;
+	u64                   unknown_tunnel_pkt;
+	u64                   frag_size_err;
+	struct u64_stats_sync syncp;
+};
+
 struct hinic3_dma_info {
 	dma_addr_t dma;
 	u32        len;
@@ -123,6 +137,10 @@ struct hinic3_txq {
 
 	struct hinic3_tx_info   *tx_info;
 	struct hinic3_io_queue  *sq;
+
+	struct hinic3_txq_stats txq_stats;
+	u64                     last_moder_packets;
+	u64                     last_moder_bytes;
 } ____cacheline_aligned;
 
 struct hinic3_dyna_txq_res {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next v05 4/5] hinic3: Add mac filter ops
  2025-11-06 11:15 [PATCH net-next v05 0/5] net: hinic3: PF initialization Fan Gong
                   ` (2 preceding siblings ...)
  2025-11-06 11:15 ` [PATCH net-next v05 3/5] hinic3: Add NIC configuration ops Fan Gong
@ 2025-11-06 11:15 ` Fan Gong
  2025-11-06 19:44   ` [External] : " ALOK TIWARI
  2025-11-06 11:15 ` [PATCH net-next v05 5/5] hinic3: Add netdev register interfaces Fan Gong
  4 siblings, 1 reply; 11+ messages in thread
From: Fan Gong @ 2025-11-06 11:15 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi

Add ops to support unicast and multicast to filter mac address and
forward packages.

Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
---
 drivers/net/ethernet/huawei/hinic3/Makefile   |   1 +
 .../ethernet/huawei/hinic3/hinic3_filter.c    | 418 ++++++++++++++++++
 .../net/ethernet/huawei/hinic3/hinic3_main.c  |   6 +
 .../huawei/hinic3/hinic3_mgmt_interface.h     |  17 +
 .../huawei/hinic3/hinic3_netdev_ops.c         |  15 +
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.c   |  24 +
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |   1 +
 .../ethernet/huawei/hinic3/hinic3_nic_dev.h   |  33 ++
 8 files changed, 515 insertions(+)

diff --git a/drivers/net/ethernet/huawei/hinic3/Makefile b/drivers/net/ethernet/huawei/hinic3/Makefile
index c3efa45a6a42..26c05ecf31c9 100644
--- a/drivers/net/ethernet/huawei/hinic3/Makefile
+++ b/drivers/net/ethernet/huawei/hinic3/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_HINIC3) += hinic3.o
 hinic3-objs := hinic3_cmdq.o \
 	       hinic3_common.o \
 	       hinic3_eqs.o \
+	       hinic3_filter.o \
 	       hinic3_hw_cfg.o \
 	       hinic3_hw_comm.o \
 	       hinic3_hwdev.o \
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_filter.c b/drivers/net/ethernet/huawei/hinic3/hinic3_filter.c
new file mode 100644
index 000000000000..ea5193c01886
--- /dev/null
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_filter.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include "hinic3_hwif.h"
+#include "hinic3_nic_dev.h"
+#include "hinic3_nic_cfg.h"
+
+static int hinic3_uc_sync(struct net_device *netdev, u8 *addr)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+	return hinic3_set_mac(nic_dev->hwdev, addr, 0,
+			      hinic3_global_func_id(nic_dev->hwdev));
+}
+
+static int hinic3_uc_unsync(struct net_device *netdev, u8 *addr)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+	/* The addr is in use */
+	if (ether_addr_equal(addr, netdev->dev_addr))
+		return 0;
+
+	return hinic3_del_mac(nic_dev->hwdev, addr, 0,
+			      hinic3_global_func_id(nic_dev->hwdev));
+}
+
+void hinic3_clean_mac_list_filter(struct net_device *netdev)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_mac_filter *ftmp;
+	struct hinic3_mac_filter *f;
+
+	list_for_each_entry_safe(f, ftmp, &nic_dev->uc_filter_list, list) {
+		if (f->state == HINIC3_MAC_HW_SYNCED)
+			hinic3_uc_unsync(netdev, f->addr);
+		list_del(&f->list);
+		kfree(f);
+	}
+
+	list_for_each_entry_safe(f, ftmp, &nic_dev->mc_filter_list, list) {
+		if (f->state == HINIC3_MAC_HW_SYNCED)
+			hinic3_uc_unsync(netdev, f->addr);
+		list_del(&f->list);
+		kfree(f);
+	}
+}
+
+static struct hinic3_mac_filter *
+hinic3_find_mac(const struct list_head *filter_list, u8 *addr)
+{
+	struct hinic3_mac_filter *f;
+
+	list_for_each_entry(f, filter_list, list) {
+		if (ether_addr_equal(addr, f->addr))
+			return f;
+	}
+	return NULL;
+}
+
+static void hinic3_add_filter(struct net_device *netdev,
+			      struct list_head *mac_filter_list,
+			      u8 *addr)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct hinic3_mac_filter *f;
+
+	f = kzalloc(sizeof(*f), GFP_ATOMIC);
+	if (!f)
+		return;
+
+	ether_addr_copy(f->addr, addr);
+
+	INIT_LIST_HEAD(&f->list);
+	list_add_tail(&f->list, mac_filter_list);
+
+	f->state = HINIC3_MAC_WAIT_HW_SYNC;
+	set_bit(HINIC3_MAC_FILTER_CHANGED, &nic_dev->flags);
+}
+
+static void hinic3_del_filter(struct net_device *netdev,
+			      struct hinic3_mac_filter *f)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+	set_bit(HINIC3_MAC_FILTER_CHANGED, &nic_dev->flags);
+
+	if (f->state == HINIC3_MAC_WAIT_HW_SYNC) {
+		/* have not added to hw, delete it directly */
+		list_del(&f->list);
+		kfree(f);
+		return;
+	}
+
+	f->state = HINIC3_MAC_WAIT_HW_UNSYNC;
+}
+
+static struct hinic3_mac_filter *
+hinic3_mac_filter_entry_clone(const struct hinic3_mac_filter *src)
+{
+	struct hinic3_mac_filter *f;
+
+	f = kzalloc(sizeof(*f), GFP_ATOMIC);
+	if (!f)
+		return NULL;
+
+	*f = *src;
+	INIT_LIST_HEAD(&f->list);
+
+	return f;
+}
+
+static void hinic3_undo_del_filter_entries(struct list_head *filter_list,
+					   const struct list_head *from)
+{
+	struct hinic3_mac_filter *ftmp;
+	struct hinic3_mac_filter *f;
+
+	list_for_each_entry_safe(f, ftmp, from, list) {
+		if (hinic3_find_mac(filter_list, f->addr))
+			continue;
+
+		if (f->state == HINIC3_MAC_HW_SYNCED)
+			f->state = HINIC3_MAC_WAIT_HW_UNSYNC;
+
+		list_move_tail(&f->list, filter_list);
+	}
+}
+
+static void hinic3_undo_add_filter_entries(struct list_head *filter_list,
+					   const struct list_head *from)
+{
+	struct hinic3_mac_filter *ftmp;
+	struct hinic3_mac_filter *tmp;
+	struct hinic3_mac_filter *f;
+
+	list_for_each_entry_safe(f, ftmp, from, list) {
+		tmp = hinic3_find_mac(filter_list, f->addr);
+		if (tmp && tmp->state == HINIC3_MAC_HW_SYNCING)
+			tmp->state = HINIC3_MAC_WAIT_HW_SYNC;
+	}
+}
+
+static void hinic3_cleanup_filter_list(const struct list_head *head)
+{
+	struct hinic3_mac_filter *ftmp;
+	struct hinic3_mac_filter *f;
+
+	list_for_each_entry_safe(f, ftmp, head, list) {
+		list_del(&f->list);
+		kfree(f);
+	}
+}
+
+static int hinic3_mac_filter_sync_hw(struct net_device *netdev,
+				     struct list_head *del_list,
+				     struct list_head *add_list)
+{
+	struct hinic3_mac_filter *ftmp;
+	struct hinic3_mac_filter *f;
+	int err, add_count = 0;
+
+	if (!list_empty(del_list)) {
+		list_for_each_entry_safe(f, ftmp, del_list, list) {
+			/* ignore errors when deleting mac */
+			hinic3_uc_unsync(netdev, f->addr);
+			list_del(&f->list);
+			kfree(f);
+		}
+	}
+
+	if (!list_empty(add_list)) {
+		list_for_each_entry_safe(f, ftmp, add_list, list) {
+			if (f->state != HINIC3_MAC_HW_SYNCING)
+				continue;
+
+			err = hinic3_uc_sync(netdev, f->addr);
+			if (err) {
+				netdev_err(netdev, "Failed to add mac\n");
+				return err;
+			}
+
+			f->state = HINIC3_MAC_HW_SYNCED;
+			add_count++;
+		}
+	}
+
+	return add_count;
+}
+
+static int hinic3_mac_filter_sync(struct net_device *netdev,
+				  struct list_head *mac_filter_list, bool uc)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	struct list_head tmp_del_list, tmp_add_list;
+	int err = 0, add_list_len = 0, add_count;
+	struct hinic3_mac_filter *fclone;
+	struct hinic3_mac_filter *ftmp;
+	struct hinic3_mac_filter *f;
+
+	INIT_LIST_HEAD(&tmp_del_list);
+	INIT_LIST_HEAD(&tmp_add_list);
+
+	list_for_each_entry_safe(f, ftmp, mac_filter_list, list) {
+		if (f->state != HINIC3_MAC_WAIT_HW_UNSYNC)
+			continue;
+
+		f->state = HINIC3_MAC_HW_UNSYNCED;
+		list_move_tail(&f->list, &tmp_del_list);
+	}
+
+	list_for_each_entry_safe(f, ftmp, mac_filter_list, list) {
+		if (f->state != HINIC3_MAC_WAIT_HW_SYNC)
+			continue;
+
+		fclone = hinic3_mac_filter_entry_clone(f);
+		if (!fclone) {
+			hinic3_undo_del_filter_entries(mac_filter_list,
+						       &tmp_del_list);
+			hinic3_undo_add_filter_entries(mac_filter_list,
+						       &tmp_add_list);
+
+			netdev_err(netdev,
+				   "Failed to clone mac_filter_entry\n");
+			err = -ENOMEM;
+			goto cleanup_tmp_filter_list;
+		}
+
+		f->state = HINIC3_MAC_HW_SYNCING;
+		list_add_tail(&fclone->list, &tmp_add_list);
+		add_list_len++;
+	}
+
+	add_count = hinic3_mac_filter_sync_hw(netdev, &tmp_del_list,
+					      &tmp_add_list);
+	if (add_count < add_list_len) {
+		/* there were errors, delete all mac in hw */
+		hinic3_undo_add_filter_entries(mac_filter_list, &tmp_add_list);
+		/* VF does not support promiscuous mode,
+		 * don't delete any other uc mac.
+		 */
+		if (!HINIC3_IS_VF(nic_dev->hwdev) || !uc) {
+			list_for_each_entry_safe(f, ftmp, mac_filter_list,
+						 list) {
+				if (f->state != HINIC3_MAC_HW_SYNCED)
+					continue;
+
+				fclone = hinic3_mac_filter_entry_clone(f);
+				if (!fclone)
+					break;
+
+				f->state = HINIC3_MAC_HW_SYNCING;
+				list_add_tail(&fclone->list, &tmp_del_list);
+			}
+		}
+
+		hinic3_mac_filter_sync_hw(netdev, &tmp_del_list, &tmp_add_list);
+
+		/* need to enter promiscuous/allmulti mode */
+		err = -ENOMEM;
+	}
+
+cleanup_tmp_filter_list:
+	hinic3_cleanup_filter_list(&tmp_del_list);
+	hinic3_cleanup_filter_list(&tmp_add_list);
+
+	return err ? err : add_count;
+}
+
+static void hinic3_mac_filter_sync_all(struct net_device *netdev)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	int add_count;
+
+	if (test_bit(HINIC3_MAC_FILTER_CHANGED, &nic_dev->flags)) {
+		clear_bit(HINIC3_MAC_FILTER_CHANGED, &nic_dev->flags);
+		add_count = hinic3_mac_filter_sync(netdev,
+						   &nic_dev->uc_filter_list,
+						   true);
+		if (add_count < 0 &&
+		    hinic3_test_support(nic_dev, HINIC3_NIC_F_PROMISC))
+			set_bit(HINIC3_PROMISC_FORCE_ON,
+				&nic_dev->rx_mod_state);
+		else if (add_count)
+			clear_bit(HINIC3_PROMISC_FORCE_ON,
+				  &nic_dev->rx_mod_state);
+
+		add_count = hinic3_mac_filter_sync(netdev,
+						   &nic_dev->mc_filter_list,
+						   false);
+		if (add_count < 0 &&
+		    hinic3_test_support(nic_dev, HINIC3_NIC_F_ALLMULTI))
+			set_bit(HINIC3_ALLMULTI_FORCE_ON,
+				&nic_dev->rx_mod_state);
+		else if (add_count)
+			clear_bit(HINIC3_ALLMULTI_FORCE_ON,
+				  &nic_dev->rx_mod_state);
+	}
+}
+
+#define HINIC3_DEFAULT_RX_MODE \
+	(L2NIC_RX_MODE_UC | L2NIC_RX_MODE_MC | L2NIC_RX_MODE_BC)
+
+static void hinic3_update_mac_filter(struct net_device *netdev,
+				     const struct netdev_hw_addr_list *src_list,
+				     struct list_head *filter_list)
+{
+	struct hinic3_mac_filter *filter;
+	struct hinic3_mac_filter *ftmp;
+	struct hinic3_mac_filter *f;
+	struct netdev_hw_addr *ha;
+
+	/* add addr if not already in the filter list */
+	netif_addr_lock_bh(netdev);
+	netdev_hw_addr_list_for_each(ha, src_list) {
+		filter = hinic3_find_mac(filter_list, ha->addr);
+		if (!filter)
+			hinic3_add_filter(netdev, filter_list, ha->addr);
+		else if (filter->state == HINIC3_MAC_WAIT_HW_UNSYNC)
+			filter->state = HINIC3_MAC_HW_SYNCED;
+	}
+	netif_addr_unlock_bh(netdev);
+
+	/* delete addr if not in netdev list */
+	list_for_each_entry_safe(f, ftmp, filter_list, list) {
+		bool found = false;
+
+		netif_addr_lock_bh(netdev);
+		netdev_hw_addr_list_for_each(ha, src_list)
+			if (ether_addr_equal(ha->addr, f->addr)) {
+				found = true;
+				break;
+			}
+		netif_addr_unlock_bh(netdev);
+
+		if (found)
+			continue;
+
+		hinic3_del_filter(netdev, f);
+	}
+}
+
+static void hinic3_sync_rx_mode_to_hw(struct net_device *netdev, int promisc_en,
+				      int allmulti_en)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	u32 rx_mod = HINIC3_DEFAULT_RX_MODE;
+	int err;
+
+	rx_mod |= (promisc_en ? L2NIC_RX_MODE_PROMISC : 0);
+	rx_mod |= (allmulti_en ? L2NIC_RX_MODE_MC_ALL : 0);
+
+	if (promisc_en != test_bit(HINIC3_HW_PROMISC_ON,
+				   &nic_dev->rx_mod_state))
+		netdev_dbg(netdev, "%s promisc mode\n",
+			   promisc_en ? "Enter" : "Left");
+	if (allmulti_en !=
+	    test_bit(HINIC3_HW_ALLMULTI_ON, &nic_dev->rx_mod_state))
+		netdev_dbg(netdev, "%s all_multi mode\n",
+			   allmulti_en ? "Enter" : "Left");
+
+	err = hinic3_set_rx_mode(nic_dev->hwdev, rx_mod);
+	if (err) {
+		netdev_err(netdev, "Failed to set rx_mode\n");
+		return;
+	}
+
+	promisc_en ? set_bit(HINIC3_HW_PROMISC_ON, &nic_dev->rx_mod_state) :
+		clear_bit(HINIC3_HW_PROMISC_ON, &nic_dev->rx_mod_state);
+
+	allmulti_en ? set_bit(HINIC3_HW_ALLMULTI_ON, &nic_dev->rx_mod_state) :
+		clear_bit(HINIC3_HW_ALLMULTI_ON, &nic_dev->rx_mod_state);
+}
+
+void hinic3_set_rx_mode_work(struct work_struct *work)
+{
+	int promisc_en = 0, allmulti_en = 0;
+	struct hinic3_nic_dev *nic_dev;
+	struct net_device *netdev;
+
+	nic_dev = container_of(work, struct hinic3_nic_dev, rx_mode_work);
+	netdev = nic_dev->netdev;
+
+	if (test_and_clear_bit(HINIC3_UPDATE_MAC_FILTER, &nic_dev->flags)) {
+		hinic3_update_mac_filter(netdev, &netdev->uc,
+					 &nic_dev->uc_filter_list);
+		hinic3_update_mac_filter(netdev, &netdev->mc,
+					 &nic_dev->mc_filter_list);
+	}
+
+	hinic3_mac_filter_sync_all(netdev);
+
+	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_PROMISC))
+		promisc_en = !!(netdev->flags & IFF_PROMISC) ||
+			test_bit(HINIC3_PROMISC_FORCE_ON,
+				 &nic_dev->rx_mod_state);
+
+	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_ALLMULTI))
+		allmulti_en = !!(netdev->flags & IFF_ALLMULTI) ||
+			test_bit(HINIC3_ALLMULTI_FORCE_ON,
+				 &nic_dev->rx_mod_state);
+
+	if (promisc_en != test_bit(HINIC3_HW_PROMISC_ON,
+				   &nic_dev->rx_mod_state) ||
+	    allmulti_en != test_bit(HINIC3_HW_ALLMULTI_ON,
+				    &nic_dev->rx_mod_state))
+		hinic3_sync_rx_mode_to_hw(netdev, promisc_en, allmulti_en);
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
index 4a47dac1c4b4..e43597937da5 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
@@ -154,6 +154,10 @@ static int hinic3_init_nic_dev(struct net_device *netdev,
 	INIT_DELAYED_WORK(&nic_dev->periodic_work,
 			  hinic3_periodic_work_handler);
 
+	INIT_LIST_HEAD(&nic_dev->uc_filter_list);
+	INIT_LIST_HEAD(&nic_dev->mc_filter_list);
+	INIT_WORK(&nic_dev->rx_mode_work, hinic3_set_rx_mode_work);
+
 	return 0;
 }
 
@@ -220,6 +224,7 @@ static void hinic3_sw_uninit(struct net_device *netdev)
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
 
 	hinic3_free_txrxqs(netdev);
+	hinic3_clean_mac_list_filter(netdev);
 	hinic3_del_mac(nic_dev->hwdev, netdev->dev_addr, 0,
 		       hinic3_global_func_id(nic_dev->hwdev));
 	hinic3_clear_rss_config(netdev);
@@ -408,6 +413,7 @@ static void hinic3_nic_remove(struct auxiliary_device *adev)
 	unregister_netdev(netdev);
 
 	disable_delayed_work_sync(&nic_dev->periodic_work);
+	cancel_work_sync(&nic_dev->rx_mode_work);
 	destroy_workqueue(nic_dev->workq);
 
 	hinic3_update_nic_feature(nic_dev, 0);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
index 68dfdfa1b1ba..a69778b09c83 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
@@ -115,6 +115,22 @@ struct l2nic_cmd_set_vport_state {
 	u8                   rsvd2[3];
 };
 
+/* *
+ * Definition of the NIC receiving mode
+ */
+#define L2NIC_RX_MODE_UC       0x01
+#define L2NIC_RX_MODE_MC       0x02
+#define L2NIC_RX_MODE_BC       0x04
+#define L2NIC_RX_MODE_MC_ALL   0x08
+#define L2NIC_RX_MODE_PROMISC  0x10
+
+struct l2nic_rx_mode_config {
+	struct mgmt_msg_head msg_head;
+	u16                  func_id;
+	u16                  rsvd1;
+	u32                  rx_mode;
+};
+
 struct l2nic_cmd_set_dcb_state {
 	struct mgmt_msg_head head;
 	u16                  func_id;
@@ -205,6 +221,7 @@ enum l2nic_cmd {
 	/* FUNC CFG */
 	L2NIC_CMD_SET_FUNC_TBL        = 5,
 	L2NIC_CMD_SET_VPORT_ENABLE    = 6,
+	L2NIC_CMD_SET_RX_MODE         = 7,
 	L2NIC_CMD_SET_SQ_CI_ATTR      = 8,
 	L2NIC_CMD_CLEAR_QP_RESOURCE   = 11,
 	L2NIC_CMD_CFG_RX_LRO          = 13,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index ad50128f3d76..335de3093382 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -828,6 +828,20 @@ static void hinic3_get_stats64(struct net_device *netdev,
 	stats->rx_dropped = dropped;
 }
 
+static void hinic3_nic_set_rx_mode(struct net_device *netdev)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+	if (netdev_uc_count(netdev) != nic_dev->netdev_uc_cnt ||
+	    netdev_mc_count(netdev) != nic_dev->netdev_mc_cnt) {
+		set_bit(HINIC3_UPDATE_MAC_FILTER, &nic_dev->flags);
+		nic_dev->netdev_uc_cnt = netdev_uc_count(netdev);
+		nic_dev->netdev_mc_cnt = netdev_mc_count(netdev);
+	}
+
+	queue_work(nic_dev->workq, &nic_dev->rx_mode_work);
+}
+
 static const struct net_device_ops hinic3_netdev_ops = {
 	.ndo_open             = hinic3_open,
 	.ndo_stop             = hinic3_close,
@@ -840,6 +854,7 @@ static const struct net_device_ops hinic3_netdev_ops = {
 	.ndo_vlan_rx_kill_vid = hinic3_vlan_rx_kill_vid,
 	.ndo_tx_timeout       = hinic3_tx_timeout,
 	.ndo_get_stats64      = hinic3_get_stats64,
+	.ndo_set_rx_mode      = hinic3_nic_set_rx_mode,
 	.ndo_start_xmit       = hinic3_xmit_frame,
 };
 
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
index 72e09402841a..92afab46309c 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
@@ -499,6 +499,30 @@ int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev)
 	return pkt_drop.msg_head.status;
 }
 
+int hinic3_set_rx_mode(struct hinic3_hwdev *hwdev, u32 enable)
+{
+	struct l2nic_rx_mode_config rx_mode_cfg = {};
+	struct mgmt_msg_params msg_params = {};
+	int err;
+
+	rx_mode_cfg.func_id = hinic3_global_func_id(hwdev);
+	rx_mode_cfg.rx_mode = enable;
+
+	mgmt_msg_params_init_default(&msg_params, &rx_mode_cfg,
+				     sizeof(rx_mode_cfg));
+
+	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+				       L2NIC_CMD_SET_RX_MODE, &msg_params);
+
+	if (err || rx_mode_cfg.msg_head.status) {
+		dev_err(hwdev->dev, "Failed to set rx mode, err: %d, status: 0x%x\n",
+			err, rx_mode_cfg.msg_head.status);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
 static int hinic3_config_vlan(struct hinic3_hwdev *hwdev,
 			      u8 opcode, u16 vlan_id, u16 func_id)
 {
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
index bb7c2a67dd4b..2c129de241eb 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -77,6 +77,7 @@ int hinic3_set_ci_table(struct hinic3_hwdev *hwdev,
 			struct hinic3_sq_attr *attr);
 int hinic3_flush_qps_res(struct hinic3_hwdev *hwdev);
 int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev);
+int hinic3_set_rx_mode(struct hinic3_hwdev *hwdev, u32 enable);
 
 int hinic3_sync_dcb_state(struct hinic3_hwdev *hwdev, u8 op_code, u8 state);
 int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
index b628294b375c..985cbd91b7c8 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
@@ -17,7 +17,9 @@
 #define HINIC3_MODERATONE_DELAY  HZ
 
 enum hinic3_flags {
+	HINIC3_MAC_FILTER_CHANGED,
 	HINIC3_RSS_ENABLE,
+	HINIC3_UPDATE_MAC_FILTER,
 };
 
 enum hinic3_event_work_flags {
@@ -40,6 +42,27 @@ struct hinic3_nic_stats {
 	struct u64_stats_sync syncp;
 };
 
+enum hinic3_rx_mode_state {
+	HINIC3_HW_PROMISC_ON,
+	HINIC3_HW_ALLMULTI_ON,
+	HINIC3_PROMISC_FORCE_ON,
+	HINIC3_ALLMULTI_FORCE_ON,
+};
+
+enum hinic3_mac_filter_state {
+	HINIC3_MAC_WAIT_HW_SYNC,
+	HINIC3_MAC_HW_SYNCING,
+	HINIC3_MAC_HW_SYNCED,
+	HINIC3_MAC_WAIT_HW_UNSYNC,
+	HINIC3_MAC_HW_UNSYNCED,
+};
+
+struct hinic3_mac_filter {
+	struct list_head list;
+	u8               addr[ETH_ALEN];
+	unsigned long    state;
+};
+
 enum hinic3_rss_hash_type {
 	HINIC3_RSS_HASH_ENGINE_TYPE_XOR  = 0,
 	HINIC3_RSS_HASH_ENGINE_TYPE_TOEP = 1,
@@ -125,8 +148,15 @@ struct hinic3_nic_dev {
 	struct workqueue_struct         *workq;
 	struct delayed_work             periodic_work;
 	struct delayed_work             moderation_task;
+	struct work_struct              rx_mode_work;
 	struct semaphore                port_state_sem;
 
+	struct list_head                uc_filter_list;
+	struct list_head                mc_filter_list;
+	unsigned long                   rx_mod_state;
+	int                             netdev_uc_cnt;
+	int                             netdev_mc_cnt;
+
 	/* flag bits defined by hinic3_event_work_flags */
 	unsigned long                   event_flag;
 	bool                            link_status_up;
@@ -137,4 +167,7 @@ int hinic3_set_hw_features(struct net_device *netdev);
 int hinic3_qps_irq_init(struct net_device *netdev);
 void hinic3_qps_irq_uninit(struct net_device *netdev);
 
+void hinic3_set_rx_mode_work(struct work_struct *work);
+void hinic3_clean_mac_list_filter(struct net_device *netdev);
+
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next v05 5/5] hinic3: Add netdev register interfaces
  2025-11-06 11:15 [PATCH net-next v05 0/5] net: hinic3: PF initialization Fan Gong
                   ` (3 preceding siblings ...)
  2025-11-06 11:15 ` [PATCH net-next v05 4/5] hinic3: Add mac filter ops Fan Gong
@ 2025-11-06 11:15 ` Fan Gong
  4 siblings, 0 replies; 11+ messages in thread
From: Fan Gong @ 2025-11-06 11:15 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi

Add netdev notifier to accept netdev event.
Refine port event type to change link status.

Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
---
 .../net/ethernet/huawei/hinic3/hinic3_hwdev.h |   9 +
 .../net/ethernet/huawei/hinic3/hinic3_irq.c   |   2 +
 .../net/ethernet/huawei/hinic3/hinic3_main.c  | 182 +++++++++++++++++-
 .../huawei/hinic3/hinic3_netdev_ops.c         |  12 ++
 .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |  18 ++
 .../ethernet/huawei/hinic3/hinic3_nic_dev.h   |   2 +
 6 files changed, 222 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
index 58bc561f95b3..9686c2600b46 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
@@ -17,6 +17,15 @@ enum hinic3_event_service_type {
 	HINIC3_EVENT_SRV_NIC  = 1
 };
 
+enum hinic3_comm_event_type {
+	HINIC3_COMM_EVENT_PCIE_LINK_DOWN = 0,
+	HINIC3_COMM_EVENT_HEART_LOST = 1,
+	HINIC3_COMM_EVENT_FAULT = 2,
+	HINIC3_COMM_EVENT_SRIOV_STATE_CHANGE = 3,
+	HINIC3_COMM_EVENT_CARD_REMOVE = 4,
+	HINIC3_COMM_EVENT_MGMT_WATCHDOG = 5,
+};
+
 enum hinic3_fault_err_level {
 	HINIC3_FAULT_LEVEL_SERIOUS_FLR = 3,
 };
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
index cb9412986c26..d793dff88109 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
@@ -215,6 +215,8 @@ static void hinic3_auto_moderation_work(struct work_struct *work)
 	nic_dev = container_of(delay, struct hinic3_nic_dev, moderation_task);
 	period = (unsigned long)(jiffies - nic_dev->last_moder_jiffies);
 	netdev = nic_dev->netdev;
+	if (!test_bit(HINIC3_INTF_UP, &nic_dev->flags))
+		return;
 
 	queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task,
 			   HINIC3_MODERATONE_DELAY);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
index e43597937da5..030a15b5ccc0 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
@@ -29,6 +29,65 @@
 #define HINIC3_DEFAULT_TXRX_MSIX_COALESC_TIMER_CFG  25
 #define HINIC3_DEFAULT_TXRX_MSIX_RESEND_TIMER_CFG   7
 
+#define HINIC3_MAX_VLAN_DEPTH_OFFLOAD_SUPPORT  1
+#define HINIC3_VLAN_CLEAR_OFFLOAD \
+	(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
+	 NETIF_F_SCTP_CRC | NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
+
+/* used for netdev notifier register/unregister */
+static DEFINE_MUTEX(hinic3_netdev_notifiers_mutex);
+static int hinic3_netdev_notifiers_ref_cnt;
+
+static u16 hinic3_get_vlan_depth(struct net_device *netdev)
+{
+	u16 vlan_depth = 0;
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+	while (is_vlan_dev(netdev)) {
+		netdev = vlan_dev_priv(netdev)->real_dev;
+		vlan_depth++;
+	}
+#endif
+	return vlan_depth;
+}
+
+static int hinic3_netdev_event(struct notifier_block *notifier,
+			       unsigned long event, void *ptr)
+{
+	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+	struct hinic3_nic_dev *nic_dev = netdev_priv(ndev);
+	u16 vlan_depth;
+
+	if (!is_vlan_dev(ndev))
+		return NOTIFY_DONE;
+
+	netdev_hold(ndev, &nic_dev->tracker, GFP_ATOMIC);
+
+	switch (event) {
+	case NETDEV_REGISTER:
+		vlan_depth = hinic3_get_vlan_depth(ndev);
+		if (vlan_depth == HINIC3_MAX_VLAN_DEPTH_OFFLOAD_SUPPORT) {
+			ndev->vlan_features &= (~HINIC3_VLAN_CLEAR_OFFLOAD);
+		} else if (vlan_depth > HINIC3_MAX_VLAN_DEPTH_OFFLOAD_SUPPORT) {
+			ndev->hw_features &= (~HINIC3_VLAN_CLEAR_OFFLOAD);
+			ndev->features &= (~HINIC3_VLAN_CLEAR_OFFLOAD);
+		}
+
+		break;
+
+	default:
+		break;
+	}
+
+	netdev_put(ndev, &nic_dev->tracker);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block hinic3_netdev_notifier = {
+	.notifier_call = hinic3_netdev_event,
+};
+
 static void init_intr_coal_param(struct net_device *netdev)
 {
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
@@ -161,6 +220,14 @@ static int hinic3_init_nic_dev(struct net_device *netdev,
 	return 0;
 }
 
+static void hinic3_free_nic_dev(struct net_device *netdev)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+	destroy_workqueue(nic_dev->workq);
+	kfree(nic_dev->vlan_bitmap);
+}
+
 static int hinic3_sw_init(struct net_device *netdev)
 {
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
@@ -238,6 +305,8 @@ static void hinic3_assign_netdev_ops(struct net_device *netdev)
 static void netdev_feature_init(struct net_device *netdev)
 {
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	netdev_features_t hw_features = 0;
+	netdev_features_t vlan_fts = 0;
 	netdev_features_t cso_fts = 0;
 	netdev_features_t tso_fts = 0;
 	netdev_features_t dft_fts;
@@ -250,7 +319,29 @@ static void netdev_feature_init(struct net_device *netdev)
 	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_TSO))
 		tso_fts |= NETIF_F_TSO | NETIF_F_TSO6;
 
-	netdev->features |= dft_fts | cso_fts | tso_fts;
+	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_RX_VLAN_STRIP |
+				HINIC3_NIC_F_TX_VLAN_INSERT))
+		vlan_fts |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+
+	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_RX_VLAN_FILTER))
+		vlan_fts |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_VXLAN_OFFLOAD))
+		tso_fts |= NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM;
+
+	/* LRO is disabled by default, only set hw features */
+	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_LRO))
+		hw_features |= NETIF_F_LRO;
+
+	netdev->features |= dft_fts | cso_fts | tso_fts | vlan_fts;
+	netdev->vlan_features |= dft_fts | cso_fts | tso_fts;
+		hw_features |= netdev->hw_features | netdev->features;
+	netdev->hw_features = hw_features;
+	netdev->priv_flags |= IFF_UNICAST_FLT;
+
+	netdev->hw_enc_features |= dft_fts;
+	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_VXLAN_OFFLOAD))
+		netdev->hw_enc_features |= cso_fts | tso_fts | NETIF_F_TSO_ECN;
 }
 
 static int hinic3_set_default_hw_feature(struct net_device *netdev)
@@ -275,6 +366,36 @@ static int hinic3_set_default_hw_feature(struct net_device *netdev)
 	return 0;
 }
 
+static void hinic3_register_notifier(struct net_device *netdev)
+{
+	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+	int err;
+
+	mutex_lock(&hinic3_netdev_notifiers_mutex);
+	hinic3_netdev_notifiers_ref_cnt++;
+	if (hinic3_netdev_notifiers_ref_cnt == 1) {
+		err = register_netdevice_notifier(&hinic3_netdev_notifier);
+		if (err) {
+			dev_dbg(nic_dev->hwdev->dev,
+				"Register netdevice notifier failed, err: %d\n",
+				err);
+			hinic3_netdev_notifiers_ref_cnt--;
+		}
+	}
+	mutex_unlock(&hinic3_netdev_notifiers_mutex);
+}
+
+static void hinic3_unregister_notifier(void)
+{
+	mutex_lock(&hinic3_netdev_notifiers_mutex);
+	if (hinic3_netdev_notifiers_ref_cnt == 1)
+		unregister_netdevice_notifier(&hinic3_netdev_notifier);
+
+	if (hinic3_netdev_notifiers_ref_cnt)
+		hinic3_netdev_notifiers_ref_cnt--;
+	mutex_unlock(&hinic3_netdev_notifiers_mutex);
+}
+
 static void hinic3_link_status_change(struct net_device *netdev,
 				      bool link_status_up)
 {
@@ -297,6 +418,44 @@ static void hinic3_link_status_change(struct net_device *netdev,
 	}
 }
 
+static void hinic3_port_module_event_handler(struct net_device *netdev,
+					     struct hinic3_event_info *event)
+{
+	const char *g_hinic3_module_link_err[LINK_ERR_NUM] = {
+		"Unrecognized module"
+	};
+	struct hinic3_port_module_event *module_event;
+	enum port_module_event_type type;
+	enum link_err_type err_type;
+
+	module_event = (struct hinic3_port_module_event *)event->event_data;
+	type = module_event->type;
+	err_type = module_event->err_type;
+
+	switch (type) {
+	case HINIC3_PORT_MODULE_CABLE_PLUGGED:
+	case HINIC3_PORT_MODULE_CABLE_UNPLUGGED:
+		netdev_info(netdev, "Port module event: Cable %s\n",
+			    type == HINIC3_PORT_MODULE_CABLE_PLUGGED ?
+			    "plugged" : "unplugged");
+		break;
+	case HINIC3_PORT_MODULE_LINK_ERR:
+		if (err_type >= LINK_ERR_NUM) {
+			netdev_info(netdev, "Link failed, Unknown error type: 0x%x\n",
+				    err_type);
+		} else {
+			netdev_info(netdev,
+				    "Link failed, error type: 0x%x: %s\n",
+				    err_type,
+				    g_hinic3_module_link_err[err_type]);
+		}
+		break;
+	default:
+		netdev_err(netdev, "Unknown port module type %d\n", type);
+		break;
+	}
+}
+
 static void hinic3_nic_event(struct auxiliary_device *adev,
 			     struct hinic3_event_info *event)
 {
@@ -310,8 +469,20 @@ static void hinic3_nic_event(struct auxiliary_device *adev,
 				   HINIC3_NIC_EVENT_LINK_UP):
 		hinic3_link_status_change(netdev, true);
 		break;
+	case HINIC3_SRV_EVENT_TYPE(HINIC3_EVENT_SRV_NIC,
+				   HINIC3_NIC_EVENT_PORT_MODULE_EVENT):
+		hinic3_port_module_event_handler(netdev, event);
+		break;
 	case HINIC3_SRV_EVENT_TYPE(HINIC3_EVENT_SRV_NIC,
 				   HINIC3_NIC_EVENT_LINK_DOWN):
+	case HINIC3_SRV_EVENT_TYPE(HINIC3_EVENT_SRV_COMM,
+				   HINIC3_COMM_EVENT_FAULT):
+	case HINIC3_SRV_EVENT_TYPE(HINIC3_EVENT_SRV_COMM,
+				   HINIC3_COMM_EVENT_PCIE_LINK_DOWN):
+	case HINIC3_SRV_EVENT_TYPE(HINIC3_EVENT_SRV_COMM,
+				   HINIC3_COMM_EVENT_HEART_LOST):
+	case HINIC3_SRV_EVENT_TYPE(HINIC3_EVENT_SRV_COMM,
+				   HINIC3_COMM_EVENT_MGMT_WATCHDOG):
 		hinic3_link_status_change(netdev, false);
 		break;
 	default:
@@ -359,7 +530,7 @@ static int hinic3_nic_probe(struct auxiliary_device *adev,
 
 	err = hinic3_init_nic_io(nic_dev);
 	if (err)
-		goto err_free_netdev;
+		goto err_free_nic_dev;
 
 	err = hinic3_sw_init(netdev);
 	if (err)
@@ -372,6 +543,8 @@ static int hinic3_nic_probe(struct auxiliary_device *adev,
 	if (err)
 		goto err_uninit_sw;
 
+	hinic3_register_notifier(netdev);
+
 	queue_delayed_work(nic_dev->workq, &nic_dev->periodic_work, HZ);
 	netif_carrier_off(netdev);
 
@@ -382,6 +555,7 @@ static int hinic3_nic_probe(struct auxiliary_device *adev,
 	return 0;
 
 err_uninit_nic_feature:
+	hinic3_unregister_notifier();
 	hinic3_update_nic_feature(nic_dev, 0);
 	hinic3_set_nic_feature_to_hw(nic_dev);
 
@@ -390,7 +564,8 @@ static int hinic3_nic_probe(struct auxiliary_device *adev,
 
 err_free_nic_io:
 	hinic3_free_nic_io(nic_dev);
-
+err_free_nic_dev:
+	hinic3_free_nic_dev(netdev);
 err_free_netdev:
 	free_netdev(netdev);
 
@@ -411,6 +586,7 @@ static void hinic3_nic_remove(struct auxiliary_device *adev)
 
 	netdev = nic_dev->netdev;
 	unregister_netdev(netdev);
+	hinic3_unregister_notifier();
 
 	disable_delayed_work_sync(&nic_dev->periodic_work);
 	cancel_work_sync(&nic_dev->rx_mode_work);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index 335de3093382..edc1d8805918 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -435,6 +435,11 @@ static int hinic3_open(struct net_device *netdev)
 	struct hinic3_dyna_qp_params qp_params;
 	int err;
 
+	if (test_bit(HINIC3_INTF_UP, &nic_dev->flags)) {
+		netdev_dbg(netdev, "Netdev already open, do nothing\n");
+		return 0;
+	}
+
 	err = hinic3_init_nicio_res(nic_dev);
 	if (err) {
 		netdev_err(netdev, "Failed to init nicio resources\n");
@@ -462,6 +467,8 @@ static int hinic3_open(struct net_device *netdev)
 	if (err)
 		goto err_close_channel;
 
+	set_bit(HINIC3_INTF_UP, &nic_dev->flags);
+
 	return 0;
 
 err_close_channel:
@@ -482,6 +489,11 @@ static int hinic3_close(struct net_device *netdev)
 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
 	struct hinic3_dyna_qp_params qp_params;
 
+	if (!test_and_clear_bit(HINIC3_INTF_UP, &nic_dev->flags)) {
+		netdev_dbg(netdev, "Netdev already close, do nothing\n");
+		return 0;
+	}
+
 	hinic3_vport_down(netdev);
 	hinic3_close_channel(netdev);
 	hinic3_uninit_qps(nic_dev, &qp_params);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
index 2c129de241eb..d7a299fb2d51 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -22,6 +22,7 @@ struct hinic3_nic_dev;
 enum hinic3_nic_event_type {
 	HINIC3_NIC_EVENT_LINK_DOWN = 0,
 	HINIC3_NIC_EVENT_LINK_UP   = 1,
+	HINIC3_NIC_EVENT_PORT_MODULE_EVENT = 2,
 };
 
 struct hinic3_sq_attr {
@@ -51,6 +52,23 @@ struct mag_cmd_set_port_enable {
 	u8                   rsvd1[3];
 };
 
+enum link_err_type {
+	LINK_ERR_MODULE_UNRECOGENIZED,
+	LINK_ERR_NUM,
+};
+
+enum port_module_event_type {
+	HINIC3_PORT_MODULE_CABLE_PLUGGED,
+	HINIC3_PORT_MODULE_CABLE_UNPLUGGED,
+	HINIC3_PORT_MODULE_LINK_ERR,
+	HINIC3_PORT_MODULE_MAX_EVENT,
+};
+
+struct hinic3_port_module_event {
+	enum port_module_event_type type;
+	enum link_err_type          err_type;
+};
+
 int hinic3_get_nic_feature_from_hw(struct hinic3_nic_dev *nic_dev);
 int hinic3_set_nic_feature_to_hw(struct hinic3_nic_dev *nic_dev);
 bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
index 985cbd91b7c8..1e8d41fc112c 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
@@ -17,6 +17,7 @@
 #define HINIC3_MODERATONE_DELAY  HZ
 
 enum hinic3_flags {
+	HINIC3_INTF_UP,
 	HINIC3_MAC_FILTER_CHANGED,
 	HINIC3_RSS_ENABLE,
 	HINIC3_UPDATE_MAC_FILTER,
@@ -119,6 +120,7 @@ struct hinic3_intr_coal_info {
 struct hinic3_nic_dev {
 	struct pci_dev                  *pdev;
 	struct net_device               *netdev;
+	netdevice_tracker               tracker;
 	struct hinic3_hwdev             *hwdev;
 	struct hinic3_nic_io            *nic_io;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [External] : [PATCH net-next v05 1/5] hinic3: Add PF framework
  2025-11-06 11:15 ` [PATCH net-next v05 1/5] hinic3: Add PF framework Fan Gong
@ 2025-11-06 14:19   ` ALOK TIWARI
  2025-11-07  8:26     ` Fan Gong
  0 siblings, 1 reply; 11+ messages in thread
From: ALOK TIWARI @ 2025-11-06 14:19 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi


> +
> +void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev)
> +{
> +	struct timespec64 ts = {};
> +	u64 time;
> +	int err;
> +
> +	ktime_get_real_ts64(&ts);
> +	time = (u64)(ts.tv_sec * MSEC_PER_SEC + ts.tv_nsec / NSEC_PER_MSEC);
> +
> +	err = hinic3_sync_time(hwdev, time);
> +	if (err)
> +		dev_err(hwdev->dev,
> +			"Synchronize UTC time to firmware failed, errno:%d.\n",

what about "failed, err=%d\n" ?

> +			err);
> +}
> +
>   static int get_hw_rx_buf_size_idx(int rx_buf_sz, u16 *buf_sz_idx)
>   {
>   	/* Supported RX buffer sizes in bytes. Configured by array index. */
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
> index 304f5691f0c2..c9c6b4fbcb12 100644
>   
>   	err = hinic3_mapping_bar(pdev, pci_adapter);
> @@ -331,8 +362,24 @@ static int hinic3_probe_func(struct hinic3_pcidev *pci_adapter)
>   	if (err)
>   		goto err_unmap_bar;
>   
> +	if (HINIC3_IS_PF(pci_adapter->hwdev)) {
> +		bdf_info.function_idx =
> +			hinic3_global_func_id(pci_adapter->hwdev);
> +		bdf_info.bus = pdev->bus->number;
> +		bdf_info.device = PCI_SLOT(pdev->devfn);
> +		bdf_info.function =  PCI_FUNC(pdev->devfn);
> +
> +		err = hinic3_set_bdf_ctxt(pci_adapter->hwdev, &bdf_info);
> +		if (err) {
> +			dev_err(&pdev->dev, "Failed to set BDF info to fw\n");
> +			goto err_uninit_func;
> +		}
> +	}
> +
>   	return 0;
>   
> +err_uninit_func:
> +	hinic3_func_uninit(pdev);
>   err_unmap_bar:
>   	hinic3_unmapping_bar(pci_adapter);
>   
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> index 6d87d4d895ba..a7c9c5bca53a 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> @@ -130,6 +130,7 @@ static int hinic3_sw_init(struct net_device *netdev)
>   {
>   	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
>   	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
> +	u8 mac_addr[ETH_ALEN];
>   	int err;
>   
>   	nic_dev->q_params.sq_depth = HINIC3_SQ_DEPTH;
> @@ -137,16 +138,29 @@ static int hinic3_sw_init(struct net_device *netdev)
>   
>   	hinic3_try_to_enable_rss(netdev);
>   
> -	/* VF driver always uses random MAC address. During VM migration to a
> -	 * new device, the new device should learn the VMs old MAC rather than
> -	 * provide its own MAC. The product design assumes that every VF is
> -	 * suspectable to migration so the device avoids offering MAC address
> -	 * to VFs.
> -	 */
> -	eth_hw_addr_random(netdev);
> +	if (HINIC3_IS_VF(hwdev)) {
> +		/* VF driver always uses random MAC address. During VM migration
> +		 * to a new device, the new device should learn the VMs old MAC
> +		 * rather than provide its own MAC. The product design assumes
> +		 * that every VF is suspectable to migration so the device

susceptible ?

> +		 * avoids offering MAC address to VFs.
> +		 */
> +		eth_hw_addr_random(netdev);
> +	} else {
> +		err = hinic3_get_default_mac(hwdev, mac_addr);
> +		if (err) {
> +			dev_err(hwdev->dev, "Failed to get MAC address\n");
> +			goto err_clear_rss_config;
> +		}
> +		eth_hw_addr_set(netdev, mac_addr);
> +	}
> +
>   	err = hinic3_set_mac(hwdev, netdev->dev_addr, 0,
>   			     hinic3_global_func_id(hwdev));
> -	if (err) {
> +	/* Failure to set MAC is not a fatal error for VF since its MAC may have
> +	 * already been set by PF
> +	 */
> +	if (err && err != -EADDRINUSE) {
>   		dev_err(hwdev->dev, "Failed to set default MAC\n");
>   		goto err_clear_rss_config;
>   	}
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
> index cf67e26acece..b4e151e88a13 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
> @@ -82,10 +82,27 @@ static struct hinic3_msg_desc *get_mbox_msg_desc(struct hinic3_mbox *mbox,
>   						 enum mbox_msg_direction_type dir,
>   						 u16 src_func_id)
>   {
> +	struct hinic3_hwdev *hwdev = mbox->hwdev;
>   	struct hinic3_msg_channel *msg_ch;
> -
> -	msg_ch = (src_func_id == MBOX_MGMT_FUNC_ID) ?
> -		&mbox->mgmt_msg : mbox->func_msg;
> +	u16 id;
> +
> +	if (src_func_id == MBOX_MGMT_FUNC_ID) {
> +		msg_ch = &mbox->mgmt_msg;
> +	} else if (HINIC3_IS_VF(hwdev)) {
> +		/* message from pf */
> +		msg_ch = mbox->func_msg;
> +		if (src_func_id != hinic3_pf_id_of_vf(hwdev) || !msg_ch)
> +			return NULL;
> +	} else if (src_func_id > hinic3_glb_pf_vf_offset(hwdev)) {
> +		/* message from vf */
> +		id = (src_func_id - 1) - hinic3_glb_pf_vf_offset(hwdev);
> +		if (id >= 1)
> +			return NULL;

hard coding id >= 1, is only one VF supported?

> +
> +		msg_ch = &mbox->func_msg[id];
> +	} else {
> +		return NULL;
> +	}
>   
>   	return (dir == MBOX_MSG_SEND) ?
>   		&msg_ch->recv_msg : &msg_ch->resp_msg;
> @@ -409,6 +426,13 @@ int hinic3_init_mbox(struct hinic3_hwdev *hwdev)
>   	if (err)
>   		goto err_destroy_workqueue;
>   
> +	if (HINIC3_IS_VF(hwdev)) {
> +		/* VF to PF mbox message channel */
> +		err = hinic3_init_func_mbox_msg_channel(hwdev);
> +		if (err)
> +			goto err_uninit_mgmt_msg_ch;
> +	}
> +
>   	err = hinic3_init_func_mbox_msg_channel(hwdev);

is hinic3_init_func* second init for PF and
VF executes both calls, is that correct?

>   	if (err)
>   		goto err_uninit_mgmt_msg_ch;
> @@ -424,8 +448,8 @@ int hinic3_init_mbox(struct hinic3_hwdev *hwdev)
>   	return 0;
>   
>   err_uninit_func_mbox_msg_ch:
> -	hinic3_uninit_func_mbox_msg_channel(hwdev);
> -
> +	if (HINIC3_IS_VF(hwdev))
> +		hinic3_uninit_func_mbox_msg_channel(hwdev);
>   err_uninit_mgmt_msg_ch:
>   	uninit_mgmt_msg_channel(mbox);
>   

Thanks,
Alok

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [External] : [PATCH net-next v05 2/5] hinic3: Add PF management interfaces
  2025-11-06 11:15 ` [PATCH net-next v05 2/5] hinic3: Add PF management interfaces Fan Gong
@ 2025-11-06 14:33   ` ALOK TIWARI
  0 siblings, 0 replies; 11+ messages in thread
From: ALOK TIWARI @ 2025-11-06 14:33 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi



On 11/6/2025 4:45 PM, Fan Gong wrote:
>   void hinic3_mgmt_msg_aeqe_handler(struct hinic3_hwdev *hwdev, u8 *header,
>   				  u8 size)
>   {
> +	struct hinic3_msg_pf_to_mgmt *pf_to_mgmt;
> +	struct hinic3_recv_msg *recv_msg;
> +	__le64 msg_header;
> +	bool is_send_dir;
> +
>   	if (MBOX_MSG_HEADER_GET(*(__force __le64 *)header, SOURCE) ==
> -				MBOX_MSG_FROM_MBOX)
> +	    MBOX_MSG_FROM_MBOX){

space before {

>   		hinic3_mbox_func_aeqe_handler(hwdev, header, size);
> +
> +		return;
> +	}
> +

Thanks,
Alok

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [External] : [PATCH net-next v05 3/5] hinic3: Add NIC configuration ops
  2025-11-06 11:15 ` [PATCH net-next v05 3/5] hinic3: Add NIC configuration ops Fan Gong
@ 2025-11-06 19:14   ` ALOK TIWARI
  0 siblings, 0 replies; 11+ messages in thread
From: ALOK TIWARI @ 2025-11-06 19:14 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi



On 11/6/2025 4:45 PM, Fan Gong wrote:
> Add ops to configure NIC feature(lro, vlan, csum...).
> Add queue work to collect NIC data.
> 
> Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
> Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
> Signed-off-by: Fan Gong <gongfan1@huawei.com>
> ---
>   .../ethernet/huawei/hinic3/hinic3_hw_comm.c   |  54 +++
>   .../ethernet/huawei/hinic3/hinic3_hw_comm.h   |   2 +
>   .../net/ethernet/huawei/hinic3/hinic3_hwdev.c |   2 +-
>   .../net/ethernet/huawei/hinic3/hinic3_hwdev.h |   9 +
>   .../net/ethernet/huawei/hinic3/hinic3_irq.c   | 136 +++++++-
>   .../net/ethernet/huawei/hinic3/hinic3_main.c  |  43 +++
>   .../huawei/hinic3/hinic3_mgmt_interface.h     |  50 +++
>   .../huawei/hinic3/hinic3_netdev_ops.c         | 318 ++++++++++++++++++
>   .../ethernet/huawei/hinic3/hinic3_nic_cfg.c   | 171 ++++++++++
>   .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |   7 +
>   .../ethernet/huawei/hinic3/hinic3_nic_dev.h   |  45 +++
>   .../net/ethernet/huawei/hinic3/hinic3_rx.h    |  20 ++
>   .../net/ethernet/huawei/hinic3/hinic3_tx.h    |  18 +
>   13 files changed, 873 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
> index 09dae2ef610c..554b3134b5a7 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
> @@ -9,6 +9,36 @@
>   #include "hinic3_hwif.h"
>   #include "hinic3_mbox.h"
>   
> +static int hinic3_get_interrupt_cfg(struct hinic3_hwdev *hwdev,
> +				    struct hinic3_interrupt_info *info)
> +{
> +	struct comm_cmd_cfg_msix_ctrl_reg msix_cfg = {};
> +	struct mgmt_msg_params msg_params = {};
> +	int err;
> +
> +	msix_cfg.func_id = hinic3_global_func_id(hwdev);
> +	msix_cfg.msix_index = info->msix_index;
> +	msix_cfg.opcode = MGMT_MSG_CMD_OP_GET;
> +
> +	mgmt_msg_params_init_default(&msg_params, &msix_cfg, sizeof(msix_cfg));
> +
> +	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
> +				       COMM_CMD_CFG_MSIX_CTRL_REG, &msg_params);
> +	if (err || msix_cfg.head.status) {
> +		dev_err(hwdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x\n",
> +			err, msix_cfg.head.status);
> +		return -EFAULT;
> +	}
> +
> +	info->lli_credit_limit = msix_cfg.lli_credit_cnt;
> +	info->lli_timer_cfg = msix_cfg.lli_timer_cnt;
> +	info->pending_limit = msix_cfg.pending_cnt;
> +	info->coalesc_timer_cfg = msix_cfg.coalesce_timer_cnt;
> +	info->resend_timer_cfg = msix_cfg.resend_timer_cnt;
> +
> +	return 0;
> +}
> +
>   int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
>   				    const struct hinic3_interrupt_info *info)
>   {
> @@ -40,6 +70,30 @@ int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
>   	return 0;
>   }
>   
> +int hinic3_set_interrupt_cfg(struct hinic3_hwdev *hwdev,
> +			     struct hinic3_interrupt_info info)
> +{
> +	struct hinic3_interrupt_info temp_info;
> +	int err;
> +
> +	temp_info.msix_index = info.msix_index;
> +
> +	err = hinic3_get_interrupt_cfg(hwdev, &temp_info);
> +	if (err)
> +		return err;
> +
> +	info.lli_credit_limit = temp_info.lli_credit_limit;
> +	info.lli_timer_cfg = temp_info.lli_timer_cfg;
> +
> +	if (!info.interrupt_coalesc_set) {
> +		info.pending_limit = temp_info.pending_limit;
> +		info.coalesc_timer_cfg = temp_info.coalesc_timer_cfg;
> +		info.resend_timer_cfg = temp_info.resend_timer_cfg;
> +	}
> +
> +	return hinic3_set_interrupt_cfg_direct(hwdev, &info);
> +}
> +
>   int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag)
>   {
>   	struct comm_cmd_func_reset func_reset = {};
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
> index c9c6b4fbcb12..8e4737c486b7 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
> @@ -23,6 +23,8 @@ struct hinic3_interrupt_info {
>   
>   int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
>   				    const struct hinic3_interrupt_info *info);
> +int hinic3_set_interrupt_cfg(struct hinic3_hwdev *hwdev,
> +			     struct hinic3_interrupt_info info);
>   int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag);
>   
>   int hinic3_get_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
> index 25e375b20174..4048b3302db7 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
> @@ -200,7 +200,7 @@ static int init_ceqs_msix_attr(struct hinic3_hwdev *hwdev)
>   	for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) {
>   		eq = &ceqs->ceq[q_id];
>   		info.msix_index = eq->msix_entry_idx;
> -		err = hinic3_set_interrupt_cfg_direct(hwdev, &info);
> +		err = hinic3_set_interrupt_cfg(hwdev, info);
>   		if (err) {
>   			dev_err(hwdev->dev, "Set msix attr for ceq %u failed\n",
>   				q_id);
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
> index 3c15f22973fe..58bc561f95b3 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
> @@ -17,6 +17,15 @@ enum hinic3_event_service_type {
>   	HINIC3_EVENT_SRV_NIC  = 1
>   };
>   
> +enum hinic3_fault_err_level {
> +	HINIC3_FAULT_LEVEL_SERIOUS_FLR = 3,
> +};
> +
> +enum hinic3_fault_source_type {
> +	HINIC3_FAULT_SRC_HW_PHY_FAULT = 9,
> +	HINIC3_FAULT_SRC_TX_TIMEOUT   = 22,
> +};
> +
>   #define HINIC3_SRV_EVENT_TYPE(svc, type)    (((svc) << 16) | (type))
>   
>   /* driver-specific data of pci_dev */
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
> index a69b361225e9..cb9412986c26 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
> @@ -10,6 +10,9 @@
>   #include "hinic3_rx.h"
>   #include "hinic3_tx.h"
>   
> +#define HINIC3_RX_RATE_THRESH  50000
> +#define HINIC3_AVG_PKT_SMALL   256U
> +
>   static int hinic3_poll(struct napi_struct *napi, int budget)
>   {
>   	struct hinic3_irq_cfg *irq_cfg =
> @@ -92,7 +95,7 @@ static int hinic3_request_irq(struct hinic3_irq_cfg *irq_cfg, u16 q_id)
>   	info.coalesc_timer_cfg =
>   		nic_dev->intr_coalesce[q_id].coalesce_timer_cfg;
>   	info.resend_timer_cfg = nic_dev->intr_coalesce[q_id].resend_timer_cfg;
> -	err = hinic3_set_interrupt_cfg_direct(nic_dev->hwdev, &info);
> +	err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info);
>   	if (err) {
>   		netdev_err(netdev, "Failed to set RX interrupt coalescing attribute.\n");
>   		qp_del_napi(irq_cfg);
> @@ -117,6 +120,134 @@ static void hinic3_release_irq(struct hinic3_irq_cfg *irq_cfg)
>   	free_irq(irq_cfg->irq_id, irq_cfg);
>   }
>   
> +static int hinic3_set_interrupt_moder(struct net_device *netdev, u16 q_id,
> +				      u8 coalesc_timer_cfg, u8 pending_limit)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_interrupt_info info = {};
> +	int err;
> +
> +	if (coalesc_timer_cfg == nic_dev->rxqs[q_id].last_coalesc_timer_cfg &&
> +	    pending_limit == nic_dev->rxqs[q_id].last_pending_limit)
> +		return 0;
> +
> +	if (q_id >= nic_dev->q_params.num_qps)
> +		return 0;
> +
> +	info.interrupt_coalesc_set = 1;
> +	info.coalesc_timer_cfg = coalesc_timer_cfg;
> +	info.pending_limit = pending_limit;
> +	info.msix_index = nic_dev->q_params.irq_cfg[q_id].msix_entry_idx;
> +	info.resend_timer_cfg =
> +		nic_dev->intr_coalesce[q_id].resend_timer_cfg;
> +
> +	err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info);
> +	if (err) {
> +		netdev_err(netdev,
> +			   "Failed to modify moderation for Queue: %u\n", q_id);
> +	} else {
> +		nic_dev->rxqs[q_id].last_coalesc_timer_cfg = coalesc_timer_cfg;
> +		nic_dev->rxqs[q_id].last_pending_limit = pending_limit;
> +	}
> +
> +	return err;
> +}
> +
> +static void hinic3_calc_coal_para(struct hinic3_intr_coal_info *q_coal,
> +				  u64 rx_rate, u8 *coalesc_timer_cfg,
> +				  u8 *pending_limit)
> +{
> +	if (rx_rate < q_coal->pkt_rate_low) {
> +		*coalesc_timer_cfg = q_coal->rx_usecs_low;
> +		*pending_limit = q_coal->rx_pending_limit_low;
> +	} else if (rx_rate > q_coal->pkt_rate_high) {
> +		*coalesc_timer_cfg = q_coal->rx_usecs_high;
> +		*pending_limit = q_coal->rx_pending_limit_high;
> +	} else {
> +		*coalesc_timer_cfg =
> +			(u8)((rx_rate - q_coal->pkt_rate_low) *
> +			     (q_coal->rx_usecs_high - q_coal->rx_usecs_low) /
> +			     (q_coal->pkt_rate_high - q_coal->pkt_rate_low) +
> +			     q_coal->rx_usecs_low);
> +
> +		*pending_limit =
> +			(u8)((rx_rate - q_coal->pkt_rate_low) *
> +			     (q_coal->rx_pending_limit_high -
> +			      q_coal->rx_pending_limit_low) /
> +			     (q_coal->pkt_rate_high - q_coal->pkt_rate_low) +
> +			     q_coal->rx_pending_limit_low);
> +	}
> +}
> +
> +static void hinic3_update_queue_coal(struct net_device *netdev, u16 qid,
> +				     u64 rx_rate, u64 avg_pkt_size, u64 tx_rate)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_intr_coal_info *q_coal;
> +	u8 coalesc_timer_cfg, pending_limit;
> +
> +	q_coal = &nic_dev->intr_coalesce[qid];
> +
> +	if (rx_rate > HINIC3_RX_RATE_THRESH &&
> +	    avg_pkt_size > HINIC3_AVG_PKT_SMALL) {
> +		hinic3_calc_coal_para(q_coal, rx_rate, &coalesc_timer_cfg,
> +				      &pending_limit);
> +	} else {
> +		coalesc_timer_cfg = 3;
> +		pending_limit = q_coal->rx_pending_limit_low;
> +	}
> +
> +	hinic3_set_interrupt_moder(netdev, qid,
> +				   coalesc_timer_cfg, pending_limit);
> +}
> +
> +static void hinic3_auto_moderation_work(struct work_struct *work)
> +{
> +	u64 rx_packets, rx_bytes, rx_pkt_diff, rx_rate, avg_pkt_size;
> +	u64 tx_packets, tx_bytes, tx_pkt_diff, tx_rate;
> +	struct hinic3_nic_dev *nic_dev;
> +	struct delayed_work *delay;
> +	struct net_device *netdev;
> +	unsigned long period;
> +	u16 qid;
> +
> +	delay = to_delayed_work(work);
> +	nic_dev = container_of(delay, struct hinic3_nic_dev, moderation_task);
> +	period = (unsigned long)(jiffies - nic_dev->last_moder_jiffies);
> +	netdev = nic_dev->netdev;
> +
> +	queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task,
> +			   HINIC3_MODERATONE_DELAY);
> +
> +	for (qid = 0; qid < nic_dev->q_params.num_qps; qid++) {
> +		rx_packets = nic_dev->rxqs[qid].rxq_stats.packets;
> +		rx_bytes = nic_dev->rxqs[qid].rxq_stats.bytes;
> +		tx_packets = nic_dev->txqs[qid].txq_stats.packets;
> +		tx_bytes = nic_dev->txqs[qid].txq_stats.bytes;
> +
> +		rx_pkt_diff =
> +			rx_packets - nic_dev->rxqs[qid].last_moder_packets;
> +		avg_pkt_size = rx_pkt_diff ?
> +			((unsigned long)(rx_bytes -
> +			 nic_dev->rxqs[qid].last_moder_bytes)) /
> +			 rx_pkt_diff : 0;
> +
> +		rx_rate = rx_pkt_diff * HZ / period;
> +		tx_pkt_diff =
> +			tx_packets - nic_dev->txqs[qid].last_moder_packets;
> +		tx_rate = tx_pkt_diff * HZ / period;
> +
> +		hinic3_update_queue_coal(netdev, qid, rx_rate, avg_pkt_size,
> +					 tx_rate);
> +		nic_dev->rxqs[qid].last_moder_packets = rx_packets;
> +		nic_dev->rxqs[qid].last_moder_bytes = rx_bytes;
> +		nic_dev->txqs[qid].last_moder_packets = tx_packets;
> +		nic_dev->txqs[qid].last_moder_bytes = tx_bytes;
> +	}
> +
> +	nic_dev->last_moder_jiffies = jiffies;
> +}
> +
>   int hinic3_qps_irq_init(struct net_device *netdev)
>   {
>   	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> @@ -157,6 +288,9 @@ int hinic3_qps_irq_init(struct net_device *netdev)
>   				      HINIC3_MSIX_ENABLE);
>   	}
>   
> +	INIT_DELAYED_WORK(&nic_dev->moderation_task,
> +			  hinic3_auto_moderation_work);
> +
>   	return 0;
>   
>   err_release_irqs:
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> index e12102806791..4a47dac1c4b4 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> @@ -108,6 +108,22 @@ static void hinic3_free_txrxqs(struct net_device *netdev)
>   	hinic3_free_txqs(netdev);
>   }
>   
> +static void hinic3_periodic_work_handler(struct work_struct *work)
> +{
> +	struct delayed_work *delay = to_delayed_work(work);
> +	struct hinic3_nic_dev *nic_dev;
> +
> +	nic_dev = container_of(delay, struct hinic3_nic_dev, periodic_work);
> +	if (test_and_clear_bit(HINIC3_EVENT_WORK_TX_TIMEOUT,
> +			       &nic_dev->event_flag))
> +		dev_info(nic_dev->hwdev->dev,
> +			 "Fault event report, src: %u, level: %u\n",
> +			 HINIC3_FAULT_SRC_TX_TIMEOUT,
> +			 HINIC3_FAULT_LEVEL_SERIOUS_FLR);
> +
> +	queue_delayed_work(nic_dev->workq, &nic_dev->periodic_work, HZ);
> +}
> +
>   static int hinic3_init_nic_dev(struct net_device *netdev,
>   			       struct hinic3_hwdev *hwdev)
>   {
> @@ -121,8 +137,23 @@ static int hinic3_init_nic_dev(struct net_device *netdev,
>   
>   	nic_dev->rx_buf_len = HINIC3_RX_BUF_LEN;
>   	nic_dev->lro_replenish_thld = HINIC3_LRO_REPLENISH_THLD;
> +	nic_dev->vlan_bitmap = kzalloc(HINIC3_VLAN_BITMAP_SIZE(nic_dev),
> +				       GFP_KERNEL);
> +	if (!nic_dev->vlan_bitmap)
> +		return -ENOMEM;
> +
>   	nic_dev->nic_svc_cap = hwdev->cfg_mgmt->cap.nic_svc_cap;
>   
> +	nic_dev->workq = create_singlethread_workqueue(HINIC3_NIC_DEV_WQ_NAME);
> +	if (!nic_dev->workq) {
> +		dev_err(hwdev->dev, "Failed to initialize nic workqueue\n");
> +		kfree(nic_dev->vlan_bitmap);
> +		return -ENOMEM;
> +	}
> +
> +	INIT_DELAYED_WORK(&nic_dev->periodic_work,
> +			  hinic3_periodic_work_handler);
> +
>   	return 0;
>   }
>   
> @@ -229,6 +260,13 @@ static int hinic3_set_default_hw_feature(struct net_device *netdev)
>   		return err;
>   	}
>   
> +	err = hinic3_set_hw_features(netdev);
> +	if (err) {
> +		hinic3_update_nic_feature(nic_dev, 0);
> +		hinic3_set_nic_feature_to_hw(nic_dev);
> +		return err;
> +	}
> +
>   	return 0;
>   }
>   
> @@ -329,6 +367,7 @@ static int hinic3_nic_probe(struct auxiliary_device *adev,
>   	if (err)
>   		goto err_uninit_sw;
>   
> +	queue_delayed_work(nic_dev->workq, &nic_dev->periodic_work, HZ);
>   	netif_carrier_off(netdev);
>   
>   	err = register_netdev(netdev);
> @@ -368,12 +407,16 @@ static void hinic3_nic_remove(struct auxiliary_device *adev)
>   	netdev = nic_dev->netdev;
>   	unregister_netdev(netdev);
>   
> +	disable_delayed_work_sync(&nic_dev->periodic_work);
> +	destroy_workqueue(nic_dev->workq);
> +
>   	hinic3_update_nic_feature(nic_dev, 0);
>   	hinic3_set_nic_feature_to_hw(nic_dev);
>   	hinic3_sw_uninit(netdev);
>   
>   	hinic3_free_nic_io(nic_dev);
>   
> +	kfree(nic_dev->vlan_bitmap);
>   	free_netdev(netdev);
>   }
>   
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
> index 3a6d3ee534d0..68dfdfa1b1ba 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
> @@ -56,6 +56,31 @@ struct l2nic_cmd_update_mac {
>   	u8                   new_mac[ETH_ALEN];
>   };
>   
> +struct l2nic_cmd_vlan_config {
> +	struct mgmt_msg_head msg_head;
> +	u16                  func_id;
> +	u8                   opcode;
> +	u8                   rsvd1;
> +	u16                  vlan_id;
> +	u16                  rsvd2;
> +};
> +
> +struct l2nic_cmd_vlan_offload {
> +	struct mgmt_msg_head msg_head;
> +	u16                  func_id;
> +	u8                   vlan_offload;
> +	u8                   vd1[5];

Probably rsvd1[5];

> +};
> +
> +/* set vlan filter */
> +struct l2nic_cmd_set_vlan_filter {
> +	struct mgmt_msg_head msg_head;
> +	u16                  func_id;
> +	u8                   resvd[2];

what about rsvd[2]; ?

> +	/* bit0:vlan filter en; bit1:broadcast_filter_en */
> +	u32                  vlan_filter_ctrl;
> +};
> +
>   struct l2nic_cmd_set_ci_attr {
>   	struct mgmt_msg_head msg_head;
>   	u16                  func_idx;
> @@ -102,6 +127,26 @@ struct l2nic_cmd_set_dcb_state {
>   	u8                   rsvd[7];
>   };
>   
> +struct l2nic_cmd_lro_config {
> +	struct mgmt_msg_head msg_head;
> +	u16                  func_id;
> +	u8                   opcode;
> +	u8                   rsvd1;
> +	u8                   lro_ipv4_en;
> +	u8                   lro_ipv6_en;
> +	/* unit is 1K */
> +	u8                   lro_max_pkt_len;
> +	u8                   resv2[13];
> +};
> +
> +struct l2nic_cmd_lro_timer {
> +	struct mgmt_msg_head msg_head;
> +	/* 1: set timer value, 0: get timer value */
> +	u8                   opcode;
> +	u8                   rsvd[3];
> +	u32                  timer;
> +};
> +
>   #define L2NIC_RSS_TYPE_VALID_MASK         BIT(23)
>   #define L2NIC_RSS_TYPE_TCP_IPV6_EXT_MASK  BIT(24)
>   #define L2NIC_RSS_TYPE_IPV6_EXT_MASK      BIT(25)
> @@ -162,11 +207,16 @@ enum l2nic_cmd {
>   	L2NIC_CMD_SET_VPORT_ENABLE    = 6,
>   	L2NIC_CMD_SET_SQ_CI_ATTR      = 8,
>   	L2NIC_CMD_CLEAR_QP_RESOURCE   = 11,
> +	L2NIC_CMD_CFG_RX_LRO          = 13,
> +	L2NIC_CMD_CFG_LRO_TIMER       = 14,
>   	L2NIC_CMD_FEATURE_NEGO        = 15,
>   	L2NIC_CMD_GET_MAC             = 20,
>   	L2NIC_CMD_SET_MAC             = 21,
>   	L2NIC_CMD_DEL_MAC             = 22,
>   	L2NIC_CMD_UPDATE_MAC          = 23,
> +	L2NIC_CMD_CFG_FUNC_VLAN       = 25,
> +	L2NIC_CMD_SET_VLAN_FILTER_EN  = 26,
> +	L2NIC_CMD_SET_RX_VLAN_OFFLOAD = 27,
>   	L2NIC_CMD_CFG_RSS             = 60,
>   	L2NIC_CMD_CFG_RSS_HASH_KEY    = 63,
>   	L2NIC_CMD_CFG_RSS_HASH_ENGINE = 64,
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> index bf199f4ce847..ad50128f3d76 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> @@ -12,6 +12,15 @@
>   #include "hinic3_rx.h"
>   #include "hinic3_tx.h"
>   
> +#define HINIC3_LRO_DEFAULT_COAL_PKT_SIZE  32
> +#define HINIC3_LRO_DEFAULT_TIME_LIMIT     16
> +
> +#define VLAN_BITMAP_BITS_SIZE(nic_dev)    (sizeof(*(nic_dev)->vlan_bitmap) * 8)
> +#define VID_LINE(nic_dev, vid)  \
> +	((vid) / VLAN_BITMAP_BITS_SIZE(nic_dev))
> +#define VID_COL(nic_dev, vid)  \
> +	((vid) & (VLAN_BITMAP_BITS_SIZE(nic_dev) - 1))
> +
>   /* try to modify the number of irq to the target number,
>    * and return the actual number of irq.
>    */
> @@ -384,6 +393,9 @@ static int hinic3_vport_up(struct net_device *netdev)
>   	if (!err && link_status_up)
>   		netif_carrier_on(netdev);
>   
> +	queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task,
> +			   HINIC3_MODERATONE_DELAY);
> +
>   	hinic3_print_link_message(netdev, link_status_up);
>   
>   	return 0;
> @@ -406,6 +418,8 @@ static void hinic3_vport_down(struct net_device *netdev)
>   	netif_carrier_off(netdev);
>   	netif_tx_disable(netdev);
>   
> +	disable_delayed_work_sync(&nic_dev->moderation_task);
> +
>   	glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
>   	hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
>   
> @@ -476,6 +490,162 @@ static int hinic3_close(struct net_device *netdev)
>   	return 0;
>   }
>   
> +#define SET_FEATURES_OP_STR(op)  ((op) ? "Enable" : "Disable")
> +
> +static int hinic3_set_feature_rx_csum(struct net_device *netdev,
> +				      netdev_features_t wanted_features,
> +				      netdev_features_t features,
> +				      netdev_features_t *failed_features)
> +{
> +	netdev_features_t changed = wanted_features ^ features;
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
> +
> +	if (changed & NETIF_F_RXCSUM)
> +		dev_dbg(hwdev->dev, "%s rx csum success\n",
> +			SET_FEATURES_OP_STR(wanted_features & NETIF_F_RXCSUM));
> +
> +	return 0;
> +}
> +
> +static int hinic3_set_feature_tso(struct net_device *netdev,
> +				  netdev_features_t wanted_features,
> +				  netdev_features_t features,
> +				  netdev_features_t *failed_features)
> +{
> +	netdev_features_t changed = wanted_features ^ features;
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
> +
> +	if (changed & NETIF_F_TSO)
> +		dev_dbg(hwdev->dev, "%s tso success\n",
> +			SET_FEATURES_OP_STR(wanted_features & NETIF_F_TSO));
> +
> +	return 0;
> +}
> +
> +static int hinic3_set_feature_lro(struct net_device *netdev,
> +				  netdev_features_t wanted_features,
> +				  netdev_features_t features,
> +				  netdev_features_t *failed_features)
> +{
> +	netdev_features_t changed = wanted_features ^ features;
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
> +	bool en = !!(wanted_features & NETIF_F_LRO);
> +	int err;
> +
> +	if (!(changed & NETIF_F_LRO))
> +		return 0;
> +
> +	err = hinic3_set_rx_lro_state(hwdev, en,
> +				      HINIC3_LRO_DEFAULT_TIME_LIMIT,
> +				      HINIC3_LRO_DEFAULT_COAL_PKT_SIZE);
> +	if (err) {
> +		dev_err(hwdev->dev, "%s lro failed\n", SET_FEATURES_OP_STR(en));
> +		*failed_features |= NETIF_F_LRO;
> +	}
> +
> +	return err;
> +}
> +
> +static int hinic3_set_feature_rx_cvlan(struct net_device *netdev,
> +				       netdev_features_t wanted_features,
> +				       netdev_features_t features,
> +				       netdev_features_t *failed_features)
> +{
> +	bool en = !!(wanted_features & NETIF_F_HW_VLAN_CTAG_RX);
> +	netdev_features_t changed = wanted_features ^ features;
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
> +	int err;
> +
> +	if (!(changed & NETIF_F_HW_VLAN_CTAG_RX))
> +		return 0;
> +
> +	err = hinic3_set_rx_vlan_offload(hwdev, en);
> +	if (err) {
> +		dev_err(hwdev->dev, "%s rxvlan failed\n",

"%s rx vlan offload failed\n" similar to below one

> +			SET_FEATURES_OP_STR(en));
> +		*failed_features |= NETIF_F_HW_VLAN_CTAG_RX;
> +	}
> +
> +	return err;
> +}
> +
> +static int hinic3_set_feature_vlan_filter(struct net_device *netdev,
> +					  netdev_features_t wanted_features,
> +					  netdev_features_t features,
> +					  netdev_features_t *failed_features)
> +{
> +	bool en = !!(wanted_features & NETIF_F_HW_VLAN_CTAG_FILTER);
> +	netdev_features_t changed = wanted_features ^ features;
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_hwdev *hwdev = nic_dev->hwdev;
> +	int err;
> +
> +	if (!(changed & NETIF_F_HW_VLAN_CTAG_FILTER))
> +		return 0;
> +
> +	err = hinic3_set_vlan_fliter(hwdev, en);
> +	if (err) {
> +		dev_err(hwdev->dev, "%s rx vlan filter failed\n",
> +			SET_FEATURES_OP_STR(en));
> +		*failed_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
> +	}
> +
> +	return err;
> +}
> +
> +static int hinic3_set_features(struct net_device *netdev,
> +			       netdev_features_t curr,
> +			       netdev_features_t wanted)
> +{
> +	netdev_features_t failed = 0;
> +	int err;
> +
> +	err = hinic3_set_feature_rx_csum(netdev, wanted, curr, &failed) |
> +	      hinic3_set_feature_tso(netdev, wanted, curr, &failed) |
> +	      hinic3_set_feature_lro(netdev, wanted, curr, &failed) |
> +	      hinic3_set_feature_rx_cvlan(netdev, wanted, curr, &failed) |
> +	      hinic3_set_feature_vlan_filter(netdev, wanted, curr, &failed);
> +	if (err) {
> +		netdev->features = wanted ^ failed;
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hinic3_ndo_set_features(struct net_device *netdev,
> +				   netdev_features_t features)
> +{
> +	return hinic3_set_features(netdev, netdev->features, features);
> +}
> +
> +static netdev_features_t hinic3_fix_features(struct net_device *netdev,
> +					     netdev_features_t features)
> +{
> +	netdev_features_t features_tmp = features;
> +
> +	/* If Rx checksum is disabled, then LRO should also be disabled */
> +	if (!(features_tmp & NETIF_F_RXCSUM))
> +		features_tmp &= ~NETIF_F_LRO;
> +
> +	return features_tmp;
> +}
> +
> +int hinic3_set_hw_features(struct net_device *netdev)
> +{
> +	netdev_features_t wanted, curr;
> +
> +	wanted = netdev->features;
> +	/* fake current features so all wanted are enabled */
> +	curr = ~wanted;
> +
> +	return hinic3_set_features(netdev, curr, wanted);
> +}
> +
>   static int hinic3_change_mtu(struct net_device *netdev, int new_mtu)
>   {
>   	int err;
> @@ -517,11 +687,159 @@ static int hinic3_set_mac_addr(struct net_device *netdev, void *addr)
>   	return 0;
>   }
>   
> +static int hinic3_vlan_rx_add_vid(struct net_device *netdev,
> +				  __be16 proto, u16 vid)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	unsigned long *vlan_bitmap = nic_dev->vlan_bitmap;
> +	u32 column, row;
> +	u16 func_id;
> +	int err;
> +
> +	column = VID_COL(nic_dev, vid);
> +	row = VID_LINE(nic_dev, vid);
> +
> +	func_id = hinic3_global_func_id(nic_dev->hwdev);
> +
> +	err = hinic3_add_vlan(nic_dev->hwdev, vid, func_id);
> +	if (err) {
> +		netdev_err(netdev, "Failed to add vlan %u\n", vid);
> +		goto out;
> +	}
> +
> +	set_bit(column, &vlan_bitmap[row]);
> +	netdev_dbg(netdev, "Add vlan %u\n", vid);
> +
> +out:
> +	return err;
> +}
> +
> +static int hinic3_vlan_rx_kill_vid(struct net_device *netdev,
> +				   __be16 proto, u16 vid)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	unsigned long *vlan_bitmap = nic_dev->vlan_bitmap;
> +	u32 column, row;
> +	u16 func_id;
> +	int err;
> +
> +	column  = VID_COL(nic_dev, vid);
> +	row = VID_LINE(nic_dev, vid);
> +
> +	func_id = hinic3_global_func_id(nic_dev->hwdev);
> +	err = hinic3_del_vlan(nic_dev->hwdev, vid, func_id);
> +	if (err) {
> +		netdev_err(netdev, "Failed to delete vlan\n");

missing %u\n", vid) similar to add vlan

> +		goto out;
> +	}
> +
> +	clear_bit(column, &vlan_bitmap[row]);
> +	netdev_dbg(netdev, "Remove vlan %u\n", vid);
> +
> +out:
> +	return err;
> +}
> +
> +static void hinic3_tx_timeout(struct net_device *netdev, unsigned int txqueue)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_io_queue *sq;
> +	bool hw_err = false;
> +	u16 sw_pi, hw_ci;
> +	u8 q_id;
> +
> +	HINIC3_NIC_STATS_INC(nic_dev, netdev_tx_timeout);
> +	netdev_err(netdev, "Tx timeout\n");
> +
> +	for (q_id = 0; q_id < nic_dev->q_params.num_qps; q_id++) {
> +		if (!netif_xmit_stopped(netdev_get_tx_queue(netdev, q_id)))
> +			continue;
> +
> +		sq = nic_dev->txqs[q_id].sq;
> +		sw_pi = hinic3_get_sq_local_pi(sq);
> +		hw_ci = hinic3_get_sq_hw_ci(sq);
> +		netdev_dbg(netdev,
> +			   "txq%u: sw_pi: %u, hw_ci: %u, sw_ci: %u, napi->state: 0x%lx.\n",
> +			   q_id, sw_pi, hw_ci, hinic3_get_sq_local_ci(sq),
> +			   nic_dev->q_params.irq_cfg[q_id].napi.state);
> +
> +		if (sw_pi != hw_ci)
> +			hw_err = true;
> +	}
> +
> +	if (hw_err)
> +		set_bit(HINIC3_EVENT_WORK_TX_TIMEOUT, &nic_dev->event_flag);
> +}
> +
> +static void hinic3_get_stats64(struct net_device *netdev,
> +			       struct rtnl_link_stats64 *stats)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	u64 bytes, packets, dropped, errors;
> +	struct hinic3_txq_stats *txq_stats;
> +	struct hinic3_rxq_stats *rxq_stats;
> +	struct hinic3_txq *txq;
> +	struct hinic3_rxq *rxq;
> +	unsigned int start;
> +	int i;
> +
> +	bytes = 0;
> +	packets = 0;
> +	dropped = 0;
> +	for (i = 0; i < nic_dev->max_qps; i++) {
> +		if (!nic_dev->txqs)
> +			break;
> +
> +		txq = &nic_dev->txqs[i];
> +		txq_stats = &txq->txq_stats;
> +		do {
> +			start = u64_stats_fetch_begin(&txq_stats->syncp);
> +			bytes += txq_stats->bytes;
> +			packets += txq_stats->packets;
> +			dropped += txq_stats->dropped;
> +		} while (u64_stats_fetch_retry(&txq_stats->syncp, start));
> +	}
> +	stats->tx_packets = packets;
> +	stats->tx_bytes   = bytes;
> +	stats->tx_dropped = dropped;
> +
> +	bytes = 0;
> +	packets = 0;
> +	errors = 0;
> +	dropped = 0;
> +	for (i = 0; i < nic_dev->max_qps; i++) {
> +		if (!nic_dev->rxqs)
> +			break;
> +
> +		rxq = &nic_dev->rxqs[i];
> +		rxq_stats = &rxq->rxq_stats;
> +		do {
> +			start = u64_stats_fetch_begin(&rxq_stats->syncp);
> +			bytes += rxq_stats->bytes;
> +			packets += rxq_stats->packets;
> +			errors += rxq_stats->csum_errors +
> +				rxq_stats->other_errors;
> +			dropped += rxq_stats->dropped;
> +		} while (u64_stats_fetch_retry(&rxq_stats->syncp, start));
> +	}
> +	stats->rx_packets = packets;
> +	stats->rx_bytes   = bytes;
> +	stats->rx_errors  = errors;
> +	stats->rx_dropped = dropped;
> +}
> +
>   static const struct net_device_ops hinic3_netdev_ops = {
>   	.ndo_open             = hinic3_open,
>   	.ndo_stop             = hinic3_close,
> +	.ndo_set_features     = hinic3_ndo_set_features,
> +	.ndo_fix_features     = hinic3_fix_features,
>   	.ndo_change_mtu       = hinic3_change_mtu,
>   	.ndo_set_mac_address  = hinic3_set_mac_addr,
> +	.ndo_validate_addr    = eth_validate_addr,
> +	.ndo_vlan_rx_add_vid  = hinic3_vlan_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid = hinic3_vlan_rx_kill_vid,
> +	.ndo_tx_timeout       = hinic3_tx_timeout,
> +	.ndo_get_stats64      = hinic3_get_stats64,
>   	.ndo_start_xmit       = hinic3_xmit_frame,
>   };
>   
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
> index 7fec13bbe60e..72e09402841a 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
> @@ -10,6 +10,9 @@
>   #include "hinic3_nic_dev.h"
>   #include "hinic3_nic_io.h"
>   
> +#define MGMT_MSG_CMD_OP_ADD  1
> +#define MGMT_MSG_CMD_OP_DEL  0
> +
>   static int hinic3_feature_nego(struct hinic3_hwdev *hwdev, u8 opcode,
>   			       u64 *s_feature, u16 size)
>   {
> @@ -57,6 +60,136 @@ bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
>   	return (nic_dev->nic_io->feature_cap & feature_bits) == feature_bits;
>   }
>   
> +static int hinic3_set_rx_lro(struct hinic3_hwdev *hwdev, u8 ipv4_en, u8 ipv6_en,
> +			     u8 lro_max_pkt_len)
> +{
> +	struct l2nic_cmd_lro_config lro_cfg = {};
> +	struct mgmt_msg_params msg_params = {};
> +	int err;
> +
> +	lro_cfg.func_id = hinic3_global_func_id(hwdev);
> +	lro_cfg.opcode = MGMT_MSG_CMD_OP_SET;
> +	lro_cfg.lro_ipv4_en = ipv4_en;
> +	lro_cfg.lro_ipv6_en = ipv6_en;
> +	lro_cfg.lro_max_pkt_len = lro_max_pkt_len;
> +
> +	mgmt_msg_params_init_default(&msg_params, &lro_cfg,
> +				     sizeof(lro_cfg));
> +
> +	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
> +				       L2NIC_CMD_CFG_RX_LRO,
> +				       &msg_params);
> +
> +	if (err || lro_cfg.msg_head.status) {
> +		dev_err(hwdev->dev, "Failed to set lro offload, err: %d, status: 0x%x\n",
> +			err, lro_cfg.msg_head.status);
> +		return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hinic3_set_rx_lro_timer(struct hinic3_hwdev *hwdev, u32 timer_value)
> +{
> +	struct l2nic_cmd_lro_timer lro_timer = {};
> +	struct mgmt_msg_params msg_params = {};
> +	int err;
> +
> +	lro_timer.opcode = MGMT_MSG_CMD_OP_SET;
> +	lro_timer.timer = timer_value;
> +
> +	mgmt_msg_params_init_default(&msg_params, &lro_timer,
> +				     sizeof(lro_timer));
> +
> +	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
> +				       L2NIC_CMD_CFG_LRO_TIMER,
> +				       &msg_params);
> +
> +	if (err || lro_timer.msg_head.status) {
> +		dev_err(hwdev->dev, "Failed to set lro timer, err: %d, status: 0x%x\n",
> +			err, lro_timer.msg_head.status);
> +
> +		return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +
> +int hinic3_set_rx_lro_state(struct hinic3_hwdev *hwdev, u8 lro_en,
> +			    u32 lro_timer, u8 lro_max_pkt_len)
> +{
> +	u8 ipv4_en, ipv6_en;
> +	int err;
> +
> +	ipv4_en = lro_en ? 1 : 0;
> +	ipv6_en = lro_en ? 1 : 0;
> +
> +	dev_dbg(hwdev->dev, "Set LRO max coalesce packet size to %uK\n",
> +		lro_max_pkt_len);
> +
> +	err = hinic3_set_rx_lro(hwdev, ipv4_en, ipv6_en, lro_max_pkt_len);
> +	if (err)
> +		return err;
> +
> +	/* we don't set LRO timer for VF */
> +	if (HINIC3_IS_VF(hwdev))
> +		return 0;
> +
> +	dev_dbg(hwdev->dev, "Set LRO timer to %u\n", lro_timer);
> +
> +	return hinic3_set_rx_lro_timer(hwdev, lro_timer);
> +}
> +
> +int hinic3_set_rx_vlan_offload(struct hinic3_hwdev *hwdev, u8 en)
> +{
> +	struct l2nic_cmd_vlan_offload vlan_cfg = {};
> +	struct mgmt_msg_params msg_params = {};
> +	int err;
> +
> +	vlan_cfg.func_id = hinic3_global_func_id(hwdev);
> +	vlan_cfg.vlan_offload = en;
> +
> +	mgmt_msg_params_init_default(&msg_params, &vlan_cfg,
> +				     sizeof(vlan_cfg));
> +
> +	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
> +				       L2NIC_CMD_SET_RX_VLAN_OFFLOAD,
> +				       &msg_params);
> +
> +	if (err || vlan_cfg.msg_head.status) {
> +		dev_err(hwdev->dev, "Failed to set rx vlan offload, err: %d, status: 0x%x\n",
> +			err, vlan_cfg.msg_head.status);
> +		return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +
> +int hinic3_set_vlan_fliter(struct hinic3_hwdev *hwdev, u32 vlan_filter_ctrl)

typo fliter -> filter

> +{
> +	struct l2nic_cmd_set_vlan_filter vlan_filter;
> +	struct mgmt_msg_params msg_params = {};
> +	int err;
> +
> +	vlan_filter.func_id = hinic3_global_func_id(hwdev);
> +	vlan_filter.vlan_filter_ctrl = vlan_filter_ctrl;
> +
> +	mgmt_msg_params_init_default(&msg_params, &vlan_filter,
> +				     sizeof(vlan_filter));
> +
> +	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
> +				       L2NIC_CMD_SET_VLAN_FILTER_EN,
> +				       &msg_params);
> +
> +	if (err || vlan_filter.msg_head.status) {
> +		dev_err(hwdev->dev, "Failed to set vlan filter, err: %d, status: 0x%x\n",
> +			err, vlan_filter.msg_head.status);
> +		return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +
>   void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap)
>   {
>   	nic_dev->nic_io->feature_cap = feature_cap;
> @@ -366,6 +499,44 @@ int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev)
>   	return pkt_drop.msg_head.status;
>   }
>   
> +static int hinic3_config_vlan(struct hinic3_hwdev *hwdev,
> +			      u8 opcode, u16 vlan_id, u16 func_id)
> +{
> +	struct l2nic_cmd_vlan_config vlan_info = {};
> +	struct mgmt_msg_params msg_params = {};
> +	int err;
> +
> +	vlan_info.opcode = opcode;
> +	vlan_info.func_id = func_id;
> +	vlan_info.vlan_id = vlan_id;
> +
> +	mgmt_msg_params_init_default(&msg_params, &vlan_info,
> +				     sizeof(vlan_info));
> +
> +	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
> +				       L2NIC_CMD_CFG_FUNC_VLAN, &msg_params);
> +
> +	if (err || vlan_info.msg_head.status) {
> +		dev_err(hwdev->dev,
> +			"Failed to %s vlan, err: %d, status: 0x%x\n",
> +			opcode == MGMT_MSG_CMD_OP_ADD ? "add" : "delete",
> +			err, vlan_info.msg_head.status);
> +		return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +
> +int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id)
> +{
> +	return hinic3_config_vlan(hwdev, MGMT_MSG_CMD_OP_ADD, vlan_id, func_id);
> +}
> +
> +int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id)
> +{
> +	return hinic3_config_vlan(hwdev, MGMT_MSG_CMD_OP_DEL, vlan_id, func_id);
> +}
> +
>   int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable)
>   {
>   	struct mag_cmd_set_port_enable en_state = {};
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
> index d4326937db48..bb7c2a67dd4b 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
> @@ -57,6 +57,11 @@ bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
>   			 enum hinic3_nic_feature_cap feature_bits);
>   void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap);
>   
> +int hinic3_set_rx_lro_state(struct hinic3_hwdev *hwdev, u8 lro_en,
> +			    u32 lro_timer, u8 lro_max_pkt_len);
> +int hinic3_set_rx_vlan_offload(struct hinic3_hwdev *hwdev, u8 en);
> +int hinic3_set_vlan_fliter(struct hinic3_hwdev *hwdev, u32 vlan_filter_ctrl);
> +
>   int hinic3_init_function_table(struct hinic3_nic_dev *nic_dev);
>   int hinic3_set_port_mtu(struct net_device *netdev, u16 new_mtu);
>   
> @@ -78,5 +83,7 @@ int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable);
>   int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up);
>   int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
>   			    bool enable);
> +int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
> +int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
>   
>   #endif
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
> index 3a9f3ccdb684..b628294b375c 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
> @@ -4,15 +4,42 @@
>   #ifndef _HINIC3_NIC_DEV_H_
>   #define _HINIC3_NIC_DEV_H_
>   
> +#include <linux/if_vlan.h>
>   #include <linux/netdevice.h>
>   
>   #include "hinic3_hw_cfg.h"
> +#include "hinic3_hwdev.h"
>   #include "hinic3_mgmt_interface.h"
>   
> +#define HINIC3_VLAN_BITMAP_BYTE_SIZE(nic_dev)  (sizeof(*(nic_dev)->vlan_bitmap))
> +#define HINIC3_VLAN_BITMAP_SIZE(nic_dev)  \
> +	(VLAN_N_VID / HINIC3_VLAN_BITMAP_BYTE_SIZE(nic_dev))
> +#define HINIC3_MODERATONE_DELAY  HZ

HINIC3_MODERATONE_DELAY -> HINIC3_MODERATION_DELAY

> +
>   enum hinic3_flags {
>   	HINIC3_RSS_ENABLE,
>   };
>   
> +enum hinic3_event_work_flags {
> +	HINIC3_EVENT_WORK_TX_TIMEOUT,
> +};
> +
> +#define HINIC3_NIC_STATS_INC(nic_dev, field) \
> +do { \
> +	u64_stats_update_begin(&(nic_dev)->stats.syncp); \
> +	(nic_dev)->stats.field++; \
> +	u64_stats_update_end(&(nic_dev)->stats.syncp); \
> +} while (0)
> +
> +struct hinic3_nic_stats {
> +	u64                   netdev_tx_timeout;
> +
> +	/* Subdivision statistics show in private tool */
> +	u64                   tx_carrier_off_drop;
> +	u64                   tx_invalid_qid;
> +	struct u64_stats_sync syncp;
> +};
> +
>   enum hinic3_rss_hash_type {
>   	HINIC3_RSS_HASH_ENGINE_TYPE_XOR  = 0,
>   	HINIC3_RSS_HASH_ENGINE_TYPE_TOEP = 1,
> @@ -55,6 +82,15 @@ struct hinic3_intr_coal_info {
>   	u8 pending_limit;
>   	u8 coalesce_timer_cfg;
>   	u8 resend_timer_cfg;
> +
> +	u64 pkt_rate_low;
> +	u8  rx_usecs_low;
> +	u8  rx_pending_limit_low;
> +	u64 pkt_rate_high;
> +	u8  rx_usecs_high;
> +	u8  rx_pending_limit_high;
> +
> +	u8  user_set_intr_coal_flag;
>   };
>   
>   struct hinic3_nic_dev {
> @@ -66,12 +102,14 @@ struct hinic3_nic_dev {
>   	u16                             max_qps;
>   	u16                             rx_buf_len;
>   	u32                             lro_replenish_thld;
> +	unsigned long                   *vlan_bitmap;
>   	unsigned long                   flags;
>   	struct hinic3_nic_service_cap   nic_svc_cap;
>   
>   	struct hinic3_dyna_txrxq_params q_params;
>   	struct hinic3_txq               *txqs;
>   	struct hinic3_rxq               *rxqs;
> +	struct hinic3_nic_stats         stats;
>   
>   	enum hinic3_rss_hash_type       rss_hash_type;
>   	struct hinic3_rss_type          rss_type;
> @@ -82,13 +120,20 @@ struct hinic3_nic_dev {
>   	struct msix_entry               *qps_msix_entries;
>   
>   	struct hinic3_intr_coal_info    *intr_coalesce;
> +	unsigned long                   last_moder_jiffies;
>   
> +	struct workqueue_struct         *workq;
> +	struct delayed_work             periodic_work;
> +	struct delayed_work             moderation_task;
>   	struct semaphore                port_state_sem;
>   
> +	/* flag bits defined by hinic3_event_work_flags */
> +	unsigned long                   event_flag;
>   	bool                            link_status_up;
>   };
>   
>   void hinic3_set_netdev_ops(struct net_device *netdev);
> +int hinic3_set_hw_features(struct net_device *netdev);
>   int hinic3_qps_irq_init(struct net_device *netdev);
>   void hinic3_qps_irq_uninit(struct net_device *netdev);
>   
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
> index 44ae841a3648..9ab9fa03d80b 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
> @@ -25,6 +25,20 @@
>   #define RQ_CQE_STATUS_GET(val, member) \
>   	FIELD_GET(RQ_CQE_STATUS_##member##_MASK, val)
>   
> +struct hinic3_rxq_stats {
> +	u64                   packets;
> +	u64                   bytes;
> +	u64                   errors;
> +	u64                   csum_errors;
> +	u64                   other_errors;
> +	u64                   dropped;
> +	u64                   rx_buf_empty;
> +	u64                   alloc_skb_err;
> +	u64                   alloc_rx_buf_err;
> +	u64                   restore_drop_sge;
> +	struct u64_stats_sync syncp;
> +};
> +
>   /* RX Completion information that is provided by HW for a specific RX WQE */
>   struct hinic3_rq_cqe {
>   	__le32 status;
> @@ -59,6 +73,7 @@ struct hinic3_rxq {
>   	u16                     buf_len;
>   	u32                     buf_len_shift;
>   
> +	struct hinic3_rxq_stats rxq_stats;
>   	u32                     cons_idx;
>   	u32                     delta;
>   
> @@ -80,6 +95,11 @@ struct hinic3_rxq {
>   	struct device          *dev; /* device for DMA mapping */
>   
>   	dma_addr_t             cqe_start_paddr;
> +
> +	u64                    last_moder_packets;
> +	u64                    last_moder_bytes;
> +	u8                     last_coalesc_timer_cfg;
> +	u8                     last_pending_limit;
>   } ____cacheline_aligned;
>   
>   struct hinic3_dyna_rxq_res {
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
> index 7e1b872ba752..7df7f3fe8061 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
> @@ -100,6 +100,20 @@ struct hinic3_sq_wqe_combo {
>   	u32                       task_type;
>   };
>   
> +struct hinic3_txq_stats {
> +	u64                   packets;
> +	u64                   bytes;
> +	u64                   busy;
> +	u64                   dropped;
> +	u64                   skb_pad_err;
> +	u64                   frag_len_overflow;
> +	u64                   offload_cow_skb_err;
> +	u64                   map_frag_err;
> +	u64                   unknown_tunnel_pkt;
> +	u64                   frag_size_err;
> +	struct u64_stats_sync syncp;
> +};
> +
>   struct hinic3_dma_info {
>   	dma_addr_t dma;
>   	u32        len;
> @@ -123,6 +137,10 @@ struct hinic3_txq {
>   
>   	struct hinic3_tx_info   *tx_info;
>   	struct hinic3_io_queue  *sq;
> +
> +	struct hinic3_txq_stats txq_stats;
> +	u64                     last_moder_packets;
> +	u64                     last_moder_bytes;
>   } ____cacheline_aligned;
>   
>   struct hinic3_dyna_txq_res {

Thanks,
Alok


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [External] : [PATCH net-next v05 4/5] hinic3: Add mac filter ops
  2025-11-06 11:15 ` [PATCH net-next v05 4/5] hinic3: Add mac filter ops Fan Gong
@ 2025-11-06 19:44   ` ALOK TIWARI
  0 siblings, 0 replies; 11+ messages in thread
From: ALOK TIWARI @ 2025-11-06 19:44 UTC (permalink / raw)
  To: Fan Gong, Zhu Yikai, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
	Markus.Elfring, pavan.chebbi
  Cc: linux-kernel, linux-doc, luosifu, Xin Guo, Shen Chenyang,
	Zhou Shuai, Wu Like, Shi Jing, Luo Yang, Meny Yossefi, Gur Stavi



On 11/6/2025 4:45 PM, Fan Gong wrote:
> Add ops to support unicast and multicast to filter mac address and
> forward packages.

packages ->

> 
> Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
> Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
> Signed-off-by: Fan Gong <gongfan1@huawei.com>
> ---
>   drivers/net/ethernet/huawei/hinic3/Makefile   |   1 +
>   .../ethernet/huawei/hinic3/hinic3_filter.c    | 418 ++++++++++++++++++
>   .../net/ethernet/huawei/hinic3/hinic3_main.c  |   6 +
>   .../huawei/hinic3/hinic3_mgmt_interface.h     |  17 +
>   .../huawei/hinic3/hinic3_netdev_ops.c         |  15 +
>   .../ethernet/huawei/hinic3/hinic3_nic_cfg.c   |  24 +
>   .../ethernet/huawei/hinic3/hinic3_nic_cfg.h   |   1 +
>   .../ethernet/huawei/hinic3/hinic3_nic_dev.h   |  33 ++
>   8 files changed, 515 insertions(+)
> +
> +void hinic3_clean_mac_list_filter(struct net_device *netdev)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct hinic3_mac_filter *ftmp;
> +	struct hinic3_mac_filter *f;
> +
> +	list_for_each_entry_safe(f, ftmp, &nic_dev->uc_filter_list, list) {
> +		if (f->state == HINIC3_MAC_HW_SYNCED)
> +			hinic3_uc_unsync(netdev, f->addr);
> +		list_del(&f->list);
> +		kfree(f);
> +	}
> +
> +	list_for_each_entry_safe(f, ftmp, &nic_dev->mc_filter_list, list) {

hinic3_uc_unsync() for both UC and MC lists.

> +		if (f->state == HINIC3_MAC_HW_SYNCED)
> +			hinic3_uc_unsync(netdev, f->addr);
> +		list_del(&f->list);
> +		kfree(f);
> +	}
> +}
> +
> +static struct hinic3_mac_filter *
> +hinic3_find_mac(const struct list_head *filter_list, u8 *addr)
> +{
> +	struct hinic3_mac_filter *f;
> +
> +	list_for_each_entry(f, filter_list, list) {
> +		if (ether_addr_equal(addr, f->addr))
> +			return f;
> +	}
> +	return NULL;
> +}
> +
> +static int hinic3_mac_filter_sync(struct net_device *netdev,
> +				  struct list_head *mac_filter_list, bool uc)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	struct list_head tmp_del_list, tmp_add_list;
> +	int err = 0, add_list_len = 0, add_count;
> +	struct hinic3_mac_filter *fclone;
> +	struct hinic3_mac_filter *ftmp;
> +	struct hinic3_mac_filter *f;
> +
> +	INIT_LIST_HEAD(&tmp_del_list);
> +	INIT_LIST_HEAD(&tmp_add_list);
> +
> +	list_for_each_entry_safe(f, ftmp, mac_filter_list, list) {
> +		if (f->state != HINIC3_MAC_WAIT_HW_UNSYNC)
> +			continue;
> +
> +		f->state = HINIC3_MAC_HW_UNSYNCED;
> +		list_move_tail(&f->list, &tmp_del_list);
> +	}
> +
> +	list_for_each_entry_safe(f, ftmp, mac_filter_list, list) {
> +		if (f->state != HINIC3_MAC_WAIT_HW_SYNC)
> +			continue;
> +
> +		fclone = hinic3_mac_filter_entry_clone(f);
> +		if (!fclone) {
> +			hinic3_undo_del_filter_entries(mac_filter_list,
> +						       &tmp_del_list);
> +			hinic3_undo_add_filter_entries(mac_filter_list,
> +						       &tmp_add_list);
> +
> +			netdev_err(netdev,
> +				   "Failed to clone mac_filter_entry\n");
> +			err = -ENOMEM;
> +			goto cleanup_tmp_filter_list;
> +		}
> +
> +		f->state = HINIC3_MAC_HW_SYNCING;
> +		list_add_tail(&fclone->list, &tmp_add_list);
> +		add_list_len++;
> +	}
> +
> +	add_count = hinic3_mac_filter_sync_hw(netdev, &tmp_del_list,
> +					      &tmp_add_list);

this could return a -error; if hinic3_set_mac fail

> +	if (add_count < add_list_len) {
> +		/* there were errors, delete all mac in hw */
> +		hinic3_undo_add_filter_entries(mac_filter_list, &tmp_add_list);
> +		/* VF does not support promiscuous mode,
> +		 * don't delete any other uc mac.
> +		 */
> +		if (!HINIC3_IS_VF(nic_dev->hwdev) || !uc) {
> +			list_for_each_entry_safe(f, ftmp, mac_filter_list,
> +						 list) {
> +				if (f->state != HINIC3_MAC_HW_SYNCED)
> +					continue;
> +
> +				fclone = hinic3_mac_filter_entry_clone(f);
> +				if (!fclone)
> +					break;
> +
> +				f->state = HINIC3_MAC_HW_SYNCING;
> +				list_add_tail(&fclone->list, &tmp_del_list);
> +			}
> +		}
> +
> +		hinic3_mac_filter_sync_hw(netdev, &tmp_del_list, &tmp_add_list);
> +
> +		/* need to enter promiscuous/allmulti mode */
> +		err = -ENOMEM;
> +	}
> +
> +cleanup_tmp_filter_list:
> +	hinic3_cleanup_filter_list(&tmp_del_list);
> +	hinic3_cleanup_filter_list(&tmp_add_list);
> +
> +	return err ? err : add_count;
> +}
> +
> +static void hinic3_mac_filter_sync_all(struct net_device *netdev)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	int add_count;
> +
> +	if (test_bit(HINIC3_MAC_FILTER_CHANGED, &nic_dev->flags)) {
> +		clear_bit(HINIC3_MAC_FILTER_CHANGED, &nic_dev->flags);
> +		add_count = hinic3_mac_filter_sync(netdev,
> +						   &nic_dev->uc_filter_list,
> +						   true);
> +		if (add_count < 0 &&
> +		    hinic3_test_support(nic_dev, HINIC3_NIC_F_PROMISC))
> +			set_bit(HINIC3_PROMISC_FORCE_ON,
> +				&nic_dev->rx_mod_state);
> +		else if (add_count)
> +			clear_bit(HINIC3_PROMISC_FORCE_ON,
> +				  &nic_dev->rx_mod_state);
> +
> +		add_count = hinic3_mac_filter_sync(netdev,
> +						   &nic_dev->mc_filter_list,
> +						   false);
> +		if (add_count < 0 &&
> +		    hinic3_test_support(nic_dev, HINIC3_NIC_F_ALLMULTI))
> +			set_bit(HINIC3_ALLMULTI_FORCE_ON,
> +				&nic_dev->rx_mod_state);
> +		else if (add_count)
> +			clear_bit(HINIC3_ALLMULTI_FORCE_ON,
> +				  &nic_dev->rx_mod_state);
> +	}
> +}
> +
> +#define HINIC3_DEFAULT_RX_MODE \
> +	(L2NIC_RX_MODE_UC | L2NIC_RX_MODE_MC | L2NIC_RX_MODE_BC)
> +
> +static void hinic3_update_mac_filter(struct net_device *netdev,
> +				     const struct netdev_hw_addr_list *src_list,
> +				     struct list_head *filter_list)
> +{
> +	struct hinic3_mac_filter *filter;
> +	struct hinic3_mac_filter *ftmp;
> +	struct hinic3_mac_filter *f;
> +	struct netdev_hw_addr *ha;
> +
> +	/* add addr if not already in the filter list */
> +	netif_addr_lock_bh(netdev);
> +	netdev_hw_addr_list_for_each(ha, src_list) {
> +		filter = hinic3_find_mac(filter_list, ha->addr);
> +		if (!filter)
> +			hinic3_add_filter(netdev, filter_list, ha->addr);
> +		else if (filter->state == HINIC3_MAC_WAIT_HW_UNSYNC)
> +			filter->state = HINIC3_MAC_HW_SYNCED;
> +	}
> +	netif_addr_unlock_bh(netdev);
> +
> +	/* delete addr if not in netdev list */
> +	list_for_each_entry_safe(f, ftmp, filter_list, list) {
> +		bool found = false;
> +
> +		netif_addr_lock_bh(netdev);
> +		netdev_hw_addr_list_for_each(ha, src_list)
> +			if (ether_addr_equal(ha->addr, f->addr)) {
> +				found = true;
> +				break;
> +			}
> +		netif_addr_unlock_bh(netdev);
> +
> +		if (found)
> +			continue;
> +
> +		hinic3_del_filter(netdev, f);
> +	}
> +}
> +
> +static void hinic3_sync_rx_mode_to_hw(struct net_device *netdev, int promisc_en,
> +				      int allmulti_en)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +	u32 rx_mod = HINIC3_DEFAULT_RX_MODE;
> +	int err;
> +
> +	rx_mod |= (promisc_en ? L2NIC_RX_MODE_PROMISC : 0);
> +	rx_mod |= (allmulti_en ? L2NIC_RX_MODE_MC_ALL : 0);
> +
> +	if (promisc_en != test_bit(HINIC3_HW_PROMISC_ON,
> +				   &nic_dev->rx_mod_state))
> +		netdev_dbg(netdev, "%s promisc mode\n",
> +			   promisc_en ? "Enter" : "Left");
> +	if (allmulti_en !=
> +	    test_bit(HINIC3_HW_ALLMULTI_ON, &nic_dev->rx_mod_state))
> +		netdev_dbg(netdev, "%s all_multi mode\n",
> +			   allmulti_en ? "Enter" : "Left");
> +
> +	err = hinic3_set_rx_mode(nic_dev->hwdev, rx_mod);
> +	if (err) {
> +		netdev_err(netdev, "Failed to set rx_mode\n");
> +		return;
> +	}
> +
> +	promisc_en ? set_bit(HINIC3_HW_PROMISC_ON, &nic_dev->rx_mod_state) :
> +		clear_bit(HINIC3_HW_PROMISC_ON, &nic_dev->rx_mod_state);
> +
> +	allmulti_en ? set_bit(HINIC3_HW_ALLMULTI_ON, &nic_dev->rx_mod_state) :
> +		clear_bit(HINIC3_HW_ALLMULTI_ON, &nic_dev->rx_mod_state);
> +}
> +
> +void hinic3_set_rx_mode_work(struct work_struct *work)
> +{
> +	int promisc_en = 0, allmulti_en = 0;
> +	struct hinic3_nic_dev *nic_dev;
> +	struct net_device *netdev;
> +
> +	nic_dev = container_of(work, struct hinic3_nic_dev, rx_mode_work);
> +	netdev = nic_dev->netdev;
> +
> +	if (test_and_clear_bit(HINIC3_UPDATE_MAC_FILTER, &nic_dev->flags)) {
> +		hinic3_update_mac_filter(netdev, &netdev->uc,
> +					 &nic_dev->uc_filter_list);
> +		hinic3_update_mac_filter(netdev, &netdev->mc,
> +					 &nic_dev->mc_filter_list);
> +	}
> +
> +	hinic3_mac_filter_sync_all(netdev);
> +
> +	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_PROMISC))
> +		promisc_en = !!(netdev->flags & IFF_PROMISC) ||
> +			test_bit(HINIC3_PROMISC_FORCE_ON,
> +				 &nic_dev->rx_mod_state);
> +
> +	if (hinic3_test_support(nic_dev, HINIC3_NIC_F_ALLMULTI))
> +		allmulti_en = !!(netdev->flags & IFF_ALLMULTI) ||
> +			test_bit(HINIC3_ALLMULTI_FORCE_ON,
> +				 &nic_dev->rx_mod_state);
> +
> +	if (promisc_en != test_bit(HINIC3_HW_PROMISC_ON,
> +				   &nic_dev->rx_mod_state) ||
> +	    allmulti_en != test_bit(HINIC3_HW_ALLMULTI_ON,
> +				    &nic_dev->rx_mod_state))
> +		hinic3_sync_rx_mode_to_hw(netdev, promisc_en, allmulti_en);
> +}
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> index 4a47dac1c4b4..e43597937da5 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
> @@ -154,6 +154,10 @@ static int hinic3_init_nic_dev(struct net_device *netdev,
>   	INIT_DELAYED_WORK(&nic_dev->periodic_work,
>   			  hinic3_periodic_work_handler);
>   
> +	INIT_LIST_HEAD(&nic_dev->uc_filter_list);
> +	INIT_LIST_HEAD(&nic_dev->mc_filter_list);
> +	INIT_WORK(&nic_dev->rx_mode_work, hinic3_set_rx_mode_work);
> +
>   	return 0;
>   }
>   
> @@ -220,6 +224,7 @@ static void hinic3_sw_uninit(struct net_device *netdev)
>   	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
>   
>   	hinic3_free_txrxqs(netdev);
> +	hinic3_clean_mac_list_filter(netdev);
>   	hinic3_del_mac(nic_dev->hwdev, netdev->dev_addr, 0,
>   		       hinic3_global_func_id(nic_dev->hwdev));
>   	hinic3_clear_rss_config(netdev);
> @@ -408,6 +413,7 @@ static void hinic3_nic_remove(struct auxiliary_device *adev)
>   	unregister_netdev(netdev);
>   
>   	disable_delayed_work_sync(&nic_dev->periodic_work);
> +	cancel_work_sync(&nic_dev->rx_mode_work);
>   	destroy_workqueue(nic_dev->workq);
>   
>   	hinic3_update_nic_feature(nic_dev, 0);
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
> index 68dfdfa1b1ba..a69778b09c83 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
> @@ -115,6 +115,22 @@ struct l2nic_cmd_set_vport_state {
>   	u8                   rsvd2[3];
>   };
>   
> +/* *
> + * Definition of the NIC receiving mode
> + */
> +#define L2NIC_RX_MODE_UC       0x01
> +#define L2NIC_RX_MODE_MC       0x02
> +#define L2NIC_RX_MODE_BC       0x04
> +#define L2NIC_RX_MODE_MC_ALL   0x08
> +#define L2NIC_RX_MODE_PROMISC  0x10
> +
> +struct l2nic_rx_mode_config {
> +	struct mgmt_msg_head msg_head;
> +	u16                  func_id;
> +	u16                  rsvd1;
> +	u32                  rx_mode;
> +};
> +
>   struct l2nic_cmd_set_dcb_state {
>   	struct mgmt_msg_head head;
>   	u16                  func_id;
> @@ -205,6 +221,7 @@ enum l2nic_cmd {
>   	/* FUNC CFG */
>   	L2NIC_CMD_SET_FUNC_TBL        = 5,
>   	L2NIC_CMD_SET_VPORT_ENABLE    = 6,
> +	L2NIC_CMD_SET_RX_MODE         = 7,
>   	L2NIC_CMD_SET_SQ_CI_ATTR      = 8,
>   	L2NIC_CMD_CLEAR_QP_RESOURCE   = 11,
>   	L2NIC_CMD_CFG_RX_LRO          = 13,
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> index ad50128f3d76..335de3093382 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> @@ -828,6 +828,20 @@ static void hinic3_get_stats64(struct net_device *netdev,
>   	stats->rx_dropped = dropped;
>   }
>   
> +static void hinic3_nic_set_rx_mode(struct net_device *netdev)
> +{
> +	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> +
> +	if (netdev_uc_count(netdev) != nic_dev->netdev_uc_cnt ||
> +	    netdev_mc_count(netdev) != nic_dev->netdev_mc_cnt) {
> +		set_bit(HINIC3_UPDATE_MAC_FILTER, &nic_dev->flags);
> +		nic_dev->netdev_uc_cnt = netdev_uc_count(netdev);
> +		nic_dev->netdev_mc_cnt = netdev_mc_count(netdev);
> +	}
> +
> +	queue_work(nic_dev->workq, &nic_dev->rx_mode_work);
> +}
> +
>   static const struct net_device_ops hinic3_netdev_ops = {
>   	.ndo_open             = hinic3_open,
>   	.ndo_stop             = hinic3_close,
> @@ -840,6 +854,7 @@ static const struct net_device_ops hinic3_netdev_ops = {
>   	.ndo_vlan_rx_kill_vid = hinic3_vlan_rx_kill_vid,
>   	.ndo_tx_timeout       = hinic3_tx_timeout,
>   	.ndo_get_stats64      = hinic3_get_stats64,
> +	.ndo_set_rx_mode      = hinic3_nic_set_rx_mode,
>   	.ndo_start_xmit       = hinic3_xmit_frame,
>   };
>   
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
> index 72e09402841a..92afab46309c 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
> @@ -499,6 +499,30 @@ int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev)
>   	return pkt_drop.msg_head.status;
>   }
>   
> +int hinic3_set_rx_mode(struct hinic3_hwdev *hwdev, u32 enable)

u32 enable -> u32 rx_mode

> +{
> +	struct l2nic_rx_mode_config rx_mode_cfg = {};
> +	struct mgmt_msg_params msg_params = {};
> +	int err;
> +
> +	rx_mode_cfg.func_id = hinic3_global_func_id(hwdev);
> +	rx_mode_cfg.rx_mode = enable;
> +
> +	mgmt_msg_params_init_default(&msg_params, &rx_mode_cfg,
> +				     sizeof(rx_mode_cfg));
> +
> +	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
> +				       L2NIC_CMD_SET_RX_MODE, &msg_params);
> +
> +	if (err || rx_mode_cfg.msg_head.status) {
> +		dev_err(hwdev->dev, "Failed to set rx mode, err: %d, status: 0x%x\n",
> +			err, rx_mode_cfg.msg_head.status);
> +		return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +
>   static int hinic3_config_vlan(struct hinic3_hwdev *hwdev,


Thanks,
Alok

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [External] : [PATCH net-next v05 1/5] hinic3: Add PF framework
  2025-11-06 14:19   ` [External] : " ALOK TIWARI
@ 2025-11-07  8:26     ` Fan Gong
  0 siblings, 0 replies; 11+ messages in thread
From: Fan Gong @ 2025-11-07  8:26 UTC (permalink / raw)
  To: alok.a.tiwari
  Cc: Markus.Elfring, andrew+netdev, davem, edumazet, gongfan1,
	guoxin09, gur.stavi, horms, kuba, linux-doc, linux-kernel,
	luosifu, luoyang82, meny.yossefi, netdev, pabeni, pavan.chebbi,
	shenchenyang1, shijing34, wulike1, zhoushuai28, zhuyikai1

On 11/6/2025 10:19 PM, ALOK TIWARI wrote:

> > +    if (src_func_id == MBOX_MGMT_FUNC_ID) {
> > +        msg_ch = &mbox->mgmt_msg;
> > +    } else if (HINIC3_IS_VF(hwdev)) {
> > +        /* message from pf */
> > +        msg_ch = mbox->func_msg;
> > +        if (src_func_id != hinic3_pf_id_of_vf(hwdev) || !msg_ch)
> > +            return NULL;
> > +    } else if (src_func_id > hinic3_glb_pf_vf_offset(hwdev)) {
> > +        /* message from vf */
> > +        id = (src_func_id - 1) - hinic3_glb_pf_vf_offset(hwdev);
> > +        if (id >= 1)
> > +            return NULL; 
> 
> hard coding id >= 1, is only one VF supported?

Hi, Alok. Thanks for your reviews.
This is an oversight on "get_mbox_msg_desc". The part of "message from vf"
should be removed in this patch because currently hinic3 driver does not support
communication between VF and PF.
Besides, as it is incorrectly placed here, it appears to support only one vf
and pf communication. But actually pf can communicate with all vf that belongs
to it. This part of code will be contained in future SRIOV subject.

> >   +    if (HINIC3_IS_VF(hwdev)) {
> > +        /* VF to PF mbox message channel */
> > +        err = hinic3_init_func_mbox_msg_channel(hwdev);
> > +        if (err)
> > +            goto err_uninit_mgmt_msg_ch;
> > +    }
> > +
> >       err = hinic3_init_func_mbox_msg_channel(hwdev);
> 
> is hinic3_init_func* second init for PF and
> VF executes both calls, is that correct? 

hinic3_init_func* only inits for VF and PF initialization is in future SRIOV
subject. So this is oversight and we should remove the redundant initialization
for PF.

The rest comments will be solved in the next version sooner.

Thanks,
Fan Gong

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2025-11-07  8:26 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-06 11:15 [PATCH net-next v05 0/5] net: hinic3: PF initialization Fan Gong
2025-11-06 11:15 ` [PATCH net-next v05 1/5] hinic3: Add PF framework Fan Gong
2025-11-06 14:19   ` [External] : " ALOK TIWARI
2025-11-07  8:26     ` Fan Gong
2025-11-06 11:15 ` [PATCH net-next v05 2/5] hinic3: Add PF management interfaces Fan Gong
2025-11-06 14:33   ` [External] : " ALOK TIWARI
2025-11-06 11:15 ` [PATCH net-next v05 3/5] hinic3: Add NIC configuration ops Fan Gong
2025-11-06 19:14   ` [External] : " ALOK TIWARI
2025-11-06 11:15 ` [PATCH net-next v05 4/5] hinic3: Add mac filter ops Fan Gong
2025-11-06 19:44   ` [External] : " ALOK TIWARI
2025-11-06 11:15 ` [PATCH net-next v05 5/5] hinic3: Add netdev register interfaces Fan Gong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).