diff options
author | Gris Ge <fge@redhat.com> | 2021-05-11 15:45:22 +0800 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2021-05-12 18:04:46 +0200 |
commit | 652ddca04c5287bd4db1ad456556b9ed028621c0 (patch) | |
tree | 3e6253f54192785bf6964d8d7d8d64c1d0b066ce | |
parent | d0e8a7e2acce845a1aae98304c8e21254573fc09 (diff) | |
download | NetworkManager-652ddca04c5287bd4db1ad456556b9ed028621c0.tar.gz |
ethtool: Introducing PAUSE support
Introducing ethtool PAUSE support with:
* ethtool.pause-autoneg on/off
* ethtool.pause-rx on/off
* ethtool.pause-tx on/off
Limitations:
* When `ethtool.pause-autoneg` is set to true, the `ethtool.pause-rx`
and `ethtool.pause-tx` will be ignored. We don't have warning for
this yet.
Unit test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/829
20 files changed, 405 insertions, 6 deletions
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 0bfc51ca68..ecb55ec30d 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -209,6 +209,7 @@ typedef struct { NMOptionBool requested[_NM_ETHTOOL_ID_FEATURE_NUM]; NMEthtoolCoalesceState *coalesce; NMEthtoolRingState * ring; + NMEthtoolPauseState * pause; } EthtoolState; typedef enum { @@ -2143,6 +2144,97 @@ _ethtool_ring_set(NMDevice * self, } static void +_ethtool_pause_reset(NMDevice *self, NMPlatform *platform, EthtoolState *ethtool_state) +{ + gs_free NMEthtoolPauseState *pause = NULL; + + nm_assert(NM_IS_DEVICE(self)); + nm_assert(NM_IS_PLATFORM(platform)); + nm_assert(ethtool_state); + + pause = g_steal_pointer(ðtool_state->pause); + if (!pause) + return; + + if (!nm_platform_ethtool_set_pause(platform, ethtool_state->ifindex, pause)) + _LOGW(LOGD_DEVICE, "ethtool: failure resetting one or more pause settings"); + else + _LOGD(LOGD_DEVICE, "ethtool: pause settings successfully reset"); +} + +static void +_ethtool_pause_set(NMDevice * self, + NMPlatform * platform, + EthtoolState * ethtool_state, + NMSettingEthtool *s_ethtool) +{ + NMEthtoolPauseState pause_old; + NMEthtoolPauseState pause_new; + GHashTable * hash; + GHashTableIter iter; + const char * name; + GVariant * variant; + gboolean has_old = FALSE; + + 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->pause); + + 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_pause(ethtool_id)) + continue; + + nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)); + + if (!has_old) { + if (!nm_platform_ethtool_get_link_pause(platform, ethtool_state->ifindex, &pause_old)) { + _LOGW(LOGD_DEVICE, + "ethtool: failure setting pause options (cannot read " + "existing setting)"); + return; + } + has_old = TRUE; + pause_new = pause_old; + } + + switch (ethtool_id) { + case NM_ETHTOOL_ID_PAUSE_AUTONEG: + pause_new.autoneg = g_variant_get_boolean(variant); + break; + case NM_ETHTOOL_ID_PAUSE_RX: + pause_new.rx = g_variant_get_boolean(variant); + break; + case NM_ETHTOOL_ID_PAUSE_TX: + pause_new.tx = g_variant_get_boolean(variant); + break; + default: + nm_assert_not_reached(); + } + } + + if (!has_old) + return; + + ethtool_state->pause = nm_memdup(&pause_old, sizeof(pause_old)); + + if (!nm_platform_ethtool_set_pause(platform, ethtool_state->ifindex, &pause_new)) { + _LOGW(LOGD_DEVICE, "ethtool: failure setting pause settings"); + return; + } + + _LOGD(LOGD_DEVICE, "ethtool: pause settings successfully set"); +} + +static void _ethtool_state_reset(NMDevice *self) { NMPlatform * platform = nm_device_get_platform(self); @@ -2158,6 +2250,8 @@ _ethtool_state_reset(NMDevice *self) _ethtool_coalesce_reset(self, platform, ethtool_state); if (ethtool_state->ring) _ethtool_ring_reset(self, platform, ethtool_state); + if (ethtool_state->pause) + _ethtool_pause_reset(self, platform, ethtool_state); } static void @@ -2191,8 +2285,10 @@ _ethtool_state_set(NMDevice *self) _ethtool_features_set(self, platform, ethtool_state, s_ethtool); _ethtool_coalesce_set(self, platform, ethtool_state, s_ethtool); _ethtool_ring_set(self, platform, ethtool_state, s_ethtool); + _ethtool_pause_set(self, platform, ethtool_state, s_ethtool); - if (ethtool_state->features || ethtool_state->coalesce || ethtool_state->ring) + if (ethtool_state->features || ethtool_state->coalesce || ethtool_state->ring + || ethtool_state->pause) priv->ethtool_state = g_steal_pointer(ðtool_state); } diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 19e672e0bd..2a068b64d2 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -4677,7 +4677,9 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( {"--coalesce", NM_ETHTOOL_TYPE_COALESCE}, {"--features", NM_ETHTOOL_TYPE_FEATURE}, {"--offload", NM_ETHTOOL_TYPE_FEATURE}, + {"--pause", NM_ETHTOOL_TYPE_PAUSE}, {"--set-ring", NM_ETHTOOL_TYPE_RING}, + {"-A", NM_ETHTOOL_TYPE_PAUSE}, {"-C", NM_ETHTOOL_TYPE_COALESCE}, {"-G", NM_ETHTOOL_TYPE_RING}, {"-K", NM_ETHTOOL_TYPE_FEATURE}, ); @@ -4717,7 +4719,7 @@ parse_ethtool_option(const char * value, w_iter = &words[2]; while (w_iter && *w_iter) { - if (ethtool_type == NM_ETHTOOL_TYPE_FEATURE) { + if (NM_IN_SET(ethtool_type, NM_ETHTOOL_TYPE_FEATURE, NM_ETHTOOL_TYPE_PAUSE)) { w_iter = _next_ethtool_options_nmternary(w_iter, ethtool_type, &ifcfg_option); if (ifcfg_option.has_value) { diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 6ab4c08d35..8fa498216c 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -593,6 +593,9 @@ const char *const _nm_ethtool_ifcfg_names[] = { ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"), ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"), ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"), + ETHT_NAME(NM_ETHTOOL_ID_PAUSE_AUTONEG, "pause-autoneg"), + ETHT_NAME(NM_ETHTOOL_ID_PAUSE_RX, "pause-rx"), + ETHT_NAME(NM_ETHTOOL_ID_PAUSE_TX, "pause-tx"), ETHT_NAME(NM_ETHTOOL_ID_RING_RX, "rx"), ETHT_NAME(NM_ETHTOOL_ID_RING_RX_JUMBO, "rx-jumbo"), ETHT_NAME(NM_ETHTOOL_ID_RING_RX_MINI, "rx-mini"), @@ -722,6 +725,15 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( {"rx-mini", NM_ETHTOOL_ID_RING_RX_MINI}, {"tx", NM_ETHTOOL_ID_RING_TX}, ); +static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( + _get_ethtoolid_pause_by_name, + NMEthtoolID, + { nm_assert(name); }, + { return NM_ETHTOOL_ID_UNKNOWN; }, + {"pause-autoneg", NM_ETHTOOL_ID_PAUSE_AUTONEG}, + {"pause-rx", NM_ETHTOOL_ID_PAUSE_RX}, + {"pause-tx", NM_ETHTOOL_ID_PAUSE_TX}, ); + const NMEthtoolData * nms_ifcfg_rh_utils_get_ethtool_by_name(const char *name, NMEthtoolType ethtool_type) { @@ -737,6 +749,9 @@ nms_ifcfg_rh_utils_get_ethtool_by_name(const char *name, NMEthtoolType ethtool_t case NM_ETHTOOL_TYPE_RING: id = _get_ethtoolid_ring_by_name(name); break; + case NM_ETHTOOL_TYPE_PAUSE: + id = _get_ethtoolid_pause_by_name(name); + break; default: nm_assert_not_reached(); return NULL; 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 d98ab355d7..d9a16a8133 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 @@ -1363,6 +1363,19 @@ write_ethtool_setting(NMConnection *connection, shvarFile *ifcfg, GError **error g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); g_string_append_printf(str, " %" G_GUINT32_FORMAT, u32); } + for (ethtool_id = _NM_ETHTOOL_ID_PAUSE_FIRST; ethtool_id <= _NM_ETHTOOL_ID_PAUSE_LAST; + ethtool_id++) { + nm_assert(nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + if (!nm_setting_option_get_boolean(NM_SETTING(s_ethtool), + nm_ethtool_data[ethtool_id]->optname, + &b)) + continue; + + _ethtool_gstring_prepare(&str, &is_first, 'A', iface); + g_string_append_c(str, ' '); + g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + g_string_append(str, b ? " on" : " off"); + } } if (str) { 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 073e4c3356..1c3cd36def 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 @@ -11359,6 +11359,7 @@ test_ethtool_names(void) } s_idxs[] = { {_NM_ETHTOOL_ID_FEATURE_FIRST, _NM_ETHTOOL_ID_FEATURE_LAST}, {_NM_ETHTOOL_ID_COALESCE_FIRST, _NM_ETHTOOL_ID_COALESCE_LAST}, + {_NM_ETHTOOL_ID_PAUSE_FIRST, _NM_ETHTOOL_ID_PAUSE_LAST}, {_NM_ETHTOOL_ID_RING_FIRST, _NM_ETHTOOL_ID_RING_LAST}, }; const NMEthtoolData *data; diff --git a/src/libnm-base/nm-base.h b/src/libnm-base/nm-base.h index 3f0ca02544..7855017776 100644 --- a/src/libnm-base/nm-base.h +++ b/src/libnm-base/nm-base.h @@ -104,7 +104,13 @@ typedef enum { NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, _NM_ETHTOOL_ID_FEATURE_LAST = NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, - _NM_ETHTOOL_ID_RING_FIRST = _NM_ETHTOOL_ID_FEATURE_LAST + 1, + _NM_ETHTOOL_ID_PAUSE_FIRST = _NM_ETHTOOL_ID_FEATURE_LAST + 1, + NM_ETHTOOL_ID_PAUSE_AUTONEG = _NM_ETHTOOL_ID_PAUSE_FIRST, + NM_ETHTOOL_ID_PAUSE_RX, + NM_ETHTOOL_ID_PAUSE_TX, + _NM_ETHTOOL_ID_PAUSE_LAST = NM_ETHTOOL_ID_PAUSE_TX, + + _NM_ETHTOOL_ID_RING_FIRST = _NM_ETHTOOL_ID_PAUSE_LAST + 1, NM_ETHTOOL_ID_RING_RX = _NM_ETHTOOL_ID_RING_FIRST, NM_ETHTOOL_ID_RING_RX_JUMBO, NM_ETHTOOL_ID_RING_RX_MINI, @@ -117,6 +123,7 @@ typedef enum { (_NM_ETHTOOL_ID_COALESCE_LAST - _NM_ETHTOOL_ID_COALESCE_FIRST + 1), _NM_ETHTOOL_ID_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1), _NM_ETHTOOL_ID_RING_NUM = (_NM_ETHTOOL_ID_RING_LAST - _NM_ETHTOOL_ID_RING_FIRST + 1), + _NM_ETHTOOL_ID_PAUSE_NUM = (_NM_ETHTOOL_ID_PAUSE_LAST - _NM_ETHTOOL_ID_PAUSE_FIRST + 1), _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1), } NMEthtoolID; @@ -128,6 +135,7 @@ typedef enum { NM_ETHTOOL_TYPE_COALESCE, NM_ETHTOOL_TYPE_FEATURE, NM_ETHTOOL_TYPE_RING, + NM_ETHTOOL_TYPE_PAUSE, } NMEthtoolType; /****************************************************************************/ @@ -150,6 +158,12 @@ nm_ethtool_id_is_ring(NMEthtoolID id) return id >= _NM_ETHTOOL_ID_RING_FIRST && id <= _NM_ETHTOOL_ID_RING_LAST; } +static inline gboolean +nm_ethtool_id_is_pause(NMEthtoolID id) +{ + return id >= _NM_ETHTOOL_ID_PAUSE_FIRST && id <= _NM_ETHTOOL_ID_PAUSE_LAST; +} + /*****************************************************************************/ typedef enum { diff --git a/src/libnm-base/nm-ethtool-base.c b/src/libnm-base/nm-ethtool-base.c index 03cbf56007..c253ab9452 100644 --- a/src/libnm-base/nm-ethtool-base.c +++ b/src/libnm-base/nm-ethtool-base.c @@ -99,6 +99,9 @@ const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = { ETHT_DATA(FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION), ETHT_DATA(FEATURE_TX_UDP_TNL_SEGMENTATION), ETHT_DATA(FEATURE_TX_VLAN_STAG_HW_INSERT), + ETHT_DATA(PAUSE_AUTONEG), + ETHT_DATA(PAUSE_RX), + ETHT_DATA(PAUSE_TX), ETHT_DATA(RING_RX), ETHT_DATA(RING_RX_JUMBO), ETHT_DATA(RING_RX_MINI), @@ -188,6 +191,9 @@ 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_PAUSE_AUTONEG, + NM_ETHTOOL_ID_PAUSE_RX, + NM_ETHTOOL_ID_PAUSE_TX, NM_ETHTOOL_ID_RING_RX, NM_ETHTOOL_ID_RING_RX_JUMBO, NM_ETHTOOL_ID_RING_RX_MINI, @@ -283,6 +289,8 @@ nm_ethtool_id_to_type(NMEthtoolID id) return NM_ETHTOOL_TYPE_FEATURE; if (nm_ethtool_id_is_ring(id)) return NM_ETHTOOL_TYPE_RING; + if (nm_ethtool_id_is_pause(id)) + return NM_ETHTOOL_TYPE_PAUSE; return 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 d422724bab..fa9c30715d 100644 --- a/src/libnm-base/nm-ethtool-utils-base.h +++ b/src/libnm-base/nm-ethtool-utils-base.h @@ -100,6 +100,10 @@ G_BEGIN_DECLS #define NM_ETHTOOL_OPTNAME_RING_RX_MINI "ring-rx-mini" #define NM_ETHTOOL_OPTNAME_RING_TX "ring-tx" +#define NM_ETHTOOL_OPTNAME_PAUSE_AUTONEG "pause-autoneg" +#define NM_ETHTOOL_OPTNAME_PAUSE_RX "pause-rx" +#define NM_ETHTOOL_OPTNAME_PAUSE_TX "pause-tx" + /*****************************************************************************/ G_END_DECLS diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index d1a5865910..3637f1b840 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -1787,6 +1787,7 @@ global: libnm_1_32_0 { global: + nm_ethtool_optname_is_pause; nm_setting_match_new; nm_setting_wired_get_accept_all_mac_addresses; } libnm_1_30_0; diff --git a/src/libnm-client-public/nm-ethtool-utils.h b/src/libnm-client-public/nm-ethtool-utils.h index d422724bab..fa9c30715d 100644 --- a/src/libnm-client-public/nm-ethtool-utils.h +++ b/src/libnm-client-public/nm-ethtool-utils.h @@ -100,6 +100,10 @@ G_BEGIN_DECLS #define NM_ETHTOOL_OPTNAME_RING_RX_MINI "ring-rx-mini" #define NM_ETHTOOL_OPTNAME_RING_TX "ring-tx" +#define NM_ETHTOOL_OPTNAME_PAUSE_AUTONEG "pause-autoneg" +#define NM_ETHTOOL_OPTNAME_PAUSE_RX "pause-rx" +#define NM_ETHTOOL_OPTNAME_PAUSE_TX "pause-tx" + /*****************************************************************************/ G_END_DECLS diff --git a/src/libnm-core-impl/nm-setting-ethtool.c b/src/libnm-core-impl/nm-setting-ethtool.c index b32372e0bd..2329f228a6 100644 --- a/src/libnm-core-impl/nm-setting-ethtool.c +++ b/src/libnm-core-impl/nm-setting-ethtool.c @@ -25,7 +25,7 @@ static const GVariantType * get_variant_type_from_ethtool_id(NMEthtoolID ethtool_id) { - if (nm_ethtool_id_is_feature(ethtool_id)) + if (nm_ethtool_id_is_feature(ethtool_id) || nm_ethtool_id_is_pause(ethtool_id)) return G_VARIANT_TYPE_BOOLEAN; if (nm_ethtool_id_is_coalesce(ethtool_id) || nm_ethtool_id_is_ring(ethtool_id)) @@ -88,6 +88,22 @@ nm_ethtool_optname_is_ring(const char *optname) return optname && nm_ethtool_id_is_ring(nm_ethtool_id_get_by_name(optname)); } +/** + * nm_ethtool_optname_is_pause: + * @optname: (allow-none): the option name to check + * + * Checks whether @optname is a valid option name for a pause setting. + * + * %Returns: %TRUE, if @optname is valid + * + * Since: 1.32 + */ +gboolean +nm_ethtool_optname_is_pause(const char *optname) +{ + return optname && nm_ethtool_id_is_pause(nm_ethtool_id_get_by_name(optname)); +} + /*****************************************************************************/ /** diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c index 6671536537..16a7e44e8e 100644 --- a/src/libnm-core-impl/tests/test-setting.c +++ b/src/libnm-core-impl/tests/test-setting.c @@ -2126,6 +2126,100 @@ test_ethtool_ring(void) nm_setting_option_get_uint32(NM_SETTING(s_ethtool), NM_ETHTOOL_OPTNAME_RING_TX, NULL)); } +static void +test_ethtool_pause(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; + gboolean out_value; + + con = nmtst_create_minimal_connection("ethtool-pause", + 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_boolean(NM_SETTING(s_ethtool), NM_ETHTOOL_OPTNAME_PAUSE_AUTONEG, FALSE); + nm_setting_option_set_boolean(NM_SETTING(s_ethtool), NM_ETHTOOL_OPTNAME_PAUSE_RX, TRUE); + nm_setting_option_set_boolean(NM_SETTING(s_ethtool), NM_ETHTOOL_OPTNAME_PAUSE_TX, TRUE); + + g_assert_true(nm_setting_option_get_boolean(NM_SETTING(s_ethtool), + NM_ETHTOOL_OPTNAME_PAUSE_AUTONEG, + &out_value)); + g_assert_true(!out_value); + g_assert_true(nm_setting_option_get_boolean(NM_SETTING(s_ethtool), + NM_ETHTOOL_OPTNAME_PAUSE_RX, + &out_value)); + g_assert_true(out_value); + g_assert_true(nm_setting_option_get_boolean(NM_SETTING(s_ethtool), + NM_ETHTOOL_OPTNAME_PAUSE_TX, + &out_value)); + g_assert_true(out_value); + + 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_boolean(NM_SETTING(s_ethtool2), + NM_ETHTOOL_OPTNAME_PAUSE_AUTONEG, + &out_value)); + g_assert_true(!out_value); + + 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_boolean(NM_SETTING(s_ethtool3), + NM_ETHTOOL_OPTNAME_PAUSE_AUTONEG, + &out_value)); + g_assert_true(!out_value); + g_assert_true(nm_setting_option_get_boolean(NM_SETTING(s_ethtool3), + NM_ETHTOOL_OPTNAME_PAUSE_RX, + &out_value)); + g_assert_true(out_value); + g_assert_true(nm_setting_option_get_boolean(NM_SETTING(s_ethtool3), + NM_ETHTOOL_OPTNAME_PAUSE_TX, + &out_value)); + g_assert_true(out_value); +} + /*****************************************************************************/ static void @@ -4402,6 +4496,7 @@ main(int argc, char **argv) g_test_add_func("/libnm/settings/ethtool/features", test_ethtool_features); g_test_add_func("/libnm/settings/ethtool/coalesce", test_ethtool_coalesce); 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/sriov/vf", test_sriov_vf); g_test_add_func("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup); diff --git a/src/libnm-core-public/nm-setting-ethtool.h b/src/libnm-core-public/nm-setting-ethtool.h index d2310b408e..eab447af09 100644 --- a/src/libnm-core-public/nm-setting-ethtool.h +++ b/src/libnm-core-public/nm-setting-ethtool.h @@ -23,6 +23,9 @@ gboolean nm_ethtool_optname_is_coalesce(const char *optname); NM_AVAILABLE_IN_1_26 gboolean nm_ethtool_optname_is_ring(const char *optname); +NM_AVAILABLE_IN_1_32 +gboolean nm_ethtool_optname_is_pause(const char *optname); + /*****************************************************************************/ #define NM_TYPE_SETTING_ETHTOOL (nm_setting_ethtool_get_type()) diff --git a/src/libnm-platform/nm-platform-utils.c b/src/libnm-platform/nm-platform-utils.c index a76f776cfb..6435dcc482 100644 --- a/src/libnm-platform/nm-platform-utils.c +++ b/src/libnm-platform/nm-platform-utils.c @@ -280,6 +280,7 @@ static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string, 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_GPAUSEPARAM, "ETHTOOL_GPAUSEPARAM"), NM_UTILS_ENUM2STR(ETHTOOL_GSET, "ETHTOOL_GSET"), NM_UTILS_ENUM2STR(ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO"), NM_UTILS_ENUM2STR(ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), @@ -289,6 +290,7 @@ static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string, 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_SPAUSEPARAM, "ETHTOOL_SPAUSEPARAM"), NM_UTILS_ENUM2STR(ETHTOOL_SSET, "ETHTOOL_SSET"), NM_UTILS_ENUM2STR(ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); @@ -1064,6 +1066,64 @@ nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring) return TRUE; } +gboolean +nmp_utils_ethtool_get_pause(int ifindex, NMEthtoolPauseState *pause) +{ + struct ethtool_pauseparam eth_data; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(pause, FALSE); + + eth_data.cmd = ETHTOOL_GPAUSEPARAM; + if (_ethtool_call_handle(&shandle, ð_data, sizeof(struct ethtool_pauseparam)) != 0) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure getting pause settings", + ifindex, + "get-pause"); + return FALSE; + } + + *pause = (NMEthtoolPauseState){ + .autoneg = eth_data.autoneg == 1, + .rx = eth_data.rx_pause == 1, + .tx = eth_data.tx_pause == 1, + }; + + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: retrieved kernel pause settings", + ifindex, + "get-pause"); + return TRUE; +} + +gboolean +nmp_utils_ethtool_set_pause(int ifindex, const NMEthtoolPauseState *pause) +{ + struct ethtool_pauseparam eth_data; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(pause, FALSE); + + eth_data = (struct ethtool_pauseparam){ + .cmd = ETHTOOL_SPAUSEPARAM, + .autoneg = pause->autoneg ? 1 : 0, + .rx_pause = pause->rx ? 1 : 0, + .tx_pause = pause->tx ? 1 : 0, + }; + + if (_ethtool_call_handle(&shandle, ð_data, sizeof(struct ethtool_pauseparam)) != 0) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure setting pause settings", + ifindex, + "set-pause"); + return FALSE; + } + nm_log_trace(LOGD_PLATFORM, "ethtool[%d]: %s: set kernel puase settings", ifindex, "set-pause"); + return TRUE; +} + /*****************************************************************************/ gboolean diff --git a/src/libnm-platform/nm-platform-utils.h b/src/libnm-platform/nm-platform-utils.h index 52fcf8d8bc..d70f4385ea 100644 --- a/src/libnm-platform/nm-platform-utils.h +++ b/src/libnm-platform/nm-platform-utils.h @@ -51,6 +51,10 @@ gboolean nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring); gboolean nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring); +gboolean nmp_utils_ethtool_get_pause(int ifindex, NMEthtoolPauseState *pause); + +gboolean nmp_utils_ethtool_set_pause(int ifindex, const NMEthtoolPauseState *pause); + /*****************************************************************************/ 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 54b251f384..2af6cbb3c9 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -3304,6 +3304,27 @@ nm_platform_ethtool_set_ring(NMPlatform *self, int ifindex, const NMEthtoolRingS return nmp_utils_ethtool_set_ring(ifindex, ring); } +gboolean +nm_platform_ethtool_get_link_pause(NMPlatform *self, int ifindex, NMEthtoolPauseState *pause) +{ + _CHECK_SELF_NETNS(self, klass, netns, FALSE); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(pause, FALSE); + + return nmp_utils_ethtool_get_pause(ifindex, pause); +} + +gboolean +nm_platform_ethtool_set_pause(NMPlatform *self, int ifindex, const NMEthtoolPauseState *pause) +{ + _CHECK_SELF_NETNS(self, klass, netns, FALSE); + + g_return_val_if_fail(ifindex > 0, FALSE); + + return nmp_utils_ethtool_set_pause(ifindex, pause); +} + /*****************************************************************************/ const NMDedupMultiHeadEntry * diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 3240293b9a..4f76e19b90 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -2366,6 +2366,12 @@ gboolean nm_platform_ethtool_get_link_ring(NMPlatform *self, int ifindex, NMEtht gboolean nm_platform_ethtool_set_ring(NMPlatform *self, int ifindex, const NMEthtoolRingState *ring); +gboolean +nm_platform_ethtool_get_link_pause(NMPlatform *self, int ifindex, NMEthtoolPauseState *pause); + +gboolean +nm_platform_ethtool_set_pause(NMPlatform *self, int ifindex, const NMEthtoolPauseState *pause); + void nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self, int ifindex, GPtrArray * ip4_dev_route_blacklist); diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index f7dc63747e..a80fd4d389 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -91,6 +91,12 @@ typedef struct { guint32 tx_pending; } NMEthtoolRingState; +typedef struct { + bool autoneg : 1; + bool rx : 1; + bool tx : 1; +} NMEthtoolPauseState; + /*****************************************************************************/ typedef struct _NMPNetns NMPNetns; diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c index 51507913b7..1a3484fcf5 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.c +++ b/src/libnmc-setting/nm-meta-setting-desc.c @@ -4269,7 +4269,7 @@ static gconstpointer _get_fcn_ethtool(ARGS_GET_FCN) RETURN_STR_TO_FREE(nm_strdup_int(u32)); } - nm_assert(nm_ethtool_id_is_feature(ethtool_id)); + nm_assert(nm_ethtool_id_is_feature(ethtool_id) || nm_ethtool_id_is_pause(ethtool_id)); if (!nm_setting_option_get_boolean(setting, nm_ethtool_data[ethtool_id]->optname, &b)) { NM_SET_OUT(out_is_default, TRUE); @@ -4309,7 +4309,7 @@ static gboolean _set_fcn_ethtool(ARGS_SET_FCN) return TRUE; } - nm_assert(nm_ethtool_id_is_feature(ethtool_id)); + nm_assert(nm_ethtool_id_is_feature(ethtool_id) || nm_ethtool_id_is_pause(ethtool_id)); value = nm_strstrip_avoid_copy_a(300, value, &value_to_free); if (NM_IN_STRSET(value, "1", "yes", "true", "on")) @@ -5651,6 +5651,30 @@ static const NMMetaPropertyInfo *const property_infos_ETHTOOL[] = { PROPERTY_INFO_ETHTOOL (COALESCE_TX_USECS_IRQ), PROPERTY_INFO_ETHTOOL (COALESCE_TX_USECS_HIGH), PROPERTY_INFO_ETHTOOL (COALESCE_TX_USECS_LOW), + PROPERTY_INFO (NM_ETHTOOL_OPTNAME_PAUSE_AUTONEG, + "Whether to automatically negotiate on pause frame of flow " + "control mechanism defined by IEEE 802.3x standard.", + .property_type = &_pt_ethtool, + .property_typ_data = + DEFINE_PROPERTY_TYP_DATA_SUBTYPE + (ethtool, .ethtool_id = NM_ETHTOOL_ID_PAUSE_AUTONEG) + ), + PROPERTY_INFO (NM_ETHTOOL_OPTNAME_PAUSE_RX, + "Whether RX pause should be enabled. Only valid when " + "automatic negotiation is disabled", + .property_type = &_pt_ethtool, + .property_typ_data = + DEFINE_PROPERTY_TYP_DATA_SUBTYPE + (ethtool, .ethtool_id = NM_ETHTOOL_ID_PAUSE_RX) + ), + PROPERTY_INFO (NM_ETHTOOL_OPTNAME_PAUSE_TX, + "Whether TX pause should be enabled. Only valid when " + "automatic negotiation is disabled", + .property_type = &_pt_ethtool, + .property_typ_data = + DEFINE_PROPERTY_TYP_DATA_SUBTYPE + (ethtool, .ethtool_id = NM_ETHTOOL_ID_PAUSE_TX) + ), PROPERTY_INFO_ETHTOOL (RING_RX), PROPERTY_INFO_ETHTOOL (RING_RX_JUMBO), PROPERTY_INFO_ETHTOOL (RING_RX_MINI), diff --git a/src/nmcli/generate-docs-nm-settings-nmcli.xml.in b/src/nmcli/generate-docs-nm-settings-nmcli.xml.in index df722c1dd4..3438d7bf2a 100644 --- a/src/nmcli/generate-docs-nm-settings-nmcli.xml.in +++ b/src/nmcli/generate-docs-nm-settings-nmcli.xml.in @@ -532,6 +532,12 @@ <property name="coalesce-tx-usecs-irq" /> <property name="coalesce-tx-usecs-high" /> <property name="coalesce-tx-usecs-low" /> + <property name="pause-autoneg" + description="Whether to automatically negotiate on pause frame of flow control mechanism defined by IEEE 802.3x standard." /> + <property name="pause-rx" + description="Whether RX pause should be enabled. Only valid when automatic negotiation is disabled" /> + <property name="pause-tx" + description="Whether TX pause should be enabled. Only valid when automatic negotiation is disabled" /> <property name="ring-rx" /> <property name="ring-rx-jumbo" /> <property name="ring-rx-mini" /> |