* [PATCH net-next] net: hns3: add support to query/set TX pfc_prevention_tout for ethtool with RX prevention disabled
@ 2026-06-15 6:34 Jijie Shao
2026-06-16 2:05 ` Jakub Kicinski
0 siblings, 1 reply; 2+ messages in thread
From: Jijie Shao @ 2026-06-15 6:34 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
From: Hao Chen <chenhao418@huawei.com>
Add ethtool support to query and configure the PFC (Priority Flow Control)
storm prevention timeout. When TX continuously sends PFC frames, the peer
end is suppressed from sending packets. If this persists, a PFC frame storm
may occur.
This feature allows configuring a timeout to prevent such storms.
Signed-off-by: Hao Chen <chenhao418@huawei.com>
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
drivers/net/ethernet/hisilicon/hns3/hnae3.h | 14 ++
.../hns3/hns3_common/hclge_comm_cmd.h | 3 +
.../ethernet/hisilicon/hns3/hns3_ethtool.c | 12 ++
.../hisilicon/hns3/hns3pf/hclge_cmd.h | 9 +
.../hisilicon/hns3/hns3pf/hclge_main.c | 172 ++++++++++++++++++
.../hisilicon/hns3/hns3pf/hclge_main.h | 7 +
6 files changed, 217 insertions(+)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index a8798eecd9fb..4286af9239b0 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -602,6 +602,10 @@ typedef int (*read_func)(struct seq_file *s, void *data);
* Config wake on lan
* dbg_get_read_func
* Return the read func for debugfs seq file
+ * set_pfc_prevention_tout
+ * Set PFC storm prevention timeout
+ * get_pfc_prevention_tout
+ * Get PFC storm prevention timeout
*/
struct hnae3_ae_ops {
int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev);
@@ -810,6 +814,8 @@ struct hnae3_ae_ops {
int (*hwtstamp_set)(struct hnae3_handle *handle,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
+ int (*set_pfc_prevention_tout)(struct hnae3_handle *handle, u16 times);
+ int (*get_pfc_prevention_tout)(struct hnae3_handle *handle, u16 *times);
};
struct hnae3_dcb_ops {
@@ -891,6 +897,14 @@ struct hnae3_roce_private_info {
unsigned long state;
};
+struct hnae3_pfc_storm_para {
+ u32 dir;
+ u32 enable;
+ u32 period_ms;
+ u32 times;
+ u32 recovery_period_ms;
+};
+
#define HNAE3_SUPPORT_APP_LOOPBACK BIT(0)
#define HNAE3_SUPPORT_PHY_LOOPBACK BIT(1)
#define HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK BIT(2)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
index 2c2a2f1e0d7a..6dde07dde1e8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
@@ -314,6 +314,9 @@ enum hclge_opcode_type {
/* Query link diagnosis info command */
HCLGE_OPC_QUERY_LINK_DIAGNOSIS = 0x702A,
+
+ /* Config pause storm param command */
+ HCLGE_OPC_CFG_PAUSE_STORM_PARA = 0x7019,
};
enum hclge_comm_cmd_return_status {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index 9cb7ce9fd311..1ecde1ec269a 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -1896,6 +1896,12 @@ static int hns3_get_tunable(struct net_device *netdev,
case ETHTOOL_TX_COPYBREAK_BUF_SIZE:
*(u32 *)data = h->kinfo.tx_spare_buf_size;
break;
+ case ETHTOOL_PFC_PREVENTION_TOUT:
+ if (!h->ae_algo->ops->get_pfc_prevention_tout)
+ return -EOPNOTSUPP;
+
+ ret = h->ae_algo->ops->get_pfc_prevention_tout(h, (u16 *)data);
+ break;
default:
ret = -EOPNOTSUPP;
break;
@@ -2021,6 +2027,12 @@ static int hns3_set_tunable(struct net_device *netdev,
netdev_info(netdev, "the active tx spare buf size is %u, due to page order\n",
priv->ring->tx_spare->len);
+ break;
+ case ETHTOOL_PFC_PREVENTION_TOUT:
+ if (!h->ae_algo->ops->set_pfc_prevention_tout)
+ return -EOPNOTSUPP;
+
+ ret = h->ae_algo->ops->set_pfc_prevention_tout(h, *(u16 *)data);
break;
default:
ret = -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 4ce92ddefcde..1c029dc32ab8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -890,6 +890,15 @@ struct hclge_query_wol_supported_cmd {
u8 rsv[20];
};
+struct hclge_pfc_storm_para_cmd {
+ __le32 dir;
+ __le32 enable;
+ __le32 period_ms;
+ __le32 times;
+ __le32 recovery_period_ms;
+ __le32 rsv;
+};
+
struct hclge_hw;
int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 2f1984930da2..ade90d1bd6df 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -3502,6 +3502,122 @@ static int hclge_set_vf_link_state(struct hnae3_handle *handle, int vf,
return ret;
}
+static int hclge_set_pfc_storm_para(struct hclge_dev *hdev,
+ struct hnae3_pfc_storm_para *para)
+{
+ struct hclge_pfc_storm_para_cmd *para_cmd;
+ struct hclge_desc desc;
+ int ret;
+
+ if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3)
+ return -EOPNOTSUPP;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_PAUSE_STORM_PARA,
+ false);
+ para_cmd = (struct hclge_pfc_storm_para_cmd *)desc.data;
+ para_cmd->dir = cpu_to_le32(para->dir);
+ para_cmd->enable = cpu_to_le32(para->enable);
+ para_cmd->period_ms = cpu_to_le32(para->period_ms);
+ para_cmd->times = cpu_to_le32(para->times);
+ para_cmd->recovery_period_ms = cpu_to_le32(para->recovery_period_ms);
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "failed to set pfc storm para, ret = %d\n", ret);
+ return ret;
+}
+
+static int hclge_get_pfc_storm_para(struct hclge_dev *hdev,
+ struct hnae3_pfc_storm_para *para)
+{
+ struct hclge_pfc_storm_para_cmd *para_cmd;
+ struct hclge_desc desc;
+ int ret;
+
+ if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3)
+ return -EOPNOTSUPP;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_PAUSE_STORM_PARA, true);
+ para_cmd = (struct hclge_pfc_storm_para_cmd *)desc.data;
+ para_cmd->dir = cpu_to_le32(para->dir);
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "failed to get pfc storm para, ret = %d\n", ret);
+ return ret;
+ }
+
+ para->enable = le32_to_cpu(para_cmd->enable);
+ para->period_ms = le32_to_cpu(para_cmd->period_ms);
+ para->times = le32_to_cpu(para_cmd->times);
+ para->recovery_period_ms = le32_to_cpu(para_cmd->recovery_period_ms);
+
+ return 0;
+}
+
+static int hclge_enable_pfc_storm_prevent(struct hclge_dev *hdev,
+ int dir, bool enable)
+{
+ struct hnae3_pfc_storm_para para = {0};
+ int ret;
+
+ para.dir = dir;
+ ret = hclge_get_pfc_storm_para(hdev, ¶);
+ if (ret)
+ return ret;
+
+ para.enable = enable;
+ return hclge_set_pfc_storm_para(hdev, ¶);
+}
+
+static int hclge_set_pfc_prevention_tout(struct hnae3_handle *h, u16 times)
+{
+ struct hclge_vport *vport = hclge_get_vport(h);
+ struct hclge_dev *hdev = vport->back;
+ struct hnae3_pfc_storm_para para;
+ int ret;
+
+ if (times > HCLGE_MAX_PFC_PREVENTION_TOUT) {
+ dev_err(&hdev->pdev->dev,
+ "times %u should be less than %u!\n",
+ times, HCLGE_MAX_PFC_PREVENTION_TOUT);
+ return -EINVAL;
+ }
+
+ para.dir = HCLGE_DIR_TX;
+ ret = hclge_get_pfc_storm_para(hdev, ¶);
+ if (ret)
+ return ret;
+
+ para.enable = times ? 1 : 0;
+ para.times = (u32)times;
+ ret = hclge_set_pfc_storm_para(hdev, ¶);
+ if (ret)
+ return ret;
+
+ hdev->pfc_prevention_tout = times;
+
+ return 0;
+}
+
+static int hclge_get_pfc_prevention_tout(struct hnae3_handle *h, u16 *times)
+{
+ struct hclge_vport *vport = hclge_get_vport(h);
+ struct hclge_dev *hdev = vport->back;
+ struct hnae3_pfc_storm_para para;
+ int ret;
+
+ para.dir = HCLGE_DIR_TX;
+ ret = hclge_get_pfc_storm_para(hdev, ¶);
+ if (ret)
+ return ret;
+
+ *times = para.enable ? (u16)para.times : 0;
+
+ return 0;
+}
+
static void hclge_set_reset_pending(struct hclge_dev *hdev,
enum hnae3_reset_type reset_type)
{
@@ -4295,6 +4411,26 @@ static int hclge_reset_prepare(struct hclge_dev *hdev)
return hclge_reset_prepare_wait(hdev);
}
+static void hclge_restore_pfc_storm_prevention_tout(struct hclge_dev *hdev)
+{
+ struct hnae3_handle *handle = &hdev->vport[0].nic;
+ int ret;
+
+ ret = hclge_enable_pfc_storm_prevent(hdev, HCLGE_DIR_RX, false);
+ if (ret == -EOPNOTSUPP)
+ return;
+ else if (ret)
+ dev_warn(&hdev->pdev->dev,
+ "failed to disable rx pfc storm prevent, ret = %d\n",
+ ret);
+
+ ret = hclge_set_pfc_prevention_tout(handle, hdev->pfc_prevention_tout);
+ if (ret)
+ dev_warn(&hdev->pdev->dev,
+ "failed to set tx pfc storm prevent, ret = %d\n",
+ ret);
+}
+
static int hclge_reset_rebuild(struct hclge_dev *hdev)
{
int ret;
@@ -4342,6 +4478,8 @@ static int hclge_reset_rebuild(struct hclge_dev *hdev)
hclge_update_reset_level(hdev);
+ hclge_restore_pfc_storm_prevention_tout(hdev);
+
return 0;
}
@@ -9256,6 +9394,32 @@ static int hclge_init_wol(struct hclge_dev *hdev)
return hclge_update_wol(hdev);
}
+static void hclge_init_pfc_prevention_tout(struct hclge_dev *hdev)
+{
+ struct hnae3_handle *handle = &hdev->vport[0].nic;
+ u16 times;
+ int ret;
+
+ ret = hclge_enable_pfc_storm_prevent(hdev, HCLGE_DIR_RX, false);
+ if (ret == -EOPNOTSUPP)
+ return;
+ else if (ret)
+ dev_warn(&hdev->pdev->dev,
+ "failed to disable rx pfc storm prevent, ret = %d\n",
+ ret);
+
+ ret = hclge_get_pfc_prevention_tout(handle, ×);
+ if (ret) {
+ dev_warn(&hdev->pdev->dev,
+ "failed to get tx pfc prevention timeout, ret = %d\n",
+ ret);
+ times = HCLGE_DEFAULT_PFC_PREVENTION_TOUT;
+ }
+
+ hdev->pfc_prevention_tout = times;
+ hdev->pfc_prevention_tout_default = times;
+}
+
static void hclge_get_wol(struct hnae3_handle *handle,
struct ethtool_wolinfo *wol)
{
@@ -9490,6 +9654,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
dev_warn(&pdev->dev,
"failed to wake on lan init, ret = %d\n", ret);
+ hclge_init_pfc_prevention_tout(hdev);
+
ret = hclge_devlink_init(hdev);
if (ret)
goto err_ptp_uninit;
@@ -9913,6 +10079,10 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
hclge_config_nic_hw_error(hdev, false);
hclge_config_rocee_ras_interrupt(hdev, false);
+ /* Restore hw default values for the next initialization */
+ hclge_set_pfc_prevention_tout(&hdev->vport->nic,
+ hdev->pfc_prevention_tout_default);
+
hclge_comm_cmd_uninit(hdev->ae_dev, &hdev->hw.hw);
hclge_misc_irq_uninit(hdev);
hclge_devlink_uninit(hdev);
@@ -10475,6 +10645,8 @@ static const struct hnae3_ae_ops hclge_ops = {
.set_wol = hclge_set_wol,
.hwtstamp_get = hclge_ptp_get_cfg,
.hwtstamp_set = hclge_ptp_set_cfg,
+ .set_pfc_prevention_tout = hclge_set_pfc_prevention_tout,
+ .get_pfc_prevention_tout = hclge_get_pfc_prevention_tout,
};
static struct hnae3_ae_algo ae_algo = {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index 87adeb64e6ea..f40d8c79257c 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -345,6 +345,11 @@ enum hclge_link_fail_code {
#define HCLGE_LINK_STATUS_DOWN 0
#define HCLGE_LINK_STATUS_UP 1
+#define HCLGE_DIR_RX 0
+#define HCLGE_DIR_TX 1
+#define HCLGE_MAX_PFC_PREVENTION_TOUT 2000
+#define HCLGE_DEFAULT_PFC_PREVENTION_TOUT 1000
+
#define HCLGE_PG_NUM 4
#define HCLGE_SCH_MODE_SP 0
#define HCLGE_SCH_MODE_DWRR 1
@@ -897,6 +902,8 @@ struct hclge_dev {
u16 vf_rss_size_max; /* HW defined VF max RSS task queue */
u16 pf_rss_size_max; /* HW defined PF max RSS task queue */
u32 tx_spare_buf_size; /* HW defined TX spare buffer size */
+ u16 pfc_prevention_tout; /* User config, restored after reset */
+ u16 pfc_prevention_tout_default; /* HW default, to avoid stale state */
u16 fdir_pf_filter_count; /* Num of guaranteed filters for this PF */
u16 num_alloc_vport; /* Num vports this driver supports */
base-commit: 2319688890d97c63da423a3c57c23b4ab5952dfc
--
2.33.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH net-next] net: hns3: add support to query/set TX pfc_prevention_tout for ethtool with RX prevention disabled
2026-06-15 6:34 [PATCH net-next] net: hns3: add support to query/set TX pfc_prevention_tout for ethtool with RX prevention disabled Jijie Shao
@ 2026-06-16 2:05 ` Jakub Kicinski
0 siblings, 0 replies; 2+ messages in thread
From: Jakub Kicinski @ 2026-06-16 2:05 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, andrew+netdev, horms, shenjian15,
liuyonglong, chenhao418, huangdonghua3, yangshuaisong, netdev,
linux-kernel
On Mon, 15 Jun 2026 14:34:27 +0800 Jijie Shao wrote:
> Add ethtool support to query and configure the PFC (Priority Flow Control)
> storm prevention timeout. When TX continuously sends PFC frames, the peer
> end is suppressed from sending packets. If this persists, a PFC frame storm
> may occur.
>
> This feature allows configuring a timeout to prevent such storms.
net-next is closed for the duration of the merge window.
It basically closes the moment Linus tags final.
Please see: https://netdev.bots.linux.dev/net-next.html
And of course:
https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html#development-cycle
--
pw-bot: defer
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-06-16 2:05 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-15 6:34 [PATCH net-next] net: hns3: add support to query/set TX pfc_prevention_tout for ethtool with RX prevention disabled Jijie Shao
2026-06-16 2:05 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox