diff mbox series

[bpf-next,v8,03/10] bpf: add struct_ops_tab to btf.

Message ID 20231030192810.382942-4-thinker.li@gmail.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Registrating struct_ops types from modules | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 2868 this patch: 1519
netdev/cc_maintainers warning 8 maintainers not CCed: jolsa@kernel.org sdf@google.com john.fastabend@gmail.com kpsingh@kernel.org yonghong.song@linux.dev netdev@vger.kernel.org haoluo@google.com daniel@iogearbox.net
netdev/build_clang fail Errors and warnings before: 1541 this patch: 170
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 2945 this patch: 1571
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 97 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-3 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-9 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-15 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-16 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-llvm-16 / build / build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-25 fail Logs for x86_64-llvm-16 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-llvm-16 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-16 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-16 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-16 / veristat
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc

Commit Message

Kui-Feng Lee Oct. 30, 2023, 7:28 p.m. UTC
From: Kui-Feng Lee <thinker.li@gmail.com>

Maintain a registry of registered struct_ops types in the per-btf (module)
struct_ops_tab. This registry allows for easy lookup of struct_ops types
that are registered by a specific module.

Every struct_ops type should have an associated module BTF to provide type
information since we are going to allow modules to define and register new
struct_ops types. Once this change is made, the bpf_struct_ops subsystem
knows where to look up type info with just a bpf_struct_ops.

The subsystem looks up struct_ops types from a given module BTF although it
is always btf_vmlinux now. Once start using struct_ops_tab, btfs other than
btf_vmlinux can be used as well.

Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
---
 include/linux/bpf.h         |  5 +--
 include/linux/btf.h         |  8 +++++
 kernel/bpf/bpf_struct_ops.c | 28 ++++++++-------
 kernel/bpf/btf.c            | 71 +++++++++++++++++++++++++++++++++++++
 kernel/bpf/verifier.c       |  2 +-
 5 files changed, 99 insertions(+), 15 deletions(-)

Comments

Martin KaFai Lau Oct. 31, 2023, 1:09 a.m. UTC | #1
On 10/30/23 12:28 PM, thinker.li@gmail.com wrote:
> From: Kui-Feng Lee <thinker.li@gmail.com>
> 
> Maintain a registry of registered struct_ops types in the per-btf (module)
> struct_ops_tab. This registry allows for easy lookup of struct_ops types
> that are registered by a specific module.
> 
> Every struct_ops type should have an associated module BTF to provide type
> information since we are going to allow modules to define and register new
> struct_ops types. Once this change is made, the bpf_struct_ops subsystem
> knows where to look up type info with just a bpf_struct_ops.

I think this part needs better description. I found it hard to parse. In particular:

...
   the "bpf_struct_ops" subsystem
        knows where to look up type info with just
     a "bpf_struct_ops"
...

May be something like:

It is a preparation work for supporting kernel module struct_ops in a latter 
patch. Each struct_ops will be registered under its own kernel module btf and 
will be stored in the newly added btf->struct_ops_tab. The bpf verifier and bpf 
syscall (e.g. prog and map cmd) can find the struct_ops and its btf 
type/size/id... information from btf->struct_ops_tab.

> 
> The subsystem looks up struct_ops types from a given module BTF although it
> is always btf_vmlinux now. Once start using struct_ops_tab, btfs other than
> btf_vmlinux can be used as well.

I think this describes about the "struct btf *btf" argument change in this 
patch. This seems unrelated to the "add struct_ops_tab to btf" change. Can it be 
in its own preparation patch?

[ ... ]

> diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
> index e35d6321a2f8..0bc21a39257d 100644
> --- a/kernel/bpf/bpf_struct_ops.c
> +++ b/kernel/bpf/bpf_struct_ops.c
> @@ -186,6 +186,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
>   			pr_warn("Error in init bpf_struct_ops %s\n",
>   				st_ops->name);
>   		} else {
> +			st_ops_desc->btf = btf;
>   			st_ops_desc->type_id = type_id;
>   			st_ops_desc->type = t;
>   			st_ops_desc->value_id = value_id;
> @@ -222,7 +223,7 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
>   extern struct btf *btf_vmlinux;
>   
>   static const struct bpf_struct_ops_desc *
> -bpf_struct_ops_find_value(u32 value_id)
> +bpf_struct_ops_find_value(struct btf *btf, u32 value_id)

The "!btf_vmlinux" check a few lines below should also be changed to "!btf". I 
think I had commented on a similar point in v5.

>   {
>   	unsigned int i;
>   
> @@ -237,7 +238,8 @@ bpf_struct_ops_find_value(u32 value_id)
>   	return NULL;
>   }
>   
> -const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
> +const struct bpf_struct_ops_desc *
> +bpf_struct_ops_find(struct btf *btf, u32 type_id)

same here.

>   {
>   	unsigned int i;
>   

[ ... ]

> +static struct bpf_struct_ops_desc *
> +btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops)
> +{
> +	struct btf_struct_ops_tab *tab, *new_tab;
> +	int i;
> +
> +	if (!btf)
> +		return ERR_PTR(-ENOENT);
> +
> +	/* Assume this function is called for a module when the module is
> +	 * loading.
> +	 */
> +
> +	tab = btf->struct_ops_tab;
> +	if (!tab) {
> +		tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]),
> +			      GFP_KERNEL);
> +		if (!tab)
> +			return ERR_PTR(-ENOMEM);
> +		tab->capacity = 4;
> +		btf->struct_ops_tab = tab;
> +	}
> +
> +	for (i = 0; i < tab->cnt; i++)
> +		if (tab->ops[i].st_ops == st_ops)
> +			return ERR_PTR(-EEXIST);
> +
> +	if (tab->cnt == tab->capacity) {
> +		new_tab = krealloc(tab, sizeof(*tab) +
> +				   sizeof(struct bpf_struct_ops *) *
> +				   tab->capacity * 2, GFP_KERNEL);

nit. Use a similar offsetof() like a few lines above.

> +		if (!new_tab)
> +			return ERR_PTR(-ENOMEM);
> +		tab = new_tab;
> +		tab->capacity *= 2;
> +		btf->struct_ops_tab = tab;
> +	}
> +
> +	btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt].st_ops = st_ops;

nit. s/btf->struct_ops_tab/tab/

> +
> +	return &btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt++];
> +}
Kui-Feng Lee Oct. 31, 2023, 4:57 p.m. UTC | #2
On 10/30/23 18:09, Martin KaFai Lau wrote:
> On 10/30/23 12:28 PM, thinker.li@gmail.com wrote:
>> From: Kui-Feng Lee <thinker.li@gmail.com>
>>
>> Maintain a registry of registered struct_ops types in the per-btf 
>> (module)
>> struct_ops_tab. This registry allows for easy lookup of struct_ops types
>> that are registered by a specific module.
>>
>> Every struct_ops type should have an associated module BTF to provide 
>> type
>> information since we are going to allow modules to define and register 
>> new
>> struct_ops types. Once this change is made, the bpf_struct_ops subsystem
>> knows where to look up type info with just a bpf_struct_ops.
> 
> I think this part needs better description. I found it hard to parse. In 
> particular:
> 
> ...
>    the "bpf_struct_ops" subsystem
>         knows where to look up type info with just
>      a "bpf_struct_ops"
> ...
> 
> May be something like:
> 
> It is a preparation work for supporting kernel module struct_ops in a 
> latter patch. Each struct_ops will be registered under its own kernel 
> module btf and will be stored in the newly added btf->struct_ops_tab. 
> The bpf verifier and bpf syscall (e.g. prog and map cmd) can find the 
> struct_ops and its btf type/size/id... information from 
> btf->struct_ops_tab.


Got it!

> 
>>
>> The subsystem looks up struct_ops types from a given module BTF 
>> although it
>> is always btf_vmlinux now. Once start using struct_ops_tab, btfs other 
>> than
>> btf_vmlinux can be used as well.
> 
> I think this describes about the "struct btf *btf" argument change in 
> this patch. This seems unrelated to the "add struct_ops_tab to btf" 
> change. Can it be in its own preparation patch?
> 

Sure!

> [ ... ]
> 
>> diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
>> index e35d6321a2f8..0bc21a39257d 100644
>> --- a/kernel/bpf/bpf_struct_ops.c
>> +++ b/kernel/bpf/bpf_struct_ops.c
>> @@ -186,6 +186,7 @@ static void bpf_struct_ops_init_one(struct 
>> bpf_struct_ops_desc *st_ops_desc,
>>               pr_warn("Error in init bpf_struct_ops %s\n",
>>                   st_ops->name);
>>           } else {
>> +            st_ops_desc->btf = btf;
>>               st_ops_desc->type_id = type_id;
>>               st_ops_desc->type = t;
>>               st_ops_desc->value_id = value_id;
>> @@ -222,7 +223,7 @@ void bpf_struct_ops_init(struct btf *btf, struct 
>> bpf_verifier_log *log)
>>   extern struct btf *btf_vmlinux;
>>   static const struct bpf_struct_ops_desc *
>> -bpf_struct_ops_find_value(u32 value_id)
>> +bpf_struct_ops_find_value(struct btf *btf, u32 value_id)
> 
> The "!btf_vmlinux" check a few lines below should also be changed to 
> "!btf". I think I had commented on a similar point in v5.

At this patch, btf is still always btf_vmlinux until the patch 6 and 7.
I will move these changes from the patch 6 and 7 to here or
the new patch mentioned above.

> 
>>   {
>>       unsigned int i;
>> @@ -237,7 +238,8 @@ bpf_struct_ops_find_value(u32 value_id)
>>       return NULL;
>>   }
>> -const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
>> +const struct bpf_struct_ops_desc *
>> +bpf_struct_ops_find(struct btf *btf, u32 type_id)
> 
> same here.
>

Got it!

>>   {
>>       unsigned int i;
> 
> [ ... ]
> 
>> +static struct bpf_struct_ops_desc *
>> +btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops)
>> +{
>> +    struct btf_struct_ops_tab *tab, *new_tab;
>> +    int i;
>> +
>> +    if (!btf)
>> +        return ERR_PTR(-ENOENT);
>> +
>> +    /* Assume this function is called for a module when the module is
>> +     * loading.
>> +     */
>> +
>> +    tab = btf->struct_ops_tab;
>> +    if (!tab) {
>> +        tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]),
>> +                  GFP_KERNEL);
>> +        if (!tab)
>> +            return ERR_PTR(-ENOMEM);
>> +        tab->capacity = 4;
>> +        btf->struct_ops_tab = tab;
>> +    }
>> +
>> +    for (i = 0; i < tab->cnt; i++)
>> +        if (tab->ops[i].st_ops == st_ops)
>> +            return ERR_PTR(-EEXIST);
>> +
>> +    if (tab->cnt == tab->capacity) {
>> +        new_tab = krealloc(tab, sizeof(*tab) +
>> +                   sizeof(struct bpf_struct_ops *) *
>> +                   tab->capacity * 2, GFP_KERNEL);
> 
> nit. Use a similar offsetof() like a few lines above.
Sure!
> 
>> +        if (!new_tab)
>> +            return ERR_PTR(-ENOMEM);
>> +        tab = new_tab;
>> +        tab->capacity *= 2;
>> +        btf->struct_ops_tab = tab;
>> +    }
>> +
>> +    btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt].st_ops = st_ops;
> 
> nit. s/btf->struct_ops_tab/tab/
> 

Sure!

>> +
>> +    return &btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt++];
>> +}
>
diff mbox series

Patch

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index b55e27162df0..77dd9a522d55 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1633,6 +1633,7 @@  struct bpf_struct_ops {
 struct bpf_struct_ops_desc {
 	struct bpf_struct_ops *st_ops;
 
+	struct btf *btf;
 	const struct btf_type *type;
 	const struct btf_type *value_type;
 	u32 type_id;
@@ -1641,7 +1642,7 @@  struct bpf_struct_ops_desc {
 
 #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL)
 #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
-const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id);
+const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *btf, u32 type_id);
 void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log);
 bool bpf_struct_ops_get(const void *kdata);
 void bpf_struct_ops_put(const void *kdata);
@@ -1684,7 +1685,7 @@  int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
 			    union bpf_attr __user *uattr);
 #endif
 #else
-static inline const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
+static inline const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *btf, u32 type_id)
 {
 	return NULL;
 }
diff --git a/include/linux/btf.h b/include/linux/btf.h
index c2231c64d60b..933a227573da 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -572,4 +572,12 @@  static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type
 	return btf_type_is_struct(t);
 }
 
+struct bpf_struct_ops;
+struct bpf_struct_ops_desc;
+
+struct bpf_struct_ops_desc *
+btf_add_struct_ops(struct bpf_struct_ops *st_ops);
+const struct bpf_struct_ops_desc *
+btf_get_struct_ops(struct btf *btf, u32 *ret_cnt);
+
 #endif
diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
index e35d6321a2f8..0bc21a39257d 100644
--- a/kernel/bpf/bpf_struct_ops.c
+++ b/kernel/bpf/bpf_struct_ops.c
@@ -186,6 +186,7 @@  static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
 			pr_warn("Error in init bpf_struct_ops %s\n",
 				st_ops->name);
 		} else {
+			st_ops_desc->btf = btf;
 			st_ops_desc->type_id = type_id;
 			st_ops_desc->type = t;
 			st_ops_desc->value_id = value_id;
@@ -222,7 +223,7 @@  void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
 extern struct btf *btf_vmlinux;
 
 static const struct bpf_struct_ops_desc *
-bpf_struct_ops_find_value(u32 value_id)
+bpf_struct_ops_find_value(struct btf *btf, u32 value_id)
 {
 	unsigned int i;
 
@@ -237,7 +238,8 @@  bpf_struct_ops_find_value(u32 value_id)
 	return NULL;
 }
 
-const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
+const struct bpf_struct_ops_desc *
+bpf_struct_ops_find(struct btf *btf, u32 type_id)
 {
 	unsigned int i;
 
@@ -317,7 +319,7 @@  static void bpf_struct_ops_map_put_progs(struct bpf_struct_ops_map *st_map)
 	}
 }
 
-static int check_zero_holes(const struct btf_type *t, void *data)
+static int check_zero_holes(const struct btf *btf, const struct btf_type *t, void *data)
 {
 	const struct btf_member *member;
 	u32 i, moff, msize, prev_mend = 0;
@@ -329,8 +331,8 @@  static int check_zero_holes(const struct btf_type *t, void *data)
 		    memchr_inv(data + prev_mend, 0, moff - prev_mend))
 			return -EINVAL;
 
-		mtype = btf_type_by_id(btf_vmlinux, member->type);
-		mtype = btf_resolve_size(btf_vmlinux, mtype, &msize);
+		mtype = btf_type_by_id(btf, member->type);
+		mtype = btf_resolve_size(btf, mtype, &msize);
 		if (IS_ERR(mtype))
 			return PTR_ERR(mtype);
 		prev_mend = moff + msize;
@@ -397,12 +399,12 @@  static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 	if (*(u32 *)key != 0)
 		return -E2BIG;
 
-	err = check_zero_holes(st_ops_desc->value_type, value);
+	err = check_zero_holes(st_ops_desc->btf, st_ops_desc->value_type, value);
 	if (err)
 		return err;
 
 	uvalue = value;
-	err = check_zero_holes(t, uvalue->data);
+	err = check_zero_holes(st_ops_desc->btf, t, uvalue->data);
 	if (err)
 		return err;
 
@@ -437,7 +439,7 @@  static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 		u32 moff;
 
 		moff = __btf_member_bit_offset(t, member) / 8;
-		ptype = btf_type_resolve_ptr(btf_vmlinux, member->type, NULL);
+		ptype = btf_type_resolve_ptr(st_ops_desc->btf, member->type, NULL);
 		if (ptype == module_type) {
 			if (*(void **)(udata + moff))
 				goto reset_unlock;
@@ -462,8 +464,8 @@  static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 		if (!ptype || !btf_type_is_func_proto(ptype)) {
 			u32 msize;
 
-			mtype = btf_type_by_id(btf_vmlinux, member->type);
-			mtype = btf_resolve_size(btf_vmlinux, mtype, &msize);
+			mtype = btf_type_by_id(st_ops_desc->btf, member->type);
+			mtype = btf_resolve_size(st_ops_desc->btf, mtype, &msize);
 			if (IS_ERR(mtype)) {
 				err = PTR_ERR(mtype);
 				goto reset_unlock;
@@ -602,6 +604,7 @@  static long bpf_struct_ops_map_delete_elem(struct bpf_map *map, void *key)
 static void bpf_struct_ops_map_seq_show_elem(struct bpf_map *map, void *key,
 					     struct seq_file *m)
 {
+	struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
 	void *value;
 	int err;
 
@@ -611,7 +614,8 @@  static void bpf_struct_ops_map_seq_show_elem(struct bpf_map *map, void *key,
 
 	err = bpf_struct_ops_map_sys_lookup_elem(map, key, value);
 	if (!err) {
-		btf_type_seq_show(btf_vmlinux, map->btf_vmlinux_value_type_id,
+		btf_type_seq_show(st_map->st_ops_desc->btf,
+				  map->btf_vmlinux_value_type_id,
 				  value, m);
 		seq_puts(m, "\n");
 	}
@@ -673,7 +677,7 @@  static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
 	struct bpf_map *map;
 	int ret;
 
-	st_ops_desc = bpf_struct_ops_find_value(attr->btf_vmlinux_value_type_id);
+	st_ops_desc = bpf_struct_ops_find_value(btf_vmlinux, attr->btf_vmlinux_value_type_id);
 	if (!st_ops_desc)
 		return ERR_PTR(-ENOTSUPP);
 
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 15d71d2986d3..076bd61d88b1 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -241,6 +241,12 @@  struct btf_id_dtor_kfunc_tab {
 	struct btf_id_dtor_kfunc dtors[];
 };
 
+struct btf_struct_ops_tab {
+	u32 cnt;
+	u32 capacity;
+	struct bpf_struct_ops_desc ops[];
+};
+
 struct btf {
 	void *data;
 	struct btf_type **types;
@@ -258,6 +264,7 @@  struct btf {
 	struct btf_kfunc_set_tab *kfunc_set_tab;
 	struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;
 	struct btf_struct_metas *struct_meta_tab;
+	struct btf_struct_ops_tab *struct_ops_tab;
 
 	/* split BTF support */
 	struct btf *base_btf;
@@ -1688,11 +1695,20 @@  static void btf_free_struct_meta_tab(struct btf *btf)
 	btf->struct_meta_tab = NULL;
 }
 
+static void btf_free_struct_ops_tab(struct btf *btf)
+{
+	struct btf_struct_ops_tab *tab = btf->struct_ops_tab;
+
+	kfree(tab);
+	btf->struct_ops_tab = NULL;
+}
+
 static void btf_free(struct btf *btf)
 {
 	btf_free_struct_meta_tab(btf);
 	btf_free_dtor_kfunc_tab(btf);
 	btf_free_kfunc_set_tab(btf);
+	btf_free_struct_ops_tab(btf);
 	kvfree(btf->types);
 	kvfree(btf->resolved_sizes);
 	kvfree(btf->resolved_ids);
@@ -8602,3 +8618,58 @@  bool btf_type_ids_nocast_alias(struct bpf_verifier_log *log,
 
 	return !strncmp(reg_name, arg_name, cmp_len);
 }
+
+static struct bpf_struct_ops_desc *
+btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops)
+{
+	struct btf_struct_ops_tab *tab, *new_tab;
+	int i;
+
+	if (!btf)
+		return ERR_PTR(-ENOENT);
+
+	/* Assume this function is called for a module when the module is
+	 * loading.
+	 */
+
+	tab = btf->struct_ops_tab;
+	if (!tab) {
+		tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]),
+			      GFP_KERNEL);
+		if (!tab)
+			return ERR_PTR(-ENOMEM);
+		tab->capacity = 4;
+		btf->struct_ops_tab = tab;
+	}
+
+	for (i = 0; i < tab->cnt; i++)
+		if (tab->ops[i].st_ops == st_ops)
+			return ERR_PTR(-EEXIST);
+
+	if (tab->cnt == tab->capacity) {
+		new_tab = krealloc(tab, sizeof(*tab) +
+				   sizeof(struct bpf_struct_ops *) *
+				   tab->capacity * 2, GFP_KERNEL);
+		if (!new_tab)
+			return ERR_PTR(-ENOMEM);
+		tab = new_tab;
+		tab->capacity *= 2;
+		btf->struct_ops_tab = tab;
+	}
+
+	btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt].st_ops = st_ops;
+
+	return &btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt++];
+}
+
+const struct bpf_struct_ops_desc *btf_get_struct_ops(struct btf *btf, u32 *ret_cnt)
+{
+	if (!btf)
+		return NULL;
+	if (!btf->struct_ops_tab)
+		return NULL;
+
+	*ret_cnt = btf->struct_ops_tab->cnt;
+	return (const struct bpf_struct_ops_desc *)btf->struct_ops_tab->ops;
+}
+
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 290e3a7ee72f..bdd166cab977 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20094,7 +20094,7 @@  static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 	}
 
 	btf_id = prog->aux->attach_btf_id;
-	st_ops_desc = bpf_struct_ops_find(btf_id);
+	st_ops_desc = bpf_struct_ops_find(btf_vmlinux, btf_id);
 	if (!st_ops_desc) {
 		verbose(env, "attach_btf_id %u is not a supported struct\n",
 			btf_id);