Commit fe307dbd authored by Dan Winship's avatar Dan Winship

core: move carrier-detect NMDeviceState handling into NMDevice

Move some duplicated carrier-handling code into NMDevice (which can
introspect itself to see if it's a subclass that has carrier).

The "mostly ignore carrier" special handling for bridges and bonds is
now also handled as part of the NMDevice-level carrier handling.

https://bugzilla.gnome.org/show_bug.cgi?id=688284
parent cc7e5cc5
......@@ -608,7 +608,6 @@ static void
set_carrier (NMDeviceAdsl *self, const gboolean carrier)
{
NMDeviceAdslPrivate *priv;
NMDeviceState state;
g_return_if_fail (NM_IS_DEVICE (self));
......@@ -619,20 +618,6 @@ set_carrier (NMDeviceAdsl *self, const gboolean carrier)
priv->carrier = carrier;
g_object_notify (G_OBJECT (self), NM_DEVICE_ADSL_CARRIER);
state = nm_device_get_state (NM_DEVICE (self));
nm_log_info (LOGD_HW, "(%s): carrier now %s (device state %d)",
nm_device_get_iface (NM_DEVICE (self)),
carrier ? "ON" : "OFF",
state);
if (state == NM_DEVICE_STATE_UNAVAILABLE) {
if (priv->carrier)
nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER);
} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
if (!priv->carrier)
nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
}
}
static gboolean
......
......@@ -80,23 +80,6 @@ nm_bond_error_quark (void)
/******************************************************************/
static void
carrier_action (NMDeviceWired *self, NMDeviceState state, gboolean carrier)
{
/* Carrier can't be used to signal availability of the bond master because
* the bond's carrier follows the slaves' carriers. So carrier gets
* ignored when determining whether or not the device can be activated.
*
* Second, just because all slaves have been removed or have lost carrier
* does not mean the master should be deactivated. This could be due to
* user addition/removal of slaves, and is also normal operation with some
* failover modes.
*
* For these reasons, carrier changes are effectively ignored by overriding
* the parent class' carrier handling and doing nothing.
*/
}
static void
update_hw_address (NMDevice *dev)
{
......@@ -463,7 +446,6 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
NMDeviceWiredClass *wired_class = NM_DEVICE_WIRED_CLASS (klass);
g_type_class_add_private (object_class, sizeof (NMDeviceBondPrivate));
......@@ -487,8 +469,6 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
parent_class->enslave_slave = enslave_slave;
parent_class->release_slave = release_slave;
wired_class->carrier_action = carrier_action;
/* properties */
g_object_class_install_property
(object_class, PROP_HW_ADDRESS,
......
......@@ -80,20 +80,6 @@ nm_bridge_error_quark (void)
/******************************************************************/
static void
carrier_action (NMDeviceWired *self, NMDeviceState state, gboolean carrier)
{
/* Bridge carrier state follows IFF_UP with no ports, and port carrier
* states when ports are added. Thus carrier isn't useful when deciding
* to auto-activate the bridge master. Also, like bond masters, when the
* carrier state changes due to slave changes, we shouldn't deactivate the
* bridge since the user may be reconfiguring ports.
*
* For these reasons, carrier changes are effectively ignored by overriding
* the parent class' carrier handling and doing nothing.
*/
}
static void
update_hw_address (NMDevice *dev)
{
......@@ -516,7 +502,6 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
NMDeviceWiredClass *wired_class = NM_DEVICE_WIRED_CLASS (klass);
g_type_class_add_private (object_class, sizeof (NMDeviceBridgePrivate));
......@@ -540,8 +525,6 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
parent_class->enslave_slave = enslave_slave;
parent_class->release_slave = release_slave;
wired_class->carrier_action = carrier_action;
/* properties */
g_object_class_install_property
(object_class, PROP_HW_ADDRESS,
......
......@@ -61,7 +61,6 @@ typedef struct {
NMNetlinkMonitor *monitor;
gulong link_connected_id;
gulong link_disconnected_id;
guint carrier_action_defer_id;
} NMDeviceVlanPrivate;
enum {
......@@ -83,8 +82,7 @@ enum {
static void
set_carrier (NMDeviceVlan *self,
const gboolean carrier,
const gboolean defer_action);
const gboolean carrier);
/******************************************************************/
......@@ -147,7 +145,7 @@ hw_bring_up (NMDevice *dev, gboolean *no_firmware)
i = 20;
while (i-- > 0) {
carrier = get_carrier_sync (NM_DEVICE_VLAN (dev));
set_carrier (NM_DEVICE_VLAN (dev), carrier, carrier ? FALSE : TRUE);
set_carrier (NM_DEVICE_VLAN (dev), carrier);
if (carrier)
break;
g_usleep (100);
......@@ -441,66 +439,17 @@ connection_match_config (NMDevice *self, const GSList *connections)
/******************************************************************/
static void
carrier_action_defer_clear (NMDeviceVlan *self)
{
NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
if (priv->carrier_action_defer_id) {
g_source_remove (priv->carrier_action_defer_id);
priv->carrier_action_defer_id = 0;
}
}
static gboolean
carrier_action_defer_cb (gpointer user_data)
{
NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
NMDeviceState state;
priv->carrier_action_defer_id = 0;
state = nm_device_get_state (NM_DEVICE (self));
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 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);
}
return FALSE;
}
static void
set_carrier (NMDeviceVlan *self,
const gboolean carrier,
const gboolean defer_action)
const gboolean carrier)
{
NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
NMDeviceState state;
if (priv->carrier == carrier)
return;
/* Clear any previous deferred action */
carrier_action_defer_clear (self);
priv->carrier = carrier;
g_object_notify (G_OBJECT (self), NM_DEVICE_VLAN_CARRIER);
state = nm_device_get_state (NM_DEVICE (self));
nm_log_info (LOGD_HW | LOGD_VLAN,
"(%s): carrier now %s (device state %d%s)",
nm_device_get_iface (NM_DEVICE (self)),
carrier ? "ON" : "OFF",
state,
defer_action ? ", deferring action for 4 seconds" : "");
if (defer_action)
priv->carrier_action_defer_id = g_timeout_add_seconds (4, carrier_action_defer_cb, self);
else
carrier_action_defer_cb (self);
}
static void
......@@ -508,27 +457,15 @@ carrier_on (NMNetlinkMonitor *monitor, int idx, NMDevice *device)
{
/* Make sure signal is for us */
if (idx == nm_device_get_ifindex (device))
set_carrier (NM_DEVICE_VLAN (device), TRUE, FALSE);
set_carrier (NM_DEVICE_VLAN (device), TRUE);
}
static void
carrier_off (NMNetlinkMonitor *monitor, int idx, NMDevice *device)
{
NMDeviceState state;
gboolean defer = FALSE;
/* Make sure signal is for us */
if (idx == nm_device_get_ifindex (device)) {
/* Defer carrier-off event actions while connected by a few seconds
* so that tripping over a cable, power-cycling a switch, or breaking
* off the RJ45 locking tab isn't so catastrophic.
*/
state = nm_device_get_state (device);
if (state > NM_DEVICE_STATE_DISCONNECTED)
defer = TRUE;
set_carrier (NM_DEVICE_VLAN (device), FALSE, defer);
}
if (idx == nm_device_get_ifindex (device))
set_carrier (NM_DEVICE_VLAN (device), FALSE);
}
static void
......@@ -704,7 +641,6 @@ dispose (GObject *object)
g_signal_handler_disconnect (priv->monitor, priv->link_connected_id);
if (priv->link_disconnected_id)
g_signal_handler_disconnect (priv->monitor, priv->link_disconnected_id);
carrier_action_defer_clear (self);
g_object_unref (priv->monitor);
......
......@@ -55,7 +55,6 @@ typedef struct {
NMNetlinkMonitor * monitor;
gulong link_connected_id;
gulong link_disconnected_id;
guint carrier_action_defer_id;
} NMDeviceWiredPrivate;
......@@ -120,67 +119,17 @@ set_speed (NMDeviceWired *self, const guint32 speed)
speed);
}
static void
carrier_action_defer_clear (NMDeviceWired *self)
{
NMDeviceWiredPrivate *priv = NM_DEVICE_WIRED_GET_PRIVATE (self);
if (priv->carrier_action_defer_id) {
g_source_remove (priv->carrier_action_defer_id);
priv->carrier_action_defer_id = 0;
}
}
static void
carrier_action (NMDeviceWired *self, NMDeviceState state, gboolean carrier)
{
NMDevice *device = NM_DEVICE (self);
if (state == NM_DEVICE_STATE_UNAVAILABLE) {
if (carrier)
nm_device_queue_state (device, 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 (device) >= NM_DEVICE_STATE_DISCONNECTED)
nm_device_queued_state_clear (device);
}
} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
if (!carrier && !nm_device_get_enslaved (device))
nm_device_queue_state (device, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
}
}
static gboolean
carrier_action_defer_cb (gpointer user_data)
{
NMDeviceWired *self = NM_DEVICE_WIRED (user_data);
NMDeviceWiredPrivate *priv = NM_DEVICE_WIRED_GET_PRIVATE (self);
priv->carrier_action_defer_id = 0;
NM_DEVICE_WIRED_GET_CLASS (self)->carrier_action (self,
nm_device_get_state (NM_DEVICE (self)),
priv->carrier);
return FALSE;
}
static void
set_carrier (NMDeviceWired *self,
const gboolean carrier,
const gboolean defer_action)
gboolean carrier)
{
NMDeviceWiredPrivate *priv = NM_DEVICE_WIRED_GET_PRIVATE (self);
NMDevice *device = NM_DEVICE (self);
NMDeviceState state;
NMDeviceWiredPrivate *priv;
guint32 caps;
if (priv->carrier == carrier)
return;
/* Clear any previous deferred action */
carrier_action_defer_clear (self);
/* Warn if we try to set carrier down on a device that
* doesn't support carrier detect. These devices assume
* the carrier is always up.
......@@ -189,32 +138,7 @@ set_carrier (NMDeviceWired *self,
g_return_if_fail (caps & NM_DEVICE_CAP_CARRIER_DETECT);
priv->carrier = carrier;
state = nm_device_get_state (device);
if (state >= NM_DEVICE_STATE_UNAVAILABLE) {
nm_log_info (LOGD_HW | NM_DEVICE_WIRED_LOG_LEVEL (device),
"(%s): carrier now %s (device state %d%s)",
nm_device_get_iface (device),
carrier ? "ON" : "OFF",
state,
defer_action ? ", deferring action for 4 seconds" : "");
}
g_object_notify (G_OBJECT (self), "carrier");
/* Retry IP configuration for master devices now that the carrier is on */
if (nm_device_is_master (device) && priv->carrier) {
if (nm_device_activate_ip4_state_in_wait (device))
nm_device_activate_stage3_ip4_start (device);
if (nm_device_activate_ip6_state_in_wait (device))
nm_device_activate_stage3_ip6_start (device);
}
if (defer_action)
priv->carrier_action_defer_id = g_timeout_add_seconds (4, carrier_action_defer_cb, self);
else
carrier_action_defer_cb (self);
}
static void
......@@ -224,14 +148,10 @@ carrier_on (NMNetlinkMonitor *monitor,
{
NMDevice *device = NM_DEVICE (user_data);
NMDeviceWired *self = NM_DEVICE_WIRED (device);
guint32 caps;
/* Make sure signal is for us */
if (idx == nm_device_get_ifindex (device)) {
caps = nm_device_get_capabilities (device);
g_return_if_fail (caps & NM_DEVICE_CAP_CARRIER_DETECT);
set_carrier (self, TRUE, FALSE);
set_carrier (self, TRUE);
set_speed (self, ethtool_get_speed (self));
}
}
......@@ -243,26 +163,10 @@ carrier_off (NMNetlinkMonitor *monitor,
{
NMDevice *device = NM_DEVICE (user_data);
NMDeviceWired *self = NM_DEVICE_WIRED (device);
guint32 caps;
/* Make sure signal is for us */
if (idx == nm_device_get_ifindex (device)) {
NMDeviceState state;
gboolean defer = FALSE;
caps = nm_device_get_capabilities (device);
g_return_if_fail (caps & NM_DEVICE_CAP_CARRIER_DETECT);
/* Defer carrier-off event actions while connected by a few seconds
* so that tripping over a cable, power-cycling a switch, or breaking
* off the RJ45 locking tab isn't so catastrophic.
*/
state = nm_device_get_state (device);
if (state > NM_DEVICE_STATE_DISCONNECTED)
defer = TRUE;
set_carrier (self, FALSE, defer);
}
if (idx == nm_device_get_ifindex (device))
set_carrier (self, FALSE);
}
static gboolean
......@@ -362,7 +266,7 @@ hw_bring_up (NMDevice *dev, gboolean *no_firmware)
caps = nm_device_get_capabilities (dev);
if (caps & NM_DEVICE_CAP_CARRIER_DETECT) {
carrier = get_carrier_sync (NM_DEVICE_WIRED (dev));
set_carrier (NM_DEVICE_WIRED (dev), carrier, carrier ? FALSE : TRUE);
set_carrier (NM_DEVICE_WIRED (dev), carrier);
}
}
return result;
......@@ -458,8 +362,6 @@ dispose (GObject *object)
priv->link_disconnected_id = 0;
}
carrier_action_defer_clear (self);
if (priv->monitor) {
g_object_unref (priv->monitor);
priv->monitor = NULL;
......@@ -473,7 +375,6 @@ nm_device_wired_class_init (NMDeviceWiredClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
NMDeviceWiredClass *wired_class = NM_DEVICE_WIRED_CLASS (klass);
g_type_class_add_private (object_class, sizeof (NMDeviceWiredPrivate));
......@@ -487,8 +388,6 @@ nm_device_wired_class_init (NMDeviceWiredClass *klass)
parent_class->connection_match_config = connection_match_config;
parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
wired_class->carrier_action = carrier_action;
}
/**
......
......@@ -42,9 +42,6 @@ typedef struct {
typedef struct {
NMDeviceClass parent;
void (*carrier_action) (NMDeviceWired *self,
NMDeviceState state,
gboolean carrier);
} NMDeviceWiredClass;
GType nm_device_wired_get_type (void);
......
......@@ -262,6 +262,10 @@ typedef struct {
guint cp_loaded_id;
guint cp_removed_id;
guint cp_updated_id;
/* Deferred carrier handling */
guint carrier_action_defer_id;
} NMDevicePrivate;
static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReason reason);
......@@ -294,6 +298,9 @@ static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *conne
static const char *state_to_string (NMDeviceState state);
static void carrier_changed (GObject *object, GParamSpec *param, gpointer user_data);
static void carrier_action_defer_clear (NMDevice *self);
static void
nm_device_init (NMDevice *self)
{
......@@ -463,6 +470,9 @@ constructor (GType type,
update_accept_ra_save (dev);
update_ip6_privacy_save (dev);
if (g_object_class_find_property (G_OBJECT_GET_CLASS (dev), "carrier"))
g_signal_connect (dev, "notify::carrier", G_CALLBACK (carrier_changed), NULL);
priv->initialized = TRUE;
return object;
......@@ -1151,6 +1161,109 @@ nm_device_get_act_request (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->act_request;
}
static void
carrier_action_defer_clear (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->carrier_action_defer_id) {
g_source_remove (priv->carrier_action_defer_id);
priv->carrier_action_defer_id = 0;
}
}
static gboolean
carrier_action_defer_cb (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMDeviceState state;
priv->carrier_action_defer_id = 0;
/* We know that carrier is FALSE */
state = nm_device_get_state (self);
if (state >= NM_DEVICE_STATE_DISCONNECTED)
nm_device_queue_state (self, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
return FALSE;
}
static void
carrier_changed (GObject *object, GParamSpec *param, gpointer user_data)
{
NMDevice *self = NM_DEVICE (object);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean carrier, defer_action = FALSE;
NMDeviceState state;
/* Clear any previous deferred action */
carrier_action_defer_clear (self);
state = nm_device_get_state (self);
if (state < NM_DEVICE_STATE_UNAVAILABLE)
return;
g_object_get (object, "carrier", &carrier, NULL);
if (nm_device_is_master (self)) {
/* Bridge/bond carrier does not affect its own activation, but
* when carrier comes on, if there are slaves waiting, it will
* restart them.
*/
if (!carrier)
return;
if (!nm_device_activate_ip4_state_in_wait (self) &&
!nm_device_activate_ip6_state_in_wait (self))
return;
} else if (nm_device_get_enslaved (self) && !carrier) {
/* Slaves don't deactivate when they lose carrier; for bonds
* in particular that would be actively counterproductive.
*/
return;
}
if (priv->act_request && !carrier)
defer_action = TRUE;
nm_log_info (LOGD_HW | LOGD_DEVICE, "(%s): carrier now %s (device state %d%s)",
nm_device_get_iface (self),
carrier ? "ON" : "OFF",
state,
defer_action ? ", deferring action for 4 seconds" : "");
if (nm_device_is_master (self)) {
if (nm_device_activate_ip4_state_in_wait (self))
nm_device_activate_stage3_ip4_start (self);
if (nm_device_activate_ip6_state_in_wait (self))
nm_device_activate_stage3_ip6_start (self);
return;
}
if (state == NM_DEVICE_STATE_UNAVAILABLE) {
if (carrier)
nm_device_queue_state (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 (self) >= NM_DEVICE_STATE_DISCONNECTED)
nm_device_queued_state_clear (self);
}
} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
if (!carrier) {
if (defer_action)
priv->carrier_action_defer_id = g_timeout_add_seconds (4, carrier_action_defer_cb, self);
else
nm_device_queue_state (self, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
}
}
}
NMConnection *
nm_device_get_connection (NMDevice *self)
{
......@@ -4328,6 +4441,8 @@ dispose (GObject *object)
priv->cp_updated_id = 0;
}
carrier_action_defer_clear (self);
g_hash_table_unref (priv->available_connections);
activation_source_clear (self, TRUE, AF_INET);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment