diff mbox series

[bpf-next,v2,2/8] bpf: btf: fix struct/union/fwd types with kind_flag

Message ID 20181214233427.2667602-1-yhs@fb.com
State Changes Requested, archived
Delegated to: BPF Maintainers
Headers show
Series bpf: btf: fix struct/union/fwd types with kind_flag | expand

Commit Message

Yonghong Song Dec. 14, 2018, 11:34 p.m. UTC
This patch fixed two issues with BTF. One is related to
struct/union bitfield encoding and the other is related to
forward type.

Issue #1 and solution:

Comments

Martin KaFai Lau Dec. 15, 2018, 4:37 p.m. UTC | #1
On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
> This patch fixed two issues with BTF. One is related to
> struct/union bitfield encoding and the other is related to
> forward type.
> 
> Issue #1 and solution:
> ======================
> 
> Current btf encoding of bitfield follows what pahole generates.
> For each bitfield, pahole will duplicate the type chain and
> put the bitfield size at the final int or enum type.
> Since the BTF enum type cannot encode bit size,
> pahole workarounds the issue by generating
> an int type whenever the enum bit size is not 32.
> 
> For example,
>   -bash-4.4$ cat t.c
>   typedef int ___int;
>   enum A { A1, A2, A3 };
>   struct t {
>     int a[5];
>     ___int b:4;
>     volatile enum A c:4;
>   } g;
>   -bash-4.4$ gcc -c -O2 -g t.c
> The current kernel supports the following BTF encoding:
>   $ pahole -JV t.o
>   [1] TYPEDEF ___int type_id=2
>   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>   [3] ENUM A size=4 vlen=3
>         A1 val=0
>         A2 val=1
>         A3 val=2
>   [4] STRUCT t size=24 vlen=3
>         a type_id=5 bits_offset=0
>         b type_id=9 bits_offset=160
>         c type_id=11 bits_offset=164
>   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>   [7] VOLATILE (anon) type_id=3
>   [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
>   [9] TYPEDEF ___int type_id=8
>   [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
>   [11] VOLATILE (anon) type_id=10
> 
> Two issues are in the above:
>   . by changing enum type to int, we lost the original
>     type information and this will not be ideal later
>     when we try to convert BTF to a header file.
>   . the type duplication for bitfields will cause
>     BTF bloat. Duplicated types cannot be deduplicated
>     later if the bitfield size is different.
> 
> To fix this issue, this patch implemented a compatible
> change for BTF struct type encoding:
>   . the bit 31 of struct_type->info, previously reserved,
>     now is used to indicate whether bitfield_size is
>     encoded in btf_member or not.
>   . if bit 31 of struct_type->info is set,
>     btf_member->offset will encode like:
>       bit 0 - 23: bit offset
>       bit 24 - 31: bitfield size
>     if bit 31 is not set, the old behavior is preserved:
>       bit 0 - 31: bit offset
> 
> So if the struct contains a bit field, the maximum bit offset
> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
> bitfield size will be 256 which is enough for today as maximum
> bitfield in compiler can be 128 where int128 type is supported.
> 
> This kernel patch intends to support the new BTF encoding:
>   $ pahole -JV t.o
>   [1] TYPEDEF ___int type_id=2
>   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>   [3] ENUM A size=4 vlen=3
>         A1 val=0
>         A2 val=1
>         A3 val=2
>   [4] STRUCT t kind_flag=1 size=24 vlen=3
>         a type_id=5 bitfield_size=0 bits_offset=0
>         b type_id=1 bitfield_size=4 bits_offset=160
>         c type_id=7 bitfield_size=4 bits_offset=164
>   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>   [7] VOLATILE (anon) type_id=3
> 
> Issue #2 and solution:
> ======================
> 
> Current forward type in BTF does not specify whether the original
> type is struct or union. This will not work for type pretty print
> and BTF-to-header-file conversion as struct/union must be specified.
>   $ cat tt.c
>   struct t;
>   union u;
>   int foo(struct t *t, union u *u) { return 0; }
>   $ gcc -c -g -O2 tt.c
>   $ pahole -JV tt.o
>   [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>   [2] FWD t type_id=0
>   [3] PTR (anon) type_id=2
>   [4] FWD u type_id=0
>   [5] PTR (anon) type_id=4
> 
> To fix this issue, similar to issue #1, type->info bit 31
> is used. If the bit is set, it is union type. Otherwise, it is
> a struct type.
> 
>   $ pahole -JV tt.o
>   [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>   [2] FWD t kind_flag=0 type_id=0
>   [3] PTR (anon) kind_flag=0 type_id=2
>   [4] FWD u kind_flag=1 type_id=0
>   [5] PTR (anon) kind_flag=0 type_id=4
> 
> Pahole/LLVM change:
> ===================
> 
> The new kind_flag functionality has been implemented in pahole
> and llvm:
>   https://github.com/yonghong-song/pahole/tree/bitfield
>   https://github.com/yonghong-song/llvm/tree/bitfield
> 
> Note that pahole hasn't implemented func/func_proto kind
> and .BTF.ext. So to print function signature with bpftool,
> the llvm compiler should be used.
> 
> Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
> Signed-off-by: Martin KaFai Lau <kafai@fb.com>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  include/uapi/linux/btf.h |  15 ++-
>  kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
>  2 files changed, 267 insertions(+), 22 deletions(-)
> 
> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> index 14f66948fc95..34aba40ed926 100644
> --- a/include/uapi/linux/btf.h
> +++ b/include/uapi/linux/btf.h
> @@ -34,7 +34,9 @@ struct btf_type {
>  	 * bits  0-15: vlen (e.g. # of struct's members)
>  	 * bits 16-23: unused
>  	 * bits 24-27: kind (e.g. int, ptr, array...etc)
> -	 * bits 28-31: unused
> +	 * bits 28-30: unused
> +	 * bit     31: kind_flag, currently used by
> +	 *             struct, union and fwd
>  	 */
>  	__u32 info;
>  	/* "size" is used by INT, ENUM, STRUCT and UNION.
> @@ -52,6 +54,7 @@ struct btf_type {
>  
>  #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
>  #define BTF_INFO_VLEN(info)	((info) & 0xffff)
> +#define BTF_INFO_KFLAG(info)	((info) >> 31)
>  
>  #define BTF_KIND_UNKN		0	/* Unknown	*/
>  #define BTF_KIND_INT		1	/* Integer	*/
> @@ -110,9 +113,17 @@ struct btf_array {
>  struct btf_member {
>  	__u32	name_off;
>  	__u32	type;
> -	__u32	offset;	/* offset in bits */
> +	__u32	offset;	/* [bitfield_size and] offset in bits */
>  };
>  
> +/* If the type info kind_flag set, the btf_member.offset
> + * contains both member bit offset and bitfield size, and
> + * bitfield size will set for struct/union bitfield members.
> + * Otherwise, it contains only bit offset.
> + */
nit. It may be better to move this comment to the btf_member.offset
above.

> +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
> +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
After re-thinking this setup again, I still think
having these macros in btf.h to also do the kflag checking
would be nice.

Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
depend on other facts,  the btf.h raw user must check kflag
anyway before calling BTF_MEMBER_BIT*().
Forcing a kflag check before the user can access these convenient
0xfffff and >>24 conversions may enforce this kflag check to
some extend.

Since it is in uapi, it will not be easy to change later.
The above concern could be overkill ;), just want to ensure
it has been thought through a bit more here.

It could be as easy as moving the new btf_member_bit*() from
btf.c to here and remove these two macros (or move them back to btf.c).

> +
>  /* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
>   * The exact number of btf_param is stored in the vlen (of the
>   * info in "struct btf_type").
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 72caa799e82f..ec3f80d3bef6 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -164,7 +164,7 @@
>  #define BITS_ROUNDUP_BYTES(bits) \
>  	(BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
>  
> -#define BTF_INFO_MASK 0x0f00ffff
> +#define BTF_INFO_MASK 0x8f00ffff
>  #define BTF_INT_MASK 0x0fffffff
>  #define BTF_TYPE_ID_VALID(type_id) ((type_id) <= BTF_MAX_TYPE)
>  #define BTF_STR_OFFSET_VALID(name_off) ((name_off) <= BTF_MAX_NAME_OFFSET)
> @@ -274,6 +274,10 @@ struct btf_kind_operations {
>  			    const struct btf_type *struct_type,
>  			    const struct btf_member *member,
>  			    const struct btf_type *member_type);
> +	int (*check_kflag_member)(struct btf_verifier_env *env,
> +				  const struct btf_type *struct_type,
> +				  const struct btf_member *member,
> +				  const struct btf_type *member_type);
>  	void (*log_details)(struct btf_verifier_env *env,
>  			    const struct btf_type *t);
>  	void (*seq_show)(const struct btf *btf, const struct btf_type *t,
> @@ -419,6 +423,25 @@ static u16 btf_type_vlen(const struct btf_type *t)
>  	return BTF_INFO_VLEN(t->info);
>  }
>

[ ... ]

> +static int btf_enum_check_kflag_member(struct btf_verifier_env *env,
> +				       const struct btf_type *struct_type,
> +				       const struct btf_member *member,
> +				       const struct btf_type *member_type)
> +{
> +	u32 struct_bits_off, nr_bits, bytes_end, struct_size;
> +	u32 int_bitsize = sizeof(int) * BITS_PER_BYTE;
> +
> +	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
> +	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
> +	if (!nr_bits) {
> +		nr_bits = int_bitsize;
For non bitfield enum (i.e. !nr_bits), does it have to check for
byte alignment also? i.e. BITS_PER_BYTE_MASKED(struct_bits_off).

Others look good.

> +	} else if (nr_bits > int_bitsize) {
> +		btf_verifier_log_member(env, struct_type, member,
> +					"Invalid member bitfield_size");
> +		return -EINVAL;
> +	}
> +
> +	struct_size = struct_type->size;
> +	bytes_end = BITS_ROUNDUP_BYTES(struct_bits_off + nr_bits);
> +	if (struct_size < bytes_end) {
> +		btf_verifier_log_member(env, struct_type, member,
> +					"Member exceeds struct_size");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
Yonghong Song Dec. 15, 2018, 5:42 p.m. UTC | #2
On 12/15/18 8:37 AM, Martin Lau wrote:
> On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
>> This patch fixed two issues with BTF. One is related to
>> struct/union bitfield encoding and the other is related to
>> forward type.
>>
>> Issue #1 and solution:
>> ======================
>>
>> Current btf encoding of bitfield follows what pahole generates.
>> For each bitfield, pahole will duplicate the type chain and
>> put the bitfield size at the final int or enum type.
>> Since the BTF enum type cannot encode bit size,
>> pahole workarounds the issue by generating
>> an int type whenever the enum bit size is not 32.
>>
>> For example,
>>    -bash-4.4$ cat t.c
>>    typedef int ___int;
>>    enum A { A1, A2, A3 };
>>    struct t {
>>      int a[5];
>>      ___int b:4;
>>      volatile enum A c:4;
>>    } g;
>>    -bash-4.4$ gcc -c -O2 -g t.c
>> The current kernel supports the following BTF encoding:
>>    $ pahole -JV t.o
>>    [1] TYPEDEF ___int type_id=2
>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>    [3] ENUM A size=4 vlen=3
>>          A1 val=0
>>          A2 val=1
>>          A3 val=2
>>    [4] STRUCT t size=24 vlen=3
>>          a type_id=5 bits_offset=0
>>          b type_id=9 bits_offset=160
>>          c type_id=11 bits_offset=164
>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>    [7] VOLATILE (anon) type_id=3
>>    [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
>>    [9] TYPEDEF ___int type_id=8
>>    [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
>>    [11] VOLATILE (anon) type_id=10
>>
>> Two issues are in the above:
>>    . by changing enum type to int, we lost the original
>>      type information and this will not be ideal later
>>      when we try to convert BTF to a header file.
>>    . the type duplication for bitfields will cause
>>      BTF bloat. Duplicated types cannot be deduplicated
>>      later if the bitfield size is different.
>>
>> To fix this issue, this patch implemented a compatible
>> change for BTF struct type encoding:
>>    . the bit 31 of struct_type->info, previously reserved,
>>      now is used to indicate whether bitfield_size is
>>      encoded in btf_member or not.
>>    . if bit 31 of struct_type->info is set,
>>      btf_member->offset will encode like:
>>        bit 0 - 23: bit offset
>>        bit 24 - 31: bitfield size
>>      if bit 31 is not set, the old behavior is preserved:
>>        bit 0 - 31: bit offset
>>
>> So if the struct contains a bit field, the maximum bit offset
>> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
>> bitfield size will be 256 which is enough for today as maximum
>> bitfield in compiler can be 128 where int128 type is supported.
>>
>> This kernel patch intends to support the new BTF encoding:
>>    $ pahole -JV t.o
>>    [1] TYPEDEF ___int type_id=2
>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>    [3] ENUM A size=4 vlen=3
>>          A1 val=0
>>          A2 val=1
>>          A3 val=2
>>    [4] STRUCT t kind_flag=1 size=24 vlen=3
>>          a type_id=5 bitfield_size=0 bits_offset=0
>>          b type_id=1 bitfield_size=4 bits_offset=160
>>          c type_id=7 bitfield_size=4 bits_offset=164
>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>    [7] VOLATILE (anon) type_id=3
>>
>> Issue #2 and solution:
>> ======================
>>
>> Current forward type in BTF does not specify whether the original
>> type is struct or union. This will not work for type pretty print
>> and BTF-to-header-file conversion as struct/union must be specified.
>>    $ cat tt.c
>>    struct t;
>>    union u;
>>    int foo(struct t *t, union u *u) { return 0; }
>>    $ gcc -c -g -O2 tt.c
>>    $ pahole -JV tt.o
>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>    [2] FWD t type_id=0
>>    [3] PTR (anon) type_id=2
>>    [4] FWD u type_id=0
>>    [5] PTR (anon) type_id=4
>>
>> To fix this issue, similar to issue #1, type->info bit 31
>> is used. If the bit is set, it is union type. Otherwise, it is
>> a struct type.
>>
>>    $ pahole -JV tt.o
>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>    [2] FWD t kind_flag=0 type_id=0
>>    [3] PTR (anon) kind_flag=0 type_id=2
>>    [4] FWD u kind_flag=1 type_id=0
>>    [5] PTR (anon) kind_flag=0 type_id=4
>>
>> Pahole/LLVM change:
>> ===================
>>
>> The new kind_flag functionality has been implemented in pahole
>> and llvm:
>>    https://github.com/yonghong-song/pahole/tree/bitfield
>>    https://github.com/yonghong-song/llvm/tree/bitfield
>>
>> Note that pahole hasn't implemented func/func_proto kind
>> and .BTF.ext. So to print function signature with bpftool,
>> the llvm compiler should be used.
>>
>> Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
>> Signed-off-by: Martin KaFai Lau <kafai@fb.com>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   include/uapi/linux/btf.h |  15 ++-
>>   kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
>>   2 files changed, 267 insertions(+), 22 deletions(-)
>>
>> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
>> index 14f66948fc95..34aba40ed926 100644
>> --- a/include/uapi/linux/btf.h
>> +++ b/include/uapi/linux/btf.h
>> @@ -34,7 +34,9 @@ struct btf_type {
>>   	 * bits  0-15: vlen (e.g. # of struct's members)
>>   	 * bits 16-23: unused
>>   	 * bits 24-27: kind (e.g. int, ptr, array...etc)
>> -	 * bits 28-31: unused
>> +	 * bits 28-30: unused
>> +	 * bit     31: kind_flag, currently used by
>> +	 *             struct, union and fwd
>>   	 */
>>   	__u32 info;
>>   	/* "size" is used by INT, ENUM, STRUCT and UNION.
>> @@ -52,6 +54,7 @@ struct btf_type {
>>   
>>   #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
>>   #define BTF_INFO_VLEN(info)	((info) & 0xffff)
>> +#define BTF_INFO_KFLAG(info)	((info) >> 31)
>>   
>>   #define BTF_KIND_UNKN		0	/* Unknown	*/
>>   #define BTF_KIND_INT		1	/* Integer	*/
>> @@ -110,9 +113,17 @@ struct btf_array {
>>   struct btf_member {
>>   	__u32	name_off;
>>   	__u32	type;
>> -	__u32	offset;	/* offset in bits */
>> +	__u32	offset;	/* [bitfield_size and] offset in bits */
>>   };
>>   
>> +/* If the type info kind_flag set, the btf_member.offset
>> + * contains both member bit offset and bitfield size, and
>> + * bitfield size will set for struct/union bitfield members.
>> + * Otherwise, it contains only bit offset.
>> + */
> nit. It may be better to move this comment to the btf_member.offset
> above.

Make sense. Will have smaller comments as well before the below two
macros to indicate kind_flag needs to be set for the below two
macros make sense.

> 
>> +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
>> +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
> After re-thinking this setup again, I still think
> having these macros in btf.h to also do the kflag checking
> would be nice.
> 
> Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
> depend on other facts,  the btf.h raw user must check kflag
> anyway before calling BTF_MEMBER_BIT*().
> Forcing a kflag check before the user can access these convenient
> 0xfffff and >>24 conversions may enforce this kflag check to
> some extend.
> 
> Since it is in uapi, it will not be easy to change later.
> The above concern could be overkill ;), just want to ensure
> it has been thought through a bit more here.
> 
> It could be as easy as moving the new btf_member_bit*() from
> btf.c to here and remove these two macros (or move them back to btf.c).

Not an expert in uapi design. I did not put static inline function 
(check kflag and get bitfield_size/offset) there with following reasons. 
(1). The user/kernel may want to check kflag either to simplify their 
code or with certain guaranteed properties, e.g., extra 
offset/regular_int checking. So putting macros in the header file will 
be good and necessary as we do not want end user (kernel/usr) to have 
"shift" or "and" in their codes.
(2). putting static inline functions there feels a little bit redundant 
and could be misleading here if used blindly. First, we have macros here 
which you can already get information. Second, if user just uses 
btf_member_bitfield_size() and assumes that bitfield_size = 0, that will 
be wrong since bitfield_size = 0 only if kflag is set, user needs to 
trace to base int to find real bitfield size if kflag = 0. So user has 
to be aware of kflag even if he calls btf_member_bitfield_size().

> 
>> +
>>   /* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
>>    * The exact number of btf_param is stored in the vlen (of the
>>    * info in "struct btf_type").
>> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
>> index 72caa799e82f..ec3f80d3bef6 100644
>> --- a/kernel/bpf/btf.c
>> +++ b/kernel/bpf/btf.c
>> @@ -164,7 +164,7 @@
>>   #define BITS_ROUNDUP_BYTES(bits) \
>>   	(BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
>>   
>> -#define BTF_INFO_MASK 0x0f00ffff
>> +#define BTF_INFO_MASK 0x8f00ffff
>>   #define BTF_INT_MASK 0x0fffffff
>>   #define BTF_TYPE_ID_VALID(type_id) ((type_id) <= BTF_MAX_TYPE)
>>   #define BTF_STR_OFFSET_VALID(name_off) ((name_off) <= BTF_MAX_NAME_OFFSET)
>> @@ -274,6 +274,10 @@ struct btf_kind_operations {
>>   			    const struct btf_type *struct_type,
>>   			    const struct btf_member *member,
>>   			    const struct btf_type *member_type);
>> +	int (*check_kflag_member)(struct btf_verifier_env *env,
>> +				  const struct btf_type *struct_type,
>> +				  const struct btf_member *member,
>> +				  const struct btf_type *member_type);
>>   	void (*log_details)(struct btf_verifier_env *env,
>>   			    const struct btf_type *t);
>>   	void (*seq_show)(const struct btf *btf, const struct btf_type *t,
>> @@ -419,6 +423,25 @@ static u16 btf_type_vlen(const struct btf_type *t)
>>   	return BTF_INFO_VLEN(t->info);
>>   }
>>
> 
> [ ... ]
> 
>> +static int btf_enum_check_kflag_member(struct btf_verifier_env *env,
>> +				       const struct btf_type *struct_type,
>> +				       const struct btf_member *member,
>> +				       const struct btf_type *member_type)
>> +{
>> +	u32 struct_bits_off, nr_bits, bytes_end, struct_size;
>> +	u32 int_bitsize = sizeof(int) * BITS_PER_BYTE;
>> +
>> +	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
>> +	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
>> +	if (!nr_bits) {
>> +		nr_bits = int_bitsize;
> For non bitfield enum (i.e. !nr_bits), does it have to check for
> byte alignment also? i.e. BITS_PER_BYTE_MASKED(struct_bits_off).

Good catch. Will add this in next revision.

> 
> Others look good.
> 
>> +	} else if (nr_bits > int_bitsize) {
>> +		btf_verifier_log_member(env, struct_type, member,
>> +					"Invalid member bitfield_size");
>> +		return -EINVAL;
>> +	}
>> +
>> +	struct_size = struct_type->size;
>> +	bytes_end = BITS_ROUNDUP_BYTES(struct_bits_off + nr_bits);
>> +	if (struct_size < bytes_end) {
>> +		btf_verifier_log_member(env, struct_type, member,
>> +					"Member exceeds struct_size");
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
Alexei Starovoitov Dec. 15, 2018, 5:44 p.m. UTC | #3
On Sat, Dec 15, 2018 at 04:37:06PM +0000, Martin Lau wrote:
> On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
> > This patch fixed two issues with BTF. One is related to
> > struct/union bitfield encoding and the other is related to
> > forward type.
> > 
> > Issue #1 and solution:
> > ======================
> > 
> > Current btf encoding of bitfield follows what pahole generates.
> > For each bitfield, pahole will duplicate the type chain and
> > put the bitfield size at the final int or enum type.
> > Since the BTF enum type cannot encode bit size,
> > pahole workarounds the issue by generating
> > an int type whenever the enum bit size is not 32.
> > 
> > For example,
> >   -bash-4.4$ cat t.c
> >   typedef int ___int;
> >   enum A { A1, A2, A3 };
> >   struct t {
> >     int a[5];
> >     ___int b:4;
> >     volatile enum A c:4;
> >   } g;
> >   -bash-4.4$ gcc -c -O2 -g t.c
> > The current kernel supports the following BTF encoding:
> >   $ pahole -JV t.o
> >   [1] TYPEDEF ___int type_id=2
> >   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >   [3] ENUM A size=4 vlen=3
> >         A1 val=0
> >         A2 val=1
> >         A3 val=2
> >   [4] STRUCT t size=24 vlen=3
> >         a type_id=5 bits_offset=0
> >         b type_id=9 bits_offset=160
> >         c type_id=11 bits_offset=164
> >   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
> >   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
> >   [7] VOLATILE (anon) type_id=3
> >   [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
> >   [9] TYPEDEF ___int type_id=8
> >   [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
> >   [11] VOLATILE (anon) type_id=10
> > 
> > Two issues are in the above:
> >   . by changing enum type to int, we lost the original
> >     type information and this will not be ideal later
> >     when we try to convert BTF to a header file.
> >   . the type duplication for bitfields will cause
> >     BTF bloat. Duplicated types cannot be deduplicated
> >     later if the bitfield size is different.
> > 
> > To fix this issue, this patch implemented a compatible
> > change for BTF struct type encoding:
> >   . the bit 31 of struct_type->info, previously reserved,
> >     now is used to indicate whether bitfield_size is
> >     encoded in btf_member or not.
> >   . if bit 31 of struct_type->info is set,
> >     btf_member->offset will encode like:
> >       bit 0 - 23: bit offset
> >       bit 24 - 31: bitfield size
> >     if bit 31 is not set, the old behavior is preserved:
> >       bit 0 - 31: bit offset
> > 
> > So if the struct contains a bit field, the maximum bit offset
> > will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
> > bitfield size will be 256 which is enough for today as maximum
> > bitfield in compiler can be 128 where int128 type is supported.
> > 
> > This kernel patch intends to support the new BTF encoding:
> >   $ pahole -JV t.o
> >   [1] TYPEDEF ___int type_id=2
> >   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >   [3] ENUM A size=4 vlen=3
> >         A1 val=0
> >         A2 val=1
> >         A3 val=2
> >   [4] STRUCT t kind_flag=1 size=24 vlen=3
> >         a type_id=5 bitfield_size=0 bits_offset=0
> >         b type_id=1 bitfield_size=4 bits_offset=160
> >         c type_id=7 bitfield_size=4 bits_offset=164
> >   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
> >   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
> >   [7] VOLATILE (anon) type_id=3
> > 
> > Issue #2 and solution:
> > ======================
> > 
> > Current forward type in BTF does not specify whether the original
> > type is struct or union. This will not work for type pretty print
> > and BTF-to-header-file conversion as struct/union must be specified.
> >   $ cat tt.c
> >   struct t;
> >   union u;
> >   int foo(struct t *t, union u *u) { return 0; }
> >   $ gcc -c -g -O2 tt.c
> >   $ pahole -JV tt.o
> >   [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >   [2] FWD t type_id=0
> >   [3] PTR (anon) type_id=2
> >   [4] FWD u type_id=0
> >   [5] PTR (anon) type_id=4
> > 
> > To fix this issue, similar to issue #1, type->info bit 31
> > is used. If the bit is set, it is union type. Otherwise, it is
> > a struct type.
> > 
> >   $ pahole -JV tt.o
> >   [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >   [2] FWD t kind_flag=0 type_id=0
> >   [3] PTR (anon) kind_flag=0 type_id=2
> >   [4] FWD u kind_flag=1 type_id=0
> >   [5] PTR (anon) kind_flag=0 type_id=4
> > 
> > Pahole/LLVM change:
> > ===================
> > 
> > The new kind_flag functionality has been implemented in pahole
> > and llvm:
> >   https://github.com/yonghong-song/pahole/tree/bitfield
> >   https://github.com/yonghong-song/llvm/tree/bitfield
> > 
> > Note that pahole hasn't implemented func/func_proto kind
> > and .BTF.ext. So to print function signature with bpftool,
> > the llvm compiler should be used.
> > 
> > Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
> > Signed-off-by: Martin KaFai Lau <kafai@fb.com>
> > Signed-off-by: Yonghong Song <yhs@fb.com>
> > ---
> >  include/uapi/linux/btf.h |  15 ++-
> >  kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
> >  2 files changed, 267 insertions(+), 22 deletions(-)
> > 
> > diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> > index 14f66948fc95..34aba40ed926 100644
> > --- a/include/uapi/linux/btf.h
> > +++ b/include/uapi/linux/btf.h
> > @@ -34,7 +34,9 @@ struct btf_type {
> >  	 * bits  0-15: vlen (e.g. # of struct's members)
> >  	 * bits 16-23: unused
> >  	 * bits 24-27: kind (e.g. int, ptr, array...etc)
> > -	 * bits 28-31: unused
> > +	 * bits 28-30: unused
> > +	 * bit     31: kind_flag, currently used by
> > +	 *             struct, union and fwd
> >  	 */
> >  	__u32 info;
> >  	/* "size" is used by INT, ENUM, STRUCT and UNION.
> > @@ -52,6 +54,7 @@ struct btf_type {
> >  
> >  #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
> >  #define BTF_INFO_VLEN(info)	((info) & 0xffff)
> > +#define BTF_INFO_KFLAG(info)	((info) >> 31)
> >  
> >  #define BTF_KIND_UNKN		0	/* Unknown	*/
> >  #define BTF_KIND_INT		1	/* Integer	*/
> > @@ -110,9 +113,17 @@ struct btf_array {
> >  struct btf_member {
> >  	__u32	name_off;
> >  	__u32	type;
> > -	__u32	offset;	/* offset in bits */
> > +	__u32	offset;	/* [bitfield_size and] offset in bits */
> >  };
> >  
> > +/* If the type info kind_flag set, the btf_member.offset
> > + * contains both member bit offset and bitfield size, and
> > + * bitfield size will set for struct/union bitfield members.
> > + * Otherwise, it contains only bit offset.
> > + */
> nit. It may be better to move this comment to the btf_member.offset
> above.
> 
> > +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
> > +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
> After re-thinking this setup again, I still think
> having these macros in btf.h to also do the kflag checking
> would be nice.
> 
> Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
> depend on other facts,  the btf.h raw user must check kflag
> anyway before calling BTF_MEMBER_BIT*().
> Forcing a kflag check before the user can access these convenient
> 0xfffff and >>24 conversions may enforce this kflag check to
> some extend.
> 
> Since it is in uapi, it will not be easy to change later.
> The above concern could be overkill ;), just want to ensure
> it has been thought through a bit more here.
> 
> It could be as easy as moving the new btf_member_bit*() from
> btf.c to here and remove these two macros (or move them back to btf.c).

I think moving:
+static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
+                                   const struct btf_member *member)
+{
+       return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
+                                          : 0;
+}

into uapi/btf.h may or may not be useful for btf uapi users.
What are the chances that these static inline helpers will be
reused by BTF logic in libbpf or other libs?
At this point we don't know.
So I would keep btf.h minimal.
I agree that BTF_MEMBER_BIT_OFFSET() shouldn't be reused blindly.
The users have to do BTF_INFO_KFLAG() check first.
But this is the case for pretty much all of BTF data structures.
In that sense BTF_MEMBER_BITFIELD_SIZE/BTF_MEMBER_BIT_OFFSET
serve as a documentation and fit the style of btf.h header.
imo having these two macros in uapi/btf.h is better than
replacing them with comment only.

After this set the whole BTF really needs to be documented.
Martin KaFai Lau Dec. 15, 2018, 10:10 p.m. UTC | #4
On Sat, Dec 15, 2018 at 09:44:44AM -0800, Alexei Starovoitov wrote:
> On Sat, Dec 15, 2018 at 04:37:06PM +0000, Martin Lau wrote:
> > On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
> > > This patch fixed two issues with BTF. One is related to
> > > struct/union bitfield encoding and the other is related to
> > > forward type.
> > > 
> > > Issue #1 and solution:
> > > ======================
> > > 
> > > Current btf encoding of bitfield follows what pahole generates.
> > > For each bitfield, pahole will duplicate the type chain and
> > > put the bitfield size at the final int or enum type.
> > > Since the BTF enum type cannot encode bit size,
> > > pahole workarounds the issue by generating
> > > an int type whenever the enum bit size is not 32.
> > > 
> > > For example,
> > >   -bash-4.4$ cat t.c
> > >   typedef int ___int;
> > >   enum A { A1, A2, A3 };
> > >   struct t {
> > >     int a[5];
> > >     ___int b:4;
> > >     volatile enum A c:4;
> > >   } g;
> > >   -bash-4.4$ gcc -c -O2 -g t.c
> > > The current kernel supports the following BTF encoding:
> > >   $ pahole -JV t.o
> > >   [1] TYPEDEF ___int type_id=2
> > >   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> > >   [3] ENUM A size=4 vlen=3
> > >         A1 val=0
> > >         A2 val=1
> > >         A3 val=2
> > >   [4] STRUCT t size=24 vlen=3
> > >         a type_id=5 bits_offset=0
> > >         b type_id=9 bits_offset=160
> > >         c type_id=11 bits_offset=164
> > >   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
> > >   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
> > >   [7] VOLATILE (anon) type_id=3
> > >   [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
> > >   [9] TYPEDEF ___int type_id=8
> > >   [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
> > >   [11] VOLATILE (anon) type_id=10
> > > 
> > > Two issues are in the above:
> > >   . by changing enum type to int, we lost the original
> > >     type information and this will not be ideal later
> > >     when we try to convert BTF to a header file.
> > >   . the type duplication for bitfields will cause
> > >     BTF bloat. Duplicated types cannot be deduplicated
> > >     later if the bitfield size is different.
> > > 
> > > To fix this issue, this patch implemented a compatible
> > > change for BTF struct type encoding:
> > >   . the bit 31 of struct_type->info, previously reserved,
> > >     now is used to indicate whether bitfield_size is
> > >     encoded in btf_member or not.
> > >   . if bit 31 of struct_type->info is set,
> > >     btf_member->offset will encode like:
> > >       bit 0 - 23: bit offset
> > >       bit 24 - 31: bitfield size
> > >     if bit 31 is not set, the old behavior is preserved:
> > >       bit 0 - 31: bit offset
> > > 
> > > So if the struct contains a bit field, the maximum bit offset
> > > will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
> > > bitfield size will be 256 which is enough for today as maximum
> > > bitfield in compiler can be 128 where int128 type is supported.
> > > 
> > > This kernel patch intends to support the new BTF encoding:
> > >   $ pahole -JV t.o
> > >   [1] TYPEDEF ___int type_id=2
> > >   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> > >   [3] ENUM A size=4 vlen=3
> > >         A1 val=0
> > >         A2 val=1
> > >         A3 val=2
> > >   [4] STRUCT t kind_flag=1 size=24 vlen=3
> > >         a type_id=5 bitfield_size=0 bits_offset=0
> > >         b type_id=1 bitfield_size=4 bits_offset=160
> > >         c type_id=7 bitfield_size=4 bits_offset=164
> > >   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
> > >   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
> > >   [7] VOLATILE (anon) type_id=3
> > > 
> > > Issue #2 and solution:
> > > ======================
> > > 
> > > Current forward type in BTF does not specify whether the original
> > > type is struct or union. This will not work for type pretty print
> > > and BTF-to-header-file conversion as struct/union must be specified.
> > >   $ cat tt.c
> > >   struct t;
> > >   union u;
> > >   int foo(struct t *t, union u *u) { return 0; }
> > >   $ gcc -c -g -O2 tt.c
> > >   $ pahole -JV tt.o
> > >   [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> > >   [2] FWD t type_id=0
> > >   [3] PTR (anon) type_id=2
> > >   [4] FWD u type_id=0
> > >   [5] PTR (anon) type_id=4
> > > 
> > > To fix this issue, similar to issue #1, type->info bit 31
> > > is used. If the bit is set, it is union type. Otherwise, it is
> > > a struct type.
> > > 
> > >   $ pahole -JV tt.o
> > >   [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> > >   [2] FWD t kind_flag=0 type_id=0
> > >   [3] PTR (anon) kind_flag=0 type_id=2
> > >   [4] FWD u kind_flag=1 type_id=0
> > >   [5] PTR (anon) kind_flag=0 type_id=4
> > > 
> > > Pahole/LLVM change:
> > > ===================
> > > 
> > > The new kind_flag functionality has been implemented in pahole
> > > and llvm:
> > >   https://github.com/yonghong-song/pahole/tree/bitfield
> > >   https://github.com/yonghong-song/llvm/tree/bitfield
> > > 
> > > Note that pahole hasn't implemented func/func_proto kind
> > > and .BTF.ext. So to print function signature with bpftool,
> > > the llvm compiler should be used.
> > > 
> > > Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
> > > Signed-off-by: Martin KaFai Lau <kafai@fb.com>
> > > Signed-off-by: Yonghong Song <yhs@fb.com>
> > > ---
> > >  include/uapi/linux/btf.h |  15 ++-
> > >  kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
> > >  2 files changed, 267 insertions(+), 22 deletions(-)
> > > 
> > > diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> > > index 14f66948fc95..34aba40ed926 100644
> > > --- a/include/uapi/linux/btf.h
> > > +++ b/include/uapi/linux/btf.h
> > > @@ -34,7 +34,9 @@ struct btf_type {
> > >  	 * bits  0-15: vlen (e.g. # of struct's members)
> > >  	 * bits 16-23: unused
> > >  	 * bits 24-27: kind (e.g. int, ptr, array...etc)
> > > -	 * bits 28-31: unused
> > > +	 * bits 28-30: unused
> > > +	 * bit     31: kind_flag, currently used by
> > > +	 *             struct, union and fwd
> > >  	 */
> > >  	__u32 info;
> > >  	/* "size" is used by INT, ENUM, STRUCT and UNION.
> > > @@ -52,6 +54,7 @@ struct btf_type {
> > >  
> > >  #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
> > >  #define BTF_INFO_VLEN(info)	((info) & 0xffff)
> > > +#define BTF_INFO_KFLAG(info)	((info) >> 31)
> > >  
> > >  #define BTF_KIND_UNKN		0	/* Unknown	*/
> > >  #define BTF_KIND_INT		1	/* Integer	*/
> > > @@ -110,9 +113,17 @@ struct btf_array {
> > >  struct btf_member {
> > >  	__u32	name_off;
> > >  	__u32	type;
> > > -	__u32	offset;	/* offset in bits */
> > > +	__u32	offset;	/* [bitfield_size and] offset in bits */
> > >  };
> > >  
> > > +/* If the type info kind_flag set, the btf_member.offset
> > > + * contains both member bit offset and bitfield size, and
> > > + * bitfield size will set for struct/union bitfield members.
> > > + * Otherwise, it contains only bit offset.
> > > + */
> > nit. It may be better to move this comment to the btf_member.offset
> > above.
> > 
> > > +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
> > > +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
> > After re-thinking this setup again, I still think
> > having these macros in btf.h to also do the kflag checking
> > would be nice.
> > 
> > Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
> > depend on other facts,  the btf.h raw user must check kflag
> > anyway before calling BTF_MEMBER_BIT*().
> > Forcing a kflag check before the user can access these convenient
> > 0xfffff and >>24 conversions may enforce this kflag check to
> > some extend.
> > 
> > Since it is in uapi, it will not be easy to change later.
> > The above concern could be overkill ;), just want to ensure
> > it has been thought through a bit more here.
> > 
> > It could be as easy as moving the new btf_member_bit*() from
> > btf.c to here and remove these two macros (or move them back to btf.c).
> 
> I think moving:
> +static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
> +                                   const struct btf_member *member)
> +{
> +       return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
> +                                          : 0;
> +}
> 
> into uapi/btf.h may or may not be useful for btf uapi users.
> What are the chances that these static inline helpers will be
> reused by BTF logic in libbpf or other libs?
> At this point we don't know.

> So I would keep btf.h minimal.
ok. Make sense

> I agree that BTF_MEMBER_BIT_OFFSET() shouldn't be reused blindly.
> The users have to do BTF_INFO_KFLAG() check first.
> But this is the case for pretty much all of BTF data structures.
Other similar situation in btf.h (i.e. a single u32 field can be
interpreted differently) has at least an union as an indication
(e.g. size and type in btf_type)

Here we cannot add the union (bitfield_offset:24 and bitfield_size:8)
and we cannot change the name "offset" also.  I am worry about
m->offset will directly be used without checking the BTF_INFO_KFLAG().

may be a "union { __u32 offset; __u32 bitsize_offset; };"......
Yonghong Song Dec. 15, 2018, 10:26 p.m. UTC | #5
On 12/15/18 2:10 PM, Martin Lau wrote:
> On Sat, Dec 15, 2018 at 09:44:44AM -0800, Alexei Starovoitov wrote:
>> On Sat, Dec 15, 2018 at 04:37:06PM +0000, Martin Lau wrote:
>>> On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
>>>> This patch fixed two issues with BTF. One is related to
>>>> struct/union bitfield encoding and the other is related to
>>>> forward type.
>>>>
>>>> Issue #1 and solution:
>>>> ======================
>>>>
>>>> Current btf encoding of bitfield follows what pahole generates.
>>>> For each bitfield, pahole will duplicate the type chain and
>>>> put the bitfield size at the final int or enum type.
>>>> Since the BTF enum type cannot encode bit size,
>>>> pahole workarounds the issue by generating
>>>> an int type whenever the enum bit size is not 32.
>>>>
>>>> For example,
>>>>    -bash-4.4$ cat t.c
>>>>    typedef int ___int;
>>>>    enum A { A1, A2, A3 };
>>>>    struct t {
>>>>      int a[5];
>>>>      ___int b:4;
>>>>      volatile enum A c:4;
>>>>    } g;
>>>>    -bash-4.4$ gcc -c -O2 -g t.c
>>>> The current kernel supports the following BTF encoding:
>>>>    $ pahole -JV t.o
>>>>    [1] TYPEDEF ___int type_id=2
>>>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>    [3] ENUM A size=4 vlen=3
>>>>          A1 val=0
>>>>          A2 val=1
>>>>          A3 val=2
>>>>    [4] STRUCT t size=24 vlen=3
>>>>          a type_id=5 bits_offset=0
>>>>          b type_id=9 bits_offset=160
>>>>          c type_id=11 bits_offset=164
>>>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>>>    [7] VOLATILE (anon) type_id=3
>>>>    [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
>>>>    [9] TYPEDEF ___int type_id=8
>>>>    [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
>>>>    [11] VOLATILE (anon) type_id=10
>>>>
>>>> Two issues are in the above:
>>>>    . by changing enum type to int, we lost the original
>>>>      type information and this will not be ideal later
>>>>      when we try to convert BTF to a header file.
>>>>    . the type duplication for bitfields will cause
>>>>      BTF bloat. Duplicated types cannot be deduplicated
>>>>      later if the bitfield size is different.
>>>>
>>>> To fix this issue, this patch implemented a compatible
>>>> change for BTF struct type encoding:
>>>>    . the bit 31 of struct_type->info, previously reserved,
>>>>      now is used to indicate whether bitfield_size is
>>>>      encoded in btf_member or not.
>>>>    . if bit 31 of struct_type->info is set,
>>>>      btf_member->offset will encode like:
>>>>        bit 0 - 23: bit offset
>>>>        bit 24 - 31: bitfield size
>>>>      if bit 31 is not set, the old behavior is preserved:
>>>>        bit 0 - 31: bit offset
>>>>
>>>> So if the struct contains a bit field, the maximum bit offset
>>>> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
>>>> bitfield size will be 256 which is enough for today as maximum
>>>> bitfield in compiler can be 128 where int128 type is supported.
>>>>
>>>> This kernel patch intends to support the new BTF encoding:
>>>>    $ pahole -JV t.o
>>>>    [1] TYPEDEF ___int type_id=2
>>>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>    [3] ENUM A size=4 vlen=3
>>>>          A1 val=0
>>>>          A2 val=1
>>>>          A3 val=2
>>>>    [4] STRUCT t kind_flag=1 size=24 vlen=3
>>>>          a type_id=5 bitfield_size=0 bits_offset=0
>>>>          b type_id=1 bitfield_size=4 bits_offset=160
>>>>          c type_id=7 bitfield_size=4 bits_offset=164
>>>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>>>    [7] VOLATILE (anon) type_id=3
>>>>
>>>> Issue #2 and solution:
>>>> ======================
>>>>
>>>> Current forward type in BTF does not specify whether the original
>>>> type is struct or union. This will not work for type pretty print
>>>> and BTF-to-header-file conversion as struct/union must be specified.
>>>>    $ cat tt.c
>>>>    struct t;
>>>>    union u;
>>>>    int foo(struct t *t, union u *u) { return 0; }
>>>>    $ gcc -c -g -O2 tt.c
>>>>    $ pahole -JV tt.o
>>>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>    [2] FWD t type_id=0
>>>>    [3] PTR (anon) type_id=2
>>>>    [4] FWD u type_id=0
>>>>    [5] PTR (anon) type_id=4
>>>>
>>>> To fix this issue, similar to issue #1, type->info bit 31
>>>> is used. If the bit is set, it is union type. Otherwise, it is
>>>> a struct type.
>>>>
>>>>    $ pahole -JV tt.o
>>>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>    [2] FWD t kind_flag=0 type_id=0
>>>>    [3] PTR (anon) kind_flag=0 type_id=2
>>>>    [4] FWD u kind_flag=1 type_id=0
>>>>    [5] PTR (anon) kind_flag=0 type_id=4
>>>>
>>>> Pahole/LLVM change:
>>>> ===================
>>>>
>>>> The new kind_flag functionality has been implemented in pahole
>>>> and llvm:
>>>>    https://github.com/yonghong-song/pahole/tree/bitfield
>>>>    https://github.com/yonghong-song/llvm/tree/bitfield
>>>>
>>>> Note that pahole hasn't implemented func/func_proto kind
>>>> and .BTF.ext. So to print function signature with bpftool,
>>>> the llvm compiler should be used.
>>>>
>>>> Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
>>>> Signed-off-by: Martin KaFai Lau <kafai@fb.com>
>>>> Signed-off-by: Yonghong Song <yhs@fb.com>
>>>> ---
>>>>   include/uapi/linux/btf.h |  15 ++-
>>>>   kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
>>>>   2 files changed, 267 insertions(+), 22 deletions(-)
>>>>
>>>> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
>>>> index 14f66948fc95..34aba40ed926 100644
>>>> --- a/include/uapi/linux/btf.h
>>>> +++ b/include/uapi/linux/btf.h
>>>> @@ -34,7 +34,9 @@ struct btf_type {
>>>>   	 * bits  0-15: vlen (e.g. # of struct's members)
>>>>   	 * bits 16-23: unused
>>>>   	 * bits 24-27: kind (e.g. int, ptr, array...etc)
>>>> -	 * bits 28-31: unused
>>>> +	 * bits 28-30: unused
>>>> +	 * bit     31: kind_flag, currently used by
>>>> +	 *             struct, union and fwd
>>>>   	 */
>>>>   	__u32 info;
>>>>   	/* "size" is used by INT, ENUM, STRUCT and UNION.
>>>> @@ -52,6 +54,7 @@ struct btf_type {
>>>>   
>>>>   #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
>>>>   #define BTF_INFO_VLEN(info)	((info) & 0xffff)
>>>> +#define BTF_INFO_KFLAG(info)	((info) >> 31)
>>>>   
>>>>   #define BTF_KIND_UNKN		0	/* Unknown	*/
>>>>   #define BTF_KIND_INT		1	/* Integer	*/
>>>> @@ -110,9 +113,17 @@ struct btf_array {
>>>>   struct btf_member {
>>>>   	__u32	name_off;
>>>>   	__u32	type;
>>>> -	__u32	offset;	/* offset in bits */
>>>> +	__u32	offset;	/* [bitfield_size and] offset in bits */
>>>>   };
>>>>   
>>>> +/* If the type info kind_flag set, the btf_member.offset
>>>> + * contains both member bit offset and bitfield size, and
>>>> + * bitfield size will set for struct/union bitfield members.
>>>> + * Otherwise, it contains only bit offset.
>>>> + */
>>> nit. It may be better to move this comment to the btf_member.offset
>>> above.
>>>
>>>> +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
>>>> +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
>>> After re-thinking this setup again, I still think
>>> having these macros in btf.h to also do the kflag checking
>>> would be nice.
>>>
>>> Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
>>> depend on other facts,  the btf.h raw user must check kflag
>>> anyway before calling BTF_MEMBER_BIT*().
>>> Forcing a kflag check before the user can access these convenient
>>> 0xfffff and >>24 conversions may enforce this kflag check to
>>> some extend.
>>>
>>> Since it is in uapi, it will not be easy to change later.
>>> The above concern could be overkill ;), just want to ensure
>>> it has been thought through a bit more here.
>>>
>>> It could be as easy as moving the new btf_member_bit*() from
>>> btf.c to here and remove these two macros (or move them back to btf.c).
>>
>> I think moving:
>> +static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
>> +                                   const struct btf_member *member)
>> +{
>> +       return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
>> +                                          : 0;
>> +}
>>
>> into uapi/btf.h may or may not be useful for btf uapi users.
>> What are the chances that these static inline helpers will be
>> reused by BTF logic in libbpf or other libs?
>> At this point we don't know.
> 
>> So I would keep btf.h minimal.
> ok. Make sense
> 
>> I agree that BTF_MEMBER_BIT_OFFSET() shouldn't be reused blindly.
>> The users have to do BTF_INFO_KFLAG() check first.
>> But this is the case for pretty much all of BTF data structures.
> Other similar situation in btf.h (i.e. a single u32 field can be
> interpreted differently) has at least an union as an indication
> (e.g. size and type in btf_type)
> 
> Here we cannot add the union (bitfield_offset:24 and bitfield_size:8)
> and we cannot change the name "offset" also.  I am worry about
> m->offset will directly be used without checking the BTF_INFO_KFLAG().
> 
> may be a "union { __u32 offset; __u32 bitsize_offset; };"......

The union with two __u32 is great idea. Maybe the
bitsize_offset becomes "bitfield_size_offset" to reflect
its real intention?

>
Daniel Borkmann Dec. 15, 2018, 10:37 p.m. UTC | #6
On 12/15/2018 12:34 AM, Yonghong Song wrote:
> This patch fixed two issues with BTF. One is related to
> struct/union bitfield encoding and the other is related to
> forward type.
> 
> Issue #1 and solution:
> ======================
> 
> Current btf encoding of bitfield follows what pahole generates.
> For each bitfield, pahole will duplicate the type chain and
> put the bitfield size at the final int or enum type.
> Since the BTF enum type cannot encode bit size,
> pahole workarounds the issue by generating
> an int type whenever the enum bit size is not 32.
> 
> For example,
>   -bash-4.4$ cat t.c
>   typedef int ___int;
>   enum A { A1, A2, A3 };
>   struct t {
>     int a[5];
>     ___int b:4;
>     volatile enum A c:4;
>   } g;
>   -bash-4.4$ gcc -c -O2 -g t.c
> The current kernel supports the following BTF encoding:
>   $ pahole -JV t.o
>   [1] TYPEDEF ___int type_id=2
>   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>   [3] ENUM A size=4 vlen=3
>         A1 val=0
>         A2 val=1
>         A3 val=2
>   [4] STRUCT t size=24 vlen=3
>         a type_id=5 bits_offset=0
>         b type_id=9 bits_offset=160
>         c type_id=11 bits_offset=164
>   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>   [7] VOLATILE (anon) type_id=3
>   [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
>   [9] TYPEDEF ___int type_id=8
>   [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
>   [11] VOLATILE (anon) type_id=10
> 
> Two issues are in the above:
>   . by changing enum type to int, we lost the original
>     type information and this will not be ideal later
>     when we try to convert BTF to a header file.
>   . the type duplication for bitfields will cause
>     BTF bloat. Duplicated types cannot be deduplicated
>     later if the bitfield size is different.
> 
> To fix this issue, this patch implemented a compatible
> change for BTF struct type encoding:
>   . the bit 31 of struct_type->info, previously reserved,
>     now is used to indicate whether bitfield_size is
>     encoded in btf_member or not.
>   . if bit 31 of struct_type->info is set,
>     btf_member->offset will encode like:
>       bit 0 - 23: bit offset
>       bit 24 - 31: bitfield size
>     if bit 31 is not set, the old behavior is preserved:
>       bit 0 - 31: bit offset
> 
> So if the struct contains a bit field, the maximum bit offset
> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
> bitfield size will be 256 which is enough for today as maximum
> bitfield in compiler can be 128 where int128 type is supported.

Looks good in general, just small nit below.

> This kernel patch intends to support the new BTF encoding:
>   $ pahole -JV t.o
>   [1] TYPEDEF ___int type_id=2
>   [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>   [3] ENUM A size=4 vlen=3
>         A1 val=0
>         A2 val=1
>         A3 val=2
>   [4] STRUCT t kind_flag=1 size=24 vlen=3
>         a type_id=5 bitfield_size=0 bits_offset=0
>         b type_id=1 bitfield_size=4 bits_offset=160
>         c type_id=7 bitfield_size=4 bits_offset=164
>   [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>   [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>   [7] VOLATILE (anon) type_id=3
[...]
> +static int btf_int_check_kflag_member(struct btf_verifier_env *env,
> +				      const struct btf_type *struct_type,
> +				      const struct btf_member *member,
> +				      const struct btf_type *member_type)
> +{
> +	u32 struct_bits_off, nr_bits, nr_int_data_bits, bytes_offset;
> +	u32 int_data = btf_type_int(member_type);
> +	u32 struct_size = struct_type->size;
> +	u32 nr_copy_bits;
> +
> +	/* a regular int type is required for the kflag int member */
> +	if (!btf_type_int_is_regular(member_type)) {
> +		btf_verifier_log_member(env, struct_type, member,
> +					"Invalid member base type");
> +		return -EINVAL;
> +	}
> +
> +	/* check sanity of bitfield size */
> +	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
> +	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
> +	nr_int_data_bits = BTF_INT_BITS(int_data);
> +	if (!nr_bits) {
> +		/* Not a bitfield member, member offset must be at byte
> +		 * boundary.
> +		 */
> +		if (BITS_PER_BYTE_MASKED(struct_bits_off)) {
> +			btf_verifier_log_member(env, struct_type, member,
> +						"Invalid member offset");
> +			return -EINVAL;
> +		}
> +
> +		nr_bits = nr_int_data_bits;
> +	} else if (nr_bits > nr_int_data_bits) {

Should the test here not include the bit offset as well aka nr_copy_bits?
Thus test would be e.g. (nr_copy_bits > nr_int_data_bits || nr_copy_bits >
BITS_PER_U64) ...

Wrt to future 256 bit support, doesn't UAPI on BTF_INT_BITS() only support
up to max 255 bits?

> +		btf_verifier_log_member(env, struct_type, member,
> +					"Invalid member bitfield_size");
> +		return -EINVAL;
> +	}
> +
> +	bytes_offset = BITS_ROUNDDOWN_BYTES(struct_bits_off);
> +	nr_copy_bits = nr_bits + BITS_PER_BYTE_MASKED(struct_bits_off);
> +	if (nr_copy_bits > BITS_PER_U64) {
> +		btf_verifier_log_member(env, struct_type, member,
> +					"nr_copy_bits exceeds 64");
> +		return -EINVAL;
> +	}
> +
> +	if (struct_size < bytes_offset ||
> +	    struct_size - bytes_offset < BITS_ROUNDUP_BYTES(nr_copy_bits)) {
> +		btf_verifier_log_member(env, struct_type, member,
> +					"Member exceeds struct_size");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
Martin KaFai Lau Dec. 15, 2018, 11:04 p.m. UTC | #7
On Sat, Dec 15, 2018 at 02:26:44PM -0800, Yonghong Song wrote:
> 
> 
> On 12/15/18 2:10 PM, Martin Lau wrote:
> > On Sat, Dec 15, 2018 at 09:44:44AM -0800, Alexei Starovoitov wrote:
> >> On Sat, Dec 15, 2018 at 04:37:06PM +0000, Martin Lau wrote:
> >>> On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
> >>>> This patch fixed two issues with BTF. One is related to
> >>>> struct/union bitfield encoding and the other is related to
> >>>> forward type.
> >>>>
> >>>> Issue #1 and solution:
> >>>> ======================
> >>>>
> >>>> Current btf encoding of bitfield follows what pahole generates.
> >>>> For each bitfield, pahole will duplicate the type chain and
> >>>> put the bitfield size at the final int or enum type.
> >>>> Since the BTF enum type cannot encode bit size,
> >>>> pahole workarounds the issue by generating
> >>>> an int type whenever the enum bit size is not 32.
> >>>>
> >>>> For example,
> >>>>    -bash-4.4$ cat t.c
> >>>>    typedef int ___int;
> >>>>    enum A { A1, A2, A3 };
> >>>>    struct t {
> >>>>      int a[5];
> >>>>      ___int b:4;
> >>>>      volatile enum A c:4;
> >>>>    } g;
> >>>>    -bash-4.4$ gcc -c -O2 -g t.c
> >>>> The current kernel supports the following BTF encoding:
> >>>>    $ pahole -JV t.o
> >>>>    [1] TYPEDEF ___int type_id=2
> >>>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >>>>    [3] ENUM A size=4 vlen=3
> >>>>          A1 val=0
> >>>>          A2 val=1
> >>>>          A3 val=2
> >>>>    [4] STRUCT t size=24 vlen=3
> >>>>          a type_id=5 bits_offset=0
> >>>>          b type_id=9 bits_offset=160
> >>>>          c type_id=11 bits_offset=164
> >>>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
> >>>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
> >>>>    [7] VOLATILE (anon) type_id=3
> >>>>    [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
> >>>>    [9] TYPEDEF ___int type_id=8
> >>>>    [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
> >>>>    [11] VOLATILE (anon) type_id=10
> >>>>
> >>>> Two issues are in the above:
> >>>>    . by changing enum type to int, we lost the original
> >>>>      type information and this will not be ideal later
> >>>>      when we try to convert BTF to a header file.
> >>>>    . the type duplication for bitfields will cause
> >>>>      BTF bloat. Duplicated types cannot be deduplicated
> >>>>      later if the bitfield size is different.
> >>>>
> >>>> To fix this issue, this patch implemented a compatible
> >>>> change for BTF struct type encoding:
> >>>>    . the bit 31 of struct_type->info, previously reserved,
> >>>>      now is used to indicate whether bitfield_size is
> >>>>      encoded in btf_member or not.
> >>>>    . if bit 31 of struct_type->info is set,
> >>>>      btf_member->offset will encode like:
> >>>>        bit 0 - 23: bit offset
> >>>>        bit 24 - 31: bitfield size
> >>>>      if bit 31 is not set, the old behavior is preserved:
> >>>>        bit 0 - 31: bit offset
> >>>>
> >>>> So if the struct contains a bit field, the maximum bit offset
> >>>> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
> >>>> bitfield size will be 256 which is enough for today as maximum
> >>>> bitfield in compiler can be 128 where int128 type is supported.
> >>>>
> >>>> This kernel patch intends to support the new BTF encoding:
> >>>>    $ pahole -JV t.o
> >>>>    [1] TYPEDEF ___int type_id=2
> >>>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >>>>    [3] ENUM A size=4 vlen=3
> >>>>          A1 val=0
> >>>>          A2 val=1
> >>>>          A3 val=2
> >>>>    [4] STRUCT t kind_flag=1 size=24 vlen=3
> >>>>          a type_id=5 bitfield_size=0 bits_offset=0
> >>>>          b type_id=1 bitfield_size=4 bits_offset=160
> >>>>          c type_id=7 bitfield_size=4 bits_offset=164
> >>>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
> >>>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
> >>>>    [7] VOLATILE (anon) type_id=3
> >>>>
> >>>> Issue #2 and solution:
> >>>> ======================
> >>>>
> >>>> Current forward type in BTF does not specify whether the original
> >>>> type is struct or union. This will not work for type pretty print
> >>>> and BTF-to-header-file conversion as struct/union must be specified.
> >>>>    $ cat tt.c
> >>>>    struct t;
> >>>>    union u;
> >>>>    int foo(struct t *t, union u *u) { return 0; }
> >>>>    $ gcc -c -g -O2 tt.c
> >>>>    $ pahole -JV tt.o
> >>>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >>>>    [2] FWD t type_id=0
> >>>>    [3] PTR (anon) type_id=2
> >>>>    [4] FWD u type_id=0
> >>>>    [5] PTR (anon) type_id=4
> >>>>
> >>>> To fix this issue, similar to issue #1, type->info bit 31
> >>>> is used. If the bit is set, it is union type. Otherwise, it is
> >>>> a struct type.
> >>>>
> >>>>    $ pahole -JV tt.o
> >>>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
> >>>>    [2] FWD t kind_flag=0 type_id=0
> >>>>    [3] PTR (anon) kind_flag=0 type_id=2
> >>>>    [4] FWD u kind_flag=1 type_id=0
> >>>>    [5] PTR (anon) kind_flag=0 type_id=4
> >>>>
> >>>> Pahole/LLVM change:
> >>>> ===================
> >>>>
> >>>> The new kind_flag functionality has been implemented in pahole
> >>>> and llvm:
> >>>>    https://github.com/yonghong-song/pahole/tree/bitfield
> >>>>    https://github.com/yonghong-song/llvm/tree/bitfield
> >>>>
> >>>> Note that pahole hasn't implemented func/func_proto kind
> >>>> and .BTF.ext. So to print function signature with bpftool,
> >>>> the llvm compiler should be used.
> >>>>
> >>>> Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
> >>>> Signed-off-by: Martin KaFai Lau <kafai@fb.com>
> >>>> Signed-off-by: Yonghong Song <yhs@fb.com>
> >>>> ---
> >>>>   include/uapi/linux/btf.h |  15 ++-
> >>>>   kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
> >>>>   2 files changed, 267 insertions(+), 22 deletions(-)
> >>>>
> >>>> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> >>>> index 14f66948fc95..34aba40ed926 100644
> >>>> --- a/include/uapi/linux/btf.h
> >>>> +++ b/include/uapi/linux/btf.h
> >>>> @@ -34,7 +34,9 @@ struct btf_type {
> >>>>   	 * bits  0-15: vlen (e.g. # of struct's members)
> >>>>   	 * bits 16-23: unused
> >>>>   	 * bits 24-27: kind (e.g. int, ptr, array...etc)
> >>>> -	 * bits 28-31: unused
> >>>> +	 * bits 28-30: unused
> >>>> +	 * bit     31: kind_flag, currently used by
> >>>> +	 *             struct, union and fwd
> >>>>   	 */
> >>>>   	__u32 info;
> >>>>   	/* "size" is used by INT, ENUM, STRUCT and UNION.
> >>>> @@ -52,6 +54,7 @@ struct btf_type {
> >>>>   
> >>>>   #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
> >>>>   #define BTF_INFO_VLEN(info)	((info) & 0xffff)
> >>>> +#define BTF_INFO_KFLAG(info)	((info) >> 31)
> >>>>   
> >>>>   #define BTF_KIND_UNKN		0	/* Unknown	*/
> >>>>   #define BTF_KIND_INT		1	/* Integer	*/
> >>>> @@ -110,9 +113,17 @@ struct btf_array {
> >>>>   struct btf_member {
> >>>>   	__u32	name_off;
> >>>>   	__u32	type;
> >>>> -	__u32	offset;	/* offset in bits */
> >>>> +	__u32	offset;	/* [bitfield_size and] offset in bits */
> >>>>   };
> >>>>   
> >>>> +/* If the type info kind_flag set, the btf_member.offset
> >>>> + * contains both member bit offset and bitfield size, and
> >>>> + * bitfield size will set for struct/union bitfield members.
> >>>> + * Otherwise, it contains only bit offset.
> >>>> + */
> >>> nit. It may be better to move this comment to the btf_member.offset
> >>> above.
> >>>
> >>>> +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
> >>>> +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
> >>> After re-thinking this setup again, I still think
> >>> having these macros in btf.h to also do the kflag checking
> >>> would be nice.
> >>>
> >>> Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
> >>> depend on other facts,  the btf.h raw user must check kflag
> >>> anyway before calling BTF_MEMBER_BIT*().
> >>> Forcing a kflag check before the user can access these convenient
> >>> 0xfffff and >>24 conversions may enforce this kflag check to
> >>> some extend.
> >>>
> >>> Since it is in uapi, it will not be easy to change later.
> >>> The above concern could be overkill ;), just want to ensure
> >>> it has been thought through a bit more here.
> >>>
> >>> It could be as easy as moving the new btf_member_bit*() from
> >>> btf.c to here and remove these two macros (or move them back to btf.c).
> >>
> >> I think moving:
> >> +static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
> >> +                                   const struct btf_member *member)
> >> +{
> >> +       return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
> >> +                                          : 0;
> >> +}
> >>
> >> into uapi/btf.h may or may not be useful for btf uapi users.
> >> What are the chances that these static inline helpers will be
> >> reused by BTF logic in libbpf or other libs?
> >> At this point we don't know.
> > 
> >> So I would keep btf.h minimal.
> > ok. Make sense
> > 
> >> I agree that BTF_MEMBER_BIT_OFFSET() shouldn't be reused blindly.
> >> The users have to do BTF_INFO_KFLAG() check first.
> >> But this is the case for pretty much all of BTF data structures.
> > Other similar situation in btf.h (i.e. a single u32 field can be
> > interpreted differently) has at least an union as an indication
> > (e.g. size and type in btf_type)
> > 
> > Here we cannot add the union (bitfield_offset:24 and bitfield_size:8)
> > and we cannot change the name "offset" also.  I am worry about
> > m->offset will directly be used without checking the BTF_INFO_KFLAG().
> > 
> > may be a "union { __u32 offset; __u32 bitsize_offset; };"......
> 
> The union with two __u32 is great idea. Maybe the
> bitsize_offset becomes "bitfield_size_offset" to reflect
> its real intention?
SGTM.  Probably also spell out when to use "offset"
and when to use "bitfield_size_offset" like the
union in "struct btf_type".  The BTF_MEMBER_BIT*() macro
may also need to adjust to access the bitfield_size_offset
instead to make the case clearer.
Yonghong Song Dec. 15, 2018, 11:12 p.m. UTC | #8
On 12/15/18 2:37 PM, Daniel Borkmann wrote:
> On 12/15/2018 12:34 AM, Yonghong Song wrote:
>> This patch fixed two issues with BTF. One is related to
>> struct/union bitfield encoding and the other is related to
>> forward type.
>>
>> Issue #1 and solution:
>> ======================
>>
>> Current btf encoding of bitfield follows what pahole generates.
>> For each bitfield, pahole will duplicate the type chain and
>> put the bitfield size at the final int or enum type.
>> Since the BTF enum type cannot encode bit size,
>> pahole workarounds the issue by generating
>> an int type whenever the enum bit size is not 32.
>>
>> For example,
>>    -bash-4.4$ cat t.c
>>    typedef int ___int;
>>    enum A { A1, A2, A3 };
>>    struct t {
>>      int a[5];
>>      ___int b:4;
>>      volatile enum A c:4;
>>    } g;
>>    -bash-4.4$ gcc -c -O2 -g t.c
>> The current kernel supports the following BTF encoding:
>>    $ pahole -JV t.o
>>    [1] TYPEDEF ___int type_id=2
>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>    [3] ENUM A size=4 vlen=3
>>          A1 val=0
>>          A2 val=1
>>          A3 val=2
>>    [4] STRUCT t size=24 vlen=3
>>          a type_id=5 bits_offset=0
>>          b type_id=9 bits_offset=160
>>          c type_id=11 bits_offset=164
>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>    [7] VOLATILE (anon) type_id=3
>>    [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
>>    [9] TYPEDEF ___int type_id=8
>>    [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
>>    [11] VOLATILE (anon) type_id=10
>>
>> Two issues are in the above:
>>    . by changing enum type to int, we lost the original
>>      type information and this will not be ideal later
>>      when we try to convert BTF to a header file.
>>    . the type duplication for bitfields will cause
>>      BTF bloat. Duplicated types cannot be deduplicated
>>      later if the bitfield size is different.
>>
>> To fix this issue, this patch implemented a compatible
>> change for BTF struct type encoding:
>>    . the bit 31 of struct_type->info, previously reserved,
>>      now is used to indicate whether bitfield_size is
>>      encoded in btf_member or not.
>>    . if bit 31 of struct_type->info is set,
>>      btf_member->offset will encode like:
>>        bit 0 - 23: bit offset
>>        bit 24 - 31: bitfield size
>>      if bit 31 is not set, the old behavior is preserved:
>>        bit 0 - 31: bit offset
>>
>> So if the struct contains a bit field, the maximum bit offset
>> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
>> bitfield size will be 256 which is enough for today as maximum
>> bitfield in compiler can be 128 where int128 type is supported.
> 
> Looks good in general, just small nit below.
> 
>> This kernel patch intends to support the new BTF encoding:
>>    $ pahole -JV t.o
>>    [1] TYPEDEF ___int type_id=2
>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>    [3] ENUM A size=4 vlen=3
>>          A1 val=0
>>          A2 val=1
>>          A3 val=2
>>    [4] STRUCT t kind_flag=1 size=24 vlen=3
>>          a type_id=5 bitfield_size=0 bits_offset=0
>>          b type_id=1 bitfield_size=4 bits_offset=160
>>          c type_id=7 bitfield_size=4 bits_offset=164
>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>    [7] VOLATILE (anon) type_id=3
> [...]
>> +static int btf_int_check_kflag_member(struct btf_verifier_env *env,
>> +				      const struct btf_type *struct_type,
>> +				      const struct btf_member *member,
>> +				      const struct btf_type *member_type)
>> +{
>> +	u32 struct_bits_off, nr_bits, nr_int_data_bits, bytes_offset;
>> +	u32 int_data = btf_type_int(member_type);
>> +	u32 struct_size = struct_type->size;
>> +	u32 nr_copy_bits;
>> +
>> +	/* a regular int type is required for the kflag int member */
>> +	if (!btf_type_int_is_regular(member_type)) {
>> +		btf_verifier_log_member(env, struct_type, member,
>> +					"Invalid member base type");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* check sanity of bitfield size */
>> +	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
>> +	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
>> +	nr_int_data_bits = BTF_INT_BITS(int_data);
>> +	if (!nr_bits) {
>> +		/* Not a bitfield member, member offset must be at byte
>> +		 * boundary.
>> +		 */
>> +		if (BITS_PER_BYTE_MASKED(struct_bits_off)) {
>> +			btf_verifier_log_member(env, struct_type, member,
>> +						"Invalid member offset");
>> +			return -EINVAL;
>> +		}
>> +
>> +		nr_bits = nr_int_data_bits;
>> +	} else if (nr_bits > nr_int_data_bits) {
> 
> Should the test here not include the bit offset as well aka nr_copy_bits?
> Thus test would be e.g. (nr_copy_bits > nr_int_data_bits || nr_copy_bits >
> BITS_PER_U64) ...

The test here is strictly checking whether the bitfield size is covered 
by underlying int type.
    struct t {
       int a:1;
       char b:8;
       ...
    }
In this case, for member "b", bitsize = 8, nr_copy_bits = 9, 
nr_int_data_bits = 8. nr_copy_bits > nr_int_data_bits, but it is legal.


> Wrt to future 256 bit support, doesn't UAPI on BTF_INT_BITS() only support
> up to max 255 bits?

This is a good question. BTF_INT_BITS() can be extended to maximum 
0xffff. There are bits available there.

When the kind_flag is set, the bitfield size can range from 1 to 255,
the same as today BTF_INT_BITS range. If user writes
struct t {
    ...
    int256 m:256;
    ...
};
The bitfield size information will not get encoded and the member "m" 
will just refer to a base type int256, the same as
struct t {
    ...
    int256 m;
    ...
};

Did you any problem with this?

> 
>> +		btf_verifier_log_member(env, struct_type, member,
>> +					"Invalid member bitfield_size");
>> +		return -EINVAL;
>> +	}
>> +
>> +	bytes_offset = BITS_ROUNDDOWN_BYTES(struct_bits_off);
>> +	nr_copy_bits = nr_bits + BITS_PER_BYTE_MASKED(struct_bits_off);
>> +	if (nr_copy_bits > BITS_PER_U64) {
>> +		btf_verifier_log_member(env, struct_type, member,
>> +					"nr_copy_bits exceeds 64");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (struct_size < bytes_offset ||
>> +	    struct_size - bytes_offset < BITS_ROUNDUP_BYTES(nr_copy_bits)) {
>> +		btf_verifier_log_member(env, struct_type, member,
>> +					"Member exceeds struct_size");
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
Yonghong Song Dec. 15, 2018, 11:13 p.m. UTC | #9
On 12/15/18 3:04 PM, Martin Lau wrote:
> On Sat, Dec 15, 2018 at 02:26:44PM -0800, Yonghong Song wrote:
>>
>>
>> On 12/15/18 2:10 PM, Martin Lau wrote:
>>> On Sat, Dec 15, 2018 at 09:44:44AM -0800, Alexei Starovoitov wrote:
>>>> On Sat, Dec 15, 2018 at 04:37:06PM +0000, Martin Lau wrote:
>>>>> On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
>>>>>> This patch fixed two issues with BTF. One is related to
>>>>>> struct/union bitfield encoding and the other is related to
>>>>>> forward type.
>>>>>>
>>>>>> Issue #1 and solution:
>>>>>> ======================
>>>>>>
>>>>>> Current btf encoding of bitfield follows what pahole generates.
>>>>>> For each bitfield, pahole will duplicate the type chain and
>>>>>> put the bitfield size at the final int or enum type.
>>>>>> Since the BTF enum type cannot encode bit size,
>>>>>> pahole workarounds the issue by generating
>>>>>> an int type whenever the enum bit size is not 32.
>>>>>>
>>>>>> For example,
>>>>>>     -bash-4.4$ cat t.c
>>>>>>     typedef int ___int;
>>>>>>     enum A { A1, A2, A3 };
>>>>>>     struct t {
>>>>>>       int a[5];
>>>>>>       ___int b:4;
>>>>>>       volatile enum A c:4;
>>>>>>     } g;
>>>>>>     -bash-4.4$ gcc -c -O2 -g t.c
>>>>>> The current kernel supports the following BTF encoding:
>>>>>>     $ pahole -JV t.o
>>>>>>     [1] TYPEDEF ___int type_id=2
>>>>>>     [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>>>     [3] ENUM A size=4 vlen=3
>>>>>>           A1 val=0
>>>>>>           A2 val=1
>>>>>>           A3 val=2
>>>>>>     [4] STRUCT t size=24 vlen=3
>>>>>>           a type_id=5 bits_offset=0
>>>>>>           b type_id=9 bits_offset=160
>>>>>>           c type_id=11 bits_offset=164
>>>>>>     [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>>>>>     [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>>>>>     [7] VOLATILE (anon) type_id=3
>>>>>>     [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
>>>>>>     [9] TYPEDEF ___int type_id=8
>>>>>>     [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
>>>>>>     [11] VOLATILE (anon) type_id=10
>>>>>>
>>>>>> Two issues are in the above:
>>>>>>     . by changing enum type to int, we lost the original
>>>>>>       type information and this will not be ideal later
>>>>>>       when we try to convert BTF to a header file.
>>>>>>     . the type duplication for bitfields will cause
>>>>>>       BTF bloat. Duplicated types cannot be deduplicated
>>>>>>       later if the bitfield size is different.
>>>>>>
>>>>>> To fix this issue, this patch implemented a compatible
>>>>>> change for BTF struct type encoding:
>>>>>>     . the bit 31 of struct_type->info, previously reserved,
>>>>>>       now is used to indicate whether bitfield_size is
>>>>>>       encoded in btf_member or not.
>>>>>>     . if bit 31 of struct_type->info is set,
>>>>>>       btf_member->offset will encode like:
>>>>>>         bit 0 - 23: bit offset
>>>>>>         bit 24 - 31: bitfield size
>>>>>>       if bit 31 is not set, the old behavior is preserved:
>>>>>>         bit 0 - 31: bit offset
>>>>>>
>>>>>> So if the struct contains a bit field, the maximum bit offset
>>>>>> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
>>>>>> bitfield size will be 256 which is enough for today as maximum
>>>>>> bitfield in compiler can be 128 where int128 type is supported.
>>>>>>
>>>>>> This kernel patch intends to support the new BTF encoding:
>>>>>>     $ pahole -JV t.o
>>>>>>     [1] TYPEDEF ___int type_id=2
>>>>>>     [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>>>     [3] ENUM A size=4 vlen=3
>>>>>>           A1 val=0
>>>>>>           A2 val=1
>>>>>>           A3 val=2
>>>>>>     [4] STRUCT t kind_flag=1 size=24 vlen=3
>>>>>>           a type_id=5 bitfield_size=0 bits_offset=0
>>>>>>           b type_id=1 bitfield_size=4 bits_offset=160
>>>>>>           c type_id=7 bitfield_size=4 bits_offset=164
>>>>>>     [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>>>>>     [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>>>>>     [7] VOLATILE (anon) type_id=3
>>>>>>
>>>>>> Issue #2 and solution:
>>>>>> ======================
>>>>>>
>>>>>> Current forward type in BTF does not specify whether the original
>>>>>> type is struct or union. This will not work for type pretty print
>>>>>> and BTF-to-header-file conversion as struct/union must be specified.
>>>>>>     $ cat tt.c
>>>>>>     struct t;
>>>>>>     union u;
>>>>>>     int foo(struct t *t, union u *u) { return 0; }
>>>>>>     $ gcc -c -g -O2 tt.c
>>>>>>     $ pahole -JV tt.o
>>>>>>     [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>>>     [2] FWD t type_id=0
>>>>>>     [3] PTR (anon) type_id=2
>>>>>>     [4] FWD u type_id=0
>>>>>>     [5] PTR (anon) type_id=4
>>>>>>
>>>>>> To fix this issue, similar to issue #1, type->info bit 31
>>>>>> is used. If the bit is set, it is union type. Otherwise, it is
>>>>>> a struct type.
>>>>>>
>>>>>>     $ pahole -JV tt.o
>>>>>>     [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>>>>     [2] FWD t kind_flag=0 type_id=0
>>>>>>     [3] PTR (anon) kind_flag=0 type_id=2
>>>>>>     [4] FWD u kind_flag=1 type_id=0
>>>>>>     [5] PTR (anon) kind_flag=0 type_id=4
>>>>>>
>>>>>> Pahole/LLVM change:
>>>>>> ===================
>>>>>>
>>>>>> The new kind_flag functionality has been implemented in pahole
>>>>>> and llvm:
>>>>>>     https://github.com/yonghong-song/pahole/tree/bitfield
>>>>>>     https://github.com/yonghong-song/llvm/tree/bitfield
>>>>>>
>>>>>> Note that pahole hasn't implemented func/func_proto kind
>>>>>> and .BTF.ext. So to print function signature with bpftool,
>>>>>> the llvm compiler should be used.
>>>>>>
>>>>>> Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
>>>>>> Signed-off-by: Martin KaFai Lau <kafai@fb.com>
>>>>>> Signed-off-by: Yonghong Song <yhs@fb.com>
>>>>>> ---
>>>>>>    include/uapi/linux/btf.h |  15 ++-
>>>>>>    kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
>>>>>>    2 files changed, 267 insertions(+), 22 deletions(-)
>>>>>>
>>>>>> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
>>>>>> index 14f66948fc95..34aba40ed926 100644
>>>>>> --- a/include/uapi/linux/btf.h
>>>>>> +++ b/include/uapi/linux/btf.h
>>>>>> @@ -34,7 +34,9 @@ struct btf_type {
>>>>>>    	 * bits  0-15: vlen (e.g. # of struct's members)
>>>>>>    	 * bits 16-23: unused
>>>>>>    	 * bits 24-27: kind (e.g. int, ptr, array...etc)
>>>>>> -	 * bits 28-31: unused
>>>>>> +	 * bits 28-30: unused
>>>>>> +	 * bit     31: kind_flag, currently used by
>>>>>> +	 *             struct, union and fwd
>>>>>>    	 */
>>>>>>    	__u32 info;
>>>>>>    	/* "size" is used by INT, ENUM, STRUCT and UNION.
>>>>>> @@ -52,6 +54,7 @@ struct btf_type {
>>>>>>    
>>>>>>    #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
>>>>>>    #define BTF_INFO_VLEN(info)	((info) & 0xffff)
>>>>>> +#define BTF_INFO_KFLAG(info)	((info) >> 31)
>>>>>>    
>>>>>>    #define BTF_KIND_UNKN		0	/* Unknown	*/
>>>>>>    #define BTF_KIND_INT		1	/* Integer	*/
>>>>>> @@ -110,9 +113,17 @@ struct btf_array {
>>>>>>    struct btf_member {
>>>>>>    	__u32	name_off;
>>>>>>    	__u32	type;
>>>>>> -	__u32	offset;	/* offset in bits */
>>>>>> +	__u32	offset;	/* [bitfield_size and] offset in bits */
>>>>>>    };
>>>>>>    
>>>>>> +/* If the type info kind_flag set, the btf_member.offset
>>>>>> + * contains both member bit offset and bitfield size, and
>>>>>> + * bitfield size will set for struct/union bitfield members.
>>>>>> + * Otherwise, it contains only bit offset.
>>>>>> + */
>>>>> nit. It may be better to move this comment to the btf_member.offset
>>>>> above.
>>>>>
>>>>>> +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
>>>>>> +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
>>>>> After re-thinking this setup again, I still think
>>>>> having these macros in btf.h to also do the kflag checking
>>>>> would be nice.
>>>>>
>>>>> Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
>>>>> depend on other facts,  the btf.h raw user must check kflag
>>>>> anyway before calling BTF_MEMBER_BIT*().
>>>>> Forcing a kflag check before the user can access these convenient
>>>>> 0xfffff and >>24 conversions may enforce this kflag check to
>>>>> some extend.
>>>>>
>>>>> Since it is in uapi, it will not be easy to change later.
>>>>> The above concern could be overkill ;), just want to ensure
>>>>> it has been thought through a bit more here.
>>>>>
>>>>> It could be as easy as moving the new btf_member_bit*() from
>>>>> btf.c to here and remove these two macros (or move them back to btf.c).
>>>>
>>>> I think moving:
>>>> +static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
>>>> +                                   const struct btf_member *member)
>>>> +{
>>>> +       return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
>>>> +                                          : 0;
>>>> +}
>>>>
>>>> into uapi/btf.h may or may not be useful for btf uapi users.
>>>> What are the chances that these static inline helpers will be
>>>> reused by BTF logic in libbpf or other libs?
>>>> At this point we don't know.
>>>
>>>> So I would keep btf.h minimal.
>>> ok. Make sense
>>>
>>>> I agree that BTF_MEMBER_BIT_OFFSET() shouldn't be reused blindly.
>>>> The users have to do BTF_INFO_KFLAG() check first.
>>>> But this is the case for pretty much all of BTF data structures.
>>> Other similar situation in btf.h (i.e. a single u32 field can be
>>> interpreted differently) has at least an union as an indication
>>> (e.g. size and type in btf_type)
>>>
>>> Here we cannot add the union (bitfield_offset:24 and bitfield_size:8)
>>> and we cannot change the name "offset" also.  I am worry about
>>> m->offset will directly be used without checking the BTF_INFO_KFLAG().
>>>
>>> may be a "union { __u32 offset; __u32 bitsize_offset; };"......
>>
>> The union with two __u32 is great idea. Maybe the
>> bitsize_offset becomes "bitfield_size_offset" to reflect
>> its real intention?
> SGTM.  Probably also spell out when to use "offset"
> and when to use "bitfield_size_offset" like the
> union in "struct btf_type".  The BTF_MEMBER_BIT*() macro
> may also need to adjust to access the bitfield_size_offset
> instead to make the case clearer.

Sounds good. This is my plan to do as well.
Alexei Starovoitov Dec. 15, 2018, 11:19 p.m. UTC | #10
On 12/15/18 3:13 PM, Yonghong Song wrote:
>> may be a "union { __u32 offset; __u32 bitsize_offset; };"......
> The union with two __u32 is great idea. Maybe the
> bitsize_offset becomes "bitfield_size_offset" to reflect
> its real intention?

I don't think union and verbose name will help.
imo it's add confusion.
I prefer to keep it as-is with simple 'offset' name.
Yonghong Song Dec. 16, 2018, 6:18 a.m. UTC | #11
On 12/15/18 9:44 AM, Alexei Starovoitov wrote:
> On Sat, Dec 15, 2018 at 04:37:06PM +0000, Martin Lau wrote:
>> On Fri, Dec 14, 2018 at 03:34:27PM -0800, Yonghong Song wrote:
>>> This patch fixed two issues with BTF. One is related to
>>> struct/union bitfield encoding and the other is related to
>>> forward type.
>>>
>>> Issue #1 and solution:
>>> ======================
>>>
>>> Current btf encoding of bitfield follows what pahole generates.
>>> For each bitfield, pahole will duplicate the type chain and
>>> put the bitfield size at the final int or enum type.
>>> Since the BTF enum type cannot encode bit size,
>>> pahole workarounds the issue by generating
>>> an int type whenever the enum bit size is not 32.
>>>
>>> For example,
>>>    -bash-4.4$ cat t.c
>>>    typedef int ___int;
>>>    enum A { A1, A2, A3 };
>>>    struct t {
>>>      int a[5];
>>>      ___int b:4;
>>>      volatile enum A c:4;
>>>    } g;
>>>    -bash-4.4$ gcc -c -O2 -g t.c
>>> The current kernel supports the following BTF encoding:
>>>    $ pahole -JV t.o
>>>    [1] TYPEDEF ___int type_id=2
>>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>    [3] ENUM A size=4 vlen=3
>>>          A1 val=0
>>>          A2 val=1
>>>          A3 val=2
>>>    [4] STRUCT t size=24 vlen=3
>>>          a type_id=5 bits_offset=0
>>>          b type_id=9 bits_offset=160
>>>          c type_id=11 bits_offset=164
>>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>>    [7] VOLATILE (anon) type_id=3
>>>    [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
>>>    [9] TYPEDEF ___int type_id=8
>>>    [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
>>>    [11] VOLATILE (anon) type_id=10
>>>
>>> Two issues are in the above:
>>>    . by changing enum type to int, we lost the original
>>>      type information and this will not be ideal later
>>>      when we try to convert BTF to a header file.
>>>    . the type duplication for bitfields will cause
>>>      BTF bloat. Duplicated types cannot be deduplicated
>>>      later if the bitfield size is different.
>>>
>>> To fix this issue, this patch implemented a compatible
>>> change for BTF struct type encoding:
>>>    . the bit 31 of struct_type->info, previously reserved,
>>>      now is used to indicate whether bitfield_size is
>>>      encoded in btf_member or not.
>>>    . if bit 31 of struct_type->info is set,
>>>      btf_member->offset will encode like:
>>>        bit 0 - 23: bit offset
>>>        bit 24 - 31: bitfield size
>>>      if bit 31 is not set, the old behavior is preserved:
>>>        bit 0 - 31: bit offset
>>>
>>> So if the struct contains a bit field, the maximum bit offset
>>> will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
>>> bitfield size will be 256 which is enough for today as maximum
>>> bitfield in compiler can be 128 where int128 type is supported.
>>>
>>> This kernel patch intends to support the new BTF encoding:
>>>    $ pahole -JV t.o
>>>    [1] TYPEDEF ___int type_id=2
>>>    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>    [3] ENUM A size=4 vlen=3
>>>          A1 val=0
>>>          A2 val=1
>>>          A3 val=2
>>>    [4] STRUCT t kind_flag=1 size=24 vlen=3
>>>          a type_id=5 bitfield_size=0 bits_offset=0
>>>          b type_id=1 bitfield_size=4 bits_offset=160
>>>          c type_id=7 bitfield_size=4 bits_offset=164
>>>    [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
>>>    [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
>>>    [7] VOLATILE (anon) type_id=3
>>>
>>> Issue #2 and solution:
>>> ======================
>>>
>>> Current forward type in BTF does not specify whether the original
>>> type is struct or union. This will not work for type pretty print
>>> and BTF-to-header-file conversion as struct/union must be specified.
>>>    $ cat tt.c
>>>    struct t;
>>>    union u;
>>>    int foo(struct t *t, union u *u) { return 0; }
>>>    $ gcc -c -g -O2 tt.c
>>>    $ pahole -JV tt.o
>>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>    [2] FWD t type_id=0
>>>    [3] PTR (anon) type_id=2
>>>    [4] FWD u type_id=0
>>>    [5] PTR (anon) type_id=4
>>>
>>> To fix this issue, similar to issue #1, type->info bit 31
>>> is used. If the bit is set, it is union type. Otherwise, it is
>>> a struct type.
>>>
>>>    $ pahole -JV tt.o
>>>    [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
>>>    [2] FWD t kind_flag=0 type_id=0
>>>    [3] PTR (anon) kind_flag=0 type_id=2
>>>    [4] FWD u kind_flag=1 type_id=0
>>>    [5] PTR (anon) kind_flag=0 type_id=4
>>>
>>> Pahole/LLVM change:
>>> ===================
>>>
>>> The new kind_flag functionality has been implemented in pahole
>>> and llvm:
>>>    https://github.com/yonghong-song/pahole/tree/bitfield
>>>    https://github.com/yonghong-song/llvm/tree/bitfield
>>>
>>> Note that pahole hasn't implemented func/func_proto kind
>>> and .BTF.ext. So to print function signature with bpftool,
>>> the llvm compiler should be used.
>>>
>>> Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
>>> Signed-off-by: Martin KaFai Lau <kafai@fb.com>
>>> Signed-off-by: Yonghong Song <yhs@fb.com>
>>> ---
>>>   include/uapi/linux/btf.h |  15 ++-
>>>   kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
>>>   2 files changed, 267 insertions(+), 22 deletions(-)
>>>
>>> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
>>> index 14f66948fc95..34aba40ed926 100644
>>> --- a/include/uapi/linux/btf.h
>>> +++ b/include/uapi/linux/btf.h
>>> @@ -34,7 +34,9 @@ struct btf_type {
>>>   	 * bits  0-15: vlen (e.g. # of struct's members)
>>>   	 * bits 16-23: unused
>>>   	 * bits 24-27: kind (e.g. int, ptr, array...etc)
>>> -	 * bits 28-31: unused
>>> +	 * bits 28-30: unused
>>> +	 * bit     31: kind_flag, currently used by
>>> +	 *             struct, union and fwd
>>>   	 */
>>>   	__u32 info;
>>>   	/* "size" is used by INT, ENUM, STRUCT and UNION.
>>> @@ -52,6 +54,7 @@ struct btf_type {
>>>   
>>>   #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
>>>   #define BTF_INFO_VLEN(info)	((info) & 0xffff)
>>> +#define BTF_INFO_KFLAG(info)	((info) >> 31)
>>>   
>>>   #define BTF_KIND_UNKN		0	/* Unknown	*/
>>>   #define BTF_KIND_INT		1	/* Integer	*/
>>> @@ -110,9 +113,17 @@ struct btf_array {
>>>   struct btf_member {
>>>   	__u32	name_off;
>>>   	__u32	type;
>>> -	__u32	offset;	/* offset in bits */
>>> +	__u32	offset;	/* [bitfield_size and] offset in bits */
>>>   };
>>>   
>>> +/* If the type info kind_flag set, the btf_member.offset
>>> + * contains both member bit offset and bitfield size, and
>>> + * bitfield size will set for struct/union bitfield members.
>>> + * Otherwise, it contains only bit offset.
>>> + */
>> nit. It may be better to move this comment to the btf_member.offset
>> above.
>>
>>> +#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
>>> +#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
>> After re-thinking this setup again, I still think
>> having these macros in btf.h to also do the kflag checking
>> would be nice.
>>
>> Unlike BTF_INFO_KIND() and BTF_INT_ENCODING() which don't
>> depend on other facts,  the btf.h raw user must check kflag
>> anyway before calling BTF_MEMBER_BIT*().
>> Forcing a kflag check before the user can access these convenient
>> 0xfffff and >>24 conversions may enforce this kflag check to
>> some extend.
>>
>> Since it is in uapi, it will not be easy to change later.
>> The above concern could be overkill ;), just want to ensure
>> it has been thought through a bit more here.
>>
>> It could be as easy as moving the new btf_member_bit*() from
>> btf.c to here and remove these two macros (or move them back to btf.c).
> 
> I think moving:
> +static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
> +                                   const struct btf_member *member)
> +{
> +       return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
> +                                          : 0;
> +}
> 
> into uapi/btf.h may or may not be useful for btf uapi users.
> What are the chances that these static inline helpers will be
> reused by BTF logic in libbpf or other libs?
> At this point we don't know.
> So I would keep btf.h minimal.
> I agree that BTF_MEMBER_BIT_OFFSET() shouldn't be reused blindly.
> The users have to do BTF_INFO_KFLAG() check first.
> But this is the case for pretty much all of BTF data structures.
> In that sense BTF_MEMBER_BITFIELD_SIZE/BTF_MEMBER_BIT_OFFSET
> serve as a documentation and fit the style of btf.h header.
> imo having these two macros in uapi/btf.h is better than
> replacing them with comment only.
> 
> After this set the whole BTF really needs to be documented.

Yes,Martin and I will work on the documentation very soon.
diff mbox series

Patch

======================

Current btf encoding of bitfield follows what pahole generates.
For each bitfield, pahole will duplicate the type chain and
put the bitfield size at the final int or enum type.
Since the BTF enum type cannot encode bit size,
pahole workarounds the issue by generating
an int type whenever the enum bit size is not 32.

For example,
  -bash-4.4$ cat t.c
  typedef int ___int;
  enum A { A1, A2, A3 };
  struct t {
    int a[5];
    ___int b:4;
    volatile enum A c:4;
  } g;
  -bash-4.4$ gcc -c -O2 -g t.c
The current kernel supports the following BTF encoding:
  $ pahole -JV t.o
  [1] TYPEDEF ___int type_id=2
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  [3] ENUM A size=4 vlen=3
        A1 val=0
        A2 val=1
        A3 val=2
  [4] STRUCT t size=24 vlen=3
        a type_id=5 bits_offset=0
        b type_id=9 bits_offset=160
        c type_id=11 bits_offset=164
  [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
  [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
  [7] VOLATILE (anon) type_id=3
  [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
  [9] TYPEDEF ___int type_id=8
  [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
  [11] VOLATILE (anon) type_id=10

Two issues are in the above:
  . by changing enum type to int, we lost the original
    type information and this will not be ideal later
    when we try to convert BTF to a header file.
  . the type duplication for bitfields will cause
    BTF bloat. Duplicated types cannot be deduplicated
    later if the bitfield size is different.

To fix this issue, this patch implemented a compatible
change for BTF struct type encoding:
  . the bit 31 of struct_type->info, previously reserved,
    now is used to indicate whether bitfield_size is
    encoded in btf_member or not.
  . if bit 31 of struct_type->info is set,
    btf_member->offset will encode like:
      bit 0 - 23: bit offset
      bit 24 - 31: bitfield size
    if bit 31 is not set, the old behavior is preserved:
      bit 0 - 31: bit offset

So if the struct contains a bit field, the maximum bit offset
will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
bitfield size will be 256 which is enough for today as maximum
bitfield in compiler can be 128 where int128 type is supported.

This kernel patch intends to support the new BTF encoding:
  $ pahole -JV t.o
  [1] TYPEDEF ___int type_id=2
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  [3] ENUM A size=4 vlen=3
        A1 val=0
        A2 val=1
        A3 val=2
  [4] STRUCT t kind_flag=1 size=24 vlen=3
        a type_id=5 bitfield_size=0 bits_offset=0
        b type_id=1 bitfield_size=4 bits_offset=160
        c type_id=7 bitfield_size=4 bits_offset=164
  [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
  [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
  [7] VOLATILE (anon) type_id=3

Issue #2 and solution:
======================

Current forward type in BTF does not specify whether the original
type is struct or union. This will not work for type pretty print
and BTF-to-header-file conversion as struct/union must be specified.
  $ cat tt.c
  struct t;
  union u;
  int foo(struct t *t, union u *u) { return 0; }
  $ gcc -c -g -O2 tt.c
  $ pahole -JV tt.o
  [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  [2] FWD t type_id=0
  [3] PTR (anon) type_id=2
  [4] FWD u type_id=0
  [5] PTR (anon) type_id=4

To fix this issue, similar to issue #1, type->info bit 31
is used. If the bit is set, it is union type. Otherwise, it is
a struct type.

  $ pahole -JV tt.o
  [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  [2] FWD t kind_flag=0 type_id=0
  [3] PTR (anon) kind_flag=0 type_id=2
  [4] FWD u kind_flag=1 type_id=0
  [5] PTR (anon) kind_flag=0 type_id=4

Pahole/LLVM change:
===================

The new kind_flag functionality has been implemented in pahole
and llvm:
  https://github.com/yonghong-song/pahole/tree/bitfield
  https://github.com/yonghong-song/llvm/tree/bitfield

Note that pahole hasn't implemented func/func_proto kind
and .BTF.ext. So to print function signature with bpftool,
the llvm compiler should be used.

Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Yonghong Song <yhs@fb.com>
---
 include/uapi/linux/btf.h |  15 ++-
 kernel/bpf/btf.c         | 274 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 267 insertions(+), 22 deletions(-)

diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index 14f66948fc95..34aba40ed926 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -34,7 +34,9 @@  struct btf_type {
 	 * bits  0-15: vlen (e.g. # of struct's members)
 	 * bits 16-23: unused
 	 * bits 24-27: kind (e.g. int, ptr, array...etc)
-	 * bits 28-31: unused
+	 * bits 28-30: unused
+	 * bit     31: kind_flag, currently used by
+	 *             struct, union and fwd
 	 */
 	__u32 info;
 	/* "size" is used by INT, ENUM, STRUCT and UNION.
@@ -52,6 +54,7 @@  struct btf_type {
 
 #define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
 #define BTF_INFO_VLEN(info)	((info) & 0xffff)
+#define BTF_INFO_KFLAG(info)	((info) >> 31)
 
 #define BTF_KIND_UNKN		0	/* Unknown	*/
 #define BTF_KIND_INT		1	/* Integer	*/
@@ -110,9 +113,17 @@  struct btf_array {
 struct btf_member {
 	__u32	name_off;
 	__u32	type;
-	__u32	offset;	/* offset in bits */
+	__u32	offset;	/* [bitfield_size and] offset in bits */
 };
 
+/* If the type info kind_flag set, the btf_member.offset
+ * contains both member bit offset and bitfield size, and
+ * bitfield size will set for struct/union bitfield members.
+ * Otherwise, it contains only bit offset.
+ */
+#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
+#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
+
 /* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
  * The exact number of btf_param is stored in the vlen (of the
  * info in "struct btf_type").
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 72caa799e82f..ec3f80d3bef6 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -164,7 +164,7 @@ 
 #define BITS_ROUNDUP_BYTES(bits) \
 	(BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
 
-#define BTF_INFO_MASK 0x0f00ffff
+#define BTF_INFO_MASK 0x8f00ffff
 #define BTF_INT_MASK 0x0fffffff
 #define BTF_TYPE_ID_VALID(type_id) ((type_id) <= BTF_MAX_TYPE)
 #define BTF_STR_OFFSET_VALID(name_off) ((name_off) <= BTF_MAX_NAME_OFFSET)
@@ -274,6 +274,10 @@  struct btf_kind_operations {
 			    const struct btf_type *struct_type,
 			    const struct btf_member *member,
 			    const struct btf_type *member_type);
+	int (*check_kflag_member)(struct btf_verifier_env *env,
+				  const struct btf_type *struct_type,
+				  const struct btf_member *member,
+				  const struct btf_type *member_type);
 	void (*log_details)(struct btf_verifier_env *env,
 			    const struct btf_type *t);
 	void (*seq_show)(const struct btf *btf, const struct btf_type *t,
@@ -419,6 +423,25 @@  static u16 btf_type_vlen(const struct btf_type *t)
 	return BTF_INFO_VLEN(t->info);
 }
 
+static bool btf_type_kflag(const struct btf_type *t)
+{
+	return BTF_INFO_KFLAG(t->info);
+}
+
+static u32 btf_member_bit_offset(const struct btf_type *struct_type,
+			     const struct btf_member *member)
+{
+	return btf_type_kflag(struct_type) ? BTF_MEMBER_BIT_OFFSET(member->offset)
+					   : member->offset;
+}
+
+static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
+				    const struct btf_member *member)
+{
+	return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
+					   : 0;
+}
+
 static u32 btf_type_int(const struct btf_type *t)
 {
 	return *(u32 *)(t + 1);
@@ -627,9 +650,17 @@  static void btf_verifier_log_member(struct btf_verifier_env *env,
 	if (env->phase != CHECK_META)
 		btf_verifier_log_type(env, struct_type, NULL);
 
-	__btf_verifier_log(log, "\t%s type_id=%u bits_offset=%u",
-			   __btf_name_by_offset(btf, member->name_off),
-			   member->type, member->offset);
+	if (btf_type_kflag(struct_type))
+		__btf_verifier_log(log,
+				   "\t%s type_id=%u bitfield_size=%u bits_offset=%u",
+				   __btf_name_by_offset(btf, member->name_off),
+				   member->type,
+				   BTF_MEMBER_BITFIELD_SIZE(member->offset),
+				   BTF_MEMBER_BIT_OFFSET(member->offset));
+	else
+		__btf_verifier_log(log, "\t%s type_id=%u bits_offset=%u",
+				   __btf_name_by_offset(btf, member->name_off),
+				   member->type, member->offset);
 
 	if (fmt && *fmt) {
 		__btf_verifier_log(log, " ");
@@ -945,6 +976,38 @@  static int btf_df_check_member(struct btf_verifier_env *env,
 	return -EINVAL;
 }
 
+static int btf_df_check_kflag_member(struct btf_verifier_env *env,
+				     const struct btf_type *struct_type,
+				     const struct btf_member *member,
+				     const struct btf_type *member_type)
+{
+	btf_verifier_log_basic(env, struct_type,
+			       "Unsupported check_kflag_member");
+	return -EINVAL;
+}
+
+/* Used for ptr, array and struct/union type members.
+ * int, enum and modifier types have their specific callback functions.
+ */
+static int btf_generic_check_kflag_member(struct btf_verifier_env *env,
+					  const struct btf_type *struct_type,
+					  const struct btf_member *member,
+					  const struct btf_type *member_type)
+{
+	if (BTF_MEMBER_BITFIELD_SIZE(member->offset)) {
+		btf_verifier_log_member(env, struct_type, member,
+					"Invalid member bitfield_size");
+		return -EINVAL;
+	}
+
+	/* bitfield size is 0, so member->offset represents bit offset only.
+	 * It is safe to call non kflag check_member variants.
+	 */
+	return btf_type_ops(member_type)->check_member(env, struct_type,
+						       member,
+						       member_type);
+}
+
 static int btf_df_resolve(struct btf_verifier_env *env,
 			  const struct resolve_vertex *v)
 {
@@ -997,6 +1060,62 @@  static int btf_int_check_member(struct btf_verifier_env *env,
 	return 0;
 }
 
+static int btf_int_check_kflag_member(struct btf_verifier_env *env,
+				      const struct btf_type *struct_type,
+				      const struct btf_member *member,
+				      const struct btf_type *member_type)
+{
+	u32 struct_bits_off, nr_bits, nr_int_data_bits, bytes_offset;
+	u32 int_data = btf_type_int(member_type);
+	u32 struct_size = struct_type->size;
+	u32 nr_copy_bits;
+
+	/* a regular int type is required for the kflag int member */
+	if (!btf_type_int_is_regular(member_type)) {
+		btf_verifier_log_member(env, struct_type, member,
+					"Invalid member base type");
+		return -EINVAL;
+	}
+
+	/* check sanity of bitfield size */
+	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
+	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
+	nr_int_data_bits = BTF_INT_BITS(int_data);
+	if (!nr_bits) {
+		/* Not a bitfield member, member offset must be at byte
+		 * boundary.
+		 */
+		if (BITS_PER_BYTE_MASKED(struct_bits_off)) {
+			btf_verifier_log_member(env, struct_type, member,
+						"Invalid member offset");
+			return -EINVAL;
+		}
+
+		nr_bits = nr_int_data_bits;
+	} else if (nr_bits > nr_int_data_bits) {
+		btf_verifier_log_member(env, struct_type, member,
+					"Invalid member bitfield_size");
+		return -EINVAL;
+	}
+
+	bytes_offset = BITS_ROUNDDOWN_BYTES(struct_bits_off);
+	nr_copy_bits = nr_bits + BITS_PER_BYTE_MASKED(struct_bits_off);
+	if (nr_copy_bits > BITS_PER_U64) {
+		btf_verifier_log_member(env, struct_type, member,
+					"nr_copy_bits exceeds 64");
+		return -EINVAL;
+	}
+
+	if (struct_size < bytes_offset ||
+	    struct_size - bytes_offset < BITS_ROUNDUP_BYTES(nr_copy_bits)) {
+		btf_verifier_log_member(env, struct_type, member,
+					"Member exceeds struct_size");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static s32 btf_int_check_meta(struct btf_verifier_env *env,
 			      const struct btf_type *t,
 			      u32 meta_left)
@@ -1016,6 +1135,11 @@  static s32 btf_int_check_meta(struct btf_verifier_env *env,
 		return -EINVAL;
 	}
 
+	if (btf_type_kflag(t)) {
+		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+		return -EINVAL;
+	}
+
 	int_data = btf_type_int(t);
 	if (int_data & ~BTF_INT_MASK) {
 		btf_verifier_log_basic(env, t, "Invalid int_data:%x",
@@ -1097,6 +1221,7 @@  static void btf_bitfield_seq_show(void *data, u8 bits_offset,
 	seq_printf(m, "0x%llx", print_num);
 }
 
+
 static void btf_int_bits_seq_show(const struct btf *btf,
 				  const struct btf_type *t,
 				  void *data, u8 bits_offset,
@@ -1163,6 +1288,7 @@  static const struct btf_kind_operations int_ops = {
 	.check_meta = btf_int_check_meta,
 	.resolve = btf_df_resolve,
 	.check_member = btf_int_check_member,
+	.check_kflag_member = btf_int_check_kflag_member,
 	.log_details = btf_int_log,
 	.seq_show = btf_int_seq_show,
 };
@@ -1192,6 +1318,31 @@  static int btf_modifier_check_member(struct btf_verifier_env *env,
 							 resolved_type);
 }
 
+static int btf_modifier_check_kflag_member(struct btf_verifier_env *env,
+					   const struct btf_type *struct_type,
+					   const struct btf_member *member,
+					   const struct btf_type *member_type)
+{
+	const struct btf_type *resolved_type;
+	u32 resolved_type_id = member->type;
+	struct btf_member resolved_member;
+	struct btf *btf = env->btf;
+
+	resolved_type = btf_type_id_size(btf, &resolved_type_id, NULL);
+	if (!resolved_type) {
+		btf_verifier_log_member(env, struct_type, member,
+					"Invalid member");
+		return -EINVAL;
+	}
+
+	resolved_member = *member;
+	resolved_member.type = resolved_type_id;
+
+	return btf_type_ops(resolved_type)->check_kflag_member(env, struct_type,
+							       &resolved_member,
+							       resolved_type);
+}
+
 static int btf_ptr_check_member(struct btf_verifier_env *env,
 				const struct btf_type *struct_type,
 				const struct btf_member *member,
@@ -1227,6 +1378,11 @@  static int btf_ref_type_check_meta(struct btf_verifier_env *env,
 		return -EINVAL;
 	}
 
+	if (btf_type_kflag(t)) {
+		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+		return -EINVAL;
+	}
+
 	if (!BTF_TYPE_ID_VALID(t->type)) {
 		btf_verifier_log_type(env, t, "Invalid type_id");
 		return -EINVAL;
@@ -1380,6 +1536,7 @@  static struct btf_kind_operations modifier_ops = {
 	.check_meta = btf_ref_type_check_meta,
 	.resolve = btf_modifier_resolve,
 	.check_member = btf_modifier_check_member,
+	.check_kflag_member = btf_modifier_check_kflag_member,
 	.log_details = btf_ref_type_log,
 	.seq_show = btf_modifier_seq_show,
 };
@@ -1388,6 +1545,7 @@  static struct btf_kind_operations ptr_ops = {
 	.check_meta = btf_ref_type_check_meta,
 	.resolve = btf_ptr_resolve,
 	.check_member = btf_ptr_check_member,
+	.check_kflag_member = btf_generic_check_kflag_member,
 	.log_details = btf_ref_type_log,
 	.seq_show = btf_ptr_seq_show,
 };
@@ -1422,6 +1580,7 @@  static struct btf_kind_operations fwd_ops = {
 	.check_meta = btf_fwd_check_meta,
 	.resolve = btf_df_resolve,
 	.check_member = btf_df_check_member,
+	.check_kflag_member = btf_df_check_kflag_member,
 	.log_details = btf_ref_type_log,
 	.seq_show = btf_df_seq_show,
 };
@@ -1480,6 +1639,11 @@  static s32 btf_array_check_meta(struct btf_verifier_env *env,
 		return -EINVAL;
 	}
 
+	if (btf_type_kflag(t)) {
+		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+		return -EINVAL;
+	}
+
 	if (t->size) {
 		btf_verifier_log_type(env, t, "size != 0");
 		return -EINVAL;
@@ -1603,6 +1767,7 @@  static struct btf_kind_operations array_ops = {
 	.check_meta = btf_array_check_meta,
 	.resolve = btf_array_resolve,
 	.check_member = btf_array_check_member,
+	.check_kflag_member = btf_generic_check_kflag_member,
 	.log_details = btf_array_log,
 	.seq_show = btf_array_seq_show,
 };
@@ -1641,6 +1806,7 @@  static s32 btf_struct_check_meta(struct btf_verifier_env *env,
 	u32 meta_needed, last_offset;
 	struct btf *btf = env->btf;
 	u32 struct_size = t->size;
+	u32 offset;
 	u16 i;
 
 	meta_needed = btf_type_vlen(t) * sizeof(*member);
@@ -1682,7 +1848,8 @@  static s32 btf_struct_check_meta(struct btf_verifier_env *env,
 			return -EINVAL;
 		}
 
-		if (is_union && member->offset) {
+		offset = btf_member_bit_offset(t, member);
+		if (is_union && offset) {
 			btf_verifier_log_member(env, t, member,
 						"Invalid member bits_offset");
 			return -EINVAL;
@@ -1692,20 +1859,20 @@  static s32 btf_struct_check_meta(struct btf_verifier_env *env,
 		 * ">" instead of ">=" because the last member could be
 		 * "char a[0];"
 		 */
-		if (last_offset > member->offset) {
+		if (last_offset > offset) {
 			btf_verifier_log_member(env, t, member,
 						"Invalid member bits_offset");
 			return -EINVAL;
 		}
 
-		if (BITS_ROUNDUP_BYTES(member->offset) > struct_size) {
+		if (BITS_ROUNDUP_BYTES(offset) > struct_size) {
 			btf_verifier_log_member(env, t, member,
 						"Member bits_offset exceeds its struct size");
 			return -EINVAL;
 		}
 
 		btf_verifier_log_member(env, t, member, NULL);
-		last_offset = member->offset;
+		last_offset = offset;
 	}
 
 	return meta_needed;
@@ -1735,9 +1902,14 @@  static int btf_struct_resolve(struct btf_verifier_env *env,
 
 		last_member_type = btf_type_by_id(env->btf,
 						  last_member_type_id);
-		err = btf_type_ops(last_member_type)->check_member(env, v->t,
-							last_member,
-							last_member_type);
+		if (btf_type_kflag(v->t))
+			err = btf_type_ops(last_member_type)->check_kflag_member(env, v->t,
+								last_member,
+								last_member_type);
+		else
+			err = btf_type_ops(last_member_type)->check_member(env, v->t,
+								last_member,
+								last_member_type);
 		if (err)
 			return err;
 	}
@@ -1759,9 +1931,14 @@  static int btf_struct_resolve(struct btf_verifier_env *env,
 			return env_stack_push(env, member_type, member_type_id);
 		}
 
-		err = btf_type_ops(member_type)->check_member(env, v->t,
-							      member,
-							      member_type);
+		if (btf_type_kflag(v->t))
+			err = btf_type_ops(member_type)->check_kflag_member(env, v->t,
+									    member,
+									    member_type);
+		else
+			err = btf_type_ops(member_type)->check_member(env, v->t,
+								      member,
+								      member_type);
 		if (err)
 			return err;
 	}
@@ -1789,17 +1966,26 @@  static void btf_struct_seq_show(const struct btf *btf, const struct btf_type *t,
 	for_each_member(i, t, member) {
 		const struct btf_type *member_type = btf_type_by_id(btf,
 								member->type);
-		u32 member_offset = member->offset;
-		u32 bytes_offset = BITS_ROUNDDOWN_BYTES(member_offset);
-		u8 bits8_offset = BITS_PER_BYTE_MASKED(member_offset);
 		const struct btf_kind_operations *ops;
+		u32 member_offset, bitfield_size;
+		u32 bytes_offset;
+		u8 bits8_offset;
 
 		if (i)
 			seq_puts(m, seq);
 
-		ops = btf_type_ops(member_type);
-		ops->seq_show(btf, member_type, member->type,
-			      data + bytes_offset, bits8_offset, m);
+		member_offset = btf_member_bit_offset(t, member);
+		bitfield_size = btf_member_bitfield_size(t, member);
+		if (bitfield_size) {
+			btf_bitfield_seq_show(data, member_offset,
+					      bitfield_size, m);
+		} else {
+			bytes_offset = BITS_ROUNDDOWN_BYTES(member_offset);
+			bits8_offset = BITS_PER_BYTE_MASKED(member_offset);
+			ops = btf_type_ops(member_type);
+			ops->seq_show(btf, member_type, member->type,
+				      data + bytes_offset, bits8_offset, m);
+		}
 	}
 	seq_puts(m, "}");
 }
@@ -1808,6 +1994,7 @@  static struct btf_kind_operations struct_ops = {
 	.check_meta = btf_struct_check_meta,
 	.resolve = btf_struct_resolve,
 	.check_member = btf_struct_check_member,
+	.check_kflag_member = btf_generic_check_kflag_member,
 	.log_details = btf_struct_log,
 	.seq_show = btf_struct_seq_show,
 };
@@ -1837,6 +2024,35 @@  static int btf_enum_check_member(struct btf_verifier_env *env,
 	return 0;
 }
 
+static int btf_enum_check_kflag_member(struct btf_verifier_env *env,
+				       const struct btf_type *struct_type,
+				       const struct btf_member *member,
+				       const struct btf_type *member_type)
+{
+	u32 struct_bits_off, nr_bits, bytes_end, struct_size;
+	u32 int_bitsize = sizeof(int) * BITS_PER_BYTE;
+
+	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
+	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
+	if (!nr_bits) {
+		nr_bits = int_bitsize;
+	} else if (nr_bits > int_bitsize) {
+		btf_verifier_log_member(env, struct_type, member,
+					"Invalid member bitfield_size");
+		return -EINVAL;
+	}
+
+	struct_size = struct_type->size;
+	bytes_end = BITS_ROUNDUP_BYTES(struct_bits_off + nr_bits);
+	if (struct_size < bytes_end) {
+		btf_verifier_log_member(env, struct_type, member,
+					"Member exceeds struct_size");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static s32 btf_enum_check_meta(struct btf_verifier_env *env,
 			       const struct btf_type *t,
 			       u32 meta_left)
@@ -1856,6 +2072,11 @@  static s32 btf_enum_check_meta(struct btf_verifier_env *env,
 		return -EINVAL;
 	}
 
+	if (btf_type_kflag(t)) {
+		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+		return -EINVAL;
+	}
+
 	if (t->size != sizeof(int)) {
 		btf_verifier_log_type(env, t, "Expected size:%zu",
 				      sizeof(int));
@@ -1924,6 +2145,7 @@  static struct btf_kind_operations enum_ops = {
 	.check_meta = btf_enum_check_meta,
 	.resolve = btf_df_resolve,
 	.check_member = btf_enum_check_member,
+	.check_kflag_member = btf_enum_check_kflag_member,
 	.log_details = btf_enum_log,
 	.seq_show = btf_enum_seq_show,
 };
@@ -1946,6 +2168,11 @@  static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
 		return -EINVAL;
 	}
 
+	if (btf_type_kflag(t)) {
+		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+		return -EINVAL;
+	}
+
 	btf_verifier_log_type(env, t, NULL);
 
 	return meta_needed;
@@ -2005,6 +2232,7 @@  static struct btf_kind_operations func_proto_ops = {
 	 * Hence, there is no btf_func_check_member().
 	 */
 	.check_member = btf_df_check_member,
+	.check_kflag_member = btf_df_check_kflag_member,
 	.log_details = btf_func_proto_log,
 	.seq_show = btf_df_seq_show,
 };
@@ -2024,6 +2252,11 @@  static s32 btf_func_check_meta(struct btf_verifier_env *env,
 		return -EINVAL;
 	}
 
+	if (btf_type_kflag(t)) {
+		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+		return -EINVAL;
+	}
+
 	btf_verifier_log_type(env, t, NULL);
 
 	return 0;
@@ -2033,6 +2266,7 @@  static struct btf_kind_operations func_ops = {
 	.check_meta = btf_func_check_meta,
 	.resolve = btf_df_resolve,
 	.check_member = btf_df_check_member,
+	.check_kflag_member = btf_df_check_kflag_member,
 	.log_details = btf_ref_type_log,
 	.seq_show = btf_df_seq_show,
 };