Skip to content

Commit c55b51a

Browse files
dlezcanorafaeljw
authored andcommitted
cpuidle: Allow idle injection to apply exit latency limit
In some cases it may be useful to specify an exit latency limit for the idle state to be used during CPU idle time injection. Instead of duplicating the information in struct cpuidle_device or propagating the latency limit in the call stack, replace the use_deepest_state field with forced_latency_limit_ns to represent that limit, so that the deepest idle state with exit latency within that limit is forced (i.e. no governors) when it is set. A zero exit latency limit for forced idle means to use governors in the usual way (analogous to use_deepest_state equal to "false" before this change). Additionally, add play_idle_precise() taking two arguments, the duration of forced idle and the idle state exit latency limit, both in nanoseconds, and redefine play_idle() as a wrapper around that new function. This change is preparatory, no functional impact is expected. Suggested-by: Rafael J. Wysocki <[email protected]> Signed-off-by: Daniel Lezcano <[email protected]> [ rjw: Subject, changelog, cpuidle_use_deepest_state() kerneldoc, whitespace ] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent cbda56d commit c55b51a

File tree

4 files changed

+23
-17
lines changed

4 files changed

+23
-17
lines changed

drivers/cpuidle/cpuidle.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,21 @@ static int find_deepest_state(struct cpuidle_driver *drv,
9999
}
100100

101101
/**
102-
* cpuidle_use_deepest_state - Set/clear governor override flag.
103-
* @enable: New value of the flag.
102+
* cpuidle_use_deepest_state - Set/unset governor override mode.
103+
* @latency_limit_ns: Idle state exit latency limit (or no override if 0).
104104
*
105-
* Set/unset the current CPU to use the deepest idle state (override governors
106-
* going forward if set).
105+
* If @latency_limit_ns is nonzero, set the current CPU to use the deepest idle
106+
* state with exit latency within @latency_limit_ns (override governors going
107+
* forward), or do not override governors if it is zero.
107108
*/
108-
void cpuidle_use_deepest_state(bool enable)
109+
void cpuidle_use_deepest_state(u64 latency_limit_ns)
109110
{
110111
struct cpuidle_device *dev;
111112

112113
preempt_disable();
113114
dev = cpuidle_get_device();
114115
if (dev)
115-
dev->use_deepest_state = enable;
116+
dev->forced_idle_latency_limit_ns = latency_limit_ns;
116117
preempt_enable();
117118
}
118119

include/linux/cpu.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,12 @@ void arch_cpu_idle_dead(void);
179179
int cpu_report_state(int cpu);
180180
int cpu_check_up_prepare(int cpu);
181181
void cpu_set_state_online(int cpu);
182-
void play_idle(unsigned long duration_us);
182+
void play_idle_precise(u64 duration_ns, u64 latency_ns);
183+
184+
static inline void play_idle(unsigned long duration_us)
185+
{
186+
play_idle_precise(duration_us * NSEC_PER_USEC, U64_MAX);
187+
}
183188

184189
#ifdef CONFIG_HOTPLUG_CPU
185190
bool cpu_wait_death(unsigned int cpu, int seconds);

include/linux/cpuidle.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,14 @@ struct cpuidle_driver_kobj;
8585
struct cpuidle_device {
8686
unsigned int registered:1;
8787
unsigned int enabled:1;
88-
unsigned int use_deepest_state:1;
8988
unsigned int poll_time_limit:1;
9089
unsigned int cpu;
9190
ktime_t next_hrtimer;
9291

9392
int last_state_idx;
9493
u64 last_residency_ns;
9594
u64 poll_limit_ns;
95+
u64 forced_idle_latency_limit_ns;
9696
struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX];
9797
struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
9898
struct cpuidle_driver_kobj *kobj_driver;
@@ -216,15 +216,15 @@ extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
216216
struct cpuidle_device *dev);
217217
extern int cpuidle_enter_s2idle(struct cpuidle_driver *drv,
218218
struct cpuidle_device *dev);
219-
extern void cpuidle_use_deepest_state(bool enable);
219+
extern void cpuidle_use_deepest_state(u64 latency_limit_ns);
220220
#else
221221
static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
222222
struct cpuidle_device *dev)
223223
{return -ENODEV; }
224224
static inline int cpuidle_enter_s2idle(struct cpuidle_driver *drv,
225225
struct cpuidle_device *dev)
226226
{return -ENODEV; }
227-
static inline void cpuidle_use_deepest_state(bool enable)
227+
static inline void cpuidle_use_deepest_state(u64 latency_limit_ns)
228228
{
229229
}
230230
#endif

kernel/sched/idle.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ static void cpuidle_idle_call(void)
165165
* until a proper wakeup interrupt happens.
166166
*/
167167

168-
if (idle_should_enter_s2idle() || dev->use_deepest_state) {
168+
if (idle_should_enter_s2idle() || dev->forced_idle_latency_limit_ns) {
169169
if (idle_should_enter_s2idle()) {
170170
rcu_idle_enter();
171171

@@ -311,7 +311,7 @@ static enum hrtimer_restart idle_inject_timer_fn(struct hrtimer *timer)
311311
return HRTIMER_NORESTART;
312312
}
313313

314-
void play_idle(unsigned long duration_us)
314+
void play_idle_precise(u64 duration_ns, u64 latency_ns)
315315
{
316316
struct idle_timer it;
317317

@@ -323,29 +323,29 @@ void play_idle(unsigned long duration_us)
323323
WARN_ON_ONCE(current->nr_cpus_allowed != 1);
324324
WARN_ON_ONCE(!(current->flags & PF_KTHREAD));
325325
WARN_ON_ONCE(!(current->flags & PF_NO_SETAFFINITY));
326-
WARN_ON_ONCE(!duration_us);
326+
WARN_ON_ONCE(!duration_ns);
327327

328328
rcu_sleep_check();
329329
preempt_disable();
330330
current->flags |= PF_IDLE;
331-
cpuidle_use_deepest_state(true);
331+
cpuidle_use_deepest_state(latency_ns);
332332

333333
it.done = 0;
334334
hrtimer_init_on_stack(&it.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
335335
it.timer.function = idle_inject_timer_fn;
336-
hrtimer_start(&it.timer, ns_to_ktime(duration_us * NSEC_PER_USEC),
336+
hrtimer_start(&it.timer, ns_to_ktime(duration_ns),
337337
HRTIMER_MODE_REL_PINNED);
338338

339339
while (!READ_ONCE(it.done))
340340
do_idle();
341341

342-
cpuidle_use_deepest_state(false);
342+
cpuidle_use_deepest_state(0);
343343
current->flags &= ~PF_IDLE;
344344

345345
preempt_fold_need_resched();
346346
preempt_enable();
347347
}
348-
EXPORT_SYMBOL_GPL(play_idle);
348+
EXPORT_SYMBOL_GPL(play_idle_precise);
349349

350350
void cpu_startup_entry(enum cpuhp_state state)
351351
{

0 commit comments

Comments
 (0)