mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-04 07:08:17 +02:00
wired: ensure carrier changes clear queued state where appropriate
Buggy kernel drivers sometimes default the carrier to ON when they are capable of link-detection but the carrier is actually off, and they quickly switch the carrier OFF when they determine actual carrier state (cdc-ether, for example). The initial carrier ON event would trigger a queued state change from UNAVAILABLE to DISCONNECTED, which may auto-activate the device. But before that state change happens, if a carrier OFF event comes in, that queued state was not getting cleared and the device would transition to DISCONNECTED even though the carrier was off. To ensure that never happens, and that we don't enter states that aren't valid when the carrier is off, we need to clear any queued state changes that wouldn't be valid in the new carrier state.
This commit is contained in:
parent
46e0af2942
commit
2318b3c525
3 changed files with 42 additions and 18 deletions
|
|
@ -71,4 +71,8 @@ NMConnectionProvider *nm_device_get_connection_provider (NMDevice *device);
|
|||
|
||||
void nm_device_recheck_available_connections (NMDevice *device);
|
||||
|
||||
void nm_device_queued_state_clear (NMDevice *device);
|
||||
|
||||
NMDeviceState nm_device_queued_state_peek (NMDevice *device);
|
||||
|
||||
#endif /* NM_DEVICE_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -147,6 +147,13 @@ carrier_action_defer_cb (gpointer user_data)
|
|||
if (state == NM_DEVICE_STATE_UNAVAILABLE) {
|
||||
if (priv->carrier)
|
||||
nm_device_queue_state (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER);
|
||||
else {
|
||||
/* clear any queued state changes if they wouldn't be valid when the
|
||||
* carrier is off.
|
||||
*/
|
||||
if (nm_device_queued_state_peek (NM_DEVICE (self)) >= NM_DEVICE_STATE_DISCONNECTED)
|
||||
nm_device_queued_state_clear (NM_DEVICE (self));
|
||||
}
|
||||
} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
|
||||
if (!priv->carrier)
|
||||
nm_device_queue_state (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
|
||||
|
|
|
|||
|
|
@ -3061,19 +3061,6 @@ clear_act_request (NMDevice *self)
|
|||
priv->act_request = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
queued_state_clear (NMDevice *self)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
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
|
||||
dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
|
||||
{
|
||||
|
|
@ -3242,7 +3229,7 @@ nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
|
|||
}
|
||||
|
||||
/* Clear any queued transitions */
|
||||
queued_state_clear (self);
|
||||
nm_device_queued_state_clear (self);
|
||||
|
||||
priv->ip4_state = priv->ip6_state = IP_NONE;
|
||||
|
||||
|
|
@ -3810,7 +3797,7 @@ dispose (GObject *object)
|
|||
}
|
||||
|
||||
/* Clear any queued transitions */
|
||||
queued_state_clear (self);
|
||||
nm_device_queued_state_clear (self);
|
||||
|
||||
/* Clean up and stop DHCP */
|
||||
dhcp4_cleanup (self, take_down, FALSE);
|
||||
|
|
@ -4563,7 +4550,7 @@ nm_device_state_changed (NMDevice *device,
|
|||
reason);
|
||||
|
||||
/* Clear any queued transitions */
|
||||
queued_state_clear (device);
|
||||
nm_device_queued_state_clear (device);
|
||||
|
||||
/* Cache the activation request for the dispatcher */
|
||||
req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
|
||||
|
|
@ -4683,7 +4670,7 @@ queued_set_state (gpointer user_data)
|
|||
priv->queued_state.id = 0;
|
||||
nm_device_state_changed (self, priv->queued_state.state, priv->queued_state.reason);
|
||||
}
|
||||
queued_state_clear (self);
|
||||
nm_device_queued_state_clear (self);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -4702,7 +4689,7 @@ nm_device_queue_state (NMDevice *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);
|
||||
nm_device_queued_state_clear (self);
|
||||
}
|
||||
|
||||
priv->queued_state.state = state;
|
||||
|
|
@ -4714,6 +4701,32 @@ nm_device_queue_state (NMDevice *self,
|
|||
priv->queued_state.id);
|
||||
}
|
||||
|
||||
NMDeviceState
|
||||
nm_device_queued_state_peek (NMDevice *self)
|
||||
{
|
||||
NMDevicePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (self != NULL, NM_DEVICE_STATE_UNKNOWN);
|
||||
g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_STATE_UNKNOWN);
|
||||
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
return priv->queued_state.id ? priv->queued_state.state : NM_DEVICE_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
void
|
||||
nm_device_queued_state_clear (NMDevice *self)
|
||||
{
|
||||
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
NMDeviceState
|
||||
nm_device_get_state (NMDevice *device)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue