Add port management of enable/disable/query/flush function.
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>
---
.../huawei/hinic3/hinic3_mgmt_interface.h | 29 ++++++++
.../huawei/hinic3/hinic3_netdev_ops.c | 60 ++++++++++++++++
.../ethernet/huawei/hinic3/hinic3_nic_cfg.c | 68 +++++++++++++++++++
.../ethernet/huawei/hinic3/hinic3_nic_cfg.h | 4 ++
4 files changed, 161 insertions(+)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
index 7012130bba1d..6cc0345c39e4 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
@@ -69,12 +69,27 @@ struct l2nic_cmd_set_ci_attr {
u64 ci_addr;
};
+struct l2nic_cmd_clear_qp_resource {
+ struct mgmt_msg_head msg_head;
+ u16 func_id;
+ u16 rsvd1;
+};
+
struct l2nic_cmd_force_pkt_drop {
struct mgmt_msg_head msg_head;
u8 port;
u8 rsvd1[3];
};
+struct l2nic_cmd_set_vport_state {
+ struct mgmt_msg_head msg_head;
+ u16 func_id;
+ u16 rsvd1;
+ /* 0--disable, 1--enable */
+ u8 state;
+ u8 rsvd2[3];
+};
+
struct l2nic_cmd_set_dcb_state {
struct mgmt_msg_head head;
u16 func_id;
@@ -172,6 +187,20 @@ enum l2nic_ucode_cmd {
L2NIC_UCODE_CMD_SET_RSS_INDIR_TBL = 4,
};
+/* hilink mac group command */
+enum mag_cmd {
+ MAG_CMD_GET_LINK_STATUS = 7,
+};
+
+/* firmware also use this cmd report link event to driver */
+struct mag_cmd_get_link_status {
+ struct mgmt_msg_head head;
+ u8 port_id;
+ /* 0:link down 1:link up */
+ u8 status;
+ u8 rsvd0[2];
+};
+
enum hinic3_nic_feature_cap {
HINIC3_NIC_F_CSUM = BIT(0),
HINIC3_NIC_F_SCTP_CRC = BIT(1),
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index 3d17ca5e7ba5..a07fa4bd71e7 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -326,6 +326,59 @@ static void hinic3_close_channel(struct net_device *netdev)
hinic3_free_qp_ctxts(nic_dev);
}
+static int hinic3_vport_up(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ bool link_status_up;
+ u16 glb_func_id;
+ int err;
+
+ glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
+ err = hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, true);
+ if (err) {
+ netdev_err(netdev, "Failed to enable vport\n");
+ goto err_flush_qps_res;
+ }
+
+ err = netif_set_real_num_queues(netdev, nic_dev->q_params.num_qps,
+ nic_dev->q_params.num_qps);
+ if (err) {
+ netdev_err(netdev, "Failed to set real number of queues\n");
+ goto err_flush_qps_res;
+ }
+ netif_tx_start_all_queues(netdev);
+
+ err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up);
+ if (!err && link_status_up)
+ netif_carrier_on(netdev);
+
+ return 0;
+
+err_flush_qps_res:
+ hinic3_flush_qps_res(nic_dev->hwdev);
+ /* wait to guarantee that no packets will be sent to host */
+ msleep(100);
+
+ return err;
+}
+
+static void hinic3_vport_down(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u16 glb_func_id;
+
+ netif_carrier_off(netdev);
+ netif_tx_disable(netdev);
+
+ glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
+ hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
+
+ hinic3_flush_txqs(netdev);
+ /* wait to guarantee that no packets will be sent to host */
+ msleep(100);
+ hinic3_flush_qps_res(nic_dev->hwdev);
+}
+
static int hinic3_open(struct net_device *netdev)
{
struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
@@ -355,8 +408,14 @@ static int hinic3_open(struct net_device *netdev)
if (err)
goto err_uninit_qps;
+ err = hinic3_vport_up(netdev);
+ if (err)
+ goto err_close_channel;
+
return 0;
+err_close_channel:
+ hinic3_close_channel(netdev);
err_uninit_qps:
hinic3_uninit_qps(nic_dev, &qp_params);
hinic3_free_channel_resources(netdev, &qp_params, &nic_dev->q_params);
@@ -373,6 +432,7 @@ static int hinic3_close(struct net_device *netdev)
struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic3_dyna_qp_params qp_params;
+ hinic3_vport_down(netdev);
hinic3_close_channel(netdev);
hinic3_uninit_qps(nic_dev, &qp_params);
hinic3_free_channel_resources(netdev, &qp_params, &nic_dev->q_params);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
index ed70750f5ae8..9349b8a314ae 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
@@ -267,6 +267,28 @@ int hinic3_set_ci_table(struct hinic3_hwdev *hwdev, struct hinic3_sq_attr *attr)
return 0;
}
+int hinic3_flush_qps_res(struct hinic3_hwdev *hwdev)
+{
+ struct l2nic_cmd_clear_qp_resource sq_res = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ sq_res.func_id = hinic3_global_func_id(hwdev);
+
+ mgmt_msg_params_init_default(&msg_params, &sq_res, sizeof(sq_res));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+ L2NIC_CMD_CLEAR_QP_RESOURCE,
+ &msg_params);
+ if (err || sq_res.msg_head.status) {
+ dev_err(hwdev->dev, "Failed to clear sq resources, err: %d, status: 0x%x\n",
+ err, sq_res.msg_head.status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev)
{
struct l2nic_cmd_force_pkt_drop pkt_drop = {};
@@ -314,3 +336,49 @@ int hinic3_sync_dcb_state(struct hinic3_hwdev *hwdev, u8 op_code, u8 state)
return 0;
}
+
+int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up)
+{
+ struct mag_cmd_get_link_status get_link = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ get_link.port_id = hinic3_physical_port_id(hwdev);
+
+ mgmt_msg_params_init_default(&msg_params, &get_link, sizeof(get_link));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_HILINK,
+ MAG_CMD_GET_LINK_STATUS, &msg_params);
+ if (err || get_link.head.status) {
+ dev_err(hwdev->dev, "Failed to get link state, err: %d, status: 0x%x\n",
+ err, get_link.head.status);
+ return -EIO;
+ }
+
+ *link_status_up = !!get_link.status;
+
+ return 0;
+}
+
+int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
+ bool enable)
+{
+ struct l2nic_cmd_set_vport_state en_state = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ en_state.func_id = func_id;
+ en_state.state = enable ? 1 : 0;
+
+ mgmt_msg_params_init_default(&msg_params, &en_state, sizeof(en_state));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+ L2NIC_CMD_SET_VPORT_ENABLE, &msg_params);
+ if (err || en_state.msg_head.status) {
+ dev_err(hwdev->dev, "Failed to set vport state, err: %d, status: 0x%x\n",
+ err, en_state.msg_head.status);
+ return -EINVAL;
+ }
+
+ 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 719b81e2bc2a..b83b567fa542 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -50,8 +50,12 @@ int hinic3_update_mac(struct hinic3_hwdev *hwdev, const u8 *old_mac,
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_sync_dcb_state(struct hinic3_hwdev *hwdev, u8 op_code, u8 state);
+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);
#endif
--
2.43.0
On Tue, Sep 09, 2025 at 03:33:37PM +0800, Fan Gong wrote:
> Add port management of enable/disable/query/flush function.
>
> 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>
...
> diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> index 3d17ca5e7ba5..a07fa4bd71e7 100644
> --- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
> @@ -326,6 +326,59 @@ static void hinic3_close_channel(struct net_device *netdev)
> hinic3_free_qp_ctxts(nic_dev);
> }
>
> +static int hinic3_vport_up(struct net_device *netdev)
> +{
> + struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> + bool link_status_up;
> + u16 glb_func_id;
> + int err;
> +
> + glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
> + err = hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, true);
> + if (err) {
> + netdev_err(netdev, "Failed to enable vport\n");
> + goto err_flush_qps_res;
> + }
> +
> + err = netif_set_real_num_queues(netdev, nic_dev->q_params.num_qps,
> + nic_dev->q_params.num_qps);
> + if (err) {
> + netdev_err(netdev, "Failed to set real number of queues\n");
> + goto err_flush_qps_res;
> + }
> + netif_tx_start_all_queues(netdev);
> +
> + err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up);
> + if (!err && link_status_up)
> + netif_carrier_on(netdev);
> +
> + return 0;
> +
> +err_flush_qps_res:
> + hinic3_flush_qps_res(nic_dev->hwdev);
> + /* wait to guarantee that no packets will be sent to host */
> + msleep(100);
I realise that Jakub's feedback on msleep() in his review of v3 was
in a different code path. But I do wonder if there is a better way.
> +
> + return err;
> +}
> +
> +static void hinic3_vport_down(struct net_device *netdev)
> +{
> + struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
> + u16 glb_func_id;
> +
> + netif_carrier_off(netdev);
> + netif_tx_disable(netdev);
> +
> + glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
> + hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
> +
> + hinic3_flush_txqs(netdev);
> + /* wait to guarantee that no packets will be sent to host */
> + msleep(100);
Likewise, here.
> + hinic3_flush_qps_res(nic_dev->hwdev);
> +}
> +
> static int hinic3_open(struct net_device *netdev)
> {
> struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
...
On 9/11/2025 8:33 PM, Simon Horman wrote: > > + err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up); > > + if (!err && link_status_up) > > + netif_carrier_on(netdev); > > + > > + return 0; > > + > > +err_flush_qps_res: > > + hinic3_flush_qps_res(nic_dev->hwdev); > > + /* wait to guarantee that no packets will be sent to host */ > > + msleep(100); > > I realise that Jakub's feedback on msleep() in his review of v3 was > in a different code path. But I do wonder if there is a better way. ... > > + hinic3_flush_txqs(netdev); > > + /* wait to guarantee that no packets will be sent to host */ > > + msleep(100); > > Likewise, here. Thanks for your review, Simon. Firstly, The main issue on the code of Jakub's feedback on msleep() is duplicate code function. The msleep() in hinic3_vport_down and hinic3_free_hwdev is repetitive because of our oversight. So we removed msleep() in hinic3_free_hwdev in v04 patch. Secondly, there is no better way indeed. As our HW bad decision, HW didn't have an accurate way of checking if rq has been flushed. The only way is to close the func & port . Then we wait for HW to process the pkts and upload them to driver. The sleep time is determined through our testing. The two calls of msleep() are the same issue. Finally, we have received your reviews on other patches and we will fix them soon in the next version.
On Thu, Sep 11, 2025 at 10:25:04PM +0800, Fan Gong wrote: > On 9/11/2025 8:33 PM, Simon Horman wrote: > > > > + err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up); > > > + if (!err && link_status_up) > > > + netif_carrier_on(netdev); > > > + > > > + return 0; > > > + > > > +err_flush_qps_res: > > > + hinic3_flush_qps_res(nic_dev->hwdev); > > > + /* wait to guarantee that no packets will be sent to host */ > > > + msleep(100); > > > > I realise that Jakub's feedback on msleep() in his review of v3 was > > in a different code path. But I do wonder if there is a better way. > > ... > > > > + hinic3_flush_txqs(netdev); > > > + /* wait to guarantee that no packets will be sent to host */ > > > + msleep(100); > > > > Likewise, here. > > Thanks for your review, Simon. > > Firstly, The main issue on the code of Jakub's feedback on msleep() is > duplicate code function. The msleep() in hinic3_vport_down and > hinic3_free_hwdev is repetitive because of our oversight. So we removed > msleep() in hinic3_free_hwdev in v04 patch. > > Secondly, there is no better way indeed. As our HW bad decision, HW > didn't have an accurate way of checking if rq has been flushed. The > only way is to close the func & port . Then we wait for HW to process > the pkts and upload them to driver. > The sleep time is determined through our testing. The two calls of > msleep() are the same issue. Thanks for the clarification, much appreciated. > Finally, we have received your reviews on other patches and we will > fix them soon in the next version. >
© 2016 - 2026 Red Hat, Inc.