summary refs log tree commit diff
path: root/src/devices/ovs/nm-device-ovs-interface.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-02-19 23:31:24 +0100
committerMichael Biebl <biebl@debian.org>2020-02-19 23:31:24 +0100
commite536d40eaea5dcdc0743b0a5e8e17faa46608a50 (patch)
tree0749832d8655215dac973866b4e724f3aea35448 /src/devices/ovs/nm-device-ovs-interface.c
parentf3c6d0765dff885e168b94f28e06ecc640315a74 (diff)
New upstream version 1.22.8 upstream/1.22.8
Diffstat (limited to 'src/devices/ovs/nm-device-ovs-interface.c')
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.c168
1 files changed, 164 insertions, 4 deletions
diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c
index 726e9901..2868dee0 100644
--- a/src/devices/ovs/nm-device-ovs-interface.c
+++ b/src/devices/ovs/nm-device-ovs-interface.c
@@ -98,10 +98,12 @@ link_changed (NMDevice *device,
 {
 	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device);
 
-	if (   pllink
-	    && priv->waiting_for_interface
-	    && nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
-		priv->waiting_for_interface = FALSE;
+	if (!pllink || !priv->waiting_for_interface)
+		return;
+
+	priv->waiting_for_interface = FALSE;
+
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
 		nm_device_bring_up (device, TRUE, NULL);
 		nm_device_activate_schedule_stage3_ip_config_start (device);
 	}
@@ -125,12 +127,14 @@ act_stage3_ip_config_start (NMDevice *device,
                             gpointer *out_config,
                             NMDeviceStateReason *out_failure_reason)
 {
+	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
 	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device);
 
 	if (!_is_internal_interface (device))
 		return NM_ACT_STAGE_RETURN_IP_FAIL;
 
 	if (nm_device_get_ip_ifindex (device) <= 0) {
+		_LOGT (LOGD_DEVICE, "waiting for link to appear");
 		priv->waiting_for_interface = TRUE;
 		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
@@ -153,6 +157,160 @@ deactivate (NMDevice *device)
 	priv->waiting_for_interface = FALSE;
 }
 
+typedef struct {
+	NMDeviceOvsInterface *self;
+	GCancellable *cancellable;
+	NMDeviceDeactivateCallback callback;
+	gpointer callback_user_data;
+	gulong link_changed_id;
+	gulong cancelled_id;
+	guint link_timeout_id;
+} DeactivateData;
+
+static void
+deactivate_invoke_cb (DeactivateData *data, GError *error)
+{
+	NMDeviceOvsInterface *self = data->self;
+
+	_LOGT (LOGD_CORE,
+	       "deactivate: async callback (%s)",
+	       error ? error->message : "success");
+	data->callback (NM_DEVICE (data->self),
+	                error,
+	                data->callback_user_data);
+
+	nm_clear_g_signal_handler (nm_device_get_platform (NM_DEVICE (data->self)),
+	                           &data->link_changed_id);
+	nm_clear_g_signal_handler (data->cancellable,
+	                           &data->cancelled_id);
+	nm_clear_g_source (&data->link_timeout_id);
+	g_object_unref (data->self);
+	g_object_unref (data->cancellable);
+	nm_g_slice_free (data);
+}
+
+static void
+deactivate_link_changed_cb (NMPlatform *platform,
+                            int obj_type_i,
+                            int ifindex,
+                            NMPlatformLink *info,
+                            int change_type_i,
+                            DeactivateData *data)
+{
+	NMDeviceOvsInterface *self = data->self;
+	const NMPlatformSignalChangeType change_type = change_type_i;
+
+	if (   change_type == NM_PLATFORM_SIGNAL_REMOVED
+	    && nm_streq0 (info->name, nm_device_get_iface (NM_DEVICE (self)))) {
+		_LOGT (LOGD_DEVICE, "deactivate: link removed, proceeding");
+		nm_device_update_from_platform_link (NM_DEVICE (self), NULL);
+		deactivate_invoke_cb (data, NULL);
+		return;
+	}
+}
+
+static gboolean
+deactivate_link_timeout (gpointer user_data)
+{
+	DeactivateData *data = user_data;
+	NMDeviceOvsInterface *self = data->self;
+
+	_LOGT (LOGD_DEVICE, "deactivate: timeout waiting link removal");
+	deactivate_invoke_cb (data, NULL);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+deactivate_cancelled_cb (GCancellable *cancellable,
+                         gpointer user_data)
+{
+	gs_free_error GError *error = NULL;
+
+	nm_utils_error_set_cancelled (&error, FALSE, NULL);
+	deactivate_invoke_cb ((DeactivateData *) user_data, error);
+}
+
+static void
+deactivate_cb_on_idle (gpointer user_data,
+                       GCancellable *cancellable)
+{
+	DeactivateData *data = user_data;
+	gs_free_error GError *cancelled_error = NULL;
+
+	g_cancellable_set_error_if_cancelled (data->cancellable, &cancelled_error);
+	deactivate_invoke_cb (data, cancelled_error);
+}
+
+static void
+deactivate_async (NMDevice *device,
+                  GCancellable *cancellable,
+                  NMDeviceDeactivateCallback callback,
+                  gpointer callback_user_data) {
+
+	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
+	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (self);
+	DeactivateData *data;
+
+	_LOGT (LOGD_CORE, "deactivate: start async");
+
+	/* We want to ensure that the kernel link for this device is
+	 * removed upon disconnection so that it will not interfere with
+	 * later activations of the same device. Unfortunately there is
+	 * no synchronization mechanism with vswitchd, we only update
+	 * ovsdb and wait that changes are picked up.
+	 */
+
+	data = g_slice_new (DeactivateData);
+	*data = (DeactivateData) {
+		.self = g_object_ref (self),
+		.cancellable = g_object_ref (cancellable),
+		.callback = callback,
+		.callback_user_data = callback_user_data,
+	};
+
+	if (   !priv->waiting_for_interface
+	    && !nm_platform_link_get_by_ifname (nm_device_get_platform (device),
+	                                        nm_device_get_iface (device))) {
+		_LOGT (LOGD_CORE, "deactivate: link not present, proceeding");
+		nm_device_update_from_platform_link (NM_DEVICE (self), NULL);
+		nm_utils_invoke_on_idle (deactivate_cb_on_idle, data, cancellable);
+		return;
+	}
+
+	if (priv->waiting_for_interface) {
+		/* At this point we have issued an INSERT and a DELETE
+		 * command for the interface to ovsdb. We don't know if
+		 * vswitchd will see the two updates or only one. We
+		 * must add a timeout to avoid waiting forever in case
+		 * the link doesn't appear.
+		 */
+		data->link_timeout_id = g_timeout_add (6000, deactivate_link_timeout, data);
+		_LOGT (LOGD_DEVICE, "deactivate: waiting for link to disappear in 6 seconds");
+	} else
+		_LOGT (LOGD_DEVICE, "deactivate: waiting for link to disappear");
+
+	data->cancelled_id = g_cancellable_connect (cancellable,
+	                                            G_CALLBACK (deactivate_cancelled_cb),
+	                                            data,
+	                                            NULL);
+	data->link_changed_id = g_signal_connect (nm_device_get_platform (device),
+	                                          NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	                                          G_CALLBACK (deactivate_link_changed_cb),
+	                                          data);
+}
+
+static gboolean
+can_update_from_platform_link (NMDevice *device, const NMPlatformLink *plink)
+{
+	/* If the device is deactivating, we already sent the
+	 * deletion command to ovsdb and we don't want to deal
+	 * with any new link appearing from the previous
+	 * activation.
+	 */
+	return    !plink
+	       || nm_device_get_state (device) != NM_DEVICE_STATE_DEACTIVATING;
+}
+
 /*****************************************************************************/
 
 static void
@@ -182,7 +340,9 @@ nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
 	device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH);
 
+	device_class->can_update_from_platform_link = can_update_from_platform_link;
 	device_class->deactivate = deactivate;
+	device_class->deactivate_async = deactivate_async;
 	device_class->get_type_description = get_type_description;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;