From 3ab2b5480f52cfa10a57dec09ce94d7826fcd299 Mon Sep 17 00:00:00 2001 From: ArenM Date: Thu, 23 Feb 2023 20:22:56 -0500 Subject: [PATCH] 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 --- Makefile | 8 +- configs/default_hooks/sxmo_hook_start.sh | 2 +- programs/sxmo_wakeafter.c | 130 ----------------------- scripts/core/sxmo_autosuspend.sh | 73 +++++-------- scripts/core/sxmo_setpermissions.sh | 2 +- scripts/core/sxmo_suspend.sh | 32 ++++++ 6 files changed, 60 insertions(+), 187 deletions(-) delete mode 100644 programs/sxmo_wakeafter.c create mode 100755 scripts/core/sxmo_suspend.sh diff --git a/Makefile b/Makefile index 2a1e7f0..2da3fa9 100644 --- a/Makefile +++ b/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 "{}" \; diff --git a/configs/default_hooks/sxmo_hook_start.sh b/configs/default_hooks/sxmo_hook_start.sh index 495ca70..e37aee2 100755 --- a/configs/default_hooks/sxmo_hook_start.sh +++ b/configs/default_hooks/sxmo_hook_start.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 diff --git a/programs/sxmo_wakeafter.c b/programs/sxmo_wakeafter.c deleted file mode 100644 index e0c35a5..0000000 --- a/programs/sxmo_wakeafter.c +++ /dev/null @@ -1,130 +0,0 @@ -#include // error handling -#include // uint64_t type -#include // dprintf -#include // strtol, exit -#include // strerror -#include // timers -#include // read -#include // epoll_* -#include // 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: \n" - "\tRun after 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]); -} diff --git a/scripts/core/sxmo_autosuspend.sh b/scripts/core/sxmo_autosuspend.sh index c7333e3..e6f20e8 100755 --- a/scripts/core/sxmo_autosuspend.sh +++ b/scripts/core/sxmo_autosuspend.sh @@ -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 diff --git a/scripts/core/sxmo_setpermissions.sh b/scripts/core/sxmo_setpermissions.sh index a51392e..b2d5afe 100755 --- a/scripts/core/sxmo_setpermissions.sh +++ b/scripts/core/sxmo_setpermissions.sh @@ -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 diff --git a/scripts/core/sxmo_suspend.sh b/scripts/core/sxmo_suspend.sh new file mode 100755 index 0000000..33138ce --- /dev/null +++ b/scripts/core/sxmo_suspend.sh @@ -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