diff options
Diffstat (limited to 'kernel/rcutree_trace.c')
-rw-r--r-- | kernel/rcutree_trace.c | 226 |
1 files changed, 197 insertions, 29 deletions
diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c index 36c95b45738e..4e144876dc68 100644 --- a/kernel/rcutree_trace.c +++ b/kernel/rcutree_trace.c | |||
@@ -46,6 +46,22 @@ | |||
46 | #define RCU_TREE_NONCORE | 46 | #define RCU_TREE_NONCORE |
47 | #include "rcutree.h" | 47 | #include "rcutree.h" |
48 | 48 | ||
49 | #ifdef CONFIG_RCU_BOOST | ||
50 | |||
51 | DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status); | ||
52 | DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_cpu); | ||
53 | DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); | ||
54 | DECLARE_PER_CPU(char, rcu_cpu_has_work); | ||
55 | |||
56 | static char convert_kthread_status(unsigned int kthread_status) | ||
57 | { | ||
58 | if (kthread_status > RCU_KTHREAD_MAX) | ||
59 | return '?'; | ||
60 | return "SRWOY"[kthread_status]; | ||
61 | } | ||
62 | |||
63 | #endif /* #ifdef CONFIG_RCU_BOOST */ | ||
64 | |||
49 | static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) | 65 | static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) |
50 | { | 66 | { |
51 | if (!rdp->beenonline) | 67 | if (!rdp->beenonline) |
@@ -57,14 +73,33 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) | |||
57 | rdp->passed_quiesc, rdp->passed_quiesc_completed, | 73 | rdp->passed_quiesc, rdp->passed_quiesc_completed, |
58 | rdp->qs_pending); | 74 | rdp->qs_pending); |
59 | #ifdef CONFIG_NO_HZ | 75 | #ifdef CONFIG_NO_HZ |
60 | seq_printf(m, " dt=%d/%d dn=%d df=%lu", | 76 | seq_printf(m, " dt=%d/%d/%d df=%lu", |
61 | rdp->dynticks->dynticks, | 77 | atomic_read(&rdp->dynticks->dynticks), |
62 | rdp->dynticks->dynticks_nesting, | 78 | rdp->dynticks->dynticks_nesting, |
63 | rdp->dynticks->dynticks_nmi, | 79 | rdp->dynticks->dynticks_nmi_nesting, |
64 | rdp->dynticks_fqs); | 80 | rdp->dynticks_fqs); |
65 | #endif /* #ifdef CONFIG_NO_HZ */ | 81 | #endif /* #ifdef CONFIG_NO_HZ */ |
66 | seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); | 82 | seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); |
67 | seq_printf(m, " ql=%ld b=%ld\n", rdp->qlen, rdp->blimit); | 83 | seq_printf(m, " ql=%ld qs=%c%c%c%c", |
84 | rdp->qlen, | ||
85 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != | ||
86 | rdp->nxttail[RCU_NEXT_TAIL]], | ||
87 | ".R"[rdp->nxttail[RCU_WAIT_TAIL] != | ||
88 | rdp->nxttail[RCU_NEXT_READY_TAIL]], | ||
89 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != | ||
90 | rdp->nxttail[RCU_WAIT_TAIL]], | ||
91 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); | ||
92 | #ifdef CONFIG_RCU_BOOST | ||
93 | seq_printf(m, " kt=%d/%c/%d ktl=%x", | ||
94 | per_cpu(rcu_cpu_has_work, rdp->cpu), | ||
95 | convert_kthread_status(per_cpu(rcu_cpu_kthread_status, | ||
96 | rdp->cpu)), | ||
97 | per_cpu(rcu_cpu_kthread_cpu, rdp->cpu), | ||
98 | per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff); | ||
99 | #endif /* #ifdef CONFIG_RCU_BOOST */ | ||
100 | seq_printf(m, " b=%ld", rdp->blimit); | ||
101 | seq_printf(m, " ci=%lu co=%lu ca=%lu\n", | ||
102 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); | ||
68 | } | 103 | } |
69 | 104 | ||
70 | #define PRINT_RCU_DATA(name, func, m) \ | 105 | #define PRINT_RCU_DATA(name, func, m) \ |
@@ -113,22 +148,42 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp) | |||
113 | rdp->qs_pending); | 148 | rdp->qs_pending); |
114 | #ifdef CONFIG_NO_HZ | 149 | #ifdef CONFIG_NO_HZ |
115 | seq_printf(m, ",%d,%d,%d,%lu", | 150 | seq_printf(m, ",%d,%d,%d,%lu", |
116 | rdp->dynticks->dynticks, | 151 | atomic_read(&rdp->dynticks->dynticks), |
117 | rdp->dynticks->dynticks_nesting, | 152 | rdp->dynticks->dynticks_nesting, |
118 | rdp->dynticks->dynticks_nmi, | 153 | rdp->dynticks->dynticks_nmi_nesting, |
119 | rdp->dynticks_fqs); | 154 | rdp->dynticks_fqs); |
120 | #endif /* #ifdef CONFIG_NO_HZ */ | 155 | #endif /* #ifdef CONFIG_NO_HZ */ |
121 | seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); | 156 | seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); |
122 | seq_printf(m, ",%ld,%ld\n", rdp->qlen, rdp->blimit); | 157 | seq_printf(m, ",%ld,\"%c%c%c%c\"", rdp->qlen, |
158 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != | ||
159 | rdp->nxttail[RCU_NEXT_TAIL]], | ||
160 | ".R"[rdp->nxttail[RCU_WAIT_TAIL] != | ||
161 | rdp->nxttail[RCU_NEXT_READY_TAIL]], | ||
162 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != | ||
163 | rdp->nxttail[RCU_WAIT_TAIL]], | ||
164 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); | ||
165 | #ifdef CONFIG_RCU_BOOST | ||
166 | seq_printf(m, ",%d,\"%c\"", | ||
167 | per_cpu(rcu_cpu_has_work, rdp->cpu), | ||
168 | convert_kthread_status(per_cpu(rcu_cpu_kthread_status, | ||
169 | rdp->cpu))); | ||
170 | #endif /* #ifdef CONFIG_RCU_BOOST */ | ||
171 | seq_printf(m, ",%ld", rdp->blimit); | ||
172 | seq_printf(m, ",%lu,%lu,%lu\n", | ||
173 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); | ||
123 | } | 174 | } |
124 | 175 | ||
125 | static int show_rcudata_csv(struct seq_file *m, void *unused) | 176 | static int show_rcudata_csv(struct seq_file *m, void *unused) |
126 | { | 177 | { |
127 | seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pqc\",\"pq\","); | 178 | seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pqc\",\"pq\","); |
128 | #ifdef CONFIG_NO_HZ | 179 | #ifdef CONFIG_NO_HZ |
129 | seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\","); | 180 | seq_puts(m, "\"dt\",\"dt nesting\",\"dt NMI nesting\",\"df\","); |
130 | #endif /* #ifdef CONFIG_NO_HZ */ | 181 | #endif /* #ifdef CONFIG_NO_HZ */ |
131 | seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\"\n"); | 182 | seq_puts(m, "\"of\",\"ri\",\"ql\",\"qs\""); |
183 | #ifdef CONFIG_RCU_BOOST | ||
184 | seq_puts(m, "\"kt\",\"ktl\""); | ||
185 | #endif /* #ifdef CONFIG_RCU_BOOST */ | ||
186 | seq_puts(m, ",\"b\",\"ci\",\"co\",\"ca\"\n"); | ||
132 | #ifdef CONFIG_TREE_PREEMPT_RCU | 187 | #ifdef CONFIG_TREE_PREEMPT_RCU |
133 | seq_puts(m, "\"rcu_preempt:\"\n"); | 188 | seq_puts(m, "\"rcu_preempt:\"\n"); |
134 | PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data_csv, m); | 189 | PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data_csv, m); |
@@ -153,34 +208,97 @@ static const struct file_operations rcudata_csv_fops = { | |||
153 | .release = single_release, | 208 | .release = single_release, |
154 | }; | 209 | }; |
155 | 210 | ||
211 | #ifdef CONFIG_RCU_BOOST | ||
212 | |||
213 | static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) | ||
214 | { | ||
215 | seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu " | ||
216 | "j=%04x bt=%04x\n", | ||
217 | rnp->grplo, rnp->grphi, | ||
218 | "T."[list_empty(&rnp->blkd_tasks)], | ||
219 | "N."[!rnp->gp_tasks], | ||
220 | "E."[!rnp->exp_tasks], | ||
221 | "B."[!rnp->boost_tasks], | ||
222 | convert_kthread_status(rnp->boost_kthread_status), | ||
223 | rnp->n_tasks_boosted, rnp->n_exp_boosts, | ||
224 | rnp->n_normal_boosts, | ||
225 | (int)(jiffies & 0xffff), | ||
226 | (int)(rnp->boost_time & 0xffff)); | ||
227 | seq_printf(m, "%s: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu\n", | ||
228 | " balk", | ||
229 | rnp->n_balk_blkd_tasks, | ||
230 | rnp->n_balk_exp_gp_tasks, | ||
231 | rnp->n_balk_boost_tasks, | ||
232 | rnp->n_balk_notblocked, | ||
233 | rnp->n_balk_notyet, | ||
234 | rnp->n_balk_nos); | ||
235 | } | ||
236 | |||
237 | static int show_rcu_node_boost(struct seq_file *m, void *unused) | ||
238 | { | ||
239 | struct rcu_node *rnp; | ||
240 | |||
241 | rcu_for_each_leaf_node(&rcu_preempt_state, rnp) | ||
242 | print_one_rcu_node_boost(m, rnp); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int rcu_node_boost_open(struct inode *inode, struct file *file) | ||
247 | { | ||
248 | return single_open(file, show_rcu_node_boost, NULL); | ||
249 | } | ||
250 | |||
251 | static const struct file_operations rcu_node_boost_fops = { | ||
252 | .owner = THIS_MODULE, | ||
253 | .open = rcu_node_boost_open, | ||
254 | .read = seq_read, | ||
255 | .llseek = seq_lseek, | ||
256 | .release = single_release, | ||
257 | }; | ||
258 | |||
259 | /* | ||
260 | * Create the rcuboost debugfs entry. Standard error return. | ||
261 | */ | ||
262 | static int rcu_boost_trace_create_file(struct dentry *rcudir) | ||
263 | { | ||
264 | return !debugfs_create_file("rcuboost", 0444, rcudir, NULL, | ||
265 | &rcu_node_boost_fops); | ||
266 | } | ||
267 | |||
268 | #else /* #ifdef CONFIG_RCU_BOOST */ | ||
269 | |||
270 | static int rcu_boost_trace_create_file(struct dentry *rcudir) | ||
271 | { | ||
272 | return 0; /* There cannot be an error if we didn't create it! */ | ||
273 | } | ||
274 | |||
275 | #endif /* #else #ifdef CONFIG_RCU_BOOST */ | ||
276 | |||
156 | static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) | 277 | static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) |
157 | { | 278 | { |
158 | unsigned long gpnum; | 279 | unsigned long gpnum; |
159 | int level = 0; | 280 | int level = 0; |
160 | int phase; | ||
161 | struct rcu_node *rnp; | 281 | struct rcu_node *rnp; |
162 | 282 | ||
163 | gpnum = rsp->gpnum; | 283 | gpnum = rsp->gpnum; |
164 | seq_printf(m, "c=%lu g=%lu s=%d jfq=%ld j=%x " | 284 | seq_printf(m, "c=%lu g=%lu s=%d jfq=%ld j=%x " |
165 | "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld\n", | 285 | "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu\n", |
166 | rsp->completed, gpnum, rsp->signaled, | 286 | rsp->completed, gpnum, rsp->signaled, |
167 | (long)(rsp->jiffies_force_qs - jiffies), | 287 | (long)(rsp->jiffies_force_qs - jiffies), |
168 | (int)(jiffies & 0xffff), | 288 | (int)(jiffies & 0xffff), |
169 | rsp->n_force_qs, rsp->n_force_qs_ngp, | 289 | rsp->n_force_qs, rsp->n_force_qs_ngp, |
170 | rsp->n_force_qs - rsp->n_force_qs_ngp, | 290 | rsp->n_force_qs - rsp->n_force_qs_ngp, |
171 | rsp->n_force_qs_lh, rsp->orphan_qlen); | 291 | rsp->n_force_qs_lh); |
172 | for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < NUM_RCU_NODES; rnp++) { | 292 | for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < NUM_RCU_NODES; rnp++) { |
173 | if (rnp->level != level) { | 293 | if (rnp->level != level) { |
174 | seq_puts(m, "\n"); | 294 | seq_puts(m, "\n"); |
175 | level = rnp->level; | 295 | level = rnp->level; |
176 | } | 296 | } |
177 | phase = gpnum & 0x1; | 297 | seq_printf(m, "%lx/%lx %c%c>%c %d:%d ^%d ", |
178 | seq_printf(m, "%lx/%lx %c%c>%c%c %d:%d ^%d ", | ||
179 | rnp->qsmask, rnp->qsmaskinit, | 298 | rnp->qsmask, rnp->qsmaskinit, |
180 | "T."[list_empty(&rnp->blocked_tasks[phase])], | 299 | ".G"[rnp->gp_tasks != NULL], |
181 | "E."[list_empty(&rnp->blocked_tasks[phase + 2])], | 300 | ".E"[rnp->exp_tasks != NULL], |
182 | "T."[list_empty(&rnp->blocked_tasks[!phase])], | 301 | ".T"[!list_empty(&rnp->blkd_tasks)], |
183 | "E."[list_empty(&rnp->blocked_tasks[!phase + 2])], | ||
184 | rnp->grplo, rnp->grphi, rnp->grpnum); | 302 | rnp->grplo, rnp->grphi, rnp->grpnum); |
185 | } | 303 | } |
186 | seq_puts(m, "\n"); | 304 | seq_puts(m, "\n"); |
@@ -212,16 +330,35 @@ static const struct file_operations rcuhier_fops = { | |||
212 | .release = single_release, | 330 | .release = single_release, |
213 | }; | 331 | }; |
214 | 332 | ||
333 | static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp) | ||
334 | { | ||
335 | unsigned long flags; | ||
336 | unsigned long completed; | ||
337 | unsigned long gpnum; | ||
338 | unsigned long gpage; | ||
339 | unsigned long gpmax; | ||
340 | struct rcu_node *rnp = &rsp->node[0]; | ||
341 | |||
342 | raw_spin_lock_irqsave(&rnp->lock, flags); | ||
343 | completed = rsp->completed; | ||
344 | gpnum = rsp->gpnum; | ||
345 | if (rsp->completed == rsp->gpnum) | ||
346 | gpage = 0; | ||
347 | else | ||
348 | gpage = jiffies - rsp->gp_start; | ||
349 | gpmax = rsp->gp_max; | ||
350 | raw_spin_unlock_irqrestore(&rnp->lock, flags); | ||
351 | seq_printf(m, "%s: completed=%ld gpnum=%lu age=%ld max=%ld\n", | ||
352 | rsp->name, completed, gpnum, gpage, gpmax); | ||
353 | } | ||
354 | |||
215 | static int show_rcugp(struct seq_file *m, void *unused) | 355 | static int show_rcugp(struct seq_file *m, void *unused) |
216 | { | 356 | { |
217 | #ifdef CONFIG_TREE_PREEMPT_RCU | 357 | #ifdef CONFIG_TREE_PREEMPT_RCU |
218 | seq_printf(m, "rcu_preempt: completed=%ld gpnum=%lu\n", | 358 | show_one_rcugp(m, &rcu_preempt_state); |
219 | rcu_preempt_state.completed, rcu_preempt_state.gpnum); | ||
220 | #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ | 359 | #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ |
221 | seq_printf(m, "rcu_sched: completed=%ld gpnum=%lu\n", | 360 | show_one_rcugp(m, &rcu_sched_state); |
222 | rcu_sched_state.completed, rcu_sched_state.gpnum); | 361 | show_one_rcugp(m, &rcu_bh_state); |
223 | seq_printf(m, "rcu_bh: completed=%ld gpnum=%lu\n", | ||
224 | rcu_bh_state.completed, rcu_bh_state.gpnum); | ||
225 | return 0; | 362 | return 0; |
226 | } | 363 | } |
227 | 364 | ||
@@ -262,7 +399,7 @@ static void print_rcu_pendings(struct seq_file *m, struct rcu_state *rsp) | |||
262 | struct rcu_data *rdp; | 399 | struct rcu_data *rdp; |
263 | 400 | ||
264 | for_each_possible_cpu(cpu) { | 401 | for_each_possible_cpu(cpu) { |
265 | rdp = rsp->rda[cpu]; | 402 | rdp = per_cpu_ptr(rsp->rda, cpu); |
266 | if (rdp->beenonline) | 403 | if (rdp->beenonline) |
267 | print_one_rcu_pending(m, rdp); | 404 | print_one_rcu_pending(m, rdp); |
268 | } | 405 | } |
@@ -294,9 +431,32 @@ static const struct file_operations rcu_pending_fops = { | |||
294 | .release = single_release, | 431 | .release = single_release, |
295 | }; | 432 | }; |
296 | 433 | ||
434 | static int show_rcutorture(struct seq_file *m, void *unused) | ||
435 | { | ||
436 | seq_printf(m, "rcutorture test sequence: %lu %s\n", | ||
437 | rcutorture_testseq >> 1, | ||
438 | (rcutorture_testseq & 0x1) ? "(test in progress)" : ""); | ||
439 | seq_printf(m, "rcutorture update version number: %lu\n", | ||
440 | rcutorture_vernum); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | static int rcutorture_open(struct inode *inode, struct file *file) | ||
445 | { | ||
446 | return single_open(file, show_rcutorture, NULL); | ||
447 | } | ||
448 | |||
449 | static const struct file_operations rcutorture_fops = { | ||
450 | .owner = THIS_MODULE, | ||
451 | .open = rcutorture_open, | ||
452 | .read = seq_read, | ||
453 | .llseek = seq_lseek, | ||
454 | .release = single_release, | ||
455 | }; | ||
456 | |||
297 | static struct dentry *rcudir; | 457 | static struct dentry *rcudir; |
298 | 458 | ||
299 | static int __init rcuclassic_trace_init(void) | 459 | static int __init rcutree_trace_init(void) |
300 | { | 460 | { |
301 | struct dentry *retval; | 461 | struct dentry *retval; |
302 | 462 | ||
@@ -314,6 +474,9 @@ static int __init rcuclassic_trace_init(void) | |||
314 | if (!retval) | 474 | if (!retval) |
315 | goto free_out; | 475 | goto free_out; |
316 | 476 | ||
477 | if (rcu_boost_trace_create_file(rcudir)) | ||
478 | goto free_out; | ||
479 | |||
317 | retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops); | 480 | retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops); |
318 | if (!retval) | 481 | if (!retval) |
319 | goto free_out; | 482 | goto free_out; |
@@ -327,20 +490,25 @@ static int __init rcuclassic_trace_init(void) | |||
327 | NULL, &rcu_pending_fops); | 490 | NULL, &rcu_pending_fops); |
328 | if (!retval) | 491 | if (!retval) |
329 | goto free_out; | 492 | goto free_out; |
493 | |||
494 | retval = debugfs_create_file("rcutorture", 0444, rcudir, | ||
495 | NULL, &rcutorture_fops); | ||
496 | if (!retval) | ||
497 | goto free_out; | ||
330 | return 0; | 498 | return 0; |
331 | free_out: | 499 | free_out: |
332 | debugfs_remove_recursive(rcudir); | 500 | debugfs_remove_recursive(rcudir); |
333 | return 1; | 501 | return 1; |
334 | } | 502 | } |
335 | 503 | ||
336 | static void __exit rcuclassic_trace_cleanup(void) | 504 | static void __exit rcutree_trace_cleanup(void) |
337 | { | 505 | { |
338 | debugfs_remove_recursive(rcudir); | 506 | debugfs_remove_recursive(rcudir); |
339 | } | 507 | } |
340 | 508 | ||
341 | 509 | ||
342 | module_init(rcuclassic_trace_init); | 510 | module_init(rcutree_trace_init); |
343 | module_exit(rcuclassic_trace_cleanup); | 511 | module_exit(rcutree_trace_cleanup); |
344 | 512 | ||
345 | MODULE_AUTHOR("Paul E. McKenney"); | 513 | MODULE_AUTHOR("Paul E. McKenney"); |
346 | MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation"); | 514 | MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation"); |