RCU priority boosting that survives semi-vicious testing
Subsystems (4)
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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 taelse { 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);
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
@@ -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(stcall_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:
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.clinux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.ckernel/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(stcall_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
-1static 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
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.clinux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.ckernel/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(stcall_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_interruptiblehere 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:
returnsprintf(page, ...);
?
Good point.
Quoted 2 lines
Also, if you decide to make
rcu_torture_preempt_errorsan 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
-1static 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
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_BOOSTis 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.clinux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.ckernel/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_interruptiblehere 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_opsrcu_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
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_BOOSTis 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 forCONFIG_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_paramwould 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.clinux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.ckernel/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_interruptiblehere 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_opsrcu_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
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_BOOSTis 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 forCONFIG_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_paramwould 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.clinux-2.6.20-rc4-rt1-rcubtorture/kernel/rcutorture.ckernel/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_interruptiblehere 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 thekthread_should_stop()check to happen.
Ah, that makes much more sense; thanks.
- Josh Triplett
Re: [RFC PATCH -rt 2/2] RCU priority boosting additions to rcutorture
In reply to Josh TriplettQuoted 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_BOOSTis 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 forCONFIG_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_paramwould 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
@@ -58,6 +58,7 @@ static int stat_interval; /* Interval bestatic 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(stcall_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(vostatic 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 *tagprintk (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:
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_BOOSTis 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 forCONFIG_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_paramwould work here.OK, am finally with you. See below for updated patch.
Looks good to me.
Quoted 1 line
Signed-off-by Paul E. McKenney <[email protected]>
- 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.clinux-2.6.20-rc4-rt1-rcub/kernel/rcupreempt.ckernel/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 (¤t->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.clinux-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
-3if (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
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.clinux-2.6.20-rc4-rt1-rcub/kernel/rcupreempt.ckernel/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_STATEaccordingly, and usingN_RCU_BOOST_STATEwithout the +1 inrbs_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_idxand 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_boostingfor 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_INFOor 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 (¤t->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.clinux-2.6.20-rc4-rt1-rcub/kernel/rtmutex.ckernel/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
-3if (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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 taelse { 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);
Josh Triplett
Acked-byQuoted 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
Signed-off-by Paul E. McKenney <[email protected]>
- Josh Triplett