diff mbox series

[v2,bpf-next,5/9] libbpf: add kind layout encoding, crc support

Message ID 20230616171728.530116-6-alan.maguire@oracle.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series bpf: support BTF kind layout info, CRCs | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next
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 success Errors and warnings before: 8 this patch: 8
netdev/cc_maintainers success CCed 12 of 12 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
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 success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning CHECK: Alignment should match open parenthesis CHECK: Please use a blank line after function/struct/union/enum declarations 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 86 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 12 this patch: 12
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-6 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-17 fail Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-19 success Logs for test_progs_no_alu32_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for test_progs_no_alu32_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-22 success Logs for test_progs_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for test_progs_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-25 success Logs for test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for veristat
bpf/vmtest-bpf-next-VM_Test-11 fail Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 fail Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 fail Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-15 fail Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 fail Logs for test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 fail Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 fail Logs for test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for test_maps on s390x with gcc

Commit Message

Alan Maguire June 16, 2023, 5:17 p.m. UTC
Support encoding of BTF kind layout data and crcs via
btf__new_empty_opts().

Current supported opts are base_btf, add_kind_layout and
add_crc.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 tools/lib/bpf/btf.c      | 99 ++++++++++++++++++++++++++++++++++++++--
 tools/lib/bpf/btf.h      | 11 +++++
 tools/lib/bpf/libbpf.map |  1 +
 3 files changed, 108 insertions(+), 3 deletions(-)

Comments

Yonghong Song June 19, 2023, 11:24 p.m. UTC | #1
On 6/16/23 10:17 AM, Alan Maguire wrote:
> Support encoding of BTF kind layout data and crcs via
> btf__new_empty_opts().
> 
> Current supported opts are base_btf, add_kind_layout and
> add_crc.
> 
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>   tools/lib/bpf/btf.c      | 99 ++++++++++++++++++++++++++++++++++++++--
>   tools/lib/bpf/btf.h      | 11 +++++
>   tools/lib/bpf/libbpf.map |  1 +
>   3 files changed, 108 insertions(+), 3 deletions(-)
> 
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index 457997c2a43c..060a93809f64 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
> @@ -16,6 +16,7 @@
>   #include <linux/err.h>
>   #include <linux/btf.h>
>   #include <gelf.h>
> +#include <zlib.h>
>   #include "btf.h"
>   #include "bpf.h"
>   #include "libbpf.h"
> @@ -882,8 +883,58 @@ void btf__free(struct btf *btf)
>   	free(btf);
>   }
>   
> -static struct btf *btf_new_empty(struct btf *base_btf)
> +static void btf_add_kind_layout(struct btf *btf, __u8 kind,
> +				__u16 flags, __u8 info_sz, __u8 elem_sz)
>   {
> +	struct btf_kind_layout *k = &btf->kind_layout[kind];
> +
> +	k->flags = flags;
> +	k->info_sz = info_sz;
> +	k->elem_sz = elem_sz;
> +	btf->hdr->kind_layout_len += sizeof(*k);
> +}
> +
> +static int btf_ensure_modifiable(struct btf *btf);
> +
> +static int btf_add_kind_layouts(struct btf *btf, struct btf_new_opts *opts)
> +{
> +	if (btf_ensure_modifiable(btf))
> +		return libbpf_err(-ENOMEM);
> +
> +	btf->kind_layout = calloc(NR_BTF_KINDS, sizeof(struct btf_kind_layout));
> +
> +	if (!btf->kind_layout)
> +		return -ENOMEM;
> +
> +	/* all supported kinds should describe their layout here. */
> +	btf_add_kind_layout(btf, BTF_KIND_UNKN, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_INT, 0, sizeof(__u32), 0);
> +	btf_add_kind_layout(btf, BTF_KIND_PTR, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_ARRAY, 0, sizeof(struct btf_array), 0);
> +	btf_add_kind_layout(btf, BTF_KIND_STRUCT, 0, 0, sizeof(struct btf_member));
> +	btf_add_kind_layout(btf, BTF_KIND_UNION, 0, 0, sizeof(struct btf_member));
> +	btf_add_kind_layout(btf, BTF_KIND_ENUM, 0, 0, sizeof(struct btf_enum));
> +	btf_add_kind_layout(btf, BTF_KIND_FWD, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_TYPEDEF, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_VOLATILE, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_CONST, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_RESTRICT, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_FUNC, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_FUNC_PROTO, 0, 0, sizeof(struct btf_param));
> +	btf_add_kind_layout(btf, BTF_KIND_VAR, 0, sizeof(struct btf_var), 0);
> +	btf_add_kind_layout(btf, BTF_KIND_DATASEC, 0, 0, sizeof(struct btf_var_secinfo));
> +	btf_add_kind_layout(btf, BTF_KIND_FLOAT, 0, 0, 0);
> +	btf_add_kind_layout(btf, BTF_KIND_DECL_TAG, BTF_KIND_LAYOUT_OPTIONAL,
> +							sizeof(struct btf_decl_tag), 0);
> +	btf_add_kind_layout(btf, BTF_KIND_TYPE_TAG, BTF_KIND_LAYOUT_OPTIONAL, 0, 0);

BTF_KIND_TYPE_TAG cannot be optional. For example,
   ptr -> type_tag -> const -> int

if type_tag becomes optional, the whole type chain cannot be parsed
properly.

Also, in Patch 3, we have

+static int btf_type_size(const struct btf *btf, const struct btf_type *t)
  {
  	const int base_size = sizeof(struct btf_type);
  	__u16 vlen = btf_vlen(t);
@@ -363,8 +391,7 @@ static int btf_type_size(const struct btf_type *t)
  	case BTF_KIND_DECL_TAG:
  		return base_size + sizeof(struct btf_decl_tag);
  	default:
-		pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
-		return -EINVAL;
+		return btf_type_size_unknown(btf, t);
  	}
  }

Clearly even if we mark decl_tag as optional, it still handled properly
in the above. So decl_tag does not need BTF_KIND_LAYOUT_OPTIONAL, right?

I guess what we really want to test is in the selftest:
   - Add a couple of new kinds for testing purpose, e.g.,
       BTF_KIND_OPTIONAL, BTF_KIND_NOT_OPTIONAL,
     generate two btf's which uses BTF_KIND_OPTIONAL
     and BTF_KIND_NOT_OPTIONAL respectively.
   - test these two btf's with this patch set to see whether it
     works as expected or not.

Does this make sense?

> +	btf_add_kind_layout(btf, BTF_KIND_ENUM64, 0, 0, sizeof(struct btf_enum64));
> +
> +	return 0;
> +}
> +
[...]
Alan Maguire June 20, 2023, 9:09 a.m. UTC | #2
On 20/06/2023 00:24, Yonghong Song wrote:
> 
> 
> On 6/16/23 10:17 AM, Alan Maguire wrote:
>> Support encoding of BTF kind layout data and crcs via
>> btf__new_empty_opts().
>>
>> Current supported opts are base_btf, add_kind_layout and
>> add_crc.
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>>   tools/lib/bpf/btf.c      | 99 ++++++++++++++++++++++++++++++++++++++--
>>   tools/lib/bpf/btf.h      | 11 +++++
>>   tools/lib/bpf/libbpf.map |  1 +
>>   3 files changed, 108 insertions(+), 3 deletions(-)
>>
>> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
>> index 457997c2a43c..060a93809f64 100644
>> --- a/tools/lib/bpf/btf.c
>> +++ b/tools/lib/bpf/btf.c
>> @@ -16,6 +16,7 @@
>>   #include <linux/err.h>
>>   #include <linux/btf.h>
>>   #include <gelf.h>
>> +#include <zlib.h>
>>   #include "btf.h"
>>   #include "bpf.h"
>>   #include "libbpf.h"
>> @@ -882,8 +883,58 @@ void btf__free(struct btf *btf)
>>       free(btf);
>>   }
>>   -static struct btf *btf_new_empty(struct btf *base_btf)
>> +static void btf_add_kind_layout(struct btf *btf, __u8 kind,
>> +                __u16 flags, __u8 info_sz, __u8 elem_sz)
>>   {
>> +    struct btf_kind_layout *k = &btf->kind_layout[kind];
>> +
>> +    k->flags = flags;
>> +    k->info_sz = info_sz;
>> +    k->elem_sz = elem_sz;
>> +    btf->hdr->kind_layout_len += sizeof(*k);
>> +}
>> +
>> +static int btf_ensure_modifiable(struct btf *btf);
>> +
>> +static int btf_add_kind_layouts(struct btf *btf, struct btf_new_opts
>> *opts)
>> +{
>> +    if (btf_ensure_modifiable(btf))
>> +        return libbpf_err(-ENOMEM);
>> +
>> +    btf->kind_layout = calloc(NR_BTF_KINDS, sizeof(struct
>> btf_kind_layout));
>> +
>> +    if (!btf->kind_layout)
>> +        return -ENOMEM;
>> +
>> +    /* all supported kinds should describe their layout here. */
>> +    btf_add_kind_layout(btf, BTF_KIND_UNKN, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_INT, 0, sizeof(__u32), 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_PTR, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_ARRAY, 0, sizeof(struct
>> btf_array), 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_STRUCT, 0, 0, sizeof(struct
>> btf_member));
>> +    btf_add_kind_layout(btf, BTF_KIND_UNION, 0, 0, sizeof(struct
>> btf_member));
>> +    btf_add_kind_layout(btf, BTF_KIND_ENUM, 0, 0, sizeof(struct
>> btf_enum));
>> +    btf_add_kind_layout(btf, BTF_KIND_FWD, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_TYPEDEF, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_VOLATILE, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_CONST, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_RESTRICT, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_FUNC, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_FUNC_PROTO, 0, 0, sizeof(struct
>> btf_param));
>> +    btf_add_kind_layout(btf, BTF_KIND_VAR, 0, sizeof(struct btf_var),
>> 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_DATASEC, 0, 0, sizeof(struct
>> btf_var_secinfo));
>> +    btf_add_kind_layout(btf, BTF_KIND_FLOAT, 0, 0, 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_DECL_TAG,
>> BTF_KIND_LAYOUT_OPTIONAL,
>> +                            sizeof(struct btf_decl_tag), 0);
>> +    btf_add_kind_layout(btf, BTF_KIND_TYPE_TAG,
>> BTF_KIND_LAYOUT_OPTIONAL, 0, 0);
> 
> BTF_KIND_TYPE_TAG cannot be optional. For example,
>   ptr -> type_tag -> const -> int
> 
> if type_tag becomes optional, the whole type chain cannot be parsed
> properly.
>

Ah, I missed that, thanks! You're absolutely right.

There are two separate concerns I think:

1. if an unknown kind (unknown to libbpf/kernel but present in the kind
   layout) is ever pointed at by another kind, regardless of optional
   status, the BTF must be rejected on the grounds that we don't really
   have a way to understand what the BTF means. That catches the case
   above.
2. however if an unknown kind exists in BTF and _is_ optional _and_
   is not pointed at by any known kinds, that is fine.

In other words it's logically possible for us to want to either
accept or reject BTF when we encounter unknown kinds that fall outside
of the existing type graph relations; the optional flag tells us which
to do.

I think for meta checking, the right way to handle 1 is to
reject BTF in the kind-specific meta checking for any known
kinds that can refer to other kinds; if the kind referred to
is > KIND_MAX, we reject the BTF.

> Also, in Patch 3, we have
> 
> +static int btf_type_size(const struct btf *btf, const struct btf_type *t)
>  {
>      const int base_size = sizeof(struct btf_type);
>      __u16 vlen = btf_vlen(t);
> @@ -363,8 +391,7 @@ static int btf_type_size(const struct btf_type *t)
>      case BTF_KIND_DECL_TAG:
>          return base_size + sizeof(struct btf_decl_tag);
>      default:
> -        pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
> -        return -EINVAL;
> +        return btf_type_size_unknown(btf, t);
>      }
>  }
> 
> Clearly even if we mark decl_tag as optional, it still handled properly
> in the above. So decl_tag does not need BTF_KIND_LAYOUT_OPTIONAL, right?
> 
But in btf_type_size_unknown() we have:

       if (!(k->flags & BTF_KIND_LAYOUT_OPTIONAL)) {
                /* a required kind, and we do not know about it.. */
                pr_debug("unknown but required kind: %u\n", kind);
                return -EINVAL;
        }

The problem however I think is that we need to spot reference
types that refer to unknown kinds regardless of optional status
as described above.

> I guess what we really want to test is in the selftest:
>   - Add a couple of new kinds for testing purpose, e.g.,
>       BTF_KIND_OPTIONAL, BTF_KIND_NOT_OPTIONAL,
>     generate two btf's which uses BTF_KIND_OPTIONAL
>     and BTF_KIND_NOT_OPTIONAL respectively.
>   - test these two btf's with this patch set to see whether it
>     works as expected or not.
> 
> Does this make sense?
>

There's a test that does this currently for libbpf only (since we
need a struct btf to load into the kernel), but nothing to cover the
case where a reference type points at a kind we don't know about.
I'll update the tests to use reference types too.

Thanks!

Alan

>> +    btf_add_kind_layout(btf, BTF_KIND_ENUM64, 0, 0, sizeof(struct
>> btf_enum64));
>> +
>> +    return 0;
>> +}
>> +
> [...]
Yonghong Song June 20, 2023, 2:41 p.m. UTC | #3
On 6/20/23 2:09 AM, Alan Maguire wrote:
> On 20/06/2023 00:24, Yonghong Song wrote:
>>
>>
>> On 6/16/23 10:17 AM, Alan Maguire wrote:
>>> Support encoding of BTF kind layout data and crcs via
>>> btf__new_empty_opts().
>>>
>>> Current supported opts are base_btf, add_kind_layout and
>>> add_crc.
>>>
>>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>>> ---
>>>    tools/lib/bpf/btf.c      | 99 ++++++++++++++++++++++++++++++++++++++--
>>>    tools/lib/bpf/btf.h      | 11 +++++
>>>    tools/lib/bpf/libbpf.map |  1 +
>>>    3 files changed, 108 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
>>> index 457997c2a43c..060a93809f64 100644
>>> --- a/tools/lib/bpf/btf.c
>>> +++ b/tools/lib/bpf/btf.c
>>> @@ -16,6 +16,7 @@
>>>    #include <linux/err.h>
>>>    #include <linux/btf.h>
>>>    #include <gelf.h>
>>> +#include <zlib.h>
>>>    #include "btf.h"
>>>    #include "bpf.h"
>>>    #include "libbpf.h"
>>> @@ -882,8 +883,58 @@ void btf__free(struct btf *btf)
>>>        free(btf);
>>>    }
>>>    -static struct btf *btf_new_empty(struct btf *base_btf)
>>> +static void btf_add_kind_layout(struct btf *btf, __u8 kind,
>>> +                __u16 flags, __u8 info_sz, __u8 elem_sz)
>>>    {
>>> +    struct btf_kind_layout *k = &btf->kind_layout[kind];
>>> +
>>> +    k->flags = flags;
>>> +    k->info_sz = info_sz;
>>> +    k->elem_sz = elem_sz;
>>> +    btf->hdr->kind_layout_len += sizeof(*k);
>>> +}
>>> +
>>> +static int btf_ensure_modifiable(struct btf *btf);
>>> +
>>> +static int btf_add_kind_layouts(struct btf *btf, struct btf_new_opts
>>> *opts)
>>> +{
>>> +    if (btf_ensure_modifiable(btf))
>>> +        return libbpf_err(-ENOMEM);
>>> +
>>> +    btf->kind_layout = calloc(NR_BTF_KINDS, sizeof(struct
>>> btf_kind_layout));
>>> +
>>> +    if (!btf->kind_layout)
>>> +        return -ENOMEM;
>>> +
>>> +    /* all supported kinds should describe their layout here. */
>>> +    btf_add_kind_layout(btf, BTF_KIND_UNKN, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_INT, 0, sizeof(__u32), 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_PTR, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_ARRAY, 0, sizeof(struct
>>> btf_array), 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_STRUCT, 0, 0, sizeof(struct
>>> btf_member));
>>> +    btf_add_kind_layout(btf, BTF_KIND_UNION, 0, 0, sizeof(struct
>>> btf_member));
>>> +    btf_add_kind_layout(btf, BTF_KIND_ENUM, 0, 0, sizeof(struct
>>> btf_enum));
>>> +    btf_add_kind_layout(btf, BTF_KIND_FWD, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_TYPEDEF, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_VOLATILE, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_CONST, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_RESTRICT, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_FUNC, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_FUNC_PROTO, 0, 0, sizeof(struct
>>> btf_param));
>>> +    btf_add_kind_layout(btf, BTF_KIND_VAR, 0, sizeof(struct btf_var),
>>> 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_DATASEC, 0, 0, sizeof(struct
>>> btf_var_secinfo));
>>> +    btf_add_kind_layout(btf, BTF_KIND_FLOAT, 0, 0, 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_DECL_TAG,
>>> BTF_KIND_LAYOUT_OPTIONAL,
>>> +                            sizeof(struct btf_decl_tag), 0);
>>> +    btf_add_kind_layout(btf, BTF_KIND_TYPE_TAG,
>>> BTF_KIND_LAYOUT_OPTIONAL, 0, 0);
>>
>> BTF_KIND_TYPE_TAG cannot be optional. For example,
>>    ptr -> type_tag -> const -> int
>>
>> if type_tag becomes optional, the whole type chain cannot be parsed
>> properly.
>>
> 
> Ah, I missed that, thanks! You're absolutely right.
> 
> There are two separate concerns I think:
> 
> 1. if an unknown kind (unknown to libbpf/kernel but present in the kind
>     layout) is ever pointed at by another kind, regardless of optional
>     status, the BTF must be rejected on the grounds that we don't really
>     have a way to understand what the BTF means. That catches the case
>     above.
> 2. however if an unknown kind exists in BTF and _is_ optional _and_
>     is not pointed at by any known kinds, that is fine.
> 
> In other words it's logically possible for us to want to either
> accept or reject BTF when we encounter unknown kinds that fall outside
> of the existing type graph relations; the optional flag tells us which
> to do.
> 
> I think for meta checking, the right way to handle 1 is to
> reject BTF in the kind-specific meta checking for any known
> kinds that can refer to other kinds; if the kind referred to
> is > KIND_MAX, we reject the BTF.

I agree with your above analysis.

> 
>> Also, in Patch 3, we have
>>
>> +static int btf_type_size(const struct btf *btf, const struct btf_type *t)
>>   {
>>       const int base_size = sizeof(struct btf_type);
>>       __u16 vlen = btf_vlen(t);
>> @@ -363,8 +391,7 @@ static int btf_type_size(const struct btf_type *t)
>>       case BTF_KIND_DECL_TAG:
>>           return base_size + sizeof(struct btf_decl_tag);
>>       default:
>> -        pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
>> -        return -EINVAL;
>> +        return btf_type_size_unknown(btf, t);
>>       }
>>   }
>>
>> Clearly even if we mark decl_tag as optional, it still handled properly
>> in the above. So decl_tag does not need BTF_KIND_LAYOUT_OPTIONAL, right?
>>
> But in btf_type_size_unknown() we have:
> 
>         if (!(k->flags & BTF_KIND_LAYOUT_OPTIONAL)) {
>                  /* a required kind, and we do not know about it.. */
>                  pr_debug("unknown but required kind: %u\n", kind);
>                  return -EINVAL;
>          }
> 
> The problem however I think is that we need to spot reference
> types that refer to unknown kinds regardless of optional status
> as described above.

What I mean is there is no need to tag decl_tag with
BTF_KIND_LAYOUT_OPTIONAL since for any btf with kind_layout,
decl_tag is already there. But BTF_KIND_LAYOUT_OPTIONAL is
still necessary for future kinds.

> 
>> I guess what we really want to test is in the selftest:
>>    - Add a couple of new kinds for testing purpose, e.g.,
>>        BTF_KIND_OPTIONAL, BTF_KIND_NOT_OPTIONAL,
>>      generate two btf's which uses BTF_KIND_OPTIONAL
>>      and BTF_KIND_NOT_OPTIONAL respectively.
>>    - test these two btf's with this patch set to see whether it
>>      works as expected or not.
>>
>> Does this make sense?
>>
> 
> There's a test that does this currently for libbpf only (since we
> need a struct btf to load into the kernel), but nothing to cover the
> case where a reference type points at a kind we don't know about.
> I'll update the tests to use reference types too.

Sounds good. thanks for adding additional tests.

> 
> Thanks!
> 
> Alan
> 
>>> +    btf_add_kind_layout(btf, BTF_KIND_ENUM64, 0, 0, sizeof(struct
>>> btf_enum64));
>>> +
>>> +    return 0;
>>> +}
>>> +
>> [...]
Andrii Nakryiko June 22, 2023, 10:04 p.m. UTC | #4
On Fri, Jun 16, 2023 at 10:18 AM Alan Maguire <alan.maguire@oracle.com> wrote:
>
> Support encoding of BTF kind layout data and crcs via
> btf__new_empty_opts().
>
> Current supported opts are base_btf, add_kind_layout and
> add_crc.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>  tools/lib/bpf/btf.c      | 99 ++++++++++++++++++++++++++++++++++++++--
>  tools/lib/bpf/btf.h      | 11 +++++
>  tools/lib/bpf/libbpf.map |  1 +
>  3 files changed, 108 insertions(+), 3 deletions(-)
>
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index 457997c2a43c..060a93809f64 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
> @@ -16,6 +16,7 @@
>  #include <linux/err.h>
>  #include <linux/btf.h>
>  #include <gelf.h>
> +#include <zlib.h>
>  #include "btf.h"
>  #include "bpf.h"
>  #include "libbpf.h"
> @@ -882,8 +883,58 @@ void btf__free(struct btf *btf)
>         free(btf);
>  }
>
> -static struct btf *btf_new_empty(struct btf *base_btf)
> +static void btf_add_kind_layout(struct btf *btf, __u8 kind,
> +                               __u16 flags, __u8 info_sz, __u8 elem_sz)
>  {
> +       struct btf_kind_layout *k = &btf->kind_layout[kind];
> +
> +       k->flags = flags;
> +       k->info_sz = info_sz;
> +       k->elem_sz = elem_sz;
> +       btf->hdr->kind_layout_len += sizeof(*k);
> +}
> +
> +static int btf_ensure_modifiable(struct btf *btf);
> +
> +static int btf_add_kind_layouts(struct btf *btf, struct btf_new_opts *opts)
> +{
> +       if (btf_ensure_modifiable(btf))
> +               return libbpf_err(-ENOMEM);
> +
> +       btf->kind_layout = calloc(NR_BTF_KINDS, sizeof(struct btf_kind_layout));
> +
> +       if (!btf->kind_layout)
> +               return -ENOMEM;
> +
> +       /* all supported kinds should describe their layout here. */

why not have a static table for each known kind and then just memcpy
it? Seems both more elegant and more maintainable?

static struct btf_kind_layout layout[] = {
    [BTF_KIND_UNKN] = {0, 0, 0},
    [BTF_KIND_UNKN] = {0, sizeof(__u32), 0},
    ...
    [BTF_KIND_STRUCT] = {0, 0, sizeof(struct btf_member)},
};

?


> +       btf_add_kind_layout(btf, BTF_KIND_UNKN, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_INT, 0, sizeof(__u32), 0);
> +       btf_add_kind_layout(btf, BTF_KIND_PTR, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_ARRAY, 0, sizeof(struct btf_array), 0);
> +       btf_add_kind_layout(btf, BTF_KIND_STRUCT, 0, 0, sizeof(struct btf_member));
> +       btf_add_kind_layout(btf, BTF_KIND_UNION, 0, 0, sizeof(struct btf_member));
> +       btf_add_kind_layout(btf, BTF_KIND_ENUM, 0, 0, sizeof(struct btf_enum));
> +       btf_add_kind_layout(btf, BTF_KIND_FWD, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_TYPEDEF, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_VOLATILE, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_CONST, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_RESTRICT, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_FUNC, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_FUNC_PROTO, 0, 0, sizeof(struct btf_param));
> +       btf_add_kind_layout(btf, BTF_KIND_VAR, 0, sizeof(struct btf_var), 0);
> +       btf_add_kind_layout(btf, BTF_KIND_DATASEC, 0, 0, sizeof(struct btf_var_secinfo));
> +       btf_add_kind_layout(btf, BTF_KIND_FLOAT, 0, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_DECL_TAG, BTF_KIND_LAYOUT_OPTIONAL,
> +                                                       sizeof(struct btf_decl_tag), 0);
> +       btf_add_kind_layout(btf, BTF_KIND_TYPE_TAG, BTF_KIND_LAYOUT_OPTIONAL, 0, 0);
> +       btf_add_kind_layout(btf, BTF_KIND_ENUM64, 0, 0, sizeof(struct btf_enum64));
> +
> +       return 0;
> +}
> +
> +static struct btf *btf_new_empty(struct btf_new_opts *opts)
> +{
> +       struct btf *base_btf = OPTS_GET(opts, base_btf, NULL);
>         struct btf *btf;
>
>         btf = calloc(1, sizeof(*btf));
> @@ -920,17 +971,53 @@ static struct btf *btf_new_empty(struct btf *base_btf)
>         btf->strs_data = btf->raw_data + btf->hdr->hdr_len;
>         btf->hdr->str_len = base_btf ? 0 : 1; /* empty string at offset 0 */
>
> +       if (opts->add_kind_layout) {
> +               int err = btf_add_kind_layouts(btf, opts);
> +

as I mentioned, I'd always add it internally, but allow to control
whether it has to be emitted into raw_data

> +               if (err) {
> +                       free(btf->raw_data);
> +                       free(btf);
> +                       return ERR_PTR(err);
> +               }
> +       }
> +       if (opts->add_crc) {
> +               if (btf->base_btf) {
> +                       struct btf_header *base_hdr = btf->base_btf->hdr;
> +
> +                       if (base_hdr->hdr_len >= sizeof(struct btf_header) &&
> +                           base_hdr->flags & BTF_FLAG_CRC_SET) {
> +                               btf->hdr->base_crc = base_hdr->crc;
> +                               btf->hdr->flags |= BTF_FLAG_BASE_CRC_SET;
> +                       }
> +               }
> +               btf->hdr->flags |= BTF_FLAG_CRC_SET;
> +       }
> +
>         return btf;
>  }
>
>  struct btf *btf__new_empty(void)
>  {
> -       return libbpf_ptr(btf_new_empty(NULL));
> +       LIBBPF_OPTS(btf_new_opts, opts);
> +
> +       return libbpf_ptr(btf_new_empty(&opts));

why? just pass NULL, it's fine, no need to create empty opts

>  }
>
>  struct btf *btf__new_empty_split(struct btf *base_btf)
>  {
> -       return libbpf_ptr(btf_new_empty(base_btf));
> +       LIBBPF_OPTS(btf_new_opts, opts);
> +
> +       opts.base_btf = base_btf;

nit: it's cleaner to initialize opts on declaration in this case

LIBBPF_OPTS(btf_new_opts, opts, .base_btf = base_btf);

> +
> +       return libbpf_ptr(btf_new_empty(&opts));
> +}
> +
> +struct btf *btf__new_empty_opts(struct btf_new_opts *opts)
> +{
> +       if (!OPTS_VALID(opts, btf_new_opts))
> +               return libbpf_err_ptr(-EINVAL);
> +
> +       return libbpf_ptr(btf_new_empty(opts));
>  }
>
>  static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
> @@ -1377,6 +1464,12 @@ static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endi
>                         btf_bswap_kind_layout_sec(p, hdr->kind_layout_len);
>                 p += hdr->kind_layout_len;
>         }
> +       if (hdr->flags & BTF_FLAG_CRC_SET) {
> +               struct btf_header *h = data;
> +
> +               h->crc = crc32(0L, (const Bytef *)&data, sizeof(data));

is crc32() part of zlib's public API?

> +       }
> +
>         *size = data_sz;
>         return data;
>  err_out:
> diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
> index 8e6880d91c84..d25bd5c19eec 100644
> --- a/tools/lib/bpf/btf.h
> +++ b/tools/lib/bpf/btf.h
> @@ -107,6 +107,17 @@ LIBBPF_API struct btf *btf__new_empty(void);
>   */
>  LIBBPF_API struct btf *btf__new_empty_split(struct btf *base_btf);
>
> +struct btf_new_opts {
> +       size_t sz;
> +       struct btf *base_btf;   /* optional base BTF */
> +       bool add_kind_layout;   /* add BTF kind layout information */

I'd say let's make it opt-out option and by default do emit kind
layout? so rather "skip_kind_layout"

> +       bool add_crc;           /* add CRC information */

same for crc? "skip_crc"?

btw, does add_crc imply both crc and base_crc (if base_btf != NULL)?
just thinking out loud if we need to control that independently...



> +       size_t:0;
> +};
> +#define btf_new_opts__last_field add_crc
> +
> +LIBBPF_API struct btf *btf__new_empty_opts(struct btf_new_opts *opts);
> +
>  LIBBPF_API struct btf *btf__parse(const char *path, struct btf_ext **btf_ext);
>  LIBBPF_API struct btf *btf__parse_split(const char *path, struct btf *base_btf);
>  LIBBPF_API struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext);
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 7521a2fb7626..edd8be4b21d0 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -395,4 +395,5 @@ LIBBPF_1.2.0 {
>  LIBBPF_1.3.0 {
>         global:
>                 bpf_obj_pin_opts;
> +               btf__new_empty_opts;
>  } LIBBPF_1.2.0;
> --
> 2.39.3
>
diff mbox series

Patch

diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 457997c2a43c..060a93809f64 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -16,6 +16,7 @@ 
 #include <linux/err.h>
 #include <linux/btf.h>
 #include <gelf.h>
+#include <zlib.h>
 #include "btf.h"
 #include "bpf.h"
 #include "libbpf.h"
@@ -882,8 +883,58 @@  void btf__free(struct btf *btf)
 	free(btf);
 }
 
-static struct btf *btf_new_empty(struct btf *base_btf)
+static void btf_add_kind_layout(struct btf *btf, __u8 kind,
+				__u16 flags, __u8 info_sz, __u8 elem_sz)
 {
+	struct btf_kind_layout *k = &btf->kind_layout[kind];
+
+	k->flags = flags;
+	k->info_sz = info_sz;
+	k->elem_sz = elem_sz;
+	btf->hdr->kind_layout_len += sizeof(*k);
+}
+
+static int btf_ensure_modifiable(struct btf *btf);
+
+static int btf_add_kind_layouts(struct btf *btf, struct btf_new_opts *opts)
+{
+	if (btf_ensure_modifiable(btf))
+		return libbpf_err(-ENOMEM);
+
+	btf->kind_layout = calloc(NR_BTF_KINDS, sizeof(struct btf_kind_layout));
+
+	if (!btf->kind_layout)
+		return -ENOMEM;
+
+	/* all supported kinds should describe their layout here. */
+	btf_add_kind_layout(btf, BTF_KIND_UNKN, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_INT, 0, sizeof(__u32), 0);
+	btf_add_kind_layout(btf, BTF_KIND_PTR, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_ARRAY, 0, sizeof(struct btf_array), 0);
+	btf_add_kind_layout(btf, BTF_KIND_STRUCT, 0, 0, sizeof(struct btf_member));
+	btf_add_kind_layout(btf, BTF_KIND_UNION, 0, 0, sizeof(struct btf_member));
+	btf_add_kind_layout(btf, BTF_KIND_ENUM, 0, 0, sizeof(struct btf_enum));
+	btf_add_kind_layout(btf, BTF_KIND_FWD, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_TYPEDEF, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_VOLATILE, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_CONST, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_RESTRICT, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_FUNC, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_FUNC_PROTO, 0, 0, sizeof(struct btf_param));
+	btf_add_kind_layout(btf, BTF_KIND_VAR, 0, sizeof(struct btf_var), 0);
+	btf_add_kind_layout(btf, BTF_KIND_DATASEC, 0, 0, sizeof(struct btf_var_secinfo));
+	btf_add_kind_layout(btf, BTF_KIND_FLOAT, 0, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_DECL_TAG, BTF_KIND_LAYOUT_OPTIONAL,
+							sizeof(struct btf_decl_tag), 0);
+	btf_add_kind_layout(btf, BTF_KIND_TYPE_TAG, BTF_KIND_LAYOUT_OPTIONAL, 0, 0);
+	btf_add_kind_layout(btf, BTF_KIND_ENUM64, 0, 0, sizeof(struct btf_enum64));
+
+	return 0;
+}
+
+static struct btf *btf_new_empty(struct btf_new_opts *opts)
+{
+	struct btf *base_btf = OPTS_GET(opts, base_btf, NULL);
 	struct btf *btf;
 
 	btf = calloc(1, sizeof(*btf));
@@ -920,17 +971,53 @@  static struct btf *btf_new_empty(struct btf *base_btf)
 	btf->strs_data = btf->raw_data + btf->hdr->hdr_len;
 	btf->hdr->str_len = base_btf ? 0 : 1; /* empty string at offset 0 */
 
+	if (opts->add_kind_layout) {
+		int err = btf_add_kind_layouts(btf, opts);
+
+		if (err) {
+			free(btf->raw_data);
+			free(btf);
+			return ERR_PTR(err);
+		}
+	}
+	if (opts->add_crc) {
+		if (btf->base_btf) {
+			struct btf_header *base_hdr = btf->base_btf->hdr;
+
+			if (base_hdr->hdr_len >= sizeof(struct btf_header) &&
+			    base_hdr->flags & BTF_FLAG_CRC_SET) {
+				btf->hdr->base_crc = base_hdr->crc;
+				btf->hdr->flags |= BTF_FLAG_BASE_CRC_SET;
+			}
+		}
+		btf->hdr->flags |= BTF_FLAG_CRC_SET;
+	}
+
 	return btf;
 }
 
 struct btf *btf__new_empty(void)
 {
-	return libbpf_ptr(btf_new_empty(NULL));
+	LIBBPF_OPTS(btf_new_opts, opts);
+
+	return libbpf_ptr(btf_new_empty(&opts));
 }
 
 struct btf *btf__new_empty_split(struct btf *base_btf)
 {
-	return libbpf_ptr(btf_new_empty(base_btf));
+	LIBBPF_OPTS(btf_new_opts, opts);
+
+	opts.base_btf = base_btf;
+
+	return libbpf_ptr(btf_new_empty(&opts));
+}
+
+struct btf *btf__new_empty_opts(struct btf_new_opts *opts)
+{
+	if (!OPTS_VALID(opts, btf_new_opts))
+		return libbpf_err_ptr(-EINVAL);
+
+	return libbpf_ptr(btf_new_empty(opts));
 }
 
 static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
@@ -1377,6 +1464,12 @@  static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endi
 			btf_bswap_kind_layout_sec(p, hdr->kind_layout_len);
 		p += hdr->kind_layout_len;
 	}
+	if (hdr->flags & BTF_FLAG_CRC_SET) {
+		struct btf_header *h = data;
+
+		h->crc = crc32(0L, (const Bytef *)&data, sizeof(data));
+	}
+
 	*size = data_sz;
 	return data;
 err_out:
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index 8e6880d91c84..d25bd5c19eec 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -107,6 +107,17 @@  LIBBPF_API struct btf *btf__new_empty(void);
  */
 LIBBPF_API struct btf *btf__new_empty_split(struct btf *base_btf);
 
+struct btf_new_opts {
+	size_t sz;
+	struct btf *base_btf;	/* optional base BTF */
+	bool add_kind_layout;	/* add BTF kind layout information */
+	bool add_crc;		/* add CRC information */
+	size_t:0;
+};
+#define btf_new_opts__last_field add_crc
+
+LIBBPF_API struct btf *btf__new_empty_opts(struct btf_new_opts *opts);
+
 LIBBPF_API struct btf *btf__parse(const char *path, struct btf_ext **btf_ext);
 LIBBPF_API struct btf *btf__parse_split(const char *path, struct btf *base_btf);
 LIBBPF_API struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext);
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 7521a2fb7626..edd8be4b21d0 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -395,4 +395,5 @@  LIBBPF_1.2.0 {
 LIBBPF_1.3.0 {
 	global:
 		bpf_obj_pin_opts;
+		btf__new_empty_opts;
 } LIBBPF_1.2.0;