High-speed links need a configurable FEC mode to interoperate with the peer, so add FEC get/set/capability callbacks. The advertised/supported FEC values are converted between the firmware and DPDK representations.
When no FEC mode has been forced firmware reports HINIC3_PORT_FEC_NOT_SET. Map that to RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) for rte_eth_fec_get() instead of returning 0, which is not a valid mode mask. Reject out-of-range firmware values and an empty converted mask so an unknown value is not reported as a valid mode. AUTO itself is not settable through the PMD: the firmware rejects a request to configure auto negotiation (management status 0x10). Keep rejecting AUTO in fec_set, mask it out of the reported capabilities, and report the state when the firmware rejects a forced mode because hinicadm5 has the port in AUTO. rte_eth_fec_get_capability() expects a too small array to return the required entry count rather than an error, so return HINIC3_FEC_CAPA_NUM_PER_SPEED in that case. Also validate out_size in the FEC mgmt helpers, as the other mgmt helpers in that file do. Signed-off-by: Jiacheng Ye <[email protected]> --- doc/guides/nics/features/hinic3.ini | 1 + doc/guides/rel_notes/release_26_11.rst | 1 + drivers/net/hinic3/base/hinic3_nic_cfg.c | 124 +++++++++++++++++++++ drivers/net/hinic3/base/hinic3_nic_cfg.h | 32 ++++++ drivers/net/hinic3/hinic3_ethdev.c | 130 +++++++++++++++++++++++ 5 files changed, 288 insertions(+) diff --git a/doc/guides/nics/features/hinic3.ini b/doc/guides/nics/features/hinic3.ini index 3405a837bf..dc4a92fc41 100644 --- a/doc/guides/nics/features/hinic3.ini +++ b/doc/guides/nics/features/hinic3.ini @@ -25,6 +25,7 @@ VLAN filter = Y Flow control = Y CRC offload = Y VLAN offload = Y +FEC = Y L3 checksum offload = Y L4 checksum offload = Y Inner L3 checksum = P diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst index e0c0e700a7..8a353476e5 100644 --- a/doc/guides/rel_notes/release_26_11.rst +++ b/doc/guides/rel_notes/release_26_11.rst @@ -166,6 +166,7 @@ New Features * **Updated Huawei hinic3 ethernet driver.** * Added CIR drop statistics and VF link aggregation (LAG) support. + * Added FEC mode get and set support. * **Updated Intel iavf driver.** diff --git a/drivers/net/hinic3/base/hinic3_nic_cfg.c b/drivers/net/hinic3/base/hinic3_nic_cfg.c index 853ca0a41b..5fcc641ac3 100644 --- a/drivers/net/hinic3/base/hinic3_nic_cfg.c +++ b/drivers/net/hinic3/base/hinic3_nic_cfg.c @@ -1753,6 +1753,130 @@ hinic3_set_link_status_follow(struct hinic3_hwdev *hwdev, return follow.head.status; } +static void +hinic3_fec_param_convert(uint32_t opcode, uint8_t in_fec_param, uint8_t *out_fec_param) +{ + uint8_t fec_value_table_length; + uint8_t i; + + struct hinic3_fec_param_value_map fec_value_table[] = { + {HINIC3_PORT_FEC_NOT_SET, RTE_BIT32(HINIC3_PORT_FEC_NOT_SET), + RTE_ETH_FEC_MODE_CAPA_MASK(AUTO)}, + {HINIC3_PORT_FEC_RSFEC, RTE_BIT32(HINIC3_PORT_FEC_RSFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(RS)}, + {HINIC3_PORT_FEC_BASEFEC, RTE_BIT32(HINIC3_PORT_FEC_BASEFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(BASER)}, + {HINIC3_PORT_FEC_NOFEC, RTE_BIT32(HINIC3_PORT_FEC_NOFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC)}, + {HINIC3_PORT_FEC_LLRSFEC, RTE_BIT32(HINIC3_PORT_FEC_LLRSFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(LLRS)} + }; + + *out_fec_param = 0; + fec_value_table_length = + (uint8_t)(sizeof(fec_value_table) / sizeof(struct hinic3_fec_param_value_map)); + + if (opcode == HINIC3_FEC_MODE_OPCODE_SET) { + for (i = 0; i < fec_value_table_length; i++) { + if ((in_fec_param & fec_value_table[i].ethtool_fec_value) != 0) + *out_fec_param = fec_value_table[i].fec_offset; + } + } + + if (opcode == HINIC3_FEC_MODE_OPCODE_GET) { + for (i = 0; i < fec_value_table_length; i++) { + if ((in_fec_param & fec_value_table[i].hinic3_fec_value) != 0) + *out_fec_param |= fec_value_table[i].ethtool_fec_value; + } + } +} + +int +hinic3_set_fec_mode(struct hinic3_hwdev *hwdev, uint8_t fecparam) +{ + struct mag_cmd_cfg_fec_mode fec_msg = { 0 }; + uint16_t out_size = sizeof(fec_msg); + uint8_t advertised_fec = 0; + int err; + + if (!hwdev) + return -EINVAL; + + hinic3_fec_param_convert(HINIC3_FEC_MODE_OPCODE_SET, fecparam, &advertised_fec); + fec_msg.opcode = HINIC3_FEC_MODE_OPCODE_SET; + fec_msg.port_id = hinic3_physical_port_id(hwdev); + fec_msg.advertised_fec = advertised_fec; + + err = hinic3_msg_to_mgmt_sync(hwdev, HINIC3_MOD_HILINK, + MAG_CMD_CFG_FEC_MODE, + &fec_msg, sizeof(fec_msg), + &fec_msg, &out_size); + + if (err || !out_size || fec_msg.head.status) { + PMD_DRV_LOG(ERR, "Failed to set fec mode, err: %d, status: 0x%x, out size: 0x%x", + err, fec_msg.head.status, out_size); + return -EINVAL; + } + + PMD_DRV_LOG(INFO, "Set fec mode success, active fec capa mode: %d", fecparam); + return 0; +} + +int +hinic3_get_fec_mode(struct hinic3_hwdev *hwdev, uint8_t *advertised_fec, + uint8_t *supported_fec) +{ + struct mag_cmd_cfg_fec_mode fec_msg = { 0 }; + uint16_t out_size = sizeof(fec_msg); + int err; + + if (!hwdev) + return -EINVAL; + + if (advertised_fec != NULL) + *advertised_fec = 0; + + if (supported_fec != NULL) + *supported_fec = 0; + + fec_msg.opcode = HINIC3_FEC_MODE_OPCODE_GET; + fec_msg.port_id = hinic3_physical_port_id(hwdev); + + err = hinic3_msg_to_mgmt_sync(hwdev, HINIC3_MOD_HILINK, + MAG_CMD_CFG_FEC_MODE, + &fec_msg, sizeof(fec_msg), + &fec_msg, &out_size); + + if (err || !out_size || fec_msg.head.status) { + PMD_DRV_LOG(ERR, "Failed to get fec mode, err: %d, status: 0x%x, out size: 0x%x", + err, fec_msg.head.status, out_size); + return -EINVAL; + } + + /* + * Reject out-of-range values instead of turning them into an invalid + * DPDK mode mask. advertised_fec is an enum, supported_fec is a bitmap + * of the same values. + */ + if ((advertised_fec != NULL && + fec_msg.advertised_fec > HINIC3_PORT_FEC_LLRSFEC) || + (supported_fec != NULL && + (fec_msg.supported_fec & ~((1U << (HINIC3_PORT_FEC_LLRSFEC + 1)) - 1)) != 0)) + return -EINVAL; + + if (advertised_fec != NULL) + hinic3_fec_param_convert(HINIC3_FEC_MODE_OPCODE_GET, + RTE_BIT32(fec_msg.advertised_fec), + advertised_fec); + + if (supported_fec != NULL) + hinic3_fec_param_convert(HINIC3_FEC_MODE_OPCODE_GET, + fec_msg.supported_fec, + supported_fec); + + return 0; +} + bool hinic3_cmd_vf_lag(void *hwdev, uint16_t func_id, uint8_t opcode) { diff --git a/drivers/net/hinic3/base/hinic3_nic_cfg.h b/drivers/net/hinic3/base/hinic3_nic_cfg.h index 6fb56fff4f..9458a576c4 100644 --- a/drivers/net/hinic3/base/hinic3_nic_cfg.h +++ b/drivers/net/hinic3/base/hinic3_nic_cfg.h @@ -967,6 +967,33 @@ int hinic3_msg_to_mgmt_sync(struct hinic3_hwdev *hwdev, enum hinic3_mod_type mod int hinic3_set_ci_table(struct hinic3_hwdev *hwdev, struct hinic3_sq_attr *attr); +enum hinic3_fec_mode_opcode { + HINIC3_FEC_MODE_OPCODE_GET = 0, + HINIC3_FEC_MODE_OPCODE_SET = 1, +}; + +struct mag_cmd_cfg_fec_mode { + struct mgmt_msg_head head; + uint8_t port_id; + uint8_t opcode; + uint8_t advertised_fec; + uint8_t supported_fec; +}; + +enum hinic3_cmd_port_fec { + HINIC3_PORT_FEC_NOT_SET = 0, + HINIC3_PORT_FEC_RSFEC = 1, + HINIC3_PORT_FEC_BASEFEC = 2, + HINIC3_PORT_FEC_NOFEC = 3, + HINIC3_PORT_FEC_LLRSFEC = 4, +}; + +struct hinic3_fec_param_value_map { + uint8_t fec_offset; + uint8_t hinic3_fec_value; + uint8_t ethtool_fec_value; +}; + /** * Update MAC address to hardware. * @@ -1583,4 +1610,9 @@ int hinic3_get_cir_drop(void *hwdev, struct hinic3_cir_drop *stats); bool hinic3_cmd_vf_lag(void *hwdev, uint16_t func_id, uint8_t opcode); +int hinic3_set_fec_mode(struct hinic3_hwdev *hwdev, uint8_t fecparam); + +int hinic3_get_fec_mode(struct hinic3_hwdev *hwdev, uint8_t *advertised_fec, + uint8_t *supported_fec); + #endif /* _HINIC3_NIC_CFG_H_ */ diff --git a/drivers/net/hinic3/hinic3_ethdev.c b/drivers/net/hinic3/hinic3_ethdev.c index 46cc19613a..a076c8ebb2 100644 --- a/drivers/net/hinic3/hinic3_ethdev.c +++ b/drivers/net/hinic3/hinic3_ethdev.c @@ -3406,6 +3406,133 @@ hinic3_dev_filter_ctrl(struct rte_eth_dev *dev, const struct rte_flow_ops **arg) return 0; } +static const uint32_t HINIC3_FEC_CAPA_NUM_PER_SPEED = 1; + +static const uint32_t hinic3_speed_capa[] = { + RTE_ETH_SPEED_NUM_NONE, RTE_ETH_SPEED_NUM_10M, + RTE_ETH_SPEED_NUM_100M, RTE_ETH_SPEED_NUM_1G, + RTE_ETH_SPEED_NUM_10G, RTE_ETH_SPEED_NUM_25G, + RTE_ETH_SPEED_NUM_40G, RTE_ETH_SPEED_NUM_50G, + RTE_ETH_SPEED_NUM_100G, RTE_ETH_SPEED_NUM_200G, + RTE_ETH_SPEED_NUM_400G, RTE_ETH_SPEED_NUM_800G +}; + +static bool +hinic3_fec_param_valid(uint32_t fec_param) +{ + if (fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(LLRS) || + fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(RS) || + fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(BASER) || + fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC)) { + return true; + } + + return false; +} + +static int +hinic3_fec_set(struct rte_eth_dev *dev, uint32_t fec_capa) +{ + struct hinic3_nic_dev *nic_dev = HINIC3_ETH_DEV_TO_PRIVATE_NIC_DEV(dev); + int err; + + if (!hinic3_fec_param_valid(fec_capa)) { + if (fec_capa & RTE_ETH_FEC_MODE_CAPA_MASK(AUTO)) + PMD_DRV_LOG(ERR, + "Unsupported fec param 0x%x, AUTO is configured by hinicadm5.", + fec_capa); + else + PMD_DRV_LOG(ERR, "Unsupported fec param 0x%x.", fec_capa); + return -EINVAL; + } + + err = hinic3_set_fec_mode(nic_dev->hwdev, (uint8_t)fec_capa); + if (err) { + /* + * A forced FEC mode can only be configured after + * auto-negotiation is disabled with the hinicadm5 tool. + * While auto-negotiation is on, the firmware rejects the SET + * request (management status 0x10), and the PMD has no way to + * toggle it. The firmware also reports the negotiated mode on + * GET, so the AUTO state cannot be detected before the attempt. + */ + PMD_DRV_LOG(ERR, + "Failed to set fec param 0x%x, err %d (AUTO is managed by hinicadm5).", + fec_capa, err); + return err; + } + + return 0; +} + +static int +hinic3_fec_get(struct rte_eth_dev *dev, uint32_t *fec_capa) +{ + struct hinic3_nic_dev *nic_dev = HINIC3_ETH_DEV_TO_PRIVATE_NIC_DEV(dev); + uint8_t advertised_fec = 0; + int err; + + err = hinic3_get_fec_mode(nic_dev->hwdev, &advertised_fec, NULL); + if (err) { + PMD_DRV_LOG(ERR, "Get fec param failed: %d.", err); + return err; + } + + if (advertised_fec == 0) { + PMD_DRV_LOG(ERR, "Firmware reported an unknown FEC mode."); + return -EINVAL; + } + + *fec_capa = (uint32_t)advertised_fec; + + return 0; +} + +static int +hinic3_fec_capability_get(struct rte_eth_dev *dev, + struct rte_eth_fec_capa *speed_fec_capa, + unsigned int num) +{ + struct hinic3_nic_dev *nic_dev = HINIC3_ETH_DEV_TO_PRIVATE_NIC_DEV(dev); + struct nic_port_info port_info = { 0 }; + uint8_t supported_fec = 0; + int err; + + if (speed_fec_capa == NULL) + return HINIC3_FEC_CAPA_NUM_PER_SPEED; + + if (num < HINIC3_FEC_CAPA_NUM_PER_SPEED) { + PMD_DRV_LOG(ERR, "Not enough array size(%u) to store FEC capabilities, should not be less than %u.", + num, HINIC3_FEC_CAPA_NUM_PER_SPEED); + return HINIC3_FEC_CAPA_NUM_PER_SPEED; + } + + err = hinic3_get_fec_mode(nic_dev->hwdev, NULL, &supported_fec); + if (err) { + PMD_DRV_LOG(ERR, "Failed to get fec capability, err: %d.", err); + return err; + } + + err = hinic3_get_port_info(nic_dev->hwdev, &port_info); + if (err) { + PMD_DRV_LOG(ERR, "Failed to get port info, err: %d.", err); + return err; + } + + if (port_info.speed >= RTE_DIM(hinic3_speed_capa)) + return -EINVAL; + + speed_fec_capa->speed = hinic3_speed_capa[port_info.speed]; + /* AUTO is managed by hinicadm5 and cannot be set through the PMD. */ + speed_fec_capa->capa = (uint32_t)supported_fec & + ~RTE_ETH_FEC_MODE_CAPA_MASK(AUTO); + + if (speed_fec_capa->capa == 0) + return -ENOTSUP; + + return HINIC3_FEC_CAPA_NUM_PER_SPEED; +} + static const struct eth_dev_ops hinic3_pmd_ops = { .dev_configure = hinic3_dev_configure, .dev_infos_get = hinic3_dev_infos_get, @@ -3452,6 +3579,9 @@ static const struct eth_dev_ops hinic3_pmd_ops = { .mac_addr_add = hinic3_mac_addr_add, .set_mc_addr_list = hinic3_set_mc_addr_list, .flow_ops_get = hinic3_dev_filter_ctrl, + .fec_get_capability = hinic3_fec_capability_get, + .fec_get = hinic3_fec_get, + .fec_set = hinic3_fec_set, }; static const struct eth_dev_ops hinic3_pmd_vf_ops = { -- 2.33.0

