linux: Use inhibitor lock to guard poll pausing

Use an inhibitor lock obtained via logind to make sure the polling
is paused before the system is put to sleep, rather than racing with
the suspension.

https://bugs.freedesktop.org/show_bug.cgi?id=99763
This commit is contained in:
Christian Kellner 2017-02-17 13:56:21 +01:00 committed by Bastien Nocera
parent 01cd65c1a7
commit 09cdb0ccfc
2 changed files with 86 additions and 1 deletions

View file

@ -9,6 +9,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/libupower-glib \
$(USB_CFLAGS) \
$(GIO_CFLAGS) \
$(GIO_UNIX_CFLAGS) \
$(GUDEV_CFLAGS) \
$(POLKIT_CFLAGS) \
$(GLIB_CFLAGS) \
@ -56,7 +57,9 @@ hidpp_test_SOURCES = \
hidpp_test_LDADD = \
-lm \
$(GLIB_LIBS) \
$(GIO_LIBS)
$(GIO_LIBS) \
$(GIO_UNIX_LIBS)
hidpp_test_CFLAGS = $(AM_CFLAGS) $(WARNINGFLAGS_C)
EXTRA_DIST = $(libupshared_la_SOURCES) \

View file

@ -27,6 +27,7 @@
#include <sys/wait.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include <gudev/gudev.h>
#include "up-backend.h"
@ -63,6 +64,7 @@ struct UpBackendPrivate
UpConfig *config;
GDBusProxy *logind_proxy;
guint logind_sleep_id;
int logind_inhibitor_fd;
};
enum {
@ -460,6 +462,78 @@ up_backend_take_action (UpBackend *backend)
NULL);
}
/**
* up_backend_inhibitor_lock_take:
* @backend: The %UpBackend class instance
*
* Acquire a sleep 'delay lock' via systemd's logind that will
* inhibit going to sleep until the lock is released again via
* up_backend_inhibitor_lock_release().
* Does nothing if the lock was already acquired.
*/
static void
up_backend_inhibitor_lock_take (UpBackend *backend)
{
GVariant *out, *input;
GUnixFDList *fds;
GError *error = NULL;
if (backend->priv->logind_inhibitor_fd > -1) {
return;
}
input = g_variant_new ("(ssss)",
"sleep", /* what */
"UPower", /* who */
"Pause device polling", /* why */
"delay"); /* mode */
out = g_dbus_proxy_call_with_unix_fd_list_sync (backend->priv->logind_proxy,
"Inhibit",
input,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&fds,
NULL,
&error);
if (out == NULL) {
g_warning ("Could not acquire inhibitor lock: %s", error->message);
return;
}
if (g_unix_fd_list_get_length (fds) != 1) {
g_warning ("Unexpected values returned by logind's 'Inhibit'");
g_variant_unref (out);
return;
}
backend->priv->logind_inhibitor_fd = g_unix_fd_list_get (fds, 0, NULL);
g_variant_unref (out);
g_debug ("Acquired inhibitor lock (%i)", backend->priv->logind_inhibitor_fd);
}
/**
* up_backend_inhibitor_lock_release:
* @backend: The %UpBackend class instance
*
* Releases a previously acquired inhibitor lock or does nothing
* if no lock is held;
*/
static void
up_backend_inhibitor_lock_release (UpBackend *backend)
{
if (backend->priv->logind_inhibitor_fd == -1) {
return;
}
close (backend->priv->logind_inhibitor_fd);
backend->priv->logind_inhibitor_fd = -1;
g_debug ("Released inhibitor lock");
}
/**
* up_backend_prepare_for_sleep:
*
@ -493,9 +567,12 @@ up_backend_prepare_for_sleep (GDBusConnection *connection,
if (will_sleep) {
up_daemon_pause_poll (backend->priv->daemon);
up_backend_inhibitor_lock_release (backend);
return;
}
up_backend_inhibitor_lock_take (backend);
/* we are waking up, lets refresh all battery devices */
g_debug ("Woke up from sleep; about to refresh devices");
array = up_device_list_get_array (backend->priv->device_list);
@ -570,6 +647,9 @@ up_backend_init (UpBackend *backend)
backend,
NULL);
backend->priv->logind_sleep_id = sleep_id;
backend->priv->logind_inhibitor_fd = -1;
up_backend_inhibitor_lock_take (backend);
}
/**
@ -597,6 +677,8 @@ up_backend_finalize (GObject *object)
g_dbus_connection_signal_unsubscribe (bus,
backend->priv->logind_sleep_id);
up_backend_inhibitor_lock_release (backend);
g_clear_object (&backend->priv->logind_proxy);
g_object_unref (backend->priv->managed_devices);