Skip to content

Commit

Permalink
platform: ethtool: support new GLINKSETTINGS kernel API
Browse files Browse the repository at this point in the history
Use the new GLINKSETTINGS/SLINKSETTINGS ethtool API [1] when
available. Using the old API, we can only enable the first 31 modes in
the advertising bitmask, and so interfaces can't negotiate higher
modes.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/686
  • Loading branch information
bengal committed Apr 16, 2021
1 parent 9d3a54d commit 4a81fe1
Showing 1 changed file with 113 additions and 18 deletions.
131 changes: 113 additions & 18 deletions src/libnm-platform/nm-platform-utils.c
Expand Up @@ -277,6 +277,7 @@ static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string,
NM_UTILS_ENUM2STR(ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"),
NM_UTILS_ENUM2STR(ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES"),
NM_UTILS_ENUM2STR(ETHTOOL_GLINK, "ETHTOOL_GLINK"),
NM_UTILS_ENUM2STR(ETHTOOL_GLINKSETTINGS, "ETHTOOL_GLINKSETTINGS"),
NM_UTILS_ENUM2STR(ETHTOOL_GPERMADDR, "ETHTOOL_GPERMADDR"),
NM_UTILS_ENUM2STR(ETHTOOL_GRINGPARAM, "ETHTOOL_GRINGPARAM"),
NM_UTILS_ENUM2STR(ETHTOOL_GSET, "ETHTOOL_GSET"),
Expand All @@ -286,6 +287,7 @@ static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string,
NM_UTILS_ENUM2STR(ETHTOOL_GWOL, "ETHTOOL_GWOL"),
NM_UTILS_ENUM2STR(ETHTOOL_SCOALESCE, "ETHTOOL_SCOALESCE"),
NM_UTILS_ENUM2STR(ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"),
NM_UTILS_ENUM2STR(ETHTOOL_SLINKSETTINGS, "ETHTOOL_SLINKSETTINGS"),
NM_UTILS_ENUM2STR(ETHTOOL_SRINGPARAM, "ETHTOOL_SRINGPARAM"),
NM_UTILS_ENUM2STR(ETHTOOL_SSET, "ETHTOOL_SSET"),
NM_UTILS_ENUM2STR(ETHTOOL_SWOL, "ETHTOOL_SWOL"), );
Expand Down Expand Up @@ -1351,6 +1353,107 @@ get_baset_mode(guint32 speed, NMPlatformLinkDuplexType duplex)
}
}

static gboolean
platform_link_duplex_type_to_native(NMPlatformLinkDuplexType duplex_type, guint8 *out_native)
{
switch (duplex_type) {
case NM_PLATFORM_LINK_DUPLEX_HALF:
*out_native = DUPLEX_HALF;
return TRUE;
case NM_PLATFORM_LINK_DUPLEX_FULL:
*out_native = DUPLEX_FULL;
return TRUE;
case NM_PLATFORM_LINK_DUPLEX_UNKNOWN:
return FALSE;
default:
g_return_val_if_reached(FALSE);
}
}

static NMOptionBool
set_link_settings_new(SocketHandle * shandle,
gboolean autoneg,
guint32 speed,
NMPlatformLinkDuplexType duplex)
{
struct ethtool_link_settings edata0;
gs_free struct ethtool_link_settings *edata = NULL;
gsize edata_size;
guint nwords;

memset(&edata0, 0, sizeof(edata0));
edata0.cmd = ETHTOOL_GLINKSETTINGS;

/* perform the handshake to find the size of masks */
if (_ethtool_call_handle(shandle, &edata0, sizeof(edata0)) < 0
|| edata0.link_mode_masks_nwords >= 0) {
/* new API not supported */
return NM_OPTION_BOOL_DEFAULT;
}

nwords = -edata0.link_mode_masks_nwords;
edata_size = sizeof(*edata) + sizeof(guint32) * nwords * 3;
edata = g_malloc0(edata_size);
edata->cmd = ETHTOOL_GLINKSETTINGS;
edata->link_mode_masks_nwords = nwords;

/* retrieve first current settings */
if (_ethtool_call_handle(shandle, edata, edata_size) < 0)
return FALSE;

/* then change the needed ones */
edata->cmd = ETHTOOL_SLINKSETTINGS;
if (autoneg) {
edata->autoneg = AUTONEG_ENABLE;

/* copy @map_supported to @map_advertising and @map_lp_advertising */
memcpy(&edata->link_mode_masks[nwords],
&edata->link_mode_masks[0],
sizeof(guint32) * nwords);
memcpy(&edata->link_mode_masks[nwords * 2],
&edata->link_mode_masks[0],
sizeof(guint32) * nwords);

if (speed != 0) {
guint32 mode;

mode = get_baset_mode(speed, duplex);

if (mode == ADVERTISED_INVALID) {
nm_log_trace(LOGD_PLATFORM,
"ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised",
shandle->ifindex,
speed,
nm_platform_link_duplex_type_to_string(duplex));
return FALSE;
}

/* We only support BASE-T modes in the first word */
if (!(edata->link_mode_masks[0] & mode)) {
nm_log_trace(LOGD_PLATFORM,
"ethtool[%d]: device does not support %uBASE-T %s duplex mode",
shandle->ifindex,
speed,
nm_platform_link_duplex_type_to_string(duplex));
return FALSE;
}

edata->link_mode_masks[nwords] =
(edata->link_mode_masks[nwords] & ~BASET_ALL_MODES) | mode;
edata->link_mode_masks[nwords * 2] = edata->link_mode_masks[nwords];
}
} else {
edata->autoneg = AUTONEG_DISABLE;

if (speed)
edata->speed = speed;

platform_link_duplex_type_to_native(duplex, &edata->duplex);
}

return _ethtool_call_handle(shandle, edata, edata_size) >= 0;
}

gboolean
nmp_utils_ethtool_set_link_settings(int ifindex,
gboolean autoneg,
Expand All @@ -1361,32 +1464,35 @@ nmp_utils_ethtool_set_link_settings(int ifindex,
struct ethtool_cmd edata = {
.cmd = ETHTOOL_GSET,
};
NMOptionBool ret;

g_return_val_if_fail(ifindex > 0, FALSE);
g_return_val_if_fail((speed && duplex != NM_PLATFORM_LINK_DUPLEX_UNKNOWN)
|| (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN),
FALSE);

ret = set_link_settings_new(&shandle, autoneg, speed, duplex);
if (ret != NM_OPTION_BOOL_DEFAULT)
return ret;

/* new ETHTOOL_GLINKSETTINGS API not supported, fall back to GSET */

/* retrieve first current settings */
if (_ethtool_call_handle(&shandle, &edata, sizeof(edata)) < 0)
return FALSE;

/* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf
*/

/* then change the needed ones */
edata.cmd = ETHTOOL_SSET;
if (autoneg) {
edata.autoneg = AUTONEG_ENABLE;
if (!speed)
if (speed == 0)
edata.advertising = edata.supported;
else {
guint32 mode;

mode = get_baset_mode(speed, duplex);

if (!mode) {
if (mode == ADVERTISED_INVALID) {
nm_log_trace(LOGD_PLATFORM,
"ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised",
ifindex,
Expand All @@ -1410,18 +1516,7 @@ nmp_utils_ethtool_set_link_settings(int ifindex,
if (speed)
ethtool_cmd_speed_set(&edata, speed);

switch (duplex) {
case NM_PLATFORM_LINK_DUPLEX_HALF:
edata.duplex = DUPLEX_HALF;
break;
case NM_PLATFORM_LINK_DUPLEX_FULL:
edata.duplex = DUPLEX_FULL;
break;
case NM_PLATFORM_LINK_DUPLEX_UNKNOWN:
break;
default:
g_return_val_if_reached(FALSE);
}
platform_link_duplex_type_to_native(duplex, &edata.duplex);
}

return _ethtool_call_handle(&shandle, &edata, sizeof(edata)) >= 0;
Expand Down

0 comments on commit 4a81fe1

Please sign in to comment.