From 388b7830f322b60960884328ff51f7b4df0ef3d3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 13 Apr 2015 16:29:37 -0500 Subject: [PATCH] platform: don't wait for udev before announcing links --- src/devices/nm-device-ethernet.c | 12 +++ src/devices/nm-device.c | 43 +++++++++- src/devices/nm-device.h | 3 + src/nm-manager.c | 5 +- src/platform/nm-fake-platform.c | 1 + src/platform/nm-linux-platform.c | 132 ++++++++++--------------------- src/platform/nm-platform.c | 11 +++ src/platform/nm-platform.h | 3 + 8 files changed, 114 insertions(+), 96 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 05bde3584..bce1cef7f 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1615,6 +1615,17 @@ carrier_changed (NMDevice *device, gboolean carrier) NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->carrier_changed (device, carrier); } +static void +link_changed (NMDevice *device, NMPlatformLink *info) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->link_changed (device, info); + if (!priv->subchan1 && info->udi) + _update_s390_subchannels (self); +} + static void dispose (GObject *object) { @@ -1714,6 +1725,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) parent_class->spec_match_list = spec_match_list; parent_class->update_connection = update_connection; parent_class->carrier_changed = carrier_changed; + parent_class->link_changed = link_changed; parent_class->state_changed = device_state_changed; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 9dfa0904a..7c264c7c8 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -176,6 +176,7 @@ typedef struct { typedef struct { gboolean in_state_changed; gboolean initialized; + gboolean platform_link_initialized; NMDeviceState state; NMDeviceStateReason state_reason; @@ -1051,6 +1052,7 @@ void nm_device_finish_init (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean platform_unmanaged = FALSE; g_assert (priv->initialized == FALSE); @@ -1063,6 +1065,20 @@ nm_device_finish_init (NMDevice *self) if (priv->master) nm_device_enslave_slave (priv->master, self, NULL); + if (priv->ifindex > 0) { + if (priv->platform_link_initialized || (priv->is_nm_owned && priv->is_software)) { + nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged); + nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, platform_unmanaged); + } else { + /* Hardware and externally-created software links stay unmanaged + * until they are fully initialized by the platform. NM created + * links must be available for activation immediately and thus + * do not get the PLATFORM_INIT unmanaged flag set. + */ + nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_PLATFORM_INIT, TRUE); + } + } + priv->initialized = TRUE; } @@ -1255,6 +1271,7 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMUtilsIPv6IfaceId token_iid; gboolean ip_ifname_changed = FALSE; + gboolean platform_unmanaged = FALSE; if (info->udi && g_strcmp0 (info->udi, priv->udi)) { /* Update UDI to what udev gives us */ @@ -1263,6 +1280,13 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); } + if (g_strcmp0 (info->driver, priv->driver)) { + /* Update driver to what udev gives us */ + g_free (priv->driver); + priv->driver = g_strdup (info->driver); + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER); + } + /* Update MTU if it has changed. */ if (priv->mtu != info->mtu) { priv->mtu = info->mtu; @@ -1355,6 +1379,22 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) } } } + + if (priv->ifindex > 0 && !priv->platform_link_initialized && info->initialized) { + priv->platform_link_initialized = TRUE; + + if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged)) { + nm_device_set_unmanaged (self, + NM_UNMANAGED_DEFAULT, + platform_unmanaged, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + } + + nm_device_set_unmanaged (self, + NM_UNMANAGED_PLATFORM_INIT, + FALSE, + NM_DEVICE_STATE_REASON_NOW_MANAGED); + } } static void @@ -7088,7 +7128,7 @@ nm_device_set_unmanaged (NMDevice *self, if (unmanaged) nm_device_state_changed (self, NM_DEVICE_STATE_UNMANAGED, reason); - else + else if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED) nm_device_state_changed (self, NM_DEVICE_STATE_UNAVAILABLE, reason); } } @@ -8716,6 +8756,7 @@ set_property (GObject *object, guint prop_id, priv->up = platform_device->up; g_free (priv->driver); priv->driver = g_strdup (platform_device->driver); + priv->platform_link_initialized = platform_device->initialized; } break; case PROP_UDI: diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index ad6de877f..f7ba2a529 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -344,6 +344,8 @@ RfKillType nm_device_get_rfkill_type (NMDevice *device); * @NM_UNMANAGED_USER: %TRUE when unmanaged by user decision (via unmanaged-specs) * @NM_UNMANAGED_PARENT: %TRUE when unmanaged due to parent device being unmanaged * @NM_UNMANAGED_EXTERNAL_DOWN: %TRUE when unmanaged because !IFF_UP and not created by NM + * @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not + * yet initialized */ typedef enum { NM_UNMANAGED_NONE = 0x00, @@ -352,6 +354,7 @@ typedef enum { NM_UNMANAGED_USER = 0x04, NM_UNMANAGED_PARENT = 0x08, NM_UNMANAGED_EXTERNAL_DOWN = 0x10, + NM_UNMANAGED_PLATFORM_INIT = 0x20, /* Boundary value */ __NM_UNMANAGED_LAST, diff --git a/src/nm-manager.c b/src/nm-manager.c index a9dd34546..25f463ad3 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1791,7 +1791,7 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); const char *iface, *driver, *type_desc; const GSList *unmanaged_specs; - gboolean user_unmanaged, sleeping, platform_unmanaged; + gboolean user_unmanaged, sleeping; gboolean enabled = FALSE; RfKillType rtype; GSList *iter, *remove = NULL; @@ -1871,9 +1871,6 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) user_unmanaged = nm_device_spec_match_list (device, unmanaged_specs); nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_USER, user_unmanaged); - if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, nm_device_get_ifindex (device), &platform_unmanaged)) - nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_DEFAULT, platform_unmanaged); - sleeping = manager_sleeping (self); nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping); diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index c9785b003..b49e02d50 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -116,6 +116,7 @@ link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) device->link.type_name = type_to_type_name (type); device->link.driver = type_to_type_name (type); device->link.udi = device->udi = g_strdup_printf ("fake:%d", ifindex); + device->link.initialized = TRUE; if (name) strcpy (device->link.name, name); switch (device->link.type) { diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 8671635f0..6cd75ecbc 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -887,19 +887,6 @@ type_to_string (NMLinkType type) } } -static gboolean -link_is_announceable (NMPlatform *platform, struct rtnl_link *rtnllink) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - /* Hardware devices must be found by udev so rules get run and tags set */ - if (g_hash_table_lookup (priv->udev_devices, - GINT_TO_POINTER (rtnl_link_get_ifindex (rtnllink)))) - return TRUE; - - return FALSE; -} - #define DEVTYPE_PREFIX "DEVTYPE=" static char * @@ -1058,15 +1045,17 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); if (udev_device) { info->driver = udev_get_driver (udev_device, info->ifindex); - if (!info->driver) - info->driver = g_intern_string (rtnl_link_get_type (rtnllink)); - if (!info->driver) - info->driver = ethtool_get_driver (info->name); - if (!info->driver) - info->driver = "unknown"; info->udi = g_udev_device_get_sysfs_path (udev_device); + info->initialized = TRUE; } + if (!info->driver) + info->driver = g_intern_string (rtnl_link_get_type (rtnllink)); + if (!info->driver) + info->driver = ethtool_get_driver (info->name); + if (!info->driver) + info->driver = "unknown"; + return TRUE; } @@ -1624,22 +1613,6 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor if (!init_link (platform, &device, rtnl_link)) return; - /* Skip devices not yet discovered by udev. They will be - * announced by udev_device_added(). This doesn't apply to removed - * devices, as those come either from udev_device_removed(), - * event_notification() or link_delete() which block the announcment - * themselves when appropriate. - */ - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - case NM_PLATFORM_SIGNAL_CHANGED: - if (!device.driver) - return; - break; - default: - break; - } - /* Link deletion or setting down is sometimes accompanied by address * and/or route deletion. * @@ -2067,15 +2040,12 @@ event_notification (struct nl_msg *msg, gpointer user_data) return NL_OK; nl_cache_remove (cached_object); - /* Don't announce removed interfaces that are not recognized by - * udev. They were either not yet discovered or they have been - * already removed and announced. - */ - if (event == RTM_DELLINK) { - if (!link_is_announceable (platform, (struct rtnl_link *) cached_object)) - return NL_OK; - } announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); + if (event == RTM_DELLINK) { + int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) cached_object); + + g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex)); + } return NL_OK; case RTM_NEWLINK: @@ -2303,12 +2273,8 @@ link_get_all (NMPlatform *platform) struct nl_object *object; for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) { - struct rtnl_link *rtnl_link = (struct rtnl_link *) object; - - if (link_is_announceable (platform, rtnl_link)) { - if (init_link (platform, &device, rtnl_link)) - g_array_append_val (links, device); - } + if (init_link (platform, &device, (struct rtnl_link *) object)) + g_array_append_val (links, device); } return links; @@ -2321,13 +2287,7 @@ _nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *l) auto_nl_object struct rtnl_link *rtnllink = NULL; rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) { - if (link_is_announceable (platform, rtnllink)) { - if (init_link (platform, l, rtnllink)) - return TRUE; - } - } - return FALSE; + return (rtnllink && init_link (platform, l, rtnllink)); } static struct nl_object * @@ -2386,13 +2346,6 @@ link_get (NMPlatform *platform, int ifindex) return NULL; } - /* physical interfaces must be found by udev before they can be used */ - if (!link_is_announceable (platform, rtnllink)) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - rtnl_link_put (rtnllink); - return NULL; - } - return rtnllink; } @@ -4432,7 +4385,6 @@ udev_device_added (NMPlatform *platform, auto_nl_object struct rtnl_link *rtnllink = NULL; const char *ifname; int ifindex; - gboolean was_announceable = FALSE; ifname = g_udev_device_get_name (udev_device); if (!ifname) { @@ -4457,15 +4409,15 @@ udev_device_added (NMPlatform *platform, } rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) - was_announceable = link_is_announceable (platform, rtnllink); + if (!rtnllink) { + warning ("(%s): udev-add: interface not known via netlink; ignoring...", ifname); + return; + } g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex), g_object_ref (udev_device)); - /* Announce devices only if they also have been discovered via Netlink. */ - if (rtnllink && link_is_announceable (platform, rtnllink)) - announce_object (platform, (struct nl_object *) rtnllink, was_announceable ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL); + announce_object (platform, (struct nl_object *) rtnllink, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL); } static void @@ -4473,9 +4425,7 @@ udev_device_removed (NMPlatform *platform, GUdevDevice *udev_device) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; int ifindex = 0; - gboolean was_announceable = FALSE; if (g_udev_device_get_property (udev_device, "IFINDEX")) ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); @@ -4500,15 +4450,7 @@ udev_device_removed (NMPlatform *platform, if (ifindex <= 0) return; - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) - was_announceable = link_is_announceable (platform, rtnllink); - g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex)); - - /* Announce device removal if it is no longer announceable. */ - if (was_announceable && !link_is_announceable (platform, rtnllink)) - announce_object (platform, (struct nl_object *) rtnllink, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); } static void @@ -4553,8 +4495,6 @@ constructed (GObject *_object) NMPlatform *platform = NM_PLATFORM (_object); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const char *udev_subsys[] = { "net", NULL }; - GUdevEnumerator *enumerator; - GList *devices, *iter; int channel_flags; gboolean status; int nle; @@ -4614,6 +4554,24 @@ constructed (GObject *_object) g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform); priv->udev_devices = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); + /* request all IPv6 addresses (hopeing that there is at least one), to check for + * the IFA_FLAGS attribute. */ + nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP); + if (nle < 0) + nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle)); + + priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); + + G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); +} + +static void +setup_devices (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GUdevEnumerator *enumerator; + GList *devices, *iter; + /* And read initial device list */ enumerator = g_udev_enumerator_new (priv->udev_client); g_udev_enumerator_add_match_subsystem (enumerator, "net"); @@ -4631,16 +4589,6 @@ constructed (GObject *_object) } g_list_free (devices); g_object_unref (enumerator); - - /* request all IPv6 addresses (hopeing that there is at least one), to check for - * the IFA_FLAGS attribute. */ - nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP); - if (nle < 0) - nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle)); - - priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); - - G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); } static void @@ -4678,6 +4626,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) object_class->constructed = constructed; object_class->finalize = nm_linux_platform_finalize; + platform_class->setup_devices = setup_devices; + platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 518b69dd0..37a1a6f04 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -405,6 +405,10 @@ nm_platform_query_devices (NMPlatform *self) NM_PLATFORM_REASON_INTERNAL); } g_array_unref (links_array); + + /* Platform specific device setup. */ + if (klass->setup_devices) + klass->setup_devices (self); } /** @@ -2608,6 +2612,12 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return (((a)->field) < ((b)->field)) ? -1 : 1; \ } G_STMT_END +#define _CMP_FIELD_BOOL(a, b, field) \ + G_STMT_START { \ + if ((!((a)->field)) != (!((b)->field))) \ + return ((!((a)->field)) < (!((b)->field))) ? -1 : 1; \ + } G_STMT_END + #define _CMP_FIELD_STR(a, b, field) \ G_STMT_START { \ int c = strcmp ((a)->field, (b)->field); \ @@ -2643,6 +2653,7 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, arp); _CMP_FIELD (a, b, mtu); _CMP_FIELD_STR0 (a, b, type_name); + _CMP_FIELD_BOOL (a, b, initialized); _CMP_FIELD_STR0 (a, b, udi); _CMP_FIELD_STR0 (a, b, driver); return 0; diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 67a9639cd..fac0e6d50 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -88,6 +88,7 @@ struct _NMPlatformLink { const char *type_name; const char *udi; const char *driver; + gboolean initialized; int master; int parent; gboolean up; @@ -358,6 +359,8 @@ struct _NMPlatform { typedef struct { GObjectClass parent; + void (*setup_devices) (NMPlatform *); + gboolean (*sysctl_set) (NMPlatform *, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *path);