[PATCH] add rcu_barrier() synchronization point
authorDipankar Sarma <dipankar@in.ibm.com>
Mon, 12 Dec 2005 08:37:05 +0000 (00:37 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 12 Dec 2005 16:57:42 +0000 (08:57 -0800)
This introduces a new interface - rcu_barrier() which waits until all
the RCUs queued until this call have been completed.

Reiser4 needs this, because we do more than just freeing memory object
in our RCU callback: we also remove it from the list hanging off
super-block.  This means, that before freeing reiser4-specific portion
of super-block (during umount) we have to wait until all pending RCU
callbacks are executed.

The only change of reiser4 made to the original patch, is exporting of
rcu_barrier().

Cc: Hans Reiser <reiser@namesys.com>
Cc: Vladimir V. Saveliev <vs@namesys.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
include/linux/rcupdate.h
kernel/rcupdate.c

index cce25591eec2076dae54c0d803e18298935d638a..a471f3bb713ee1045fb0cddb71d2dc9fcacb54ab 100644 (file)
@@ -100,6 +100,7 @@ struct rcu_data {
        struct rcu_head *donelist;
        struct rcu_head **donetail;
        int cpu;
+       struct rcu_head barrier;
 };
 
 DECLARE_PER_CPU(struct rcu_data, rcu_data);
@@ -285,6 +286,7 @@ extern void FASTCALL(call_rcu_bh(struct rcu_head *head,
 extern __deprecated_for_modules void synchronize_kernel(void);
 extern void synchronize_rcu(void);
 void synchronize_idle(void);
+extern void rcu_barrier(void);
 
 #endif /* __KERNEL__ */
 #endif /* __LINUX_RCUPDATE_H */
index c4d159a21e042cc184a6d04e64b7b2273dfb5872..f45b91723dc65f6ba177e4034ef67b2fbacbd5e5 100644 (file)
@@ -116,6 +116,10 @@ void fastcall call_rcu(struct rcu_head *head,
        local_irq_restore(flags);
 }
 
+static atomic_t rcu_barrier_cpu_count;
+static struct semaphore rcu_barrier_sema;
+static struct completion rcu_barrier_completion;
+
 /**
  * call_rcu_bh - Queue an RCU for invocation after a quicker grace period.
  * @head: structure to be used for queueing the RCU updates.
@@ -162,6 +166,42 @@ long rcu_batches_completed(void)
        return rcu_ctrlblk.completed;
 }
 
+static void rcu_barrier_callback(struct rcu_head *notused)
+{
+       if (atomic_dec_and_test(&rcu_barrier_cpu_count))
+               complete(&rcu_barrier_completion);
+}
+
+/*
+ * Called with preemption disabled, and from cross-cpu IRQ context.
+ */
+static void rcu_barrier_func(void *notused)
+{
+       int cpu = smp_processor_id();
+       struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
+       struct rcu_head *head;
+
+       head = &rdp->barrier;
+       atomic_inc(&rcu_barrier_cpu_count);
+       call_rcu(head, rcu_barrier_callback);
+}
+
+/**
+ * rcu_barrier - Wait until all the in-flight RCUs are complete.
+ */
+void rcu_barrier(void)
+{
+       BUG_ON(in_interrupt());
+       /* Take cpucontrol semaphore to protect against CPU hotplug */
+       down(&rcu_barrier_sema);
+       init_completion(&rcu_barrier_completion);
+       atomic_set(&rcu_barrier_cpu_count, 0);
+       on_each_cpu(rcu_barrier_func, NULL, 0, 1);
+       wait_for_completion(&rcu_barrier_completion);
+       up(&rcu_barrier_sema);
+}
+EXPORT_SYMBOL_GPL(rcu_barrier);
+
 /*
  * Invoke the completed RCU callbacks. They are expected to be in
  * a per-cpu list.
@@ -457,6 +497,7 @@ static struct notifier_block __devinitdata rcu_nb = {
  */
 void __init rcu_init(void)
 {
+       sema_init(&rcu_barrier_sema, 1);
        rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE,
                        (void *)(long)smp_processor_id());
        /* Register notifier for non-boot CPUs */