Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Add btf enum64 support

Currently, BTF only supports upto 32bit enum value with BTF_KIND_ENUM.
But in kernel, some enum indeed has 64bit values, e.g.,
in uapi bpf.h, we have
enum {
BPF_F_INDEX_MASK = 0xffffffffULL,
BPF_F_CURRENT_CPU = BPF_F_INDEX_MASK,
BPF_F_CTXLEN_MASK = (0xfffffULL << 32),
};
In this case, BTF_KIND_ENUM will encode the value of BPF_F_CTXLEN_MASK
as 0, which certainly is incorrect.

This patch added a new btf kind, BTF_KIND_ENUM64, which permits
64bit value to cover the above use case. The BTF_KIND_ENUM64 has
the following three fields followed by the common type:
struct bpf_enum64 {
__u32 nume_off;
__u32 val_lo32;
__u32 val_hi32;
};
Currently, btf type section has an alignment of 4 as all element types
are u32. Representing the value with __u64 will introduce a pad
for bpf_enum64 and may also introduce misalignment for the 64bit value.
Hence, two members of val_hi32 and val_lo32 are chosen to avoid these issues.

The kflag is also introduced for BTF_KIND_ENUM and BTF_KIND_ENUM64
to indicate whether the value is signed or unsigned. The kflag intends
to provide consistent output of BTF C fortmat with the original
source code. For example, the original BTF_KIND_ENUM bit value is 0xffffffff.
The format C has two choices, printing out 0xffffffff or -1 and current libbpf
prints out as unsigned value. But if the signedness is preserved in btf,
the value can be printed the same as the original source code.
The kflag value 0 means unsigned values, which is consistent to the default
by libbpf and should also cover most cases as well.

The new BTF_KIND_ENUM64 is intended to support the enum value represented as
64bit value. But it can represent all BTF_KIND_ENUM values as well.
The compiler ([1]) and pahole will generate BTF_KIND_ENUM64 only if the value has
to be represented with 64 bits.

In addition, a static inline function btf_kind_core_compat() is introduced which
will be used later when libbpf relo_core.c changed. Here the kernel shares the
same relo_core.c with libbpf.

[1] https://reviews.llvm.org/D124641

Acked-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/r/20220607062600.3716578-1-yhs@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Yonghong Song and committed by
Alexei Starovoitov
6089fb32 02f4afeb

+185 -21
+28
include/linux/btf.h
··· 177 177 return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM; 178 178 } 179 179 180 + static inline bool btf_is_any_enum(const struct btf_type *t) 181 + { 182 + return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM || 183 + BTF_INFO_KIND(t->info) == BTF_KIND_ENUM64; 184 + } 185 + 186 + static inline bool btf_kind_core_compat(const struct btf_type *t1, 187 + const struct btf_type *t2) 188 + { 189 + return BTF_INFO_KIND(t1->info) == BTF_INFO_KIND(t2->info) || 190 + (btf_is_any_enum(t1) && btf_is_any_enum(t2)); 191 + } 192 + 180 193 static inline bool str_is_empty(const char *s) 181 194 { 182 195 return !s || !s[0]; ··· 203 190 static inline bool btf_is_enum(const struct btf_type *t) 204 191 { 205 192 return btf_kind(t) == BTF_KIND_ENUM; 193 + } 194 + 195 + static inline bool btf_is_enum64(const struct btf_type *t) 196 + { 197 + return btf_kind(t) == BTF_KIND_ENUM64; 198 + } 199 + 200 + static inline u64 btf_enum64_value(const struct btf_enum64 *e) 201 + { 202 + return ((u64)e->val_hi32 << 32) | e->val_lo32; 206 203 } 207 204 208 205 static inline bool btf_is_composite(const struct btf_type *t) ··· 353 330 static inline struct btf_enum *btf_enum(const struct btf_type *t) 354 331 { 355 332 return (struct btf_enum *)(t + 1); 333 + } 334 + 335 + static inline struct btf_enum64 *btf_enum64(const struct btf_type *t) 336 + { 337 + return (struct btf_enum64 *)(t + 1); 356 338 } 357 339 358 340 static inline const struct btf_var_secinfo *btf_type_var_secinfo(
+14 -3
include/uapi/linux/btf.h
··· 36 36 * bits 24-28: kind (e.g. int, ptr, array...etc) 37 37 * bits 29-30: unused 38 38 * bit 31: kind_flag, currently used by 39 - * struct, union and fwd 39 + * struct, union, enum, fwd and enum64 40 40 */ 41 41 __u32 info; 42 - /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC. 42 + /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64. 43 43 * "size" tells the size of the type it is describing. 44 44 * 45 45 * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, ··· 63 63 BTF_KIND_ARRAY = 3, /* Array */ 64 64 BTF_KIND_STRUCT = 4, /* Struct */ 65 65 BTF_KIND_UNION = 5, /* Union */ 66 - BTF_KIND_ENUM = 6, /* Enumeration */ 66 + BTF_KIND_ENUM = 6, /* Enumeration up to 32-bit values */ 67 67 BTF_KIND_FWD = 7, /* Forward */ 68 68 BTF_KIND_TYPEDEF = 8, /* Typedef */ 69 69 BTF_KIND_VOLATILE = 9, /* Volatile */ ··· 76 76 BTF_KIND_FLOAT = 16, /* Floating point */ 77 77 BTF_KIND_DECL_TAG = 17, /* Decl Tag */ 78 78 BTF_KIND_TYPE_TAG = 18, /* Type Tag */ 79 + BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */ 79 80 80 81 NR_BTF_KINDS, 81 82 BTF_KIND_MAX = NR_BTF_KINDS - 1, ··· 185 184 */ 186 185 struct btf_decl_tag { 187 186 __s32 component_idx; 187 + }; 188 + 189 + /* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64". 190 + * The exact number of btf_enum64 is stored in the vlen (of the 191 + * info in "struct btf_type"). 192 + */ 193 + struct btf_enum64 { 194 + __u32 name_off; 195 + __u32 val_lo32; 196 + __u32 val_hi32; 188 197 }; 189 198 190 199 #endif /* _UAPI__LINUX_BTF_H__ */
+128 -14
kernel/bpf/btf.c
··· 309 309 [BTF_KIND_FLOAT] = "FLOAT", 310 310 [BTF_KIND_DECL_TAG] = "DECL_TAG", 311 311 [BTF_KIND_TYPE_TAG] = "TYPE_TAG", 312 + [BTF_KIND_ENUM64] = "ENUM64", 312 313 }; 313 314 314 315 const char *btf_type_str(const struct btf_type *t) ··· 667 666 case BTF_KIND_ENUM: 668 667 case BTF_KIND_DATASEC: 669 668 case BTF_KIND_FLOAT: 669 + case BTF_KIND_ENUM64: 670 670 return true; 671 671 } 672 672 ··· 711 709 static const struct btf_decl_tag *btf_type_decl_tag(const struct btf_type *t) 712 710 { 713 711 return (const struct btf_decl_tag *)(t + 1); 712 + } 713 + 714 + static const struct btf_enum64 *btf_type_enum64(const struct btf_type *t) 715 + { 716 + return (const struct btf_enum64 *)(t + 1); 714 717 } 715 718 716 719 static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t) ··· 1026 1019 parens = "{"; 1027 1020 break; 1028 1021 case BTF_KIND_ENUM: 1022 + case BTF_KIND_ENUM64: 1029 1023 prefix = "enum"; 1030 1024 break; 1031 1025 default: ··· 1842 1834 case BTF_KIND_UNION: 1843 1835 case BTF_KIND_ENUM: 1844 1836 case BTF_KIND_FLOAT: 1837 + case BTF_KIND_ENUM64: 1845 1838 size = type->size; 1846 1839 goto resolved; 1847 1840 ··· 3679 3670 { 3680 3671 const struct btf_enum *enums = btf_type_enum(t); 3681 3672 struct btf *btf = env->btf; 3673 + const char *fmt_str; 3682 3674 u16 i, nr_enums; 3683 3675 u32 meta_needed; 3684 3676 ··· 3690 3680 btf_verifier_log_basic(env, t, 3691 3681 "meta_left:%u meta_needed:%u", 3692 3682 meta_left, meta_needed); 3693 - return -EINVAL; 3694 - } 3695 - 3696 - if (btf_type_kflag(t)) { 3697 - btf_verifier_log_type(env, t, "Invalid btf_info kind_flag"); 3698 3683 return -EINVAL; 3699 3684 } 3700 3685 ··· 3723 3718 3724 3719 if (env->log.level == BPF_LOG_KERNEL) 3725 3720 continue; 3726 - btf_verifier_log(env, "\t%s val=%d\n", 3721 + fmt_str = btf_type_kflag(t) ? "\t%s val=%d\n" : "\t%s val=%u\n"; 3722 + btf_verifier_log(env, fmt_str, 3727 3723 __btf_name_by_offset(btf, enums[i].name_off), 3728 3724 enums[i].val); 3729 3725 } ··· 3765 3759 return; 3766 3760 } 3767 3761 3768 - btf_show_type_value(show, "%d", v); 3762 + if (btf_type_kflag(t)) 3763 + btf_show_type_value(show, "%d", v); 3764 + else 3765 + btf_show_type_value(show, "%u", v); 3769 3766 btf_show_end_type(show); 3770 3767 } 3771 3768 ··· 3779 3770 .check_kflag_member = btf_enum_check_kflag_member, 3780 3771 .log_details = btf_enum_log, 3781 3772 .show = btf_enum_show, 3773 + }; 3774 + 3775 + static s32 btf_enum64_check_meta(struct btf_verifier_env *env, 3776 + const struct btf_type *t, 3777 + u32 meta_left) 3778 + { 3779 + const struct btf_enum64 *enums = btf_type_enum64(t); 3780 + struct btf *btf = env->btf; 3781 + const char *fmt_str; 3782 + u16 i, nr_enums; 3783 + u32 meta_needed; 3784 + 3785 + nr_enums = btf_type_vlen(t); 3786 + meta_needed = nr_enums * sizeof(*enums); 3787 + 3788 + if (meta_left < meta_needed) { 3789 + btf_verifier_log_basic(env, t, 3790 + "meta_left:%u meta_needed:%u", 3791 + meta_left, meta_needed); 3792 + return -EINVAL; 3793 + } 3794 + 3795 + if (t->size > 8 || !is_power_of_2(t->size)) { 3796 + btf_verifier_log_type(env, t, "Unexpected size"); 3797 + return -EINVAL; 3798 + } 3799 + 3800 + /* enum type either no name or a valid one */ 3801 + if (t->name_off && 3802 + !btf_name_valid_identifier(env->btf, t->name_off)) { 3803 + btf_verifier_log_type(env, t, "Invalid name"); 3804 + return -EINVAL; 3805 + } 3806 + 3807 + btf_verifier_log_type(env, t, NULL); 3808 + 3809 + for (i = 0; i < nr_enums; i++) { 3810 + if (!btf_name_offset_valid(btf, enums[i].name_off)) { 3811 + btf_verifier_log(env, "\tInvalid name_offset:%u", 3812 + enums[i].name_off); 3813 + return -EINVAL; 3814 + } 3815 + 3816 + /* enum member must have a valid name */ 3817 + if (!enums[i].name_off || 3818 + !btf_name_valid_identifier(btf, enums[i].name_off)) { 3819 + btf_verifier_log_type(env, t, "Invalid name"); 3820 + return -EINVAL; 3821 + } 3822 + 3823 + if (env->log.level == BPF_LOG_KERNEL) 3824 + continue; 3825 + 3826 + fmt_str = btf_type_kflag(t) ? "\t%s val=%lld\n" : "\t%s val=%llu\n"; 3827 + btf_verifier_log(env, fmt_str, 3828 + __btf_name_by_offset(btf, enums[i].name_off), 3829 + btf_enum64_value(enums + i)); 3830 + } 3831 + 3832 + return meta_needed; 3833 + } 3834 + 3835 + static void btf_enum64_show(const struct btf *btf, const struct btf_type *t, 3836 + u32 type_id, void *data, u8 bits_offset, 3837 + struct btf_show *show) 3838 + { 3839 + const struct btf_enum64 *enums = btf_type_enum64(t); 3840 + u32 i, nr_enums = btf_type_vlen(t); 3841 + void *safe_data; 3842 + s64 v; 3843 + 3844 + safe_data = btf_show_start_type(show, t, type_id, data); 3845 + if (!safe_data) 3846 + return; 3847 + 3848 + v = *(u64 *)safe_data; 3849 + 3850 + for (i = 0; i < nr_enums; i++) { 3851 + if (v != btf_enum64_value(enums + i)) 3852 + continue; 3853 + 3854 + btf_show_type_value(show, "%s", 3855 + __btf_name_by_offset(btf, 3856 + enums[i].name_off)); 3857 + 3858 + btf_show_end_type(show); 3859 + return; 3860 + } 3861 + 3862 + if (btf_type_kflag(t)) 3863 + btf_show_type_value(show, "%lld", v); 3864 + else 3865 + btf_show_type_value(show, "%llu", v); 3866 + btf_show_end_type(show); 3867 + } 3868 + 3869 + static struct btf_kind_operations enum64_ops = { 3870 + .check_meta = btf_enum64_check_meta, 3871 + .resolve = btf_df_resolve, 3872 + .check_member = btf_enum_check_member, 3873 + .check_kflag_member = btf_enum_check_kflag_member, 3874 + .log_details = btf_enum_log, 3875 + .show = btf_enum64_show, 3782 3876 }; 3783 3877 3784 3878 static s32 btf_func_proto_check_meta(struct btf_verifier_env *env, ··· 4550 4438 [BTF_KIND_FLOAT] = &float_ops, 4551 4439 [BTF_KIND_DECL_TAG] = &decl_tag_ops, 4552 4440 [BTF_KIND_TYPE_TAG] = &modifier_ops, 4441 + [BTF_KIND_ENUM64] = &enum64_ops, 4553 4442 }; 4554 4443 4555 4444 static s32 btf_check_meta(struct btf_verifier_env *env, ··· 5412 5299 /* skip modifiers */ 5413 5300 while (btf_type_is_modifier(t)) 5414 5301 t = btf_type_by_id(btf, t->type); 5415 - if (btf_type_is_small_int(t) || btf_type_is_enum(t)) 5302 + if (btf_type_is_small_int(t) || btf_is_any_enum(t)) 5416 5303 /* accessing a scalar */ 5417 5304 return true; 5418 5305 if (!btf_type_is_ptr(t)) { ··· 5876 5763 if (btf_type_is_ptr(t)) 5877 5764 /* kernel size of pointer. Not BPF's size of pointer*/ 5878 5765 return sizeof(void *); 5879 - if (btf_type_is_int(t) || btf_type_is_enum(t)) 5766 + if (btf_type_is_int(t) || btf_is_any_enum(t)) 5880 5767 return t->size; 5881 5768 *bad_type = t; 5882 5769 return -EINVAL; ··· 6024 5911 * to context only. And only global functions can be replaced. 6025 5912 * Hence type check only those types. 6026 5913 */ 6027 - if (btf_type_is_int(t1) || btf_type_is_enum(t1)) 5914 + if (btf_type_is_int(t1) || btf_is_any_enum(t1)) 6028 5915 continue; 6029 5916 if (!btf_type_is_ptr(t1)) { 6030 5917 bpf_log(log, ··· 6521 6408 t = btf_type_by_id(btf, t->type); 6522 6409 while (btf_type_is_modifier(t)) 6523 6410 t = btf_type_by_id(btf, t->type); 6524 - if (!btf_type_is_int(t) && !btf_type_is_enum(t)) { 6411 + if (!btf_type_is_int(t) && !btf_is_any_enum(t)) { 6525 6412 bpf_log(log, 6526 6413 "Global function %s() doesn't return scalar. Only those are supported.\n", 6527 6414 tname); ··· 6536 6423 t = btf_type_by_id(btf, args[i].type); 6537 6424 while (btf_type_is_modifier(t)) 6538 6425 t = btf_type_by_id(btf, t->type); 6539 - if (btf_type_is_int(t) || btf_type_is_enum(t)) { 6426 + if (btf_type_is_int(t) || btf_is_any_enum(t)) { 6540 6427 reg->type = SCALAR_VALUE; 6541 6428 continue; 6542 6429 } ··· 7448 7335 case BTF_KIND_UNION: 7449 7336 case BTF_KIND_ENUM: 7450 7337 case BTF_KIND_FWD: 7338 + case BTF_KIND_ENUM64: 7451 7339 return 1; 7452 7340 case BTF_KIND_INT: 7453 7341 /* just reject deprecated bitfield-like integers; all other ··· 7501 7387 * field-based relocations. This function assumes that root types were already 7502 7388 * checked for name match. Beyond that initial root-level name check, names 7503 7389 * are completely ignored. Compatibility rules are as follows: 7504 - * - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but 7390 + * - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs/ENUM64s are considered compatible, but 7505 7391 * kind should match for local and target types (i.e., STRUCT is not 7506 7392 * compatible with UNION); 7507 - * - for ENUMs, the size is ignored; 7393 + * - for ENUMs/ENUM64s, the size is ignored; 7508 7394 * - for INT, size and signedness are ignored; 7509 7395 * - for ARRAY, dimensionality is ignored, element types are checked for 7510 7396 * compatibility recursively;
+1 -1
kernel/bpf/verifier.c
··· 10901 10901 goto err_free; 10902 10902 ret_type = btf_type_skip_modifiers(btf, func_proto->type, NULL); 10903 10903 scalar_return = 10904 - btf_type_is_small_int(ret_type) || btf_type_is_enum(ret_type); 10904 + btf_type_is_small_int(ret_type) || btf_is_any_enum(ret_type); 10905 10905 if (i && !scalar_return && env->subprog_info[i].has_ld_abs) { 10906 10906 verbose(env, "LD_ABS is only allowed in functions that return 'int'.\n"); 10907 10907 goto err_free;
+14 -3
tools/include/uapi/linux/btf.h
··· 36 36 * bits 24-28: kind (e.g. int, ptr, array...etc) 37 37 * bits 29-30: unused 38 38 * bit 31: kind_flag, currently used by 39 - * struct, union and fwd 39 + * struct, union, enum, fwd and enum64 40 40 */ 41 41 __u32 info; 42 - /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC. 42 + /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64. 43 43 * "size" tells the size of the type it is describing. 44 44 * 45 45 * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, ··· 63 63 BTF_KIND_ARRAY = 3, /* Array */ 64 64 BTF_KIND_STRUCT = 4, /* Struct */ 65 65 BTF_KIND_UNION = 5, /* Union */ 66 - BTF_KIND_ENUM = 6, /* Enumeration */ 66 + BTF_KIND_ENUM = 6, /* Enumeration up to 32-bit values */ 67 67 BTF_KIND_FWD = 7, /* Forward */ 68 68 BTF_KIND_TYPEDEF = 8, /* Typedef */ 69 69 BTF_KIND_VOLATILE = 9, /* Volatile */ ··· 76 76 BTF_KIND_FLOAT = 16, /* Floating point */ 77 77 BTF_KIND_DECL_TAG = 17, /* Decl Tag */ 78 78 BTF_KIND_TYPE_TAG = 18, /* Type Tag */ 79 + BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */ 79 80 80 81 NR_BTF_KINDS, 81 82 BTF_KIND_MAX = NR_BTF_KINDS - 1, ··· 185 184 */ 186 185 struct btf_decl_tag { 187 186 __s32 component_idx; 187 + }; 188 + 189 + /* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64". 190 + * The exact number of btf_enum64 is stored in the vlen (of the 191 + * info in "struct btf_type"). 192 + */ 193 + struct btf_enum64 { 194 + __u32 name_off; 195 + __u32 val_lo32; 196 + __u32 val_hi32; 188 197 }; 189 198 190 199 #endif /* _UAPI__LINUX_BTF_H__ */