Skip to content

Commit acbc661

Browse files
spandruvadarafaeljw
authored andcommitted
powercap: idle_inject: Add update callback
The powercap/idle_inject core uses play_idle_precise() to inject idle time. But play_idle_precise() can't ensure that the CPU is fully idle for the specified duration because of wakeups due to interrupts. To compensate for the reduced idle time due to these wakes, the caller can adjust requested idle time for the next cycle. The goal of idle injection is to keep system at some idle percent on average, so this is fine to overshoot or undershoot instantaneous idle times. The idle inject core provides an interface idle_inject_set_duration() to set idle and runtime duration. Some architectures provide interface to get actual idle time observed by the hardware. So, the effective idle percent can be adjusted using the hardware feedback. For example, Intel CPUs provides package idle counters, which is currently used by Intel powerclamp driver to readjust runtime duration. When the caller's desired idle time over a period is less or greater than the actual CPU idle time observed by the hardware, caller can readjust idle and runtime duration for the next cycle. The only way this can be done currently is by monitoring hardware idle time from a different software thread and readjust idle and runtime duration using idle_inject_set_duration(). This can be avoided by adding a callback which callers can register and readjust from this callback function. Add a capability to register an optional update() callback, which can be called from the idle inject core before waking up CPUs for idle injection. This callback can be registered via a new interface: idle_inject_register_full(). During this process of constantly adjusting idle and runtime duration there can be some cases where actual idle time is more than the desired. In this case idle inject can be skipped for a cycle. If update() callback returns false, then the idle inject core skips waking up CPUs for the idle injection. Signed-off-by: Srinivas Pandruvada <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent bbfc334 commit acbc661

File tree

2 files changed

+49
-6
lines changed

2 files changed

+49
-6
lines changed

drivers/powercap/idle_inject.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,29 @@ struct idle_inject_thread {
6363
* @idle_duration_us: duration of CPU idle time to inject
6464
* @run_duration_us: duration of CPU run time to allow
6565
* @latency_us: max allowed latency
66+
* @update: Optional callback deciding whether or not to skip idle
67+
* injection in the given cycle.
6668
* @cpumask: mask of CPUs affected by idle injection
69+
*
70+
* This structure is used to define per instance idle inject device data. Each
71+
* instance has an idle duration, a run duration and mask of CPUs to inject
72+
* idle.
73+
*
74+
* Actual CPU idle time is injected by calling kernel scheduler interface
75+
* play_idle_precise(). There is one optional callback that can be registered
76+
* by calling idle_inject_register_full():
77+
*
78+
* update() - This callback is invoked just before waking up CPUs to inject
79+
* idle. If it returns false, CPUs are not woken up to inject idle in the given
80+
* cycle. It also allows the caller to readjust the idle and run duration by
81+
* calling idle_inject_set_duration() for the next cycle.
6782
*/
6883
struct idle_inject_device {
6984
struct hrtimer timer;
7085
unsigned int idle_duration_us;
7186
unsigned int run_duration_us;
7287
unsigned int latency_us;
88+
bool (*update)(void);
7389
unsigned long cpumask[];
7490
};
7591

@@ -111,11 +127,12 @@ static enum hrtimer_restart idle_inject_timer_fn(struct hrtimer *timer)
111127
struct idle_inject_device *ii_dev =
112128
container_of(timer, struct idle_inject_device, timer);
113129

130+
if (!ii_dev->update || (ii_dev->update && ii_dev->update()))
131+
idle_inject_wakeup(ii_dev);
132+
114133
duration_us = READ_ONCE(ii_dev->run_duration_us);
115134
duration_us += READ_ONCE(ii_dev->idle_duration_us);
116135

117-
idle_inject_wakeup(ii_dev);
118-
119136
hrtimer_forward_now(timer, ns_to_ktime(duration_us * NSEC_PER_USEC));
120137

121138
return HRTIMER_RESTART;
@@ -295,17 +312,22 @@ static int idle_inject_should_run(unsigned int cpu)
295312
}
296313

297314
/**
298-
* idle_inject_register - initialize idle injection on a set of CPUs
315+
* idle_inject_register_full - initialize idle injection on a set of CPUs
299316
* @cpumask: CPUs to be affected by idle injection
317+
* @update: This callback is called just before waking up CPUs to inject
318+
* idle
300319
*
301320
* This function creates an idle injection control device structure for the
302-
* given set of CPUs and initializes the timer associated with it. It does not
303-
* start any injection cycles.
321+
* given set of CPUs and initializes the timer associated with it. This
322+
* function also allows to register update()callback.
323+
* It does not start any injection cycles.
304324
*
305325
* Return: NULL if memory allocation fails, idle injection control device
306326
* pointer on success.
307327
*/
308-
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
328+
329+
struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
330+
bool (*update)(void))
309331
{
310332
struct idle_inject_device *ii_dev;
311333
int cpu, cpu_rb;
@@ -318,6 +340,7 @@ struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
318340
hrtimer_init(&ii_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
319341
ii_dev->timer.function = idle_inject_timer_fn;
320342
ii_dev->latency_us = UINT_MAX;
343+
ii_dev->update = update;
321344

322345
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask)) {
323346

@@ -342,6 +365,23 @@ struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
342365

343366
return NULL;
344367
}
368+
EXPORT_SYMBOL_NS_GPL(idle_inject_register_full, IDLE_INJECT);
369+
370+
/**
371+
* idle_inject_register - initialize idle injection on a set of CPUs
372+
* @cpumask: CPUs to be affected by idle injection
373+
*
374+
* This function creates an idle injection control device structure for the
375+
* given set of CPUs and initializes the timer associated with it. It does not
376+
* start any injection cycles.
377+
*
378+
* Return: NULL if memory allocation fails, idle injection control device
379+
* pointer on success.
380+
*/
381+
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
382+
{
383+
return idle_inject_register_full(cpumask, NULL);
384+
}
345385
EXPORT_SYMBOL_NS_GPL(idle_inject_register, IDLE_INJECT);
346386

347387
/**

include/linux/idle_inject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ struct idle_inject_device;
1313

1414
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask);
1515

16+
struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
17+
bool (*update)(void));
18+
1619
void idle_inject_unregister(struct idle_inject_device *ii_dev);
1720

1821
int idle_inject_start(struct idle_inject_device *ii_dev);

0 commit comments

Comments
 (0)