From: Yu Watanabe Date: Mon, 27 Dec 2021 02:09:02 +0000 (+0900) Subject: network: move SR-IOV related functions to src/shared/netif-sriov.[ch] X-Git-Tag: v251-rc1~497^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b4fd34d81cb4aee49d10e16aa9b51e46f524528d;p=thirdparty%2Fsystemd.git network: move SR-IOV related functions to src/shared/netif-sriov.[ch] --- diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 4c3bf97311d..08e3f13f5a5 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -73,15 +73,15 @@ Link.Unmanaged, config_parse_bool, Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy) Link.RequiredForOnline, config_parse_required_for_online, 0, 0 Link.RequiredFamilyForOnline, config_parse_required_family_for_online, 0, offsetof(Network, required_family_for_online) -SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0 -SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0 -SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, 0 -SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, 0 -SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, 0 -SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, 0 -SR-IOV.Trust, config_parse_sr_iov_boolean, 0, 0 -SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, 0 -SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, 0 +SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.Trust, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, offsetof(Network, sr_iov_by_section) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.KeepMaster, config_parse_bool, 0, offsetof(Network, keep_master) Network.BatmanAdvanced, config_parse_ifname, 0, offsetof(Network, batadv_name) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 70e8af5bd49..df4b6f23ad5 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -321,7 +321,7 @@ int network_verify(Network *network) { network_drop_invalid_route_prefixes(network); network_drop_invalid_routing_policy_rules(network); network_drop_invalid_traffic_control(network); - r = network_drop_invalid_sr_iov(network); + r = sr_iov_drop_invalid_sections(network->sr_iov_by_section); if (r < 0) return r; network_drop_invalid_static_leases(network); diff --git a/src/network/networkd-sriov.c b/src/network/networkd-sriov.c index 79e607b3dab..cf138c73702 100644 --- a/src/network/networkd-sriov.c +++ b/src/network/networkd-sriov.c @@ -1,84 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later * Copyright © 2020 VMware, Inc. */ -#include "alloc-util.h" #include "netlink-util.h" +#include "networkd-link.h" #include "networkd-manager.h" #include "networkd-sriov.h" -#include "parse-util.h" -#include "set.h" -#include "string-util.h" - -static int sr_iov_new(SRIOV **ret) { - SRIOV *sr_iov; - - assert(ret); - - sr_iov = new(SRIOV, 1); - if (!sr_iov) - return -ENOMEM; - - *sr_iov = (SRIOV) { - .vf = UINT32_MAX, - .vlan_proto = ETH_P_8021Q, - .vf_spoof_check_setting = -1, - .trust = -1, - .query_rss = -1, - .link_state = _SR_IOV_LINK_STATE_INVALID, - }; - - *ret = TAKE_PTR(sr_iov); - - return 0; -} - -static int sr_iov_new_static(Network *network, const char *filename, unsigned section_line, SRIOV **ret) { - _cleanup_(config_section_freep) ConfigSection *n = NULL; - _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL; - SRIOV *existing = NULL; - int r; - - assert(network); - assert(ret); - assert(filename); - assert(section_line > 0); - - r = config_section_new(filename, section_line, &n); - if (r < 0) - return r; - - existing = ordered_hashmap_get(network->sr_iov_by_section, n); - if (existing) { - *ret = existing; - return 0; - } - - r = sr_iov_new(&sr_iov); - if (r < 0) - return r; - - sr_iov->network = network; - sr_iov->section = TAKE_PTR(n); - - r = ordered_hashmap_ensure_put(&network->sr_iov_by_section, &config_section_hash_ops, sr_iov->section, sr_iov); - if (r < 0) - return r; - - *ret = TAKE_PTR(sr_iov); - return 0; -} - -SRIOV *sr_iov_free(SRIOV *sr_iov) { - if (!sr_iov) - return NULL; - - if (sr_iov->network && sr_iov->section) - ordered_hashmap_remove(sr_iov->network->sr_iov_by_section, sr_iov->section); - - config_section_free(sr_iov->section); - - return mfree(sr_iov); -} static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -119,104 +45,16 @@ static int sr_iov_configure(Link *link, SRIOV *sr_iov) { r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m"); - - r = sd_netlink_message_open_container(req, IFLA_VF_INFO); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_VF_INFO container: %m"); - - if (!ether_addr_is_null(&sr_iov->mac)) { - struct ifla_vf_mac ivm = { - .vf = sr_iov->vf, - }; - - memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN); - r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_MAC: %m"); - } - - if (sr_iov->vf_spoof_check_setting >= 0) { - struct ifla_vf_spoofchk ivs = { - .vf = sr_iov->vf, - .setting = sr_iov->vf_spoof_check_setting, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_SPOOFCHK: %m"); - } - - if (sr_iov->query_rss >= 0) { - struct ifla_vf_rss_query_en ivs = { - .vf = sr_iov->vf, - .setting = sr_iov->query_rss, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_RSS_QUERY_EN: %m"); - } - - if (sr_iov->trust >= 0) { - struct ifla_vf_trust ivt = { - .vf = sr_iov->vf, - .setting = sr_iov->trust, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_TRUST: %m"); - } - - if (sr_iov->link_state >= 0) { - struct ifla_vf_link_state ivl = { - .vf = sr_iov->vf, - .link_state = sr_iov->link_state, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_LINK_STATE: %m"); - } - - if (sr_iov->vlan > 0) { - /* Because of padding, first the buffer must be initialized with 0. */ - struct ifla_vf_vlan_info ivvi = {}; - ivvi.vf = sr_iov->vf; - ivvi.vlan = sr_iov->vlan; - ivvi.qos = sr_iov->qos; - ivvi.vlan_proto = htobe16(sr_iov->vlan_proto); - - r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_VF_VLAN_LIST container: %m"); - - r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_VLAN_INFO: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_VF_VLAN_LIST container: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_VF_INFO container: %m"); + return r; - r = sd_netlink_message_close_container(req); + r = sr_iov_set_netlink_message(sr_iov, req); if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m"); + return r; r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler, link_netlink_destroy_callback, link); if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + return r; link_ref(link); link->sr_iov_messages++; @@ -241,7 +79,9 @@ int link_configure_sr_iov(Link *link) { ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) { r = sr_iov_configure(link, sr_iov); if (r < 0) - return r; + return log_link_warning_errno(link, r, + "Failed to configure SR-IOV virtual function %"PRIu32": %m", + sr_iov->vf); } if (link->sr_iov_messages == 0) @@ -251,312 +91,3 @@ int link_configure_sr_iov(Link *link) { return 0; } - -static int sr_iov_section_verify(SRIOV *sr_iov) { - assert(sr_iov); - - if (section_is_invalid(sr_iov->section)) - return -EINVAL; - - if (sr_iov->vf == UINT32_MAX) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: [SR-IOV] section without VirtualFunction= field configured. " - "Ignoring [SR-IOV] section from line %u.", - sr_iov->section->filename, sr_iov->section->line); - - return 0; -} - -int network_drop_invalid_sr_iov(Network *network) { - _cleanup_hashmap_free_ Hashmap *hashmap = NULL; - SRIOV *sr_iov; - int r; - - assert(network); - - ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section) { - SRIOV *dup; - - if (sr_iov_section_verify(sr_iov) < 0) { - sr_iov_free(sr_iov); - continue; - } - - assert(sr_iov->vf < INT_MAX); - - dup = hashmap_remove(hashmap, UINT32_TO_PTR(sr_iov->vf + 1)); - if (dup) { - log_warning("%s: Conflicting [SR-IOV] section is specified at line %u and %u, " - "dropping the [SR-IOV] section specified at line %u.", - dup->section->filename, sr_iov->section->line, - dup->section->line, dup->section->line); - sr_iov_free(dup); - } - - r = hashmap_ensure_put(&hashmap, NULL, UINT32_TO_PTR(sr_iov->vf + 1), sr_iov); - if (r < 0) - return log_oom(); - assert(r > 0); - } - - return 0; -} - -int config_parse_sr_iov_uint32( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - uint32_t k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue)) { - if (streq(lvalue, "VirtualFunction")) - sr_iov->vf = UINT32_MAX; - else if (streq(lvalue, "VLANId")) - sr_iov->vlan = 0; - else if (streq(lvalue, "QualityOfService")) - sr_iov->qos = 0; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; - } - - r = safe_atou32(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - if (streq(lvalue, "VLANId")) { - if (k == 0 || k > 4095) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k); - return 0; - } - sr_iov->vlan = k; - } else if (streq(lvalue, "VirtualFunction")) { - if (k >= INT_MAX) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k); - return 0; - } - sr_iov->vf = k; - } else if (streq(lvalue, "QualityOfService")) - sr_iov->qos = k; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_vlan_proto( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue) || streq(rvalue, "802.1Q")) - sr_iov->vlan_proto = ETH_P_8021Q; - else if (streq(rvalue, "802.1ad")) - sr_iov->vlan_proto = ETH_P_8021AD; - else { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_link_state( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use - * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */ - - if (isempty(rvalue)) { - sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID; - TAKE_PTR(sr_iov); - return 0; - } - - if (streq(rvalue, "auto")) { - sr_iov->link_state = SR_IOV_LINK_STATE_AUTO; - TAKE_PTR(sr_iov); - return 0; - } - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE; - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_boolean( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue)) { - if (streq(lvalue, "MACSpoofCheck")) - sr_iov->vf_spoof_check_setting = -1; - else if (streq(lvalue, "QueryReceiveSideScaling")) - sr_iov->query_rss = -1; - else if (streq(lvalue, "Trust")) - sr_iov->trust = -1; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; - } - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue); - return 0; - } - - if (streq(lvalue, "MACSpoofCheck")) - sr_iov->vf_spoof_check_setting = r; - else if (streq(lvalue, "QueryReceiveSideScaling")) - sr_iov->query_rss = r; - else if (streq(lvalue, "Trust")) - sr_iov->trust = r; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_mac( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue)) { - sr_iov->mac = ETHER_ADDR_NULL; - TAKE_PTR(sr_iov); - return 0; - } - - r = parse_ether_addr(rvalue, &sr_iov->mac); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - TAKE_PTR(sr_iov); - return 0; -} diff --git a/src/network/networkd-sriov.h b/src/network/networkd-sriov.h index 270a82b233d..4251fddf88b 100644 --- a/src/network/networkd-sriov.h +++ b/src/network/networkd-sriov.h @@ -2,45 +2,8 @@ * Copyright © 2020 VMware, Inc. */ #pragma once -#include +#include "netif-sriov.h" -#include "conf-parser.h" -#include "ether-addr-util.h" -#include "networkd-link.h" -#include "networkd-network.h" -#include "networkd-util.h" +typedef struct Link Link; -typedef enum SRIOVLinkState { - SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO, - SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE, - SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE, - _SR_IOV_LINK_STATE_MAX, - _SR_IOV_LINK_STATE_INVALID = -EINVAL, -} SRIOVLinkState; - -typedef struct SRIOV { - ConfigSection *section; - Network *network; - - uint32_t vf; /* 0 - 2147483646 */ - uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */ - uint32_t qos; - uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */ - int vf_spoof_check_setting; - int query_rss; - int trust; - SRIOVLinkState link_state; - struct ether_addr mac; -} SRIOV; - -SRIOV *sr_iov_free(SRIOV *sr_iov); int link_configure_sr_iov(Link *link); -int network_drop_invalid_sr_iov(Network *network); - -DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_free); - -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac); diff --git a/src/shared/meson.build b/src/shared/meson.build index f58d623f4a1..66715e00c3c 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -224,6 +224,8 @@ shared_sources = files(''' net-condition.h netif-naming-scheme.c netif-naming-scheme.h + netif-sriov.c + netif-sriov.h netif-util.c netif-util.h nscd-flush.h diff --git a/src/shared/netif-sriov.c b/src/shared/netif-sriov.c new file mode 100644 index 00000000000..fc40ccbbb6c --- /dev/null +++ b/src/shared/netif-sriov.c @@ -0,0 +1,487 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "netlink-util.h" +#include "netif-sriov.h" +#include "parse-util.h" +#include "set.h" +#include "string-util.h" + +static int sr_iov_new(SRIOV **ret) { + SRIOV *sr_iov; + + assert(ret); + + sr_iov = new(SRIOV, 1); + if (!sr_iov) + return -ENOMEM; + + *sr_iov = (SRIOV) { + .vf = UINT32_MAX, + .vlan_proto = ETH_P_8021Q, + .vf_spoof_check_setting = -1, + .trust = -1, + .query_rss = -1, + .link_state = _SR_IOV_LINK_STATE_INVALID, + }; + + *ret = TAKE_PTR(sr_iov); + + return 0; +} + +static int sr_iov_new_static(OrderedHashmap **sr_iov_by_section, const char *filename, unsigned section_line, SRIOV **ret) { + _cleanup_(config_section_freep) ConfigSection *n = NULL; + _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL; + SRIOV *existing = NULL; + int r; + + assert(sr_iov_by_section); + assert(filename); + assert(section_line > 0); + assert(ret); + + r = config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + existing = ordered_hashmap_get(*sr_iov_by_section, n); + if (existing) { + *ret = existing; + return 0; + } + + r = sr_iov_new(&sr_iov); + if (r < 0) + return r; + + r = ordered_hashmap_ensure_put(sr_iov_by_section, &config_section_hash_ops, n, sr_iov); + if (r < 0) + return r; + + sr_iov->section = TAKE_PTR(n); + sr_iov->sr_iov_by_section = *sr_iov_by_section; + + *ret = TAKE_PTR(sr_iov); + return 0; +} + +SRIOV *sr_iov_free(SRIOV *sr_iov) { + if (!sr_iov) + return NULL; + + if (sr_iov->sr_iov_by_section && sr_iov->section) + ordered_hashmap_remove(sr_iov->sr_iov_by_section, sr_iov->section); + + config_section_free(sr_iov->section); + + return mfree(sr_iov); +} + +int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req) { + int r; + + assert(sr_iov); + assert(req); + + r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(req, IFLA_VF_INFO); + if (r < 0) + return r; + + if (!ether_addr_is_null(&sr_iov->mac)) { + struct ifla_vf_mac ivm = { + .vf = sr_iov->vf, + }; + + memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN); + r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac)); + if (r < 0) + return r; + } + + if (sr_iov->vf_spoof_check_setting >= 0) { + struct ifla_vf_spoofchk ivs = { + .vf = sr_iov->vf, + .setting = sr_iov->vf_spoof_check_setting, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk)); + if (r < 0) + return r; + } + + if (sr_iov->query_rss >= 0) { + struct ifla_vf_rss_query_en ivs = { + .vf = sr_iov->vf, + .setting = sr_iov->query_rss, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en)); + if (r < 0) + return r; + } + + if (sr_iov->trust >= 0) { + struct ifla_vf_trust ivt = { + .vf = sr_iov->vf, + .setting = sr_iov->trust, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust)); + if (r < 0) + return r; + } + + if (sr_iov->link_state >= 0) { + struct ifla_vf_link_state ivl = { + .vf = sr_iov->vf, + .link_state = sr_iov->link_state, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state)); + if (r < 0) + return r; + } + + if (sr_iov->vlan > 0) { + /* Because of padding, first the buffer must be initialized with 0. */ + struct ifla_vf_vlan_info ivvi = {}; + ivvi.vf = sr_iov->vf; + ivvi.vlan = sr_iov->vlan; + ivvi.qos = sr_iov->qos; + ivvi.vlan_proto = htobe16(sr_iov->vlan_proto); + + r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST); + if (r < 0) + return r; + + r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info)); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(req); + if (r < 0) + return r; + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(req); + if (r < 0) + return r; + + return 0; +} + +static int sr_iov_section_verify(SRIOV *sr_iov) { + assert(sr_iov); + + if (section_is_invalid(sr_iov->section)) + return -EINVAL; + + if (sr_iov->vf == UINT32_MAX) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: [SR-IOV] section without VirtualFunction= field configured. " + "Ignoring [SR-IOV] section from line %u.", + sr_iov->section->filename, sr_iov->section->line); + + return 0; +} + +int sr_iov_drop_invalid_sections(OrderedHashmap *sr_iov_by_section) { + _cleanup_hashmap_free_ Hashmap *hashmap = NULL; + SRIOV *sr_iov; + int r; + + ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) { + SRIOV *dup; + + if (sr_iov_section_verify(sr_iov) < 0) { + sr_iov_free(sr_iov); + continue; + } + + assert(sr_iov->vf < INT_MAX); + + dup = hashmap_remove(hashmap, UINT32_TO_PTR(sr_iov->vf + 1)); + if (dup) { + log_warning("%s: Conflicting [SR-IOV] section is specified at line %u and %u, " + "dropping the [SR-IOV] section specified at line %u.", + dup->section->filename, sr_iov->section->line, + dup->section->line, dup->section->line); + sr_iov_free(dup); + } + + r = hashmap_ensure_put(&hashmap, NULL, UINT32_TO_PTR(sr_iov->vf + 1), sr_iov); + if (r < 0) + return log_oom(); + assert(r > 0); + } + + return 0; +} + +int config_parse_sr_iov_uint32( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + uint32_t k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue)) { + if (streq(lvalue, "VirtualFunction")) + sr_iov->vf = UINT32_MAX; + else if (streq(lvalue, "VLANId")) + sr_iov->vlan = 0; + else if (streq(lvalue, "QualityOfService")) + sr_iov->qos = 0; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; + } + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "VLANId")) { + if (k == 0 || k > 4095) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k); + return 0; + } + sr_iov->vlan = k; + } else if (streq(lvalue, "VirtualFunction")) { + if (k >= INT_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k); + return 0; + } + sr_iov->vf = k; + } else if (streq(lvalue, "QualityOfService")) + sr_iov->qos = k; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_vlan_proto( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue) || streq(rvalue, "802.1Q")) + sr_iov->vlan_proto = ETH_P_8021Q; + else if (streq(rvalue, "802.1ad")) + sr_iov->vlan_proto = ETH_P_8021AD; + else { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_link_state( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use + * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */ + + if (isempty(rvalue)) { + sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID; + TAKE_PTR(sr_iov); + return 0; + } + + if (streq(rvalue, "auto")) { + sr_iov->link_state = SR_IOV_LINK_STATE_AUTO; + TAKE_PTR(sr_iov); + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE; + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_boolean( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue)) { + if (streq(lvalue, "MACSpoofCheck")) + sr_iov->vf_spoof_check_setting = -1; + else if (streq(lvalue, "QueryReceiveSideScaling")) + sr_iov->query_rss = -1; + else if (streq(lvalue, "Trust")) + sr_iov->trust = -1; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "MACSpoofCheck")) + sr_iov->vf_spoof_check_setting = r; + else if (streq(lvalue, "QueryReceiveSideScaling")) + sr_iov->query_rss = r; + else if (streq(lvalue, "Trust")) + sr_iov->trust = r; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_mac( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue)) { + sr_iov->mac = ETHER_ADDR_NULL; + TAKE_PTR(sr_iov); + return 0; + } + + r = parse_ether_addr(rvalue, &sr_iov->mac); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + TAKE_PTR(sr_iov); + return 0; +} diff --git a/src/shared/netif-sriov.h b/src/shared/netif-sriov.h new file mode 100644 index 00000000000..871496287f9 --- /dev/null +++ b/src/shared/netif-sriov.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "conf-parser.h" +#include "ether-addr-util.h" +#include "hashmap.h" + +typedef enum SRIOVLinkState { + SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO, + SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE, + SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE, + _SR_IOV_LINK_STATE_MAX, + _SR_IOV_LINK_STATE_INVALID = -EINVAL, +} SRIOVLinkState; + +typedef struct SRIOV { + ConfigSection *section; + OrderedHashmap *sr_iov_by_section; + + uint32_t vf; /* 0 - 2147483646 */ + uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */ + uint32_t qos; + uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */ + int vf_spoof_check_setting; + int query_rss; + int trust; + SRIOVLinkState link_state; + struct ether_addr mac; +} SRIOV; + +SRIOV *sr_iov_free(SRIOV *sr_iov); +int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req); +int sr_iov_drop_invalid_sections(OrderedHashmap *sr_iov_by_section); + +DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_free); + +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);