aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paul.mckenney@linaro.org>2010-09-09 16:40:39 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2010-11-17 18:45:09 -0500
commitb2c0710c464ede15e1fc52fb1e7ee9ba54cea186 (patch)
tree7524518fec8a02e53c3fab558b40a5e94f0bb5ec
parent8e8be45e8e55daa381028aec339829929ddb53a5 (diff)
rcu: move TINY_RCU from softirq to kthread
If RCU priority boosting is to be meaningful, callback invocation must be boosted in addition to preempted RCU readers. Otherwise, in presence of CPU real-time threads, the grace period ends, but the callbacks don't get invoked. If the callbacks don't get invoked, the associated memory doesn't get freed, so the system is still subject to OOM. But it is not reasonable to priority-boost RCU_SOFTIRQ, so this commit moves the callback invocations to a kthread, which can be boosted easily. Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-rw-r--r--include/linux/rcupdate.h1
-rw-r--r--include/linux/rcutiny.h8
-rw-r--r--include/linux/rcutree.h1
-rw-r--r--kernel/rcutiny.c71
-rw-r--r--kernel/rcutiny_plugin.h15
5 files changed, 70 insertions, 26 deletions
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 03cda7bed985..7142ee3304ab 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -118,7 +118,6 @@ static inline int rcu_preempt_depth(void)
118#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ 118#endif /* #else #ifdef CONFIG_PREEMPT_RCU */
119 119
120/* Internal to kernel */ 120/* Internal to kernel */
121extern void rcu_init(void);
122extern void rcu_sched_qs(int cpu); 121extern void rcu_sched_qs(int cpu);
123extern void rcu_bh_qs(int cpu); 122extern void rcu_bh_qs(int cpu);
124extern void rcu_check_callbacks(int cpu, int user); 123extern void rcu_check_callbacks(int cpu, int user);
diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h
index 13877cb93a60..ea025a611fcc 100644
--- a/include/linux/rcutiny.h
+++ b/include/linux/rcutiny.h
@@ -27,7 +27,9 @@
27 27
28#include <linux/cache.h> 28#include <linux/cache.h>
29 29
30#define rcu_init_sched() do { } while (0) 30static inline void rcu_init(void)
31{
32}
31 33
32#ifdef CONFIG_TINY_RCU 34#ifdef CONFIG_TINY_RCU
33 35
@@ -125,16 +127,12 @@ static inline void rcu_cpu_stall_reset(void)
125} 127}
126 128
127#ifdef CONFIG_DEBUG_LOCK_ALLOC 129#ifdef CONFIG_DEBUG_LOCK_ALLOC
128
129extern int rcu_scheduler_active __read_mostly; 130extern int rcu_scheduler_active __read_mostly;
130extern void rcu_scheduler_starting(void); 131extern void rcu_scheduler_starting(void);
131
132#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 132#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
133
134static inline void rcu_scheduler_starting(void) 133static inline void rcu_scheduler_starting(void)
135{ 134{
136} 135}
137
138#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 136#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
139 137
140#endif /* __LINUX_RCUTINY_H */ 138#endif /* __LINUX_RCUTINY_H */
diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h
index 95518e628794..c0e96833aa73 100644
--- a/include/linux/rcutree.h
+++ b/include/linux/rcutree.h
@@ -30,6 +30,7 @@
30#ifndef __LINUX_RCUTREE_H 30#ifndef __LINUX_RCUTREE_H
31#define __LINUX_RCUTREE_H 31#define __LINUX_RCUTREE_H
32 32
33extern void rcu_init(void);
33extern void rcu_note_context_switch(int cpu); 34extern void rcu_note_context_switch(int cpu);
34extern int rcu_needs_cpu(int cpu); 35extern int rcu_needs_cpu(int cpu);
35extern void rcu_cpu_stall_reset(void); 36extern void rcu_cpu_stall_reset(void);
diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c
index d806735342ac..86eef29cdfb2 100644
--- a/kernel/rcutiny.c
+++ b/kernel/rcutiny.c
@@ -59,8 +59,15 @@ int rcu_scheduler_active __read_mostly;
59EXPORT_SYMBOL_GPL(rcu_scheduler_active); 59EXPORT_SYMBOL_GPL(rcu_scheduler_active);
60#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 60#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
61 61
62/* Controls for rcu_cbs() kthread, replacing RCU_SOFTIRQ used previously. */
63static struct task_struct *rcu_cbs_task;
64static DECLARE_WAIT_QUEUE_HEAD(rcu_cbs_wq);
65static unsigned long have_rcu_cbs;
66static void invoke_rcu_cbs(void);
67
62/* Forward declarations for rcutiny_plugin.h. */ 68/* Forward declarations for rcutiny_plugin.h. */
63static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp); 69static void rcu_process_callbacks(struct rcu_ctrlblk *rcp);
70static int rcu_cbs(void *arg);
64static void __call_rcu(struct rcu_head *head, 71static void __call_rcu(struct rcu_head *head,
65 void (*func)(struct rcu_head *rcu), 72 void (*func)(struct rcu_head *rcu),
66 struct rcu_ctrlblk *rcp); 73 struct rcu_ctrlblk *rcp);
@@ -123,7 +130,7 @@ void rcu_sched_qs(int cpu)
123{ 130{
124 if (rcu_qsctr_help(&rcu_sched_ctrlblk) + 131 if (rcu_qsctr_help(&rcu_sched_ctrlblk) +
125 rcu_qsctr_help(&rcu_bh_ctrlblk)) 132 rcu_qsctr_help(&rcu_bh_ctrlblk))
126 raise_softirq(RCU_SOFTIRQ); 133 invoke_rcu_cbs();
127} 134}
128 135
129/* 136/*
@@ -132,7 +139,7 @@ void rcu_sched_qs(int cpu)
132void rcu_bh_qs(int cpu) 139void rcu_bh_qs(int cpu)
133{ 140{
134 if (rcu_qsctr_help(&rcu_bh_ctrlblk)) 141 if (rcu_qsctr_help(&rcu_bh_ctrlblk))
135 raise_softirq(RCU_SOFTIRQ); 142 invoke_rcu_cbs();
136} 143}
137 144
138/* 145/*
@@ -152,10 +159,10 @@ void rcu_check_callbacks(int cpu, int user)
152} 159}
153 160
154/* 161/*
155 * Helper function for rcu_process_callbacks() that operates on the 162 * Invoke the RCU callbacks on the specified rcu_ctrlkblk structure
156 * specified rcu_ctrlkblk structure. 163 * whose grace period has elapsed.
157 */ 164 */
158static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) 165static void rcu_process_callbacks(struct rcu_ctrlblk *rcp)
159{ 166{
160 struct rcu_head *next, *list; 167 struct rcu_head *next, *list;
161 unsigned long flags; 168 unsigned long flags;
@@ -180,19 +187,52 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp)
180 next = list->next; 187 next = list->next;
181 prefetch(next); 188 prefetch(next);
182 debug_rcu_head_unqueue(list); 189 debug_rcu_head_unqueue(list);
190 local_bh_disable();
183 list->func(list); 191 list->func(list);
192 local_bh_enable();
184 list = next; 193 list = next;
185 } 194 }
186} 195}
187 196
188/* 197/*
189 * Invoke any callbacks whose grace period has completed. 198 * This kthread invokes RCU callbacks whose grace periods have
199 * elapsed. It is awakened as needed, and takes the place of the
200 * RCU_SOFTIRQ that was used previously for this purpose.
201 * This is a kthread, but it is never stopped, at least not until
202 * the system goes down.
203 */
204static int rcu_cbs(void *arg)
205{
206 unsigned long work;
207 unsigned long flags;
208
209 for (;;) {
210 wait_event(rcu_cbs_wq, have_rcu_cbs != 0);
211 local_irq_save(flags);
212 work = have_rcu_cbs;
213 have_rcu_cbs = 0;
214 local_irq_restore(flags);
215 if (work) {
216 rcu_process_callbacks(&rcu_sched_ctrlblk);
217 rcu_process_callbacks(&rcu_bh_ctrlblk);
218 rcu_preempt_process_callbacks();
219 }
220 }
221
222 return 0; /* Not reached, but needed to shut gcc up. */
223}
224
225/*
226 * Wake up rcu_cbs() to process callbacks now eligible for invocation.
190 */ 227 */
191static void rcu_process_callbacks(struct softirq_action *unused) 228static void invoke_rcu_cbs(void)
192{ 229{
193 __rcu_process_callbacks(&rcu_sched_ctrlblk); 230 unsigned long flags;
194 __rcu_process_callbacks(&rcu_bh_ctrlblk); 231
195 rcu_preempt_process_callbacks(); 232 local_irq_save(flags);
233 have_rcu_cbs = 1;
234 wake_up(&rcu_cbs_wq);
235 local_irq_restore(flags);
196} 236}
197 237
198/* 238/*
@@ -282,7 +322,12 @@ void rcu_barrier_sched(void)
282} 322}
283EXPORT_SYMBOL_GPL(rcu_barrier_sched); 323EXPORT_SYMBOL_GPL(rcu_barrier_sched);
284 324
285void __init rcu_init(void) 325/*
326 * Spawn the kthread that invokes RCU callbacks.
327 */
328static int __init rcu_spawn_kthreads(void)
286{ 329{
287 open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); 330 rcu_cbs_task = kthread_run(rcu_cbs, NULL, "rcu_cbs");
331 return 0;
288} 332}
333early_initcall(rcu_spawn_kthreads);
diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h
index 6ceca4f745ff..95f9239df512 100644
--- a/kernel/rcutiny_plugin.h
+++ b/kernel/rcutiny_plugin.h
@@ -22,6 +22,8 @@
22 * Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com> 22 * Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
23 */ 23 */
24 24
25#include <linux/kthread.h>
26
25#ifdef CONFIG_TINY_PREEMPT_RCU 27#ifdef CONFIG_TINY_PREEMPT_RCU
26 28
27#include <linux/delay.h> 29#include <linux/delay.h>
@@ -164,9 +166,9 @@ static void rcu_preempt_cpu_qs(void)
164 if (!rcu_preempt_blocked_readers_any()) 166 if (!rcu_preempt_blocked_readers_any())
165 rcu_preempt_ctrlblk.rcb.donetail = rcu_preempt_ctrlblk.nexttail; 167 rcu_preempt_ctrlblk.rcb.donetail = rcu_preempt_ctrlblk.nexttail;
166 168
167 /* If there are done callbacks, make RCU_SOFTIRQ process them. */ 169 /* If there are done callbacks, cause them to be invoked. */
168 if (*rcu_preempt_ctrlblk.rcb.donetail != NULL) 170 if (*rcu_preempt_ctrlblk.rcb.donetail != NULL)
169 raise_softirq(RCU_SOFTIRQ); 171 invoke_rcu_cbs();
170} 172}
171 173
172/* 174/*
@@ -374,7 +376,7 @@ static void rcu_preempt_check_callbacks(void)
374 rcu_preempt_cpu_qs(); 376 rcu_preempt_cpu_qs();
375 if (&rcu_preempt_ctrlblk.rcb.rcucblist != 377 if (&rcu_preempt_ctrlblk.rcb.rcucblist !=
376 rcu_preempt_ctrlblk.rcb.donetail) 378 rcu_preempt_ctrlblk.rcb.donetail)
377 raise_softirq(RCU_SOFTIRQ); 379 invoke_rcu_cbs();
378 if (rcu_preempt_gp_in_progress() && 380 if (rcu_preempt_gp_in_progress() &&
379 rcu_cpu_blocking_cur_gp() && 381 rcu_cpu_blocking_cur_gp() &&
380 rcu_preempt_running_reader()) 382 rcu_preempt_running_reader())
@@ -383,7 +385,7 @@ static void rcu_preempt_check_callbacks(void)
383 385
384/* 386/*
385 * TINY_PREEMPT_RCU has an extra callback-list tail pointer to 387 * TINY_PREEMPT_RCU has an extra callback-list tail pointer to
386 * update, so this is invoked from __rcu_process_callbacks() to 388 * update, so this is invoked from rcu_process_callbacks() to
387 * handle that case. Of course, it is invoked for all flavors of 389 * handle that case. Of course, it is invoked for all flavors of
388 * RCU, but RCU callbacks can appear only on one of the lists, and 390 * RCU, but RCU callbacks can appear only on one of the lists, and
389 * neither ->nexttail nor ->donetail can possibly be NULL, so there 391 * neither ->nexttail nor ->donetail can possibly be NULL, so there
@@ -400,7 +402,7 @@ static void rcu_preempt_remove_callbacks(struct rcu_ctrlblk *rcp)
400 */ 402 */
401static void rcu_preempt_process_callbacks(void) 403static void rcu_preempt_process_callbacks(void)
402{ 404{
403 __rcu_process_callbacks(&rcu_preempt_ctrlblk.rcb); 405 rcu_process_callbacks(&rcu_preempt_ctrlblk.rcb);
404} 406}
405 407
406/* 408/*
@@ -599,14 +601,13 @@ static void rcu_preempt_process_callbacks(void)
599#endif /* #else #ifdef CONFIG_TINY_PREEMPT_RCU */ 601#endif /* #else #ifdef CONFIG_TINY_PREEMPT_RCU */
600 602
601#ifdef CONFIG_DEBUG_LOCK_ALLOC 603#ifdef CONFIG_DEBUG_LOCK_ALLOC
602
603#include <linux/kernel_stat.h> 604#include <linux/kernel_stat.h>
604 605
605/* 606/*
606 * During boot, we forgive RCU lockdep issues. After this function is 607 * During boot, we forgive RCU lockdep issues. After this function is
607 * invoked, we start taking RCU lockdep issues seriously. 608 * invoked, we start taking RCU lockdep issues seriously.
608 */ 609 */
609void rcu_scheduler_starting(void) 610void __init rcu_scheduler_starting(void)
610{ 611{
611 WARN_ON(nr_context_switches() > 0); 612 WARN_ON(nr_context_switches() > 0);
612 rcu_scheduler_active = 1; 613 rcu_scheduler_active = 1;