Support ethdev methods to query and set FEC information. Limitations: ignoring rte_eth_fec_get_capability() results can lead to NOFEC if the device is not strated.
Signed-off-by: Denis Pryazhennikov <denis.pryazhenni...@arknetworks.am> Reviewed-by: Ivan Malov <ivan.ma...@arknetworks.am> Reviewed-by: Andy Moreton <amore...@xilinx.com> Reviewed-by: Ferruh Yigit <ferruh.yi...@amd.com> --- doc/guides/nics/features/sfc.ini | 1 + drivers/net/sfc/sfc.h | 2 + drivers/net/sfc/sfc_ethdev.c | 337 +++++++++++++++++++++++++++++++ drivers/net/sfc/sfc_port.c | 24 ++- 4 files changed, 353 insertions(+), 11 deletions(-) diff --git a/doc/guides/nics/features/sfc.ini b/doc/guides/nics/features/sfc.ini index f5ac644278ae..e0b9bfb7f7bf 100644 --- a/doc/guides/nics/features/sfc.ini +++ b/doc/guides/nics/features/sfc.ini @@ -24,6 +24,7 @@ RSS reta update = Y SR-IOV = Y Flow control = Y VLAN offload = P +FEC = Y L3 checksum offload = Y L4 checksum offload = Y Inner L3 checksum = Y diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h index 730d054aea74..e42abe42cb8a 100644 --- a/drivers/net/sfc/sfc.h +++ b/drivers/net/sfc/sfc.h @@ -68,6 +68,8 @@ struct sfc_port { uint32_t phy_adv_cap_mask; uint32_t phy_adv_cap; + uint32_t fec_cfg; + bool fec_auto; unsigned int flow_ctrl; boolean_t flow_ctrl_autoneg; diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c index 6d41eb000345..05fc70541b10 100644 --- a/drivers/net/sfc/sfc_ethdev.c +++ b/drivers/net/sfc/sfc_ethdev.c @@ -2343,6 +2343,340 @@ sfc_rx_metadata_negotiate(struct rte_eth_dev *dev, uint64_t *features) return 0; } +static unsigned int +sfc_fec_get_capa_speed_to_fec(uint32_t supported_caps, + struct rte_eth_fec_capa *speed_fec_capa) +{ + int num = 0; + + if (supported_caps & (1u << EFX_PHY_CAP_10000FDX)) { + if (speed_fec_capa) { + speed_fec_capa[num].speed = RTE_ETH_SPEED_NUM_10G; + speed_fec_capa[num].capa = + RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) | + RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) | + RTE_ETH_FEC_MODE_CAPA_MASK(BASER); + } + num++; + } + if (supported_caps & (1u << EFX_PHY_CAP_25000FDX)) { + if (speed_fec_capa) { + speed_fec_capa[num].speed = RTE_ETH_SPEED_NUM_25G; + speed_fec_capa[num].capa = + RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) | + RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) | + RTE_ETH_FEC_MODE_CAPA_MASK(BASER) | + RTE_ETH_FEC_MODE_CAPA_MASK(RS); + } + num++; + } + if (supported_caps & (1u << EFX_PHY_CAP_40000FDX)) { + if (speed_fec_capa) { + speed_fec_capa[num].speed = RTE_ETH_SPEED_NUM_40G; + speed_fec_capa[num].capa = + RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) | + RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) | + RTE_ETH_FEC_MODE_CAPA_MASK(BASER); + } + num++; + } + if (supported_caps & (1u << EFX_PHY_CAP_50000FDX)) { + if (speed_fec_capa) { + speed_fec_capa[num].speed = RTE_ETH_SPEED_NUM_50G; + speed_fec_capa[num].capa = + RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) | + RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) | + RTE_ETH_FEC_MODE_CAPA_MASK(BASER) | + RTE_ETH_FEC_MODE_CAPA_MASK(RS); + } + num++; + } + if (supported_caps & (1u << EFX_PHY_CAP_100000FDX)) { + if (speed_fec_capa) { + speed_fec_capa[num].speed = RTE_ETH_SPEED_NUM_100G; + speed_fec_capa[num].capa = + RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) | + RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) | + RTE_ETH_FEC_MODE_CAPA_MASK(RS); + } + num++; + } + + return num; +} + +static int +sfc_fec_get_capability(struct rte_eth_dev *dev, + struct rte_eth_fec_capa *speed_fec_capa, + unsigned int num) +{ + struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev); + unsigned int num_entries; + uint32_t supported_caps; + + sfc_adapter_lock(sa); + + efx_phy_adv_cap_get(sa->nic, EFX_PHY_CAP_PERM, &supported_caps); + + num_entries = sfc_fec_get_capa_speed_to_fec(supported_caps, NULL); + if (!speed_fec_capa || num < num_entries) + goto adapter_unlock; + + num_entries = sfc_fec_get_capa_speed_to_fec(supported_caps, + speed_fec_capa); + +adapter_unlock: + sfc_adapter_unlock(sa); + + return num_entries; +} + +static uint32_t +sfc_efx_caps_to_fec(uint32_t caps, bool is_25g) +{ + bool rs_req = caps & EFX_PHY_CAP_FEC_BIT(RS_FEC_REQUESTED); + bool rs = caps & EFX_PHY_CAP_FEC_BIT(RS_FEC); + uint32_t fec_capa = 0; + bool baser_req; + bool baser; + + if (is_25g) { + baser = caps & EFX_PHY_CAP_FEC_BIT(25G_BASER_FEC); + baser_req = caps & EFX_PHY_CAP_FEC_BIT(25G_BASER_FEC_REQUESTED); + } else { + baser = caps & EFX_PHY_CAP_FEC_BIT(BASER_FEC); + baser_req = caps & EFX_PHY_CAP_FEC_BIT(BASER_FEC_REQUESTED); + } + + if (!baser && !rs) + return RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_NOFEC); + + if (rs_req) + fec_capa |= RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_RS); + + if (baser_req) + fec_capa |= RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_BASER); + + + return fec_capa; +} + +static int +sfc_fec_get(struct rte_eth_dev *dev, uint32_t *fec_capa) +{ + struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev); + struct sfc_port *port = &sa->port; + struct rte_eth_link current_link; + efx_phy_fec_type_t active_fec; + bool is_25g = false; + int rc = 0; + + sfc_adapter_lock(sa); + + sfc_dev_get_rte_link(dev, 1, ¤t_link); + + if (current_link.link_status == RTE_ETH_LINK_DOWN) { + uint32_t speed = current_link.link_speed; + + if (port->fec_auto) { + *fec_capa = RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_AUTO); + goto adapter_unlock; + } + + is_25g = (speed == RTE_ETH_SPEED_NUM_25G || + speed == RTE_ETH_SPEED_NUM_50G); + + *fec_capa = sfc_efx_caps_to_fec(port->fec_cfg, is_25g); + + goto adapter_unlock; + } + + rc = efx_phy_fec_type_get(sa->nic, &active_fec); + if (rc != 0) + goto adapter_unlock; + + switch (active_fec) { + case EFX_PHY_FEC_NONE: + *fec_capa = RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_NOFEC); + break; + case EFX_PHY_FEC_BASER: + *fec_capa = RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_BASER); + break; + case EFX_PHY_FEC_RS: + *fec_capa = RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_RS); + break; + default: + break; + } + +adapter_unlock: + sfc_adapter_unlock(sa); + + return rc; +} + +static int +sfc_fec_capa_check(struct rte_eth_dev *dev, uint32_t fec_capa, + uint32_t supported_caps) +{ + struct rte_eth_fec_capa *speed_fec_capa; + struct rte_eth_link current_link; + bool is_supported = false; + unsigned int num_entries; + bool auto_fec = false; + unsigned int i; + + struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev); + + if (sa->state != SFC_ETHDEV_STARTED) + return 0; + + if (fec_capa & RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_AUTO)) { + auto_fec = true; + fec_capa &= ~RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_AUTO); + } + + /* + * If only the AUTO bit is set, the decision on which FEC + * mode to use will be made by HW/FW or driver. + */ + if (auto_fec && fec_capa == 0) + return 0; + + sfc_dev_get_rte_link(dev, 1, ¤t_link); + + num_entries = sfc_fec_get_capa_speed_to_fec(supported_caps, NULL); + if (num_entries == 0) + return -ENOTSUP; + + speed_fec_capa = rte_calloc("fec_capa", num_entries, + sizeof(*speed_fec_capa), 0); + num_entries = sfc_fec_get_capa_speed_to_fec(supported_caps, + speed_fec_capa); + + for (i = 0; i < num_entries; i++) { + if (speed_fec_capa[i].speed == current_link.link_speed) { + if ((fec_capa & speed_fec_capa[i].capa) != 0) + is_supported = true; + + break; + } + } + + rte_free(speed_fec_capa); + + if (is_supported) + return 0; + + return -ENOTSUP; +} + +static int +sfc_fec_capa_to_efx(uint32_t supported_caps, uint32_t fec_capa, + uint32_t *efx_fec_caps) +{ + bool fec_is_set = false; + bool auto_fec = false; + bool nofec = false; + uint32_t ret = 0; + + if (efx_fec_caps == NULL) + return -EINVAL; + + if (fec_capa & RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_AUTO)) + auto_fec = true; + + if (fec_capa & RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_NOFEC)) + nofec = true; + + if (fec_capa == RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_AUTO)) { + ret |= (EFX_PHY_CAP_FEC_BIT(BASER_FEC) | + EFX_PHY_CAP_FEC_BIT(25G_BASER_FEC) | + EFX_PHY_CAP_FEC_BIT(RS_FEC)) & supported_caps; + goto done; + } + + if (fec_capa & RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_RS)) { + fec_is_set = true; + + if (supported_caps & EFX_PHY_CAP_FEC_BIT(RS_FEC)) { + ret |= EFX_PHY_CAP_FEC_BIT(RS_FEC) | + EFX_PHY_CAP_FEC_BIT(RS_FEC_REQUESTED); + } + } + if (fec_capa & RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_BASER)) { + if (!auto_fec && fec_is_set) + return -EINVAL; + + if (supported_caps & EFX_PHY_CAP_FEC_BIT(BASER_FEC)) { + ret |= EFX_PHY_CAP_FEC_BIT(BASER_FEC) | + EFX_PHY_CAP_FEC_BIT(BASER_FEC_REQUESTED); + } + if (supported_caps & EFX_PHY_CAP_FEC_BIT(25G_BASER_FEC)) { + ret |= EFX_PHY_CAP_FEC_BIT(25G_BASER_FEC) | + EFX_PHY_CAP_FEC_BIT(25G_BASER_FEC_REQUESTED); + } + } + + if (ret == 0 && !nofec) + return -ENOTSUP; + +done: + *efx_fec_caps = ret; + return 0; +} + +static int +sfc_fec_set(struct rte_eth_dev *dev, uint32_t fec_capa) +{ + struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev); + struct sfc_port *port = &sa->port; + uint32_t supported_caps; + uint32_t efx_fec_caps; + uint32_t updated_caps; + int rc = 0; + + sfc_adapter_lock(sa); + + efx_phy_adv_cap_get(sa->nic, EFX_PHY_CAP_PERM, &supported_caps); + + rc = sfc_fec_capa_check(dev, fec_capa, supported_caps); + if (rc != 0) + goto adapter_unlock; + + rc = sfc_fec_capa_to_efx(supported_caps, fec_capa, &efx_fec_caps); + if (rc != 0) + goto adapter_unlock; + + if (sa->state == SFC_ETHDEV_STARTED) { + efx_phy_adv_cap_get(sa->nic, EFX_PHY_CAP_CURRENT, + &updated_caps); + updated_caps = updated_caps & ~EFX_PHY_CAP_FEC_MASK; + updated_caps |= efx_fec_caps; + + rc = efx_phy_adv_cap_set(sa->nic, updated_caps); + if (rc != 0) + goto adapter_unlock; + } + + port->fec_cfg = efx_fec_caps; + /* + * There is no chance to recognize AUTO mode from the + * saved FEC capabilities as AUTO mode can have the same + * set of bits as any other mode from the EFX point of view. + * Save it in the proper variable. + */ + if (fec_capa & RTE_ETH_FEC_MODE_TO_CAPA(RTE_ETH_FEC_AUTO)) + port->fec_auto = true; + else + port->fec_auto = false; + +adapter_unlock: + sfc_adapter_unlock(sa); + + return rc; +} + static const struct eth_dev_ops sfc_eth_dev_ops = { .dev_configure = sfc_dev_configure, .dev_start = sfc_dev_start, @@ -2392,6 +2726,9 @@ static const struct eth_dev_ops sfc_eth_dev_ops = { .pool_ops_supported = sfc_pool_ops_supported, .representor_info_get = sfc_representor_info_get, .rx_metadata_negotiate = sfc_rx_metadata_negotiate, + .fec_get_capability = sfc_fec_get_capability, + .fec_get = sfc_fec_get, + .fec_set = sfc_fec_set, }; struct sfc_ethdev_init_data { diff --git a/drivers/net/sfc/sfc_port.c b/drivers/net/sfc/sfc_port.c index 5f312ab1ba83..0c887ddedb09 100644 --- a/drivers/net/sfc/sfc_port.c +++ b/drivers/net/sfc/sfc_port.c @@ -228,17 +228,8 @@ sfc_port_start(struct sfc_adapter *sa) /* Preserve pause capabilities set by above efx_mac_fcntl_set() */ efx_phy_adv_cap_get(sa->nic, EFX_PHY_CAP_CURRENT, &phy_adv_cap); SFC_ASSERT((port->phy_adv_cap & phy_pause_caps) == 0); - phy_adv_cap = port->phy_adv_cap | (phy_adv_cap & phy_pause_caps); - - /* - * No controls for FEC yet. Use default FEC mode. - * I.e. advertise everything supported (*_FEC=1), but do not request - * anything explicitly (*_FEC_REQUESTED=0). - */ - phy_adv_cap |= port->phy_adv_cap_mask & - (1u << EFX_PHY_CAP_BASER_FEC | - 1u << EFX_PHY_CAP_RS_FEC | - 1u << EFX_PHY_CAP_25G_BASER_FEC); + phy_adv_cap = port->phy_adv_cap | (phy_adv_cap & phy_pause_caps) | + port->fec_cfg; sfc_log_init(sa, "set phy adv caps to %#x", phy_adv_cap); rc = efx_phy_adv_cap_set(sa->nic, phy_adv_cap); @@ -469,6 +460,17 @@ sfc_port_attach(struct sfc_adapter *sa) port->mac_stats_update_period_ms = kvarg_stats_update_period_ms; + /* + * Set default FEC mode. + * I.e. advertise everything supported (*_FEC=1), but do not request + * anything explicitly (*_FEC_REQUESTED=0). + */ + port->fec_cfg = port->phy_adv_cap_mask & + (EFX_PHY_CAP_FEC_BIT(BASER_FEC) | + EFX_PHY_CAP_FEC_BIT(RS_FEC) | + EFX_PHY_CAP_FEC_BIT(25G_BASER_FEC)); + port->fec_auto = true; + sfc_log_init(sa, "done"); return 0; -- 2.37.0 (Apple Git-136)