1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <linux/ethtool.h>
6 #include <linux/netdevice.h>
7 #include <linux/sockios.h>
9 #include "conf-parser.h"
10 #include "ethtool-util.h"
11 #include "extract-word.h"
14 #include "memory-util.h"
15 #include "socket-util.h"
16 #include "string-table.h"
20 static const char* const duplex_table
[_DUP_MAX
] = {
25 DEFINE_STRING_TABLE_LOOKUP(duplex
, Duplex
);
26 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex
, duplex
, Duplex
, "Failed to parse duplex setting");
31 } wol_option_map
[] = {
33 { WAKE_UCAST
, "unicast", },
34 { WAKE_MCAST
, "multicast", },
35 { WAKE_BCAST
, "broadcast", },
37 { WAKE_MAGIC
, "magic", },
38 { WAKE_MAGICSECURE
, "secureon", },
41 int wol_options_to_string_alloc(uint32_t opts
, char **ret
) {
42 _cleanup_free_
char *str
= NULL
;
46 if (opts
== UINT32_MAX
) {
51 for (size_t i
= 0; i
< ELEMENTSOF(wol_option_map
); i
++)
52 if (opts
& wol_option_map
[i
].opt
&&
53 !strextend_with_separator(&str
, ",", wol_option_map
[i
].name
))
66 static const char* const port_table
[] = {
67 [NET_DEV_PORT_TP
] = "tp",
68 [NET_DEV_PORT_AUI
] = "aui",
69 [NET_DEV_PORT_MII
] = "mii",
70 [NET_DEV_PORT_FIBRE
] = "fibre",
71 [NET_DEV_PORT_BNC
] = "bnc",
74 DEFINE_STRING_TABLE_LOOKUP(port
, NetDevPort
);
75 DEFINE_CONFIG_PARSE_ENUM(config_parse_port
, port
, NetDevPort
, "Failed to parse Port setting");
77 static const char* const mdi_table
[] = {
78 [ETH_TP_MDI_INVALID
] = "unknown",
80 [ETH_TP_MDI_X
] = "mdi-x",
81 [ETH_TP_MDI_AUTO
] = "auto",
84 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi
, int);
86 static const char* const netdev_feature_table
[_NET_DEV_FEAT_MAX
] = {
87 [NET_DEV_FEAT_SG
] = "tx-scatter-gather",
88 [NET_DEV_FEAT_IP_CSUM
] = "tx-checksum-ipv4",
89 [NET_DEV_FEAT_HW_CSUM
] = "tx-checksum-ip-generic",
90 [NET_DEV_FEAT_IPV6_CSUM
] = "tx-checksum-ipv6",
91 [NET_DEV_FEAT_HIGHDMA
] = "highdma",
92 [NET_DEV_FEAT_FRAGLIST
] = "tx-scatter-gather-fraglist",
93 [NET_DEV_FEAT_HW_VLAN_CTAG_TX
] = "tx-vlan-hw-insert",
94 [NET_DEV_FEAT_HW_VLAN_CTAG_RX
] = "rx-vlan-hw-parse",
95 [NET_DEV_FEAT_HW_VLAN_CTAG_FILTER
] = "rx-vlan-filter",
96 [NET_DEV_FEAT_HW_VLAN_STAG_TX
] = "tx-vlan-stag-hw-insert",
97 [NET_DEV_FEAT_HW_VLAN_STAG_RX
] = "rx-vlan-stag-hw-parse",
98 [NET_DEV_FEAT_HW_VLAN_STAG_FILTER
] = "rx-vlan-stag-filter",
99 [NET_DEV_FEAT_VLAN_CHALLENGED
] = "vlan-challenged",
100 [NET_DEV_FEAT_GSO
] = "tx-generic-segmentation",
101 [NET_DEV_FEAT_LLTX
] = "tx-lockless",
102 [NET_DEV_FEAT_NETNS_LOCAL
] = "netns-local",
103 [NET_DEV_FEAT_GRO
] = "rx-gro",
104 [NET_DEV_FEAT_GRO_HW
] = "rx-gro-hw",
105 [NET_DEV_FEAT_LRO
] = "rx-lro",
106 [NET_DEV_FEAT_TSO
] = "tx-tcp-segmentation",
107 [NET_DEV_FEAT_GSO_ROBUST
] = "tx-gso-robust",
108 [NET_DEV_FEAT_TSO_ECN
] = "tx-tcp-ecn-segmentation",
109 [NET_DEV_FEAT_TSO_MANGLEID
] = "tx-tcp-mangleid-segmentation",
110 [NET_DEV_FEAT_TSO6
] = "tx-tcp6-segmentation",
111 [NET_DEV_FEAT_FSO
] = "tx-fcoe-segmentation",
112 [NET_DEV_FEAT_GSO_GRE
] = "tx-gre-segmentation",
113 [NET_DEV_FEAT_GSO_GRE_CSUM
] = "tx-gre-csum-segmentation",
114 [NET_DEV_FEAT_GSO_IPXIP4
] = "tx-ipxip4-segmentation",
115 [NET_DEV_FEAT_GSO_IPXIP6
] = "tx-ipxip6-segmentation",
116 [NET_DEV_FEAT_GSO_UDP_TUNNEL
] = "tx-udp_tnl-segmentation",
117 [NET_DEV_FEAT_GSO_UDP_TUNNEL_CSUM
] = "tx-udp_tnl-csum-segmentation",
118 [NET_DEV_FEAT_GSO_PARTIAL
] = "tx-gso-partial",
119 [NET_DEV_FEAT_GSO_TUNNEL_REMCSUM
] = "tx-tunnel-remcsum-segmentation",
120 [NET_DEV_FEAT_GSO_SCTP
] = "tx-sctp-segmentation",
121 [NET_DEV_FEAT_GSO_ESP
] = "tx-esp-segmentation",
122 [NET_DEV_FEAT_GSO_UDP_L4
] = "tx-udp-segmentation",
123 [NET_DEV_FEAT_GSO_FRAGLIST
] = "tx-gso-list",
124 [NET_DEV_FEAT_FCOE_CRC
] = "tx-checksum-fcoe-crc",
125 [NET_DEV_FEAT_SCTP_CRC
] = "tx-checksum-sctp",
126 [NET_DEV_FEAT_FCOE_MTU
] = "fcoe-mtu",
127 [NET_DEV_FEAT_NTUPLE
] = "rx-ntuple-filter",
128 [NET_DEV_FEAT_RXHASH
] = "rx-hashing",
129 [NET_DEV_FEAT_RXCSUM
] = "rx-checksum",
130 [NET_DEV_FEAT_NOCACHE_COPY
] = "tx-nocache-copy",
131 [NET_DEV_FEAT_LOOPBACK
] = "loopback",
132 [NET_DEV_FEAT_RXFCS
] = "rx-fcs",
133 [NET_DEV_FEAT_RXALL
] = "rx-all",
134 [NET_DEV_FEAT_HW_L2FW_DOFFLOAD
] = "l2-fwd-offload",
135 [NET_DEV_FEAT_HW_TC
] = "hw-tc-offload",
136 [NET_DEV_FEAT_HW_ESP
] = "esp-hw-offload",
137 [NET_DEV_FEAT_HW_ESP_TX_CSUM
] = "esp-tx-csum-hw-offload",
138 [NET_DEV_FEAT_RX_UDP_TUNNEL_PORT
] = "rx-udp_tunnel-port-offload",
139 [NET_DEV_FEAT_HW_TLS_RECORD
] = "tls-hw-record",
140 [NET_DEV_FEAT_HW_TLS_TX
] = "tls-hw-tx-offload",
141 [NET_DEV_FEAT_HW_TLS_RX
] = "tls-hw-rx-offload",
142 [NET_DEV_FEAT_GRO_FRAGLIST
] = "rx-gro-list",
143 [NET_DEV_FEAT_HW_MACSEC
] = "macsec-hw-offload",
144 [NET_DEV_FEAT_GRO_UDP_FWD
] = "rx-udp-gro-forwarding",
145 [NET_DEV_FEAT_HW_HSR_TAG_INS
] = "hsr-tag-ins-offload",
146 [NET_DEV_FEAT_HW_HSR_TAG_RM
] = "hsr-tag-rm-offload",
147 [NET_DEV_FEAT_HW_HSR_FWD
] = "hsr-fwd-offload",
148 [NET_DEV_FEAT_HW_HSR_DUP
] = "hsr-dup-offload",
150 [NET_DEV_FEAT_TXCSUM
] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
153 static const char* const ethtool_link_mode_bit_table
[] = {
154 [ETHTOOL_LINK_MODE_10baseT_Half_BIT
] = "10baset-half",
155 [ETHTOOL_LINK_MODE_10baseT_Full_BIT
] = "10baset-full",
156 [ETHTOOL_LINK_MODE_100baseT_Half_BIT
] = "100baset-half",
157 [ETHTOOL_LINK_MODE_100baseT_Full_BIT
] = "100baset-full",
158 [ETHTOOL_LINK_MODE_1000baseT_Half_BIT
] = "1000baset-half",
159 [ETHTOOL_LINK_MODE_1000baseT_Full_BIT
] = "1000baset-full",
160 [ETHTOOL_LINK_MODE_Autoneg_BIT
] = "autonegotiation",
161 [ETHTOOL_LINK_MODE_TP_BIT
] = "tp",
162 [ETHTOOL_LINK_MODE_AUI_BIT
] = "aui",
163 [ETHTOOL_LINK_MODE_MII_BIT
] = "mii",
164 [ETHTOOL_LINK_MODE_FIBRE_BIT
] = "fibre",
165 [ETHTOOL_LINK_MODE_BNC_BIT
] = "bnc",
166 [ETHTOOL_LINK_MODE_10000baseT_Full_BIT
] = "10000baset-full",
167 [ETHTOOL_LINK_MODE_Pause_BIT
] = "pause",
168 [ETHTOOL_LINK_MODE_Asym_Pause_BIT
] = "asym-pause",
169 [ETHTOOL_LINK_MODE_2500baseX_Full_BIT
] = "2500basex-full",
170 [ETHTOOL_LINK_MODE_Backplane_BIT
] = "backplane",
171 [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
] = "1000basekx-full",
172 [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT
] = "10000basekx4-full",
173 [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT
] = "10000basekr-full",
174 [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT
] = "10000baser-fec",
175 [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT
] = "20000basemld2-full",
176 [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT
] = "20000basekr2-full",
177 [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT
] = "40000basekr4-full",
178 [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT
] = "40000basecr4-full",
179 [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT
] = "40000basesr4-full",
180 [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT
] = "40000baselr4-full",
181 [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT
] = "56000basekr4-full",
182 [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT
] = "56000basecr4-full",
183 [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT
] = "56000basesr4-full",
184 [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT
] = "56000baselr4-full",
185 [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT
] = "25000basecr-full",
186 [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT
] = "25000basekr-full",
187 [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT
] = "25000basesr-full",
188 [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT
] = "50000basecr2-full",
189 [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT
] = "50000basekr2-full",
190 [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT
] = "100000basekr4-full",
191 [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT
] = "100000basesr4-full",
192 [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT
] = "100000basecr4-full",
193 [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT
] = "100000baselr4-er4-full",
194 [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT
] = "50000basesr2-full",
195 [ETHTOOL_LINK_MODE_1000baseX_Full_BIT
] = "1000basex-full",
196 [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT
] = "10000basecr-full",
197 [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT
] = "10000basesr-full",
198 [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT
] = "10000baselr-full",
199 [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT
] = "10000baselrm-full",
200 [ETHTOOL_LINK_MODE_10000baseER_Full_BIT
] = "10000baseer-full",
201 [ETHTOOL_LINK_MODE_2500baseT_Full_BIT
] = "2500baset-full",
202 [ETHTOOL_LINK_MODE_5000baseT_Full_BIT
] = "5000baset-full",
203 [ETHTOOL_LINK_MODE_FEC_NONE_BIT
] = "fec-none",
204 [ETHTOOL_LINK_MODE_FEC_RS_BIT
] = "fec-rs",
205 [ETHTOOL_LINK_MODE_FEC_BASER_BIT
] = "fec-baser",
206 [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT
] = "50000basekr-full",
207 [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT
] = "50000basesr-full",
208 [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT
] = "50000basecr-full",
209 [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT
] = "50000baselr-er-fr-full",
210 [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT
] = "50000basedr-full",
211 [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT
] = "100000basekr2-full",
212 [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT
] = "100000basesr2-full",
213 [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT
] = "100000basecr2-full",
214 [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT
] = "100000baselr2-er2-fr2-full",
215 [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT
] = "100000basedr2-full",
216 [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT
] = "200000basekr4-full",
217 [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT
] = "200000basesr4-full",
218 [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT
] = "200000baselr4-er4-fr4-full",
219 [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT
] = "200000basedr4-full",
220 [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT
] = "200000basecr4-full",
221 [ETHTOOL_LINK_MODE_100baseT1_Full_BIT
] = "100baset1-full",
222 [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT
] = "1000baset1-full",
223 [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT
] = "400000basekr8-full",
224 [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT
] = "400000basesr8-full",
225 [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT
] = "400000baselr8-er8-fr8-full",
226 [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT
] = "400000basedr8-full",
227 [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT
] = "400000basecr8-full",
228 [ETHTOOL_LINK_MODE_FEC_LLRS_BIT
] = "fec-llrs",
229 [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT
] = "100000basekr-full",
230 [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT
] = "100000basesr-full",
231 [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT
] = "100000baselr-er-fr-full",
232 [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT
] = "100000basecr-full",
233 [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT
] = "100000basedr-full",
234 [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT
] = "200000basekr2-full",
235 [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT
] = "200000basesr2-full",
236 [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT
] = "200000baselr2-er2-fr2-full",
237 [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT
] = "200000basedr2-full",
238 [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT
] = "200000basecr2-full",
239 [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT
] = "400000basekr4-full",
240 [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT
] = "400000basesr4-full",
241 [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT
] = "400000baselr4-er4-fr4-full",
242 [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT
] = "400000basedr4-full",
243 [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT
] = "400000basecr4-full",
244 [ETHTOOL_LINK_MODE_100baseFX_Half_BIT
] = "100basefx-half",
245 [ETHTOOL_LINK_MODE_100baseFX_Full_BIT
] = "100basefx-full",
247 /* Make sure the array is large enough to fit all bits */
248 assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table
)-1) / 32 < N_ADVERTISE
);
250 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit
, enum ethtool_link_mode_bit_indices
);
252 static int ethtool_connect(int *ethtool_fd
) {
257 /* This does nothing if already connected. */
258 if (*ethtool_fd
>= 0)
261 fd
= socket_ioctl_fd();
263 return log_debug_errno(fd
, "ethtool: could not create control socket: %m");
269 int ethtool_get_driver(int *ethtool_fd
, const char *ifname
, char **ret
) {
270 struct ethtool_drvinfo ecmd
= {
271 .cmd
= ETHTOOL_GDRVINFO
,
274 .ifr_data
= (void*) &ecmd
,
283 r
= ethtool_connect(ethtool_fd
);
287 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
289 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
292 if (isempty(ecmd
.driver
))
295 d
= strdup(ecmd
.driver
);
303 int ethtool_get_link_info(
306 int *ret_autonegotiation
,
309 NetDevPort
*ret_port
) {
311 struct ethtool_cmd ecmd
= {
315 .ifr_data
= (void*) &ecmd
,
322 r
= ethtool_connect(ethtool_fd
);
326 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
328 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
331 if (ret_autonegotiation
)
332 *ret_autonegotiation
= ecmd
.autoneg
;
337 speed
= ethtool_cmd_speed(&ecmd
);
338 *ret_speed
= speed
== (uint32_t) SPEED_UNKNOWN
?
339 UINT64_MAX
: (uint64_t) speed
* 1000 * 1000;
343 *ret_duplex
= ecmd
.duplex
;
346 *ret_port
= ecmd
.port
;
351 int ethtool_get_permanent_hw_addr(int *ethtool_fd
, const char *ifname
, struct hw_addr_data
*ret
) {
352 _cleanup_close_
int fd
= -EBADF
;
354 struct ethtool_perm_addr addr
;
355 uint8_t space
[HW_ADDR_MAX_SIZE
];
357 .addr
.cmd
= ETHTOOL_GPERMADDR
,
358 .addr
.size
= HW_ADDR_MAX_SIZE
,
361 .ifr_data
= (caddr_t
) &epaddr
,
370 r
= ethtool_connect(ethtool_fd
);
374 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
376 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
379 if (epaddr
.addr
.size
== 0)
382 if (epaddr
.addr
.size
> HW_ADDR_MAX_SIZE
)
385 ret
->length
= epaddr
.addr
.size
;
386 memcpy(ret
->bytes
, epaddr
.addr
.data
, epaddr
.addr
.size
);
390 #define UPDATE(dest, val, updated) \
392 typeof(val) _v = (val); \
398 #define UPDATE_WITH_MAX(dest, max, val, updated) \
400 typeof(dest) _v = (val); \
401 typeof(dest) _max = (max); \
402 if (_v == 0 || _v > _max) \
413 const uint8_t password
[SOPASS_MAX
]) {
415 struct ethtool_wolinfo ecmd
= {
419 .ifr_data
= (void*) &ecmd
,
421 bool need_update
= false;
427 if (wolopts
== UINT32_MAX
&& !password
)
428 /* Nothing requested. Return earlier. */
431 r
= ethtool_connect(ethtool_fd
);
435 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
439 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
442 if (wolopts
== UINT32_MAX
) {
443 /* When password is specified without valid WoL options specified, then enable
444 * WAKE_MAGICSECURE flag if supported. */
445 wolopts
= ecmd
.wolopts
;
446 if (password
&& FLAGS_SET(ecmd
.supported
, WAKE_MAGICSECURE
))
447 wolopts
|= WAKE_MAGICSECURE
;
450 if ((wolopts
& ~ecmd
.supported
) != 0) {
451 _cleanup_free_
char *str
= NULL
;
453 (void) wol_options_to_string_alloc(wolopts
& ~ecmd
.supported
, &str
);
454 log_debug("Network interface %s does not support requested Wake on LAN options \"%s\", ignoring.",
457 wolopts
&= ecmd
.supported
;
460 if (!FLAGS_SET(wolopts
, WAKE_MAGICSECURE
))
461 /* When WAKE_MAGICSECURE flag is not set, then ignore password. */
464 UPDATE(ecmd
.wolopts
, wolopts
, need_update
);
466 memcmp(ecmd
.sopass
, password
, sizeof(ecmd
.sopass
)) != 0) {
467 memcpy(ecmd
.sopass
, password
, sizeof(ecmd
.sopass
));
474 ecmd
.cmd
= ETHTOOL_SWOL
;
475 return RET_NERRNO(ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
));
478 int ethtool_set_nic_buffer_size(int *ethtool_fd
, const char *ifname
, const netdev_ring_param
*ring
) {
479 struct ethtool_ringparam ecmd
= {
480 .cmd
= ETHTOOL_GRINGPARAM
,
483 .ifr_data
= (void*) &ecmd
,
485 bool need_update
= false;
493 !ring
->rx_mini
.set
&&
494 !ring
->rx_jumbo
.set
&&
498 r
= ethtool_connect(ethtool_fd
);
502 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
504 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
508 UPDATE_WITH_MAX(ecmd
.rx_pending
, ecmd
.rx_max_pending
, ring
->rx
.value
, need_update
);
510 if (ring
->rx_mini
.set
)
511 UPDATE_WITH_MAX(ecmd
.rx_mini_pending
, ecmd
.rx_mini_max_pending
, ring
->rx_mini
.value
, need_update
);
513 if (ring
->rx_jumbo
.set
)
514 UPDATE_WITH_MAX(ecmd
.rx_jumbo_pending
, ecmd
.rx_jumbo_max_pending
, ring
->rx_jumbo
.value
, need_update
);
517 UPDATE_WITH_MAX(ecmd
.tx_pending
, ecmd
.tx_max_pending
, ring
->tx
.value
, need_update
);
522 ecmd
.cmd
= ETHTOOL_SRINGPARAM
;
523 return RET_NERRNO(ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
));
526 static int get_stringset(int ethtool_fd
, const char *ifname
, enum ethtool_stringset stringset_id
, struct ethtool_gstrings
**ret
) {
527 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
529 struct ethtool_sset_info info
;
532 .info
.cmd
= ETHTOOL_GSSET_INFO
,
533 .info
.sset_mask
= UINT64_C(1) << stringset_id
,
536 .ifr_data
= (void*) &buffer
,
540 assert(ethtool_fd
>= 0);
544 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
546 if (ioctl(ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
549 if (buffer
.info
.sset_mask
== 0)
552 #pragma GCC diagnostic push
553 #if HAVE_ZERO_LENGTH_BOUNDS
554 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
556 len
= buffer
.info
.data
[0];
557 #pragma GCC diagnostic pop
561 strings
= malloc0(offsetof(struct ethtool_gstrings
, data
) + len
* ETH_GSTRING_LEN
);
565 strings
->cmd
= ETHTOOL_GSTRINGS
;
566 strings
->string_set
= stringset_id
;
569 ifr
.ifr_data
= (void*) strings
;
571 if (ioctl(ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
574 *ret
= TAKE_PTR(strings
);
578 static int get_features(int ethtool_fd
, const char *ifname
, uint32_t n_features
, struct ethtool_gfeatures
**ret
) {
579 _cleanup_free_
struct ethtool_gfeatures
*gfeatures
= NULL
;
582 assert(ethtool_fd
>= 0);
585 assert(n_features
> 0);
587 gfeatures
= malloc0(offsetof(struct ethtool_gfeatures
, features
) +
588 DIV_ROUND_UP(n_features
, 32U) * sizeof(gfeatures
->features
[0]));
592 gfeatures
->cmd
= ETHTOOL_GFEATURES
;
593 gfeatures
->size
= DIV_ROUND_UP(n_features
, 32U);
595 ifr
= (struct ifreq
) {
596 .ifr_data
= (void*) gfeatures
,
598 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
600 if (ioctl(ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
603 *ret
= TAKE_PTR(gfeatures
);
607 static int set_features_bit(
608 const struct ethtool_gstrings
*strings
,
609 const struct ethtool_gfeatures
*gfeatures
,
610 struct ethtool_sfeatures
*sfeatures
,
622 for (uint32_t i
= 0; i
< strings
->len
; i
++) {
623 uint32_t block
, mask
;
625 if (!strneq((const char*) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
, ETH_GSTRING_LEN
))
629 mask
= UINT32_C(1) << (i
% 32);
631 if (!FLAGS_SET(gfeatures
->features
[block
].available
, mask
) ||
632 FLAGS_SET(gfeatures
->features
[block
].never_changed
, mask
))
635 sfeatures
->features
[block
].valid
|= mask
;
636 SET_FLAG(sfeatures
->features
[block
].requested
, mask
, flag
);
644 static int set_features_multiple_bit(
645 const struct ethtool_gstrings
*strings
,
646 const struct ethtool_gfeatures
*gfeatures
,
647 struct ethtool_sfeatures
*sfeatures
,
662 for (uint32_t i
= 0; i
< strings
->len
; i
++) {
663 uint32_t block
, mask
;
665 if (!startswith((const char*) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
))
669 mask
= UINT32_C(1) << (i
% 32);
671 if (!FLAGS_SET(gfeatures
->features
[block
].available
, mask
) ||
672 FLAGS_SET(gfeatures
->features
[block
].never_changed
, mask
)) {
677 /* The flags is explicitly set by set_features_bit() */
678 if (FLAGS_SET(sfeatures
->features
[block
].valid
, mask
))
681 sfeatures
->features
[block
].valid
|= mask
;
682 SET_FLAG(sfeatures
->features
[block
].requested
, mask
, flag
);
687 return found
? 0 : r
;
690 int ethtool_set_features(int *ethtool_fd
, const char *ifname
, const int features
[static _NET_DEV_FEAT_MAX
]) {
691 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
692 _cleanup_free_
struct ethtool_gfeatures
*gfeatures
= NULL
;
693 _cleanup_free_
struct ethtool_sfeatures
*sfeatures
= NULL
;
702 for (size_t i
= 0; i
< _NET_DEV_FEAT_MAX
; i
++)
703 if (features
[i
] >= 0) {
711 r
= ethtool_connect(ethtool_fd
);
715 r
= get_stringset(*ethtool_fd
, ifname
, ETH_SS_FEATURES
, &strings
);
717 return log_debug_errno(r
, "ethtool: could not get ethtool feature strings: %m");
719 r
= get_features(*ethtool_fd
, ifname
, strings
->len
, &gfeatures
);
721 return log_debug_errno(r
, "ethtool: could not get ethtool features for %s: %m", ifname
);
723 sfeatures
= malloc0(offsetof(struct ethtool_sfeatures
, features
) +
724 DIV_ROUND_UP(strings
->len
, 32U) * sizeof(sfeatures
->features
[0]));
726 return log_oom_debug();
728 sfeatures
->cmd
= ETHTOOL_SFEATURES
;
729 sfeatures
->size
= DIV_ROUND_UP(strings
->len
, 32U);
731 for (size_t i
= 0; i
< _NET_DEV_FEAT_SIMPLE_MAX
; i
++) {
732 r
= set_features_bit(strings
, gfeatures
, sfeatures
, netdev_feature_table
[i
], features
[i
]);
734 log_debug_errno(r
, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table
[i
], ifname
);
737 for (size_t i
= _NET_DEV_FEAT_SIMPLE_MAX
; i
< _NET_DEV_FEAT_MAX
; i
++) {
738 r
= set_features_multiple_bit(strings
, gfeatures
, sfeatures
, netdev_feature_table
[i
], features
[i
]);
740 log_debug_errno(r
, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table
[i
], ifname
);
743 ifr
= (struct ifreq
) {
744 .ifr_data
= (void*) sfeatures
,
746 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
748 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
749 return log_debug_errno(errno
, "ethtool: could not set ethtool features for %s", ifname
);
754 static int get_glinksettings(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**ret
) {
756 struct ethtool_link_settings req
;
757 uint32_t link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
759 .req
.cmd
= ETHTOOL_GLINKSETTINGS
,
761 struct ethtool_link_usettings
*u
;
768 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
769 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
770 agree with user, it returns the bitmap length it is expecting from user as a negative
771 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
772 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
773 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
776 ifr
->ifr_data
= (void *) &ecmd
;
778 if (ioctl(fd
, SIOCETHTOOL
, ifr
) < 0)
781 if (ecmd
.req
.link_mode_masks_nwords
>= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
784 ecmd
.req
.link_mode_masks_nwords
= -ecmd
.req
.link_mode_masks_nwords
;
786 ifr
->ifr_data
= (void *) &ecmd
;
788 if (ioctl(fd
, SIOCETHTOOL
, ifr
) < 0)
791 if (ecmd
.req
.link_mode_masks_nwords
<= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
794 u
= new(struct ethtool_link_usettings
, 1);
798 *u
= (struct ethtool_link_usettings
) {
803 memcpy(u
->link_modes
.supported
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
805 offset
+= ecmd
.req
.link_mode_masks_nwords
;
806 memcpy(u
->link_modes
.advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
808 offset
+= ecmd
.req
.link_mode_masks_nwords
;
809 memcpy(u
->link_modes
.lp_advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
816 static int get_gset(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**ret
) {
817 struct ethtool_link_usettings
*e
;
818 struct ethtool_cmd ecmd
= {
826 ifr
->ifr_data
= (void *) &ecmd
;
828 if (ioctl(fd
, SIOCETHTOOL
, ifr
) < 0)
831 e
= new(struct ethtool_link_usettings
, 1);
835 *e
= (struct ethtool_link_usettings
) {
836 .base
.cmd
= ETHTOOL_GSET
,
837 .base
.link_mode_masks_nwords
= 1,
838 .base
.speed
= ethtool_cmd_speed(&ecmd
),
839 .base
.duplex
= ecmd
.duplex
,
840 .base
.port
= ecmd
.port
,
841 .base
.phy_address
= ecmd
.phy_address
,
842 .base
.autoneg
= ecmd
.autoneg
,
843 .base
.mdio_support
= ecmd
.mdio_support
,
844 .base
.eth_tp_mdix
= ecmd
.eth_tp_mdix
,
845 .base
.eth_tp_mdix_ctrl
= ecmd
.eth_tp_mdix_ctrl
,
847 .link_modes
.supported
[0] = ecmd
.supported
,
848 .link_modes
.advertising
[0] = ecmd
.advertising
,
849 .link_modes
.lp_advertising
[0] = ecmd
.lp_advertising
,
857 static int set_slinksettings(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
859 struct ethtool_link_settings req
;
860 uint32_t link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
868 if (u
->base
.cmd
!= ETHTOOL_GLINKSETTINGS
|| u
->base
.link_mode_masks_nwords
<= 0)
872 ecmd
.req
.cmd
= ETHTOOL_SLINKSETTINGS
;
874 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.supported
, 4 * ecmd
.req
.link_mode_masks_nwords
);
876 offset
+= ecmd
.req
.link_mode_masks_nwords
;
877 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
879 offset
+= ecmd
.req
.link_mode_masks_nwords
;
880 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.lp_advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
882 ifr
->ifr_data
= (void *) &ecmd
;
884 return RET_NERRNO(ioctl(fd
, SIOCETHTOOL
, ifr
));
887 static int set_sset(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
888 struct ethtool_cmd ecmd
= {
896 if (u
->base
.cmd
!= ETHTOOL_GSET
|| u
->base
.link_mode_masks_nwords
<= 0)
899 ecmd
.supported
= u
->link_modes
.supported
[0];
900 ecmd
.advertising
= u
->link_modes
.advertising
[0];
901 ecmd
.lp_advertising
= u
->link_modes
.lp_advertising
[0];
903 ethtool_cmd_speed_set(&ecmd
, u
->base
.speed
);
905 ecmd
.duplex
= u
->base
.duplex
;
906 ecmd
.port
= u
->base
.port
;
907 ecmd
.phy_address
= u
->base
.phy_address
;
908 ecmd
.autoneg
= u
->base
.autoneg
;
909 ecmd
.mdio_support
= u
->base
.mdio_support
;
910 ecmd
.eth_tp_mdix
= u
->base
.eth_tp_mdix
;
911 ecmd
.eth_tp_mdix_ctrl
= u
->base
.eth_tp_mdix_ctrl
;
913 ifr
->ifr_data
= (void *) &ecmd
;
915 return RET_NERRNO(ioctl(fd
, SIOCETHTOOL
, ifr
));
918 int ethtool_set_glinksettings(
922 const uint32_t advertise
[static N_ADVERTISE
],
928 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
929 struct ifreq ifr
= {};
930 bool changed
= false;
937 if (autonegotiation
< 0 && memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
) &&
938 speed
== 0 && duplex
< 0 && port
< 0 && mdi
== ETH_TP_MDI_INVALID
)
941 /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
942 * writable if the driver supports multiple link modes. If it is enabled then they are
943 * read-only. If the link is up they represent the negotiated link mode; if the link is down,
944 * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN
945 * or the best enabled duplex mode. */
947 if (speed
> 0 || duplex
>= 0 || port
>= 0) {
948 if (autonegotiation
== AUTONEG_ENABLE
|| !memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
949 log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings.");
951 duplex
= _DUP_INVALID
;
952 port
= _NET_DEV_PORT_INVALID
;
954 log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation.");
955 autonegotiation
= AUTONEG_DISABLE
;
959 r
= ethtool_connect(fd
);
963 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
965 r
= get_glinksettings(*fd
, &ifr
, &u
);
967 r
= get_gset(*fd
, &ifr
, &u
);
969 return log_debug_errno(r
, "ethtool: Cannot get device settings for %s: %m", ifname
);
973 UPDATE(u
->base
.speed
, DIV_ROUND_UP(speed
, 1000000), changed
);
976 UPDATE(u
->base
.duplex
, duplex
, changed
);
979 UPDATE(u
->base
.port
, port
, changed
);
981 if (autonegotiation
>= 0)
982 UPDATE(u
->base
.autoneg
, autonegotiation
, changed
);
984 if (!memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
985 UPDATE(u
->base
.autoneg
, AUTONEG_ENABLE
, changed
);
988 memcmp(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
) != 0 ||
989 !memeqzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
990 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
991 memcpy(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
);
992 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
993 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
996 if (mdi
!= ETH_TP_MDI_INVALID
) {
997 if (u
->base
.eth_tp_mdix_ctrl
== ETH_TP_MDI_INVALID
)
998 log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname
);
1000 UPDATE(u
->base
.eth_tp_mdix_ctrl
, mdi
, changed
);
1006 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
1007 r
= set_slinksettings(*fd
, &ifr
, u
);
1009 r
= set_sset(*fd
, &ifr
, u
);
1011 return log_debug_errno(r
, "ethtool: Cannot set device settings for %s: %m", ifname
);
1016 int ethtool_set_channels(int *fd
, const char *ifname
, const netdev_channels
*channels
) {
1017 struct ethtool_channels ecmd
= {
1018 .cmd
= ETHTOOL_GCHANNELS
,
1020 struct ifreq ifr
= {
1021 .ifr_data
= (void*) &ecmd
,
1023 bool need_update
= false;
1030 if (!channels
->rx
.set
&&
1031 !channels
->tx
.set
&&
1032 !channels
->other
.set
&&
1033 !channels
->combined
.set
)
1036 r
= ethtool_connect(fd
);
1040 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
1042 if (ioctl(*fd
, SIOCETHTOOL
, &ifr
) < 0)
1045 if (channels
->rx
.set
)
1046 UPDATE_WITH_MAX(ecmd
.rx_count
, ecmd
.max_rx
, channels
->rx
.value
, need_update
);
1048 if (channels
->tx
.set
)
1049 UPDATE_WITH_MAX(ecmd
.tx_count
, ecmd
.max_tx
, channels
->tx
.value
, need_update
);
1051 if (channels
->other
.set
)
1052 UPDATE_WITH_MAX(ecmd
.other_count
, ecmd
.max_other
, channels
->other
.value
, need_update
);
1054 if (channels
->combined
.set
)
1055 UPDATE_WITH_MAX(ecmd
.combined_count
, ecmd
.max_combined
, channels
->combined
.value
, need_update
);
1060 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
1061 return RET_NERRNO(ioctl(*fd
, SIOCETHTOOL
, &ifr
));
1064 int ethtool_set_flow_control(int *fd
, const char *ifname
, int rx
, int tx
, int autoneg
) {
1065 struct ethtool_pauseparam ecmd
= {
1066 .cmd
= ETHTOOL_GPAUSEPARAM
,
1068 struct ifreq ifr
= {
1069 .ifr_data
= (void*) &ecmd
,
1071 bool need_update
= false;
1077 if (rx
< 0 && tx
< 0 && autoneg
< 0)
1080 r
= ethtool_connect(fd
);
1084 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
1086 if (ioctl(*fd
, SIOCETHTOOL
, &ifr
) < 0)
1090 UPDATE(ecmd
.rx_pause
, (uint32_t) rx
, need_update
);
1093 UPDATE(ecmd
.tx_pause
, (uint32_t) tx
, need_update
);
1096 UPDATE(ecmd
.autoneg
, (uint32_t) autoneg
, need_update
);
1101 ecmd
.cmd
= ETHTOOL_SPAUSEPARAM
;
1102 return RET_NERRNO(ioctl(*fd
, SIOCETHTOOL
, &ifr
));
1105 int ethtool_set_nic_coalesce_settings(int *ethtool_fd
, const char *ifname
, const netdev_coalesce_param
*coalesce
) {
1106 struct ethtool_coalesce ecmd
= {
1107 .cmd
= ETHTOOL_GCOALESCE
,
1109 struct ifreq ifr
= {
1110 .ifr_data
= (void*) &ecmd
,
1112 bool need_update
= false;
1119 if (coalesce
->use_adaptive_rx_coalesce
< 0 &&
1120 coalesce
->use_adaptive_tx_coalesce
< 0 &&
1121 !coalesce
->rx_coalesce_usecs
.set
&&
1122 !coalesce
->rx_max_coalesced_frames
.set
&&
1123 !coalesce
->rx_coalesce_usecs_irq
.set
&&
1124 !coalesce
->rx_max_coalesced_frames_irq
.set
&&
1125 !coalesce
->tx_coalesce_usecs
.set
&&
1126 !coalesce
->tx_max_coalesced_frames
.set
&&
1127 !coalesce
->tx_coalesce_usecs_irq
.set
&&
1128 !coalesce
->tx_max_coalesced_frames_irq
.set
&&
1129 !coalesce
->stats_block_coalesce_usecs
.set
&&
1130 !coalesce
->pkt_rate_low
.set
&&
1131 !coalesce
->rx_coalesce_usecs_low
.set
&&
1132 !coalesce
->rx_max_coalesced_frames_low
.set
&&
1133 !coalesce
->tx_coalesce_usecs_low
.set
&&
1134 !coalesce
->tx_max_coalesced_frames_low
.set
&&
1135 !coalesce
->pkt_rate_high
.set
&&
1136 !coalesce
->rx_coalesce_usecs_high
.set
&&
1137 !coalesce
->rx_max_coalesced_frames_high
.set
&&
1138 !coalesce
->tx_coalesce_usecs_high
.set
&&
1139 !coalesce
->tx_max_coalesced_frames_high
.set
&&
1140 !coalesce
->rate_sample_interval
.set
)
1143 r
= ethtool_connect(ethtool_fd
);
1147 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
1149 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
1152 if (coalesce
->use_adaptive_rx_coalesce
>= 0)
1153 UPDATE(ecmd
.use_adaptive_rx_coalesce
, (uint32_t) coalesce
->use_adaptive_rx_coalesce
, need_update
);
1155 if (coalesce
->use_adaptive_tx_coalesce
>= 0)
1156 UPDATE(ecmd
.use_adaptive_tx_coalesce
, (uint32_t) coalesce
->use_adaptive_tx_coalesce
, need_update
);
1158 if (coalesce
->rx_coalesce_usecs
.set
)
1159 UPDATE(ecmd
.rx_coalesce_usecs
, coalesce
->rx_coalesce_usecs
.value
, need_update
);
1161 if (coalesce
->rx_max_coalesced_frames
.set
)
1162 UPDATE(ecmd
.rx_max_coalesced_frames
, coalesce
->rx_max_coalesced_frames
.value
, need_update
);
1164 if (coalesce
->rx_coalesce_usecs_irq
.set
)
1165 UPDATE(ecmd
.rx_coalesce_usecs_irq
, coalesce
->rx_coalesce_usecs_irq
.value
, need_update
);
1167 if (coalesce
->rx_max_coalesced_frames_irq
.set
)
1168 UPDATE(ecmd
.rx_max_coalesced_frames_irq
, coalesce
->rx_max_coalesced_frames_irq
.value
, need_update
);
1170 if (coalesce
->tx_coalesce_usecs
.set
)
1171 UPDATE(ecmd
.tx_coalesce_usecs
, coalesce
->tx_coalesce_usecs
.value
, need_update
);
1173 if (coalesce
->tx_max_coalesced_frames
.set
)
1174 UPDATE(ecmd
.tx_max_coalesced_frames
, coalesce
->tx_max_coalesced_frames
.value
, need_update
);
1176 if (coalesce
->tx_coalesce_usecs_irq
.set
)
1177 UPDATE(ecmd
.tx_coalesce_usecs_irq
, coalesce
->tx_coalesce_usecs_irq
.value
, need_update
);
1179 if (coalesce
->tx_max_coalesced_frames_irq
.set
)
1180 UPDATE(ecmd
.tx_max_coalesced_frames_irq
, coalesce
->tx_max_coalesced_frames_irq
.value
, need_update
);
1182 if (coalesce
->stats_block_coalesce_usecs
.set
)
1183 UPDATE(ecmd
.stats_block_coalesce_usecs
, coalesce
->stats_block_coalesce_usecs
.value
, need_update
);
1185 if (coalesce
->pkt_rate_low
.set
)
1186 UPDATE(ecmd
.pkt_rate_low
, coalesce
->pkt_rate_low
.value
, need_update
);
1188 if (coalesce
->rx_coalesce_usecs_low
.set
)
1189 UPDATE(ecmd
.rx_coalesce_usecs_low
, coalesce
->rx_coalesce_usecs_low
.value
, need_update
);
1191 if (coalesce
->rx_max_coalesced_frames_low
.set
)
1192 UPDATE(ecmd
.rx_max_coalesced_frames_low
, coalesce
->rx_max_coalesced_frames_low
.value
, need_update
);
1194 if (coalesce
->tx_coalesce_usecs_low
.set
)
1195 UPDATE(ecmd
.tx_coalesce_usecs_low
, coalesce
->tx_coalesce_usecs_low
.value
, need_update
);
1197 if (coalesce
->tx_max_coalesced_frames_low
.set
)
1198 UPDATE(ecmd
.tx_max_coalesced_frames_low
, coalesce
->tx_max_coalesced_frames_low
.value
, need_update
);
1200 if (coalesce
->pkt_rate_high
.set
)
1201 UPDATE(ecmd
.pkt_rate_high
, coalesce
->pkt_rate_high
.value
, need_update
);
1203 if (coalesce
->rx_coalesce_usecs_high
.set
)
1204 UPDATE(ecmd
.rx_coalesce_usecs_high
, coalesce
->rx_coalesce_usecs_high
.value
, need_update
);
1206 if (coalesce
->rx_max_coalesced_frames_high
.set
)
1207 UPDATE(ecmd
.rx_max_coalesced_frames_high
, coalesce
->rx_max_coalesced_frames_high
.value
, need_update
);
1209 if (coalesce
->tx_coalesce_usecs_high
.set
)
1210 UPDATE(ecmd
.tx_coalesce_usecs_high
, coalesce
->tx_coalesce_usecs_high
.value
, need_update
);
1212 if (coalesce
->tx_max_coalesced_frames_high
.set
)
1213 UPDATE(ecmd
.tx_max_coalesced_frames_high
, coalesce
->tx_max_coalesced_frames_high
.value
, need_update
);
1215 if (coalesce
->rate_sample_interval
.set
)
1216 UPDATE(ecmd
.rate_sample_interval
, DIV_ROUND_UP(coalesce
->rate_sample_interval
.value
, USEC_PER_SEC
), need_update
);
1221 ecmd
.cmd
= ETHTOOL_SCOALESCE
;
1222 return RET_NERRNO(ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
));
1225 int config_parse_advertise(
1227 const char *filename
,
1229 const char *section
,
1230 unsigned section_line
,
1237 uint32_t *advertise
= ASSERT_PTR(data
);
1245 if (isempty(rvalue
)) {
1246 /* Empty string resets the value. */
1247 memzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
);
1251 for (const char *p
= rvalue
;;) {
1252 _cleanup_free_
char *w
= NULL
;
1253 enum ethtool_link_mode_bit_indices mode
;
1255 r
= extract_first_word(&p
, &w
, NULL
, 0);
1259 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1260 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue
);
1266 mode
= ethtool_link_mode_bit_from_string(w
);
1267 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1268 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1269 if ((int) mode
< 0) {
1270 log_syntax(unit
, LOG_WARNING
, filename
, line
, mode
,
1271 "Failed to parse advertise mode, ignoring: %s", w
);
1275 advertise
[mode
/ 32] |= 1UL << (mode
% 32);
1279 int config_parse_mdi(
1281 const char *filename
,
1283 const char *section
,
1284 unsigned section_line
,
1291 uint8_t *mdi
= ASSERT_PTR(data
);
1296 if (isempty(rvalue
)) {
1297 *mdi
= ETH_TP_MDI_INVALID
;
1301 if (STR_IN_SET(rvalue
, "mdi", "straight")) {
1306 if (STR_IN_SET(rvalue
, "mdi-x", "mdix", "crossover")) {
1307 *mdi
= ETH_TP_MDI_X
;
1311 if (streq(rvalue
, "auto")) {
1312 *mdi
= ETH_TP_MDI_AUTO
;
1316 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1317 "Failed to parse %s= setting, ignoring assignment: %s", lvalue
, rvalue
);
1321 int config_parse_ring_buffer_or_channel(
1323 const char *filename
,
1325 const char *section
,
1326 unsigned section_line
,
1333 u32_opt
*dst
= ASSERT_PTR(data
);
1342 if (isempty(rvalue
)) {
1348 if (streq(rvalue
, "max")) {
1354 r
= safe_atou32(rvalue
, &k
);
1356 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1357 "Failed to parse %s=, ignoring: %s", lvalue
, rvalue
);
1361 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1362 "Invalid %s= value, ignoring: %s", lvalue
, rvalue
);
1371 int config_parse_wol(
1373 const char *filename
,
1375 const char *section
,
1376 unsigned section_line
,
1383 uint32_t new_opts
= 0, *opts
= data
;
1392 if (isempty(rvalue
)) {
1393 *opts
= UINT32_MAX
; /* Do not update WOL option. */
1397 if (streq(rvalue
, "off")) {
1398 *opts
= 0; /* Disable WOL. */
1402 for (const char *p
= rvalue
;;) {
1403 _cleanup_free_
char *w
= NULL
;
1406 r
= extract_first_word(&p
, &w
, NULL
, 0);
1410 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1411 "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue
);
1417 for (size_t i
= 0; i
< ELEMENTSOF(wol_option_map
); i
++)
1418 if (streq(w
, wol_option_map
[i
].name
)) {
1419 new_opts
|= wol_option_map
[i
].opt
;
1425 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1426 "Unknown wake-on-lan mode '%s', ignoring.", w
);
1429 if (*opts
== UINT32_MAX
)
1437 int config_parse_coalesce_u32(
1439 const char *filename
,
1441 const char *section
,
1442 unsigned section_line
,
1448 u32_opt
*dst
= data
;
1452 if (isempty(rvalue
)) {
1458 r
= safe_atou32(rvalue
, &k
);
1460 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1461 "Failed to parse %s=, ignoring: %s", lvalue
, rvalue
);
1470 int config_parse_coalesce_sec(
1472 const char *filename
,
1474 const char *section
,
1475 unsigned section_line
,
1481 u32_opt
*dst
= data
;
1485 if (isempty(rvalue
)) {
1491 r
= parse_sec(rvalue
, &usec
);
1493 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1494 "Failed to parse coalesce setting value, ignoring: %s", rvalue
);
1498 if (usec
> UINT32_MAX
) {
1499 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1500 "Too large %s= value, ignoring: %s", lvalue
, rvalue
);
1504 if (STR_IN_SET(lvalue
, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec
< 1) {
1505 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1506 "Invalid %s= value, ignoring: %s", lvalue
, rvalue
);
1510 dst
->value
= (uint32_t) usec
;