RCU priority boosting that survives moderate testing
Subsystems (4)
RFC PATCH 1/1 RCU priority boosting that survives moderate testing
Hello!
This is a updated version of the earlier RCU-boosting patch
(http://lkml.org/lkml/2007/1/2/347). It boosts the priority of RCU read-side critical sections in -rt kernels, and the context diff is almost 300 lines shorter than its predecessor. Simplifications were inspired by the act of attempting to design enterprise-level testing for this patch's predecessor -- after all, you don't have to write tests for any code that you manage to eliminate!
Still lacks tie-in to OOM, and still needs more vigorous testing (though less so than its predecessor). However, a design doc is on its way.
This version permits the system administrator to manually adjust the priority of the RCU-booster task, which will result in RCU boosting to the priority one slot less-favored than the booster task itself. Any tasks that have been previously boosted will have their priority adjusted to align with the RCU-booster task's new priority.
As always, any and all comments appreciated!
---
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);