]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: wait until the DSA master interface becomes up 21737/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 9 Dec 2021 17:49:07 +0000 (02:49 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 16 Dec 2021 17:00:25 +0000 (02:00 +0900)
This is for the DSA subsystem, which have several stacked interfaces
on the master interface. To bring up a stacked interface, it is necessary
that the master is already up. See
https://github.com/systemd/systemd/issues/7478#issuecomment-348508263.

Note this is not necessary for newer kernels which includes
https://github.com/torvalds/linux/commit/9d5ef190e5615a7b63af89f88c4106a5bc127974.

Fixes #7478.

src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-setlink.c

index 7bc391d68d80c88a1bd9d457dd97f60d62f7d7ae..cea700bbb8449203740a9457a358d6d881cba105 100644 (file)
@@ -2550,6 +2550,23 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
         if (r < 0)
                 log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m");
 
+        if (streq_ptr(link->driver, "dsa")) {
+                uint32_t dsa_master_ifindex;
+
+                r = sd_netlink_message_read_u32(message, IFLA_LINK, &dsa_master_ifindex);
+                if (r < 0) {
+                        dsa_master_ifindex = 0;
+                        if (r != -ENODATA)
+                                log_link_warning_errno(link, r, "rtnl: failed to read ifindex of the DSA master interface, ignoring: %m");
+                } else if (dsa_master_ifindex > INT_MAX) {
+                        dsa_master_ifindex = 0;
+                        log_link_warning(link, "rtnl: received too large DSA master ifindex (%"PRIu32" > INT_MAX), ignoring.",
+                                         dsa_master_ifindex);
+                }
+
+                link->dsa_master_ifindex = (int) dsa_master_ifindex;
+        }
+
         *ret = TAKE_PTR(link);
         return 0;
 }
index 4ef86f485bc7ef6ceecd610b64333d4c055509f0..8bf1cc67f7188a1195432db0fbfb493408ec3bab 100644 (file)
@@ -48,6 +48,7 @@ typedef struct Link {
 
         int ifindex;
         int master_ifindex;
+        int dsa_master_ifindex;
         char *ifname;
         char **alternative_names;
         char *kind;
index 7de62711c57b80d9590da8614e8984c6dbf3f241..2b659e8f3683e265c26749991d4950e65ea8874b 100644 (file)
@@ -948,6 +948,41 @@ int link_configure_mtu(Link *link) {
         return link_request_to_set_mtu(link, mtu);
 }
 
+static int link_up_dsa_slave(Link *link) {
+        Link *master;
+        int r;
+
+        assert(link);
+
+        /* For older kernels (specifically, older than 9d5ef190e5615a7b63af89f88c4106a5bc127974, kernel-5.12),
+         * it is necessary to bring up a DSA slave that its master interface is already up. And bringing up
+         * the slave fails with -ENETDOWN. So, let's bring up the master even if it is not managed by us,
+         * and try to bring up the slave after the master becomes up. */
+
+        if (link->dsa_master_ifindex <= 0)
+                return 0;
+
+        if (!streq_ptr(link->driver, "dsa"))
+                return 0;
+
+        if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0)
+                return 0;
+
+        if (master->state == LINK_STATE_UNMANAGED) {
+                /* If the DSA master interface is unmanaged, then it will never become up.
+                 * Let's request to bring up the master. */
+                r = link_request_to_bring_up_or_down(master, /* up = */ true);
+                if (r < 0)
+                        return r;
+        }
+
+        r = link_request_to_bring_up_or_down(link, /* up = */ true);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
 static int link_up_or_down_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool up, bool on_activate) {
         int r;
 
@@ -958,7 +993,9 @@ static int link_up_or_down_handler_internal(sd_netlink *rtnl, sd_netlink_message
                 goto on_error;
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0) {
+        if (r == -ENETDOWN && up && link_up_dsa_slave(link) > 0)
+                log_link_message_debug_errno(link, m, r, "Could not bring up dsa slave, retrying again after dsa master becomes up");
+        else if (r < 0) {
                 const char *error_msg;
 
                 error_msg = up ?
@@ -1130,9 +1167,21 @@ int link_request_to_activate(Link *link) {
         return 0;
 }
 
-static bool link_is_ready_to_bring_up_or_down(Link *link) {
+static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) {
         assert(link);
 
+        if (up && link->dsa_master_ifindex > 0) {
+                Link *master;
+
+                /* The master inteface must be up. See comments in link_up_dsa_slave(). */
+
+                if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0)
+                        return false;
+
+                if (!FLAGS_SET(master->flags, IFF_UP))
+                        return false;
+        }
+
         if (link->state == LINK_STATE_UNMANAGED)
                 return true;
 
@@ -1160,7 +1209,7 @@ int request_process_link_up_or_down(Request *req) {
         link = req->link;
         up = PTR_TO_INT(req->userdata);
 
-        if (!link_is_ready_to_bring_up_or_down(link))
+        if (!link_is_ready_to_bring_up_or_down(link, up))
                 return 0;
 
         r = link_up_or_down(link, up, req->netlink_handler);