tree_trace.c 13.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * 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
15 16
 * along with this program; if not, you can access it online at
 * http://www.gnu.org/licenses/gpl-2.0.html.
17 18 19 20 21 22
 *
 * Copyright IBM Corporation, 2008
 *
 * Papers:  http://www.rdrop.com/users/paulmck/RCU
 *
 * For detailed explanation of Read-Copy Update mechanism see -
23
 *		Documentation/RCU
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>
A
Arun Sharma 已提交
34
#include <linux/atomic.h>
35 36 37 38 39 40 41 42 43 44 45
#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>

46
#define RCU_TREE_NONCORE
47
#include "tree.h"
48

49 50
DECLARE_PER_CPU_SHARED_ALIGNED(unsigned long, rcu_qs_ctr);

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 77 78 79 80
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)
{
}

81
static int show_rcubarrier(struct seq_file *m, void *v)
82 83 84 85 86 87 88 89
{
	struct rcu_state *rsp = (struct rcu_state *)m->private;
	seq_printf(m, "bcc: %d nbd: %lu\n",
		   atomic_read(&rsp->barrier_cpu_count),
		   rsp->n_barrier_done);
	return 0;
}

90
static int rcubarrier_open(struct inode *inode, struct file *file)
91
{
92
	return single_open(file, show_rcubarrier, inode->i_private);
93 94
}

95
static const struct file_operations rcubarrier_fops = {
96
	.owner = THIS_MODULE,
97
	.open = rcubarrier_open,
98 99
	.read = seq_read,
	.llseek = no_llseek,
A
Al Viro 已提交
100
	.release = single_release,
101 102
};

103 104
#ifdef CONFIG_RCU_BOOST

105 106 107 108
static char convert_kthread_status(unsigned int kthread_status)
{
	if (kthread_status > RCU_KTHREAD_MAX)
		return '?';
109
	return "SRWOY"[kthread_status];
110 111
}

112 113
#endif /* #ifdef CONFIG_RCU_BOOST */

114 115
static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
{
P
Paul E. McKenney 已提交
116 117
	long ql, qll;

118 119
	if (!rdp->beenonline)
		return;
120
	seq_printf(m, "%3d%cc=%ld g=%ld pq=%d/%d qp=%d",
121 122
		   rdp->cpu,
		   cpu_is_offline(rdp->cpu) ? '!' : ' ',
P
Paul E. McKenney 已提交
123
		   ulong2long(rdp->completed), ulong2long(rdp->gpnum),
124 125 126
		   rdp->passed_quiesce,
		   rdp->rcu_qs_ctr_snap == per_cpu(rcu_qs_ctr, rdp->cpu),
		   rdp->qs_pending);
127
	seq_printf(m, " dt=%d/%llx/%d df=%lu",
128
		   atomic_read(&rdp->dynticks->dynticks),
129
		   rdp->dynticks->dynticks_nesting,
130
		   rdp->dynticks->dynticks_nmi_nesting,
131
		   rdp->dynticks_fqs);
132
	seq_printf(m, " of=%lu", rdp->offline_fqs);
P
Paul E. McKenney 已提交
133 134 135
	rcu_nocb_q_lengths(rdp, &ql, &qll);
	qll += rdp->qlen_lazy;
	ql += rdp->qlen;
136
	seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c",
P
Paul E. McKenney 已提交
137
		   qll, ql,
138 139 140 141 142 143
		   ".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]],
144 145
		   ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]);
#ifdef CONFIG_RCU_BOOST
146
	seq_printf(m, " kt=%d/%c ktl=%x",
147 148 149
		   per_cpu(rcu_cpu_has_work, rdp->cpu),
		   convert_kthread_status(per_cpu(rcu_cpu_kthread_status,
					  rdp->cpu)),
150 151 152
		   per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff);
#endif /* #ifdef CONFIG_RCU_BOOST */
	seq_printf(m, " b=%ld", rdp->blimit);
153 154 155
	seq_printf(m, " ci=%lu nci=%lu co=%lu ca=%lu\n",
		   rdp->n_cbs_invoked, rdp->n_nocbs_invoked,
		   rdp->n_cbs_orphaned, rdp->n_cbs_adopted);
156 157
}

158
static int show_rcudata(struct seq_file *m, void *v)
159 160 161 162 163
{
	print_one_rcu_data(m, (struct rcu_data *)v);
	return 0;
}

164
static const struct seq_operations rcudate_op = {
165 166 167
	.start = r_start,
	.next  = r_next,
	.stop  = r_stop,
168
	.show  = show_rcudata,
169 170
};

171
static int rcudata_open(struct inode *inode, struct file *file)
172
{
173
	return r_open(inode, file, &rcudate_op);
174 175
}

176
static const struct file_operations rcudata_fops = {
177
	.owner = THIS_MODULE,
178
	.open = rcudata_open,
179 180 181 182 183
	.read = seq_read,
	.llseek = no_llseek,
	.release = seq_release,
};

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
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\n",
		   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,
A
Al Viro 已提交
213
	.release = single_release,
214 215
};

216 217 218 219
#ifdef CONFIG_RCU_BOOST

static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp)
{
220
	seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu ",
221 222 223 224 225
		   rnp->grplo, rnp->grphi,
		   "T."[list_empty(&rnp->blkd_tasks)],
		   "N."[!rnp->gp_tasks],
		   "E."[!rnp->exp_tasks],
		   "B."[!rnp->boost_tasks],
226
		   convert_kthread_status(rnp->boost_kthread_status),
227
		   rnp->n_tasks_boosted, rnp->n_exp_boosts,
228 229
		   rnp->n_normal_boosts);
	seq_printf(m, "j=%04x bt=%04x\n",
230 231
		   (int)(jiffies & 0xffff),
		   (int)(rnp->boost_time & 0xffff));
232
	seq_printf(m, "    balk: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu\n",
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
		   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,
259
	.llseek = no_llseek,
260 261 262
	.release = single_release,
};

263
#endif /* #ifdef CONFIG_RCU_BOOST */
264

265 266
static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
{
267
	unsigned long gpnum;
268 269 270
	int level = 0;
	struct rcu_node *rnp;

271
	gpnum = rsp->gpnum;
272 273
	seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x ",
		   ulong2long(rsp->completed), ulong2long(gpnum),
P
Paul E. McKenney 已提交
274
		   rsp->fqs_state,
275
		   (long)(rsp->jiffies_force_qs - jiffies),
276 277
		   (int)(jiffies & 0xffff));
	seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n",
278 279
		   rsp->n_force_qs, rsp->n_force_qs_ngp,
		   rsp->n_force_qs - rsp->n_force_qs_ngp,
280
		   ACCESS_ONCE(rsp->n_force_qs_lh), rsp->qlen_lazy, rsp->qlen);
281
	for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < rcu_num_nodes; rnp++) {
282 283 284 285
		if (rnp->level != level) {
			seq_puts(m, "\n");
			level = rnp->level;
		}
286
		seq_printf(m, "%lx/%lx %c%c>%c %d:%d ^%d    ",
287
			   rnp->qsmask, rnp->qsmaskinit,
288 289 290
			   ".G"[rnp->gp_tasks != NULL],
			   ".E"[rnp->exp_tasks != NULL],
			   ".T"[!list_empty(&rnp->blkd_tasks)],
291 292 293 294 295
			   rnp->grplo, rnp->grphi, rnp->grpnum);
	}
	seq_puts(m, "\n");
}

296
static int show_rcuhier(struct seq_file *m, void *v)
297
{
298 299
	struct rcu_state *rsp = (struct rcu_state *)m->private;
	print_one_rcu_state(m, rsp);
300 301 302 303 304
	return 0;
}

static int rcuhier_open(struct inode *inode, struct file *file)
{
305
	return single_open(file, show_rcuhier, inode->i_private);
306 307
}

308
static const struct file_operations rcuhier_fops = {
309 310 311
	.owner = THIS_MODULE,
	.open = rcuhier_open,
	.read = seq_read,
M
Michael Wang 已提交
312
	.llseek = no_llseek,
A
Al Viro 已提交
313
	.release = single_release,
M
Michael Wang 已提交
314 315
};

316 317 318 319 320 321 322 323 324 325
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);
P
Paul E. McKenney 已提交
326 327 328
	completed = ACCESS_ONCE(rsp->completed);
	gpnum = ACCESS_ONCE(rsp->gpnum);
	if (completed == gpnum)
329 330 331 332 333
		gpage = 0;
	else
		gpage = jiffies - rsp->gp_start;
	gpmax = rsp->gp_max;
	raw_spin_unlock_irqrestore(&rnp->lock, flags);
334 335
	seq_printf(m, "completed=%ld  gpnum=%ld  age=%ld  max=%ld\n",
		   ulong2long(completed), ulong2long(gpnum), gpage, gpmax);
336 337
}

338
static int show_rcugp(struct seq_file *m, void *v)
339
{
340 341
	struct rcu_state *rsp = (struct rcu_state *)m->private;
	show_one_rcugp(m, rsp);
342 343 344 345 346
	return 0;
}

static int rcugp_open(struct inode *inode, struct file *file)
{
347
	return single_open(file, show_rcugp, inode->i_private);
348 349
}

350
static const struct file_operations rcugp_fops = {
351 352 353
	.owner = THIS_MODULE,
	.open = rcugp_open,
	.read = seq_read,
M
Michael Wang 已提交
354
	.llseek = no_llseek,
A
Al Viro 已提交
355
	.release = single_release,
M
Michael Wang 已提交
356 357
};

358 359
static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp)
{
360 361
	if (!rdp->beenonline)
		return;
362
	seq_printf(m, "%3d%cnp=%ld ",
363 364
		   rdp->cpu,
		   cpu_is_offline(rdp->cpu) ? '!' : ' ',
365 366
		   rdp->n_rcu_pending);
	seq_printf(m, "qsp=%ld rpq=%ld cbr=%ld cng=%ld ",
367
		   rdp->n_rp_qs_pending,
368
		   rdp->n_rp_report_qs,
369
		   rdp->n_rp_cb_ready,
370
		   rdp->n_rp_cpu_needs_gp);
371
	seq_printf(m, "gpc=%ld gps=%ld nn=%ld ndw%ld\n",
372 373
		   rdp->n_rp_gp_completed,
		   rdp->n_rp_gp_started,
374
		   rdp->n_rp_nocb_defer_wakeup,
375 376 377
		   rdp->n_rp_need_nothing);
}

378
static int show_rcu_pending(struct seq_file *m, void *v)
379 380 381 382 383
{
	print_one_rcu_pending(m, (struct rcu_data *)v);
	return 0;
}

384
static const struct seq_operations rcu_pending_op = {
385 386 387
	.start = r_start,
	.next  = r_next,
	.stop  = r_stop,
388
	.show  = show_rcu_pending,
389 390
};

391
static int rcu_pending_open(struct inode *inode, struct file *file)
392
{
393
	return r_open(inode, file, &rcu_pending_op);
394 395
}

396
static const struct file_operations rcu_pending_fops = {
397
	.owner = THIS_MODULE,
398
	.open = rcu_pending_open,
399 400 401 402 403
	.read = seq_read,
	.llseek = no_llseek,
	.release = seq_release,
};

404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
static int show_rcutorture(struct seq_file *m, void *unused)
{
	seq_printf(m, "rcutorture test sequence: %lu %s\n",
		   rcutorture_testseq >> 1,
		   (rcutorture_testseq & 0x1) ? "(test in progress)" : "");
	seq_printf(m, "rcutorture update version number: %lu\n",
		   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,
};

427 428
static struct dentry *rcudir;

429
static int __init rcutree_trace_init(void)
430
{
431
	struct rcu_state *rsp;
432
	struct dentry *retval;
433
	struct dentry *rspdir;
434

435 436
	rcudir = debugfs_create_dir("rcu", NULL);
	if (!rcudir)
437
		goto free_out;
438

439 440 441 442
	for_each_rcu_flavor(rsp) {
		rspdir = debugfs_create_dir(rsp->name, rcudir);
		if (!rspdir)
			goto free_out;
443

444 445 446 447
		retval = debugfs_create_file("rcudata", 0444,
				rspdir, rsp, &rcudata_fops);
		if (!retval)
			goto free_out;
448

449 450 451 452 453
		retval = debugfs_create_file("rcuexp", 0444,
				rspdir, rsp, &rcuexp_fops);
		if (!retval)
			goto free_out;

454 455 456 457
		retval = debugfs_create_file("rcu_pending", 0444,
				rspdir, rsp, &rcu_pending_fops);
		if (!retval)
			goto free_out;
458

459 460 461 462
		retval = debugfs_create_file("rcubarrier", 0444,
				rspdir, rsp, &rcubarrier_fops);
		if (!retval)
			goto free_out;
463 464

#ifdef CONFIG_RCU_BOOST
465 466 467
		if (rsp == &rcu_preempt_state) {
			retval = debugfs_create_file("rcuboost", 0444,
				rspdir, NULL, &rcu_node_boost_fops);
M
Michael Wang 已提交
468 469
			if (!retval)
				goto free_out;
470 471
		}
#endif
M
Michael Wang 已提交
472

473 474 475 476
		retval = debugfs_create_file("rcugp", 0444,
				rspdir, rsp, &rcugp_fops);
		if (!retval)
			goto free_out;
477

478 479 480 481 482
		retval = debugfs_create_file("rcuhier", 0444,
				rspdir, rsp, &rcuhier_fops);
		if (!retval)
			goto free_out;
	}
483

484 485 486 487
	retval = debugfs_create_file("rcutorture", 0444, rcudir,
						NULL, &rcutorture_fops);
	if (!retval)
		goto free_out;
488 489
	return 0;
free_out:
490
	debugfs_remove_recursive(rcudir);
491 492 493
	return 1;
}

494
static void __exit rcutree_trace_cleanup(void)
495
{
496
	debugfs_remove_recursive(rcudir);
497 498 499
}


500 501
module_init(rcutree_trace_init);
module_exit(rcutree_trace_cleanup);
502 503 504 505

MODULE_AUTHOR("Paul E. McKenney");
MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation");
MODULE_LICENSE("GPL");