mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
Suspend-to-idle timer quiescing support for v3.20-rc1
Till now suspend-to-idle has not been able to save much more energy than runtime PM because of timer interrupts that periodically bring CPUs out of idle while they are waiting for a wakeup interrupt. Of course, the timer interrupts are not wakeup ones, so the handling of them can be deferred until a real wakeup interrupt happens, but at the same time we don't want to mass-expire timers at that point. The solution is to suspend the entire timekeeping when the last CPU is entering an idle state and resume it when the first CPU goes out of idle. That has to be done with care, though, so as to avoid accessing suspended clocksources etc. end we need extra support from idle drivers for that. This series of commits adds support for quiescing timers during suspend-to-idle and adds the requisite callbacks to intel_idle and the ACPI cpuidle driver. / -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABCAAGBQJU4PNaAAoJEILEb/54YlRxgjsP/0UbDGbltVyM8VFhsobqhOni thKJTJsqWqYgsPfTufbOGyvP6zskbsarDlzCXoKXuHaynIqcxY8xfZvMdcQr1j0S nhKdqv4R6qlP3w2cFxXVZwhw21X3YO1zIxpi5Do1HdVuWoOvxq8Dk4cU8MrgOJC0 6ThC9Q7klheV4tY6Narlmmf6sX5O+S/EaqnupESSG4cqxNmlPw5AguLviBaUNVAY RSjUX8LAce05bOIGEpaFY+vUws+jlU7/T/GEajquEsGF9zalh2CsWso5nQvilxrJ 22MVqXUyHaXmTC+U7nV78qRkavR6zyr3v/JBDse56qRI1mFlmyvGh8mE5ukmpqJE Cg5rRC68o71xlBSVGhKW3Os2ks2Nenj2NLltrTyuh43OBJ691TaLsZnKh5nYt/MW MZdqRRjIDTMF+/P1u4wY8S63labrrmp7w4T720CgaZCLJ/9VfZQuqKXTTm2R5/II eDhFvdYXoP2748uUOn5sOr5/o0xhnMdaxykZZxE3IkSpOpIV1Mo2HWTIyDYXlILP 0OuJUUZFZtFOjWGCPn3YgoFT94C3nlO1vkXw//44okTUiUaaOZz+VWDF4fxdVeLR 8NGTe+/QzEq+2lbs+ZWRSM1hPukOntFcwCgWXFiqh9x2F00LAw9JpkiKBujxTjUV m2WstYaML3W7gBMyhxg0 =55Jb -----END PGP SIGNATURE----- Merge tag 'suspend-to-idle-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull suspend-to-idle updates from Rafael Wysocki: "Suspend-to-idle timer quiescing support for v3.20-rc1 Until now suspend-to-idle has not been able to save much more energy than runtime PM because of timer interrupts that periodically bring CPUs out of idle while they are waiting for a wakeup interrupt. Of course, the timer interrupts are not wakeup ones, so the handling of them can be deferred until a real wakeup interrupt happens, but at the same time we don't want to mass-expire timers at that point. The solution is to suspend the entire timekeeping when the last CPU is entering an idle state and resume it when the first CPU goes out of idle. That has to be done with care, though, so as to avoid accessing suspended clocksources etc. end we need extra support from idle drivers for that. This series of commits adds support for quiescing timers during suspend-to-idle and adds the requisite callbacks to intel_idle and the ACPI cpuidle driver" * tag 'suspend-to-idle-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: ACPI / idle: Implement ->enter_freeze callback routine intel_idle: Add ->enter_freeze callbacks PM / sleep: Make it possible to quiesce timers during suspend-to-idle timekeeping: Make it safe to use the fast timekeeper while suspended timekeeping: Pass readout base to update_fast_timekeeper() PM / sleep: Re-implement suspend-to-idle handling
This commit is contained in:
commit
99fa0ad92c
11 changed files with 407 additions and 108 deletions
|
|
@ -394,6 +394,56 @@ void tick_resume(void)
|
|||
}
|
||||
}
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(tick_freeze_lock);
|
||||
static unsigned int tick_freeze_depth;
|
||||
|
||||
/**
|
||||
* tick_freeze - Suspend the local tick and (possibly) timekeeping.
|
||||
*
|
||||
* Check if this is the last online CPU executing the function and if so,
|
||||
* suspend timekeeping. Otherwise suspend the local tick.
|
||||
*
|
||||
* Call with interrupts disabled. Must be balanced with %tick_unfreeze().
|
||||
* Interrupts must not be enabled before the subsequent %tick_unfreeze().
|
||||
*/
|
||||
void tick_freeze(void)
|
||||
{
|
||||
raw_spin_lock(&tick_freeze_lock);
|
||||
|
||||
tick_freeze_depth++;
|
||||
if (tick_freeze_depth == num_online_cpus()) {
|
||||
timekeeping_suspend();
|
||||
} else {
|
||||
tick_suspend();
|
||||
tick_suspend_broadcast();
|
||||
}
|
||||
|
||||
raw_spin_unlock(&tick_freeze_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_unfreeze - Resume the local tick and (possibly) timekeeping.
|
||||
*
|
||||
* Check if this is the first CPU executing the function and if so, resume
|
||||
* timekeeping. Otherwise resume the local tick.
|
||||
*
|
||||
* Call with interrupts disabled. Must be balanced with %tick_freeze().
|
||||
* Interrupts must not be enabled after the preceding %tick_freeze().
|
||||
*/
|
||||
void tick_unfreeze(void)
|
||||
{
|
||||
raw_spin_lock(&tick_freeze_lock);
|
||||
|
||||
if (tick_freeze_depth == num_online_cpus())
|
||||
timekeeping_resume();
|
||||
else
|
||||
tick_resume();
|
||||
|
||||
tick_freeze_depth--;
|
||||
|
||||
raw_spin_unlock(&tick_freeze_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_init - initialize the tick control
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -230,9 +230,7 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk)
|
|||
|
||||
/**
|
||||
* update_fast_timekeeper - Update the fast and NMI safe monotonic timekeeper.
|
||||
* @tk: The timekeeper from which we take the update
|
||||
* @tkf: The fast timekeeper to update
|
||||
* @tbase: The time base for the fast timekeeper (mono/raw)
|
||||
* @tkr: Timekeeping readout base from which we take the update
|
||||
*
|
||||
* We want to use this from any context including NMI and tracing /
|
||||
* instrumenting the timekeeping code itself.
|
||||
|
|
@ -244,11 +242,11 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk)
|
|||
* smp_wmb(); <- Ensure that the last base[1] update is visible
|
||||
* tkf->seq++;
|
||||
* smp_wmb(); <- Ensure that the seqcount update is visible
|
||||
* update(tkf->base[0], tk);
|
||||
* update(tkf->base[0], tkr);
|
||||
* smp_wmb(); <- Ensure that the base[0] update is visible
|
||||
* tkf->seq++;
|
||||
* smp_wmb(); <- Ensure that the seqcount update is visible
|
||||
* update(tkf->base[1], tk);
|
||||
* update(tkf->base[1], tkr);
|
||||
*
|
||||
* The reader side does:
|
||||
*
|
||||
|
|
@ -269,7 +267,7 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk)
|
|||
* slightly wrong timestamp (a few nanoseconds). See
|
||||
* @ktime_get_mono_fast_ns.
|
||||
*/
|
||||
static void update_fast_timekeeper(struct timekeeper *tk)
|
||||
static void update_fast_timekeeper(struct tk_read_base *tkr)
|
||||
{
|
||||
struct tk_read_base *base = tk_fast_mono.base;
|
||||
|
||||
|
|
@ -277,7 +275,7 @@ static void update_fast_timekeeper(struct timekeeper *tk)
|
|||
raw_write_seqcount_latch(&tk_fast_mono.seq);
|
||||
|
||||
/* Update base[0] */
|
||||
memcpy(base, &tk->tkr, sizeof(*base));
|
||||
memcpy(base, tkr, sizeof(*base));
|
||||
|
||||
/* Force readers back to base[0] */
|
||||
raw_write_seqcount_latch(&tk_fast_mono.seq);
|
||||
|
|
@ -334,6 +332,35 @@ u64 notrace ktime_get_mono_fast_ns(void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_get_mono_fast_ns);
|
||||
|
||||
/* Suspend-time cycles value for halted fast timekeeper. */
|
||||
static cycle_t cycles_at_suspend;
|
||||
|
||||
static cycle_t dummy_clock_read(struct clocksource *cs)
|
||||
{
|
||||
return cycles_at_suspend;
|
||||
}
|
||||
|
||||
/**
|
||||
* halt_fast_timekeeper - Prevent fast timekeeper from accessing clocksource.
|
||||
* @tk: Timekeeper to snapshot.
|
||||
*
|
||||
* It generally is unsafe to access the clocksource after timekeeping has been
|
||||
* suspended, so take a snapshot of the readout base of @tk and use it as the
|
||||
* fast timekeeper's readout base while suspended. It will return the same
|
||||
* number of cycles every time until timekeeping is resumed at which time the
|
||||
* proper readout base for the fast timekeeper will be restored automatically.
|
||||
*/
|
||||
static void halt_fast_timekeeper(struct timekeeper *tk)
|
||||
{
|
||||
static struct tk_read_base tkr_dummy;
|
||||
struct tk_read_base *tkr = &tk->tkr;
|
||||
|
||||
memcpy(&tkr_dummy, tkr, sizeof(tkr_dummy));
|
||||
cycles_at_suspend = tkr->read(tkr->clock);
|
||||
tkr_dummy.read = dummy_clock_read;
|
||||
update_fast_timekeeper(&tkr_dummy);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD
|
||||
|
||||
static inline void update_vsyscall(struct timekeeper *tk)
|
||||
|
|
@ -462,7 +489,7 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action)
|
|||
memcpy(&shadow_timekeeper, &tk_core.timekeeper,
|
||||
sizeof(tk_core.timekeeper));
|
||||
|
||||
update_fast_timekeeper(tk);
|
||||
update_fast_timekeeper(&tk->tkr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1170,7 +1197,7 @@ void timekeeping_inject_sleeptime64(struct timespec64 *delta)
|
|||
* xtime/wall_to_monotonic/jiffies/etc are
|
||||
* still managed by arch specific suspend/resume code.
|
||||
*/
|
||||
static void timekeeping_resume(void)
|
||||
void timekeeping_resume(void)
|
||||
{
|
||||
struct timekeeper *tk = &tk_core.timekeeper;
|
||||
struct clocksource *clock = tk->tkr.clock;
|
||||
|
|
@ -1251,7 +1278,7 @@ static void timekeeping_resume(void)
|
|||
hrtimers_resume();
|
||||
}
|
||||
|
||||
static int timekeeping_suspend(void)
|
||||
int timekeeping_suspend(void)
|
||||
{
|
||||
struct timekeeper *tk = &tk_core.timekeeper;
|
||||
unsigned long flags;
|
||||
|
|
@ -1296,6 +1323,7 @@ static int timekeeping_suspend(void)
|
|||
}
|
||||
|
||||
timekeeping_update(tk, TK_MIRROR);
|
||||
halt_fast_timekeeper(tk);
|
||||
write_seqcount_end(&tk_core.seq);
|
||||
raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,5 +16,7 @@ extern int timekeeping_inject_offset(struct timespec *ts);
|
|||
extern s32 timekeeping_get_tai_offset(void);
|
||||
extern void timekeeping_set_tai_offset(s32 tai_offset);
|
||||
extern void timekeeping_clocktai(struct timespec *ts);
|
||||
extern int timekeeping_suspend(void);
|
||||
extern void timekeeping_resume(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue