1 /* SPDX-License-Identifier: LGPL-2.1-or-later
2 * Copyright © 2020 VMware, Inc. */
4 #include "alloc-util.h"
5 #include "netlink-util.h"
6 #include "networkd-manager.h"
7 #include "networkd-sriov.h"
8 #include "parse-util.h"
10 #include "string-util.h"
12 static int sr_iov_new(SRIOV
**ret
) {
15 sr_iov
= new(SRIOV
, 1);
21 .vlan_proto
= ETH_P_8021Q
,
22 .vf_spoof_check_setting
= -1,
25 .link_state
= _SR_IOV_LINK_STATE_INVALID
,
28 *ret
= TAKE_PTR(sr_iov
);
33 static int sr_iov_new_static(Network
*network
, const char *filename
, unsigned section_line
, SRIOV
**ret
) {
34 _cleanup_(network_config_section_freep
) NetworkConfigSection
*n
= NULL
;
35 _cleanup_(sr_iov_freep
) SRIOV
*sr_iov
= NULL
;
36 SRIOV
*existing
= NULL
;
42 assert(section_line
> 0);
44 r
= network_config_section_new(filename
, section_line
, &n
);
48 existing
= ordered_hashmap_get(network
->sr_iov_by_section
, n
);
54 r
= sr_iov_new(&sr_iov
);
58 sr_iov
->network
= network
;
59 sr_iov
->section
= TAKE_PTR(n
);
61 r
= ordered_hashmap_ensure_put(&network
->sr_iov_by_section
, &network_config_hash_ops
, sr_iov
->section
, sr_iov
);
65 *ret
= TAKE_PTR(sr_iov
);
69 SRIOV
*sr_iov_free(SRIOV
*sr_iov
) {
73 if (sr_iov
->network
&& sr_iov
->section
)
74 ordered_hashmap_remove(sr_iov
->network
->sr_iov_by_section
, sr_iov
->section
);
76 network_config_section_free(sr_iov
->section
);
81 static int sr_iov_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
85 assert(link
->sr_iov_messages
> 0);
86 link
->sr_iov_messages
--;
88 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
91 r
= sd_netlink_message_get_errno(m
);
92 if (r
< 0 && r
!= -EEXIST
) {
93 log_link_message_error_errno(link
, m
, r
, "Could not set up SR-IOV");
94 link_enter_failed(link
);
98 if (link
->sr_iov_messages
== 0) {
99 log_link_debug(link
, "SR-IOV configured");
100 link
->sr_iov_configured
= true;
101 link_check_ready(link
);
107 static int sr_iov_configure(Link
*link
, SRIOV
*sr_iov
) {
108 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
112 assert(link
->manager
);
113 assert(link
->manager
->rtnl
);
114 assert(link
->ifindex
> 0);
116 log_link_debug(link
, "Setting SR-IOV virtual function %"PRIu32
, sr_iov
->vf
);
118 r
= sd_rtnl_message_new_link(link
->manager
->rtnl
, &req
, RTM_SETLINK
, link
->ifindex
);
120 return log_link_error_errno(link
, r
, "Could not allocate RTM_SETLINK message: %m");
122 r
= sd_netlink_message_open_container(req
, IFLA_VFINFO_LIST
);
124 return log_link_error_errno(link
, r
, "Could not open IFLA_VFINFO_LIST container: %m");
126 r
= sd_netlink_message_open_container(req
, IFLA_VF_INFO
);
128 return log_link_error_errno(link
, r
, "Could not open IFLA_VF_INFO container: %m");
130 if (!ether_addr_is_null(&sr_iov
->mac
)) {
131 struct ifla_vf_mac ivm
= {
135 memcpy(ivm
.mac
, &sr_iov
->mac
, ETH_ALEN
);
136 r
= sd_netlink_message_append_data(req
, IFLA_VF_MAC
, &ivm
, sizeof(struct ifla_vf_mac
));
138 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_MAC: %m");
141 if (sr_iov
->vf_spoof_check_setting
>= 0) {
142 struct ifla_vf_spoofchk ivs
= {
144 .setting
= sr_iov
->vf_spoof_check_setting
,
147 r
= sd_netlink_message_append_data(req
, IFLA_VF_SPOOFCHK
, &ivs
, sizeof(struct ifla_vf_spoofchk
));
149 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_SPOOFCHK: %m");
152 if (sr_iov
->query_rss
>= 0) {
153 struct ifla_vf_rss_query_en ivs
= {
155 .setting
= sr_iov
->query_rss
,
158 r
= sd_netlink_message_append_data(req
, IFLA_VF_RSS_QUERY_EN
, &ivs
, sizeof(struct ifla_vf_rss_query_en
));
160 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_RSS_QUERY_EN: %m");
163 if (sr_iov
->trust
>= 0) {
164 struct ifla_vf_trust ivt
= {
166 .setting
= sr_iov
->trust
,
169 r
= sd_netlink_message_append_data(req
, IFLA_VF_TRUST
, &ivt
, sizeof(struct ifla_vf_trust
));
171 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_TRUST: %m");
174 if (sr_iov
->link_state
>= 0) {
175 struct ifla_vf_link_state ivl
= {
177 .link_state
= sr_iov
->link_state
,
180 r
= sd_netlink_message_append_data(req
, IFLA_VF_LINK_STATE
, &ivl
, sizeof(struct ifla_vf_link_state
));
182 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_LINK_STATE: %m");
185 if (sr_iov
->vlan
> 0) {
186 /* Because of padding, first the buffer must be initialized with 0. */
187 struct ifla_vf_vlan_info ivvi
= {};
188 ivvi
.vf
= sr_iov
->vf
;
189 ivvi
.vlan
= sr_iov
->vlan
;
190 ivvi
.qos
= sr_iov
->qos
;
191 ivvi
.vlan_proto
= htobe16(sr_iov
->vlan_proto
);
193 r
= sd_netlink_message_open_container(req
, IFLA_VF_VLAN_LIST
);
195 return log_link_error_errno(link
, r
, "Could not open IFLA_VF_VLAN_LIST container: %m");
197 r
= sd_netlink_message_append_data(req
, IFLA_VF_VLAN_INFO
, &ivvi
, sizeof(struct ifla_vf_vlan_info
));
199 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_VLAN_INFO: %m");
201 r
= sd_netlink_message_close_container(req
);
203 return log_link_error_errno(link
, r
, "Could not close IFLA_VF_VLAN_LIST container: %m");
206 r
= sd_netlink_message_close_container(req
);
208 return log_link_error_errno(link
, r
, "Could not close IFLA_VF_INFO container: %m");
210 r
= sd_netlink_message_close_container(req
);
212 return log_link_error_errno(link
, r
, "Could not close IFLA_VFINFO_LIST container: %m");
214 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, sr_iov_handler
,
215 link_netlink_destroy_callback
, link
);
217 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
220 link
->sr_iov_messages
++;
225 int link_configure_sr_iov(Link
*link
) {
230 assert(link
->network
);
232 if (link
->sr_iov_messages
!= 0) {
233 log_link_debug(link
, "SR-IOV is configuring.");
237 link
->sr_iov_configured
= false;
239 ORDERED_HASHMAP_FOREACH(sr_iov
, link
->network
->sr_iov_by_section
) {
240 r
= sr_iov_configure(link
, sr_iov
);
245 if (link
->sr_iov_messages
== 0)
246 link
->sr_iov_configured
= true;
248 log_link_debug(link
, "Configuring SR-IOV");
253 static int sr_iov_section_verify(SRIOV
*sr_iov
) {
256 if (section_is_invalid(sr_iov
->section
))
259 if (sr_iov
->vf
== (uint32_t) -1)
260 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
261 "%s: [SRIOV] section without VirtualFunction= field configured. "
262 "Ignoring [SRIOV] section from line %u.",
263 sr_iov
->section
->filename
, sr_iov
->section
->line
);
268 void network_drop_invalid_sr_iov(Network
*network
) {
273 ORDERED_HASHMAP_FOREACH(sr_iov
, network
->sr_iov_by_section
)
274 if (sr_iov_section_verify(sr_iov
) < 0)
278 int config_parse_sr_iov_uint32(
280 const char *filename
,
283 unsigned section_line
,
290 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
291 Network
*network
= data
;
300 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
304 if (isempty(rvalue
)) {
305 if (streq(lvalue
, "VirtualFunction"))
306 sr_iov
->vf
= (uint32_t) -1;
307 else if (streq(lvalue
, "VLANId"))
309 else if (streq(lvalue
, "QualityOfService"))
312 assert_not_reached("Invalid lvalue");
318 r
= safe_atou32(rvalue
, &k
);
320 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
321 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
325 if (streq(lvalue
, "VLANId")) {
326 if (k
== 0 || k
> 4095) {
327 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid SR-IOV VLANId: %d", k
);
331 } else if (streq(lvalue
, "VirtualFunction")) {
333 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid SR-IOV virtual function: %d", k
);
337 } else if (streq(lvalue
, "QualityOfService"))
340 assert_not_reached("Invalid lvalue");
346 int config_parse_sr_iov_vlan_proto(
348 const char *filename
,
351 unsigned section_line
,
358 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
359 Network
*network
= data
;
367 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
371 if (isempty(rvalue
) || streq(rvalue
, "802.1Q"))
372 sr_iov
->vlan_proto
= ETH_P_8021Q
;
373 else if (streq(rvalue
, "802.1ad"))
374 sr_iov
->vlan_proto
= ETH_P_8021AD
;
376 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
377 "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
385 int config_parse_sr_iov_link_state(
387 const char *filename
,
390 unsigned section_line
,
397 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
398 Network
*network
= data
;
406 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
410 /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
411 * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
413 if (isempty(rvalue
)) {
414 sr_iov
->link_state
= _SR_IOV_LINK_STATE_INVALID
;
419 if (streq(rvalue
, "auto")) {
420 sr_iov
->link_state
= SR_IOV_LINK_STATE_AUTO
;
425 r
= parse_boolean(rvalue
);
427 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
428 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
432 sr_iov
->link_state
= r
? SR_IOV_LINK_STATE_ENABLE
: SR_IOV_LINK_STATE_DISABLE
;
437 int config_parse_sr_iov_boolean(
439 const char *filename
,
442 unsigned section_line
,
449 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
450 Network
*network
= data
;
458 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
462 if (isempty(rvalue
)) {
463 if (streq(lvalue
, "MACSpoofCheck"))
464 sr_iov
->vf_spoof_check_setting
= -1;
465 else if (streq(lvalue
, "QueryReceiveSideScaling"))
466 sr_iov
->query_rss
= -1;
467 else if (streq(lvalue
, "Trust"))
470 assert_not_reached("Invalid lvalue");
476 r
= parse_boolean(rvalue
);
478 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse '%s=', ignoring: %s", lvalue
, rvalue
);
482 if (streq(lvalue
, "MACSpoofCheck"))
483 sr_iov
->vf_spoof_check_setting
= r
;
484 else if (streq(lvalue
, "QueryReceiveSideScaling"))
485 sr_iov
->query_rss
= r
;
486 else if (streq(lvalue
, "Trust"))
489 assert_not_reached("Invalid lvalue");
495 int config_parse_sr_iov_mac(
497 const char *filename
,
500 unsigned section_line
,
507 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
508 Network
*network
= data
;
516 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
520 if (isempty(rvalue
)) {
521 sr_iov
->mac
= ETHER_ADDR_NULL
;
526 r
= ether_addr_from_string(rvalue
, &sr_iov
->mac
);
528 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
529 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);