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
) {
233 link
->sr_iov_configured
= false;
234 link
->sr_iov_messages
= 0;
236 ORDERED_HASHMAP_FOREACH(sr_iov
, link
->network
->sr_iov_by_section
) {
237 r
= sr_iov_configure(link
, sr_iov
);
242 if (link
->sr_iov_messages
== 0)
243 link
->sr_iov_configured
= true;
245 log_link_debug(link
, "Configuring SR-IOV");
250 static int sr_iov_section_verify(SRIOV
*sr_iov
) {
253 if (section_is_invalid(sr_iov
->section
))
256 if (sr_iov
->vf
== (uint32_t) -1)
257 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
258 "%s: [SRIOV] section without VirtualFunction= field configured. "
259 "Ignoring [SRIOV] section from line %u.",
260 sr_iov
->section
->filename
, sr_iov
->section
->line
);
265 void network_drop_invalid_sr_iov(Network
*network
) {
270 ORDERED_HASHMAP_FOREACH(sr_iov
, network
->sr_iov_by_section
)
271 if (sr_iov_section_verify(sr_iov
) < 0)
275 int config_parse_sr_iov_uint32(
277 const char *filename
,
280 unsigned section_line
,
287 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
288 Network
*network
= data
;
297 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
301 if (isempty(rvalue
)) {
302 if (streq(lvalue
, "VirtualFunction"))
303 sr_iov
->vf
= (uint32_t) -1;
304 else if (streq(lvalue
, "VLANId"))
306 else if (streq(lvalue
, "QualityOfService"))
309 assert_not_reached("Invalid lvalue");
315 r
= safe_atou32(rvalue
, &k
);
317 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
318 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
322 if (streq(lvalue
, "VLANId")) {
323 if (k
== 0 || k
> 4095) {
324 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid SR-IOV VLANId: %d", k
);
328 } else if (streq(lvalue
, "VirtualFunction")) {
330 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid SR-IOV virtual function: %d", k
);
334 } else if (streq(lvalue
, "QualityOfService"))
337 assert_not_reached("Invalid lvalue");
343 int config_parse_sr_iov_vlan_proto(
345 const char *filename
,
348 unsigned section_line
,
355 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
356 Network
*network
= data
;
364 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
368 if (isempty(rvalue
) || streq(rvalue
, "802.1Q"))
369 sr_iov
->vlan_proto
= ETH_P_8021Q
;
370 else if (streq(rvalue
, "802.1ad"))
371 sr_iov
->vlan_proto
= ETH_P_8021AD
;
373 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
374 "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
382 int config_parse_sr_iov_link_state(
384 const char *filename
,
387 unsigned section_line
,
394 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
395 Network
*network
= data
;
403 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
407 /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
408 * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
410 if (isempty(rvalue
)) {
411 sr_iov
->link_state
= _SR_IOV_LINK_STATE_INVALID
;
416 if (streq(rvalue
, "auto")) {
417 sr_iov
->link_state
= SR_IOV_LINK_STATE_AUTO
;
422 r
= parse_boolean(rvalue
);
424 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
425 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);
429 sr_iov
->link_state
= r
? SR_IOV_LINK_STATE_ENABLE
: SR_IOV_LINK_STATE_DISABLE
;
434 int config_parse_sr_iov_boolean(
436 const char *filename
,
439 unsigned section_line
,
446 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
447 Network
*network
= data
;
455 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
459 if (isempty(rvalue
)) {
460 if (streq(lvalue
, "MACSpoofCheck"))
461 sr_iov
->vf_spoof_check_setting
= -1;
462 else if (streq(lvalue
, "QueryReceiveSideScaling"))
463 sr_iov
->query_rss
= -1;
464 else if (streq(lvalue
, "Trust"))
467 assert_not_reached("Invalid lvalue");
473 r
= parse_boolean(rvalue
);
475 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse '%s=', ignoring: %s", lvalue
, rvalue
);
479 if (streq(lvalue
, "MACSpoofCheck"))
480 sr_iov
->vf_spoof_check_setting
= r
;
481 else if (streq(lvalue
, "QueryReceiveSideScaling"))
482 sr_iov
->query_rss
= r
;
483 else if (streq(lvalue
, "Trust"))
486 assert_not_reached("Invalid lvalue");
492 int config_parse_sr_iov_mac(
494 const char *filename
,
497 unsigned section_line
,
504 _cleanup_(sr_iov_free_or_set_invalidp
) SRIOV
*sr_iov
= NULL
;
505 Network
*network
= data
;
513 r
= sr_iov_new_static(network
, filename
, section_line
, &sr_iov
);
517 if (isempty(rvalue
)) {
518 sr_iov
->mac
= ETHER_ADDR_NULL
;
523 r
= ether_addr_from_string(rvalue
, &sr_iov
->mac
);
525 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
526 "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue
, rvalue
);