Commit d4e6cb32 authored by Maxime Chevallier's avatar Maxime Chevallier Committed by Jakub Kicinski
Browse files

net: phy: phy_caps: Don't skip better duplex macth on non-exact match



When performing a non-exact phy_caps lookup, we are looking for a
supported mode that matches as closely as possible the passed speed/duplex.

Blamed patch broke that logic by returning a match too early in case
the caller asks for half-duplex, as a full-duplex linkmode may match
first, and returned as a non-exact match without even trying to mach on
half-duplex modes.

Reported-by: default avatarJijie Shao <shaojijie@huawei.com>
Closes: https://lore.kernel.org/netdev/20250603102500.4ec743cf@fedora/T/#m22ed60ca635c67dc7d9cbb47e8995b2beb5c1576


Tested-by: default avatarJijie Shao <shaojijie@huawei.com>
Reviewed-by: default avatarLarysa Zaremba <larysa.zaremba@intel.com>
Fixes: fc81e257 ("net: phy: phy_caps: Allow looking-up link caps based on speed and duplex")
Signed-off-by: default avatarMaxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20250606094321.483602-1-maxime.chevallier@bootlin.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 27cea0e4
Loading
Loading
Loading
Loading
+12 −6
Original line number Diff line number Diff line
@@ -188,6 +188,9 @@ phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only)
 * When @exact is not set, we return either an exact match, or matching capabilities
 * at lower speed, or the lowest matching speed, or NULL.
 *
 * Non-exact matches will try to return an exact speed and duplex match, but may
 * return matching capabilities with same speed but a different duplex.
 *
 * Returns: a matched link_capabilities according to the above process, NULL
 *	    otherwise.
 */
@@ -195,7 +198,7 @@ const struct link_capabilities *
phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported,
		bool exact)
{
	const struct link_capabilities *lcap, *last = NULL;
	const struct link_capabilities *lcap, *match = NULL, *last = NULL;

	for_each_link_caps_desc_speed(lcap) {
		if (linkmode_intersects(lcap->linkmodes, supported)) {
@@ -204,16 +207,19 @@ phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported,
			if (lcap->speed == speed && lcap->duplex == duplex) {
				return lcap;
			} else if (!exact) {
				if (lcap->speed <= speed)
					return lcap;
				if (!match && lcap->speed <= speed)
					match = lcap;

				if (lcap->speed < speed)
					break;
			}
		}
	}

	if (!exact)
		return last;
	if (!match && !exact)
		match = last;

	return NULL;
	return match;
}
EXPORT_SYMBOL_GPL(phy_caps_lookup);