diff --git a/SOURCES/1014-settings-fix-wait-device-timeout-rh1853348.patch b/SOURCES/1014-settings-fix-wait-device-timeout-rh1853348.patch new file mode 100644 index 0000000..8ac0a91 --- /dev/null +++ b/SOURCES/1014-settings-fix-wait-device-timeout-rh1853348.patch @@ -0,0 +1,1277 @@ +From 707e63e684e3cbd3dff986a26ca725329c5e70f5 Mon Sep 17 00:00:00 2001 +From: Beniamino Galvani +Date: Wed, 5 Aug 2020 13:26:34 +0200 +Subject: [PATCH 1/7] initrd: disable STP on bridges + +NM enables by default STP on bridges, which causes a forwarding delay +of 15 seconds on boot. Disable it. + +(cherry picked from commit 0a006c04121a13221de585d3f778c5341f36b504) +(cherry picked from commit e2830af77a981fef5b7ea7e54dc13de17231fee9) +--- + src/initrd/nmi-cmdline-reader.c | 12 +++++++++--- + src/initrd/tests/test-cmdline-reader.c | 1 + + 2 files changed, 10 insertions(+), 3 deletions(-) + +diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c +index 17f9e1dfd2a9..8ba6dbb827b6 100644 +--- a/src/initrd/nmi-cmdline-reader.c ++++ b/src/initrd/nmi-cmdline-reader.c +@@ -584,7 +584,6 @@ reader_parse_master (Reader *reader, + { + NMConnection *connection; + NMSettingConnection *s_con; +- NMSettingBond *s_bond; + gs_free char *master_to_free = NULL; + const char *master; + char *slaves; +@@ -603,8 +602,15 @@ reader_parse_master (Reader *reader, + s_con = nm_connection_get_setting_connection (connection); + master = nm_setting_connection_get_uuid (s_con); + +- if (nm_streq (type_name, NM_SETTING_BOND_SETTING_NAME)) { +- s_bond = (NMSettingBond *)nm_connection_get_setting_by_name (connection, type_name); ++ if (nm_streq (type_name, NM_SETTING_BRIDGE_SETTING_NAME)) { ++ NMSettingBridge *s_bridge = nm_connection_get_setting_bridge (connection); ++ ++ /* Avoid the forwarding delay */ ++ g_object_set (s_bridge, ++ NM_SETTING_BRIDGE_STP, FALSE, ++ NULL); ++ } else if (nm_streq (type_name, NM_SETTING_BOND_SETTING_NAME)) { ++ NMSettingBond *s_bond = nm_connection_get_setting_bond (connection); + + opts = get_word (&argument, ':'); + while (opts && *opts) { +diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c +index 04594c48bee6..074654d347d8 100644 +--- a/src/initrd/tests/test-cmdline-reader.c ++++ b/src/initrd/tests/test-cmdline-reader.c +@@ -774,6 +774,7 @@ test_bridge (void) + + s_bridge = nm_connection_get_setting_bridge (connection); + g_assert (s_bridge); ++ g_assert_cmpint (nm_setting_bridge_get_stp (s_bridge), ==, FALSE); + + connection = g_hash_table_lookup (connections, "eth0"); + g_assert (connection); +-- +2.26.2 + + +From 43d1669601cc106e354bbbb1fc94240a0275c9e9 Mon Sep 17 00:00:00 2001 +From: Beniamino Galvani +Date: Wed, 5 Aug 2020 16:57:41 +0200 +Subject: [PATCH 2/7] initrd: wait for bootdev or all devices if rd.neednet=1 + +The network-legacy dracut module waits for all ethernet devices if the +command line contains rd.neednet=1. It also waits for the device +specified by 'bootdev='. + +Do the same. + +https://bugzilla.redhat.com/show_bug.cgi?id=1853348 +(cherry picked from commit f114e16fddf854d72a8b08318359569cd83245e3) +(cherry picked from commit 98ccd2a10e89d6d251a519eb63d085b2f327bad8) +--- + src/initrd/nm-initrd-generator.h | 2 + + src/initrd/nmi-cmdline-reader.c | 35 +++++++++++-- + src/initrd/tests/test-cmdline-reader.c | 70 ++++++++++++++++++++++++++ + 3 files changed, 104 insertions(+), 3 deletions(-) + +diff --git a/src/initrd/nm-initrd-generator.h b/src/initrd/nm-initrd-generator.h +index 9719992125ea..8e17f0455574 100644 +--- a/src/initrd/nm-initrd-generator.h ++++ b/src/initrd/nm-initrd-generator.h +@@ -9,6 +9,8 @@ + #include "nm-connection.h" + #include "nm-utils.h" + ++#define NMI_WAIT_DEVICE_TIMEOUT_MS 60000 ++ + static inline gboolean + guess_ip_address_family (const char *str) + { +diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c +index 8ba6dbb827b6..85be9f132eda 100644 +--- a/src/initrd/nmi-cmdline-reader.c ++++ b/src/initrd/nmi-cmdline-reader.c +@@ -873,6 +873,30 @@ reader_add_nameservers (Reader *reader, GPtrArray *nameservers) + } + } + ++static void ++connection_set_needed (NMConnection *connection) ++{ ++ NMSettingConnection *s_con; ++ ++ s_con = nm_connection_get_setting_connection (connection); ++ if (!nm_streq0 (nm_setting_connection_get_connection_type (s_con), ++ NM_SETTING_WIRED_SETTING_NAME)) ++ return; ++ ++ if (nm_str_is_empty (nm_setting_connection_get_interface_name (s_con))) ++ return; ++ ++ g_object_set (s_con, ++ NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, NMI_WAIT_DEVICE_TIMEOUT_MS, ++ NULL); ++} ++ ++static void ++connection_set_needed_cb (gpointer key, gpointer value, gpointer user_data) ++{ ++ connection_set_needed (value); ++} ++ + GHashTable * + nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv, char **hostname) + { +@@ -1007,11 +1031,16 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv, char ** + + connection = reader_get_connection (reader, bootdev, NULL, TRUE); + reader->bootdev_connection = connection; ++ connection_set_needed (connection); + } + +- if (neednet && g_hash_table_size (reader->hash) == 0) { +- /* Make sure there's some connection. */ +- reader_get_default_connection (reader); ++ if (neednet) { ++ if (g_hash_table_size (reader->hash) == 0) { ++ /* Make sure there's some connection. */ ++ reader_get_default_connection (reader); ++ } ++ ++ g_hash_table_foreach (reader->hash, connection_set_needed_cb, NULL); + } + + if (routes) +diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c +index 074654d347d8..7787cf5ea043 100644 +--- a/src/initrd/tests/test-cmdline-reader.c ++++ b/src/initrd/tests/test-cmdline-reader.c +@@ -49,6 +49,7 @@ test_auto (void) + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Wired Connection"); + g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_MULTIPLE); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1); + + g_assert (nm_setting_connection_get_autoconnect (s_con)); + +@@ -190,6 +191,7 @@ test_if_ip4_manual (void) + "ip=203.0.113.2::203.0.113.1:26:" + "hostname1.example.com:eth4"); + NMConnection *connection; ++ NMSettingConnection *s_con; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + NMIPAddress *ip_addr; +@@ -205,6 +207,10 @@ test_if_ip4_manual (void) + nmtst_assert_connection_verifies_without_normalization (connection); + g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth3"); + ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1); ++ + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); +@@ -294,6 +300,7 @@ test_multiple_merge (void) + const char *const*ARGV = NM_MAKE_STRV ("ip=192.0.2.2:::::eth0", + "ip=[2001:db8::2]:::::eth0"); + NMConnection *connection; ++ NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; +@@ -310,6 +317,10 @@ test_multiple_merge (void) + nmtst_assert_connection_verifies_without_normalization (connection); + g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0"); + ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1); ++ + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + +@@ -341,6 +352,7 @@ test_multiple_bootdev (void) + "ip=eth4:dhcp", + "bootdev=eth4"); + NMConnection *connection; ++ NMSettingConnection *s_con; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + gs_free char *hostname = NULL; +@@ -352,12 +364,18 @@ test_multiple_bootdev (void) + + connection = g_hash_table_lookup (connections, "eth3"); + g_assert (connection); ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + + connection = g_hash_table_lookup (connections, "eth4"); + g_assert (connection); ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS); + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); +@@ -388,6 +406,7 @@ test_bootdev (void) + g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "ens3"); + g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "ens3"); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout(s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS); + + connection = g_hash_table_lookup (connections, "vlan2"); + g_assert (connection); +@@ -1255,6 +1274,56 @@ test_bootif_ip (void) + g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6)); + } + ++static void ++test_neednet (void) ++{ ++ gs_unref_hashtable GHashTable *connections = NULL; ++ const char *const*ARGV = NM_MAKE_STRV ("rd.neednet", ++ "ip=eno1:dhcp", ++ "ip=172.25.1.100::172.25.1.1:24::eno2", ++ "bridge=br0:eno3"); ++ NMConnection *connection; ++ NMSettingConnection *s_con; ++ gs_free char *hostname = NULL; ++ ++ connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV, &hostname); ++ g_assert (connections); ++ g_assert_cmpint (g_hash_table_size (connections), ==, 4); ++ g_assert_cmpstr (hostname, ==, NULL); ++ ++ connection = g_hash_table_lookup (connections, "eno1"); ++ g_assert (connection); ++ nmtst_assert_connection_verifies_without_normalization (connection); ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eno1"); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS); ++ ++ connection = g_hash_table_lookup (connections, "eno2"); ++ g_assert (connection); ++ nmtst_assert_connection_verifies_without_normalization (connection); ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eno2"); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS); ++ ++ connection = g_hash_table_lookup (connections, "eno3"); ++ g_assert (connection); ++ nmtst_assert_connection_verifies_without_normalization (connection); ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eno3"); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS); ++ ++ connection = g_hash_table_lookup (connections, "br0"); ++ g_assert (connection); ++ nmtst_assert_connection_verifies_without_normalization (connection); ++ s_con = nm_connection_get_setting_connection (connection); ++ g_assert (s_con); ++ g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "br0"); ++ g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1); ++} ++ + static void + test_bootif_no_ip (void) + { +@@ -1451,6 +1520,7 @@ int main (int argc, char **argv) + g_test_add_func ("/initrd/cmdline/bootif/no_ip", test_bootif_no_ip); + g_test_add_func ("/initrd/cmdline/bootif/hwtype", test_bootif_hwtype); + g_test_add_func ("/initrd/cmdline/bootif/off", test_bootif_off); ++ g_test_add_func ("/initrd/cmdline/neednet", test_neednet); + + return g_test_run (); + } +-- +2.26.2 + + +From 38da5dabaccc377695e6cc4d3621cdf0f12b28e1 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 11 Aug 2020 15:32:46 +0200 +Subject: [PATCH 3/7] settings: let NMSettings reference NMManager + +NMSettings needs access to the list of all devices, which is tracked +by NMManager. Of course, this ties NMSettings and NMManager closer +together. Note that NMManager already owns a reference to NMSettings, +so they are in fact related. + +The alternatives of just letting NMSettings reference NMManager (and +vice versa) would be more complicated, and likely not help to simplify +the code (on the contrary). + +(cherry picked from commit d27a6055b9094163356ae2c650c566307df9a93c) +(cherry picked from commit 1745b4e0c0895df61cd4117b304dfa4e9a1d8c53) +--- + src/nm-manager.c | 2 +- + src/settings/nm-settings.c | 44 ++++++++++++++++++++++++++++++++++++-- + src/settings/nm-settings.h | 3 ++- + 3 files changed, 45 insertions(+), 4 deletions(-) + +diff --git a/src/nm-manager.c b/src/nm-manager.c +index d687fcd7f219..6095f2687153 100644 +--- a/src/nm-manager.c ++++ b/src/nm-manager.c +@@ -7455,7 +7455,7 @@ constructed (GObject *object) + + G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object); + +- priv->settings = nm_settings_new (); ++ priv->settings = nm_settings_new (self); + + nm_dbus_object_export (NM_DBUS_OBJECT (priv->settings)); + +diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c +index 0a1e7b47d6c4..85c5e5e77f85 100644 +--- a/src/settings/nm-settings.c ++++ b/src/settings/nm-settings.c +@@ -60,6 +60,7 @@ + #include "plugins/keyfile/nms-keyfile-storage.h" + #include "nm-agent-manager.h" + #include "nm-config.h" ++#include "nm-manager.h" + #include "nm-audit-manager.h" + #include "NetworkManagerUtils.h" + #include "nm-dispatcher.h" +@@ -324,6 +325,7 @@ _sett_conn_entry_find_shadowed_storage (SettConnEntry *sett_conn_entry, + /*****************************************************************************/ + + NM_GOBJECT_PROPERTIES_DEFINE (NMSettings, ++ PROP_MANAGER, + PROP_UNMANAGED_SPECS, + PROP_HOSTNAME, + PROP_CAN_MODIFY, +@@ -348,6 +350,8 @@ typedef struct { + + NMPlatform *platform; + ++ NMManager *manager; ++ + NMHostnameManager *hostname_manager; + + NMSessionMonitor *session_monitor; +@@ -3805,6 +3809,26 @@ get_property (GObject *object, guint prop_id, + } + } + ++static void ++set_property (GObject *object, guint prop_id, ++ const GValue *value, GParamSpec *pspec) ++{ ++ NMSettings *self = NM_SETTINGS (object); ++ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); ++ ++ switch (prop_id) { ++ case PROP_MANAGER: ++ /* construct-only */ ++ priv->manager = g_value_get_pointer (value); ++ nm_assert (NM_IS_MANAGER (priv->manager)); ++ g_object_add_weak_pointer (G_OBJECT (priv->manager), (gpointer *) &priv->manager); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ + /*****************************************************************************/ + + static void +@@ -3833,9 +3857,13 @@ nm_settings_init (NMSettings *self) + } + + NMSettings * +-nm_settings_new (void) ++nm_settings_new (NMManager *manager) + { +- return g_object_new (NM_TYPE_SETTINGS, NULL); ++ nm_assert (NM_IS_MANAGER (manager)); ++ ++ return g_object_new (NM_TYPE_SETTINGS, ++ NM_SETTINGS_MANAGER, manager, ++ NULL); + } + + static void +@@ -3915,6 +3943,11 @@ finalize (GObject *object) + g_clear_object (&priv->config); + + g_clear_object (&priv->platform); ++ ++ if (priv->manager) { ++ g_object_remove_weak_pointer (G_OBJECT (priv->manager), (gpointer *) &priv->manager); ++ priv->manager = NULL; ++ } + } + + static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( +@@ -4051,9 +4084,16 @@ nm_settings_class_init (NMSettingsClass *class) + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings); + + object_class->get_property = get_property; ++ object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + ++ obj_properties[PROP_MANAGER] = ++ g_param_spec_pointer (NM_SETTINGS_MANAGER, "", "", ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_WRITABLE | ++ G_PARAM_STATIC_STRINGS); ++ + obj_properties[PROP_UNMANAGED_SPECS] = + g_param_spec_boxed (NM_SETTINGS_UNMANAGED_SPECS, "", "", + G_TYPE_STRV, +diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h +index aa7e36e09395..b7baf4373280 100644 +--- a/src/settings/nm-settings.h ++++ b/src/settings/nm-settings.h +@@ -26,6 +26,7 @@ + #define NM_SETTINGS_CAN_MODIFY "can-modify" + #define NM_SETTINGS_CONNECTIONS "connections" + #define NM_SETTINGS_STARTUP_COMPLETE "startup-complete" ++#define NM_SETTINGS_MANAGER "manager" + + #define NM_SETTINGS_SIGNAL_CONNECTION_ADDED "connection-added" + #define NM_SETTINGS_SIGNAL_CONNECTION_UPDATED "connection-updated" +@@ -53,7 +54,7 @@ GType nm_settings_get_type (void); + NMSettings *nm_settings_get (void); + #define NM_SETTINGS_GET (nm_settings_get ()) + +-NMSettings *nm_settings_new (void); ++NMSettings *nm_settings_new (NMManager *manager); + + gboolean nm_settings_start (NMSettings *self, GError **error); + +-- +2.26.2 + + +From bc8ea71287204996830a4f02657d4d036e8c85ff Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 11 Aug 2020 15:34:59 +0200 +Subject: [PATCH 4/7] settings: rework wait-device-timeout handling and + consider device compatibility + +A profile can configure "connection.wait-device-timeout" to indicate +that startup complete is blocked until a suitable device around. +This is useful for NetworkManager-wait-online and initrd mode. + +Previously, we looked at NMPlatform whether a link with matching +interface-name was present. That is wrong because it cannot handle +profiles that rely on "ethernet.mac-address" setting or other "match" +settings. Also, the mere presence of the link does not yet mean +that the NMDevice was created and ready. In fact, there is a race here: +NMPlatform indicates that the device is ready (unblocking NMSettings), +but there is no corresponding NMDevice yet which keeps NetworkManager +busy to block startup complete. + +Rework this. Now, only check whether there is a compatible device for +the profile. + +Since we wait for compatible devices, it works now not only for the +interface name. Note that we do some optimizations so that we don't have +to re-evaluate all profiles (w.r.t. all devices) whenever something on the +device changes: we only care about this when all devices finally become +ready. + +Also, we no longer start the timeout for "connection.wait-device-timeout" +when the profile appears. Instead, there is one system-wide start time +(NMSettingsPrivate.startup_complete_start_timestamp_msec). That simplifies +code and makes sense: we start waiting when NetworkManager is starting, not +when the profile gets added. Also, we wait for all profiles to become +ready together. + +(cherry picked from commit 3df662f534c49c9df3e16808be125a562c97d954) +(cherry picked from commit 7ab8c5ba252e38b5cdeb958a7753df5fee1c55db) +--- + clients/common/settings-docs.h.in | 2 +- + libnm-core/nm-setting-connection.c | 23 +- + src/nm-manager.c | 32 ++- + src/settings/nm-settings.c | 336 +++++++++++++++-------------- + src/settings/nm-settings.h | 3 +- + 5 files changed, 209 insertions(+), 187 deletions(-) + +diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in +index 7b891500d8c9..606a1b95c5df 100644 +--- a/clients/common/settings-docs.h.in ++++ b/clients/common/settings-docs.h.in +@@ -171,7 +171,7 @@ + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).") + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).") + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").") +-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT N_("Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. Note that this property only works together with NMSettingConnection:interface-name to identify the device that will be waited for. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.") ++#define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT N_("Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.") + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_ZONE N_("The trust level of a the connection. Free form case-insensitive string (for example \"Home\", \"Work\", \"Public\"). NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately.") + #define DESCRIBE_DOC_NM_SETTING_DCB_APP_FCOE_FLAGS N_("Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4).") + #define DESCRIBE_DOC_NM_SETTING_DCB_APP_FCOE_MODE N_("The FCoE controller mode; either \"fabric\" (default) or \"vn2vn\".") +diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c +index 018129bef200..03f8fb23c099 100644 +--- a/libnm-core/nm-setting-connection.c ++++ b/libnm-core/nm-setting-connection.c +@@ -689,7 +689,7 @@ nm_setting_connection_is_slave_type (NMSettingConnection *setting, + * @setting: the #NMSettingConnection + * + * Returns: the %NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT property with +- * the timeout in milli seconds. -1 is the default. ++ * the timeout in milliseconds. -1 is the default. + * + * Since: 1.20 + */ +@@ -1218,20 +1218,6 @@ after_interface_name: + return FALSE; + } + +- if ( priv->wait_device_timeout != -1 +- && !priv->interface_name) { +- /* currently, only waiting by interface-name is implemented. Hence reject +- * configurations that are not implemented (yet). */ +- g_set_error (error, +- NM_CONNECTION_ERROR, +- NM_CONNECTION_ERROR_INVALID_PROPERTY, +- _("wait-device-timeout requires %s"), +- NM_SETTING_CONNECTION_INTERFACE_NAME); +- g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, +- NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT); +- return FALSE; +- } +- + if (priv->mud_url) { + if (!priv->mud_url[0]) { + g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, +@@ -2314,10 +2300,9 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass) + * Timeout in milliseconds to wait for device at startup. + * During boot, devices may take a while to be detected by the driver. + * This property will cause to delay NetworkManager-wait-online.service +- * and nm-online to give the device a chance to appear. +- * +- * Note that this property only works together with NMSettingConnection:interface-name +- * to identify the device that will be waited for. ++ * and nm-online to give the device a chance to appear. This works by ++ * waiting for the given timeout until a compatible device for the ++ * profile is available and managed. + * + * The value 0 means no wait time. The default value is -1, which + * currently has the same meaning as no wait time. +diff --git a/src/nm-manager.c b/src/nm-manager.c +index 6095f2687153..778e3b946ac9 100644 +--- a/src/nm-manager.c ++++ b/src/nm-manager.c +@@ -1561,13 +1561,6 @@ check_if_startup_complete (NMManager *self) + if (!priv->devices_inited) + return; + +- reason = nm_settings_get_startup_complete_blocked_reason (priv->settings); +- if (reason) { +- _LOGD (LOGD_CORE, "startup complete is waiting for connection (%s)", +- reason); +- return; +- } +- + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + reason = nm_device_has_pending_action_reason (device); + if (reason) { +@@ -1578,6 +1571,31 @@ check_if_startup_complete (NMManager *self) + } + } + ++ /* All NMDevice must be ready. But also NMSettings tracks profiles that wait for ++ * ready devices via "connection.wait-device-timeout". ++ * ++ * Note that we only re-check nm_settings_get_startup_complete_blocked_reason() when ++ * all of the devices become ready (again). ++ * ++ * For example, assume we have device "eth1" and "profile-eth2" which waits for "eth2". ++ * If "eth1" is ready (no pending action), we only need to re-evaluate "profile-eth2" ++ * if we have another device ("eth2"), that becomes non-ready (had pending actions) ++ * and again become ready. We don't need to check "profile-eth2" until "eth2" becomes ++ * non-ready. ++ * That is why nm_settings_get_startup_complete_blocked_reason() only has any significance ++ * if all devices are ready too. It allows us to cut down the number of checks whether ++ * NMSettings is ready. That's because we don't need to re-evaluate on minor changes of ++ * a device, only when all devices become managed and ready. */ ++ ++ g_signal_handlers_block_by_func (priv->settings, settings_startup_complete_changed, self); ++ reason = nm_settings_get_startup_complete_blocked_reason (priv->settings, TRUE); ++ g_signal_handlers_unblock_by_func (priv->settings, settings_startup_complete_changed, self); ++ if (reason) { ++ _LOGD (LOGD_CORE, "startup complete is waiting for connection (%s)", ++ reason); ++ return; ++ } ++ + _LOGI (LOGD_CORE, "startup complete"); + + priv->startup = FALSE; +diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c +index 85c5e5e77f85..ef86d2d2c836 100644 +--- a/src/settings/nm-settings.c ++++ b/src/settings/nm-settings.c +@@ -376,9 +376,9 @@ typedef struct { + GSList *unmanaged_specs; + GSList *unrecognized_specs; + ++ gint64 startup_complete_start_timestamp_msec; + GHashTable *startup_complete_idx; +- NMSettingsConnection *startup_complete_blocked_by; +- gulong startup_complete_platform_change_id; ++ CList startup_complete_scd_lst_head; + guint startup_complete_timeout_id; + + guint connections_len; +@@ -427,7 +427,7 @@ static void default_wired_clear_tag (NMSettings *self, + static void _clear_connections_cached_list (NMSettingsPrivate *priv); + + static void _startup_complete_check (NMSettings *self, +- gint64 now_us); ++ gint64 now_msec); + + /*****************************************************************************/ + +@@ -465,34 +465,51 @@ _emit_connection_flags_changed (NMSettings *self, + + typedef struct { + NMSettingsConnection *sett_conn; +- gint64 start_at; +- gint64 timeout; ++ CList scd_lst; ++ gint64 timeout_msec; + } StartupCompleteData; + + static void + _startup_complete_data_destroy (StartupCompleteData *scd) + { ++ c_list_unlink_stale (&scd->scd_lst); + g_object_unref (scd->sett_conn); +- g_slice_free (StartupCompleteData, scd); ++ nm_g_slice_free (scd); + } + + static gboolean +-_startup_complete_check_is_ready (NMPlatform *platform, ++_startup_complete_check_is_ready (NMSettings *self, + NMSettingsConnection *sett_conn) + { +- const NMPlatformLink *plink; +- const char *ifname; ++ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); ++ NMConnection *conn; ++ const CList *tmp_lst; ++ NMDevice *device; ++ ++ if (!priv->manager) ++ return TRUE; ++ ++ conn = nm_settings_connection_get_connection (sett_conn); + +- /* FIXME: instead of just looking for the interface name, it would be better +- * to wait for a device that is compatible with the profile. */ ++ nm_manager_for_each_device (priv->manager, device, tmp_lst) { + +- ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (sett_conn)); ++ if (!nm_device_is_real (device)) ++ continue; ++ ++ if ( nm_device_get_state (device) < NM_DEVICE_STATE_UNAVAILABLE ++ || nm_device_has_pending_action (device)) { ++ /* while a device is not yet available and still has a pending ++ * action itself, it's not a suitable candidate. */ ++ continue; ++ } ++ ++ if (!nm_device_check_connection_compatible (device, conn, NULL)) ++ continue; + +- if (!ifname) + return TRUE; ++ } + +- plink = nm_platform_link_get_by_ifname (platform, ifname); +- return plink && plink->initialized; ++ return FALSE; + } + + static gboolean +@@ -506,117 +523,98 @@ _startup_complete_timeout_cb (gpointer user_data) + return G_SOURCE_REMOVE; + } + +-static void +-_startup_complete_platform_change_cb (NMPlatform *platform, +- int obj_type_i, +- int ifindex, +- const NMPlatformLink *link, +- int change_type_i, +- NMSettings *self) +-{ +- const NMPlatformSignalChangeType change_type = change_type_i; +- NMSettingsPrivate *priv; +- const char *ifname; +- +- if (change_type == NM_PLATFORM_SIGNAL_REMOVED) +- return; +- +- if (!link->initialized) +- return; +- +- priv = NM_SETTINGS_GET_PRIVATE (self); +- +- ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)); +- if ( ifname +- && !nm_streq (ifname, link->name)) +- return; +- +- nm_assert (priv->startup_complete_timeout_id > 0); +- +- nm_clear_g_source (&priv->startup_complete_timeout_id); +- priv->startup_complete_timeout_id = g_idle_add (_startup_complete_timeout_cb, self); +-} +- + static void + _startup_complete_check (NMSettings *self, +- gint64 now_us) ++ gint64 now_msec) + { + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); +- gint64 next_expiry; ++ StartupCompleteData *scd_not_ready; ++ StartupCompleteData *scd_safe; + StartupCompleteData *scd; +- NMSettingsConnection *next_sett_conn = NULL; +- GHashTableIter iter; ++ gint64 elapsed_msec; ++ CList ready_lst; ++ ++ if (priv->startup_complete_start_timestamp_msec == 0) { ++ /* we are already done for good or didn't start yet. */ ++ return; ++ } + + if (!priv->started) { +- /* before we are started, we don't setup the timers... */ ++ /* before we are started there is no need to evaluate our list because ++ * we are anyway blocking startup-complete. */ + return; + } + +- if (!priv->startup_complete_idx) ++ if (c_list_is_empty (&priv->startup_complete_scd_lst_head)) + goto ready; + +- if (!now_us) +- now_us = nm_utils_get_monotonic_timestamp_usec (); ++ nm_utils_get_monotonic_timestamp_msec_cached (&now_msec); + +- next_expiry = 0; ++ elapsed_msec = now_msec - priv->startup_complete_start_timestamp_msec; + +- g_hash_table_iter_init (&iter, priv->startup_complete_idx); +- while (g_hash_table_iter_next (&iter, (gpointer *) &scd, NULL)) { +- gint64 expiry; +- +- if (scd->start_at == 0) { +- /* once ready, the decision is remembered and there is nothing +- * left to check. */ +- continue; +- } ++ /* We search the entire list whether they all timed-out or found a compatible device. ++ * We do that by appending elements that are ready to the end of the list, so that ++ * we hopefully keep testing the elements that are ready already (and can shortcut ++ * the test in common cases). ++ * ++ * Note that all profiles that we wait for need to have their dependencies satisfied ++ * at the same time. For example, consider connection A is waiting for device A' which is ready. ++ * Connection B waits for device B', which isn't ready. Once B'/B becomes ready, A/A' must ++ * still be ready. Otherwise, we would wait for A/A' to become ready again. */ ++ scd_not_ready = NULL; ++ c_list_init (&ready_lst); ++ c_list_for_each_entry_safe (scd, scd_safe, &priv->startup_complete_scd_lst_head, scd_lst) { + +- expiry = scd->start_at + scd->timeout; +- if (expiry <= now_us) { +- scd->start_at = 0; +- continue; +- } ++ if (scd->timeout_msec <= elapsed_msec) ++ goto next_with_ready; + +- if (_startup_complete_check_is_ready (priv->platform, scd->sett_conn)) { +- scd->start_at = 0; +- continue; +- } ++ if (_startup_complete_check_is_ready (self, scd->sett_conn)) ++ goto next_with_ready; + +- next_expiry = expiry; +- next_sett_conn = scd->sett_conn; +- /* we found one timeout for which to wait. that's good enough. */ ++ scd_not_ready = scd; + break; ++ ++next_with_ready: ++ /* this element is ready. We move it to a temporary list, so that we ++ * can reorder the list (to next time evaluate the non-ready element first). */ ++ nm_c_list_move_tail (&ready_lst, &scd->scd_lst); + } ++ c_list_splice (&priv->startup_complete_scd_lst_head, &ready_lst); + + nm_clear_g_source (&priv->startup_complete_timeout_id); +- nm_g_object_ref_set (&priv->startup_complete_blocked_by, next_sett_conn); +- if (next_expiry > 0) { +- nm_assert (priv->startup_complete_blocked_by); +- if (priv->startup_complete_platform_change_id == 0) { +- priv->startup_complete_platform_change_id = g_signal_connect (priv->platform, +- NM_PLATFORM_SIGNAL_LINK_CHANGED, +- G_CALLBACK (_startup_complete_platform_change_cb), +- self); +- } +- priv->startup_complete_timeout_id = g_timeout_add (NM_MIN (3600u*1000u, (next_expiry - now_us) / 1000u), ++ ++ if (scd_not_ready) { ++ gint64 timeout_msec; ++ ++ timeout_msec = priv->startup_complete_start_timestamp_msec + scd_not_ready->timeout_msec - nm_utils_get_monotonic_timestamp_msec (); ++ priv->startup_complete_timeout_id = g_timeout_add (NM_CLAMP (0, timeout_msec, 60000), + _startup_complete_timeout_cb, + self); +- _LOGT ("startup-complete: wait for device \"%s\" due to connection %s (%s)", +- nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)), +- nm_settings_connection_get_uuid (priv->startup_complete_blocked_by), +- nm_settings_connection_get_id (priv->startup_complete_blocked_by)); ++ _LOGT ("startup-complete: wait for suitable device for connection \"%s\" (%s) which has \"connection.wait-device-timeout\" set", ++ nm_settings_connection_get_id (scd_not_ready->sett_conn), ++ nm_settings_connection_get_uuid (scd_not_ready->sett_conn)); + return; + } + +- nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy); +- nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id); ++ if (_LOGW_ENABLED ()) { ++ c_list_for_each_entry (scd, &priv->startup_complete_scd_lst_head, scd_lst) { ++ if (!_startup_complete_check_is_ready (self, scd->sett_conn)) { ++ _LOGW ("startup-complete: profile \"%s\" (%s) was waiting for non-existing device (with timeout \"connection.wait-device-timeout=%"G_GINT64_FORMAT"\")", ++ nm_settings_connection_get_id (scd->sett_conn), ++ nm_settings_connection_get_uuid (scd->sett_conn), ++ scd->timeout_msec); ++ } ++ } ++ } + + ready: +- _LOGT ("startup-complete: ready, no profiles to wait for"); ++ nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy); ++ nm_assert (c_list_is_empty (&priv->startup_complete_scd_lst_head)); + nm_assert (priv->started); +- nm_assert (!priv->startup_complete_blocked_by); ++ _LOGT ("startup-complete: ready, no more profiles to wait for"); ++ priv->startup_complete_start_timestamp_msec = 0; + nm_assert (!priv->startup_complete_idx); + nm_assert (priv->startup_complete_timeout_id == 0); +- nm_assert (priv->startup_complete_platform_change_id == 0); + _notify (self, PROP_STARTUP_COMPLETE); + } + +@@ -626,75 +624,95 @@ _startup_complete_notify_connection (NMSettings *self, + gboolean forget) + { + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); +- gint64 timeout; +- gint64 now_us = 0; +- +- nm_assert ( !priv->started +- || priv->startup_complete_idx); +- +- timeout = 0; +- if (!forget) { +- NMSettingConnection *s_con; +- gint32 v; +- +- s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn)); +- v = nm_setting_connection_get_wait_device_timeout (s_con); +- if (v > 0) { +- nm_assert (nm_setting_connection_get_interface_name (s_con)); +- timeout = ((gint64) v) * 1000; +- } ++ StartupCompleteData *scd; ++ gint64 timeout_msec; ++ gint64 now_msec = 0; ++ NMSettingConnection *s_con; ++ gint32 v; ++ ++ nm_assert (priv->startup_complete_start_timestamp_msec != 0); ++ ++ if (forget) { ++ if (!priv->startup_complete_idx) ++ return; ++ if (!g_hash_table_remove (priv->startup_complete_idx, &sett_conn)) ++ return; ++ goto check; + } + +- if (timeout == 0) { +- if ( !priv->startup_complete_idx +- || !g_hash_table_remove (priv->startup_complete_idx, &sett_conn)) ++ s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn)); ++ v = nm_setting_connection_get_wait_device_timeout (s_con); ++ if (v > 0) ++ timeout_msec = v; ++ else ++ timeout_msec = 0; ++ ++ if (!priv->startup_complete_idx) { ++ nm_assert (!priv->started); ++ ++ if (timeout_msec == 0) ++ return; ++ ++ priv->startup_complete_idx = g_hash_table_new_full (nm_pdirect_hash, ++ nm_pdirect_equal, ++ NULL, ++ (GDestroyNotify) _startup_complete_data_destroy); ++ scd = NULL; ++ } else ++ scd = g_hash_table_lookup (priv->startup_complete_idx, &sett_conn); ++ ++ if (!scd) { ++ if (timeout_msec == 0) + return; ++ scd = g_slice_new (StartupCompleteData); ++ *scd = (StartupCompleteData) { ++ .sett_conn = g_object_ref (sett_conn), ++ .timeout_msec = timeout_msec, ++ }; ++ g_hash_table_add (priv->startup_complete_idx, scd); ++ c_list_link_tail (&priv->startup_complete_scd_lst_head, &scd->scd_lst); + } else { +- StartupCompleteData *scd; +- +- if (!priv->startup_complete_idx) { +- nm_assert (!priv->started); +- priv->startup_complete_idx = g_hash_table_new_full (nm_pdirect_hash, +- nm_pdirect_equal, +- NULL, +- (GDestroyNotify) _startup_complete_data_destroy); +- scd = NULL; +- } else +- scd = g_hash_table_lookup (priv->startup_complete_idx, &sett_conn); +- if (!scd) { +- now_us = nm_utils_get_monotonic_timestamp_usec (); +- scd = g_slice_new (StartupCompleteData); +- *scd = (StartupCompleteData) { +- .sett_conn = g_object_ref (sett_conn), +- .start_at = now_us, +- .timeout = timeout, +- }; +- g_hash_table_add (priv->startup_complete_idx, scd); +- } else { +- if (scd->start_at == 0) { +- /* the entry already is ready and no longer relevant. Ignore it. */ +- return; +- } +- scd->timeout = timeout; +- } ++ scd->timeout_msec = timeout_msec; ++ nm_c_list_move_front (&priv->startup_complete_scd_lst_head, &scd->scd_lst); + } + +- _startup_complete_check (self, now_us); ++check: ++ _startup_complete_check (self, now_msec); + } + + const char * +-nm_settings_get_startup_complete_blocked_reason (NMSettings *self) ++nm_settings_get_startup_complete_blocked_reason (NMSettings *self, ++ gboolean force_reload) + { + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); +- const char *uuid = NULL; ++ StartupCompleteData *scd; ++ const char *uuid; + +- if (priv->started) { +- if (!priv->startup_complete_idx) +- return NULL; +- if (priv->startup_complete_blocked_by) +- uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by); +- } +- return uuid ?: "unknown"; ++ if (priv->startup_complete_start_timestamp_msec == 0) ++ goto out_done; ++ ++ if (force_reload) ++ _startup_complete_check (self, 0); ++ ++ if (c_list_is_empty (&priv->startup_complete_scd_lst_head)) ++ goto out_done; ++ ++ scd = c_list_first_entry (&priv->startup_complete_scd_lst_head, StartupCompleteData, scd_lst); ++ ++ nm_assert (scd); ++ nm_assert (NM_IS_SETTINGS_CONNECTION (scd->sett_conn)); ++ nm_assert (scd == nm_g_hash_table_lookup (priv->startup_complete_idx, &scd->sett_conn)); ++ ++ uuid = nm_settings_connection_get_uuid (scd->sett_conn); ++ if (uuid) ++ return uuid; ++ ++ g_return_val_if_reached ("settings-starting"); ++ ++out_done: ++ if (!priv->started) ++ return "settings-starting"; ++ return NULL; + } + + /*****************************************************************************/ +@@ -1142,8 +1160,7 @@ _connection_changed_update (NMSettings *self, + _emit_connection_updated (self, sett_conn, update_reason); + } + +- if ( !priv->started +- || priv->startup_complete_idx) { ++ if (priv->startup_complete_start_timestamp_msec != 0) { + if (nm_settings_has_connection (self, sett_conn)) + _startup_complete_notify_connection (self, sett_conn, FALSE); + } +@@ -1217,8 +1234,7 @@ _connection_changed_delete (NMSettings *self, + nm_key_file_db_remove_key (priv->kf_db_timestamps, uuid); + nm_key_file_db_remove_key (priv->kf_db_seen_bssids, uuid); + +- if ( !priv->started +- || priv->startup_complete_idx) ++ if (priv->startup_complete_start_timestamp_msec != 0) + _startup_complete_notify_connection (self, sett_conn, TRUE); + } + +@@ -3715,6 +3731,8 @@ nm_settings_start (NMSettings *self, GError **error) + + nm_assert (!priv->started); + ++ priv->startup_complete_start_timestamp_msec = nm_utils_get_monotonic_timestamp_msec (); ++ + priv->hostname_manager = g_object_ref (nm_hostname_manager_get ()); + + priv->kf_db_timestamps = nm_key_file_db_new (NMSTATEDIR "/timestamps", +@@ -3801,7 +3819,7 @@ get_property (GObject *object, guint prop_id, + g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv)); + break; + case PROP_STARTUP_COMPLETE: +- g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self)); ++ g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self, FALSE)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +@@ -3838,6 +3856,7 @@ nm_settings_init (NMSettings *self) + + c_list_init (&priv->auth_lst_head); + c_list_init (&priv->connections_lst_head); ++ c_list_init (&priv->startup_complete_scd_lst_head); + + c_list_init (&priv->sce_dirty_lst_head); + priv->sce_idx = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, +@@ -3877,9 +3896,8 @@ dispose (GObject *object) + nm_assert (g_hash_table_size (priv->sce_idx) == 0); + + nm_clear_g_source (&priv->startup_complete_timeout_id); +- nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id); + nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy); +- g_clear_object (&priv->startup_complete_blocked_by); ++ nm_assert (c_list_is_empty (&priv->startup_complete_scd_lst_head)); + + while ((iter = c_list_first (&priv->auth_lst_head))) + nm_auth_chain_destroy (nm_auth_chain_parent_lst_entry (iter)); +diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h +index b7baf4373280..35c62ff71dd2 100644 +--- a/src/settings/nm-settings.h ++++ b/src/settings/nm-settings.h +@@ -123,7 +123,8 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device); + + void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting); + +-const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self); ++const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self, ++ gboolean force_reload); + + void nm_settings_kf_db_write (NMSettings *settings); + +-- +2.26.2 + + +From 8ffcba687043cded8cd229d0ea2546654ef25e04 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Thu, 13 Aug 2020 10:09:09 +0200 +Subject: [PATCH 5/7] settings: suppress wrong warning about + wait-device-timeout for devices that are still busy + +Imagine we wait for a device, the device appears and starts activating. +That might take a while (during which it has a pending action). In the +meantime, the "connection.wait-device-timeout" timeout expires. + +Now we want to log a warning about profiles that don't have their +device upon timeout. However, that the device is still busy at that +point is irrelevant. Skip logging a message about those profiles. + +Fixes: 3df662f534c4 ('settings: rework wait-device-timeout handling and consider device compatibility') +(cherry picked from commit d9568ca3ee810897633bbc05f4f01e416add8182) +(cherry picked from commit 4e6fcb4a71f708dc06117584799015644abca81f) +--- + src/settings/nm-settings.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c +index ef86d2d2c836..4573d5f78ac3 100644 +--- a/src/settings/nm-settings.c ++++ b/src/settings/nm-settings.c +@@ -479,7 +479,8 @@ _startup_complete_data_destroy (StartupCompleteData *scd) + + static gboolean + _startup_complete_check_is_ready (NMSettings *self, +- NMSettingsConnection *sett_conn) ++ NMSettingsConnection *sett_conn, ++ gboolean ignore_pending_actions) + { + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + NMConnection *conn; +@@ -497,7 +498,8 @@ _startup_complete_check_is_ready (NMSettings *self, + continue; + + if ( nm_device_get_state (device) < NM_DEVICE_STATE_UNAVAILABLE +- || nm_device_has_pending_action (device)) { ++ || ( !ignore_pending_actions ++ && nm_device_has_pending_action (device))) { + /* while a device is not yet available and still has a pending + * action itself, it's not a suitable candidate. */ + continue; +@@ -568,7 +570,7 @@ _startup_complete_check (NMSettings *self, + if (scd->timeout_msec <= elapsed_msec) + goto next_with_ready; + +- if (_startup_complete_check_is_ready (self, scd->sett_conn)) ++ if (_startup_complete_check_is_ready (self, scd->sett_conn, FALSE)) + goto next_with_ready; + + scd_not_ready = scd; +@@ -598,7 +600,7 @@ next_with_ready: + + if (_LOGW_ENABLED ()) { + c_list_for_each_entry (scd, &priv->startup_complete_scd_lst_head, scd_lst) { +- if (!_startup_complete_check_is_ready (self, scd->sett_conn)) { ++ if (!_startup_complete_check_is_ready (self, scd->sett_conn, TRUE)) { + _LOGW ("startup-complete: profile \"%s\" (%s) was waiting for non-existing device (with timeout \"connection.wait-device-timeout=%"G_GINT64_FORMAT"\")", + nm_settings_connection_get_id (scd->sett_conn), + nm_settings_connection_get_uuid (scd->sett_conn), +-- +2.26.2 + + +From 3f1067a30b23e9eec12d1e39233604c6962c963d Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Wed, 12 Aug 2020 17:35:25 +0200 +Subject: [PATCH 6/7] initrd: always set "connection.wait-device-timeout" even + if profile has no interface-name set + +Since commit 3df662f534c4 ('settings: rework wait-device-timeout +handling and consider device compatibility'), "connection.wait-device-timeout" +works with profiles in general and doesn't require an interface-name +set. + +Remove that restriction and let initrd generator create profiles that +always wait. + +(cherry picked from commit 52af5e901e4e5e7727ae83db18a37730b5f898fe) +(cherry picked from commit f0d8d6f15778ffa861312c0c5a22f6bffe07c359) +--- + src/initrd/nmi-cmdline-reader.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c +index 85be9f132eda..be39ef896fb4 100644 +--- a/src/initrd/nmi-cmdline-reader.c ++++ b/src/initrd/nmi-cmdline-reader.c +@@ -883,11 +883,8 @@ connection_set_needed (NMConnection *connection) + NM_SETTING_WIRED_SETTING_NAME)) + return; + +- if (nm_str_is_empty (nm_setting_connection_get_interface_name (s_con))) +- return; +- + g_object_set (s_con, +- NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, NMI_WAIT_DEVICE_TIMEOUT_MS, ++ NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, (int) NMI_WAIT_DEVICE_TIMEOUT_MS, + NULL); + } + +-- +2.26.2 + + +From 98fca91c687084a80231d5f797f3257cc826a7fd Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Wed, 12 Aug 2020 16:57:02 +0200 +Subject: [PATCH 7/7] rhel8: revert changes to translated strings in libnm/docs + +--- + clients/common/settings-docs.h.in | 2 +- + libnm-core/nm-setting-connection.c | 9 +++++---- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in +index 606a1b95c5df..7b891500d8c9 100644 +--- a/clients/common/settings-docs.h.in ++++ b/clients/common/settings-docs.h.in +@@ -171,7 +171,7 @@ + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).") + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).") + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").") +-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT N_("Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.") ++#define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT N_("Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. Note that this property only works together with NMSettingConnection:interface-name to identify the device that will be waited for. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.") + #define DESCRIBE_DOC_NM_SETTING_CONNECTION_ZONE N_("The trust level of a the connection. Free form case-insensitive string (for example \"Home\", \"Work\", \"Public\"). NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately.") + #define DESCRIBE_DOC_NM_SETTING_DCB_APP_FCOE_FLAGS N_("Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4).") + #define DESCRIBE_DOC_NM_SETTING_DCB_APP_FCOE_MODE N_("The FCoE controller mode; either \"fabric\" (default) or \"vn2vn\".") +diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c +index 03f8fb23c099..fd2309e76cc6 100644 +--- a/libnm-core/nm-setting-connection.c ++++ b/libnm-core/nm-setting-connection.c +@@ -689,7 +689,7 @@ nm_setting_connection_is_slave_type (NMSettingConnection *setting, + * @setting: the #NMSettingConnection + * + * Returns: the %NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT property with +- * the timeout in milliseconds. -1 is the default. ++ * the timeout in milli seconds. -1 is the default. + * + * Since: 1.20 + */ +@@ -2300,9 +2300,10 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass) + * Timeout in milliseconds to wait for device at startup. + * During boot, devices may take a while to be detected by the driver. + * This property will cause to delay NetworkManager-wait-online.service +- * and nm-online to give the device a chance to appear. This works by +- * waiting for the given timeout until a compatible device for the +- * profile is available and managed. ++ * and nm-online to give the device a chance to appear. ++ * ++ * Note that this property only works together with NMSettingConnection:interface-name ++ * to identify the device that will be waited for. + * + * The value 0 means no wait time. The default value is -1, which + * currently has the same meaning as no wait time. +-- +2.26.2 + diff --git a/SOURCES/1015-local-route-fixes-rh1868982.patch b/SOURCES/1015-local-route-fixes-rh1868982.patch new file mode 100644 index 0000000..903cd98 --- /dev/null +++ b/SOURCES/1015-local-route-fixes-rh1868982.patch @@ -0,0 +1,80 @@ +From 09c7c911d702fbe906c5c97435551416d27a2bf9 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 21 Jul 2020 13:51:17 +0200 +Subject: [PATCH 1/2] core: only expose "type unicast" routes on D-Bus + +Currently, we would not mark non-unicast routes with their type, so they +would wrongly appear as unicast routes in the D-Bus API. + +That is wrong. For now, just hide them. + +Fixes: 5d0d13f57010 ('platform: add support for local routes') +(cherry picked from commit 5035687a7be4c67e32b4e08319f2e66c76b42212) +(cherry picked from commit cf3cff4f12e78615c18fd829b1f32eac4f607c0b) +--- + src/nm-ip4-config.c | 3 +++ + src/nm-ip6-config.c | 3 +++ + 2 files changed, 6 insertions(+) + +diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c +index f017a3e809f4..9ad811642317 100644 +--- a/src/nm-ip4-config.c ++++ b/src/nm-ip4-config.c +@@ -3227,6 +3227,9 @@ out_addresses_cached: + + nm_assert (_route_valid (route)); + ++ if (route->type_coerced != nm_platform_route_type_coerce (RTN_UNICAST)) ++ continue; ++ + g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&route_builder, "{sv}", + "dest", +diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c +index 1589cad004af..deb30e77d757 100644 +--- a/src/nm-ip6-config.c ++++ b/src/nm-ip6-config.c +@@ -2682,6 +2682,9 @@ out_addresses_cached: + + nm_assert (_route_valid (route)); + ++ if (route->type_coerced != nm_platform_route_type_coerce (RTN_UNICAST)) ++ continue; ++ + g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&route_builder, "{sv}", + "dest", +-- +2.26.2 + + +From 52fe9dec97eb7d57594141732694453ce2db08d5 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 21 Jul 2020 18:06:21 +0200 +Subject: [PATCH 2/2] core: fix selecting of best-default-route to consider + only unicast routes + +Fixes: 5d0d13f57010 ('platform: add support for local routes') +(cherry picked from commit 2a1bac6b8a7d107f33bbdb9d91eaf924fa67b64a) +(cherry picked from commit 99b362c9443591b2b643aa09980f1a5dd42b1f6b) +--- + src/nm-ip4-config.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h +index dc8222fd5a7b..ea0617497607 100644 +--- a/src/nm-ip4-config.h ++++ b/src/nm-ip4-config.h +@@ -83,7 +83,8 @@ nm_ip_config_best_default_route_is (const NMPObject *obj) + * Note that this only considers the main routing table. */ + return r + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) +- && nm_platform_route_table_is_main (r->table_coerced); ++ && nm_platform_route_table_is_main (r->table_coerced) ++ && r->type_coerced == nm_platform_route_type_coerce (1 /*RTN_UNICAST*/); + } + + const NMPObject *_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp); +-- +2.26.2 + diff --git a/SPECS/NetworkManager.spec b/SPECS/NetworkManager.spec index c58e91d..047f931 100644 --- a/SPECS/NetworkManager.spec +++ b/SPECS/NetworkManager.spec @@ -7,7 +7,7 @@ %global epoch_version 1 %global rpm_version 1.26.0 %global real_version 1.26.0 -%global release_version 4 +%global release_version 6 %global snapshot %{nil} %global git_sha %{nil} @@ -163,6 +163,8 @@ Patch1010: 1010-shared-extend-NM_IN_STRSET-and-NM_IN_SET-rh1847814.patch Patch1011: 1011-bond-fix-can_reapply_change-rh1847814.patch Patch1012: 1012-bond-let-reapply-reapply-rh1847814.patch Patch1013: 1013-dhcp6-hostname-rh1858344.patch +Patch1014: 1014-settings-fix-wait-device-timeout-rh1853348.patch +Patch1015: 1015-local-route-fixes-rh1868982.patch # The pregenerated docs contain default values and paths that depend # on the configure options when creating the source tarball. @@ -1110,6 +1112,12 @@ fi %changelog +* Mon Aug 17 2020 Thomas Haller - 1:1.26.0-6 +- core: fix handling of local routes as default route and on D-Bus (rh #1868982) + +* Thu Aug 13 2020 Thomas Haller - 1:1.26.0-5 +- core: fix wait-device-timeout race and support general device matches (rh #1853348) + * Tue Aug 11 2020 Antonio Cardace - 1:1.26.0-4 - bond: fix Reapply does not update bond options (rh #1847814) - dhcp: support DHCPv6 fqdn_fqdn option for hostname (rh #1858344)