diff options
Diffstat (limited to 'src/devices/ovs')
| -rw-r--r-- | src/devices/ovs/nm-device-ovs-interface.c | 168 |
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; |