Merge branch 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux...
[linux-block.git] / kernel / rcupdate.c
index a967c9feb90a045f4f7f3631dbc93ad5aebf67f9..bd5d5c8e51408343f3067a80611d5d1fed8ca89d 100644 (file)
@@ -98,6 +98,30 @@ void synchronize_rcu(void)
 }
 EXPORT_SYMBOL_GPL(synchronize_rcu);
 
+/**
+ * synchronize_rcu_bh - wait until an rcu_bh grace period has elapsed.
+ *
+ * Control will return to the caller some time after a full rcu_bh grace
+ * period has elapsed, in other words after all currently executing rcu_bh
+ * read-side critical sections have completed.  RCU read-side critical
+ * sections are delimited by rcu_read_lock_bh() and rcu_read_unlock_bh(),
+ * and may be nested.
+ */
+void synchronize_rcu_bh(void)
+{
+       struct rcu_synchronize rcu;
+
+       if (rcu_blocking_is_gp())
+               return;
+
+       init_completion(&rcu.completion);
+       /* Will wake me after RCU finished. */
+       call_rcu_bh(&rcu.head, wakeme_after_rcu);
+       /* Wait for it. */
+       wait_for_completion(&rcu.completion);
+}
+EXPORT_SYMBOL_GPL(synchronize_rcu_bh);
+
 static void rcu_barrier_callback(struct rcu_head *notused)
 {
        if (atomic_dec_and_test(&rcu_barrier_cpu_count))
@@ -129,6 +153,7 @@ static void rcu_barrier_func(void *type)
 static inline void wait_migrated_callbacks(void)
 {
        wait_event(rcu_migrate_wq, !atomic_read(&rcu_migrate_type_count));
+       smp_mb(); /* In case we didn't sleep. */
 }
 
 /*
@@ -192,9 +217,13 @@ static void rcu_migrate_callback(struct rcu_head *notused)
                wake_up(&rcu_migrate_wq);
 }
 
+extern int rcu_cpu_notify(struct notifier_block *self,
+                         unsigned long action, void *hcpu);
+
 static int __cpuinit rcu_barrier_cpu_hotplug(struct notifier_block *self,
                unsigned long action, void *hcpu)
 {
+       rcu_cpu_notify(self, action, hcpu);
        if (action == CPU_DYING) {
                /*
                 * preempt_disable() in on_each_cpu() prevents stop_machine(),
@@ -209,7 +238,8 @@ static int __cpuinit rcu_barrier_cpu_hotplug(struct notifier_block *self,
                call_rcu_bh(rcu_migrate_head, rcu_migrate_callback);
                call_rcu_sched(rcu_migrate_head + 1, rcu_migrate_callback);
                call_rcu(rcu_migrate_head + 2, rcu_migrate_callback);
-       } else if (action == CPU_POST_DEAD) {
+       } else if (action == CPU_DOWN_PREPARE) {
+               /* Don't need to wait until next removal operation. */
                /* rcu_migrate_head is protected by cpu_add_remove_lock */
                wait_migrated_callbacks();
        }
@@ -219,8 +249,18 @@ static int __cpuinit rcu_barrier_cpu_hotplug(struct notifier_block *self,
 
 void __init rcu_init(void)
 {
+       int i;
+
        __rcu_init();
-       hotcpu_notifier(rcu_barrier_cpu_hotplug, 0);
+       cpu_notifier(rcu_barrier_cpu_hotplug, 0);
+
+       /*
+        * We don't need protection against CPU-hotplug here because
+        * this is called early in boot, before either interrupts
+        * or the scheduler are operational.
+        */
+       for_each_online_cpu(i)
+               rcu_barrier_cpu_hotplug(NULL, CPU_UP_PREPARE, (void *)(long)i);
 }
 
 void rcu_scheduler_starting(void)