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) {
case NM_ETHERNET_TYPE_BOND:
/* 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;
default:

View File

@@ -153,13 +153,18 @@ typedef enum {
IP_DONE
} IpState;
typedef struct {
NMDeviceState state;
NMDeviceStateReason reason;
guint id;
} QueuedState;
typedef struct {
gboolean disposed;
gboolean initialized;
NMDeviceState state;
guint failed_to_disconnected_id;
guint unavailable_to_disconnected_id;
QueuedState queued_state;
char * udi;
char * path;
@@ -2852,22 +2857,16 @@ clear_act_request (NMDevice *self)
}
static void
delayed_transitions_clear (NMDevice *self)
queued_state_clear (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->failed_to_disconnected_id) {
nm_log_dbg (LOGD_DEVICE, "(%s): clearing failed->disconnected transition",
nm_device_get_iface (self));
g_source_remove (priv->failed_to_disconnected_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;
if (priv->queued_state.id) {
nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)",
nm_device_get_iface (self), priv->queued_state.id);
g_source_remove (priv->queued_state.id);
}
memset (&priv->queued_state, 0, sizeof (priv->queued_state));
}
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_INET6);
/* Clear any delayed transitions */
delayed_transitions_clear (self);
/* Clear any queued transitions */
queued_state_clear (self);
priv->ip4_state = priv->ip6_state = IP_NONE;
@@ -3493,8 +3492,8 @@ dispose (GObject *object)
}
}
/* Clear any delayed transitions */
delayed_transitions_clear (self);
/* Clear any queued transitions */
queued_state_clear (self);
/* Clean up and stop DHCP */
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);
}
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
nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
{
@@ -4128,8 +4101,8 @@ nm_device_state_changed (NMDevice *device,
state,
reason);
/* Clear any delayed transitions */
delayed_transitions_clear (device);
/* Clear any queued transitions */
queued_state_clear (device);
/* Cache the activation request for the dispatcher */
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)) {
nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED",
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 {
nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED",
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
* 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;
default:
break;
@@ -4213,6 +4186,52 @@ nm_device_state_changed (NMDevice *device,
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
nm_device_get_state (NMDevice *device)
{

View File

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