diff mbox series

[bpf-next,v13,04/14] bpf: add struct_ops_tab to btf.

Message ID 20231209002709.535966-5-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
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success SINGLE THREAD; Generated files up to date; no warnings/errors; no diff in generated;
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: 7874 this patch: 7875
netdev/cc_maintainers warning 7 maintainers not CCed: kpsingh@kernel.org haoluo@google.com daniel@iogearbox.net yonghong.song@linux.dev jolsa@kernel.org sdf@google.com john.fastabend@gmail.com
netdev/build_clang fail Errors and warnings before: 2577 this patch: 2579
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: 8409 this patch: 8410
netdev/checkpatch warning WARNING: line length of 83 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-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-13 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-15 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 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 x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 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-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 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-25 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-30 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18

Commit Message

Kui-Feng Lee Dec. 9, 2023, 12:26 a.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.

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.

Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
---
 include/linux/btf.h |  5 ++++
 kernel/bpf/btf.c    | 73 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+)

Comments

Martin KaFai Lau Dec. 15, 2023, 2:22 a.m. UTC | #1
On 12/8/23 4:26 PM, thinker.li@gmail.com wrote:
> +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)

		*ret_cnt = 0;

unless the later patch checks the return value NULL before using *ret_cnt.
Anyway, better to set *ret_cnt to 0 if the btf has no struct_ops.

The same should go for the "!btf" case above but I suspect the above !btf check 
is unnecessary also and the caller should have checked for !btf itself instead 
of expecting a list of struct_ops from a NULL btf. Lets continue the review on 
the later patches for now to confirm where the above !btf case might happen.

> +		return NULL;
> +
> +	*ret_cnt = btf->struct_ops_tab->cnt;
> +	return (const struct bpf_struct_ops_desc *)btf->struct_ops_tab->ops;
> +}
Kui-Feng Lee Dec. 15, 2023, 9:42 p.m. UTC | #2
On 12/14/23 18:22, Martin KaFai Lau wrote:
> On 12/8/23 4:26 PM, thinker.li@gmail.com wrote:
>> +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)
> 
>          *ret_cnt = 0;
> 
> unless the later patch checks the return value NULL before using *ret_cnt.
> Anyway, better to set *ret_cnt to 0 if the btf has no struct_ops.
> 
> The same should go for the "!btf" case above but I suspect the above 
> !btf check is unnecessary also and the caller should have checked for 
> !btf itself instead of expecting a list of struct_ops from a NULL btf. 
> Lets continue the review on the later patches for now to confirm where 
> the above !btf case might happen.

Checking callers, I didn't find anything that make btf here NULL so far.
It is safe to remove !btf check. For the same reason as assigning
*ret_cnt for safe, this check should be fine here as well, right?

I don't have strong opinion here. What I though is to keep the values
as it is without any side-effect if the function call fails and if
possible. And, the callers should not expect the callee to set some
specific values when a call fails.

> 
>> +        return NULL;
>> +
>> +    *ret_cnt = btf->struct_ops_tab->cnt;
>> +    return (const struct bpf_struct_ops_desc *)btf->struct_ops_tab->ops;
>> +}
>
Martin KaFai Lau Dec. 16, 2023, 1:19 a.m. UTC | #3
On 12/15/23 1:42 PM, Kui-Feng Lee wrote:
> 
> 
> On 12/14/23 18:22, Martin KaFai Lau wrote:
>> On 12/8/23 4:26 PM, thinker.li@gmail.com wrote:
>>> +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)
>>
>>          *ret_cnt = 0;
>>
>> unless the later patch checks the return value NULL before using *ret_cnt.
>> Anyway, better to set *ret_cnt to 0 if the btf has no struct_ops.
>>
>> The same should go for the "!btf" case above but I suspect the above !btf 
>> check is unnecessary also and the caller should have checked for !btf itself 
>> instead of expecting a list of struct_ops from a NULL btf. Lets continue the 
>> review on the later patches for now to confirm where the above !btf case might 
>> happen.
> 
> Checking callers, I didn't find anything that make btf here NULL so far.

> It is safe to remove !btf check. For the same reason as assigning
> *ret_cnt for safe, this check should be fine here as well, right?

If for safety, why ref_cnt is not checked for NULL also? The userspace passed-in 
btf should have been checked for NULL long time before reaching here. There is 
no need to be over protective here. It would really need a BUG_ON instead if btf 
was NULL here (don't add a BUG_ON though).

afaict, no function in btf.c is checking the btf argument for NULL also.

> 
> I don't have strong opinion here. What I though is to keep the values
> as it is without any side-effect if the function call fails and if
> possible. And, the callers should not expect the callee to set some
> specific values when a call fails.

For *ref_cnt stay uninit, there is a bug in patch 10 which exactly assumes 0 is 
set in *ret_cnt when there is no struct_ops. It is a good signal on how this 
function will be used.

I think it is arguable whether returning NULL here is failure. I would argue 
getting a 0 struct_ops_desc array is not a failure. It is why the !btf case 
confuses the return NULL case to mean a never would happen error instead of 
meaning there is no struct_ops. Taking out the !btf case, NULL means there is no 
struct_ops (instead of failure), so 0 cnt.

Anyhow, either assign 0 to *ret_cnt here, or fix patch 10 to init the local cnt 
0 and write a warning comment here in btf_get_struct_ops() saying ret_cnt won't 
be set when there is no struct_ops in the btf.

When looking at it again, how about moving the bpf_struct_ops_find_*() to btf.c. 
Then it will avoid the need of the new btf_get_struct_ops() function. 
bpf_struct_ops_find_*() can directly use the 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;
>>> +}
>>
Kui-Feng Lee Dec. 16, 2023, 5:43 a.m. UTC | #4
On 12/15/23 17:19, Martin KaFai Lau wrote:
> On 12/15/23 1:42 PM, Kui-Feng Lee wrote:
>>
>>
>> On 12/14/23 18:22, Martin KaFai Lau wrote:
>>> On 12/8/23 4:26 PM, thinker.li@gmail.com wrote:
>>>> +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)
>>>
>>>          *ret_cnt = 0;
>>>
>>> unless the later patch checks the return value NULL before using 
>>> *ret_cnt.
>>> Anyway, better to set *ret_cnt to 0 if the btf has no struct_ops.
>>>
>>> The same should go for the "!btf" case above but I suspect the above 
>>> !btf check is unnecessary also and the caller should have checked for 
>>> !btf itself instead of expecting a list of struct_ops from a NULL 
>>> btf. Lets continue the review on the later patches for now to confirm 
>>> where the above !btf case might happen.
>>
>> Checking callers, I didn't find anything that make btf here NULL so far.
> 
>> It is safe to remove !btf check. For the same reason as assigning
>> *ret_cnt for safe, this check should be fine here as well, right?
> 
> If for safety, why ref_cnt is not checked for NULL also? The userspace 
> passed-in btf should have been checked for NULL long time before 
> reaching here. There is no need to be over protective here. It would 
> really need a BUG_ON instead if btf was NULL here (don't add a BUG_ON 
> though).
> 
> afaict, no function in btf.c is checking the btf argument for NULL also.
> 
>>
>> I don't have strong opinion here. What I though is to keep the values
>> as it is without any side-effect if the function call fails and if
>> possible. And, the callers should not expect the callee to set some
>> specific values when a call fails.
> 
> For *ref_cnt stay uninit, there is a bug in patch 10 which exactly 
> assumes 0 is set in *ret_cnt when there is no struct_ops. It is a good 
> signal on how this function will be used.
> 
> I think it is arguable whether returning NULL here is failure. I would 
> argue getting a 0 struct_ops_desc array is not a failure. It is why the 
> !btf case confuses the return NULL case to mean a never would happen 
> error instead of meaning there is no struct_ops. Taking out the !btf 
> case, NULL means there is no struct_ops (instead of failure), so 0 cnt.
> 
> Anyhow, either assign 0 to *ret_cnt here, or fix patch 10 to init the 
> local cnt 0 and write a warning comment here in btf_get_struct_ops() 
> saying ret_cnt won't be set when there is no struct_ops in the btf.


I will fix at the patch 10 by setting local cnt 0.

> 
> When looking at it again, how about moving the bpf_struct_ops_find_*() 
> to btf.c. Then it will avoid the need of the new btf_get_struct_ops() 
> function. bpf_struct_ops_find_*() can directly use the btf->struct_ops_tab.
> 

I prefer to keep them in bpf_struct_ops.c if it is ok to you.
Fixing the initialization issue of bpf_struct_ops_find()
should be enough.

> 
>>
>>>
>>>> +        return NULL;
>>>> +
>>>> +    *ret_cnt = btf->struct_ops_tab->cnt;
>>>> +    return (const struct bpf_struct_ops_desc 
>>>> *)btf->struct_ops_tab->ops;
>>>> +}
>>>
>
Martin KaFai Lau Dec. 16, 2023, 4:48 p.m. UTC | #5
On 12/15/23 9:43 PM, Kui-Feng Lee wrote:
> 
> 
> On 12/15/23 17:19, Martin KaFai Lau wrote:
>> On 12/15/23 1:42 PM, Kui-Feng Lee wrote:
>>>
>>>
>>> On 12/14/23 18:22, Martin KaFai Lau wrote:
>>>> On 12/8/23 4:26 PM, thinker.li@gmail.com wrote:
>>>>> +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)
>>>>
>>>>          *ret_cnt = 0;
>>>>
>>>> unless the later patch checks the return value NULL before using *ret_cnt.
>>>> Anyway, better to set *ret_cnt to 0 if the btf has no struct_ops.
>>>>
>>>> The same should go for the "!btf" case above but I suspect the above !btf 
>>>> check is unnecessary also and the caller should have checked for !btf itself 
>>>> instead of expecting a list of struct_ops from a NULL btf. Lets continue the 
>>>> review on the later patches for now to confirm where the above !btf case 
>>>> might happen.
>>>
>>> Checking callers, I didn't find anything that make btf here NULL so far.
>>
>>> It is safe to remove !btf check. For the same reason as assigning
>>> *ret_cnt for safe, this check should be fine here as well, right?
>>
>> If for safety, why ref_cnt is not checked for NULL also? The userspace 
>> passed-in btf should have been checked for NULL long time before reaching 
>> here. There is no need to be over protective here. It would really need a 
>> BUG_ON instead if btf was NULL here (don't add a BUG_ON though).
>>
>> afaict, no function in btf.c is checking the btf argument for NULL also.
>>
>>>
>>> I don't have strong opinion here. What I though is to keep the values
>>> as it is without any side-effect if the function call fails and if
>>> possible. And, the callers should not expect the callee to set some
>>> specific values when a call fails.
>>
>> For *ref_cnt stay uninit, there is a bug in patch 10 which exactly assumes 0 
>> is set in *ret_cnt when there is no struct_ops. It is a good signal on how 
>> this function will be used.
>>
>> I think it is arguable whether returning NULL here is failure. I would argue 
>> getting a 0 struct_ops_desc array is not a failure. It is why the !btf case 
>> confuses the return NULL case to mean a never would happen error instead of 
>> meaning there is no struct_ops. Taking out the !btf case, NULL means there is 
>> no struct_ops (instead of failure), so 0 cnt.
>>
>> Anyhow, either assign 0 to *ret_cnt here, or fix patch 10 to init the local 
>> cnt 0 and write a warning comment here in btf_get_struct_ops() saying ret_cnt 
>> won't be set when there is no struct_ops in the btf.
> 
> 
> I will fix at the patch 10 by setting local cnt 0.
> 
>>
>> When looking at it again, how about moving the bpf_struct_ops_find_*() to 
>> btf.c. Then it will avoid the need of the new btf_get_struct_ops() function. 
>> bpf_struct_ops_find_*() can directly use the btf->struct_ops_tab.
>>
> 
> I prefer to keep them in bpf_struct_ops.c if it is ok to you.
> Fixing the initialization issue of bpf_struct_ops_find()
> should be enough.

If choosing between fixing the bug in patch 10 and moving them to btf.c, I would 
avoid patch 10 (and future) bug to begin with by moving them to btf.c. Moving 
them should be cheap unless there is other dependency that I have overlooked.

> 
>>
>>>
>>>>
>>>>> +        return NULL;
>>>>> +
>>>>> +    *ret_cnt = btf->struct_ops_tab->cnt;
>>>>> +    return (const struct bpf_struct_ops_desc *)btf->struct_ops_tab->ops;
>>>>> +}
>>>>
>>
Kui-Feng Lee Dec. 17, 2023, 7:09 a.m. UTC | #6
On 12/16/23 08:48, Martin KaFai Lau wrote:
> On 12/15/23 9:43 PM, Kui-Feng Lee wrote:
>>
>>
>> On 12/15/23 17:19, Martin KaFai Lau wrote:
>>> On 12/15/23 1:42 PM, Kui-Feng Lee wrote:
>>>>
>>>>
>>>> On 12/14/23 18:22, Martin KaFai Lau wrote:
>>>>> On 12/8/23 4:26 PM, thinker.li@gmail.com wrote:
>>>>>> +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)
>>>>>
>>>>>          *ret_cnt = 0;
>>>>>
>>>>> unless the later patch checks the return value NULL before using 
>>>>> *ret_cnt.
>>>>> Anyway, better to set *ret_cnt to 0 if the btf has no struct_ops.
>>>>>
>>>>> The same should go for the "!btf" case above but I suspect the 
>>>>> above !btf check is unnecessary also and the caller should have 
>>>>> checked for !btf itself instead of expecting a list of struct_ops 
>>>>> from a NULL btf. Lets continue the review on the later patches for 
>>>>> now to confirm where the above !btf case might happen.
>>>>
>>>> Checking callers, I didn't find anything that make btf here NULL so 
>>>> far.
>>>
>>>> It is safe to remove !btf check. For the same reason as assigning
>>>> *ret_cnt for safe, this check should be fine here as well, right?
>>>
>>> If for safety, why ref_cnt is not checked for NULL also? The 
>>> userspace passed-in btf should have been checked for NULL long time 
>>> before reaching here. There is no need to be over protective here. It 
>>> would really need a BUG_ON instead if btf was NULL here (don't add a 
>>> BUG_ON though).
>>>
>>> afaict, no function in btf.c is checking the btf argument for NULL also.
>>>
>>>>
>>>> I don't have strong opinion here. What I though is to keep the values
>>>> as it is without any side-effect if the function call fails and if
>>>> possible. And, the callers should not expect the callee to set some
>>>> specific values when a call fails.
>>>
>>> For *ref_cnt stay uninit, there is a bug in patch 10 which exactly 
>>> assumes 0 is set in *ret_cnt when there is no struct_ops. It is a 
>>> good signal on how this function will be used.
>>>
>>> I think it is arguable whether returning NULL here is failure. I 
>>> would argue getting a 0 struct_ops_desc array is not a failure. It is 
>>> why the !btf case confuses the return NULL case to mean a never would 
>>> happen error instead of meaning there is no struct_ops. Taking out 
>>> the !btf case, NULL means there is no struct_ops (instead of 
>>> failure), so 0 cnt.
>>>
>>> Anyhow, either assign 0 to *ret_cnt here, or fix patch 10 to init the 
>>> local cnt 0 and write a warning comment here in btf_get_struct_ops() 
>>> saying ret_cnt won't be set when there is no struct_ops in the btf.
>>
>>
>> I will fix at the patch 10 by setting local cnt 0
>>
>>>
>>> When looking at it again, how about moving the 
>>> bpf_struct_ops_find_*() to btf.c. Then it will avoid the need of the 
>>> new btf_get_struct_ops() function. bpf_struct_ops_find_*() can 
>>> directly use the btf->struct_ops_tab.
>>>
>>
>> I prefer to keep them in bpf_struct_ops.c if it is ok to you.
>> Fixing the initialization issue of bpf_struct_ops_find()
>> should be enough.
> 
> If choosing between fixing the bug in patch 10 and moving them to btf.c, 
> I would avoid patch 10 (and future) bug to begin with by moving them to 
> btf.c. Moving them should be cheap unless there is other dependency that 
> I have overlooked.

Ok! Got it!

> 
>>
>>>
>>>>
>>>>>
>>>>>> +        return NULL;
>>>>>> +
>>>>>> +    *ret_cnt = btf->struct_ops_tab->cnt;
>>>>>> +    return (const struct bpf_struct_ops_desc 
>>>>>> *)btf->struct_ops_tab->ops;
>>>>>> +}
>>>>>
>>>
>
diff mbox series

Patch

diff --git a/include/linux/btf.h b/include/linux/btf.h
index 1d852dad7473..e2f4b85cf82a 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -584,4 +584,9 @@  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_desc;
+
+const struct bpf_struct_ops_desc *
+btf_get_struct_ops(struct btf *btf, u32 *ret_cnt);
+
 #endif
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 6935a88ed190..edbe3cbf2dcc 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);
@@ -8604,3 +8620,60 @@  bool btf_type_ids_nocast_alias(struct bpf_verifier_log *log,
 
 	return !strncmp(reg_name, arg_name, cmp_len);
 }
+
+static int
+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 -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 -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 -EEXIST;
+
+	if (tab->cnt == tab->capacity) {
+		new_tab = krealloc(tab,
+				   offsetof(struct btf_struct_ops_tab,
+					    ops[tab->capacity * 2]),
+				   GFP_KERNEL);
+		if (!new_tab)
+			return -ENOMEM;
+		tab = new_tab;
+		tab->capacity *= 2;
+		btf->struct_ops_tab = tab;
+	}
+
+	tab->ops[btf->struct_ops_tab->cnt].st_ops = st_ops;
+
+	btf->struct_ops_tab->cnt++;
+
+	return 0;
+}
+
+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;
+}