forked from mirrors/linux
		
	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, | ||||
| 				     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) : | ||||
| 		      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); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 	const u32 *table; | ||||
|  | @ -160,7 +162,7 @@ u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper) | |||
| 	u32 max_size; | ||||
| 	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); | ||||
| 	if (i < max_size) | ||||
| 		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) | ||||
| { | ||||
| 	struct mlx5e_port_eth_proto eproto; | ||||
| 	bool force_legacy = false; | ||||
| 	bool ext; | ||||
| 	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); | ||||
| 	if (err) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	*speed = mlx5e_port_ptys2speed(mdev, eproto.oper); | ||||
| 	if (ext && !eproto.admin) { | ||||
| 		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)) | ||||
| 		err = -EINVAL; | ||||
| 
 | ||||
|  | @ -201,7 +209,7 @@ int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) | |||
| 	if (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) | ||||
| 		if (eproto.cap & MLX5E_PROT_MASK(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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 	const u32 *table; | ||||
| 	u32 max_size; | ||||
| 	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) { | ||||
| 		if (table[i] == speed) | ||||
| 			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); | ||||
| int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable, | ||||
| 			   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_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_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, | ||||
| 			     u32 eth_proto_oper, | ||||
| 			     u32 eth_proto_oper, bool force_legacy, | ||||
| 			     struct ethtool_link_ksettings *link_ksettings) | ||||
| { | ||||
| 	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)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper); | ||||
| 	speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper, force_legacy); | ||||
| 	if (!speed) { | ||||
| 		speed = SPEED_UNKNOWN; | ||||
| 		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
 | ||||
| 	 * mutually exclusive. Hence try reading legacy advertising | ||||
| 	 * when extended advertising is zero. | ||||
| 	 * admin_ext indicates how eth_proto_admin should be | ||||
| 	 * interpreted | ||||
| 	 * admin_ext indicates which proto_admin (ext vs. legacy) | ||||
| 	 * should be read and interpreted | ||||
| 	 */ | ||||
| 	admin_ext = ext; | ||||
| 	if (ext && !eth_proto_admin) { | ||||
|  | @ -924,7 +924,7 @@ int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv, | |||
| 		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_lp	    = MLX5_GET(ptys_reg, out, eth_proto_lp_advertise); | ||||
| 	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_advertising(eth_proto_admin, tx_pause, rx_pause, link_ksettings, | ||||
| 			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; | ||||
| 
 | ||||
|  | @ -1016,45 +1017,69 @@ static u32 mlx5e_ethtool2ptys_ext_adver_link(const unsigned long *link_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, | ||||
| 				     const struct ethtool_link_ksettings *link_ksettings) | ||||
| { | ||||
| 	struct mlx5_core_dev *mdev = priv->mdev; | ||||
| 	struct mlx5e_port_eth_proto eproto; | ||||
| 	const unsigned long *adver; | ||||
| 	bool an_changes = false; | ||||
| 	u8 an_disable_admin; | ||||
| 	bool ext_supported; | ||||
| 	bool ext_requested; | ||||
| 	u8 an_disable_cap; | ||||
| 	bool an_disable; | ||||
| 	u32 link_modes; | ||||
| 	u8 an_status; | ||||
| 	u8 autoneg; | ||||
| 	u32 speed; | ||||
| 	bool ext; | ||||
| 	int err; | ||||
| 
 | ||||
| 	u32 (*ethtool2ptys_adver_func)(const unsigned long *adver); | ||||
| 
 | ||||
| #define MLX5E_PTYS_EXT ((1ULL << ETHTOOL_LINK_MODE_50000baseKR_Full_BIT) - 1) | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 	adver = link_ksettings->link_modes.advertising; | ||||
| 	autoneg = link_ksettings->base.autoneg; | ||||
| 	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; | ||||
| 	err = mlx5_port_query_eth_proto(mdev, 1, ext_requested, &eproto); | ||||
| 	err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto); | ||||
| 	if (err) { | ||||
| 		netdev_err(priv->netdev, "%s: query port eth proto failed: %d\n", | ||||
| 			   __func__, err); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	link_modes = link_ksettings->base.autoneg == AUTONEG_ENABLE ? | ||||
| 		ethtool2ptys_adver_func(link_ksettings->link_modes.advertising) : | ||||
| 		mlx5e_port_speed2linkmodes(mdev, speed); | ||||
| 	link_modes = autoneg == AUTONEG_ENABLE ? ethtool2ptys_adver_func(adver) : | ||||
| 		mlx5e_port_speed2linkmodes(mdev, speed, !ext); | ||||
| 
 | ||||
| 	link_modes = link_modes & eproto.cap; | ||||
| 	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, | ||||
| 				    &an_disable_admin); | ||||
| 
 | ||||
| 	an_disable = link_ksettings->base.autoneg == AUTONEG_DISABLE; | ||||
| 	an_disable = autoneg == AUTONEG_DISABLE; | ||||
| 	an_changes = ((!an_disable && an_disable_admin) || | ||||
| 		      (an_disable && !an_disable_admin)); | ||||
| 
 | ||||
| 	if (!an_changes && link_modes == eproto.admin) | ||||
| 		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); | ||||
| 
 | ||||
| out: | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Aya Levin
						Aya Levin