Blame view
kernel/rcu/tree_trace.c
12.7 KB
64db4cfff
|
1 |
/* |
47dbc9066
|
2 |
* Read-Copy Update tracing for hierarchical implementation. |
64db4cfff
|
3 4 5 6 7 8 9 10 11 12 13 14 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License |
87de1cfdc
|
15 16 |
* along with this program; if not, you can access it online at * http://www.gnu.org/licenses/gpl-2.0.html. |
64db4cfff
|
17 18 |
* * Copyright IBM Corporation, 2008 |
47dbc9066
|
19 |
* Author: Paul E. McKenney |
64db4cfff
|
20 21 22 23 |
* * Papers: http://www.rdrop.com/users/paulmck/RCU * * For detailed explanation of Read-Copy Update mechanism see - |
a71fca58b
|
24 |
* Documentation/RCU |
64db4cfff
|
25 26 27 28 29 30 31 32 33 34 |
* */ #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/spinlock.h> #include <linux/smp.h> #include <linux/rcupdate.h> #include <linux/interrupt.h> #include <linux/sched.h> |
60063497a
|
35 |
#include <linux/atomic.h> |
64db4cfff
|
36 |
#include <linux/bitops.h> |
64db4cfff
|
37 |
#include <linux/completion.h> |
64db4cfff
|
38 39 40 41 42 43 |
#include <linux/percpu.h> #include <linux/notifier.h> #include <linux/cpu.h> #include <linux/mutex.h> #include <linux/debugfs.h> #include <linux/seq_file.h> |
9f77da9f4
|
44 |
#define RCU_TREE_NONCORE |
4102adab9
|
45 |
#include "tree.h" |
6258c4fb5
|
46 |
|
5cd37193c
|
47 |
DECLARE_PER_CPU_SHARED_ALIGNED(unsigned long, rcu_qs_ctr); |
374b928ee
|
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
static int r_open(struct inode *inode, struct file *file, const struct seq_operations *op) { int ret = seq_open(file, op); if (!ret) { struct seq_file *m = (struct seq_file *)file->private_data; m->private = inode->i_private; } return ret; } static void *r_start(struct seq_file *m, loff_t *pos) { struct rcu_state *rsp = (struct rcu_state *)m->private; *pos = cpumask_next(*pos - 1, cpu_possible_mask); if ((*pos) < nr_cpu_ids) return per_cpu_ptr(rsp->rda, *pos); return NULL; } static void *r_next(struct seq_file *m, void *v, loff_t *pos) { (*pos)++; return r_start(m, pos); } static void r_stop(struct seq_file *m, void *v) { } |
6ee0886ff
|
77 |
static int show_rcubarrier(struct seq_file *m, void *v) |
c25e557f5
|
78 79 |
{ struct rcu_state *rsp = (struct rcu_state *)m->private; |
4f525a528
|
80 81 |
seq_printf(m, "bcc: %d bseq: %lu ", |
c25e557f5
|
82 |
atomic_read(&rsp->barrier_cpu_count), |
4f525a528
|
83 |
rsp->barrier_sequence); |
c25e557f5
|
84 85 |
return 0; } |
6ee0886ff
|
86 |
static int rcubarrier_open(struct inode *inode, struct file *file) |
c25e557f5
|
87 |
{ |
6ee0886ff
|
88 |
return single_open(file, show_rcubarrier, inode->i_private); |
c25e557f5
|
89 |
} |
6ee0886ff
|
90 |
static const struct file_operations rcubarrier_fops = { |
c25e557f5
|
91 |
.owner = THIS_MODULE, |
6ee0886ff
|
92 |
.open = rcubarrier_open, |
c25e557f5
|
93 94 |
.read = seq_read, .llseek = no_llseek, |
7ee2b9e56
|
95 |
.release = single_release, |
c25e557f5
|
96 |
}; |
a46e0899e
|
97 |
#ifdef CONFIG_RCU_BOOST |
d71df90ea
|
98 99 100 101 |
static char convert_kthread_status(unsigned int kthread_status) { if (kthread_status > RCU_KTHREAD_MAX) return '?'; |
15ba0ba86
|
102 |
return "SRWOY"[kthread_status]; |
d71df90ea
|
103 |
} |
a46e0899e
|
104 |
#endif /* #ifdef CONFIG_RCU_BOOST */ |
64db4cfff
|
105 106 |
static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) { |
3fbfbf7a3
|
107 |
long ql, qll; |
64db4cfff
|
108 109 |
if (!rdp->beenonline) return; |
0d43eb34f
|
110 |
seq_printf(m, "%3d%cc=%ld g=%ld cnq=%d/%d:%d", |
64db4cfff
|
111 112 |
rdp->cpu, cpu_is_offline(rdp->cpu) ? '!' : ' ', |
42c3533ee
|
113 |
ulong2long(rdp->completed), ulong2long(rdp->gpnum), |
5b74c4589
|
114 |
rdp->cpu_no_qs.b.norm, |
5cd37193c
|
115 |
rdp->rcu_qs_ctr_snap == per_cpu(rcu_qs_ctr, rdp->cpu), |
97c668b8e
|
116 |
rdp->core_needs_qs); |
9b2e4f188
|
117 |
seq_printf(m, " dt=%d/%llx/%d df=%lu", |
23b5c8fa0
|
118 |
atomic_read(&rdp->dynticks->dynticks), |
64db4cfff
|
119 |
rdp->dynticks->dynticks_nesting, |
23b5c8fa0
|
120 |
rdp->dynticks->dynticks_nmi_nesting, |
64db4cfff
|
121 |
rdp->dynticks_fqs); |
2036d94a7
|
122 |
seq_printf(m, " of=%lu", rdp->offline_fqs); |
3fbfbf7a3
|
123 124 125 |
rcu_nocb_q_lengths(rdp, &ql, &qll); qll += rdp->qlen_lazy; ql += rdp->qlen; |
486e25934
|
126 |
seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c", |
3fbfbf7a3
|
127 |
qll, ql, |
0ac3d136b
|
128 129 130 131 132 133 |
".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != rdp->nxttail[RCU_NEXT_TAIL]], ".R"[rdp->nxttail[RCU_WAIT_TAIL] != rdp->nxttail[RCU_NEXT_READY_TAIL]], ".W"[rdp->nxttail[RCU_DONE_TAIL] != rdp->nxttail[RCU_WAIT_TAIL]], |
a46e0899e
|
134 135 |
".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); #ifdef CONFIG_RCU_BOOST |
62ab70724
|
136 |
seq_printf(m, " kt=%d/%c ktl=%x", |
d71df90ea
|
137 138 139 |
per_cpu(rcu_cpu_has_work, rdp->cpu), convert_kthread_status(per_cpu(rcu_cpu_kthread_status, rdp->cpu)), |
a46e0899e
|
140 141 142 |
per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff); #endif /* #ifdef CONFIG_RCU_BOOST */ seq_printf(m, " b=%ld", rdp->blimit); |
c635a4e1c
|
143 144 145 146 |
seq_printf(m, " ci=%lu nci=%lu co=%lu ca=%lu ", rdp->n_cbs_invoked, rdp->n_nocbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); |
64db4cfff
|
147 |
} |
c011c41f1
|
148 |
static int show_rcudata(struct seq_file *m, void *v) |
878eda72e
|
149 150 151 152 |
{ print_one_rcu_data(m, (struct rcu_data *)v); return 0; } |
c011c41f1
|
153 |
static const struct seq_operations rcudate_op = { |
878eda72e
|
154 155 156 |
.start = r_start, .next = r_next, .stop = r_stop, |
c011c41f1
|
157 |
.show = show_rcudata, |
878eda72e
|
158 |
}; |
c011c41f1
|
159 |
static int rcudata_open(struct inode *inode, struct file *file) |
878eda72e
|
160 |
{ |
c011c41f1
|
161 |
return r_open(inode, file, &rcudate_op); |
878eda72e
|
162 |
} |
c011c41f1
|
163 |
static const struct file_operations rcudata_fops = { |
878eda72e
|
164 |
.owner = THIS_MODULE, |
c011c41f1
|
165 |
.open = rcudata_open, |
878eda72e
|
166 167 168 169 |
.read = seq_read, .llseek = no_llseek, .release = seq_release, }; |
7bd8f2a74
|
170 171 |
static int show_rcuexp(struct seq_file *m, void *v) { |
df5bd5144
|
172 |
int cpu; |
7bd8f2a74
|
173 |
struct rcu_state *rsp = (struct rcu_state *)m->private; |
df5bd5144
|
174 |
struct rcu_data *rdp; |
e2fd9d358
|
175 |
unsigned long s1 = 0, s2 = 0, s3 = 0; |
df5bd5144
|
176 177 178 |
for_each_possible_cpu(cpu) { rdp = per_cpu_ptr(rsp->rda, cpu); |
d40a4f09a
|
179 180 181 |
s1 += atomic_long_read(&rdp->exp_workdone1); s2 += atomic_long_read(&rdp->exp_workdone2); s3 += atomic_long_read(&rdp->exp_workdone3); |
df5bd5144
|
182 |
} |
e2fd9d358
|
183 184 185 |
seq_printf(m, "s=%lu wd1=%lu wd2=%lu wd3=%lu n=%lu enq=%d sc=%lu ", rsp->expedited_sequence, s1, s2, s3, |
7bd8f2a74
|
186 |
atomic_long_read(&rsp->expedited_normal), |
3a6d7c64d
|
187 |
atomic_read(&rsp->expedited_need_qs), |
d6ada2cf2
|
188 |
rsp->expedited_sequence / 2); |
7bd8f2a74
|
189 190 191 192 193 194 195 196 197 198 199 200 201 |
return 0; } static int rcuexp_open(struct inode *inode, struct file *file) { return single_open(file, show_rcuexp, inode->i_private); } static const struct file_operations rcuexp_fops = { .owner = THIS_MODULE, .open = rcuexp_open, .read = seq_read, .llseek = no_llseek, |
7ee2b9e56
|
202 |
.release = single_release, |
7bd8f2a74
|
203 |
}; |
0ea1f2ebe
|
204 205 206 207 |
#ifdef CONFIG_RCU_BOOST static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) { |
5cf05ad75
|
208 |
seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu ", |
0ea1f2ebe
|
209 210 211 212 213 |
rnp->grplo, rnp->grphi, "T."[list_empty(&rnp->blkd_tasks)], "N."[!rnp->gp_tasks], "E."[!rnp->exp_tasks], "B."[!rnp->boost_tasks], |
d71df90ea
|
214 |
convert_kthread_status(rnp->boost_kthread_status), |
0ea1f2ebe
|
215 |
rnp->n_tasks_boosted, rnp->n_exp_boosts, |
5cf05ad75
|
216 217 218 |
rnp->n_normal_boosts); seq_printf(m, "j=%04x bt=%04x ", |
0ea1f2ebe
|
219 220 |
(int)(jiffies & 0xffff), (int)(rnp->boost_time & 0xffff)); |
5cf05ad75
|
221 222 |
seq_printf(m, " balk: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu ", |
0ea1f2ebe
|
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
rnp->n_balk_blkd_tasks, rnp->n_balk_exp_gp_tasks, rnp->n_balk_boost_tasks, rnp->n_balk_notblocked, rnp->n_balk_notyet, rnp->n_balk_nos); } static int show_rcu_node_boost(struct seq_file *m, void *unused) { struct rcu_node *rnp; rcu_for_each_leaf_node(&rcu_preempt_state, rnp) print_one_rcu_node_boost(m, rnp); return 0; } static int rcu_node_boost_open(struct inode *inode, struct file *file) { return single_open(file, show_rcu_node_boost, NULL); } static const struct file_operations rcu_node_boost_fops = { .owner = THIS_MODULE, .open = rcu_node_boost_open, .read = seq_read, |
29c67764f
|
249 |
.llseek = no_llseek, |
0ea1f2ebe
|
250 251 |
.release = single_release, }; |
6ee0886ff
|
252 |
#endif /* #ifdef CONFIG_RCU_BOOST */ |
0ea1f2ebe
|
253 |
|
64db4cfff
|
254 255 |
static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) { |
20133cfce
|
256 |
unsigned long gpnum; |
64db4cfff
|
257 258 |
int level = 0; struct rcu_node *rnp; |
3397e040d
|
259 |
gpnum = rsp->gpnum; |
6ee0886ff
|
260 261 |
seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x ", ulong2long(rsp->completed), ulong2long(gpnum), |
77f81fe08
|
262 |
rsp->gp_state, |
64db4cfff
|
263 |
(long)(rsp->jiffies_force_qs - jiffies), |
5cf05ad75
|
264 265 266 |
(int)(jiffies & 0xffff)); seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld ", |
64db4cfff
|
267 268 |
rsp->n_force_qs, rsp->n_force_qs_ngp, rsp->n_force_qs - rsp->n_force_qs_ngp, |
7d0ae8086
|
269 |
READ_ONCE(rsp->n_force_qs_lh), rsp->qlen_lazy, rsp->qlen); |
f885b7f2b
|
270 |
for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < rcu_num_nodes; rnp++) { |
64db4cfff
|
271 272 273 274 275 |
if (rnp->level != level) { seq_puts(m, " "); level = rnp->level; } |
0aa04b055
|
276 277 |
seq_printf(m, "%lx/%lx->%lx %c%c>%c %d:%d ^%d ", rnp->qsmask, rnp->qsmaskinit, rnp->qsmaskinitnext, |
12f5f524c
|
278 279 280 |
".G"[rnp->gp_tasks != NULL], ".E"[rnp->exp_tasks != NULL], ".T"[!list_empty(&rnp->blkd_tasks)], |
64db4cfff
|
281 282 283 284 285 |
rnp->grplo, rnp->grphi, rnp->grpnum); } seq_puts(m, " "); } |
6ee0886ff
|
286 |
static int show_rcuhier(struct seq_file *m, void *v) |
64db4cfff
|
287 |
{ |
6ee0886ff
|
288 289 |
struct rcu_state *rsp = (struct rcu_state *)m->private; print_one_rcu_state(m, rsp); |
64db4cfff
|
290 291 292 293 294 |
return 0; } static int rcuhier_open(struct inode *inode, struct file *file) { |
6ee0886ff
|
295 |
return single_open(file, show_rcuhier, inode->i_private); |
64db4cfff
|
296 |
} |
9b2619aff
|
297 |
static const struct file_operations rcuhier_fops = { |
64db4cfff
|
298 299 300 |
.owner = THIS_MODULE, .open = rcuhier_open, .read = seq_read, |
a608d84bd
|
301 |
.llseek = no_llseek, |
7ee2b9e56
|
302 |
.release = single_release, |
a608d84bd
|
303 |
}; |
15ba0ba86
|
304 305 306 307 308 309 310 311 |
static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp) { unsigned long flags; unsigned long completed; unsigned long gpnum; unsigned long gpage; unsigned long gpmax; struct rcu_node *rnp = &rsp->node[0]; |
6cf100812
|
312 |
raw_spin_lock_irqsave_rcu_node(rnp, flags); |
7d0ae8086
|
313 314 |
completed = READ_ONCE(rsp->completed); gpnum = READ_ONCE(rsp->gpnum); |
42c3533ee
|
315 |
if (completed == gpnum) |
15ba0ba86
|
316 317 318 319 320 |
gpage = 0; else gpage = jiffies - rsp->gp_start; gpmax = rsp->gp_max; raw_spin_unlock_irqrestore(&rnp->lock, flags); |
6ee0886ff
|
321 322 323 |
seq_printf(m, "completed=%ld gpnum=%ld age=%ld max=%ld ", ulong2long(completed), ulong2long(gpnum), gpage, gpmax); |
15ba0ba86
|
324 |
} |
6ee0886ff
|
325 |
static int show_rcugp(struct seq_file *m, void *v) |
64db4cfff
|
326 |
{ |
6ee0886ff
|
327 328 |
struct rcu_state *rsp = (struct rcu_state *)m->private; show_one_rcugp(m, rsp); |
64db4cfff
|
329 330 331 332 333 |
return 0; } static int rcugp_open(struct inode *inode, struct file *file) { |
6ee0886ff
|
334 |
return single_open(file, show_rcugp, inode->i_private); |
64db4cfff
|
335 |
} |
9b2619aff
|
336 |
static const struct file_operations rcugp_fops = { |
64db4cfff
|
337 338 339 |
.owner = THIS_MODULE, .open = rcugp_open, .read = seq_read, |
66b38bc52
|
340 |
.llseek = no_llseek, |
7ee2b9e56
|
341 |
.release = single_release, |
66b38bc52
|
342 |
}; |
7ba5c840e
|
343 344 |
static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp) { |
51d0f16d4
|
345 346 |
if (!rdp->beenonline) return; |
5cf05ad75
|
347 |
seq_printf(m, "%3d%cnp=%ld ", |
7ba5c840e
|
348 349 |
rdp->cpu, cpu_is_offline(rdp->cpu) ? '!' : ' ', |
5cf05ad75
|
350 351 |
rdp->n_rcu_pending); seq_printf(m, "qsp=%ld rpq=%ld cbr=%ld cng=%ld ", |
97c668b8e
|
352 |
rdp->n_rp_core_needs_qs, |
d21670aca
|
353 |
rdp->n_rp_report_qs, |
7ba5c840e
|
354 |
rdp->n_rp_cb_ready, |
5cf05ad75
|
355 |
rdp->n_rp_cpu_needs_gp); |
96d3fd0d3
|
356 357 |
seq_printf(m, "gpc=%ld gps=%ld nn=%ld ndw%ld ", |
7ba5c840e
|
358 359 |
rdp->n_rp_gp_completed, rdp->n_rp_gp_started, |
96d3fd0d3
|
360 |
rdp->n_rp_nocb_defer_wakeup, |
7ba5c840e
|
361 362 |
rdp->n_rp_need_nothing); } |
c011c41f1
|
363 |
static int show_rcu_pending(struct seq_file *m, void *v) |
51d0f16d4
|
364 365 366 367 |
{ print_one_rcu_pending(m, (struct rcu_data *)v); return 0; } |
c011c41f1
|
368 |
static const struct seq_operations rcu_pending_op = { |
51d0f16d4
|
369 370 371 |
.start = r_start, .next = r_next, .stop = r_stop, |
c011c41f1
|
372 |
.show = show_rcu_pending, |
51d0f16d4
|
373 |
}; |
c011c41f1
|
374 |
static int rcu_pending_open(struct inode *inode, struct file *file) |
51d0f16d4
|
375 |
{ |
c011c41f1
|
376 |
return r_open(inode, file, &rcu_pending_op); |
51d0f16d4
|
377 |
} |
c011c41f1
|
378 |
static const struct file_operations rcu_pending_fops = { |
51d0f16d4
|
379 |
.owner = THIS_MODULE, |
c011c41f1
|
380 |
.open = rcu_pending_open, |
51d0f16d4
|
381 382 383 384 |
.read = seq_read, .llseek = no_llseek, .release = seq_release, }; |
4a2986568
|
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
static int show_rcutorture(struct seq_file *m, void *unused) { seq_printf(m, "rcutorture test sequence: %lu %s ", rcutorture_testseq >> 1, (rcutorture_testseq & 0x1) ? "(test in progress)" : ""); seq_printf(m, "rcutorture update version number: %lu ", rcutorture_vernum); return 0; } static int rcutorture_open(struct inode *inode, struct file *file) { return single_open(file, show_rcutorture, NULL); } static const struct file_operations rcutorture_fops = { .owner = THIS_MODULE, .open = rcutorture_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; |
7ba5c840e
|
409 |
static struct dentry *rcudir; |
7ba5c840e
|
410 |
|
deb7a4181
|
411 |
static int __init rcutree_trace_init(void) |
64db4cfff
|
412 |
{ |
573bcd40d
|
413 |
struct rcu_state *rsp; |
22f00b69f
|
414 |
struct dentry *retval; |
573bcd40d
|
415 |
struct dentry *rspdir; |
22f00b69f
|
416 |
|
64db4cfff
|
417 418 |
rcudir = debugfs_create_dir("rcu", NULL); if (!rcudir) |
22f00b69f
|
419 |
goto free_out; |
64db4cfff
|
420 |
|
573bcd40d
|
421 422 423 424 |
for_each_rcu_flavor(rsp) { rspdir = debugfs_create_dir(rsp->name, rcudir); if (!rspdir) goto free_out; |
878eda72e
|
425 |
|
6ee0886ff
|
426 427 428 429 |
retval = debugfs_create_file("rcudata", 0444, rspdir, rsp, &rcudata_fops); if (!retval) goto free_out; |
51d0f16d4
|
430 |
|
7bd8f2a74
|
431 432 433 434 |
retval = debugfs_create_file("rcuexp", 0444, rspdir, rsp, &rcuexp_fops); if (!retval) goto free_out; |
6ee0886ff
|
435 436 437 438 |
retval = debugfs_create_file("rcu_pending", 0444, rspdir, rsp, &rcu_pending_fops); if (!retval) goto free_out; |
c25e557f5
|
439 |
|
6ee0886ff
|
440 441 442 443 |
retval = debugfs_create_file("rcubarrier", 0444, rspdir, rsp, &rcubarrier_fops); if (!retval) goto free_out; |
29c67764f
|
444 445 |
#ifdef CONFIG_RCU_BOOST |
6ee0886ff
|
446 447 448 |
if (rsp == &rcu_preempt_state) { retval = debugfs_create_file("rcuboost", 0444, rspdir, NULL, &rcu_node_boost_fops); |
66b38bc52
|
449 450 |
if (!retval) goto free_out; |
6ee0886ff
|
451 452 |
} #endif |
66b38bc52
|
453 |
|
6ee0886ff
|
454 455 456 457 |
retval = debugfs_create_file("rcugp", 0444, rspdir, rsp, &rcugp_fops); if (!retval) goto free_out; |
64db4cfff
|
458 |
|
6ee0886ff
|
459 460 461 462 463 |
retval = debugfs_create_file("rcuhier", 0444, rspdir, rsp, &rcuhier_fops); if (!retval) goto free_out; } |
7ba5c840e
|
464 |
|
4a2986568
|
465 466 467 468 |
retval = debugfs_create_file("rcutorture", 0444, rcudir, NULL, &rcutorture_fops); if (!retval) goto free_out; |
64db4cfff
|
469 470 |
return 0; free_out: |
22f00b69f
|
471 |
debugfs_remove_recursive(rcudir); |
64db4cfff
|
472 473 |
return 1; } |
47dbc9066
|
474 |
device_initcall(rcutree_trace_init); |