aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@linaro.org>2014-03-03 02:48:50 -0500
committerIngo Molnar <mingo@kernel.org>2014-03-11 06:52:45 -0400
commit907e30f1bb4a9656d351aa705c1e5931da908701 (patch)
treead5f47cbc477986bb2bc5b0a111053f276d3f82b
parentd27c8438eeedb6e1367b592c101e3c87cca0b50f (diff)
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: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Nicolas Pitre <nico@linaro.org> Signed-off-by: Peter 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.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--drivers/cpuidle/cpuidle.c96
-rw-r--r--include/linux/cpuidle.h19
2 files changed, 94 insertions, 21 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 09d05ab262be..1506d69b3f0f 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -65,6 +65,26 @@ int cpuidle_play_dead(void)
65} 65}
66 66
67/** 67/**
68 * cpuidle_enabled - check if the cpuidle framework is ready
69 * @dev: cpuidle device for this cpu
70 * @drv: cpuidle driver for this cpu
71 *
72 * Return 0 on success, otherwise:
73 * -NODEV : the cpuidle framework is not available
74 * -EBUSY : the cpuidle framework is not initialized
75 */
76int cpuidle_enabled(struct cpuidle_driver *drv, struct cpuidle_device *dev)
77{
78 if (off || !initialized)
79 return -ENODEV;
80
81 if (!drv || !dev || !dev->enabled)
82 return -EBUSY;
83
84 return 0;
85}
86
87/**
68 * cpuidle_enter_state - enter the state and update stats 88 * cpuidle_enter_state - enter the state and update stats
69 * @dev: cpuidle device for this cpu 89 * @dev: cpuidle device for this cpu
70 * @drv: cpuidle driver for this cpu 90 * @drv: cpuidle driver for this cpu
@@ -108,6 +128,51 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
108} 128}
109 129
110/** 130/**
131 * cpuidle_select - ask the cpuidle framework to choose an idle state
132 *
133 * @drv: the cpuidle driver
134 * @dev: the cpuidle device
135 *
136 * Returns the index of the idle state.
137 */
138int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
139{
140 return cpuidle_curr_governor->select(drv, dev);
141}
142
143/**
144 * cpuidle_enter - enter into the specified idle state
145 *
146 * @drv: the cpuidle driver tied with the cpu
147 * @dev: the cpuidle device
148 * @index: the index in the idle state table
149 *
150 * Returns the index in the idle state, < 0 in case of error.
151 * The error code depends on the backend driver
152 */
153int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
154 int index)
155{
156 if (cpuidle_state_is_coupled(dev, drv, index))
157 return cpuidle_enter_state_coupled(dev, drv, index);
158 return cpuidle_enter_state(dev, drv, index);
159}
160
161/**
162 * cpuidle_reflect - tell the underlying governor what was the state
163 * we were in
164 *
165 * @dev : the cpuidle device
166 * @index: the index in the idle state table
167 *
168 */
169void cpuidle_reflect(struct cpuidle_device *dev, int index)
170{
171 if (cpuidle_curr_governor->reflect)
172 cpuidle_curr_governor->reflect(dev, index);
173}
174
175/**
111 * cpuidle_idle_call - the main idle loop 176 * cpuidle_idle_call - the main idle loop
112 * 177 *
113 * NOTE: no locks or semaphores should be used here 178 * NOTE: no locks or semaphores should be used here
@@ -116,26 +181,21 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
116int cpuidle_idle_call(void) 181int cpuidle_idle_call(void)
117{ 182{
118 struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); 183 struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
119 struct cpuidle_driver *drv; 184 struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
120 int next_state, entered_state; 185 int next_state, entered_state, ret;
121 bool broadcast; 186 bool broadcast;
122 187
123 if (off || !initialized) 188 ret = cpuidle_enabled(drv, dev);
124 return -ENODEV; 189 if (ret < 0)
125 190 return ret;
126 /* check if the device is ready */
127 if (!dev || !dev->enabled)
128 return -EBUSY;
129
130 drv = cpuidle_get_cpu_driver(dev);
131 191
132 /* ask the governor for the next state */ 192 /* ask the governor for the next state */
133 next_state = cpuidle_curr_governor->select(drv, dev); 193 next_state = cpuidle_select(drv, dev);
194
134 if (need_resched()) { 195 if (need_resched()) {
135 dev->last_residency = 0; 196 dev->last_residency = 0;
136 /* give the governor an opportunity to reflect on the outcome */ 197 /* give the governor an opportunity to reflect on the outcome */
137 if (cpuidle_curr_governor->reflect) 198 cpuidle_reflect(dev, next_state);
138 cpuidle_curr_governor->reflect(dev, next_state);
139 local_irq_enable(); 199 local_irq_enable();
140 return 0; 200 return 0;
141 } 201 }
@@ -146,14 +206,9 @@ int cpuidle_idle_call(void)
146 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu)) 206 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu))
147 return -EBUSY; 207 return -EBUSY;
148 208
149
150 trace_cpu_idle_rcuidle(next_state, dev->cpu); 209 trace_cpu_idle_rcuidle(next_state, dev->cpu);
151 210
152 if (cpuidle_state_is_coupled(dev, drv, next_state)) 211 entered_state = cpuidle_enter(drv, dev, next_state);
153 entered_state = cpuidle_enter_state_coupled(dev, drv,
154 next_state);
155 else
156 entered_state = cpuidle_enter_state(dev, drv, next_state);
157 212
158 trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); 213 trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
159 214
@@ -161,8 +216,7 @@ int cpuidle_idle_call(void)
161 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); 216 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
162 217
163 /* give the governor an opportunity to reflect on the outcome */ 218 /* give the governor an opportunity to reflect on the outcome */
164 if (cpuidle_curr_governor->reflect) 219 cpuidle_reflect(dev, entered_state);
165 cpuidle_curr_governor->reflect(dev, entered_state);
166 220
167 return 0; 221 return 0;
168} 222}
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 50fcbb0ac4e7..accc2dd72049 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -119,6 +119,15 @@ struct cpuidle_driver {
119 119
120#ifdef CONFIG_CPU_IDLE 120#ifdef CONFIG_CPU_IDLE
121extern void disable_cpuidle(void); 121extern void disable_cpuidle(void);
122
123extern int cpuidle_enabled(struct cpuidle_driver *drv,
124 struct cpuidle_device *dev);
125extern int cpuidle_select(struct cpuidle_driver *drv,
126 struct cpuidle_device *dev);
127extern int cpuidle_enter(struct cpuidle_driver *drv,
128 struct cpuidle_device *dev, int index);
129extern void cpuidle_reflect(struct cpuidle_device *dev, int index);
130
122extern int cpuidle_idle_call(void); 131extern int cpuidle_idle_call(void);
123extern int cpuidle_register_driver(struct cpuidle_driver *drv); 132extern int cpuidle_register_driver(struct cpuidle_driver *drv);
124extern struct cpuidle_driver *cpuidle_get_driver(void); 133extern struct cpuidle_driver *cpuidle_get_driver(void);
@@ -141,6 +150,16 @@ extern int cpuidle_play_dead(void);
141extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); 150extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
142#else 151#else
143static inline void disable_cpuidle(void) { } 152static inline void disable_cpuidle(void) { }
153static inline int cpuidle_enabled(struct cpuidle_driver *drv,
154 struct cpuidle_device *dev)
155{return -ENODEV; }
156static inline int cpuidle_select(struct cpuidle_driver *drv,
157 struct cpuidle_device *dev)
158{return -ENODEV; }
159static inline int cpuidle_enter(struct cpuidle_driver *drv,
160 struct cpuidle_device *dev, int index)
161{return -ENODEV; }
162static inline void cpuidle_reflect(struct cpuidle_device *dev, int index) { }
144static inline int cpuidle_idle_call(void) { return -ENODEV; } 163static inline int cpuidle_idle_call(void) { return -ENODEV; }
145static inline int cpuidle_register_driver(struct cpuidle_driver *drv) 164static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
146{return -ENODEV; } 165{return -ENODEV; }