summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2021-04-02 16:50:34 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2021-04-16 18:47:17 +0200
commit4a81fe13ae18cb6d157d7cb3eec300f3061263fe (patch)
tree0ac2ef2dfae7f0753599e8a1392cc4f0615c6913
parent9d3a54d26988c9675ad2ec44c8f4cace3fab9f51 (diff)
downloadNetworkManager-bg/ethtool-glinksettings.tar.gz
platform: ethtool: support new GLINKSETTINGS kernel APIbg/ethtool-glinksettings
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
-rw-r--r--src/libnm-platform/nm-platform-utils.c131
1 files changed, 113 insertions, 18 deletions
diff --git a/src/libnm-platform/nm-platform-utils.c b/src/libnm-platform/nm-platform-utils.c
index a46deb5923..1a95adb6a2 100644
--- a/src/libnm-platform/nm-platform-utils.c
+++ b/src/libnm-platform/nm-platform-utils.c
@@ -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"),
@@ -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"), );
@@ -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,
@@ -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,
@@ -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;