提交 4446a36f 编写于 作者: P Paul E. McKenney 提交者: Ingo Molnar

rcu: add call_rcu_sched()

Fourth cut of patch to provide the call_rcu_sched().  This is again to
synchronize_sched() as call_rcu() is to synchronize_rcu().

Should be fine for experimental and -rt use, but not ready for inclusion.
With some luck, I will be able to tell Andrew to come out of hiding on
the next round.

Passes multi-day rcutorture sessions with concurrent CPU hotplugging.

Fixes since the first version include a bug that could result in
indefinite blocking (spotted by Gautham Shenoy), better resiliency
against CPU-hotplug operations, and other minor fixes.

Fixes since the second version include reworking grace-period detection
to avoid deadlocks that could happen when running concurrently with
CPU hotplug, adding Mathieu's fix to avoid the softlockup messages,
as well as Mathieu's fix to allow use earlier in boot.

Fixes since the third version include a wrong-CPU bug spotted by
Andrew, getting rid of the obsolete synchronize_kernel API that somehow
snuck back in, merging spin_unlock() and local_irq_restore() in a
few places, commenting the code that checks for quiescent states based
on interrupting from user-mode execution or the idle loop, removing
some inline attributes, and some code-style changes.

Known/suspected shortcomings:

o	I still do not entirely trust the sleep/wakeup logic.  Next step
	will be to use a private snapshot of the CPU online mask in
	rcu_sched_grace_period() -- if the CPU wasn't there at the start
	of the grace period, we don't need to hear from it.  And the
	bit about accounting for changes in online CPUs inside of
	rcu_sched_grace_period() is ugly anyway.

o	It might be good for rcu_sched_grace_period() to invoke
	resched_cpu() when a given CPU wasn't responding quickly,
	but resched_cpu() is declared static...

This patch also fixes a long-standing bug in the earlier preemptable-RCU
implementation of synchronize_rcu() that could result in loss of
concurrent external changes to a task's CPU affinity mask.  I still cannot
remember who reported this...
Signed-off-by: NPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: NMathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Signed-off-by: NIngo Molnar <mingo@elte.hu>
Signed-off-by: NThomas Gleixner <tglx@linutronix.de>
上级 8b09dee6
...@@ -151,7 +151,10 @@ extern struct lockdep_map rcu_lock_map; ...@@ -151,7 +151,10 @@ extern struct lockdep_map rcu_lock_map;
#define __synchronize_sched() synchronize_rcu() #define __synchronize_sched() synchronize_rcu()
#define call_rcu_sched(head, func) call_rcu(head, func)
extern void __rcu_init(void); extern void __rcu_init(void);
#define rcu_init_sched() do { } while (0)
extern void rcu_check_callbacks(int cpu, int user); extern void rcu_check_callbacks(int cpu, int user);
extern void rcu_restart_cpu(int cpu); extern void rcu_restart_cpu(int cpu);
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/seqlock.h> #include <linux/seqlock.h>
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <linux/completion.h>
/** /**
* struct rcu_head - callback structure for use with RCU * struct rcu_head - callback structure for use with RCU
...@@ -168,6 +169,27 @@ struct rcu_head { ...@@ -168,6 +169,27 @@ struct rcu_head {
(p) = (v); \ (p) = (v); \
}) })
/* Infrastructure to implement the synchronize_() primitives. */
struct rcu_synchronize {
struct rcu_head head;
struct completion completion;
};
extern void wakeme_after_rcu(struct rcu_head *head);
#define synchronize_rcu_xxx(name, func) \
void name(void) \
{ \
struct rcu_synchronize rcu; \
\
init_completion(&rcu.completion); \
/* Will wake me after RCU finished. */ \
func(&rcu.head, wakeme_after_rcu); \
/* Wait for it. */ \
wait_for_completion(&rcu.completion); \
}
/** /**
* synchronize_sched - block until all CPUs have exited any non-preemptive * synchronize_sched - block until all CPUs have exited any non-preemptive
* kernel code sequences. * kernel code sequences.
......
...@@ -40,10 +40,39 @@ ...@@ -40,10 +40,39 @@
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/seqlock.h> #include <linux/seqlock.h>
#define rcu_qsctr_inc(cpu) struct rcu_dyntick_sched {
int dynticks;
int dynticks_snap;
int sched_qs;
int sched_qs_snap;
int sched_dynticks_snap;
};
DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);
static inline void rcu_qsctr_inc(int cpu)
{
struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
rdssp->sched_qs++;
}
#define rcu_bh_qsctr_inc(cpu) #define rcu_bh_qsctr_inc(cpu)
#define call_rcu_bh(head, rcu) call_rcu(head, rcu) #define call_rcu_bh(head, rcu) call_rcu(head, rcu)
/**
* call_rcu_sched - Queue RCU callback for invocation after sched grace period.
* @head: structure to be used for queueing the RCU updates.
* @func: actual update function to be invoked after the grace period
*
* The update function will be invoked some time after a full
* synchronize_sched()-style grace period elapses, in other words after
* all currently executing preempt-disabled sections of code (including
* hardirq handlers, NMI handlers, and local_irq_save() blocks) have
* completed.
*/
extern void call_rcu_sched(struct rcu_head *head,
void (*func)(struct rcu_head *head));
extern void __rcu_read_lock(void) __acquires(RCU); extern void __rcu_read_lock(void) __acquires(RCU);
extern void __rcu_read_unlock(void) __releases(RCU); extern void __rcu_read_unlock(void) __releases(RCU);
extern int rcu_pending(int cpu); extern int rcu_pending(int cpu);
...@@ -55,6 +84,7 @@ extern int rcu_needs_cpu(int cpu); ...@@ -55,6 +84,7 @@ extern int rcu_needs_cpu(int cpu);
extern void __synchronize_sched(void); extern void __synchronize_sched(void);
extern void __rcu_init(void); extern void __rcu_init(void);
extern void rcu_init_sched(void);
extern void rcu_check_callbacks(int cpu, int user); extern void rcu_check_callbacks(int cpu, int user);
extern void rcu_restart_cpu(int cpu); extern void rcu_restart_cpu(int cpu);
extern long rcu_batches_completed(void); extern long rcu_batches_completed(void);
...@@ -81,20 +111,20 @@ extern struct rcupreempt_trace *rcupreempt_trace_cpu(int cpu); ...@@ -81,20 +111,20 @@ extern struct rcupreempt_trace *rcupreempt_trace_cpu(int cpu);
struct softirq_action; struct softirq_action;
#ifdef CONFIG_NO_HZ #ifdef CONFIG_NO_HZ
DECLARE_PER_CPU(long, dynticks_progress_counter); DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);
static inline void rcu_enter_nohz(void) static inline void rcu_enter_nohz(void)
{ {
smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
__get_cpu_var(dynticks_progress_counter)++; __get_cpu_var(rcu_dyntick_sched).dynticks++;
WARN_ON(__get_cpu_var(dynticks_progress_counter) & 0x1); WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1);
} }
static inline void rcu_exit_nohz(void) static inline void rcu_exit_nohz(void)
{ {
__get_cpu_var(dynticks_progress_counter)++;
smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
WARN_ON(!(__get_cpu_var(dynticks_progress_counter) & 0x1)); __get_cpu_var(rcu_dyntick_sched).dynticks++;
WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1));
} }
#else /* CONFIG_NO_HZ */ #else /* CONFIG_NO_HZ */
......
...@@ -758,6 +758,7 @@ static void __init do_initcalls(void) ...@@ -758,6 +758,7 @@ static void __init do_initcalls(void)
*/ */
static void __init do_basic_setup(void) static void __init do_basic_setup(void)
{ {
rcu_init_sched(); /* needed by module_init stage. */
/* drivers will send hotplug events */ /* drivers will send hotplug events */
init_workqueues(); init_workqueues();
usermodehelper_init(); usermodehelper_init();
......
...@@ -39,18 +39,12 @@ ...@@ -39,18 +39,12 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/module.h> #include <linux/module.h>
struct rcu_synchronize {
struct rcu_head head;
struct completion completion;
};
static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL}; static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL};
static atomic_t rcu_barrier_cpu_count; static atomic_t rcu_barrier_cpu_count;
static DEFINE_MUTEX(rcu_barrier_mutex); static DEFINE_MUTEX(rcu_barrier_mutex);
...@@ -60,7 +54,7 @@ static struct completion rcu_barrier_completion; ...@@ -60,7 +54,7 @@ static struct completion rcu_barrier_completion;
* Awaken the corresponding synchronize_rcu() instance now that a * Awaken the corresponding synchronize_rcu() instance now that a
* grace period has elapsed. * grace period has elapsed.
*/ */
static void wakeme_after_rcu(struct rcu_head *head) void wakeme_after_rcu(struct rcu_head *head)
{ {
struct rcu_synchronize *rcu; struct rcu_synchronize *rcu;
...@@ -77,17 +71,7 @@ static void wakeme_after_rcu(struct rcu_head *head) ...@@ -77,17 +71,7 @@ static void wakeme_after_rcu(struct rcu_head *head)
* sections are delimited by rcu_read_lock() and rcu_read_unlock(), * sections are delimited by rcu_read_lock() and rcu_read_unlock(),
* and may be nested. * and may be nested.
*/ */
void synchronize_rcu(void) synchronize_rcu_xxx(synchronize_rcu, call_rcu)
{
struct rcu_synchronize rcu;
init_completion(&rcu.completion);
/* Will wake me after RCU finished */
call_rcu(&rcu.head, wakeme_after_rcu);
/* Wait for it */
wait_for_completion(&rcu.completion);
}
EXPORT_SYMBOL_GPL(synchronize_rcu); EXPORT_SYMBOL_GPL(synchronize_rcu);
static void rcu_barrier_callback(struct rcu_head *notused) static void rcu_barrier_callback(struct rcu_head *notused)
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册