Commit f5e7d2f9 authored by spiiroin's avatar spiiroin

[mce-io] Augment resume detection with timerfd source. Fixes JB#43297

MCE needs to perform some tasks like rethinking timer expiration when device
resumes from suspend. The resume detection is built on assumption that some
input devices will send EV_SYN events on resume. In devices where such events
are not emitted, on resume tasks can get indefinitely delayed.

Take advantage on the fact that system time gets updated on resume, and
use timerfd to trigger resume detection logic whenever system time changes.
Signed-off-by: spiiroin's avatarSimo Piiroinen <simo.piiroinen@jollamobile.com>
parent b1740900
......@@ -283,6 +283,7 @@ mce-io.o:\
mce-io.h\
mce-lib.h\
mce-log.h\
mce-wakelock.h\
mce.h\
mce-io.pic.o:\
......@@ -292,6 +293,7 @@ mce-io.pic.o:\
mce-io.h\
mce-lib.h\
mce-log.h\
mce-wakelock.h\
mce.h\
mce-lib.o:\
......@@ -412,6 +414,7 @@ mce.o:\
mce-dsme.h\
mce-fbdev.h\
mce-hbtimer.h\
mce-io.h\
mce-log.h\
mce-modules.h\
mce-sensorfw.h\
......@@ -438,6 +441,7 @@ mce.pic.o:\
mce-dsme.h\
mce-fbdev.h\
mce-hbtimer.h\
mce-io.h\
mce-log.h\
mce-modules.h\
mce-sensorfw.h\
......
......@@ -25,11 +25,14 @@
#include "mce.h"
#include "mce-log.h"
#include "mce-lib.h"
#include "mce-wakelock.h"
#ifdef ENABLE_WAKELOCKS
# include "libwakelock.h"
#endif
#include <sys/timerfd.h>
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
......@@ -38,6 +41,10 @@
#include <glib/gstdio.h>
#ifndef TFD_TIMER_CANCELON_SET
# define TFD_TIMER_CANCELON_SET (1<<1)
#endif
/* ========================================================================= *
* CONSTANTS
* ========================================================================= */
......@@ -93,6 +100,11 @@ static GSList *file_monitors = NULL;
static void io_detect_resume (void);
static gboolean mce_io_resume_timer_cb (GIOChannel *chn, GIOCondition cnd, gpointer aptr);
static bool mce_io_prime_resume_timer (void);
void mce_io_init_resume_timer (void);
void mce_io_quit_resume_timer (void);
// GLIB_IO_HELPERS
static const char *io_condition_repr (GIOCondition cond);
......@@ -125,6 +137,7 @@ int mce_io_mon_get_fd (const mce_io_mon_t *iom
// MISC_UTILS
guint mce_io_add_watch (int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr);
gboolean mce_close_file (const gchar *const file, FILE **fp);
gboolean mce_read_chunk_from_file (const gchar *const file, void **data, gssize *len, int flags);
gboolean mce_read_string_from_file (const gchar *const file, gchar **string);
......@@ -180,6 +193,166 @@ EXIT:
return;
}
/** Timerfd we use for detecting resume
*/
static int mce_io_resume_timer_fd = -1;
/** Glib io watch for mce_io_resume_timer_fd
*/
static guint mce_io_resume_timer_id = 0;
/** Virtual wakelock used for protecting resume timer wakeups
*/
static const char mce_io_resume_timer_wakelock[] = "mce_io_resume_timer";
/** Glib io callback for mce_io_resume_timer_fd
*/
static gboolean
mce_io_resume_timer_cb(GIOChannel *chn, GIOCondition cnd, gpointer aptr)
{
(void)chn;
(void)aptr;
/* Deny suspending while handling timer wakeup */
mce_wakelock_obtain(mce_io_resume_timer_wakelock, -1);
gboolean result = G_SOURCE_REMOVE;
if( mce_io_resume_timer_fd == -1 || mce_io_resume_timer_id == 0 ) {
mce_log(LL_WARN, "stray resume timer wakeup");
goto EXIT;
}
if( cnd & ~G_IO_IN ) {
mce_log(LL_CRIT, "unexpected resume timer wakeup: %s",
io_condition_repr(cnd));
goto EXIT;
}
/* Read trigger count. Expected result is ECANCELED */
uint64_t cnt = 0;
int res = read(mce_io_resume_timer_fd, &cnt, sizeof cnt);
if( res == -1 ) {
if( errno == EAGAIN || errno == EINTR ) {
/* Silentry ignore temporary errors */
}
else if( errno == ECANCELED ) {
/* The expected error */
mce_log(LL_DEBUG, "resume timer wakeup");
io_detect_resume();
if( !mce_io_prime_resume_timer() )
goto EXIT;
}
else {
/* Some unexpected error */
mce_log(LL_CRIT, "can't read resume timer: %m");
goto EXIT;
}
}
else {
/* Silentry ignore timer actually triggering */
if( !mce_io_prime_resume_timer() )
goto EXIT;
}
result = G_SOURCE_CONTINUE;
EXIT:
if( result != G_SOURCE_CONTINUE && mce_io_resume_timer_id ) {
mce_log(LL_CRIT, "disabling resume timer");
mce_io_resume_timer_id = 0;
mce_io_quit_resume_timer();
}
mce_wakelock_release(mce_io_resume_timer_wakelock);
return result;
}
/** Program resume timer fd
*/
static bool
mce_io_prime_resume_timer(void)
{
bool ack = false;
if( mce_io_resume_timer_fd == -1 )
goto EXIT;
/* Use dummy interval as we are basically only interested in
* getting ECANCELED when CLOCK_REALTIME is adjusted due to
* device resuming from suspend.
*/
struct itimerspec its = { };
int flags = TFD_TIMER_ABSTIME | TFD_TIMER_CANCELON_SET;
if( timerfd_settime(mce_io_resume_timer_fd, flags, &its, 0) == -1 ) {
mce_log(LL_WARN, "can't program resume timer: %m");
goto EXIT;
}
ack = true;
EXIT:
return ack;
}
/** Create resume timer fd
*/
void
mce_io_init_resume_timer(void)
{
bool ack = false;
/* Create realtime clock timerfd */
int clockid = CLOCK_REALTIME;
int flags = O_NONBLOCK | O_CLOEXEC;
mce_io_resume_timer_fd = timerfd_create(clockid, flags);
if( mce_io_resume_timer_fd == -1 ) {
mce_log(LL_WARN, "timerfd_create: %m");
goto EXIT;
}
/* Program dummy wakeup time */
if( !mce_io_prime_resume_timer() )
goto EXIT;
/* Add io watch for the timerfd */
mce_io_resume_timer_id = mce_io_add_watch(mce_io_resume_timer_fd,
false,
G_IO_IN,
mce_io_resume_timer_cb,
0);
if( !mce_io_resume_timer_id )
goto EXIT;
ack = true;
EXIT:
if( !ack ) {
mce_io_quit_resume_timer();
mce_log(LL_WARN, "detect resume via timerfd not in use");
}
}
/** Delete resume timer fd
*/
void
mce_io_quit_resume_timer(void)
{
if( mce_io_resume_timer_id ) {
g_source_remove(mce_io_resume_timer_id),
mce_io_resume_timer_id = 0;
}
if( mce_io_resume_timer_fd != -1 ) {
close(mce_io_resume_timer_fd),
mce_io_resume_timer_fd = -1;
}
}
/* ========================================================================= *
* GLIB_IO_HELPERS
* ========================================================================= */
......@@ -1105,6 +1278,40 @@ void *mce_io_mon_get_user_data(const mce_io_mon_t *iomon)
* MISC_UTILS
* ========================================================================= */
/** Helper for creating I/O watch for file descriptor
*
* @param fd File descriptor for which to add glib io watch
* @param close_on_unref True to transfer fd owhership to io watch
* @param cnd Condition bitmask
* @param io_cb Callback function
* @param aptr User data to pass to the callback
*
* @return wath id on success, or 0 on failure
*/
guint
mce_io_add_watch(int fd, bool close_on_unref,
GIOCondition cnd, GIOFunc io_cb, gpointer aptr)
{
guint wid = 0;
GIOChannel *chn = 0;
if( !(chn = g_io_channel_unix_new(fd)) )
goto cleanup;
g_io_channel_set_close_on_unref(chn, close_on_unref);
cnd |= G_IO_ERR | G_IO_HUP | G_IO_NVAL;
if( !(wid = g_io_add_watch(chn, cnd, io_cb, aptr)) )
goto cleanup;
cleanup:
if( chn != 0 ) g_io_channel_unref(chn);
return wid;
}
/**
* Helper function for closing files that checks for NULL,
* prints proper error messages and NULLs the file pointer after close
......
......@@ -168,4 +168,8 @@ gboolean mce_io_update_file_atomic(const char *path,
const void *data, size_t size,
mode_t mode, gboolean keep_backup);
/* Resume from suspend detection */
void mce_io_init_resume_timer(void);
void mce_io_quit_resume_timer(void);
#endif /* _MCE_IO_H_ */
......@@ -24,6 +24,7 @@
*/
#include "mce.h"
#include "mce-io.h"
#include "mce-log.h"
#include "mce-common.h"
#include "mce-conf.h"
......@@ -1141,6 +1142,9 @@ int main(int argc, char **argv)
g_idle_add(mce_auto_exit_cb, 0);
}
/* Use timerfd to detect resume from suspend */
mce_io_init_resume_timer();
/* Run the main loop */
g_main_loop_run(mainloop);
......@@ -1148,6 +1152,8 @@ int main(int argc, char **argv)
* either because we requested or because of an error
*/
EXIT:
mce_io_quit_resume_timer();
/* Unload all modules */
mce_modules_exit();
......
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