Commit f41d911f authored by Paul E. McKenney's avatar Paul E. McKenney Committed by Ingo Molnar

rcu: Merge preemptable-RCU functionality into hierarchical RCU

Create a kernel/rcutree_plugin.h file that contains definitions
for preemptable RCU (or, under the #else branch of the #ifdef,
empty definitions for the classic non-preemptable semantics).
These definitions fit into plugins defined in kernel/rcutree.c
for this purpose.

This variant of preemptable RCU uses a new algorithm whose
read-side expense is roughly that of classic hierarchical RCU
under CONFIG_PREEMPT. This new algorithm's update-side expense
is similar to that of classic hierarchical RCU, and, in absence
of read-side preemption or blocking, is exactly that of classic
hierarchical RCU.  Perhaps more important, this new algorithm
has a much simpler implementation, saving well over 1,000 lines
of code compared to mainline's implementation of preemptable
RCU, which will hopefully be retired in favor of this new
algorithm.

The simplifications are obtained by maintaining per-task
nesting state for running tasks, and using a simple
lock-protected algorithm to handle accounting when tasks block
within RCU read-side critical sections, making use of lessons
learned while creating numerous user-level RCU implementations
over the past 18 months.
Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: laijs@cn.fujitsu.com
Cc: dipankar@in.ibm.com
Cc: akpm@linux-foundation.org
Cc: mathieu.desnoyers@polymtl.ca
Cc: josht@linux.vnet.ibm.com
Cc: dvhltc@us.ibm.com
Cc: niv@us.ibm.com
Cc: peterz@infradead.org
Cc: rostedt@goodmis.org
LKML-Reference: <12509746134003-git-send-email->
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent a157229c
...@@ -94,6 +94,20 @@ extern struct group_info init_groups; ...@@ -94,6 +94,20 @@ extern struct group_info init_groups;
# define CAP_INIT_BSET CAP_INIT_EFF_SET # define CAP_INIT_BSET CAP_INIT_EFF_SET
#endif #endif
#ifdef CONFIG_PREEMPT_RCU
#define INIT_TASK_RCU_PREEMPT(tsk) \
.rcu_read_lock_nesting = 0, \
.rcu_flipctr_idx = 0,
#elif defined(CONFIG_TREE_PREEMPT_RCU)
#define INIT_TASK_RCU_PREEMPT(tsk) \
.rcu_read_lock_nesting = 0, \
.rcu_read_unlock_special = 0, \
.rcu_blocked_cpu = -1, \
.rcu_node_entry = LIST_HEAD_INIT(tsk.rcu_node_entry),
#else
#define INIT_TASK_RCU_PREEMPT(tsk)
#endif
extern struct cred init_cred; extern struct cred init_cred;
#ifdef CONFIG_PERF_COUNTERS #ifdef CONFIG_PERF_COUNTERS
...@@ -173,6 +187,7 @@ extern struct cred init_cred; ...@@ -173,6 +187,7 @@ extern struct cred init_cred;
INIT_LOCKDEP \ INIT_LOCKDEP \
INIT_FTRACE_GRAPH \ INIT_FTRACE_GRAPH \
INIT_TRACE_RECURSION \ INIT_TRACE_RECURSION \
INIT_TASK_RCU_PREEMPT(tsk) \
} }
......
...@@ -66,7 +66,7 @@ extern void rcu_scheduler_starting(void); ...@@ -66,7 +66,7 @@ extern void rcu_scheduler_starting(void);
extern int rcu_needs_cpu(int cpu); extern int rcu_needs_cpu(int cpu);
extern int rcu_scheduler_active; extern int rcu_scheduler_active;
#if defined(CONFIG_TREE_RCU) #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU)
#include <linux/rcutree.h> #include <linux/rcutree.h>
#elif defined(CONFIG_PREEMPT_RCU) #elif defined(CONFIG_PREEMPT_RCU)
#include <linux/rcupreempt.h> #include <linux/rcupreempt.h>
......
...@@ -98,6 +98,10 @@ static inline long rcu_batches_completed_bh(void) ...@@ -98,6 +98,10 @@ static inline long rcu_batches_completed_bh(void)
return rcu_batches_completed(); return rcu_batches_completed();
} }
static inline void exit_rcu(void)
{
}
#ifdef CONFIG_RCU_TRACE #ifdef CONFIG_RCU_TRACE
struct rcupreempt_trace; struct rcupreempt_trace;
extern long *rcupreempt_flipctr(int cpu); extern long *rcupreempt_flipctr(int cpu);
......
...@@ -35,14 +35,30 @@ extern void rcu_bh_qs(int cpu); ...@@ -35,14 +35,30 @@ extern void rcu_bh_qs(int cpu);
extern int rcu_needs_cpu(int cpu); extern int rcu_needs_cpu(int cpu);
#ifdef CONFIG_TREE_PREEMPT_RCU
extern void __rcu_read_lock(void);
extern void __rcu_read_unlock(void);
extern void exit_rcu(void);
#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */
static inline void __rcu_read_lock(void) static inline void __rcu_read_lock(void)
{ {
preempt_disable(); preempt_disable();
} }
static inline void __rcu_read_unlock(void) static inline void __rcu_read_unlock(void)
{ {
preempt_enable(); preempt_enable();
} }
static inline void exit_rcu(void)
{
}
#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */
static inline void __rcu_read_lock_bh(void) static inline void __rcu_read_lock_bh(void)
{ {
local_bh_disable(); local_bh_disable();
......
...@@ -1210,6 +1210,13 @@ struct task_struct { ...@@ -1210,6 +1210,13 @@ struct task_struct {
int rcu_flipctr_idx; int rcu_flipctr_idx;
#endif /* #ifdef CONFIG_PREEMPT_RCU */ #endif /* #ifdef CONFIG_PREEMPT_RCU */
#ifdef CONFIG_TREE_PREEMPT_RCU
int rcu_read_lock_nesting;
char rcu_read_unlock_special;
int rcu_blocked_cpu;
struct list_head rcu_node_entry;
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
struct sched_info sched_info; struct sched_info sched_info;
#endif #endif
...@@ -1723,6 +1730,36 @@ extern cputime_t task_gtime(struct task_struct *p); ...@@ -1723,6 +1730,36 @@ extern cputime_t task_gtime(struct task_struct *p);
#define tsk_used_math(p) ((p)->flags & PF_USED_MATH) #define tsk_used_math(p) ((p)->flags & PF_USED_MATH)
#define used_math() tsk_used_math(current) #define used_math() tsk_used_math(current)
#ifdef CONFIG_TREE_PREEMPT_RCU
#define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */
#define RCU_READ_UNLOCK_NEED_QS (1 << 1) /* RCU core needs CPU response. */
#define RCU_READ_UNLOCK_GOT_QS (1 << 2) /* CPU has responded to RCU core. */
static inline void rcu_copy_process(struct task_struct *p)
{
p->rcu_read_lock_nesting = 0;
p->rcu_read_unlock_special = 0;
p->rcu_blocked_cpu = -1;
INIT_LIST_HEAD(&p->rcu_node_entry);
}
#elif defined(CONFIG_PREEMPT_RCU)
static inline void rcu_copy_process(struct task_struct *p)
{
p->rcu_read_lock_nesting = 0;
p->rcu_flipctr_idx = 0;
}
#else
static inline void rcu_copy_process(struct task_struct *p)
{
}
#endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern int set_cpus_allowed_ptr(struct task_struct *p, extern int set_cpus_allowed_ptr(struct task_struct *p,
const struct cpumask *new_mask); const struct cpumask *new_mask);
......
...@@ -335,11 +335,20 @@ config PREEMPT_RCU ...@@ -335,11 +335,20 @@ config PREEMPT_RCU
now-naive assumptions about each RCU read-side critical section now-naive assumptions about each RCU read-side critical section
remaining on a given CPU through its execution. remaining on a given CPU through its execution.
config TREE_PREEMPT_RCU
bool "Preemptable tree-based hierarchical RCU"
depends on PREEMPT
help
This option selects the RCU implementation that is
designed for very large SMP systems with hundreds or
thousands of CPUs, but for which real-time response
is also required.
endchoice endchoice
config RCU_TRACE config RCU_TRACE
bool "Enable tracing for RCU" bool "Enable tracing for RCU"
depends on TREE_RCU || PREEMPT_RCU depends on TREE_RCU || PREEMPT_RCU || TREE_PREEMPT_RCU
help help
This option provides tracing in RCU which presents stats This option provides tracing in RCU which presents stats
in debugfs for debugging RCU implementation. in debugfs for debugging RCU implementation.
...@@ -351,7 +360,7 @@ config RCU_FANOUT ...@@ -351,7 +360,7 @@ config RCU_FANOUT
int "Tree-based hierarchical RCU fanout value" int "Tree-based hierarchical RCU fanout value"
range 2 64 if 64BIT range 2 64 if 64BIT
range 2 32 if !64BIT range 2 32 if !64BIT
depends on TREE_RCU depends on TREE_RCU || TREE_PREEMPT_RCU
default 64 if 64BIT default 64 if 64BIT
default 32 if !64BIT default 32 if !64BIT
help help
...@@ -366,7 +375,7 @@ config RCU_FANOUT ...@@ -366,7 +375,7 @@ config RCU_FANOUT
config RCU_FANOUT_EXACT config RCU_FANOUT_EXACT
bool "Disable tree-based hierarchical RCU auto-balancing" bool "Disable tree-based hierarchical RCU auto-balancing"
depends on TREE_RCU depends on TREE_RCU || TREE_PREEMPT_RCU
default n default n
help help
This option forces use of the exact RCU_FANOUT value specified, This option forces use of the exact RCU_FANOUT value specified,
...@@ -379,11 +388,12 @@ config RCU_FANOUT_EXACT ...@@ -379,11 +388,12 @@ config RCU_FANOUT_EXACT
Say N if unsure. Say N if unsure.
config TREE_RCU_TRACE config TREE_RCU_TRACE
def_bool RCU_TRACE && TREE_RCU def_bool RCU_TRACE && ( TREE_RCU || TREE_PREEMPT_RCU )
select DEBUG_FS select DEBUG_FS
help help
This option provides tracing for the TREE_RCU implementation, This option provides tracing for the TREE_RCU and
permitting Makefile to trivially select kernel/rcutree_trace.c. TREE_PREEMPT_RCU implementations, permitting Makefile to
trivially select kernel/rcutree_trace.c.
config PREEMPT_RCU_TRACE config PREEMPT_RCU_TRACE
def_bool RCU_TRACE && PREEMPT_RCU def_bool RCU_TRACE && PREEMPT_RCU
......
...@@ -81,6 +81,7 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ ...@@ -81,6 +81,7 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_SECCOMP) += seccomp.o
obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
obj-$(CONFIG_TREE_RCU) += rcutree.o obj-$(CONFIG_TREE_RCU) += rcutree.o
obj-$(CONFIG_TREE_PREEMPT_RCU) += rcutree.o
obj-$(CONFIG_PREEMPT_RCU) += rcupreempt.o obj-$(CONFIG_PREEMPT_RCU) += rcupreempt.o
obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o
obj-$(CONFIG_PREEMPT_RCU_TRACE) += rcupreempt_trace.o obj-$(CONFIG_PREEMPT_RCU_TRACE) += rcupreempt_trace.o
......
...@@ -1010,6 +1010,7 @@ NORET_TYPE void do_exit(long code) ...@@ -1010,6 +1010,7 @@ NORET_TYPE void do_exit(long code)
__free_pipe_info(tsk->splice_pipe); __free_pipe_info(tsk->splice_pipe);
preempt_disable(); preempt_disable();
exit_rcu();
/* causes final put_task_struct in finish_task_switch(). */ /* causes final put_task_struct in finish_task_switch(). */
tsk->state = TASK_DEAD; tsk->state = TASK_DEAD;
schedule(); schedule();
......
...@@ -1022,10 +1022,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1022,10 +1022,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
copy_flags(clone_flags, p); copy_flags(clone_flags, p);
INIT_LIST_HEAD(&p->children); INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling); INIT_LIST_HEAD(&p->sibling);
#ifdef CONFIG_PREEMPT_RCU rcu_copy_process(p);
p->rcu_read_lock_nesting = 0;
p->rcu_flipctr_idx = 0;
#endif /* #ifdef CONFIG_PREEMPT_RCU */
p->vfork_done = NULL; p->vfork_done = NULL;
spin_lock_init(&p->alloc_lock); spin_lock_init(&p->alloc_lock);
......
...@@ -80,6 +80,21 @@ DEFINE_PER_CPU(struct rcu_data, rcu_sched_data); ...@@ -80,6 +80,21 @@ DEFINE_PER_CPU(struct rcu_data, rcu_sched_data);
struct rcu_state rcu_bh_state = RCU_STATE_INITIALIZER(rcu_bh_state); struct rcu_state rcu_bh_state = RCU_STATE_INITIALIZER(rcu_bh_state);
DEFINE_PER_CPU(struct rcu_data, rcu_bh_data); DEFINE_PER_CPU(struct rcu_data, rcu_bh_data);
extern long rcu_batches_completed_sched(void);
static void cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp,
struct rcu_node *rnp, unsigned long flags);
static void cpu_quiet_msk_finish(struct rcu_state *rsp, unsigned long flags);
static void __rcu_process_callbacks(struct rcu_state *rsp,
struct rcu_data *rdp);
static void __call_rcu(struct rcu_head *head,
void (*func)(struct rcu_head *rcu),
struct rcu_state *rsp);
static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp);
static void __cpuinit rcu_init_percpu_data(int cpu, struct rcu_state *rsp,
int preemptable);
#include "rcutree_plugin.h"
/* /*
* Note a quiescent state. Because we do not need to know * Note a quiescent state. Because we do not need to know
* how many quiescent states passed, just if there was at least * how many quiescent states passed, just if there was at least
...@@ -87,16 +102,27 @@ DEFINE_PER_CPU(struct rcu_data, rcu_bh_data); ...@@ -87,16 +102,27 @@ DEFINE_PER_CPU(struct rcu_data, rcu_bh_data);
*/ */
void rcu_sched_qs(int cpu) void rcu_sched_qs(int cpu)
{ {
struct rcu_data *rdp = &per_cpu(rcu_sched_data, cpu); unsigned long flags;
struct rcu_data *rdp;
local_irq_save(flags);
rdp = &per_cpu(rcu_sched_data, cpu);
rdp->passed_quiesc = 1; rdp->passed_quiesc = 1;
rdp->passed_quiesc_completed = rdp->completed; rdp->passed_quiesc_completed = rdp->completed;
rcu_preempt_qs(cpu);
local_irq_restore(flags);
} }
void rcu_bh_qs(int cpu) void rcu_bh_qs(int cpu)
{ {
struct rcu_data *rdp = &per_cpu(rcu_bh_data, cpu); unsigned long flags;
struct rcu_data *rdp;
local_irq_save(flags);
rdp = &per_cpu(rcu_bh_data, cpu);
rdp->passed_quiesc = 1; rdp->passed_quiesc = 1;
rdp->passed_quiesc_completed = rdp->completed; rdp->passed_quiesc_completed = rdp->completed;
local_irq_restore(flags);
} }
#ifdef CONFIG_NO_HZ #ifdef CONFIG_NO_HZ
...@@ -122,16 +148,6 @@ long rcu_batches_completed_sched(void) ...@@ -122,16 +148,6 @@ long rcu_batches_completed_sched(void)
} }
EXPORT_SYMBOL_GPL(rcu_batches_completed_sched); EXPORT_SYMBOL_GPL(rcu_batches_completed_sched);
/*
* Return the number of RCU batches processed thus far for debug & stats.
* @@@ placeholder, maps to rcu_batches_completed_sched().
*/
long rcu_batches_completed(void)
{
return rcu_batches_completed_sched();
}
EXPORT_SYMBOL_GPL(rcu_batches_completed);
/* /*
* Return the number of RCU BH batches processed thus far for debug & stats. * Return the number of RCU BH batches processed thus far for debug & stats.
*/ */
...@@ -193,6 +209,10 @@ static int rcu_implicit_offline_qs(struct rcu_data *rdp) ...@@ -193,6 +209,10 @@ static int rcu_implicit_offline_qs(struct rcu_data *rdp)
return 1; return 1;
} }
/* If preemptable RCU, no point in sending reschedule IPI. */
if (rdp->preemptable)
return 0;
/* The CPU is online, so send it a reschedule IPI. */ /* The CPU is online, so send it a reschedule IPI. */
if (rdp->cpu != smp_processor_id()) if (rdp->cpu != smp_processor_id())
smp_send_reschedule(rdp->cpu); smp_send_reschedule(rdp->cpu);
...@@ -473,6 +493,7 @@ static void print_other_cpu_stall(struct rcu_state *rsp) ...@@ -473,6 +493,7 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
printk(KERN_ERR "INFO: RCU detected CPU stalls:"); printk(KERN_ERR "INFO: RCU detected CPU stalls:");
for (; rnp_cur < rnp_end; rnp_cur++) { for (; rnp_cur < rnp_end; rnp_cur++) {
rcu_print_task_stall(rnp);
if (rnp_cur->qsmask == 0) if (rnp_cur->qsmask == 0)
continue; continue;
for (cpu = 0; cpu <= rnp_cur->grphi - rnp_cur->grplo; cpu++) for (cpu = 0; cpu <= rnp_cur->grphi - rnp_cur->grplo; cpu++)
...@@ -685,6 +706,19 @@ rcu_process_gp_end(struct rcu_state *rsp, struct rcu_data *rdp) ...@@ -685,6 +706,19 @@ rcu_process_gp_end(struct rcu_state *rsp, struct rcu_data *rdp)
local_irq_restore(flags); local_irq_restore(flags);
} }
/*
* Clean up after the prior grace period and let rcu_start_gp() start up
* the next grace period if one is needed. Note that the caller must
* hold rnp->lock, as required by rcu_start_gp(), which will release it.
*/
static void cpu_quiet_msk_finish(struct rcu_state *rsp, unsigned long flags)
__releases(rnp->lock)
{
rsp->completed = rsp->gpnum;
rcu_process_gp_end(rsp, rsp->rda[smp_processor_id()]);
rcu_start_gp(rsp, flags); /* releases root node's rnp->lock. */
}
/* /*
* Similar to cpu_quiet(), for which it is a helper function. Allows * Similar to cpu_quiet(), for which it is a helper function. Allows
* a group of CPUs to be quieted at one go, though all the CPUs in the * a group of CPUs to be quieted at one go, though all the CPUs in the
...@@ -706,7 +740,7 @@ cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp, struct rcu_node *rnp, ...@@ -706,7 +740,7 @@ cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp, struct rcu_node *rnp,
return; return;
} }
rnp->qsmask &= ~mask; rnp->qsmask &= ~mask;
if (rnp->qsmask != 0) { if (rnp->qsmask != 0 || rcu_preempted_readers(rnp)) {
/* Other bits still set at this level, so done. */ /* Other bits still set at this level, so done. */
spin_unlock_irqrestore(&rnp->lock, flags); spin_unlock_irqrestore(&rnp->lock, flags);
...@@ -726,14 +760,10 @@ cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp, struct rcu_node *rnp, ...@@ -726,14 +760,10 @@ cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp, struct rcu_node *rnp,
/* /*
* Get here if we are the last CPU to pass through a quiescent * Get here if we are the last CPU to pass through a quiescent
* state for this grace period. Clean up and let rcu_start_gp() * state for this grace period. Invoke cpu_quiet_msk_finish()
* start up the next grace period if one is needed. Note that * to clean up and start the next grace period if one is needed.
* we still hold rnp->lock, as required by rcu_start_gp(), which
* will release it.
*/ */
rsp->completed = rsp->gpnum; cpu_quiet_msk_finish(rsp, flags); /* releases rnp->lock. */
rcu_process_gp_end(rsp, rsp->rda[smp_processor_id()]);
rcu_start_gp(rsp, flags); /* releases rnp->lock. */
} }
/* /*
...@@ -840,11 +870,11 @@ static void __rcu_offline_cpu(int cpu, struct rcu_state *rsp) ...@@ -840,11 +870,11 @@ static void __rcu_offline_cpu(int cpu, struct rcu_state *rsp)
spin_lock(&rnp->lock); /* irqs already disabled. */ spin_lock(&rnp->lock); /* irqs already disabled. */
rnp->qsmaskinit &= ~mask; rnp->qsmaskinit &= ~mask;
if (rnp->qsmaskinit != 0) { if (rnp->qsmaskinit != 0) {
spin_unlock(&rnp->lock); /* irqs already disabled. */ spin_unlock(&rnp->lock); /* irqs remain disabled. */
break; break;
} }
mask = rnp->grpmask; mask = rnp->grpmask;
spin_unlock(&rnp->lock); /* irqs already disabled. */ spin_unlock(&rnp->lock); /* irqs remain disabled. */
rnp = rnp->parent; rnp = rnp->parent;
} while (rnp != NULL); } while (rnp != NULL);
lastcomp = rsp->completed; lastcomp = rsp->completed;
...@@ -1007,6 +1037,7 @@ void rcu_check_callbacks(int cpu, int user) ...@@ -1007,6 +1037,7 @@ void rcu_check_callbacks(int cpu, int user)
rcu_bh_qs(cpu); rcu_bh_qs(cpu);
} }
rcu_preempt_check_callbacks(cpu);
raise_softirq(RCU_SOFTIRQ); raise_softirq(RCU_SOFTIRQ);
} }
...@@ -1188,6 +1219,7 @@ static void rcu_process_callbacks(struct softirq_action *unused) ...@@ -1188,6 +1219,7 @@ static void rcu_process_callbacks(struct softirq_action *unused)
__rcu_process_callbacks(&rcu_sched_state, __rcu_process_callbacks(&rcu_sched_state,
&__get_cpu_var(rcu_sched_data)); &__get_cpu_var(rcu_sched_data));
__rcu_process_callbacks(&rcu_bh_state, &__get_cpu_var(rcu_bh_data)); __rcu_process_callbacks(&rcu_bh_state, &__get_cpu_var(rcu_bh_data));
rcu_preempt_process_callbacks();
/* /*
* Memory references from any later RCU read-side critical sections * Memory references from any later RCU read-side critical sections
...@@ -1251,17 +1283,6 @@ void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) ...@@ -1251,17 +1283,6 @@ void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
} }
EXPORT_SYMBOL_GPL(call_rcu_sched); EXPORT_SYMBOL_GPL(call_rcu_sched);
/*
* @@@ Queue an RCU callback for invocation after a grace period.
* @@@ Placeholder pending rcutree_plugin.h.
*/
void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
{
call_rcu_sched(head, func);
}
EXPORT_SYMBOL_GPL(call_rcu);
/* /*
* Queue an RCU for invocation after a quicker grace period. * Queue an RCU for invocation after a quicker grace period.
*/ */
...@@ -1335,7 +1356,8 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp) ...@@ -1335,7 +1356,8 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
static int rcu_pending(int cpu) static int rcu_pending(int cpu)
{ {
return __rcu_pending(&rcu_sched_state, &per_cpu(rcu_sched_data, cpu)) || return __rcu_pending(&rcu_sched_state, &per_cpu(rcu_sched_data, cpu)) ||
__rcu_pending(&rcu_bh_state, &per_cpu(rcu_bh_data, cpu)); __rcu_pending(&rcu_bh_state, &per_cpu(rcu_bh_data, cpu)) ||
rcu_preempt_pending(cpu);
} }
/* /*
...@@ -1348,7 +1370,8 @@ int rcu_needs_cpu(int cpu) ...@@ -1348,7 +1370,8 @@ int rcu_needs_cpu(int cpu)
{ {
/* RCU callbacks either ready or pending? */ /* RCU callbacks either ready or pending? */
return per_cpu(rcu_sched_data, cpu).nxtlist || return per_cpu(rcu_sched_data, cpu).nxtlist ||
per_cpu(rcu_bh_data, cpu).nxtlist; per_cpu(rcu_bh_data, cpu).nxtlist ||
rcu_preempt_needs_cpu(cpu);
} }
/* /*
...@@ -1383,7 +1406,7 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp) ...@@ -1383,7 +1406,7 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
* that this CPU cannot possibly have any RCU callbacks in flight yet. * that this CPU cannot possibly have any RCU callbacks in flight yet.
*/ */
static void __cpuinit static void __cpuinit
rcu_init_percpu_data(int cpu, struct rcu_state *rsp) rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptable)
{ {
unsigned long flags; unsigned long flags;
long lastcomp; long lastcomp;
...@@ -1399,6 +1422,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp) ...@@ -1399,6 +1422,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
rdp->passed_quiesc = 0; /* We could be racing with new GP, */ rdp->passed_quiesc = 0; /* We could be racing with new GP, */
rdp->qs_pending = 1; /* so set up to respond to current GP. */ rdp->qs_pending = 1; /* so set up to respond to current GP. */
rdp->beenonline = 1; /* We have now been online. */ rdp->beenonline = 1; /* We have now been online. */
rdp->preemptable = preemptable;
rdp->passed_quiesc_completed = lastcomp - 1; rdp->passed_quiesc_completed = lastcomp - 1;
rdp->blimit = blimit; rdp->blimit = blimit;
spin_unlock(&rnp->lock); /* irqs remain disabled. */ spin_unlock(&rnp->lock); /* irqs remain disabled. */
...@@ -1441,12 +1465,13 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp) ...@@ -1441,12 +1465,13 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
static void __cpuinit rcu_online_cpu(int cpu) static void __cpuinit rcu_online_cpu(int cpu)
{ {
rcu_init_percpu_data(cpu, &rcu_sched_state); rcu_init_percpu_data(cpu, &rcu_sched_state, 0);
rcu_init_percpu_data(cpu, &rcu_bh_state); rcu_init_percpu_data(cpu, &rcu_bh_state, 0);
rcu_preempt_init_percpu_data(cpu);
} }
/* /*
* Handle CPU online/offline notifcation events. * Handle CPU online/offline notification events.
*/ */
int __cpuinit rcu_cpu_notify(struct notifier_block *self, int __cpuinit rcu_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
...@@ -1521,6 +1546,7 @@ static void __init rcu_init_one(struct rcu_state *rsp) ...@@ -1521,6 +1546,7 @@ static void __init rcu_init_one(struct rcu_state *rsp)
rnp = rsp->level[i]; rnp = rsp->level[i];
for (j = 0; j < rsp->levelcnt[i]; j++, rnp++) { for (j = 0; j < rsp->levelcnt[i]; j++, rnp++) {
spin_lock_init(&rnp->lock); spin_lock_init(&rnp->lock);
rnp->gpnum = 0;
rnp->qsmask = 0; rnp->qsmask = 0;
rnp->qsmaskinit = 0; rnp->qsmaskinit = 0;
rnp->grplo = j * cpustride; rnp->grplo = j * cpustride;
...@@ -1538,13 +1564,16 @@ static void __init rcu_init_one(struct rcu_state *rsp) ...@@ -1538,13 +1564,16 @@ static void __init rcu_init_one(struct rcu_state *rsp)
j / rsp->levelspread[i - 1]; j / rsp->levelspread[i - 1];
} }
rnp->level = i; rnp->level = i;
INIT_LIST_HEAD(&rnp->blocked_tasks[0]);
INIT_LIST_HEAD(&rnp->blocked_tasks[1]);
} }
} }
} }
/* /*
* Helper macro for __rcu_init(). To be used nowhere else! * Helper macro for __rcu_init() and __rcu_init_preempt(). To be used
* Assigns leaf node pointers into each CPU's rcu_data structure. * nowhere else! Assigns leaf node pointers into each CPU's rcu_data
* structure.
*/ */
#define RCU_INIT_FLAVOR(rsp, rcu_data) \ #define RCU_INIT_FLAVOR(rsp, rcu_data) \
do { \ do { \
...@@ -1560,18 +1589,38 @@ do { \ ...@@ -1560,18 +1589,38 @@ do { \
} \ } \
} while (0) } while (0)
#ifdef CONFIG_TREE_PREEMPT_RCU
void __init __rcu_init_preempt(void)
{
int i; /* All used by RCU_INIT_FLAVOR(). */
int j;
struct rcu_node *rnp;
RCU_INIT_FLAVOR(&rcu_preempt_state, rcu_preempt_data);
}
#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */
void __init __rcu_init_preempt(void)
{
}
#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */
void __init __rcu_init(void) void __init __rcu_init(void)
{ {
int i; /* All used by RCU_DATA_PTR_INIT(). */ int i; /* All used by RCU_INIT_FLAVOR(). */
int j; int j;
struct rcu_node *rnp; struct rcu_node *rnp;
printk(KERN_INFO "Hierarchical RCU implementation.\n"); rcu_bootup_announce();
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR #ifdef CONFIG_RCU_CPU_STALL_DETECTOR
printk(KERN_INFO "RCU-based detection of stalled CPUs is enabled.\n"); printk(KERN_INFO "RCU-based detection of stalled CPUs is enabled.\n");
#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */ #endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
RCU_INIT_FLAVOR(&rcu_sched_state, rcu_sched_data); RCU_INIT_FLAVOR(&rcu_sched_state, rcu_sched_data);
RCU_INIT_FLAVOR(&rcu_bh_state, rcu_bh_data); RCU_INIT_FLAVOR(&rcu_bh_state, rcu_bh_data);
__rcu_init_preempt();
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
} }
......
...@@ -80,6 +80,7 @@ struct rcu_dynticks { ...@@ -80,6 +80,7 @@ struct rcu_dynticks {
*/ */
struct rcu_node { struct rcu_node {
spinlock_t lock; spinlock_t lock;
long gpnum; /* Current grace period for this node. */
unsigned long qsmask; /* CPUs or groups that need to switch in */ unsigned long qsmask; /* CPUs or groups that need to switch in */
/* order for current grace period to proceed.*/ /* order for current grace period to proceed.*/
unsigned long qsmaskinit; unsigned long qsmaskinit;
...@@ -90,6 +91,8 @@ struct rcu_node { ...@@ -90,6 +91,8 @@ struct rcu_node {
u8 grpnum; /* CPU/group number for next level up. */ u8 grpnum; /* CPU/group number for next level up. */
u8 level; /* root is at level 0. */ u8 level; /* root is at level 0. */
struct rcu_node *parent; struct rcu_node *parent;
struct list_head blocked_tasks[2];
/* Tasks blocked in RCU read-side critsect. */
} ____cacheline_internodealigned_in_smp; } ____cacheline_internodealigned_in_smp;
/* Index values for nxttail array in struct rcu_data. */ /* Index values for nxttail array in struct rcu_data. */
...@@ -111,6 +114,7 @@ struct rcu_data { ...@@ -111,6 +114,7 @@ struct rcu_data {
bool passed_quiesc; /* User-mode/idle loop etc. */ bool passed_quiesc; /* User-mode/idle loop etc. */
bool qs_pending; /* Core waits for quiesc state. */ bool qs_pending; /* Core waits for quiesc state. */
bool beenonline; /* CPU online at least once. */ bool beenonline; /* CPU online at least once. */
bool preemptable; /* Preemptable RCU? */
struct rcu_node *mynode; /* This CPU's leaf of hierarchy */ struct rcu_node *mynode; /* This CPU's leaf of hierarchy */
unsigned long grpmask; /* Mask to apply to leaf qsmask. */ unsigned long grpmask; /* Mask to apply to leaf qsmask. */
...@@ -244,5 +248,10 @@ DECLARE_PER_CPU(struct rcu_data, rcu_sched_data); ...@@ -244,5 +248,10 @@ DECLARE_PER_CPU(struct rcu_data, rcu_sched_data);
extern struct rcu_state rcu_bh_state; extern struct rcu_state rcu_bh_state;
DECLARE_PER_CPU(struct rcu_data, rcu_bh_data); DECLARE_PER_CPU(struct rcu_data, rcu_bh_data);
#ifdef CONFIG_TREE_PREEMPT_RCU
extern struct rcu_state rcu_preempt_state;
DECLARE_PER_CPU(struct rcu_data, rcu_preempt_data);
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
#endif /* #ifdef RCU_TREE_NONCORE */ #endif /* #ifdef RCU_TREE_NONCORE */
This diff is collapsed.
...@@ -77,6 +77,10 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) ...@@ -77,6 +77,10 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
static int show_rcudata(struct seq_file *m, void *unused) static int show_rcudata(struct seq_file *m, void *unused)
{ {
#ifdef CONFIG_TREE_PREEMPT_RCU
seq_puts(m, "rcu_preempt:\n");
PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data, m);
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
seq_puts(m, "rcu_sched:\n"); seq_puts(m, "rcu_sched:\n");
PRINT_RCU_DATA(rcu_sched_data, print_one_rcu_data, m); PRINT_RCU_DATA(rcu_sched_data, print_one_rcu_data, m);
seq_puts(m, "rcu_bh:\n"); seq_puts(m, "rcu_bh:\n");
...@@ -125,6 +129,10 @@ static int show_rcudata_csv(struct seq_file *m, void *unused) ...@@ -125,6 +129,10 @@ static int show_rcudata_csv(struct seq_file *m, void *unused)
seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\","); seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\",");
#endif /* #ifdef CONFIG_NO_HZ */ #endif /* #ifdef CONFIG_NO_HZ */
seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\"\n"); seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\"\n");
#ifdef CONFIG_TREE_PREEMPT_RCU
seq_puts(m, "\"rcu_preempt:\"\n");
PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data_csv, m);
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
seq_puts(m, "\"rcu_sched:\"\n"); seq_puts(m, "\"rcu_sched:\"\n");
PRINT_RCU_DATA(rcu_sched_data, print_one_rcu_data_csv, m); PRINT_RCU_DATA(rcu_sched_data, print_one_rcu_data_csv, m);
seq_puts(m, "\"rcu_bh:\"\n"); seq_puts(m, "\"rcu_bh:\"\n");
...@@ -172,6 +180,10 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) ...@@ -172,6 +180,10 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
static int show_rcuhier(struct seq_file *m, void *unused) static int show_rcuhier(struct seq_file *m, void *unused)
{ {
#ifdef CONFIG_TREE_PREEMPT_RCU
seq_puts(m, "rcu_preempt:\n");
print_one_rcu_state(m, &rcu_preempt_state);
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
seq_puts(m, "rcu_sched:\n"); seq_puts(m, "rcu_sched:\n");
print_one_rcu_state(m, &rcu_sched_state); print_one_rcu_state(m, &rcu_sched_state);
seq_puts(m, "rcu_bh:\n"); seq_puts(m, "rcu_bh:\n");
...@@ -194,6 +206,10 @@ static struct file_operations rcuhier_fops = { ...@@ -194,6 +206,10 @@ static struct file_operations rcuhier_fops = {
static int show_rcugp(struct seq_file *m, void *unused) static int show_rcugp(struct seq_file *m, void *unused)
{ {
#ifdef CONFIG_TREE_PREEMPT_RCU
seq_printf(m, "rcu_preempt: completed=%ld gpnum=%ld\n",
rcu_preempt_state.completed, rcu_preempt_state.gpnum);
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
seq_printf(m, "rcu_sched: completed=%ld gpnum=%ld\n", seq_printf(m, "rcu_sched: completed=%ld gpnum=%ld\n",
rcu_sched_state.completed, rcu_sched_state.gpnum); rcu_sched_state.completed, rcu_sched_state.gpnum);
seq_printf(m, "rcu_bh: completed=%ld gpnum=%ld\n", seq_printf(m, "rcu_bh: completed=%ld gpnum=%ld\n",
...@@ -244,6 +260,10 @@ static void print_rcu_pendings(struct seq_file *m, struct rcu_state *rsp) ...@@ -244,6 +260,10 @@ static void print_rcu_pendings(struct seq_file *m, struct rcu_state *rsp)
static int show_rcu_pending(struct seq_file *m, void *unused) static int show_rcu_pending(struct seq_file *m, void *unused)
{ {
#ifdef CONFIG_TREE_PREEMPT_RCU
seq_puts(m, "rcu_preempt:\n");
print_rcu_pendings(m, &rcu_preempt_state);
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
seq_puts(m, "rcu_sched:\n"); seq_puts(m, "rcu_sched:\n");
print_rcu_pendings(m, &rcu_sched_state); print_rcu_pendings(m, &rcu_sched_state);
seq_puts(m, "rcu_bh:\n"); seq_puts(m, "rcu_bh:\n");
......
...@@ -725,7 +725,7 @@ config RCU_TORTURE_TEST_RUNNABLE ...@@ -725,7 +725,7 @@ config RCU_TORTURE_TEST_RUNNABLE
config RCU_CPU_STALL_DETECTOR config RCU_CPU_STALL_DETECTOR
bool "Check for stalled CPUs delaying RCU grace periods" bool "Check for stalled CPUs delaying RCU grace periods"
depends on CLASSIC_RCU || TREE_RCU depends on CLASSIC_RCU || TREE_RCU || TREE_PREEMPT_RCU
default n default n
help help
This option causes RCU to printk information on which This option causes RCU to printk information on which
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment