Skip to contentSkip to conversation

RCU priority boosting that survives semi-vicious testing

RFC PATCH In review Paul E. McKenney 3 participants 14 messages Jan 25, 2007 – Jan 29, 2007

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

cover RCU priority boosting that survives semi-vicious testing

Hello!

This is an update of the patch at http://lkml.org/lkml/2007/01/15/219.
This series contains (1) the RCU-boost patch itself and (2) some rcutorture modifications to test it more thoroughly. This version is getting quite close.

There is still no OOM tie-in. As before, dynamic adjustment of the booster's priority will dynamically adjust the boostees.

Thanx, Paul

#1 lore raw

RFC PATCH 1/2 RCU priority boosting that survives semi-vicious testing

Here is the priority-boosting piece of the set. Very similar to the earlier patch (http://lkml.org/lkml/2007/01/15/219).

---

 include/linux/init_task.h  |   12 +
 include/linux/rcupdate.h   |   12 +
 include/linux/rcupreempt.h |   19 +
 include/linux/sched.h      |   16 +
 init/main.c                |    1 
 kernel/Kconfig.preempt     |   32 ++
 kernel/fork.c              |    6 
 kernel/rcupreempt.c        |  536 +++++++++++++++++++++++++++++++++++++++++++++
 kernel/rtmutex.c           |    9 
 kernel/sched.c             |    5 
 10 files changed, 645 insertions(+), 3 deletions(-)
include/linux/init_task.h +12 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/init_task.h linux-2.6.20-rc4-rt1-rcub/include/linux/init_task.h
@@ -87,6 +87,17 @@ extern struct nsproxy init_nsproxy;
 	.siglock	= __SPIN_LOCK_UNLOCKED(sighand.siglock),	\
 }
 
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+#define INIT_RCU_BOOST_PRIO .rcu_prio	= MAX_PRIO,
+#define INIT_PREEMPT_RCU_BOOST(tsk)					\
+	.rcub_rbdp	= NULL,						\
+	.rcub_state	= RCU_BOOST_IDLE,				\
+	.rcub_entry	= LIST_HEAD_INIT(tsk.rcub_entry),
+#else /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+#define INIT_RCU_BOOST_PRIO
+#define INIT_PREEMPT_RCU_BOOST(tsk)
+#endif /* #else #ifdef CONFIG_PREEMPT_RCU_BOOST */
+
 extern struct group_info init_groups;
 
 /*
@@ -143,6 +154,7 @@ extern struct group_info init_groups;
 	.pi_lock	= RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock),		\
 	INIT_TRACE_IRQFLAGS						\
 	INIT_LOCKDEP							\
+	INIT_PREEMPT_RCU_BOOST(tsk)					\
 }
 
 
include/linux/rcupdate.h +12 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/rcupdate.h linux-2.6.20-rc4-rt1-rcub/include/linux/rcupdate.h
@@ -227,6 +227,18 @@ extern void rcu_barrier(void);
 extern void rcu_init(void);
 extern void rcu_advance_callbacks(int cpu, int user);
 extern void rcu_check_callbacks(int cpu, int user);
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+extern void init_rcu_boost_late(void);
+extern void __rcu_preempt_boost(void);
+#define rcu_preempt_boost() \
+	do { \
+		if (unlikely(current->rcu_read_lock_nesting > 0)) \
+			__rcu_preempt_boost(); \
+	} while (0)
+#else /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+#define init_rcu_boost_late()
+#define rcu_preempt_boost()
+#endif /* #else #ifdef CONFIG_PREEMPT_RCU_BOOST */
 
 #endif /* __KERNEL__ */
 #endif /* __LINUX_RCUPDATE_H */
include/linux/rcupreempt.h +19 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/rcupreempt.h linux-2.6.20-rc4-rt1-rcub/include/linux/rcupreempt.h
@@ -42,6 +42,25 @@
 #include <linux/cpumask.h>
 #include <linux/seqlock.h>
 
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+/*
+ * Task state with respect to being RCU-boosted.  This state is changed
+ * by the task itself in response to the following three events:
+ * 1. Preemption (or block on lock) while in RCU read-side critical section.
+ * 2. Outermost rcu_read_unlock() for blocked RCU read-side critical section.
+ *
+ * The RCU-boost task also updates the state when boosting priority.
+ */
+enum rcu_boost_state {
+	RCU_BOOST_IDLE = 0,	   /* Not yet blocked if in RCU read-side. */
+	RCU_BOOST_BLOCKED = 1,	   /* Blocked from RCU read-side. */
+	RCU_BOOSTED = 2,	   /* Boosting complete. */
+};
+
+#define N_RCU_BOOST_STATE (RCU_BOOSTED + 1)
+
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+
 #define rcu_qsctr_inc(cpu)
 #define rcu_bh_qsctr_inc(cpu)
 #define call_rcu_bh(head, rcu) call_rcu(head, rcu)
include/linux/sched.h +16 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/sched.h linux-2.6.20-rc4-rt1-rcub/include/linux/sched.h
@@ -699,6 +699,14 @@ struct signal_struct {
 #define is_rt_policy(p)		((p) != SCHED_NORMAL && (p) != SCHED_BATCH)
 #define has_rt_policy(p)	unlikely(is_rt_policy((p)->policy))
 
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+#define set_rcu_prio(p, prio)  ((p)->rcu_prio = (prio))
+#define get_rcu_prio(p) ((p)->rcu_prio)
+#else /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+#define set_rcu_prio(p, prio)  do { } while (0)
+#define get_rcu_prio(p) MAX_PRIO
+#endif /* #else #ifdef CONFIG_PREEMPT_RCU_BOOST */
+
 /*
  * Some day this will be a full-fledged user tracking system..
  */
@@ -982,6 +990,9 @@ struct task_struct {
 #endif
 	int load_weight;	/* for niceness load balancing purposes */
 	int prio, static_prio, normal_prio;
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+	int rcu_prio;
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
 	struct list_head run_list;
 	struct prio_array *array;
 
@@ -1003,6 +1014,11 @@ struct task_struct {
         atomic_t *rcu_flipctr1;
         atomic_t *rcu_flipctr2;
 #endif
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+	struct rcu_boost_dat *rcub_rbdp;
+	enum rcu_boost_state rcub_state;
+	struct list_head rcub_entry;
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
 
 #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
 	struct sched_info sched_info;
init/main.c +1 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/init/main.c linux-2.6.20-rc4-rt1-rcub/init/main.c
@@ -712,6 +712,7 @@ static void __init do_basic_setup(void)
 	init_workqueues();
 	usermodehelper_init();
 	driver_init();
+	init_rcu_boost_late();
 
 #ifdef CONFIG_SYSCTL
 	sysctl_init();
kernel/Kconfig.preempt +32 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/Kconfig.preempt linux-2.6.20-rc4-rt1-rcub/kernel/Kconfig.preempt
@@ -176,3 +176,35 @@ config RCU_TRACE
 
 	  Say Y here if you want to enable RCU tracing
 	  Say N if you are unsure.
+
+config PREEMPT_RCU_BOOST
+	bool "Enable priority boosting of RCU read-side critical sections"
+	depends on PREEMPT_RCU
+	default n
+	help
+	  This option permits priority boosting of RCU read-side critical
+	  sections that have been preempted in order to prevent indefinite
+	  delay of grace periods in face of runaway non-realtime processes.
+
+	  Say N if you are unsure.
+
+config PREEMPT_RCU_BOOST_STATS
+	bool "Enable RCU priority-boosting statistic printing"
+	depends on PREEMPT_RCU_BOOST
+	default n
+	help
+	  This option enables debug printk()s of RCU boost statistics,
+	  which are normally only used to debug RCU priority boost
+	  implementations.
+
+	  Say N if you are unsure.
+
+config PREEMPT_RCU_BOOST_STATS_INTERVAL
+	int "RCU priority-boosting statistic printing interval (seconds)"
+	depends on PREEMPT_RCU_BOOST_STATS
+	default 100
+	range 10 86400
+	help
+	  This option controls the timing of debug printk()s of RCU boost
+	  statistics, which are normally only used to debug RCU priority
+	  boost implementations.
kernel/fork.c +6 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/fork.c linux-2.6.20-rc4-rt1-rcub/kernel/fork.c
@@ -1129,6 +1129,12 @@ static struct task_struct *copy_process(
 	p->softirq_context = 0;
 #endif
 	p->pagefault_disabled = 0;
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+	p->rcu_prio = MAX_PRIO;
+	p->rcub_rbdp = NULL;
+	p->rcub_state = RCU_BOOST_IDLE;
+	INIT_LIST_HEAD(&p->rcub_entry);
+#endif
 #ifdef CONFIG_LOCKDEP
 	p->lockdep_depth = 0; /* no locks held yet */
 	p->curr_chain_key = 0;
kernel/rcupreempt.c +536 -0

566 lines not on this page · raw message

kernel/rtmutex.c +6 -3
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rtmutex.c linux-2.6.20-rc4-rt1-rcub/kernel/rtmutex.c
@@ -105,11 +105,14 @@ static inline void init_lists(struct rt_
  */
 int rt_mutex_getprio(struct task_struct *task)
 {
+	int prio = task->normal_prio;
+
+	if (get_rcu_prio(task) < prio)
+		prio = get_rcu_prio(task);
 	if (likely(!task_has_pi_waiters(task)))
-		return task->normal_prio;
+		return prio;
 
-	return min(task_top_pi_waiter(task)->pi_list_entry.prio,
-		   task->normal_prio);
+	return min(task_top_pi_waiter(task)->pi_list_entry.prio, prio);
 }
 
 /*
kernel/sched.c +5 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/sched.c linux-2.6.20-rc4-rt1-rcub/kernel/sched.c
@@ -1962,6 +1962,7 @@ void fastcall sched_fork(struct task_str
 	 * Make sure we do not leak PI boosting priority to the child:
 	 */
 	p->prio = current->normal_prio;
+	set_rcu_prio(p, MAX_PRIO);
 
 	INIT_LIST_HEAD(&p->run_list);
 	p->array = NULL;
@@ -2044,6 +2045,7 @@ void fastcall wake_up_new_task(struct ta
 			else {
 				p->prio = current->prio;
 				p->normal_prio = current->normal_prio;
+				set_rcu_prio(p, MAX_PRIO);
 				__activate_task_after(p, current, rq);
 			}
 			set_need_resched();
@@ -3986,6 +3988,8 @@ void __sched __schedule(void)
 	}
 	profile_hit(SCHED_PROFILING, __builtin_return_address(0));
 
+	rcu_preempt_boost();
+
 	preempt_disable(); // FIXME: disable irqs here
 	prev = current;
 	release_kernel_lock(prev);
@@ -5725,6 +5729,7 @@ void __cpuinit init_idle(struct task_str
 	idle->sleep_avg = 0;
 	idle->array = NULL;
 	idle->prio = idle->normal_prio = MAX_PRIO;
+	set_rcu_prio(idle, MAX_PRIO);
 	idle->state = TASK_RUNNING;
 	idle->cpus_allowed = cpumask_of_cpu(cpu);
 	set_task_cpu(idle, cpu);
#2 lore raw

RFC PATCH 2/2 RCU priority boosting additions to rcutorture

This patch adds an optional preemption kernel thread to the rcutorture tests. This thread sets itself to a low RT priority and chews up CPU in 10-second bursts, verifying that grace periods progress during this 10-second interval. This has thus far passed about 30 hours of RCU torture testing on a 4-CPU (a pair of 2-CPU dies) 64-bit Xeon system.

I am experimenting with more-vicious tests, but extra viciousness thus far comes at the expense of grotesque code.

---

 rcutorture.c |   85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 84 insertions(+), 1 deletion(-)
kernel/rcutorture.c +84 -1
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcutorture.c linux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.c
@@ -194,6 +194,8 @@ struct rcu_torture_ops {
 	int (*completed)(void);
 	void (*deferredfree)(struct rcu_torture *p);
 	void (*sync)(void);
+	void (*preemptstart)(void);
+	void (*preemptend)(void);
 	int (*stats)(char *page);
 	char *name;
 };
@@ -258,6 +260,71 @@ static void rcu_torture_deferred_free(st
 	call_rcu(&p->rtort_rcu, rcu_torture_cb);
 }
 
+#ifndef CONFIG_PREEMPT_RCU_BOOST
+static void rcu_preempt_start(void) { }
+static void rcu_preempt_end(void) { }
+static int rcu_preempt_stats(char *page) { return 0; }
+#else /* #ifndef CONFIG_PREEMPT_RCU_BOOST */
+
+static struct task_struct *rcu_preeempt_task;
+static long rcu_torture_preempt_errors = 0;
+
+static int rcu_torture_preempt(void *arg)
+{
+	int completedstart;
+	time_t gcstart;
+	struct sched_param sp;
+
+	sp.sched_priority = MAX_RT_PRIO - 1;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;
+
+	do {
+		completedstart = rcu_torture_completed();
+		gcstart = xtime.tv_sec;
+		while ((xtime.tv_sec - gcstart < 10) &&
+		       (rcu_torture_completed() == completedstart))
+			cond_resched();
+		if (rcu_torture_completed() == completedstart)
+			rcu_torture_preempt_errors++;
+		schedule_timeout_interruptible(shuffle_interval * HZ);
+	} while (!kthread_should_stop());
+	return NULL;
+}
+
+static void rcu_preempt_start(void)
+{
+	rcu_preeempt_task = kthread_run(rcu_torture_preempt, NULL,
+					"rcu_torture_preempt");
+	if (IS_ERR(rcu_preeempt_task)) {
+		VERBOSE_PRINTK_ERRSTRING("Failed to create preempter");
+		rcu_preeempt_task = NULL;
+	}
+}
+
+static void rcu_preempt_end(void)
+{
+	if (rcu_preeempt_task != NULL) {
+		VERBOSE_PRINTK_STRING("Stopping rcu_preempt task");
+		kthread_stop(rcu_preeempt_task);
+	}
+	rcu_preeempt_task = NULL;
+}
+
+static int rcu_preempt_stats(char *page) {
+	int cnt = 0;
+
+	cnt += sprintf(&page[cnt],
+		       "Preemption stalls: %ld\n", rcu_torture_preempt_errors);
+	return (cnt);
+}
+#endif /* #else #ifndef CONFIG_PREEMPT_RCU_BOOST */
+
+static void rcu_preemptstart(void)
+{
+	
+}
+
 static struct rcu_torture_ops rcu_ops = {
 	.init = NULL,
 	.cleanup = NULL,
@@ -267,7 +334,9 @@ static struct rcu_torture_ops rcu_ops = 
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_torture_deferred_free,
 	.sync = synchronize_rcu,
-	.stats = NULL,
+	.preemptstart = rcu_preempt_start,
+	.preemptend = rcu_preempt_end,
+	.stats = rcu_preempt_stats,
 	.name = "rcu"
 };
 
@@ -306,6 +375,8 @@ static struct rcu_torture_ops rcu_sync_o
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = synchronize_rcu,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_sync"
 };
@@ -370,6 +441,8 @@ static struct rcu_torture_ops rcu_bh_ops
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_bh_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_bh"
 };
@@ -383,6 +456,8 @@ static struct rcu_torture_ops rcu_bh_syn
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_bh_sync"
 };
@@ -464,6 +539,8 @@ static struct rcu_torture_ops srcu_ops =
 	.completed = srcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = srcu_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = srcu_torture_stats,
 	.name = "srcu"
 };
@@ -502,6 +579,8 @@ static struct rcu_torture_ops sched_ops 
 	.completed = sched_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = sched_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "sched"
 };
@@ -856,6 +935,8 @@ rcu_torture_cleanup(void)
 		kthread_stop(stats_task);
 	}
 	stats_task = NULL;
+	if (cur_ops->preemptend != NULL)
+		cur_ops->preemptend();
 
 	/* Wait for all RCU callbacks to fire.  */
 	rcu_barrier();
@@ -997,6 +1078,8 @@ rcu_torture_init(void)
 			goto unwind;
 		}
 	}
+	if (cur_ops->preemptstart != NULL)
+		cur_ops->preemptstart();
 	return 0;
 
 unwind:
#3 lore raw

Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture

Quoted 9 lines · Paul E. McKenney

This patch adds an optional preemption kernel thread to the rcutorture tests. This thread sets itself to a low RT priority and chews up CPU in 10-second bursts, verifying that grace periods progress during this 10-second interval. This has thus far passed about 30 hours of RCU torture testing on a 4-CPU (a pair of 2-CPU dies) 64-bit Xeon system.

I am experimenting with more-vicious tests, but extra viciousness thus far comes at the expense of grotesque code.

Overall, the new feature seems like a good idea, and it should exercise the new RCU boosting code. Some comments below.

One major item: this new test feature really needs a new module parameter to enable or disable it.

Quoted 14 lines

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcutorture.c linux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.c

kernel/rcutorture.c +2 -0
@@ -194,6 +194,8 @@ struct rcu_torture_ops {
 	int (*completed)(void);
 	void (*deferredfree)(struct rcu_torture *p);
 	void (*sync)(void);
+	void (*preemptstart)(void);
+	void (*preemptend)(void);
 	int (*stats)(char *page);
 	char *name;
 };
@@ -258,6 +260,71 @@ static void rcu_torture_deferred_free(st
 	call_rcu(&p->rtort_rcu, rcu_torture_cb);

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 270

Quoted 10 lines
kernel/rcutorture.c +8 -0
 }
 
+#ifndef CONFIG_PREEMPT_RCU_BOOST
+static void rcu_preempt_start(void) { }
+static void rcu_preempt_end(void) { }
+static int rcu_preempt_stats(char *page) { return 0; }
+#else /* #ifndef CONFIG_PREEMPT_RCU_BOOST */
+
+static struct task_struct *rcu_preeempt_task;
+static long rcu_torture_preempt_errors = 0;

Might as well make this an unsigned long; negative values wouldn't make sense.

Quoted 9 lines
kernel/rcutorture.c +9 -0
+static int rcu_torture_preempt(void *arg)
+{
+	int completedstart;
+	time_t gcstart;
+	struct sched_param sp;
+
+	sp.sched_priority = MAX_RT_PRIO - 1;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 290

Quoted 10 lines
kernel/rcutorture.c +10 -0
+
+	do {
+		completedstart = rcu_torture_completed();
+		gcstart = xtime.tv_sec;
+		while ((xtime.tv_sec - gcstart < 10) &&
+		       (rcu_torture_completed() == completedstart))
+			cond_resched();
+		if (rcu_torture_completed() == completedstart)
+			rcu_torture_preempt_errors++;
+		schedule_timeout_interruptible(shuffle_interval * HZ);

Why call schedule_timeout_interruptible here without actually handling interruptions? So that you can send it a signal to cause the shuffle early?

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 300

Quoted 10 lines
kernel/rcutorture.c +10 -0
+	} while (!kthread_should_stop());
+	return NULL;
+}
+
+static void rcu_preempt_start(void)
+{
+	rcu_preeempt_task = kthread_run(rcu_torture_preempt, NULL,
+					"rcu_torture_preempt");
+	if (IS_ERR(rcu_preeempt_task)) {
+		VERBOSE_PRINTK_ERRSTRING("Failed to create preempter");

This ought to include the errno value, PTR_ERR(rcu_preempt_task).

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 307

Quoted 7 lines
kernel/rcutorture.c +7 -0
+		rcu_preeempt_task = NULL;
+	}
+}
+
+static void rcu_preempt_end(void)
+{
+	if (rcu_preeempt_task != NULL) {

if (rcu_preempt_task) would work just as well here.

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 320

Quoted 13 lines
kernel/rcutorture.c +13 -0
+		VERBOSE_PRINTK_STRING("Stopping rcu_preempt task");
+		kthread_stop(rcu_preeempt_task);
+	}
+	rcu_preeempt_task = NULL;
+}
+
+static int rcu_preempt_stats(char *page) {
+	int cnt = 0;
+
+	cnt += sprintf(&page[cnt],
+		       "Preemption stalls: %ld\n", rcu_torture_preempt_errors);
+	return (cnt);
+}

How about just:
return sprintf(page, ...);
?

Also, if you decide to make rcu_torture_preempt_errors an unsigned long as suggested above, this should use %lu.

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 327

Quoted 7 lines
kernel/rcutorture.c +7 -0
+#endif /* #else #ifndef CONFIG_PREEMPT_RCU_BOOST */
+
+static void rcu_preemptstart(void)
+{
+	
+}
+

This looks like a bit of stray code.

Quoted 15 lines
kernel/rcutorture.c +3 -1
 static struct rcu_torture_ops rcu_ops = {
 	.init = NULL,
 	.cleanup = NULL,
@@ -267,7 +334,9 @@ static struct rcu_torture_ops rcu_ops = 
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_torture_deferred_free,
 	.sync = synchronize_rcu,
-	.stats = NULL,
+	.preemptstart = rcu_preempt_start,
+	.preemptend = rcu_preempt_end,
+	.stats = rcu_preempt_stats,
 	.name = "rcu"
 };
 

kernel/rcutorture.c static struct rcu_torture_ops rcu_sync_o · line 382

Quoted 8 lines
kernel/rcutorture.c +2 -0
@@ -306,6 +375,8 @@ static struct rcu_torture_ops rcu_sync_o
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = synchronize_rcu,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_sync"
 };

Much like other common structures such as struct file_operations, no need to explicitly specify members as NULL here; any member you don't specify will get a NULL value. That avoids the need to update every use of this structure whenever you add a new member used by only some of them.

kernel/rcutorture.c static struct rcu_torture_ops rcu_bh_ops · line 448

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -370,6 +441,8 @@ static struct rcu_torture_ops rcu_bh_ops
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_bh_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_bh"
 };

Likewise.

kernel/rcutorture.c static struct rcu_torture_ops rcu_bh_syn · line 463

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -383,6 +456,8 @@ static struct rcu_torture_ops rcu_bh_syn
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_bh_sync"
 };

Likewise.

kernel/rcutorture.c static struct rcu_torture_ops srcu_ops = · line 546

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -464,6 +539,8 @@ static struct rcu_torture_ops srcu_ops =
 	.completed = srcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = srcu_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = srcu_torture_stats,
 	.name = "srcu"
 };

Likewise.

kernel/rcutorture.c static struct rcu_torture_ops sched_ops · line 585

Quoted 8 lines
kernel/rcutorture.c +2 -0
@@ -502,6 +579,8 @@ static struct rcu_torture_ops sched_ops 
 	.completed = sched_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = sched_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "sched"

Likewise.

kernel/rcutorture.c rcu_torture_cleanup(void) · line 938

Quoted 5 lines
kernel/rcutorture.c +1 -0
@@ -856,6 +935,8 @@ rcu_torture_cleanup(void)
 		kthread_stop(stats_task);
 	}
 	stats_task = NULL;
+	if (cur_ops->preemptend != NULL)

if (cur_ops->preemptend) would work as well.

kernel/rcutorture.c rcu_torture_init(void) · line 1081

Quoted 5 lines
kernel/rcutorture.c +1 -0
@@ -997,6 +1078,8 @@ rcu_torture_init(void)
 			goto unwind;
 		}
 	}
+	if (cur_ops->preemptstart != NULL)

Likewise.

- Josh Triplett

#4 lore raw

Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture

Quoted 8 lines · Josh Triplett
Quoted 7 lines · Paul E. McKenney

This patch adds an optional preemption kernel thread to the rcutorture tests. This thread sets itself to a low RT priority and chews up CPU in 10-second bursts, verifying that grace periods progress during this 10-second interval. This has thus far passed about 30 hours of RCU torture testing on a 4-CPU (a pair of 2-CPU dies) 64-bit Xeon system.

Quoted 5 lines
Quoted 2 lines

I am experimenting with more-vicious tests, but extra viciousness thus far comes at the expense of grotesque code.

Overall, the new feature seems like a good idea, and it should exercise the new RCU boosting code. Some comments below.

Thank you for the review!!!

Quoted 2 lines

One major item: this new test feature really needs a new module parameter to enable or disable it.

CONFIG_PREEMPT_RCU_BOOST is the parameter -- if not set, then no test. This parameter is provided by the accompanying RCU-boost patch.

Quoted 16 lines
Quoted 16 lines

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcutorture.c linux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.c

kernel/rcutorture.c +2 -0
@@ -194,6 +194,8 @@ struct rcu_torture_ops {
 	int (*completed)(void);
 	void (*deferredfree)(struct rcu_torture *p);
 	void (*sync)(void);
+	void (*preemptstart)(void);
+	void (*preemptend)(void);
 	int (*stats)(char *page);
 	char *name;
 };
@@ -258,6 +260,71 @@ static void rcu_torture_deferred_free(st
 	call_rcu(&p->rtort_rcu, rcu_torture_cb);
 }
 
Quoted 10 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 270

Quoted 8 lines
kernel/rcutorture.c +8 -0
+#ifndef CONFIG_PREEMPT_RCU_BOOST
+static void rcu_preempt_start(void) { }
+static void rcu_preempt_end(void) { }
+static int rcu_preempt_stats(char *page) { return 0; }
+#else /* #ifndef CONFIG_PREEMPT_RCU_BOOST */
+
+static struct task_struct *rcu_preeempt_task;
+static long rcu_torture_preempt_errors = 0;

Might as well make this an unsigned long; negative values wouldn't make sense.

Good point, fixed.

Quoted 14 lines
Quoted 14 lines
kernel/rcutorture.c +14 -0
+static int rcu_torture_preempt(void *arg)
+{
+	int completedstart;
+	time_t gcstart;
+	struct sched_param sp;
+
+	sp.sched_priority = MAX_RT_PRIO - 1;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;
+
+	do {
+		completedstart = rcu_torture_completed();
+		gcstart = xtime.tv_sec;
+		while ((xtime.tv_sec - gcstart < 10) &&
Quoted 8 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 290

Quoted 5 lines
kernel/rcutorture.c +5 -0
+		       (rcu_torture_completed() == completedstart))
+			cond_resched();
+		if (rcu_torture_completed() == completedstart)
+			rcu_torture_preempt_errors++;
+		schedule_timeout_interruptible(shuffle_interval * HZ);

Why call schedule_timeout_interruptible here without actually handling interruptions? So that you can send it a signal to cause the shuffle early?

It allows you to kill the process in order to get the module unload to happen more quickly in case someone specified an overly long interval. But now that you mention this, a simple one-second sleep is probably appropriate here.

Quoted 4 lines
Quoted 4 lines
kernel/rcutorture.c +4 -0
+	} while (!kthread_should_stop());
+	return NULL;
+}
+
Quoted 8 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 300

Quoted 6 lines
kernel/rcutorture.c +6 -0
+static void rcu_preempt_start(void)
+{
+	rcu_preeempt_task = kthread_run(rcu_torture_preempt, NULL,
+					"rcu_torture_preempt");
+	if (IS_ERR(rcu_preeempt_task)) {
+		VERBOSE_PRINTK_ERRSTRING("Failed to create preempter");

This ought to include the errno value, PTR_ERR(rcu_preempt_task).

Good point -- what I should do is return this value so that rcu_torture_init() can return it, failing the module-load process and unwinding.

Quoted 9 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 307

Quoted 7 lines
kernel/rcutorture.c +7 -0
+		rcu_preeempt_task = NULL;
+	}
+}
+
+static void rcu_preempt_end(void)
+{
+	if (rcu_preeempt_task != NULL) {

if (rcu_preempt_task) would work just as well here.

True, but was being consistent with usage elsewhere in this file.

Quoted 9 lines
Quoted 9 lines
kernel/rcutorture.c +9 -0
+		VERBOSE_PRINTK_STRING("Stopping rcu_preempt task");
+		kthread_stop(rcu_preeempt_task);
+	}
+	rcu_preeempt_task = NULL;
+}
+
+static int rcu_preempt_stats(char *page) {
+	int cnt = 0;
+
Quoted 8 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 320

Quoted 4 lines
kernel/rcutorture.c +4 -0
+	cnt += sprintf(&page[cnt],
+		       "Preemption stalls: %ld\n", rcu_torture_preempt_errors);
+	return (cnt);
+}

How about just:
return sprintf(page, ...);
?

Good point.

Quoted 2 lines

Also, if you decide to make rcu_torture_preempt_errors an unsigned long as suggested above, this should use %lu.

Done.

Quoted 9 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 327

Quoted 7 lines
kernel/rcutorture.c +7 -0
+#endif /* #else #ifndef CONFIG_PREEMPT_RCU_BOOST */
+
+static void rcu_preemptstart(void)
+{
+	
+}
+

This looks like a bit of stray code.

Yep, good eyes! Deleted.

Quoted 14 lines
Quoted 14 lines
kernel/rcutorture.c +3 -1
 static struct rcu_torture_ops rcu_ops = {
 	.init = NULL,
 	.cleanup = NULL,
@@ -267,7 +334,9 @@ static struct rcu_torture_ops rcu_ops = 
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_torture_deferred_free,
 	.sync = synchronize_rcu,
-	.stats = NULL,
+	.preemptstart = rcu_preempt_start,
+	.preemptend = rcu_preempt_end,
+	.stats = rcu_preempt_stats,
 	.name = "rcu"
 };
 
Quoted 14 lines

kernel/rcutorture.c static struct rcu_torture_ops rcu_sync_o · line 382

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -306,6 +375,8 @@ static struct rcu_torture_ops rcu_sync_o
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = synchronize_rcu,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_sync"
 };

Much like other common structures such as struct file_operations, no need to explicitly specify members as NULL here; any member you don't specify will get a NULL value. That avoids the need to update every use of this structure whenever you add a new member used by only some of them.

Untrusting, aren't I? ;-)

I removed all the "= NULL" entries.

Quoted 47 lines

kernel/rcutorture.c static struct rcu_torture_ops rcu_bh_ops · line 448

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -370,6 +441,8 @@ static struct rcu_torture_ops rcu_bh_ops
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_bh_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_bh"
 };

Likewise.

kernel/rcutorture.c static struct rcu_torture_ops rcu_bh_syn · line 463

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -383,6 +456,8 @@ static struct rcu_torture_ops rcu_bh_syn
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_bh_sync"
 };

Likewise.

kernel/rcutorture.c static struct rcu_torture_ops srcu_ops = · line 546

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -464,6 +539,8 @@ static struct rcu_torture_ops srcu_ops =
 	.completed = srcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = srcu_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = srcu_torture_stats,
 	.name = "srcu"
 };

Likewise.

kernel/rcutorture.c static struct rcu_torture_ops sched_ops · line 585

Quoted 8 lines
kernel/rcutorture.c +2 -0
@@ -502,6 +579,8 @@ static struct rcu_torture_ops sched_ops 
 	.completed = sched_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = sched_torture_synchronize,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "sched"

Likewise.

Quoted 7 lines

kernel/rcutorture.c rcu_torture_cleanup(void) · line 938

Quoted 5 lines
kernel/rcutorture.c +1 -0
@@ -856,6 +935,8 @@ rcu_torture_cleanup(void)
 		kthread_stop(stats_task);
 	}
 	stats_task = NULL;
+	if (cur_ops->preemptend != NULL)

if (cur_ops->preemptend) would work as well.

True, though again there is a lot of existing "!= NULL" in this file and elsewhere. Many thousands of them through the kernel. ;-)

Quoted 7 lines

kernel/rcutorture.c rcu_torture_init(void) · line 1081

Quoted 5 lines
kernel/rcutorture.c +1 -0
@@ -997,6 +1078,8 @@ rcu_torture_init(void)
 			goto unwind;
 		}
 	}
+	if (cur_ops->preemptstart != NULL)

Likewise.

I will run this through the mill and repost.

Thanx, Paul

#5 lore raw

Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture

Quoted 6 lines · Paul E. McKenney
Quoted 2 lines · Josh Triplett

One major item: this new test feature really needs a new module parameter to enable or disable it.

CONFIG_PREEMPT_RCU_BOOST is the parameter -- if not set, then no test. This parameter is provided by the accompanying RCU-boost patch.

It seems useful for rcutorture to use or not use the preempting thread independently of CONFIG_PREEMPT_RCU_BOOST. That would bring you from two cases to four, and the two new cases both make sense:

* CONFIG_PREEMPT_RCU_BOOST=n, but run rcutorture with the preempting thread. This configuration allows you to demonstrate the need for CONFIG_PREEMPT_RCU_BOOST, by showing what happens when you need it and don't have it.

* CONFIG_PREEMPT_RCU_BOOST=y, but run rcutorture without the preempting thread. This configuration allows you to test with rcutorture while running a *real* real-time workload rather than the simple preempting thread, or just test basic RCU functionality.

A simple boolean module_param would work here.

At some point, we may want to add the ability to run multiple preempting threads, but that doesn't need to happen for this patch.

Quoted 27 lines
Quoted 18 lines
Quoted 17 lines · Paul E. McKenney

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcutorture.c linux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.c

kernel/rcutorture.c +13 -0
 
+static int rcu_torture_preempt(void *arg)
+{
+	int completedstart;
+	time_t gcstart;
+	struct sched_param sp;
+
+	sp.sched_priority = MAX_RT_PRIO - 1;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;
+
+	do {
+		completedstart = rcu_torture_completed();
+		gcstart = xtime.tv_sec;
Quoted 8 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 290

Quoted 6 lines
kernel/rcutorture.c +6 -0
+		while ((xtime.tv_sec - gcstart < 10) &&
+		       (rcu_torture_completed() == completedstart))
+			cond_resched();
+		if (rcu_torture_completed() == completedstart)
+			rcu_torture_preempt_errors++;
+		schedule_timeout_interruptible(shuffle_interval * HZ);

Why call schedule_timeout_interruptible here without actually handling interruptions? So that you can send it a signal to cause the shuffle early?

Quoted 2 lines

It allows you to kill the process in order to get the module unload to happen more quickly in case someone specified an overly long interval.

I didn't actually know that you could kill a kthread from userspace. :)

That rationale makes sense.

Quoted 2 lines

But now that you mention this, a simple one-second sleep is probably appropriate here.

OK.

Quoted 12 lines
Quoted 11 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 300

Quoted 10 lines
kernel/rcutorture.c +10 -0
+	} while (!kthread_should_stop());
+	return NULL;
+}
+
+static void rcu_preempt_start(void)
+{
+	rcu_preeempt_task = kthread_run(rcu_torture_preempt, NULL,
+					"rcu_torture_preempt");
+	if (IS_ERR(rcu_preeempt_task)) {
+		VERBOSE_PRINTK_ERRSTRING("Failed to create preempter");

This ought to include the errno value, PTR_ERR(rcu_preempt_task).

Quoted 3 lines

Good point -- what I should do is return this value so that rcu_torture_init() can return it, failing the module-load process and unwinding.

Even better, yes.

Quoted 10 lines
Quoted 8 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 307

Quoted 7 lines
kernel/rcutorture.c +7 -0
+		rcu_preeempt_task = NULL;
+	}
+}
+
+static void rcu_preempt_end(void)
+{
+	if (rcu_preeempt_task != NULL) {

if (rcu_preempt_task) would work just as well here.

True, but was being consistent with usage elsewhere in this file.

Fair enough; don't worry about it for this patch, then. I'll deal with that particular style cleanup later, throughout rcutorture.

Quoted 28 lines
Quoted 14 lines
Quoted 14 lines

static struct rcu_torture_ops rcu_ops = {

kernel/rcutorture.c +3 -1
 	.init = NULL,
 	.cleanup = NULL,
@@ -267,7 +334,9 @@ static struct rcu_torture_ops rcu_ops = 
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_torture_deferred_free,
 	.sync = synchronize_rcu,
-	.stats = NULL,
+	.preemptstart = rcu_preempt_start,
+	.preemptend = rcu_preempt_end,
+	.stats = rcu_preempt_stats,
 	.name = "rcu"
 };
 
Quoted 13 lines

kernel/rcutorture.c static struct rcu_torture_ops rcu_sync_o · line 382

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -306,6 +375,8 @@ static struct rcu_torture_ops rcu_sync_o
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = synchronize_rcu,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_sync"
 };

Much like other common structures such as struct file_operations, no need to explicitly specify members as NULL here; any member you don't specify will get a NULL value. That avoids the need to update every use of this structure whenever you add a new member used by only some of them.

Quoted 1 line

Untrusting, aren't I? ;-)

Heh. I have that problem as well; I always hestitate to trust the compiler to initialize values.

Quoted 1 line

I removed all the "= NULL" entries.

Thanks.

Quoted 9 lines
Quoted 6 lines

kernel/rcutorture.c rcu_torture_cleanup(void) · line 938

Quoted 5 lines
kernel/rcutorture.c +1 -0
@@ -856,6 +935,8 @@ rcu_torture_cleanup(void)
 		kthread_stop(stats_task);
 	}
 	stats_task = NULL;
+	if (cur_ops->preemptend != NULL)

if (cur_ops->preemptend) would work as well.

True, though again there is a lot of existing "!= NULL" in this file and elsewhere. Many thousands of them through the kernel. ;-)

As before, don't worry about it for this patch then.

Quoted 1 line

I will run this through the mill and repost.

Thanks!

- Josh Triplett

#6 lore raw

Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture

Quoted 17 lines · Josh Triplett
Quoted 6 lines · Paul E. McKenney
Quoted 2 lines · Josh Triplett

One major item: this new test feature really needs a new module parameter to enable or disable it.

CONFIG_PREEMPT_RCU_BOOST is the parameter -- if not set, then no test. This parameter is provided by the accompanying RCU-boost patch.

It seems useful for rcutorture to use or not use the preempting thread independently of CONFIG_PREEMPT_RCU_BOOST. That would bring you from two cases to four, and the two new cases both make sense:

* CONFIG_PREEMPT_RCU_BOOST=n, but run rcutorture with the preempting thread. This configuration allows you to demonstrate the need for CONFIG_PREEMPT_RCU_BOOST, by showing what happens when you need it and don't have it.

Quoted 6 lines

* CONFIG_PREEMPT_RCU_BOOST=y, but run rcutorture without the preempting thread. This configuration allows you to test with rcutorture while running a *real* real-time workload rather than the simple preempting thread, or just test basic RCU functionality.

A simple boolean module_param would work here.

OK, sold! I will add this. Perhaps CONFIG_PREEMPT_RCU_TORTURE.

Quoted 2 lines

At some point, we may want to add the ability to run multiple preempting threads, but that doesn't need to happen for this patch.

I considered that for this initial round, but you only need to preempt a single RCU reader to force the RCU booster to do something. ;-)

Quoted 27 lines
Quoted 27 lines
Quoted 26 lines
Quoted 13 lines · Paul E. McKenney

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcutorture.c linux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.c

kernel/rcutorture.c +9 -0
 
+static int rcu_torture_preempt(void *arg)
+{
+	int completedstart;
+	time_t gcstart;
+	struct sched_param sp;
+
+	sp.sched_priority = MAX_RT_PRIO - 1;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 290

Quoted 10 lines
kernel/rcutorture.c +10 -0
+
+	do {
+		completedstart = rcu_torture_completed();
+		gcstart = xtime.tv_sec;
+		while ((xtime.tv_sec - gcstart < 10) &&
+		       (rcu_torture_completed() == completedstart))
+			cond_resched();
+		if (rcu_torture_completed() == completedstart)
+			rcu_torture_preempt_errors++;
+		schedule_timeout_interruptible(shuffle_interval * HZ);

Why call schedule_timeout_interruptible here without actually handling interruptions? So that you can send it a signal to cause the shuffle early?

Quoted 6 lines
Quoted 2 lines

It allows you to kill the process in order to get the module unload to happen more quickly in case someone specified an overly long interval.

I didn't actually know that you could kill a kthread from userspace. :)

That rationale makes sense.

It won't actually die, but if I understand correctly (a big "if") the signal would cause schedule_timeout_interruptible() to return, allowing the kthread_should_stop() check to happen.

Quoted 32 lines
Quoted 2 lines

But now that you mention this, a simple one-second sleep is probably appropriate here.

OK.

Quoted 12 lines
Quoted 11 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 300

Quoted 10 lines
kernel/rcutorture.c +10 -0
+	} while (!kthread_should_stop());
+	return NULL;
+}
+
+static void rcu_preempt_start(void)
+{
+	rcu_preeempt_task = kthread_run(rcu_torture_preempt, NULL,
+					"rcu_torture_preempt");
+	if (IS_ERR(rcu_preeempt_task)) {
+		VERBOSE_PRINTK_ERRSTRING("Failed to create preempter");

This ought to include the errno value, PTR_ERR(rcu_preempt_task).

Quoted 3 lines

Good point -- what I should do is return this value so that rcu_torture_init() can return it, failing the module-load process and unwinding.

Even better, yes.

Quoted 9 lines
Quoted 8 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 307

Quoted 7 lines
kernel/rcutorture.c +7 -0
+		rcu_preeempt_task = NULL;
+	}
+}
+
+static void rcu_preempt_end(void)
+{
+	if (rcu_preeempt_task != NULL) {

if (rcu_preempt_task) would work just as well here.

Quoted 4 lines
Quoted 1 line

True, but was being consistent with usage elsewhere in this file.

Fair enough; don't worry about it for this patch, then. I'll deal with that particular style cleanup later, throughout rcutorture.

Sounds good to me! ;-)

Quoted 44 lines
Quoted 28 lines
Quoted 14 lines
Quoted 14 lines

static struct rcu_torture_ops rcu_ops = {

kernel/rcutorture.c +3 -1
 	.init = NULL,
 	.cleanup = NULL,
@@ -267,7 +334,9 @@ static struct rcu_torture_ops rcu_ops = 
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_torture_deferred_free,
 	.sync = synchronize_rcu,
-	.stats = NULL,
+	.preemptstart = rcu_preempt_start,
+	.preemptend = rcu_preempt_end,
+	.stats = rcu_preempt_stats,
 	.name = "rcu"
 };
 
Quoted 13 lines

kernel/rcutorture.c static struct rcu_torture_ops rcu_sync_o · line 382

Quoted 9 lines
kernel/rcutorture.c +2 -0
@@ -306,6 +375,8 @@ static struct rcu_torture_ops rcu_sync_o
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = synchronize_rcu,
+	.preemptstart = NULL,
+	.preemptend = NULL,
 	.stats = NULL,
 	.name = "rcu_sync"
 };

Much like other common structures such as struct file_operations, no need to explicitly specify members as NULL here; any member you don't specify will get a NULL value. That avoids the need to update every use of this structure whenever you add a new member used by only some of them.

Quoted 1 line

Untrusting, aren't I? ;-)

Heh. I have that problem as well; I always hestitate to trust the compiler to initialize values.

Quoted 1 line

I removed all the "= NULL" entries.

Thanks.

Quoted 7 lines
Quoted 6 lines

kernel/rcutorture.c rcu_torture_cleanup(void) · line 938

Quoted 5 lines
kernel/rcutorture.c +1 -0
@@ -856,6 +935,8 @@ rcu_torture_cleanup(void)
 		kthread_stop(stats_task);
 	}
 	stats_task = NULL;
+	if (cur_ops->preemptend != NULL)

if (cur_ops->preemptend) would work as well.

Quoted 6 lines
Quoted 2 lines

True, though again there is a lot of existing "!= NULL" in this file and elsewhere. Many thousands of them through the kernel. ;-)

As before, don't worry about it for this patch then.

Quoted 1 line

I will run this through the mill and repost.

But with the new kernel parameter this time. ;-)

Thanx, Paul

#7 lore raw

Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture

Quoted 16 lines · Paul E. McKenney
Quoted 15 lines · Josh Triplett
Quoted 5 lines · Paul E. McKenney
Quoted 2 lines · Josh Triplett

One major item: this new test feature really needs a new module parameter to enable or disable it.

CONFIG_PREEMPT_RCU_BOOST is the parameter -- if not set, then no test. This parameter is provided by the accompanying RCU-boost patch.

It seems useful for rcutorture to use or not use the preempting thread independently of CONFIG_PREEMPT_RCU_BOOST. That would bring you from two cases to four, and the two new cases both make sense:

* CONFIG_PREEMPT_RCU_BOOST=n, but run rcutorture with the preempting thread. This configuration allows you to demonstrate the need for CONFIG_PREEMPT_RCU_BOOST, by showing what happens when you need it and don't have it.

Quoted 8 lines
Quoted 6 lines

* CONFIG_PREEMPT_RCU_BOOST=y, but run rcutorture without the preempting thread. This configuration allows you to test with rcutorture while running a *real* real-time workload rather than the simple preempting thread, or just test basic RCU functionality.

A simple boolean module_param would work here.

OK, sold! I will add this. Perhaps CONFIG_PREEMPT_RCU_TORTURE.

Why a config option? Why not a module parameter, settable at module load time?

static int enable_preempter;
...
module_param(enable_preempter, bool, 0);
MODULE_PARM_DESC(enable_preempter, "Enable preempting thread, to test RCU priority boosting");
...
rcu_torture_cleanup(void)
{
...

	if (enable_preempter && cur_ops->preemptend)
		cur_ops->preemptend();
...
	if (enable_preempter && cur_ops->preemptstart)
		cur_ops->preemptstart();

Then just remove the #ifdef CONFIG_PREEMPT_RCU_BOOST from rcutorture entirely, and always supply the preempter functions. rcutorture then doesn't depend on CONFIG_PREEMPT_RCU_BOOST at all, and the module parameter determines whether to run the preempter thread.

Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
Quoted 17 lines
Quoted 16 lines · Paul E. McKenney

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcutorture.c linux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.c

kernel/rcutorture.c +13 -0
+static int rcu_torture_preempt(void *arg)
+{
+	int completedstart;
+	time_t gcstart;
+	struct sched_param sp;
+
+	sp.sched_priority = MAX_RT_PRIO - 1;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;
+
+	do {
+		completedstart = rcu_torture_completed();
+		gcstart = xtime.tv_sec;
Quoted 8 lines

kernel/rcutorture.c static void rcu_torture_deferred_free(st · line 290

Quoted 6 lines
kernel/rcutorture.c +6 -0
+		while ((xtime.tv_sec - gcstart < 10) &&
+		       (rcu_torture_completed() == completedstart))
+			cond_resched();
+		if (rcu_torture_completed() == completedstart)
+			rcu_torture_preempt_errors++;
+		schedule_timeout_interruptible(shuffle_interval * HZ);

Why call schedule_timeout_interruptible here without actually handling interruptions? So that you can send it a signal to cause the shuffle early?

It allows you to kill the process in order to get the module unload to happen more quickly in case someone specified an overly long interval.

Quoted 7 lines
Quoted 3 lines

I didn't actually know that you could kill a kthread from userspace. :)

That rationale makes sense.

It won't actually die, but if I understand correctly (a big "if") the signal would cause schedule_timeout_interruptible() to return, allowing the kthread_should_stop() check to happen.

Ah, that makes much more sense; thanks.

- Josh Triplett

#8 lore raw

Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture

In reply to Josh Triplett
Quoted 17 lines · Josh Triplett
Quoted 6 lines · Paul E. McKenney
Quoted 2 lines · Josh Triplett

One major item: this new test feature really needs a new module parameter to enable or disable it.

CONFIG_PREEMPT_RCU_BOOST is the parameter -- if not set, then no test. This parameter is provided by the accompanying RCU-boost patch.

It seems useful for rcutorture to use or not use the preempting thread independently of CONFIG_PREEMPT_RCU_BOOST. That would bring you from two cases to four, and the two new cases both make sense:

* CONFIG_PREEMPT_RCU_BOOST=n, but run rcutorture with the preempting thread. This configuration allows you to demonstrate the need for CONFIG_PREEMPT_RCU_BOOST, by showing what happens when you need it and don't have it.

Quoted 6 lines

* CONFIG_PREEMPT_RCU_BOOST=y, but run rcutorture without the preempting thread. This configuration allows you to test with rcutorture while running a *real* real-time workload rather than the simple preempting thread, or just test basic RCU functionality.

A simple boolean module_param would work here.

OK, am finally with you. See below for updated patch.

Quoted 2 lines

At some point, we may want to add the ability to run multiple preempting threads, but that doesn't need to happen for this patch.

Or to make it bounce around onto multiple CPUs, I suppose. Leaving these out for the moment. ;-)

---

 rcutorture.c |   90 +++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 76 insertions(+), 14 deletions(-)
kernel/rcutorture.c +76 -14
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcutorture.c linux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.c
@@ -58,6 +58,7 @@ static int stat_interval;	/* Interval be
 static int verbose;		/* Print more debug info. */
 static int test_no_idle_hz;	/* Test RCU's support for tickless idle CPUs. */
 static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/
+static int preempt_torture = 0;	/* Realtime task preempts torture readers. */
 static char *torture_type = "rcu"; /* What RCU implementation to torture. */
 
 module_param(nreaders, int, 0);
@@ -72,6 +73,8 @@ module_param(test_no_idle_hz, bool, 0);
 MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs");
 module_param(shuffle_interval, int, 0);
 MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles");
+module_param(preempt_torture, bool, 0);
+MODULE_PARM_DESC(preempt_torture, "Enable realtime preemption torture");
 module_param(torture_type, charp, 0);
 MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)");
 
@@ -194,6 +197,8 @@ struct rcu_torture_ops {
 	int (*completed)(void);
 	void (*deferredfree)(struct rcu_torture *p);
 	void (*sync)(void);
+	long (*preemptstart)(void);
+	void (*preemptend)(void);
 	int (*stats)(char *page);
 	char *name;
 };
@@ -258,16 +263,74 @@ static void rcu_torture_deferred_free(st
 	call_rcu(&p->rtort_rcu, rcu_torture_cb);
 }
 
+static struct task_struct *rcu_preeempt_task;
+static unsigned long rcu_torture_preempt_errors = 0;
+
+static int rcu_torture_preempt(void *arg)
+{
+	int completedstart;
+	int err;
+	time_t gcstart;
+	struct sched_param sp;
+
+	sp.sched_priority = MAX_RT_PRIO - 1;
+	err = sched_setscheduler(current, SCHED_RR, &sp);
+	if (err != 0)
+		printk(KERN_ALERT "rcu_torture_preempt() priority err: %d\n",
+		       err);
+	current->flags |= PF_NOFREEZE;
+
+	do {
+		completedstart = rcu_torture_completed();
+		gcstart = xtime.tv_sec;
+		while ((xtime.tv_sec - gcstart < 10) &&
+		       (rcu_torture_completed() == completedstart))
+			cond_resched();
+		if (rcu_torture_completed() == completedstart)
+			rcu_torture_preempt_errors++;
+		schedule_timeout_interruptible(HZ);
+	} while (!kthread_should_stop());
+	return NULL;
+}
+
+static long rcu_preempt_start(void)
+{
+	long retval = 0;
+
+	rcu_preeempt_task = kthread_run(rcu_torture_preempt, NULL,
+					"rcu_torture_preempt");
+	if (IS_ERR(rcu_preeempt_task)) {
+		VERBOSE_PRINTK_ERRSTRING("Failed to create preempter");
+		retval = PTR_ERR(rcu_preeempt_task);
+		rcu_preeempt_task = NULL;
+	}
+	return retval;
+}
+
+static void rcu_preempt_end(void)
+{
+	if (rcu_preeempt_task != NULL) {
+		VERBOSE_PRINTK_STRING("Stopping rcu_preempt task");
+		kthread_stop(rcu_preeempt_task);
+	}
+	rcu_preeempt_task = NULL;
+}
+
+static int rcu_preempt_stats(char *page) {
+	return sprintf(page,
+		       "Preemption stalls: %lu\n", rcu_torture_preempt_errors);
+}
+
 static struct rcu_torture_ops rcu_ops = {
-	.init = NULL,
-	.cleanup = NULL,
 	.readlock = rcu_torture_read_lock,
 	.readdelay = rcu_read_delay,
 	.readunlock = rcu_torture_read_unlock,
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_torture_deferred_free,
 	.sync = synchronize_rcu,
-	.stats = NULL,
+	.preemptstart = rcu_preempt_start,
+	.preemptend = rcu_preempt_end,
+	.stats = rcu_preempt_stats,
 	.name = "rcu"
 };
 
@@ -299,14 +362,12 @@ static void rcu_sync_torture_init(void)
 
 static struct rcu_torture_ops rcu_sync_ops = {
 	.init = rcu_sync_torture_init,
-	.cleanup = NULL,
 	.readlock = rcu_torture_read_lock,
 	.readdelay = rcu_read_delay,
 	.readunlock = rcu_torture_read_unlock,
 	.completed = rcu_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = synchronize_rcu,
-	.stats = NULL,
 	.name = "rcu_sync"
 };
 
@@ -362,28 +423,23 @@ static void rcu_bh_torture_synchronize(v
 }
 
 static struct rcu_torture_ops rcu_bh_ops = {
-	.init = NULL,
-	.cleanup = NULL,
 	.readlock = rcu_bh_torture_read_lock,
 	.readdelay = rcu_read_delay,  /* just reuse rcu's version. */
 	.readunlock = rcu_bh_torture_read_unlock,
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_bh_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
-	.stats = NULL,
 	.name = "rcu_bh"
 };
 
 static struct rcu_torture_ops rcu_bh_sync_ops = {
 	.init = rcu_sync_torture_init,
-	.cleanup = NULL,
 	.readlock = rcu_bh_torture_read_lock,
 	.readdelay = rcu_read_delay,  /* just reuse rcu's version. */
 	.readunlock = rcu_bh_torture_read_unlock,
 	.completed = rcu_bh_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = rcu_bh_torture_synchronize,
-	.stats = NULL,
 	.name = "rcu_bh_sync"
 };
 
@@ -495,14 +551,12 @@ static void sched_torture_synchronize(vo
 
 static struct rcu_torture_ops sched_ops = {
 	.init = rcu_sync_torture_init,
-	.cleanup = NULL,
 	.readlock = sched_torture_read_lock,
 	.readdelay = rcu_read_delay,  /* just reuse rcu's version. */
 	.readunlock = sched_torture_read_unlock,
 	.completed = sched_torture_completed,
 	.deferredfree = rcu_sync_torture_deferred_free,
 	.sync = sched_torture_synchronize,
-	.stats = NULL,
 	.name = "sched"
 };
 
@@ -801,9 +855,10 @@ rcu_torture_print_module_parms(char *tag
 	printk(KERN_ALERT "%s" TORTURE_FLAG
 		"--- %s: nreaders=%d nfakewriters=%d "
 		"stat_interval=%d verbose=%d test_no_idle_hz=%d "
-		"shuffle_interval = %d\n",
+		"shuffle_interval=%d preempt_torture=%d\n",
 		torture_type, tag, nrealreaders, nfakewriters,
-		stat_interval, verbose, test_no_idle_hz, shuffle_interval);
+		stat_interval, verbose, test_no_idle_hz, shuffle_interval,
+		preempt_torture);
 }
 
 static void
@@ -856,6 +911,8 @@ rcu_torture_cleanup(void)
 		kthread_stop(stats_task);
 	}
 	stats_task = NULL;
+	if (preempt_torture && (cur_ops->preemptend != NULL))
+		cur_ops->preemptend();
 
 	/* Wait for all RCU callbacks to fire.  */
 	rcu_barrier();
@@ -997,6 +1054,11 @@ rcu_torture_init(void)
 			goto unwind;
 		}
 	}
+	if (preempt_torture && (cur_ops->preemptstart != NULL)) {
+		firsterr = cur_ops->preemptstart();
+		if (firsterr != 0)
+			goto unwind;
+	}
 	return 0;
 
 unwind:
#9 lore raw

Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture

Quoted 16 lines · Paul E. McKenney
Quoted 15 lines · Josh Triplett
Quoted 5 lines · Paul E. McKenney
Quoted 2 lines · Josh Triplett

One major item: this new test feature really needs a new module parameter to enable or disable it.

CONFIG_PREEMPT_RCU_BOOST is the parameter -- if not set, then no test. This parameter is provided by the accompanying RCU-boost patch.

It seems useful for rcutorture to use or not use the preempting thread independently of CONFIG_PREEMPT_RCU_BOOST. That would bring you from two cases to four, and the two new cases both make sense:

* CONFIG_PREEMPT_RCU_BOOST=n, but run rcutorture with the preempting thread. This configuration allows you to demonstrate the need for CONFIG_PREEMPT_RCU_BOOST, by showing what happens when you need it and don't have it.

Quoted 8 lines
Quoted 6 lines

* CONFIG_PREEMPT_RCU_BOOST=y, but run rcutorture without the preempting thread. This configuration allows you to test with rcutorture while running a *real* real-time workload rather than the simple preempting thread, or just test basic RCU functionality.

A simple boolean module_param would work here.

OK, am finally with you. See below for updated patch.

Looks good to me.

Quoted 1 line

- Josh Triplett

#10 lore raw
In reply to Paul E. McKenney

Overall, this code looks sensible to me. Some comments on the patch below.

Quoted 2 lines · Paul E. McKenney
include/linux/rcupreempt.h +0 -0

[...]

include/linux/rcupreempt.h line 60

Quoted 7 lines
include/linux/rcupreempt.h +7 -0
+enum rcu_boost_state {
+	RCU_BOOST_IDLE = 0,	   /* Not yet blocked if in RCU read-side. */
+	RCU_BOOST_BLOCKED = 1,	   /* Blocked from RCU read-side. */
+	RCU_BOOSTED = 2,	   /* Boosting complete. */
+};
+
+#define N_RCU_BOOST_STATE (RCU_BOOSTED + 1)

Here, you define the three possible states, the number of states. But later...

Quoted 4 lines

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcupreempt.c linux-2.6.20-rc4-rt1-rcub/kernel/rcupreempt.c

kernel/rcupreempt.c +0 -0
@@ -49,6 +49,7 @@

[...]

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 109

Quoted 3 lines
kernel/rcupreempt.c +3 -0
+#ifdef CONFIG_PREEMPT_RCU_BOOST_STATS
+	long rbs_stats[N_RCU_BOOST_DAT_EVENTS][N_RCU_BOOST_STATE + 1];
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST_STATS */

...you declare this array to have space for one more than that, and then...

Quoted 4 lines
kernel/rcupreempt.c +4 -0
+#ifdef CONFIG_PREEMPT_RCU_BOOST_STATS
+
+/*
+ * Function to increment indicated ->rbs_stats[] element.

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 130

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+ */
+static inline void rcu_boost_dat_stat(struct rcu_boost_dat *rbdp,
+				      int event,
+				      enum rcu_boost_state oldstate)
+{
+	if (oldstate >= RCU_BOOST_IDLE &&
+	    oldstate <= RCU_BOOSTED) {
+		rbdp->rbs_stats[event][oldstate]++;
+	} else {
+		rbdp->rbs_stats[event][N_RCU_BOOST_STATE]++;

...you use the last element as what looks like an "unknown" counter. How about instead defining the enum to have an "unknown" state, defining N_RCU_BOOST_STATE accordingly, and using N_RCU_BOOST_STATE without the +1 in rbs_stats? I think that would make the code much more self-documenting, and less error-prone.

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+#define rcu_boost_dat_stat_block(rbdp, oldstate) \
+	rcu_boost_dat_stat(rbdp, RCU_BOOST_DAT_BLOCK, oldstate)
+#define rcu_boost_dat_stat_boost(rbdp, oldstate) \
+	rcu_boost_dat_stat(rbdp, RCU_BOOST_DAT_BOOST, oldstate)
+#define rcu_boost_dat_stat_unlock(rbdp, oldstate) \

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 148

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+	rcu_boost_dat_stat(rbdp, RCU_BOOST_DAT_UNLOCK, oldstate)
+
+/*
+ * Prefix for kprint() strings for periodic statistics messages.
+ */
+static char *rcu_boost_state_event[] = {
+	"block:  ",
+	"boost:  ",
+	"unlock: ",
+};

I don't know for sure, but I *think* that GCC may sometimes generate more efficient code if you use a char [][], rather than a char *[].

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 160

Quoted 12 lines
kernel/rcupreempt.c +12 -0
+
+/*
+ * Indicators for numbers in kprint() strings.  "!" indicates a state-event
+ * pair that should not happen, while "?" indicates a state that should
+ * not happen.
+ */
+static char *rcu_boost_state_error[] = {
+       /*ibBe*/
+	"   ?",  /* block */
+	"!  ?",  /* boost */
+	"?  ?",  /* unlock */
+};

Likewise. Also, the columns here don't seem to line up, probably because the comment uses spaces and the other lines use a tab.

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 167

Quoted 3 lines
kernel/rcupreempt.c +3 -0
+static void rcu_boost_dat_stat_print(void)
+{
+	char buf[N_RCU_BOOST_STATE * (sizeof(long) * 3 + 2) + 2];

This expression really begs for some constants in place of the magic numbers, or failing that, a comment.

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 180

Quoted 13 lines
kernel/rcupreempt.c +13 -0
+	int cpu;
+	int event;
+	int i;
+	static time_t lastprint = 0;
+	struct rcu_boost_dat *rbdp;
+	int state;
+	struct rcu_boost_dat sum;
+
+	/* Wait a graceful interval between printk spamming. */
+
+	if (xtime.tv_sec - lastprint <
+	    CONFIG_PREEMPT_RCU_BOOST_STATS_INTERVAL)
+		return;

How about printk_ratelimit? It takes a parameter for the rate. (On the other hand, it also takes a spinlock, which you might not want here, even in the stats code.)

Quoted 8 lines
kernel/rcupreempt.c +8 -0
+	/* Print them out! */
+
+	printk(KERN_ALERT
+	       "rcu_boost_dat: idx=%d "
+	       "b=%ld ul=%ld ub=%ld boost: a=%ld b=%ld\n",
+	       rcu_boost_idx,
+	       sum.rbs_blocked, sum.rbs_unlock, sum.rbs_unboosted,
+	       sum.rbs_boost_attempt, sum.rbs_boost);

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 230

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+	for (event = 0; event < N_RCU_BOOST_DAT_EVENTS; event++) {
+		i = 0;
+		for (state = 0; state <= N_RCU_BOOST_STATE; state++) {
+			i += sprintf(&buf[i], " %ld%c",
+				     sum.rbs_stats[event][state],
+				     rcu_boost_state_error[event][state]);
+		}
+		printk(KERN_ALERT "rcu_boost_dat %s %s\n",
+		       rcu_boost_state_event[event], buf);
+	}

Most or all of these counters could likely become unsigned, since negative values don't make sense for them.

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+	/* Go away and don't come back for awhile. */
+
+	lastprint = xtime.tv_sec;
+}
+

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 291

Quoted 10 lines
kernel/rcupreempt.c +9 -0
 
+/*
+ * Return the current boost index for boosting target tasks.
+ * May only be invoked by the booster task, so guaranteed to
+ * already be initialized.
+ */
+static inline int rcu_boost_idx_boosting(void)
+{
+	return (rcu_boost_idx + 1) & (RCU_BOOST_ELEMENTS - 1);
+}

Quoted for later reference.

Quoted 23 lines
kernel/rcupreempt.c +23 -0
+/*
+ * Initialize RCU-boost state.  This happens early in the boot process,
+ * when the scheduler does not yet exist.  So don't try to use it.
+ */
+static void init_rcu_boost_early(void)
+{
+	struct rcu_boost_dat *rbdp;
+	int cpu;
+	int i;
+
+	for_each_possible_cpu(cpu) {
+		rbdp = per_cpu(rcu_boost_dat, cpu);
+		for (i = 0; i < RCU_BOOST_ELEMENTS; i++) {
+			rbdp[i].rbs_mutex =
+				RAW_SPIN_LOCK_UNLOCKED(rbdp[i].rbs_mutex);
+			INIT_LIST_HEAD(&rbdp[i].rbs_toboost);
+			INIT_LIST_HEAD(&rbdp[i].rbs_boosted);
+			rbdp[i].rbs_blocked = 0;
+			rbdp[i].rbs_boost_attempt = 0;
+			rbdp[i].rbs_boost = 0;
+			rbdp[i].rbs_unlock = 0;
+			rbdp[i].rbs_unboosted = 0;
+#ifdef CONFIG_PREEMPT_RCU_BOOST_STATS

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 278

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+			{
+				int j, k;
+
+				for (j = 0; j < N_RCU_BOOST_DAT_EVENTS; j++)
+					for (k = 0; k <= N_RCU_BOOST_STATE; k++)
+						rbdp[i].rbs_stats[j][k] = 0;
+			}
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST_STATS */
+		}
+		smp_wmb();

Barrier without an accompanying comment. You want to make sure the structure gets initialized before setting rcu_boost_idx and thus allowing boosting, right?

Quoted 4 lines
kernel/rcupreempt.c +3 -0
+		rcu_boost_idx = 0;
+	}
+}
 

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 302

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+/*
+ * Return the list on which the calling task should add itself, or
+ * NULL if too early during initialization.
+ */
+static inline struct rcu_boost_dat *rcu_rbd_new(void)
+{
+	int cpu = raw_smp_processor_id();  /* locks used, so preemption OK. */
+	int idx = rcu_boost_idx;
+
+	smp_read_barrier_depends(); barrier();

Barriers without accompanying comments, though in this case they seem fairly obvious.

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 315

Quoted 13 lines
kernel/rcupreempt.c +13 -0
+	if (unlikely(idx < 0))
+		return (NULL);
+	return &per_cpu(rcu_boost_dat, cpu)[idx];
+}
+
+/*
+ * Return the list from which to boost target tasks.
+ * May only be invoked by the booster task, so guaranteed to
+ * already be initialized.
+ */
+static inline struct rcu_boost_dat *rcu_rbd_boosting(int cpu)
+{
+	int idx = (rcu_boost_idx + 1) & (RCU_BOOST_ELEMENTS - 1);

You defined rcu_boost_idx_boosting for exactly this expression.

kernel/rcupreempt.c

Quoted 1 line
kernel/rcupreempt.c +1 -0
+	return &per_cpu(rcu_boost_dat, cpu)[idx];

For that matter, since you wrapped it in the function, you could just call it directly in this array index operation.

Quoted 28 lines
kernel/rcupreempt.c +27 -0
+}
 
+/*
+ * Priority-boost tasks stuck in RCU read-side critical sections as
+ * needed (presumably rarely).
+ */
+static int rcu_booster(void *arg)
+{
+	int cpu;
+	struct sched_param sp;
+
+	sp.sched_priority = PREEMPT_RCU_BOOSTER_PRIO;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;
+
+	do {
+
+		/* Advance the lists of tasks. */
+
+		rcu_boost_idx = (rcu_boost_idx + 1) % RCU_BOOST_ELEMENTS;
+		for_each_possible_cpu(cpu) {
+
+			/*
+			 * Boost all sufficiently aged readers.
+			 * Readers must first be preempted or block
+			 * on a mutex in an RCU read-side critical section,
+			 * then remain in that critical section for
+			 * RCU_BOOST_ELEMENTS-1 time intervals.

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 467

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+			 * So most of the time we should end up doing
+			 * nothing.
+			 */
+
+			rcu_boost_one_reader_list(rcu_rbd_boosting(cpu));
+
+			/*
+			 * Large SMP systems may need to sleep sometimes
+			 * in this loop.  Or have multiple RCU-boost tasks.
+			 */

The idea of multiple RCU-boost tasks gives me an idea: what about having per-CPU boost queues, and using that to eliminate the need to lock the queues? It might not do quite as good a job of scheduling, but it would eliminate locking overhead *and* solve the problem you describe here.

Quoted 20 lines
kernel/rcupreempt.c +20 -0
+		}
+
+		/*
+		 * Sleep to allow any unstalled RCU read-side critical
+		 * sections to age out of the list.  @@@ FIXME: reduce,
+		 * adjust, or eliminate in case of OOM.
+		 */
+
+		schedule_timeout_uninterruptible(HZ / 100);
+
+		/* Print stats if enough time has passed. */
+
+		rcu_boost_dat_stat_print();
+
+	} while (!kthread_should_stop());
+
+	return 0;
+}
+
+/*

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 497

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+ * Perform the portions of RCU-boost initialization that require the
+ * scheduler to be up and running.
+ */
+void init_rcu_boost_late(void)
+{
+	int i;
+
+	/* Spawn RCU-boost task. */
+
+	printk(KERN_ALERT "Starting RCU priority booster\n");

Judging by other initialization code, I think this belongs at KERN_INFO or similar.

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 502

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+	rcu_boost_task = kthread_run(rcu_booster, NULL, "RCU Prio Booster");
+	if (IS_ERR(rcu_boost_task)) {
+		i = PTR_ERR(rcu_boost_task);
+		printk(KERN_ALERT
+		       "Unable to create RCU Priority Booster, errno %d\n", -i);

No need for a temporary here.

Quoted 19 lines
kernel/rcupreempt.c +19 -0
+
+		/*
+		 * Continue running, but tasks permanently blocked
+		 * in RCU read-side critical sections will be able
+		 * to stall grace-period processing, potentially
+		 * OOMing the machine.
+		 */
+
+		rcu_boost_task = NULL;
+	}
+}
+
+/*
+ * Update task's RCU-boost state to reflect blocking in RCU read-side
+ * critical section, so that the RCU-boost task can find it in case it
+ * later needs its priority boosted.
+ */
+void __rcu_preempt_boost(void)
+{

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 531

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+	struct rcu_boost_dat *rbdp;
+	unsigned long oldirq;
+
+	/* Identify list to place task on for possible later boosting. */
+
+	local_irq_save(oldirq);
+	rbdp = rcu_rbd_new();
+	if (rbdp == NULL) {
+		local_irq_restore(oldirq);
+		printk("Preempted RCU read-side critical section too early.\n");

This printk lacks a priority.

Quoted 17 lines
kernel/rcupreempt.c +17 -0
+		return;
+	}
+	spin_lock(&rbdp->rbs_mutex);
+	rbdp->rbs_blocked++;
+
+	/*
+	 * Update state.  We hold the lock and aren't yet on the list,
+	 * so the booster cannot mess with us yet.
+	 */
+
+	rcu_boost_dat_stat_block(rbdp, current->rcub_state);
+	if (current->rcub_state != RCU_BOOST_IDLE) {
+
+		/*
+		 * We have been here before, so just update stats.
+		 * It may seem strange to do all this work just to
+		 * accumulate statistics, but this is such a

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 558

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+		 * low-probability code path that we shouldn't care.
+		 * If it becomes a problem, it can be fixed.
+		 */
+
+		spin_unlock_irqrestore(&rbdp->rbs_mutex, oldirq);
+		return;
+	}
+	current->rcub_state = RCU_BOOST_BLOCKED;
+
+	/* Now add ourselves to the list so that the booster can find use. */

Typo: s/use/us/

Quoted 10 lines
kernel/rcupreempt.c +5 -0
+
+	list_add_tail(&current->rcub_entry, &rbdp->rbs_toboost);
+	current->rcub_rbdp = rbdp;
+	spin_unlock_irqrestore(&rbdp->rbs_mutex, oldirq);
+}

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rtmutex.c linux-2.6.20-rc4-rt1-rcub/kernel/rtmutex.c

kernel/rtmutex.c static inline void init_lists(struct rt_ · line 111

Quoted 7 lines
kernel/rtmutex.c +4 -0
@@ -105,11 +105,14 @@ static inline void init_lists(struct rt_
  */
 int rt_mutex_getprio(struct task_struct *task)
 {
+	int prio = task->normal_prio;
+
+	if (get_rcu_prio(task) < prio)
+		prio = get_rcu_prio(task);

int prio = min(task->normal_prio, get_rcu_prio(task)); ?

kernel/rtmutex.c static inline void init_lists(struct rt_ · line 116

Quoted 8 lines
kernel/rtmutex.c +2 -3
 	if (likely(!task_has_pi_waiters(task)))
-		return task->normal_prio;
+		return prio;
 
-	return min(task_top_pi_waiter(task)->pi_list_entry.prio,
-		   task->normal_prio);
+	return min(task_top_pi_waiter(task)->pi_list_entry.prio, prio);
 }

- Josh Triplett

#11 lore raw
Quoted 40 lines · Josh Triplett

Overall, this code looks sensible to me. Some comments on the patch below.

Quoted 2 lines · Paul E. McKenney
include/linux/rcupreempt.h +0 -0

[...]

include/linux/rcupreempt.h line 60

Quoted 7 lines
include/linux/rcupreempt.h +7 -0
+enum rcu_boost_state {
+	RCU_BOOST_IDLE = 0,	   /* Not yet blocked if in RCU read-side. */
+	RCU_BOOST_BLOCKED = 1,	   /* Blocked from RCU read-side. */
+	RCU_BOOSTED = 2,	   /* Boosting complete. */
+};
+
+#define N_RCU_BOOST_STATE (RCU_BOOSTED + 1)

Here, you define the three possible states, the number of states. But later...

Quoted 4 lines

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rcupreempt.c linux-2.6.20-rc4-rt1-rcub/kernel/rcupreempt.c

kernel/rcupreempt.c +0 -0
@@ -49,6 +49,7 @@

[...]

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 109

Quoted 3 lines
kernel/rcupreempt.c +3 -0
+#ifdef CONFIG_PREEMPT_RCU_BOOST_STATS
+	long rbs_stats[N_RCU_BOOST_DAT_EVENTS][N_RCU_BOOST_STATE + 1];
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST_STATS */

...you declare this array to have space for one more than that, and then...

Quoted 12 lines
kernel/rcupreempt.c +12 -0
+#ifdef CONFIG_PREEMPT_RCU_BOOST_STATS
+
+/*
+ * Function to increment indicated ->rbs_stats[] element.
+ */
+static inline void rcu_boost_dat_stat(struct rcu_boost_dat *rbdp,
+				      int event,
+				      enum rcu_boost_state oldstate)
+{
+	if (oldstate >= RCU_BOOST_IDLE &&
+	    oldstate <= RCU_BOOSTED) {
+		rbdp->rbs_stats[event][oldstate]++;
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 130

Quoted 2 lines
kernel/rcupreempt.c +2 -0
+	} else {
+		rbdp->rbs_stats[event][N_RCU_BOOST_STATE]++;

...you use the last element as what looks like an "unknown" counter. How about instead defining the enum to have an "unknown" state, defining N_RCU_BOOST_STATE accordingly, and using N_RCU_BOOST_STATE without the +1 in rbs_stats? I think that would make the code much more self-documenting, and less error-prone.

Excellent point. I have used the N+1 trick for some decades, so perhaps it is time to retire it anyway. ;-)

Quoted 10 lines
Quoted 10 lines
kernel/rcupreempt.c +10 -0
+#define rcu_boost_dat_stat_block(rbdp, oldstate) \
+	rcu_boost_dat_stat(rbdp, RCU_BOOST_DAT_BLOCK, oldstate)
+#define rcu_boost_dat_stat_boost(rbdp, oldstate) \
+	rcu_boost_dat_stat(rbdp, RCU_BOOST_DAT_BOOST, oldstate)
+#define rcu_boost_dat_stat_unlock(rbdp, oldstate) \
+	rcu_boost_dat_stat(rbdp, RCU_BOOST_DAT_UNLOCK, oldstate)
+
+/*
+ * Prefix for kprint() strings for periodic statistics messages.
+ */
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 148

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+static char *rcu_boost_state_event[] = {
+	"block:  ",
+	"boost:  ",
+	"unlock: ",
+};

I don't know for sure, but I *think* that GCC may sometimes generate more efficient code if you use a char [][], rather than a char *[].

Hmmm... "char *[]" says "array of strings" to me, while char[][] seems more opaque. Might be just me, though.

Quoted 7 lines
Quoted 7 lines
kernel/rcupreempt.c +7 -0
+
+/*
+ * Indicators for numbers in kprint() strings.  "!" indicates a state-event
+ * pair that should not happen, while "?" indicates a state that should
+ * not happen.
+ */
+static char *rcu_boost_state_error[] = {
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 160

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+       /*ibBe*/
+	"   ?",  /* block */
+	"!  ?",  /* boost */
+	"?  ?",  /* unlock */
+};

Likewise. Also, the columns here don't seem to line up, probably because the comment uses spaces and the other lines use a tab.

Yeah, the "*" of the comment has to line up with the leading quote of the following strings. I bumped everything over a space's worth, so that each line now has a leading tab.

Quoted 6 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 167

Quoted 3 lines
kernel/rcupreempt.c +3 -0
+static void rcu_boost_dat_stat_print(void)
+{
+	char buf[N_RCU_BOOST_STATE * (sizeof(long) * 3 + 2) + 2];

This expression really begs for some constants in place of the magic numbers, or failing that, a comment.

How about the following?

	/* Three decimal digits per byte plus spacing per number and line. */
Quoted 9 lines
Quoted 9 lines
kernel/rcupreempt.c +9 -0
+	int cpu;
+	int event;
+	int i;
+	static time_t lastprint = 0;
+	struct rcu_boost_dat *rbdp;
+	int state;
+	struct rcu_boost_dat sum;
+
+	/* Wait a graceful interval between printk spamming. */
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 180

Quoted 4 lines
kernel/rcupreempt.c +4 -0
+
+	if (xtime.tv_sec - lastprint <
+	    CONFIG_PREEMPT_RCU_BOOST_STATS_INTERVAL)
+		return;

How about printk_ratelimit? It takes a parameter for the rate. (On the other hand, it also takes a spinlock, which you might not want here, even in the stats code.)

It also allows bursts. printk_ratelimit() seems to be designed to prevent log overflow due to excessive error rates. In contrast I want to print stats at a more-or-less regular interval, and I don't want to incur the overhead of gathering and formatting stats if they aren't going to print.

Quoted 13 lines
Quoted 13 lines
kernel/rcupreempt.c +13 -0
+	/* Print them out! */
+
+	printk(KERN_ALERT
+	       "rcu_boost_dat: idx=%d "
+	       "b=%ld ul=%ld ub=%ld boost: a=%ld b=%ld\n",
+	       rcu_boost_idx,
+	       sum.rbs_blocked, sum.rbs_unlock, sum.rbs_unboosted,
+	       sum.rbs_boost_attempt, sum.rbs_boost);
+	for (event = 0; event < N_RCU_BOOST_DAT_EVENTS; event++) {
+		i = 0;
+		for (state = 0; state <= N_RCU_BOOST_STATE; state++) {
+			i += sprintf(&buf[i], " %ld%c",
+				     sum.rbs_stats[event][state],
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 230

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+				     rcu_boost_state_error[event][state]);
+		}
+		printk(KERN_ALERT "rcu_boost_dat %s %s\n",
+		       rcu_boost_state_event[event], buf);
+	}

Most or all of these counters could likely become unsigned, since negative values don't make sense for them.

Good point, fixed.

Quoted 8 lines
Quoted 8 lines
kernel/rcupreempt.c +8 -0
+	/* Go away and don't come back for awhile. */
+
+	lastprint = xtime.tv_sec;
+}
+
+/*
+ * Return the current boost index for boosting target tasks.
+ * May only be invoked by the booster task, so guaranteed to
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 291

Quoted 6 lines

+ * already be initialized.
+ */

kernel/rcupreempt.c +4 -0
+static inline int rcu_boost_idx_boosting(void)
+{
+	return (rcu_boost_idx + 1) & (RCU_BOOST_ELEMENTS - 1);
+}

Quoted for later reference.

Right. Removed this unused function, good eyes.

Quoted 29 lines
Quoted 29 lines
kernel/rcupreempt.c +29 -0
+/*
+ * Initialize RCU-boost state.  This happens early in the boot process,
+ * when the scheduler does not yet exist.  So don't try to use it.
+ */
+static void init_rcu_boost_early(void)
+{
+	struct rcu_boost_dat *rbdp;
+	int cpu;
+	int i;
+
+	for_each_possible_cpu(cpu) {
+		rbdp = per_cpu(rcu_boost_dat, cpu);
+		for (i = 0; i < RCU_BOOST_ELEMENTS; i++) {
+			rbdp[i].rbs_mutex =
+				RAW_SPIN_LOCK_UNLOCKED(rbdp[i].rbs_mutex);
+			INIT_LIST_HEAD(&rbdp[i].rbs_toboost);
+			INIT_LIST_HEAD(&rbdp[i].rbs_boosted);
+			rbdp[i].rbs_blocked = 0;
+			rbdp[i].rbs_boost_attempt = 0;
+			rbdp[i].rbs_boost = 0;
+			rbdp[i].rbs_unlock = 0;
+			rbdp[i].rbs_unboosted = 0;
+#ifdef CONFIG_PREEMPT_RCU_BOOST_STATS
+			{
+				int j, k;
+
+				for (j = 0; j < N_RCU_BOOST_DAT_EVENTS; j++)
+					for (k = 0; k <= N_RCU_BOOST_STATE; k++)
+						rbdp[i].rbs_stats[j][k] = 0;
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 278

Quoted 4 lines
kernel/rcupreempt.c +4 -0
+			}
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST_STATS */
+		}
+		smp_wmb();

Barrier without an accompanying comment. You want to make sure the structure gets initialized before setting rcu_boost_idx and thus allowing boosting, right?

Excellent point! How about the following:

	smp_wmb();  /* Make sure readers see above initialization. */
	rcu_boost_idx = 0;  /* Allow readers to access data. */
Quoted 4 lines
Quoted 3 lines
kernel/rcupreempt.c +3 -0
+		rcu_boost_idx = 0;
+	}
+}
Quoted 13 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 302

Quoted 10 lines
kernel/rcupreempt.c +10 -0
+/*
+ * Return the list on which the calling task should add itself, or
+ * NULL if too early during initialization.
+ */
+static inline struct rcu_boost_dat *rcu_rbd_new(void)
+{
+	int cpu = raw_smp_processor_id();  /* locks used, so preemption OK. */
+	int idx = rcu_boost_idx;
+
+	smp_read_barrier_depends(); barrier();

Barriers without accompanying comments, though in this case they seem fairly obvious.

It probably won't be obvious to me six months from now. :-/

How about the following?

	smp_read_barrier_depends(); barrier(); /* rmb() on Alpha for idx. */

I was tempted to use rcu_dereference() here and rcu_assign_pointer() above, but I was afraid that confusion would result.

Quoted 16 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 315

Quoted 13 lines
kernel/rcupreempt.c +13 -0
+	if (unlikely(idx < 0))
+		return (NULL);
+	return &per_cpu(rcu_boost_dat, cpu)[idx];
+}
+
+/*
+ * Return the list from which to boost target tasks.
+ * May only be invoked by the booster task, so guaranteed to
+ * already be initialized.
+ */
+static inline struct rcu_boost_dat *rcu_rbd_boosting(int cpu)
+{
+	int idx = (rcu_boost_idx + 1) & (RCU_BOOST_ELEMENTS - 1);

You defined rcu_boost_idx_boosting for exactly this expression.

Quoted 4 lines

kernel/rcupreempt.c

Quoted 1 line
kernel/rcupreempt.c +1 -0
+	return &per_cpu(rcu_boost_dat, cpu)[idx];

For that matter, since you wrapped it in the function, you could just call it directly in this array index operation.

Since this is the only use of that function, I left it inline and added a "Use rcu_boost_dat element least recently the destination for task blocking in RCU read-side critical sections" to rcu_rbd_boosting()'s comment header.

Seem reasonable?

Quoted 35 lines
Quoted 35 lines
kernel/rcupreempt.c +34 -0
+}
 
+/*
+ * Priority-boost tasks stuck in RCU read-side critical sections as
+ * needed (presumably rarely).
+ */
+static int rcu_booster(void *arg)
+{
+	int cpu;
+	struct sched_param sp;
+
+	sp.sched_priority = PREEMPT_RCU_BOOSTER_PRIO;
+	sched_setscheduler(current, SCHED_RR, &sp);
+	current->flags |= PF_NOFREEZE;
+
+	do {
+
+		/* Advance the lists of tasks. */
+
+		rcu_boost_idx = (rcu_boost_idx + 1) % RCU_BOOST_ELEMENTS;
+		for_each_possible_cpu(cpu) {
+
+			/*
+			 * Boost all sufficiently aged readers.
+			 * Readers must first be preempted or block
+			 * on a mutex in an RCU read-side critical section,
+			 * then remain in that critical section for
+			 * RCU_BOOST_ELEMENTS-1 time intervals.
+			 * So most of the time we should end up doing
+			 * nothing.
+			 */
+
+			rcu_boost_one_reader_list(rcu_rbd_boosting(cpu));
+
+			/*
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 467

Quoted 3 lines
kernel/rcupreempt.c +3 -0
+			 * Large SMP systems may need to sleep sometimes
+			 * in this loop.  Or have multiple RCU-boost tasks.
+			 */

The idea of multiple RCU-boost tasks gives me an idea: what about having per-CPU boost queues, and using that to eliminate the need to lock the queues? It might not do quite as good a job of scheduling, but it would eliminate locking overhead *and* solve the problem you describe here.

Nice try! ;-)

The problem is that the tasks on a given queue might migrate to some other CPU before they do their rcu_read_unlock(). We already have per-CPU boost queues, but their purpose is to promote cache locality in the (hopefully) common case where the task's block time is short enough that it runs on the same CPU. Should also reduce memory contention should there be a flurry of blocks on many CPUs from within RCU read-side critical sections.

But locking is still required, sad to say!

Unless I am misunderstanding your suggestion, in which case please enlighten me.

Quoted 25 lines
Quoted 25 lines
kernel/rcupreempt.c +25 -0
+		}
+
+		/*
+		 * Sleep to allow any unstalled RCU read-side critical
+		 * sections to age out of the list.  @@@ FIXME: reduce,
+		 * adjust, or eliminate in case of OOM.
+		 */
+
+		schedule_timeout_uninterruptible(HZ / 100);
+
+		/* Print stats if enough time has passed. */
+
+		rcu_boost_dat_stat_print();
+
+	} while (!kthread_should_stop());
+
+	return 0;
+}
+
+/*
+ * Perform the portions of RCU-boost initialization that require the
+ * scheduler to be up and running.
+ */
+void init_rcu_boost_late(void)
+{
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 497

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+	int i;
+
+	/* Spawn RCU-boost task. */
+
+	printk(KERN_ALERT "Starting RCU priority booster\n");

Judging by other initialization code, I think this belongs at KERN_INFO or similar.

Works for me!

Quoted 7 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 502

Quoted 5 lines
kernel/rcupreempt.c +5 -0
+	rcu_boost_task = kthread_run(rcu_booster, NULL, "RCU Prio Booster");
+	if (IS_ERR(rcu_boost_task)) {
+		i = PTR_ERR(rcu_boost_task);
+		printk(KERN_ALERT
+		       "Unable to create RCU Priority Booster, errno %d\n", -i);

No need for a temporary here.

Good point, fixed!

Quoted 23 lines
Quoted 23 lines
kernel/rcupreempt.c +23 -0
+
+		/*
+		 * Continue running, but tasks permanently blocked
+		 * in RCU read-side critical sections will be able
+		 * to stall grace-period processing, potentially
+		 * OOMing the machine.
+		 */
+
+		rcu_boost_task = NULL;
+	}
+}
+
+/*
+ * Update task's RCU-boost state to reflect blocking in RCU read-side
+ * critical section, so that the RCU-boost task can find it in case it
+ * later needs its priority boosted.
+ */
+void __rcu_preempt_boost(void)
+{
+	struct rcu_boost_dat *rbdp;
+	unsigned long oldirq;
+
+	/* Identify list to place task on for possible later boosting. */
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 531

Quoted 6 lines
kernel/rcupreempt.c +6 -0
+
+	local_irq_save(oldirq);
+	rbdp = rcu_rbd_new();
+	if (rbdp == NULL) {
+		local_irq_restore(oldirq);
+		printk("Preempted RCU read-side critical section too early.\n");

This printk lacks a priority.

Good catch! I chose KERN_ALERT, but please let me know if one of the others would be more appropriate.

Quoted 21 lines
Quoted 21 lines
kernel/rcupreempt.c +21 -0
+		return;
+	}
+	spin_lock(&rbdp->rbs_mutex);
+	rbdp->rbs_blocked++;
+
+	/*
+	 * Update state.  We hold the lock and aren't yet on the list,
+	 * so the booster cannot mess with us yet.
+	 */
+
+	rcu_boost_dat_stat_block(rbdp, current->rcub_state);
+	if (current->rcub_state != RCU_BOOST_IDLE) {
+
+		/*
+		 * We have been here before, so just update stats.
+		 * It may seem strange to do all this work just to
+		 * accumulate statistics, but this is such a
+		 * low-probability code path that we shouldn't care.
+		 * If it becomes a problem, it can be fixed.
+		 */
+
Quoted 8 lines

kernel/rcupreempt.c static struct rcu_ctrlblk rcu_ctrlblk = · line 558

Quoted 6 lines
kernel/rcupreempt.c +6 -0
+		spin_unlock_irqrestore(&rbdp->rbs_mutex, oldirq);
+		return;
+	}
+	current->rcub_state = RCU_BOOST_BLOCKED;
+
+	/* Now add ourselves to the list so that the booster can find use. */

Typo: s/use/us/

Goode catche, fixede!

Quoted 6 lines
Quoted 5 lines
kernel/rcupreempt.c +5 -0
+
+	list_add_tail(&current->rcub_entry, &rbdp->rbs_toboost);
+	current->rcub_rbdp = rbdp;
+	spin_unlock_irqrestore(&rbdp->rbs_mutex, oldirq);
+}
Quoted 13 lines

kernel/rtmutex.c static inline void init_lists(struct rt_ · line 111

Quoted 11 lines

diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rtmutex.c linux-2.6.20-rc4-rt1-rcub/kernel/rtmutex.c

kernel/rtmutex.c +4 -0
@@ -105,11 +105,14 @@ static inline void init_lists(struct rt_
  */
 int rt_mutex_getprio(struct task_struct *task)
 {
+	int prio = task->normal_prio;
+
+	if (get_rcu_prio(task) < prio)
+		prio = get_rcu_prio(task);

int prio = min(task->normal_prio, get_rcu_prio(task)); ?

Good point -- and it even fits nicely on the declaration line for prio! ;-)

Quoted 8 lines

kernel/rtmutex.c static inline void init_lists(struct rt_ · line 116

Quoted 8 lines
kernel/rtmutex.c +2 -3
 	if (likely(!task_has_pi_waiters(task)))
-		return task->normal_prio;
+		return prio;
 
-	return min(task_top_pi_waiter(task)->pi_list_entry.prio,
-		   task->normal_prio);
+	return min(task_top_pi_waiter(task)->pi_list_entry.prio, prio);
 }

Thank you again for the careful and thorough review!!!

I will test these changes and send out an update.

Thanx, Paul

#12 lore raw
Quoted 2 lines · Paul E. McKenney
Quoted 1 line · Josh Triplett

Overall, this code looks sensible to me. Some comments on the patch below.

[ . . . ]

Quoted 3 lines

Thank you again for the careful and thorough review!!!

I will test these changes and send out an update.

And here is the updated RCU priority-boost patch.

---

 include/linux/init_task.h  |   12 +
 include/linux/rcupdate.h   |   12 +
 include/linux/rcupreempt.h |   20 +
 include/linux/sched.h      |   16 +
 init/main.c                |    1 
 kernel/Kconfig.preempt     |   32 ++
 kernel/fork.c              |    6 
 kernel/rcupreempt.c        |  528 +++++++++++++++++++++++++++++++++++++++++++++
 kernel/rtmutex.c           |    7 
 kernel/sched.c             |    5 
 10 files changed, 636 insertions(+), 3 deletions(-)
include/linux/init_task.h +12 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/init_task.h linux-2.6.20-rc4-rt1-rcub/include/linux/init_task.h
@@ -87,6 +87,17 @@ extern struct nsproxy init_nsproxy;
 	.siglock	= __SPIN_LOCK_UNLOCKED(sighand.siglock),	\
 }
 
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+#define INIT_RCU_BOOST_PRIO .rcu_prio	= MAX_PRIO,
+#define INIT_PREEMPT_RCU_BOOST(tsk)					\
+	.rcub_rbdp	= NULL,						\
+	.rcub_state	= RCU_BOOST_IDLE,				\
+	.rcub_entry	= LIST_HEAD_INIT(tsk.rcub_entry),
+#else /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+#define INIT_RCU_BOOST_PRIO
+#define INIT_PREEMPT_RCU_BOOST(tsk)
+#endif /* #else #ifdef CONFIG_PREEMPT_RCU_BOOST */
+
 extern struct group_info init_groups;
 
 /*
@@ -143,6 +154,7 @@ extern struct group_info init_groups;
 	.pi_lock	= RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock),		\
 	INIT_TRACE_IRQFLAGS						\
 	INIT_LOCKDEP							\
+	INIT_PREEMPT_RCU_BOOST(tsk)					\
 }
 
 
include/linux/rcupdate.h +12 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/rcupdate.h linux-2.6.20-rc4-rt1-rcub/include/linux/rcupdate.h
@@ -227,6 +227,18 @@ extern void rcu_barrier(void);
 extern void rcu_init(void);
 extern void rcu_advance_callbacks(int cpu, int user);
 extern void rcu_check_callbacks(int cpu, int user);
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+extern void init_rcu_boost_late(void);
+extern void __rcu_preempt_boost(void);
+#define rcu_preempt_boost() \
+	do { \
+		if (unlikely(current->rcu_read_lock_nesting > 0)) \
+			__rcu_preempt_boost(); \
+	} while (0)
+#else /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+#define init_rcu_boost_late()
+#define rcu_preempt_boost()
+#endif /* #else #ifdef CONFIG_PREEMPT_RCU_BOOST */
 
 #endif /* __KERNEL__ */
 #endif /* __LINUX_RCUPDATE_H */
include/linux/rcupreempt.h +20 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/rcupreempt.h linux-2.6.20-rc4-rt1-rcub/include/linux/rcupreempt.h
@@ -42,6 +42,26 @@
 #include <linux/cpumask.h>
 #include <linux/seqlock.h>
 
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+/*
+ * Task state with respect to being RCU-boosted.  This state is changed
+ * by the task itself in response to the following three events:
+ * 1. Preemption (or block on lock) while in RCU read-side critical section.
+ * 2. Outermost rcu_read_unlock() for blocked RCU read-side critical section.
+ *
+ * The RCU-boost task also updates the state when boosting priority.
+ */
+enum rcu_boost_state {
+	RCU_BOOST_IDLE = 0,	   /* Not yet blocked if in RCU read-side. */
+	RCU_BOOST_BLOCKED = 1,	   /* Blocked from RCU read-side. */
+	RCU_BOOSTED = 2,	   /* Boosting complete. */
+	RCU_BOOST_INVALID = 3,	   /* For bogus state sightings. */
+};
+
+#define N_RCU_BOOST_STATE (RCU_BOOST_INVALID + 1)
+
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+
 #define rcu_qsctr_inc(cpu)
 #define rcu_bh_qsctr_inc(cpu)
 #define call_rcu_bh(head, rcu) call_rcu(head, rcu)
include/linux/sched.h +16 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/include/linux/sched.h linux-2.6.20-rc4-rt1-rcub/include/linux/sched.h
@@ -699,6 +699,14 @@ struct signal_struct {
 #define is_rt_policy(p)		((p) != SCHED_NORMAL && (p) != SCHED_BATCH)
 #define has_rt_policy(p)	unlikely(is_rt_policy((p)->policy))
 
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+#define set_rcu_prio(p, prio)  ((p)->rcu_prio = (prio))
+#define get_rcu_prio(p) ((p)->rcu_prio)
+#else /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
+#define set_rcu_prio(p, prio)  do { } while (0)
+#define get_rcu_prio(p) MAX_PRIO
+#endif /* #else #ifdef CONFIG_PREEMPT_RCU_BOOST */
+
 /*
  * Some day this will be a full-fledged user tracking system..
  */
@@ -982,6 +990,9 @@ struct task_struct {
 #endif
 	int load_weight;	/* for niceness load balancing purposes */
 	int prio, static_prio, normal_prio;
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+	int rcu_prio;
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
 	struct list_head run_list;
 	struct prio_array *array;
 
@@ -1003,6 +1014,11 @@ struct task_struct {
         atomic_t *rcu_flipctr1;
         atomic_t *rcu_flipctr2;
 #endif
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+	struct rcu_boost_dat *rcub_rbdp;
+	enum rcu_boost_state rcub_state;
+	struct list_head rcub_entry;
+#endif /* #ifdef CONFIG_PREEMPT_RCU_BOOST */
 
 #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
 	struct sched_info sched_info;
init/main.c +1 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/init/main.c linux-2.6.20-rc4-rt1-rcub/init/main.c
@@ -712,6 +712,7 @@ static void __init do_basic_setup(void)
 	init_workqueues();
 	usermodehelper_init();
 	driver_init();
+	init_rcu_boost_late();
 
 #ifdef CONFIG_SYSCTL
 	sysctl_init();
kernel/Kconfig.preempt +32 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/Kconfig.preempt linux-2.6.20-rc4-rt1-rcub/kernel/Kconfig.preempt
@@ -176,3 +176,35 @@ config RCU_TRACE
 
 	  Say Y here if you want to enable RCU tracing
 	  Say N if you are unsure.
+
+config PREEMPT_RCU_BOOST
+	bool "Enable priority boosting of RCU read-side critical sections"
+	depends on PREEMPT_RCU
+	default n
+	help
+	  This option permits priority boosting of RCU read-side critical
+	  sections that have been preempted in order to prevent indefinite
+	  delay of grace periods in face of runaway non-realtime processes.
+
+	  Say N if you are unsure.
+
+config PREEMPT_RCU_BOOST_STATS
+	bool "Enable RCU priority-boosting statistic printing"
+	depends on PREEMPT_RCU_BOOST
+	default n
+	help
+	  This option enables debug printk()s of RCU boost statistics,
+	  which are normally only used to debug RCU priority boost
+	  implementations.
+
+	  Say N if you are unsure.
+
+config PREEMPT_RCU_BOOST_STATS_INTERVAL
+	int "RCU priority-boosting statistic printing interval (seconds)"
+	depends on PREEMPT_RCU_BOOST_STATS
+	default 100
+	range 10 86400
+	help
+	  This option controls the timing of debug printk()s of RCU boost
+	  statistics, which are normally only used to debug RCU priority
+	  boost implementations.
kernel/fork.c +6 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/fork.c linux-2.6.20-rc4-rt1-rcub/kernel/fork.c
@@ -1129,6 +1129,12 @@ static struct task_struct *copy_process(
 	p->softirq_context = 0;
 #endif
 	p->pagefault_disabled = 0;
+#ifdef CONFIG_PREEMPT_RCU_BOOST
+	p->rcu_prio = MAX_PRIO;
+	p->rcub_rbdp = NULL;
+	p->rcub_state = RCU_BOOST_IDLE;
+	INIT_LIST_HEAD(&p->rcub_entry);
+#endif
 #ifdef CONFIG_LOCKDEP
 	p->lockdep_depth = 0; /* no locks held yet */
 	p->curr_chain_key = 0;
kernel/rcupreempt.c +528 -0

558 lines not on this page · raw message

kernel/rtmutex.c +4 -3
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/rtmutex.c linux-2.6.20-rc4-rt1-rcub/kernel/rtmutex.c
@@ -105,11 +105,12 @@ static inline void init_lists(struct rt_
  */
 int rt_mutex_getprio(struct task_struct *task)
 {
+	int prio = min(task->normal_prio, get_rcu_prio(task));
+
 	if (likely(!task_has_pi_waiters(task)))
-		return task->normal_prio;
+		return prio;
 
-	return min(task_top_pi_waiter(task)->pi_list_entry.prio,
-		   task->normal_prio);
+	return min(task_top_pi_waiter(task)->pi_list_entry.prio, prio);
 }
 
 /*
kernel/sched.c +5 -0
diff -urpNa -X dontdiff linux-2.6.20-rc4-rt1/kernel/sched.c linux-2.6.20-rc4-rt1-rcub/kernel/sched.c
@@ -1962,6 +1962,7 @@ void fastcall sched_fork(struct task_str
 	 * Make sure we do not leak PI boosting priority to the child:
 	 */
 	p->prio = current->normal_prio;
+	set_rcu_prio(p, MAX_PRIO);
 
 	INIT_LIST_HEAD(&p->run_list);
 	p->array = NULL;
@@ -2044,6 +2045,7 @@ void fastcall wake_up_new_task(struct ta
 			else {
 				p->prio = current->prio;
 				p->normal_prio = current->normal_prio;
+				set_rcu_prio(p, MAX_PRIO);
 				__activate_task_after(p, current, rq);
 			}
 			set_need_resched();
@@ -3986,6 +3988,8 @@ void __sched __schedule(void)
 	}
 	profile_hit(SCHED_PROFILING, __builtin_return_address(0));
 
+	rcu_preempt_boost();
+
 	preempt_disable(); // FIXME: disable irqs here
 	prev = current;
 	release_kernel_lock(prev);
@@ -5725,6 +5729,7 @@ void __cpuinit init_idle(struct task_str
 	idle->sleep_avg = 0;
 	idle->array = NULL;
 	idle->prio = idle->normal_prio = MAX_PRIO;
+	set_rcu_prio(idle, MAX_PRIO);
 	idle->state = TASK_RUNNING;
 	idle->cpus_allowed = cpumask_of_cpu(cpu);
 	set_task_cpu(idle, cpu);
Acked-by
#13 lore raw
Quoted 11 lines · Paul E. McKenney
Quoted 2 lines · Paul E. McKenney
Quoted 1 line · Josh Triplett

Overall, this code looks sensible to me. Some comments on the patch below.

[ . . . ]

Quoted 3 lines

Thank you again for the careful and thorough review!!!

I will test these changes and send out an update.

And here is the updated RCU priority-boost patch.

Thanks for integrating my changes, and so quickly! :)

Quoted 1 line

- Josh Triplett