lockdep_proc.c 17.6 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Ingo Molnar's avatar
Ingo Molnar committed
2 3 4 5 6 7 8
/*
 * kernel/lockdep_proc.c
 *
 * Runtime locking correctness validator
 *
 * Started by Ingo Molnar:
 *
Peter Zijlstra's avatar
Peter Zijlstra committed
9
 *  Copyright (C) 2006,2007 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
10
 *  Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
Ingo Molnar's avatar
Ingo Molnar committed
11 12 13 14
 *
 * Code for /proc/lockdep and /proc/lockdep_stats:
 *
 */
15
#include <linux/export.h>
Ingo Molnar's avatar
Ingo Molnar committed
16 17 18 19
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
#include <linux/debug_locks.h>
20 21
#include <linux/vmalloc.h>
#include <linux/sort.h>
22
#include <linux/uaccess.h>
23
#include <asm/div64.h>
Ingo Molnar's avatar
Ingo Molnar committed
24 25 26 27 28

#include "lockdep_internals.h"

static void *l_next(struct seq_file *m, void *v, loff_t *pos)
{
29
	return seq_list_next(v, &all_lock_classes, pos);
Ingo Molnar's avatar
Ingo Molnar committed
30 31 32 33
}

static void *l_start(struct seq_file *m, loff_t *pos)
{
34
	return seq_list_start_head(&all_lock_classes, *pos);
Ingo Molnar's avatar
Ingo Molnar committed
35 36 37 38 39 40
}

static void l_stop(struct seq_file *m, void *v)
{
}

41 42
static void print_name(struct seq_file *m, struct lock_class *class)
{
43
	char str[KSYM_NAME_LEN];
44 45 46 47 48 49 50 51 52 53 54 55 56 57
	const char *name = class->name;

	if (!name) {
		name = __get_key_name(class->key, str);
		seq_printf(m, "%s", name);
	} else{
		seq_printf(m, "%s", name);
		if (class->name_version > 1)
			seq_printf(m, "#%d", class->name_version);
		if (class->subclass)
			seq_printf(m, "/%d", class->subclass);
	}
}

Ingo Molnar's avatar
Ingo Molnar committed
58 59
static int l_show(struct seq_file *m, void *v)
{
60
	struct lock_class *class = list_entry(v, struct lock_class, lock_entry);
61
	struct lock_list *entry;
62
	char usage[LOCK_USAGE_CHARS];
Ingo Molnar's avatar
Ingo Molnar committed
63

64
	if (v == &all_lock_classes) {
65 66 67 68
		seq_printf(m, "all lock classes:\n");
		return 0;
	}

Ingo Molnar's avatar
Ingo Molnar committed
69 70 71 72
	seq_printf(m, "%p", class->key);
#ifdef CONFIG_DEBUG_LOCKDEP
	seq_printf(m, " OPS:%8ld", class->ops);
#endif
73 74 75 76
#ifdef CONFIG_PROVE_LOCKING
	seq_printf(m, " FD:%5ld", lockdep_count_forward_deps(class));
	seq_printf(m, " BD:%5ld", lockdep_count_backward_deps(class));
#endif
Ingo Molnar's avatar
Ingo Molnar committed
77

78 79
	get_usage_chars(class, usage);
	seq_printf(m, " %s", usage);
Ingo Molnar's avatar
Ingo Molnar committed
80

81 82 83 84 85 86
	seq_printf(m, ": ");
	print_name(m, class);
	seq_puts(m, "\n");

	list_for_each_entry(entry, &class->locks_after, entry) {
		if (entry->distance == 1) {
87
			seq_printf(m, " -> [%p] ", entry->class->key);
88 89 90
			print_name(m, entry->class);
			seq_puts(m, "\n");
		}
Ingo Molnar's avatar
Ingo Molnar committed
91 92 93 94 95 96
	}
	seq_puts(m, "\n");

	return 0;
}

97
static const struct seq_operations lockdep_ops = {
Ingo Molnar's avatar
Ingo Molnar committed
98 99 100 101 102 103 104 105
	.start	= l_start,
	.next	= l_next,
	.stop	= l_stop,
	.show	= l_show,
};

static int lockdep_open(struct inode *inode, struct file *file)
{
106
	return seq_open(file, &lockdep_ops);
Ingo Molnar's avatar
Ingo Molnar committed
107 108
}

109
static const struct file_operations proc_lockdep_operations = {
Ingo Molnar's avatar
Ingo Molnar committed
110 111 112 113 114 115
	.open		= lockdep_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

116
#ifdef CONFIG_PROVE_LOCKING
117 118 119 120 121
static void *lc_start(struct seq_file *m, loff_t *pos)
{
	if (*pos == 0)
		return SEQ_START_TOKEN;

122 123
	if (*pos - 1 < nr_lock_chains)
		return lock_chains + (*pos - 1);
124 125 126 127

	return NULL;
}

128 129 130 131 132 133
static void *lc_next(struct seq_file *m, void *v, loff_t *pos)
{
	(*pos)++;
	return lc_start(m, pos);
}

134 135 136 137 138 139 140 141 142 143 144
static void lc_stop(struct seq_file *m, void *v)
{
}

static int lc_show(struct seq_file *m, void *v)
{
	struct lock_chain *chain = v;
	struct lock_class *class;
	int i;

	if (v == SEQ_START_TOKEN) {
145 146
		if (nr_chain_hlocks > MAX_LOCKDEP_CHAIN_HLOCKS)
			seq_printf(m, "(buggered) ");
147 148 149 150 151 152 153 154
		seq_printf(m, "all lock chains:\n");
		return 0;
	}

	seq_printf(m, "irq_context: %d\n", chain->irq_context);

	for (i = 0; i < chain->depth; i++) {
		class = lock_chain_get_class(chain, i);
155 156 157
		if (!class->key)
			continue;

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
		seq_printf(m, "[%p] ", class->key);
		print_name(m, class);
		seq_puts(m, "\n");
	}
	seq_puts(m, "\n");

	return 0;
}

static const struct seq_operations lockdep_chains_ops = {
	.start	= lc_start,
	.next	= lc_next,
	.stop	= lc_stop,
	.show	= lc_show,
};

static int lockdep_chains_open(struct inode *inode, struct file *file)
{
176
	return seq_open(file, &lockdep_chains_ops);
177 178 179 180 181 182 183 184
}

static const struct file_operations proc_lockdep_chains_operations = {
	.open		= lockdep_chains_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};
185
#endif /* CONFIG_PROVE_LOCKING */
186

Ingo Molnar's avatar
Ingo Molnar committed
187 188 189
static void lockdep_stats_debug_show(struct seq_file *m)
{
#ifdef CONFIG_DEBUG_LOCKDEP
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
	unsigned long long hi1 = debug_atomic_read(hardirqs_on_events),
			   hi2 = debug_atomic_read(hardirqs_off_events),
			   hr1 = debug_atomic_read(redundant_hardirqs_on),
			   hr2 = debug_atomic_read(redundant_hardirqs_off),
			   si1 = debug_atomic_read(softirqs_on_events),
			   si2 = debug_atomic_read(softirqs_off_events),
			   sr1 = debug_atomic_read(redundant_softirqs_on),
			   sr2 = debug_atomic_read(redundant_softirqs_off);

	seq_printf(m, " chain lookup misses:           %11llu\n",
		debug_atomic_read(chain_lookup_misses));
	seq_printf(m, " chain lookup hits:             %11llu\n",
		debug_atomic_read(chain_lookup_hits));
	seq_printf(m, " cyclic checks:                 %11llu\n",
		debug_atomic_read(nr_cyclic_checks));
205 206 207 208
	seq_printf(m, " redundant checks:              %11llu\n",
		debug_atomic_read(nr_redundant_checks));
	seq_printf(m, " redundant links:               %11llu\n",
		debug_atomic_read(nr_redundant));
209 210 211 212 213 214 215 216 217 218 219 220 221
	seq_printf(m, " find-mask forwards checks:     %11llu\n",
		debug_atomic_read(nr_find_usage_forwards_checks));
	seq_printf(m, " find-mask backwards checks:    %11llu\n",
		debug_atomic_read(nr_find_usage_backwards_checks));

	seq_printf(m, " hardirq on events:             %11llu\n", hi1);
	seq_printf(m, " hardirq off events:            %11llu\n", hi2);
	seq_printf(m, " redundant hardirq ons:         %11llu\n", hr1);
	seq_printf(m, " redundant hardirq offs:        %11llu\n", hr2);
	seq_printf(m, " softirq on events:             %11llu\n", si1);
	seq_printf(m, " softirq off events:            %11llu\n", si2);
	seq_printf(m, " redundant softirq ons:         %11llu\n", sr1);
	seq_printf(m, " redundant softirq offs:        %11llu\n", sr2);
Ingo Molnar's avatar
Ingo Molnar committed
222 223 224 225 226 227 228 229 230 231 232 233 234
#endif
}

static int lockdep_stats_show(struct seq_file *m, void *v)
{
	struct lock_class *class;
	unsigned long nr_unused = 0, nr_uncategorized = 0,
		      nr_irq_safe = 0, nr_irq_unsafe = 0,
		      nr_softirq_safe = 0, nr_softirq_unsafe = 0,
		      nr_hardirq_safe = 0, nr_hardirq_unsafe = 0,
		      nr_irq_read_safe = 0, nr_irq_read_unsafe = 0,
		      nr_softirq_read_safe = 0, nr_softirq_read_unsafe = 0,
		      nr_hardirq_read_safe = 0, nr_hardirq_read_unsafe = 0,
235
		      sum_forward_deps = 0;
Ingo Molnar's avatar
Ingo Molnar committed
236 237 238 239 240 241 242 243 244

	list_for_each_entry(class, &all_lock_classes, lock_entry) {

		if (class->usage_mask == 0)
			nr_unused++;
		if (class->usage_mask == LOCKF_USED)
			nr_uncategorized++;
		if (class->usage_mask & LOCKF_USED_IN_IRQ)
			nr_irq_safe++;
Peter Zijlstra's avatar
Peter Zijlstra committed
245
		if (class->usage_mask & LOCKF_ENABLED_IRQ)
Ingo Molnar's avatar
Ingo Molnar committed
246 247 248
			nr_irq_unsafe++;
		if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ)
			nr_softirq_safe++;
Peter Zijlstra's avatar
Peter Zijlstra committed
249
		if (class->usage_mask & LOCKF_ENABLED_SOFTIRQ)
Ingo Molnar's avatar
Ingo Molnar committed
250 251 252
			nr_softirq_unsafe++;
		if (class->usage_mask & LOCKF_USED_IN_HARDIRQ)
			nr_hardirq_safe++;
Peter Zijlstra's avatar
Peter Zijlstra committed
253
		if (class->usage_mask & LOCKF_ENABLED_HARDIRQ)
Ingo Molnar's avatar
Ingo Molnar committed
254 255 256
			nr_hardirq_unsafe++;
		if (class->usage_mask & LOCKF_USED_IN_IRQ_READ)
			nr_irq_read_safe++;
Peter Zijlstra's avatar
Peter Zijlstra committed
257
		if (class->usage_mask & LOCKF_ENABLED_IRQ_READ)
Ingo Molnar's avatar
Ingo Molnar committed
258 259 260
			nr_irq_read_unsafe++;
		if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ_READ)
			nr_softirq_read_safe++;
Peter Zijlstra's avatar
Peter Zijlstra committed
261
		if (class->usage_mask & LOCKF_ENABLED_SOFTIRQ_READ)
Ingo Molnar's avatar
Ingo Molnar committed
262 263 264
			nr_softirq_read_unsafe++;
		if (class->usage_mask & LOCKF_USED_IN_HARDIRQ_READ)
			nr_hardirq_read_safe++;
Peter Zijlstra's avatar
Peter Zijlstra committed
265
		if (class->usage_mask & LOCKF_ENABLED_HARDIRQ_READ)
Ingo Molnar's avatar
Ingo Molnar committed
266 267
			nr_hardirq_read_unsafe++;

268
#ifdef CONFIG_PROVE_LOCKING
269
		sum_forward_deps += lockdep_count_forward_deps(class);
270
#endif
Ingo Molnar's avatar
Ingo Molnar committed
271
	}
272
#ifdef CONFIG_DEBUG_LOCKDEP
273
	DEBUG_LOCKS_WARN_ON(debug_atomic_read(nr_unused_locks) != nr_unused);
Ingo Molnar's avatar
Ingo Molnar committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
#endif
	seq_printf(m, " lock-classes:                  %11lu [max: %lu]\n",
			nr_lock_classes, MAX_LOCKDEP_KEYS);
	seq_printf(m, " direct dependencies:           %11lu [max: %lu]\n",
			nr_list_entries, MAX_LOCKDEP_ENTRIES);
	seq_printf(m, " indirect dependencies:         %11lu\n",
			sum_forward_deps);

	/*
	 * Total number of dependencies:
	 *
	 * All irq-safe locks may nest inside irq-unsafe locks,
	 * plus all the other known dependencies:
	 */
	seq_printf(m, " all direct dependencies:       %11lu\n",
			nr_irq_unsafe * nr_irq_safe +
			nr_hardirq_unsafe * nr_hardirq_safe +
			nr_list_entries);

Peter Zijlstra's avatar
Peter Zijlstra committed
293
#ifdef CONFIG_PROVE_LOCKING
Ingo Molnar's avatar
Ingo Molnar committed
294 295
	seq_printf(m, " dependency chains:             %11lu [max: %lu]\n",
			nr_lock_chains, MAX_LOCKDEP_CHAINS);
296
	seq_printf(m, " dependency chain hlocks:       %11d [max: %lu]\n",
297
			nr_chain_hlocks, MAX_LOCKDEP_CHAIN_HLOCKS);
Peter Zijlstra's avatar
Peter Zijlstra committed
298
#endif
Ingo Molnar's avatar
Ingo Molnar committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346

#ifdef CONFIG_TRACE_IRQFLAGS
	seq_printf(m, " in-hardirq chains:             %11u\n",
			nr_hardirq_chains);
	seq_printf(m, " in-softirq chains:             %11u\n",
			nr_softirq_chains);
#endif
	seq_printf(m, " in-process chains:             %11u\n",
			nr_process_chains);
	seq_printf(m, " stack-trace entries:           %11lu [max: %lu]\n",
			nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES);
	seq_printf(m, " combined max dependencies:     %11u\n",
			(nr_hardirq_chains + 1) *
			(nr_softirq_chains + 1) *
			(nr_process_chains + 1)
	);
	seq_printf(m, " hardirq-safe locks:            %11lu\n",
			nr_hardirq_safe);
	seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
			nr_hardirq_unsafe);
	seq_printf(m, " softirq-safe locks:            %11lu\n",
			nr_softirq_safe);
	seq_printf(m, " softirq-unsafe locks:          %11lu\n",
			nr_softirq_unsafe);
	seq_printf(m, " irq-safe locks:                %11lu\n",
			nr_irq_safe);
	seq_printf(m, " irq-unsafe locks:              %11lu\n",
			nr_irq_unsafe);

	seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
			nr_hardirq_read_safe);
	seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
			nr_hardirq_read_unsafe);
	seq_printf(m, " softirq-read-safe locks:       %11lu\n",
			nr_softirq_read_safe);
	seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
			nr_softirq_read_unsafe);
	seq_printf(m, " irq-read-safe locks:           %11lu\n",
			nr_irq_read_safe);
	seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
			nr_irq_read_unsafe);

	seq_printf(m, " uncategorized locks:           %11lu\n",
			nr_uncategorized);
	seq_printf(m, " unused locks:                  %11lu\n",
			nr_unused);
	seq_printf(m, " max locking depth:             %11u\n",
			max_lockdep_depth);
Ingo Molnar's avatar
Ingo Molnar committed
347
#ifdef CONFIG_PROVE_LOCKING
348 349
	seq_printf(m, " max bfs queue depth:           %11u\n",
			max_bfs_queue_depth);
Ingo Molnar's avatar
Ingo Molnar committed
350
#endif
Ingo Molnar's avatar
Ingo Molnar committed
351 352 353 354 355 356 357 358 359 360 361 362
	lockdep_stats_debug_show(m);
	seq_printf(m, " debug_locks:                   %11u\n",
			debug_locks);

	return 0;
}

static int lockdep_stats_open(struct inode *inode, struct file *file)
{
	return single_open(file, lockdep_stats_show, NULL);
}

363
static const struct file_operations proc_lockdep_stats_operations = {
Ingo Molnar's avatar
Ingo Molnar committed
364 365 366
	.open		= lockdep_stats_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
367
	.release	= single_release,
Ingo Molnar's avatar
Ingo Molnar committed
368 369
};

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
#ifdef CONFIG_LOCK_STAT

struct lock_stat_data {
	struct lock_class *class;
	struct lock_class_stats stats;
};

struct lock_stat_seq {
	struct lock_stat_data *iter_end;
	struct lock_stat_data stats[MAX_LOCKDEP_KEYS];
};

/*
 * sort on absolute number of contentions
 */
static int lock_stat_cmp(const void *l, const void *r)
{
	const struct lock_stat_data *dl = l, *dr = r;
	unsigned long nl, nr;

	nl = dl->stats.read_waittime.nr + dl->stats.write_waittime.nr;
	nr = dr->stats.read_waittime.nr + dr->stats.write_waittime.nr;

	return nr - nl;
}

static void seq_line(struct seq_file *m, char c, int offset, int length)
{
	int i;

	for (i = 0; i < offset; i++)
		seq_puts(m, " ");
	for (i = 0; i < length; i++)
		seq_printf(m, "%c", c);
	seq_puts(m, "\n");
}

static void snprint_time(char *buf, size_t bufsiz, s64 nr)
{
409 410
	s64 div;
	s32 rem;
411

412
	nr += 5; /* for display rounding */
413 414
	div = div_s64_rem(nr, 1000, &rem);
	snprintf(buf, bufsiz, "%lld.%02d", (long long)div, (int)rem/10);
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
}

static void seq_time(struct seq_file *m, s64 time)
{
	char num[15];

	snprint_time(num, sizeof(num), time);
	seq_printf(m, " %14s", num);
}

static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
{
	seq_printf(m, "%14lu", lt->nr);
	seq_time(m, lt->min);
	seq_time(m, lt->max);
	seq_time(m, lt->total);
431
	seq_time(m, lt->nr ? div_s64(lt->total, lt->nr) : 0);
432 433 434 435
}

static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
{
436
	struct lockdep_subclass_key *ckey;
437
	struct lock_class_stats *stats;
438 439
	struct lock_class *class;
	const char *cname;
440
	int i, namelen;
441
	char name[39];
442 443 444 445

	class = data->class;
	stats = &data->stats;

446 447 448 449 450 451
	namelen = 38;
	if (class->name_version > 1)
		namelen -= 2; /* XXX truncates versions > 9 */
	if (class->subclass)
		namelen -= 2;

452 453 454 455 456 457 458 459 460
	rcu_read_lock_sched();
	cname = rcu_dereference_sched(class->name);
	ckey  = rcu_dereference_sched(class->key);

	if (!cname && !ckey) {
		rcu_read_unlock_sched();
		return;

	} else if (!cname) {
461 462 463
		char str[KSYM_NAME_LEN];
		const char *key_name;

464
		key_name = __get_key_name(ckey, str);
465 466
		snprintf(name, namelen, "%s", key_name);
	} else {
467
		snprintf(name, namelen, "%s", cname);
468
	}
469 470
	rcu_read_unlock_sched();

471
	namelen = strlen(name);
472 473 474 475 476 477 478 479
	if (class->name_version > 1) {
		snprintf(name+namelen, 3, "#%d", class->name_version);
		namelen += 2;
	}
	if (class->subclass) {
		snprintf(name+namelen, 3, "/%d", class->subclass);
		namelen += 2;
	}
480 481 482 483 484 485 486

	if (stats->write_holdtime.nr) {
		if (stats->read_holdtime.nr)
			seq_printf(m, "%38s-W:", name);
		else
			seq_printf(m, "%40s:", name);

487
		seq_printf(m, "%14lu ", stats->bounces[bounce_contended_write]);
488
		seq_lock_time(m, &stats->write_waittime);
489
		seq_printf(m, " %14lu ", stats->bounces[bounce_acquired_write]);
490 491 492 493 494 495
		seq_lock_time(m, &stats->write_holdtime);
		seq_puts(m, "\n");
	}

	if (stats->read_holdtime.nr) {
		seq_printf(m, "%38s-R:", name);
496
		seq_printf(m, "%14lu ", stats->bounces[bounce_contended_read]);
497
		seq_lock_time(m, &stats->read_waittime);
498
		seq_printf(m, " %14lu ", stats->bounces[bounce_acquired_read]);
499 500 501 502 503 504 505 506 507 508
		seq_lock_time(m, &stats->read_holdtime);
		seq_puts(m, "\n");
	}

	if (stats->read_waittime.nr + stats->write_waittime.nr == 0)
		return;

	if (stats->read_holdtime.nr)
		namelen += 2;

Peter Zijlstra's avatar
Peter Zijlstra committed
509
	for (i = 0; i < LOCKSTAT_POINTS; i++) {
510 511 512 513 514 515 516 517 518 519
		char ip[32];

		if (class->contention_point[i] == 0)
			break;

		if (!i)
			seq_line(m, '-', 40-namelen, namelen);

		snprintf(ip, sizeof(ip), "[<%p>]",
				(void *)class->contention_point[i]);
520 521 522
		seq_printf(m, "%40s %14lu %29s %pS\n",
			   name, stats->contention_point[i],
			   ip, (void *)class->contention_point[i]);
523
	}
Peter Zijlstra's avatar
Peter Zijlstra committed
524 525 526 527 528 529 530 531 532 533 534
	for (i = 0; i < LOCKSTAT_POINTS; i++) {
		char ip[32];

		if (class->contending_point[i] == 0)
			break;

		if (!i)
			seq_line(m, '-', 40-namelen, namelen);

		snprintf(ip, sizeof(ip), "[<%p>]",
				(void *)class->contending_point[i]);
535 536 537
		seq_printf(m, "%40s %14lu %29s %pS\n",
			   name, stats->contending_point[i],
			   ip, (void *)class->contending_point[i]);
Peter Zijlstra's avatar
Peter Zijlstra committed
538
	}
539 540
	if (i) {
		seq_puts(m, "\n");
541
		seq_line(m, '.', 0, 40 + 1 + 12 * (14 + 1));
542 543 544 545 546 547
		seq_puts(m, "\n");
	}
}

static void seq_header(struct seq_file *m)
{
548
	seq_puts(m, "lock_stat version 0.4\n");
549 550 551 552

	if (unlikely(!debug_locks))
		seq_printf(m, "*WARNING* lock debugging disabled!! - possibly due to a lockdep warning\n");

553 554
	seq_line(m, '-', 0, 40 + 1 + 12 * (14 + 1));
	seq_printf(m, "%40s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s "
555
			"%14s %14s\n",
556
			"class name",
557
			"con-bounces",
558 559 560 561
			"contentions",
			"waittime-min",
			"waittime-max",
			"waittime-total",
562
			"waittime-avg",
563
			"acq-bounces",
564 565 566
			"acquisitions",
			"holdtime-min",
			"holdtime-max",
567 568 569
			"holdtime-total",
			"holdtime-avg");
	seq_line(m, '-', 0, 40 + 1 + 12 * (14 + 1));
570 571 572 573 574 575
	seq_printf(m, "\n");
}

static void *ls_start(struct seq_file *m, loff_t *pos)
{
	struct lock_stat_seq *data = m->private;
576
	struct lock_stat_data *iter;
577

578 579
	if (*pos == 0)
		return SEQ_START_TOKEN;
580

581 582 583
	iter = data->stats + (*pos - 1);
	if (iter >= data->iter_end)
		iter = NULL;
Peter Zijlstra's avatar
Peter Zijlstra committed
584

585
	return iter;
586 587 588 589 590
}

static void *ls_next(struct seq_file *m, void *v, loff_t *pos)
{
	(*pos)++;
591
	return ls_start(m, pos);
592 593 594 595 596 597 598 599
}

static void ls_stop(struct seq_file *m, void *v)
{
}

static int ls_show(struct seq_file *m, void *v)
{
600 601 602 603
	if (v == SEQ_START_TOKEN)
		seq_header(m);
	else
		seq_stats(m, v);
604 605 606 607

	return 0;
}

608
static const struct seq_operations lockstat_ops = {
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
	.start	= ls_start,
	.next	= ls_next,
	.stop	= ls_stop,
	.show	= ls_show,
};

static int lock_stat_open(struct inode *inode, struct file *file)
{
	int res;
	struct lock_class *class;
	struct lock_stat_seq *data = vmalloc(sizeof(struct lock_stat_seq));

	if (!data)
		return -ENOMEM;

	res = seq_open(file, &lockstat_ops);
	if (!res) {
		struct lock_stat_data *iter = data->stats;
		struct seq_file *m = file->private_data;

		list_for_each_entry(class, &all_lock_classes, lock_entry) {
			iter->class = class;
			iter->stats = lock_stats(class);
			iter++;
		}
		data->iter_end = iter;

636
		sort(data->stats, data->iter_end - data->stats,
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
				sizeof(struct lock_stat_data),
				lock_stat_cmp, NULL);

		m->private = data;
	} else
		vfree(data);

	return res;
}

static ssize_t lock_stat_write(struct file *file, const char __user *buf,
			       size_t count, loff_t *ppos)
{
	struct lock_class *class;
	char c;

	if (count) {
		if (get_user(c, buf))
			return -EFAULT;

		if (c != '0')
			return count;

		list_for_each_entry(class, &all_lock_classes, lock_entry)
			clear_lock_stats(class);
	}
	return count;
}

static int lock_stat_release(struct inode *inode, struct file *file)
{
	struct seq_file *seq = file->private_data;

	vfree(seq->private);
	return seq_release(inode, file);
}

static const struct file_operations proc_lock_stat_operations = {
	.open		= lock_stat_open,
	.write		= lock_stat_write,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= lock_stat_release,
};
#endif /* CONFIG_LOCK_STAT */

Ingo Molnar's avatar
Ingo Molnar committed
683 684
static int __init lockdep_proc_init(void)
{
685
	proc_create("lockdep", S_IRUSR, NULL, &proc_lockdep_operations);
686
#ifdef CONFIG_PROVE_LOCKING
687 688
	proc_create("lockdep_chains", S_IRUSR, NULL,
		    &proc_lockdep_chains_operations);
689
#endif
690 691
	proc_create("lockdep_stats", S_IRUSR, NULL,
		    &proc_lockdep_stats_operations);
Ingo Molnar's avatar
Ingo Molnar committed
692

693
#ifdef CONFIG_LOCK_STAT
Li Zefan's avatar
Li Zefan committed
694 695
	proc_create("lock_stat", S_IRUSR | S_IWUSR, NULL,
		    &proc_lock_stat_operations);
696 697
#endif

Ingo Molnar's avatar
Ingo Molnar committed
698 699 700 701 702
	return 0;
}

__initcall(lockdep_proc_init);