Blame view
kernel/rcutree_trace.c
12.9 KB
64db4cfff
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * Read-Copy Update tracing for classic implementation * * 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 * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Copyright IBM Corporation, 2008 * * Papers: http://www.rdrop.com/users/paulmck/RCU * * For detailed explanation of Read-Copy Update mechanism see - |
a71fca58b
|
23 |
* Documentation/RCU |
64db4cfff
|
24 25 26 27 28 29 30 31 32 33 |
* */ #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
|
34 |
#include <linux/atomic.h> |
64db4cfff
|
35 36 37 38 39 40 41 42 43 44 |
#include <linux/bitops.h> #include <linux/module.h> #include <linux/completion.h> #include <linux/moduleparam.h> #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
|
45 |
#define RCU_TREE_NONCORE |
6258c4fb5
|
46 |
#include "rcutree.h" |
374b928ee
|
47 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 |
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
|
76 |
static int show_rcubarrier(struct seq_file *m, void *v) |
c25e557f5
|
77 78 79 80 81 82 83 84 |
{ struct rcu_state *rsp = (struct rcu_state *)m->private; seq_printf(m, "bcc: %d nbd: %lu ", atomic_read(&rsp->barrier_cpu_count), rsp->n_barrier_done); return 0; } |
6ee0886ff
|
85 |
static int rcubarrier_open(struct inode *inode, struct file *file) |
c25e557f5
|
86 |
{ |
6ee0886ff
|
87 |
return single_open(file, show_rcubarrier, inode->i_private); |
c25e557f5
|
88 |
} |
6ee0886ff
|
89 |
static const struct file_operations rcubarrier_fops = { |
c25e557f5
|
90 |
.owner = THIS_MODULE, |
6ee0886ff
|
91 |
.open = rcubarrier_open, |
c25e557f5
|
92 93 |
.read = seq_read, .llseek = no_llseek, |
7ee2b9e56
|
94 |
.release = single_release, |
c25e557f5
|
95 |
}; |
a46e0899e
|
96 |
#ifdef CONFIG_RCU_BOOST |
d71df90ea
|
97 98 99 100 |
static char convert_kthread_status(unsigned int kthread_status) { if (kthread_status > RCU_KTHREAD_MAX) return '?'; |
15ba0ba86
|
101 |
return "SRWOY"[kthread_status]; |
d71df90ea
|
102 |
} |
a46e0899e
|
103 |
#endif /* #ifdef CONFIG_RCU_BOOST */ |
64db4cfff
|
104 105 |
static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) { |
3fbfbf7a3
|
106 |
long ql, qll; |
64db4cfff
|
107 108 |
if (!rdp->beenonline) return; |
42c3533ee
|
109 |
seq_printf(m, "%3d%cc=%ld g=%ld pq=%d qp=%d", |
64db4cfff
|
110 111 |
rdp->cpu, cpu_is_offline(rdp->cpu) ? '!' : ' ', |
42c3533ee
|
112 |
ulong2long(rdp->completed), ulong2long(rdp->gpnum), |
d7d6a11e8
|
113 |
rdp->passed_quiesce, rdp->qs_pending); |
9b2e4f188
|
114 |
seq_printf(m, " dt=%d/%llx/%d df=%lu", |
23b5c8fa0
|
115 |
atomic_read(&rdp->dynticks->dynticks), |
64db4cfff
|
116 |
rdp->dynticks->dynticks_nesting, |
23b5c8fa0
|
117 |
rdp->dynticks->dynticks_nmi_nesting, |
64db4cfff
|
118 |
rdp->dynticks_fqs); |
2036d94a7
|
119 |
seq_printf(m, " of=%lu", rdp->offline_fqs); |
3fbfbf7a3
|
120 121 122 |
rcu_nocb_q_lengths(rdp, &ql, &qll); qll += rdp->qlen_lazy; ql += rdp->qlen; |
486e25934
|
123 |
seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c", |
3fbfbf7a3
|
124 |
qll, ql, |
0ac3d136b
|
125 126 127 128 129 130 |
".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
|
131 132 |
".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); #ifdef CONFIG_RCU_BOOST |
62ab70724
|
133 |
seq_printf(m, " kt=%d/%c ktl=%x", |
d71df90ea
|
134 135 136 |
per_cpu(rcu_cpu_has_work, rdp->cpu), convert_kthread_status(per_cpu(rcu_cpu_kthread_status, rdp->cpu)), |
a46e0899e
|
137 138 139 |
per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff); #endif /* #ifdef CONFIG_RCU_BOOST */ seq_printf(m, " b=%ld", rdp->blimit); |
c635a4e1c
|
140 141 142 143 |
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
|
144 |
} |
c011c41f1
|
145 |
static int show_rcudata(struct seq_file *m, void *v) |
878eda72e
|
146 147 148 149 |
{ print_one_rcu_data(m, (struct rcu_data *)v); return 0; } |
c011c41f1
|
150 |
static const struct seq_operations rcudate_op = { |
878eda72e
|
151 152 153 |
.start = r_start, .next = r_next, .stop = r_stop, |
c011c41f1
|
154 |
.show = show_rcudata, |
878eda72e
|
155 |
}; |
c011c41f1
|
156 |
static int rcudata_open(struct inode *inode, struct file *file) |
878eda72e
|
157 |
{ |
c011c41f1
|
158 |
return r_open(inode, file, &rcudate_op); |
878eda72e
|
159 |
} |
c011c41f1
|
160 |
static const struct file_operations rcudata_fops = { |
878eda72e
|
161 |
.owner = THIS_MODULE, |
c011c41f1
|
162 |
.open = rcudata_open, |
878eda72e
|
163 164 165 166 |
.read = seq_read, .llseek = no_llseek, .release = seq_release, }; |
7bd8f2a74
|
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
static int show_rcuexp(struct seq_file *m, void *v) { struct rcu_state *rsp = (struct rcu_state *)m->private; seq_printf(m, "s=%lu d=%lu w=%lu tf=%lu wd1=%lu wd2=%lu n=%lu sc=%lu dt=%lu dl=%lu dx=%lu ", atomic_long_read(&rsp->expedited_start), atomic_long_read(&rsp->expedited_done), atomic_long_read(&rsp->expedited_wrap), atomic_long_read(&rsp->expedited_tryfail), atomic_long_read(&rsp->expedited_workdone1), atomic_long_read(&rsp->expedited_workdone2), atomic_long_read(&rsp->expedited_normal), atomic_long_read(&rsp->expedited_stoppedcpus), atomic_long_read(&rsp->expedited_done_tries), atomic_long_read(&rsp->expedited_done_lost), atomic_long_read(&rsp->expedited_done_exit)); 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
|
197 |
.release = single_release, |
7bd8f2a74
|
198 |
}; |
0ea1f2ebe
|
199 200 201 202 |
#ifdef CONFIG_RCU_BOOST static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) { |
5cf05ad75
|
203 |
seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu ", |
0ea1f2ebe
|
204 205 206 207 208 |
rnp->grplo, rnp->grphi, "T."[list_empty(&rnp->blkd_tasks)], "N."[!rnp->gp_tasks], "E."[!rnp->exp_tasks], "B."[!rnp->boost_tasks], |
d71df90ea
|
209 |
convert_kthread_status(rnp->boost_kthread_status), |
0ea1f2ebe
|
210 |
rnp->n_tasks_boosted, rnp->n_exp_boosts, |
5cf05ad75
|
211 212 213 |
rnp->n_normal_boosts); seq_printf(m, "j=%04x bt=%04x ", |
0ea1f2ebe
|
214 215 |
(int)(jiffies & 0xffff), (int)(rnp->boost_time & 0xffff)); |
5cf05ad75
|
216 217 |
seq_printf(m, " balk: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu ", |
0ea1f2ebe
|
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
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
|
244 |
.llseek = no_llseek, |
0ea1f2ebe
|
245 246 |
.release = single_release, }; |
6ee0886ff
|
247 |
#endif /* #ifdef CONFIG_RCU_BOOST */ |
0ea1f2ebe
|
248 |
|
64db4cfff
|
249 250 |
static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) { |
20133cfce
|
251 |
unsigned long gpnum; |
64db4cfff
|
252 253 |
int level = 0; struct rcu_node *rnp; |
3397e040d
|
254 |
gpnum = rsp->gpnum; |
6ee0886ff
|
255 256 |
seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x ", ulong2long(rsp->completed), ulong2long(gpnum), |
42c3533ee
|
257 |
rsp->fqs_state, |
64db4cfff
|
258 |
(long)(rsp->jiffies_force_qs - jiffies), |
5cf05ad75
|
259 260 261 |
(int)(jiffies & 0xffff)); seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld ", |
64db4cfff
|
262 263 |
rsp->n_force_qs, rsp->n_force_qs_ngp, rsp->n_force_qs - rsp->n_force_qs_ngp, |
b1420f1c8
|
264 |
rsp->n_force_qs_lh, rsp->qlen_lazy, rsp->qlen); |
f885b7f2b
|
265 |
for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < rcu_num_nodes; rnp++) { |
64db4cfff
|
266 267 268 269 270 |
if (rnp->level != level) { seq_puts(m, " "); level = rnp->level; } |
12f5f524c
|
271 |
seq_printf(m, "%lx/%lx %c%c>%c %d:%d ^%d ", |
64db4cfff
|
272 |
rnp->qsmask, rnp->qsmaskinit, |
12f5f524c
|
273 274 275 |
".G"[rnp->gp_tasks != NULL], ".E"[rnp->exp_tasks != NULL], ".T"[!list_empty(&rnp->blkd_tasks)], |
64db4cfff
|
276 277 278 279 280 |
rnp->grplo, rnp->grphi, rnp->grpnum); } seq_puts(m, " "); } |
6ee0886ff
|
281 |
static int show_rcuhier(struct seq_file *m, void *v) |
64db4cfff
|
282 |
{ |
6ee0886ff
|
283 284 |
struct rcu_state *rsp = (struct rcu_state *)m->private; print_one_rcu_state(m, rsp); |
64db4cfff
|
285 286 287 288 289 |
return 0; } static int rcuhier_open(struct inode *inode, struct file *file) { |
6ee0886ff
|
290 |
return single_open(file, show_rcuhier, inode->i_private); |
64db4cfff
|
291 |
} |
9b2619aff
|
292 |
static const struct file_operations rcuhier_fops = { |
64db4cfff
|
293 294 295 |
.owner = THIS_MODULE, .open = rcuhier_open, .read = seq_read, |
a608d84bd
|
296 |
.llseek = no_llseek, |
7ee2b9e56
|
297 |
.release = single_release, |
a608d84bd
|
298 |
}; |
15ba0ba86
|
299 300 301 302 303 304 305 306 307 308 |
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]; raw_spin_lock_irqsave(&rnp->lock, flags); |
42c3533ee
|
309 310 311 |
completed = ACCESS_ONCE(rsp->completed); gpnum = ACCESS_ONCE(rsp->gpnum); if (completed == gpnum) |
15ba0ba86
|
312 313 314 315 316 |
gpage = 0; else gpage = jiffies - rsp->gp_start; gpmax = rsp->gp_max; raw_spin_unlock_irqrestore(&rnp->lock, flags); |
6ee0886ff
|
317 318 319 |
seq_printf(m, "completed=%ld gpnum=%ld age=%ld max=%ld ", ulong2long(completed), ulong2long(gpnum), gpage, gpmax); |
15ba0ba86
|
320 |
} |
6ee0886ff
|
321 |
static int show_rcugp(struct seq_file *m, void *v) |
64db4cfff
|
322 |
{ |
6ee0886ff
|
323 324 |
struct rcu_state *rsp = (struct rcu_state *)m->private; show_one_rcugp(m, rsp); |
64db4cfff
|
325 326 327 328 329 |
return 0; } static int rcugp_open(struct inode *inode, struct file *file) { |
6ee0886ff
|
330 |
return single_open(file, show_rcugp, inode->i_private); |
64db4cfff
|
331 |
} |
9b2619aff
|
332 |
static const struct file_operations rcugp_fops = { |
64db4cfff
|
333 334 335 |
.owner = THIS_MODULE, .open = rcugp_open, .read = seq_read, |
66b38bc52
|
336 |
.llseek = no_llseek, |
7ee2b9e56
|
337 |
.release = single_release, |
66b38bc52
|
338 |
}; |
7ba5c840e
|
339 340 |
static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp) { |
51d0f16d4
|
341 342 |
if (!rdp->beenonline) return; |
5cf05ad75
|
343 |
seq_printf(m, "%3d%cnp=%ld ", |
7ba5c840e
|
344 345 |
rdp->cpu, cpu_is_offline(rdp->cpu) ? '!' : ' ', |
5cf05ad75
|
346 347 |
rdp->n_rcu_pending); seq_printf(m, "qsp=%ld rpq=%ld cbr=%ld cng=%ld ", |
7ba5c840e
|
348 |
rdp->n_rp_qs_pending, |
d21670aca
|
349 |
rdp->n_rp_report_qs, |
7ba5c840e
|
350 |
rdp->n_rp_cb_ready, |
5cf05ad75
|
351 |
rdp->n_rp_cpu_needs_gp); |
4605c0143
|
352 353 |
seq_printf(m, "gpc=%ld gps=%ld nn=%ld ", |
7ba5c840e
|
354 355 |
rdp->n_rp_gp_completed, rdp->n_rp_gp_started, |
7ba5c840e
|
356 357 |
rdp->n_rp_need_nothing); } |
c011c41f1
|
358 |
static int show_rcu_pending(struct seq_file *m, void *v) |
51d0f16d4
|
359 360 361 362 |
{ print_one_rcu_pending(m, (struct rcu_data *)v); return 0; } |
c011c41f1
|
363 |
static const struct seq_operations rcu_pending_op = { |
51d0f16d4
|
364 365 366 |
.start = r_start, .next = r_next, .stop = r_stop, |
c011c41f1
|
367 |
.show = show_rcu_pending, |
51d0f16d4
|
368 |
}; |
c011c41f1
|
369 |
static int rcu_pending_open(struct inode *inode, struct file *file) |
51d0f16d4
|
370 |
{ |
c011c41f1
|
371 |
return r_open(inode, file, &rcu_pending_op); |
51d0f16d4
|
372 |
} |
c011c41f1
|
373 |
static const struct file_operations rcu_pending_fops = { |
51d0f16d4
|
374 |
.owner = THIS_MODULE, |
c011c41f1
|
375 |
.open = rcu_pending_open, |
51d0f16d4
|
376 377 378 379 |
.read = seq_read, .llseek = no_llseek, .release = seq_release, }; |
4a2986568
|
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
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
|
404 |
static struct dentry *rcudir; |
7ba5c840e
|
405 |
|
deb7a4181
|
406 |
static int __init rcutree_trace_init(void) |
64db4cfff
|
407 |
{ |
573bcd40d
|
408 |
struct rcu_state *rsp; |
22f00b69f
|
409 |
struct dentry *retval; |
573bcd40d
|
410 |
struct dentry *rspdir; |
22f00b69f
|
411 |
|
64db4cfff
|
412 413 |
rcudir = debugfs_create_dir("rcu", NULL); if (!rcudir) |
22f00b69f
|
414 |
goto free_out; |
64db4cfff
|
415 |
|
573bcd40d
|
416 417 418 419 |
for_each_rcu_flavor(rsp) { rspdir = debugfs_create_dir(rsp->name, rcudir); if (!rspdir) goto free_out; |
878eda72e
|
420 |
|
6ee0886ff
|
421 422 423 424 |
retval = debugfs_create_file("rcudata", 0444, rspdir, rsp, &rcudata_fops); if (!retval) goto free_out; |
51d0f16d4
|
425 |
|
7bd8f2a74
|
426 427 428 429 |
retval = debugfs_create_file("rcuexp", 0444, rspdir, rsp, &rcuexp_fops); if (!retval) goto free_out; |
6ee0886ff
|
430 431 432 433 |
retval = debugfs_create_file("rcu_pending", 0444, rspdir, rsp, &rcu_pending_fops); if (!retval) goto free_out; |
c25e557f5
|
434 |
|
6ee0886ff
|
435 436 437 438 |
retval = debugfs_create_file("rcubarrier", 0444, rspdir, rsp, &rcubarrier_fops); if (!retval) goto free_out; |
29c67764f
|
439 440 |
#ifdef CONFIG_RCU_BOOST |
6ee0886ff
|
441 442 443 |
if (rsp == &rcu_preempt_state) { retval = debugfs_create_file("rcuboost", 0444, rspdir, NULL, &rcu_node_boost_fops); |
66b38bc52
|
444 445 |
if (!retval) goto free_out; |
6ee0886ff
|
446 447 |
} #endif |
66b38bc52
|
448 |
|
6ee0886ff
|
449 450 451 452 |
retval = debugfs_create_file("rcugp", 0444, rspdir, rsp, &rcugp_fops); if (!retval) goto free_out; |
64db4cfff
|
453 |
|
6ee0886ff
|
454 455 456 457 458 |
retval = debugfs_create_file("rcuhier", 0444, rspdir, rsp, &rcuhier_fops); if (!retval) goto free_out; } |
7ba5c840e
|
459 |
|
4a2986568
|
460 461 462 463 |
retval = debugfs_create_file("rcutorture", 0444, rcudir, NULL, &rcutorture_fops); if (!retval) goto free_out; |
64db4cfff
|
464 465 |
return 0; free_out: |
22f00b69f
|
466 |
debugfs_remove_recursive(rcudir); |
64db4cfff
|
467 468 |
return 1; } |
deb7a4181
|
469 |
static void __exit rcutree_trace_cleanup(void) |
64db4cfff
|
470 |
{ |
22f00b69f
|
471 |
debugfs_remove_recursive(rcudir); |
64db4cfff
|
472 |
} |
deb7a4181
|
473 474 |
module_init(rcutree_trace_init); module_exit(rcutree_trace_cleanup); |
64db4cfff
|
475 476 477 478 |
MODULE_AUTHOR("Paul E. McKenney"); MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation"); MODULE_LICENSE("GPL"); |