Commit c085b6e3 authored by Thomas Haller's avatar Thomas Haller

platform/ethtool: add code to get/set offload features via ethtool

Also, add two more features "tx-tcp-segmentation" and
"tx-tcp6-segmentation". There are two reasons for that:

 - systemd-networkd supports setting these two features,
   so lets support them too (apparently they are important
   enough for networkd).

 - these two features are already implicitly covered by "tso".
   Like for the "ethtool" program, "tso" is an alias for several
   actual features. By adding two features that are already
   also covered by an alias (which sets multiple kernel names
   at once), we showcase how aliases for the same feature can
   coexist. In particular, note how setting
   "tso on tx-tcp6-segmentation off" will behave as one would
   expect: all 4 tso features covered by the alias are enabled,
   except that particular one.
parent 14f963cd
......@@ -5881,6 +5881,8 @@ static const NMMetaPropertyInfo *const property_infos_ETHTOOL[] = {
PROPERTY_INFO_ETHTOOL (FEATURE_TSO),
PROPERTY_INFO_ETHTOOL (FEATURE_TX),
PROPERTY_INFO_ETHTOOL (FEATURE_TXVLAN),
PROPERTY_INFO_ETHTOOL (FEATURE_TX_TCP6_SEGMENTATION),
PROPERTY_INFO_ETHTOOL (FEATURE_TX_TCP_SEGMENTATION),
NULL,
};
......
......@@ -31,17 +31,19 @@ G_BEGIN_DECLS
/*****************************************************************************/
#define NM_ETHTOOL_OPTNAME_FEATURE_GRO "feature-gro"
#define NM_ETHTOOL_OPTNAME_FEATURE_GSO "feature-gso"
#define NM_ETHTOOL_OPTNAME_FEATURE_LRO "feature-lro"
#define NM_ETHTOOL_OPTNAME_FEATURE_NTUPLE "feature-ntuple"
#define NM_ETHTOOL_OPTNAME_FEATURE_RX "feature-rx"
#define NM_ETHTOOL_OPTNAME_FEATURE_RXHASH "feature-rxhash"
#define NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN "feature-rxvlan"
#define NM_ETHTOOL_OPTNAME_FEATURE_SG "feature-sg"
#define NM_ETHTOOL_OPTNAME_FEATURE_TSO "feature-tso"
#define NM_ETHTOOL_OPTNAME_FEATURE_TX "feature-tx"
#define NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN "feature-txvlan"
#define NM_ETHTOOL_OPTNAME_FEATURE_GRO "feature-gro"
#define NM_ETHTOOL_OPTNAME_FEATURE_GSO "feature-gso"
#define NM_ETHTOOL_OPTNAME_FEATURE_LRO "feature-lro"
#define NM_ETHTOOL_OPTNAME_FEATURE_NTUPLE "feature-ntuple"
#define NM_ETHTOOL_OPTNAME_FEATURE_RX "feature-rx"
#define NM_ETHTOOL_OPTNAME_FEATURE_RXHASH "feature-rxhash"
#define NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN "feature-rxvlan"
#define NM_ETHTOOL_OPTNAME_FEATURE_SG "feature-sg"
#define NM_ETHTOOL_OPTNAME_FEATURE_TSO "feature-tso"
#define NM_ETHTOOL_OPTNAME_FEATURE_TX "feature-tx"
#define NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN "feature-txvlan"
#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP6_SEGMENTATION "feature-tx-tcp6-segmentation"
#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_SEGMENTATION "feature-tx-tcp-segmentation"
gboolean nm_ethtool_optname_is_feature (const char *optname);
......
......@@ -46,6 +46,8 @@ const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = {
ETHT_DATA (FEATURE_TSO),
ETHT_DATA (FEATURE_TX),
ETHT_DATA (FEATURE_TXVLAN),
ETHT_DATA (FEATURE_TX_TCP6_SEGMENTATION),
ETHT_DATA (FEATURE_TX_TCP_SEGMENTATION),
[_NM_ETHTOOL_ID_NUM] = NULL,
};
......@@ -61,6 +63,8 @@ const guint8 const _by_name[_NM_ETHTOOL_ID_NUM] = {
NM_ETHTOOL_ID_FEATURE_SG,
NM_ETHTOOL_ID_FEATURE_TSO,
NM_ETHTOOL_ID_FEATURE_TX,
NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION,
NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION,
NM_ETHTOOL_ID_FEATURE_TXVLAN,
};
......
......@@ -40,7 +40,9 @@ typedef enum {
NM_ETHTOOL_ID_FEATURE_TSO,
NM_ETHTOOL_ID_FEATURE_TX,
NM_ETHTOOL_ID_FEATURE_TXVLAN,
_NM_ETHTOOL_ID_FEATURE_LAST = NM_ETHTOOL_ID_FEATURE_TXVLAN,
NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION,
NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION,
_NM_ETHTOOL_ID_FEATURE_LAST = NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION,
_NM_ETHTOOL_ID_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1),
_NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_FEATURE_LAST,
......
This diff is collapsed.
......@@ -23,6 +23,9 @@
#include "nm-platform.h"
#include "nm-setting-wired.h"
#include "nm-ethtool-utils.h"
/*****************************************************************************/
const char *nmp_utils_ethtool_get_driver (int ifindex);
gboolean nmp_utils_ethtool_supports_carrier_detect (int ifindex);
......@@ -35,6 +38,10 @@ gboolean nmp_utils_ethtool_set_wake_on_lan (int ifindex, NMSettingWiredWakeOnLan
gboolean nmp_utils_ethtool_get_link_settings (int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex);
gboolean nmp_utils_ethtool_set_link_settings (int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex);
gboolean nmp_utils_ethtool_get_permanent_address (int ifindex,
guint8 *buf,
size_t *length);
typedef struct {
/* We don't want to include <linux/ethtool.h> in header files,
* thus create a ABI compatible version of struct ethtool_drvinfo.*/
......@@ -55,9 +62,52 @@ typedef struct {
gboolean nmp_utils_ethtool_get_driver_info (int ifindex,
NMPUtilsEthtoolDriverInfo *data);
gboolean nmp_utils_ethtool_get_permanent_address (int ifindex,
guint8 *buf,
size_t *length);
typedef struct {
NMEthtoolID ethtool_id;
guint8 n_kernel_names;
/* one NMEthtoolID refers to one or more kernel_names. The reason for supporting this complexity
* (where one NMSettingEthtool option refers to multiple kernel features) is to follow what
* ethtool does, where "tx" is an alias for multiple features. */
const char *const*kernel_names;
} NMEthtoolFeatureInfo;
typedef struct {
const NMEthtoolFeatureInfo *info;
guint idx_ss_features;
/* one NMEthtoolFeatureInfo references one or more kernel_names. This is the index
* of the matching info->kernel_names */
guint8 idx_kernel_name;
bool available:1;
bool requested:1;
bool active:1;
bool never_changed:1;
} NMEthtoolFeatureState;
struct _NMEthtoolFeatureStates {
guint n_states;
guint n_ss_features;
/* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */
const NMEthtoolFeatureState *const*states_indexed[_NM_ETHTOOL_ID_FEATURE_NUM];
/* the same content, here as a list of n_states entries. */
const NMEthtoolFeatureState states_list[];
};
NMEthtoolFeatureStates *nmp_utils_ethtool_get_features (int ifindex);
gboolean nmp_utils_ethtool_set_features (int ifindex,
const NMEthtoolFeatureStates *features,
const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */,
gboolean do_set /* or reset */);
/*****************************************************************************/
gboolean nmp_utils_mii_supports_carrier_detect (int ifindex);
......
......@@ -3213,6 +3213,32 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_platform_link_duplex_type_to_string, NMPlatformLi
/*****************************************************************************/
NMEthtoolFeatureStates *
nm_platform_ethtool_get_link_features (NMPlatform *self, int ifindex)
{
_CHECK_SELF_NETNS (self, klass, netns, NULL);
g_return_val_if_fail (ifindex > 0, NULL);
return nmp_utils_ethtool_get_features (ifindex);
}
gboolean
nm_platform_ethtool_set_features (NMPlatform *self,
int ifindex,
const NMEthtoolFeatureStates *features,
const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */,
gboolean do_set /* or reset */)
{
_CHECK_SELF_NETNS (self, klass, netns, FALSE);
g_return_val_if_fail (ifindex > 0, FALSE);
return nmp_utils_ethtool_set_features (ifindex, features, requested, do_set);
}
/*****************************************************************************/
const NMDedupMultiHeadEntry *
nm_platform_lookup_all (NMPlatform *platform,
NMPCacheIdType cache_id_type,
......
......@@ -1534,6 +1534,17 @@ int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPla
gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password);
gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex);
gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex);
typedef struct _NMEthtoolFeatureStates NMEthtoolFeatureStates;
NMEthtoolFeatureStates *nm_platform_ethtool_get_link_features (NMPlatform *self,
int ifindex);
gboolean nm_platform_ethtool_set_features (NMPlatform *self,
int ifindex,
const NMEthtoolFeatureStates *features,
const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */,
gboolean do_set /* or reset */);
const char * nm_platform_link_duplex_type_to_string (NMPlatformLinkDuplexType duplex);
void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self,
......
......@@ -2713,6 +2713,102 @@ test_sysctl_netns_switch (void)
/*****************************************************************************/
static void
ethtool_features_dump (const NMEthtoolFeatureStates *features)
{
guint i, j;
g_assert (features);
_LOGT (">>> %u features (%u ss-features)", features->n_states, features->n_ss_features);
for (i = 0; i < features->n_states; i++) {
const NMEthtoolFeatureState *s = &features->states_list[i];
_LOGT (">>> feature-list[%3u]: %3d = %-32s (%3u) | %s %s %s %s",
i,
(int) s->info->ethtool_id,
s->info->kernel_names[s->idx_kernel_name],
s->idx_ss_features,
s->active ? "ACT" : "act",
s->available ? "AVA" : "ava",
s->never_changed ? "NCH" : "nch",
s->requested ? "REQ" : "req");
}
for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) {
_LOGT (">>> feature-idx [%3u]: %-32s = %u features",
i + (guint) _NM_ETHTOOL_ID_FEATURE_FIRST,
nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname,
(guint) NM_PTRARRAY_LEN (features->states_indexed[i]));
for (j = 0; features->states_indexed[i] && features->states_indexed[i][j]; j++) {
const NMEthtoolFeatureState *s = features->states_indexed[i][j];
_LOGT (">>> %3u: %-32s | %s %s %s %s",
j,
s->info->kernel_names[s->idx_kernel_name],
s->active ? "ACT" : "act",
s->available ? "AVA" : "ava",
s->never_changed ? "NCH" : "nch",
s->requested ? "REQ" : "req");
}
}
}
static void
test_ethtool_features_get (void)
{
gs_unref_ptrarray GPtrArray *gfree_keeper = g_ptr_array_new_with_free_func (g_free);
const int IFINDEX = 1;
guint i;
guint i_run;
for (i_run = 0; i_run < 5; i_run++) {
NMEthtoolFeatureStates *features;
NMTernary *requested;
gboolean do_set = TRUE;
requested = g_new (NMTernary, _NM_ETHTOOL_ID_FEATURE_NUM);
for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++)
requested[i] = NM_TERNARY_DEFAULT;
g_ptr_array_add (gfree_keeper, requested);
if (i_run == 0) {
requested[NM_ETHTOOL_ID_FEATURE_RX] = NM_TERNARY_FALSE;
requested[NM_ETHTOOL_ID_FEATURE_TSO] = NM_TERNARY_FALSE;
requested[NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION] = NM_TERNARY_FALSE;
} else if (i_run == 1)
do_set = FALSE;
else if (i_run == 2) {
requested[NM_ETHTOOL_ID_FEATURE_TSO] = NM_TERNARY_FALSE;
requested[NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION] = NM_TERNARY_TRUE;
} else if (i_run == 3)
do_set = FALSE;
_LOGT (">>> ethtool-features-get RUN %u (do-set=%s", i_run, do_set ? "set" : "reset");
features = nmp_utils_ethtool_get_features (IFINDEX);
g_ptr_array_add (gfree_keeper, features);
ethtool_features_dump (features);
if (_LOGT_ENABLED ()) {
int ignore;
ignore = system ("ethtool -k lo");
(void) ignore;
}
if (!do_set) {
requested = gfree_keeper->pdata[i_run * 2 - 2];
features = gfree_keeper->pdata[i_run * 2 - 1];
}
nmp_utils_ethtool_set_features (IFINDEX, features, requested, do_set);
}
}
/*****************************************************************************/
NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
void
......@@ -2774,5 +2870,7 @@ _nmtstp_setup_tests (void)
g_test_add_func ("/general/sysctl/rename", test_sysctl_rename);
g_test_add_func ("/general/sysctl/netns-switch", test_sysctl_netns_switch);
g_test_add_func ("/link/ethtool/features/get", test_ethtool_features_get);
}
}
......@@ -477,17 +477,19 @@ const char *const _nm_ethtool_ifcfg_names[] = {
#define ETHT_NAME(eid, ename) \
[eid - _NM_ETHTOOL_ID_FEATURE_FIRST] = ""ename""
/* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GRO, "gro"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GSO, "gso"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_LRO, "lro"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_NTUPLE, "ntuple"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX, "rx"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXHASH, "rxhash"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXVLAN, "rxvlan"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_SG, "sg"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TSO, "tso"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX, "tx"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TXVLAN, "txvlan"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GRO, "gro"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GSO, "gso"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_LRO, "lro"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_NTUPLE, "ntuple"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX, "rx"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXHASH, "rxhash"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXVLAN, "rxvlan"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_SG, "sg"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TSO, "tso"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX, "tx"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TXVLAN, "txvlan"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"),
ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"),
};
const NMEthtoolData *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment