core: add generic queued state transitions

There are now three places we need delayed state transitions:

1) unavailable to disconnected
2) failed to disconnected
3) bond unavailable to disconnected

(3) wasn't doing a delayed transition, but we can't change
state from inside a state-change handler otherwise we may not
end up fully processing the current state chagne.  So it needs a
delayed transition too; add some generic code to make that
easier to do.
This commit is contained in:
Dan Williams
2011-12-08 11:46:58 -06:00
parent cd0058baa7
commit fcc441622a
3 changed files with 72 additions and 49 deletions

View File

@@ -342,7 +342,7 @@ device_state_changed (NMDevice *device,
switch (priv->type) { switch (priv->type) {
case NM_ETHERNET_TYPE_BOND: case NM_ETHERNET_TYPE_BOND:
/* Use NM_DEVICE_STATE_REASON_CARRIER to make sure num retries is reset */ /* Use NM_DEVICE_STATE_REASON_CARRIER to make sure num retries is reset */
nm_device_state_changed (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER); nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER);
break; break;
default: default:

View File

@@ -153,13 +153,18 @@ typedef enum {
IP_DONE IP_DONE
} IpState; } IpState;
typedef struct {
NMDeviceState state;
NMDeviceStateReason reason;
guint id;
} QueuedState;
typedef struct { typedef struct {
gboolean disposed; gboolean disposed;
gboolean initialized; gboolean initialized;
NMDeviceState state; NMDeviceState state;
guint failed_to_disconnected_id; QueuedState queued_state;
guint unavailable_to_disconnected_id;
char * udi; char * udi;
char * path; char * path;
@@ -2852,22 +2857,16 @@ clear_act_request (NMDevice *self)
} }
static void static void
delayed_transitions_clear (NMDevice *self) queued_state_clear (NMDevice *self)
{ {
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->failed_to_disconnected_id) { if (priv->queued_state.id) {
nm_log_dbg (LOGD_DEVICE, "(%s): clearing failed->disconnected transition", nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)",
nm_device_get_iface (self)); nm_device_get_iface (self), priv->queued_state.id);
g_source_remove (priv->failed_to_disconnected_id); g_source_remove (priv->queued_state.id);
priv->failed_to_disconnected_id = 0;
}
if (priv->unavailable_to_disconnected_id) {
nm_log_dbg (LOGD_DEVICE, "(%s): clearing unavailable->disconnected transition",
nm_device_get_iface (self));
g_source_remove (priv->unavailable_to_disconnected_id);
priv->unavailable_to_disconnected_id = 0;
} }
memset (&priv->queued_state, 0, sizeof (priv->queued_state));
} }
static void static void
@@ -3018,8 +3017,8 @@ nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
activation_source_clear (self, TRUE, AF_INET); activation_source_clear (self, TRUE, AF_INET);
activation_source_clear (self, TRUE, AF_INET6); activation_source_clear (self, TRUE, AF_INET6);
/* Clear any delayed transitions */ /* Clear any queued transitions */
delayed_transitions_clear (self); queued_state_clear (self);
priv->ip4_state = priv->ip6_state = IP_NONE; priv->ip4_state = priv->ip6_state = IP_NONE;
@@ -3493,8 +3492,8 @@ dispose (GObject *object)
} }
} }
/* Clear any delayed transitions */ /* Clear any queued transitions */
delayed_transitions_clear (self); queued_state_clear (self);
/* Clean up and stop DHCP */ /* Clean up and stop DHCP */
dhcp4_cleanup (self, take_down, FALSE); dhcp4_cleanup (self, take_down, FALSE);
@@ -3909,32 +3908,6 @@ nm_device_class_init (NMDeviceClass *klass)
dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR); dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR);
} }
static gboolean
failed_to_disconnected (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
nm_log_dbg (LOGD_DEVICE, "(%s): running failed->disconnected transition",
nm_device_get_iface (self));
priv->failed_to_disconnected_id = 0;
nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
return FALSE;
}
static gboolean
unavailable_to_disconnected (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
nm_log_dbg (LOGD_DEVICE, "(%s): running unavailable->disconnected transition",
nm_device_get_iface (self));
priv->unavailable_to_disconnected_id = 0;
nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
return FALSE;
}
void void
nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing) nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
{ {
@@ -4128,8 +4101,8 @@ nm_device_state_changed (NMDevice *device,
state, state,
reason); reason);
/* Clear any delayed transitions */ /* Clear any queued transitions */
delayed_transitions_clear (device); queued_state_clear (device);
/* Cache the activation request for the dispatcher */ /* Cache the activation request for the dispatcher */
req = priv->act_request ? g_object_ref (priv->act_request) : NULL; req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
@@ -4182,7 +4155,7 @@ nm_device_state_changed (NMDevice *device,
if (nm_device_is_available (device)) { if (nm_device_is_available (device)) {
nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED", nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED",
nm_device_get_iface (device)); nm_device_get_iface (device));
priv->unavailable_to_disconnected_id = g_idle_add (unavailable_to_disconnected, device); nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
} else { } else {
nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED", nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED",
nm_device_get_iface (device)); nm_device_get_iface (device));
@@ -4199,7 +4172,7 @@ nm_device_state_changed (NMDevice *device,
* immediately because we can't change states again from the state * immediately because we can't change states again from the state
* handler for a variety of reasons. * handler for a variety of reasons.
*/ */
priv->failed_to_disconnected_id = g_idle_add (failed_to_disconnected, device); nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
break; break;
default: default:
break; break;
@@ -4213,6 +4186,52 @@ nm_device_state_changed (NMDevice *device,
g_object_unref (req); g_object_unref (req);
} }
static gboolean
queued_set_state (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->queued_state.id) {
priv->queued_state.id = 0;
nm_log_dbg (LOGD_DEVICE, "(%s): running queued state change to %s (id %d)",
nm_device_get_iface (self),
state_to_string (priv->queued_state.state),
priv->queued_state.id);
nm_device_state_changed (self, priv->queued_state.state, priv->queued_state.reason);
}
queued_state_clear (self);
return FALSE;
}
void
nm_device_queue_state (NMDevice *self,
NMDeviceState state,
NMDeviceStateReason reason)
{
NMDevicePrivate *priv;
g_return_if_fail (self != NULL);
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
/* We should only ever have one delayed state transition at a time */
if (priv->queued_state.id) {
g_warn_if_fail (priv->queued_state.id == 0);
queued_state_clear (self);
}
priv->queued_state.state = state;
priv->queued_state.reason = reason;
priv->queued_state.id = g_idle_add (queued_set_state, self);
nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s (id %d)",
nm_device_get_iface (self), state_to_string (state),
priv->queued_state.id);
}
NMDeviceState NMDeviceState
nm_device_get_state (NMDevice *device) nm_device_get_state (NMDevice *device)
{ {

View File

@@ -228,6 +228,10 @@ void nm_device_state_changed (NMDevice *device,
NMDeviceState state, NMDeviceState state,
NMDeviceStateReason reason); NMDeviceStateReason reason);
void nm_device_queue_state (NMDevice *self,
NMDeviceState state,
NMDeviceStateReason reason);
gboolean nm_device_get_firmware_missing (NMDevice *self); gboolean nm_device_get_firmware_missing (NMDevice *self);
gboolean nm_device_activate (NMDevice *device, NMActRequest *req, GError **error); gboolean nm_device_activate (NMDevice *device, NMActRequest *req, GError **error);