hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
kernel/drivers/cpuidle/cpuidle.c
....@@ -17,6 +17,7 @@
1717 #include <linux/pm_qos.h>
1818 #include <linux/cpu.h>
1919 #include <linux/cpuidle.h>
20
+#include <linux/irq_pipeline.h>
2021 #include <linux/ktime.h>
2122 #include <linux/hrtimer.h>
2223 #include <linux/module.h>
....@@ -219,6 +220,22 @@
219220 broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
220221
221222 /*
223
+ * A companion core running on the oob stage of the IRQ
224
+ * pipeline may deny switching to a deeper C-state. If so,
225
+ * call the default idle routine instead. If the core cannot
226
+ * bear with the latency induced by the default idling
227
+ * operation, then CPUIDLE is not usable and should be
228
+ * disabled at build time. The in-band stage is currently
229
+ * stalled, hard irqs are on. irq_cpuidle_enter() leaves us
230
+ * stalled but returns with hard irqs off so that no event may
231
+ * sneak in until we actually go idle.
232
+ */
233
+ if (!irq_cpuidle_enter(dev, target_state)) {
234
+ default_idle_call();
235
+ return -EBUSY;
236
+ }
237
+
238
+ /*
222239 * Tell the time framework to switch to a broadcast timer because our
223240 * local timer will be shut down. If a local timer is used from another
224241 * CPU as a broadcast timer, this call may fail if it is not available.
....@@ -247,6 +264,7 @@
247264 if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
248265 rcu_idle_enter();
249266 entered_state = target_state->enter(dev, drv, index);
267
+ hard_cond_local_irq_enable();
250268 if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
251269 rcu_idle_exit();
252270 start_critical_timings();