2
0
mirror of https://github.com/koreader/koreader synced 2024-11-10 01:10:34 +00:00

Kobo: Refactor suspend in order to be able to catch input events sent during the 2s window of ntx madness (#12330)

I don't even remember how badly things broke (at least on old devices) without it, despite it making absolutely no sense at all (state-extended just flips a global that dictates whether some things get flagged as wakeup sources or not).

So, don't rock the boat too much: we don't remove it, but instead of using a sleep, we use a task deadline instead, which ensures we'll keep processing input events in the right order in the meantime. We'll already have neutered input by this point, so we'll only process power events anyway.

That means that the only iffy things are potentially *when* and *where* we have to potentially cancel that task. Resume makes sense, of course, and we log an info message to make the log flow clear; but we also do so in suspend... just in case. With a warning log because that probably indicates something fishy went on.

Also cleanup the comments while I'm there, and actually rewrite the wakeup_count stuff properly so it could actually theoretically be used if ntx kernels were actually reliable. Spoiler alert: they're not, this is still horribly broken on at least Mk < 7. Works just fine on a Forma, though, so, yay.

Fix #12325
This commit is contained in:
NiLuJe 2024-08-12 03:47:43 +02:00 committed by GitHub
parent 59476d38f4
commit 4264d915b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 78 deletions

View File

@ -727,6 +727,9 @@ function Kobo:init()
end
end
-- Just to be safe, we absolutely don't want to call open on this, so just use stat
self.has_wakeup_count = util.pathExists("/sys/power/wakeup_count")
-- Automagic sysfs discovery
if self.automagic_sysfs then
-- Battery
@ -1194,10 +1197,17 @@ end
-- NOTE: We overload this to make sure checkUnexpectedWakeup doesn't trip *before* the newly scheduled suspend
function Kobo:rescheduleSuspend()
UIManager:unschedule(self.suspend)
UIManager:unschedule(self._doSuspend)
UIManager:unschedule(self.checkUnexpectedWakeup)
UIManager:scheduleIn(self.suspend_wait_timeout, self.suspend, self)
end
function Kobo:scheduleUnexpectedWakeupGuard()
self.unexpected_wakeup_count = self.unexpected_wakeup_count + 1
logger.dbg("Kobo suspend: scheduling unexpected wakeup guard")
UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self)
end
function Kobo:checkUnexpectedWakeup()
-- Just in case another event like SleepCoverClosed also scheduled a suspend
UIManager:unschedule(self.suspend)
@ -1309,6 +1319,12 @@ function Kobo:standby(max_duration)
end
function Kobo:suspend()
-- If there's a _doSuspend still scheduled, something is going seriously wrong
-- (e.g., we caught multiple Suspend events without a Resume in between)...
if UIManager:unschedule(self._doSuspend) then
logger.warn("Kobo suspend: cancelled a pending suspend request via *suspend*. This is most likely a bug.")
end
-- On MTK, any suspend/standby attempt while plugged-in will hang the kernel... -_-"
-- NOTE: isCharging is still true while isCharged!
if self:isMTK() and self.powerd:isCharging() then
@ -1316,45 +1332,14 @@ function Kobo:suspend()
-- Do the usual scheduling dance, so we get a chance to fire the UnexpectedWakeupLimit event...
UIManager:unschedule(self.checkUnexpectedWakeup)
self.unexpected_wakeup_count = self.unexpected_wakeup_count + 1
logger.dbg("Kobo suspend: scheduling unexpected wakeup guard")
UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self)
self:scheduleUnexpectedWakeupGuard()
return
end
logger.info("Kobo suspend: going to sleep . . .")
UIManager:unschedule(self.checkUnexpectedWakeup)
-- NOTE: Sleep as little as possible here, sleeping has a tendency to make
-- everything mysteriously hang...
-- Depending on device/FW version, some kernels do not support
-- wakeup_count, account for that
--
-- NOTE: ... and of course, it appears to be broken, which probably
-- explains why nickel doesn't use this facility...
-- (By broken, I mean that the system wakes up right away).
-- So, unless that changes, unconditionally disable it.
--[[
local f, re, err_msg, err_code
local has_wakeup_count = false
f = io.open("/sys/power/wakeup_count", "re")
if f ~= nil then
f:close()
has_wakeup_count = true
end
-- Clear the kernel ring buffer... (we're missing a proper -C flag...)
--dmesg -c >/dev/null
-- Go to sleep
local curr_wakeup_count
if has_wakeup_count then
curr_wakeup_count = "$(cat /sys/power/wakeup_count)"
logger.dbg("Kobo suspend: Current WakeUp count:", curr_wakeup_count)
end
-]]
-- everything mysteriously hang...
-- NOTE: Sets gSleep_Mode_Suspend to 1. Used as a flag throughout the
-- kernel to suspend/resume various subsystems
@ -1363,37 +1348,50 @@ function Kobo:suspend()
if ret then
logger.dbg("Kobo suspend: successfully asked the kernel to put subsystems to sleep")
else
logger.warn("Kobo suspend: the kernel refused to flag subsystems for suspend!")
logger.err("Kobo suspend: the kernel refused to flag subsystems for suspend, aborting this attempt!")
-- We'd be going to standby instead of suspend, so, just try again later.
self:scheduleUnexpectedWakeupGuard()
return
end
ffiUtil.sleep(2)
logger.dbg("Kobo suspend: waited for 2s because of reasons...")
-- NOTE: As nonsensical as it looks given that the above just flips a global,
-- I have traumatic memories of things going awry if we don't sleep between the two writes...
logger.dbg("Kobo suspend: waiting for 2s because of reasons...")
-- We keep polling for input in order to be able to catch power events with extremely unlucky timing (#12325)...
UIManager:scheduleIn(2, self._doSuspend, self)
end
function Kobo:_doSuspend()
os.execute("sync")
logger.dbg("Kobo suspend: synced FS")
-- Depending on device/FW version, some kernels do not support wakeup_count, account for that.
-- NOTE: ...and of course, it appears to be broken on older devices,
-- which probably explains why nickel doesn't use this facility there...
-- (By broken, I mean that the system wakes up right away despite the successful write).
-- As we can't really divine where and when it'll work properly, unconditionally disable it.
--[[
if has_wakeup_count then
f = io.open("/sys/power/wakeup_count", "we")
if not f then
logger.err("cannot open /sys/power/wakeup_count")
return false
if self.has_wakeup_count then
self.curr_wakeup_count = self.powerd:read_int_file("/sys/power/wakeup_count")
logger.dbg("Kobo suspend: Current WakeUp count:", self.curr_wakeup_count)
local ret = ffiUtil.writeToSysfs(self.curr_wakeup_count, "/sys/power/wakeup_count")
if ret then
logger.dbg("Kobo suspend: WakeUp count matched")
else
logger.err("Kobo suspend: WakeUp count mismatch, aborting this suspend attempt!")
-- This means that there was at least one wakeup event since our read,
-- abort this attempt (i.e., don't write to state for now) and just schedule the wakeup guard.
self:scheduleUnexpectedWakeupGuard()
return
end
re, err_msg, err_code = f:write(tostring(curr_wakeup_count), "\n")
logger.dbg("Kobo suspend: wrote WakeUp count:", curr_wakeup_count)
if not re then
logger.err("Kobo suspend: failed to write WakeUp count:",
err_msg,
err_code)
end
f:close()
end
--]]
logger.dbg("Kobo suspend: asking for a suspend to RAM . . .")
local suspend_time = time.boottime_or_realtime_coarse()
ret = ffiUtil.writeToSysfs("mem", "/sys/power/state")
local ret = ffiUtil.writeToSysfs("mem", "/sys/power/state")
-- NOTE: At this point, we *should* be in suspend to RAM, as such,
-- execution should only resume on wakeup...
@ -1406,42 +1404,25 @@ function Kobo:suspend()
self:toggleChargingLED(false)
end
else
-- Most of the potential failures ought to be -EBUSY
-- (usually, because of the EPDC or touch panel).
-- NOTE: On recent enough kernels, with debugfs enabled and mounted, see also
-- /sys/kernel/debug/suspend_stats & /sys/kernel/debug/wakeup_sources
logger.warn("Kobo suspend: the kernel refused to enter suspend!")
-- Reset state-extended back to 0 since we are giving up.
-- NOTE: Despite it making little sense,
-- we reset state-extended back to 0 to mimic Nickel's own
-- 1 -> mem -> 0 loop in case of suspend failures...
-- c.f., nickel_suspend_strace.txt for more details.
ffiUtil.writeToSysfs("0", "/sys/power/state-extended")
if G_reader_settings:isTrue("pm_debug_entry_failure") then
self:toggleChargingLED(true)
end
end
-- NOTE: Ideally, we'd need a way to warn the user that suspending
-- gloriously failed at this point...
-- We can safely assume that just from a non-zero return code, without
-- looking at the detailed stderr message
-- (most of the failures we'll see are -EBUSY anyway)
-- For reference, when that happens to nickel, it appears to keep retrying
-- to wakeup & sleep ad nauseam,
-- which is where the non-sensical 1 -> mem -> 0 loop idea comes from...
-- cf. nickel_suspend_strace.txt for more details.
-- NOTE: On recent enough kernels, with debugfs enabled and mounted, see also
-- /sys/kernel/debug/suspend_stats & /sys/kernel/debug/wakeup_sources
--[[
if has_wakeup_count then
logger.dbg("wakeup count: $(cat /sys/power/wakeup_count)")
end
-- Print tke kernel log since our attempt to sleep...
--dmesg -c
--]]
-- NOTE: We unflag /sys/power/state-extended in Kobo:resume() to keep
-- things tidy and easier to follow
-- Kobo:resume() will reset unexpected_wakeup_count and unschedule the check to signal a sane wakeup.
self.unexpected_wakeup_count = self.unexpected_wakeup_count + 1
logger.dbg("Kobo suspend: scheduling unexpected wakeup guard")
UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self)
-- Kobo:resume() will also reset unexpected_wakeup_count and unschedule the check to signal a sane wakeup.
self:scheduleUnexpectedWakeupGuard()
end
function Kobo:resume()
@ -1451,6 +1432,10 @@ function Kobo:resume()
-- Unschedule the checkUnexpectedWakeup shenanigans.
UIManager:unschedule(self.checkUnexpectedWakeup)
UIManager:unschedule(self.suspend)
-- Cancel any pending suspend request
if UIManager:unschedule(self._doSuspend) then
logger.info("Kobo resume: cancelled a pending suspend request")
end
-- Now that we're up, unflag subsystems for suspend...
-- NOTE: Sets gSleep_Mode_Suspend to 0. Used as a flag throughout the

View File

@ -1,7 +1,7 @@
local BasePowerD = require("device/generic/powerd")
local Math = require("optmath")
local NickelConf = require("device/kobo/nickel_conf")
local SysfsLight = require ("device/sysfs_light")
local SysfsLight = require("device/sysfs_light")
local UIManager
local logger = require("logger")
local RTC = require("ffi/rtc")