]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-sriov.c
Merge pull request #33146 from DaanDeMeyer/clang
[thirdparty/systemd.git] / src / network / networkd-sriov.c
index 7a76b61c4aefec0384bb1b92500564b0717eb4df..78d8cefe7cf6801937e798d25aa51b216251a00a 100644 (file)
@@ -1,92 +1,19 @@
 /* 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) {
@@ -104,432 +31,322 @@ static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         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;
 }