summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShuaisong Yang <yangshuaisong@h-partners.com>2026-06-24 22:13:18 +0800
committerJakub Kicinski <kuba@kernel.org>2026-06-25 09:15:44 -0700
commitc711f6d1cee955e04d1cd1f76cd8abd024b27a72 (patch)
treeeec0d683f82e3fa1f316aacfeb96c816c7adcf3e
parentc01f6e6bdc1ccd21b2d07d23f50b82437b8cbf88 (diff)
net: hns3: fix permanent link down deadlock after reset
Fix a critical race condition deadlock where the network interface remains permanently Link Down after a hardware reset under specific ethtool sequences. This issue exclusively manifests in firmware-controlled PHY topologies where the driver relies on the IMP firmware to arbitrate link parameters. Standard devices driven by the kernel's native PHY_LIB are unaffected. The deadlock occurs via the following path: 1. User disables autoneg and forces an unmatched speed, forcing link down: `ethtool -s ethx autoneg off speed 10 duplex full` 2. User re-enables autoneg: `ethtool -s ethx autoneg on`. The netdev stack passes cmd->base.speed as SPEED_UNKNOWN (0xffffffff). 3. Driver saves req_autoneg=1, but before the interface can link up, a hardware reset is triggered. 4. During reset recovery, MAC init reads the un-synchronized runtime state mac.autoneg (which is still 0/OFF), misinterprets it as forced mode, and pushes the cached SPEED_UNKNOWN into the hardware registers, causing the MAC firmware state machine to freeze. Meanwhile, PHY init reads req_autoneg=1 and enables PHY autoneg. Since the MAC is frozen with 0xffffffff and PHY is running autoneg, they mismatch permanently. Fix this by: 1. Intercepting SPEED_UNKNOWN/DUPLEX_UNKNOWN in hclge_set_phy_link_ksettings() and hclge_cfg_mac_speed_dup_h() to prevent it from corrupting the driver's cached valid configuration. 2. Save req_autoneg in hclge_set_autoneg(). 3. Aligning the state judgment in hclge_set_autoneg_speed_dup() to use req_autoneg instead of the un-synchronized runtime mac.autoneg, ensuring both MAC and PHY consistently enter the autoneg branch to eliminate configuration discrepancies during reset recovery. Fixes: 05eb60e9648c ("net: hns3: using user configure after hardware reset") Signed-off-by: Shuaisong Yang <yangshuaisong@h-partners.com> Signed-off-by: Jijie Shao <shaojijie@huawei.com> Link: https://patch.msgid.link/20260624141319.271439-4-shaojijie@huawei.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c22
1 files changed, 15 insertions, 7 deletions
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index fb12ba77228c..d176100d3e4c 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -2585,8 +2585,10 @@ static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed,
return ret;
hdev->hw.mac.req_lane_num = lane_num;
- hdev->hw.mac.req_speed = (u32)speed;
- hdev->hw.mac.req_duplex = duplex;
+ if (speed != SPEED_UNKNOWN)
+ hdev->hw.mac.req_speed = (u32)speed;
+ if (duplex != DUPLEX_UNKNOWN)
+ hdev->hw.mac.req_duplex = duplex;
return 0;
}
@@ -2617,6 +2619,7 @@ static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
+ int ret;
if (!hdev->hw.mac.support_autoneg) {
if (enable) {
@@ -2628,7 +2631,10 @@ static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable)
}
}
- return hclge_set_autoneg_en(hdev, enable);
+ ret = hclge_set_autoneg_en(hdev, enable);
+ if (!ret)
+ hdev->hw.mac.req_autoneg = enable;
+ return ret;
}
static int hclge_get_autoneg(struct hnae3_handle *handle)
@@ -3343,8 +3349,10 @@ hclge_set_phy_link_ksettings(struct hnae3_handle *handle,
return ret;
hdev->hw.mac.req_autoneg = cmd->base.autoneg;
- hdev->hw.mac.req_speed = cmd->base.speed;
- hdev->hw.mac.req_duplex = cmd->base.duplex;
+ if (cmd->base.speed != SPEED_UNKNOWN)
+ hdev->hw.mac.req_speed = cmd->base.speed;
+ if (cmd->base.duplex != DUPLEX_UNKNOWN)
+ hdev->hw.mac.req_duplex = cmd->base.duplex;
return 0;
}
@@ -9313,12 +9321,12 @@ static int hclge_set_autoneg_speed_dup(struct hclge_dev *hdev)
int ret;
if (hdev->hw.mac.support_autoneg) {
- ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg);
+ ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.req_autoneg);
if (ret)
return ret;
}
- if (!hdev->hw.mac.autoneg) {
+ if (!hdev->hw.mac.req_autoneg) {
ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.req_speed,
hdev->hw.mac.req_duplex,
hdev->hw.mac.req_lane_num);