mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 02:50:43 +00:00
net/mlx5e: Fix matching of speed to PRM link modes
Speed translation is performed based on legacy or extended PTYS
register. Translate speed with respect to:
1) Capability bit of extended PTYS table.
2) User request:
a) When auto-negotiation is turned on, inspect advertisement whether it
contains extended link modes.
b) When auto-negotiation is turned off, speed > 100Gbps (maximal
speed supported in legacy mode).
With both conditions fulfilled translation is done with extended PTYS
table otherwise use legacy PTYS table.
Without this patch 25/50/100 Gbps speed cannot be set, since try to
configure in extended mode but read from legacy mode.
Fixes: dd1b9e09c1
("net/mlx5: ethtool, Allow legacy link-modes configuration via non-extended ptys")
Signed-off-by: Aya Levin <ayal@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
This commit is contained in:
parent
694826e366
commit
4b95840a6c
3 changed files with 70 additions and 34 deletions
|
@ -78,9 +78,10 @@ static const u32 mlx5e_ext_link_speed[MLX5E_EXT_LINK_MODES_NUMBER] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev,
|
static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev,
|
||||||
const u32 **arr, u32 *size)
|
const u32 **arr, u32 *size,
|
||||||
|
bool force_legacy)
|
||||||
{
|
{
|
||||||
bool ext = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
|
bool ext = force_legacy ? false : MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
|
||||||
|
|
||||||
*size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) :
|
*size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) :
|
||||||
ARRAY_SIZE(mlx5e_link_speed);
|
ARRAY_SIZE(mlx5e_link_speed);
|
||||||
|
@ -152,7 +153,8 @@ int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
|
||||||
sizeof(out), MLX5_REG_PTYS, 0, 1);
|
sizeof(out), MLX5_REG_PTYS, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper)
|
u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper,
|
||||||
|
bool force_legacy)
|
||||||
{
|
{
|
||||||
unsigned long temp = eth_proto_oper;
|
unsigned long temp = eth_proto_oper;
|
||||||
const u32 *table;
|
const u32 *table;
|
||||||
|
@ -160,7 +162,7 @@ u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper)
|
||||||
u32 max_size;
|
u32 max_size;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
mlx5e_port_get_speed_arr(mdev, &table, &max_size);
|
mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy);
|
||||||
i = find_first_bit(&temp, max_size);
|
i = find_first_bit(&temp, max_size);
|
||||||
if (i < max_size)
|
if (i < max_size)
|
||||||
speed = table[i];
|
speed = table[i];
|
||||||
|
@ -170,6 +172,7 @@ u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper)
|
||||||
int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
|
int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
|
||||||
{
|
{
|
||||||
struct mlx5e_port_eth_proto eproto;
|
struct mlx5e_port_eth_proto eproto;
|
||||||
|
bool force_legacy = false;
|
||||||
bool ext;
|
bool ext;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -177,8 +180,13 @@ int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
|
||||||
err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
|
err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
if (ext && !eproto.admin) {
|
||||||
*speed = mlx5e_port_ptys2speed(mdev, eproto.oper);
|
force_legacy = true;
|
||||||
|
err = mlx5_port_query_eth_proto(mdev, 1, false, &eproto);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*speed = mlx5e_port_ptys2speed(mdev, eproto.oper, force_legacy);
|
||||||
if (!(*speed))
|
if (!(*speed))
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
|
||||||
|
@ -201,7 +209,7 @@ int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
mlx5e_port_get_speed_arr(mdev, &table, &max_size);
|
mlx5e_port_get_speed_arr(mdev, &table, &max_size, false);
|
||||||
for (i = 0; i < max_size; ++i)
|
for (i = 0; i < max_size; ++i)
|
||||||
if (eproto.cap & MLX5E_PROT_MASK(i))
|
if (eproto.cap & MLX5E_PROT_MASK(i))
|
||||||
max_speed = max(max_speed, table[i]);
|
max_speed = max(max_speed, table[i]);
|
||||||
|
@ -210,14 +218,15 @@ int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed)
|
u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed,
|
||||||
|
bool force_legacy)
|
||||||
{
|
{
|
||||||
u32 link_modes = 0;
|
u32 link_modes = 0;
|
||||||
const u32 *table;
|
const u32 *table;
|
||||||
u32 max_size;
|
u32 max_size;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
mlx5e_port_get_speed_arr(mdev, &table, &max_size);
|
mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy);
|
||||||
for (i = 0; i < max_size; ++i) {
|
for (i = 0; i < max_size; ++i) {
|
||||||
if (table[i] == speed)
|
if (table[i] == speed)
|
||||||
link_modes |= MLX5E_PROT_MASK(i);
|
link_modes |= MLX5E_PROT_MASK(i);
|
||||||
|
|
|
@ -48,10 +48,12 @@ void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status,
|
||||||
u8 *an_disable_cap, u8 *an_disable_admin);
|
u8 *an_disable_cap, u8 *an_disable_admin);
|
||||||
int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
|
int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
|
||||||
u32 proto_admin, bool ext);
|
u32 proto_admin, bool ext);
|
||||||
u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper);
|
u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper,
|
||||||
|
bool force_legacy);
|
||||||
int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed);
|
int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed);
|
||||||
int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed);
|
int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed);
|
||||||
u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed);
|
u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed,
|
||||||
|
bool force_legacy);
|
||||||
|
|
||||||
int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out);
|
int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out);
|
||||||
int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in);
|
int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in);
|
||||||
|
|
|
@ -785,7 +785,7 @@ static void ptys2ethtool_supported_advertised_port(struct ethtool_link_ksettings
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_speed_duplex(struct net_device *netdev,
|
static void get_speed_duplex(struct net_device *netdev,
|
||||||
u32 eth_proto_oper,
|
u32 eth_proto_oper, bool force_legacy,
|
||||||
struct ethtool_link_ksettings *link_ksettings)
|
struct ethtool_link_ksettings *link_ksettings)
|
||||||
{
|
{
|
||||||
struct mlx5e_priv *priv = netdev_priv(netdev);
|
struct mlx5e_priv *priv = netdev_priv(netdev);
|
||||||
|
@ -795,7 +795,7 @@ static void get_speed_duplex(struct net_device *netdev,
|
||||||
if (!netif_carrier_ok(netdev))
|
if (!netif_carrier_ok(netdev))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper);
|
speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper, force_legacy);
|
||||||
if (!speed) {
|
if (!speed) {
|
||||||
speed = SPEED_UNKNOWN;
|
speed = SPEED_UNKNOWN;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -914,8 +914,8 @@ int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv,
|
||||||
/* Fields: eth_proto_admin and ext_eth_proto_admin are
|
/* Fields: eth_proto_admin and ext_eth_proto_admin are
|
||||||
* mutually exclusive. Hence try reading legacy advertising
|
* mutually exclusive. Hence try reading legacy advertising
|
||||||
* when extended advertising is zero.
|
* when extended advertising is zero.
|
||||||
* admin_ext indicates how eth_proto_admin should be
|
* admin_ext indicates which proto_admin (ext vs. legacy)
|
||||||
* interpreted
|
* should be read and interpreted
|
||||||
*/
|
*/
|
||||||
admin_ext = ext;
|
admin_ext = ext;
|
||||||
if (ext && !eth_proto_admin) {
|
if (ext && !eth_proto_admin) {
|
||||||
|
@ -924,7 +924,7 @@ int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv,
|
||||||
admin_ext = false;
|
admin_ext = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
eth_proto_oper = MLX5_GET_ETH_PROTO(ptys_reg, out, ext,
|
eth_proto_oper = MLX5_GET_ETH_PROTO(ptys_reg, out, admin_ext,
|
||||||
eth_proto_oper);
|
eth_proto_oper);
|
||||||
eth_proto_lp = MLX5_GET(ptys_reg, out, eth_proto_lp_advertise);
|
eth_proto_lp = MLX5_GET(ptys_reg, out, eth_proto_lp_advertise);
|
||||||
an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
|
an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
|
||||||
|
@ -939,7 +939,8 @@ int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv,
|
||||||
get_supported(mdev, eth_proto_cap, link_ksettings);
|
get_supported(mdev, eth_proto_cap, link_ksettings);
|
||||||
get_advertising(eth_proto_admin, tx_pause, rx_pause, link_ksettings,
|
get_advertising(eth_proto_admin, tx_pause, rx_pause, link_ksettings,
|
||||||
admin_ext);
|
admin_ext);
|
||||||
get_speed_duplex(priv->netdev, eth_proto_oper, link_ksettings);
|
get_speed_duplex(priv->netdev, eth_proto_oper, !admin_ext,
|
||||||
|
link_ksettings);
|
||||||
|
|
||||||
eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap;
|
eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap;
|
||||||
|
|
||||||
|
@ -1016,45 +1017,69 @@ static u32 mlx5e_ethtool2ptys_ext_adver_link(const unsigned long *link_modes)
|
||||||
return ptys_modes;
|
return ptys_modes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ext_link_mode_requested(const unsigned long *adver)
|
||||||
|
{
|
||||||
|
#define MLX5E_MIN_PTYS_EXT_LINK_MODE_BIT ETHTOOL_LINK_MODE_50000baseKR_Full_BIT
|
||||||
|
int size = __ETHTOOL_LINK_MODE_MASK_NBITS - MLX5E_MIN_PTYS_EXT_LINK_MODE_BIT;
|
||||||
|
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes);
|
||||||
|
|
||||||
|
bitmap_set(modes, MLX5E_MIN_PTYS_EXT_LINK_MODE_BIT, size);
|
||||||
|
return bitmap_intersects(modes, adver, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ext_speed_requested(u32 speed)
|
||||||
|
{
|
||||||
|
#define MLX5E_MAX_PTYS_LEGACY_SPEED 100000
|
||||||
|
return !!(speed > MLX5E_MAX_PTYS_LEGACY_SPEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ext_requested(u8 autoneg, const unsigned long *adver, u32 speed)
|
||||||
|
{
|
||||||
|
bool ext_link_mode = ext_link_mode_requested(adver);
|
||||||
|
bool ext_speed = ext_speed_requested(speed);
|
||||||
|
|
||||||
|
return autoneg == AUTONEG_ENABLE ? ext_link_mode : ext_speed;
|
||||||
|
}
|
||||||
|
|
||||||
int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
|
int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
|
||||||
const struct ethtool_link_ksettings *link_ksettings)
|
const struct ethtool_link_ksettings *link_ksettings)
|
||||||
{
|
{
|
||||||
struct mlx5_core_dev *mdev = priv->mdev;
|
struct mlx5_core_dev *mdev = priv->mdev;
|
||||||
struct mlx5e_port_eth_proto eproto;
|
struct mlx5e_port_eth_proto eproto;
|
||||||
|
const unsigned long *adver;
|
||||||
bool an_changes = false;
|
bool an_changes = false;
|
||||||
u8 an_disable_admin;
|
u8 an_disable_admin;
|
||||||
bool ext_supported;
|
bool ext_supported;
|
||||||
bool ext_requested;
|
|
||||||
u8 an_disable_cap;
|
u8 an_disable_cap;
|
||||||
bool an_disable;
|
bool an_disable;
|
||||||
u32 link_modes;
|
u32 link_modes;
|
||||||
u8 an_status;
|
u8 an_status;
|
||||||
|
u8 autoneg;
|
||||||
u32 speed;
|
u32 speed;
|
||||||
|
bool ext;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
u32 (*ethtool2ptys_adver_func)(const unsigned long *adver);
|
u32 (*ethtool2ptys_adver_func)(const unsigned long *adver);
|
||||||
|
|
||||||
#define MLX5E_PTYS_EXT ((1ULL << ETHTOOL_LINK_MODE_50000baseKR_Full_BIT) - 1)
|
adver = link_ksettings->link_modes.advertising;
|
||||||
|
autoneg = link_ksettings->base.autoneg;
|
||||||
ext_requested = !!(link_ksettings->link_modes.advertising[0] >
|
|
||||||
MLX5E_PTYS_EXT ||
|
|
||||||
link_ksettings->link_modes.advertising[1]);
|
|
||||||
ext_supported = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
|
|
||||||
ext_requested &= ext_supported;
|
|
||||||
|
|
||||||
speed = link_ksettings->base.speed;
|
speed = link_ksettings->base.speed;
|
||||||
ethtool2ptys_adver_func = ext_requested ?
|
|
||||||
mlx5e_ethtool2ptys_ext_adver_link :
|
ext = ext_requested(autoneg, adver, speed),
|
||||||
|
ext_supported = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
|
||||||
|
if (!ext_supported && ext)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
ethtool2ptys_adver_func = ext ? mlx5e_ethtool2ptys_ext_adver_link :
|
||||||
mlx5e_ethtool2ptys_adver_link;
|
mlx5e_ethtool2ptys_adver_link;
|
||||||
err = mlx5_port_query_eth_proto(mdev, 1, ext_requested, &eproto);
|
err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
|
||||||
if (err) {
|
if (err) {
|
||||||
netdev_err(priv->netdev, "%s: query port eth proto failed: %d\n",
|
netdev_err(priv->netdev, "%s: query port eth proto failed: %d\n",
|
||||||
__func__, err);
|
__func__, err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
link_modes = link_ksettings->base.autoneg == AUTONEG_ENABLE ?
|
link_modes = autoneg == AUTONEG_ENABLE ? ethtool2ptys_adver_func(adver) :
|
||||||
ethtool2ptys_adver_func(link_ksettings->link_modes.advertising) :
|
mlx5e_port_speed2linkmodes(mdev, speed, !ext);
|
||||||
mlx5e_port_speed2linkmodes(mdev, speed);
|
|
||||||
|
|
||||||
link_modes = link_modes & eproto.cap;
|
link_modes = link_modes & eproto.cap;
|
||||||
if (!link_modes) {
|
if (!link_modes) {
|
||||||
|
@ -1067,14 +1092,14 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
|
||||||
mlx5_port_query_eth_autoneg(mdev, &an_status, &an_disable_cap,
|
mlx5_port_query_eth_autoneg(mdev, &an_status, &an_disable_cap,
|
||||||
&an_disable_admin);
|
&an_disable_admin);
|
||||||
|
|
||||||
an_disable = link_ksettings->base.autoneg == AUTONEG_DISABLE;
|
an_disable = autoneg == AUTONEG_DISABLE;
|
||||||
an_changes = ((!an_disable && an_disable_admin) ||
|
an_changes = ((!an_disable && an_disable_admin) ||
|
||||||
(an_disable && !an_disable_admin));
|
(an_disable && !an_disable_admin));
|
||||||
|
|
||||||
if (!an_changes && link_modes == eproto.admin)
|
if (!an_changes && link_modes == eproto.admin)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
mlx5_port_set_eth_ptys(mdev, an_disable, link_modes, ext_requested);
|
mlx5_port_set_eth_ptys(mdev, an_disable, link_modes, ext);
|
||||||
mlx5_toggle_port_link(mdev);
|
mlx5_toggle_port_link(mdev);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
Loading…
Reference in a new issue