Read wakeup_count instead of using sxmo_wakeafter
The approach sxmo_wakeafter uses is flawed because the kernel isn't obligated to pass control to it after suspend. I'm pretty sure it normally gets called when the kernel updates the system clock. Since we're waiting for some time after every suspend we're not actually using opportunistic suspend. It's much simpler to read wakeup_count to ask the kernel to wait until there's no active locks. Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
This commit is contained in:
parent
b3da010599
commit
3ab2b5480f
8
Makefile
8
Makefile
|
@ -11,8 +11,7 @@ OPENRC:=1
|
|||
CC ?= $(CROSS_COMPILE)gcc
|
||||
PROGRAMS = \
|
||||
programs/sxmo_aligned_sleep \
|
||||
programs/sxmo_vibrate \
|
||||
programs/sxmo_wakeafter
|
||||
programs/sxmo_vibrate
|
||||
|
||||
all: $(PROGRAMS)
|
||||
|
||||
|
@ -24,9 +23,6 @@ shellcheck:
|
|||
programs/%: programs/%.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
programs/sxmo_wakeafter: programs/sxmo_wakeafter.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -Wl,--no-as-needed -lcap -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f programs/sxmo_aligned_sleep programs/sxmo_vibrate
|
||||
|
||||
|
@ -77,8 +73,6 @@ install-scripts: $(PROGRAMS)
|
|||
|
||||
install -D programs/sxmo_aligned_sleep $(DESTDIR)$(PREFIX)/bin/
|
||||
install -D programs/sxmo_vibrate $(DESTDIR)$(PREFIX)/bin/
|
||||
install -D programs/sxmo_wakeafter $(DESTDIR)$(PREFIX)/bin/
|
||||
setcap 'cap_block_suspend=p cap_wake_alarm=p' $(DESTDIR)$(PREFIX)/bin/sxmo_wakeafter
|
||||
|
||||
find $(DESTDIR)$(PREFIX)/share/sxmo/default_hooks/ -type f -exec ./setup_config_version.sh "{}" \;
|
||||
find $(DESTDIR)$(PREFIX)/share/sxmo/appcfg/ -type f -exec ./setup_config_version.sh "{}" \;
|
||||
|
|
|
@ -64,7 +64,7 @@ esac
|
|||
sxmo_hook_unlock.sh
|
||||
|
||||
# Turn on auto-suspend
|
||||
if [ -w "/sys/power/autosleep" ] && [ -f "/sys/power/wake_lock" ]; then
|
||||
if [ -w "/sys/power/wakeup_count" ] && [ -f "/sys/power/wake_lock" ]; then
|
||||
superctl start sxmo_autosuspend
|
||||
fi
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
#include <errno.h> // error handling
|
||||
#include <stdint.h> // uint64_t type
|
||||
#include <stdio.h> // dprintf
|
||||
#include <stdlib.h> // strtol, exit
|
||||
#include <string.h> // strerror
|
||||
#include <sys/timerfd.h> // timers
|
||||
#include <unistd.h> // read
|
||||
#include <sys/epoll.h> // epoll_*
|
||||
#include <sys/capability.h> // cap_*
|
||||
|
||||
#define MAX_EVENTS 1
|
||||
|
||||
#define try(func, args...) { \
|
||||
if (-1 == func(args)) { \
|
||||
perror(#func); \
|
||||
return 1; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
|
||||
|
||||
void error(char *message, int err) {
|
||||
dprintf(2, "%s: %s\n", message, strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void usage(char *name) {
|
||||
eprintf(
|
||||
"%s: <seconds> <command>\n"
|
||||
"\tRun <command> after <seconds> or after a suspension wake up. "
|
||||
"Hold the system awake until the command finished\n",
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
void check_cap_avail(cap_value_t cap) {
|
||||
if (!CAP_IS_SUPPORTED(CAP_SETFCAP)) {
|
||||
fprintf(stderr, "ERROR: %s isn't available on your system\n", cap_to_name(cap));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int check_perimsions() {
|
||||
const cap_value_t cap_list[2] = {CAP_BLOCK_SUSPEND, CAP_WAKE_ALARM};
|
||||
|
||||
check_cap_avail(CAP_BLOCK_SUSPEND);
|
||||
check_cap_avail(CAP_WAKE_ALARM);
|
||||
|
||||
cap_t caps = cap_get_proc();
|
||||
if (caps == NULL) {
|
||||
perror("cap_get_proc");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try(cap_set_flag, caps, CAP_EFFECTIVE, 2, cap_list, CAP_SET);
|
||||
|
||||
if (-1 == cap_set_proc(caps)) {
|
||||
perror("cap_set_proc");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try(cap_free, caps);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint64_t buf;
|
||||
long duration = -1;
|
||||
int time_fd, epollfd;
|
||||
|
||||
struct epoll_event ev, events[MAX_EVENTS];
|
||||
|
||||
struct timespec now;
|
||||
struct itimerspec time = {
|
||||
.it_value = { .tv_sec = 0, .tv_nsec = 0 },
|
||||
.it_interval = { .tv_sec = 0, .tv_nsec = 0 }
|
||||
};
|
||||
|
||||
// Check permissions
|
||||
if (check_perimsions()) {
|
||||
fprintf(stderr, "Hint: make sure this executeable has the "
|
||||
"cap_block_suspend and cap_wake_alarm privilages\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Process arguments
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duration = strtol(argv[1], NULL, 0);
|
||||
|
||||
if (duration <= 0 || errno == ERANGE) {
|
||||
usage(argv[0]);
|
||||
eprintf("Error: seconds must be a positive integer\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create timer
|
||||
if ((time_fd = timerfd_create(CLOCK_REALTIME_ALARM, 0)) == -1)
|
||||
error("Failed to create timerfd", errno);
|
||||
|
||||
// Setup epoll
|
||||
epollfd = epoll_create1(0);
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.fd = time_fd;
|
||||
epoll_ctl(epollfd, EPOLL_CTL_ADD, time_fd, &ev);
|
||||
|
||||
// Calculate absolute time to wakeup
|
||||
try(clock_gettime, CLOCK_REALTIME, &now);
|
||||
time.it_value.tv_sec = now.tv_sec + duration;
|
||||
time.it_value.tv_nsec = now.tv_nsec;
|
||||
|
||||
/* if (timerfd_settime(time_fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &time, NULL) == -1) { */
|
||||
try(timerfd_settime, time_fd, TFD_TIMER_ABSTIME, &time, NULL);
|
||||
|
||||
// Wait for timer
|
||||
int ret;
|
||||
do {
|
||||
ret = epoll_wait(epollfd, events, MAX_EVENTS, -1);
|
||||
if (ret == -1 && errno == EINTR) {
|
||||
eprintf("Woke up\n");
|
||||
break;
|
||||
}
|
||||
} while (ret < 1);
|
||||
|
||||
system(argv[2]);
|
||||
}
|
|
@ -6,53 +6,30 @@
|
|||
# shellcheck source=scripts/core/sxmo_common.sh
|
||||
. sxmo_common.sh
|
||||
|
||||
finish() {
|
||||
if [ -n "$INITIAL" ]; then
|
||||
echo "$INITIAL" > /sys/power/autosleep
|
||||
set -e
|
||||
# TODO: debugging only
|
||||
set -x
|
||||
|
||||
while true; do
|
||||
# Make sure it's fresh before checking locks, reading wakeup_count will
|
||||
# block so we can't poll it here
|
||||
sxmo_hook_check_state_mutexes.sh
|
||||
|
||||
# Reading from wakeup_count blocks until there are no wakelocks
|
||||
wakeup_count=$(cat /sys/power/wakeup_count)
|
||||
|
||||
# If the wakeup count has changed since we read it, this will fail so we
|
||||
# know to try again. If something takes a wake_lock after we do this, it
|
||||
# will cause the kernel to abort suspend.
|
||||
echo "$wakeup_count" > /sys/power/wakeup_count || continue
|
||||
|
||||
# If sxmo_suspend failed then we didn't enter suspend, it should be safe
|
||||
# to retry immediately. There's a delay so we don't eat up all the
|
||||
# system resoures if the kernel can't suspend.
|
||||
if ! sxmo_suspend.sh; then
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
kill "$WAKEPID"
|
||||
exit
|
||||
}
|
||||
|
||||
autosuspend() {
|
||||
YEARS8_TO_SEC=268435455
|
||||
|
||||
INITIAL="$(cat /sys/power/autosleep)"
|
||||
trap 'finish' TERM INT EXIT
|
||||
|
||||
while : ; do
|
||||
# necessary?
|
||||
echo "$INITIAL" > /sys/power/autosleep
|
||||
|
||||
suspend_time=99999999 # far away
|
||||
mnc="$(sxmo_hook_mnc.sh)"
|
||||
if [ -n "$mnc" ] && [ "$mnc" -gt 0 ] && [ "$mnc" -lt "$YEARS8_TO_SEC" ]; then
|
||||
if [ "$mnc" -le 15 ]; then # cronjob imminent
|
||||
sxmo_wakelock.sh lock waiting_cronjob infinite
|
||||
suspend_time=$((mnc + 1)) # to arm the following one
|
||||
else
|
||||
suspend_time=$((mnc - 10))
|
||||
fi
|
||||
fi
|
||||
|
||||
sxmo_wakeafter "$suspend_time" "sxmo_autosuspend.sh wokeup" &
|
||||
WAKEPID=$!
|
||||
sleep 1 # wait for it to epoll pwait
|
||||
|
||||
echo mem > /sys/power/autosleep
|
||||
wait
|
||||
done
|
||||
}
|
||||
|
||||
wokeup() {
|
||||
# 10s basic hold
|
||||
sxmo_wakelock.sh lock woke_up 10000000000
|
||||
|
||||
sxmo_hook_postwake.sh
|
||||
}
|
||||
|
||||
if [ -z "$*" ]; then
|
||||
set -- autosuspend
|
||||
fi
|
||||
|
||||
"$@"
|
||||
sleep 10
|
||||
done
|
||||
|
|
|
@ -18,7 +18,7 @@ fi
|
|||
# users can override this in sxmo_deviceprofile_mydevice.sh
|
||||
files="${SXMO_SYS_FILES:-"/sys/power/state /sys/power/mem_sleep /dev/rtc0"}"
|
||||
|
||||
for file in $files /sys/power/autosleep; do
|
||||
for file in $files /sys/power/wakeup_count; do
|
||||
[ -e "$file" ] && chmod a+rw "$file"
|
||||
done
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# Copyright 2022 Sxmo Contributors
|
||||
|
||||
# include common definitions
|
||||
# shellcheck source=scripts/core/sxmo_common.sh
|
||||
. sxmo_common.sh
|
||||
|
||||
sxmo_log "going to suspend to crust"
|
||||
|
||||
if suspend_time="$(sxmo_hook_mnc.sh)"; then
|
||||
sxmo_log "calling suspend with suspend_time <$suspend_time>"
|
||||
|
||||
start="$(date "+%s")"
|
||||
rtcwake -m mem -s "$suspend_time" || exit 1
|
||||
|
||||
#We woke up again
|
||||
time_spent="$(( $(date "+%s") - start ))"
|
||||
|
||||
if [ "$suspend_time" -gt 0 ] && [ "$((time_spent + 10))" -ge "$suspend_time" ]; then
|
||||
UNSUSPENDREASON="rtc"
|
||||
fi
|
||||
else
|
||||
sxmo_log "fake suspend (suspend_time ($suspend_time) less than zero)"
|
||||
UNSUSPENDREASON=rtc # we fake the crust for those seconds
|
||||
fi
|
||||
|
||||
if [ "$UNSUSPENDREASON" = "rtc" ]; then
|
||||
sxmo_wakelock.sh lock waiting_cronjob infinite
|
||||
fi
|
||||
|
||||
sxmo_hook_postwake.sh
|
Loading…
Reference in New Issue