PM: cpu: Make notifier chain use a raw_spinlock_t
[linux-2.6-microblaze.git] / kernel / cpu_pm.c
index f7e1d0e..246efc7 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/syscore_ops.h>
 
-static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain);
+/*
+ * atomic_notifiers use a spinlock_t, which can block under PREEMPT_RT.
+ * Notifications for cpu_pm will be issued by the idle task itself, which can
+ * never block, IOW it requires using a raw_spinlock_t.
+ */
+static struct {
+       struct raw_notifier_head chain;
+       raw_spinlock_t lock;
+} cpu_pm_notifier = {
+       .chain = RAW_NOTIFIER_INIT(cpu_pm_notifier.chain),
+       .lock  = __RAW_SPIN_LOCK_UNLOCKED(cpu_pm_notifier.lock),
+};
 
 static int cpu_pm_notify(enum cpu_pm_event event)
 {
        int ret;
 
        /*
-        * atomic_notifier_call_chain has a RCU read critical section, which
-        * could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let
-        * RCU know this.
+        * This introduces a RCU read critical section, which could be
+        * disfunctional in cpu idle. Copy RCU_NONIDLE code to let RCU know
+        * this.
         */
        rcu_irq_enter_irqson();
-       ret = atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL);
+       rcu_read_lock();
+       ret = raw_notifier_call_chain(&cpu_pm_notifier.chain, event, NULL);
+       rcu_read_unlock();
        rcu_irq_exit_irqson();
 
        return notifier_to_errno(ret);
@@ -33,10 +46,13 @@ static int cpu_pm_notify(enum cpu_pm_event event)
 
 static int cpu_pm_notify_robust(enum cpu_pm_event event_up, enum cpu_pm_event event_down)
 {
+       unsigned long flags;
        int ret;
 
        rcu_irq_enter_irqson();
-       ret = atomic_notifier_call_chain_robust(&cpu_pm_notifier_chain, event_up, event_down, NULL);
+       raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
+       ret = raw_notifier_call_chain_robust(&cpu_pm_notifier.chain, event_up, event_down, NULL);
+       raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
        rcu_irq_exit_irqson();
 
        return notifier_to_errno(ret);
@@ -49,12 +65,17 @@ static int cpu_pm_notify_robust(enum cpu_pm_event event_up, enum cpu_pm_event ev
  * Add a driver to a list of drivers that are notified about
  * CPU and CPU cluster low power entry and exit.
  *
- * This function may sleep, and has the same return conditions as
- * raw_notifier_chain_register.
+ * This function has the same return conditions as raw_notifier_chain_register.
  */
 int cpu_pm_register_notifier(struct notifier_block *nb)
 {
-       return atomic_notifier_chain_register(&cpu_pm_notifier_chain, nb);
+       unsigned long flags;
+       int ret;
+
+       raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
+       ret = raw_notifier_chain_register(&cpu_pm_notifier.chain, nb);
+       raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
 
@@ -64,12 +85,17 @@ EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
  *
  * Remove a driver from the CPU PM notifier list.
  *
- * This function may sleep, and has the same return conditions as
- * raw_notifier_chain_unregister.
+ * This function has the same return conditions as raw_notifier_chain_unregister.
  */
 int cpu_pm_unregister_notifier(struct notifier_block *nb)
 {
-       return atomic_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
+       unsigned long flags;
+       int ret;
+
+       raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
+       ret = raw_notifier_chain_unregister(&cpu_pm_notifier.chain, nb);
+       raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);