Commit 907e30f1 authored by Daniel Lezcano's avatar Daniel Lezcano Committed by Ingo Molnar

idle/cpuidle: Split cpuidle_idle_call main function into smaller functions

In order to allow better integration between the cpuidle framework and the
scheduler, reducing the distance between these two sub-components will
facilitate this integration by moving part of the cpuidle code in the idle
task file and, because idle.c is in the sched directory, we have access to
the scheduler's private structures.

This patch splits the cpuidle_idle_call main entry function into 3 calls
to a newly added API:

 1. select the idle state
 2. enter the idle state
 3. reflect the idle state

The cpuidle_idle_call calls these three functions to implement the main
idle entry function.
Signed-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: default avatarNicolas Pitre <nico@linaro.org>
Signed-off-by: default avatarPeter Zijlstra <peterz@infradead.org>
Cc: rjw@rjwysocki.net
Cc: preeti@linux.vnet.ibm.com
Link: http://lkml.kernel.org/r/1393832934-11625-1-git-send-email-daniel.lezcano@linaro.orgSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent d27c8438
...@@ -64,6 +64,26 @@ int cpuidle_play_dead(void) ...@@ -64,6 +64,26 @@ int cpuidle_play_dead(void)
return -ENODEV; return -ENODEV;
} }
/**
* cpuidle_enabled - check if the cpuidle framework is ready
* @dev: cpuidle device for this cpu
* @drv: cpuidle driver for this cpu
*
* Return 0 on success, otherwise:
* -NODEV : the cpuidle framework is not available
* -EBUSY : the cpuidle framework is not initialized
*/
int cpuidle_enabled(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
if (off || !initialized)
return -ENODEV;
if (!drv || !dev || !dev->enabled)
return -EBUSY;
return 0;
}
/** /**
* cpuidle_enter_state - enter the state and update stats * cpuidle_enter_state - enter the state and update stats
* @dev: cpuidle device for this cpu * @dev: cpuidle device for this cpu
...@@ -107,6 +127,51 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, ...@@ -107,6 +127,51 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
return entered_state; return entered_state;
} }
/**
* cpuidle_select - ask the cpuidle framework to choose an idle state
*
* @drv: the cpuidle driver
* @dev: the cpuidle device
*
* Returns the index of the idle state.
*/
int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
return cpuidle_curr_governor->select(drv, dev);
}
/**
* cpuidle_enter - enter into the specified idle state
*
* @drv: the cpuidle driver tied with the cpu
* @dev: the cpuidle device
* @index: the index in the idle state table
*
* Returns the index in the idle state, < 0 in case of error.
* The error code depends on the backend driver
*/
int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
int index)
{
if (cpuidle_state_is_coupled(dev, drv, index))
return cpuidle_enter_state_coupled(dev, drv, index);
return cpuidle_enter_state(dev, drv, index);
}
/**
* cpuidle_reflect - tell the underlying governor what was the state
* we were in
*
* @dev : the cpuidle device
* @index: the index in the idle state table
*
*/
void cpuidle_reflect(struct cpuidle_device *dev, int index)
{
if (cpuidle_curr_governor->reflect)
cpuidle_curr_governor->reflect(dev, index);
}
/** /**
* cpuidle_idle_call - the main idle loop * cpuidle_idle_call - the main idle loop
* *
...@@ -116,26 +181,21 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, ...@@ -116,26 +181,21 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
int cpuidle_idle_call(void) int cpuidle_idle_call(void)
{ {
struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
struct cpuidle_driver *drv; struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
int next_state, entered_state; int next_state, entered_state, ret;
bool broadcast; bool broadcast;
if (off || !initialized) ret = cpuidle_enabled(drv, dev);
return -ENODEV; if (ret < 0)
return ret;
/* check if the device is ready */
if (!dev || !dev->enabled)
return -EBUSY;
drv = cpuidle_get_cpu_driver(dev);
/* ask the governor for the next state */ /* ask the governor for the next state */
next_state = cpuidle_curr_governor->select(drv, dev); next_state = cpuidle_select(drv, dev);
if (need_resched()) { if (need_resched()) {
dev->last_residency = 0; dev->last_residency = 0;
/* give the governor an opportunity to reflect on the outcome */ /* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect) cpuidle_reflect(dev, next_state);
cpuidle_curr_governor->reflect(dev, next_state);
local_irq_enable(); local_irq_enable();
return 0; return 0;
} }
...@@ -146,14 +206,9 @@ int cpuidle_idle_call(void) ...@@ -146,14 +206,9 @@ int cpuidle_idle_call(void)
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu)) clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu))
return -EBUSY; return -EBUSY;
trace_cpu_idle_rcuidle(next_state, dev->cpu); trace_cpu_idle_rcuidle(next_state, dev->cpu);
if (cpuidle_state_is_coupled(dev, drv, next_state)) entered_state = cpuidle_enter(drv, dev, next_state);
entered_state = cpuidle_enter_state_coupled(dev, drv,
next_state);
else
entered_state = cpuidle_enter_state(dev, drv, next_state);
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
...@@ -161,8 +216,7 @@ int cpuidle_idle_call(void) ...@@ -161,8 +216,7 @@ int cpuidle_idle_call(void)
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
/* give the governor an opportunity to reflect on the outcome */ /* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect) cpuidle_reflect(dev, entered_state);
cpuidle_curr_governor->reflect(dev, entered_state);
return 0; return 0;
} }
......
...@@ -119,6 +119,15 @@ struct cpuidle_driver { ...@@ -119,6 +119,15 @@ struct cpuidle_driver {
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
extern void disable_cpuidle(void); extern void disable_cpuidle(void);
extern int cpuidle_enabled(struct cpuidle_driver *drv,
struct cpuidle_device *dev);
extern int cpuidle_select(struct cpuidle_driver *drv,
struct cpuidle_device *dev);
extern int cpuidle_enter(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int index);
extern void cpuidle_reflect(struct cpuidle_device *dev, int index);
extern int cpuidle_idle_call(void); extern int cpuidle_idle_call(void);
extern int cpuidle_register_driver(struct cpuidle_driver *drv); extern int cpuidle_register_driver(struct cpuidle_driver *drv);
extern struct cpuidle_driver *cpuidle_get_driver(void); extern struct cpuidle_driver *cpuidle_get_driver(void);
...@@ -141,6 +150,16 @@ extern int cpuidle_play_dead(void); ...@@ -141,6 +150,16 @@ extern int cpuidle_play_dead(void);
extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
#else #else
static inline void disable_cpuidle(void) { } static inline void disable_cpuidle(void) { }
static inline int cpuidle_enabled(struct cpuidle_driver *drv,
struct cpuidle_device *dev)
{return -ENODEV; }
static inline int cpuidle_select(struct cpuidle_driver *drv,
struct cpuidle_device *dev)
{return -ENODEV; }
static inline int cpuidle_enter(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int index)
{return -ENODEV; }
static inline void cpuidle_reflect(struct cpuidle_device *dev, int index) { }
static inline int cpuidle_idle_call(void) { return -ENODEV; } static inline int cpuidle_idle_call(void) { return -ENODEV; }
static inline int cpuidle_register_driver(struct cpuidle_driver *drv) static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
{return -ENODEV; } {return -ENODEV; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment