diff --git a/NEWS b/NEWS
index 427a1afe9..fc0a1f108 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,7 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
ipv4.routed-dns and ipv6.routed-dns properties; when enabled, each
name server is reached only via the device that specifies it.
* Support OCI in nm-cloud-setup
+* Added support for ethtool FEC mode
=============================================
NetworkManager-1.50
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index e8f7156bf..96572f970 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -315,6 +315,7 @@ typedef struct {
NMEthtoolPauseState *pause;
NMEthtoolChannelsState *channels;
NMEthtoolEEEState *eee;
+ uint32_t fec_mode;
} EthtoolState;
typedef enum {
@@ -2681,6 +2682,19 @@ _ethtool_features_reset(NMDevice *self, NMPlatform *platform, EthtoolState *etht
_LOGD(LOGD_DEVICE, "ethtool: offload features successfully reset");
}
+static void
+_ethtool_fec_reset(NMDevice *self, NMPlatform *platform, EthtoolState *ethtool_state)
+{
+ if (ethtool_state->fec_mode) {
+ if (!nm_platform_ethtool_set_fec_mode(platform,
+ ethtool_state->ifindex,
+ ethtool_state->fec_mode))
+ _LOGW(LOGD_DEVICE, "ethtool: failure resetting FEC");
+ else
+ _LOGD(LOGD_DEVICE, "ethtool: FEC successfully reset");
+ }
+}
+
static void
_ethtool_features_set(NMDevice *self,
NMPlatform *platform,
@@ -2712,6 +2726,55 @@ _ethtool_features_set(NMDevice *self,
ethtool_state->features = g_steal_pointer(&features);
}
+static void
+_ethtool_fec_set(NMDevice *self,
+ NMPlatform *platform,
+ EthtoolState *ethtool_state,
+ NMSettingEthtool *s_ethtool)
+{
+ uint32_t old_fec_mode;
+ uint32_t fec_mode = NM_SETTING_ETHTOOL_FEC_MODE_NONE;
+ GHashTable *hash;
+ GHashTableIter iter;
+ const char *name;
+ GVariant *variant;
+
+ nm_assert(NM_IS_DEVICE(self));
+ nm_assert(NM_IS_PLATFORM(platform));
+ nm_assert(NM_IS_SETTING_ETHTOOL(s_ethtool));
+ nm_assert(ethtool_state);
+ nm_assert(!ethtool_state->fec_mode);
+
+ hash = _nm_setting_option_hash(NM_SETTING(s_ethtool), FALSE);
+ if (!hash)
+ return;
+
+ g_hash_table_iter_init(&iter, hash);
+ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &variant)) {
+ NMEthtoolID ethtool_id = nm_ethtool_id_get_by_name(name);
+
+ if (!nm_ethtool_id_is_fec(ethtool_id))
+ continue;
+
+ nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32));
+ fec_mode = g_variant_get_uint32(variant);
+ }
+
+ nm_platform_ethtool_get_fec_mode(platform, ethtool_state->ifindex, &old_fec_mode);
+
+ /* The NM_SETTING_ETHTOOL_FEC_MODE_NONE is query only value, hence do nothing. */
+ if (!fec_mode || fec_mode == NM_SETTING_ETHTOOL_FEC_MODE_NONE) {
+ return;
+ }
+
+ if (!nm_platform_ethtool_set_fec_mode(platform, ethtool_state->ifindex, fec_mode))
+ _LOGW(LOGD_DEVICE, "ethtool: failure setting FEC %d", fec_mode);
+ else {
+ _LOGD(LOGD_DEVICE, "ethtool: FEC %d successfully set", fec_mode);
+ ethtool_state->fec_mode = old_fec_mode;
+ }
+}
+
static void
_ethtool_coalesce_reset(NMDevice *self, NMPlatform *platform, EthtoolState *ethtool_state)
{
@@ -3193,6 +3256,7 @@ _ethtool_state_reset(NMDevice *self)
_ethtool_pause_reset(self, platform, ethtool_state);
_ethtool_channels_reset(self, platform, ethtool_state);
_ethtool_eee_reset(self, platform, ethtool_state);
+ _ethtool_fec_reset(self, platform, ethtool_state);
}
static void
@@ -3229,9 +3293,11 @@ _ethtool_state_set(NMDevice *self)
_ethtool_pause_set(self, platform, ethtool_state, s_ethtool);
_ethtool_channels_set(self, platform, ethtool_state, s_ethtool);
_ethtool_eee_set(self, platform, ethtool_state, s_ethtool);
+ _ethtool_fec_set(self, platform, ethtool_state, s_ethtool);
if (ethtool_state->features || ethtool_state->coalesce || ethtool_state->ring
- || ethtool_state->pause || ethtool_state->channels || ethtool_state->eee)
+ || ethtool_state->pause || ethtool_state->channels || ethtool_state->eee
+ || ethtool_state->fec_mode != 0)
priv->ethtool_state = g_steal_pointer(ðtool_state);
}
@@ -8625,7 +8691,7 @@ port_state_changed(NMDevice *port,
/**
* nm_device_controller_add_port:
* @self: the controller device
- * @port: the port device to attach as port
+ * @port: the port device to attach as port
* @configure: pass %TRUE if the port should be configured by the controller, or
* %FALSE if it is already configured outside NetworkManager
*
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 93b24230b..6e0411c65 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -1454,6 +1454,15 @@ write_ethtool_setting(NMConnection *connection, shvarFile *ifcfg, GError **error
return FALSE;
}
}
+ if (ethtool_id == NM_ETHTOOL_ID_FEC_MODE) {
+ if (nm_setting_option_get_uint32(NM_SETTING(s_ethtool),
+ nm_ethtool_data[ethtool_id]->optname,
+ &u32)) {
+ nm_sprintf_buf(prop_name, "ethtool.%s", nm_ethtool_data[ethtool_id]->optname);
+ set_error_unsupported(error, connection, prop_name, FALSE);
+ return FALSE;
+ }
+ }
if (!any_option) {
/* Write an empty dummy "-A" option without arguments. This is to
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 334662c36..fc7c7e64a 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -3623,7 +3623,8 @@ test_roundtrip_ethtool(void)
optname = nm_ethtool_data[ethtool_id]->optname;
vtype = nm_ethtool_id_get_variant_type(ethtool_id);
- if (nm_ethtool_optname_is_channels(optname) || nm_ethtool_optname_is_eee(optname)) {
+ if (nm_ethtool_optname_is_channels(optname) || nm_ethtool_optname_is_eee(optname)
+ || nm_ethtool_optname_is_fec(optname)) {
/* Not supported */
continue;
}
diff --git a/src/libnm-base/nm-base.h b/src/libnm-base/nm-base.h
index 4b1bff547..048c02416 100644
--- a/src/libnm-base/nm-base.h
+++ b/src/libnm-base/nm-base.h
@@ -135,7 +135,11 @@ typedef enum {
NM_ETHTOOL_ID_CHANNELS_COMBINED,
_NM_ETHTOOL_ID_CHANNELS_LAST = NM_ETHTOOL_ID_CHANNELS_COMBINED,
- _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_CHANNELS_LAST,
+ _NM_ETHTOOL_ID_FEC_FIRST = _NM_ETHTOOL_ID_CHANNELS_LAST + 1,
+ NM_ETHTOOL_ID_FEC_MODE = _NM_ETHTOOL_ID_FEC_FIRST,
+ _NM_ETHTOOL_ID_FEC_LAST = NM_ETHTOOL_ID_FEC_MODE,
+
+ _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_FEC_LAST,
_NM_ETHTOOL_ID_COALESCE_NUM =
(_NM_ETHTOOL_ID_COALESCE_LAST - _NM_ETHTOOL_ID_COALESCE_FIRST + 1),
@@ -158,6 +162,7 @@ typedef enum {
NM_ETHTOOL_TYPE_PAUSE,
NM_ETHTOOL_TYPE_CHANNELS,
NM_ETHTOOL_TYPE_EEE,
+ NM_ETHTOOL_TYPE_FEC,
} NMEthtoolType;
/****************************************************************************/
@@ -198,6 +203,12 @@ nm_ethtool_id_is_eee(NMEthtoolID id)
return id >= _NM_ETHTOOL_ID_EEE_FIRST && id <= _NM_ETHTOOL_ID_EEE_LAST;
}
+static inline gboolean
+nm_ethtool_id_is_fec(NMEthtoolID id)
+{
+ return id >= _NM_ETHTOOL_ID_FEC_FIRST && id <= _NM_ETHTOOL_ID_FEC_LAST;
+}
+
/*****************************************************************************/
typedef enum {
diff --git a/src/libnm-base/nm-ethtool-base.c b/src/libnm-base/nm-ethtool-base.c
index 8e7f7814a..6f2ab2111 100644
--- a/src/libnm-base/nm-ethtool-base.c
+++ b/src/libnm-base/nm-ethtool-base.c
@@ -111,6 +111,7 @@ const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = {
ETHT_DATA(CHANNELS_TX),
ETHT_DATA(CHANNELS_OTHER),
ETHT_DATA(CHANNELS_COMBINED),
+ ETHT_DATA(FEC_MODE),
[_NM_ETHTOOL_ID_NUM] = NULL,
};
@@ -201,6 +202,7 @@ static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = {
NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION,
NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT,
NM_ETHTOOL_ID_FEATURE_TXVLAN,
+ NM_ETHTOOL_ID_FEC_MODE,
NM_ETHTOOL_ID_PAUSE_AUTONEG,
NM_ETHTOOL_ID_PAUSE_RX,
NM_ETHTOOL_ID_PAUSE_TX,
@@ -305,6 +307,8 @@ nm_ethtool_id_to_type(NMEthtoolID id)
return NM_ETHTOOL_TYPE_CHANNELS;
if (nm_ethtool_id_is_eee(id))
return NM_ETHTOOL_TYPE_EEE;
+ if (nm_ethtool_id_is_fec(id))
+ return NM_ETHTOOL_TYPE_FEC;
return NM_ETHTOOL_TYPE_UNKNOWN;
}
@@ -319,6 +323,7 @@ nm_ethtool_id_get_variant_type(NMEthtoolID ethtool_id)
return G_VARIANT_TYPE_BOOLEAN;
case NM_ETHTOOL_TYPE_CHANNELS:
case NM_ETHTOOL_TYPE_COALESCE:
+ case NM_ETHTOOL_TYPE_FEC:
case NM_ETHTOOL_TYPE_RING:
return G_VARIANT_TYPE_UINT32;
case NM_ETHTOOL_TYPE_UNKNOWN:
diff --git a/src/libnm-base/nm-ethtool-utils-base.h b/src/libnm-base/nm-ethtool-utils-base.h
index 75fb63c57..435e88d23 100644
--- a/src/libnm-base/nm-ethtool-utils-base.h
+++ b/src/libnm-base/nm-ethtool-utils-base.h
@@ -109,6 +109,8 @@ G_BEGIN_DECLS
#define NM_ETHTOOL_OPTNAME_CHANNELS_OTHER "channels-other"
#define NM_ETHTOOL_OPTNAME_CHANNELS_COMBINED "channels-combined"
+#define NM_ETHTOOL_OPTNAME_FEC_MODE "fec-mode"
+
#define NM_ETHTOOL_OPTNAME_EEE_ENABLED "eee-enabled"
/*****************************************************************************/
diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver
index 67e970e54..3b658b939 100644
--- a/src/libnm-client-impl/libnm.ver
+++ b/src/libnm-client-impl/libnm.ver
@@ -2043,4 +2043,6 @@ global:
nm_setting_connection_clear_ip_ping_addresses;
nm_setting_connection_remove_ip_ping_address_by_value;
nm_setting_connection_get_ip_ping_addresses_require_all;
+ nm_setting_ethtool_fec_mode_get_type;
+ nm_ethtool_optname_is_fec;
} libnm_1_50_0;
diff --git a/src/libnm-client-public/nm-ethtool-utils.h b/src/libnm-client-public/nm-ethtool-utils.h
index 75fb63c57..435e88d23 100644
--- a/src/libnm-client-public/nm-ethtool-utils.h
+++ b/src/libnm-client-public/nm-ethtool-utils.h
@@ -109,6 +109,8 @@ G_BEGIN_DECLS
#define NM_ETHTOOL_OPTNAME_CHANNELS_OTHER "channels-other"
#define NM_ETHTOOL_OPTNAME_CHANNELS_COMBINED "channels-combined"
+#define NM_ETHTOOL_OPTNAME_FEC_MODE "fec-mode"
+
#define NM_ETHTOOL_OPTNAME_EEE_ENABLED "eee-enabled"
/*****************************************************************************/
diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
index 402e5a82b..cdc190d12 100644
--- a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
+++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
@@ -1361,6 +1361,10 @@
dbus-type="u"
is-setting-option="1"
/>
+
= (_NM_SETTING_ETHTOOL_FEC_MODE_LAST << 1)) {
+ gs_free const char *cur_fec_mode = NULL;
+ gs_free const char **valid_all = NULL;
+ gs_free const char *valid_str = NULL;
+
+ cur_fec_mode = _nm_utils_enum_to_str_full(nm_setting_ethtool_fec_mode_get_type(),
+ (int) (fec_mode & INT_MAX),
+ ", ",
+ NULL);
+ valid_all = nm_utils_enum_get_values(nm_setting_ethtool_fec_mode_get_type(), 0, G_MAXUINT);
+ valid_str = g_strjoinv(",", (char **) valid_all);
+
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not valid FEC modes, valid modes are combinations of %s"),
+ cur_fec_mode,
+ valid_str);
+ g_prefix_error(error,
+ "%s.%s: ",
+ NM_SETTING_ETHTOOL_SETTING_NAME,
+ NM_ETHTOOL_OPTNAME_FEC_MODE);
+ return FALSE;
+ }
+
return TRUE;
}
diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c
index 55acbe94c..2064162a9 100644
--- a/src/libnm-core-impl/tests/test-setting.c
+++ b/src/libnm-core-impl/tests/test-setting.c
@@ -2383,6 +2383,85 @@ test_ethtool_eee(void)
}
/*****************************************************************************/
+static void
+test_ethtool_fec(void)
+{
+ gs_unref_object NMConnection *con = NULL;
+ gs_unref_object NMConnection *con2 = NULL;
+ gs_unref_object NMConnection *con3 = NULL;
+ gs_unref_variant GVariant *variant = NULL;
+ gs_free_error GError *error = NULL;
+ nm_auto_unref_keyfile GKeyFile *keyfile = NULL;
+ NMSettingConnection *s_con;
+ NMSettingEthtool *s_ethtool;
+ NMSettingEthtool *s_ethtool2;
+ NMSettingEthtool *s_ethtool3;
+ guint32 out_value;
+ guint32 expected_fec_mode =
+ NM_SETTING_ETHTOOL_FEC_MODE_AUTO | NM_SETTING_ETHTOOL_FEC_MODE_BASER;
+
+ con =
+ nmtst_create_minimal_connection("ethtool-fec", NULL, NM_SETTING_WIRED_SETTING_NAME, &s_con);
+ s_ethtool = NM_SETTING_ETHTOOL(nm_setting_ethtool_new());
+ nm_connection_add_setting(con, NM_SETTING(s_ethtool));
+
+ nm_setting_option_set_uint32(NM_SETTING(s_ethtool),
+ NM_ETHTOOL_OPTNAME_FEC_MODE,
+ expected_fec_mode);
+
+ g_assert_true(nm_setting_option_get_uint32(NM_SETTING(s_ethtool),
+ NM_ETHTOOL_OPTNAME_FEC_MODE,
+ &out_value));
+ g_assert_true(out_value == expected_fec_mode);
+
+ nmtst_connection_normalize(con);
+
+ variant = nm_connection_to_dbus(con, NM_CONNECTION_SERIALIZE_ALL);
+
+ con2 = nm_simple_connection_new_from_dbus(variant, &error);
+ nmtst_assert_success(con2, error);
+
+ s_ethtool2 = NM_SETTING_ETHTOOL(nm_connection_get_setting(con2, NM_TYPE_SETTING_ETHTOOL));
+
+ g_assert_true(nm_setting_option_get_uint32(NM_SETTING(s_ethtool2),
+ NM_ETHTOOL_OPTNAME_FEC_MODE,
+ &out_value));
+ g_assert_true(out_value == expected_fec_mode);
+
+ nmtst_assert_connection_verifies_without_normalization(con2);
+
+ nmtst_assert_connection_equals(con, FALSE, con2, FALSE);
+
+ con2 = nm_simple_connection_new_from_dbus(variant, &error);
+ nmtst_assert_success(con2, error);
+
+ keyfile = nm_keyfile_write(con, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, &error);
+ nmtst_assert_success(keyfile, error);
+
+ con3 = nm_keyfile_read(keyfile,
+ "/ignored/current/working/directory/for/loading/relative/paths",
+ NM_KEYFILE_HANDLER_FLAGS_NONE,
+ NULL,
+ NULL,
+ &error);
+ nmtst_assert_success(con3, error);
+
+ nm_keyfile_read_ensure_id(con3, "unused-because-already-has-id");
+ nm_keyfile_read_ensure_uuid(con3, "unused-because-already-has-uuid");
+
+ nmtst_connection_normalize(con3);
+
+ nmtst_assert_connection_equals(con, FALSE, con3, FALSE);
+
+ s_ethtool3 = NM_SETTING_ETHTOOL(nm_connection_get_setting(con3, NM_TYPE_SETTING_ETHTOOL));
+
+ g_assert_true(nm_setting_option_get_uint32(NM_SETTING(s_ethtool3),
+ NM_ETHTOOL_OPTNAME_FEC_MODE,
+ &out_value));
+ g_assert_true(out_value == expected_fec_mode);
+}
+/*****************************************************************************/
+
static void
test_sriov_vf(void)
{
@@ -5486,6 +5565,7 @@ main(int argc, char **argv)
g_test_add_func("/libnm/settings/ethtool/ring", test_ethtool_ring);
g_test_add_func("/libnm/settings/ethtool/pause", test_ethtool_pause);
g_test_add_func("/libnm/settings/ethtool/eee", test_ethtool_eee);
+ g_test_add_func("/libnm/settings/ethtool/fec", test_ethtool_fec);
g_test_add_func("/libnm/settings/6lowpan/1", test_6lowpan_1);
diff --git a/src/libnm-core-public/nm-setting-ethtool.h b/src/libnm-core-public/nm-setting-ethtool.h
index fe1fbc0e3..867bb0f2f 100644
--- a/src/libnm-core-public/nm-setting-ethtool.h
+++ b/src/libnm-core-public/nm-setting-ethtool.h
@@ -32,6 +32,9 @@ gboolean nm_ethtool_optname_is_channels(const char *optname);
NM_AVAILABLE_IN_1_46
gboolean nm_ethtool_optname_is_eee(const char *optname);
+NM_AVAILABLE_IN_1_52
+gboolean nm_ethtool_optname_is_fec(const char *optname);
+
/*****************************************************************************/
#define NM_TYPE_SETTING_ETHTOOL (nm_setting_ethtool_get_type())
@@ -74,6 +77,30 @@ NM_AVAILABLE_IN_1_14
NM_DEPRECATED_IN_1_26
void nm_setting_ethtool_clear_features(NMSettingEthtool *setting);
+/**
+ * NMSettingEthtoolFecMode:
+ * @NM_SETTING_ETHTOOL_FEC_MODE_NONE: FEC mode configuration is not supported.
+ * @NM_SETTING_ETHTOOL_FEC_MODE_AUTO: Select default/best FEC mode automatically.
+ * @NM_SETTING_ETHTOOL_FEC_MODE_OFF: No FEC mode.
+ * @NM_SETTING_ETHTOOL_FEC_MODE_RS: Reed-Solomon FEC Mode.
+ * @NM_SETTING_ETHTOOL_FEC_MODE_BASER: Base-R/Reed-Solomon FEC Mode.
+ * @NM_SETTING_ETHTOOL_FEC_MODE_LLRS: Low Latency Reed Solomon FEC Mode.
+ *
+ * These flags modify the ethtool FEC(Forward Error Correction) mode.
+ *
+ * Since: 1.52
+ **/
+typedef enum { /*< flags >*/
+ NM_SETTING_ETHTOOL_FEC_MODE_NONE = 1 << 0, /*< skip >*/
+ NM_SETTING_ETHTOOL_FEC_MODE_AUTO = 1 << 1,
+ NM_SETTING_ETHTOOL_FEC_MODE_OFF = 1 << 2,
+ NM_SETTING_ETHTOOL_FEC_MODE_RS = 1 << 3,
+ NM_SETTING_ETHTOOL_FEC_MODE_BASER = 1 << 4,
+ NM_SETTING_ETHTOOL_FEC_MODE_LLRS = 1 << 5,
+ /* New constant should align with linux/ethtool.h ETHTOOL_FEC_XXX */
+ _NM_SETTING_ETHTOOL_FEC_MODE_LAST = NM_SETTING_ETHTOOL_FEC_MODE_LLRS, /*< skip >*/
+} NMSettingEthtoolFecMode;
+
G_END_DECLS
#endif /* __NM_SETTING_ETHTOOL_H__ */
diff --git a/src/libnm-platform/nm-platform-utils.c b/src/libnm-platform/nm-platform-utils.c
index 209f00931..15aac11c2 100644
--- a/src/libnm-platform/nm-platform-utils.c
+++ b/src/libnm-platform/nm-platform-utils.c
@@ -1820,6 +1820,46 @@ nmp_utils_ethtool_set_wake_on_lan(int ifindex,
return _ethtool_call_once(ifindex, &wol_info, sizeof(wol_info)) >= 0;
}
+gboolean
+nmp_utils_ethtool_get_fec_mode(int ifindex, uint32_t *fec_mode)
+{
+ int r;
+ struct ethtool_fecparam fec_param = {
+ .cmd = ETHTOOL_GFECPARAM,
+ .fec = 0,
+ };
+
+ g_return_val_if_fail(ifindex > 0, FALSE);
+
+ if (_ethtool_call_once(ifindex, &fec_param, sizeof(fec_param)) >= 0) {
+ nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: get FEC options 0x%x", ifindex, fec_param.fec);
+ *fec_mode = fec_param.fec;
+ return TRUE;
+ } else {
+ r = -NM_ERRNO_NATIVE(errno);
+ nm_log_dbg(LOGD_PLATFORM,
+ "ethtool[%d]: ETHTOOL_GFECPARAM failure get fec mode: (%s)",
+ ifindex,
+ nm_strerror_native(-r));
+ return FALSE;
+ }
+}
+
+gboolean
+nmp_utils_ethtool_set_fec_mode(int ifindex, uint32_t fec_mode)
+{
+ struct ethtool_fecparam fec_param = {
+ .cmd = ETHTOOL_SFECPARAM,
+ .fec = fec_mode,
+ };
+
+ g_return_val_if_fail(ifindex > 0, FALSE);
+
+ nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: setting FEC options 0x%x", ifindex, fec_mode);
+
+ return _ethtool_call_once(ifindex, &fec_param, sizeof(fec_param)) >= 0;
+}
+
/******************************************************************************
* mii
*****************************************************************************/
diff --git a/src/libnm-platform/nm-platform-utils.h b/src/libnm-platform/nm-platform-utils.h
index 96ac22ef3..f47bcd492 100644
--- a/src/libnm-platform/nm-platform-utils.h
+++ b/src/libnm-platform/nm-platform-utils.h
@@ -66,6 +66,10 @@ gboolean nmp_utils_ethtool_get_eee(int ifindex, NMEthtoolEEEState *eee);
gboolean nmp_utils_ethtool_set_eee(int ifindex, const NMEthtoolEEEState *eee);
+gboolean nmp_utils_ethtool_get_fec_mode(int ifindex, uint32_t *fec_mode);
+
+gboolean nmp_utils_ethtool_set_fec_mode(int ifindex, uint32_t fec_mode);
+
/*****************************************************************************/
gboolean nmp_utils_mii_supports_carrier_detect(int ifindex);
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index ff45cf3d0..5cc97717f 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -3617,6 +3617,26 @@ nm_platform_ethtool_set_features(
return nmp_utils_ethtool_set_features(ifindex, features, requested, do_set);
}
+gboolean
+nm_platform_ethtool_get_fec_mode(NMPlatform *self, int ifindex, uint32_t *fec_mode)
+{
+ _CHECK_SELF_NETNS(self, klass, netns, FALSE);
+
+ g_return_val_if_fail(ifindex > 0, FALSE);
+
+ return nmp_utils_ethtool_get_fec_mode(ifindex, fec_mode);
+}
+
+gboolean
+nm_platform_ethtool_set_fec_mode(NMPlatform *self, int ifindex, uint32_t fec_mode)
+{
+ _CHECK_SELF_NETNS(self, klass, netns, FALSE);
+
+ g_return_val_if_fail(ifindex > 0, FALSE);
+
+ return nmp_utils_ethtool_set_fec_mode(ifindex, fec_mode);
+}
+
gboolean
nm_platform_ethtool_get_link_coalesce(NMPlatform *self,
int ifindex,
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index 30d9037e6..ec7067a52 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -2652,6 +2652,10 @@ gboolean nm_platform_ethtool_set_channels(NMPlatform *self,
int ifindex,
const NMEthtoolChannelsState *channels);
+gboolean nm_platform_ethtool_get_fec_mode(NMPlatform *self, int ifindex, uint32_t *fec_mode);
+
+gboolean nm_platform_ethtool_set_fec_mode(NMPlatform *self, int ifindex, uint32_t fec_mode);
+
gboolean
nm_platform_ethtool_get_link_pause(NMPlatform *self, int ifindex, NMEthtoolPauseState *pause);
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index 0fa6cf345..14d8a2c10 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include "libnm-core-aux-intern/nm-common-macros.h"
#include "libnm-glib-aux/nm-enum-utils.h"
@@ -4459,6 +4460,21 @@ _get_fcn_ethtool(ARGS_GET_FCN)
if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
s = gettext(s);
return s;
+ case NM_ETHTOOL_TYPE_FEC:
+ if (!nm_setting_option_get_uint32(setting, nm_ethtool_data[ethtool_id]->optname, &u32)) {
+ NM_SET_OUT(out_is_default, TRUE);
+ return NULL;
+ }
+ s = _nm_utils_enum_to_str_full(nm_setting_ethtool_fec_mode_get_type(),
+ (int) (u32 & INT_MAX),
+ ", ",
+ NULL);
+ if (s == NULL) {
+ NM_SET_OUT(out_is_default, TRUE);
+ }
+ if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
+ s = gettext(s);
+ return s;
case NM_ETHTOOL_TYPE_UNKNOWN:
nm_assert_not_reached();
}
@@ -4469,9 +4485,11 @@ _get_fcn_ethtool(ARGS_GET_FCN)
static gboolean
_set_fcn_ethtool(ARGS_SET_FCN)
{
- NMEthtoolID ethtool_id = property_info->property_typ_data->subtype.ethtool.ethtool_id;
- gint64 i64;
- NMTernary t;
+ NMEthtoolID ethtool_id = property_info->property_typ_data->subtype.ethtool.ethtool_id;
+ gint64 i64;
+ NMTernary t;
+ int fec_mode = 0;
+ gs_free char *invalid_fec_mode = NULL;
if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value))
goto do_unset;
@@ -4512,6 +4530,30 @@ _set_fcn_ethtool(ARGS_SET_FCN)
nm_setting_option_set_boolean(setting, nm_ethtool_data[ethtool_id]->optname, !!t);
return TRUE;
+ case NM_ETHTOOL_TYPE_FEC:
+ if (_nm_utils_enum_from_str_full(nm_setting_ethtool_fec_mode_get_type(),
+ value,
+ &fec_mode,
+ &invalid_fec_mode,
+ NULL)) {
+ nm_setting_option_set_uint32(setting,
+ NM_ETHTOOL_OPTNAME_FEC_MODE,
+ (uint32_t) (fec_mode & UINT32_MAX));
+ return TRUE;
+ } else {
+ gs_free const char **valid_all = NULL;
+ gs_free const char *valid_str = NULL;
+
+ valid_all =
+ nm_utils_enum_get_values(nm_setting_ethtool_fec_mode_get_type(), 0, G_MAXUINT);
+ valid_str = g_strjoinv(",", (char **) valid_all);
+ nm_utils_error_set(error,
+ NM_UTILS_ERROR_INVALID_ARGUMENT,
+ _("'%s' is not valid FEC modes, valid modes are combinations of %s"),
+ invalid_fec_mode,
+ valid_str);
+ return FALSE;
+ }
case NM_ETHTOOL_TYPE_UNKNOWN:
nm_assert_not_reached();
}
@@ -5989,6 +6031,15 @@ static const NMMetaPropertyInfo *const property_infos_ETHTOOL[] = {
PROPERTY_INFO_ETHTOOL (CHANNELS_TX),
PROPERTY_INFO_ETHTOOL (CHANNELS_OTHER),
PROPERTY_INFO_ETHTOOL (CHANNELS_COMBINED),
+ PROPERTY_INFO (NM_ETHTOOL_OPTNAME_FEC_MODE,
+ "The Forward Error Correction(FEC) encoding modes to set. "
+ "Not all devices support all options. "
+ "May be any combination of auto, off, rs, baser, llrs.",
+ .property_type = &_pt_ethtool,
+ .property_typ_data =
+ DEFINE_PROPERTY_TYP_DATA_SUBTYPE
+ (ethtool, .ethtool_id = NM_ETHTOOL_ID_FEC_MODE)
+ ),
NULL,
};
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.c b/src/nmcli/gen-metadata-nm-settings-nmcli.c
index 0d207321a..2ba959956 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.c
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.c
@@ -124,6 +124,8 @@ get_ethtool_format(const NMMetaPropertyInfo *prop_info)
case NM_ETHTOOL_TYPE_PAUSE:
case NM_ETHTOOL_TYPE_EEE:
return g_strdup("ternary");
+ case NM_ETHTOOL_TYPE_FEC:
+ return g_strdup("flags (NMSettingEthtoolFecMode)");
case NM_ETHTOOL_TYPE_UNKNOWN:
nm_assert_not_reached();
};
@@ -334,6 +336,13 @@ append_ethtool_valid_values(const NMMetaPropertyInfo *prop_info, GPtrArray *vali
case NM_ETHTOOL_TYPE_EEE:
append_vals(valid_values, "on", "off", "ignore");
break;
+ case NM_ETHTOOL_TYPE_FEC:
+ _append_enum_valid_values(NM_TYPE_SETTING_ETHTOOL_FEC_MODE,
+ 0,
+ G_MAXUINT,
+ NULL,
+ valid_values);
+ break;
case NM_ETHTOOL_TYPE_UNKNOWN:
nm_assert_not_reached();
}
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index f0b77433c..57dfbe4b7 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -1096,6 +1096,10 @@
+