Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: add linkmode helper for setting flow control advertisement

Add a linkmode helper to set the flow control advertisement in an
ethtool linkmode mask according to the tx/rx capabilities. This
implementation is moved from phylib, and documented with an
analysis of its shortcomings.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Russell King and committed by
David S. Miller
45c767fa a87ae8a9

+54 -16
+51
drivers/net/phy/linkmode.c
··· 42 42 } 43 43 } 44 44 EXPORT_SYMBOL_GPL(linkmode_resolve_pause); 45 + 46 + /** 47 + * linkmode_set_pause - set the pause mode advertisement 48 + * @advertisement: advertisement in ethtool format 49 + * @tx: boolean from ethtool struct ethtool_pauseparam tx_pause member 50 + * @rx: boolean from ethtool struct ethtool_pauseparam rx_pause member 51 + * 52 + * Configure the advertised Pause and Asym_Pause bits according to the 53 + * capabilities of provided in @tx and @rx. 54 + * 55 + * We convert as follows: 56 + * tx rx Pause AsymDir 57 + * 0 0 0 0 58 + * 0 1 1 1 59 + * 1 0 0 1 60 + * 1 1 1 0 61 + * 62 + * Note: this translation from ethtool tx/rx notation to the advertisement 63 + * is actually very problematical. Here are some examples: 64 + * 65 + * For tx=0 rx=1, meaning transmit is unsupported, receive is supported: 66 + * 67 + * Local device Link partner 68 + * Pause AsymDir Pause AsymDir Result 69 + * 1 1 1 0 TX + RX - but we have no TX support. 70 + * 1 1 0 1 Only this gives RX only 71 + * 72 + * For tx=1 rx=1, meaning we have the capability to transmit and receive 73 + * pause frames: 74 + * 75 + * Local device Link partner 76 + * Pause AsymDir Pause AsymDir Result 77 + * 1 0 0 1 Disabled - but since we do support tx and rx, 78 + * this should resolve to RX only. 79 + * 80 + * Hence, asking for: 81 + * rx=1 tx=0 gives Pause+AsymDir advertisement, but we may end up 82 + * resolving to tx+rx pause or only rx pause depending on 83 + * the partners advertisement. 84 + * rx=0 tx=1 gives AsymDir only, which will only give tx pause if 85 + * the partners advertisement allows it. 86 + * rx=1 tx=1 gives Pause only, which will only allow tx+rx pause 87 + * if the other end also advertises Pause. 88 + */ 89 + void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx) 90 + { 91 + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertisement, rx); 92 + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertisement, 93 + rx ^ tx); 94 + } 95 + EXPORT_SYMBOL_GPL(linkmode_set_pause);
+1 -16
drivers/net/phy/phy_device.c
··· 2361 2361 __ETHTOOL_DECLARE_LINK_MODE_MASK(oldadv); 2362 2362 2363 2363 linkmode_copy(oldadv, phydev->advertising); 2364 - 2365 - linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, 2366 - phydev->advertising); 2367 - linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 2368 - phydev->advertising); 2369 - 2370 - if (rx) { 2371 - linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, 2372 - phydev->advertising); 2373 - linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 2374 - phydev->advertising); 2375 - } 2376 - 2377 - if (tx) 2378 - linkmode_change_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 2379 - phydev->advertising); 2364 + linkmode_set_pause(phydev->advertising, tx, rx); 2380 2365 2381 2366 if (!linkmode_equal(oldadv, phydev->advertising) && 2382 2367 phydev->autoneg)
+2
include/linux/linkmode.h
··· 92 92 const unsigned long *partner_adv, 93 93 bool *tx_pause, bool *rx_pause); 94 94 95 + void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx); 96 + 95 97 #endif /* __LINKMODE_H */