mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
Merge branch 'add-resolve_btfids-support-for-__arena-kfunc-suffix'
Kumar Kartikeya Dwivedi says: ==================== Add resolve_btfids support for __arena kfunc suffix Use __arena/__arena__nullable suffixes to emit address_space(1) annotations on kfunc definitions in vmlinux.h. See commits for details. Changelog: ---------- v1 -> v2 v1: https://lore.kernel.org/bpf/20260809085155.3305519-1-memxor@gmail.com * Avoid enumerating all the ways resolve_btfids can emit the "address_space(1)" attribute in its header comment and in kfuncs.rst. (Ihor) * Drop the kfunc_has_arena_arg() helper: add_arena_tagged_proto() returns the original prototype when nothing needs tagging, so it can be invoked unconditionally for every kfunc. (Ihor) * Add resolve_btfids selftest cases with mixed tagged and untagged arguments, and a kfunc that combines the KF_ARENA_RET flag with suffixed arena arguments. (Ihor) ==================== Link: https://patch.msgid.link/20260812193842.2879226-1-memxor@gmail.com Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit is contained in:
@@ -513,8 +513,8 @@ At kernel build time the ``resolve_btfids`` tool finds all kfuncs declared with
|
||||
``BTF_KFUNCS_START()`` and emits their BTF annotations into the kernel's BTF.
|
||||
For each kfunc it emits a ``bpf_kfunc`` BTF decl tag, a ``bpf_fastcall`` decl
|
||||
tag when the kfunc is flagged ``KF_FASTCALL``, and the ``address_space(1)`` type
|
||||
attribute on the return value and/or arguments flagged ``KF_ARENA_RET``,
|
||||
``KF_ARENA_ARG1`` or ``KF_ARENA_ARG2`` (see section 2.8).
|
||||
attribute on the return value and/or arguments that use arena pointers (see
|
||||
sections 2.3.8 and 2.8).
|
||||
|
||||
2.7 Specifying no-cast aliases with ___init
|
||||
--------------------------------------------
|
||||
|
||||
@@ -64,8 +64,8 @@
|
||||
* each such kfunc it:
|
||||
*
|
||||
* - emits a "bpf_kfunc" decl tag, and "bpf_fastcall" when KF_FASTCALL is set;
|
||||
* - wraps the return value and/or arguments flagged KF_ARENA_RET,
|
||||
* KF_ARENA_ARG1 or KF_ARENA_ARG2 with the "address_space(1)" type attribute;
|
||||
* - wraps the return value and/or arguments that use arena pointers
|
||||
* with the "address_space(1)" type attribute;
|
||||
* - rewrites the prototype of KF_IMPLICIT_ARGS kfuncs.
|
||||
*
|
||||
* These kfunc annotations were historically produced by pahole.
|
||||
@@ -182,6 +182,8 @@ struct object {
|
||||
#define KF_IMPLICIT_ARGS (1 << 16)
|
||||
#define KF_IMPL_SUFFIX "_impl"
|
||||
#define TYPE_ATTR_ARENA "address_space(1)"
|
||||
#define PARAM_SUFFIX_ARENA "__arena"
|
||||
#define PARAM_SUFFIX_ARENA_NULLABLE "__arena__nullable"
|
||||
|
||||
struct kfunc {
|
||||
struct rb_node rb_node;
|
||||
@@ -1067,6 +1069,22 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool param_name_has_suffix(const char *name, const char *suffix)
|
||||
{
|
||||
size_t name_len = strlen(name);
|
||||
size_t suffix_len = strlen(suffix);
|
||||
|
||||
return name_len >= suffix_len && !strcmp(name + name_len - suffix_len, suffix);
|
||||
}
|
||||
|
||||
static bool is_arena_param(const struct btf *btf, const struct btf_param *param)
|
||||
{
|
||||
const char *name = btf__name_by_offset(btf, param->name_off);
|
||||
|
||||
return param_name_has_suffix(name, PARAM_SUFFIX_ARENA) ||
|
||||
param_name_has_suffix(name, PARAM_SUFFIX_ARENA_NULLABLE);
|
||||
}
|
||||
|
||||
static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
|
||||
{
|
||||
Elf_Data *idlist = obj->efile.idlist;
|
||||
@@ -1299,8 +1317,12 @@ add_new_proto:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_arena_arg(struct kfunc *kfunc, u32 idx)
|
||||
static bool is_arena_arg(const struct btf *btf, const struct kfunc *kfunc,
|
||||
const struct btf_param *param, u32 idx)
|
||||
{
|
||||
if (is_arena_param(btf, param))
|
||||
return true;
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
return kfunc->flags & KF_ARENA_ARG1;
|
||||
@@ -1339,23 +1361,36 @@ static s32 arena_tag_ptr(struct btf *btf, u32 ptr_id, struct kfunc *kfunc)
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a FUNC_PROTO for @kfunc with each relevant pointer tagged with
|
||||
* an "address_space(1)" attribute. The original proto may be shared
|
||||
* with other FUNCs, so it is never modified in place.
|
||||
* Add a FUNC_PROTO for @kfunc with each arena pointer tagged with an
|
||||
* "address_space(1)" attribute. The original proto may be shared with
|
||||
* other FUNCs, so it is never modified in place. Returns the original
|
||||
* proto id when @kfunc has no arena return value or arguments.
|
||||
*/
|
||||
static s32 add_arena_tagged_proto(struct btf *btf, struct kfunc *kfunc)
|
||||
{
|
||||
const struct btf_type *func = btf__type_by_id(btf, kfunc->btf_id);
|
||||
u32 proto_id = func->type;
|
||||
const struct btf_type *proto = btf__type_by_id(btf, proto_id);
|
||||
const struct btf_param *params = btf_params(proto);
|
||||
u32 nr_params = btf_vlen(proto);
|
||||
s32 ret_type_id = proto->type;
|
||||
const struct btf_type *t;
|
||||
struct btf_param *params;
|
||||
struct btf_param *tag_params;
|
||||
s32 new_proto_id, id;
|
||||
const char *name;
|
||||
bool has_arena_arg = false;
|
||||
int err, i;
|
||||
|
||||
for (i = 0; i < nr_params; i++) {
|
||||
if (is_arena_arg(btf, kfunc, ¶ms[i], i)) {
|
||||
has_arena_arg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(kfunc->flags & KF_ARENA_RET) && !has_arena_arg)
|
||||
return proto_id;
|
||||
|
||||
if (kfunc->flags & KF_ARENA_RET) {
|
||||
ret_type_id = arena_tag_ptr(btf, ret_type_id, kfunc);
|
||||
if (ret_type_id < 0)
|
||||
@@ -1383,19 +1418,18 @@ static s32 add_arena_tagged_proto(struct btf *btf, struct kfunc *kfunc)
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_params; i++) {
|
||||
if (!is_arena_arg(kfunc, i))
|
||||
t = btf__type_by_id(btf, new_proto_id);
|
||||
tag_params = btf_params(t);
|
||||
if (!is_arena_arg(btf, kfunc, &tag_params[i], i))
|
||||
continue;
|
||||
|
||||
t = btf__type_by_id(btf, new_proto_id);
|
||||
params = btf_params(t);
|
||||
|
||||
id = arena_tag_ptr(btf, params[i].type, kfunc);
|
||||
id = arena_tag_ptr(btf, tag_params[i].type, kfunc);
|
||||
if (id < 0)
|
||||
return id;
|
||||
|
||||
t = btf__type_by_id(btf, new_proto_id);
|
||||
params = btf_params(t);
|
||||
params[i].type = id;
|
||||
tag_params = btf_params(t);
|
||||
tag_params[i].type = id;
|
||||
}
|
||||
|
||||
pr_debug("added arena-tagged proto for kfunc %s: %d\n", kfunc->name, new_proto_id);
|
||||
@@ -1403,7 +1437,7 @@ static s32 add_arena_tagged_proto(struct btf *btf, struct kfunc *kfunc)
|
||||
return new_proto_id;
|
||||
}
|
||||
|
||||
static int process_kfunc_with_arena_flags(struct btf2btf_context *ctx,
|
||||
static int process_kfunc_with_arena_attrs(struct btf2btf_context *ctx,
|
||||
struct kfunc *kfunc)
|
||||
{
|
||||
struct btf_type *t;
|
||||
@@ -1463,11 +1497,9 @@ static int btf2btf(struct object *obj)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kfunc->flags & (KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2)) {
|
||||
err = process_kfunc_with_arena_flags(&ctx, kfunc);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = process_kfunc_with_arena_attrs(&ctx, kfunc);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define DECL_TAG_FASTCALL "bpf_fastcall"
|
||||
#define DECL_TAG_KFUNC "bpf_kfunc"
|
||||
#define TYPE_ATTR_ARENA "address_space(1)"
|
||||
#define ARENA_ARG(n) (1U << (n))
|
||||
|
||||
#ifndef KF_FASTCALL
|
||||
#define KF_FASTCALL (1 << 12)
|
||||
@@ -49,13 +50,20 @@ struct kfunc_symbol {
|
||||
const char *name;
|
||||
s32 id;
|
||||
u32 flags;
|
||||
u32 arena_args;
|
||||
bool arena_ret;
|
||||
};
|
||||
|
||||
static struct kfunc_symbol kfunc_symbols[] = {
|
||||
{ "kfunc_a", -1, 0 },
|
||||
{ "kfunc_b", -1, KF_FASTCALL },
|
||||
{ "kfunc_c", -1, KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2 },
|
||||
{ "kfunc_d", -1, KF_ARENA_ARG2 },
|
||||
{ "kfunc_a", -1, 0, 0, false },
|
||||
{ "kfunc_b", -1, KF_FASTCALL, 0, false },
|
||||
{ "kfunc_c", -1, KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2,
|
||||
ARENA_ARG(0) | ARENA_ARG(1), true },
|
||||
{ "kfunc_d", -1, KF_ARENA_ARG2, ARENA_ARG(1), false },
|
||||
{ "kfunc_e", -1, 0, ARENA_ARG(0) | ARENA_ARG(1) | ARENA_ARG(2) |
|
||||
ARENA_ARG(3) | ARENA_ARG(4), false },
|
||||
{ "kfunc_f", -1, 0, ARENA_ARG(1), false },
|
||||
{ "kfunc_g", -1, KF_ARENA_RET, ARENA_ARG(0) | ARENA_ARG(1), true },
|
||||
};
|
||||
|
||||
/* Align the .BTF_ids section to 4 bytes */
|
||||
@@ -105,6 +113,9 @@ BTF_ID_FLAGS(func, kfunc_a)
|
||||
BTF_ID_FLAGS(func, kfunc_b, KF_FASTCALL)
|
||||
BTF_ID_FLAGS(func, kfunc_c, KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2)
|
||||
BTF_ID_FLAGS(func, kfunc_d, KF_ARENA_ARG2)
|
||||
BTF_ID_FLAGS(func, kfunc_e)
|
||||
BTF_ID_FLAGS(func, kfunc_f)
|
||||
BTF_ID_FLAGS(func, kfunc_g, KF_ARENA_RET)
|
||||
BTF_KFUNCS_END(test_kfunc_set)
|
||||
|
||||
/*
|
||||
@@ -112,6 +123,9 @@ BTF_KFUNCS_END(test_kfunc_set)
|
||||
* actually sort at least one of the two sets.
|
||||
*/
|
||||
BTF_KFUNCS_START(test_kfunc_set_rev)
|
||||
BTF_ID_FLAGS(func, kfunc_g, KF_ARENA_RET)
|
||||
BTF_ID_FLAGS(func, kfunc_f)
|
||||
BTF_ID_FLAGS(func, kfunc_e)
|
||||
BTF_ID_FLAGS(func, kfunc_d, KF_ARENA_ARG2)
|
||||
BTF_ID_FLAGS(func, kfunc_c, KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2)
|
||||
BTF_ID_FLAGS(func, kfunc_b, KF_FASTCALL)
|
||||
@@ -301,15 +315,15 @@ void test_resolve_btfids(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check resolve_btfids wrapped exactly the arena-flagged return/args
|
||||
* with the address_space(1) type attribute, and left other
|
||||
* Check resolve_btfids wrapped exactly the arena-flagged or suffixed
|
||||
* return/args with the address_space(1) type attribute, and left other
|
||||
* pointers/returns untouched.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++) {
|
||||
const struct btf_type *fn, *proto;
|
||||
const struct btf_param *params;
|
||||
const char *name = kfunc_symbols[i].name;
|
||||
u32 fl = kfunc_symbols[i].flags;
|
||||
u32 arena_args = kfunc_symbols[i].arena_args;
|
||||
__u32 nr;
|
||||
|
||||
fn = btf__type_by_id(btf, kfunc_symbols[i].id);
|
||||
@@ -322,15 +336,10 @@ void test_resolve_btfids(void)
|
||||
nr = btf_vlen(proto);
|
||||
|
||||
ASSERT_EQ(is_arena_tagged_ptr(btf, proto->type),
|
||||
!!(fl & KF_ARENA_RET), name);
|
||||
if (nr > 0) {
|
||||
ASSERT_EQ(is_arena_tagged_ptr(btf, params[0].type),
|
||||
!!(fl & KF_ARENA_ARG1), name);
|
||||
}
|
||||
if (nr > 1) {
|
||||
ASSERT_EQ(is_arena_tagged_ptr(btf, params[1].type),
|
||||
!!(fl & KF_ARENA_ARG2), name);
|
||||
}
|
||||
kfunc_symbols[i].arena_ret, name);
|
||||
for (j = 0; j < nr; j++)
|
||||
ASSERT_EQ(is_arena_tagged_ptr(btf, params[j].type),
|
||||
!!(arena_args & ARENA_ARG(j)), name);
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
@@ -68,3 +68,23 @@ int kfunc_d(struct root_struct *a, struct root_struct *b)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kfunc_e(struct root_struct *a__arena,
|
||||
struct root_struct *b__arena__nullable,
|
||||
struct root_struct *c__arena,
|
||||
struct root_struct *d__arena__nullable,
|
||||
struct root_struct *e__arena)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kfunc_f(struct root_struct *a, struct root_struct *b__arena, int flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct root_struct *kfunc_g(struct root_struct *a__arena,
|
||||
struct root_struct *b__arena__nullable)
|
||||
{
|
||||
return a__arena;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user