/* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright © 2020 VMware, Inc. */
-#include "alloc-util.h"
-#include "netlink-util.h"
+#include "device-enumerator-private.h"
+#include "device-util.h"
+#include "fd-util.h"
+#include "networkd-link.h"
#include "networkd-manager.h"
+#include "networkd-queue.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_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_(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_put(&network->sr_iov_by_section, &network_config_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);
-
- 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) {
+static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, SRIOV *sr_iov) {
int r;
+ assert(m);
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) {
return 1;
}
-static int sr_iov_configure(Link *link, SRIOV *sr_iov) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+static int sr_iov_configure(SRIOV *sr_iov, Link *link, Request *req) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
+ assert(sr_iov);
assert(link);
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
+ assert(req);
- 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");
+ log_link_debug(link, "Setting SR-IOV virtual function %"PRIu32".", sr_iov->vf);
- r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex);
if (r < 0)
- return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m");
+ return r;
- r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
+ r = sr_iov_set_netlink_message(sr_iov, m);
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");
+ return r;
- 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");
+ return request_call_netlink_async(link->manager->rtnl, m, req);
+}
- 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");
- }
+static int sr_iov_process_request(Request *req, Link *link, SRIOV *sr_iov) {
+ int r;
- 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");
+ assert(req);
+ assert(link);
+ assert(sr_iov);
- 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");
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
- r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler,
- link_netlink_destroy_callback, link);
+ r = sr_iov_configure(sr_iov, link, req);
if (r < 0)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+ return log_link_warning_errno(link, r,
+ "Failed to configure SR-IOV virtual function %"PRIu32": %m",
+ sr_iov->vf);
- link_ref(link);
- link->sr_iov_messages++;
-
- return 0;
+ return 1;
}
-int link_configure_sr_iov(Link *link) {
+int link_request_sr_iov_vfs(Link *link) {
SRIOV *sr_iov;
int r;
assert(link);
assert(link->network);
- if (link->sr_iov_messages != 0) {
- log_link_debug(link, "SR-IOV is configuring.");
- return 0;
- }
-
link->sr_iov_configured = false;
ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) {
- r = sr_iov_configure(link, sr_iov);
+ 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 r;
+ return log_link_warning_errno(link, r,
+ "Failed to request SR-IOV virtual function %"PRIu32": %m",
+ sr_iov->vf);
}
- if (link->sr_iov_messages == 0)
+ if (link->sr_iov_messages == 0) {
link->sr_iov_configured = true;
- else
+ link_check_ready(link);
+ } else
log_link_debug(link, "Configuring SR-IOV");
return 0;
}
-static int sr_iov_section_verify(SRIOV *sr_iov) {
- assert(sr_iov);
+static int find_ifindex_from_pci_dev_port(sd_device *pci_dev, const char *dev_port) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ int ifindex, r;
+
+ assert(pci_dev);
+ assert(dev_port);
- if (section_is_invalid(sr_iov->section))
- return -EINVAL;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
- if (sr_iov->vf == UINT32_MAX)
- 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);
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
- return 0;
-}
+ r = sd_device_enumerator_add_match_parent(e, pci_dev);
+ if (r < 0)
+ return r;
-void network_drop_invalid_sr_iov(Network *network) {
- SRIOV *sr_iov;
+ r = sd_device_enumerator_add_match_subsystem(e, "net", true);
+ if (r < 0)
+ return r;
- assert(network);
+ r = sd_device_enumerator_add_match_sysattr(e, "dev_port", dev_port, true);
+ if (r < 0)
+ return r;
+
+ dev = sd_device_enumerator_get_device_first(e);
+ if (!dev)
+ return -ENODEV; /* no device found */
+
+ if (sd_device_enumerator_get_device_next(e))
+ return -ENXIO; /* multiple devices found */
+
+ r = sd_device_get_ifindex(dev, &ifindex);
+ if (r < 0)
+ return r;
- ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section)
- if (sr_iov_section_verify(sr_iov) < 0)
- sr_iov_free(sr_iov);
+ assert(ifindex > 0);
+ return ifindex;
}
-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;
+static int manager_update_sr_iov_ifindices(Manager *manager, int phys_port_ifindex, int virt_port_ifindex) {
+ Link *phys_link = NULL, *virt_link = NULL;
int r;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ assert(manager);
+ assert(phys_port_ifindex > 0);
+ assert(virt_port_ifindex > 0);
+
+ /* This sets ifindices only when both interfaces are already managed by us. */
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+ r = link_get_by_index(manager, phys_port_ifindex, &phys_link);
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("Invalid lvalue");
-
- TAKE_PTR(sr_iov);
- return 0;
- }
+ r = link_get_by_index(manager, virt_port_ifindex, &virt_link);
+ if (r < 0)
+ return r;
- 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;
+ /* update VF ifindex in PF */
+ r = set_ensure_put(&phys_link->sr_iov_virt_port_ifindices, NULL, INT_TO_PTR(virt_port_ifindex));
+ if (r < 0)
+ return r;
+
+ log_link_debug(phys_link,
+ "Found SR-IOV VF port %s(%i).",
+ virt_link ? virt_link->ifname : "n/a", virt_port_ifindex);
+
+ /* update PF ifindex in VF */
+ if (virt_link->sr_iov_phys_port_ifindex > 0 && virt_link->sr_iov_phys_port_ifindex != phys_port_ifindex) {
+ Link *old_phys_link;
+
+ if (link_get_by_index(manager, virt_link->sr_iov_phys_port_ifindex, &old_phys_link) >= 0)
+ set_remove(old_phys_link->sr_iov_virt_port_ifindices, INT_TO_PTR(virt_port_ifindex));
}
- 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("Invalid lvalue");
-
- TAKE_PTR(sr_iov);
+ virt_link->sr_iov_phys_port_ifindex = phys_port_ifindex;
+
+ log_link_debug(virt_link,
+ "Found SR-IOV PF port %s(%i).",
+ phys_link ? phys_link->ifname : "n/a", phys_port_ifindex);
+
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;
+static int link_set_sr_iov_phys_port(Link *link, sd_device *pci_dev, const char *dev_port) {
+ _cleanup_(sd_device_unrefp) sd_device *pci_physfn_dev = NULL;
int r;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ assert(link);
+ assert(link->manager);
+ assert(pci_dev);
+ assert(dev_port);
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+ if (link->sr_iov_phys_port_ifindex > 0)
+ return 0;
+
+ r = sd_device_new_child(&pci_physfn_dev, pci_dev, "physfn");
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;
- }
+ r = find_ifindex_from_pci_dev_port(pci_physfn_dev, dev_port);
+ if (r < 0)
+ return r;
- TAKE_PTR(sr_iov);
- return 0;
+ return manager_update_sr_iov_ifindices(link->manager, r, link->ifindex);
}
-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;
+static int link_set_sr_iov_virt_ports(Link *link, sd_device *pci_dev, const char *dev_port) {
+ const char *name;
int r;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ assert(link);
+ assert(link->manager);
+ assert(pci_dev);
+ assert(dev_port);
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
+ set_clear(link->sr_iov_virt_port_ifindices);
- /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
- * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
+ FOREACH_DEVICE_CHILD_WITH_SUFFIX(pci_dev, child, name) {
+ const char *n;
- if (isempty(rvalue)) {
- sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
- TAKE_PTR(sr_iov);
- return 0;
- }
+ /* Accept name prefixed with "virtfn", but refuse "virtfn" itself. */
+ n = startswith(name, "virtfn");
+ if (isempty(n) || !in_charset(n, DIGITS))
+ continue;
- if (streq(rvalue, "auto")) {
- sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
- TAKE_PTR(sr_iov);
- return 0;
- }
+ r = find_ifindex_from_pci_dev_port(child, dev_port);
+ if (r < 0)
+ continue;
- 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;
+ if (manager_update_sr_iov_ifindices(link->manager, link->ifindex, r) < 0)
+ continue;
}
- 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 link_set_sr_iov_ifindices(Link *link) {
+ const char *dev_port;
+ sd_device *pci_dev;
int r;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ assert(link);
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
+ if (!link->dev)
+ return -ENODEV;
- 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);
+ r = sd_device_get_parent_with_subsystem_devtype(link->dev, "pci", NULL, &pci_dev);
+ if (ERRNO_IS_NEG_DEVICE_ABSENT(r))
return 0;
- }
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to get parent PCI device: %m");
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
+ /* This may return -EINVAL or -ENODEV, instead of -ENOENT, if the device has been removed or is being
+ * removed. Let's ignore the error codes here. */
+ r = sd_device_get_sysattr_value(link->dev, "dev_port", &dev_port);
+ if (ERRNO_IS_NEG_DEVICE_ABSENT(r) || r == -EINVAL)
return 0;
- }
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to get 'dev_port' sysfs attribute: %m");
- 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");
+ r = link_set_sr_iov_phys_port(link, pci_dev, dev_port);
+ if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
+ return log_link_debug_errno(link, r, "Failed to set SR-IOV physical port: %m");
+
+ r = link_set_sr_iov_virt_ports(link, pci_dev, dev_port);
+ if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
+ return log_link_debug_errno(link, r, "Failed to set SR-IOV virtual ports: %m");
- 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;
+void link_clear_sr_iov_ifindices(Link *link) {
+ void *v;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ assert(link);
+ assert(link->manager);
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
+ if (link->sr_iov_phys_port_ifindex > 0) {
+ Link *phys_link;
- if (isempty(rvalue)) {
- sr_iov->mac = ETHER_ADDR_NULL;
- TAKE_PTR(sr_iov);
- return 0;
+ if (link_get_by_index(link->manager, link->sr_iov_phys_port_ifindex, &phys_link) >= 0)
+ set_remove(phys_link->sr_iov_virt_port_ifindices, INT_TO_PTR(link->ifindex));
+
+ link->sr_iov_phys_port_ifindex = 0;
}
- r = ether_addr_from_string(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;
+ while ((v = set_steal_first(link->sr_iov_virt_port_ifindices))) {
+ Link *virt_link;
+
+ if (link_get_by_index(link->manager, PTR_TO_INT(v), &virt_link) >= 0)
+ virt_link->sr_iov_phys_port_ifindex = 0;
}
+}
- TAKE_PTR(sr_iov);
- return 0;
+bool check_ready_for_all_sr_iov_ports(
+ Link *link,
+ bool allow_unmanaged, /* for the main target */
+ bool (check_one)(Link *link, bool allow_unmanaged)) {
+
+ Link *phys_link;
+ void *v;
+
+ assert(link);
+ assert(link->manager);
+ assert(check_one);
+
+ /* Some drivers make VF ports become down when their PF port becomes down, and may fail to configure
+ * VF ports. Also, when a VF port becomes up/down, its PF port and other VF ports may become down.
+ * See issue #23315. */
+
+ /* First, check the main target. */
+ if (!check_one(link, allow_unmanaged))
+ return false;
+
+ /* If this is a VF port, then also check the PF port. */
+ if (link->sr_iov_phys_port_ifindex > 0) {
+ if (link_get_by_index(link->manager, link->sr_iov_phys_port_ifindex, &phys_link) < 0 ||
+ !check_one(phys_link, /* allow_unmanaged = */ true))
+ return false;
+ } else
+ phys_link = link;
+
+ /* Also check all VF ports. */
+ SET_FOREACH(v, phys_link->sr_iov_virt_port_ifindices) {
+ int ifindex = PTR_TO_INT(v);
+ Link *virt_link;
+
+ if (ifindex == link->ifindex)
+ continue; /* The main target link is a VF port, and its state is already checked. */
+
+ if (link_get_by_index(link->manager, ifindex, &virt_link) < 0)
+ return false;
+
+ if (!check_one(virt_link, /* allow_unmanaged = */ true))
+ return false;
+ }
+
+ return true;
}