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_allocated(&network
->sr_iov_by_section
, &network_config_hash_ops
);
65 r
= ordered_hashmap_put(network
->sr_iov_by_section
, sr_iov
->section
, sr_iov
);
69 *ret
= TAKE_PTR(sr_iov
);
73 SRIOV
*sr_iov_free(SRIOV
*sr_iov
) {
77 if (sr_iov
->network
&& sr_iov
->section
)
78 ordered_hashmap_remove(sr_iov
->network
->sr_iov_by_section
, sr_iov
->section
);
80 network_config_section_free(sr_iov
->section
);
85 static int sr_iov_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
89 assert(link
->sr_iov_messages
> 0);
90 link
->sr_iov_messages
--;
92 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
95 r
= sd_netlink_message_get_errno(m
);
96 if (r
< 0 && r
!= -EEXIST
) {
97 log_link_message_error_errno(link
, m
, r
, "Could not set up SR-IOV");
98 link_enter_failed(link
);
102 if (link
->sr_iov_messages
== 0) {
103 log_link_debug(link
, "SR-IOV configured");
104 link
->sr_iov_configured
= true;
105 link_check_ready(link
);
111 static int sr_iov_configure(Link
*link
, SRIOV
*sr_iov
) {
112 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
116 assert(link
->manager
);
117 assert(link
->manager
->rtnl
);
118 assert(link
->ifindex
> 0);
120 log_link_debug(link
, "Setting SR-IOV virtual function %"PRIu32
, sr_iov
->vf
);
122 r
= sd_rtnl_message_new_link(link
->manager
->rtnl
, &req
, RTM_SETLINK
, link
->ifindex
);
124 return log_link_error_errno(link
, r
, "Could not allocate RTM_SETLINK message: %m");
126 r
= sd_netlink_message_open_container(req
, IFLA_VFINFO_LIST
);
128 return log_link_error_errno(link
, r
, "Could not open IFLA_VFINFO_LIST container: %m");
130 r
= sd_netlink_message_open_container(req
, IFLA_VF_INFO
);
132 return log_link_error_errno(link
, r
, "Could not open IFLA_VF_INFO container: %m");
134 if (!ether_addr_is_null(&sr_iov
->mac
)) {
135 struct ifla_vf_mac ivm
= {
139 memcpy(ivm
.mac
, &sr_iov
->mac
, ETH_ALEN
);
140 r
= sd_netlink_message_append_data(req
, IFLA_VF_MAC
, &ivm
, sizeof(struct ifla_vf_mac
));
142 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_MAC: %m");
145 if (sr_iov
->vf_spoof_check_setting
>= 0) {
146 struct ifla_vf_spoofchk ivs
= {
148 .setting
= sr_iov
->vf_spoof_check_setting
,
151 r
= sd_netlink_message_append_data(req
, IFLA_VF_SPOOFCHK
, &ivs
, sizeof(struct ifla_vf_spoofchk
));
153 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_SPOOFCHK: %m");
156 if (sr_iov
->query_rss
>= 0) {
157 struct ifla_vf_rss_query_en ivs
= {
159 .setting
= sr_iov
->query_rss
,
162 r
= sd_netlink_message_append_data(req
, IFLA_VF_RSS_QUERY_EN
, &ivs
, sizeof(struct ifla_vf_rss_query_en
));
164 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_RSS_QUERY_EN: %m");
167 if (sr_iov
->trust
>= 0) {
168 struct ifla_vf_trust ivt
= {
170 .setting
= sr_iov
->trust
,
173 r
= sd_netlink_message_append_data(req
, IFLA_VF_TRUST
, &ivt
, sizeof(struct ifla_vf_trust
));
175 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_TRUST: %m");
178 if (sr_iov
->link_state
>= 0) {
179 struct ifla_vf_link_state ivl
= {
181 .link_state
= sr_iov
->link_state
,
184 r
= sd_netlink_message_append_data(req
, IFLA_VF_LINK_STATE
, &ivl
, sizeof(struct ifla_vf_link_state
));
186 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_LINK_STATE: %m");
189 if (sr_iov
->vlan
> 0) {
190 /* Because of padding, first the buffer must be initialized with 0. */
191 struct ifla_vf_vlan_info ivvi
= {};
192 ivvi
.vf
= sr_iov
->vf
;
193 ivvi
.vlan
= sr_iov
->vlan
;
194 ivvi
.qos
= sr_iov
->qos
;
195 ivvi
.vlan_proto
= htobe16(sr_iov
->vlan_proto
);
197 r
= sd_netlink_message_open_container(req
, IFLA_VF_VLAN_LIST
);
199 return log_link_error_errno(link
, r
, "Could not open IFLA_VF_VLAN_LIST container: %m");
201 r
= sd_netlink_message_append_data(req
, IFLA_VF_VLAN_INFO
, &ivvi
, sizeof(struct ifla_vf_vlan_info
));
203 return log_link_error_errno(link
, r
, "Could not append IFLA_VF_VLAN_INFO: %m");
205 r
= sd_netlink_message_close_container(req
);
207 return log_link_error_errno(link
, r
, "Could not close IFLA_VF_VLAN_LIST container: %m");
210 r
= sd_netlink_message_close_container(req
);
212 return log_link_error_errno(link
, r
, "Could not close IFLA_VF_INFO container: %m");
214 r
= sd_netlink_message_close_container(req
);
216 return log_link_error_errno(link
, r
, "Could not close IFLA_VFINFO_LIST container: %m");
218 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, sr_iov_handler
,
219 link_netlink_destroy_callback
, link
);
221 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
224 link
->sr_iov_messages
++;
229 int link_configure_sr_iov(Link
*link
) {
234 assert(link
->network
);
236 if (link
->sr_iov_messages
!= 0) {
237 log_link_debug(link
, "SR-IOV is configuring.");
241 link
->sr_iov_configured
= false;
243 ORDERED_HASHMAP_FOREACH(sr_iov
, link
->network
->sr_iov_by_section
) {
244 r
= sr_iov_configure(link
, sr_iov
);
249 if (link
->sr_iov_messages
== 0)
250 link
->sr_iov_configured
= true;
252 log_link_debug(link
, "Configuring SR-IOV");
257 static int sr_iov_section_verify(SRIOV
*sr_iov
) {
260 if (section_is_invalid(sr_iov
->section
))
263 if (sr_iov
->vf
== (uint32_t) -1)
264 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
265 "%s: [SRIOV] section without VirtualFunction= field configured. "
266 "Ignoring [SRIOV] section from line %u.",
267 sr_iov
->section
->filename
, sr_iov
->section
->line
);
272 void network_drop_invalid_sr_iov(Network
*network
) {
277 ORDERED_HASHMAP_FOREACH(sr_iov
, network
->sr_iov_by_section
)
278 if (sr_iov_section_verify(sr_iov
) < 0)
282 int config_parse_sr_iov_uint32(
284 const char *filename
,
287 unsigned section_line
,
294 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
295 Network
*network
= data
;
304 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
308 if (isempty(rvalue
)) {
309 if (streq(lvalue
, "VirtualFunction"))
310 sr_iov
->vf
= (uint32_t) -1;
311 else if (streq(lvalue
, "VLANId"))
313 else if (streq(lvalue
, "QualityOfService"))
316 assert_not_reached("Invalid lvalue");
322 r
= safe_atou32(rvalue
, &k
);
324 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
325 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
329 if (streq(lvalue
, "VLANId")) {
330 if (k
== 0 || k
> 4095) {
331 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid SR-IOV VLANId: %d", k
);
335 } else if (streq(lvalue
, "VirtualFunction")) {
337 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid SR-IOV virtual function: %d", k
);
341 } else if (streq(lvalue
, "QualityOfService"))
344 assert_not_reached("Invalid lvalue");
350 int config_parse_sr_iov_vlan_proto(
352 const char *filename
,
355 unsigned section_line
,
362 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
363 Network
*network
= data
;
371 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
375 if (isempty(rvalue
) || streq(rvalue
, "802.1Q"))
376 sr_iov
->vlan_proto
= ETH_P_8021Q
;
377 else if (streq(rvalue
, "802.1ad"))
378 sr_iov
->vlan_proto
= ETH_P_8021AD
;
380 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
381 "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
389 int config_parse_sr_iov_link_state(
391 const char *filename
,
394 unsigned section_line
,
401 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
402 Network
*network
= data
;
410 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
414 /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
415 * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
417 if (isempty(rvalue
)) {
418 sr_iov
->link_state
= _SR_IOV_LINK_STATE_INVALID
;
423 if (streq(rvalue
, "auto")) {
424 sr_iov
->link_state
= SR_IOV_LINK_STATE_AUTO
;
429 r
= parse_boolean(rvalue
);
431 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
432 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
436 sr_iov
->link_state
= r
? SR_IOV_LINK_STATE_ENABLE
: SR_IOV_LINK_STATE_DISABLE
;
441 int config_parse_sr_iov_boolean(
443 const char *filename
,
446 unsigned section_line
,
453 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
454 Network
*network
= data
;
462 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
466 if (isempty(rvalue
)) {
467 if (streq(lvalue
, "MACSpoofCheck"))
468 sr_iov
->vf_spoof_check_setting
= -1;
469 else if (streq(lvalue
, "QueryReceiveSideScaling"))
470 sr_iov
->query_rss
= -1;
471 else if (streq(lvalue
, "Trust"))
474 assert_not_reached("Invalid lvalue");
480 r
= parse_boolean(rvalue
);
482 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse '%s=', ignoring: %s", lvalue
, rvalue
);
486 if (streq(lvalue
, "MACSpoofCheck"))
487 sr_iov
->vf_spoof_check_setting
= r
;
488 else if (streq(lvalue
, "QueryReceiveSideScaling"))
489 sr_iov
->query_rss
= r
;
490 else if (streq(lvalue
, "Trust"))
493 assert_not_reached("Invalid lvalue");
499 int config_parse_sr_iov_mac(
501 const char *filename
,
504 unsigned section_line
,
511 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
512 Network
*network
= data
;
520 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
524 if (isempty(rvalue
)) {
525 sr_iov
->mac
= ETHER_ADDR_NULL
;
530 r
= ether_addr_from_string(rvalue
, &sr_iov
->mac
);
532 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
533 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);