diff options
Diffstat (limited to 'kernel/lockdep.c')
-rw-r--r-- | kernel/lockdep.c | 84 |
1 files changed, 46 insertions, 38 deletions
diff --git a/kernel/lockdep.c b/kernel/lockdep.c index e9c759f06c1d..ec21304856d1 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c | |||
@@ -431,20 +431,7 @@ static struct stack_trace lockdep_init_trace = { | |||
431 | /* | 431 | /* |
432 | * Various lockdep statistics: | 432 | * Various lockdep statistics: |
433 | */ | 433 | */ |
434 | atomic_t chain_lookup_hits; | 434 | DEFINE_PER_CPU(struct lockdep_stats, lockdep_stats); |
435 | atomic_t chain_lookup_misses; | ||
436 | atomic_t hardirqs_on_events; | ||
437 | atomic_t hardirqs_off_events; | ||
438 | atomic_t redundant_hardirqs_on; | ||
439 | atomic_t redundant_hardirqs_off; | ||
440 | atomic_t softirqs_on_events; | ||
441 | atomic_t softirqs_off_events; | ||
442 | atomic_t redundant_softirqs_on; | ||
443 | atomic_t redundant_softirqs_off; | ||
444 | atomic_t nr_unused_locks; | ||
445 | atomic_t nr_cyclic_checks; | ||
446 | atomic_t nr_find_usage_forwards_checks; | ||
447 | atomic_t nr_find_usage_backwards_checks; | ||
448 | #endif | 435 | #endif |
449 | 436 | ||
450 | /* | 437 | /* |
@@ -748,7 +735,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) | |||
748 | return NULL; | 735 | return NULL; |
749 | } | 736 | } |
750 | class = lock_classes + nr_lock_classes++; | 737 | class = lock_classes + nr_lock_classes++; |
751 | debug_atomic_inc(&nr_unused_locks); | 738 | debug_atomic_inc(nr_unused_locks); |
752 | class->key = key; | 739 | class->key = key; |
753 | class->name = lock->name; | 740 | class->name = lock->name; |
754 | class->subclass = subclass; | 741 | class->subclass = subclass; |
@@ -818,7 +805,8 @@ static struct lock_list *alloc_list_entry(void) | |||
818 | * Add a new dependency to the head of the list: | 805 | * Add a new dependency to the head of the list: |
819 | */ | 806 | */ |
820 | static int add_lock_to_list(struct lock_class *class, struct lock_class *this, | 807 | static int add_lock_to_list(struct lock_class *class, struct lock_class *this, |
821 | struct list_head *head, unsigned long ip, int distance) | 808 | struct list_head *head, unsigned long ip, |
809 | int distance, struct stack_trace *trace) | ||
822 | { | 810 | { |
823 | struct lock_list *entry; | 811 | struct lock_list *entry; |
824 | /* | 812 | /* |
@@ -829,11 +817,9 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this, | |||
829 | if (!entry) | 817 | if (!entry) |
830 | return 0; | 818 | return 0; |
831 | 819 | ||
832 | if (!save_trace(&entry->trace)) | ||
833 | return 0; | ||
834 | |||
835 | entry->class = this; | 820 | entry->class = this; |
836 | entry->distance = distance; | 821 | entry->distance = distance; |
822 | entry->trace = *trace; | ||
837 | /* | 823 | /* |
838 | * Since we never remove from the dependency list, the list can | 824 | * Since we never remove from the dependency list, the list can |
839 | * be walked lockless by other CPUs, it's only allocation | 825 | * be walked lockless by other CPUs, it's only allocation |
@@ -1205,7 +1191,7 @@ check_noncircular(struct lock_list *root, struct lock_class *target, | |||
1205 | { | 1191 | { |
1206 | int result; | 1192 | int result; |
1207 | 1193 | ||
1208 | debug_atomic_inc(&nr_cyclic_checks); | 1194 | debug_atomic_inc(nr_cyclic_checks); |
1209 | 1195 | ||
1210 | result = __bfs_forwards(root, target, class_equal, target_entry); | 1196 | result = __bfs_forwards(root, target, class_equal, target_entry); |
1211 | 1197 | ||
@@ -1242,7 +1228,7 @@ find_usage_forwards(struct lock_list *root, enum lock_usage_bit bit, | |||
1242 | { | 1228 | { |
1243 | int result; | 1229 | int result; |
1244 | 1230 | ||
1245 | debug_atomic_inc(&nr_find_usage_forwards_checks); | 1231 | debug_atomic_inc(nr_find_usage_forwards_checks); |
1246 | 1232 | ||
1247 | result = __bfs_forwards(root, (void *)bit, usage_match, target_entry); | 1233 | result = __bfs_forwards(root, (void *)bit, usage_match, target_entry); |
1248 | 1234 | ||
@@ -1265,7 +1251,7 @@ find_usage_backwards(struct lock_list *root, enum lock_usage_bit bit, | |||
1265 | { | 1251 | { |
1266 | int result; | 1252 | int result; |
1267 | 1253 | ||
1268 | debug_atomic_inc(&nr_find_usage_backwards_checks); | 1254 | debug_atomic_inc(nr_find_usage_backwards_checks); |
1269 | 1255 | ||
1270 | result = __bfs_backwards(root, (void *)bit, usage_match, target_entry); | 1256 | result = __bfs_backwards(root, (void *)bit, usage_match, target_entry); |
1271 | 1257 | ||
@@ -1635,12 +1621,20 @@ check_deadlock(struct task_struct *curr, struct held_lock *next, | |||
1635 | */ | 1621 | */ |
1636 | static int | 1622 | static int |
1637 | check_prev_add(struct task_struct *curr, struct held_lock *prev, | 1623 | check_prev_add(struct task_struct *curr, struct held_lock *prev, |
1638 | struct held_lock *next, int distance) | 1624 | struct held_lock *next, int distance, int trylock_loop) |
1639 | { | 1625 | { |
1640 | struct lock_list *entry; | 1626 | struct lock_list *entry; |
1641 | int ret; | 1627 | int ret; |
1642 | struct lock_list this; | 1628 | struct lock_list this; |
1643 | struct lock_list *uninitialized_var(target_entry); | 1629 | struct lock_list *uninitialized_var(target_entry); |
1630 | /* | ||
1631 | * Static variable, serialized by the graph_lock(). | ||
1632 | * | ||
1633 | * We use this static variable to save the stack trace in case | ||
1634 | * we call into this function multiple times due to encountering | ||
1635 | * trylocks in the held lock stack. | ||
1636 | */ | ||
1637 | static struct stack_trace trace; | ||
1644 | 1638 | ||
1645 | /* | 1639 | /* |
1646 | * Prove that the new <prev> -> <next> dependency would not | 1640 | * Prove that the new <prev> -> <next> dependency would not |
@@ -1688,20 +1682,23 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, | |||
1688 | } | 1682 | } |
1689 | } | 1683 | } |
1690 | 1684 | ||
1685 | if (!trylock_loop && !save_trace(&trace)) | ||
1686 | return 0; | ||
1687 | |||
1691 | /* | 1688 | /* |
1692 | * Ok, all validations passed, add the new lock | 1689 | * Ok, all validations passed, add the new lock |
1693 | * to the previous lock's dependency list: | 1690 | * to the previous lock's dependency list: |
1694 | */ | 1691 | */ |
1695 | ret = add_lock_to_list(hlock_class(prev), hlock_class(next), | 1692 | ret = add_lock_to_list(hlock_class(prev), hlock_class(next), |
1696 | &hlock_class(prev)->locks_after, | 1693 | &hlock_class(prev)->locks_after, |
1697 | next->acquire_ip, distance); | 1694 | next->acquire_ip, distance, &trace); |
1698 | 1695 | ||
1699 | if (!ret) | 1696 | if (!ret) |
1700 | return 0; | 1697 | return 0; |
1701 | 1698 | ||
1702 | ret = add_lock_to_list(hlock_class(next), hlock_class(prev), | 1699 | ret = add_lock_to_list(hlock_class(next), hlock_class(prev), |
1703 | &hlock_class(next)->locks_before, | 1700 | &hlock_class(next)->locks_before, |
1704 | next->acquire_ip, distance); | 1701 | next->acquire_ip, distance, &trace); |
1705 | if (!ret) | 1702 | if (!ret) |
1706 | return 0; | 1703 | return 0; |
1707 | 1704 | ||
@@ -1731,6 +1728,7 @@ static int | |||
1731 | check_prevs_add(struct task_struct *curr, struct held_lock *next) | 1728 | check_prevs_add(struct task_struct *curr, struct held_lock *next) |
1732 | { | 1729 | { |
1733 | int depth = curr->lockdep_depth; | 1730 | int depth = curr->lockdep_depth; |
1731 | int trylock_loop = 0; | ||
1734 | struct held_lock *hlock; | 1732 | struct held_lock *hlock; |
1735 | 1733 | ||
1736 | /* | 1734 | /* |
@@ -1756,7 +1754,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next) | |||
1756 | * added: | 1754 | * added: |
1757 | */ | 1755 | */ |
1758 | if (hlock->read != 2) { | 1756 | if (hlock->read != 2) { |
1759 | if (!check_prev_add(curr, hlock, next, distance)) | 1757 | if (!check_prev_add(curr, hlock, next, |
1758 | distance, trylock_loop)) | ||
1760 | return 0; | 1759 | return 0; |
1761 | /* | 1760 | /* |
1762 | * Stop after the first non-trylock entry, | 1761 | * Stop after the first non-trylock entry, |
@@ -1779,6 +1778,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next) | |||
1779 | if (curr->held_locks[depth].irq_context != | 1778 | if (curr->held_locks[depth].irq_context != |
1780 | curr->held_locks[depth-1].irq_context) | 1779 | curr->held_locks[depth-1].irq_context) |
1781 | break; | 1780 | break; |
1781 | trylock_loop = 1; | ||
1782 | } | 1782 | } |
1783 | return 1; | 1783 | return 1; |
1784 | out_bug: | 1784 | out_bug: |
@@ -1825,7 +1825,7 @@ static inline int lookup_chain_cache(struct task_struct *curr, | |||
1825 | list_for_each_entry(chain, hash_head, entry) { | 1825 | list_for_each_entry(chain, hash_head, entry) { |
1826 | if (chain->chain_key == chain_key) { | 1826 | if (chain->chain_key == chain_key) { |
1827 | cache_hit: | 1827 | cache_hit: |
1828 | debug_atomic_inc(&chain_lookup_hits); | 1828 | debug_atomic_inc(chain_lookup_hits); |
1829 | if (very_verbose(class)) | 1829 | if (very_verbose(class)) |
1830 | printk("\nhash chain already cached, key: " | 1830 | printk("\nhash chain already cached, key: " |
1831 | "%016Lx tail class: [%p] %s\n", | 1831 | "%016Lx tail class: [%p] %s\n", |
@@ -1890,7 +1890,7 @@ cache_hit: | |||
1890 | chain_hlocks[chain->base + j] = class - lock_classes; | 1890 | chain_hlocks[chain->base + j] = class - lock_classes; |
1891 | } | 1891 | } |
1892 | list_add_tail_rcu(&chain->entry, hash_head); | 1892 | list_add_tail_rcu(&chain->entry, hash_head); |
1893 | debug_atomic_inc(&chain_lookup_misses); | 1893 | debug_atomic_inc(chain_lookup_misses); |
1894 | inc_chains(); | 1894 | inc_chains(); |
1895 | 1895 | ||
1896 | return 1; | 1896 | return 1; |
@@ -2311,7 +2311,12 @@ void trace_hardirqs_on_caller(unsigned long ip) | |||
2311 | return; | 2311 | return; |
2312 | 2312 | ||
2313 | if (unlikely(curr->hardirqs_enabled)) { | 2313 | if (unlikely(curr->hardirqs_enabled)) { |
2314 | debug_atomic_inc(&redundant_hardirqs_on); | 2314 | /* |
2315 | * Neither irq nor preemption are disabled here | ||
2316 | * so this is racy by nature but loosing one hit | ||
2317 | * in a stat is not a big deal. | ||
2318 | */ | ||
2319 | __debug_atomic_inc(redundant_hardirqs_on); | ||
2315 | return; | 2320 | return; |
2316 | } | 2321 | } |
2317 | /* we'll do an OFF -> ON transition: */ | 2322 | /* we'll do an OFF -> ON transition: */ |
@@ -2338,7 +2343,7 @@ void trace_hardirqs_on_caller(unsigned long ip) | |||
2338 | 2343 | ||
2339 | curr->hardirq_enable_ip = ip; | 2344 | curr->hardirq_enable_ip = ip; |
2340 | curr->hardirq_enable_event = ++curr->irq_events; | 2345 | curr->hardirq_enable_event = ++curr->irq_events; |
2341 | debug_atomic_inc(&hardirqs_on_events); | 2346 | debug_atomic_inc(hardirqs_on_events); |
2342 | } | 2347 | } |
2343 | EXPORT_SYMBOL(trace_hardirqs_on_caller); | 2348 | EXPORT_SYMBOL(trace_hardirqs_on_caller); |
2344 | 2349 | ||
@@ -2370,9 +2375,9 @@ void trace_hardirqs_off_caller(unsigned long ip) | |||
2370 | curr->hardirqs_enabled = 0; | 2375 | curr->hardirqs_enabled = 0; |
2371 | curr->hardirq_disable_ip = ip; | 2376 | curr->hardirq_disable_ip = ip; |
2372 | curr->hardirq_disable_event = ++curr->irq_events; | 2377 | curr->hardirq_disable_event = ++curr->irq_events; |
2373 | debug_atomic_inc(&hardirqs_off_events); | 2378 | debug_atomic_inc(hardirqs_off_events); |
2374 | } else | 2379 | } else |
2375 | debug_atomic_inc(&redundant_hardirqs_off); | 2380 | debug_atomic_inc(redundant_hardirqs_off); |
2376 | } | 2381 | } |
2377 | EXPORT_SYMBOL(trace_hardirqs_off_caller); | 2382 | EXPORT_SYMBOL(trace_hardirqs_off_caller); |
2378 | 2383 | ||
@@ -2396,7 +2401,7 @@ void trace_softirqs_on(unsigned long ip) | |||
2396 | return; | 2401 | return; |
2397 | 2402 | ||
2398 | if (curr->softirqs_enabled) { | 2403 | if (curr->softirqs_enabled) { |
2399 | debug_atomic_inc(&redundant_softirqs_on); | 2404 | debug_atomic_inc(redundant_softirqs_on); |
2400 | return; | 2405 | return; |
2401 | } | 2406 | } |
2402 | 2407 | ||
@@ -2406,7 +2411,7 @@ void trace_softirqs_on(unsigned long ip) | |||
2406 | curr->softirqs_enabled = 1; | 2411 | curr->softirqs_enabled = 1; |
2407 | curr->softirq_enable_ip = ip; | 2412 | curr->softirq_enable_ip = ip; |
2408 | curr->softirq_enable_event = ++curr->irq_events; | 2413 | curr->softirq_enable_event = ++curr->irq_events; |
2409 | debug_atomic_inc(&softirqs_on_events); | 2414 | debug_atomic_inc(softirqs_on_events); |
2410 | /* | 2415 | /* |
2411 | * We are going to turn softirqs on, so set the | 2416 | * We are going to turn softirqs on, so set the |
2412 | * usage bit for all held locks, if hardirqs are | 2417 | * usage bit for all held locks, if hardirqs are |
@@ -2436,10 +2441,10 @@ void trace_softirqs_off(unsigned long ip) | |||
2436 | curr->softirqs_enabled = 0; | 2441 | curr->softirqs_enabled = 0; |
2437 | curr->softirq_disable_ip = ip; | 2442 | curr->softirq_disable_ip = ip; |
2438 | curr->softirq_disable_event = ++curr->irq_events; | 2443 | curr->softirq_disable_event = ++curr->irq_events; |
2439 | debug_atomic_inc(&softirqs_off_events); | 2444 | debug_atomic_inc(softirqs_off_events); |
2440 | DEBUG_LOCKS_WARN_ON(!softirq_count()); | 2445 | DEBUG_LOCKS_WARN_ON(!softirq_count()); |
2441 | } else | 2446 | } else |
2442 | debug_atomic_inc(&redundant_softirqs_off); | 2447 | debug_atomic_inc(redundant_softirqs_off); |
2443 | } | 2448 | } |
2444 | 2449 | ||
2445 | static void __lockdep_trace_alloc(gfp_t gfp_mask, unsigned long flags) | 2450 | static void __lockdep_trace_alloc(gfp_t gfp_mask, unsigned long flags) |
@@ -2644,7 +2649,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this, | |||
2644 | return 0; | 2649 | return 0; |
2645 | break; | 2650 | break; |
2646 | case LOCK_USED: | 2651 | case LOCK_USED: |
2647 | debug_atomic_dec(&nr_unused_locks); | 2652 | debug_atomic_dec(nr_unused_locks); |
2648 | break; | 2653 | break; |
2649 | default: | 2654 | default: |
2650 | if (!debug_locks_off_graph_unlock()) | 2655 | if (!debug_locks_off_graph_unlock()) |
@@ -2750,7 +2755,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, | |||
2750 | if (!class) | 2755 | if (!class) |
2751 | return 0; | 2756 | return 0; |
2752 | } | 2757 | } |
2753 | debug_atomic_inc((atomic_t *)&class->ops); | 2758 | atomic_inc((atomic_t *)&class->ops); |
2754 | if (very_verbose(class)) { | 2759 | if (very_verbose(class)) { |
2755 | printk("\nacquire class [%p] %s", class->key, class->name); | 2760 | printk("\nacquire class [%p] %s", class->key, class->name); |
2756 | if (class->name_version > 1) | 2761 | if (class->name_version > 1) |
@@ -3801,8 +3806,11 @@ void lockdep_rcu_dereference(const char *file, const int line) | |||
3801 | { | 3806 | { |
3802 | struct task_struct *curr = current; | 3807 | struct task_struct *curr = current; |
3803 | 3808 | ||
3809 | #ifndef CONFIG_PROVE_RCU_REPEATEDLY | ||
3804 | if (!debug_locks_off()) | 3810 | if (!debug_locks_off()) |
3805 | return; | 3811 | return; |
3812 | #endif /* #ifdef CONFIG_PROVE_RCU_REPEATEDLY */ | ||
3813 | /* Note: the following can be executed concurrently, so be careful. */ | ||
3806 | printk("\n===================================================\n"); | 3814 | printk("\n===================================================\n"); |
3807 | printk( "[ INFO: suspicious rcu_dereference_check() usage. ]\n"); | 3815 | printk( "[ INFO: suspicious rcu_dereference_check() usage. ]\n"); |
3808 | printk( "---------------------------------------------------\n"); | 3816 | printk( "---------------------------------------------------\n"); |