From f92c5bb18cee41d48b95d61a2a2dc613203844ca Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 17 Feb 2022 18:47:26 +0900 Subject: [PATCH 1/3] udev-util: introduce udev_available() helper function --- src/shared/udev-util.c | 15 +++++++++++++++ src/shared/udev-util.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index e2574b9af3..2f21ee6dbe 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -20,6 +20,7 @@ #include "path-util.h" #include "signal-util.h" #include "socket-util.h" +#include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strxcpyx.h" @@ -723,3 +724,17 @@ int on_ac_power(void) { return found_online || !found_offline; } + +bool udev_available(void) { + static int cache = -1; + + /* The service systemd-udevd is started only when /sys is read write. + * See systemd-udevd.service: ConditionPathIsReadWrite=/sys + * Also, our container interface (http://systemd.io/CONTAINER_INTERFACE/) states that /sys must + * be mounted in read-only mode in containers. */ + + if (cache >= 0) + return cache; + + return (cache = path_is_read_only_fs("/sys/") <= 0); +} diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index 8d21dc4364..a48beb95ea 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -55,6 +55,8 @@ int udev_queue_init(void); int on_ac_power(void); +bool udev_available(void); + #if HAVE_SYS_SDT_H /* Each trace point can have different number of additional arguments. Note that when the macro is used only From 82f52245baa2fe26f1de7b50a036d3746deb7df1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 17 Feb 2022 18:55:24 +0900 Subject: [PATCH 2/3] network: use udev_available() where applicable --- src/network/networkd-link.c | 4 +--- src/network/networkd-manager.c | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 238b68579c..5706174f5b 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -59,7 +59,6 @@ #include "networkd-sysctl.h" #include "set.h" #include "socket-util.h" -#include "stat-util.h" #include "stdio-util.h" #include "string-table.h" #include "strv.h" @@ -1451,8 +1450,7 @@ static int link_check_initialized(Link *link) { assert(link); - if (path_is_read_only_fs("/sys") > 0) - /* no udev */ + if (!udev_available()) return link_initialized_and_synced(link); /* udev should be around */ diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 553aa2beb1..2453c33259 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -55,6 +55,7 @@ #include "sysctl-util.h" #include "tclass.h" #include "tmpfile-util.h" +#include "udev-util.h" /* use 128 MB for receive socket kernel queue. */ #define RCVBUF_SIZE (128*1024*1024) @@ -172,7 +173,7 @@ static int manager_connect_udev(Manager *m) { /* udev does not initialize devices inside containers, so we rely on them being already * initialized before entering the container. */ - if (path_is_read_only_fs("/sys") > 0) + if (!udev_available()) return 0; r = sd_device_monitor_new(&m->device_monitor); From e1658632aefc963c4a651de433cceb3a9512afd6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 17 Feb 2022 05:24:26 +0900 Subject: [PATCH 3/3] network: call ethtool after link is initialized by udevd Fixes #22538. --- src/network/networkd-link.c | 125 +++++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 38 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5706174f5b..6fdf800782 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2022,6 +2022,85 @@ static int link_update_master(Link *link, sd_netlink_message *message) { return 0; } +static int link_update_driver(Link *link, sd_netlink_message *message) { + int r; + + assert(link); + assert(link->manager); + assert(message); + + /* Driver is already read. Assuming the driver is never changed. */ + if (link->driver) + return 0; + + /* When udevd is running, read the driver after the interface is initialized by udevd. + * Otherwise, ethtool may not work correctly. See issue #22538. + * When udevd is not running, read the value when the interface is detected. */ + if (link->state != (udev_available() ? LINK_STATE_INITIALIZED : LINK_STATE_PENDING)) + return 0; + + r = ethtool_get_driver(&link->manager->ethtool_fd, link->ifname, &link->driver); + if (r < 0) { + log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m"); + return 0; + } + + log_link_debug(link, "Found driver: %s", strna(link->driver)); + + if (streq_ptr(link->driver, "dsa")) { + uint32_t dsa_master_ifindex = 0; + + r = sd_netlink_message_read_u32(message, IFLA_LINK, &dsa_master_ifindex); + if (r < 0 && r != -ENODATA) + return log_link_debug_errno(link, r, "rtnl: failed to read ifindex of the DSA master interface: %m"); + + if (dsa_master_ifindex > INT_MAX) { + log_link_debug(link, "rtnl: received too large DSA master ifindex (%"PRIu32" > INT_MAX), ignoring.", + dsa_master_ifindex); + dsa_master_ifindex = 0; + } + + link->dsa_master_ifindex = (int) dsa_master_ifindex; + } + + return 0; +} + +static int link_update_permanent_hardware_address(Link *link, sd_netlink_message *message) { + int r; + + assert(link); + assert(link->manager); + assert(message); + + if (link->permanent_hw_addr.length > 0) + return 0; + + /* When udevd is running, read the permanent hardware address after the interface is + * initialized by udevd. Otherwise, ethtool may not work correctly. See issue #22538. + * When udevd is not running, read the value when the interface is detected. */ + if (link->state != (udev_available() ? LINK_STATE_INITIALIZED : LINK_STATE_PENDING)) + return 0; + + r = netlink_message_read_hw_addr(message, IFLA_PERM_ADDRESS, &link->permanent_hw_addr); + if (r < 0) { + if (r != -ENODATA) + return log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute: %m"); + + if (netlink_message_read_hw_addr(message, IFLA_ADDRESS, NULL) >= 0) { + /* Fallback to ethtool, if the link has a hardware address. */ + r = ethtool_get_permanent_hw_addr(&link->manager->ethtool_fd, link->ifname, &link->permanent_hw_addr); + if (r < 0) + log_link_debug_errno(link, r, "Permanent hardware address not found, continuing without: %m"); + } + } + + if (link->permanent_hw_addr.length > 0) + log_link_debug(link, "Saved permanent hardware address: %s", HW_ADDR_TO_STR(&link->permanent_hw_addr)); + + return 0; +} + static int link_update_hardware_address(Link *link, sd_netlink_message *message) { struct hw_addr_data addr; int r; @@ -2309,6 +2388,14 @@ static int link_update(Link *link, sd_netlink_message *message) { if (r < 0) return r; + r = link_update_driver(link, message); + if (r < 0) + return r; + + r = link_update_permanent_hardware_address(link, message); + if (r < 0) + return r; + r = link_update_hardware_address(link, message); if (r < 0) return r; @@ -2421,44 +2508,6 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { log_link_debug(link, "Saved new link: ifindex=%i, iftype=%s(%u), kind=%s", link->ifindex, strna(arphrd_to_name(link->iftype)), link->iftype, strna(link->kind)); - r = netlink_message_read_hw_addr(message, IFLA_PERM_ADDRESS, &link->permanent_hw_addr); - if (r < 0) { - if (r != -ENODATA) - log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute, ignoring: %m"); - - if (netlink_message_read_hw_addr(message, IFLA_ADDRESS, NULL) >= 0) { - /* Fallback to ethtool, if the link has a hardware address. */ - r = ethtool_get_permanent_hw_addr(&manager->ethtool_fd, link->ifname, &link->permanent_hw_addr); - if (r < 0) - log_link_debug_errno(link, r, "Permanent hardware address not found, continuing without: %m"); - } - } - if (link->permanent_hw_addr.length > 0) - log_link_debug(link, "Saved permanent hardware address: %s", HW_ADDR_TO_STR(&link->permanent_hw_addr)); - - r = ethtool_get_driver(&manager->ethtool_fd, link->ifname, &link->driver); - if (r < 0) - log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m"); - else - log_link_debug(link, "Found driver: %s", strna(link->driver)); - - 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; }