406-net-phy-improve-phylib-correctness-for-non-autoneg-s.patch 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. From: Russell King <[email protected]>
  2. Date: Thu, 5 Jan 2017 16:32:14 +0000
  3. Subject: [PATCH] net: phy: improve phylib correctness for non-autoneg
  4. settings
  5. phylib has some undesirable behaviour when forcing a link mode through
  6. ethtool. phylib uses this code:
  7. idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
  8. features);
  9. to find an index in the settings table. phy_find_setting() starts at
  10. index 0, and scans upwards looking for an exact speed and duplex match.
  11. When it doesn't find it, it returns MAX_NUM_SETTINGS - 1, which is
  12. 10baseT-Half duplex.
  13. phy_find_valid() then scans from the point (and effectively only checks
  14. one entry) before bailing out, returning MAX_NUM_SETTINGS - 1.
  15. phy_sanitize_settings() then sets ->speed to SPEED_10 and ->duplex to
  16. DUPLEX_HALF whether or not 10baseT-Half is supported or not. This goes
  17. against all the comments against these functions, and 10baseT-Half may
  18. not even be supported by the hardware.
  19. Rework these functions, introducing a new method of scanning the table.
  20. There are two modes of lookup that phylib wants: exact, and inexact.
  21. - in exact mode, we return either an exact match or failure
  22. - in inexact mode, we return an exact match if it exists, a match at
  23. the highest speed that is not greater than the requested speed
  24. (ignoring duplex), or failing that, the lowest supported speed, or
  25. failure.
  26. The biggest difference is that we always check whether the entry is
  27. supported before further consideration, so all unsupported entries are
  28. not considered as candidates.
  29. This results in arguably saner behaviour, better matches the comments,
  30. and is probably what users would expect.
  31. This becomes important as ethernet speeds increase, PHYs exist which do
  32. not support the 10Mbit speeds, and half-duplex is likely to become
  33. obsolete - it's already not even an option on 10Gbit and faster links.
  34. Signed-off-by: Russell King <[email protected]>
  35. ---
  36. --- a/drivers/net/phy/phy.c
  37. +++ b/drivers/net/phy/phy.c
  38. @@ -160,7 +160,9 @@ struct phy_setting {
  39. u32 setting;
  40. };
  41. -/* A mapping of all SUPPORTED settings to speed/duplex */
  42. +/* A mapping of all SUPPORTED settings to speed/duplex. This table
  43. + * must be grouped by speed and sorted in descending match priority
  44. + * - iow, descending speed. */
  45. static const struct phy_setting settings[] = {
  46. {
  47. .speed = SPEED_10000,
  48. @@ -219,45 +221,70 @@ static const struct phy_setting settings
  49. },
  50. };
  51. -#define MAX_NUM_SETTINGS ARRAY_SIZE(settings)
  52. -
  53. /**
  54. - * phy_find_setting - find a PHY settings array entry that matches speed & duplex
  55. + * phy_lookup_setting - lookup a PHY setting
  56. * @speed: speed to match
  57. * @duplex: duplex to match
  58. + * @feature: allowed link modes
  59. + * @exact: an exact match is required
  60. + *
  61. + * Search the settings array for a setting that matches the speed and
  62. + * duplex, and which is supported.
  63. + *
  64. + * If @exact is unset, either an exact match or %NULL for no match will
  65. + * be returned.
  66. *
  67. - * Description: Searches the settings array for the setting which
  68. - * matches the desired speed and duplex, and returns the index
  69. - * of that setting. Returns the index of the last setting if
  70. - * none of the others match.
  71. + * If @exact is set, an exact match, the fastest supported setting at
  72. + * or below the specified speed, the slowest supported setting, or if
  73. + * they all fail, %NULL will be returned.
  74. */
  75. -static inline unsigned int phy_find_setting(int speed, int duplex)
  76. +static const struct phy_setting *
  77. +phy_lookup_setting(int speed, int duplex, u32 features, bool exact)
  78. {
  79. - unsigned int idx = 0;
  80. + const struct phy_setting *p, *match = NULL, *last = NULL;
  81. + int i;
  82. - while (idx < ARRAY_SIZE(settings) &&
  83. - (settings[idx].speed != speed || settings[idx].duplex != duplex))
  84. - idx++;
  85. + for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
  86. + if (p->setting & features) {
  87. + last = p;
  88. + if (p->speed == speed && p->duplex == duplex) {
  89. + /* Exact match for speed and duplex */
  90. + match = p;
  91. + break;
  92. + } else if (!exact) {
  93. + if (!match && p->speed <= speed)
  94. + /* Candidate */
  95. + match = p;
  96. +
  97. + if (p->speed < speed)
  98. + break;
  99. + }
  100. + }
  101. + }
  102. - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
  103. + if (!match && !exact)
  104. + match = last;
  105. +
  106. + return match;
  107. }
  108. /**
  109. - * phy_find_valid - find a PHY setting that matches the requested features mask
  110. - * @idx: The first index in settings[] to search
  111. - * @features: A mask of the valid settings
  112. + * phy_find_valid - find a PHY setting that matches the requested parameters
  113. + * @speed: desired speed
  114. + * @duplex: desired duplex
  115. + * @supported: mask of supported link modes
  116. *
  117. - * Description: Returns the index of the first valid setting less
  118. - * than or equal to the one pointed to by idx, as determined by
  119. - * the mask in features. Returns the index of the last setting
  120. - * if nothing else matches.
  121. + * Locate a supported phy setting that is, in priority order:
  122. + * - an exact match for the specified speed and duplex mode
  123. + * - a match for the specified speed, or slower speed
  124. + * - the slowest supported speed
  125. + * Returns the matched phy_setting entry, or %NULL if no supported phy
  126. + * settings were found.
  127. */
  128. -static inline unsigned int phy_find_valid(unsigned int idx, u32 features)
  129. +static const struct phy_setting *
  130. +phy_find_valid(int speed, int duplex, u32 supported)
  131. {
  132. - while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features))
  133. - idx++;
  134. -
  135. - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
  136. + return phy_lookup_setting(speed, duplex, supported, false);
  137. }
  138. /**
  139. @@ -271,12 +298,7 @@ static inline unsigned int phy_find_vali
  140. */
  141. static inline bool phy_check_valid(int speed, int duplex, u32 features)
  142. {
  143. - unsigned int idx;
  144. -
  145. - idx = phy_find_valid(phy_find_setting(speed, duplex), features);
  146. -
  147. - return settings[idx].speed == speed && settings[idx].duplex == duplex &&
  148. - (settings[idx].setting & features);
  149. + return !!phy_lookup_setting(speed, duplex, features, true);
  150. }
  151. /**
  152. @@ -289,18 +311,22 @@ static inline bool phy_check_valid(int s
  153. */
  154. static void phy_sanitize_settings(struct phy_device *phydev)
  155. {
  156. + const struct phy_setting *setting;
  157. u32 features = phydev->supported;
  158. - unsigned int idx;
  159. /* Sanitize settings based on PHY capabilities */
  160. if ((features & SUPPORTED_Autoneg) == 0)
  161. phydev->autoneg = AUTONEG_DISABLE;
  162. - idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
  163. - features);
  164. -
  165. - phydev->speed = settings[idx].speed;
  166. - phydev->duplex = settings[idx].duplex;
  167. + setting = phy_find_valid(phydev->speed, phydev->duplex, features);
  168. + if (setting) {
  169. + phydev->speed = setting->speed;
  170. + phydev->duplex = setting->duplex;
  171. + } else {
  172. + /* We failed to find anything (no supported speeds?) */
  173. + phydev->speed = SPEED_UNKNOWN;
  174. + phydev->duplex = DUPLEX_UNKNOWN;
  175. + }
  176. }
  177. /**