From 41ce9d769d394d19f1caab8b8a89b7dea50db0bc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 15 Jan 2022 03:35:27 +0900 Subject: [PATCH] udev/net: allow to set number of SR-IOV virtual functions This adds SR-IOVVirtualFunctions= setting in [Link] section. --- man/systemd.link.xml | 9 ++ src/network/networkd-network.c | 2 +- src/shared/netif-sriov.c | 150 ++++++++++++++++++++- src/shared/netif-sriov.h | 7 +- src/udev/net/link-config-gperf.gperf | 1 + src/udev/net/link-config.c | 8 +- src/udev/net/link-config.h | 1 + test/fuzz/fuzz-link-parser/directives.link | 1 + 8 files changed, 173 insertions(+), 6 deletions(-) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 60d2c11e3c..700defeda6 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -946,6 +946,15 @@ + + SR-IOVVirtualFunctions= + + Specifies the number of SR-IOV virtual functions. Takes an integer in the range + 0…2147483647. Defaults to unset, and automatically determined from the values specified in + the VirtualFunction= settings in the [SR-IOV] sections. + + + diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index df4b6f23ad..3142be471f 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 = sr_iov_drop_invalid_sections(network->sr_iov_by_section); + r = sr_iov_drop_invalid_sections(UINT32_MAX, network->sr_iov_by_section); if (r < 0) return r; network_drop_invalid_static_leases(network); diff --git a/src/shared/netif-sriov.c b/src/shared/netif-sriov.c index fc40ccbbb6..720aa65f4c 100644 --- a/src/shared/netif-sriov.c +++ b/src/shared/netif-sriov.c @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "device-util.h" #include "netlink-util.h" #include "netif-sriov.h" #include "parse-util.h" #include "set.h" +#include "stdio-util.h" #include "string-util.h" static int sr_iov_new(SRIOV **ret) { @@ -179,7 +181,100 @@ int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req) { return 0; } -static int sr_iov_section_verify(SRIOV *sr_iov) { +int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret) { + const char *str; + uint32_t n; + int r; + + assert(device); + assert(ret); + + r = sd_device_get_sysattr_value(device, "device/sriov_numvfs", &str); + if (r < 0) + return r; + + r = safe_atou32(str, &n); + if (r < 0) + return r; + + *ret = n; + return 0; +} + +int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) { + char val[DECIMAL_STR_MAX(uint32_t)]; + const char *str; + int r; + + assert(device); + + if (num_vfs == UINT32_MAX) { + uint32_t current_num_vfs; + SRIOV *sr_iov; + + /* If the number of virtual functions is not specified, then use the maximum number of VF + 1. */ + + num_vfs = 0; + ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) + num_vfs = MAX(num_vfs, sr_iov->vf + 1); + + if (num_vfs == 0) /* No VF is configured. */ + return 0; + + r = sr_iov_get_num_vfs(device, ¤t_num_vfs); + if (r < 0) + return log_device_debug_errno(device, r, "Failed to get the current number of SR-IOV virtual functions: %m"); + + /* Enough VFs already exist. */ + if (num_vfs <= current_num_vfs) + return 0; + + } else if (num_vfs == 0) { + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0"); + if (r < 0) + log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute, ignoring: %m"); + + /* Gracefully handle the error in disabling VFs when the interface does not support SR-IOV. */ + return r == -ENOENT ? 0 : r; + } + + /* So, the interface does not have enough VFs. Before increasing the number of VFs, check the + * maximum allowed number of VFs from the sriov_totalvfs sysattr. Note that the sysattr + * currently exists only for PCI drivers. Hence, ignore -ENOENT. + * TODO: netdevsim provides the information in debugfs. */ + r = sd_device_get_sysattr_value(device, "device/sriov_totalvfs", &str); + if (r >= 0) { + uint32_t max_num_vfs; + + r = safe_atou32(str, &max_num_vfs); + if (r < 0) + return log_device_debug_errno(device, r, "Failed to parse device/sriov_totalvfs sysfs attribute '%s': %m", str); + + if (num_vfs > max_num_vfs) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(ERANGE), + "Specified number of virtual functions is out of range. " + "The maximum allowed value is %"PRIu32".", + max_num_vfs); + + } else if (r != -ENOENT) /* Currently, only PCI driver has the attribute. */ + return log_device_debug_errno(device, r, "Failed to read device/sriov_totalvfs sysfs attribute: %m"); + + xsprintf(val, "%"PRIu32, num_vfs); + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val); + if (r == -EBUSY) { + /* Some devices e.g. netdevsim refuse to set sriov_numvfs if it has non-zero value. */ + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0"); + if (r >= 0) + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val); + } + if (r < 0) + return log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute: %m"); + + log_device_debug(device, "device/sriov_numvfs sysfs attribute set to '%s'.", val); + return 0; +} + +static int sr_iov_section_verify(uint32_t num_vfs, SRIOV *sr_iov) { assert(sr_iov); if (section_is_invalid(sr_iov->section)) @@ -191,10 +286,16 @@ static int sr_iov_section_verify(SRIOV *sr_iov) { "Ignoring [SR-IOV] section from line %u.", sr_iov->section->filename, sr_iov->section->line); + if (sr_iov->vf >= num_vfs) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: VirtualFunction= must be smaller than the value specified in SR-IOVVirtualFunctions=. " + "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) { +int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) { _cleanup_hashmap_free_ Hashmap *hashmap = NULL; SRIOV *sr_iov; int r; @@ -202,7 +303,7 @@ int sr_iov_drop_invalid_sections(OrderedHashmap *sr_iov_by_section) { ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) { SRIOV *dup; - if (sr_iov_section_verify(sr_iov) < 0) { + if (sr_iov_section_verify(num_vfs, sr_iov) < 0) { sr_iov_free(sr_iov); continue; } @@ -485,3 +586,46 @@ int config_parse_sr_iov_mac( TAKE_PTR(sr_iov); return 0; } + +int config_parse_sr_iov_num_vfs( + 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) { + + uint32_t n, *num_vfs = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *num_vfs = UINT32_MAX; + return 0; + } + + r = safe_atou32(rvalue, &n); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (n > INT_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "The number of SR-IOV virtual functions is too large. It must be equal to " + "or smaller than 2147483647. Ignoring assignment: %"PRIu32, n); + return 0; + } + + *num_vfs = n; + return 0; +} diff --git a/src/shared/netif-sriov.h b/src/shared/netif-sriov.h index 871496287f..4c85f101c7 100644 --- a/src/shared/netif-sriov.h +++ b/src/shared/netif-sriov.h @@ -3,6 +3,8 @@ #include +#include "sd-device.h" + #include "conf-parser.h" #include "ether-addr-util.h" #include "hashmap.h" @@ -32,7 +34,9 @@ typedef struct 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); +int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret); +int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section); +int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section); DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_free); @@ -41,3 +45,4 @@ 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); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_num_vfs); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 3732fd53ef..7dde4ed59f 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -102,6 +102,7 @@ Link.RxMaxCoalescedHighFrames, config_parse_coalesce_u32, Link.TxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high) Link.TxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high) Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rate_sample_interval) +Link.SR-IOVVirtualFunctions, config_parse_sr_iov_num_vfs, 0, offsetof(LinkConfig, sr_iov_num_vfs) SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 3e8b6aaaf2..5bd029ecc5 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -250,6 +250,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { .txqueuelen = UINT32_MAX, .coalesce.use_adaptive_rx_coalesce = -1, .coalesce.use_adaptive_tx_coalesce = -1, + .sr_iov_num_vfs = UINT32_MAX, }; for (i = 0; i < ELEMENTSOF(config->features); i++) @@ -290,7 +291,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { if (r < 0) return r; - r = sr_iov_drop_invalid_sections(config->sr_iov_by_section); + r = sr_iov_drop_invalid_sections(config->sr_iov_num_vfs, config->sr_iov_by_section); if (r < 0) return r; @@ -874,6 +875,11 @@ static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) { assert(link); assert(link->config); + assert(link->device); + + r = sr_iov_set_num_vfs(link->device, link->config->sr_iov_num_vfs, link->config->sr_iov_by_section); + if (r < 0) + log_link_warning_errno(link, r, "Failed to set the number of SR-IOV virtual functions, ignoring: %m"); ORDERED_HASHMAP_FOREACH(sr_iov, link->config->sr_iov_by_section) { r = sr_iov_configure(link, rtnl, sr_iov); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index e71738cfbf..0d1d117f2e 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -77,6 +77,7 @@ struct LinkConfig { int autoneg_flow_control; netdev_coalesce_param coalesce; + uint32_t sr_iov_num_vfs; OrderedHashmap *sr_iov_by_section; LIST_FIELDS(LinkConfig, configs); diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index 87b435c745..a5773826c5 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -80,6 +80,7 @@ RxMaxCoalescedHighFrames= TxCoalesceHighSec= TxMaxCoalescedHighFrames= CoalescePacketRateSampleIntervalSec= +SR-IOVVirtualFunctions= [SR-IOV] VirtualFunction= MACSpoofCheck=