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 61e7d624101..4f4a016a425 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -39,9 +39,11 @@ Match.Credential, config_parse_net_condition,
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(LinkConfig, conditions)
Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
+/* udev property */
Link.Property, config_parse_udev_property, 0, offsetof(LinkConfig, properties)
Link.ImportProperty, config_parse_udev_property_name, 0, offsetof(LinkConfig, import_properties)
Link.UnsetProperty, config_parse_udev_property_name, 0, offsetof(LinkConfig, unset_properties)
+/* rtnl setlink */
Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy)
Link.MACAddress, config_parse_hw_addr, 0, offsetof(LinkConfig, hw_addr)
Link.NamePolicy, config_parse_name_policy, 0, offsetof(LinkConfig, name_policy)
@@ -53,12 +55,19 @@ Link.TransmitQueues, config_parse_rx_tx_queues,
Link.ReceiveQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, rxqueues)
Link.TransmitQueueLength, config_parse_txqueuelen, 0, offsetof(LinkConfig, txqueuelen)
Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(LinkConfig, mtu)
+Link.GenericSegmentOffloadMaxBytes, config_parse_iec_size, 0, offsetof(LinkConfig, gso_max_size)
+Link.GenericSegmentOffloadMaxSegments, config_parse_uint32, 0, offsetof(LinkConfig, gso_max_segments)
+/* ethtool link settings */
Link.BitsPerSecond, config_parse_si_uint64, 0, offsetof(LinkConfig, speed)
Link.Duplex, config_parse_duplex, 0, offsetof(LinkConfig, duplex)
Link.AutoNegotiation, config_parse_tristate, 0, offsetof(LinkConfig, autonegotiation)
+Link.Advertise, config_parse_advertise, 0, offsetof(LinkConfig, advertise)
+Link.Port, config_parse_port, 0, offsetof(LinkConfig, port)
+Link.MDI, config_parse_mdi, 0, offsetof(LinkConfig, mdi)
+/* ethtool WoL settings */
Link.WakeOnLan, config_parse_wol, 0, offsetof(LinkConfig, wol)
Link.WakeOnLanPassword, config_parse_wol_password, 0, 0
-Link.Port, config_parse_port, 0, offsetof(LinkConfig, port)
+/* ethtool features */
Link.ReceiveChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_RXCSUM])
Link.TransmitChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TXCSUM])
Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
@@ -76,20 +85,21 @@ Link.TransmitVLANSTAGHardwareAcceleration, config_parse_tristate,
Link.NTupleFilter, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_NTUPLE])
Link.ReceiveFCS, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_RXFCS])
Link.ReceiveAll, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_RXALL])
+/* ethtool channels */
Link.RxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.rx)
Link.TxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.tx)
Link.OtherChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.other)
Link.CombinedChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.combined)
-Link.Advertise, config_parse_advertise, 0, offsetof(LinkConfig, advertise)
+/* ethtool ring parameters */
Link.RxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx)
Link.RxMiniBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_mini)
Link.RxJumboBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_jumbo)
Link.TxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.tx)
+/* ethtool pause parameters */
Link.RxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, rx_flow_control)
Link.TxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, tx_flow_control)
Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, autoneg_flow_control)
-Link.GenericSegmentOffloadMaxBytes, config_parse_iec_size, 0, offsetof(LinkConfig, gso_max_size)
-Link.GenericSegmentOffloadMaxSegments, config_parse_uint32, 0, offsetof(LinkConfig, gso_max_segments)
+/* ethtool coalesce settings */
Link.RxCoalesceSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs)
Link.RxMaxCoalescedFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames)
Link.RxCoalesceIrqSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs_irq)
@@ -112,8 +122,9 @@ Link.RxMaxCoalescedHighFrames, config_parse_coalesce_u32,
Link.TxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high)
Link.TxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high)
Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rate_sample_interval)
+/* Rx RPS CPU mask */
Link.ReceivePacketSteeringCPUMask, config_parse_rps_cpu_mask, 0, offsetof(LinkConfig, rps_cpu_mask)
-Link.MDI, config_parse_mdi, 0, offsetof(LinkConfig, mdi)
+/* SR-IOV settings */
Link.SR-IOVVirtualFunctions, config_parse_sr_iov_num_vfs, 0, offsetof(LinkConfig, sr_iov_num_vfs)
SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section)
SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section)
@@ -124,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 fd796bbaa53..dcbc61359c5 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -56,9 +56,13 @@ struct LinkConfig {
LIST_HEAD(Condition, conditions);
char *description;
+
+ /* udev property */
char **properties;
char **import_properties;
char **unset_properties;
+
+ /* rtnl setlink */
struct hw_addr_data hw_addr;
MACAddressPolicy mac_address_policy;
NamePolicy *name_policy;
@@ -72,24 +76,47 @@ struct LinkConfig {
uint32_t mtu;
uint32_t gso_max_segments;
size_t gso_max_size;
+
+ /* ethtool link settings */
uint64_t speed;
Duplex duplex;
int autonegotiation;
uint32_t advertise[N_ADVERTISE];
+ NetDevPort port;
+ uint8_t mdi;
+
+ /* ethtool WoL */
uint32_t wol;
char *wol_password_file;
uint8_t *wol_password;
- NetDevPort port;
+
+ /* ethtool features */
int features[_NET_DEV_FEAT_MAX];
+
+ /* ethtool channels */
netdev_channels channels;
+
+ /* ethtool ring parameters */
netdev_ring_param ring;
+
+ /* ethtool pause parameters */
int rx_flow_control;
int tx_flow_control;
int autoneg_flow_control;
+
+ /* ethtool coalesce settings */
netdev_coalesce_param coalesce;
- uint8_t mdi;
+
+ /* 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;
+ /* SR-IOV */
uint32_t sr_iov_num_vfs;
OrderedHashmap *sr_iov_by_section;