Skip to content

Commit

Permalink
Merge pull request #68 from spiiroin/block_suspend_while_dsme_restarts
Browse files Browse the repository at this point in the history
Block suspend for 60 seconds if dsme makes abnormal exit
  • Loading branch information
spiiroin committed Apr 23, 2014
2 parents 1b383e6 + 196702c commit 091c47e
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 8 deletions.
31 changes: 24 additions & 7 deletions dsme/dsme-wdd-wd.c
Expand Up @@ -99,6 +99,19 @@ void dsme_wd_kick(void)
}
}

void dsme_wd_kick_from_sighnd(void)
{
// NOTE: called from signal handler - must stay async-signal-safe

for( size_t i = 0; i < WD_COUNT; ++i) {
if( wd_fd[i] == -1 )
continue;
if( write(wd_fd[i], "*", 1) == -1 ) {
/* dontcare, but need to keep the compiler happy */
}
}
}

static void check_for_wd_flags(bool wd_enabled[])
{
unsigned long len = 0;
Expand Down Expand Up @@ -179,24 +192,28 @@ bool dsme_wd_init(void)
void dsme_wd_quit(void)
{
for( size_t i = 0; i < WD_COUNT; ++i ) {
if( wd_fd[i] == -1 )
int fd = wd_fd[i];

if( fd == -1 )
continue;

if( TEMP_FAILURE_RETRY(write(wd_fd[i], "V", 1)) == -1 ) {
/* Remove the fd from the array already before attempting to
* close it so that dsme_wd_kick_from_sighnd() does not have
* a chance to use stale file descriptors */
wd_fd[i] = -1;

if( TEMP_FAILURE_RETRY(write(fd, "V", 1)) == -1 ) {
fprintf(stderr, ME "%s: failed to clear nowayout: %m\n",
wd[i].file);
}
else {
else {
fprintf(stderr, ME "%s: cleared nowayout state\n",
wd[i].file);
}

if( TEMP_FAILURE_RETRY(close(wd_fd[i])) == -1 ) {
if( TEMP_FAILURE_RETRY(close(fd)) == -1 ) {
fprintf(stderr, ME "%s: failed to close file: %m\n",
wd[i].file);
}

/* Mark it as closed even if there were errors */
wd_fd[i] = -1;
}
}
1 change: 1 addition & 0 deletions dsme/dsme-wdd-wd.h
Expand Up @@ -40,6 +40,7 @@ extern "C" {
#endif

void dsme_wd_kick(void);
void dsme_wd_kick_from_sighnd(void);
bool dsme_wd_init(void);
void dsme_wd_quit(void);

Expand Down
119 changes: 118 additions & 1 deletion dsme/dsme-wdd.c
Expand Up @@ -70,6 +70,7 @@ static void mainloop(unsigned sleep_interval,

static volatile bool run = true;

static volatile bool dsme_abnormal_exit = false;

/**
Usage
Expand Down Expand Up @@ -334,6 +335,7 @@ static void mainloop(unsigned sleep_interval,
// dsme server has failed to respond in due time
fprintf(stderr, ME "dsme-server nonresponsive; quitting\n");
run = false;
dsme_abnormal_exit = true;
goto done_running;
}

Expand Down Expand Up @@ -430,6 +432,105 @@ static bool kill_and_wait(pid_t pid, int sig, int max_wait)
return res;
}

/** Wakelock that DSME "leaks" on abnormal exit
*
* The purpose of the leak is to block late suspend while
* dsme is not running, so that:
* 1) suspend does not inhibit systemd from restarting dsme
* or rebooting the device
* 2) repeating suspend/resume cycles do not feed the hw
* watchdog and we get the watchdog reboot if dsme restart
* does not succeed
*/
#define DSME_RESTART_WAKELOCK "dsme_restart"

/** Sysfs helper for wakelock manipulation
*/
static void wakelock_write(const char *path, const char *data, size_t size)
{
// NOTE: called from signal handler - must stay async-signal-safe

int fd = open(path, O_WRONLY);
if( fd != -1 ) {
if( write(fd, data, size) == -1 ) {
/* dontcare, but need to keep the compiler happy */
}
close(fd);
}
}

/** Get restart wakelock
*
* Used for blocking suspend for one minute when dsme restart
* or watchdog reboot is expected to happen.
*/
static void obtain_restart_wakelock(void)
{
// NOTE: called from signal handler - must stay async-signal-safe

static const char path[] = "/sys/power/wake_lock";
static const char text[] = DSME_RESTART_WAKELOCK " 60000000000\n";
wakelock_write(path, text, sizeof text - 1);
}

/** Clear restart wakelock
*
* Used when dsme makes successful startup or normal exit
*/
static void release_restart_wakelock(void)
{
static const char path[] = "/sys/power/wake_unlock";
static const char text[] = DSME_RESTART_WAKELOCK "\n";
wakelock_write(path, text, sizeof text - 1);
}

/** Set wakelock before invoking default signal handler
*
* If dsme dies due to signal, set wakelock with timeout before
* invoking default signal handler.
*
* The wakelock will be cleared on dsme restart.
*
* The timeout must be long enough to allow watchdog reboot to
* happen if dsme restart fails.
*/
static void handle_terminating_signal(int sig)
{
// NOTE: signal handler - must stay async-signal-safe

/* Do not try anything fancy if we have been here
* before or are already on abnormal exit path */
if( !dsme_abnormal_exit ) {
dsme_abnormal_exit = true;

/* restore default signal handler */
signal(sig, SIG_DFL);

/* get a wakelock and kick the watchdogs */
obtain_restart_wakelock();
dsme_wd_kick_from_sighnd();

/* invoke default signal handler */
raise(sig);
}
_exit(EXIT_FAILURE);
}

/** Trap signals that normally terminate a process
*
* Some of these will be overridden later on.
*/
static void trap_terminating_signals(void)
{
static const int lut[] =
{
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV,
SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGBUS
};

for( size_t i = 0; i < sizeof lut / sizeof *lut; ++i )
signal(lut[i], handle_terminating_signal);
}
/**
@todo Possibility to alter priority of initial module somehow
*/
Expand All @@ -443,6 +544,8 @@ int main(int argc, char *argv[])
}
dsme_wd_kick();

trap_terminating_signals();

// set up signal handler
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);
Expand Down Expand Up @@ -555,10 +658,17 @@ int main(int argc, char *argv[])
set_nonblocking(from_child[0]);
}

/* Before entering the mainloop, clear wakelock that might be set if dsme
* is restarting after signal / watchdog pingpong failure */
release_restart_wakelock();

unsigned sleep_interval = DSME_HEARTBEAT_INTERVAL;
mainloop(sleep_interval, to_child[1], from_child[0]);
fprintf(stderr, ME "Exited main loop, quitting\n");

/* Get wakelock after exiting the mainloop, will be cleared
* if we make orderly normal exit */
obtain_restart_wakelock();

/* Bring down the dsme-server child process
*
Expand All @@ -581,13 +691,20 @@ int main(int argc, char *argv[])
dsme_wd_kick();
fprintf(stderr, ME "dsme-server stop failed, leaving watchdogs"
" active\n");
dsme_abnormal_exit = true;
}

/* Remove the PID file */
if (remove(DSME_PID_FILE) < 0 && errno != ENOENT) {
fprintf(stderr, ME "Couldn't remove lockfile: %m\n");
}

/* Clear wakelock on normal, successful exit */
if( dsme_abnormal_exit )
fprintf(stderr, ME "abnormal exit, leaving wakelock active\n");
else
release_restart_wakelock();

fprintf(stderr, "DSME %s terminating\n", STRINGIFY(PRG_VERSION));
return EXIT_SUCCESS;
return dsme_abnormal_exit ? EXIT_FAILURE : EXIT_SUCCESS;
}

0 comments on commit 091c47e

Please sign in to comment.