debuggers.hg

changeset 22810:75b6287626ee

rcupdate: Make rcu_barrier() more paranoia-proof

I'm not sure my original barrier function is correct. It may allow a
CPU to exit the barrier loop, with no local work to do, while RCU work
is pending on other CPUs and needing one or more quiescent periods to
flush the work through.

Although rcu_pending() may handle this, it is easiest to follow
Linux's example and simply call_rcu() a callback function on every
CPU. When the callback has executed on every CPU, we know that all
previously-queued RCU work is completed, and we can exit the barrier.

Signed-off-by: Keir Fraser <keir@xen.org>
author Keir Fraser <keir@xen.org>
date Fri Jan 14 16:38:51 2011 +0000 (2011-01-14)
parents 3ce532e56efd
children 7f6d529b5f4f
files xen/common/rcupdate.c
line diff
     1.1 --- a/xen/common/rcupdate.c	Fri Jan 14 15:47:01 2011 +0000
     1.2 +++ b/xen/common/rcupdate.c	Fri Jan 14 16:38:51 2011 +0000
     1.3 @@ -61,16 +61,34 @@ static int qhimark = 10000;
     1.4  static int qlowmark = 100;
     1.5  static int rsinterval = 1000;
     1.6  
     1.7 -static int rcu_barrier_action(void *unused)
     1.8 +struct rcu_barrier_data {
     1.9 +    struct rcu_head head;
    1.10 +    atomic_t *cpu_count;
    1.11 +};
    1.12 +
    1.13 +static void rcu_barrier_callback(struct rcu_head *head)
    1.14  {
    1.15 -    unsigned int cpu = smp_processor_id();
    1.16 +    struct rcu_barrier_data *data = container_of(
    1.17 +        head, struct rcu_barrier_data, head);
    1.18 +    atomic_inc(data->cpu_count);
    1.19 +}
    1.20 +
    1.21 +static int rcu_barrier_action(void *_cpu_count)
    1.22 +{
    1.23 +    struct rcu_barrier_data data = { .cpu_count = _cpu_count };
    1.24  
    1.25      ASSERT(!local_irq_is_enabled());
    1.26      local_irq_enable();
    1.27  
    1.28 -    while ( rcu_needs_cpu(cpu) )
    1.29 +    /*
    1.30 +     * When callback is executed, all previously-queued RCU work on this CPU
    1.31 +     * is completed. When all CPUs have executed their callback, data.cpu_count
    1.32 +     * will have been incremented to include every online CPU.
    1.33 +     */
    1.34 +    call_rcu(&data.head, rcu_barrier_callback);
    1.35 +
    1.36 +    while ( atomic_read(data.cpu_count) != cpus_weight(cpu_online_map) )
    1.37      {
    1.38 -        rcu_check_callbacks(cpu);
    1.39          process_pending_softirqs();
    1.40          cpu_relax();
    1.41      }
    1.42 @@ -82,7 +100,8 @@ static int rcu_barrier_action(void *unus
    1.43  
    1.44  int rcu_barrier(void)
    1.45  {
    1.46 -    return stop_machine_run(rcu_barrier_action, NULL, NR_CPUS);
    1.47 +    atomic_t cpu_count = ATOMIC_INIT(0);
    1.48 +    return stop_machine_run(rcu_barrier_action, &cpu_count, NR_CPUS);
    1.49  }
    1.50  
    1.51  static void force_quiescent_state(struct rcu_data *rdp,