When a [SR-IOV] section has no setting, e.g.
```ini
[SR-IOV]
VirtualFunction=0
```
then the kernel previously replied -EINVAL, as we send a rtnl message
with an empty IFLA_VF_INFO container.
See See do_setvfinfo() in net/core/rtnetlink.c of the kernel.
When a [SR-IOV] section that has an unsupported settings by the
interface driver, then previously the kernel partially applied
settings and returned -EOPNOTSUPP. E.f.
```ini
[SR-IOV]
VirtualFunction=0
LinkState=auto
Trust=true
MACAddress=02:01:00:3e:61:34
```
and the interface does not support configuring the link state, then
the MAC address is assigned, but the trust is not applied:
```
enp3s0f0: Failed to configure SR-IOV virtual function 0, ignoring: Operation not supported
vf 0 link/ether 02:01:00:3e:61:34 brd ff:ff:ff:ff:ff:ff, spoof checking on, link-state auto, trust off
```
To fix such issues, this makes networkd/udevd send each attribute
for VF one-by-one.
Fixes #37257 and #37275.
[REQUEST_TYPE_SET_LINK_MAC] = "MAC address",
[REQUEST_TYPE_SET_LINK_MASTER] = "master interface",
[REQUEST_TYPE_SET_LINK_MTU] = "MTU",
- [REQUEST_TYPE_SRIOV] = "SR-IOV",
+ [REQUEST_TYPE_SRIOV_VF_MAC] = "SR-IOV VF MAC address",
+ [REQUEST_TYPE_SRIOV_VF_SPOOFCHK] = "SR-IOV VF spoof check",
+ [REQUEST_TYPE_SRIOV_VF_RSS_QUERY_EN] = "SR-IOV VF RSS query",
+ [REQUEST_TYPE_SRIOV_VF_TRUST] = "SR-IOV VF trust",
+ [REQUEST_TYPE_SRIOV_VF_LINK_STATE] = "SR-IOV VF link state",
+ [REQUEST_TYPE_SRIOV_VF_VLAN_LIST] = "SR-IOV VF vlan list",
[REQUEST_TYPE_TC_QDISC] = "QDisc",
[REQUEST_TYPE_TC_CLASS] = "TClass",
[REQUEST_TYPE_UP_DOWN] = "bring link up or down",
#include "alloc-util.h"
#include "hash-funcs.h"
+#include "netif-sriov.h"
typedef struct Link Link;
typedef struct NetDev NetDev;
REQUEST_TYPE_SET_LINK_MAC, /* Setting MAC address. */
REQUEST_TYPE_SET_LINK_MASTER, /* Setting IFLA_MASTER. */
REQUEST_TYPE_SET_LINK_MTU, /* Setting MTU. */
- REQUEST_TYPE_SRIOV,
+ _REQUEST_TYPE_SRIOV_BASE,
+ REQUEST_TYPE_SRIOV_VF_MAC = _REQUEST_TYPE_SRIOV_BASE + SR_IOV_VF_MAC,
+ REQUEST_TYPE_SRIOV_VF_SPOOFCHK = _REQUEST_TYPE_SRIOV_BASE + SR_IOV_VF_SPOOFCHK,
+ REQUEST_TYPE_SRIOV_VF_RSS_QUERY_EN = _REQUEST_TYPE_SRIOV_BASE + SR_IOV_VF_RSS_QUERY_EN,
+ REQUEST_TYPE_SRIOV_VF_TRUST = _REQUEST_TYPE_SRIOV_BASE + SR_IOV_VF_TRUST,
+ REQUEST_TYPE_SRIOV_VF_LINK_STATE = _REQUEST_TYPE_SRIOV_BASE + SR_IOV_VF_LINK_STATE,
+ REQUEST_TYPE_SRIOV_VF_VLAN_LIST = _REQUEST_TYPE_SRIOV_BASE + SR_IOV_VF_VLAN_LIST,
REQUEST_TYPE_TC_CLASS,
REQUEST_TYPE_TC_QDISC,
REQUEST_TYPE_UP_DOWN,
assert(link->ifindex > 0);
assert(req);
- log_link_debug(link, "Setting up SR-IOV virtual function %"PRIu32".", sr_iov->vf);
+ SRIOVAttribute attr = req->type - _REQUEST_TYPE_SRIOV_BASE;
+ assert(attr >= 0);
+ assert(attr < _SR_IOV_ATTRIBUTE_MAX);
+
+ log_link_debug(link, "Setting up %s for SR-IOV virtual function %"PRIu32".",
+ sr_iov_attribute_to_string(attr), sr_iov->vf);
r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex);
if (r < 0)
return r;
- r = sr_iov_set_netlink_message(sr_iov, m);
+ r = sr_iov_set_netlink_message(sr_iov, attr, m);
if (r < 0)
return r;
link->sr_iov_configured = false;
ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) {
- r = link_queue_request_safe(link, REQUEST_TYPE_SRIOV,
- sr_iov, NULL,
- sr_iov_hash_func,
- sr_iov_compare_func,
- sr_iov_process_request,
- &link->sr_iov_messages,
- sr_iov_handler,
- NULL);
- if (r < 0)
- return log_link_warning_errno(link, r,
- "Failed to request to set up SR-IOV virtual function %"PRIu32": %m",
- sr_iov->vf);
+ for (SRIOVAttribute attr = 0; attr < _SR_IOV_ATTRIBUTE_MAX; attr++) {
+ if (!sr_iov_has_config(sr_iov, attr))
+ continue;
+
+ r = link_queue_request_safe(
+ link,
+ _REQUEST_TYPE_SRIOV_BASE + attr,
+ sr_iov,
+ NULL,
+ sr_iov_hash_func,
+ sr_iov_compare_func,
+ sr_iov_process_request,
+ &link->sr_iov_messages,
+ sr_iov_handler,
+ NULL);
+ if (r < 0)
+ return log_link_warning_errno(link, r,
+ "Failed to request to set up SR-IOV virtual function %"PRIu32": %m",
+ sr_iov->vf);
+ }
}
if (link->sr_iov_messages == 0) {
#include "parse-util.h"
#include "set.h"
#include "stdio-util.h"
+#include "string-table.h"
#include "string-util.h"
static SRIOV* sr_iov_free(SRIOV *sr_iov) {
sr_iov_hash_func,
sr_iov_compare_func);
-int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req) {
+static const char * const sr_iov_attribute_table[_SR_IOV_ATTRIBUTE_MAX] = {
+ [SR_IOV_VF_MAC] = "MAC address",
+ [SR_IOV_VF_SPOOFCHK] = "spoof check",
+ [SR_IOV_VF_RSS_QUERY_EN] = "RSS query",
+ [SR_IOV_VF_TRUST] = "trust",
+ [SR_IOV_VF_LINK_STATE] = "link state",
+ [SR_IOV_VF_VLAN_LIST] = "vlan list",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(sr_iov_attribute, SRIOVAttribute);
+
+bool sr_iov_has_config(SRIOV *sr_iov, SRIOVAttribute attr) {
+ assert(sr_iov);
+
+ switch (attr) {
+ case SR_IOV_VF_MAC:
+ return !ether_addr_is_null(&sr_iov->mac);
+
+ case SR_IOV_VF_SPOOFCHK:
+ return sr_iov->vf_spoof_check_setting >= 0;
+
+ case SR_IOV_VF_RSS_QUERY_EN:
+ return sr_iov->query_rss >= 0;
+
+ case SR_IOV_VF_TRUST:
+ return sr_iov->trust >= 0;
+
+ case SR_IOV_VF_LINK_STATE:
+ return sr_iov->link_state >= 0;
+
+ case SR_IOV_VF_VLAN_LIST:
+ return sr_iov->vlan > 0;
+
+ default:
+ assert_not_reached();
+ }
+}
+
+int sr_iov_set_netlink_message(SRIOV *sr_iov, SRIOVAttribute attr, sd_netlink_message *req) {
+
int r;
assert(sr_iov);
if (r < 0)
return r;
- if (!ether_addr_is_null(&sr_iov->mac)) {
+ switch (attr) {
+ case SR_IOV_VF_MAC: {
+ if (ether_addr_is_null(&sr_iov->mac))
+ return -ENODATA;
+
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;
+ break;
}
+ case SR_IOV_VF_SPOOFCHK: {
+ if (sr_iov->vf_spoof_check_setting < 0)
+ return -ENODATA;
- 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;
+ break;
}
+ case SR_IOV_VF_RSS_QUERY_EN: {
+ if (sr_iov->query_rss < 0)
+ return -ENODATA;
- 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;
+ break;
}
+ case SR_IOV_VF_TRUST: {
+ if (sr_iov->trust < 0)
+ return -ENODATA;
- 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;
+ break;
}
+ case SR_IOV_VF_LINK_STATE: {
+ if (sr_iov->link_state < 0)
+ return -ENODATA;
- 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;
+ break;
}
+ case SR_IOV_VF_VLAN_LIST: {
+ if (sr_iov->vlan <= 0)
+ return -ENODATA;
- 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;
return r;
r = sd_netlink_message_close_container(req);
- if (r < 0)
- return r;
+ break;
}
+ default:
+ assert_not_reached();
+ }
+ if (r < 0)
+ return r;
r = sd_netlink_message_close_container(req);
if (r < 0)
#include "ether-addr-util.h"
#include "hashmap.h"
+typedef enum SRIOVAttribute {
+ SR_IOV_VF_MAC,
+ SR_IOV_VF_SPOOFCHK,
+ SR_IOV_VF_RSS_QUERY_EN,
+ SR_IOV_VF_TRUST,
+ SR_IOV_VF_LINK_STATE,
+ SR_IOV_VF_VLAN_LIST,
+ _SR_IOV_ATTRIBUTE_MAX,
+ _SR_IOV_ATTRIBUTE_INVALID = -EINVAL,
+} SRIOVAttribute;
+
typedef enum SRIOVLinkState {
SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
struct ether_addr mac;
} SRIOV;
+const char* sr_iov_attribute_to_string(SRIOVAttribute a) _const_;
+
void sr_iov_hash_func(const SRIOV *sr_iov, struct siphash *state);
int sr_iov_compare_func(const SRIOV *s1, const SRIOV *s2);
-int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req);
+bool sr_iov_has_config(SRIOV *sr_iov, SRIOVAttribute attr);
+int sr_iov_set_netlink_message(SRIOV *sr_iov, SRIOVAttribute attr, sd_netlink_message *req);
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);
}
static int sr_iov_configure(Link *link, sd_netlink **rtnl, SRIOV *sr_iov) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
assert(rtnl);
assert(link->ifindex > 0);
- if (!*rtnl) {
- r = sd_netlink_open(rtnl);
+ for (SRIOVAttribute attr = 0; attr < _SR_IOV_ATTRIBUTE_MAX; attr++) {
+ if (!sr_iov_has_config(sr_iov, attr))
+ continue;
+
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ r = sd_rtnl_message_new_link(*rtnl, &req, RTM_SETLINK, link->ifindex);
if (r < 0)
return r;
- }
- r = sd_rtnl_message_new_link(*rtnl, &req, RTM_SETLINK, link->ifindex);
- if (r < 0)
- return r;
-
- r = sr_iov_set_netlink_message(sr_iov, req);
- if (r < 0)
- return r;
+ r = sr_iov_set_netlink_message(sr_iov, attr, req);
+ if (r < 0)
+ return r;
- r = sd_netlink_call(*rtnl, req, 0, NULL);
- if (r < 0)
- return r;
+ r = sd_netlink_call(*rtnl, req, 0, NULL);
+ if (r < 0)
+ return r;
+ }
return 0;
}