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
);
437 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
440 if (wolopts
== UINT32_MAX
) {
441 /* When password is specified without valid WoL options specified, then enable
442 * WAKE_MAGICSECURE flag if supported. */
443 wolopts
= ecmd
.wolopts
;
444 if (password
&& FLAGS_SET(ecmd
.supported
, WAKE_MAGICSECURE
))
445 wolopts
|= WAKE_MAGICSECURE
;
448 if ((wolopts
& ~ecmd
.supported
) != 0) {
449 _cleanup_free_
char *str
= NULL
;
451 (void) wol_options_to_string_alloc(wolopts
& ~ecmd
.supported
, &str
);
452 log_debug("Network interface %s does not support requested Wake on LAN options \"%s\", ignoring.",
455 wolopts
&= ecmd
.supported
;
458 if (!FLAGS_SET(wolopts
, WAKE_MAGICSECURE
))
459 /* When WAKE_MAGICSECURE flag is not set, then ignore password. */
462 UPDATE(ecmd
.wolopts
, wolopts
, need_update
);
464 memcmp(ecmd
.sopass
, password
, sizeof(ecmd
.sopass
)) != 0) {
465 memcpy(ecmd
.sopass
, password
, sizeof(ecmd
.sopass
));
470 explicit_bzero_safe(&ecmd
, sizeof(ecmd
));
474 ecmd
.cmd
= ETHTOOL_SWOL
;
475 r
= RET_NERRNO(ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
));
477 explicit_bzero_safe(&ecmd
, sizeof(ecmd
));
481 int ethtool_set_nic_buffer_size(int *ethtool_fd
, const char *ifname
, const netdev_ring_param
*ring
) {
482 struct ethtool_ringparam ecmd
= {
483 .cmd
= ETHTOOL_GRINGPARAM
,
486 .ifr_data
= (void*) &ecmd
,
488 bool need_update
= false;
496 !ring
->rx_mini
.set
&&
497 !ring
->rx_jumbo
.set
&&
501 r
= ethtool_connect(ethtool_fd
);
505 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
507 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
511 UPDATE_WITH_MAX(ecmd
.rx_pending
, ecmd
.rx_max_pending
, ring
->rx
.value
, need_update
);
513 if (ring
->rx_mini
.set
)
514 UPDATE_WITH_MAX(ecmd
.rx_mini_pending
, ecmd
.rx_mini_max_pending
, ring
->rx_mini
.value
, need_update
);
516 if (ring
->rx_jumbo
.set
)
517 UPDATE_WITH_MAX(ecmd
.rx_jumbo_pending
, ecmd
.rx_jumbo_max_pending
, ring
->rx_jumbo
.value
, need_update
);
520 UPDATE_WITH_MAX(ecmd
.tx_pending
, ecmd
.tx_max_pending
, ring
->tx
.value
, need_update
);
525 ecmd
.cmd
= ETHTOOL_SRINGPARAM
;
526 return RET_NERRNO(ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
));
529 static int get_stringset(int ethtool_fd
, const char *ifname
, enum ethtool_stringset stringset_id
, struct ethtool_gstrings
**ret
) {
530 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
532 struct ethtool_sset_info info
;
535 .info
.cmd
= ETHTOOL_GSSET_INFO
,
536 .info
.sset_mask
= UINT64_C(1) << stringset_id
,
539 .ifr_data
= (void*) &buffer
,
543 assert(ethtool_fd
>= 0);
547 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
549 if (ioctl(ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
552 if (buffer
.info
.sset_mask
== 0)
555 #pragma GCC diagnostic push
556 #if HAVE_ZERO_LENGTH_BOUNDS
557 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
559 len
= buffer
.info
.data
[0];
560 #pragma GCC diagnostic pop
564 strings
= malloc0(offsetof(struct ethtool_gstrings
, data
) + len
* ETH_GSTRING_LEN
);
568 strings
->cmd
= ETHTOOL_GSTRINGS
;
569 strings
->string_set
= stringset_id
;
572 ifr
.ifr_data
= (void*) strings
;
574 if (ioctl(ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
577 *ret
= TAKE_PTR(strings
);
581 static int get_features(int ethtool_fd
, const char *ifname
, uint32_t n_features
, struct ethtool_gfeatures
**ret
) {
582 _cleanup_free_
struct ethtool_gfeatures
*gfeatures
= NULL
;
585 assert(ethtool_fd
>= 0);
588 assert(n_features
> 0);
590 gfeatures
= malloc0(offsetof(struct ethtool_gfeatures
, features
) +
591 DIV_ROUND_UP(n_features
, 32U) * sizeof(gfeatures
->features
[0]));
595 gfeatures
->cmd
= ETHTOOL_GFEATURES
;
596 gfeatures
->size
= DIV_ROUND_UP(n_features
, 32U);
598 ifr
= (struct ifreq
) {
599 .ifr_data
= (void*) gfeatures
,
601 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
603 if (ioctl(ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
606 *ret
= TAKE_PTR(gfeatures
);
610 static int set_features_bit(
611 const struct ethtool_gstrings
*strings
,
612 const struct ethtool_gfeatures
*gfeatures
,
613 struct ethtool_sfeatures
*sfeatures
,
625 for (uint32_t i
= 0; i
< strings
->len
; i
++) {
626 uint32_t block
, mask
;
628 if (!strneq((const char*) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
, ETH_GSTRING_LEN
))
632 mask
= UINT32_C(1) << (i
% 32);
634 if (!FLAGS_SET(gfeatures
->features
[block
].available
, mask
) ||
635 FLAGS_SET(gfeatures
->features
[block
].never_changed
, mask
))
638 sfeatures
->features
[block
].valid
|= mask
;
639 SET_FLAG(sfeatures
->features
[block
].requested
, mask
, flag
);
647 static int set_features_multiple_bit(
648 const struct ethtool_gstrings
*strings
,
649 const struct ethtool_gfeatures
*gfeatures
,
650 struct ethtool_sfeatures
*sfeatures
,
665 for (uint32_t i
= 0; i
< strings
->len
; i
++) {
666 uint32_t block
, mask
;
668 if (!startswith((const char*) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
))
672 mask
= UINT32_C(1) << (i
% 32);
674 if (!FLAGS_SET(gfeatures
->features
[block
].available
, mask
) ||
675 FLAGS_SET(gfeatures
->features
[block
].never_changed
, mask
)) {
680 /* The flags is explicitly set by set_features_bit() */
681 if (FLAGS_SET(sfeatures
->features
[block
].valid
, mask
))
684 sfeatures
->features
[block
].valid
|= mask
;
685 SET_FLAG(sfeatures
->features
[block
].requested
, mask
, flag
);
690 return found
? 0 : r
;
693 int ethtool_set_features(int *ethtool_fd
, const char *ifname
, const int features
[static _NET_DEV_FEAT_MAX
]) {
694 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
695 _cleanup_free_
struct ethtool_gfeatures
*gfeatures
= NULL
;
696 _cleanup_free_
struct ethtool_sfeatures
*sfeatures
= NULL
;
705 for (size_t i
= 0; i
< _NET_DEV_FEAT_MAX
; i
++)
706 if (features
[i
] >= 0) {
714 r
= ethtool_connect(ethtool_fd
);
718 r
= get_stringset(*ethtool_fd
, ifname
, ETH_SS_FEATURES
, &strings
);
720 return log_debug_errno(r
, "ethtool: could not get ethtool feature strings: %m");
722 r
= get_features(*ethtool_fd
, ifname
, strings
->len
, &gfeatures
);
724 return log_debug_errno(r
, "ethtool: could not get ethtool features for %s: %m", ifname
);
726 sfeatures
= malloc0(offsetof(struct ethtool_sfeatures
, features
) +
727 DIV_ROUND_UP(strings
->len
, 32U) * sizeof(sfeatures
->features
[0]));
729 return log_oom_debug();
731 sfeatures
->cmd
= ETHTOOL_SFEATURES
;
732 sfeatures
->size
= DIV_ROUND_UP(strings
->len
, 32U);
734 for (size_t i
= 0; i
< _NET_DEV_FEAT_SIMPLE_MAX
; i
++) {
735 r
= set_features_bit(strings
, gfeatures
, sfeatures
, netdev_feature_table
[i
], features
[i
]);
737 log_debug_errno(r
, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table
[i
], ifname
);
740 for (size_t i
= _NET_DEV_FEAT_SIMPLE_MAX
; i
< _NET_DEV_FEAT_MAX
; i
++) {
741 r
= set_features_multiple_bit(strings
, gfeatures
, sfeatures
, netdev_feature_table
[i
], features
[i
]);
743 log_debug_errno(r
, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table
[i
], ifname
);
746 ifr
= (struct ifreq
) {
747 .ifr_data
= (void*) sfeatures
,
749 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
751 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
752 return log_debug_errno(errno
, "ethtool: could not set ethtool features for %s", ifname
);
757 static int get_glinksettings(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**ret
) {
759 struct ethtool_link_settings req
;
760 uint32_t link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
762 .req
.cmd
= ETHTOOL_GLINKSETTINGS
,
764 struct ethtool_link_usettings
*u
;
771 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
772 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
773 agree with user, it returns the bitmap length it is expecting from user as a negative
774 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
775 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
776 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
779 ifr
->ifr_data
= (void *) &ecmd
;
781 if (ioctl(fd
, SIOCETHTOOL
, ifr
) < 0)
784 if (ecmd
.req
.link_mode_masks_nwords
>= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
787 ecmd
.req
.link_mode_masks_nwords
= -ecmd
.req
.link_mode_masks_nwords
;
789 ifr
->ifr_data
= (void *) &ecmd
;
791 if (ioctl(fd
, SIOCETHTOOL
, ifr
) < 0)
794 if (ecmd
.req
.link_mode_masks_nwords
<= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
797 u
= new(struct ethtool_link_usettings
, 1);
801 *u
= (struct ethtool_link_usettings
) {
806 memcpy(u
->link_modes
.supported
, &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
.advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
811 offset
+= ecmd
.req
.link_mode_masks_nwords
;
812 memcpy(u
->link_modes
.lp_advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
819 static int get_gset(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**ret
) {
820 struct ethtool_link_usettings
*e
;
821 struct ethtool_cmd ecmd
= {
829 ifr
->ifr_data
= (void *) &ecmd
;
831 if (ioctl(fd
, SIOCETHTOOL
, ifr
) < 0)
834 e
= new(struct ethtool_link_usettings
, 1);
838 *e
= (struct ethtool_link_usettings
) {
839 .base
.cmd
= ETHTOOL_GSET
,
840 .base
.link_mode_masks_nwords
= 1,
841 .base
.speed
= ethtool_cmd_speed(&ecmd
),
842 .base
.duplex
= ecmd
.duplex
,
843 .base
.port
= ecmd
.port
,
844 .base
.phy_address
= ecmd
.phy_address
,
845 .base
.autoneg
= ecmd
.autoneg
,
846 .base
.mdio_support
= ecmd
.mdio_support
,
847 .base
.eth_tp_mdix
= ecmd
.eth_tp_mdix
,
848 .base
.eth_tp_mdix_ctrl
= ecmd
.eth_tp_mdix_ctrl
,
850 .link_modes
.supported
[0] = ecmd
.supported
,
851 .link_modes
.advertising
[0] = ecmd
.advertising
,
852 .link_modes
.lp_advertising
[0] = ecmd
.lp_advertising
,
860 static int set_slinksettings(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
862 struct ethtool_link_settings req
;
863 uint32_t link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
871 if (u
->base
.cmd
!= ETHTOOL_GLINKSETTINGS
|| u
->base
.link_mode_masks_nwords
<= 0)
875 ecmd
.req
.cmd
= ETHTOOL_SLINKSETTINGS
;
877 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.supported
, 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
.advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
882 offset
+= ecmd
.req
.link_mode_masks_nwords
;
883 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.lp_advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
885 ifr
->ifr_data
= (void *) &ecmd
;
887 return RET_NERRNO(ioctl(fd
, SIOCETHTOOL
, ifr
));
890 static int set_sset(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
891 struct ethtool_cmd ecmd
= {
899 if (u
->base
.cmd
!= ETHTOOL_GSET
|| u
->base
.link_mode_masks_nwords
<= 0)
902 ecmd
.supported
= u
->link_modes
.supported
[0];
903 ecmd
.advertising
= u
->link_modes
.advertising
[0];
904 ecmd
.lp_advertising
= u
->link_modes
.lp_advertising
[0];
906 ethtool_cmd_speed_set(&ecmd
, u
->base
.speed
);
908 ecmd
.duplex
= u
->base
.duplex
;
909 ecmd
.port
= u
->base
.port
;
910 ecmd
.phy_address
= u
->base
.phy_address
;
911 ecmd
.autoneg
= u
->base
.autoneg
;
912 ecmd
.mdio_support
= u
->base
.mdio_support
;
913 ecmd
.eth_tp_mdix
= u
->base
.eth_tp_mdix
;
914 ecmd
.eth_tp_mdix_ctrl
= u
->base
.eth_tp_mdix_ctrl
;
916 ifr
->ifr_data
= (void *) &ecmd
;
918 return RET_NERRNO(ioctl(fd
, SIOCETHTOOL
, ifr
));
921 int ethtool_set_glinksettings(
925 const uint32_t advertise
[static N_ADVERTISE
],
931 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
932 struct ifreq ifr
= {};
933 bool changed
= false;
940 if (autonegotiation
< 0 && memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
) &&
941 speed
== 0 && duplex
< 0 && port
< 0 && mdi
== ETH_TP_MDI_INVALID
)
944 /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
945 * writable if the driver supports multiple link modes. If it is enabled then they are
946 * read-only. If the link is up they represent the negotiated link mode; if the link is down,
947 * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN
948 * or the best enabled duplex mode. */
950 if (speed
> 0 || duplex
>= 0 || port
>= 0) {
951 if (autonegotiation
== AUTONEG_ENABLE
|| !memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
952 log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings.");
954 duplex
= _DUP_INVALID
;
955 port
= _NET_DEV_PORT_INVALID
;
957 log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation.");
958 autonegotiation
= AUTONEG_DISABLE
;
962 r
= ethtool_connect(fd
);
966 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
968 r
= get_glinksettings(*fd
, &ifr
, &u
);
970 r
= get_gset(*fd
, &ifr
, &u
);
972 return log_debug_errno(r
, "ethtool: Cannot get device settings for %s: %m", ifname
);
976 UPDATE(u
->base
.speed
, DIV_ROUND_UP(speed
, 1000000), changed
);
979 UPDATE(u
->base
.duplex
, duplex
, changed
);
982 UPDATE(u
->base
.port
, port
, changed
);
984 if (autonegotiation
>= 0)
985 UPDATE(u
->base
.autoneg
, autonegotiation
, changed
);
987 if (!memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
988 UPDATE(u
->base
.autoneg
, AUTONEG_ENABLE
, changed
);
991 memcmp(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
) != 0 ||
992 !memeqzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
993 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
994 memcpy(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
);
995 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
996 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
999 if (mdi
!= ETH_TP_MDI_INVALID
) {
1000 if (u
->base
.eth_tp_mdix_ctrl
== ETH_TP_MDI_INVALID
)
1001 log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname
);
1003 UPDATE(u
->base
.eth_tp_mdix_ctrl
, mdi
, changed
);
1009 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
1010 r
= set_slinksettings(*fd
, &ifr
, u
);
1012 r
= set_sset(*fd
, &ifr
, u
);
1014 return log_debug_errno(r
, "ethtool: Cannot set device settings for %s: %m", ifname
);
1019 int ethtool_set_channels(int *fd
, const char *ifname
, const netdev_channels
*channels
) {
1020 struct ethtool_channels ecmd
= {
1021 .cmd
= ETHTOOL_GCHANNELS
,
1023 struct ifreq ifr
= {
1024 .ifr_data
= (void*) &ecmd
,
1026 bool need_update
= false;
1033 if (!channels
->rx
.set
&&
1034 !channels
->tx
.set
&&
1035 !channels
->other
.set
&&
1036 !channels
->combined
.set
)
1039 r
= ethtool_connect(fd
);
1043 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
1045 if (ioctl(*fd
, SIOCETHTOOL
, &ifr
) < 0)
1048 if (channels
->rx
.set
)
1049 UPDATE_WITH_MAX(ecmd
.rx_count
, ecmd
.max_rx
, channels
->rx
.value
, need_update
);
1051 if (channels
->tx
.set
)
1052 UPDATE_WITH_MAX(ecmd
.tx_count
, ecmd
.max_tx
, channels
->tx
.value
, need_update
);
1054 if (channels
->other
.set
)
1055 UPDATE_WITH_MAX(ecmd
.other_count
, ecmd
.max_other
, channels
->other
.value
, need_update
);
1057 if (channels
->combined
.set
)
1058 UPDATE_WITH_MAX(ecmd
.combined_count
, ecmd
.max_combined
, channels
->combined
.value
, need_update
);
1063 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
1064 return RET_NERRNO(ioctl(*fd
, SIOCETHTOOL
, &ifr
));
1067 int ethtool_set_flow_control(int *fd
, const char *ifname
, int rx
, int tx
, int autoneg
) {
1068 struct ethtool_pauseparam ecmd
= {
1069 .cmd
= ETHTOOL_GPAUSEPARAM
,
1071 struct ifreq ifr
= {
1072 .ifr_data
= (void*) &ecmd
,
1074 bool need_update
= false;
1080 if (rx
< 0 && tx
< 0 && autoneg
< 0)
1083 r
= ethtool_connect(fd
);
1087 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
1089 if (ioctl(*fd
, SIOCETHTOOL
, &ifr
) < 0)
1093 UPDATE(ecmd
.rx_pause
, (uint32_t) rx
, need_update
);
1096 UPDATE(ecmd
.tx_pause
, (uint32_t) tx
, need_update
);
1099 UPDATE(ecmd
.autoneg
, (uint32_t) autoneg
, need_update
);
1104 ecmd
.cmd
= ETHTOOL_SPAUSEPARAM
;
1105 return RET_NERRNO(ioctl(*fd
, SIOCETHTOOL
, &ifr
));
1108 int ethtool_set_nic_coalesce_settings(int *ethtool_fd
, const char *ifname
, const netdev_coalesce_param
*coalesce
) {
1109 struct ethtool_coalesce ecmd
= {
1110 .cmd
= ETHTOOL_GCOALESCE
,
1112 struct ifreq ifr
= {
1113 .ifr_data
= (void*) &ecmd
,
1115 bool need_update
= false;
1122 if (coalesce
->use_adaptive_rx_coalesce
< 0 &&
1123 coalesce
->use_adaptive_tx_coalesce
< 0 &&
1124 !coalesce
->rx_coalesce_usecs
.set
&&
1125 !coalesce
->rx_max_coalesced_frames
.set
&&
1126 !coalesce
->rx_coalesce_usecs_irq
.set
&&
1127 !coalesce
->rx_max_coalesced_frames_irq
.set
&&
1128 !coalesce
->tx_coalesce_usecs
.set
&&
1129 !coalesce
->tx_max_coalesced_frames
.set
&&
1130 !coalesce
->tx_coalesce_usecs_irq
.set
&&
1131 !coalesce
->tx_max_coalesced_frames_irq
.set
&&
1132 !coalesce
->stats_block_coalesce_usecs
.set
&&
1133 !coalesce
->pkt_rate_low
.set
&&
1134 !coalesce
->rx_coalesce_usecs_low
.set
&&
1135 !coalesce
->rx_max_coalesced_frames_low
.set
&&
1136 !coalesce
->tx_coalesce_usecs_low
.set
&&
1137 !coalesce
->tx_max_coalesced_frames_low
.set
&&
1138 !coalesce
->pkt_rate_high
.set
&&
1139 !coalesce
->rx_coalesce_usecs_high
.set
&&
1140 !coalesce
->rx_max_coalesced_frames_high
.set
&&
1141 !coalesce
->tx_coalesce_usecs_high
.set
&&
1142 !coalesce
->tx_max_coalesced_frames_high
.set
&&
1143 !coalesce
->rate_sample_interval
.set
)
1146 r
= ethtool_connect(ethtool_fd
);
1150 strscpy(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), ifname
);
1152 if (ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
) < 0)
1155 if (coalesce
->use_adaptive_rx_coalesce
>= 0)
1156 UPDATE(ecmd
.use_adaptive_rx_coalesce
, (uint32_t) coalesce
->use_adaptive_rx_coalesce
, need_update
);
1158 if (coalesce
->use_adaptive_tx_coalesce
>= 0)
1159 UPDATE(ecmd
.use_adaptive_tx_coalesce
, (uint32_t) coalesce
->use_adaptive_tx_coalesce
, need_update
);
1161 if (coalesce
->rx_coalesce_usecs
.set
)
1162 UPDATE(ecmd
.rx_coalesce_usecs
, coalesce
->rx_coalesce_usecs
.value
, need_update
);
1164 if (coalesce
->rx_max_coalesced_frames
.set
)
1165 UPDATE(ecmd
.rx_max_coalesced_frames
, coalesce
->rx_max_coalesced_frames
.value
, need_update
);
1167 if (coalesce
->rx_coalesce_usecs_irq
.set
)
1168 UPDATE(ecmd
.rx_coalesce_usecs_irq
, coalesce
->rx_coalesce_usecs_irq
.value
, need_update
);
1170 if (coalesce
->rx_max_coalesced_frames_irq
.set
)
1171 UPDATE(ecmd
.rx_max_coalesced_frames_irq
, coalesce
->rx_max_coalesced_frames_irq
.value
, need_update
);
1173 if (coalesce
->tx_coalesce_usecs
.set
)
1174 UPDATE(ecmd
.tx_coalesce_usecs
, coalesce
->tx_coalesce_usecs
.value
, need_update
);
1176 if (coalesce
->tx_max_coalesced_frames
.set
)
1177 UPDATE(ecmd
.tx_max_coalesced_frames
, coalesce
->tx_max_coalesced_frames
.value
, need_update
);
1179 if (coalesce
->tx_coalesce_usecs_irq
.set
)
1180 UPDATE(ecmd
.tx_coalesce_usecs_irq
, coalesce
->tx_coalesce_usecs_irq
.value
, need_update
);
1182 if (coalesce
->tx_max_coalesced_frames_irq
.set
)
1183 UPDATE(ecmd
.tx_max_coalesced_frames_irq
, coalesce
->tx_max_coalesced_frames_irq
.value
, need_update
);
1185 if (coalesce
->stats_block_coalesce_usecs
.set
)
1186 UPDATE(ecmd
.stats_block_coalesce_usecs
, coalesce
->stats_block_coalesce_usecs
.value
, need_update
);
1188 if (coalesce
->pkt_rate_low
.set
)
1189 UPDATE(ecmd
.pkt_rate_low
, coalesce
->pkt_rate_low
.value
, need_update
);
1191 if (coalesce
->rx_coalesce_usecs_low
.set
)
1192 UPDATE(ecmd
.rx_coalesce_usecs_low
, coalesce
->rx_coalesce_usecs_low
.value
, need_update
);
1194 if (coalesce
->rx_max_coalesced_frames_low
.set
)
1195 UPDATE(ecmd
.rx_max_coalesced_frames_low
, coalesce
->rx_max_coalesced_frames_low
.value
, need_update
);
1197 if (coalesce
->tx_coalesce_usecs_low
.set
)
1198 UPDATE(ecmd
.tx_coalesce_usecs_low
, coalesce
->tx_coalesce_usecs_low
.value
, need_update
);
1200 if (coalesce
->tx_max_coalesced_frames_low
.set
)
1201 UPDATE(ecmd
.tx_max_coalesced_frames_low
, coalesce
->tx_max_coalesced_frames_low
.value
, need_update
);
1203 if (coalesce
->pkt_rate_high
.set
)
1204 UPDATE(ecmd
.pkt_rate_high
, coalesce
->pkt_rate_high
.value
, need_update
);
1206 if (coalesce
->rx_coalesce_usecs_high
.set
)
1207 UPDATE(ecmd
.rx_coalesce_usecs_high
, coalesce
->rx_coalesce_usecs_high
.value
, need_update
);
1209 if (coalesce
->rx_max_coalesced_frames_high
.set
)
1210 UPDATE(ecmd
.rx_max_coalesced_frames_high
, coalesce
->rx_max_coalesced_frames_high
.value
, need_update
);
1212 if (coalesce
->tx_coalesce_usecs_high
.set
)
1213 UPDATE(ecmd
.tx_coalesce_usecs_high
, coalesce
->tx_coalesce_usecs_high
.value
, need_update
);
1215 if (coalesce
->tx_max_coalesced_frames_high
.set
)
1216 UPDATE(ecmd
.tx_max_coalesced_frames_high
, coalesce
->tx_max_coalesced_frames_high
.value
, need_update
);
1218 if (coalesce
->rate_sample_interval
.set
)
1219 UPDATE(ecmd
.rate_sample_interval
, DIV_ROUND_UP(coalesce
->rate_sample_interval
.value
, USEC_PER_SEC
), need_update
);
1224 ecmd
.cmd
= ETHTOOL_SCOALESCE
;
1225 return RET_NERRNO(ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
));
1228 int config_parse_advertise(
1230 const char *filename
,
1232 const char *section
,
1233 unsigned section_line
,
1240 uint32_t *advertise
= ASSERT_PTR(data
);
1248 if (isempty(rvalue
)) {
1249 /* Empty string resets the value. */
1250 memzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
);
1254 for (const char *p
= rvalue
;;) {
1255 _cleanup_free_
char *w
= NULL
;
1256 enum ethtool_link_mode_bit_indices mode
;
1258 r
= extract_first_word(&p
, &w
, NULL
, 0);
1262 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1263 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue
);
1269 mode
= ethtool_link_mode_bit_from_string(w
);
1270 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1271 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1272 if ((int) mode
< 0) {
1273 log_syntax(unit
, LOG_WARNING
, filename
, line
, mode
,
1274 "Failed to parse advertise mode, ignoring: %s", w
);
1278 advertise
[mode
/ 32] |= 1UL << (mode
% 32);
1282 int config_parse_mdi(
1284 const char *filename
,
1286 const char *section
,
1287 unsigned section_line
,
1294 uint8_t *mdi
= ASSERT_PTR(data
);
1299 if (isempty(rvalue
)) {
1300 *mdi
= ETH_TP_MDI_INVALID
;
1304 if (STR_IN_SET(rvalue
, "mdi", "straight")) {
1309 if (STR_IN_SET(rvalue
, "mdi-x", "mdix", "crossover")) {
1310 *mdi
= ETH_TP_MDI_X
;
1314 if (streq(rvalue
, "auto")) {
1315 *mdi
= ETH_TP_MDI_AUTO
;
1319 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1320 "Failed to parse %s= setting, ignoring assignment: %s", lvalue
, rvalue
);
1324 int config_parse_ring_buffer_or_channel(
1326 const char *filename
,
1328 const char *section
,
1329 unsigned section_line
,
1336 u32_opt
*dst
= ASSERT_PTR(data
);
1345 if (isempty(rvalue
)) {
1351 if (streq(rvalue
, "max")) {
1357 r
= safe_atou32(rvalue
, &k
);
1359 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1360 "Failed to parse %s=, ignoring: %s", lvalue
, rvalue
);
1364 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1365 "Invalid %s= value, ignoring: %s", lvalue
, rvalue
);
1374 int config_parse_wol(
1376 const char *filename
,
1378 const char *section
,
1379 unsigned section_line
,
1386 uint32_t new_opts
= 0, *opts
= data
;
1395 if (isempty(rvalue
)) {
1396 *opts
= UINT32_MAX
; /* Do not update WOL option. */
1400 if (streq(rvalue
, "off")) {
1401 *opts
= 0; /* Disable WOL. */
1405 for (const char *p
= rvalue
;;) {
1406 _cleanup_free_
char *w
= NULL
;
1409 r
= extract_first_word(&p
, &w
, NULL
, 0);
1413 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1414 "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue
);
1420 for (size_t i
= 0; i
< ELEMENTSOF(wol_option_map
); i
++)
1421 if (streq(w
, wol_option_map
[i
].name
)) {
1422 new_opts
|= wol_option_map
[i
].opt
;
1428 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1429 "Unknown wake-on-lan mode '%s', ignoring.", w
);
1432 if (*opts
== UINT32_MAX
)
1440 int config_parse_coalesce_u32(
1442 const char *filename
,
1444 const char *section
,
1445 unsigned section_line
,
1451 u32_opt
*dst
= data
;
1455 if (isempty(rvalue
)) {
1461 r
= safe_atou32(rvalue
, &k
);
1463 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1464 "Failed to parse %s=, ignoring: %s", lvalue
, rvalue
);
1473 int config_parse_coalesce_sec(
1475 const char *filename
,
1477 const char *section
,
1478 unsigned section_line
,
1484 u32_opt
*dst
= data
;
1488 if (isempty(rvalue
)) {
1494 r
= parse_sec(rvalue
, &usec
);
1496 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1497 "Failed to parse coalesce setting value, ignoring: %s", rvalue
);
1501 if (usec
> UINT32_MAX
) {
1502 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1503 "Too large %s= value, ignoring: %s", lvalue
, rvalue
);
1507 if (STR_IN_SET(lvalue
, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec
< 1) {
1508 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1509 "Invalid %s= value, ignoring: %s", lvalue
, rvalue
);
1513 dst
->value
= (uint32_t) usec
;