merge: really implement DEACTIVATING state and fix re-activation crash (rh #1058843)

Really implement the DEACTIVATING state this time.  This now allows easy
"pre-down" hooks whenever we choose to implement that.

Next, fix a crash during re-activation where a pending activation request
mis-interpreted device state changes from a previous activation request
that was deactivating.
This commit is contained in:
Dan Williams 2014-02-25 18:05:55 -06:00
commit 53720c3837
5 changed files with 52 additions and 49 deletions

View file

@ -229,6 +229,7 @@ typedef struct {
guint32 ip4_address;
NMActRequest * queued_act_request;
NMActRequest * act_request;
guint act_source_id;
gpointer act_source_func;
@ -4436,11 +4437,7 @@ nm_device_activate_ip6_state_in_wait (NMDevice *self)
static void
clear_act_request (NMDevice *self)
{
NMDevicePrivate * priv;
g_return_if_fail (self != NULL);
priv = NM_DEVICE_GET_PRIVATE (self);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (!priv->act_request)
return;
@ -4452,8 +4449,7 @@ clear_act_request (NMDevice *self)
priv->master_ready_id = 0;
}
g_object_unref (priv->act_request);
priv->act_request = NULL;
g_clear_object (&priv->act_request);
}
static void
@ -4812,8 +4808,8 @@ impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context)
NULL);
}
void
nm_device_activate (NMDevice *self, NMActRequest *req)
static void
_device_activate (NMDevice *self, NMActRequest *req)
{
NMDevicePrivate *priv;
NMConnection *connection;
@ -4840,16 +4836,32 @@ nm_device_activate (NMDevice *self, NMActRequest *req)
priv->act_request = g_object_ref (req);
g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION);
/* HACK: update the state a bit early to avoid a race between the
* scheduled stage1 handler and nm_policy_device_change_check() thinking
* that the activation request isn't deferred because the deferred bit
* gets cleared a bit too early, when the connection becomes valid.
*/
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
nm_device_activate_schedule_stage1_device_prepare (self);
}
void
nm_device_queue_activation (NMDevice *self, NMActRequest *req)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (!priv->act_request) {
/* Just activate immediately */
_device_activate (self, req);
return;
}
/* supercede any already-queued request */
g_clear_object (&priv->queued_act_request);
priv->queued_act_request = g_object_ref (req);
/* Deactivate existing activation request first */
nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.",
nm_device_get_iface (self));
nm_device_state_changed (self,
NM_DEVICE_STATE_DEACTIVATING,
NM_DEVICE_STATE_REASON_NONE);
}
/*
* nm_device_is_activating
*
@ -5554,6 +5566,7 @@ dispose (GObject *object)
activation_source_clear (self, TRUE, AF_INET6);
clear_act_request (self);
g_clear_object (&priv->queued_act_request);
platform = nm_platform_get ();
g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self);
@ -6422,8 +6435,10 @@ nm_device_state_changed (NMDevice *device,
/* Cache the activation request for the dispatcher */
req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
if (state <= NM_DEVICE_STATE_UNAVAILABLE)
if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
_clear_available_connections (device, TRUE);
g_clear_object (&priv->queued_act_request);
}
/* Update the available connections list when a device first becomes available */
if ( state >= NM_DEVICE_STATE_DISCONNECTED
@ -6508,7 +6523,14 @@ nm_device_state_changed (NMDevice *device,
nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, reason);
break;
case NM_DEVICE_STATE_DISCONNECTED:
if (old_state > NM_DEVICE_STATE_DISCONNECTED && priv->default_unmanaged)
if (priv->queued_act_request) {
NMActRequest *queued_req;
queued_req = priv->queued_act_request;
priv->queued_act_request = NULL;
_device_activate (device, queued_req);
g_object_unref (queued_req);
} else if (old_state > NM_DEVICE_STATE_DISCONNECTED && priv->default_unmanaged)
nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
break;
case NM_DEVICE_STATE_ACTIVATED:

View file

@ -314,7 +314,7 @@ void nm_device_queue_ip_config_change (NMDevice *self);
gboolean nm_device_get_firmware_missing (NMDevice *self);
void nm_device_activate (NMDevice *device, NMActRequest *req);
void nm_device_queue_activation (NMDevice *device, NMActRequest *req);
void nm_device_set_connection_provider (NMDevice *device, NMConnectionProvider *provider);

View file

@ -307,6 +307,10 @@ device_state_changed (NMActiveConnection *active,
{
NMActiveConnectionState ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
/* Ignore state changes when this activation request is not yet active */
if (NM_ACTIVE_CONNECTION (nm_device_get_act_request (device)) != active)
return;
/* Set NMActiveConnection state based on the device's state */
switch (new_state) {
case NM_DEVICE_STATE_PREPARE:

View file

@ -325,28 +325,12 @@ device_state_changed (NMDevice *device,
NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data);
NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
/* When already deactivated or before activation, device state changes are useless */
if (priv->state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
return;
if (old_state < NM_DEVICE_STATE_DISCONNECTED)
return;
if (old_state > NM_DEVICE_STATE_DISCONNECTED) {
/* Ignore disconnects if this ActiveConnection has not yet started
* activating. This is caused by activating a device when it's
* already activated, which causes a deactivating of the device before
* activating the new connection.
*/
if (new_state == NM_DEVICE_STATE_DISCONNECTED &&
old_state > NM_DEVICE_STATE_DISCONNECTED &&
priv->state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
return;
}
/* If the device used to be active, but now is disconnected/failed, we
* no longer care about its state.
*/
if (new_state <= NM_DEVICE_STATE_DISCONNECTED || new_state == NM_DEVICE_STATE_FAILED)
g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_state_changed), self);
}
/* Let subclasses handle the state change */
if (NM_ACTIVE_CONNECTION_GET_CLASS (self)->device_state_changed)
NM_ACTIVE_CONNECTION_GET_CLASS (self)->device_state_changed (self, device, new_state, old_state);
@ -362,6 +346,8 @@ device_master_changed (GObject *object,
NMActiveConnection *master;
NMActiveConnectionState master_state;
if (NM_ACTIVE_CONNECTION (nm_device_get_act_request (device)) != self)
return;
if (!nm_device_get_master (device))
return;
g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_master_changed), self);

View file

@ -1955,7 +1955,7 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
nm_active_connection_set_assumed (active, TRUE);
nm_active_connection_export (active);
active_connection_add (self, active);
nm_device_activate (device, NM_ACT_REQUEST (active));
nm_device_queue_activation (device, NM_ACT_REQUEST (active));
} else {
nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s",
nm_connection_get_path (connection),
@ -2872,17 +2872,8 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
nm_active_connection_get_path (master_ac));
}
/* Tear down any existing connection */
if (nm_device_get_act_request (device)) {
nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.",
nm_device_get_iface (device));
nm_device_state_changed (device,
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_NONE);
}
/* Start the new activation */
nm_device_activate (device, NM_ACT_REQUEST (active));
nm_device_queue_activation (device, NM_ACT_REQUEST (active));
return TRUE;
}