mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-25 06:03:40 +03:00
Merge pull request #16219 from ssahani/network-sr-iov
network: Introduce SR-IOV
This commit is contained in:
commit
1447dc625a
@ -237,6 +237,93 @@
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>[SR-IOV] Section Options</title>
|
||||
<para>The <literal>[SR-IOV]</literal> section accepts the
|
||||
following keys. Specify several <literal>[SR-IOV]</literal>
|
||||
sections to configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource
|
||||
into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV improves
|
||||
north-south network performance (that is, traffic with endpoints outside the host machine) by allowing traffic to
|
||||
bypass the host machine’s network stack.</para>
|
||||
|
||||
<variablelist class='network-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>VirtualFunction=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move data
|
||||
in and out. Takes an unsigned integer in the range 0..2147483646. This option is compulsory.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>VLANId=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies VLAN ID of the virtual function. Takes an unsigned integer in the range 1..4095.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>QualityOfService=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies quality of service of the virtual function. Takes an unsigned integer in the range 1..4294967294.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>VLANProtocol=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
|
||||
<literal>802.1ad</literal>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>MACSpoofCheck=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will be used.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>QueryReceiveSideScaling=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
|
||||
configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
|
||||
considered sensitive on some devices where this information is shared between VF and the
|
||||
physical function (PF). When unset, the kernel's default will be used.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Trust=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean. Allows to set trust mode of the virtual function (VF). When set, VF
|
||||
users can set a specific feature which may impact security and/or performance. When unset,
|
||||
the kernel's default will be used.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LinkState=</varname></term>
|
||||
<listitem>
|
||||
<para>Allows to set the link state of the virtual function (VF). Takes a boolean or a
|
||||
special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
|
||||
reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
|
||||
communicate with other VFs on this host even if the PF link state is down,
|
||||
<literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
|
||||
the kernel's default will be used.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>MACAddress=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies the MAC address for the virtual function.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>[Network] Section Options</title>
|
||||
|
||||
|
@ -537,6 +537,43 @@ static const NLTypeSystem rtnl_prop_list_type_system = {
|
||||
.types = rtnl_prop_list_types,
|
||||
};
|
||||
|
||||
static const NLType rtnl_vf_vlan_list_types[] = {
|
||||
[IFLA_VF_VLAN_INFO] = { .size = sizeof(struct ifla_vf_vlan_info) },
|
||||
};
|
||||
|
||||
static const NLTypeSystem rtnl_vf_vlan_type_system = {
|
||||
.count = ELEMENTSOF(rtnl_vf_vlan_list_types),
|
||||
.types = rtnl_vf_vlan_list_types,
|
||||
};
|
||||
|
||||
static const NLType rtnl_vf_vlan_info_types[] = {
|
||||
[IFLA_VF_MAC] = { .size = sizeof(struct ifla_vf_mac) },
|
||||
[IFLA_VF_VLAN] = { .size = sizeof(struct ifla_vf_vlan) },
|
||||
[IFLA_VF_VLAN_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_type_system},
|
||||
[IFLA_VF_TX_RATE] = { .size = sizeof(struct ifla_vf_tx_rate) },
|
||||
[IFLA_VF_SPOOFCHK] = { .size = sizeof(struct ifla_vf_spoofchk) },
|
||||
[IFLA_VF_RATE] = { .size = sizeof(struct ifla_vf_rate) },
|
||||
[IFLA_VF_LINK_STATE] = { .size = sizeof(struct ifla_vf_link_state) },
|
||||
[IFLA_VF_RSS_QUERY_EN] = { .size = sizeof(struct ifla_vf_rss_query_en) },
|
||||
[IFLA_VF_TRUST] = { .size = sizeof(struct ifla_vf_trust) },
|
||||
[IFLA_VF_IB_NODE_GUID] = { .size = sizeof(struct ifla_vf_guid) },
|
||||
[IFLA_VF_IB_PORT_GUID] = { .size = sizeof(struct ifla_vf_guid) },
|
||||
};
|
||||
|
||||
static const NLTypeSystem rtnl_vf_vlan_info_type_system = {
|
||||
.count = ELEMENTSOF(rtnl_vf_vlan_info_types),
|
||||
.types = rtnl_vf_vlan_info_types,
|
||||
};
|
||||
|
||||
static const NLType rtnl_link_io_srv_types[] = {
|
||||
[IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_info_type_system },
|
||||
};
|
||||
|
||||
static const NLTypeSystem rtnl_io_srv_type_system = {
|
||||
.count = ELEMENTSOF(rtnl_link_io_srv_types),
|
||||
.types = rtnl_link_io_srv_types,
|
||||
};
|
||||
|
||||
static const NLType rtnl_link_types[] = {
|
||||
[IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
|
||||
[IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR },
|
||||
@ -564,10 +601,8 @@ static const NLType rtnl_link_types[] = {
|
||||
[IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
|
||||
[IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 },
|
||||
[IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
|
||||
/*
|
||||
[IFLA_NUM_VF],
|
||||
[IFLA_VFINFO_LIST] = {. type = NETLINK_TYPE_NESTED, },
|
||||
*/
|
||||
[IFLA_NUM_VF] = { .type = NETLINK_TYPE_U32 },
|
||||
[IFLA_VFINFO_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_io_srv_type_system },
|
||||
[IFLA_STATS64] = { .size = sizeof(struct rtnl_link_stats64) },
|
||||
/*
|
||||
[IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED },
|
||||
|
@ -105,6 +105,8 @@ sources = files('''
|
||||
networkd-routing-policy-rule.h
|
||||
networkd-speed-meter.c
|
||||
networkd-speed-meter.h
|
||||
networkd-sriov.c
|
||||
networkd-sriov.h
|
||||
networkd-util.c
|
||||
networkd-util.h
|
||||
networkd-wifi.c
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
@ -31,6 +32,7 @@
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-neighbor.h"
|
||||
#include "networkd-sriov.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-wifi.h"
|
||||
@ -1127,6 +1129,9 @@ void link_check_ready(Link *link) {
|
||||
if (!link->tc_configured)
|
||||
return;
|
||||
|
||||
if (!link->sr_iov_configured)
|
||||
return;
|
||||
|
||||
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
|
||||
|
||||
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address)
|
||||
@ -2838,6 +2843,28 @@ static int link_configure_traffic_control(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_configure_sr_iov(Link *link) {
|
||||
SRIOV *sr_iov;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
link->sr_iov_configured = false;
|
||||
link->sr_iov_messages = 0;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section, i) {
|
||||
r = sr_iov_configure(link, sr_iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link->sr_iov_messages == 0)
|
||||
link->sr_iov_configured = true;
|
||||
else
|
||||
log_link_debug(link, "Configuring SR-IOV");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_configure(Link *link) {
|
||||
int r;
|
||||
|
||||
@ -2849,6 +2876,10 @@ static int link_configure(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_configure_sr_iov(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->iftype == ARPHRD_CAN)
|
||||
return link_configure_can(link);
|
||||
|
||||
|
@ -82,6 +82,7 @@ typedef struct Link {
|
||||
unsigned routing_policy_rule_messages;
|
||||
unsigned routing_policy_rule_remove_messages;
|
||||
unsigned tc_messages;
|
||||
unsigned sr_iov_messages;
|
||||
unsigned enslaving;
|
||||
|
||||
Set *addresses;
|
||||
@ -118,6 +119,7 @@ typedef struct Link {
|
||||
bool static_nexthops_configured:1;
|
||||
bool routing_policy_rules_configured:1;
|
||||
bool tc_configured:1;
|
||||
bool sr_iov_configured:1;
|
||||
bool setting_mtu:1;
|
||||
bool setting_genmode:1;
|
||||
bool ipv6_mtu_set:1;
|
||||
|
@ -15,6 +15,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
|
||||
#include "networkd-ipv4ll.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-sriov.h"
|
||||
#include "qdisc.h"
|
||||
#include "tclass.h"
|
||||
#include "vlan-util.h"
|
||||
@ -53,6 +54,15 @@ Link.Multicast, config_parse_tristate,
|
||||
Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
|
||||
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
|
||||
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
|
||||
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
|
||||
Network.Description, config_parse_string, 0, offsetof(Network, description)
|
||||
Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
|
||||
Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "network-internal.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-sriov.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-lookup.h"
|
||||
#include "set.h"
|
||||
@ -158,6 +159,7 @@ int network_verify(Network *network) {
|
||||
Route *route, *route_next;
|
||||
FdbEntry *fdb, *fdb_next;
|
||||
TrafficControl *tc;
|
||||
SRIOV *sr_iov;
|
||||
Iterator i;
|
||||
|
||||
assert(network);
|
||||
@ -330,6 +332,10 @@ int network_verify(Network *network) {
|
||||
if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
|
||||
traffic_control_free(tc);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section, i)
|
||||
if (sr_iov_section_verify(sr_iov) < 0)
|
||||
sr_iov_free(sr_iov);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -484,6 +490,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||
filename, NETWORK_DIRS, dropin_dirname,
|
||||
"Match\0"
|
||||
"Link\0"
|
||||
"SR-IOV\0"
|
||||
"Network\0"
|
||||
"Address\0"
|
||||
"Neighbor\0"
|
||||
@ -731,6 +738,7 @@ static Network *network_free(Network *network) {
|
||||
hashmap_free(network->prefixes_by_section);
|
||||
hashmap_free(network->route_prefixes_by_section);
|
||||
hashmap_free(network->rules_by_section);
|
||||
ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
|
||||
ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
|
||||
|
||||
if (network->manager &&
|
||||
|
@ -312,6 +312,7 @@ struct Network {
|
||||
Hashmap *route_prefixes_by_section;
|
||||
Hashmap *rules_by_section;
|
||||
OrderedHashmap *tc_by_section;
|
||||
OrderedHashmap *sr_iov_by_section;
|
||||
|
||||
/* All kinds of DNS configuration */
|
||||
struct in_addr_data *dns;
|
||||
|
501
src/network/networkd-sriov.c
Normal file
501
src/network/networkd-sriov.c
Normal file
@ -0,0 +1,501 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+
|
||||
* Copyright © 2020 VMware, Inc. */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "netlink-util.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;
|
||||
|
||||
sr_iov = new(SRIOV, 1);
|
||||
if (!sr_iov)
|
||||
return -ENOMEM;
|
||||
|
||||
*sr_iov = (SRIOV) {
|
||||
.vf = (uint32_t) -1,
|
||||
.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_(network_config_section_freep) NetworkConfigSection *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 = network_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_allocated(&network->sr_iov_by_section, &network_config_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ordered_hashmap_put(network->sr_iov_by_section, 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);
|
||||
|
||||
network_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;
|
||||
|
||||
assert(link);
|
||||
assert(link->sr_iov_messages > 0);
|
||||
link->sr_iov_messages--;
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
return 1;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0 && r != -EEXIST) {
|
||||
log_link_message_error_errno(link, m, r, "Could not set up SR-IOV");
|
||||
link_enter_failed(link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (link->sr_iov_messages == 0) {
|
||||
log_link_debug(link, "SR-IOV configured");
|
||||
link->sr_iov_configured = true;
|
||||
link_check_ready(link);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sr_iov_configure(Link *link, SRIOV *sr_iov) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->rtnl);
|
||||
assert(link->ifindex > 0);
|
||||
|
||||
log_link_debug(link, "Setting SR-IOV virtual function %"PRIu32, sr_iov->vf);
|
||||
|
||||
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");
|
||||
|
||||
r = sd_netlink_message_close_container(req);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m");
|
||||
|
||||
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");
|
||||
|
||||
link_ref(link);
|
||||
link->sr_iov_messages++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_t) -1)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: [SRIOV] section without VirtualFunction= field configured. "
|
||||
"Ignoring [SRIOV] section from line %u.",
|
||||
sr_iov->section->filename, sr_iov->section->line);
|
||||
|
||||
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_t) -1;
|
||||
else if (streq(lvalue, "VLANId"))
|
||||
sr_iov->vlan = 0;
|
||||
else if (streq(lvalue, "QualityOfService"))
|
||||
sr_iov->qos = 0;
|
||||
else
|
||||
assert_not_reached("Invalid lvalue");
|
||||
|
||||
TAKE_PTR(sr_iov);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou32(rvalue, &k);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, 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_ERR, 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_ERR, 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("Invalid lvalue");
|
||||
|
||||
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_ERR, 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_ERR, 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("Invalid lvalue");
|
||||
|
||||
TAKE_PTR(sr_iov);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, 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("Invalid lvalue");
|
||||
|
||||
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 = ether_addr_from_string(rvalue, &sr_iov->mac);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TAKE_PTR(sr_iov);
|
||||
return 0;
|
||||
}
|
46
src/network/networkd-sriov.h
Normal file
46
src/network/networkd-sriov.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+
|
||||
* Copyright © 2020 VMware, Inc. */
|
||||
#pragma once
|
||||
|
||||
#include <linux/if_link.h>
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-util.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 = -1,
|
||||
} SRIOVLinkState;
|
||||
|
||||
typedef struct SRIOV {
|
||||
NetworkConfigSection *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 sr_iov_configure(Link *link, SRIOV *sr_iov);
|
||||
int sr_iov_section_verify(SRIOV *sr_iov);
|
||||
|
||||
DEFINE_NETWORK_SECTION_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);
|
@ -38,6 +38,16 @@ MTUBytes=
|
||||
Multicast=
|
||||
MACAddress=
|
||||
Group=
|
||||
[SR-IOV]
|
||||
VirtualFunction=
|
||||
MACSpoofCheck=
|
||||
VLANId=
|
||||
VLANProtocol=
|
||||
QualityOfService=
|
||||
QueryReceiveSideScaling=
|
||||
Trust=
|
||||
LinkState=
|
||||
MACAddress=
|
||||
[BridgeFDB]
|
||||
VLANId=
|
||||
MACAddress=
|
||||
|
37
test/test-network/conf/25-sriov.network
Normal file
37
test/test-network/conf/25-sriov.network
Normal file
@ -0,0 +1,37 @@
|
||||
[Match]
|
||||
Name=eni99np1
|
||||
|
||||
[Network]
|
||||
Address=192.168.100.100/24
|
||||
|
||||
[SR-IOV]
|
||||
VirtualFunction=0
|
||||
VLANId=5
|
||||
VLANProtocol=802.1ad
|
||||
QualityOfService=1
|
||||
MACSpoofCheck=yes
|
||||
QueryReceiveSideScaling=yes
|
||||
Trust=yes
|
||||
LinkState=yes
|
||||
MACAddress=00:11:22:33:44:55
|
||||
|
||||
[SR-IOV]
|
||||
VirtualFunction=1
|
||||
VLANId=6
|
||||
VLANProtocol=802.1Q
|
||||
QualityOfService=2
|
||||
MACSpoofCheck=no
|
||||
QueryReceiveSideScaling=no
|
||||
Trust=no
|
||||
LinkState=no
|
||||
MACAddress=00:11:22:33:44:56
|
||||
|
||||
[SR-IOV]
|
||||
VirtualFunction=2
|
||||
VLANId=7
|
||||
QualityOfService=3
|
||||
MACSpoofCheck=no
|
||||
QueryReceiveSideScaling=no
|
||||
Trust=no
|
||||
LinkState=auto
|
||||
MACAddress=00:11:22:33:44:57
|
@ -158,6 +158,33 @@ def expectedFailureIfAlternativeNameIsNotAvailable():
|
||||
|
||||
return f
|
||||
|
||||
def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
|
||||
def f(func):
|
||||
call('rmmod netdevsim', stderr=subprocess.DEVNULL)
|
||||
rc = call('modprobe netdevsim', stderr=subprocess.DEVNULL)
|
||||
if rc != 0:
|
||||
return unittest.expectedFailure(func)
|
||||
|
||||
try:
|
||||
with open('/sys/bus/netdevsim/new_device', mode='w') as f:
|
||||
f.write('99 1')
|
||||
except Exception as error:
|
||||
return unittest.expectedFailure(func)
|
||||
|
||||
call('udevadm settle')
|
||||
call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
|
||||
try:
|
||||
with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
|
||||
f.write('3')
|
||||
except Exception as error:
|
||||
call('rmmod netdevsim', stderr=subprocess.DEVNULL)
|
||||
return unittest.expectedFailure(func)
|
||||
|
||||
call('rmmod netdevsim', stderr=subprocess.DEVNULL)
|
||||
return func
|
||||
|
||||
return f
|
||||
|
||||
def expectedFailureIfCAKEIsNotAvailable():
|
||||
def f(func):
|
||||
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
|
||||
@ -1695,6 +1722,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||
'25-route-vrf.network',
|
||||
'25-gateway-static.network',
|
||||
'25-gateway-next-static.network',
|
||||
'25-sriov.network',
|
||||
'25-sysctl-disable-ipv6.network',
|
||||
'25-sysctl.network',
|
||||
'25-test1.network',
|
||||
@ -2237,7 +2265,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||
self.assertRegex(output, 'inet6 .* scope link')
|
||||
output = check_output('ip -4 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
|
||||
self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
|
||||
output = check_output('ip -6 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
|
||||
@ -2260,7 +2288,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||
self.assertRegex(output, 'inet6 .* scope link')
|
||||
output = check_output('ip -4 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
|
||||
self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
|
||||
output = check_output('ip -6 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
|
||||
@ -2508,6 +2536,32 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||
self.assertRegex(output, 'quanta 1 2 3 4 5')
|
||||
self.assertRegex(output, 'priomap 3 4 5 6 7')
|
||||
|
||||
@expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
|
||||
def test_sriov(self):
|
||||
call('rmmod netdevsim', stderr=subprocess.DEVNULL)
|
||||
call('modprobe netdevsim', stderr=subprocess.DEVNULL)
|
||||
with open('/sys/bus/netdevsim/new_device', mode='w') as f:
|
||||
f.write('99 1')
|
||||
|
||||
call('udevadm settle')
|
||||
call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
|
||||
with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
|
||||
f.write('3')
|
||||
|
||||
copy_unit_to_networkd_unit_path('25-sriov.network')
|
||||
start_networkd()
|
||||
self.wait_online(['eni99np1:routable'])
|
||||
|
||||
output = check_output('ip link show dev eni99np1')
|
||||
print(output)
|
||||
self.assertRegex(output,
|
||||
'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
|
||||
'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
|
||||
'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
|
||||
)
|
||||
|
||||
call('rmmod netdevsim', stderr=subprocess.DEVNULL)
|
||||
|
||||
class NetworkdStateFileTests(unittest.TestCase, Utilities):
|
||||
links = [
|
||||
'dummy98',
|
||||
|
Loading…
x
Reference in New Issue
Block a user