synchronize_sched() without migration
RFC PATCH 1/1 synchronize_sched() without migration
The combination of CPU hotplug and PREEMPT_RCU has resulted in deadlocks due to the migration-based implementation of synchronize_sched() in -rt. This experimental patch maps synchronize_sched() back onto Classic RCU, eliminating the migration, thus hopefully also eliminating the deadlocks. It is not clear that this is a good long-term approach, but it will at least permit people doing CPU hotplug in -rt kernels additional wiggle room in their design and implementation.
The basic approach is to cause the -rt kernel to incorporate rcuclassic.c as well as rcupreempt.c, but to #ifdef out the conflicting portions of rcuclassic.c so that only the code needed to implement synchronize_sched() remains in a PREEMPT_RT build. Invocations of grace-period detection from the scheduling-clock interrupt go to rcuclassic.c, which then invokes the corresponding functions in rcupreempt.c (with _rt suffix added to keep the linker happy).
If this patch does turn out to be the right approach, the #ifdefs in kernel/rcuclassic.c will be dealt with. ;-)
Lightly tested only on x86 machines, bugs no doubt remain.
---
include/linux/rcuclassic.h | 79 +++++----------------------------------- include/linux/rcupdate.h | 43 ++++++++++++++++----- include/linux/rcupreempt.h | 29 ++++++-------- kernel/Makefile | 2 - kernel/rcuclassic.c | 88 +++++++++++++++++++++++++++++++++++++++------ kernel/rcupdate.c | 22 ++++++++--- kernel/rcupreempt.c | 46 +++++------------------ 7 files changed, 160 insertions(+), 149 deletions(-)
include/linux/rcuclassic.h +11 -68
@@ -43,78 +43,19 @@#include <linux/seqlock.h> -/* Global control variables for rcupdate callback mechanism. */ -struct rcu_ctrlblk { -long cur;/* Current batch number. */ -long completed;/* Number of the last completed batch */ -int next_pending;/* Is the next batch already waiting? */ - -int signaled; - -spinlock_t lock____cacheline_internodealigned_in_smp ; -cpumask_t cpumask;/* CPUs that need to switch in order */ -/* for current batch to proceed. */ -}____cacheline_internodealigned_in_smp ; - -/* Is batch a before batch b ? */ -static inline int rcu_batch_before (long a,long b) -{ -return (a - b) <0 ; -} - -/* Is batch a after batch b ? */ -static inline int rcu_batch_after (long a,long b) -{ -return (a - b) >0 ; -} +DECLARE_PER_CPU (int, rcu_data_bh_passed_quiesc);/* -* Per-CPU data for Read-Copy UPdate. -* nxtlist - new callbacks are added here -* curlist - current batch for which quiescent cycle started if any -*/ -struct rcu_data { -/* 1) quiescent state handling : */ -long quiescbatch;/* Batch # for grace period */ -int passed_quiesc;/* User-mode/idle loop etc. */ -int qs_pending;/* core waits for quiesc state */ - -/* 2) batch handling */ -long batch;/* Batch # for current RCU batch */ -struct rcu_head *nxtlist; -struct rcu_head **nxttail; -long qlen;/* # of queued callbacks */ -struct rcu_head *curlist; -struct rcu_head **curtail; -struct rcu_head *donelist; -struct rcu_head **donetail; -long blimit;/* Upper limit on a processed batch */ -int cpu; -}; - -DECLARE_PER_CPU(struct rcu_data, rcu_data); -DECLARE_PER_CPU(struct rcu_data, rcu_bh_data); - -/* -* Increment the quiescent state counter. +* Increment the bottom-half quiescent state counter. * The counter is a bit degenerated: We do not need to know * how many quiescent states passed, just if there was at least * one since the start of the grace period. Thus just a flag. */ -static inline void rcu_qsctr_inc (int cpu) -{ -struct rcu_data *rdp = &per_cpu (rcu_data, cpu); - rdp->passed_quiesc =1 ; -}static inline void rcu_bh_qsctr_inc (int cpu) { -struct rcu_data *rdp = &per_cpu (rcu_bh_data, cpu); - rdp->passed_quiesc =1 ; +per_cpu (rcu_data_bh_passed_quiesc, cpu) =1 ; } -extern int rcu_pending (int cpu); -extern int rcu_needs_cpu (int cpu); -#define __rcu_read_lock () \ do { \ preempt_disable(); \
@@ -139,13 +80,15 @@ extern int rcu_needs_cpu(int cpu);#define __synchronize_sched () synchronize_rcu() -extern void __rcu_init (void ); -extern void rcu_check_callbacks (int cpu,int user); -extern void rcu_restart_cpu (int cpu); -extern long rcu_batches_completed (void ); +#define rcu_advance_callbacks_rt (cpu, user) +#define rcu_check_callbacks_rt (cpu, user) +#define rcu_init_rt () +#define rcu_needs_cpu_rt (cpu) 0 +#define rcu_pending_rt (cpu) 0 +#define rcu_process_callbacks_rt (unused) -struct softirq_action ; -extern void rcu_process_callbacks (struct softirq_action *unused); +extern void FASTCALL (call_rcu_classic (struct rcu_head *head, +void (*func)(struct rcu_head *head)));#endif /* __KERNEL__ */ #endif /* __LINUX_RCUCLASSIC_H */
include/linux/rcupdate.h +32 -11
69 lines not on this page · raw message
include/linux/rcupreempt.h +13 -16
@@ -42,30 +42,27 @@#include <linux/cpumask.h> #include <linux/seqlock.h> -#define rcu_qsctr_inc (cpu) -#define rcu_bh_qsctr_inc (cpu)#define call_rcu_bh (head, rcu) call_rcu(head, rcu) - -extern void __rcu_read_lock (void ); -extern void __rcu_read_unlock (void ); -extern int rcu_pending (int cpu); -extern int rcu_needs_cpu (int cpu); - +#define rcu_bh_qsctr_inc (cpu)#define __rcu_read_lock_bh () { rcu_read_lock(); local_bh_disable(); }#define __rcu_read_unlock_bh () { local_bh_enable(); rcu_read_unlock(); } -#define __rcu_read_lock_nesting () (current->rcu_read_lock_nesting) +extern void FASTCALL (call_rcu_classic (struct rcu_head *head, +void (*func)(struct rcu_head *head))); +extern void FASTCALL (call_rcu_preempt (struct rcu_head *head, +void (*func)(struct rcu_head *head))); +extern void __rcu_read_lock (void ); +extern void __rcu_read_unlock (void );extern void __synchronize_sched (void ); -extern void __rcu_init (void ); -extern void rcu_check_callbacks (int cpu,int user); -extern void rcu_restart_cpu (int cpu); -extern long rcu_batches_completed (void ); - +extern void rcu_advance_callbacks_rt (int cpu,int user); +extern void rcu_check_callbacks_rt (int cpu,int user); +extern void rcu_init_rt (void ); +extern int rcu_needs_cpu_rt (int cpu); +extern int rcu_pending_rt (int cpu);struct softirq_action ; - -extern void rcu_process_callbacks (struct softirq_action *unused); +extern void rcu_process_callbacks_rt (struct softirq_action *unused);#endif /* __KERNEL__ */ #endif /* __LINUX_RCUPREEMPT_H */
kernel/Makefile +1 -1
@@ -56,7 +56,7 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o obj-$(CONFIG_CLASSIC_RCU) += rcupdate.o rcuclassic.o -obj-$(CONFIG_PREEMPT_RCU) += rcupdate.o rcupreempt.o +obj-$(CONFIG_PREEMPT_RCU) += rcupdate.o rcupreempt.o rcuclassic.o obj-$(CONFIG_RCU_TRACE) += rcupreempt_trace.o obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
kernel/rcuclassic.c +77 -11
185 lines not on this page · raw message
kernel/rcupdate.c +16 -6
@@ -51,6 +51,7 @@ struct rcu_synchronize {struct completion completion; }; +DEFINE_PER_CPU (int, rcu_data_passed_quiesc); static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head);static atomic_t rcu_barrier_cpu_count;static DEFINE_MUTEX (rcu_barrier_mutex);
@@ -89,6 +90,21 @@ void synchronize_rcu(void)wait_for_completion (&rcu.completion); } +#ifdef CONFIG_PREEMPT_RCU + +/* +* Map synchronize_sched() to the classic RCU implementation. +*/ +void __synchronize_sched (void ) +{ +struct rcu_synchronize rcu; + +init_completion (&rcu.completion); +call_rcu_classic (&rcu.head, wakeme_after_rcu); +wait_for_completion (&rcu.completion); +} +#endif /* #ifdef CONFIG_PREEMPT_RCU */ +static void rcu_barrier_callback (struct rcu_head *notused) {if (atomic_dec_and_test (&rcu_barrier_cpu_count))
@@ -122,10 +138,4 @@ void rcu_barrier(void)mutex_unlock (&rcu_barrier_mutex); }EXPORT_SYMBOL_GPL (rcu_barrier); - -void __init rcu_init (void ) -{ -__rcu_init (); -} -EXPORT_SYMBOL_GPL (synchronize_rcu);
kernel/rcupreempt.c +10 -36
103 lines not on this page · raw message