summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGris Ge <fge@redhat.com>2021-05-11 15:45:22 +0800
committerThomas Haller <thaller@redhat.com>2021-05-12 18:04:46 +0200
commit652ddca04c5287bd4db1ad456556b9ed028621c0 (patch)
tree3e6253f54192785bf6964d8d7d8d64c1d0b066ce
parentd0e8a7e2acce845a1aae98304c8e21254573fc09 (diff)
downloadNetworkManager-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
-rw-r--r--src/core/devices/nm-device.c98
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c13
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c1
-rw-r--r--src/libnm-base/nm-base.h16
-rw-r--r--src/libnm-base/nm-ethtool-base.c8
-rw-r--r--src/libnm-base/nm-ethtool-utils-base.h4
-rw-r--r--src/libnm-client-impl/libnm.ver1
-rw-r--r--src/libnm-client-public/nm-ethtool-utils.h4
-rw-r--r--src/libnm-core-impl/nm-setting-ethtool.c18
-rw-r--r--src/libnm-core-impl/tests/test-setting.c95
-rw-r--r--src/libnm-core-public/nm-setting-ethtool.h3
-rw-r--r--src/libnm-platform/nm-platform-utils.c60
-rw-r--r--src/libnm-platform/nm-platform-utils.h4
-rw-r--r--src/libnm-platform/nm-platform.c21
-rw-r--r--src/libnm-platform/nm-platform.h6
-rw-r--r--src/libnm-platform/nmp-base.h6
-rw-r--r--src/libnmc-setting/nm-meta-setting-desc.c28
-rw-r--r--src/nmcli/generate-docs-nm-settings-nmcli.xml.in6
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(&ethtool_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(&ethtool_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, &eth_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, &eth_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" />