net: phy: phy_caps: Don't skip better duplex macth on non-exact match
authorMaxime Chevallier <maxime.chevallier@bootlin.com>
Fri, 6 Jun 2025 09:43:20 +0000 (11:43 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 12 Jun 2025 15:00:53 +0000 (08:00 -0700)
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: Jijie Shao <shaojijie@huawei.com>
Closes: https://lore.kernel.org/netdev/20250603102500.4ec743cf@fedora/T/#m22ed60ca635c67dc7d9cbb47e8995b2beb5c1576
Tested-by: Jijie Shao <shaojijie@huawei.com>
Reviewed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Fixes: fc81e257d19f ("net: phy: phy_caps: Allow looking-up link caps based on speed and duplex")
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20250606094321.483602-1-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/phy_caps.c

index 7033216897264e31d7bd3c85bdec066cc983497c..38417e2886118c9eafeed0b72a20f41eb9f6234f 100644 (file)
@@ -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);