From cdc9be29b1f4eefd5d384b0a9fed25675c66def5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 7 Feb 2025 13:47:36 +0900 Subject: [PATCH] udev/net: support to configure Energy Efficient Ethernet settings Closes #36278. --- man/systemd.link.xml | 52 ++++++++++++++++++++++++++ src/shared/ethtool-util.c | 56 ++++++++++++++++++++++++++++ src/shared/ethtool-util.h | 8 ++++ src/udev/net/link-config-gperf.gperf | 5 +++ src/udev/net/link-config.c | 7 ++++ src/udev/net/link-config.h | 6 +++ 6 files changed, 134 insertions(+) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 9ddcaf7c09d..42ad2c27eae 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -1361,6 +1361,58 @@ + + [EnergyEfficientEthernet] Section Options + The [EnergyEfficientEthernet] section controls the Energy Efficient Ethernet (EEE) feature of the + interface, and accepts the following keys. + + + + Enabled= + + Takes a boolean argument. When true, the Energy Efficient Ethernet (EEE) feature will be + enabled on the interface. Defaults to unset, and the enablement of EEE will be unchanged. + + + + + + + TxLowPowerIdle= + + Takes a boolean argument. When true, the transmit Low Power Idle (Tx-LPI) mode of the Energy + Efficient Ethernet feature will be enabled on the interface. Defaults to unset, and the enablement + of the mode will be unchanged. + + + + + + + TxLowPowerIdleSec= + + Takes a timespan. This configures how long the interface should not enter the Low Power Idle + mode after transmission. If it is too short, may decrease performance. If it is too long, may not + gain energy saving. Defaults to unset, and the timespan will be unchanged. + + + + + + + LinkMode= + + Takes a list of link modes, e.g. 1000baset-full. See the table for + Advertise= setting in [Link] section in the above for possible values. This + configures the Energy Efficient Ethernet capable connection modes to be advertised. Defaults to + unset, and the advertised modes will be unchanged. + + + + + + + Specifiers diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index aeed917a6a5..b29d2641ef2 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -1123,6 +1123,62 @@ int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr)); } +int ethtool_set_eee_settings( + int *ethtool_fd, + const char *ifname, + int enabled, + int tx_lpi_enabled, + usec_t tx_lpi_timer_usec, + uint32_t advertise) { + + int r; + + assert(ethtool_fd); + assert(ifname); + + if (enabled < 0 && + tx_lpi_enabled < 0 && + tx_lpi_timer_usec == USEC_INFINITY && + advertise == 0) + return 0; /* Nothing requested. */ + + r = ethtool_connect(ethtool_fd); + if (r < 0) + return r; + + struct ethtool_eee ecmd = { + .cmd = ETHTOOL_GEEE, + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd, + }; + + strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); + + if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) + return -errno; + + if (ecmd.supported == 0) + return 0; /* Unsupported. */ + + bool need_update = false; + + if (enabled >= 0) + UPDATE(ecmd.eee_enabled, (uint32_t) enabled, need_update); + if (tx_lpi_enabled >= 0) + UPDATE(ecmd.tx_lpi_enabled, (uint32_t) tx_lpi_enabled, need_update); + if (tx_lpi_timer_usec != USEC_INFINITY) + UPDATE(ecmd.tx_lpi_timer, (uint32_t) MIN(DIV_ROUND_UP(tx_lpi_timer_usec, USEC_PER_MSEC), (usec_t) UINT32_MAX), need_update); + if (advertise != 0) + UPDATE(ecmd.advertised, advertise & ecmd.supported, need_update); + + if (!need_update) + return 0; /* Nothing changed. */ + + ecmd.cmd = ETHTOOL_SEEE; + return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr)); +} + int config_parse_advertise( const char *unit, const char *filename, diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h index d431b4452ce..9aa36da227a 100644 --- a/src/shared/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -7,6 +7,7 @@ #include "conf-parser.h" #include "ether-addr-util.h" +#include "time-util.h" #define N_ADVERTISE 4 @@ -180,6 +181,13 @@ int ethtool_set_glinksettings( int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels); int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg); int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce); +int ethtool_set_eee_settings( + int *ethtool_fd, + const char *ifname, + int enabled, + int tx_lpi_enabled, + usec_t tx_lpi_timer_usec, + uint32_t advertise); const char* duplex_to_string(Duplex d) _const_; Duplex duplex_from_string(const char *d) _pure_; diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 8535562998e..4f4a016a425 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -135,3 +135,8 @@ SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, SR-IOV.Trust, config_parse_sr_iov_boolean, 0, offsetof(LinkConfig, sr_iov_by_section) SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, offsetof(LinkConfig, sr_iov_by_section) SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, offsetof(LinkConfig, sr_iov_by_section) +/* ethtool EEE settings */ +EnergyEfficientEthernet.Enable, config_parse_tristate, 0, offsetof(LinkConfig, eee_enabled) +EnergyEfficientEthernet.TxLowPowerIdle, config_parse_tristate, 0, offsetof(LinkConfig, eee_tx_lpi_enabled) +EnergyEfficientEthernet.TxLowPowerIdleSec, config_parse_sec, 0, offsetof(LinkConfig, eee_tx_lpi_timer_usec) +EnergyEfficientEthernet.LinkMode, config_parse_advertise, 0, offsetof(LinkConfig, eee_advertise) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 5a4b7261bbd..3400286566f 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -263,6 +263,9 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { .coalesce.use_adaptive_tx_coalesce = -1, .mdi = ETH_TP_MDI_INVALID, .sr_iov_num_vfs = UINT32_MAX, + .eee_enabled = -1, + .eee_tx_lpi_enabled = -1, + .eee_tx_lpi_timer_usec = USEC_INFINITY, }; FOREACH_ELEMENT(feature, config->features) @@ -548,6 +551,10 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) { if (r < 0) log_link_warning_errno(link, r, "Could not set coalesce settings, ignoring: %m"); + r = ethtool_set_eee_settings(ethtool_fd, name, config->eee_enabled, config->eee_tx_lpi_enabled, config->eee_tx_lpi_timer_usec, config->eee_advertise[0]); + if (r < 0) + log_link_warning_errno(link, r, "Could not set energy efficient ethernet settings, ignoring: %m"); + return 0; } diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 6f4a1b1a63d..dcbc61359c5 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -107,6 +107,12 @@ struct LinkConfig { /* ethtool coalesce settings */ netdev_coalesce_param coalesce; + /* ethtool energy efficient ethernet settings */ + int eee_enabled; + int eee_tx_lpi_enabled; + usec_t eee_tx_lpi_timer_usec; + uint32_t eee_advertise[N_ADVERTISE]; + /* Rx RPS CPU mask */ CPUSet *rps_cpu_mask;