| @@ -1071,13 +1071,15 @@ static inline void check_data_structures(void) { } · 1070 unmodified lines |
| 1071 | 1071 | |
| 1072 | 1072 | #endif /* CONFIG_DEBUG_LOCKDEP */ |
| 1073 | 1073 | |
| 1074 | +static void init_chain_block_buckets(void); |
| 1075 | + |
| 1074 | 1076 | /* |
| 1075 | 1077 | * Initialize the lock_classes[] array elements, the free_lock_classes list |
| 1076 | 1078 | * and also the delayed_free structure. |
| 1077 | 1079 | */ |
| 1078 | 1080 | static void init_data_structures_once(void) |
| 1079 | 1081 | { |
| 1080 | | − static bool ds_initialized, rcu_head_initialized; |
| 1082 | + static bool __read_mostly ds_initialized, rcu_head_initialized; |
| 1081 | 1083 | int i; |
| 1082 | 1084 | |
| 1083 | 1085 | if (likely(rcu_head_initialized)) |
| @@ -1101,6 +1103,7 @@ static void init_data_structures_once(void) · 17 unmodified lines |
| 1101 | 1103 | INIT_LIST_HEAD(&lock_classes[i].locks_after); |
| 1102 | 1104 | INIT_LIST_HEAD(&lock_classes[i].locks_before); |
| 1103 | 1105 | } |
| 1106 | + init_chain_block_buckets(); |
| 1104 | 1107 | } |
| 1105 | 1108 | |
| 1106 | 1109 | static inline struct hlist_head *keyhashentry(const struct lock_class_key *key) |
| @@ -2627,7 +2630,233 @@ struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; · 1520 unmodified lines |
| 2627 | 2630 | static DECLARE_BITMAP(lock_chains_in_use, MAX_LOCKDEP_CHAINS); |
| 2628 | 2631 | static u16 chain_hlocks[MAX_LOCKDEP_CHAIN_HLOCKS]; |
| 2629 | 2632 | unsigned long nr_zapped_lock_chains; |
| 2630 | | −unsigned int nr_chain_hlocks; |
| 2633 | +unsigned int nr_free_chain_hlocks; /* Free chain_hlocks in buckets */ |
| 2634 | +unsigned int nr_lost_chain_hlocks; /* Lost chain_hlocks */ |
| 2635 | +unsigned int nr_large_chain_blocks; /* size > MAX_CHAIN_BUCKETS */ |
| 2636 | + |
| 2637 | +/* |
| 2638 | + * The first 2 chain_hlocks entries in the chain block in the bucket |
| 2639 | + * list contains the following meta data: |
| 2640 | + * |
| 2641 | + * entry[0]: |
| 2642 | + * Bit 15 - always set to 1 (it is not a class index) |
| 2643 | + * Bits 0-14 - upper 15 bits of the next block index |
| 2644 | + * entry[1] - lower 16 bits of next block index |
| 2645 | + * |
| 2646 | + * A next block index of all 1 bits means it is the end of the list. |
| 2647 | + * |
| 2648 | + * On the unsized bucket (bucket-0), the 3rd and 4th entries contain |
| 2649 | + * the chain block size: |
| 2650 | + * |
| 2651 | + * entry[2] - upper 16 bits of the chain block size |
| 2652 | + * entry[3] - lower 16 bits of the chain block size |
| 2653 | + */ |
| 2654 | +#define MAX_CHAIN_BUCKETS 16 |
| 2655 | +#define CHAIN_BLK_FLAG (1U << 15) |
| 2656 | +#define CHAIN_BLK_LIST_END 0xFFFFU |
| 2657 | + |
| 2658 | +static int chain_block_buckets[MAX_CHAIN_BUCKETS]; |
| 2659 | + |
| 2660 | +static inline int size_to_bucket(int size) |
| 2661 | +{ |
| 2662 | + if (size > MAX_CHAIN_BUCKETS) |
| 2663 | + return 0; |
| 2664 | + |
| 2665 | + return size - 1; |
| 2666 | +} |
| 2667 | + |
| 2668 | +/* |
| 2669 | + * Iterate all the chain blocks in a bucket. |
| 2670 | + */ |
| 2671 | +#define for_each_chain_block(bucket, prev, curr) \ |
| 2672 | + for ((prev) = -1, (curr) = chain_block_buckets[bucket]; \ |
| 2673 | + (curr) >= 0; \ |
| 2674 | + (prev) = (curr), (curr) = chain_block_next(curr)) |
| 2675 | + |
| 2676 | +/* |
| 2677 | + * next block or -1 |
| 2678 | + */ |
| 2679 | +static inline int chain_block_next(int offset) |
| 2680 | +{ |
| 2681 | + int next = chain_hlocks[offset]; |
| 2682 | + |
| 2683 | + WARN_ON_ONCE(!(next & CHAIN_BLK_FLAG)); |
| 2684 | + |
| 2685 | + if (next == CHAIN_BLK_LIST_END) |
| 2686 | + return -1; |
| 2687 | + |
| 2688 | + next &= ~CHAIN_BLK_FLAG; |
| 2689 | + next <<= 16; |
| 2690 | + next |= chain_hlocks[offset + 1]; |
| 2691 | + |
| 2692 | + return next; |
| 2693 | +} |
| 2694 | + |
| 2695 | +/* |
| 2696 | + * bucket-0 only |
| 2697 | + */ |
| 2698 | +static inline int chain_block_size(int offset) |
| 2699 | +{ |
| 2700 | + return (chain_hlocks[offset + 2] << 16) | chain_hlocks[offset + 3]; |
| 2701 | +} |
| 2702 | + |
| 2703 | +static inline void init_chain_block(int offset, int next, int bucket, int size) |
| 2704 | +{ |
| 2705 | + chain_hlocks[offset] = (next >> 16) | CHAIN_BLK_FLAG; |
| 2706 | + chain_hlocks[offset + 1] = (u16)next; |
| 2707 | + |
| 2708 | + if (size && !bucket) { |
| 2709 | + chain_hlocks[offset + 2] = size >> 16; |
| 2710 | + chain_hlocks[offset + 3] = (u16)size; |
| 2711 | + } |
| 2712 | +} |
| 2713 | + |
| 2714 | +static inline void add_chain_block(int offset, int size) |
| 2715 | +{ |
| 2716 | + int bucket = size_to_bucket(size); |
| 2717 | + int next = chain_block_buckets[bucket]; |
| 2718 | + int prev, curr; |
| 2719 | + |
| 2720 | + if (unlikely(size < 2)) { |
| 2721 | + /* |
| 2722 | + * We can't store single entries on the freelist. Leak them. |
| 2723 | + * |
| 2724 | + * One possible way out would be to uniquely mark them, other |
| 2725 | + * than with CHAIN_BLK_FLAG, such that we can recover them when |
| 2726 | + * the block before it is re-added. |
| 2727 | + */ |
| 2728 | + if (size) |
| 2729 | + nr_lost_chain_hlocks++; |
| 2730 | + return; |
| 2731 | + } |
| 2732 | + |
| 2733 | + nr_free_chain_hlocks += size; |
| 2734 | + if (!bucket) { |
| 2735 | + nr_large_chain_blocks++; |
| 2736 | + |
| 2737 | + /* |
| 2738 | + * Variable sized, sort large to small. |
| 2739 | + */ |
| 2740 | + for_each_chain_block(0, prev, curr) { |
| 2741 | + if (size >= chain_block_size(curr)) |
| 2742 | + break; |
| 2743 | + } |
| 2744 | + init_chain_block(offset, curr, 0, size); |
| 2745 | + if (prev < 0) |
| 2746 | + chain_block_buckets[0] = offset; |
| 2747 | + else |
| 2748 | + init_chain_block(prev, offset, 0, 0); |
| 2749 | + return; |
| 2750 | + } |
| 2751 | + /* |
| 2752 | + * Fixed size, add to head. |
| 2753 | + */ |
| 2754 | + init_chain_block(offset, next, bucket, size); |
| 2755 | + chain_block_buckets[bucket] = offset; |
| 2756 | +} |
| 2757 | + |
| 2758 | +/* |
| 2759 | + * Only the first block in the list can be deleted. |
| 2760 | + * |
| 2761 | + * For the variable size bucket[0], the first block (the largest one) is |
| 2762 | + * returned, broken up and put back into the pool. So if a chain block of |
| 2763 | + * length > MAX_CHAIN_BUCKETS is ever used and zapped, it will just be |
| 2764 | + * queued up after the primordial chain block and never be used until the |
| 2765 | + * hlock entries in the primordial chain block is almost used up. That |
| 2766 | + * causes fragmentation and reduce allocation efficiency. That can be |
| 2767 | + * monitored by looking at the "large chain blocks" number in lockdep_stats. |
| 2768 | + */ |
| 2769 | +static inline void del_chain_block(int bucket, int size, int next) |
| 2770 | +{ |
| 2771 | + nr_free_chain_hlocks -= size; |
| 2772 | + chain_block_buckets[bucket] = next; |
| 2773 | + |
| 2774 | + if (!bucket) |
| 2775 | + nr_large_chain_blocks--; |
| 2776 | +} |
| 2777 | + |
| 2778 | +static void init_chain_block_buckets(void) |
| 2779 | +{ |
| 2780 | + int i; |
| 2781 | + |
| 2782 | + for (i = 0; i < MAX_CHAIN_BUCKETS; i++) |
| 2783 | + chain_block_buckets[i] = -1; |
| 2784 | + |
| 2785 | + add_chain_block(0, ARRAY_SIZE(chain_hlocks)); |
| 2786 | +} |
| 2787 | + |
| 2788 | +/* |
| 2789 | + * Return offset of a chain block of the right size or -1 if not found. |
| 2790 | + * |
| 2791 | + * Fairly simple worst-fit allocator with the addition of a number of size |
| 2792 | + * specific free lists. |
| 2793 | + */ |
| 2794 | +static int alloc_chain_hlocks(int req) |
| 2795 | +{ |
| 2796 | + int bucket, curr, size; |
| 2797 | + |
| 2798 | + /* |
| 2799 | + * We rely on the MSB to act as an escape bit to denote freelist |
| 2800 | + * pointers. Make sure this bit isn't set in 'normal' class_idx usage. |
| 2801 | + */ |
| 2802 | + BUILD_BUG_ON((MAX_LOCKDEP_KEYS-1) & CHAIN_BLK_FLAG); |
| 2803 | + |
| 2804 | + init_data_structures_once(); |
| 2805 | + |
| 2806 | + if (nr_free_chain_hlocks < req) |
| 2807 | + return -1; |
| 2808 | + |
| 2809 | + /* |
| 2810 | + * We require a minimum of 2 (u16) entries to encode a freelist |
| 2811 | + * 'pointer'. |
| 2812 | + */ |
| 2813 | + req = max(req, 2); |
| 2814 | + bucket = size_to_bucket(req); |
| 2815 | + curr = chain_block_buckets[bucket]; |
| 2816 | + |
| 2817 | + if (bucket) { |
| 2818 | + if (curr >= 0) { |
| 2819 | + del_chain_block(bucket, req, chain_block_next(curr)); |
| 2820 | + return curr; |
| 2821 | + } |
| 2822 | + /* Try bucket 0 */ |
| 2823 | + curr = chain_block_buckets[0]; |
| 2824 | + } |
| 2825 | + |
| 2826 | + /* |
| 2827 | + * The variable sized freelist is sorted by size; the first entry is |
| 2828 | + * the largest. Use it if it fits. |
| 2829 | + */ |
| 2830 | + if (curr >= 0) { |
| 2831 | + size = chain_block_size(curr); |
| 2832 | + if (likely(size >= req)) { |
| 2833 | + del_chain_block(0, size, chain_block_next(curr)); |
| 2834 | + add_chain_block(curr + req, size - req); |
| 2835 | + return curr; |
| 2836 | + } |
| 2837 | + } |
| 2838 | + |
| 2839 | + /* |
| 2840 | + * Last resort, split a block in a larger sized bucket. |
| 2841 | + */ |
| 2842 | + for (size = MAX_CHAIN_BUCKETS; size > req; size--) { |
| 2843 | + bucket = size_to_bucket(size); |
| 2844 | + curr = chain_block_buckets[bucket]; |
| 2845 | + if (curr < 0) |
| 2846 | + continue; |
| 2847 | + |
| 2848 | + del_chain_block(bucket, size, chain_block_next(curr)); |
| 2849 | + add_chain_block(curr + req, size - req); |
| 2850 | + return curr; |
| 2851 | + } |
| 2852 | + |
| 2853 | + return -1; |
| 2854 | +} |
| 2855 | + |
| 2856 | +static inline void free_chain_hlocks(int base, int size) |
| 2857 | +{ |
| 2858 | + add_chain_block(base, max(size, 2)); |
| 2859 | +} |
| 2631 | 2860 | |
| 2632 | 2861 | struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i) |
| 2633 | 2862 | { |
| @@ -2828,15 +3057,8 @@ static inline int add_chain_cache(struct task_struct *curr, · 194 unmodified lines |
| 2828 | 3057 | BUILD_BUG_ON((1UL << 6) <= ARRAY_SIZE(curr->held_locks)); |
| 2829 | 3058 | BUILD_BUG_ON((1UL << 8*sizeof(chain_hlocks[0])) <= ARRAY_SIZE(lock_classes)); |
| 2830 | 3059 | |
| 2831 | | − if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) { |
| 2832 | | − chain->base = nr_chain_hlocks; |
| 2833 | | − for (j = 0; j < chain->depth - 1; j++, i++) { |
| 2834 | | − int lock_id = curr->held_locks[i].class_idx; |
| 2835 | | − chain_hlocks[chain->base + j] = lock_id; |
| 2836 | | − } |
| 2837 | | − chain_hlocks[chain->base + j] = class - lock_classes; |
| 2838 | | − nr_chain_hlocks += chain->depth; |
| 2839 | | − } else { |
| 3060 | + j = alloc_chain_hlocks(chain->depth); |
| 3061 | + if (j < 0) { |
| 2840 | 3062 | if (!debug_locks_off_graph_unlock()) |
| 2841 | 3063 | return 0; |
| 2842 | 3064 | |
| @@ -2845,6 +3067,13 @@ static inline int add_chain_cache(struct task_struct *curr, · 2 unmodified lines |
| 2845 | 3067 | return 0; |
| 2846 | 3068 | } |
| 2847 | 3069 | |
| 3070 | + chain->base = j; |
| 3071 | + for (j = 0; j < chain->depth - 1; j++, i++) { |
| 3072 | + int lock_id = curr->held_locks[i].class_idx; |
| 3073 | + |
| 3074 | + chain_hlocks[chain->base + j] = lock_id; |
| 3075 | + } |
| 3076 | + chain_hlocks[chain->base + j] = class - lock_classes; |
| 2848 | 3077 | hlist_add_head_rcu(&chain->entry, hash_head); |
| 2849 | 3078 | debug_atomic_inc(chain_lookup_misses); |
| 2850 | 3079 | inc_chains(chain->irq_context); |
| @@ -2991,6 +3220,8 @@ static inline int validate_chain(struct task_struct *curr, · 140 unmodified lines |
| 2991 | 3220 | { |
| 2992 | 3221 | return 1; |
| 2993 | 3222 | } |
| 3223 | + |
| 3224 | +static void init_chain_block_buckets(void) { } |
| 2994 | 3225 | #endif /* CONFIG_PROVE_LOCKING */ |
| 2995 | 3226 | |
| 2996 | 3227 | /* |
| @@ -4788,6 +5019,7 @@ static void remove_class_from_lock_chain(struct pending_free *pf, · 1791 unmodified lines |
| 4788 | 5019 | return; |
| 4789 | 5020 | |
| 4790 | 5021 | free_lock_chain: |
| 5022 | + free_chain_hlocks(chain->base, chain->depth); |
| 4791 | 5023 | /* Overwrite the chain key for concurrent RCU readers. */ |
| 4792 | 5024 | WRITE_ONCE(chain->chain_key, INITIAL_CHAIN_KEY); |
| 4793 | 5025 | dec_chains(chain->irq_context); |