| .. | .. |
|---|
| 24 | 24 | |
|---|
| 25 | 25 | #include "lockdep_internals.h" |
|---|
| 26 | 26 | |
|---|
| 27 | +/* |
|---|
| 28 | + * Since iteration of lock_classes is done without holding the lockdep lock, |
|---|
| 29 | + * it is not safe to iterate all_lock_classes list directly as the iteration |
|---|
| 30 | + * may branch off to free_lock_classes or the zapped list. Iteration is done |
|---|
| 31 | + * directly on the lock_classes array by checking the lock_classes_in_use |
|---|
| 32 | + * bitmap and max_lock_class_idx. |
|---|
| 33 | + */ |
|---|
| 34 | +#define iterate_lock_classes(idx, class) \ |
|---|
| 35 | + for (idx = 0, class = lock_classes; idx <= max_lock_class_idx; \ |
|---|
| 36 | + idx++, class++) |
|---|
| 37 | + |
|---|
| 27 | 38 | static void *l_next(struct seq_file *m, void *v, loff_t *pos) |
|---|
| 28 | 39 | { |
|---|
| 29 | | - return seq_list_next(v, &all_lock_classes, pos); |
|---|
| 40 | + struct lock_class *class = v; |
|---|
| 41 | + |
|---|
| 42 | + ++class; |
|---|
| 43 | + *pos = class - lock_classes; |
|---|
| 44 | + return (*pos > max_lock_class_idx) ? NULL : class; |
|---|
| 30 | 45 | } |
|---|
| 31 | 46 | |
|---|
| 32 | 47 | static void *l_start(struct seq_file *m, loff_t *pos) |
|---|
| 33 | 48 | { |
|---|
| 34 | | - return seq_list_start_head(&all_lock_classes, *pos); |
|---|
| 49 | + unsigned long idx = *pos; |
|---|
| 50 | + |
|---|
| 51 | + if (idx > max_lock_class_idx) |
|---|
| 52 | + return NULL; |
|---|
| 53 | + return lock_classes + idx; |
|---|
| 35 | 54 | } |
|---|
| 36 | 55 | |
|---|
| 37 | 56 | static void l_stop(struct seq_file *m, void *v) |
|---|
| .. | .. |
|---|
| 57 | 76 | |
|---|
| 58 | 77 | static int l_show(struct seq_file *m, void *v) |
|---|
| 59 | 78 | { |
|---|
| 60 | | - struct lock_class *class = list_entry(v, struct lock_class, lock_entry); |
|---|
| 79 | + struct lock_class *class = v; |
|---|
| 61 | 80 | struct lock_list *entry; |
|---|
| 62 | 81 | char usage[LOCK_USAGE_CHARS]; |
|---|
| 82 | + int idx = class - lock_classes; |
|---|
| 63 | 83 | |
|---|
| 64 | | - if (v == &all_lock_classes) { |
|---|
| 84 | + if (v == lock_classes) |
|---|
| 65 | 85 | seq_printf(m, "all lock classes:\n"); |
|---|
| 86 | + |
|---|
| 87 | + if (!test_bit(idx, lock_classes_in_use)) |
|---|
| 66 | 88 | return 0; |
|---|
| 67 | | - } |
|---|
| 68 | 89 | |
|---|
| 69 | 90 | seq_printf(m, "%p", class->key); |
|---|
| 70 | 91 | #ifdef CONFIG_DEBUG_LOCKDEP |
|---|
| 71 | | - seq_printf(m, " OPS:%8ld", class->ops); |
|---|
| 92 | + seq_printf(m, " OPS:%8ld", debug_class_ops_read(class)); |
|---|
| 72 | 93 | #endif |
|---|
| 73 | 94 | #ifdef CONFIG_PROVE_LOCKING |
|---|
| 74 | 95 | seq_printf(m, " FD:%5ld", lockdep_count_forward_deps(class)); |
|---|
| .. | .. |
|---|
| 104 | 125 | #ifdef CONFIG_PROVE_LOCKING |
|---|
| 105 | 126 | static void *lc_start(struct seq_file *m, loff_t *pos) |
|---|
| 106 | 127 | { |
|---|
| 128 | + if (*pos < 0) |
|---|
| 129 | + return NULL; |
|---|
| 130 | + |
|---|
| 107 | 131 | if (*pos == 0) |
|---|
| 108 | 132 | return SEQ_START_TOKEN; |
|---|
| 109 | 133 | |
|---|
| 110 | | - if (*pos - 1 < nr_lock_chains) |
|---|
| 111 | | - return lock_chains + (*pos - 1); |
|---|
| 112 | | - |
|---|
| 113 | | - return NULL; |
|---|
| 134 | + return lock_chains + (*pos - 1); |
|---|
| 114 | 135 | } |
|---|
| 115 | 136 | |
|---|
| 116 | 137 | static void *lc_next(struct seq_file *m, void *v, loff_t *pos) |
|---|
| 117 | 138 | { |
|---|
| 118 | | - (*pos)++; |
|---|
| 139 | + *pos = lockdep_next_lockchain(*pos - 1) + 1; |
|---|
| 119 | 140 | return lc_start(m, pos); |
|---|
| 120 | 141 | } |
|---|
| 121 | 142 | |
|---|
| .. | .. |
|---|
| 128 | 149 | struct lock_chain *chain = v; |
|---|
| 129 | 150 | struct lock_class *class; |
|---|
| 130 | 151 | int i; |
|---|
| 152 | + static const char * const irq_strs[] = { |
|---|
| 153 | + [0] = "0", |
|---|
| 154 | + [LOCK_CHAIN_HARDIRQ_CONTEXT] = "hardirq", |
|---|
| 155 | + [LOCK_CHAIN_SOFTIRQ_CONTEXT] = "softirq", |
|---|
| 156 | + [LOCK_CHAIN_SOFTIRQ_CONTEXT| |
|---|
| 157 | + LOCK_CHAIN_HARDIRQ_CONTEXT] = "hardirq|softirq", |
|---|
| 158 | + }; |
|---|
| 131 | 159 | |
|---|
| 132 | 160 | if (v == SEQ_START_TOKEN) { |
|---|
| 133 | | - if (nr_chain_hlocks > MAX_LOCKDEP_CHAIN_HLOCKS) |
|---|
| 161 | + if (!nr_free_chain_hlocks) |
|---|
| 134 | 162 | seq_printf(m, "(buggered) "); |
|---|
| 135 | 163 | seq_printf(m, "all lock chains:\n"); |
|---|
| 136 | 164 | return 0; |
|---|
| 137 | 165 | } |
|---|
| 138 | 166 | |
|---|
| 139 | | - seq_printf(m, "irq_context: %d\n", chain->irq_context); |
|---|
| 167 | + seq_printf(m, "irq_context: %s\n", irq_strs[chain->irq_context]); |
|---|
| 140 | 168 | |
|---|
| 141 | 169 | for (i = 0; i < chain->depth; i++) { |
|---|
| 142 | 170 | class = lock_chain_get_class(chain, i); |
|---|
| .. | .. |
|---|
| 211 | 239 | |
|---|
| 212 | 240 | #ifdef CONFIG_PROVE_LOCKING |
|---|
| 213 | 241 | struct lock_class *class; |
|---|
| 242 | + unsigned long idx; |
|---|
| 214 | 243 | |
|---|
| 215 | | - list_for_each_entry(class, &all_lock_classes, lock_entry) { |
|---|
| 244 | + iterate_lock_classes(idx, class) { |
|---|
| 245 | + if (!test_bit(idx, lock_classes_in_use)) |
|---|
| 246 | + continue; |
|---|
| 216 | 247 | |
|---|
| 217 | 248 | if (class->usage_mask == 0) |
|---|
| 218 | 249 | nr_unused++; |
|---|
| .. | .. |
|---|
| 245 | 276 | |
|---|
| 246 | 277 | sum_forward_deps += lockdep_count_forward_deps(class); |
|---|
| 247 | 278 | } |
|---|
| 279 | + |
|---|
| 248 | 280 | #ifdef CONFIG_DEBUG_LOCKDEP |
|---|
| 249 | 281 | DEBUG_LOCKS_WARN_ON(debug_atomic_read(nr_unused_locks) != nr_unused); |
|---|
| 250 | 282 | #endif |
|---|
| .. | .. |
|---|
| 270 | 302 | |
|---|
| 271 | 303 | #ifdef CONFIG_PROVE_LOCKING |
|---|
| 272 | 304 | seq_printf(m, " dependency chains: %11lu [max: %lu]\n", |
|---|
| 273 | | - nr_lock_chains, MAX_LOCKDEP_CHAINS); |
|---|
| 274 | | - seq_printf(m, " dependency chain hlocks: %11d [max: %lu]\n", |
|---|
| 275 | | - nr_chain_hlocks, MAX_LOCKDEP_CHAIN_HLOCKS); |
|---|
| 305 | + lock_chain_count(), MAX_LOCKDEP_CHAINS); |
|---|
| 306 | + seq_printf(m, " dependency chain hlocks used: %11lu [max: %lu]\n", |
|---|
| 307 | + MAX_LOCKDEP_CHAIN_HLOCKS - |
|---|
| 308 | + (nr_free_chain_hlocks + nr_lost_chain_hlocks), |
|---|
| 309 | + MAX_LOCKDEP_CHAIN_HLOCKS); |
|---|
| 310 | + seq_printf(m, " dependency chain hlocks lost: %11u\n", |
|---|
| 311 | + nr_lost_chain_hlocks); |
|---|
| 276 | 312 | #endif |
|---|
| 277 | 313 | |
|---|
| 278 | 314 | #ifdef CONFIG_TRACE_IRQFLAGS |
|---|
| .. | .. |
|---|
| 285 | 321 | nr_process_chains); |
|---|
| 286 | 322 | seq_printf(m, " stack-trace entries: %11lu [max: %lu]\n", |
|---|
| 287 | 323 | nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES); |
|---|
| 324 | +#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) |
|---|
| 325 | + seq_printf(m, " number of stack traces: %11llu\n", |
|---|
| 326 | + lockdep_stack_trace_count()); |
|---|
| 327 | + seq_printf(m, " number of stack hash chains: %11llu\n", |
|---|
| 328 | + lockdep_stack_hash_count()); |
|---|
| 329 | +#endif |
|---|
| 288 | 330 | seq_printf(m, " combined max dependencies: %11u\n", |
|---|
| 289 | 331 | (nr_hardirq_chains + 1) * |
|---|
| 290 | 332 | (nr_softirq_chains + 1) * |
|---|
| .. | .. |
|---|
| 326 | 368 | seq_printf(m, " max bfs queue depth: %11u\n", |
|---|
| 327 | 369 | max_bfs_queue_depth); |
|---|
| 328 | 370 | #endif |
|---|
| 371 | + seq_printf(m, " max lock class index: %11lu\n", |
|---|
| 372 | + max_lock_class_idx); |
|---|
| 329 | 373 | lockdep_stats_debug_show(m); |
|---|
| 330 | 374 | seq_printf(m, " debug_locks: %11u\n", |
|---|
| 331 | 375 | debug_locks); |
|---|
| 332 | 376 | |
|---|
| 377 | + /* |
|---|
| 378 | + * Zappped classes and lockdep data buffers reuse statistics. |
|---|
| 379 | + */ |
|---|
| 380 | + seq_puts(m, "\n"); |
|---|
| 381 | + seq_printf(m, " zapped classes: %11lu\n", |
|---|
| 382 | + nr_zapped_classes); |
|---|
| 383 | +#ifdef CONFIG_PROVE_LOCKING |
|---|
| 384 | + seq_printf(m, " zapped lock chains: %11lu\n", |
|---|
| 385 | + nr_zapped_lock_chains); |
|---|
| 386 | + seq_printf(m, " large chain blocks: %11u\n", |
|---|
| 387 | + nr_large_chain_blocks); |
|---|
| 388 | +#endif |
|---|
| 333 | 389 | return 0; |
|---|
| 334 | 390 | } |
|---|
| 335 | 391 | |
|---|
| .. | .. |
|---|
| 399 | 455 | |
|---|
| 400 | 456 | static void seq_stats(struct seq_file *m, struct lock_stat_data *data) |
|---|
| 401 | 457 | { |
|---|
| 402 | | - struct lockdep_subclass_key *ckey; |
|---|
| 458 | + const struct lockdep_subclass_key *ckey; |
|---|
| 403 | 459 | struct lock_class_stats *stats; |
|---|
| 404 | 460 | struct lock_class *class; |
|---|
| 405 | 461 | const char *cname; |
|---|
| .. | .. |
|---|
| 591 | 647 | if (!res) { |
|---|
| 592 | 648 | struct lock_stat_data *iter = data->stats; |
|---|
| 593 | 649 | struct seq_file *m = file->private_data; |
|---|
| 650 | + unsigned long idx; |
|---|
| 594 | 651 | |
|---|
| 595 | | - list_for_each_entry(class, &all_lock_classes, lock_entry) { |
|---|
| 652 | + iterate_lock_classes(idx, class) { |
|---|
| 653 | + if (!test_bit(idx, lock_classes_in_use)) |
|---|
| 654 | + continue; |
|---|
| 596 | 655 | iter->class = class; |
|---|
| 597 | 656 | iter->stats = lock_stats(class); |
|---|
| 598 | 657 | iter++; |
|---|
| 599 | 658 | } |
|---|
| 659 | + |
|---|
| 600 | 660 | data->iter_end = iter; |
|---|
| 601 | 661 | |
|---|
| 602 | 662 | sort(data->stats, data->iter_end - data->stats, |
|---|
| .. | .. |
|---|
| 614 | 674 | size_t count, loff_t *ppos) |
|---|
| 615 | 675 | { |
|---|
| 616 | 676 | struct lock_class *class; |
|---|
| 677 | + unsigned long idx; |
|---|
| 617 | 678 | char c; |
|---|
| 618 | 679 | |
|---|
| 619 | 680 | if (count) { |
|---|
| .. | .. |
|---|
| 623 | 684 | if (c != '0') |
|---|
| 624 | 685 | return count; |
|---|
| 625 | 686 | |
|---|
| 626 | | - list_for_each_entry(class, &all_lock_classes, lock_entry) |
|---|
| 687 | + iterate_lock_classes(idx, class) { |
|---|
| 688 | + if (!test_bit(idx, lock_classes_in_use)) |
|---|
| 689 | + continue; |
|---|
| 627 | 690 | clear_lock_stats(class); |
|---|
| 691 | + } |
|---|
| 628 | 692 | } |
|---|
| 629 | 693 | return count; |
|---|
| 630 | 694 | } |
|---|
| .. | .. |
|---|
| 637 | 701 | return seq_release(inode, file); |
|---|
| 638 | 702 | } |
|---|
| 639 | 703 | |
|---|
| 640 | | -static const struct file_operations proc_lock_stat_operations = { |
|---|
| 641 | | - .open = lock_stat_open, |
|---|
| 642 | | - .write = lock_stat_write, |
|---|
| 643 | | - .read = seq_read, |
|---|
| 644 | | - .llseek = seq_lseek, |
|---|
| 645 | | - .release = lock_stat_release, |
|---|
| 704 | +static const struct proc_ops lock_stat_proc_ops = { |
|---|
| 705 | + .proc_open = lock_stat_open, |
|---|
| 706 | + .proc_write = lock_stat_write, |
|---|
| 707 | + .proc_read = seq_read, |
|---|
| 708 | + .proc_lseek = seq_lseek, |
|---|
| 709 | + .proc_release = lock_stat_release, |
|---|
| 646 | 710 | }; |
|---|
| 647 | 711 | #endif /* CONFIG_LOCK_STAT */ |
|---|
| 648 | 712 | |
|---|
| .. | .. |
|---|
| 654 | 718 | #endif |
|---|
| 655 | 719 | proc_create_single("lockdep_stats", S_IRUSR, NULL, lockdep_stats_show); |
|---|
| 656 | 720 | #ifdef CONFIG_LOCK_STAT |
|---|
| 657 | | - proc_create("lock_stat", S_IRUSR | S_IWUSR, NULL, |
|---|
| 658 | | - &proc_lock_stat_operations); |
|---|
| 721 | + proc_create("lock_stat", S_IRUSR | S_IWUSR, NULL, &lock_stat_proc_ops); |
|---|
| 659 | 722 | #endif |
|---|
| 660 | 723 | |
|---|
| 661 | 724 | return 0; |
|---|