diff options
Diffstat (limited to 'kernel/rcutree_trace.c')
-rw-r--r-- | kernel/rcutree_trace.c | 180 |
1 files changed, 165 insertions, 15 deletions
diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c index c8e97853b970..aa0fd72b4bc7 100644 --- a/kernel/rcutree_trace.c +++ b/kernel/rcutree_trace.c | |||
@@ -46,6 +46,18 @@ | |||
46 | #define RCU_TREE_NONCORE | 46 | #define RCU_TREE_NONCORE |
47 | #include "rcutree.h" | 47 | #include "rcutree.h" |
48 | 48 | ||
49 | DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status); | ||
50 | DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_cpu); | ||
51 | DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); | ||
52 | DECLARE_PER_CPU(char, rcu_cpu_has_work); | ||
53 | |||
54 | static char convert_kthread_status(unsigned int kthread_status) | ||
55 | { | ||
56 | if (kthread_status > RCU_KTHREAD_MAX) | ||
57 | return '?'; | ||
58 | return "SRWOY"[kthread_status]; | ||
59 | } | ||
60 | |||
49 | static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) | 61 | static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) |
50 | { | 62 | { |
51 | if (!rdp->beenonline) | 63 | if (!rdp->beenonline) |
@@ -64,7 +76,21 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) | |||
64 | rdp->dynticks_fqs); | 76 | rdp->dynticks_fqs); |
65 | #endif /* #ifdef CONFIG_NO_HZ */ | 77 | #endif /* #ifdef CONFIG_NO_HZ */ |
66 | seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); | 78 | seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); |
67 | seq_printf(m, " ql=%ld b=%ld", rdp->qlen, rdp->blimit); | 79 | seq_printf(m, " ql=%ld qs=%c%c%c%c kt=%d/%c/%d ktl=%x b=%ld", |
80 | rdp->qlen, | ||
81 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != | ||
82 | rdp->nxttail[RCU_NEXT_TAIL]], | ||
83 | ".R"[rdp->nxttail[RCU_WAIT_TAIL] != | ||
84 | rdp->nxttail[RCU_NEXT_READY_TAIL]], | ||
85 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != | ||
86 | rdp->nxttail[RCU_WAIT_TAIL]], | ||
87 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]], | ||
88 | per_cpu(rcu_cpu_has_work, rdp->cpu), | ||
89 | convert_kthread_status(per_cpu(rcu_cpu_kthread_status, | ||
90 | rdp->cpu)), | ||
91 | per_cpu(rcu_cpu_kthread_cpu, rdp->cpu), | ||
92 | per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff, | ||
93 | rdp->blimit); | ||
68 | seq_printf(m, " ci=%lu co=%lu ca=%lu\n", | 94 | seq_printf(m, " ci=%lu co=%lu ca=%lu\n", |
69 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); | 95 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); |
70 | } | 96 | } |
@@ -121,7 +147,18 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp) | |||
121 | rdp->dynticks_fqs); | 147 | rdp->dynticks_fqs); |
122 | #endif /* #ifdef CONFIG_NO_HZ */ | 148 | #endif /* #ifdef CONFIG_NO_HZ */ |
123 | seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); | 149 | seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); |
124 | seq_printf(m, ",%ld,%ld", rdp->qlen, rdp->blimit); | 150 | seq_printf(m, ",%ld,\"%c%c%c%c\",%d,\"%c\",%ld", rdp->qlen, |
151 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != | ||
152 | rdp->nxttail[RCU_NEXT_TAIL]], | ||
153 | ".R"[rdp->nxttail[RCU_WAIT_TAIL] != | ||
154 | rdp->nxttail[RCU_NEXT_READY_TAIL]], | ||
155 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != | ||
156 | rdp->nxttail[RCU_WAIT_TAIL]], | ||
157 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]], | ||
158 | per_cpu(rcu_cpu_has_work, rdp->cpu), | ||
159 | convert_kthread_status(per_cpu(rcu_cpu_kthread_status, | ||
160 | rdp->cpu)), | ||
161 | rdp->blimit); | ||
125 | seq_printf(m, ",%lu,%lu,%lu\n", | 162 | seq_printf(m, ",%lu,%lu,%lu\n", |
126 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); | 163 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); |
127 | } | 164 | } |
@@ -157,11 +194,76 @@ static const struct file_operations rcudata_csv_fops = { | |||
157 | .release = single_release, | 194 | .release = single_release, |
158 | }; | 195 | }; |
159 | 196 | ||
197 | #ifdef CONFIG_RCU_BOOST | ||
198 | |||
199 | static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) | ||
200 | { | ||
201 | seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu " | ||
202 | "j=%04x bt=%04x\n", | ||
203 | rnp->grplo, rnp->grphi, | ||
204 | "T."[list_empty(&rnp->blkd_tasks)], | ||
205 | "N."[!rnp->gp_tasks], | ||
206 | "E."[!rnp->exp_tasks], | ||
207 | "B."[!rnp->boost_tasks], | ||
208 | convert_kthread_status(rnp->boost_kthread_status), | ||
209 | rnp->n_tasks_boosted, rnp->n_exp_boosts, | ||
210 | rnp->n_normal_boosts, | ||
211 | (int)(jiffies & 0xffff), | ||
212 | (int)(rnp->boost_time & 0xffff)); | ||
213 | seq_printf(m, "%s: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu\n", | ||
214 | " balk", | ||
215 | rnp->n_balk_blkd_tasks, | ||
216 | rnp->n_balk_exp_gp_tasks, | ||
217 | rnp->n_balk_boost_tasks, | ||
218 | rnp->n_balk_notblocked, | ||
219 | rnp->n_balk_notyet, | ||
220 | rnp->n_balk_nos); | ||
221 | } | ||
222 | |||
223 | static int show_rcu_node_boost(struct seq_file *m, void *unused) | ||
224 | { | ||
225 | struct rcu_node *rnp; | ||
226 | |||
227 | rcu_for_each_leaf_node(&rcu_preempt_state, rnp) | ||
228 | print_one_rcu_node_boost(m, rnp); | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int rcu_node_boost_open(struct inode *inode, struct file *file) | ||
233 | { | ||
234 | return single_open(file, show_rcu_node_boost, NULL); | ||
235 | } | ||
236 | |||
237 | static const struct file_operations rcu_node_boost_fops = { | ||
238 | .owner = THIS_MODULE, | ||
239 | .open = rcu_node_boost_open, | ||
240 | .read = seq_read, | ||
241 | .llseek = seq_lseek, | ||
242 | .release = single_release, | ||
243 | }; | ||
244 | |||
245 | /* | ||
246 | * Create the rcuboost debugfs entry. Standard error return. | ||
247 | */ | ||
248 | static int rcu_boost_trace_create_file(struct dentry *rcudir) | ||
249 | { | ||
250 | return !debugfs_create_file("rcuboost", 0444, rcudir, NULL, | ||
251 | &rcu_node_boost_fops); | ||
252 | } | ||
253 | |||
254 | #else /* #ifdef CONFIG_RCU_BOOST */ | ||
255 | |||
256 | static int rcu_boost_trace_create_file(struct dentry *rcudir) | ||
257 | { | ||
258 | return 0; /* There cannot be an error if we didn't create it! */ | ||
259 | } | ||
260 | |||
261 | #endif /* #else #ifdef CONFIG_RCU_BOOST */ | ||
262 | |||
160 | static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) | 263 | static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) |
161 | { | 264 | { |
162 | unsigned long gpnum; | 265 | unsigned long gpnum; |
163 | int level = 0; | 266 | int level = 0; |
164 | int phase; | ||
165 | struct rcu_node *rnp; | 267 | struct rcu_node *rnp; |
166 | 268 | ||
167 | gpnum = rsp->gpnum; | 269 | gpnum = rsp->gpnum; |
@@ -178,13 +280,11 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) | |||
178 | seq_puts(m, "\n"); | 280 | seq_puts(m, "\n"); |
179 | level = rnp->level; | 281 | level = rnp->level; |
180 | } | 282 | } |
181 | phase = gpnum & 0x1; | 283 | seq_printf(m, "%lx/%lx %c%c>%c %d:%d ^%d ", |
182 | seq_printf(m, "%lx/%lx %c%c>%c%c %d:%d ^%d ", | ||
183 | rnp->qsmask, rnp->qsmaskinit, | 284 | rnp->qsmask, rnp->qsmaskinit, |
184 | "T."[list_empty(&rnp->blocked_tasks[phase])], | 285 | ".G"[rnp->gp_tasks != NULL], |
185 | "E."[list_empty(&rnp->blocked_tasks[phase + 2])], | 286 | ".E"[rnp->exp_tasks != NULL], |
186 | "T."[list_empty(&rnp->blocked_tasks[!phase])], | 287 | ".T"[!list_empty(&rnp->blkd_tasks)], |
187 | "E."[list_empty(&rnp->blocked_tasks[!phase + 2])], | ||
188 | rnp->grplo, rnp->grphi, rnp->grpnum); | 288 | rnp->grplo, rnp->grphi, rnp->grpnum); |
189 | } | 289 | } |
190 | seq_puts(m, "\n"); | 290 | seq_puts(m, "\n"); |
@@ -216,16 +316,35 @@ static const struct file_operations rcuhier_fops = { | |||
216 | .release = single_release, | 316 | .release = single_release, |
217 | }; | 317 | }; |
218 | 318 | ||
319 | static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp) | ||
320 | { | ||
321 | unsigned long flags; | ||
322 | unsigned long completed; | ||
323 | unsigned long gpnum; | ||
324 | unsigned long gpage; | ||
325 | unsigned long gpmax; | ||
326 | struct rcu_node *rnp = &rsp->node[0]; | ||
327 | |||
328 | raw_spin_lock_irqsave(&rnp->lock, flags); | ||
329 | completed = rsp->completed; | ||
330 | gpnum = rsp->gpnum; | ||
331 | if (rsp->completed == rsp->gpnum) | ||
332 | gpage = 0; | ||
333 | else | ||
334 | gpage = jiffies - rsp->gp_start; | ||
335 | gpmax = rsp->gp_max; | ||
336 | raw_spin_unlock_irqrestore(&rnp->lock, flags); | ||
337 | seq_printf(m, "%s: completed=%ld gpnum=%lu age=%ld max=%ld\n", | ||
338 | rsp->name, completed, gpnum, gpage, gpmax); | ||
339 | } | ||
340 | |||
219 | static int show_rcugp(struct seq_file *m, void *unused) | 341 | static int show_rcugp(struct seq_file *m, void *unused) |
220 | { | 342 | { |
221 | #ifdef CONFIG_TREE_PREEMPT_RCU | 343 | #ifdef CONFIG_TREE_PREEMPT_RCU |
222 | seq_printf(m, "rcu_preempt: completed=%ld gpnum=%lu\n", | 344 | show_one_rcugp(m, &rcu_preempt_state); |
223 | rcu_preempt_state.completed, rcu_preempt_state.gpnum); | ||
224 | #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ | 345 | #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ |
225 | seq_printf(m, "rcu_sched: completed=%ld gpnum=%lu\n", | 346 | show_one_rcugp(m, &rcu_sched_state); |
226 | rcu_sched_state.completed, rcu_sched_state.gpnum); | 347 | show_one_rcugp(m, &rcu_bh_state); |
227 | seq_printf(m, "rcu_bh: completed=%ld gpnum=%lu\n", | ||
228 | rcu_bh_state.completed, rcu_bh_state.gpnum); | ||
229 | return 0; | 348 | return 0; |
230 | } | 349 | } |
231 | 350 | ||
@@ -298,6 +417,29 @@ static const struct file_operations rcu_pending_fops = { | |||
298 | .release = single_release, | 417 | .release = single_release, |
299 | }; | 418 | }; |
300 | 419 | ||
420 | static int show_rcutorture(struct seq_file *m, void *unused) | ||
421 | { | ||
422 | seq_printf(m, "rcutorture test sequence: %lu %s\n", | ||
423 | rcutorture_testseq >> 1, | ||
424 | (rcutorture_testseq & 0x1) ? "(test in progress)" : ""); | ||
425 | seq_printf(m, "rcutorture update version number: %lu\n", | ||
426 | rcutorture_vernum); | ||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int rcutorture_open(struct inode *inode, struct file *file) | ||
431 | { | ||
432 | return single_open(file, show_rcutorture, NULL); | ||
433 | } | ||
434 | |||
435 | static const struct file_operations rcutorture_fops = { | ||
436 | .owner = THIS_MODULE, | ||
437 | .open = rcutorture_open, | ||
438 | .read = seq_read, | ||
439 | .llseek = seq_lseek, | ||
440 | .release = single_release, | ||
441 | }; | ||
442 | |||
301 | static struct dentry *rcudir; | 443 | static struct dentry *rcudir; |
302 | 444 | ||
303 | static int __init rcutree_trace_init(void) | 445 | static int __init rcutree_trace_init(void) |
@@ -318,6 +460,9 @@ static int __init rcutree_trace_init(void) | |||
318 | if (!retval) | 460 | if (!retval) |
319 | goto free_out; | 461 | goto free_out; |
320 | 462 | ||
463 | if (rcu_boost_trace_create_file(rcudir)) | ||
464 | goto free_out; | ||
465 | |||
321 | retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops); | 466 | retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops); |
322 | if (!retval) | 467 | if (!retval) |
323 | goto free_out; | 468 | goto free_out; |
@@ -331,6 +476,11 @@ static int __init rcutree_trace_init(void) | |||
331 | NULL, &rcu_pending_fops); | 476 | NULL, &rcu_pending_fops); |
332 | if (!retval) | 477 | if (!retval) |
333 | goto free_out; | 478 | goto free_out; |
479 | |||
480 | retval = debugfs_create_file("rcutorture", 0444, rcudir, | ||
481 | NULL, &rcutorture_fops); | ||
482 | if (!retval) | ||
483 | goto free_out; | ||
334 | return 0; | 484 | return 0; |
335 | free_out: | 485 | free_out: |
336 | debugfs_remove_recursive(rcudir); | 486 | debugfs_remove_recursive(rcudir); |