Skip to contentSkip to conversation

Backport TREE_RCU to 2.6.27

PATCH New Paul E. McKenney 1 participant 1 message Aug 21, 2009

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Backport TREE_RCU to 2.6.27

This is a backport of TREE_RCU to 2.6.27. Testing is in progress, with encouraging results thus far (famous last words). Given that I developed TREE_RCU on 2.6.26 and 2.6.27, I am hopeful that things will progress nicely (more famous last words). ;-)

This is a single patch combining a number of mainline patches for testing convenience. I have them applied to my local git tree as a series of cherry-picks of the corresponding mainline commits with a couple of additional merge-conflict commits as follows:

726befc8122a259af7132f5accf48c01e719bee1 rcu: Add __rcu_pending tracing to hierarchical RCU 7fb69d35deadcf3a133c5b2d93ff3602beb7f6f5 rcu: Make hierarchical RCU less IPI-happy b007daec94213e8012f4b7608e78e1ccdd4b3e3c rcu: Teach RCU that idle task is not quiscent state at boot (fix merge conflict) 0def84e22f98c09deca952c004845838800ede83 rcu: Teach RCU that idle task is not quiscent state at boot (merge conflict) 569dc44edc63d7c1d876b98af274d779e2bc8616 rcu: add __cpuinit to rcu_init_percpu_data() a714405953163fc3d69fb9955c378933f7f28fac rcu: make treercu safe for suspend and resume 3724df4eaa756542233685c93923f2933eda3481 rcu: fix rcutree grace-period-latency bug on small systems 8f7b6bc3b347a18bb29d8edd7a2a3eb22f954a4e "Tree RCU": scalable classic RCU implementation (merge-conflict fix) b802a3fe204973b727aa7e0e3ae731a2c7830e2b "Tree RCU": scalable classic RCU implementation (merge conflict)

If this is useful, I would of course be happy to create a patch set, bisectable or otherwise (of course, the above series is -not- bisectable).

---

 Documentation/RCU/00-INDEX             |    2 
 Documentation/RCU/trace.txt            |  413 ++++++++
 arch/powerpc/platforms/pseries/rtasd.c |    4 
 include/linux/hardirq.h                |   14 
 include/linux/rcuclassic.h             |    6 
 include/linux/rcupdate.h               |   16 
 include/linux/rcupreempt.h             |   15 
 include/linux/rcutree.h                |  341 +++++++
 init/Kconfig                           |   18 
 init/main.c                            |    3 
 kernel/Kconfig.preempt                 |   62 +
 kernel/Makefile                        |    6 
 kernel/rcuclassic.c                    |    6 
 kernel/rcupdate.c                      |    8 
 kernel/rcupreempt.c                    |   26 
 kernel/rcupreempt_trace.c              |   10 
 kernel/rcutree.c                       | 1534 +++++++++++++++++++++++++++++++++
 kernel/rcutree_trace.c                 |  329 +++++++
 kernel/softirq.c                       |    5 
 lib/Kconfig.debug                      |   13 
 20 files changed, 2796 insertions(+), 35 deletions(-)
Documentation/RCU/00-INDEX +2 -0
index 461481d..7dc0695 100644
@@ -16,6 +16,8 @@ RTFP.txt
 	- List of RCU papers (bibliography) going back to 1980.
 torture.txt
 	- RCU Torture Test Operation (CONFIG_RCU_TORTURE_TEST)
+trace.txt
+	- CONFIG_RCU_TRACE debugfs files and formats
 UP.txt
 	- RCU on Uniprocessor Systems
 whatisRCU.txt
Documentation/RCU/trace.txt new file +413 -0

413 lines not on this page · raw message

arch/powerpc/platforms/pseries/rtasd.c +4 -0
index c9ffd8c..d8e784a 100644
@@ -208,6 +208,7 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal)
 		break;
 	case ERR_TYPE_KERNEL_PANIC:
 	default:
+		WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
 		spin_unlock_irqrestore(&rtasd_log_lock, s);
 		return;
 	}
@@ -227,6 +228,7 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal)
 	/* Check to see if we need to or have stopped logging */
 	if (fatal || !logging_enabled) {
 		logging_enabled = 0;
+		WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
 		spin_unlock_irqrestore(&rtasd_log_lock, s);
 		return;
 	}
@@ -249,11 +251,13 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal)
 		else
 			rtas_log_start += 1;
 
+		WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
 		spin_unlock_irqrestore(&rtasd_log_lock, s);
 		wake_up_interruptible(&rtas_log_wait);
 		break;
 	case ERR_TYPE_KERNEL_PANIC:
 	default:
+		WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
 		spin_unlock_irqrestore(&rtasd_log_lock, s);
 		return;
 	}
include/linux/hardirq.h +8 -6
index 181006c..9b70b92 100644
@@ -118,13 +118,17 @@ static inline void account_system_vtime(struct task_struct *tsk)
 }
 #endif
 
-#if defined(CONFIG_PREEMPT_RCU) && defined(CONFIG_NO_HZ)
+#if defined(CONFIG_NO_HZ) && !defined(CONFIG_CLASSIC_RCU)
 extern void rcu_irq_enter(void);
 extern void rcu_irq_exit(void);
+extern void rcu_nmi_enter(void);
+extern void rcu_nmi_exit(void);
 #else
 # define rcu_irq_enter() do { } while (0)
 # define rcu_irq_exit() do { } while (0)
-#endif /* CONFIG_PREEMPT_RCU */
+# define rcu_nmi_enter() do { } while (0)
+# define rcu_nmi_exit() do { } while (0)
+#endif /* #if defined(CONFIG_NO_HZ) && !defined(CONFIG_CLASSIC_RCU) */
 
 /*
  * It is safe to do non-atomic ops on ->hardirq_context,
@@ -134,7 +138,6 @@ extern void rcu_irq_exit(void);
  */
 #define __irq_enter()					\
 	do {						\
-		rcu_irq_enter();			\
 		account_system_vtime(current);		\
 		add_preempt_count(HARDIRQ_OFFSET);	\
 		trace_hardirq_enter();			\
@@ -153,7 +156,6 @@ extern void irq_enter(void);
 		trace_hardirq_exit();			\
 		account_system_vtime(current);		\
 		sub_preempt_count(HARDIRQ_OFFSET);	\
-		rcu_irq_exit();				\
 	} while (0)
 
 /*
@@ -161,7 +163,7 @@ extern void irq_enter(void);
  */
 extern void irq_exit(void);
 
-#define nmi_enter()		do { lockdep_off(); __irq_enter(); } while (0)
-#define nmi_exit()		do { __irq_exit(); lockdep_on(); } while (0)
+#define nmi_enter()		do { lockdep_off(); rcu_nmi_enter(); __irq_enter(); } while (0)
+#define nmi_exit()		do { __irq_exit(); rcu_nmi_exit(); lockdep_on(); } while (0)
 
 #endif /* LINUX_HARDIRQ_H */
include/linux/rcuclassic.h +6 -0
index 4ab8436..b815f3c 100644
@@ -164,4 +164,10 @@ extern long rcu_batches_completed_bh(void);
 #define rcu_enter_nohz()	do { } while (0)
 #define rcu_exit_nohz()		do { } while (0)
 
+/* A context switch is a grace period for rcuclassic. */
+static inline int rcu_blocking_is_gp(void)
+{
+	return num_online_cpus() == 1;
+}
+
 #endif /* __LINUX_RCUCLASSIC_H */
include/linux/rcupdate.h +13 -3
index e8b4039..a4320f7 100644
@@ -52,11 +52,18 @@ struct rcu_head {
 	void (*func)(struct rcu_head *head);
 };
 
-#ifdef CONFIG_CLASSIC_RCU
+/* Internal to kernel, but needed by rcupreempt.h. */
+extern int rcu_scheduler_active;
+
+#if defined(CONFIG_CLASSIC_RCU)
 #include <linux/rcuclassic.h>
-#else /* #ifdef CONFIG_CLASSIC_RCU */
+#elif defined(CONFIG_TREE_RCU)
+#include <linux/rcutree.h>
+#elif defined(CONFIG_PREEMPT_RCU)
 #include <linux/rcupreempt.h>
-#endif /* #else #ifdef CONFIG_CLASSIC_RCU */
+#else
+#error "Unknown RCU implementation specified to kernel configuration"
+#endif /* #else #if defined(CONFIG_CLASSIC_RCU) */
 
 #define RCU_HEAD_INIT 	{ .next = NULL, .func = NULL }
 #define RCU_HEAD(head) struct rcu_head head = RCU_HEAD_INIT
@@ -183,6 +190,8 @@ void name(void) \
 { \
 	struct rcu_synchronize rcu; \
 	\
+	if (rcu_blocking_is_gp()) \
+		return; \
 	init_completion(&rcu.completion); \
 	/* Will wake me after RCU finished. */ \
 	func(&rcu.head, wakeme_after_rcu); \
@@ -251,6 +260,7 @@ extern void rcu_barrier_sched(void);
 
 /* Internal to kernel */
 extern void rcu_init(void);
+extern void rcu_scheduler_starting(void);
 extern int rcu_needs_cpu(int cpu);
 
 #endif /* __LINUX_RCUPDATE_H */
include/linux/rcupreempt.h +15 -0
index 0967f03..f4997ba 100644
@@ -137,4 +137,19 @@ static inline void rcu_exit_nohz(void)
 #define rcu_exit_nohz()		do { } while (0)
 #endif /* CONFIG_NO_HZ */
 
+/*
+ * A context switch is a grace period for rcupreempt synchronize_rcu()
+ * only during early boot, before the scheduler has been initialized.
+ * So, how the heck do we get a context switch?  Well, if the caller
+ * invokes synchronize_rcu(), they are willing to accept a context
+ * switch, so we simply pretend that one happened.
+ *
+ * After boot, there might be a blocked or preempted task in an RCU
+ * read-side critical section, so we cannot then take the fastpath.
+ */
+static inline int rcu_blocking_is_gp(void)
+{
+	return num_online_cpus() == 1 && !rcu_scheduler_active;
+}
+
 #endif /* __LINUX_RCUPREEMPT_H */
include/linux/rcutree.h new file +341 -0

341 lines not on this page · raw message

init/Kconfig +12 -6
index c11da38..fdb0bcd 100644
@@ -903,10 +903,16 @@ source "block/Kconfig"
 config PREEMPT_NOTIFIERS
 	bool
 
-config CLASSIC_RCU
-	def_bool !PREEMPT_RCU
+config TREE_RCU_TRACE
+	def_bool RCU_TRACE && TREE_RCU
+	select DEBUG_FS
 	help
-	  This option selects the classic RCU implementation that is
-	  designed for best read-side performance on non-realtime
-	  systems.  Classic RCU is the default.  Note that the
-	  PREEMPT_RCU symbol is used to select/deselect this option.
+	  This option provides tracing for the TREE_RCU implementation,
+	  permitting Makefile to trivially select kernel/rcutree_trace.c.
+
+config PREEMPT_RCU_TRACE
+	def_bool RCU_TRACE && PREEMPT_RCU
+	select DEBUG_FS
+	help
+	  This option provides tracing for the PREEMPT_RCU implementation,
+	  permitting Makefile to trivially select kernel/rcupreempt_trace.c.
init/main.c +2 -1
index 3820323..a05fdc7 100644
@@ -102,7 +102,7 @@ static inline void mark_rodata_ro(void) { }
 extern void tc_init(void);
 #endif
 
-enum system_states system_state;
+enum system_states system_state __read_mostly;
 EXPORT_SYMBOL(system_state);
 
 /*
@@ -473,6 +473,7 @@ static void noinline __init_refok rest_init(void)
 	 * at least once to get things moving:
 	 */
 	init_idle_bootup_task(current);
+	rcu_scheduler_starting();
 	preempt_enable_no_resched();
 	schedule();
 	preempt_disable();
kernel/Kconfig.preempt +56 -6

82 lines not on this page · raw message

kernel/Makefile +3 -3
index 4e1d7df..101e880 100644
@@ -74,10 +74,10 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
 obj-$(CONFIG_SECCOMP) += seccomp.o
 obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
 obj-$(CONFIG_CLASSIC_RCU) += rcuclassic.o
+obj-$(CONFIG_TREE_RCU) += rcutree.o
 obj-$(CONFIG_PREEMPT_RCU) += rcupreempt.o
-ifeq ($(CONFIG_PREEMPT_RCU),y)
-obj-$(CONFIG_RCU_TRACE) += rcupreempt_trace.o
-endif
+obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o
+obj-$(CONFIG_PREEMPT_RCU_TRACE) += rcupreempt_trace.o
 obj-$(CONFIG_RELAY) += relay.o
 obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
 obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
kernel/rcuclassic.c +3 -3
index aad93cd..0bcb212 100644
@@ -518,8 +518,8 @@ int rcu_needs_cpu(int cpu)
 void rcu_check_callbacks(int cpu, int user)
 {
 	if (user ||
-	    (idle_cpu(cpu) && !in_softirq() &&
-				hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
+	    (idle_cpu(cpu) && rcu_scheduler_active &&
+	     !in_softirq() && hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
 
 		/*
 		 * Get here if this CPU took its interrupt from user
@@ -555,7 +555,7 @@ void rcu_check_callbacks(int cpu, int user)
 	raise_rcu_softirq();
 }
 
-static void rcu_init_percpu_data(int cpu, struct rcu_ctrlblk *rcp,
+static void __cpuinit rcu_init_percpu_data(int cpu, struct rcu_ctrlblk *rcp,
 						struct rcu_data *rdp)
 {
 	memset(rdp, 0, sizeof(*rdp));
kernel/rcupdate.c +8 -0
index 467d594..2b317d1 100644
@@ -44,6 +44,7 @@
 #include <linux/cpu.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
+#include <linux/kernel_stat.h>
 
 enum rcu_barrier {
 	RCU_BARRIER_STD,
@@ -55,6 +56,7 @@ static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL};
 static atomic_t rcu_barrier_cpu_count;
 static DEFINE_MUTEX(rcu_barrier_mutex);
 static struct completion rcu_barrier_completion;
+int rcu_scheduler_active __read_mostly;
 
 /*
  * Awaken the corresponding synchronize_rcu() instance now that a
@@ -167,3 +169,9 @@ void __init rcu_init(void)
 	__rcu_init();
 }
 
+void rcu_scheduler_starting(void)
+{
+	WARN_ON(num_online_cpus() != 1);
+	WARN_ON(nr_context_switches() > 0);
+	rcu_scheduler_active = 1;
+}
kernel/rcupreempt.c +26 -0
index 2782793..e353a01 100644
@@ -559,6 +559,16 @@ void rcu_irq_exit(void)
 	}
 }
 
+void rcu_nmi_enter(void)
+{
+	rcu_irq_enter();
+}
+
+void rcu_nmi_exit(void)
+{
+	rcu_irq_exit();
+}
+
 static void dyntick_save_progress_counter(int cpu)
 {
 	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
@@ -1174,7 +1184,23 @@ EXPORT_SYMBOL_GPL(call_rcu_sched);
  * in -rt this does -not- necessarily result in all currently executing
  * interrupt -handlers- having completed.
  */
+<<<<<<< HEAD:kernel/rcupreempt.c
 synchronize_rcu_xxx(__synchronize_sched, call_rcu_sched)
+=======
+void __synchronize_sched(void)
+{
+	struct rcu_synchronize rcu;
+
+	if (num_online_cpus() == 1)
+		return;  /* blocking is gp if only one CPU! */
+
+	init_completion(&rcu.completion);
+	/* Will wake me after RCU finished. */
+	call_rcu_sched(&rcu.head, wakeme_after_rcu);
+	/* Wait for it. */
+	wait_for_completion(&rcu.completion);
+}
+>>>>>>> a682604... rcu: Teach RCU that idle task is not quiscent state at boot:kernel/rcupreempt.c
 EXPORT_SYMBOL_GPL(__synchronize_sched);
 
 /*
kernel/rcupreempt_trace.c +5 -5
index 5edf82c..def42e8 100644
@@ -149,12 +149,12 @@ static void rcupreempt_trace_sum(struct rcupreempt_trace *sp)
 		sp->done_length += cp->done_length;
 		sp->done_add += cp->done_add;
 		sp->done_remove += cp->done_remove;
-		atomic_set(&sp->done_invoked, atomic_read(&cp->done_invoked));
+		atomic_add(atomic_read(&cp->done_invoked), &sp->done_invoked);
 		sp->rcu_check_callbacks += cp->rcu_check_callbacks;
-		atomic_set(&sp->rcu_try_flip_1,
-			   atomic_read(&cp->rcu_try_flip_1));
-		atomic_set(&sp->rcu_try_flip_e1,
-			   atomic_read(&cp->rcu_try_flip_e1));
+		atomic_add(atomic_read(&cp->rcu_try_flip_1),
+			   &sp->rcu_try_flip_1);
+		atomic_add(atomic_read(&cp->rcu_try_flip_e1),
+			   &sp->rcu_try_flip_e1);
 		sp->rcu_try_flip_i1 += cp->rcu_try_flip_i1;
 		sp->rcu_try_flip_ie1 += cp->rcu_try_flip_ie1;
 		sp->rcu_try_flip_g1 += cp->rcu_try_flip_g1;
kernel/rcutree.c new file +1534 -0

1,534 lines not on this page · raw message

kernel/rcutree_trace.c new file +329 -0

329 lines not on this page · raw message

kernel/softirq.c +3 -2
index c506f26..ff5db2e 100644
@@ -256,6 +256,7 @@ void irq_enter(void)
 {
 #ifdef CONFIG_NO_HZ
 	int cpu = smp_processor_id();
+	rcu_irq_enter();
 	if (idle_cpu(cpu) && !in_interrupt())
 		tick_nohz_stop_idle(cpu);
 #endif
@@ -285,9 +286,9 @@ void irq_exit(void)
 
 #ifdef CONFIG_NO_HZ
 	/* Make sure that timer wheel updates are propagated */
-	if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched())
-		tick_nohz_stop_sched_tick(0);
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

lib/Kconfig.debug +13 -0
index 0b50481..1687d6d 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Part of the diff is not shown here. View the raw message.