aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2010-06-29 19:49:16 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2010-08-20 11:55:00 -0400
commita57eb940d130477a799dfb24a570ee04979c0f7f (patch)
tree5add1c135a302cf1c1a454b0620ed17eb802923b /include
parent4d87ffadbba88105f33271bef5f2c79366c6a4e1 (diff)
rcu: Add a TINY_PREEMPT_RCU
Implement a small-memory-footprint uniprocessor-only implementation of preemptible RCU. This implementation uses but a single blocked-tasks list rather than the combinatorial number used per leaf rcu_node by TREE_PREEMPT_RCU, which reduces memory consumption and greatly simplifies processing. This version also takes advantage of uniprocessor execution to accelerate grace periods in the case where there are no readers. The general design is otherwise broadly similar to that of TREE_PREEMPT_RCU. This implementation is a step towards having RCU implementation driven off of the SMP and PREEMPT kernel configuration variables, which can happen once this implementation has accumulated sufficient experience. Removed ACCESS_ONCE() from __rcu_read_unlock() and added barrier() as suggested by Steve Rostedt in order to avoid the compiler-reordering issue noted by Mathieu Desnoyers (http://lkml.org/lkml/2010/8/16/183). As can be seen below, CONFIG_TINY_PREEMPT_RCU represents almost 5Kbyte savings compared to CONFIG_TREE_PREEMPT_RCU. Of course, for non-real-time workloads, CONFIG_TINY_RCU is even better. CONFIG_TREE_PREEMPT_RCU text data bss dec filename 13 0 0 13 kernel/rcupdate.o 6170 825 28 7023 kernel/rcutree.o ---- 7026 Total CONFIG_TINY_PREEMPT_RCU text data bss dec filename 13 0 0 13 kernel/rcupdate.o 2081 81 8 2170 kernel/rcutiny.o ---- 2183 Total CONFIG_TINY_RCU (non-preemptible) text data bss dec filename 13 0 0 13 kernel/rcupdate.o 719 25 0 744 kernel/rcutiny.o --- 757 Total Requested-by: Loïc Minier <loic.minier@canonical.com> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'include')
-rw-r--r--include/linux/hardirq.h2
-rw-r--r--include/linux/init_task.h10
-rw-r--r--include/linux/rcupdate.h3
-rw-r--r--include/linux/rcutiny.h126
-rw-r--r--include/linux/rcutree.h2
-rw-r--r--include/linux/sched.h10
6 files changed, 108 insertions, 45 deletions
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
index d5b387669dab..1f4517d55b19 100644
--- a/include/linux/hardirq.h
+++ b/include/linux/hardirq.h
@@ -139,7 +139,7 @@ static inline void account_system_vtime(struct task_struct *tsk)
139#endif 139#endif
140 140
141#if defined(CONFIG_NO_HZ) 141#if defined(CONFIG_NO_HZ)
142#if defined(CONFIG_TINY_RCU) 142#if defined(CONFIG_TINY_RCU) || defined(CONFIG_TINY_PREEMPT_RCU)
143extern void rcu_enter_nohz(void); 143extern void rcu_enter_nohz(void);
144extern void rcu_exit_nohz(void); 144extern void rcu_exit_nohz(void);
145 145
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 6460fc65ed6b..2fea6c8ef6ba 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -82,11 +82,17 @@ extern struct group_info init_groups;
82# define CAP_INIT_BSET CAP_FULL_SET 82# define CAP_INIT_BSET CAP_FULL_SET
83 83
84#ifdef CONFIG_TREE_PREEMPT_RCU 84#ifdef CONFIG_TREE_PREEMPT_RCU
85#define INIT_TASK_RCU_TREE_PREEMPT() \
86 .rcu_blocked_node = NULL,
87#else
88#define INIT_TASK_RCU_TREE_PREEMPT(tsk)
89#endif
90#ifdef CONFIG_PREEMPT_RCU
85#define INIT_TASK_RCU_PREEMPT(tsk) \ 91#define INIT_TASK_RCU_PREEMPT(tsk) \
86 .rcu_read_lock_nesting = 0, \ 92 .rcu_read_lock_nesting = 0, \
87 .rcu_read_unlock_special = 0, \ 93 .rcu_read_unlock_special = 0, \
88 .rcu_blocked_node = NULL, \ 94 .rcu_node_entry = LIST_HEAD_INIT(tsk.rcu_node_entry), \
89 .rcu_node_entry = LIST_HEAD_INIT(tsk.rcu_node_entry), 95 INIT_TASK_RCU_TREE_PREEMPT()
90#else 96#else
91#define INIT_TASK_RCU_PREEMPT(tsk) 97#define INIT_TASK_RCU_PREEMPT(tsk)
92#endif 98#endif
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 27b44b3e3024..24b896649384 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -58,7 +58,6 @@ struct rcu_head {
58}; 58};
59 59
60/* Exported common interfaces */ 60/* Exported common interfaces */
61extern void rcu_barrier(void);
62extern void rcu_barrier_bh(void); 61extern void rcu_barrier_bh(void);
63extern void rcu_barrier_sched(void); 62extern void rcu_barrier_sched(void);
64extern void synchronize_sched_expedited(void); 63extern void synchronize_sched_expedited(void);
@@ -69,7 +68,7 @@ extern void rcu_init(void);
69 68
70#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) 69#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU)
71#include <linux/rcutree.h> 70#include <linux/rcutree.h>
72#elif defined(CONFIG_TINY_RCU) 71#elif defined(CONFIG_TINY_RCU) || defined(CONFIG_TINY_PREEMPT_RCU)
73#include <linux/rcutiny.h> 72#include <linux/rcutiny.h>
74#else 73#else
75#error "Unknown RCU implementation specified to kernel configuration" 74#error "Unknown RCU implementation specified to kernel configuration"
diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h
index e2e893144a84..4cc5eba41616 100644
--- a/include/linux/rcutiny.h
+++ b/include/linux/rcutiny.h
@@ -29,66 +29,51 @@
29 29
30void rcu_sched_qs(int cpu); 30void rcu_sched_qs(int cpu);
31void rcu_bh_qs(int cpu); 31void rcu_bh_qs(int cpu);
32static inline void rcu_note_context_switch(int cpu)
33{
34 rcu_sched_qs(cpu);
35}
36 32
33#ifdef CONFIG_TINY_RCU
37#define __rcu_read_lock() preempt_disable() 34#define __rcu_read_lock() preempt_disable()
38#define __rcu_read_unlock() preempt_enable() 35#define __rcu_read_unlock() preempt_enable()
36#else /* #ifdef CONFIG_TINY_RCU */
37void __rcu_read_lock(void);
38void __rcu_read_unlock(void);
39#endif /* #else #ifdef CONFIG_TINY_RCU */
39#define __rcu_read_lock_bh() local_bh_disable() 40#define __rcu_read_lock_bh() local_bh_disable()
40#define __rcu_read_unlock_bh() local_bh_enable() 41#define __rcu_read_unlock_bh() local_bh_enable()
41#define call_rcu_sched call_rcu 42extern void call_rcu_sched(struct rcu_head *head,
43 void (*func)(struct rcu_head *rcu));
42 44
43#define rcu_init_sched() do { } while (0) 45#define rcu_init_sched() do { } while (0)
44extern void rcu_check_callbacks(int cpu, int user);
45 46
46static inline int rcu_needs_cpu(int cpu) 47extern void synchronize_sched(void);
47{
48 return 0;
49}
50 48
51/* 49#ifdef CONFIG_TINY_RCU
52 * Return the number of grace periods.
53 */
54static inline long rcu_batches_completed(void)
55{
56 return 0;
57}
58 50
59/* 51#define call_rcu call_rcu_sched
60 * Return the number of bottom-half grace periods.
61 */
62static inline long rcu_batches_completed_bh(void)
63{
64 return 0;
65}
66 52
67static inline void rcu_force_quiescent_state(void) 53static inline void synchronize_rcu(void)
68{ 54{
55 synchronize_sched();
69} 56}
70 57
71static inline void rcu_bh_force_quiescent_state(void) 58static inline void synchronize_rcu_expedited(void)
72{ 59{
60 synchronize_sched(); /* Only one CPU, so pretty fast anyway!!! */
73} 61}
74 62
75static inline void rcu_sched_force_quiescent_state(void) 63static inline void rcu_barrier(void)
76{ 64{
65 rcu_barrier_sched(); /* Only one CPU, so only one list of callbacks! */
77} 66}
78 67
79extern void synchronize_sched(void); 68#else /* #ifdef CONFIG_TINY_RCU */
80 69
81static inline void synchronize_rcu(void) 70void synchronize_rcu(void);
82{ 71void rcu_barrier(void);
83 synchronize_sched(); 72void synchronize_rcu_expedited(void);
84}
85 73
86static inline void synchronize_rcu_bh(void) 74#endif /* #else #ifdef CONFIG_TINY_RCU */
87{
88 synchronize_sched();
89}
90 75
91static inline void synchronize_rcu_expedited(void) 76static inline void synchronize_rcu_bh(void)
92{ 77{
93 synchronize_sched(); 78 synchronize_sched();
94} 79}
@@ -117,15 +102,82 @@ static inline void rcu_exit_nohz(void)
117 102
118#endif /* #else #ifdef CONFIG_NO_HZ */ 103#endif /* #else #ifdef CONFIG_NO_HZ */
119 104
105#ifdef CONFIG_TINY_RCU
106
107static inline void rcu_preempt_note_context_switch(void)
108{
109}
110
120static inline void exit_rcu(void) 111static inline void exit_rcu(void)
121{ 112{
122} 113}
123 114
115static inline int rcu_needs_cpu(int cpu)
116{
117 return 0;
118}
119
124static inline int rcu_preempt_depth(void) 120static inline int rcu_preempt_depth(void)
125{ 121{
126 return 0; 122 return 0;
127} 123}
128 124
125#else /* #ifdef CONFIG_TINY_RCU */
126
127void rcu_preempt_note_context_switch(void);
128extern void exit_rcu(void);
129int rcu_preempt_needs_cpu(void);
130
131static inline int rcu_needs_cpu(int cpu)
132{
133 return rcu_preempt_needs_cpu();
134}
135
136/*
137 * Defined as macro as it is a very low level header
138 * included from areas that don't even know about current
139 * FIXME: combine with include/linux/rcutree.h into rcupdate.h.
140 */
141#define rcu_preempt_depth() (current->rcu_read_lock_nesting)
142
143#endif /* #else #ifdef CONFIG_TINY_RCU */
144
145static inline void rcu_note_context_switch(int cpu)
146{
147 rcu_sched_qs(cpu);
148 rcu_preempt_note_context_switch();
149}
150
151extern void rcu_check_callbacks(int cpu, int user);
152
153/*
154 * Return the number of grace periods.
155 */
156static inline long rcu_batches_completed(void)
157{
158 return 0;
159}
160
161/*
162 * Return the number of bottom-half grace periods.
163 */
164static inline long rcu_batches_completed_bh(void)
165{
166 return 0;
167}
168
169static inline void rcu_force_quiescent_state(void)
170{
171}
172
173static inline void rcu_bh_force_quiescent_state(void)
174{
175}
176
177static inline void rcu_sched_force_quiescent_state(void)
178{
179}
180
129#ifdef CONFIG_DEBUG_LOCK_ALLOC 181#ifdef CONFIG_DEBUG_LOCK_ALLOC
130 182
131extern int rcu_scheduler_active __read_mostly; 183extern int rcu_scheduler_active __read_mostly;
diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h
index c0ed1c056f29..c13b85dd22bc 100644
--- a/include/linux/rcutree.h
+++ b/include/linux/rcutree.h
@@ -95,6 +95,8 @@ static inline void synchronize_rcu_bh_expedited(void)
95 synchronize_sched_expedited(); 95 synchronize_sched_expedited();
96} 96}
97 97
98extern void rcu_barrier(void);
99
98extern void rcu_check_callbacks(int cpu, int user); 100extern void rcu_check_callbacks(int cpu, int user);
99 101
100extern long rcu_batches_completed(void); 102extern long rcu_batches_completed(void);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 2c756666c111..e18473f0eb78 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1202,11 +1202,13 @@ struct task_struct {
1202 unsigned int policy; 1202 unsigned int policy;
1203 cpumask_t cpus_allowed; 1203 cpumask_t cpus_allowed;
1204 1204
1205#ifdef CONFIG_TREE_PREEMPT_RCU 1205#ifdef CONFIG_PREEMPT_RCU
1206 int rcu_read_lock_nesting; 1206 int rcu_read_lock_nesting;
1207 char rcu_read_unlock_special; 1207 char rcu_read_unlock_special;
1208 struct rcu_node *rcu_blocked_node;
1209 struct list_head rcu_node_entry; 1208 struct list_head rcu_node_entry;
1209#endif /* #ifdef CONFIG_PREEMPT_RCU */
1210#ifdef CONFIG_TREE_PREEMPT_RCU
1211 struct rcu_node *rcu_blocked_node;
1210#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 1212#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
1211 1213
1212#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) 1214#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
@@ -1740,7 +1742,7 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *
1740#define tsk_used_math(p) ((p)->flags & PF_USED_MATH) 1742#define tsk_used_math(p) ((p)->flags & PF_USED_MATH)
1741#define used_math() tsk_used_math(current) 1743#define used_math() tsk_used_math(current)
1742 1744
1743#ifdef CONFIG_TREE_PREEMPT_RCU 1745#ifdef CONFIG_PREEMPT_RCU
1744 1746
1745#define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */ 1747#define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */
1746#define RCU_READ_UNLOCK_NEED_QS (1 << 1) /* RCU core needs CPU response. */ 1748#define RCU_READ_UNLOCK_NEED_QS (1 << 1) /* RCU core needs CPU response. */
@@ -1749,7 +1751,9 @@ static inline void rcu_copy_process(struct task_struct *p)
1749{ 1751{
1750 p->rcu_read_lock_nesting = 0; 1752 p->rcu_read_lock_nesting = 0;
1751 p->rcu_read_unlock_special = 0; 1753 p->rcu_read_unlock_special = 0;
1754#ifdef CONFIG_TREE_PREEMPT_RCU
1752 p->rcu_blocked_node = NULL; 1755 p->rcu_blocked_node = NULL;
1756#endif
1753 INIT_LIST_HEAD(&p->rcu_node_entry); 1757 INIT_LIST_HEAD(&p->rcu_node_entry);
1754} 1758}
1755 1759