Commit 05bf58ca4b8f0be7d7af830f943f6d6b2c9ccee1
Exists in
master
and in
13 other branches
Merge branch 'sched-idle-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull sched/idle changes from Ingo Molnar: "More idle code reorganization, to prepare for more integration. (Sent separately because it depended on pending timer work, which is now upstream)" * 'sched-idle-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: sched/idle: Add more comments to the code sched/idle: Move idle conditions in cpuidle_idle main function sched/idle: Reorganize the idle loop cpuidle/idle: Move the cpuidle_idle_call function to idle.c idle/cpuidle: Split cpuidle_idle_call main function into smaller functions
Showing 3 changed files Side-by-side Diff
drivers/cpuidle/cpuidle.c
... | ... | @@ -65,6 +65,26 @@ |
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 | + */ | |
76 | +int 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 | 88 | * cpuidle_enter_state - enter the state and update stats |
69 | 89 | * @dev: cpuidle device for this cpu |
70 | 90 | * @drv: cpuidle driver for this cpu |
71 | 91 | |
72 | 92 | |
73 | 93 | |
74 | 94 | |
75 | 95 | |
76 | 96 | |
... | ... | @@ -109,63 +129,48 @@ |
109 | 129 | } |
110 | 130 | |
111 | 131 | /** |
112 | - * cpuidle_idle_call - the main idle loop | |
132 | + * cpuidle_select - ask the cpuidle framework to choose an idle state | |
113 | 133 | * |
114 | - * NOTE: no locks or semaphores should be used here | |
115 | - * return non-zero on failure | |
134 | + * @drv: the cpuidle driver | |
135 | + * @dev: the cpuidle device | |
136 | + * | |
137 | + * Returns the index of the idle state. | |
116 | 138 | */ |
117 | -int cpuidle_idle_call(void) | |
139 | +int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |
118 | 140 | { |
119 | - struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | |
120 | - struct cpuidle_driver *drv; | |
121 | - int next_state, entered_state; | |
122 | - bool broadcast; | |
141 | + return cpuidle_curr_governor->select(drv, dev); | |
142 | +} | |
123 | 143 | |
124 | - if (off || !initialized) | |
125 | - return -ENODEV; | |
144 | +/** | |
145 | + * cpuidle_enter - enter into the specified idle state | |
146 | + * | |
147 | + * @drv: the cpuidle driver tied with the cpu | |
148 | + * @dev: the cpuidle device | |
149 | + * @index: the index in the idle state table | |
150 | + * | |
151 | + * Returns the index in the idle state, < 0 in case of error. | |
152 | + * The error code depends on the backend driver | |
153 | + */ | |
154 | +int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev, | |
155 | + int index) | |
156 | +{ | |
157 | + if (cpuidle_state_is_coupled(dev, drv, index)) | |
158 | + return cpuidle_enter_state_coupled(dev, drv, index); | |
159 | + return cpuidle_enter_state(dev, drv, index); | |
160 | +} | |
126 | 161 | |
127 | - /* check if the device is ready */ | |
128 | - if (!dev || !dev->enabled) | |
129 | - return -EBUSY; | |
130 | - | |
131 | - drv = cpuidle_get_cpu_driver(dev); | |
132 | - | |
133 | - /* ask the governor for the next state */ | |
134 | - next_state = cpuidle_curr_governor->select(drv, dev); | |
135 | - if (need_resched()) { | |
136 | - dev->last_residency = 0; | |
137 | - /* give the governor an opportunity to reflect on the outcome */ | |
138 | - if (cpuidle_curr_governor->reflect) | |
139 | - cpuidle_curr_governor->reflect(dev, next_state); | |
140 | - local_irq_enable(); | |
141 | - return 0; | |
142 | - } | |
143 | - | |
144 | - broadcast = !!(drv->states[next_state].flags & CPUIDLE_FLAG_TIMER_STOP); | |
145 | - | |
146 | - if (broadcast && | |
147 | - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu)) | |
148 | - return -EBUSY; | |
149 | - | |
150 | - | |
151 | - trace_cpu_idle_rcuidle(next_state, dev->cpu); | |
152 | - | |
153 | - if (cpuidle_state_is_coupled(dev, drv, next_state)) | |
154 | - entered_state = cpuidle_enter_state_coupled(dev, drv, | |
155 | - next_state); | |
156 | - else | |
157 | - entered_state = cpuidle_enter_state(dev, drv, next_state); | |
158 | - | |
159 | - trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); | |
160 | - | |
161 | - if (broadcast) | |
162 | - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); | |
163 | - | |
164 | - /* give the governor an opportunity to reflect on the outcome */ | |
162 | +/** | |
163 | + * cpuidle_reflect - tell the underlying governor what was the state | |
164 | + * we were in | |
165 | + * | |
166 | + * @dev : the cpuidle device | |
167 | + * @index: the index in the idle state table | |
168 | + * | |
169 | + */ | |
170 | +void cpuidle_reflect(struct cpuidle_device *dev, int index) | |
171 | +{ | |
165 | 172 | if (cpuidle_curr_governor->reflect) |
166 | - cpuidle_curr_governor->reflect(dev, entered_state); | |
167 | - | |
168 | - return 0; | |
173 | + cpuidle_curr_governor->reflect(dev, index); | |
169 | 174 | } |
170 | 175 | |
171 | 176 | /** |
include/linux/cpuidle.h
... | ... | @@ -119,7 +119,15 @@ |
119 | 119 | |
120 | 120 | #ifdef CONFIG_CPU_IDLE |
121 | 121 | extern void disable_cpuidle(void); |
122 | -extern int cpuidle_idle_call(void); | |
122 | + | |
123 | +extern int cpuidle_enabled(struct cpuidle_driver *drv, | |
124 | + struct cpuidle_device *dev); | |
125 | +extern int cpuidle_select(struct cpuidle_driver *drv, | |
126 | + struct cpuidle_device *dev); | |
127 | +extern int cpuidle_enter(struct cpuidle_driver *drv, | |
128 | + struct cpuidle_device *dev, int index); | |
129 | +extern void cpuidle_reflect(struct cpuidle_device *dev, int index); | |
130 | + | |
123 | 131 | extern int cpuidle_register_driver(struct cpuidle_driver *drv); |
124 | 132 | extern struct cpuidle_driver *cpuidle_get_driver(void); |
125 | 133 | extern struct cpuidle_driver *cpuidle_driver_ref(void); |
... | ... | @@ -141,7 +149,16 @@ |
141 | 149 | extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); |
142 | 150 | #else |
143 | 151 | static inline void disable_cpuidle(void) { } |
144 | -static inline int cpuidle_idle_call(void) { return -ENODEV; } | |
152 | +static inline int cpuidle_enabled(struct cpuidle_driver *drv, | |
153 | + struct cpuidle_device *dev) | |
154 | +{return -ENODEV; } | |
155 | +static inline int cpuidle_select(struct cpuidle_driver *drv, | |
156 | + struct cpuidle_device *dev) | |
157 | +{return -ENODEV; } | |
158 | +static inline int cpuidle_enter(struct cpuidle_driver *drv, | |
159 | + struct cpuidle_device *dev, int index) | |
160 | +{return -ENODEV; } | |
161 | +static inline void cpuidle_reflect(struct cpuidle_device *dev, int index) { } | |
145 | 162 | static inline int cpuidle_register_driver(struct cpuidle_driver *drv) |
146 | 163 | {return -ENODEV; } |
147 | 164 | static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; } |
... | ... | @@ -163,6 +180,8 @@ |
163 | 180 | {return -ENODEV; } |
164 | 181 | static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } |
165 | 182 | static inline int cpuidle_play_dead(void) {return -ENODEV; } |
183 | +static inline struct cpuidle_driver *cpuidle_get_cpu_driver( | |
184 | + struct cpuidle_device *dev) {return NULL; } | |
166 | 185 | #endif |
167 | 186 | |
168 | 187 | #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED |
kernel/sched/idle.c
... | ... | @@ -63,6 +63,136 @@ |
63 | 63 | local_irq_enable(); |
64 | 64 | } |
65 | 65 | |
66 | +/** | |
67 | + * cpuidle_idle_call - the main idle function | |
68 | + * | |
69 | + * NOTE: no locks or semaphores should be used here | |
70 | + * return non-zero on failure | |
71 | + */ | |
72 | +static int cpuidle_idle_call(void) | |
73 | +{ | |
74 | + struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | |
75 | + struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | |
76 | + int next_state, entered_state, ret; | |
77 | + bool broadcast; | |
78 | + | |
79 | + /* | |
80 | + * Check if the idle task must be rescheduled. If it is the | |
81 | + * case, exit the function after re-enabling the local irq and | |
82 | + * set again the polling flag | |
83 | + */ | |
84 | + if (current_clr_polling_and_test()) { | |
85 | + local_irq_enable(); | |
86 | + __current_set_polling(); | |
87 | + return 0; | |
88 | + } | |
89 | + | |
90 | + /* | |
91 | + * During the idle period, stop measuring the disabled irqs | |
92 | + * critical sections latencies | |
93 | + */ | |
94 | + stop_critical_timings(); | |
95 | + | |
96 | + /* | |
97 | + * Tell the RCU framework we are entering an idle section, | |
98 | + * so no more rcu read side critical sections and one more | |
99 | + * step to the grace period | |
100 | + */ | |
101 | + rcu_idle_enter(); | |
102 | + | |
103 | + /* | |
104 | + * Check if the cpuidle framework is ready, otherwise fallback | |
105 | + * to the default arch specific idle method | |
106 | + */ | |
107 | + ret = cpuidle_enabled(drv, dev); | |
108 | + | |
109 | + if (!ret) { | |
110 | + /* | |
111 | + * Ask the governor to choose an idle state it thinks | |
112 | + * it is convenient to go to. There is *always* a | |
113 | + * convenient idle state | |
114 | + */ | |
115 | + next_state = cpuidle_select(drv, dev); | |
116 | + | |
117 | + /* | |
118 | + * The idle task must be scheduled, it is pointless to | |
119 | + * go to idle, just update no idle residency and get | |
120 | + * out of this function | |
121 | + */ | |
122 | + if (current_clr_polling_and_test()) { | |
123 | + dev->last_residency = 0; | |
124 | + entered_state = next_state; | |
125 | + local_irq_enable(); | |
126 | + } else { | |
127 | + broadcast = !!(drv->states[next_state].flags & | |
128 | + CPUIDLE_FLAG_TIMER_STOP); | |
129 | + | |
130 | + if (broadcast) | |
131 | + /* | |
132 | + * Tell the time framework to switch | |
133 | + * to a broadcast timer because our | |
134 | + * local timer will be shutdown. If a | |
135 | + * local timer is used from another | |
136 | + * cpu as a broadcast timer, this call | |
137 | + * may fail if it is not available | |
138 | + */ | |
139 | + ret = clockevents_notify( | |
140 | + CLOCK_EVT_NOTIFY_BROADCAST_ENTER, | |
141 | + &dev->cpu); | |
142 | + | |
143 | + if (!ret) { | |
144 | + trace_cpu_idle_rcuidle(next_state, dev->cpu); | |
145 | + | |
146 | + /* | |
147 | + * Enter the idle state previously | |
148 | + * returned by the governor | |
149 | + * decision. This function will block | |
150 | + * until an interrupt occurs and will | |
151 | + * take care of re-enabling the local | |
152 | + * interrupts | |
153 | + */ | |
154 | + entered_state = cpuidle_enter(drv, dev, | |
155 | + next_state); | |
156 | + | |
157 | + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, | |
158 | + dev->cpu); | |
159 | + | |
160 | + if (broadcast) | |
161 | + clockevents_notify( | |
162 | + CLOCK_EVT_NOTIFY_BROADCAST_EXIT, | |
163 | + &dev->cpu); | |
164 | + | |
165 | + /* | |
166 | + * Give the governor an opportunity to reflect on the | |
167 | + * outcome | |
168 | + */ | |
169 | + cpuidle_reflect(dev, entered_state); | |
170 | + } | |
171 | + } | |
172 | + } | |
173 | + | |
174 | + /* | |
175 | + * We can't use the cpuidle framework, let's use the default | |
176 | + * idle routine | |
177 | + */ | |
178 | + if (ret) | |
179 | + arch_cpu_idle(); | |
180 | + | |
181 | + __current_set_polling(); | |
182 | + | |
183 | + /* | |
184 | + * It is up to the idle functions to enable back the local | |
185 | + * interrupt | |
186 | + */ | |
187 | + if (WARN_ON_ONCE(irqs_disabled())) | |
188 | + local_irq_enable(); | |
189 | + | |
190 | + rcu_idle_exit(); | |
191 | + start_critical_timings(); | |
192 | + | |
193 | + return 0; | |
194 | +} | |
195 | + | |
66 | 196 | /* |
67 | 197 | * Generic idle loop implementation |
68 | 198 | */ |
69 | 199 | |
... | ... | @@ -90,23 +220,11 @@ |
90 | 220 | * know that the IPI is going to arrive right |
91 | 221 | * away |
92 | 222 | */ |
93 | - if (cpu_idle_force_poll || tick_check_broadcast_expired()) { | |
223 | + if (cpu_idle_force_poll || tick_check_broadcast_expired()) | |
94 | 224 | cpu_idle_poll(); |
95 | - } else { | |
96 | - if (!current_clr_polling_and_test()) { | |
97 | - stop_critical_timings(); | |
98 | - rcu_idle_enter(); | |
99 | - if (cpuidle_idle_call()) | |
100 | - arch_cpu_idle(); | |
101 | - if (WARN_ON_ONCE(irqs_disabled())) | |
102 | - local_irq_enable(); | |
103 | - rcu_idle_exit(); | |
104 | - start_critical_timings(); | |
105 | - } else { | |
106 | - local_irq_enable(); | |
107 | - } | |
108 | - __current_set_polling(); | |
109 | - } | |
225 | + else | |
226 | + cpuidle_idle_call(); | |
227 | + | |
110 | 228 | arch_cpu_idle_exit(); |
111 | 229 | } |
112 | 230 |