Skip to content

Conversation

@kernel-patches-daemon-bpf
Copy link

Pull request for series with
subject: BTF performance optimizations with permutation and binary search
version: 5
url: https://patchwork.kernel.org/project/netdevbpf/list/?series=1020436

@kernel-patches-daemon-bpf
Copy link
Author

Upstream branch: b54a8e1
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1020436
version: 5

@kernel-patches-review-bot
Copy link

commit f421675e6cb4fbbd964ab62815a267637761a891
Author: Donglin Peng <pengdonglin@xiaomi.com>

libbpf: Optimize type lookup with binary search for sorted BTF

This patch introduces binary search optimization for BTF type lookups
when the BTF instance contains sorted types. However, the binary search
path never executes and there are NULL pointer dereference issues.

> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index 97451a929..30be62ee2 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
> @@ -26,6 +26,10 @@
>
>  #define BTF_MAX_NR_TYPES 0x7fffffffU
>  #define BTF_MAX_STR_OFFSET 0x7fffffffU
> +/*
> + * sort verification occurs lazily upon first btf_find_type_by_name_kind() call
> + */
> +#define BTF_NEED_SORT_CHECK ((__u32)-1)

[ ... ]

> @@ -92,6 +96,16 @@ struct btf {
>  	 *   - for split BTF counts number of types added on top of base BTF.
>  	 */
>  	__u32 nr_types;
> +	/* number of sorted and named types in this BTF instance:
> +	 *   - doesn't include special [0] void type;
> +	 *   - for split BTF counts number of sorted and named types added on
> +	 *     top of base BTF.
> +	 *   - BTF_NEED_SORT_CHECK value indicates sort validation will be performed
> +	 *     on first call to btf_find_type_by_name_kind.
> +	 *   - zero value indicates applied sorting check with unsorted BTF or no
> +	 *     named types.
> +	 */
> +	__u32 nr_sorted_types;

[ ... ]

> @@ -897,44 +911,126 @@ int btf__resolve_type(const struct btf *btf, __u32 type_id)
>  	return type_id;
>  }
>
> -__s32 btf__find_by_name(const struct btf *btf, const char *type_name)
> +/* Performs binary search within specified type ID range to find the leftmost
> + * BTF type matching the given name. The search assumes types are sorted by
> + * name in lexicographical order within the specified range.
> + *
> + * Return: Type ID of leftmost matching type, or -ENOENT if not found
> + */
> +static __s32 btf_find_type_by_name_bsearch(const struct btf *btf, const char *name,
> +						__s32 start_id, __s32 end_id)
>  {
> -	__u32 i, nr_types = btf__type_cnt(btf);
> +	const struct btf_type *t;
> +	const char *tname;
> +	__s32 l, r, m, lmost = -ENOENT;
> +	int ret;
> +
> +	l = start_id;
> +	r = end_id;
> +	while (l <= r) {
> +		m = l + (r - l) / 2;
> +		t = btf_type_by_id(btf, m);
> +		tname = btf__str_by_offset(btf, t->name_off);
> +		ret = strcmp(tname, name);
>                      ^^^^^^

Can strcmp receive a NULL tname here? Anonymous types have name_off=0,
and btf__str_by_offset() can return NULL for invalid offsets. The old
btf__find_by_name() checked "if (name && !strcmp(...))" before calling
strcmp.

> +		if (ret < 0) {
> +			l = m + 1;
> +		} else {
> +			if (ret == 0)
> +				lmost = m;
> +			r = m - 1;
> +		}
> +	}
> +
> +	return lmost;
> +}
> +
> +/* Searches for a BTF type by name and optionally by kind. The function first
> + * checks if the search should start from the base BTF (if @start_id is before
> + * current BTF's start_id). If types are sorted, it uses binary search to find
> + * the leftmost matching type and then verifies the kind. For unsorted types,
> + * it falls back to linear search through all types.
> + *
> + * The function handles split BTF scenarios by recursively searching in base
> + * BTFs when necessary. When @kind is -1, only the name matching is performed.
> + *
> + * Return: Type ID of matching type on success, -ENOENT if not found
> + */
> +static __s32 btf_find_type_by_name_kind(const struct btf *btf, int start_id,
> +				   const char *type_name, __u32 kind)
> +{
> +	const struct btf_type *t;
> +	const char *tname;
> +	int err = -ENOENT;
> +
> +	if (start_id < btf->start_id) {
> +		err = btf_find_type_by_name_kind(btf->base_btf, start_id,
> +			type_name, kind);
> +		if (err > 0)
> +			goto out;
> +		start_id = btf->start_id;
> +	}
> +
> +	if (btf->nr_sorted_types != BTF_NEED_SORT_CHECK) {

Where is nr_sorted_types updated from BTF_NEED_SORT_CHECK? I only see
three assignments in the entire file:

  btf_new_empty():             btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
  btf_new():                   btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
  btf_invalidate_raw_data():   btf->nr_sorted_types = BTF_NEED_SORT_CHECK;

The comment claims "sort verification occurs lazily upon first
btf_find_type_by_name_kind() call" but this function never updates
nr_sorted_types. Does this mean the binary search path never executes?

> +		/* binary search */
> +		__s32 end_id;
> +		bool skip_first;
> +		int ret;
> +
> +		end_id = btf->start_id + btf->nr_sorted_types - 1;
> +		ret = btf_find_type_by_name_bsearch(btf, type_name, start_id, end_id);
> +		if (ret < 0)
> +			goto out;
> +		if (kind == -1)
> +			return ret;
> +		skip_first = true;
> +		do {
> +			t = btf_type_by_id(btf, ret);
> +			if (BTF_INFO_KIND(t->info) != kind) {
> +				if (skip_first) {
> +					skip_first = false;
> +					continue;
> +				}
> +			} else if (skip_first) {
> +				return ret;
> +			}
> +			tname = btf__str_by_offset(btf, t->name_off);
> +			if (!strcmp(tname, type_name))
>                              ^^^^^^

Same NULL dereference concern here - tname isn't checked before strcmp.

> +				return ret;
> +			else
> +				break;
> +		} while (++ret <= end_id);
> +	} else {
> +		/* linear search */
> +		__u32 i, total;
>
> -	if (!strcmp(type_name, "void"))
> -		return 0;
> -
> -	for (i = 1; i < nr_types; i++) {
> -		const struct btf_type *t = btf__type_by_id(btf, i);
> -		const char *name = btf__name_by_offset(btf, t->name_off);
> -
> -		if (name && !strcmp(type_name, name))
> -			return i;
> +		total = btf__type_cnt(btf);
> +		for (i = start_id; i < total; i++) {
> +			t = btf_type_by_id(btf, i);
> +			if (kind != -1 && btf_kind(t) != kind)
> +				continue;
> +			tname = btf__str_by_offset(btf, t->name_off);
> +			if (tname && !strcmp(tname, type_name))
> +				return i;
> +		}
>  	}
>
> -	return libbpf_err(-ENOENT);
> +out:
> +	return err;
>  }

[ ... ]

> @@ -1006,6 +1102,7 @@ static struct btf *btf_new_empty(struct btf *base_btf)
>  	btf->fd = -1;
>  	btf->ptr_sz = sizeof(void *);
>  	btf->swapped_endian = false;
> +	btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
>
>  	if (base_btf) {
>  		btf->base_btf = base_btf;

[ ... ]

> @@ -1057,6 +1154,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, b
>  	btf->start_id = 1;
>  	btf->start_str_off = 0;
>  	btf->fd = -1;
> +	btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
>
>  	if (base_btf) {
>  		btf->base_btf = base_btf;

[ ... ]

> @@ -1715,6 +1813,7 @@ static void btf_invalidate_raw_data(struct btf *btf)
>  		free(btf->raw_data_swapped);
>  		btf->raw_data_swapped = NULL;
>  	}
> +	btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
>  }

[ ... ]



AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

In-Reply-To-Subject: libbpf: Optimize type lookup with binary search for sorted BTF
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/19137195500

@kernel-patches-daemon-bpf
Copy link
Author

@kernel-patches-review-bot
Copy link

commit 5d7c8265900e4f7349847b5229ea57dd24bb4273
Author: Donglin Peng <pengdonglin@xiaomi.com>

libbpf: Add BTF permutation support for type reordering

Introduce btf__permute() API to allow in-place rearrangement of BTF
types. This function reorganizes BTF type order according to a provided
array of type IDs, updating all type references to maintain
consistency.

> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index 9c389b2c8..97451a929 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c

[ ... ]

> +int btf__permute(struct btf *btf, __u32 *ids, __u32 ids_sz, const struct btf_permute_opts *opts)
> +{
> +	struct btf_permute p;
> +	int err = 0;
> +	__u32 *ids_map = NULL;
> +
> +	if (!OPTS_VALID(opts, btf_permute_opts) || (ids_sz > btf->nr_types))
> +		return libbpf_err(-EINVAL);
> +
> +	ids_map = calloc(ids_sz, sizeof(*ids_map));

Can ids_map overflow here in btf_permute_shuffle_types()?

The ids_map array is allocated with ids_sz elements, giving valid
indices from 0 to ids_sz-1. However, in btf_permute_shuffle_types()
the code accesses:

    ids_map = &p->ids_map[id - btf->start_id]

where 'id' is validated only to be in range [btf->start_id,
btf->start_id + btf->nr_types). When ids_sz < btf->nr_types
(truncation case) and a user provides a type ID where
(id - btf->start_id) >= ids_sz, this results in an out-of-bounds
write to ids_map.

For example, with start_id=1, nr_types=100, and ids_sz=50:
- ids_map has 50 elements (indices 0-49)
- If user includes type ID 75 in the ids array
- btf__type_by_id(btf, 75) succeeds (75 < 101)
- ids_map[75-1] = ids_map[74] writes beyond the array

The same issue occurs in btf_permute_remap_type_id() where the code
reads from ids_map without validating the index is within ids_sz:

    new_type_id = p->ids_map[*type_id - p->btf->start_id]

If any included type references a type ID beyond ids_sz, this reads
uninitialized memory (or out-of-bounds if the ID is >= ids_sz +
start_id).

> +	if (!ids_map) {
> +		err = -ENOMEM;
> +		goto done;
> +	}
> +
> +	p.btf = btf;
> +	p.btf_ext = OPTS_GET(opts, btf_ext, NULL);
> +	p.ids = ids;
> +	p.ids_map = ids_map;
> +	p.ids_sz = ids_sz;
> +
> +	if (btf_ensure_modifiable(btf)) {
> +		err = -ENOMEM;
> +		goto done;
> +	}
> +	err = btf_permute_shuffle_types(&p);
> +	if (err < 0) {
> +		goto done;
> +	}
> +	err = btf_permute_remap_types(&p);
> +	if (err < 0) {
> +		goto done;
> +	}
> +
> +done:
> +	free(ids_map);
> +	return libbpf_err(err);
> +}

[ ... ]

> +static int btf_permute_shuffle_types(struct btf_permute *p)
> +{
> +	struct btf *btf = p->btf;
> +	const struct btf_type *t;
> +	__u32 *new_offs = NULL, *ids_map;
> +	void *nt, *new_types = NULL;
> +	int i, id, len, err;
> +
> +	new_offs = calloc(p->ids_sz, sizeof(*new_offs));
> +	new_types = calloc(btf->hdr->type_len, 1);
> +	if (!new_offs || !new_types) {
> +		err = -ENOMEM;
> +		goto out_err;
> +	}
> +
> +	nt = new_types;
> +	for (i = 0; i < p->ids_sz; i++) {
> +		id = p->ids[i];
> +		/* type IDs from base_btf and the VOID type are not allowed */
> +		if (id < btf->start_id) {
> +			err = -EINVAL;
> +			goto out_err;
> +		}
> +		/* must be a valid type ID */
> +		t = btf__type_by_id(btf, id);
> +		if (!t) {
> +			err = -EINVAL;
> +			goto out_err;
> +		}
> +		ids_map = &p->ids_map[id - btf->start_id];
> +		/* duplicate type IDs are not allowed */
> +		if (*ids_map) {
> +			err = -EINVAL;
> +			goto out_err;
> +		}
> +		len = btf_type_size(t);
> +		memcpy(nt, t, len);
> +		new_offs[i] = nt - new_types;
> +		*ids_map = btf->start_id + i;
> +		nt += len;
> +	}

[ ... ]

> +static int btf_permute_remap_type_id(__u32 *type_id, void *ctx)
> +{
> +	struct btf_permute *p = ctx;
> +	__u32 new_type_id = *type_id;
> +
> +	/* skip references that point into the base BTF */
> +	if (new_type_id < p->btf->start_id)
> +		return 0;
> +
> +	new_type_id = p->ids_map[*type_id - p->btf->start_id];
> +	if (new_type_id > BTF_MAX_NR_TYPES)
> +		return -EINVAL;
> +
> +	*type_id = new_type_id;
> +	return 0;
> +}



AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

In-Reply-To-Subject: libbpf: Add BTF permutation support for type reordering
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/19137195500

@kernel-patches-review-bot
Copy link

commit 47a7fa533e862fce2bccba2bd0da1474b5e04685
Author: Donglin Peng <pengdonglin@xiaomi.com>

btf: Add lazy sorting validation for binary search

Implement lazy validation of BTF type ordering to enable efficient binary
search for sorted BTF while maintaining linear search fallback for unsorted
cases.

> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 66cb739a0..33c327d3c 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -552,6 +552,70 @@ u32 btf_nr_types(const struct btf *btf)
>  	return total;
>  }
>
> +/* Verifies that BTF types are sorted in ascending order according to their
> + * names, with named types appearing before anonymous types. If the ordering
> + * is correct, counts the number of named types and updates the BTF object's
> + * nr_sorted_types field.
> + *
> + * Return: true if types are properly sorted, false otherwise
> + */
> +static bool btf_check_sorted(struct btf *btf)
> +{
> +	const struct btf_type *t;
> +	int i, n, k = 0, nr_sorted_types;
> +
> +	if (likely(btf->nr_sorted_types != BTF_NEED_SORT_CHECK))
> +		goto out;
> +	btf->nr_sorted_types = 0;
                            ^

Can multiple threads race when writing to btf->nr_sorted_types here?
Looking at btf_find_by_name_kind()->btf_check_sorted(), I see that
btf_find_by_name_kind() receives a const pointer but casts away the const
to call btf_check_sorted(). The function bpf_find_btf_id() calls
btf_find_by_name_kind() without holding any locks (line 737), and later
explicitly unlocks before calling it again (lines 756-757).

This means multiple threads can concurrently enter btf_check_sorted() and
write to btf->nr_sorted_types. While the validation logic is idempotent
and all threads would compute the same value, the concurrent writes to the
same memory location without synchronization could trigger KCSAN warnings.

Should this use atomic operations, or should the validation be performed
under a lock during BTF initialization before the BTF becomes visible to
multiple threads?

> +
> +	if (btf->nr_types < 2)
> +		goto out;
> +
> +	nr_sorted_types = 0;
> +	n = btf_nr_types(btf) - 1;
> +	for (i = btf_start_id(btf); i < n; i++) {
> +		k = i + 1;
> +		if (btf_compare_type_names(&i, &k, btf) > 0)
> +			goto out;
> +
> +		t = btf_type_by_id(btf, i);
> +		if (t->name_off)
> +			nr_sorted_types++;
> +	}
> +
> +	t = btf_type_by_id(btf, k);
> +	if (t->name_off)
> +		nr_sorted_types++;
> +	if (nr_sorted_types)
> +		btf->nr_sorted_types = nr_sorted_types;
                             ^

The same race condition applies to this write of nr_sorted_types as well.

> +
> +out:
> +	return btf->nr_sorted_types > 0;
> +}

[ ... ]

> @@ -610,7 +674,7 @@ s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
>  			goto out;
>  	}
>
> -	if (btf->nr_sorted_types != BTF_NEED_SORT_CHECK) {
> +	if (btf_check_sorted((struct btf *)btf)) {
                                  ^

The const cast here enables the concurrent writes discussed above. Is
there a reason to mark the btf parameter as const if we're modifying it?



AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

In-Reply-To-Subject: btf: Add lazy sorting validation for binary search
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/19137195500

@kernel-patches-daemon-bpf
Copy link
Author

@kernel-patches-daemon-bpf
Copy link
Author

@kernel-patches-daemon-bpf
Copy link
Author

Upstream branch: b54a8e1
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1020436
version: 5

@kernel-patches-daemon-bpf
Copy link
Author

Upstream branch: 6f1f4c1
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1020436
version: 5

@kernel-patches-daemon-bpf
Copy link
Author

Upstream branch: f8c67d8
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1020436
version: 5

Donglin Peng added 5 commits November 6, 2025 15:57
Refactor btf_dedup_remap_types() by extracting its core logic into a new
btf_remap_types() helper function. This eliminates code duplication
and improves modularity while maintaining the same functionality.

The new function encapsulates iteration over BTF types and BTF ext
sections, accepting a callback for flexible type ID remapping. This
makes the type remapping logic more maintainable and reusable.

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
Introduce btf__permute() API to allow in-place rearrangement of BTF types.
This function reorganizes BTF type order according to a provided array of
type IDs, updating all type references to maintain consistency.

The permutation process involves:
1. Shuffling types into new order based on the provided IDs array
2. Remapping all type ID references to point to new locations
3. Handling BTF extension data if provided via options

This is particularly useful for optimizing type locality after BTF
deduplication or for meeting specific layout requirements in specialized
use cases.

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
This patch introduces binary search optimization for BTF type lookups
when the BTF instance contains sorted types.

The optimization significantly improves performance when searching for
types in large BTF instances with sorted type names. For unsorted BTF
or when nr_sorted_types is zero, the implementation falls back to
the original linear search algorithm.

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
This patch adds lazy validation of BTF type ordering to determine if types
are sorted by name. The check is performed on first access and cached,
enabling efficient binary search for sorted BTF while maintaining linear
search fallback for unsorted cases.

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
Improve btf_find_by_name_kind() performance by adding binary search
support for sorted types. Falls back to linear search for compatibility.

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
Donglin Peng added 2 commits November 6, 2025 15:57
Implement lazy validation of BTF type ordering to enable efficient
binary search for sorted BTF while maintaining linear search fallback
for unsorted cases.

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
This patch introduces test cases for the btf__permute function to ensure
it works correctly with both base BTF and split BTF scenarios.

The test suite includes:
- test_permute_base: Validates permutation on base BTF
- test_permute_split: Tests permutation on split BTF
- test_permute_drop_base: Validates type dropping on base BTF
- test_permute_drop_split: Tests type dropping on split BTF

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant