diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2016-05-13 14:42:14 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2016-05-13 14:42:14 +0200 |
commit | 3fdffab9550bf7e55815d7a4453854057de55f0a (patch) | |
tree | 43d30c21420e050154bc121aa314b3756a62aba7 | |
parent | 81499355b049fffadfa6576bbcd2d2fb9fac9d13 (diff) | |
parent | 81ea812362de2757979e2c675774fc445400c59f (diff) |
core: merge branch 'bg/disconnect-on-suspend-rh1330694-v2'
Let devices go through the disconnected state before sleeping so that
applications are notified of the change in connectivity and dispatcher
scripts are run.
https://bugzilla.redhat.com/show_bug.cgi?id=1330694
https://bugzilla.gnome.org/show_bug.cgi?id=748531
-rw-r--r-- | src/devices/nm-device.c | 23 | ||||
-rw-r--r-- | src/devices/nm-device.h | 4 | ||||
-rw-r--r-- | src/nm-manager.c | 117 | ||||
-rw-r--r-- | src/nm-sleep-monitor.c | 70 | ||||
-rw-r--r-- | src/nm-sleep-monitor.h | 6 |
5 files changed, 208 insertions, 12 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index c709989e27..1b25d957a2 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -9491,6 +9491,7 @@ nm_device_get_unmanaged_flags (NMDevice *self, NMUnmanagedFlags flag) * @allow_state_transition: if %FALSE, setting flags never triggers a device * state change. If %TRUE, the device can change state, if it is real and * switches from managed to unmanaged (or vice versa). + * @now: whether the state change should be immediate or delayed * @reason: the device state reason passed to nm_device_state_changed() if * the device becomes managed/unmanaged. This is only relevant if the * device switches state and if @allow_state_transition is %TRUE. @@ -9502,11 +9503,13 @@ _set_unmanaged_flags (NMDevice *self, NMUnmanagedFlags flags, NMUnmanFlagOp set_op, gboolean allow_state_transition, + gboolean now, NMDeviceStateReason reason) { NMDevicePrivate *priv; gboolean was_managed, transition_state; NMUnmanagedFlags old_flags, old_mask; + NMDeviceState new_state; const char *operation = NULL; char str1[512]; char str2[512]; @@ -9578,10 +9581,11 @@ _set_unmanaged_flags (NMDevice *self, #undef _FMT if (transition_state) { - if (was_managed) - nm_device_state_changed (self, NM_DEVICE_STATE_UNMANAGED, reason); + new_state = was_managed ? NM_DEVICE_STATE_UNMANAGED : NM_DEVICE_STATE_UNAVAILABLE; + if (now) + nm_device_state_changed (self, new_state, reason); else - nm_device_state_changed (self, NM_DEVICE_STATE_UNAVAILABLE, reason); + nm_device_queue_state (self, new_state, reason); } } @@ -9599,7 +9603,7 @@ nm_device_set_unmanaged_flags (NMDevice *self, NMUnmanagedFlags flags, NMUnmanFlagOp set_op) { - _set_unmanaged_flags (self, flags, set_op, FALSE, NM_DEVICE_STATE_REASON_NONE); + _set_unmanaged_flags (self, flags, set_op, FALSE, FALSE, NM_DEVICE_STATE_REASON_NONE); } /** @@ -9620,7 +9624,16 @@ nm_device_set_unmanaged_by_flags (NMDevice *self, NMUnmanFlagOp set_op, NMDeviceStateReason reason) { - _set_unmanaged_flags (self, flags, set_op, TRUE, reason); + _set_unmanaged_flags (self, flags, set_op, TRUE, TRUE, reason); +} + +void +nm_device_set_unmanaged_by_flags_queue (NMDevice *self, + NMUnmanagedFlags flags, + NMUnmanFlagOp set_op, + NMDeviceStateReason reason) +{ + _set_unmanaged_flags (self, flags, set_op, TRUE, FALSE, reason); } void diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 9f6898505c..2180439631 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -489,6 +489,10 @@ void nm_device_set_unmanaged_by_flags (NMDevice *device, NMUnmanagedFlags flags, NMUnmanFlagOp set_op, NMDeviceStateReason reason); +void nm_device_set_unmanaged_by_flags_queue (NMDevice *self, + NMUnmanagedFlags flags, + NMUnmanFlagOp set_op, + NMDeviceStateReason reason); void nm_device_set_unmanaged_by_user_config (NMDevice *self, const GSList *unmanaged_specs); void nm_device_set_unmanaged_by_user_udev (NMDevice *self); void nm_device_set_unmanaged_by_quitting (NMDevice *device); diff --git a/src/nm-manager.c b/src/nm-manager.c index 2edf3fda49..ab22d9cc76 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -79,6 +79,9 @@ static gboolean find_master (NMManager *self, static void nm_manager_update_state (NMManager *manager); static void connection_changed (NMManager *self, NMConnection *connection); +static void device_sleep_cb (NMDevice *device, + GParamSpec *pspec, + NMManager *self); #define TAG_ACTIVE_CONNETION_ADD_AND_ACTIVATE "act-con-add-and-activate" @@ -127,6 +130,7 @@ typedef struct { NMSleepMonitor *sleep_monitor; GSList *auth_chains; + GHashTable *sleep_devices; /* Firmware dir monitor */ GFileMonitor *fw_monitor; @@ -3818,6 +3822,98 @@ device_is_wake_on_lan (NMDevice *device) return nm_platform_link_get_wake_on_lan (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device)); } +static gboolean +sleep_devices_add (NMManager *self, NMDevice *device, gboolean suspending) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMSleepMonitorInhibitorHandle *handle = NULL; + + if (g_hash_table_lookup_extended (priv->sleep_devices, device, NULL, (gpointer *) &handle)) { + if (suspending) { + /* if we are suspending, always insert a new handle in sleep_devices. + * Even if we had an old handle, it might be stale by now. */ + g_hash_table_insert (priv->sleep_devices, device, + nm_sleep_monitor_inhibit_take (priv->sleep_monitor)); + if (handle) + nm_sleep_monitor_inhibit_release (priv->sleep_monitor, handle); + } + return FALSE; + } + + g_hash_table_insert (priv->sleep_devices, + g_object_ref (device), + suspending + ? nm_sleep_monitor_inhibit_take (priv->sleep_monitor) + : NULL); + g_signal_connect (device, "notify::" NM_DEVICE_STATE, (GCallback) device_sleep_cb, self); + return TRUE; +} + +static gboolean +sleep_devices_remove (NMManager *self, NMDevice *device) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMSleepMonitorInhibitorHandle *handle; + + if (!g_hash_table_lookup_extended (priv->sleep_devices, device, NULL, (gpointer *) &handle)) + return FALSE; + + if (handle) + nm_sleep_monitor_inhibit_release (priv->sleep_monitor, handle); + + /* Remove device from hash */ + g_signal_handlers_disconnect_by_func (device, device_sleep_cb, self); + g_hash_table_remove (priv->sleep_devices, device); + g_object_unref (device); + return TRUE; +} + +static void +sleep_devices_clear (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; + NMSleepMonitorInhibitorHandle *handle; + GHashTableIter iter; + + if (!priv->sleep_devices) + return; + + g_hash_table_iter_init (&iter, priv->sleep_devices); + while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &handle)) { + g_signal_handlers_disconnect_by_func (device, device_sleep_cb, self); + if (handle) + nm_sleep_monitor_inhibit_release (priv->sleep_monitor, handle); + g_object_unref (device); + g_hash_table_iter_remove (&iter); + } +} + +static void +device_sleep_cb (NMDevice *device, + GParamSpec *pspec, + NMManager *self) +{ + switch (nm_device_get_state (device)) { + case NM_DEVICE_STATE_DISCONNECTED: + _LOGD (LOGD_SUSPEND, "sleep: unmanaging device %s", nm_device_get_ip_iface (device)); + nm_device_set_unmanaged_by_flags_queue (device, + NM_UNMANAGED_SLEEPING, + TRUE, + NM_DEVICE_STATE_REASON_SLEEPING); + break; + case NM_DEVICE_STATE_UNMANAGED: + _LOGD (LOGD_SUSPEND, "sleep: device %s is ready", nm_device_get_ip_iface (device)); + + if (!sleep_devices_remove (self, device)) + g_return_if_reached (); + + break; + default: + return; + } +} + static void do_sleep_wake (NMManager *self, gboolean sleeping_changed) { @@ -3841,15 +3937,28 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) if (nm_device_is_software (device)) continue; /* Wake-on-LAN devices will be taken down post-suspend rather than pre- */ - if (suspending && device_is_wake_on_lan (device)) + if (suspending && device_is_wake_on_lan (device)) { + _LOGD (LOGD_SUSPEND, "sleep: device %s has wake-on-lan, skipping", + nm_device_get_ip_iface (device)); continue; + } + + if (nm_device_is_activating (device) || + nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { + _LOGD (LOGD_SUSPEND, "sleep: wait disconnection of device %s", + nm_device_get_ip_iface (device)); - nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_SLEEPING, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); + if (sleep_devices_add (self, device, suspending)) + nm_device_queue_state (device, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_SLEEPING); + } else { + nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_SLEEPING, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); + } } } else { _LOGI (LOGD_SUSPEND, "%s...", waking_from_suspend ? "waking up" : "re-enabling"); if (waking_from_suspend) { + sleep_devices_clear (self); /* Belatedly take down Wake-on-LAN devices; ideally we wouldn't have to do this * but for now it's the only way to make sure we re-check their connectivity. */ @@ -5231,6 +5340,7 @@ nm_manager_init (NMManager *self) priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, self); priv->metered = NM_METERED_UNKNOWN; + priv->sleep_devices = g_hash_table_new (g_direct_hash, g_direct_equal); } static gboolean @@ -5435,6 +5545,9 @@ dispose (GObject *object) } _set_prop_filter (manager, NULL); + sleep_devices_clear (manager); + g_clear_pointer (&priv->sleep_devices, g_hash_table_unref); + if (priv->sleep_monitor) { g_signal_handlers_disconnect_by_func (priv->sleep_monitor, sleeping_cb, manager); g_clear_object (&priv->sleep_monitor); diff --git a/src/nm-sleep-monitor.c b/src/nm-sleep-monitor.c index 42a61f6fe3..13ce1be000 100644 --- a/src/nm-sleep-monitor.c +++ b/src/nm-sleep-monitor.c @@ -73,6 +73,8 @@ struct _NMSleepMonitor { GCancellable *cancellable; gint inhibit_fd; + GSList *handles_active; + GSList *handles_stale; gulong sig_id_1; gulong sig_id_2; @@ -122,14 +124,22 @@ upower_resuming_cb (GDBusProxy *proxy, gpointer user_data) #else /* USE_UPOWER */ static void -drop_inhibitor (NMSleepMonitor *self) +drop_inhibitor (NMSleepMonitor *self, gboolean force) { + if (!force && self->handles_active) + return; + if (self->inhibit_fd >= 0) { _LOGD ("inhibit: dropping sleep inhibitor %d", self->inhibit_fd); close (self->inhibit_fd); self->inhibit_fd = -1; } + if (self->handles_active) { + self->handles_stale = g_slist_concat (self->handles_stale, self->handles_active); + self->handles_active = NULL; + } + nm_clear_g_cancellable (&self->cancellable); } @@ -170,7 +180,7 @@ take_inhibitor (NMSleepMonitor *self) g_return_if_fail (NM_IS_SLEEP_MONITOR (self)); g_return_if_fail (G_IS_DBUS_PROXY (self->proxy)); - drop_inhibitor (self); + drop_inhibitor (self, TRUE); _LOGD ("inhibit: taking sleep inhibitor..."); self->cancellable = g_cancellable_new (); @@ -212,7 +222,7 @@ name_owner_cb (GObject *object, if (owner) take_inhibitor (self); else - drop_inhibitor (self); + drop_inhibitor (self, TRUE); g_free (owner); } #endif /* USE_UPOWER */ @@ -234,7 +244,57 @@ sleep_signal (NMSleepMonitor *self, #if !USE_UPOWER if (is_about_to_suspend) - drop_inhibitor (self); + drop_inhibitor (self, FALSE); +#endif +} + +/** + * nm_sleep_monitor_inhibit_take: + * @self: the #NMSleepMonitor instance + * + * Prevent the release of inhibitor lock + * + * Returns: an inhibitor handle that must be returned via + * nm_sleep_monitor_inhibit_release(). + **/ +NMSleepMonitorInhibitorHandle * +nm_sleep_monitor_inhibit_take (NMSleepMonitor *self) +{ + g_return_val_if_fail (NM_IS_SLEEP_MONITOR (self), NULL); + + self->handles_active = g_slist_prepend (self->handles_active, NULL); + return (NMSleepMonitorInhibitorHandle *) self->handles_active; +} + +/** + * nm_sleep_monitor_inhibit_release: + * @self: the #NMSleepMonitor instance + * @handle: the #NMSleepMonitorInhibitorHandle inhibitor handle. + * + * Allow again the release of inhibitor lock + **/ +void +nm_sleep_monitor_inhibit_release (NMSleepMonitor *self, + NMSleepMonitorInhibitorHandle *handle) +{ + GSList *l; + + g_return_if_fail (NM_IS_SLEEP_MONITOR (self)); + g_return_if_fail (handle); + + l = (GSList *) handle; + + if (g_slist_position (self->handles_active, l) < 0) { + if (g_slist_position (self->handles_stale, l) < 0) + g_return_if_reached (); + self->handles_stale = g_slist_delete_link (self->handles_stale, l); + return; + } + + self->handles_active = g_slist_delete_link (self->handles_active, l); + +#if !USE_UPOWER + drop_inhibitor (self, FALSE); #endif } @@ -303,7 +363,7 @@ dispose (GObject *object) NMSleepMonitor *self = NM_SLEEP_MONITOR (object); #if !USE_UPOWER - drop_inhibitor (self); + drop_inhibitor (self, TRUE); #endif nm_clear_g_cancellable (&self->cancellable); diff --git a/src/nm-sleep-monitor.h b/src/nm-sleep-monitor.h index 494a188eae..140f964f06 100644 --- a/src/nm-sleep-monitor.h +++ b/src/nm-sleep-monitor.h @@ -36,6 +36,12 @@ typedef struct _NMSleepMonitorClass NMSleepMonitorClass; GType nm_sleep_monitor_get_type (void) G_GNUC_CONST; NMSleepMonitor *nm_sleep_monitor_new (void); +typedef struct _NMSleepMonitorInhibitorHandle NMSleepMonitorInhibitorHandle; + +NMSleepMonitorInhibitorHandle *nm_sleep_monitor_inhibit_take (NMSleepMonitor *self); +void nm_sleep_monitor_inhibit_release (NMSleepMonitor *self, + NMSleepMonitorInhibitorHandle *handle); + G_END_DECLS #endif /* __NETWORKMANAGER_SLEEP_MONITOR_H__ */ |