bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next 00/12] bpf: Add 64bit enum value support
@ 2022-05-01 19:00 Yonghong Song
  2022-05-01 19:00 ` [PATCH bpf-next 01/12] bpf: Add btf enum64 support Yonghong Song
                   ` (11 more replies)
  0 siblings, 12 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Currently, btf only supports upto 32bit enum value with BTF_KIND_ENUM.
But in kernel, some enum 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),
  };
With BTF_KIND_ENUM, the value for BPF_F_CTXLEN_MASK will be encoded
as 0 which is incorrect.

To solve this problem, BTF_KIND_ENUM64 is proposed in this patch set
to support enum 64bit values. Also, since sometimes there is a need
to generate C code from btf, e.g., vmlinux.h, btf kflag support
is also added for BTF_KIND_ENUM and BTF_KIND_ENUM64, which will
permit proper value printout, signed or unsigned.

In the rest of this patch set, Patch #1 added kernel support,
Patches #2 - #4 for libbpf, Patch #5 for bpftool. Patches #6 - #11
are for various selftests, and Patch #12 added BTF_KIND_ENUM64
in btf documentation.

Yonghong Song (12):
  bpf: Add btf enum64 support
  libbpf: Permit 64bit relocation value
  libbpf: Fix an error in 64bit relocation value computation
  libbpf: Add btf enum64 support
  bpftool: Add btf enum64 support
  selftests/bpf: Fix selftests failure
  selftests/bpf: Test new libbpf enum32/enum64 API functions
  selftests/bpf: Add BTF_KIND_ENUM64 unit tests
  selftests/bpf: Test BTF_KIND_ENUM64 for deduplication
  selftests/bpf: add a test for enum64 value relocation
  selftests/bpf: Clarify llvm dependency with possible selftest failures
  docs/bpf: Update documentation for BTF_KIND_ENUM64 support

 Documentation/bpf/btf.rst                     |  34 ++-
 include/linux/btf.h                           |  18 +-
 include/uapi/linux/btf.h                      |  17 +-
 kernel/bpf/btf.c                              | 132 +++++++++-
 tools/bpf/bpftool/btf.c                       |  47 +++-
 tools/bpf/bpftool/btf_dumper.c                |  32 +++
 tools/bpf/bpftool/gen.c                       |   1 +
 tools/include/uapi/linux/btf.h                |  17 +-
 tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
 tools/lib/bpf/btf.h                           |  21 ++
 tools/lib/bpf/btf_dump.c                      |  94 ++++++--
 tools/lib/bpf/libbpf.c                        |  64 ++++-
 tools/lib/bpf/libbpf.map                      |   4 +
 tools/lib/bpf/libbpf_internal.h               |   2 +
 tools/lib/bpf/linker.c                        |   2 +
 tools/lib/bpf/relo_core.c                     | 119 +++++----
 tools/lib/bpf/relo_core.h                     |   4 +-
 tools/testing/selftests/bpf/README.rst        |  18 ++
 tools/testing/selftests/bpf/btf_helpers.c     |  21 +-
 tools/testing/selftests/bpf/prog_tests/btf.c  | 128 ++++++++--
 .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
 .../selftests/bpf/prog_tests/btf_write.c      | 120 +++++++---
 .../selftests/bpf/prog_tests/core_reloc.c     |  43 ++++
 .../bpf/progs/btf__core_reloc_enum64val.c     |   3 +
 .../progs/btf__core_reloc_enum64val___diff.c  |   3 +
 .../btf__core_reloc_enum64val___err_missing.c |   3 +
 ...btf__core_reloc_enum64val___val3_missing.c |   3 +
 .../bpf/progs/btf_dump_test_case_syntax.c     |   2 +-
 .../selftests/bpf/progs/core_reloc_types.h    |  47 ++++
 .../bpf/progs/test_core_reloc_enum64val.c     |  53 ++++
 tools/testing/selftests/bpf/test_btf.h        |   1 +
 31 files changed, 1126 insertions(+), 163 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___diff.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___err_missing.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___val3_missing.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_enum64val.c

-- 
2.30.2


^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 01/12] bpf: Add btf enum64 support
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-09  0:33   ` Dave Marchevsky
  2022-05-09 22:29   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value Yonghong Song
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

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 bytes followed by the common type:
  struct bpf_enum64 {
    __u32 nume_off;
    __u32 hi32;
    __u32 lo32;
  };
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 hi32 and 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, print 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 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 value size of BTF_KIND_ENUM64 is encoded to 8 to represent its intent.
The compiler ([1]) and pahole will generate BTF_KIND_ENUM64 only if the value has
to be represented with 64 bits.

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

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 include/linux/btf.h            |  18 ++++-
 include/uapi/linux/btf.h       |  17 ++++-
 kernel/bpf/btf.c               | 132 ++++++++++++++++++++++++++++++---
 tools/include/uapi/linux/btf.h |  17 ++++-
 4 files changed, 168 insertions(+), 16 deletions(-)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index 2611cea2c2b6..280c33c9414a 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -174,7 +174,8 @@ static inline bool btf_type_is_small_int(const struct btf_type *t)
 
 static inline bool btf_type_is_enum(const struct btf_type *t)
 {
-	return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM;
+	return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM ||
+	       BTF_INFO_KIND(t->info) == BTF_KIND_ENUM64;
 }
 
 static inline bool str_is_empty(const char *s)
@@ -192,6 +193,16 @@ static inline bool btf_is_enum(const struct btf_type *t)
 	return btf_kind(t) == BTF_KIND_ENUM;
 }
 
+static inline bool btf_is_enum64(const struct btf_type *t)
+{
+	return btf_kind(t) == BTF_KIND_ENUM64;
+}
+
+static inline u64 btf_enum64_value(const struct btf_enum64 *e)
+{
+	return (u64)e->hi32 << 32 | e->lo32;
+}
+
 static inline bool btf_is_composite(const struct btf_type *t)
 {
 	u16 kind = btf_kind(t);
@@ -332,6 +343,11 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t)
 	return (struct btf_enum *)(t + 1);
 }
 
+static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
+{
+	return (struct btf_enum64 *)(t + 1);
+}
+
 static inline const struct btf_var_secinfo *btf_type_var_secinfo(
 		const struct btf_type *t)
 {
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index a9162a6c0284..2aac226a27b2 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -36,10 +36,10 @@ struct btf_type {
 	 * bits 24-28: kind (e.g. int, ptr, array...etc)
 	 * bits 29-30: unused
 	 * bit     31: kind_flag, currently used by
-	 *             struct, union and fwd
+	 *             struct, union, enum, fwd and enum64
 	 */
 	__u32 info;
-	/* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
+	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
 	 * "size" tells the size of the type it is describing.
 	 *
 	 * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
@@ -63,7 +63,7 @@ enum {
 	BTF_KIND_ARRAY		= 3,	/* Array	*/
 	BTF_KIND_STRUCT		= 4,	/* Struct	*/
 	BTF_KIND_UNION		= 5,	/* Union	*/
-	BTF_KIND_ENUM		= 6,	/* Enumeration	*/
+	BTF_KIND_ENUM		= 6,	/* Enumeration for int/unsigned int values */
 	BTF_KIND_FWD		= 7,	/* Forward	*/
 	BTF_KIND_TYPEDEF	= 8,	/* Typedef	*/
 	BTF_KIND_VOLATILE	= 9,	/* Volatile	*/
@@ -76,6 +76,7 @@ enum {
 	BTF_KIND_FLOAT		= 16,	/* Floating point	*/
 	BTF_KIND_DECL_TAG	= 17,	/* Decl Tag */
 	BTF_KIND_TYPE_TAG	= 18,	/* Type Tag */
+	BTF_KIND_ENUM64		= 19,	/* Enumeration for long/unsigned long values */
 
 	NR_BTF_KINDS,
 	BTF_KIND_MAX		= NR_BTF_KINDS - 1,
@@ -186,4 +187,14 @@ struct btf_decl_tag {
        __s32   component_idx;
 };
 
+/* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64".
+ * The exact number of btf_enum64 is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_enum64 {
+	__u32	name_off;
+	__u32	hi32;
+	__u32	lo32;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 2f0b0440131c..17e24b362d3d 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -307,6 +307,7 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
 	[BTF_KIND_FLOAT]	= "FLOAT",
 	[BTF_KIND_DECL_TAG]	= "DECL_TAG",
 	[BTF_KIND_TYPE_TAG]	= "TYPE_TAG",
+	[BTF_KIND_ENUM64]	= "ENUM64",
 };
 
 const char *btf_type_str(const struct btf_type *t)
@@ -664,6 +665,7 @@ static bool btf_type_has_size(const struct btf_type *t)
 	case BTF_KIND_ENUM:
 	case BTF_KIND_DATASEC:
 	case BTF_KIND_FLOAT:
+	case BTF_KIND_ENUM64:
 		return true;
 	}
 
@@ -709,6 +711,11 @@ static const struct btf_decl_tag *btf_type_decl_tag(const struct btf_type *t)
 	return (const struct btf_decl_tag *)(t + 1);
 }
 
+static const struct btf_enum64 *btf_type_enum64(const struct btf_type *t)
+{
+	return (const struct btf_enum64 *)(t + 1);
+}
+
 static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t)
 {
 	return kind_ops[BTF_INFO_KIND(t->info)];
@@ -1017,6 +1024,7 @@ static const char *btf_show_name(struct btf_show *show)
 			parens = "{";
 		break;
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		prefix = "enum";
 		break;
 	default:
@@ -1832,6 +1840,7 @@ __btf_resolve_size(const struct btf *btf, const struct btf_type *type,
 		case BTF_KIND_UNION:
 		case BTF_KIND_ENUM:
 		case BTF_KIND_FLOAT:
+		case BTF_KIND_ENUM64:
 			size = type->size;
 			goto resolved;
 
@@ -3668,6 +3677,7 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
 {
 	const struct btf_enum *enums = btf_type_enum(t);
 	struct btf *btf = env->btf;
+	const char *fmt_str;
 	u16 i, nr_enums;
 	u32 meta_needed;
 
@@ -3681,11 +3691,6 @@ 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 > 8 || !is_power_of_2(t->size)) {
 		btf_verifier_log_type(env, t, "Unexpected size");
 		return -EINVAL;
@@ -3716,7 +3721,8 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
 
 		if (env->log.level == BPF_LOG_KERNEL)
 			continue;
-		btf_verifier_log(env, "\t%s val=%d\n",
+		fmt_str = btf_type_kflag(t) ? "\t%s val=%u\n" : "\t%s val=%d\n";
+		btf_verifier_log(env, fmt_str,
 				 __btf_name_by_offset(btf, enums[i].name_off),
 				 enums[i].val);
 	}
@@ -3757,7 +3763,10 @@ static void btf_enum_show(const struct btf *btf, const struct btf_type *t,
 		return;
 	}
 
-	btf_show_type_value(show, "%d", v);
+	if (btf_type_kflag(t))
+		btf_show_type_value(show, "%u", v);
+	else
+		btf_show_type_value(show, "%d", v);
 	btf_show_end_type(show);
 }
 
@@ -3770,6 +3779,109 @@ static struct btf_kind_operations enum_ops = {
 	.show = btf_enum_show,
 };
 
+static s32 btf_enum64_check_meta(struct btf_verifier_env *env,
+				 const struct btf_type *t,
+				 u32 meta_left)
+{
+	const struct btf_enum64 *enums = btf_type_enum64(t);
+	struct btf *btf = env->btf;
+	const char *fmt_str;
+	u16 i, nr_enums;
+	u32 meta_needed;
+
+	nr_enums = btf_type_vlen(t);
+	meta_needed = nr_enums * sizeof(*enums);
+
+	if (meta_left < meta_needed) {
+		btf_verifier_log_basic(env, t,
+				       "meta_left:%u meta_needed:%u",
+				       meta_left, meta_needed);
+		return -EINVAL;
+	}
+
+	if (t->size != 8) {
+		btf_verifier_log_type(env, t, "Unexpected size");
+		return -EINVAL;
+	}
+
+	/* enum type either no name or a valid one */
+	if (t->name_off &&
+	    !btf_name_valid_identifier(env->btf, t->name_off)) {
+		btf_verifier_log_type(env, t, "Invalid name");
+		return -EINVAL;
+	}
+
+	btf_verifier_log_type(env, t, NULL);
+
+	for (i = 0; i < nr_enums; i++) {
+		if (!btf_name_offset_valid(btf, enums[i].name_off)) {
+			btf_verifier_log(env, "\tInvalid name_offset:%u",
+					 enums[i].name_off);
+			return -EINVAL;
+		}
+
+		/* enum member must have a valid name */
+		if (!enums[i].name_off ||
+		    !btf_name_valid_identifier(btf, enums[i].name_off)) {
+			btf_verifier_log_type(env, t, "Invalid name");
+			return -EINVAL;
+		}
+
+		if (env->log.level == BPF_LOG_KERNEL)
+			continue;
+
+		fmt_str = btf_type_kflag(t) ? "\t%s val=%llu\n" : "\t%s val=%lld\n";
+		btf_verifier_log(env, fmt_str,
+				 __btf_name_by_offset(btf, enums[i].name_off),
+				 btf_enum64_value(enums + i));
+	}
+
+	return meta_needed;
+}
+
+static void btf_enum64_show(const struct btf *btf, const struct btf_type *t,
+			    u32 type_id, void *data, u8 bits_offset,
+			    struct btf_show *show)
+{
+	const struct btf_enum64 *enums = btf_type_enum64(t);
+	u32 i, nr_enums = btf_type_vlen(t);
+	void *safe_data;
+	s64 v;
+
+	safe_data = btf_show_start_type(show, t, type_id, data);
+	if (!safe_data)
+		return;
+
+	v = *(u64 *)safe_data;
+
+	for (i = 0; i < nr_enums; i++) {
+		if (v != btf_enum64_value(enums + i))
+			continue;
+
+		btf_show_type_value(show, "%s",
+				    __btf_name_by_offset(btf,
+							 enums[i].name_off));
+
+		btf_show_end_type(show);
+		return;
+	}
+
+	if (btf_type_kflag(t))
+		btf_show_type_value(show, "%llu", v);
+	else
+		btf_show_type_value(show, "%lld", v);
+	btf_show_end_type(show);
+}
+
+static struct btf_kind_operations enum64_ops = {
+	.check_meta = btf_enum64_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,
+	.show = btf_enum64_show,
+};
+
 static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
 				     const struct btf_type *t,
 				     u32 meta_left)
@@ -4436,6 +4548,7 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
 	[BTF_KIND_FLOAT] = &float_ops,
 	[BTF_KIND_DECL_TAG] = &decl_tag_ops,
 	[BTF_KIND_TYPE_TAG] = &modifier_ops,
+	[BTF_KIND_ENUM64] = &enum64_ops,
 };
 
 static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -7329,6 +7442,7 @@ int __bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
 	case BTF_KIND_UNION:
 	case BTF_KIND_ENUM:
 	case BTF_KIND_FWD:
+	case BTF_KIND_ENUM64:
 		return 1;
 	case BTF_KIND_INT:
 		/* just reject deprecated bitfield-like integers; all other
@@ -7381,10 +7495,10 @@ int __bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
  * field-based relocations. This function assumes that root types were already
  * checked for name match. Beyond that initial root-level name check, names
  * are completely ignored. Compatibility rules are as follows:
- *   - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but
+ *   - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs/ENUM64s are considered compatible, but
  *     kind should match for local and target types (i.e., STRUCT is not
  *     compatible with UNION);
- *   - for ENUMs, the size is ignored;
+ *   - for ENUMs/ENUM64s, the size is ignored;
  *   - for INT, size and signedness are ignored;
  *   - for ARRAY, dimensionality is ignored, element types are checked for
  *     compatibility recursively;
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index a9162a6c0284..2aac226a27b2 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -36,10 +36,10 @@ struct btf_type {
 	 * bits 24-28: kind (e.g. int, ptr, array...etc)
 	 * bits 29-30: unused
 	 * bit     31: kind_flag, currently used by
-	 *             struct, union and fwd
+	 *             struct, union, enum, fwd and enum64
 	 */
 	__u32 info;
-	/* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
+	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
 	 * "size" tells the size of the type it is describing.
 	 *
 	 * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
@@ -63,7 +63,7 @@ enum {
 	BTF_KIND_ARRAY		= 3,	/* Array	*/
 	BTF_KIND_STRUCT		= 4,	/* Struct	*/
 	BTF_KIND_UNION		= 5,	/* Union	*/
-	BTF_KIND_ENUM		= 6,	/* Enumeration	*/
+	BTF_KIND_ENUM		= 6,	/* Enumeration for int/unsigned int values */
 	BTF_KIND_FWD		= 7,	/* Forward	*/
 	BTF_KIND_TYPEDEF	= 8,	/* Typedef	*/
 	BTF_KIND_VOLATILE	= 9,	/* Volatile	*/
@@ -76,6 +76,7 @@ enum {
 	BTF_KIND_FLOAT		= 16,	/* Floating point	*/
 	BTF_KIND_DECL_TAG	= 17,	/* Decl Tag */
 	BTF_KIND_TYPE_TAG	= 18,	/* Type Tag */
+	BTF_KIND_ENUM64		= 19,	/* Enumeration for long/unsigned long values */
 
 	NR_BTF_KINDS,
 	BTF_KIND_MAX		= NR_BTF_KINDS - 1,
@@ -186,4 +187,14 @@ struct btf_decl_tag {
        __s32   component_idx;
 };
 
+/* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64".
+ * The exact number of btf_enum64 is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_enum64 {
+	__u32	name_off;
+	__u32	hi32;
+	__u32	lo32;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
  2022-05-01 19:00 ` [PATCH bpf-next 01/12] bpf: Add btf enum64 support Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-09  1:06   ` Dave Marchevsky
  2022-05-09 22:37   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation Yonghong Song
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Currently, the libbpf limits the relocation value to be 32bit
since all current relocations have such a limit. But with
BTF_KIND_ENUM64 support, the enum value could be 64bit.
So let us permit 64bit relocation value in libbpf.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/lib/bpf/relo_core.c | 24 ++++++++++++------------
 tools/lib/bpf/relo_core.h |  4 ++--
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
index ba4453dfd1ed..2ed94daabbe5 100644
--- a/tools/lib/bpf/relo_core.c
+++ b/tools/lib/bpf/relo_core.c
@@ -583,7 +583,7 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
 static int bpf_core_calc_field_relo(const char *prog_name,
 				    const struct bpf_core_relo *relo,
 				    const struct bpf_core_spec *spec,
-				    __u32 *val, __u32 *field_sz, __u32 *type_id,
+				    __u64 *val, __u32 *field_sz, __u32 *type_id,
 				    bool *validate)
 {
 	const struct bpf_core_accessor *acc;
@@ -708,7 +708,7 @@ static int bpf_core_calc_field_relo(const char *prog_name,
 
 static int bpf_core_calc_type_relo(const struct bpf_core_relo *relo,
 				   const struct bpf_core_spec *spec,
-				   __u32 *val, bool *validate)
+				   __u64 *val, bool *validate)
 {
 	__s64 sz;
 
@@ -751,7 +751,7 @@ static int bpf_core_calc_type_relo(const struct bpf_core_relo *relo,
 
 static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
 				      const struct bpf_core_spec *spec,
-				      __u32 *val)
+				      __u64 *val)
 {
 	const struct btf_type *t;
 	const struct btf_enum *e;
@@ -929,7 +929,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 			int insn_idx, const struct bpf_core_relo *relo,
 			int relo_idx, const struct bpf_core_relo_res *res)
 {
-	__u32 orig_val, new_val;
+	__u64 orig_val, new_val;
 	__u8 class;
 
 	class = BPF_CLASS(insn->code);
@@ -954,14 +954,14 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 		if (BPF_SRC(insn->code) != BPF_K)
 			return -EINVAL;
 		if (res->validate && insn->imm != orig_val) {
-			pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %u -> %u\n",
+			pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %llu -> %llu\n",
 				prog_name, relo_idx,
 				insn_idx, insn->imm, orig_val, new_val);
 			return -EINVAL;
 		}
 		orig_val = insn->imm;
 		insn->imm = new_val;
-		pr_debug("prog '%s': relo #%d: patched insn #%d (ALU/ALU64) imm %u -> %u\n",
+		pr_debug("prog '%s': relo #%d: patched insn #%d (ALU/ALU64) imm %llu -> %llu\n",
 			 prog_name, relo_idx, insn_idx,
 			 orig_val, new_val);
 		break;
@@ -969,12 +969,12 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 	case BPF_ST:
 	case BPF_STX:
 		if (res->validate && insn->off != orig_val) {
-			pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDX/ST/STX) value: got %u, exp %u -> %u\n",
+			pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDX/ST/STX) value: got %u, exp %llu -> %llu\n",
 				prog_name, relo_idx, insn_idx, insn->off, orig_val, new_val);
 			return -EINVAL;
 		}
 		if (new_val > SHRT_MAX) {
-			pr_warn("prog '%s': relo #%d: insn #%d (LDX/ST/STX) value too big: %u\n",
+			pr_warn("prog '%s': relo #%d: insn #%d (LDX/ST/STX) value too big: %llu\n",
 				prog_name, relo_idx, insn_idx, new_val);
 			return -ERANGE;
 		}
@@ -987,7 +987,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 
 		orig_val = insn->off;
 		insn->off = new_val;
-		pr_debug("prog '%s': relo #%d: patched insn #%d (LDX/ST/STX) off %u -> %u\n",
+		pr_debug("prog '%s': relo #%d: patched insn #%d (LDX/ST/STX) off %llu -> %llu\n",
 			 prog_name, relo_idx, insn_idx, orig_val, new_val);
 
 		if (res->new_sz != res->orig_sz) {
@@ -1026,7 +1026,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 
 		imm = insn[0].imm + ((__u64)insn[1].imm << 32);
 		if (res->validate && imm != orig_val) {
-			pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %u -> %u\n",
+			pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
 				prog_name, relo_idx,
 				insn_idx, (unsigned long long)imm,
 				orig_val, new_val);
@@ -1035,7 +1035,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 
 		insn[0].imm = new_val;
 		insn[1].imm = 0; /* currently only 32-bit values are supported */
-		pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n",
+		pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
 			 prog_name, relo_idx, insn_idx,
 			 (unsigned long long)imm, new_val);
 		break;
@@ -1261,7 +1261,7 @@ int bpf_core_calc_relo_insn(const char *prog_name,
 			 * decision and value, otherwise it's dangerous to
 			 * proceed due to ambiguity
 			 */
-			pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n",
+			pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %llu != %s %llu\n",
 				prog_name, relo_idx,
 				cand_res.poison ? "failure" : "success", cand_res.new_val,
 				targ_res->poison ? "failure" : "success", targ_res->new_val);
diff --git a/tools/lib/bpf/relo_core.h b/tools/lib/bpf/relo_core.h
index 073039d8ca4f..7df0da082f2c 100644
--- a/tools/lib/bpf/relo_core.h
+++ b/tools/lib/bpf/relo_core.h
@@ -46,9 +46,9 @@ struct bpf_core_spec {
 
 struct bpf_core_relo_res {
 	/* expected value in the instruction, unless validate == false */
-	__u32 orig_val;
+	__u64 orig_val;
 	/* new value that needs to be patched up to */
-	__u32 new_val;
+	__u64 new_val;
 	/* relocation unsuccessful, poison instruction, but don't fail load */
 	bool poison;
 	/* some relocations can't be validated against orig_val */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
  2022-05-01 19:00 ` [PATCH bpf-next 01/12] bpf: Add btf enum64 support Yonghong Song
  2022-05-01 19:00 ` [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-09  0:55   ` Dave Marchevsky
  2022-05-09 22:37   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 04/12] libbpf: Add btf enum64 support Yonghong Song
                   ` (8 subsequent siblings)
  11 siblings, 2 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Currently, the 64bit relocation value in the instruction
is computed as follows:
  __u64 imm = insn[0].imm + ((__u64)insn[1].imm << 32)

Suppose insn[0].imm = -1 (0xffffffff) and insn[1].imm = 1.
With the above computation, insn[0].imm will first sign-extend
to 64bit -1 (0xffffffffFFFFFFFF) and then add 0x1FFFFFFFF,
producing incorrect value 0xFFFFFFFF. The correct value
should be 0x1FFFFFFFF.

Changing insn[0].imm to __u32 first will prevent 64bit sign
extension and fix the issue.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/lib/bpf/relo_core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
index 2ed94daabbe5..f25ffd03c3b1 100644
--- a/tools/lib/bpf/relo_core.c
+++ b/tools/lib/bpf/relo_core.c
@@ -1024,7 +1024,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 			return -EINVAL;
 		}
 
-		imm = insn[0].imm + ((__u64)insn[1].imm << 32);
+		imm = (__u32)insn[0].imm + ((__u64)insn[1].imm << 32);
 		if (res->validate && imm != orig_val) {
 			pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
 				prog_name, relo_idx,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (2 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-03 17:22   ` kernel test robot
  2022-05-09 23:25   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 05/12] bpftool: " Yonghong Song
                   ` (7 subsequent siblings)
  11 siblings, 2 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
btf__add_enum_value() and introduced the following new APIs
  btf__add_enum32()
  btf__add_enum32_value()
  btf__add_enum64()
  btf__add_enum64_value()
due to new kind and introduction of kflag.

To support old kernel with enum64, the sanitization is
added to replace BTF_KIND_ENUM64 with a bunch of
pointer-to-void types.

The enum64 value relocation is also supported. The enum64
forward resolution, with enum type as forward declaration
and enum64 as the actual definition, is also supported.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
 tools/lib/bpf/btf.h                           |  21 ++
 tools/lib/bpf/btf_dump.c                      |  94 ++++++--
 tools/lib/bpf/libbpf.c                        |  64 ++++-
 tools/lib/bpf/libbpf.map                      |   4 +
 tools/lib/bpf/libbpf_internal.h               |   2 +
 tools/lib/bpf/linker.c                        |   2 +
 tools/lib/bpf/relo_core.c                     |  93 ++++---
 .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
 .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
 10 files changed, 450 insertions(+), 72 deletions(-)

diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index bb1e06eb1eca..77fe14e99eeb 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -318,6 +318,8 @@ static int btf_type_size(const struct btf_type *t)
 		return base_size + vlen * sizeof(struct btf_var_secinfo);
 	case BTF_KIND_DECL_TAG:
 		return base_size + sizeof(struct btf_decl_tag);
+	case BTF_KIND_ENUM64:
+		return base_size + vlen * sizeof(struct btf_enum64);
 	default:
 		pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
 		return -EINVAL;
@@ -334,6 +336,7 @@ static void btf_bswap_type_base(struct btf_type *t)
 static int btf_bswap_type_rest(struct btf_type *t)
 {
 	struct btf_var_secinfo *v;
+	struct btf_enum64 *e64;
 	struct btf_member *m;
 	struct btf_array *a;
 	struct btf_param *p;
@@ -394,6 +397,13 @@ static int btf_bswap_type_rest(struct btf_type *t)
 	case BTF_KIND_DECL_TAG:
 		btf_decl_tag(t)->component_idx = bswap_32(btf_decl_tag(t)->component_idx);
 		return 0;
+	case BTF_KIND_ENUM64:
+		for (i = 0, e64 = btf_enum64(t); i < vlen; i++, e64++) {
+			e64->name_off = bswap_32(e64->name_off);
+			e64->hi32 = bswap_32(e64->hi32);
+			e64->lo32 = bswap_32(e64->lo32);
+		}
+		return 0;
 	default:
 		pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
 		return -EINVAL;
@@ -599,6 +609,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
 		case BTF_KIND_ENUM:
 		case BTF_KIND_DATASEC:
 		case BTF_KIND_FLOAT:
+		case BTF_KIND_ENUM64:
 			size = t->size;
 			goto done;
 		case BTF_KIND_PTR:
@@ -645,6 +656,7 @@ int btf__align_of(const struct btf *btf, __u32 id)
 	case BTF_KIND_INT:
 	case BTF_KIND_ENUM:
 	case BTF_KIND_FLOAT:
+	case BTF_KIND_ENUM64:
 		return min(btf_ptr_sz(btf), (size_t)t->size);
 	case BTF_KIND_PTR:
 		return btf_ptr_sz(btf);
@@ -2211,6 +2223,171 @@ int btf__add_enum_value(struct btf *btf, const char *name, __s64 value)
 	return 0;
 }
 
+static int btf_add_enum_common(struct btf *btf, const char *name,
+			       bool is_unsigned, __u8 kind, __u32 tsize)
+{
+	struct btf_type *t;
+	int sz, name_off = 0;
+
+	if (btf_ensure_modifiable(btf))
+		return libbpf_err(-ENOMEM);
+
+	sz = sizeof(struct btf_type);
+	t = btf_add_type_mem(btf, sz);
+	if (!t)
+		return libbpf_err(-ENOMEM);
+
+	if (name && name[0]) {
+		name_off = btf__add_str(btf, name);
+		if (name_off < 0)
+			return name_off;
+	}
+
+	/* start out with vlen=0; it will be adjusted when adding enum values */
+	t->name_off = name_off;
+	t->info = btf_type_info(kind, 0, is_unsigned);
+	t->size = tsize;
+
+	return btf_commit_type(btf, sz);
+}
+
+/*
+ * Append new BTF_KIND_ENUM type with:
+ *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
+ *   - *is_unsigned* - whether the enum values are unsigned or not;
+ *
+ * Enum initially has no enum values in it (and corresponds to enum forward
+ * declaration). Enumerator values can be added by btf__add_enum64_value()
+ * immediately after btf__add_enum() succeeds.
+ *
+ * Returns:
+ *   - >0, type ID of newly added BTF type;
+ *   - <0, on error.
+ */
+int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned)
+{
+	return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM, 4);
+}
+
+/*
+ * Append new enum value for the current ENUM type with:
+ *   - *name* - name of the enumerator value, can't be NULL or empty;
+ *   - *value* - integer value corresponding to enum value *name*;
+ * Returns:
+ *   -  0, on success;
+ *   - <0, on error.
+ */
+int btf__add_enum32_value(struct btf *btf, const char *name, __s32 value)
+{
+	struct btf_enum *v;
+	struct btf_type *t;
+	int sz, name_off;
+
+	/* last type should be BTF_KIND_ENUM */
+	if (btf->nr_types == 0)
+		return libbpf_err(-EINVAL);
+	t = btf_last_type(btf);
+	if (!btf_is_enum(t))
+		return libbpf_err(-EINVAL);
+
+	/* non-empty name */
+	if (!name || !name[0])
+		return libbpf_err(-EINVAL);
+
+	/* decompose and invalidate raw data */
+	if (btf_ensure_modifiable(btf))
+		return libbpf_err(-ENOMEM);
+
+	sz = sizeof(struct btf_enum);
+	v = btf_add_type_mem(btf, sz);
+	if (!v)
+		return libbpf_err(-ENOMEM);
+
+	name_off = btf__add_str(btf, name);
+	if (name_off < 0)
+		return name_off;
+
+	v->name_off = name_off;
+	v->val = value;
+
+	/* update parent type's vlen */
+	t = btf_last_type(btf);
+	btf_type_inc_vlen(t);
+
+	btf->hdr->type_len += sz;
+	btf->hdr->str_off += sz;
+	return 0;
+}
+
+/*
+ * Append new BTF_KIND_ENUM64 type with:
+ *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
+ *   - *is_unsigned* - whether the enum values are unsigned or not;
+ *
+ * Enum64 initially has no enum values in it (and corresponds to enum forward
+ * declaration). Enumerator values can be added by btf__add_enum64_value()
+ * immediately after btf__add_enum64() succeeds.
+ *
+ * Returns:
+ *   - >0, type ID of newly added BTF type;
+ *   - <0, on error.
+ */
+int btf__add_enum64(struct btf *btf, const char *name, bool is_unsigned)
+{
+	return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM64, 8);
+}
+
+/*
+ * Append new enum value for the current ENUM64 type with:
+ *   - *name* - name of the enumerator value, can't be NULL or empty;
+ *   - *value* - integer value corresponding to enum value *name*;
+ * Returns:
+ *   -  0, on success;
+ *   - <0, on error.
+ */
+int btf__add_enum64_value(struct btf *btf, const char *name, __u64 value)
+{
+	struct btf_enum64 *v;
+	struct btf_type *t;
+	int sz, name_off;
+
+	/* last type should be BTF_KIND_ENUM64 */
+	if (btf->nr_types == 0)
+		return libbpf_err(-EINVAL);
+	t = btf_last_type(btf);
+	if (!btf_is_enum64(t))
+		return libbpf_err(-EINVAL);
+
+	/* non-empty name */
+	if (!name || !name[0])
+		return libbpf_err(-EINVAL);
+
+	/* decompose and invalidate raw data */
+	if (btf_ensure_modifiable(btf))
+		return libbpf_err(-ENOMEM);
+
+	sz = sizeof(struct btf_enum64);
+	v = btf_add_type_mem(btf, sz);
+	if (!v)
+		return libbpf_err(-ENOMEM);
+
+	name_off = btf__add_str(btf, name);
+	if (name_off < 0)
+		return name_off;
+
+	v->name_off = name_off;
+	v->hi32 = value >> 32;
+	v->lo32 = (__u32)value;
+
+	/* update parent type's vlen */
+	t = btf_last_type(btf);
+	btf_type_inc_vlen(t);
+
+	btf->hdr->type_len += sz;
+	btf->hdr->str_off += sz;
+	return 0;
+}
+
 /*
  * Append new BTF_KIND_FWD type with:
  *   - *name*, non-empty/non-NULL name;
@@ -2242,7 +2419,7 @@ int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind)
 		/* enum forward in BTF currently is just an enum with no enum
 		 * values; we also assume a standard 4-byte size for it
 		 */
-		return btf__add_enum(btf, name, sizeof(int));
+		return btf__add_enum32(btf, name, false);
 	default:
 		return libbpf_err(-EINVAL);
 	}
@@ -3485,6 +3662,7 @@ static long btf_hash_enum(struct btf_type *t)
 /* Check structural equality of two ENUMs. */
 static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
 {
+	const struct btf_enum64 *n1, *n2;
 	const struct btf_enum *m1, *m2;
 	__u16 vlen;
 	int i;
@@ -3493,26 +3671,40 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
 		return false;
 
 	vlen = btf_vlen(t1);
-	m1 = btf_enum(t1);
-	m2 = btf_enum(t2);
-	for (i = 0; i < vlen; i++) {
-		if (m1->name_off != m2->name_off || m1->val != m2->val)
-			return false;
-		m1++;
-		m2++;
+	if (btf_is_enum(t1)) {
+		m1 = btf_enum(t1);
+		m2 = btf_enum(t2);
+		for (i = 0; i < vlen; i++) {
+			if (m1->name_off != m2->name_off || m1->val != m2->val)
+				return false;
+			m1++;
+			m2++;
+		}
+	} else {
+		n1 = btf_enum64(t1);
+		n2 = btf_enum64(t2);
+		for (i = 0; i < vlen; i++) {
+			if (n1->name_off != n2->name_off || n1->hi32 != n2->hi32 ||
+			    n1->lo32 != n2->lo32)
+				return false;
+			n1++;
+			n2++;
+		}
 	}
 	return true;
 }
 
 static inline bool btf_is_enum_fwd(struct btf_type *t)
 {
-	return btf_is_enum(t) && btf_vlen(t) == 0;
+	return (btf_is_enum(t) || btf_is_enum64(t)) && btf_vlen(t) == 0;
 }
 
 static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
 {
-	if (!btf_is_enum_fwd(t1) && !btf_is_enum_fwd(t2))
+	if (!btf_is_enum_fwd(t1) && !btf_is_enum_fwd(t2)) {
 		return btf_equal_enum(t1, t2);
+	}
+
 	/* ignore vlen when comparing */
 	return t1->name_off == t2->name_off &&
 	       (t1->info & ~0xffff) == (t2->info & ~0xffff) &&
@@ -3731,6 +3923,7 @@ static int btf_dedup_prep(struct btf_dedup *d)
 			h = btf_hash_int_decl_tag(t);
 			break;
 		case BTF_KIND_ENUM:
+		case BTF_KIND_ENUM64:
 			h = btf_hash_enum(t);
 			break;
 		case BTF_KIND_STRUCT:
@@ -3800,6 +3993,7 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
 		break;
 
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		h = btf_hash_enum(t);
 		for_each_dedup_cand(d, hash_entry, h) {
 			cand_id = (__u32)(long)hash_entry->value;
@@ -4113,6 +4307,7 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
 		return btf_equal_int_tag(cand_type, canon_type);
 
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		return btf_compat_enum(cand_type, canon_type);
 
 	case BTF_KIND_FWD:
@@ -4717,6 +4912,7 @@ int btf_type_visit_type_ids(struct btf_type *t, type_id_visit_fn visit, void *ct
 	case BTF_KIND_INT:
 	case BTF_KIND_FLOAT:
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		return 0;
 
 	case BTF_KIND_FWD:
@@ -4811,6 +5007,16 @@ int btf_type_visit_str_offs(struct btf_type *t, str_off_visit_fn visit, void *ct
 		}
 		break;
 	}
+	case BTF_KIND_ENUM64: {
+		struct btf_enum64 *m = btf_enum64(t);
+
+		for (i = 0, n = btf_vlen(t); i < n; i++, m++) {
+			err = visit(&m->name_off, ctx);
+			if (err)
+				return err;
+		}
+		break;
+	}
 	case BTF_KIND_FUNC_PROTO: {
 		struct btf_param *m = btf_params(t);
 
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index 951ac7475794..90f35bc00038 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -213,8 +213,14 @@ LIBBPF_API int btf__add_field(struct btf *btf, const char *name, int field_type_
 			      __u32 bit_offset, __u32 bit_size);
 
 /* enum construction APIs */
+LIBBPF_DEPRECATED_SINCE(0, 8, "btf__add_enum is deprecated; use btf__add_enum32 or btf__add_enum64")
 LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32 bytes_sz);
+LIBBPF_DEPRECATED_SINCE(0, 8, "btf__add_enum_value is deprecated; use btf_add_enum32_value or btf_add_enum64_value")
 LIBBPF_API int btf__add_enum_value(struct btf *btf, const char *name, __s64 value);
+LIBBPF_API int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned);
+LIBBPF_API int btf__add_enum32_value(struct btf *btf, const char *name, __s32 value);
+LIBBPF_API int btf__add_enum64(struct btf *btf, const char *name, bool is_unsigned);
+LIBBPF_API int btf__add_enum64_value(struct btf *btf, const char *name, __u64 value);
 
 enum btf_fwd_kind {
 	BTF_FWD_STRUCT = 0,
@@ -454,6 +460,11 @@ static inline bool btf_is_enum(const struct btf_type *t)
 	return btf_kind(t) == BTF_KIND_ENUM;
 }
 
+static inline bool btf_is_enum64(const struct btf_type *t)
+{
+	return btf_kind(t) == BTF_KIND_ENUM64;
+}
+
 static inline bool btf_is_fwd(const struct btf_type *t)
 {
 	return btf_kind(t) == BTF_KIND_FWD;
@@ -549,6 +560,16 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t)
 	return (struct btf_enum *)(t + 1);
 }
 
+static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
+{
+	return (struct btf_enum64 *)(t + 1);
+}
+
+static inline __u64 btf_enum64_value(const struct btf_enum64 *e)
+{
+	return (__u64)e->hi32 << 32 | e->lo32;
+}
+
 static inline struct btf_member *btf_members(const struct btf_type *t)
 {
 	return (struct btf_member *)(t + 1);
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index 6b1bc1f43728..c3f99ca29426 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -320,6 +320,7 @@ static int btf_dump_mark_referenced(struct btf_dump *d)
 		case BTF_KIND_ENUM:
 		case BTF_KIND_FWD:
 		case BTF_KIND_FLOAT:
+		case BTF_KIND_ENUM64:
 			break;
 
 		case BTF_KIND_VOLATILE:
@@ -539,6 +540,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
 	}
 	case BTF_KIND_ENUM:
 	case BTF_KIND_FWD:
+	case BTF_KIND_ENUM64:
 		/*
 		 * non-anonymous or non-referenced enums are top-level
 		 * declarations and should be emitted. Same logic can be
@@ -739,6 +741,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
 		tstate->emit_state = EMITTED;
 		break;
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		if (top_level_def) {
 			btf_dump_emit_enum_def(d, id, t, 0);
 			btf_dump_printf(d, ";\n\n");
@@ -993,8 +996,11 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
 				   const struct btf_type *t,
 				   int lvl)
 {
-	const struct btf_enum *v = btf_enum(t);
+	bool is_unsigned = btf_kflag(t);
+	const struct btf_enum64 *v64;
+	const struct btf_enum *v;
 	__u16 vlen = btf_vlen(t);
+	const char *fmt_str;
 	const char *name;
 	size_t dup_cnt;
 	int i;
@@ -1005,18 +1011,47 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
 
 	if (vlen) {
 		btf_dump_printf(d, " {");
-		for (i = 0; i < vlen; i++, v++) {
-			name = btf_name_of(d, v->name_off);
-			/* enumerators share namespace with typedef idents */
-			dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
-			if (dup_cnt > 1) {
-				btf_dump_printf(d, "\n%s%s___%zu = %u,",
-						pfx(lvl + 1), name, dup_cnt,
-						(__u32)v->val);
-			} else {
-				btf_dump_printf(d, "\n%s%s = %u,",
-						pfx(lvl + 1), name,
-						(__u32)v->val);
+		if (btf_is_enum(t)) {
+			v = btf_enum(t);
+			for (i = 0; i < vlen; i++, v++) {
+				name = btf_name_of(d, v->name_off);
+				/* enumerators share namespace with typedef idents */
+				dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
+				if (dup_cnt > 1) {
+					fmt_str = is_unsigned ? "\n%s%s___%zu = %u,"
+							      : "\n%s%s___%zu = %d,";
+					btf_dump_printf(d, fmt_str,
+							pfx(lvl + 1), name, dup_cnt,
+							v->val);
+				} else {
+					fmt_str = is_unsigned ? "\n%s%s = %u,"
+							      : "\n%s%s = %d,";
+					btf_dump_printf(d, fmt_str,
+							pfx(lvl + 1), name,
+							v->val);
+				}
+			}
+		} else {
+			v64 = btf_enum64(t);
+			for (i = 0; i < vlen; i++, v64++) {
+				__u64 val = btf_enum64_value(v64);
+
+				name = btf_name_of(d, v64->name_off);
+				/* enumerators share namespace with typedef idents */
+				dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
+				if (dup_cnt > 1) {
+					fmt_str = is_unsigned ? "\n%s%s___%zu = %lluULL,"
+							      : "\n%s%s___%zu = %lldLL,";
+					btf_dump_printf(d, fmt_str,
+							pfx(lvl + 1), name, dup_cnt,
+							val);
+				} else {
+					fmt_str = is_unsigned ? "\n%s%s = %lluULL,"
+							      : "\n%s%s = %lldLL,";
+					btf_dump_printf(d, fmt_str,
+							pfx(lvl + 1), name,
+							val);
+				}
 			}
 		}
 		btf_dump_printf(d, "\n%s}", pfx(lvl));
@@ -1183,6 +1218,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
 		case BTF_KIND_UNION:
 		case BTF_KIND_TYPEDEF:
 		case BTF_KIND_FLOAT:
+		case BTF_KIND_ENUM64:
 			goto done;
 		default:
 			pr_warn("unexpected type in decl chain, kind:%u, id:[%u]\n",
@@ -1312,6 +1348,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
 				btf_dump_emit_struct_fwd(d, id, t);
 			break;
 		case BTF_KIND_ENUM:
+		case BTF_KIND_ENUM64:
 			btf_dump_emit_mods(d, decls);
 			/* inline anonymous enum */
 			if (t->name_off == 0 && !d->skip_anon_defs)
@@ -2024,7 +2061,9 @@ static int btf_dump_enum_data(struct btf_dump *d,
 			      __u32 id,
 			      const void *data)
 {
+	const struct btf_enum64 *e64;
 	const struct btf_enum *e;
+	bool is_unsigned;
 	__s64 value;
 	int i, err;
 
@@ -2032,14 +2071,26 @@ static int btf_dump_enum_data(struct btf_dump *d,
 	if (err)
 		return err;
 
-	for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) {
-		if (value != e->val)
-			continue;
-		btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off));
-		return 0;
-	}
+	is_unsigned = btf_kflag(t);
+	if (btf_is_enum(t)) {
+		for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) {
+			if (value != e->val)
+				continue;
+			btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off));
+			return 0;
+		}
 
-	btf_dump_type_values(d, "%d", value);
+		btf_dump_type_values(d, is_unsigned ? "%u" : "%d", value);
+	} else {
+		for (i = 0, e64 = btf_enum64(t); i < btf_vlen(t); i++, e64++) {
+			if (value != btf_enum64_value(e64))
+				continue;
+			btf_dump_type_values(d, "%s", btf_name_of(d, e64->name_off));
+			return 0;
+		}
+
+		btf_dump_type_values(d, is_unsigned ? "%lluULL" : "%lldLL", value);
+	}
 	return 0;
 }
 
@@ -2099,6 +2150,7 @@ static int btf_dump_type_data_check_overflow(struct btf_dump *d,
 	case BTF_KIND_FLOAT:
 	case BTF_KIND_PTR:
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		if (data + bits_offset / 8 + size > d->typed_dump->data_end)
 			return -E2BIG;
 		break;
@@ -2203,6 +2255,7 @@ static int btf_dump_type_data_check_zero(struct btf_dump *d,
 		return -ENODATA;
 	}
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		err = btf_dump_get_enum_value(d, t, data, id, &value);
 		if (err)
 			return err;
@@ -2275,6 +2328,7 @@ static int btf_dump_dump_type_data(struct btf_dump *d,
 		err = btf_dump_struct_data(d, t, id, data);
 		break;
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		/* handle bitfield and int enum values */
 		if (bit_sz) {
 			__u64 print_num;
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 63c0f412266c..2e8b843ff5ef 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2114,6 +2114,7 @@ static const char *__btf_kind_str(__u16 kind)
 	case BTF_KIND_FLOAT: return "float";
 	case BTF_KIND_DECL_TAG: return "decl_tag";
 	case BTF_KIND_TYPE_TAG: return "type_tag";
+	case BTF_KIND_ENUM64: return "enum64";
 	default: return "unknown";
 	}
 }
@@ -2642,9 +2643,10 @@ static bool btf_needs_sanitization(struct bpf_object *obj)
 	bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
 	bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
 	bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
+	bool has_enum64 = kernel_supports(obj, FEAT_BTF_ENUM64);
 
 	return !has_func || !has_datasec || !has_func_global || !has_float ||
-	       !has_decl_tag || !has_type_tag;
+	       !has_decl_tag || !has_type_tag || !has_enum64;
 }
 
 static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
@@ -2655,6 +2657,7 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
 	bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
 	bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
 	bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
+	bool has_enum64 = kernel_supports(obj, FEAT_BTF_ENUM64);
 	struct btf_type *t;
 	int i, j, vlen;
 
@@ -2717,6 +2720,17 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
 			/* replace TYPE_TAG with a CONST */
 			t->name_off = 0;
 			t->info = BTF_INFO_ENC(BTF_KIND_CONST, 0, 0);
+		} else if (!has_enum64 && btf_is_enum(t)) {
+			/* clear the kflag */
+			t->info &= 0x7fffffff;
+		} else if (!has_enum64 && btf_is_enum64(t)) {
+			/* replace ENUM64 with pointer->void's */
+			vlen = btf_vlen(t);
+			for (j = 0; j <= vlen; j++, t++) {
+				t->name_off = 0;
+				t->info = BTF_INFO_ENC(BTF_KIND_PTR, 0, 0);
+				t->type = 0;
+			}
 		}
 	}
 }
@@ -3563,6 +3577,12 @@ static enum kcfg_type find_kcfg_type(const struct btf *btf, int id,
 		if (strcmp(name, "libbpf_tristate"))
 			return KCFG_UNKNOWN;
 		return KCFG_TRISTATE;
+	case BTF_KIND_ENUM64:
+		if (t->size != 8)
+			return KCFG_UNKNOWN;
+		if (strcmp(name, "libbpf_tristate"))
+			return KCFG_UNKNOWN;
+		return KCFG_TRISTATE;
 	case BTF_KIND_ARRAY:
 		if (btf_array(t)->nelems == 0)
 			return KCFG_UNKNOWN;
@@ -4746,6 +4766,17 @@ static int probe_kern_bpf_cookie(void)
 	return probe_fd(ret);
 }
 
+static int probe_kern_btf_enum64(void)
+{
+	static const char strs[] = "\0enum64";
+	__u32 types[] = {
+		BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
+	};
+
+	return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
+					     strs, sizeof(strs)));
+}
+
 enum kern_feature_result {
 	FEAT_UNKNOWN = 0,
 	FEAT_SUPPORTED = 1,
@@ -4811,6 +4842,9 @@ static struct kern_feature_desc {
 	[FEAT_BPF_COOKIE] = {
 		"BPF cookie support", probe_kern_bpf_cookie,
 	},
+	[FEAT_BTF_ENUM64] = {
+		"BTF_KIND_ENUM64 support", probe_kern_btf_enum64,
+	},
 };
 
 bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
@@ -5296,6 +5330,15 @@ void bpf_core_free_cands(struct bpf_core_cand_list *cands)
 	free(cands);
 }
 
+static bool btf_is_enum_enum64(const struct btf_type *t1,
+			       const struct btf_type *t2) {
+	if (btf_is_enum(t1) && btf_is_enum64(t2))
+		return true;
+	if (btf_is_enum(t2) && btf_is_enum64(t1))
+		return true;
+	return false;
+}
+
 int bpf_core_add_cands(struct bpf_core_cand *local_cand,
 		       size_t local_essent_len,
 		       const struct btf *targ_btf,
@@ -5315,8 +5358,10 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
 	n = btf__type_cnt(targ_btf);
 	for (i = targ_start_id; i < n; i++) {
 		t = btf__type_by_id(targ_btf, i);
-		if (btf_kind(t) != btf_kind(local_t))
-			continue;
+		if (btf_kind(t) != btf_kind(local_t)) {
+			if (!btf_is_enum_enum64(t, local_t))
+				continue;
+		}
 
 		targ_name = btf__name_by_offset(targ_btf, t->name_off);
 		if (str_is_empty(targ_name))
@@ -5529,8 +5574,10 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
 	/* caller made sure that names match (ignoring flavor suffix) */
 	local_type = btf__type_by_id(local_btf, local_id);
 	targ_type = btf__type_by_id(targ_btf, targ_id);
-	if (btf_kind(local_type) != btf_kind(targ_type))
-		return 0;
+	if (btf_kind(local_type) != btf_kind(targ_type)) {
+		if (!btf_is_enum_enum64(local_type, targ_type))
+			return 0;
+	}
 
 recur:
 	depth--;
@@ -5542,8 +5589,10 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
 	if (!local_type || !targ_type)
 		return -EINVAL;
 
-	if (btf_kind(local_type) != btf_kind(targ_type))
-		return 0;
+	if (btf_kind(local_type) != btf_kind(targ_type)) {
+		if (!btf_is_enum_enum64(local_type, targ_type))
+			return 0;
+	}
 
 	switch (btf_kind(local_type)) {
 	case BTF_KIND_UNKN:
@@ -5551,6 +5600,7 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
 	case BTF_KIND_UNION:
 	case BTF_KIND_ENUM:
 	case BTF_KIND_FWD:
+	case BTF_KIND_ENUM64:
 		return 1;
 	case BTF_KIND_INT:
 		/* just reject deprecated bitfield-like integers; all other
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index b5bc84039407..acde13bd48c8 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -448,6 +448,10 @@ LIBBPF_0.8.0 {
 		bpf_object__open_subskeleton;
 		bpf_program__attach_kprobe_multi_opts;
 		bpf_program__attach_usdt;
+		btf__add_enum32;
+		btf__add_enum32_value;
+		btf__add_enum64;
+		btf__add_enum64_value;
 		libbpf_register_prog_handler;
 		libbpf_unregister_prog_handler;
 } LIBBPF_0.7.0;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 4abdbe2fea9d..10c16acfa8ae 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -351,6 +351,8 @@ enum kern_feature_id {
 	FEAT_MEMCG_ACCOUNT,
 	/* BPF cookie (bpf_get_attach_cookie() BPF helper) support */
 	FEAT_BPF_COOKIE,
+	/* BTF_KIND_ENUM64 support and BTF_KIND_ENUM kflag support */
+	FEAT_BTF_ENUM64,
 	__FEAT_CNT,
 };
 
diff --git a/tools/lib/bpf/linker.c b/tools/lib/bpf/linker.c
index 9aa016fb55aa..1e1ef3302921 100644
--- a/tools/lib/bpf/linker.c
+++ b/tools/lib/bpf/linker.c
@@ -1343,6 +1343,7 @@ static bool glob_sym_btf_matches(const char *sym_name, bool exact,
 	case BTF_KIND_FWD:
 	case BTF_KIND_FUNC:
 	case BTF_KIND_VAR:
+	case BTF_KIND_ENUM64:
 		n1 = btf__str_by_offset(btf1, t1->name_off);
 		n2 = btf__str_by_offset(btf2, t2->name_off);
 		if (strcmp(n1, n2) != 0) {
@@ -1358,6 +1359,7 @@ static bool glob_sym_btf_matches(const char *sym_name, bool exact,
 	switch (btf_kind(t1)) {
 	case BTF_KIND_UNKN: /* void */
 	case BTF_KIND_FWD:
+	case BTF_KIND_ENUM64:
 		return true;
 	case BTF_KIND_INT:
 	case BTF_KIND_FLOAT:
diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
index f25ffd03c3b1..1e751400427b 100644
--- a/tools/lib/bpf/relo_core.c
+++ b/tools/lib/bpf/relo_core.c
@@ -231,11 +231,15 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
 	spec->len++;
 
 	if (core_relo_is_enumval_based(relo->kind)) {
-		if (!btf_is_enum(t) || spec->raw_len > 1 || access_idx >= btf_vlen(t))
+		if (!(btf_is_enum(t) || btf_is_enum64(t)) ||
+		    spec->raw_len > 1 || access_idx >= btf_vlen(t))
 			return -EINVAL;
 
 		/* record enumerator name in a first accessor */
-		acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off);
+		if (btf_is_enum(t))
+			acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off);
+		else
+			acc->name = btf__name_by_offset(btf, btf_enum64(t)[access_idx].name_off);
 		return 0;
 	}
 
@@ -340,15 +344,19 @@ static int bpf_core_fields_are_compat(const struct btf *local_btf,
 
 	if (btf_is_composite(local_type) && btf_is_composite(targ_type))
 		return 1;
-	if (btf_kind(local_type) != btf_kind(targ_type))
-		return 0;
+	if (btf_kind(local_type) != btf_kind(targ_type)) {
+		if (btf_is_enum(local_type) && btf_is_enum64(targ_type)) ;
+		else if (btf_is_enum64(local_type) && btf_is_enum(targ_type)) ;
+		else return 0;
+	}
 
 	switch (btf_kind(local_type)) {
 	case BTF_KIND_PTR:
 	case BTF_KIND_FLOAT:
 		return 1;
 	case BTF_KIND_FWD:
-	case BTF_KIND_ENUM: {
+	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64: {
 		const char *local_name, *targ_name;
 		size_t local_len, targ_len;
 
@@ -494,29 +502,48 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
 
 	if (core_relo_is_enumval_based(local_spec->relo_kind)) {
 		size_t local_essent_len, targ_essent_len;
+		const struct btf_enum64 *e64;
 		const struct btf_enum *e;
 		const char *targ_name;
 
 		/* has to resolve to an enum */
 		targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id, &targ_id);
-		if (!btf_is_enum(targ_type))
+		if (!btf_is_enum(targ_type) && !btf_is_enum64(targ_type))
 			return 0;
 
 		local_essent_len = bpf_core_essential_name_len(local_acc->name);
 
-		for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) {
-			targ_name = btf__name_by_offset(targ_spec->btf, e->name_off);
-			targ_essent_len = bpf_core_essential_name_len(targ_name);
-			if (targ_essent_len != local_essent_len)
-				continue;
-			if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
-				targ_acc->type_id = targ_id;
-				targ_acc->idx = i;
-				targ_acc->name = targ_name;
-				targ_spec->len++;
-				targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
-				targ_spec->raw_len++;
-				return 1;
+		if (btf_is_enum(targ_type)) {
+			for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) {
+				targ_name = btf__name_by_offset(targ_spec->btf, e->name_off);
+				targ_essent_len = bpf_core_essential_name_len(targ_name);
+				if (targ_essent_len != local_essent_len)
+					continue;
+				if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
+					targ_acc->type_id = targ_id;
+					targ_acc->idx = i;
+					targ_acc->name = targ_name;
+					targ_spec->len++;
+					targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
+					targ_spec->raw_len++;
+					return 1;
+				}
+			}
+		} else {
+			for (i = 0, e64 = btf_enum64(targ_type); i < btf_vlen(targ_type); i++, e64++) {
+				targ_name = btf__name_by_offset(targ_spec->btf, e64->name_off);
+				targ_essent_len = bpf_core_essential_name_len(targ_name);
+				if (targ_essent_len != local_essent_len)
+					continue;
+				if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
+					targ_acc->type_id = targ_id;
+					targ_acc->idx = i;
+					targ_acc->name = targ_name;
+					targ_spec->len++;
+					targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
+					targ_spec->raw_len++;
+					return 1;
+				}
 			}
 		}
 		return 0;
@@ -681,7 +708,7 @@ static int bpf_core_calc_field_relo(const char *prog_name,
 		break;
 	case BPF_CORE_FIELD_SIGNED:
 		/* enums will be assumed unsigned */
-		*val = btf_is_enum(mt) ||
+		*val = btf_is_enum(mt) || btf_is_enum64(mt) ||
 		       (btf_int_encoding(mt) & BTF_INT_SIGNED);
 		if (validate)
 			*validate = true; /* signedness is never ambiguous */
@@ -753,6 +780,7 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
 				      const struct bpf_core_spec *spec,
 				      __u64 *val)
 {
+	const struct btf_enum64 *e64;
 	const struct btf_type *t;
 	const struct btf_enum *e;
 
@@ -764,8 +792,13 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
 		if (!spec)
 			return -EUCLEAN; /* request instruction poisoning */
 		t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
-		e = btf_enum(t) + spec->spec[0].idx;
-		*val = e->val;
+		if (btf_is_enum(t)) {
+			e = btf_enum(t) + spec->spec[0].idx;
+			*val = e->val;
+		} else {
+			e64 = btf_enum64(t) + spec->spec[0].idx;
+			*val = btf_enum64_value(e64);
+		}
 		break;
 	default:
 		return -EOPNOTSUPP;
@@ -1034,7 +1067,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
 		}
 
 		insn[0].imm = new_val;
-		insn[1].imm = 0; /* currently only 32-bit values are supported */
+		insn[1].imm = new_val >> 32;
 		pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
 			 prog_name, relo_idx, insn_idx,
 			 (unsigned long long)imm, new_val);
@@ -1056,6 +1089,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
  */
 int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *spec)
 {
+	const struct btf_enum64 *e64;
 	const struct btf_type *t;
 	const struct btf_enum *e;
 	const char *s;
@@ -1086,10 +1120,15 @@ int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *s
 
 	if (core_relo_is_enumval_based(spec->relo_kind)) {
 		t = skip_mods_and_typedefs(spec->btf, type_id, NULL);
-		e = btf_enum(t) + spec->raw_spec[0];
-		s = btf__name_by_offset(spec->btf, e->name_off);
-
-		append_buf("::%s = %u", s, e->val);
+		if (btf_is_enum(t)) {
+			e = btf_enum(t) + spec->raw_spec[0];
+			s = btf__name_by_offset(spec->btf, e->name_off);
+			append_buf("::%s = %u", s, e->val);
+		} else {
+			e64 = btf_enum64(t) + spec->raw_spec[0];
+			s = btf__name_by_offset(spec->btf, e64->name_off);
+			append_buf("::%s = %llu", s, btf_enum64_value(e64));
+		}
 		return len;
 	}
 
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index 5fce7008d1ff..1d3ac4496e7b 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -159,16 +159,16 @@ static void test_btf_dump_incremental(void)
 	 * struct s { int x; };
 	 *
 	 */
-	id = btf__add_enum(btf, "x", 4);
+	id = btf__add_enum32(btf, "x", false);
 	ASSERT_EQ(id, 1, "enum_declaration_id");
-	id = btf__add_enum(btf, "x", 4);
+	id = btf__add_enum32(btf, "x", true);
 	ASSERT_EQ(id, 2, "named_enum_id");
-	err = btf__add_enum_value(btf, "X", 1);
+	err = btf__add_enum32_value(btf, "X", 1);
 	ASSERT_OK(err, "named_enum_val_ok");
 
-	id = btf__add_enum(btf, NULL, 4);
+	id = btf__add_enum32(btf, NULL, true);
 	ASSERT_EQ(id, 3, "anon_enum_id");
-	err = btf__add_enum_value(btf, "Y", 1);
+	err = btf__add_enum32_value(btf, "Y", 1);
 	ASSERT_OK(err, "anon_enum_val_ok");
 
 	id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_write.c b/tools/testing/selftests/bpf/prog_tests/btf_write.c
index addf99c05896..be958ab26ebd 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_write.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_write.c
@@ -152,11 +152,11 @@ static void gen_btf(struct btf *btf)
 		     "\t'f1' type_id=1 bits_offset=0 bitfield_size=16", "raw_dump");
 
 	/* ENUM */
-	id = btf__add_enum(btf, "e1", 4);
+	id = btf__add_enum32(btf, "e1", true);
 	ASSERT_EQ(id, 9, "enum_id");
-	err = btf__add_enum_value(btf, "v1", 1);
+	err = btf__add_enum32_value(btf, "v1", 1);
 	ASSERT_OK(err, "v1_res");
-	err = btf__add_enum_value(btf, "v2", 2);
+	err = btf__add_enum32_value(btf, "v2", 2);
 	ASSERT_OK(err, "v2_res");
 
 	t = btf__type_by_id(btf, 9);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 05/12] bpftool: Add btf enum64 support
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (3 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 04/12] libbpf: Add btf enum64 support Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-09 23:31   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure Yonghong Song
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Add BTF_KIND_ENUM64 support.
For example, the following enum is defined in uapi bpf.h.
  $ cat core.c
  enum A {
        BPF_F_INDEX_MASK                = 0xffffffffULL,
        BPF_F_CURRENT_CPU               = BPF_F_INDEX_MASK,
        BPF_F_CTXLEN_MASK               = (0xfffffULL << 32),
  } g;
Compiled with
  clang -target bpf -O2 -g -c core.c
Using bpftool to dump types and generate format C file:
  $ bpftool btf dump file core.o
  ...
  [1] ENUM64 'A' size=8 vlen=3
        'BPF_F_INDEX_MASK' val=4294967295ULL
        'BPF_F_CURRENT_CPU' val=4294967295ULL
        'BPF_F_CTXLEN_MASK' val=4503595332403200ULL
  $ bpftool btf dump file core.o format c
  ...
  enum A {
        BPF_F_INDEX_MASK = 4294967295ULL,
        BPF_F_CURRENT_CPU = 4294967295ULL,
        BPF_F_CTXLEN_MASK = 4503595332403200ULL,
  };
  ...

The 64bit value is represented properly in BTF and C dump.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/bpf/bpftool/btf.c        | 47 ++++++++++++++++++++++++++++++++--
 tools/bpf/bpftool/btf_dumper.c | 32 +++++++++++++++++++++++
 tools/bpf/bpftool/gen.c        |  1 +
 3 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index a2c665beda87..ae3c413fa3b1 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -40,6 +40,7 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
 	[BTF_KIND_FLOAT]	= "FLOAT",
 	[BTF_KIND_DECL_TAG]	= "DECL_TAG",
 	[BTF_KIND_TYPE_TAG]	= "TYPE_TAG",
+	[BTF_KIND_ENUM64]	= "ENUM64",
 };
 
 struct btf_attach_point {
@@ -228,10 +229,52 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
 			if (json_output) {
 				jsonw_start_object(w);
 				jsonw_string_field(w, "name", name);
-				jsonw_uint_field(w, "val", v->val);
+				if (btf_kflag(t))
+					jsonw_uint_field(w, "val", v->val);
+				else
+					jsonw_int_field(w, "val", v->val);
 				jsonw_end_object(w);
 			} else {
-				printf("\n\t'%s' val=%u", name, v->val);
+				if (btf_kflag(t))
+					printf("\n\t'%s' val=%u", name, v->val);
+				else
+					printf("\n\t'%s' val=%d", name, v->val);
+			}
+		}
+		if (json_output)
+			jsonw_end_array(w);
+		break;
+	}
+	case BTF_KIND_ENUM64: {
+		const struct btf_enum64 *v = (const void *)(t + 1);
+		__u16 vlen = BTF_INFO_VLEN(t->info);
+		int i;
+
+		if (json_output) {
+			jsonw_uint_field(w, "size", t->size);
+			jsonw_uint_field(w, "vlen", vlen);
+			jsonw_name(w, "values");
+			jsonw_start_array(w);
+		} else {
+			printf(" size=%u vlen=%u", t->size, vlen);
+		}
+		for (i = 0; i < vlen; i++, v++) {
+			const char *name = btf_str(btf, v->name_off);
+			__u64 val = (__u64)v->hi32 << 32 | v->lo32;
+
+			if (json_output) {
+				jsonw_start_object(w);
+				jsonw_string_field(w, "name", name);
+				if (btf_kflag(t))
+					jsonw_uint_field(w, "val", val);
+				else
+					jsonw_int_field(w, "val", val);
+				jsonw_end_object(w);
+			} else {
+				if (btf_kflag(t))
+					printf("\n\t'%s' val=%lluULL", name, val);
+				else
+					printf("\n\t'%s' val=%lldLL", name, val);
 			}
 		}
 		if (json_output)
diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c
index f5dddf8ef404..f9f38384b9a6 100644
--- a/tools/bpf/bpftool/btf_dumper.c
+++ b/tools/bpf/bpftool/btf_dumper.c
@@ -182,6 +182,35 @@ static int btf_dumper_enum(const struct btf_dumper *d,
 	return 0;
 }
 
+static int btf_dumper_enum64(const struct btf_dumper *d,
+			     const struct btf_type *t,
+			     const void *data)
+{
+	const struct btf_enum64 *enums = btf_enum64(t);
+	__u32 hi32, lo32;
+	__u64 value;
+	__u16 i;
+
+	if (t->size != 8)
+		return -EINVAL;
+
+	value = *(__u64 *)data;
+	hi32 = value >> 32;
+	lo32 = (__u32)value;
+
+	for (i = 0; i < btf_vlen(t); i++) {
+		if (hi32 == enums[i].hi32 && lo32 == enums[i].lo32) {
+			jsonw_string(d->jw,
+				     btf__name_by_offset(d->btf,
+							 enums[i].name_off));
+			return 0;
+		}
+	}
+
+	jsonw_int(d->jw, value);
+	return 0;
+}
+
 static bool is_str_array(const struct btf *btf, const struct btf_array *arr,
 			 const char *s)
 {
@@ -542,6 +571,8 @@ static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
 		return btf_dumper_array(d, type_id, data);
 	case BTF_KIND_ENUM:
 		return btf_dumper_enum(d, t, data);
+	case BTF_KIND_ENUM64:
+		return btf_dumper_enum64(d, t, data);
 	case BTF_KIND_PTR:
 		btf_dumper_ptr(d, t, data);
 		return 0;
@@ -618,6 +649,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
 			      btf__name_by_offset(btf, t->name_off));
 		break;
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 		BTF_PRINT_ARG("enum %s ",
 			      btf__name_by_offset(btf, t->name_off));
 		break;
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index 7678af364793..b80c3577057f 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -1746,6 +1746,7 @@ btfgen_mark_type(struct btfgen_info *info, unsigned int type_id, bool follow_poi
 	case BTF_KIND_INT:
 	case BTF_KIND_FLOAT:
 	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
 	case BTF_KIND_STRUCT:
 	case BTF_KIND_UNION:
 		break;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (4 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 05/12] bpftool: " Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-09  2:21   ` Dave Marchevsky
  2022-05-09 23:34   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 07/12] selftests/bpf: Test new libbpf enum32/enum64 API functions Yonghong Song
                   ` (5 subsequent siblings)
  11 siblings, 2 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

The kflag is supported now for BTF_KIND_ENUM.
So remove the test which tests verifier failure
due to existence of kflag.

With enum64 support in kernel and libbpf,
selftest btf_dump/btf_dump failed with
no-enum64 support llvm for the following
enum definition:
 enum e2 {
        C = 100,
        D = 4294967295,
        E = 0,
 };

With the no-enum64 support llvm, the signedness is
'signed' by default, and D (4294967295 = 0xffffffff)
will print as -1. With enum64 support llvm, the signedness
is 'unsigned' and the value of D will print as 4294967295.
To support both old and new compilers, this patch
changed the value to 268435455 = 0xfffffff which works
with both enum64 or non-enum64 support llvm.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/testing/selftests/bpf/prog_tests/btf.c  | 20 -------------------
 .../bpf/progs/btf_dump_test_case_syntax.c     |  2 +-
 2 files changed, 1 insertion(+), 21 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
index ba5bde53d418..8e068e06b3e8 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -2896,26 +2896,6 @@ static struct btf_raw_test raw_tests[] = {
 	.err_str = "Invalid btf_info kind_flag",
 },
 
-{
-	.descr = "invalid enum kind_flag",
-	.raw_types = {
-		BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),		/* [1] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 1, 1), 4),	/* [2] */
-		BTF_ENUM_ENC(NAME_TBD, 0),
-		BTF_END_RAW,
-	},
-	BTF_STR_SEC("\0A"),
-	.map_type = BPF_MAP_TYPE_ARRAY,
-	.map_name = "enum_type_check_btf",
-	.key_size = sizeof(int),
-	.value_size = sizeof(int),
-	.key_type_id = 1,
-	.value_type_id = 1,
-	.max_entries = 4,
-	.btf_load_err = true,
-	.err_str = "Invalid btf_info kind_flag",
-},
-
 {
 	.descr = "valid fwd kind_flag",
 	.raw_types = {
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
index 1c7105fcae3c..4068cea4be53 100644
--- a/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
@@ -13,7 +13,7 @@ enum e1 {
 
 enum e2 {
 	C = 100,
-	D = 4294967295,
+	D = 268435455,
 	E = 0,
 };
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 07/12] selftests/bpf: Test new libbpf enum32/enum64 API functions
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (5 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-01 19:00 ` [PATCH bpf-next 08/12] selftests/bpf: Add BTF_KIND_ENUM64 unit tests Yonghong Song
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Add tests to use the newer libbpf enum32/enum64 API functions
in selftest btf_write.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/testing/selftests/bpf/btf_helpers.c     |  21 +++-
 .../selftests/bpf/prog_tests/btf_write.c      | 114 +++++++++++++-----
 2 files changed, 105 insertions(+), 30 deletions(-)

diff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c
index b5941d514e17..1086e3490043 100644
--- a/tools/testing/selftests/bpf/btf_helpers.c
+++ b/tools/testing/selftests/bpf/btf_helpers.c
@@ -26,11 +26,12 @@ static const char * const btf_kind_str_mapping[] = {
 	[BTF_KIND_FLOAT]	= "FLOAT",
 	[BTF_KIND_DECL_TAG]	= "DECL_TAG",
 	[BTF_KIND_TYPE_TAG]	= "TYPE_TAG",
+	[BTF_KIND_ENUM64]	= "ENUM64",
 };
 
 static const char *btf_kind_str(__u16 kind)
 {
-	if (kind > BTF_KIND_TYPE_TAG)
+	if (kind > BTF_KIND_ENUM64)
 		return "UNKNOWN";
 	return btf_kind_str_mapping[kind];
 }
@@ -139,14 +140,30 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
 	}
 	case BTF_KIND_ENUM: {
 		const struct btf_enum *v = btf_enum(t);
+		const char *fmt_str;
 
+		fmt_str = btf_kflag(t) ? "\n\t'%s' val=%u" : "\n\t'%s' val=%d";
 		fprintf(out, " size=%u vlen=%u", t->size, vlen);
 		for (i = 0; i < vlen; i++, v++) {
-			fprintf(out, "\n\t'%s' val=%u",
+			fprintf(out, fmt_str,
 				btf_str(btf, v->name_off), v->val);
 		}
 		break;
 	}
+	case BTF_KIND_ENUM64: {
+		const struct btf_enum64 *v = btf_enum64(t);
+		const char *fmt_str;
+
+		fmt_str = btf_kflag(t) ? "\n\t'%s' val=%llu" : "\n\t'%s' val=%lld";
+
+		fprintf(out, " size=%u vlen=%u", t->size, vlen);
+		for (i = 0; i < vlen; i++, v++) {
+			fprintf(out, fmt_str,
+				btf_str(btf, v->name_off),
+				(__u64)v->hi32 << 32 | v->lo32);
+		}
+		break;
+	}
 	case BTF_KIND_FWD:
 		fprintf(out, " fwd_kind=%s", btf_kflag(t) ? "union" : "struct");
 		break;
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_write.c b/tools/testing/selftests/bpf/prog_tests/btf_write.c
index be958ab26ebd..68286a72db31 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_write.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_write.c
@@ -9,6 +9,7 @@ static void gen_btf(struct btf *btf)
 	const struct btf_var_secinfo *vi;
 	const struct btf_type *t;
 	const struct btf_member *m;
+	const struct btf_enum64 *v64;
 	const struct btf_enum *v;
 	const struct btf_param *p;
 	int id, err, str_off;
@@ -307,6 +308,48 @@ static void gen_btf(struct btf *btf)
 	ASSERT_EQ(t->type, 1, "tag_type");
 	ASSERT_STREQ(btf_type_raw_dump(btf, 20),
 		     "[20] TYPE_TAG 'tag1' type_id=1", "raw_dump");
+
+	/* ENUM64 */
+	id = btf__add_enum64(btf, "e1", false);
+	ASSERT_EQ(id, 21, "enum64_id");
+	err = btf__add_enum64_value(btf, "v1", -1);
+	ASSERT_OK(err, "v1_res");
+	err = btf__add_enum64_value(btf, "v2", 0x123456789); /* 4886718345 */
+	ASSERT_OK(err, "v2_res");
+	t = btf__type_by_id(btf, 21);
+	ASSERT_STREQ(btf__str_by_offset(btf, t->name_off), "e1", "enum64_name");
+	ASSERT_EQ(btf_kind(t), BTF_KIND_ENUM64, "enum64_kind");
+	ASSERT_EQ(btf_vlen(t), 2, "enum64_vlen");
+	ASSERT_EQ(t->size, 8, "enum64_sz");
+	v64 = btf_enum64(t) + 0;
+	ASSERT_STREQ(btf__str_by_offset(btf, v64->name_off), "v1", "v1_name");
+	ASSERT_EQ(v64->hi32, 0xffffffff, "v1_val");
+	ASSERT_EQ(v64->lo32, 0xffffffff, "v1_val");
+	v64 = btf_enum64(t) + 1;
+	ASSERT_STREQ(btf__str_by_offset(btf, v64->name_off), "v2", "v2_name");
+	ASSERT_EQ(v64->hi32, 0x1, "v2_val");
+	ASSERT_EQ(v64->lo32, 0x23456789, "v2_val");
+	ASSERT_STREQ(btf_type_raw_dump(btf, 21),
+		     "[21] ENUM64 'e1' size=8 vlen=2\n"
+		     "\t'v1' val=-1\n"
+		     "\t'v2' val=4886718345", "raw_dump");
+
+	id = btf__add_enum64(btf, "e1", true);
+	ASSERT_EQ(id, 22, "enum64_id");
+	err = btf__add_enum64_value(btf, "v1", 0xffffffffFFFFFFFF); /* 18446744073709551615 */
+	ASSERT_OK(err, "v1_res");
+	t = btf__type_by_id(btf, 22);
+	ASSERT_STREQ(btf__str_by_offset(btf, t->name_off), "e1", "enum64_name");
+	ASSERT_EQ(btf_kind(t), BTF_KIND_ENUM64, "enum64_kind");
+	ASSERT_EQ(btf_vlen(t), 1, "enum64_vlen");
+	ASSERT_EQ(t->size, 8, "enum64_sz");
+	v64 = btf_enum64(t) + 0;
+	ASSERT_STREQ(btf__str_by_offset(btf, v64->name_off), "v1", "v1_name");
+	ASSERT_EQ(v64->hi32, 0xffffffff, "v1_val");
+	ASSERT_EQ(v64->lo32, 0xffffffff, "v1_val");
+	ASSERT_STREQ(btf_type_raw_dump(btf, 22),
+		     "[22] ENUM64 'e1' size=8 vlen=1\n"
+		     "\t'v1' val=18446744073709551615", "raw_dump");
 }
 
 static void test_btf_add()
@@ -348,7 +391,12 @@ static void test_btf_add()
 		"\ttype_id=1 offset=4 size=8",
 		"[18] DECL_TAG 'tag1' type_id=16 component_idx=-1",
 		"[19] DECL_TAG 'tag2' type_id=14 component_idx=1",
-		"[20] TYPE_TAG 'tag1' type_id=1");
+		"[20] TYPE_TAG 'tag1' type_id=1",
+		"[21] ENUM64 'e1' size=8 vlen=2\n"
+		"\t'v1' val=-1\n"
+		"\t'v2' val=4886718345",
+		"[22] ENUM64 'e1' size=8 vlen=1\n"
+		"\t'v1' val=18446744073709551615");
 
 	btf__free(btf);
 }
@@ -370,7 +418,7 @@ static void test_btf_add_btf()
 	gen_btf(btf2);
 
 	id = btf__add_btf(btf1, btf2);
-	if (!ASSERT_EQ(id, 21, "id"))
+	if (!ASSERT_EQ(id, 23, "id"))
 		goto cleanup;
 
 	VALIDATE_RAW_BTF(
@@ -403,36 +451,46 @@ static void test_btf_add_btf()
 		"[18] DECL_TAG 'tag1' type_id=16 component_idx=-1",
 		"[19] DECL_TAG 'tag2' type_id=14 component_idx=1",
 		"[20] TYPE_TAG 'tag1' type_id=1",
+		"[21] ENUM64 'e1' size=8 vlen=2\n"
+		"\t'v1' val=-1\n"
+		"\t'v2' val=4886718345",
+		"[22] ENUM64 'e1' size=8 vlen=1\n"
+		"\t'v1' val=18446744073709551615",
 
 		/* types appended from the second BTF */
-		"[21] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
-		"[22] PTR '(anon)' type_id=21",
-		"[23] CONST '(anon)' type_id=25",
-		"[24] VOLATILE '(anon)' type_id=23",
-		"[25] RESTRICT '(anon)' type_id=24",
-		"[26] ARRAY '(anon)' type_id=22 index_type_id=21 nr_elems=10",
-		"[27] STRUCT 's1' size=8 vlen=2\n"
-		"\t'f1' type_id=21 bits_offset=0\n"
-		"\t'f2' type_id=21 bits_offset=32 bitfield_size=16",
-		"[28] UNION 'u1' size=8 vlen=1\n"
-		"\t'f1' type_id=21 bits_offset=0 bitfield_size=16",
-		"[29] ENUM 'e1' size=4 vlen=2\n"
+		"[23] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+		"[24] PTR '(anon)' type_id=23",
+		"[25] CONST '(anon)' type_id=27",
+		"[26] VOLATILE '(anon)' type_id=25",
+		"[27] RESTRICT '(anon)' type_id=26",
+		"[28] ARRAY '(anon)' type_id=24 index_type_id=23 nr_elems=10",
+		"[29] STRUCT 's1' size=8 vlen=2\n"
+		"\t'f1' type_id=23 bits_offset=0\n"
+		"\t'f2' type_id=23 bits_offset=32 bitfield_size=16",
+		"[30] UNION 'u1' size=8 vlen=1\n"
+		"\t'f1' type_id=23 bits_offset=0 bitfield_size=16",
+		"[31] ENUM 'e1' size=4 vlen=2\n"
 		"\t'v1' val=1\n"
 		"\t'v2' val=2",
-		"[30] FWD 'struct_fwd' fwd_kind=struct",
-		"[31] FWD 'union_fwd' fwd_kind=union",
-		"[32] ENUM 'enum_fwd' size=4 vlen=0",
-		"[33] TYPEDEF 'typedef1' type_id=21",
-		"[34] FUNC 'func1' type_id=35 linkage=global",
-		"[35] FUNC_PROTO '(anon)' ret_type_id=21 vlen=2\n"
-		"\t'p1' type_id=21\n"
-		"\t'p2' type_id=22",
-		"[36] VAR 'var1' type_id=21, linkage=global-alloc",
-		"[37] DATASEC 'datasec1' size=12 vlen=1\n"
-		"\ttype_id=21 offset=4 size=8",
-		"[38] DECL_TAG 'tag1' type_id=36 component_idx=-1",
-		"[39] DECL_TAG 'tag2' type_id=34 component_idx=1",
-		"[40] TYPE_TAG 'tag1' type_id=21");
+		"[32] FWD 'struct_fwd' fwd_kind=struct",
+		"[33] FWD 'union_fwd' fwd_kind=union",
+		"[34] ENUM 'enum_fwd' size=4 vlen=0",
+		"[35] TYPEDEF 'typedef1' type_id=23",
+		"[36] FUNC 'func1' type_id=37 linkage=global",
+		"[37] FUNC_PROTO '(anon)' ret_type_id=23 vlen=2\n"
+		"\t'p1' type_id=23\n"
+		"\t'p2' type_id=24",
+		"[38] VAR 'var1' type_id=23, linkage=global-alloc",
+		"[39] DATASEC 'datasec1' size=12 vlen=1\n"
+		"\ttype_id=23 offset=4 size=8",
+		"[40] DECL_TAG 'tag1' type_id=38 component_idx=-1",
+		"[41] DECL_TAG 'tag2' type_id=36 component_idx=1",
+		"[42] TYPE_TAG 'tag1' type_id=23",
+		"[43] ENUM64 'e1' size=8 vlen=2\n"
+		"\t'v1' val=-1\n"
+		"\t'v2' val=4886718345",
+		"[44] ENUM64 'e1' size=8 vlen=1\n"
+		"\t'v1' val=18446744073709551615");
 
 cleanup:
 	btf__free(btf1);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 08/12] selftests/bpf: Add BTF_KIND_ENUM64 unit tests
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (6 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 07/12] selftests/bpf: Test new libbpf enum32/enum64 API functions Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-01 19:00 ` [PATCH bpf-next 09/12] selftests/bpf: Test BTF_KIND_ENUM64 for deduplication Yonghong Song
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Add unit tests for basic BTF_KIND_ENUM64 encoding.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/testing/selftests/bpf/prog_tests/btf.c | 38 ++++++++++++++++++++
 tools/testing/selftests/bpf/test_btf.h       |  1 +
 2 files changed, 39 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
index 8e068e06b3e8..2af2ce5ec165 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -4052,6 +4052,44 @@ static struct btf_raw_test raw_tests[] = {
 	.btf_load_err = true,
 	.err_str = "Type tags don't precede modifiers",
 },
+{
+	.descr = "enum64 test #1",
+	.raw_types = {
+		BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),			/* [1] */
+		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 2), 8),	/* [2] */
+		BTF_ENUM64_ENC(NAME_TBD, 0, 0),
+		BTF_ENUM64_ENC(NAME_TBD, 1, 1),
+		BTF_END_RAW,
+	},
+	BTF_STR_SEC("\0a\0b\0c"),
+	.map_type = BPF_MAP_TYPE_ARRAY,
+	.map_name = "tag_type_check_btf",
+	.key_size = sizeof(int),
+	.value_size = 8,
+	.key_type_id = 1,
+	.value_type_id = 2,
+	.max_entries = 1,
+},
+{
+	.descr = "enum64 test #2, invalid size",
+	.raw_types = {
+		BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),			/* [1] */
+		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 2), 4),	/* [2] */
+		BTF_ENUM64_ENC(NAME_TBD, 0, 0),
+		BTF_ENUM64_ENC(NAME_TBD, 1, 1),
+		BTF_END_RAW,
+	},
+	BTF_STR_SEC("\0a\0b\0c"),
+	.map_type = BPF_MAP_TYPE_ARRAY,
+	.map_name = "tag_type_check_btf",
+	.key_size = sizeof(int),
+	.value_size = 8,
+	.key_type_id = 1,
+	.value_type_id = 2,
+	.max_entries = 1,
+	.btf_load_err = true,
+	.err_str = "Unexpected size",
+},
 
 }; /* struct btf_raw_test raw_tests[] */
 
diff --git a/tools/testing/selftests/bpf/test_btf.h b/tools/testing/selftests/bpf/test_btf.h
index 128989bed8b7..240abfca2a8b 100644
--- a/tools/testing/selftests/bpf/test_btf.h
+++ b/tools/testing/selftests/bpf/test_btf.h
@@ -39,6 +39,7 @@
 #define BTF_MEMBER_ENC(name, type, bits_offset)	\
 	(name), (type), (bits_offset)
 #define BTF_ENUM_ENC(name, val) (name), (val)
+#define BTF_ENUM64_ENC(name, hi32, lo32) (name), (hi32), (lo32)
 #define BTF_MEMBER_OFFSET(bitfield_size, bits_offset) \
 	((bitfield_size) << 24 | (bits_offset))
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 09/12] selftests/bpf: Test BTF_KIND_ENUM64 for deduplication
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (7 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 08/12] selftests/bpf: Add BTF_KIND_ENUM64 unit tests Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-09 23:37   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 10/12] selftests/bpf: add a test for enum64 value relocation Yonghong Song
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/testing/selftests/bpf/prog_tests/btf.c | 70 +++++++++++++++++++-
 1 file changed, 68 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
index 2af2ce5ec165..5295261f5393 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -7018,9 +7018,12 @@ static struct btf_dedup_test dedup_tests[] = {
 			BTF_DECL_TAG_ENC(NAME_TBD, 13, 1),				/* [16] decl_tag */
 			BTF_DECL_TAG_ENC(NAME_TBD, 7, -1),				/* [17] decl_tag */
 			BTF_TYPE_TAG_ENC(NAME_TBD, 8),					/* [18] type_tag */
+			BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 2), 8),	/* [19] enum64 */
+				BTF_ENUM64_ENC(NAME_TBD, 0, 0),
+				BTF_ENUM64_ENC(NAME_TBD, 1, 1),
 			BTF_END_RAW,
 		},
-		BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R"),
+		BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R\0S\0T\0U"),
 	},
 	.expect = {
 		.raw_types = {
@@ -7048,9 +7051,12 @@ static struct btf_dedup_test dedup_tests[] = {
 			BTF_DECL_TAG_ENC(NAME_TBD, 13, 1),				/* [16] decl_tag */
 			BTF_DECL_TAG_ENC(NAME_TBD, 7, -1),				/* [17] decl_tag */
 			BTF_TYPE_TAG_ENC(NAME_TBD, 8),					/* [18] type_tag */
+			BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 2), 8),	/* [19] enum64 */
+				BTF_ENUM64_ENC(NAME_TBD, 0, 0),
+				BTF_ENUM64_ENC(NAME_TBD, 1, 1),
 			BTF_END_RAW,
 		},
-		BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R"),
+		BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R\0S\0T\0U"),
 	},
 },
 {
@@ -7511,6 +7517,64 @@ static struct btf_dedup_test dedup_tests[] = {
 		BTF_STR_SEC("\0tag1\0t\0m"),
 	},
 },
+{
+	.descr = "dedup: enum64, standalone",
+	.input = {
+		.raw_types = {
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
+			BTF_END_RAW,
+		},
+		BTF_STR_SEC("\0e1\0e1_val"),
+	},
+	.expect = {
+		.raw_types = {
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
+			BTF_END_RAW,
+		},
+		BTF_STR_SEC("\0e1\0e1_val"),
+	},
+},
+{
+	.descr = "dedup: enum64, fwd resolution",
+	.input = {
+		.raw_types = {
+			/* [1] fwd enum64 'e1' before full enum */
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
+			/* [2] full enum64 'e1' after fwd */
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
+			/* [3] full enum64 'e2' before fwd */
+			BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(4), 0, 456),
+			/* [4] fwd enum64 'e2' after full enum */
+			BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
+			/* [5] incompatible full enum64 with different value */
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(2), 0, 321),
+			BTF_END_RAW,
+		},
+		BTF_STR_SEC("\0e1\0e1_val\0e2\0e2_val"),
+	},
+	.expect = {
+		.raw_types = {
+			/* [1] full enum64 'e1' */
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
+			/* [2] full enum64 'e2' */
+			BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(4), 0, 456),
+			/* [3] incompatible full enum64 with different value */
+			BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
+				BTF_ENUM64_ENC(NAME_NTH(2), 0, 321),
+			BTF_END_RAW,
+		},
+		BTF_STR_SEC("\0e1\0e1_val\0e2\0e2_val"),
+	},
+},
 
 };
 
@@ -7535,6 +7599,8 @@ static int btf_type_size(const struct btf_type *t)
 		return base_size + sizeof(__u32);
 	case BTF_KIND_ENUM:
 		return base_size + vlen * sizeof(struct btf_enum);
+	case BTF_KIND_ENUM64:
+		return base_size + vlen * sizeof(struct btf_enum64);
 	case BTF_KIND_ARRAY:
 		return base_size + sizeof(struct btf_array);
 	case BTF_KIND_STRUCT:
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 10/12] selftests/bpf: add a test for enum64 value relocation
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (8 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 09/12] selftests/bpf: Test BTF_KIND_ENUM64 for deduplication Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-09 23:38   ` Andrii Nakryiko
  2022-05-01 19:00 ` [PATCH bpf-next 11/12] selftests/bpf: Clarify llvm dependency with possible selftest failures Yonghong Song
  2022-05-01 19:01 ` [PATCH bpf-next 12/12] docs/bpf: Update documentation for BTF_KIND_ENUM64 support Yonghong Song
  11 siblings, 1 reply; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Add a test for enum64 value relocations.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 .../selftests/bpf/prog_tests/core_reloc.c     | 43 +++++++++++++++
 .../bpf/progs/btf__core_reloc_enum64val.c     |  3 ++
 .../progs/btf__core_reloc_enum64val___diff.c  |  3 ++
 .../btf__core_reloc_enum64val___err_missing.c |  3 ++
 ...btf__core_reloc_enum64val___val3_missing.c |  3 ++
 .../selftests/bpf/progs/core_reloc_types.h    | 47 ++++++++++++++++
 .../bpf/progs/test_core_reloc_enum64val.c     | 53 +++++++++++++++++++
 7 files changed, 155 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___diff.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___err_missing.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___val3_missing.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_enum64val.c

diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c
index f28f75aa9154..b0054f16c9cd 100644
--- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c
+++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c
@@ -355,6 +355,25 @@ static int duration = 0;
 	.fails = true,							\
 }
 
+#define ENUM64VAL_CASE_COMMON(name)					\
+	.case_name = #name,						\
+	.bpf_obj_file = "test_core_reloc_enum64val.o",			\
+	.btf_src_file = "btf__core_reloc_" #name ".o",			\
+	.raw_tp_name = "sys_enter",					\
+	.prog_name = "test_core_enum64val"
+
+#define ENUM64VAL_CASE(name, ...) {					\
+	ENUM64VAL_CASE_COMMON(name),					\
+	.output = STRUCT_TO_CHAR_PTR(core_reloc_enum64val_output)	\
+			__VA_ARGS__,					\
+	.output_len = sizeof(struct core_reloc_enum64val_output),	\
+}
+
+#define ENUM64VAL_ERR_CASE(name) {					\
+	ENUM64VAL_CASE_COMMON(name),					\
+	.fails = true,							\
+}
+
 struct core_reloc_test_case;
 
 typedef int (*setup_test_fn)(struct core_reloc_test_case *test);
@@ -822,6 +841,30 @@ static const struct core_reloc_test_case test_cases[] = {
 		.anon_val2 = 0x222,
 	}),
 	ENUMVAL_ERR_CASE(enumval___err_missing),
+
+	/* 64bit enumerator value existence and value relocations */
+	ENUM64VAL_CASE(enum64val, {
+		.named_val1_exists = true,
+		.named_val2_exists = true,
+		.named_val3_exists = true,
+		.named_val1 = 0x1ffffffffULL,
+		.named_val2 = 0x2,
+	}),
+	ENUM64VAL_CASE(enum64val___diff, {
+		.named_val1_exists = true,
+		.named_val2_exists = true,
+		.named_val3_exists = true,
+		.named_val1 = 0x101ffffffffULL,
+		.named_val2 = 0x202ffffffffULL,
+	}),
+	ENUM64VAL_CASE(enum64val___val3_missing, {
+		.named_val1_exists = true,
+		.named_val2_exists = true,
+		.named_val3_exists = false,
+		.named_val1 = 0x111ffffffffULL,
+		.named_val2 = 0x222,
+	}),
+	ENUM64VAL_ERR_CASE(enum64val___err_missing),
 };
 
 struct data {
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val.c
new file mode 100644
index 000000000000..888e79db6a77
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_enum64val x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___diff.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___diff.c
new file mode 100644
index 000000000000..194749130d87
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___diff.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_enum64val___diff x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___err_missing.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___err_missing.c
new file mode 100644
index 000000000000..3d732d4193e4
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___err_missing.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_enum64val___err_missing x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___val3_missing.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___val3_missing.c
new file mode 100644
index 000000000000..17cf5d6a848d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___val3_missing.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_enum64val___val3_missing x) {}
diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h
index c95c0cabe951..e6e12a93514b 100644
--- a/tools/testing/selftests/bpf/progs/core_reloc_types.h
+++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h
@@ -1099,6 +1099,15 @@ struct core_reloc_enumval_output {
 	int anon_val2;
 };
 
+struct core_reloc_enum64val_output {
+	bool named_val1_exists;
+	bool named_val2_exists;
+	bool named_val3_exists;
+
+	long named_val1;
+	long named_val2;
+};
+
 enum named_enum {
 	NAMED_ENUM_VAL1 = 1,
 	NAMED_ENUM_VAL2 = 2,
@@ -1116,6 +1125,16 @@ struct core_reloc_enumval {
 	anon_enum f2;
 };
 
+enum named_enum64 {
+	NAMED_ENUM64_VAL1 = 0x1ffffffffULL,
+	NAMED_ENUM64_VAL2 = 0x2,
+	NAMED_ENUM64_VAL3 = 0x3ffffffffULL,
+};
+
+struct core_reloc_enum64val {
+	enum named_enum64 f1;
+};
+
 /* differing enumerator values */
 enum named_enum___diff {
 	NAMED_ENUM_VAL1___diff = 101,
@@ -1134,6 +1153,16 @@ struct core_reloc_enumval___diff {
 	anon_enum___diff f2;
 };
 
+enum named_enum64___diff {
+	NAMED_ENUM64_VAL1___diff = 0x101ffffffffULL,
+	NAMED_ENUM64_VAL2___diff = 0x202ffffffffULL,
+	NAMED_ENUM64_VAL3___diff = 0x303ffffffffULL,
+};
+
+struct core_reloc_enum64val___diff {
+	enum named_enum64___diff f1;
+};
+
 /* missing (optional) third enum value */
 enum named_enum___val3_missing {
 	NAMED_ENUM_VAL1___val3_missing = 111,
@@ -1150,6 +1179,15 @@ struct core_reloc_enumval___val3_missing {
 	anon_enum___val3_missing f2;
 };
 
+enum named_enum64___val3_missing {
+	NAMED_ENUM64_VAL1___val3_missing = 0x111ffffffffULL,
+	NAMED_ENUM64_VAL2___val3_missing = 0x222,
+};
+
+struct core_reloc_enum64val___val3_missing {
+	enum named_enum64___val3_missing f1;
+};
+
 /* missing (mandatory) second enum value, should fail */
 enum named_enum___err_missing {
 	NAMED_ENUM_VAL1___err_missing = 1,
@@ -1165,3 +1203,12 @@ struct core_reloc_enumval___err_missing {
 	enum named_enum___err_missing f1;
 	anon_enum___err_missing f2;
 };
+
+enum named_enum64___err_missing {
+	NAMED_ENUM64_VAL1___err_missing = 0x1ffffffffULL,
+	NAMED_ENUM64_VAL3___err_missing = 0x3ffffffffULL,
+};
+
+struct core_reloc_enum64val___err_missing {
+	enum named_enum64___err_missing f1;
+};
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_enum64val.c b/tools/testing/selftests/bpf/progs/test_core_reloc_enum64val.c
new file mode 100644
index 000000000000..1bccf9214547
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_enum64val.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+char _license[] SEC("license") = "GPL";
+
+struct {
+	char in[256];
+	char out[256];
+	bool skip;
+} data = {};
+
+enum named_enum64 {
+	NAMED_ENUM64_VAL1 = 0x1ffffffffULL,
+	NAMED_ENUM64_VAL2 = 0x2ffffffffULL,
+	NAMED_ENUM64_VAL3 = 0x3ffffffffULL,
+};
+
+struct core_reloc_enum64val_output {
+	bool named_val1_exists;
+	bool named_val2_exists;
+	bool named_val3_exists;
+
+	long named_val1;
+	long named_val2;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_enum64val(void *ctx)
+{
+#if __has_builtin(__builtin_preserve_enum_value)
+	struct core_reloc_enum64val_output *out = (void *)&data.out;
+	enum named_enum64 named = 0;
+
+	out->named_val1_exists = bpf_core_enum_value_exists(named, NAMED_ENUM64_VAL1);
+	out->named_val2_exists = bpf_core_enum_value_exists(enum named_enum64, NAMED_ENUM64_VAL2);
+	out->named_val3_exists = bpf_core_enum_value_exists(enum named_enum64, NAMED_ENUM64_VAL3);
+
+	out->named_val1 = bpf_core_enum_value(named, NAMED_ENUM64_VAL1);
+	out->named_val2 = bpf_core_enum_value(named, NAMED_ENUM64_VAL2);
+	/* NAMED_ENUM64_VAL3 value is optional */
+
+#else
+	data.skip = true;
+#endif
+
+	return 0;
+}
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 11/12] selftests/bpf: Clarify llvm dependency with possible selftest failures
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (9 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 10/12] selftests/bpf: add a test for enum64 value relocation Yonghong Song
@ 2022-05-01 19:00 ` Yonghong Song
  2022-05-01 19:01 ` [PATCH bpf-next 12/12] docs/bpf: Update documentation for BTF_KIND_ENUM64 support Yonghong Song
  11 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:00 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Certain subtests in selftests core_reloc and core_reloc_btfgen
requires llvm ENUM64 support in llvm15. If an older compiler
is used, these subtests will fail. Make this requirement clear
in selftests README.rst file.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 tools/testing/selftests/bpf/README.rst | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/tools/testing/selftests/bpf/README.rst b/tools/testing/selftests/bpf/README.rst
index eb1b7541f39d..a68f448a6043 100644
--- a/tools/testing/selftests/bpf/README.rst
+++ b/tools/testing/selftests/bpf/README.rst
@@ -266,3 +266,21 @@ from running test_progs will look like:
   test_xdpwall:FAIL:Does LLVM have https://reviews.llvm.org/D109073? unexpected error: -4007
 
 __ https://reviews.llvm.org/D109073
+
+ENUM64 support and Clang version
+================================
+
+There are a few selftests requiring LLVM ENUM64 support. The LLVM ENUM64 is
+introduced in `Clang 15` [0_]. Without proper compiler support, the following selftests
+will fail:
+
+.. code-block:: console
+
+  #45 /72    core_reloc/enum64val:FAIL
+  #45 /73    core_reloc/enum64val___diff:FAIL
+  #45 /74    core_reloc/enum64val___val3_missing:FAIL
+  #46 /72    core_reloc_btfgen/enum64val:FAIL
+  #46 /73    core_reloc_btfgen/enum64val___diff:FAIL
+  #46 /74    core_reloc_btfgen/enum64val___val3_missing:FAIL
+
+.. _0: https://reviews.llvm.org/D124641
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH bpf-next 12/12] docs/bpf: Update documentation for BTF_KIND_ENUM64 support
  2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
                   ` (10 preceding siblings ...)
  2022-05-01 19:00 ` [PATCH bpf-next 11/12] selftests/bpf: Clarify llvm dependency with possible selftest failures Yonghong Song
@ 2022-05-01 19:01 ` Yonghong Song
  11 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-01 19:01 UTC (permalink / raw)
  To: bpf; +Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

Add BTF_KIND_ENUM64 documentation in btf.rst.
Also fixed a typo for section number for BTF_KIND_TYPE_TAG
from 2.2.17 to 2.2.18.

Signed-off-by: Yonghong Song <yhs@fb.com>
---
 Documentation/bpf/btf.rst | 34 +++++++++++++++++++++++++++++-----
 1 file changed, 29 insertions(+), 5 deletions(-)

diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
index 7940da9bc6c1..7186990fb9c3 100644
--- a/Documentation/bpf/btf.rst
+++ b/Documentation/bpf/btf.rst
@@ -74,7 +74,7 @@ sequentially and type id is assigned to each recognized type starting from id
     #define BTF_KIND_ARRAY          3       /* Array        */
     #define BTF_KIND_STRUCT         4       /* Struct       */
     #define BTF_KIND_UNION          5       /* Union        */
-    #define BTF_KIND_ENUM           6       /* Enumeration  */
+    #define BTF_KIND_ENUM           6       /* Enumeration for int/unsigned int values */
     #define BTF_KIND_FWD            7       /* Forward      */
     #define BTF_KIND_TYPEDEF        8       /* Typedef      */
     #define BTF_KIND_VOLATILE       9       /* Volatile     */
@@ -87,6 +87,7 @@ sequentially and type id is assigned to each recognized type starting from id
     #define BTF_KIND_FLOAT          16      /* Floating point       */
     #define BTF_KIND_DECL_TAG       17      /* Decl Tag     */
     #define BTF_KIND_TYPE_TAG       18      /* Type Tag     */
+    #define BTF_KIND_ENUM64         19      /* Enumeration for long/unsigned long values */
 
 Note that the type section encodes debug info, not just pure types.
 ``BTF_KIND_FUNC`` is not a type, and it represents a defined subprogram.
@@ -101,10 +102,10 @@ Each type contains the following common data::
          * bits 24-28: kind (e.g. int, ptr, array...etc)
          * bits 29-30: unused
          * bit     31: kind_flag, currently used by
-         *             struct, union and fwd
+         *             struct, union, fwd, enum and enum64.
          */
         __u32 info;
-        /* "size" is used by INT, ENUM, STRUCT and UNION.
+        /* "size" is used by INT, ENUM, STRUCT, UNION and ENUM64.
          * "size" tells the size of the type it is describing.
          *
          * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
@@ -281,7 +282,7 @@ modes exist:
 
 ``struct btf_type`` encoding requirement:
   * ``name_off``: 0 or offset to a valid C identifier
-  * ``info.kind_flag``: 0
+  * ``info.kind_flag``: 0 for signed, 1 for unsigned
   * ``info.kind``: BTF_KIND_ENUM
   * ``info.vlen``: number of enum values
   * ``size``: 4
@@ -493,7 +494,7 @@ the attribute is applied to a ``struct``/``union`` member or
 a ``func`` argument, and ``btf_decl_tag.component_idx`` should be a
 valid index (starting from 0) pointing to a member or an argument.
 
-2.2.17 BTF_KIND_TYPE_TAG
+2.2.18 BTF_KIND_TYPE_TAG
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
 ``struct btf_type`` encoding requirement:
@@ -516,6 +517,29 @@ type_tag, then zero or more const/volatile/restrict/typedef
 and finally the base type. The base type is one of
 int, ptr, array, struct, union, enum, func_proto and float types.
 
+2.2.19 BTF_KIND_ENUM64
+~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+  * ``name_off``: 0 or offset to a valid C identifier
+  * ``info.kind_flag``: 0 for signed, 1 for unsigned
+  * ``info.kind``: BTF_KIND_ENUM64
+  * ``info.vlen``: number of enum values
+  * ``size``: 8
+
+``btf_type`` is followed by ``info.vlen`` number of ``struct btf_enum64``.::
+
+    struct btf_enum64 {
+        __u32   name_off;
+        __u32   hi32;
+        __u32   lo32;
+    };
+
+The ``btf_enum64`` encoding:
+  * ``name_off``: offset to a valid C identifier
+  * ``hi32``: high 32-bit value for a 64-bit value
+  * ``lo32``: lower 32-bit value for a 64-bit value
+
 3. BTF Kernel API
 =================
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-01 19:00 ` [PATCH bpf-next 04/12] libbpf: Add btf enum64 support Yonghong Song
@ 2022-05-03 17:22   ` kernel test robot
  2022-05-05 22:44     ` Yonghong Song
  2022-05-09 23:25   ` Andrii Nakryiko
  1 sibling, 1 reply; 47+ messages in thread
From: kernel test robot @ 2022-05-03 17:22 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: kbuild-all, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	kernel-team

Hi Yonghong,

I love your patch! Perhaps something to improve:

[auto build test WARNING on bpf-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Yonghong-Song/bpf-Add-64bit-enum-value-support/20220502-030301
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
config: i386-randconfig-m021 (https://download.01.org/0day-ci/archive/20220504/202205040133.jd7yTwg5-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.2.0-20) 11.2.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

New smatch warnings:
tools/lib/bpf/relo_core.c:348 bpf_core_fields_are_compat() warn: if();

Old smatch warnings:
tools/lib/bpf/relo_core.c:349 bpf_core_fields_are_compat() warn: if();

vim +348 tools/lib/bpf/relo_core.c

   314	
   315	/* Check two types for compatibility for the purpose of field access
   316	 * relocation. const/volatile/restrict and typedefs are skipped to ensure we
   317	 * are relocating semantically compatible entities:
   318	 *   - any two STRUCTs/UNIONs are compatible and can be mixed;
   319	 *   - any two FWDs are compatible, if their names match (modulo flavor suffix);
   320	 *   - any two PTRs are always compatible;
   321	 *   - for ENUMs, names should be the same (ignoring flavor suffix) or at
   322	 *     least one of enums should be anonymous;
   323	 *   - for ENUMs, check sizes, names are ignored;
   324	 *   - for INT, size and signedness are ignored;
   325	 *   - any two FLOATs are always compatible;
   326	 *   - for ARRAY, dimensionality is ignored, element types are checked for
   327	 *     compatibility recursively;
   328	 *   - everything else shouldn't be ever a target of relocation.
   329	 * These rules are not set in stone and probably will be adjusted as we get
   330	 * more experience with using BPF CO-RE relocations.
   331	 */
   332	static int bpf_core_fields_are_compat(const struct btf *local_btf,
   333					      __u32 local_id,
   334					      const struct btf *targ_btf,
   335					      __u32 targ_id)
   336	{
   337		const struct btf_type *local_type, *targ_type;
   338	
   339	recur:
   340		local_type = skip_mods_and_typedefs(local_btf, local_id, &local_id);
   341		targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
   342		if (!local_type || !targ_type)
   343			return -EINVAL;
   344	
   345		if (btf_is_composite(local_type) && btf_is_composite(targ_type))
   346			return 1;
   347		if (btf_kind(local_type) != btf_kind(targ_type)) {
 > 348			if (btf_is_enum(local_type) && btf_is_enum64(targ_type)) ;
   349			else if (btf_is_enum64(local_type) && btf_is_enum(targ_type)) ;
   350			else return 0;
   351		}
   352	
   353		switch (btf_kind(local_type)) {
   354		case BTF_KIND_PTR:
   355		case BTF_KIND_FLOAT:
   356			return 1;
   357		case BTF_KIND_FWD:
   358		case BTF_KIND_ENUM:
   359		case BTF_KIND_ENUM64: {
   360			const char *local_name, *targ_name;
   361			size_t local_len, targ_len;
   362	
   363			local_name = btf__name_by_offset(local_btf,
   364							 local_type->name_off);
   365			targ_name = btf__name_by_offset(targ_btf, targ_type->name_off);
   366			local_len = bpf_core_essential_name_len(local_name);
   367			targ_len = bpf_core_essential_name_len(targ_name);
   368			/* one of them is anonymous or both w/ same flavor-less names */
   369			return local_len == 0 || targ_len == 0 ||
   370			       (local_len == targ_len &&
   371				strncmp(local_name, targ_name, local_len) == 0);
   372		}
   373		case BTF_KIND_INT:
   374			/* just reject deprecated bitfield-like integers; all other
   375			 * integers are by default compatible between each other
   376			 */
   377			return btf_int_offset(local_type) == 0 &&
   378			       btf_int_offset(targ_type) == 0;
   379		case BTF_KIND_ARRAY:
   380			local_id = btf_array(local_type)->type;
   381			targ_id = btf_array(targ_type)->type;
   382			goto recur;
   383		default:
   384			return 0;
   385		}
   386	}
   387	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-03 17:22   ` kernel test robot
@ 2022-05-05 22:44     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-05 22:44 UTC (permalink / raw)
  To: kernel test robot, bpf
  Cc: kbuild-all, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	kernel-team



On 5/3/22 10:22 AM, kernel test robot wrote:
> Hi Yonghong,
> 
> I love your patch! Perhaps something to improve:
> 
> [auto build test WARNING on bpf-next/master]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Yonghong-Song/bpf-Add-64bit-enum-value-support/20220502-030301
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
> config: i386-randconfig-m021 (https://download.01.org/0day-ci/archive/20220504/202205040133.jd7yTwg5-lkp@intel.com/config )
> compiler: gcc-11 (Debian 11.2.0-20) 11.2.0
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
> 
> New smatch warnings:
> tools/lib/bpf/relo_core.c:348 bpf_core_fields_are_compat() warn: if();
> 
> Old smatch warnings:
> tools/lib/bpf/relo_core.c:349 bpf_core_fields_are_compat() warn: if();

The following change should work:

diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
index 1e751400427b..2c8d5292e946 100644
--- a/tools/lib/bpf/relo_core.c
+++ b/tools/lib/bpf/relo_core.c
@@ -345,9 +345,8 @@ static int bpf_core_fields_are_compat(const struct 
btf *local_btf,
         if (btf_is_composite(local_type) && btf_is_composite(targ_type))
                 return 1;
         if (btf_kind(local_type) != btf_kind(targ_type)) {
-               if (btf_is_enum(local_type) && btf_is_enum64(targ_type)) ;
-               else if (btf_is_enum64(local_type) && 
btf_is_enum(targ_type)) ;
-               else return 0;
+               if (!btf_is_enum(local_type) || !btf_is_enum64(targ_type))
+                       return 0;
         }

         switch (btf_kind(local_type)) {

I will wait for more comments before submitting version 2.

> 
> vim +348 tools/lib/bpf/relo_core.c
> 
>     314	
>     315	/* Check two types for compatibility for the purpose of field access
>     316	 * relocation. const/volatile/restrict and typedefs are skipped to ensure we
>     317	 * are relocating semantically compatible entities:
>     318	 *   - any two STRUCTs/UNIONs are compatible and can be mixed;
>     319	 *   - any two FWDs are compatible, if their names match (modulo flavor suffix);
>     320	 *   - any two PTRs are always compatible;
>     321	 *   - for ENUMs, names should be the same (ignoring flavor suffix) or at
>     322	 *     least one of enums should be anonymous;
>     323	 *   - for ENUMs, check sizes, names are ignored;
>     324	 *   - for INT, size and signedness are ignored;
>     325	 *   - any two FLOATs are always compatible;
>     326	 *   - for ARRAY, dimensionality is ignored, element types are checked for
>     327	 *     compatibility recursively;
>     328	 *   - everything else shouldn't be ever a target of relocation.
>     329	 * These rules are not set in stone and probably will be adjusted as we get
>     330	 * more experience with using BPF CO-RE relocations.
>     331	 */
>     332	static int bpf_core_fields_are_compat(const struct btf *local_btf,
>     333					      __u32 local_id,
>     334					      const struct btf *targ_btf,
>     335					      __u32 targ_id)
>     336	{
>     337		const struct btf_type *local_type, *targ_type;
>     338	
>     339	recur:
>     340		local_type = skip_mods_and_typedefs(local_btf, local_id, &local_id);
>     341		targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
>     342		if (!local_type || !targ_type)
>     343			return -EINVAL;
>     344	
>     345		if (btf_is_composite(local_type) && btf_is_composite(targ_type))
>     346			return 1;
>     347		if (btf_kind(local_type) != btf_kind(targ_type)) {
>   > 348			if (btf_is_enum(local_type) && btf_is_enum64(targ_type)) ;
>     349			else if (btf_is_enum64(local_type) && btf_is_enum(targ_type)) ;
>     350			else return 0;
>     351		}
>     352	
>     353		switch (btf_kind(local_type)) {
>     354		case BTF_KIND_PTR:
>     355		case BTF_KIND_FLOAT:
>     356			return 1;
>     357		case BTF_KIND_FWD:
>     358		case BTF_KIND_ENUM:
>     359		case BTF_KIND_ENUM64: {
>     360			const char *local_name, *targ_name;
>     361			size_t local_len, targ_len;
>     362	
>     363			local_name = btf__name_by_offset(local_btf,
>     364							 local_type->name_off);
>     365			targ_name = btf__name_by_offset(targ_btf, targ_type->name_off);
>     366			local_len = bpf_core_essential_name_len(local_name);
>     367			targ_len = bpf_core_essential_name_len(targ_name);
>     368			/* one of them is anonymous or both w/ same flavor-less names */
>     369			return local_len == 0 || targ_len == 0 ||
>     370			       (local_len == targ_len &&
>     371				strncmp(local_name, targ_name, local_len) == 0);
>     372		}
>     373		case BTF_KIND_INT:
>     374			/* just reject deprecated bitfield-like integers; all other
>     375			 * integers are by default compatible between each other
>     376			 */
>     377			return btf_int_offset(local_type) == 0 &&
>     378			       btf_int_offset(targ_type) == 0;
>     379		case BTF_KIND_ARRAY:
>     380			local_id = btf_array(local_type)->type;
>     381			targ_id = btf_array(targ_type)->type;
>     382			goto recur;
>     383		default:
>     384			return 0;
>     385		}
>     386	}
>     387	
> 

^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 01/12] bpf: Add btf enum64 support
  2022-05-01 19:00 ` [PATCH bpf-next 01/12] bpf: Add btf enum64 support Yonghong Song
@ 2022-05-09  0:33   ` Dave Marchevsky
  2022-05-09 22:29   ` Andrii Nakryiko
  1 sibling, 0 replies; 47+ messages in thread
From: Dave Marchevsky @ 2022-05-09  0:33 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

On 5/1/22 3:00 PM, Yonghong Song wrote:   
> 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 bytes followed by the common type:
>   struct bpf_enum64 {
>     __u32 nume_off;
>     __u32 hi32;
>     __u32 lo32;
>   };
> 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 hi32 and 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, print 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 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 value size of BTF_KIND_ENUM64 is encoded to 8 to represent its intent.
> The compiler ([1]) and pahole will generate BTF_KIND_ENUM64 only if the value has
> to be represented with 64 bits.
> 
>   [1] https://reviews.llvm.org/D124641
> 
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  include/linux/btf.h            |  18 ++++-
>  include/uapi/linux/btf.h       |  17 ++++-
>  kernel/bpf/btf.c               | 132 ++++++++++++++++++++++++++++++---
>  tools/include/uapi/linux/btf.h |  17 ++++-
>  4 files changed, 168 insertions(+), 16 deletions(-)

[...]

> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> index a9162a6c0284..2aac226a27b2 100644
> --- a/include/uapi/linux/btf.h
> +++ b/include/uapi/linux/btf.h
> @@ -36,10 +36,10 @@ struct btf_type {
>  	 * bits 24-28: kind (e.g. int, ptr, array...etc)
>  	 * bits 29-30: unused
>  	 * bit     31: kind_flag, currently used by
> -	 *             struct, union and fwd
> +	 *             struct, union, enum, fwd and enum64
>  	 */
>  	__u32 info;
> -	/* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
> +	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
>  	 * "size" tells the size of the type it is describing.
>  	 *
>  	 * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
> @@ -63,7 +63,7 @@ enum {
>  	BTF_KIND_ARRAY		= 3,	/* Array	*/
>  	BTF_KIND_STRUCT		= 4,	/* Struct	*/
>  	BTF_KIND_UNION		= 5,	/* Union	*/
> -	BTF_KIND_ENUM		= 6,	/* Enumeration	*/
> +	BTF_KIND_ENUM		= 6,	/* Enumeration for int/unsigned int values */

nit: Maybe it would be more clear to say something like "Enumeration
representable in <= 32 bits", and something similar for ENUM64? This applies to
docs/bpf patch as well. I don't feel strongly about it.

>  	BTF_KIND_FWD		= 7,	/* Forward	*/
>  	BTF_KIND_TYPEDEF	= 8,	/* Typedef	*/
>  	BTF_KIND_VOLATILE	= 9,	/* Volatile	*/
> @@ -76,6 +76,7 @@ enum {
>  	BTF_KIND_FLOAT		= 16,	/* Floating point	*/
>  	BTF_KIND_DECL_TAG	= 17,	/* Decl Tag */
>  	BTF_KIND_TYPE_TAG	= 18,	/* Type Tag */
> +	BTF_KIND_ENUM64		= 19,	/* Enumeration for long/unsigned long values */
>  
>  	NR_BTF_KINDS,
>  	BTF_KIND_MAX		= NR_BTF_KINDS - 1,
> @@ -186,4 +187,14 @@ struct btf_decl_tag {
>         __s32   component_idx;
>  };

[...]

> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 2f0b0440131c..17e24b362d3d 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -307,6 +307,7 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
>  	[BTF_KIND_FLOAT]	= "FLOAT",
>  	[BTF_KIND_DECL_TAG]	= "DECL_TAG",
>  	[BTF_KIND_TYPE_TAG]	= "TYPE_TAG",
> +	[BTF_KIND_ENUM64]	= "ENUM64",
>  };
>  
>  const char *btf_type_str(const struct btf_type *t)
> @@ -664,6 +665,7 @@ static bool btf_type_has_size(const struct btf_type *t)
>  	case BTF_KIND_ENUM:
>  	case BTF_KIND_DATASEC:
>  	case BTF_KIND_FLOAT:
> +	case BTF_KIND_ENUM64:
>  		return true;
>  	}
>  

[...]

> @@ -1832,6 +1840,7 @@ __btf_resolve_size(const struct btf *btf, const struct btf_type *type,
>  		case BTF_KIND_UNION:
>  		case BTF_KIND_ENUM:
>  		case BTF_KIND_FLOAT:
> +		case BTF_KIND_ENUM64:
>  			size = type->size;
>  			goto resolved;

Is it possible to replace this check w/ btf_type_has_size that you also updated?
Looks like case's match, aside from BTF_KIND_DATASEC.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation
  2022-05-01 19:00 ` [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation Yonghong Song
@ 2022-05-09  0:55   ` Dave Marchevsky
  2022-05-09  0:56     ` Dave Marchevsky
  2022-05-09 22:37   ` Andrii Nakryiko
  1 sibling, 1 reply; 47+ messages in thread
From: Dave Marchevsky @ 2022-05-09  0:55 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

On 5/1/22 3:00 PM, Yonghong Song wrote:   
> Currently, the 64bit relocation value in the instruction
> is computed as follows:
>   __u64 imm = insn[0].imm + ((__u64)insn[1].imm << 32)
> 
> Suppose insn[0].imm = -1 (0xffffffff) and insn[1].imm = 1.
> With the above computation, insn[0].imm will first sign-extend
> to 64bit -1 (0xffffffffFFFFFFFF) and then add 0x1FFFFFFFF,
> producing incorrect value 0xFFFFFFFF. The correct value
> should be 0x1FFFFFFFF.
> 
> Changing insn[0].imm to __u32 first will prevent 64bit sign
> extension and fix the issue.
> 
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  tools/lib/bpf/relo_core.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 

Signed-off-by: Dave Marchevsky <davemarchevsky@fb.com>


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation
  2022-05-09  0:55   ` Dave Marchevsky
@ 2022-05-09  0:56     ` Dave Marchevsky
  0 siblings, 0 replies; 47+ messages in thread
From: Dave Marchevsky @ 2022-05-09  0:56 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

On 5/8/22 8:55 PM, Dave Marchevsky wrote:   
> On 5/1/22 3:00 PM, Yonghong Song wrote:   
>> Currently, the 64bit relocation value in the instruction
>> is computed as follows:
>>   __u64 imm = insn[0].imm + ((__u64)insn[1].imm << 32)
>>
>> Suppose insn[0].imm = -1 (0xffffffff) and insn[1].imm = 1.
>> With the above computation, insn[0].imm will first sign-extend
>> to 64bit -1 (0xffffffffFFFFFFFF) and then add 0x1FFFFFFFF,
>> producing incorrect value 0xFFFFFFFF. The correct value
>> should be 0x1FFFFFFFF.
>>
>> Changing insn[0].imm to __u32 first will prevent 64bit sign
>> extension and fix the issue.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>  tools/lib/bpf/relo_core.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
> 
> Signed-off-by: Dave Marchevsky <davemarchevsky@fb.com>
> 

Whoops, meant:

Acked-by: Dave Marchevsky <davemarchevsky@fb.com>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value
  2022-05-01 19:00 ` [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value Yonghong Song
@ 2022-05-09  1:06   ` Dave Marchevsky
  2022-05-10 19:35     ` Yonghong Song
  2022-05-09 22:37   ` Andrii Nakryiko
  1 sibling, 1 reply; 47+ messages in thread
From: Dave Marchevsky @ 2022-05-09  1:06 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

On 5/1/22 3:00 PM, Yonghong Song wrote:   
> Currently, the libbpf limits the relocation value to be 32bit
> since all current relocations have such a limit. But with
> BTF_KIND_ENUM64 support, the enum value could be 64bit.
> So let us permit 64bit relocation value in libbpf.
> 
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  tools/lib/bpf/relo_core.c | 24 ++++++++++++------------
>  tools/lib/bpf/relo_core.h |  4 ++--
>  2 files changed, 14 insertions(+), 14 deletions(-)

[...]

> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
> index ba4453dfd1ed..2ed94daabbe5 100644
> --- a/tools/lib/bpf/relo_core.c
> +++ b/tools/lib/bpf/relo_core.c

[...]


> @@ -1035,7 +1035,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>  
>  		insn[0].imm = new_val;
>  		insn[1].imm = 0; /* currently only 32-bit values are supported */
> -		pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n",
> +		pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>  			 prog_name, relo_idx, insn_idx,
>  			 (unsigned long long)imm, new_val);
>  		break;

Since new_val is 64bit now, should the insn[1].imm be set here, and the comment
about 32-bit be removed?

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure
  2022-05-01 19:00 ` [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure Yonghong Song
@ 2022-05-09  2:21   ` Dave Marchevsky
  2022-05-10 19:40     ` Yonghong Song
  2022-05-09 23:34   ` Andrii Nakryiko
  1 sibling, 1 reply; 47+ messages in thread
From: Dave Marchevsky @ 2022-05-09  2:21 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team

On 5/1/22 3:00 PM, Yonghong Song wrote:   
> The kflag is supported now for BTF_KIND_ENUM.
> So remove the test which tests verifier failure
> due to existence of kflag.
> 
> With enum64 support in kernel and libbpf,
> selftest btf_dump/btf_dump failed with
> no-enum64 support llvm for the following
> enum definition:
>  enum e2 {
>         C = 100,
>         D = 4294967295,
>         E = 0,
>  };
> 
> With the no-enum64 support llvm, the signedness is
> 'signed' by default, and D (4294967295 = 0xffffffff)
> will print as -1. With enum64 support llvm, the signedness
> is 'unsigned' and the value of D will print as 4294967295.

To confirm my understanding, this signedness-by-default change is for _printing
btf in c format only_ and doesn't correspond to any difference in interpretation
, since all BTF enums before addition of new kflag were assumed to be signed,
and new kflag's default value is signed as well.

> To support both old and new compilers, this patch
> changed the value to 268435455 = 0xfffffff which works
> with both enum64 or non-enum64 support llvm.
> 
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---

Aside from the general question, for changes in this patch:

Acked-by: Dave Marchevsky <davemarchevsky@fb.com>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 01/12] bpf: Add btf enum64 support
  2022-05-01 19:00 ` [PATCH bpf-next 01/12] bpf: Add btf enum64 support Yonghong Song
  2022-05-09  0:33   ` Dave Marchevsky
@ 2022-05-09 22:29   ` Andrii Nakryiko
  2022-05-10 22:06     ` Yonghong Song
  1 sibling, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 22:29 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>
> 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 bytes followed by the common type:

you probably meant three fields, not bytes

>   struct bpf_enum64 {
>     __u32 nume_off;
>     __u32 hi32;
>     __u32 lo32;

I'd like to nitpick on name here, as hi/lo of what? Maybe val_hi32 and
val_lo32? Can we also reverse the order here? For x86 you'll be able
to use &lo32 to get value directly if you really want, without a local
copy. It also just logically seems better to have something low first,
then high next.


>   };
> 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 hi32 and 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, print 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 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 value size of BTF_KIND_ENUM64 is encoded to 8 to represent its intent.
> The compiler ([1]) and pahole will generate BTF_KIND_ENUM64 only if the value has
> to be represented with 64 bits.
>
>   [1] https://reviews.llvm.org/D124641
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  include/linux/btf.h            |  18 ++++-
>  include/uapi/linux/btf.h       |  17 ++++-
>  kernel/bpf/btf.c               | 132 ++++++++++++++++++++++++++++++---
>  tools/include/uapi/linux/btf.h |  17 ++++-
>  4 files changed, 168 insertions(+), 16 deletions(-)
>
> diff --git a/include/linux/btf.h b/include/linux/btf.h
> index 2611cea2c2b6..280c33c9414a 100644
> --- a/include/linux/btf.h
> +++ b/include/linux/btf.h
> @@ -174,7 +174,8 @@ static inline bool btf_type_is_small_int(const struct btf_type *t)
>
>  static inline bool btf_type_is_enum(const struct btf_type *t)
>  {
> -       return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM;
> +       return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM ||
> +              BTF_INFO_KIND(t->info) == BTF_KIND_ENUM64;
>  }

a bit hesitant about this change, there is no helper to check for ENUM
vs ENUM64. Inside the kernel this change seems to be correct as we
don't care, but for libbpf I'd probably keep btf_is_enum() unchanged
(you can't really work with them in the same generic way in
user-space, as their memory layout is very different, so it's better
not to generalize them unnecessarily)

>
>  static inline bool str_is_empty(const char *s)
> @@ -192,6 +193,16 @@ static inline bool btf_is_enum(const struct btf_type *t)
>         return btf_kind(t) == BTF_KIND_ENUM;
>  }
>
> +static inline bool btf_is_enum64(const struct btf_type *t)
> +{
> +       return btf_kind(t) == BTF_KIND_ENUM64;
> +}
> +
> +static inline u64 btf_enum64_value(const struct btf_enum64 *e)
> +{
> +       return (u64)e->hi32 << 32 | e->lo32;

this might be correct but () around bit shift would make it more obvious

> +}
> +
>  static inline bool btf_is_composite(const struct btf_type *t)
>  {
>         u16 kind = btf_kind(t);
> @@ -332,6 +343,11 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t)
>         return (struct btf_enum *)(t + 1);
>  }
>
> +static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
> +{
> +       return (struct btf_enum64 *)(t + 1);
> +}
> +
>  static inline const struct btf_var_secinfo *btf_type_var_secinfo(
>                 const struct btf_type *t)
>  {
> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> index a9162a6c0284..2aac226a27b2 100644
> --- a/include/uapi/linux/btf.h
> +++ b/include/uapi/linux/btf.h
> @@ -36,10 +36,10 @@ struct btf_type {
>          * bits 24-28: kind (e.g. int, ptr, array...etc)
>          * bits 29-30: unused
>          * bit     31: kind_flag, currently used by
> -        *             struct, union and fwd
> +        *             struct, union, enum, fwd and enum64

see comment below on kflag for enum itself, but reading this I
realized that we don't really describe the meaning of kind_flag for
different kinds. Should it be done here?

>          */
>         __u32 info;
> -       /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
> +       /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
>          * "size" tells the size of the type it is describing.
>          *
>          * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
> @@ -63,7 +63,7 @@ enum {
>         BTF_KIND_ARRAY          = 3,    /* Array        */
>         BTF_KIND_STRUCT         = 4,    /* Struct       */
>         BTF_KIND_UNION          = 5,    /* Union        */
> -       BTF_KIND_ENUM           = 6,    /* Enumeration  */
> +       BTF_KIND_ENUM           = 6,    /* Enumeration for int/unsigned int values */

nit: "Enumeration for up to 32-bit values" ?

>         BTF_KIND_FWD            = 7,    /* Forward      */
>         BTF_KIND_TYPEDEF        = 8,    /* Typedef      */
>         BTF_KIND_VOLATILE       = 9,    /* Volatile     */
> @@ -76,6 +76,7 @@ enum {
>         BTF_KIND_FLOAT          = 16,   /* Floating point       */
>         BTF_KIND_DECL_TAG       = 17,   /* Decl Tag */
>         BTF_KIND_TYPE_TAG       = 18,   /* Type Tag */
> +       BTF_KIND_ENUM64         = 19,   /* Enumeration for long/unsigned long values */

and then "for 64-bit values" (or maybe up to 64 bit values, but in
practice we won't do that, right?)

>
>         NR_BTF_KINDS,
>         BTF_KIND_MAX            = NR_BTF_KINDS - 1,
> @@ -186,4 +187,14 @@ struct btf_decl_tag {
>         __s32   component_idx;
>  };
>

[...]

> @@ -3716,7 +3721,8 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
>
>                 if (env->log.level == BPF_LOG_KERNEL)
>                         continue;
> -               btf_verifier_log(env, "\t%s val=%d\n",
> +               fmt_str = btf_type_kflag(t) ? "\t%s val=%u\n" : "\t%s val=%d\n";
> +               btf_verifier_log(env, fmt_str,
>                                  __btf_name_by_offset(btf, enums[i].name_off),
>                                  enums[i].val);
>         }
> @@ -3757,7 +3763,10 @@ static void btf_enum_show(const struct btf *btf, const struct btf_type *t,
>                 return;
>         }
>
> -       btf_show_type_value(show, "%d", v);
> +       if (btf_type_kflag(t))

libbpf's assumption right now and most common case is unsigned enum,
so it seems more desirable to have kflag == 0 mean unsigned, with
kflag == 1 being signed. It will make most existing enum definitions
not change but also make it easy for libbpf's btf_dumper to use kflag
if it's set, but otherwise have the same unsigned printing whether
enum is really unsigned or Clang is too old to emit the kflag for
enum. WDYT?

> +               btf_show_type_value(show, "%u", v);
> +       else
> +               btf_show_type_value(show, "%d", v);

you didn't got with ternary operator for fmt string selector like in
previous case? I have mild preference for keeping it consistent (and
keeping btf_type_kflag(t) ? "fmt1" : "fmt2" inline)

>         btf_show_end_type(show);
>  }
>
> @@ -3770,6 +3779,109 @@ static struct btf_kind_operations enum_ops = {
>         .show = btf_enum_show,
>  };
>
> +static s32 btf_enum64_check_meta(struct btf_verifier_env *env,
> +                                const struct btf_type *t,
> +                                u32 meta_left)
> +{
> +       const struct btf_enum64 *enums = btf_type_enum64(t);
> +       struct btf *btf = env->btf;
> +       const char *fmt_str;
> +       u16 i, nr_enums;
> +       u32 meta_needed;
> +
> +       nr_enums = btf_type_vlen(t);
> +       meta_needed = nr_enums * sizeof(*enums);
> +
> +       if (meta_left < meta_needed) {
> +               btf_verifier_log_basic(env, t,
> +                                      "meta_left:%u meta_needed:%u",
> +                                      meta_left, meta_needed);
> +               return -EINVAL;
> +       }
> +
> +       if (t->size != 8) {

technically there is nothing wrong with using enum64 for smaller
sizes, right? Any particular reason to prevent this? We can just
define that 64-bit value is sign-extended if enum is signed and has
size < 8?

> +               btf_verifier_log_type(env, t, "Unexpected size");
> +               return -EINVAL;
> +       }
> +
> +       /* enum type either no name or a valid one */
> +       if (t->name_off &&
> +           !btf_name_valid_identifier(env->btf, t->name_off)) {
> +               btf_verifier_log_type(env, t, "Invalid name");
> +               return -EINVAL;
> +       }
> +

[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation
  2022-05-01 19:00 ` [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation Yonghong Song
  2022-05-09  0:55   ` Dave Marchevsky
@ 2022-05-09 22:37   ` Andrii Nakryiko
  2022-05-10 22:11     ` Yonghong Song
  1 sibling, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 22:37 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>
> Currently, the 64bit relocation value in the instruction
> is computed as follows:
>   __u64 imm = insn[0].imm + ((__u64)insn[1].imm << 32)
>
> Suppose insn[0].imm = -1 (0xffffffff) and insn[1].imm = 1.
> With the above computation, insn[0].imm will first sign-extend
> to 64bit -1 (0xffffffffFFFFFFFF) and then add 0x1FFFFFFFF,
> producing incorrect value 0xFFFFFFFF. The correct value
> should be 0x1FFFFFFFF.
>
> Changing insn[0].imm to __u32 first will prevent 64bit sign
> extension and fix the issue.
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  tools/lib/bpf/relo_core.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
> index 2ed94daabbe5..f25ffd03c3b1 100644
> --- a/tools/lib/bpf/relo_core.c
> +++ b/tools/lib/bpf/relo_core.c
> @@ -1024,7 +1024,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>                         return -EINVAL;
>                 }
>
> -               imm = insn[0].imm + ((__u64)insn[1].imm << 32);
> +               imm = (__u32)insn[0].imm + ((__u64)insn[1].imm << 32);

great catch, it should also probably be written as | instead of + operation?

>                 if (res->validate && imm != orig_val) {
>                         pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
>                                 prog_name, relo_idx,
> --
> 2.30.2
>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value
  2022-05-01 19:00 ` [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value Yonghong Song
  2022-05-09  1:06   ` Dave Marchevsky
@ 2022-05-09 22:37   ` Andrii Nakryiko
  2022-05-10 22:14     ` Yonghong Song
  1 sibling, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 22:37 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>
> Currently, the libbpf limits the relocation value to be 32bit
> since all current relocations have such a limit. But with
> BTF_KIND_ENUM64 support, the enum value could be 64bit.
> So let us permit 64bit relocation value in libbpf.
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  tools/lib/bpf/relo_core.c | 24 ++++++++++++------------
>  tools/lib/bpf/relo_core.h |  4 ++--
>  2 files changed, 14 insertions(+), 14 deletions(-)
>

[...]

> @@ -929,7 +929,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>                         int insn_idx, const struct bpf_core_relo *relo,
>                         int relo_idx, const struct bpf_core_relo_res *res)
>  {
> -       __u32 orig_val, new_val;
> +       __u64 orig_val, new_val;
>         __u8 class;
>
>         class = BPF_CLASS(insn->code);
> @@ -954,14 +954,14 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>                 if (BPF_SRC(insn->code) != BPF_K)
>                         return -EINVAL;
>                 if (res->validate && insn->imm != orig_val) {
> -                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %u -> %u\n",
> +                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %llu -> %llu\n",
>                                 prog_name, relo_idx,
>                                 insn_idx, insn->imm, orig_val, new_val);

%llu is not valid formatter for __u64 on all architectures, please add
explicit (unsigned long long) cast

but also in general for non-ldimm64 instructions we need to check that
new value fits in 32 bits

[...]

> @@ -1026,7 +1026,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>
>                 imm = insn[0].imm + ((__u64)insn[1].imm << 32);
>                 if (res->validate && imm != orig_val) {
> -                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %u -> %u\n",
> +                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
>                                 prog_name, relo_idx,
>                                 insn_idx, (unsigned long long)imm,
>                                 orig_val, new_val);
> @@ -1035,7 +1035,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>
>                 insn[0].imm = new_val;
>                 insn[1].imm = 0; /* currently only 32-bit values are supported */

as Dave mentioned, not anymore, so this should take higher 32-bit of new_val


> -               pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n",
> +               pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>                          prog_name, relo_idx, insn_idx,
>                          (unsigned long long)imm, new_val);
>                 break;
> @@ -1261,7 +1261,7 @@ int bpf_core_calc_relo_insn(const char *prog_name,
>                          * decision and value, otherwise it's dangerous to
>                          * proceed due to ambiguity
>                          */
> -                       pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n",
> +                       pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %llu != %s %llu\n",
>                                 prog_name, relo_idx,
>                                 cand_res.poison ? "failure" : "success", cand_res.new_val,
>                                 targ_res->poison ? "failure" : "success", targ_res->new_val);
> diff --git a/tools/lib/bpf/relo_core.h b/tools/lib/bpf/relo_core.h
> index 073039d8ca4f..7df0da082f2c 100644
> --- a/tools/lib/bpf/relo_core.h
> +++ b/tools/lib/bpf/relo_core.h
> @@ -46,9 +46,9 @@ struct bpf_core_spec {
>
>  struct bpf_core_relo_res {
>         /* expected value in the instruction, unless validate == false */
> -       __u32 orig_val;
> +       __u64 orig_val;
>         /* new value that needs to be patched up to */
> -       __u32 new_val;
> +       __u64 new_val;
>         /* relocation unsuccessful, poison instruction, but don't fail load */
>         bool poison;
>         /* some relocations can't be validated against orig_val */
> --
> 2.30.2
>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-01 19:00 ` [PATCH bpf-next 04/12] libbpf: Add btf enum64 support Yonghong Song
  2022-05-03 17:22   ` kernel test robot
@ 2022-05-09 23:25   ` Andrii Nakryiko
  2022-05-10 22:40     ` Yonghong Song
  1 sibling, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 23:25 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>
> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
> btf__add_enum_value() and introduced the following new APIs
>   btf__add_enum32()
>   btf__add_enum32_value()
>   btf__add_enum64()
>   btf__add_enum64_value()
> due to new kind and introduction of kflag.
>
> To support old kernel with enum64, the sanitization is
> added to replace BTF_KIND_ENUM64 with a bunch of
> pointer-to-void types.
>
> The enum64 value relocation is also supported. The enum64
> forward resolution, with enum type as forward declaration
> and enum64 as the actual definition, is also supported.
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
>  tools/lib/bpf/btf.h                           |  21 ++
>  tools/lib/bpf/btf_dump.c                      |  94 ++++++--
>  tools/lib/bpf/libbpf.c                        |  64 ++++-
>  tools/lib/bpf/libbpf.map                      |   4 +
>  tools/lib/bpf/libbpf_internal.h               |   2 +
>  tools/lib/bpf/linker.c                        |   2 +
>  tools/lib/bpf/relo_core.c                     |  93 ++++---
>  .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
>  .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
>  10 files changed, 450 insertions(+), 72 deletions(-)
>

This is a huge patch touching very different and logically independent
parts of libbpf. Please split it into smaller parts, e.g.:
  - libbpf.c changes (sanitization and kcfg);
  - BTF public API helpers (btf_is_enum64, btf__add_enum64);
  - btf_dump changes;
  - btf__dedup changes;
  - CO-RE relocations.

It will be easier to discuss each in a separate patch.

[...]

> +static int btf_add_enum_common(struct btf *btf, const char *name,
> +                              bool is_unsigned, __u8 kind, __u32 tsize)
> +{
> +       struct btf_type *t;
> +       int sz, name_off = 0;
> +
> +       if (btf_ensure_modifiable(btf))
> +               return libbpf_err(-ENOMEM);
> +
> +       sz = sizeof(struct btf_type);
> +       t = btf_add_type_mem(btf, sz);
> +       if (!t)
> +               return libbpf_err(-ENOMEM);
> +
> +       if (name && name[0]) {
> +               name_off = btf__add_str(btf, name);
> +               if (name_off < 0)
> +                       return name_off;
> +       }
> +
> +       /* start out with vlen=0; it will be adjusted when adding enum values */
> +       t->name_off = name_off;
> +       t->info = btf_type_info(kind, 0, is_unsigned);

As mentioned on another patch, I think unsigned should be default
(despite UAPI having s32 as type for enum's val), because that's what
we assume in practice. It makes backwards compatibility easier in more
than one place


> +       t->size = tsize;
> +
> +       return btf_commit_type(btf, sz);
> +}
> +
> +/*
> + * Append new BTF_KIND_ENUM type with:
> + *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
> + *   - *is_unsigned* - whether the enum values are unsigned or not;
> + *
> + * Enum initially has no enum values in it (and corresponds to enum forward
> + * declaration). Enumerator values can be added by btf__add_enum64_value()
> + * immediately after btf__add_enum() succeeds.
> + *
> + * Returns:
> + *   - >0, type ID of newly added BTF type;
> + *   - <0, on error.
> + */
> +int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned)

given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
btf__add_enum()/btf__add_enum_value() and not deprecate anything.
ENUM64 can be thought about as more of a special case, so I think it's
ok.

> +{
> +       return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM, 4);
> +}
> +

[...]

>  /*
>   * Append new BTF_KIND_FWD type with:
>   *   - *name*, non-empty/non-NULL name;
> @@ -2242,7 +2419,7 @@ int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind)
>                 /* enum forward in BTF currently is just an enum with no enum
>                  * values; we also assume a standard 4-byte size for it
>                  */
> -               return btf__add_enum(btf, name, sizeof(int));
> +               return btf__add_enum32(btf, name, false);
>         default:
>                 return libbpf_err(-EINVAL);
>         }
> @@ -3485,6 +3662,7 @@ static long btf_hash_enum(struct btf_type *t)
>  /* Check structural equality of two ENUMs. */
>  static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
>  {
> +       const struct btf_enum64 *n1, *n2;
>         const struct btf_enum *m1, *m2;
>         __u16 vlen;
>         int i;
> @@ -3493,26 +3671,40 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)

they are so different that I think separate btf_equal_enum64() and
similar approaches everywhere makes sense. Yes, it's enum, but in
practice two very different kinds and should be handled differently

>                 return false;
>
>         vlen = btf_vlen(t1);
> -       m1 = btf_enum(t1);
> -       m2 = btf_enum(t2);
> -       for (i = 0; i < vlen; i++) {
> -               if (m1->name_off != m2->name_off || m1->val != m2->val)
> -                       return false;
> -               m1++;
> -               m2++;

[...]

>  enum btf_fwd_kind {
>         BTF_FWD_STRUCT = 0,
> @@ -454,6 +460,11 @@ static inline bool btf_is_enum(const struct btf_type *t)
>         return btf_kind(t) == BTF_KIND_ENUM;
>  }
>
> +static inline bool btf_is_enum64(const struct btf_type *t)
> +{
> +       return btf_kind(t) == BTF_KIND_ENUM64;

please also add #define BTF_KIND_ENUM64 19 to avoid user breakage if
they don't have very latest kernel UAPI header, same as we did for
TYPE_TAG and others

> +}
> +
>  static inline bool btf_is_fwd(const struct btf_type *t)
>  {
>         return btf_kind(t) == BTF_KIND_FWD;

[...]

> @@ -993,8 +996,11 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
>                                    const struct btf_type *t,
>                                    int lvl)
>  {
> -       const struct btf_enum *v = btf_enum(t);
> +       bool is_unsigned = btf_kflag(t);
> +       const struct btf_enum64 *v64;
> +       const struct btf_enum *v;
>         __u16 vlen = btf_vlen(t);
> +       const char *fmt_str;
>         const char *name;
>         size_t dup_cnt;
>         int i;
> @@ -1005,18 +1011,47 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
>
>         if (vlen) {
>                 btf_dump_printf(d, " {");
> -               for (i = 0; i < vlen; i++, v++) {
> -                       name = btf_name_of(d, v->name_off);
> -                       /* enumerators share namespace with typedef idents */
> -                       dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
> -                       if (dup_cnt > 1) {
> -                               btf_dump_printf(d, "\n%s%s___%zu = %u,",
> -                                               pfx(lvl + 1), name, dup_cnt,
> -                                               (__u32)v->val);
> -                       } else {
> -                               btf_dump_printf(d, "\n%s%s = %u,",
> -                                               pfx(lvl + 1), name,
> -                                               (__u32)v->val);
> +               if (btf_is_enum(t)) {
> +                       v = btf_enum(t);
> +                       for (i = 0; i < vlen; i++, v++) {
> +                               name = btf_name_of(d, v->name_off);
> +                               /* enumerators share namespace with typedef idents */
> +                               dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
> +                               if (dup_cnt > 1) {
> +                                       fmt_str = is_unsigned ? "\n%s%s___%zu = %u,"
> +                                                             : "\n%s%s___%zu = %d,";
> +                                       btf_dump_printf(d, fmt_str,
> +                                                       pfx(lvl + 1), name, dup_cnt,
> +                                                       v->val);
> +                               } else {
> +                                       fmt_str = is_unsigned ? "\n%s%s = %u,"
> +                                                             : "\n%s%s = %d,";
> +                                       btf_dump_printf(d, fmt_str,
> +                                                       pfx(lvl + 1), name,
> +                                                       v->val);
> +                               }
> +                       }
> +               } else {
> +                       v64 = btf_enum64(t);
> +                       for (i = 0; i < vlen; i++, v64++) {
> +                               __u64 val = btf_enum64_value(v64);
> +
> +                               name = btf_name_of(d, v64->name_off);
> +                               /* enumerators share namespace with typedef idents */
> +                               dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
> +                               if (dup_cnt > 1) {
> +                                       fmt_str = is_unsigned ? "\n%s%s___%zu = %lluULL,"
> +                                                             : "\n%s%s___%zu = %lldLL,";
> +                                       btf_dump_printf(d, fmt_str,
> +                                                       pfx(lvl + 1), name, dup_cnt,
> +                                                       val);
> +                               } else {
> +                                       fmt_str = is_unsigned ? "\n%s%s = %lluULL,"
> +                                                             : "\n%s%s = %lldLL,";
> +                                       btf_dump_printf(d, fmt_str,
> +                                                       pfx(lvl + 1), name,
> +                                                       val);
> +                               }
>                         }

yeah, let's just have btf_dump_emit_enum64_def(), there is very little
that can be reused, I think it will be cleaning to keep enum and
enum64 separate everywhere where we actually need to iterate
enumerators and do something about them

>                 }
>                 btf_dump_printf(d, "\n%s}", pfx(lvl));
> @@ -1183,6 +1218,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
>                 case BTF_KIND_UNION:
>                 case BTF_KIND_TYPEDEF:
>                 case BTF_KIND_FLOAT:

[...]

> -       btf_dump_type_values(d, "%d", value);
> +               btf_dump_type_values(d, is_unsigned ? "%u" : "%d", value);
> +       } else {
> +               for (i = 0, e64 = btf_enum64(t); i < btf_vlen(t); i++, e64++) {
> +                       if (value != btf_enum64_value(e64))
> +                               continue;
> +                       btf_dump_type_values(d, "%s", btf_name_of(d, e64->name_off));
> +                       return 0;
> +               }
> +
> +               btf_dump_type_values(d, is_unsigned ? "%lluULL" : "%lldLL", value);
> +       }

ditto, also beware of %lld/%llu use with __u64/__s64, it gives
compilation warnings without cast on some architectures

>         return 0;
>  }
>

[...]

> @@ -2717,6 +2720,17 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
>                         /* replace TYPE_TAG with a CONST */
>                         t->name_off = 0;
>                         t->info = BTF_INFO_ENC(BTF_KIND_CONST, 0, 0);
> +               } else if (!has_enum64 && btf_is_enum(t)) {
> +                       /* clear the kflag */
> +                       t->info &= 0x7fffffff;

please use btf_type_info() helper (defined in libbpf_internal.h) or
just plain BTF_INFO_ENC() like all other cases around instead of
hard-coding magic masks

> +               } else if (!has_enum64 && btf_is_enum64(t)) {
> +                       /* replace ENUM64 with pointer->void's */
> +                       vlen = btf_vlen(t);
> +                       for (j = 0; j <= vlen; j++, t++) {
> +                               t->name_off = 0;
> +                               t->info = BTF_INFO_ENC(BTF_KIND_PTR, 0, 0);
> +                               t->type = 0;
> +                       }

I don't think we can replace each enumerator with a new kind, it
breaks type ID numbering. struct btf_member has matching layout, so we
can replace ENUM64 with UNION (easier to keep offsets as zeroes),
WDYT?

>                 }
>         }
>  }
> @@ -3563,6 +3577,12 @@ static enum kcfg_type find_kcfg_type(const struct btf *btf, int id,
>                 if (strcmp(name, "libbpf_tristate"))
>                         return KCFG_UNKNOWN;
>                 return KCFG_TRISTATE;
> +       case BTF_KIND_ENUM64:
> +               if (t->size != 8)
> +                       return KCFG_UNKNOWN;

I think I don't like this t->size == 8 more and more. At some we'll
decide it's ok and then we'll have to go and adjust everything again.
It requires pretty much zero effort to support from the very beginning
and makes tons of sense to allow that, let's allow it.

> +               if (strcmp(name, "libbpf_tristate"))
> +                       return KCFG_UNKNOWN;
> +               return KCFG_TRISTATE;
>         case BTF_KIND_ARRAY:
>                 if (btf_array(t)->nelems == 0)
>                         return KCFG_UNKNOWN;
> @@ -4746,6 +4766,17 @@ static int probe_kern_bpf_cookie(void)
>         return probe_fd(ret);
>  }
>
> +static int probe_kern_btf_enum64(void)
> +{
> +       static const char strs[] = "\0enum64";
> +       __u32 types[] = {
> +               BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
> +       };
> +
> +       return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
> +                                            strs, sizeof(strs)));
> +}
> +
>  enum kern_feature_result {
>         FEAT_UNKNOWN = 0,
>         FEAT_SUPPORTED = 1,
> @@ -4811,6 +4842,9 @@ static struct kern_feature_desc {
>         [FEAT_BPF_COOKIE] = {
>                 "BPF cookie support", probe_kern_bpf_cookie,
>         },
> +       [FEAT_BTF_ENUM64] = {
> +               "BTF_KIND_ENUM64 support", probe_kern_btf_enum64,
> +       },
>  };
>
>  bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
> @@ -5296,6 +5330,15 @@ void bpf_core_free_cands(struct bpf_core_cand_list *cands)
>         free(cands);
>  }
>
> +static bool btf_is_enum_enum64(const struct btf_type *t1,
> +                              const struct btf_type *t2) {
> +       if (btf_is_enum(t1) && btf_is_enum64(t2))
> +               return true;
> +       if (btf_is_enum(t2) && btf_is_enum64(t1))
> +               return true;
> +       return false;
> +}
> +

maybe simplify and rename to

static bool btf_are_enums(...) {
    return (btf_is_enum(t1) || btf_is_enum64(t1)) && (same for t2)?
}

>  int bpf_core_add_cands(struct bpf_core_cand *local_cand,
>                        size_t local_essent_len,
>                        const struct btf *targ_btf,
> @@ -5315,8 +5358,10 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
>         n = btf__type_cnt(targ_btf);
>         for (i = targ_start_id; i < n; i++) {
>                 t = btf__type_by_id(targ_btf, i);
> -               if (btf_kind(t) != btf_kind(local_t))
> -                       continue;
> +               if (btf_kind(t) != btf_kind(local_t)) {
> +                       if (!btf_is_enum_enum64(t, local_t))
> +                               continue;
> +               }

let's extract this into a helper and call it btf_kinds_are_compat() or
something along those lines?

>
>                 targ_name = btf__name_by_offset(targ_btf, t->name_off);
>                 if (str_is_empty(targ_name))
> @@ -5529,8 +5574,10 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
>         /* caller made sure that names match (ignoring flavor suffix) */
>         local_type = btf__type_by_id(local_btf, local_id);
>         targ_type = btf__type_by_id(targ_btf, targ_id);
> -       if (btf_kind(local_type) != btf_kind(targ_type))
> -               return 0;
> +       if (btf_kind(local_type) != btf_kind(targ_type)) {
> +               if (!btf_is_enum_enum64(local_type, targ_type))
> +                       return 0;
> +       }
>
>  recur:
>         depth--;
> @@ -5542,8 +5589,10 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
>         if (!local_type || !targ_type)
>                 return -EINVAL;
>
> -       if (btf_kind(local_type) != btf_kind(targ_type))
> -               return 0;
> +       if (btf_kind(local_type) != btf_kind(targ_type)) {
> +               if (!btf_is_enum_enum64(local_type, targ_type))
> +                       return 0;
> +       }

and reuse it in many places like here and above

>
>         switch (btf_kind(local_type)) {
>         case BTF_KIND_UNKN:
> @@ -5551,6 +5600,7 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
>         case BTF_KIND_UNION:
>         case BTF_KIND_ENUM:
>         case BTF_KIND_FWD:
> +       case BTF_KIND_ENUM64:
>                 return 1;
>         case BTF_KIND_INT:
>                 /* just reject deprecated bitfield-like integers; all other
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index b5bc84039407..acde13bd48c8 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -448,6 +448,10 @@ LIBBPF_0.8.0 {
>                 bpf_object__open_subskeleton;
>                 bpf_program__attach_kprobe_multi_opts;
>                 bpf_program__attach_usdt;
> +               btf__add_enum32;
> +               btf__add_enum32_value;
> +               btf__add_enum64;
> +               btf__add_enum64_value;
>                 libbpf_register_prog_handler;
>                 libbpf_unregister_prog_handler;
>  } LIBBPF_0.7.0;
> diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
> index 4abdbe2fea9d..10c16acfa8ae 100644
> --- a/tools/lib/bpf/libbpf_internal.h
> +++ b/tools/lib/bpf/libbpf_internal.h
> @@ -351,6 +351,8 @@ enum kern_feature_id {
>         FEAT_MEMCG_ACCOUNT,
>         /* BPF cookie (bpf_get_attach_cookie() BPF helper) support */
>         FEAT_BPF_COOKIE,
> +       /* BTF_KIND_ENUM64 support and BTF_KIND_ENUM kflag support */
> +       FEAT_BTF_ENUM64,
>         __FEAT_CNT,
>  };
>
> diff --git a/tools/lib/bpf/linker.c b/tools/lib/bpf/linker.c
> index 9aa016fb55aa..1e1ef3302921 100644
> --- a/tools/lib/bpf/linker.c
> +++ b/tools/lib/bpf/linker.c
> @@ -1343,6 +1343,7 @@ static bool glob_sym_btf_matches(const char *sym_name, bool exact,
>         case BTF_KIND_FWD:
>         case BTF_KIND_FUNC:
>         case BTF_KIND_VAR:
> +       case BTF_KIND_ENUM64:
>                 n1 = btf__str_by_offset(btf1, t1->name_off);
>                 n2 = btf__str_by_offset(btf2, t2->name_off);
>                 if (strcmp(n1, n2) != 0) {
> @@ -1358,6 +1359,7 @@ static bool glob_sym_btf_matches(const char *sym_name, bool exact,
>         switch (btf_kind(t1)) {
>         case BTF_KIND_UNKN: /* void */
>         case BTF_KIND_FWD:
> +       case BTF_KIND_ENUM64:

this should be lower, along with BTF_KIND_ENUM (btw, maybe keep it
next to BTF_KIND_ENUM64 in switches like this, e.g. in the one right
above in the patch)

>                 return true;
>         case BTF_KIND_INT:
>         case BTF_KIND_FLOAT:
> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
> index f25ffd03c3b1..1e751400427b 100644
> --- a/tools/lib/bpf/relo_core.c
> +++ b/tools/lib/bpf/relo_core.c
> @@ -231,11 +231,15 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
>         spec->len++;
>
>         if (core_relo_is_enumval_based(relo->kind)) {
> -               if (!btf_is_enum(t) || spec->raw_len > 1 || access_idx >= btf_vlen(t))
> +               if (!(btf_is_enum(t) || btf_is_enum64(t)) ||
> +                   spec->raw_len > 1 || access_idx >= btf_vlen(t))
>                         return -EINVAL;
>
>                 /* record enumerator name in a first accessor */
> -               acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off);
> +               if (btf_is_enum(t))
> +                       acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off);
> +               else
> +                       acc->name = btf__name_by_offset(btf, btf_enum64(t)[access_idx].name_off);

mild nit: it seems like extracting name_off into a variable (based on
btf_is_enum(t)) would be a bit cleaner, then just one
btf__name_by_offset() call with that name_off?

>                 return 0;
>         }
>
> @@ -340,15 +344,19 @@ static int bpf_core_fields_are_compat(const struct btf *local_btf,
>
>         if (btf_is_composite(local_type) && btf_is_composite(targ_type))
>                 return 1;
> -       if (btf_kind(local_type) != btf_kind(targ_type))
> -               return 0;
> +       if (btf_kind(local_type) != btf_kind(targ_type)) {
> +               if (btf_is_enum(local_type) && btf_is_enum64(targ_type)) ;
> +               else if (btf_is_enum64(local_type) && btf_is_enum(targ_type)) ;
> +               else return 0;
> +       }

use proposed btf_kinds_are_compat() here?

>
>         switch (btf_kind(local_type)) {
>         case BTF_KIND_PTR:
>         case BTF_KIND_FLOAT:
>                 return 1;
>         case BTF_KIND_FWD:
> -       case BTF_KIND_ENUM: {
> +       case BTF_KIND_ENUM:
> +       case BTF_KIND_ENUM64: {
>                 const char *local_name, *targ_name;
>                 size_t local_len, targ_len;
>
> @@ -494,29 +502,48 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
>
>         if (core_relo_is_enumval_based(local_spec->relo_kind)) {
>                 size_t local_essent_len, targ_essent_len;
> +               const struct btf_enum64 *e64;
>                 const struct btf_enum *e;
>                 const char *targ_name;
>
>                 /* has to resolve to an enum */
>                 targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id, &targ_id);
> -               if (!btf_is_enum(targ_type))
> +               if (!btf_is_enum(targ_type) && !btf_is_enum64(targ_type))
>                         return 0;
>
>                 local_essent_len = bpf_core_essential_name_len(local_acc->name);
>
> -               for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) {
> -                       targ_name = btf__name_by_offset(targ_spec->btf, e->name_off);
> -                       targ_essent_len = bpf_core_essential_name_len(targ_name);
> -                       if (targ_essent_len != local_essent_len)
> -                               continue;
> -                       if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {


so idea here is to find enumerator with matching name and record its
name and position, let's extract that part of the logic into a helper
and keep the targ_acc/targ_spec initialization in one piece. It will
be easier to follow the intent and less opportunity to get out of
sync.

> -                               targ_acc->type_id = targ_id;
> -                               targ_acc->idx = i;
> -                               targ_acc->name = targ_name;
> -                               targ_spec->len++;
> -                               targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
> -                               targ_spec->raw_len++;
> -                               return 1;
> +               if (btf_is_enum(targ_type)) {
> +                       for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) {
> +                               targ_name = btf__name_by_offset(targ_spec->btf, e->name_off);
> +                               targ_essent_len = bpf_core_essential_name_len(targ_name);
> +                               if (targ_essent_len != local_essent_len)
> +                                       continue;
> +                               if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
> +                                       targ_acc->type_id = targ_id;
> +                                       targ_acc->idx = i;
> +                                       targ_acc->name = targ_name;
> +                                       targ_spec->len++;
> +                                       targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
> +                                       targ_spec->raw_len++;
> +                                       return 1;
> +                               }
> +                       }
> +               } else {
> +                       for (i = 0, e64 = btf_enum64(targ_type); i < btf_vlen(targ_type); i++, e64++) {
> +                               targ_name = btf__name_by_offset(targ_spec->btf, e64->name_off);
> +                               targ_essent_len = bpf_core_essential_name_len(targ_name);
> +                               if (targ_essent_len != local_essent_len)
> +                                       continue;
> +                               if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
> +                                       targ_acc->type_id = targ_id;
> +                                       targ_acc->idx = i;
> +                                       targ_acc->name = targ_name;
> +                                       targ_spec->len++;
> +                                       targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
> +                                       targ_spec->raw_len++;
> +                                       return 1;
> +                               }
>                         }
>                 }
>                 return 0;
> @@ -681,7 +708,7 @@ static int bpf_core_calc_field_relo(const char *prog_name,
>                 break;
>         case BPF_CORE_FIELD_SIGNED:
>                 /* enums will be assumed unsigned */
> -               *val = btf_is_enum(mt) ||
> +               *val = btf_is_enum(mt) || btf_is_enum64(mt) ||
>                        (btf_int_encoding(mt) & BTF_INT_SIGNED);
>                 if (validate)
>                         *validate = true; /* signedness is never ambiguous */
> @@ -753,6 +780,7 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
>                                       const struct bpf_core_spec *spec,
>                                       __u64 *val)
>  {
> +       const struct btf_enum64 *e64;
>         const struct btf_type *t;
>         const struct btf_enum *e;
>
> @@ -764,8 +792,13 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
>                 if (!spec)
>                         return -EUCLEAN; /* request instruction poisoning */
>                 t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
> -               e = btf_enum(t) + spec->spec[0].idx;
> -               *val = e->val;
> +               if (btf_is_enum(t)) {
> +                       e = btf_enum(t) + spec->spec[0].idx;
> +                       *val = e->val;
> +               } else {
> +                       e64 = btf_enum64(t) + spec->spec[0].idx;
> +                       *val = btf_enum64_value(e64);
> +               }

I think with sign bit we now have further complication: for 32-bit
enums we need to sign extend 32-bit values to s64 and then cast as
u64, no? Seems like a helper to abstract that is good to have here.
Otherwise relocating enum ABC { D = -1 } will produce invalid ldimm64
instruction, right?

Also keep in mind that you can use btf_enum()/btf_enum64() as an
array, so above you can write just as

*val = btf_is_enum(t)
    ? btf_enum(t)[spec->spec[0].idx]
    : btf_enum64(t)[spec->spec[0].idx];

But we need sign check and extension, so better to have a separate helper.

>                 break;
>         default:
>                 return -EOPNOTSUPP;
> @@ -1034,7 +1067,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>                 }
>
>                 insn[0].imm = new_val;
> -               insn[1].imm = 0; /* currently only 32-bit values are supported */
> +               insn[1].imm = new_val >> 32;

for 32-bit instructions (ALU/ALU32, etc) we need to make sure that
new_val fits in 32 bits. And we need to be careful about
signed/unsigned, because for signed case all-zero or all-one upper 32
bits are ok (sign extension). Can we know the expected signed/unsigned
operation from bpf_insn itself? We should be, right?

>                 pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>                          prog_name, relo_idx, insn_idx,
>                          (unsigned long long)imm, new_val);
> @@ -1056,6 +1089,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>   */
>  int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *spec)
>  {
> +       const struct btf_enum64 *e64;
>         const struct btf_type *t;
>         const struct btf_enum *e;
>         const char *s;
> @@ -1086,10 +1120,15 @@ int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *s
>
>         if (core_relo_is_enumval_based(spec->relo_kind)) {
>                 t = skip_mods_and_typedefs(spec->btf, type_id, NULL);
> -               e = btf_enum(t) + spec->raw_spec[0];
> -               s = btf__name_by_offset(spec->btf, e->name_off);
> -
> -               append_buf("::%s = %u", s, e->val);
> +               if (btf_is_enum(t)) {
> +                       e = btf_enum(t) + spec->raw_spec[0];
> +                       s = btf__name_by_offset(spec->btf, e->name_off);
> +                       append_buf("::%s = %u", s, e->val);
> +               } else {
> +                       e64 = btf_enum64(t) + spec->raw_spec[0];
> +                       s = btf__name_by_offset(spec->btf, e64->name_off);
> +                       append_buf("::%s = %llu", s, btf_enum64_value(e64));

%llu problem here again

> +               }
>                 return len;
>         }
>

[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 05/12] bpftool: Add btf enum64 support
  2022-05-01 19:00 ` [PATCH bpf-next 05/12] bpftool: " Yonghong Song
@ 2022-05-09 23:31   ` Andrii Nakryiko
  2022-05-10 22:43     ` Yonghong Song
  0 siblings, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 23:31 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>
> Add BTF_KIND_ENUM64 support.
> For example, the following enum is defined in uapi bpf.h.
>   $ cat core.c
>   enum A {
>         BPF_F_INDEX_MASK                = 0xffffffffULL,
>         BPF_F_CURRENT_CPU               = BPF_F_INDEX_MASK,
>         BPF_F_CTXLEN_MASK               = (0xfffffULL << 32),
>   } g;
> Compiled with
>   clang -target bpf -O2 -g -c core.c
> Using bpftool to dump types and generate format C file:
>   $ bpftool btf dump file core.o
>   ...
>   [1] ENUM64 'A' size=8 vlen=3
>         'BPF_F_INDEX_MASK' val=4294967295ULL
>         'BPF_F_CURRENT_CPU' val=4294967295ULL
>         'BPF_F_CTXLEN_MASK' val=4503595332403200ULL
>   $ bpftool btf dump file core.o format c
>   ...
>   enum A {
>         BPF_F_INDEX_MASK = 4294967295ULL,
>         BPF_F_CURRENT_CPU = 4294967295ULL,
>         BPF_F_CTXLEN_MASK = 4503595332403200ULL,

maybe we should have some heuristic that if the value is "big enough"
(e.g., larger than 1-128 millions) and is unsigned we should emit it
as hex?

>   };
>   ...
>
> The 64bit value is represented properly in BTF and C dump.
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---

just minor nits, LGTM

Acked-by: Andrii Nakryiko <andrii@kernel.org>

>  tools/bpf/bpftool/btf.c        | 47 ++++++++++++++++++++++++++++++++--
>  tools/bpf/bpftool/btf_dumper.c | 32 +++++++++++++++++++++++
>  tools/bpf/bpftool/gen.c        |  1 +
>  3 files changed, 78 insertions(+), 2 deletions(-)
>

[...]
> +       case BTF_KIND_ENUM64: {
> +               const struct btf_enum64 *v = (const void *)(t + 1);

can use btf_enum64() helper from libbpf?

> +               __u16 vlen = BTF_INFO_VLEN(t->info);

btf_vlen(t)

> +               int i;
> +
> +               if (json_output) {
> +                       jsonw_uint_field(w, "size", t->size);
> +                       jsonw_uint_field(w, "vlen", vlen);
> +                       jsonw_name(w, "values");
> +                       jsonw_start_array(w);
> +               } else {
> +                       printf(" size=%u vlen=%u", t->size, vlen);
> +               }
> +               for (i = 0; i < vlen; i++, v++) {
> +                       const char *name = btf_str(btf, v->name_off);
> +                       __u64 val = (__u64)v->hi32 << 32 | v->lo32;

() ?

> +
> +                       if (json_output) {
> +                               jsonw_start_object(w);
> +                               jsonw_string_field(w, "name", name);
> +                               if (btf_kflag(t))
> +                                       jsonw_uint_field(w, "val", val);
> +                               else
> +                                       jsonw_int_field(w, "val", val);
> +                               jsonw_end_object(w);
> +                       } else {
> +                               if (btf_kflag(t))
> +                                       printf("\n\t'%s' val=%lluULL", name, val);
> +                               else
> +                                       printf("\n\t'%s' val=%lldLL", name, val);
>                         }
>                 }
>                 if (json_output)
> diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c
> index f5dddf8ef404..f9f38384b9a6 100644
> --- a/tools/bpf/bpftool/btf_dumper.c
> +++ b/tools/bpf/bpftool/btf_dumper.c
> @@ -182,6 +182,35 @@ static int btf_dumper_enum(const struct btf_dumper *d,
>         return 0;
>  }
>
> +static int btf_dumper_enum64(const struct btf_dumper *d,
> +                            const struct btf_type *t,
> +                            const void *data)
> +{
> +       const struct btf_enum64 *enums = btf_enum64(t);
> +       __u32 hi32, lo32;
> +       __u64 value;
> +       __u16 i;
> +
> +       if (t->size != 8)
> +               return -EINVAL;

no need

> +
> +       value = *(__u64 *)data;
> +       hi32 = value >> 32;
> +       lo32 = (__u32)value;
> +

[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure
  2022-05-01 19:00 ` [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure Yonghong Song
  2022-05-09  2:21   ` Dave Marchevsky
@ 2022-05-09 23:34   ` Andrii Nakryiko
  2022-05-10 22:44     ` Yonghong Song
  1 sibling, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 23:34 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>
> The kflag is supported now for BTF_KIND_ENUM.
> So remove the test which tests verifier failure
> due to existence of kflag.
>
> With enum64 support in kernel and libbpf,
> selftest btf_dump/btf_dump failed with
> no-enum64 support llvm for the following
> enum definition:
>  enum e2 {
>         C = 100,
>         D = 4294967295,
>         E = 0,
>  };
>
> With the no-enum64 support llvm, the signedness is
> 'signed' by default, and D (4294967295 = 0xffffffff)
> will print as -1. With enum64 support llvm, the signedness
> is 'unsigned' and the value of D will print as 4294967295.
> To support both old and new compilers, this patch
> changed the value to 268435455 = 0xfffffff which works
> with both enum64 or non-enum64 support llvm.
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  tools/testing/selftests/bpf/prog_tests/btf.c  | 20 -------------------
>  .../bpf/progs/btf_dump_test_case_syntax.c     |  2 +-
>  2 files changed, 1 insertion(+), 21 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
> index ba5bde53d418..8e068e06b3e8 100644
> --- a/tools/testing/selftests/bpf/prog_tests/btf.c
> +++ b/tools/testing/selftests/bpf/prog_tests/btf.c
> @@ -2896,26 +2896,6 @@ static struct btf_raw_test raw_tests[] = {
>         .err_str = "Invalid btf_info kind_flag",
>  },
>
> -{
> -       .descr = "invalid enum kind_flag",
> -       .raw_types = {
> -               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
> -               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 1, 1), 4),  /* [2] */
> -               BTF_ENUM_ENC(NAME_TBD, 0),
> -               BTF_END_RAW,
> -       },
> -       BTF_STR_SEC("\0A"),
> -       .map_type = BPF_MAP_TYPE_ARRAY,
> -       .map_name = "enum_type_check_btf",
> -       .key_size = sizeof(int),
> -       .value_size = sizeof(int),
> -       .key_type_id = 1,
> -       .value_type_id = 1,
> -       .max_entries = 4,
> -       .btf_load_err = true,
> -       .err_str = "Invalid btf_info kind_flag",
> -},
> -
>  {
>         .descr = "valid fwd kind_flag",
>         .raw_types = {
> diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
> index 1c7105fcae3c..4068cea4be53 100644
> --- a/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
> +++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
> @@ -13,7 +13,7 @@ enum e1 {
>
>  enum e2 {
>         C = 100,
> -       D = 4294967295,
> +       D = 268435455,
>         E = 0,
>  };

can you please also add btf_dump tests for >32-bit enums at the same
time? Both signed and unsigned?


>
> --
> 2.30.2
>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 09/12] selftests/bpf: Test BTF_KIND_ENUM64 for deduplication
  2022-05-01 19:00 ` [PATCH bpf-next 09/12] selftests/bpf: Test BTF_KIND_ENUM64 for deduplication Yonghong Song
@ 2022-05-09 23:37   ` Andrii Nakryiko
  2022-05-10 22:44     ` Yonghong Song
  0 siblings, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 23:37 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---

Please add some commit message, however trivial.

Can you please add a simple test to validate that enum and enum64 are
not deduplicated against each other?


>  tools/testing/selftests/bpf/prog_tests/btf.c | 70 +++++++++++++++++++-
>  1 file changed, 68 insertions(+), 2 deletions(-)
>

[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 10/12] selftests/bpf: add a test for enum64 value relocation
  2022-05-01 19:00 ` [PATCH bpf-next 10/12] selftests/bpf: add a test for enum64 value relocation Yonghong Song
@ 2022-05-09 23:38   ` Andrii Nakryiko
  2022-05-10 22:45     ` Yonghong Song
  0 siblings, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-09 23:38 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Sun, May 1, 2022 at 12:01 PM Yonghong Song <yhs@fb.com> wrote:
>
> Add a test for enum64 value relocations.
>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---

Looks good, but can you also add some signed enums for testing?

>  .../selftests/bpf/prog_tests/core_reloc.c     | 43 +++++++++++++++
>  .../bpf/progs/btf__core_reloc_enum64val.c     |  3 ++
>  .../progs/btf__core_reloc_enum64val___diff.c  |  3 ++
>  .../btf__core_reloc_enum64val___err_missing.c |  3 ++
>  ...btf__core_reloc_enum64val___val3_missing.c |  3 ++
>  .../selftests/bpf/progs/core_reloc_types.h    | 47 ++++++++++++++++
>  .../bpf/progs/test_core_reloc_enum64val.c     | 53 +++++++++++++++++++
>  7 files changed, 155 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val.c
>  create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___diff.c
>  create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___err_missing.c
>  create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___val3_missing.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_enum64val.c
>

[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value
  2022-05-09  1:06   ` Dave Marchevsky
@ 2022-05-10 19:35     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 19:35 UTC (permalink / raw)
  To: Dave Marchevsky, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team



On 5/8/22 6:06 PM, Dave Marchevsky wrote:
> On 5/1/22 3:00 PM, Yonghong Song wrote:
>> Currently, the libbpf limits the relocation value to be 32bit
>> since all current relocations have such a limit. But with
>> BTF_KIND_ENUM64 support, the enum value could be 64bit.
>> So let us permit 64bit relocation value in libbpf.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   tools/lib/bpf/relo_core.c | 24 ++++++++++++------------
>>   tools/lib/bpf/relo_core.h |  4 ++--
>>   2 files changed, 14 insertions(+), 14 deletions(-)
> 
> [...]
> 
>> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
>> index ba4453dfd1ed..2ed94daabbe5 100644
>> --- a/tools/lib/bpf/relo_core.c
>> +++ b/tools/lib/bpf/relo_core.c
> 
> [...]
> 
> 
>> @@ -1035,7 +1035,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>   
>>   		insn[0].imm = new_val;
>>   		insn[1].imm = 0; /* currently only 32-bit values are supported */
>> -		pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n",
>> +		pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>>   			 prog_name, relo_idx, insn_idx,
>>   			 (unsigned long long)imm, new_val);
>>   		break;
> 
> Since new_val is 64bit now, should the insn[1].imm be set here, and the comment
> about 32-bit be removed?

The comment and setting of insn[1].imm are changed in patch #4. But yes, 
I can move the change here as well.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure
  2022-05-09  2:21   ` Dave Marchevsky
@ 2022-05-10 19:40     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 19:40 UTC (permalink / raw)
  To: Dave Marchevsky, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team



On 5/8/22 7:21 PM, Dave Marchevsky wrote:
> On 5/1/22 3:00 PM, Yonghong Song wrote:
>> The kflag is supported now for BTF_KIND_ENUM.
>> So remove the test which tests verifier failure
>> due to existence of kflag.
>>
>> With enum64 support in kernel and libbpf,
>> selftest btf_dump/btf_dump failed with
>> no-enum64 support llvm for the following
>> enum definition:
>>   enum e2 {
>>          C = 100,
>>          D = 4294967295,
>>          E = 0,
>>   };
>>
>> With the no-enum64 support llvm, the signedness is
>> 'signed' by default, and D (4294967295 = 0xffffffff)
>> will print as -1. With enum64 support llvm, the signedness
>> is 'unsigned' and the value of D will print as 4294967295.
> 
> To confirm my understanding, this signedness-by-default change is for _printing
> btf in c format only_ and doesn't correspond to any difference in interpretation
> , since all BTF enums before addition of new kflag were assumed to be signed,
> and new kflag's default value is signed as well.

Yes, the signedness is only used for printing in C format. For
values, the signedness is really depending on corresponding declaration
type or the context where the value is used.

In the next revision, I may change the default signedness to be unsigned
to cover common case like
    enum {
       A = 0,
       B, C, D, ...
    }

> 
>> To support both old and new compilers, this patch
>> changed the value to 268435455 = 0xfffffff which works
>> with both enum64 or non-enum64 support llvm.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
> 
> Aside from the general question, for changes in this patch:
> 
> Acked-by: Dave Marchevsky <davemarchevsky@fb.com>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 01/12] bpf: Add btf enum64 support
  2022-05-09 22:29   ` Andrii Nakryiko
@ 2022-05-10 22:06     ` Yonghong Song
  2022-05-10 23:18       ` Andrii Nakryiko
  0 siblings, 1 reply; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:06 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 3:29 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> 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 bytes followed by the common type:
> 
> you probably meant three fields, not bytes

correct.

> 
>>    struct bpf_enum64 {
>>      __u32 nume_off;
>>      __u32 hi32;
>>      __u32 lo32;
> 
> I'd like to nitpick on name here, as hi/lo of what? Maybe val_hi32 and
> val_lo32? Can we also reverse the order here? For x86 you'll be able
> to use &lo32 to get value directly if you really want, without a local
> copy. It also just logically seems better to have something low first,
> then high next.

I can go with val_hi32, val_lo32 and put val_lo32 before val_hi32.
I don't have any preference for the ordering of these two fields.

> 
> 
>>    };
>> 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 hi32 and 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, print 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 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 value size of BTF_KIND_ENUM64 is encoded to 8 to represent its intent.
>> The compiler ([1]) and pahole will generate BTF_KIND_ENUM64 only if the value has
>> to be represented with 64 bits.
>>
>>    [1] https://reviews.llvm.org/D124641
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   include/linux/btf.h            |  18 ++++-
>>   include/uapi/linux/btf.h       |  17 ++++-
>>   kernel/bpf/btf.c               | 132 ++++++++++++++++++++++++++++++---
>>   tools/include/uapi/linux/btf.h |  17 ++++-
>>   4 files changed, 168 insertions(+), 16 deletions(-)
>>
>> diff --git a/include/linux/btf.h b/include/linux/btf.h
>> index 2611cea2c2b6..280c33c9414a 100644
>> --- a/include/linux/btf.h
>> +++ b/include/linux/btf.h
>> @@ -174,7 +174,8 @@ static inline bool btf_type_is_small_int(const struct btf_type *t)
>>
>>   static inline bool btf_type_is_enum(const struct btf_type *t)
>>   {
>> -       return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM;
>> +       return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM ||
>> +              BTF_INFO_KIND(t->info) == BTF_KIND_ENUM64;
>>   }
> 
> a bit hesitant about this change, there is no helper to check for ENUM
> vs ENUM64. Inside the kernel this change seems to be correct as we
> don't care, but for libbpf I'd probably keep btf_is_enum() unchanged
> (you can't really work with them in the same generic way in
> user-space, as their memory layout is very different, so it's better
> not to generalize them unnecessarily)

Let me introduce a new helper called
btf_type_is_any_enum(...) to check both
BTF_KIND_ENUM or BTF_KIND_ENUM64.

> 
>>
>>   static inline bool str_is_empty(const char *s)
>> @@ -192,6 +193,16 @@ static inline bool btf_is_enum(const struct btf_type *t)
>>          return btf_kind(t) == BTF_KIND_ENUM;
>>   }
>>
>> +static inline bool btf_is_enum64(const struct btf_type *t)
>> +{
>> +       return btf_kind(t) == BTF_KIND_ENUM64;
>> +}
>> +
>> +static inline u64 btf_enum64_value(const struct btf_enum64 *e)
>> +{
>> +       return (u64)e->hi32 << 32 | e->lo32;
> 
> this might be correct but () around bit shift would make it more obvious

I can do this.

> 
>> +}
>> +
>>   static inline bool btf_is_composite(const struct btf_type *t)
>>   {
>>          u16 kind = btf_kind(t);
>> @@ -332,6 +343,11 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t)
>>          return (struct btf_enum *)(t + 1);
>>   }
>>
>> +static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
>> +{
>> +       return (struct btf_enum64 *)(t + 1);
>> +}
>> +
>>   static inline const struct btf_var_secinfo *btf_type_var_secinfo(
>>                  const struct btf_type *t)
>>   {
>> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
>> index a9162a6c0284..2aac226a27b2 100644
>> --- a/include/uapi/linux/btf.h
>> +++ b/include/uapi/linux/btf.h
>> @@ -36,10 +36,10 @@ struct btf_type {
>>           * bits 24-28: kind (e.g. int, ptr, array...etc)
>>           * bits 29-30: unused
>>           * bit     31: kind_flag, currently used by
>> -        *             struct, union and fwd
>> +        *             struct, union, enum, fwd and enum64
> 
> see comment below on kflag for enum itself, but reading this I
> realized that we don't really describe the meaning of kind_flag for
> different kinds. Should it be done here?

We have detailed description in Documentation/bpf/btf.rst.
Hopefully it will be enough if people wants to understand
what kflag means for each kind.


> 
>>           */
>>          __u32 info;
>> -       /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
>> +       /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
>>           * "size" tells the size of the type it is describing.
>>           *
>>           * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
>> @@ -63,7 +63,7 @@ enum {
>>          BTF_KIND_ARRAY          = 3,    /* Array        */
>>          BTF_KIND_STRUCT         = 4,    /* Struct       */
>>          BTF_KIND_UNION          = 5,    /* Union        */
>> -       BTF_KIND_ENUM           = 6,    /* Enumeration  */
>> +       BTF_KIND_ENUM           = 6,    /* Enumeration for int/unsigned int values */
> 
> nit: "Enumeration for up to 32-bit values" ?

This should work.

> 
>>          BTF_KIND_FWD            = 7,    /* Forward      */
>>          BTF_KIND_TYPEDEF        = 8,    /* Typedef      */
>>          BTF_KIND_VOLATILE       = 9,    /* Volatile     */
>> @@ -76,6 +76,7 @@ enum {
>>          BTF_KIND_FLOAT          = 16,   /* Floating point       */
>>          BTF_KIND_DECL_TAG       = 17,   /* Decl Tag */
>>          BTF_KIND_TYPE_TAG       = 18,   /* Type Tag */
>> +       BTF_KIND_ENUM64         = 19,   /* Enumeration for long/unsigned long values */
> 
> and then "for 64-bit values" (or maybe up to 64 bit values, but in
> practice we won't do that, right?)

We can do "up to 64-bit values". In practice, from llvm and pahole,
we will only encode 64-bit values in ENUM64.

> 
>>
>>          NR_BTF_KINDS,
>>          BTF_KIND_MAX            = NR_BTF_KINDS - 1,
>> @@ -186,4 +187,14 @@ struct btf_decl_tag {
>>          __s32   component_idx;
>>   };
>>
> 
> [...]
> 
>> @@ -3716,7 +3721,8 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
>>
>>                  if (env->log.level == BPF_LOG_KERNEL)
>>                          continue;
>> -               btf_verifier_log(env, "\t%s val=%d\n",
>> +               fmt_str = btf_type_kflag(t) ? "\t%s val=%u\n" : "\t%s val=%d\n";
>> +               btf_verifier_log(env, fmt_str,
>>                                   __btf_name_by_offset(btf, enums[i].name_off),
>>                                   enums[i].val);
>>          }
>> @@ -3757,7 +3763,10 @@ static void btf_enum_show(const struct btf *btf, const struct btf_type *t,
>>                  return;
>>          }
>>
>> -       btf_show_type_value(show, "%d", v);
>> +       if (btf_type_kflag(t))
> 
> libbpf's assumption right now and most common case is unsigned enum,
> so it seems more desirable to have kflag == 0 mean unsigned, with
> kflag == 1 being signed. It will make most existing enum definitions
> not change but also make it easy for libbpf's btf_dumper to use kflag
> if it's set, but otherwise have the same unsigned printing whether
> enum is really unsigned or Clang is too old to emit the kflag for
> enum. WDYT?

Right, libbpf assumption is unsigned enum and the kernel prints as 
signed. I agree that default unsigned should cover more cases.
Will change that in the next revision.

> 
>> +               btf_show_type_value(show, "%u", v);
>> +       else
>> +               btf_show_type_value(show, "%d", v);
> 
> you didn't got with ternary operator for fmt string selector like in
> previous case? I have mild preference for keeping it consistent (and
> keeping btf_type_kflag(t) ? "fmt1" : "fmt2" inline)

The reason I didn't do it is the line is a little long.
But I can do it.

> 
>>          btf_show_end_type(show);
>>   }
>>
>> @@ -3770,6 +3779,109 @@ static struct btf_kind_operations enum_ops = {
>>          .show = btf_enum_show,
>>   };
>>
>> +static s32 btf_enum64_check_meta(struct btf_verifier_env *env,
>> +                                const struct btf_type *t,
>> +                                u32 meta_left)
>> +{
>> +       const struct btf_enum64 *enums = btf_type_enum64(t);
>> +       struct btf *btf = env->btf;
>> +       const char *fmt_str;
>> +       u16 i, nr_enums;
>> +       u32 meta_needed;
>> +
>> +       nr_enums = btf_type_vlen(t);
>> +       meta_needed = nr_enums * sizeof(*enums);
>> +
>> +       if (meta_left < meta_needed) {
>> +               btf_verifier_log_basic(env, t,
>> +                                      "meta_left:%u meta_needed:%u",
>> +                                      meta_left, meta_needed);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (t->size != 8) {
> 
> technically there is nothing wrong with using enum64 for smaller
> sizes, right? Any particular reason to prevent this? We can just
> define that 64-bit value is sign-extended if enum is signed and has
> size < 8?

My original idea is to support 64-bit enum only for ENUM64 kind.
But it is certainly possible to encode 32-bit enums as well for
ENUM64. So I will remove this restriction.

The dwarf only generates sizes 4 (for up-to 32 bit values)
and 8 (for 64 bit values). But BTF_KIND_ENUM supports 1/2/4/8
sizes, so BTF_KIND_ENUM64 will also support 1/2/4/8 sizes.

> 
>> +               btf_verifier_log_type(env, t, "Unexpected size");
>> +               return -EINVAL;
>> +       }
>> +
>> +       /* enum type either no name or a valid one */
>> +       if (t->name_off &&
>> +           !btf_name_valid_identifier(env->btf, t->name_off)) {
>> +               btf_verifier_log_type(env, t, "Invalid name");
>> +               return -EINVAL;
>> +       }
>> +
> 
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation
  2022-05-09 22:37   ` Andrii Nakryiko
@ 2022-05-10 22:11     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:11 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 3:37 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> Currently, the 64bit relocation value in the instruction
>> is computed as follows:
>>    __u64 imm = insn[0].imm + ((__u64)insn[1].imm << 32)
>>
>> Suppose insn[0].imm = -1 (0xffffffff) and insn[1].imm = 1.
>> With the above computation, insn[0].imm will first sign-extend
>> to 64bit -1 (0xffffffffFFFFFFFF) and then add 0x1FFFFFFFF,
>> producing incorrect value 0xFFFFFFFF. The correct value
>> should be 0x1FFFFFFFF.
>>
>> Changing insn[0].imm to __u32 first will prevent 64bit sign
>> extension and fix the issue.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   tools/lib/bpf/relo_core.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
>> index 2ed94daabbe5..f25ffd03c3b1 100644
>> --- a/tools/lib/bpf/relo_core.c
>> +++ b/tools/lib/bpf/relo_core.c
>> @@ -1024,7 +1024,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>                          return -EINVAL;
>>                  }
>>
>> -               imm = insn[0].imm + ((__u64)insn[1].imm << 32);
>> +               imm = (__u32)insn[0].imm + ((__u64)insn[1].imm << 32);
> 
> great catch, it should also probably be written as | instead of + operation?

The '|' also works. I used '|' in other places, so will change to use 
'|' as well.

> 
>>                  if (res->validate && imm != orig_val) {
>>                          pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
>>                                  prog_name, relo_idx,
>> --
>> 2.30.2
>>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value
  2022-05-09 22:37   ` Andrii Nakryiko
@ 2022-05-10 22:14     ` Yonghong Song
  2022-05-10 23:19       ` Andrii Nakryiko
  0 siblings, 1 reply; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:14 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 3:37 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> Currently, the libbpf limits the relocation value to be 32bit
>> since all current relocations have such a limit. But with
>> BTF_KIND_ENUM64 support, the enum value could be 64bit.
>> So let us permit 64bit relocation value in libbpf.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   tools/lib/bpf/relo_core.c | 24 ++++++++++++------------
>>   tools/lib/bpf/relo_core.h |  4 ++--
>>   2 files changed, 14 insertions(+), 14 deletions(-)
>>
> 
> [...]
> 
>> @@ -929,7 +929,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>                          int insn_idx, const struct bpf_core_relo *relo,
>>                          int relo_idx, const struct bpf_core_relo_res *res)
>>   {
>> -       __u32 orig_val, new_val;
>> +       __u64 orig_val, new_val;
>>          __u8 class;
>>
>>          class = BPF_CLASS(insn->code);
>> @@ -954,14 +954,14 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>                  if (BPF_SRC(insn->code) != BPF_K)
>>                          return -EINVAL;
>>                  if (res->validate && insn->imm != orig_val) {
>> -                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %u -> %u\n",
>> +                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %llu -> %llu\n",
>>                                  prog_name, relo_idx,
>>                                  insn_idx, insn->imm, orig_val, new_val);
> 
> %llu is not valid formatter for __u64 on all architectures, please add
> explicit (unsigned long long) cast

Okay, will do.

> 
> but also in general for non-ldimm64 instructions we need to check that
> new value fits in 32 bits

The real 64-bit value can only be retrieved for ldimm64 insn, so I 
suppose it should be fine here. But let me double check.

> 
> [...]
> 
>> @@ -1026,7 +1026,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>
>>                  imm = insn[0].imm + ((__u64)insn[1].imm << 32);
>>                  if (res->validate && imm != orig_val) {
>> -                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %u -> %u\n",
>> +                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
>>                                  prog_name, relo_idx,
>>                                  insn_idx, (unsigned long long)imm,
>>                                  orig_val, new_val);
>> @@ -1035,7 +1035,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>
>>                  insn[0].imm = new_val;
>>                  insn[1].imm = 0; /* currently only 32-bit values are supported */
> 
> as Dave mentioned, not anymore, so this should take higher 32-bit of new_val

Will do.

> 
> 
>> -               pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n",
>> +               pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>>                           prog_name, relo_idx, insn_idx,
>>                           (unsigned long long)imm, new_val);
>>                  break;
>> @@ -1261,7 +1261,7 @@ int bpf_core_calc_relo_insn(const char *prog_name,
>>                           * decision and value, otherwise it's dangerous to
>>                           * proceed due to ambiguity
>>                           */
>> -                       pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n",
>> +                       pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %llu != %s %llu\n",
>>                                  prog_name, relo_idx,
>>                                  cand_res.poison ? "failure" : "success", cand_res.new_val,
>>                                  targ_res->poison ? "failure" : "success", targ_res->new_val);
[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-09 23:25   ` Andrii Nakryiko
@ 2022-05-10 22:40     ` Yonghong Song
  2022-05-10 23:02       ` Yonghong Song
  2022-05-10 23:38       ` Andrii Nakryiko
  0 siblings, 2 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:40 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 4:25 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
>> btf__add_enum_value() and introduced the following new APIs
>>    btf__add_enum32()
>>    btf__add_enum32_value()
>>    btf__add_enum64()
>>    btf__add_enum64_value()
>> due to new kind and introduction of kflag.
>>
>> To support old kernel with enum64, the sanitization is
>> added to replace BTF_KIND_ENUM64 with a bunch of
>> pointer-to-void types.
>>
>> The enum64 value relocation is also supported. The enum64
>> forward resolution, with enum type as forward declaration
>> and enum64 as the actual definition, is also supported.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
>>   tools/lib/bpf/btf.h                           |  21 ++
>>   tools/lib/bpf/btf_dump.c                      |  94 ++++++--
>>   tools/lib/bpf/libbpf.c                        |  64 ++++-
>>   tools/lib/bpf/libbpf.map                      |   4 +
>>   tools/lib/bpf/libbpf_internal.h               |   2 +
>>   tools/lib/bpf/linker.c                        |   2 +
>>   tools/lib/bpf/relo_core.c                     |  93 ++++---
>>   .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
>>   .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
>>   10 files changed, 450 insertions(+), 72 deletions(-)
>>
> 
> This is a huge patch touching very different and logically independent
> parts of libbpf. Please split it into smaller parts, e.g.:
>    - libbpf.c changes (sanitization and kcfg);
>    - BTF public API helpers (btf_is_enum64, btf__add_enum64);
>    - btf_dump changes;
>    - btf__dedup changes;
>    - CO-RE relocations.
> 
> It will be easier to discuss each in a separate patch.

okay.

> 
> [...]
> 
>> +static int btf_add_enum_common(struct btf *btf, const char *name,
>> +                              bool is_unsigned, __u8 kind, __u32 tsize)
>> +{
>> +       struct btf_type *t;
>> +       int sz, name_off = 0;
>> +
>> +       if (btf_ensure_modifiable(btf))
>> +               return libbpf_err(-ENOMEM);
>> +
>> +       sz = sizeof(struct btf_type);
>> +       t = btf_add_type_mem(btf, sz);
>> +       if (!t)
>> +               return libbpf_err(-ENOMEM);
>> +
>> +       if (name && name[0]) {
>> +               name_off = btf__add_str(btf, name);
>> +               if (name_off < 0)
>> +                       return name_off;
>> +       }
>> +
>> +       /* start out with vlen=0; it will be adjusted when adding enum values */
>> +       t->name_off = name_off;
>> +       t->info = btf_type_info(kind, 0, is_unsigned);
> 
> As mentioned on another patch, I think unsigned should be default
> (despite UAPI having s32 as type for enum's val), because that's what
> we assume in practice. It makes backwards compatibility easier in more
> than one place

okay.

> 
> 
>> +       t->size = tsize;
>> +
>> +       return btf_commit_type(btf, sz);
>> +}
>> +
>> +/*
>> + * Append new BTF_KIND_ENUM type with:
>> + *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
>> + *   - *is_unsigned* - whether the enum values are unsigned or not;
>> + *
>> + * Enum initially has no enum values in it (and corresponds to enum forward
>> + * declaration). Enumerator values can be added by btf__add_enum64_value()
>> + * immediately after btf__add_enum() succeeds.
>> + *
>> + * Returns:
>> + *   - >0, type ID of newly added BTF type;
>> + *   - <0, on error.
>> + */
>> +int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned)
> 
> given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
> btf__add_enum()/btf__add_enum_value() and not deprecate anything.
> ENUM64 can be thought about as more of a special case, so I think it's
> ok.

The current btf__add_enum api:
LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32 
bytes_sz);

The issue is it doesn't have signedness parameter. if the user input
is
    enum { A = -1, B = 0, C = 1 };
the actual printout btf format will be
    enum { A 4294967295, B = 0, C = 1}
does not match the original source.

> 
>> +{
>> +       return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM, 4);
>> +}
>> +
> 
> [...]
> 
>>   /*
>>    * Append new BTF_KIND_FWD type with:
>>    *   - *name*, non-empty/non-NULL name;
>> @@ -2242,7 +2419,7 @@ int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind)
>>                  /* enum forward in BTF currently is just an enum with no enum
>>                   * values; we also assume a standard 4-byte size for it
>>                   */
>> -               return btf__add_enum(btf, name, sizeof(int));
>> +               return btf__add_enum32(btf, name, false);
>>          default:
>>                  return libbpf_err(-EINVAL);
>>          }
>> @@ -3485,6 +3662,7 @@ static long btf_hash_enum(struct btf_type *t)
>>   /* Check structural equality of two ENUMs. */
>>   static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
>>   {
>> +       const struct btf_enum64 *n1, *n2;
>>          const struct btf_enum *m1, *m2;
>>          __u16 vlen;
>>          int i;
>> @@ -3493,26 +3671,40 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
> 
> they are so different that I think separate btf_equal_enum64() and
> similar approaches everywhere makes sense. Yes, it's enum, but in
> practice two very different kinds and should be handled differently

okay.

> 
>>                  return false;
>>
>>          vlen = btf_vlen(t1);
>> -       m1 = btf_enum(t1);
>> -       m2 = btf_enum(t2);
>> -       for (i = 0; i < vlen; i++) {
>> -               if (m1->name_off != m2->name_off || m1->val != m2->val)
>> -                       return false;
>> -               m1++;
>> -               m2++;
> 
> [...]
> 
>>   enum btf_fwd_kind {
>>          BTF_FWD_STRUCT = 0,
>> @@ -454,6 +460,11 @@ static inline bool btf_is_enum(const struct btf_type *t)
>>          return btf_kind(t) == BTF_KIND_ENUM;
>>   }
>>
>> +static inline bool btf_is_enum64(const struct btf_type *t)
>> +{
>> +       return btf_kind(t) == BTF_KIND_ENUM64;
> 
> please also add #define BTF_KIND_ENUM64 19 to avoid user breakage if
> they don't have very latest kernel UAPI header, same as we did for
> TYPE_TAG and others

okay.

> 
>> +}
>> +
>>   static inline bool btf_is_fwd(const struct btf_type *t)
>>   {
>>          return btf_kind(t) == BTF_KIND_FWD;
> 
> [...]
> 
>> @@ -993,8 +996,11 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
>>                                     const struct btf_type *t,
>>                                     int lvl)
>>   {
>> -       const struct btf_enum *v = btf_enum(t);
>> +       bool is_unsigned = btf_kflag(t);
>> +       const struct btf_enum64 *v64;
>> +       const struct btf_enum *v;
>>          __u16 vlen = btf_vlen(t);
>> +       const char *fmt_str;
>>          const char *name;
>>          size_t dup_cnt;
>>          int i;
>> @@ -1005,18 +1011,47 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
>>
>>          if (vlen) {
>>                  btf_dump_printf(d, " {");
>> -               for (i = 0; i < vlen; i++, v++) {
>> -                       name = btf_name_of(d, v->name_off);
>> -                       /* enumerators share namespace with typedef idents */
>> -                       dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
>> -                       if (dup_cnt > 1) {
>> -                               btf_dump_printf(d, "\n%s%s___%zu = %u,",
>> -                                               pfx(lvl + 1), name, dup_cnt,
>> -                                               (__u32)v->val);
>> -                       } else {
>> -                               btf_dump_printf(d, "\n%s%s = %u,",
>> -                                               pfx(lvl + 1), name,
>> -                                               (__u32)v->val);
>> +               if (btf_is_enum(t)) {
>> +                       v = btf_enum(t);
>> +                       for (i = 0; i < vlen; i++, v++) {
>> +                               name = btf_name_of(d, v->name_off);
>> +                               /* enumerators share namespace with typedef idents */
>> +                               dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
>> +                               if (dup_cnt > 1) {
>> +                                       fmt_str = is_unsigned ? "\n%s%s___%zu = %u,"
>> +                                                             : "\n%s%s___%zu = %d,";
>> +                                       btf_dump_printf(d, fmt_str,
>> +                                                       pfx(lvl + 1), name, dup_cnt,
>> +                                                       v->val);
>> +                               } else {
>> +                                       fmt_str = is_unsigned ? "\n%s%s = %u,"
>> +                                                             : "\n%s%s = %d,";
>> +                                       btf_dump_printf(d, fmt_str,
>> +                                                       pfx(lvl + 1), name,
>> +                                                       v->val);
>> +                               }
>> +                       }
>> +               } else {
>> +                       v64 = btf_enum64(t);
>> +                       for (i = 0; i < vlen; i++, v64++) {
>> +                               __u64 val = btf_enum64_value(v64);
>> +
>> +                               name = btf_name_of(d, v64->name_off);
>> +                               /* enumerators share namespace with typedef idents */
>> +                               dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
>> +                               if (dup_cnt > 1) {
>> +                                       fmt_str = is_unsigned ? "\n%s%s___%zu = %lluULL,"
>> +                                                             : "\n%s%s___%zu = %lldLL,";
>> +                                       btf_dump_printf(d, fmt_str,
>> +                                                       pfx(lvl + 1), name, dup_cnt,
>> +                                                       val);
>> +                               } else {
>> +                                       fmt_str = is_unsigned ? "\n%s%s = %lluULL,"
>> +                                                             : "\n%s%s = %lldLL,";
>> +                                       btf_dump_printf(d, fmt_str,
>> +                                                       pfx(lvl + 1), name,
>> +                                                       val);
>> +                               }
>>                          }
> 
> yeah, let's just have btf_dump_emit_enum64_def(), there is very little
> that can be reused, I think it will be cleaning to keep enum and
> enum64 separate everywhere where we actually need to iterate
> enumerators and do something about them

okay.

> 
>>                  }
>>                  btf_dump_printf(d, "\n%s}", pfx(lvl));
>> @@ -1183,6 +1218,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
>>                  case BTF_KIND_UNION:
>>                  case BTF_KIND_TYPEDEF:
>>                  case BTF_KIND_FLOAT:
> 
> [...]
> 
>> -       btf_dump_type_values(d, "%d", value);
>> +               btf_dump_type_values(d, is_unsigned ? "%u" : "%d", value);
>> +       } else {
>> +               for (i = 0, e64 = btf_enum64(t); i < btf_vlen(t); i++, e64++) {
>> +                       if (value != btf_enum64_value(e64))
>> +                               continue;
>> +                       btf_dump_type_values(d, "%s", btf_name_of(d, e64->name_off));
>> +                       return 0;
>> +               }
>> +
>> +               btf_dump_type_values(d, is_unsigned ? "%lluULL" : "%lldLL", value);
>> +       }
> 
> ditto, also beware of %lld/%llu use with __u64/__s64, it gives
> compilation warnings without cast on some architectures

okay.

> 
>>          return 0;
>>   }
>>
> 
> [...]
> 
>> @@ -2717,6 +2720,17 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
>>                          /* replace TYPE_TAG with a CONST */
>>                          t->name_off = 0;
>>                          t->info = BTF_INFO_ENC(BTF_KIND_CONST, 0, 0);
>> +               } else if (!has_enum64 && btf_is_enum(t)) {
>> +                       /* clear the kflag */
>> +                       t->info &= 0x7fffffff;
> 
> please use btf_type_info() helper (defined in libbpf_internal.h) or
> just plain BTF_INFO_ENC() like all other cases around instead of
> hard-coding magic masks

okay.

> 
>> +               } else if (!has_enum64 && btf_is_enum64(t)) {
>> +                       /* replace ENUM64 with pointer->void's */
>> +                       vlen = btf_vlen(t);
>> +                       for (j = 0; j <= vlen; j++, t++) {
>> +                               t->name_off = 0;
>> +                               t->info = BTF_INFO_ENC(BTF_KIND_PTR, 0, 0);
>> +                               t->type = 0;
>> +                       }
> 
> I don't think we can replace each enumerator with a new kind, it
> breaks type ID numbering. struct btf_member has matching layout, so we
> can replace ENUM64 with UNION (easier to keep offsets as zeroes),
> WDYT?

Yes, my above approach won't work. I will replace it with UNION with
members be int/ptr types.

> 
>>                  }
>>          }
>>   }
>> @@ -3563,6 +3577,12 @@ static enum kcfg_type find_kcfg_type(const struct btf *btf, int id,
>>                  if (strcmp(name, "libbpf_tristate"))
>>                          return KCFG_UNKNOWN;
>>                  return KCFG_TRISTATE;
>> +       case BTF_KIND_ENUM64:
>> +               if (t->size != 8)
>> +                       return KCFG_UNKNOWN;
> 
> I think I don't like this t->size == 8 more and more. At some we'll
> decide it's ok and then we'll have to go and adjust everything again.
> It requires pretty much zero effort to support from the very beginning
> and makes tons of sense to allow that, let's allow it.

Will remove this.

> 
>> +               if (strcmp(name, "libbpf_tristate"))
>> +                       return KCFG_UNKNOWN;
>> +               return KCFG_TRISTATE;
>>          case BTF_KIND_ARRAY:
>>                  if (btf_array(t)->nelems == 0)
>>                          return KCFG_UNKNOWN;
>> @@ -4746,6 +4766,17 @@ static int probe_kern_bpf_cookie(void)
>>          return probe_fd(ret);
>>   }
>>
>> +static int probe_kern_btf_enum64(void)
>> +{
>> +       static const char strs[] = "\0enum64";
>> +       __u32 types[] = {
>> +               BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
>> +       };
>> +
>> +       return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
>> +                                            strs, sizeof(strs)));
>> +}
>> +
>>   enum kern_feature_result {
>>          FEAT_UNKNOWN = 0,
>>          FEAT_SUPPORTED = 1,
>> @@ -4811,6 +4842,9 @@ static struct kern_feature_desc {
>>          [FEAT_BPF_COOKIE] = {
>>                  "BPF cookie support", probe_kern_bpf_cookie,
>>          },
>> +       [FEAT_BTF_ENUM64] = {
>> +               "BTF_KIND_ENUM64 support", probe_kern_btf_enum64,
>> +       },
>>   };
>>
>>   bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
>> @@ -5296,6 +5330,15 @@ void bpf_core_free_cands(struct bpf_core_cand_list *cands)
>>          free(cands);
>>   }
>>
>> +static bool btf_is_enum_enum64(const struct btf_type *t1,
>> +                              const struct btf_type *t2) {
>> +       if (btf_is_enum(t1) && btf_is_enum64(t2))
>> +               return true;
>> +       if (btf_is_enum(t2) && btf_is_enum64(t1))
>> +               return true;
>> +       return false;
>> +}
>> +
> 
> maybe simplify and rename to
> 
> static bool btf_are_enums(...) {
>      return (btf_is_enum(t1) || btf_is_enum64(t1)) && (same for t2)?
> }

Right this can be simplified.

> 
>>   int bpf_core_add_cands(struct bpf_core_cand *local_cand,
>>                         size_t local_essent_len,
>>                         const struct btf *targ_btf,
>> @@ -5315,8 +5358,10 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
>>          n = btf__type_cnt(targ_btf);
>>          for (i = targ_start_id; i < n; i++) {
>>                  t = btf__type_by_id(targ_btf, i);
>> -               if (btf_kind(t) != btf_kind(local_t))
>> -                       continue;
>> +               if (btf_kind(t) != btf_kind(local_t)) {
>> +                       if (!btf_is_enum_enum64(t, local_t))
>> +                               continue;
>> +               }
> 
> let's extract this into a helper and call it btf_kinds_are_compat() or
> something along those lines?

okay.

> 
>>
>>                  targ_name = btf__name_by_offset(targ_btf, t->name_off);
>>                  if (str_is_empty(targ_name))
>> @@ -5529,8 +5574,10 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
>>          /* caller made sure that names match (ignoring flavor suffix) */
>>          local_type = btf__type_by_id(local_btf, local_id);
>>          targ_type = btf__type_by_id(targ_btf, targ_id);
>> -       if (btf_kind(local_type) != btf_kind(targ_type))
>> -               return 0;
>> +       if (btf_kind(local_type) != btf_kind(targ_type)) {
>> +               if (!btf_is_enum_enum64(local_type, targ_type))
>> +                       return 0;
>> +       }
>>
>>   recur:
>>          depth--;
>> @@ -5542,8 +5589,10 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
>>          if (!local_type || !targ_type)
>>                  return -EINVAL;
>>
>> -       if (btf_kind(local_type) != btf_kind(targ_type))
>> -               return 0;
>> +       if (btf_kind(local_type) != btf_kind(targ_type)) {
>> +               if (!btf_is_enum_enum64(local_type, targ_type))
>> +                       return 0;
>> +       }
> 
> and reuse it in many places like here and above

ditto.

> 
>>
>>          switch (btf_kind(local_type)) {
>>          case BTF_KIND_UNKN:
>> @@ -5551,6 +5600,7 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
>>          case BTF_KIND_UNION:
>>          case BTF_KIND_ENUM:
>>          case BTF_KIND_FWD:
>> +       case BTF_KIND_ENUM64:
>>                  return 1;
>>          case BTF_KIND_INT:
>>                  /* just reject deprecated bitfield-like integers; all other
>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>> index b5bc84039407..acde13bd48c8 100644
>> --- a/tools/lib/bpf/libbpf.map
>> +++ b/tools/lib/bpf/libbpf.map
>> @@ -448,6 +448,10 @@ LIBBPF_0.8.0 {
>>                  bpf_object__open_subskeleton;
>>                  bpf_program__attach_kprobe_multi_opts;
>>                  bpf_program__attach_usdt;
>> +               btf__add_enum32;
>> +               btf__add_enum32_value;
>> +               btf__add_enum64;
>> +               btf__add_enum64_value;
>>                  libbpf_register_prog_handler;
>>                  libbpf_unregister_prog_handler;
>>   } LIBBPF_0.7.0;
>> diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
>> index 4abdbe2fea9d..10c16acfa8ae 100644
>> --- a/tools/lib/bpf/libbpf_internal.h
>> +++ b/tools/lib/bpf/libbpf_internal.h
>> @@ -351,6 +351,8 @@ enum kern_feature_id {
>>          FEAT_MEMCG_ACCOUNT,
>>          /* BPF cookie (bpf_get_attach_cookie() BPF helper) support */
>>          FEAT_BPF_COOKIE,
>> +       /* BTF_KIND_ENUM64 support and BTF_KIND_ENUM kflag support */
>> +       FEAT_BTF_ENUM64,
>>          __FEAT_CNT,
>>   };
>>
>> diff --git a/tools/lib/bpf/linker.c b/tools/lib/bpf/linker.c
>> index 9aa016fb55aa..1e1ef3302921 100644
>> --- a/tools/lib/bpf/linker.c
>> +++ b/tools/lib/bpf/linker.c
>> @@ -1343,6 +1343,7 @@ static bool glob_sym_btf_matches(const char *sym_name, bool exact,
>>          case BTF_KIND_FWD:
>>          case BTF_KIND_FUNC:
>>          case BTF_KIND_VAR:
>> +       case BTF_KIND_ENUM64:
>>                  n1 = btf__str_by_offset(btf1, t1->name_off);
>>                  n2 = btf__str_by_offset(btf2, t2->name_off);
>>                  if (strcmp(n1, n2) != 0) {
>> @@ -1358,6 +1359,7 @@ static bool glob_sym_btf_matches(const char *sym_name, bool exact,
>>          switch (btf_kind(t1)) {
>>          case BTF_KIND_UNKN: /* void */
>>          case BTF_KIND_FWD:
>> +       case BTF_KIND_ENUM64:
> 
> this should be lower, along with BTF_KIND_ENUM (btw, maybe keep it
> next to BTF_KIND_ENUM64 in switches like this, e.g. in the one right
> above in the patch)

My mistake. Will fix.

> 
>>                  return true;
>>          case BTF_KIND_INT:
>>          case BTF_KIND_FLOAT:
>> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
>> index f25ffd03c3b1..1e751400427b 100644
>> --- a/tools/lib/bpf/relo_core.c
>> +++ b/tools/lib/bpf/relo_core.c
>> @@ -231,11 +231,15 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
>>          spec->len++;
>>
>>          if (core_relo_is_enumval_based(relo->kind)) {
>> -               if (!btf_is_enum(t) || spec->raw_len > 1 || access_idx >= btf_vlen(t))
>> +               if (!(btf_is_enum(t) || btf_is_enum64(t)) ||
>> +                   spec->raw_len > 1 || access_idx >= btf_vlen(t))
>>                          return -EINVAL;
>>
>>                  /* record enumerator name in a first accessor */
>> -               acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off);
>> +               if (btf_is_enum(t))
>> +                       acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off);
>> +               else
>> +                       acc->name = btf__name_by_offset(btf, btf_enum64(t)[access_idx].name_off);
> 
> mild nit: it seems like extracting name_off into a variable (based on
> btf_is_enum(t)) would be a bit cleaner, then just one
> btf__name_by_offset() call with that name_off?

Will do.

> 
>>                  return 0;
>>          }
>>
>> @@ -340,15 +344,19 @@ static int bpf_core_fields_are_compat(const struct btf *local_btf,
>>
>>          if (btf_is_composite(local_type) && btf_is_composite(targ_type))
>>                  return 1;
>> -       if (btf_kind(local_type) != btf_kind(targ_type))
>> -               return 0;
>> +       if (btf_kind(local_type) != btf_kind(targ_type)) {
>> +               if (btf_is_enum(local_type) && btf_is_enum64(targ_type)) ;
>> +               else if (btf_is_enum64(local_type) && btf_is_enum(targ_type)) ;
>> +               else return 0;
>> +       }
> 
> use proposed btf_kinds_are_compat() here?

Right. Can do this.

> 
>>
>>          switch (btf_kind(local_type)) {
>>          case BTF_KIND_PTR:
>>          case BTF_KIND_FLOAT:
>>                  return 1;
>>          case BTF_KIND_FWD:
>> -       case BTF_KIND_ENUM: {
>> +       case BTF_KIND_ENUM:
>> +       case BTF_KIND_ENUM64: {
>>                  const char *local_name, *targ_name;
>>                  size_t local_len, targ_len;
>>
>> @@ -494,29 +502,48 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
>>
>>          if (core_relo_is_enumval_based(local_spec->relo_kind)) {
>>                  size_t local_essent_len, targ_essent_len;
>> +               const struct btf_enum64 *e64;
>>                  const struct btf_enum *e;
>>                  const char *targ_name;
>>
>>                  /* has to resolve to an enum */
>>                  targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id, &targ_id);
>> -               if (!btf_is_enum(targ_type))
>> +               if (!btf_is_enum(targ_type) && !btf_is_enum64(targ_type))
>>                          return 0;
>>
>>                  local_essent_len = bpf_core_essential_name_len(local_acc->name);
>>
>> -               for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) {
>> -                       targ_name = btf__name_by_offset(targ_spec->btf, e->name_off);
>> -                       targ_essent_len = bpf_core_essential_name_len(targ_name);
>> -                       if (targ_essent_len != local_essent_len)
>> -                               continue;
>> -                       if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
> 
> 
> so idea here is to find enumerator with matching name and record its
> name and position, let's extract that part of the logic into a helper
> and keep the targ_acc/targ_spec initialization in one piece. It will
> be easier to follow the intent and less opportunity to get out of
> sync.

Will do.

> 
>> -                               targ_acc->type_id = targ_id;
>> -                               targ_acc->idx = i;
>> -                               targ_acc->name = targ_name;
>> -                               targ_spec->len++;
>> -                               targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
>> -                               targ_spec->raw_len++;
>> -                               return 1;
>> +               if (btf_is_enum(targ_type)) {
>> +                       for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) {
>> +                               targ_name = btf__name_by_offset(targ_spec->btf, e->name_off);
>> +                               targ_essent_len = bpf_core_essential_name_len(targ_name);
>> +                               if (targ_essent_len != local_essent_len)
>> +                                       continue;
>> +                               if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
>> +                                       targ_acc->type_id = targ_id;
>> +                                       targ_acc->idx = i;
>> +                                       targ_acc->name = targ_name;
>> +                                       targ_spec->len++;
>> +                                       targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
>> +                                       targ_spec->raw_len++;
>> +                                       return 1;
>> +                               }
>> +                       }
>> +               } else {
>> +                       for (i = 0, e64 = btf_enum64(targ_type); i < btf_vlen(targ_type); i++, e64++) {
>> +                               targ_name = btf__name_by_offset(targ_spec->btf, e64->name_off);
>> +                               targ_essent_len = bpf_core_essential_name_len(targ_name);
>> +                               if (targ_essent_len != local_essent_len)
>> +                                       continue;
>> +                               if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) {
>> +                                       targ_acc->type_id = targ_id;
>> +                                       targ_acc->idx = i;
>> +                                       targ_acc->name = targ_name;
>> +                                       targ_spec->len++;
>> +                                       targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
>> +                                       targ_spec->raw_len++;
>> +                                       return 1;
>> +                               }
>>                          }
>>                  }
>>                  return 0;
>> @@ -681,7 +708,7 @@ static int bpf_core_calc_field_relo(const char *prog_name,
>>                  break;
>>          case BPF_CORE_FIELD_SIGNED:
>>                  /* enums will be assumed unsigned */
>> -               *val = btf_is_enum(mt) ||
>> +               *val = btf_is_enum(mt) || btf_is_enum64(mt) ||
>>                         (btf_int_encoding(mt) & BTF_INT_SIGNED);
>>                  if (validate)
>>                          *validate = true; /* signedness is never ambiguous */
>> @@ -753,6 +780,7 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
>>                                        const struct bpf_core_spec *spec,
>>                                        __u64 *val)
>>   {
>> +       const struct btf_enum64 *e64;
>>          const struct btf_type *t;
>>          const struct btf_enum *e;
>>
>> @@ -764,8 +792,13 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
>>                  if (!spec)
>>                          return -EUCLEAN; /* request instruction poisoning */
>>                  t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
>> -               e = btf_enum(t) + spec->spec[0].idx;
>> -               *val = e->val;
>> +               if (btf_is_enum(t)) {
>> +                       e = btf_enum(t) + spec->spec[0].idx;
>> +                       *val = e->val;
>> +               } else {
>> +                       e64 = btf_enum64(t) + spec->spec[0].idx;
>> +                       *val = btf_enum64_value(e64);
>> +               }
> 
> I think with sign bit we now have further complication: for 32-bit
> enums we need to sign extend 32-bit values to s64 and then cast as
> u64, no? Seems like a helper to abstract that is good to have here.
> Otherwise relocating enum ABC { D = -1 } will produce invalid ldimm64
> instruction, right?

We should be fine here. For enum32, we have
struct btf_enum {
         __u32   name_off;
         __s32   val;
};
So above *val = e->val will first sign extend from __s32 to __s64
and then the __u64. Let me have a helper with additional comments
to make it clear.

> 
> Also keep in mind that you can use btf_enum()/btf_enum64() as an
> array, so above you can write just as
> 
> *val = btf_is_enum(t)
>      ? btf_enum(t)[spec->spec[0].idx]
>      : btf_enum64(t)[spec->spec[0].idx];
> 
> But we need sign check and extension, so better to have a separate helper.
> 
>>                  break;
>>          default:
>>                  return -EOPNOTSUPP;
>> @@ -1034,7 +1067,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>                  }
>>
>>                  insn[0].imm = new_val;
>> -               insn[1].imm = 0; /* currently only 32-bit values are supported */
>> +               insn[1].imm = new_val >> 32;
> 
> for 32-bit instructions (ALU/ALU32, etc) we need to make sure that
> new_val fits in 32 bits. And we need to be careful about
> signed/unsigned, because for signed case all-zero or all-one upper 32
> bits are ok (sign extension). Can we know the expected signed/unsigned
> operation from bpf_insn itself? We should be, right?

The core relocation insn for constant is
   move r1, <32bit value>
or
   ldimm_64 r1, <64bit value>
and there are no signedness information.
So the 64bit value (except sign extension) can only from
ldimm_64. We should be okay here, but I can double check.

> 
>>                  pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>>                           prog_name, relo_idx, insn_idx,
>>                           (unsigned long long)imm, new_val);
>> @@ -1056,6 +1089,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>    */
>>   int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *spec)
>>   {
>> +       const struct btf_enum64 *e64;
>>          const struct btf_type *t;
>>          const struct btf_enum *e;
>>          const char *s;
>> @@ -1086,10 +1120,15 @@ int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *s
>>
>>          if (core_relo_is_enumval_based(spec->relo_kind)) {
>>                  t = skip_mods_and_typedefs(spec->btf, type_id, NULL);
>> -               e = btf_enum(t) + spec->raw_spec[0];
>> -               s = btf__name_by_offset(spec->btf, e->name_off);
>> -
>> -               append_buf("::%s = %u", s, e->val);
>> +               if (btf_is_enum(t)) {
>> +                       e = btf_enum(t) + spec->raw_spec[0];
>> +                       s = btf__name_by_offset(spec->btf, e->name_off);
>> +                       append_buf("::%s = %u", s, e->val);
>> +               } else {
>> +                       e64 = btf_enum64(t) + spec->raw_spec[0];
>> +                       s = btf__name_by_offset(spec->btf, e64->name_off);
>> +                       append_buf("::%s = %llu", s, btf_enum64_value(e64));
> 
> %llu problem here again

okay.

> 
>> +               }
>>                  return len;
>>          }
>>
> 
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 05/12] bpftool: Add btf enum64 support
  2022-05-09 23:31   ` Andrii Nakryiko
@ 2022-05-10 22:43     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:43 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 4:31 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> Add BTF_KIND_ENUM64 support.
>> For example, the following enum is defined in uapi bpf.h.
>>    $ cat core.c
>>    enum A {
>>          BPF_F_INDEX_MASK                = 0xffffffffULL,
>>          BPF_F_CURRENT_CPU               = BPF_F_INDEX_MASK,
>>          BPF_F_CTXLEN_MASK               = (0xfffffULL << 32),
>>    } g;
>> Compiled with
>>    clang -target bpf -O2 -g -c core.c
>> Using bpftool to dump types and generate format C file:
>>    $ bpftool btf dump file core.o
>>    ...
>>    [1] ENUM64 'A' size=8 vlen=3
>>          'BPF_F_INDEX_MASK' val=4294967295ULL
>>          'BPF_F_CURRENT_CPU' val=4294967295ULL
>>          'BPF_F_CTXLEN_MASK' val=4503595332403200ULL
>>    $ bpftool btf dump file core.o format c
>>    ...
>>    enum A {
>>          BPF_F_INDEX_MASK = 4294967295ULL,
>>          BPF_F_CURRENT_CPU = 4294967295ULL,
>>          BPF_F_CTXLEN_MASK = 4503595332403200ULL,
> 
> maybe we should have some heuristic that if the value is "big enough"
> (e.g., larger than 1-128 millions) and is unsigned we should emit it
> as hex?

I thought about this. But since you are also suggesting this, I will
do this (greater than 1 million).

> 
>>    };
>>    ...
>>
>> The 64bit value is represented properly in BTF and C dump.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
> 
> just minor nits, LGTM
> 
> Acked-by: Andrii Nakryiko <andrii@kernel.org>
> 
>>   tools/bpf/bpftool/btf.c        | 47 ++++++++++++++++++++++++++++++++--
>>   tools/bpf/bpftool/btf_dumper.c | 32 +++++++++++++++++++++++
>>   tools/bpf/bpftool/gen.c        |  1 +
>>   3 files changed, 78 insertions(+), 2 deletions(-)
>>
> 
> [...]
>> +       case BTF_KIND_ENUM64: {
>> +               const struct btf_enum64 *v = (const void *)(t + 1);
> 
> can use btf_enum64() helper from libbpf?
> 
>> +               __u16 vlen = BTF_INFO_VLEN(t->info);
> 
> btf_vlen(t)

I copied the code from above BTF_KIND_ENUM. But I certainly can do this.

> 
>> +               int i;
>> +
>> +               if (json_output) {
>> +                       jsonw_uint_field(w, "size", t->size);
>> +                       jsonw_uint_field(w, "vlen", vlen);
>> +                       jsonw_name(w, "values");
>> +                       jsonw_start_array(w);
>> +               } else {
>> +                       printf(" size=%u vlen=%u", t->size, vlen);
>> +               }
>> +               for (i = 0; i < vlen; i++, v++) {
>> +                       const char *name = btf_str(btf, v->name_off);
>> +                       __u64 val = (__u64)v->hi32 << 32 | v->lo32;
> 
> () ?

okay.

> 
>> +
>> +                       if (json_output) {
>> +                               jsonw_start_object(w);
>> +                               jsonw_string_field(w, "name", name);
>> +                               if (btf_kflag(t))
>> +                                       jsonw_uint_field(w, "val", val);
>> +                               else
>> +                                       jsonw_int_field(w, "val", val);
>> +                               jsonw_end_object(w);
>> +                       } else {
>> +                               if (btf_kflag(t))
>> +                                       printf("\n\t'%s' val=%lluULL", name, val);
>> +                               else
>> +                                       printf("\n\t'%s' val=%lldLL", name, val);
>>                          }
>>                  }
>>                  if (json_output)
>> diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c
>> index f5dddf8ef404..f9f38384b9a6 100644
>> --- a/tools/bpf/bpftool/btf_dumper.c
>> +++ b/tools/bpf/bpftool/btf_dumper.c
>> @@ -182,6 +182,35 @@ static int btf_dumper_enum(const struct btf_dumper *d,
>>          return 0;
>>   }
>>
>> +static int btf_dumper_enum64(const struct btf_dumper *d,
>> +                            const struct btf_type *t,
>> +                            const void *data)
>> +{
>> +       const struct btf_enum64 *enums = btf_enum64(t);
>> +       __u32 hi32, lo32;
>> +       __u64 value;
>> +       __u16 i;
>> +
>> +       if (t->size != 8)
>> +               return -EINVAL;
> 
> no need

sure.

> 
>> +
>> +       value = *(__u64 *)data;
>> +       hi32 = value >> 32;
>> +       lo32 = (__u32)value;
>> +
> 
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure
  2022-05-09 23:34   ` Andrii Nakryiko
@ 2022-05-10 22:44     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:44 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 4:34 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> The kflag is supported now for BTF_KIND_ENUM.
>> So remove the test which tests verifier failure
>> due to existence of kflag.
>>
>> With enum64 support in kernel and libbpf,
>> selftest btf_dump/btf_dump failed with
>> no-enum64 support llvm for the following
>> enum definition:
>>   enum e2 {
>>          C = 100,
>>          D = 4294967295,
>>          E = 0,
>>   };
>>
>> With the no-enum64 support llvm, the signedness is
>> 'signed' by default, and D (4294967295 = 0xffffffff)
>> will print as -1. With enum64 support llvm, the signedness
>> is 'unsigned' and the value of D will print as 4294967295.
>> To support both old and new compilers, this patch
>> changed the value to 268435455 = 0xfffffff which works
>> with both enum64 or non-enum64 support llvm.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   tools/testing/selftests/bpf/prog_tests/btf.c  | 20 -------------------
>>   .../bpf/progs/btf_dump_test_case_syntax.c     |  2 +-
>>   2 files changed, 1 insertion(+), 21 deletions(-)
>>
>> diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
>> index ba5bde53d418..8e068e06b3e8 100644
>> --- a/tools/testing/selftests/bpf/prog_tests/btf.c
>> +++ b/tools/testing/selftests/bpf/prog_tests/btf.c
>> @@ -2896,26 +2896,6 @@ static struct btf_raw_test raw_tests[] = {
>>          .err_str = "Invalid btf_info kind_flag",
>>   },
>>
>> -{
>> -       .descr = "invalid enum kind_flag",
>> -       .raw_types = {
>> -               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
>> -               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 1, 1), 4),  /* [2] */
>> -               BTF_ENUM_ENC(NAME_TBD, 0),
>> -               BTF_END_RAW,
>> -       },
>> -       BTF_STR_SEC("\0A"),
>> -       .map_type = BPF_MAP_TYPE_ARRAY,
>> -       .map_name = "enum_type_check_btf",
>> -       .key_size = sizeof(int),
>> -       .value_size = sizeof(int),
>> -       .key_type_id = 1,
>> -       .value_type_id = 1,
>> -       .max_entries = 4,
>> -       .btf_load_err = true,
>> -       .err_str = "Invalid btf_info kind_flag",
>> -},
>> -
>>   {
>>          .descr = "valid fwd kind_flag",
>>          .raw_types = {
>> diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
>> index 1c7105fcae3c..4068cea4be53 100644
>> --- a/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
>> +++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
>> @@ -13,7 +13,7 @@ enum e1 {
>>
>>   enum e2 {
>>          C = 100,
>> -       D = 4294967295,
>> +       D = 268435455,
>>          E = 0,
>>   };
> 
> can you please also add btf_dump tests for >32-bit enums at the same
> time? Both signed and unsigned?

will do.

> 
> 
>>
>> --
>> 2.30.2
>>

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 09/12] selftests/bpf: Test BTF_KIND_ENUM64 for deduplication
  2022-05-09 23:37   ` Andrii Nakryiko
@ 2022-05-10 22:44     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:44 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 4:37 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
> 
> Please add some commit message, however trivial.
> 
> Can you please add a simple test to validate that enum and enum64 are
> not deduplicated against each other?

okay.

> 
> 
>>   tools/testing/selftests/bpf/prog_tests/btf.c | 70 +++++++++++++++++++-
>>   1 file changed, 68 insertions(+), 2 deletions(-)
>>
> 
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 10/12] selftests/bpf: add a test for enum64 value relocation
  2022-05-09 23:38   ` Andrii Nakryiko
@ 2022-05-10 22:45     ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 22:45 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/9/22 4:38 PM, Andrii Nakryiko wrote:
> On Sun, May 1, 2022 at 12:01 PM Yonghong Song <yhs@fb.com> wrote:
>>
>> Add a test for enum64 value relocations.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
> 
> Looks good, but can you also add some signed enums for testing?

okay.

> 
>>   .../selftests/bpf/prog_tests/core_reloc.c     | 43 +++++++++++++++
>>   .../bpf/progs/btf__core_reloc_enum64val.c     |  3 ++
>>   .../progs/btf__core_reloc_enum64val___diff.c  |  3 ++
>>   .../btf__core_reloc_enum64val___err_missing.c |  3 ++
>>   ...btf__core_reloc_enum64val___val3_missing.c |  3 ++
>>   .../selftests/bpf/progs/core_reloc_types.h    | 47 ++++++++++++++++
>>   .../bpf/progs/test_core_reloc_enum64val.c     | 53 +++++++++++++++++++
>>   7 files changed, 155 insertions(+)
>>   create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val.c
>>   create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___diff.c
>>   create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___err_missing.c
>>   create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_enum64val___val3_missing.c
>>   create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_enum64val.c
>>
> 
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-10 22:40     ` Yonghong Song
@ 2022-05-10 23:02       ` Yonghong Song
  2022-05-10 23:40         ` Andrii Nakryiko
  2022-05-10 23:38       ` Andrii Nakryiko
  1 sibling, 1 reply; 47+ messages in thread
From: Yonghong Song @ 2022-05-10 23:02 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/10/22 3:40 PM, Yonghong Song wrote:
> 
> 
> On 5/9/22 4:25 PM, Andrii Nakryiko wrote:
>> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>>
>>> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
>>> btf__add_enum_value() and introduced the following new APIs
>>>    btf__add_enum32()
>>>    btf__add_enum32_value()
>>>    btf__add_enum64()
>>>    btf__add_enum64_value()
>>> due to new kind and introduction of kflag.
>>>
>>> To support old kernel with enum64, the sanitization is
>>> added to replace BTF_KIND_ENUM64 with a bunch of
>>> pointer-to-void types.
>>>
>>> The enum64 value relocation is also supported. The enum64
>>> forward resolution, with enum type as forward declaration
>>> and enum64 as the actual definition, is also supported.
>>>
>>> Signed-off-by: Yonghong Song <yhs@fb.com>
>>> ---
>>>   tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
>>>   tools/lib/bpf/btf.h                           |  21 ++
>>>   tools/lib/bpf/btf_dump.c                      |  94 ++++++--
>>>   tools/lib/bpf/libbpf.c                        |  64 ++++-
>>>   tools/lib/bpf/libbpf.map                      |   4 +
>>>   tools/lib/bpf/libbpf_internal.h               |   2 +
>>>   tools/lib/bpf/linker.c                        |   2 +
>>>   tools/lib/bpf/relo_core.c                     |  93 ++++---
>>>   .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
>>>   .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
>>>   10 files changed, 450 insertions(+), 72 deletions(-)
>>>
>>
[...]
>>
>>
>>> +       t->size = tsize;
>>> +
>>> +       return btf_commit_type(btf, sz);
>>> +}
>>> +
>>> +/*
>>> + * Append new BTF_KIND_ENUM type with:
>>> + *   - *name* - name of the enum, can be NULL or empty for anonymous 
>>> enums;
>>> + *   - *is_unsigned* - whether the enum values are unsigned or not;
>>> + *
>>> + * Enum initially has no enum values in it (and corresponds to enum 
>>> forward
>>> + * declaration). Enumerator values can be added by 
>>> btf__add_enum64_value()
>>> + * immediately after btf__add_enum() succeeds.
>>> + *
>>> + * Returns:
>>> + *   - >0, type ID of newly added BTF type;
>>> + *   - <0, on error.
>>> + */
>>> +int btf__add_enum32(struct btf *btf, const char *name, bool 
>>> is_unsigned)
>>
>> given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
>> btf__add_enum()/btf__add_enum_value() and not deprecate anything.
>> ENUM64 can be thought about as more of a special case, so I think it's
>> ok.
> 
> The current btf__add_enum api:
> LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32 
> bytes_sz);
> 
> The issue is it doesn't have signedness parameter. if the user input
> is
>     enum { A = -1, B = 0, C = 1 };
> the actual printout btf format will be
>     enum { A 4294967295, B = 0, C = 1}
> does not match the original source.

I think I found a way to keep the current btf__add_enum() API.
Initially, the signedness will be unsigned. But during
btf__add_enum_value() api calls, if any negative value
is found, the signedness will change to signed. I think
this should work.

> 
>>
>>> +{
>>> +       return btf_add_enum_common(btf, name, is_unsigned, 
>>> BTF_KIND_ENUM, 4);
>>> +}
>>> +
>>
>> [...]
>>
[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 01/12] bpf: Add btf enum64 support
  2022-05-10 22:06     ` Yonghong Song
@ 2022-05-10 23:18       ` Andrii Nakryiko
  2022-05-11  0:17         ` Yonghong Song
  0 siblings, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-10 23:18 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Tue, May 10, 2022 at 3:06 PM Yonghong Song <yhs@fb.com> wrote:
>
>
>
> On 5/9/22 3:29 PM, Andrii Nakryiko wrote:
> > On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
> >>
> >> 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 bytes followed by the common type:
> >
> > you probably meant three fields, not bytes
>
> correct.
>
> >
> >>    struct bpf_enum64 {
> >>      __u32 nume_off;
> >>      __u32 hi32;
> >>      __u32 lo32;
> >
> > I'd like to nitpick on name here, as hi/lo of what? Maybe val_hi32 and
> > val_lo32? Can we also reverse the order here? For x86 you'll be able
> > to use &lo32 to get value directly if you really want, without a local
> > copy. It also just logically seems better to have something low first,
> > then high next.
>
> I can go with val_hi32, val_lo32 and put val_lo32 before val_hi32.
> I don't have any preference for the ordering of these two fields.
>
> >
> >
> >>    };
> >> 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 hi32 and 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, print 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 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 value size of BTF_KIND_ENUM64 is encoded to 8 to represent its intent.
> >> The compiler ([1]) and pahole will generate BTF_KIND_ENUM64 only if the value has
> >> to be represented with 64 bits.
> >>
> >>    [1] https://reviews.llvm.org/D124641
> >>
> >> Signed-off-by: Yonghong Song <yhs@fb.com>
> >> ---
> >>   include/linux/btf.h            |  18 ++++-
> >>   include/uapi/linux/btf.h       |  17 ++++-
> >>   kernel/bpf/btf.c               | 132 ++++++++++++++++++++++++++++++---
> >>   tools/include/uapi/linux/btf.h |  17 ++++-
> >>   4 files changed, 168 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/include/linux/btf.h b/include/linux/btf.h
> >> index 2611cea2c2b6..280c33c9414a 100644
> >> --- a/include/linux/btf.h
> >> +++ b/include/linux/btf.h
> >> @@ -174,7 +174,8 @@ static inline bool btf_type_is_small_int(const struct btf_type *t)
> >>
> >>   static inline bool btf_type_is_enum(const struct btf_type *t)
> >>   {
> >> -       return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM;
> >> +       return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM ||
> >> +              BTF_INFO_KIND(t->info) == BTF_KIND_ENUM64;
> >>   }
> >
> > a bit hesitant about this change, there is no helper to check for ENUM
> > vs ENUM64. Inside the kernel this change seems to be correct as we
> > don't care, but for libbpf I'd probably keep btf_is_enum() unchanged
> > (you can't really work with them in the same generic way in
> > user-space, as their memory layout is very different, so it's better
> > not to generalize them unnecessarily)
>
> Let me introduce a new helper called
> btf_type_is_any_enum(...) to check both
> BTF_KIND_ENUM or BTF_KIND_ENUM64.
>
> >
> >>
> >>   static inline bool str_is_empty(const char *s)
> >> @@ -192,6 +193,16 @@ static inline bool btf_is_enum(const struct btf_type *t)
> >>          return btf_kind(t) == BTF_KIND_ENUM;
> >>   }
> >>
> >> +static inline bool btf_is_enum64(const struct btf_type *t)
> >> +{
> >> +       return btf_kind(t) == BTF_KIND_ENUM64;
> >> +}
> >> +
> >> +static inline u64 btf_enum64_value(const struct btf_enum64 *e)
> >> +{
> >> +       return (u64)e->hi32 << 32 | e->lo32;
> >
> > this might be correct but () around bit shift would make it more obvious
>
> I can do this.
>
> >
> >> +}
> >> +
> >>   static inline bool btf_is_composite(const struct btf_type *t)
> >>   {
> >>          u16 kind = btf_kind(t);
> >> @@ -332,6 +343,11 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t)
> >>          return (struct btf_enum *)(t + 1);
> >>   }
> >>
> >> +static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
> >> +{
> >> +       return (struct btf_enum64 *)(t + 1);
> >> +}
> >> +
> >>   static inline const struct btf_var_secinfo *btf_type_var_secinfo(
> >>                  const struct btf_type *t)
> >>   {
> >> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> >> index a9162a6c0284..2aac226a27b2 100644
> >> --- a/include/uapi/linux/btf.h
> >> +++ b/include/uapi/linux/btf.h
> >> @@ -36,10 +36,10 @@ struct btf_type {
> >>           * bits 24-28: kind (e.g. int, ptr, array...etc)
> >>           * bits 29-30: unused
> >>           * bit     31: kind_flag, currently used by
> >> -        *             struct, union and fwd
> >> +        *             struct, union, enum, fwd and enum64
> >
> > see comment below on kflag for enum itself, but reading this I
> > realized that we don't really describe the meaning of kind_flag for
> > different kinds. Should it be done here?
>
> We have detailed description in Documentation/bpf/btf.rst.
> Hopefully it will be enough if people wants to understand
> what kflag means for each kind.
>
>
> >
> >>           */
> >>          __u32 info;
> >> -       /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
> >> +       /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
> >>           * "size" tells the size of the type it is describing.
> >>           *
> >>           * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
> >> @@ -63,7 +63,7 @@ enum {
> >>          BTF_KIND_ARRAY          = 3,    /* Array        */
> >>          BTF_KIND_STRUCT         = 4,    /* Struct       */
> >>          BTF_KIND_UNION          = 5,    /* Union        */
> >> -       BTF_KIND_ENUM           = 6,    /* Enumeration  */
> >> +       BTF_KIND_ENUM           = 6,    /* Enumeration for int/unsigned int values */
> >
> > nit: "Enumeration for up to 32-bit values" ?
>
> This should work.
>
> >
> >>          BTF_KIND_FWD            = 7,    /* Forward      */
> >>          BTF_KIND_TYPEDEF        = 8,    /* Typedef      */
> >>          BTF_KIND_VOLATILE       = 9,    /* Volatile     */
> >> @@ -76,6 +76,7 @@ enum {
> >>          BTF_KIND_FLOAT          = 16,   /* Floating point       */
> >>          BTF_KIND_DECL_TAG       = 17,   /* Decl Tag */
> >>          BTF_KIND_TYPE_TAG       = 18,   /* Type Tag */
> >> +       BTF_KIND_ENUM64         = 19,   /* Enumeration for long/unsigned long values */
> >
> > and then "for 64-bit values" (or maybe up to 64 bit values, but in
> > practice we won't do that, right?)
>
> We can do "up to 64-bit values". In practice, from llvm and pahole,
> we will only encode 64-bit values in ENUM64.
>
> >
> >>
> >>          NR_BTF_KINDS,
> >>          BTF_KIND_MAX            = NR_BTF_KINDS - 1,
> >> @@ -186,4 +187,14 @@ struct btf_decl_tag {
> >>          __s32   component_idx;
> >>   };
> >>
> >
> > [...]
> >
> >> @@ -3716,7 +3721,8 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
> >>
> >>                  if (env->log.level == BPF_LOG_KERNEL)
> >>                          continue;
> >> -               btf_verifier_log(env, "\t%s val=%d\n",
> >> +               fmt_str = btf_type_kflag(t) ? "\t%s val=%u\n" : "\t%s val=%d\n";
> >> +               btf_verifier_log(env, fmt_str,
> >>                                   __btf_name_by_offset(btf, enums[i].name_off),
> >>                                   enums[i].val);
> >>          }
> >> @@ -3757,7 +3763,10 @@ static void btf_enum_show(const struct btf *btf, const struct btf_type *t,
> >>                  return;
> >>          }
> >>
> >> -       btf_show_type_value(show, "%d", v);
> >> +       if (btf_type_kflag(t))
> >
> > libbpf's assumption right now and most common case is unsigned enum,
> > so it seems more desirable to have kflag == 0 mean unsigned, with
> > kflag == 1 being signed. It will make most existing enum definitions
> > not change but also make it easy for libbpf's btf_dumper to use kflag
> > if it's set, but otherwise have the same unsigned printing whether
> > enum is really unsigned or Clang is too old to emit the kflag for
> > enum. WDYT?
>
> Right, libbpf assumption is unsigned enum and the kernel prints as
> signed. I agree that default unsigned should cover more cases.
> Will change that in the next revision.
>
> >
> >> +               btf_show_type_value(show, "%u", v);
> >> +       else
> >> +               btf_show_type_value(show, "%d", v);
> >
> > you didn't got with ternary operator for fmt string selector like in
> > previous case? I have mild preference for keeping it consistent (and
> > keeping btf_type_kflag(t) ? "fmt1" : "fmt2" inline)
>
> The reason I didn't do it is the line is a little long.
> But I can do it.
>
> >
> >>          btf_show_end_type(show);
> >>   }
> >>
> >> @@ -3770,6 +3779,109 @@ static struct btf_kind_operations enum_ops = {
> >>          .show = btf_enum_show,
> >>   };
> >>
> >> +static s32 btf_enum64_check_meta(struct btf_verifier_env *env,
> >> +                                const struct btf_type *t,
> >> +                                u32 meta_left)
> >> +{
> >> +       const struct btf_enum64 *enums = btf_type_enum64(t);
> >> +       struct btf *btf = env->btf;
> >> +       const char *fmt_str;
> >> +       u16 i, nr_enums;
> >> +       u32 meta_needed;
> >> +
> >> +       nr_enums = btf_type_vlen(t);
> >> +       meta_needed = nr_enums * sizeof(*enums);
> >> +
> >> +       if (meta_left < meta_needed) {
> >> +               btf_verifier_log_basic(env, t,
> >> +                                      "meta_left:%u meta_needed:%u",
> >> +                                      meta_left, meta_needed);
> >> +               return -EINVAL;
> >> +       }
> >> +
> >> +       if (t->size != 8) {
> >
> > technically there is nothing wrong with using enum64 for smaller
> > sizes, right? Any particular reason to prevent this? We can just
> > define that 64-bit value is sign-extended if enum is signed and has
> > size < 8?
>
> My original idea is to support 64-bit enum only for ENUM64 kind.
> But it is certainly possible to encode 32-bit enums as well for
> ENUM64. So I will remove this restriction.
>
> The dwarf only generates sizes 4 (for up-to 32 bit values)
> and 8 (for 64 bit values). But BTF_KIND_ENUM supports 1/2/4/8
> sizes, so BTF_KIND_ENUM64 will also support 1/2/4/8 sizes.

Little known fact, but it's not true:

$ bpftool btf dump file /sys/kernel/btf/vmlinux| rg 'ENUM.*size=1' -A8
[83476] ENUM 'hub_led_mode' size=1 vlen=8
        'INDICATOR_AUTO' val=0
        'INDICATOR_CYCLE' val=1
        'INDICATOR_GREEN_BLINK' val=2
        'INDICATOR_GREEN_BLINK_OFF' val=3
        'INDICATOR_AMBER_BLINK' val=4
        'INDICATOR_AMBER_BLINK_OFF' val=5
        'INDICATOR_ALT_BLINK' val=6
        'INDICATOR_ALT_BLINK_OFF' val=7

Defined as packed enum:

enum hub_led_mode {
        INDICATOR_AUTO = 0,
        INDICATOR_CYCLE,
        /* software blinks for attention:  software, hardware, reserved */
        INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
        INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
        INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
} __attribute__ ((packed));


>
> >
> >> +               btf_verifier_log_type(env, t, "Unexpected size");
> >> +               return -EINVAL;
> >> +       }
> >> +
> >> +       /* enum type either no name or a valid one */
> >> +       if (t->name_off &&
> >> +           !btf_name_valid_identifier(env->btf, t->name_off)) {
> >> +               btf_verifier_log_type(env, t, "Invalid name");
> >> +               return -EINVAL;
> >> +       }
> >> +
> >
> > [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value
  2022-05-10 22:14     ` Yonghong Song
@ 2022-05-10 23:19       ` Andrii Nakryiko
  0 siblings, 0 replies; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-10 23:19 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Tue, May 10, 2022 at 3:14 PM Yonghong Song <yhs@fb.com> wrote:
>
>
>
> On 5/9/22 3:37 PM, Andrii Nakryiko wrote:
> > On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
> >>
> >> Currently, the libbpf limits the relocation value to be 32bit
> >> since all current relocations have such a limit. But with
> >> BTF_KIND_ENUM64 support, the enum value could be 64bit.
> >> So let us permit 64bit relocation value in libbpf.
> >>
> >> Signed-off-by: Yonghong Song <yhs@fb.com>
> >> ---
> >>   tools/lib/bpf/relo_core.c | 24 ++++++++++++------------
> >>   tools/lib/bpf/relo_core.h |  4 ++--
> >>   2 files changed, 14 insertions(+), 14 deletions(-)
> >>
> >
> > [...]
> >
> >> @@ -929,7 +929,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>                          int insn_idx, const struct bpf_core_relo *relo,
> >>                          int relo_idx, const struct bpf_core_relo_res *res)
> >>   {
> >> -       __u32 orig_val, new_val;
> >> +       __u64 orig_val, new_val;
> >>          __u8 class;
> >>
> >>          class = BPF_CLASS(insn->code);
> >> @@ -954,14 +954,14 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>                  if (BPF_SRC(insn->code) != BPF_K)
> >>                          return -EINVAL;
> >>                  if (res->validate && insn->imm != orig_val) {
> >> -                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %u -> %u\n",
> >> +                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %llu -> %llu\n",
> >>                                  prog_name, relo_idx,
> >>                                  insn_idx, insn->imm, orig_val, new_val);
> >
> > %llu is not valid formatter for __u64 on all architectures, please add
> > explicit (unsigned long long) cast
>
> Okay, will do.
>
> >
> > but also in general for non-ldimm64 instructions we need to check that
> > new value fits in 32 bits
>
> The real 64-bit value can only be retrieved for ldimm64 insn, so I
> suppose it should be fine here. But let me double check.

So, technically (I don't think that happens in practice, though), you
can have ALU operation with a local 32-bit enum with some reasonable
value, which in the kernel is actually ENUM64 with huge value.

>
> >
> > [...]
> >
> >> @@ -1026,7 +1026,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>
> >>                  imm = insn[0].imm + ((__u64)insn[1].imm << 32);
> >>                  if (res->validate && imm != orig_val) {
> >> -                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %u -> %u\n",
> >> +                       pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
> >>                                  prog_name, relo_idx,
> >>                                  insn_idx, (unsigned long long)imm,
> >>                                  orig_val, new_val);
> >> @@ -1035,7 +1035,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>
> >>                  insn[0].imm = new_val;
> >>                  insn[1].imm = 0; /* currently only 32-bit values are supported */
> >
> > as Dave mentioned, not anymore, so this should take higher 32-bit of new_val
>
> Will do.
>
> >
> >
> >> -               pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n",
> >> +               pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
> >>                           prog_name, relo_idx, insn_idx,
> >>                           (unsigned long long)imm, new_val);
> >>                  break;
> >> @@ -1261,7 +1261,7 @@ int bpf_core_calc_relo_insn(const char *prog_name,
> >>                           * decision and value, otherwise it's dangerous to
> >>                           * proceed due to ambiguity
> >>                           */
> >> -                       pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n",
> >> +                       pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %llu != %s %llu\n",
> >>                                  prog_name, relo_idx,
> >>                                  cand_res.poison ? "failure" : "success", cand_res.new_val,
> >>                                  targ_res->poison ? "failure" : "success", targ_res->new_val);
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-10 22:40     ` Yonghong Song
  2022-05-10 23:02       ` Yonghong Song
@ 2022-05-10 23:38       ` Andrii Nakryiko
  2022-05-11  0:39         ` Yonghong Song
  1 sibling, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-10 23:38 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Tue, May 10, 2022 at 3:40 PM Yonghong Song <yhs@fb.com> wrote:
>
>
>
> On 5/9/22 4:25 PM, Andrii Nakryiko wrote:
> > On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
> >>
> >> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
> >> btf__add_enum_value() and introduced the following new APIs
> >>    btf__add_enum32()
> >>    btf__add_enum32_value()
> >>    btf__add_enum64()
> >>    btf__add_enum64_value()
> >> due to new kind and introduction of kflag.
> >>
> >> To support old kernel with enum64, the sanitization is
> >> added to replace BTF_KIND_ENUM64 with a bunch of
> >> pointer-to-void types.
> >>
> >> The enum64 value relocation is also supported. The enum64
> >> forward resolution, with enum type as forward declaration
> >> and enum64 as the actual definition, is also supported.
> >>
> >> Signed-off-by: Yonghong Song <yhs@fb.com>
> >> ---
> >>   tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
> >>   tools/lib/bpf/btf.h                           |  21 ++
> >>   tools/lib/bpf/btf_dump.c                      |  94 ++++++--
> >>   tools/lib/bpf/libbpf.c                        |  64 ++++-
> >>   tools/lib/bpf/libbpf.map                      |   4 +
> >>   tools/lib/bpf/libbpf_internal.h               |   2 +
> >>   tools/lib/bpf/linker.c                        |   2 +
> >>   tools/lib/bpf/relo_core.c                     |  93 ++++---
> >>   .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
> >>   .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
> >>   10 files changed, 450 insertions(+), 72 deletions(-)
> >>
> >

[...]

> >
> >
> >> +       t->size = tsize;
> >> +
> >> +       return btf_commit_type(btf, sz);
> >> +}
> >> +
> >> +/*
> >> + * Append new BTF_KIND_ENUM type with:
> >> + *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
> >> + *   - *is_unsigned* - whether the enum values are unsigned or not;
> >> + *
> >> + * Enum initially has no enum values in it (and corresponds to enum forward
> >> + * declaration). Enumerator values can be added by btf__add_enum64_value()
> >> + * immediately after btf__add_enum() succeeds.
> >> + *
> >> + * Returns:
> >> + *   - >0, type ID of newly added BTF type;
> >> + *   - <0, on error.
> >> + */
> >> +int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned)
> >
> > given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
> > btf__add_enum()/btf__add_enum_value() and not deprecate anything.
> > ENUM64 can be thought about as more of a special case, so I think it's
> > ok.
>
> The current btf__add_enum api:
> LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32
> bytes_sz);
>
> The issue is it doesn't have signedness parameter. if the user input
> is
>     enum { A = -1, B = 0, C = 1 };
> the actual printout btf format will be
>     enum { A 4294967295, B = 0, C = 1}
> does not match the original source.

Oh, I didn't realize that's the reason. I still like btf__add_enum()
name much better, can you please do the same macro trick that I did
for bpf_prog_load() based on the number of arguments? We'll be able to
preserve good API name and add extra argument. Once this lands we'll
need to update pahole to added signedness bit, but otherwise I don't
think there are many other users of these APIs currently (I might be
wrong, but macro magic gives us backwards compat anyway).

>
> >
> >> +{
> >> +       return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM, 4);
> >> +}
> >> +
> >
> > [...]
> >
> >>   /*

[...]

> >> @@ -764,8 +792,13 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
> >>                  if (!spec)
> >>                          return -EUCLEAN; /* request instruction poisoning */
> >>                  t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
> >> -               e = btf_enum(t) + spec->spec[0].idx;
> >> -               *val = e->val;
> >> +               if (btf_is_enum(t)) {
> >> +                       e = btf_enum(t) + spec->spec[0].idx;
> >> +                       *val = e->val;
> >> +               } else {
> >> +                       e64 = btf_enum64(t) + spec->spec[0].idx;
> >> +                       *val = btf_enum64_value(e64);
> >> +               }
> >
> > I think with sign bit we now have further complication: for 32-bit
> > enums we need to sign extend 32-bit values to s64 and then cast as
> > u64, no? Seems like a helper to abstract that is good to have here.
> > Otherwise relocating enum ABC { D = -1 } will produce invalid ldimm64
> > instruction, right?
>
> We should be fine here. For enum32, we have
> struct btf_enum {
>          __u32   name_off;
>          __s32   val;
> };
> So above *val = e->val will first sign extend from __s32 to __s64
> and then the __u64. Let me have a helper with additional comments
> to make it clear.
>

Ok, great! Let's just shorten this as I suggested below?

> >
> > Also keep in mind that you can use btf_enum()/btf_enum64() as an
> > array, so above you can write just as
> >
> > *val = btf_is_enum(t)
> >      ? btf_enum(t)[spec->spec[0].idx]
> >      : btf_enum64(t)[spec->spec[0].idx];
> >
> > But we need sign check and extension, so better to have a separate helper.
> >
> >>                  break;
> >>          default:
> >>                  return -EOPNOTSUPP;
> >> @@ -1034,7 +1067,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>                  }
> >>
> >>                  insn[0].imm = new_val;
> >> -               insn[1].imm = 0; /* currently only 32-bit values are supported */
> >> +               insn[1].imm = new_val >> 32;
> >
> > for 32-bit instructions (ALU/ALU32, etc) we need to make sure that
> > new_val fits in 32 bits. And we need to be careful about
> > signed/unsigned, because for signed case all-zero or all-one upper 32
> > bits are ok (sign extension). Can we know the expected signed/unsigned
> > operation from bpf_insn itself? We should be, right?
>
> The core relocation insn for constant is
>    move r1, <32bit value>
> or
>    ldimm_64 r1, <64bit value>
> and there are no signedness information.
> So the 64bit value (except sign extension) can only from
> ldimm_64. We should be okay here, but I can double check.

not sure how full 64-bit -1 should be loaded into register then. Does
compiler generate extra sign-extending bit shifts or embedded constant
is considered to be a signed constant always?

>
> >
> >>                  pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
> >>                           prog_name, relo_idx, insn_idx,
> >>                           (unsigned long long)imm, new_val);
> >> @@ -1056,6 +1089,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>    */

[...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-10 23:02       ` Yonghong Song
@ 2022-05-10 23:40         ` Andrii Nakryiko
  0 siblings, 0 replies; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-10 23:40 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Tue, May 10, 2022 at 4:02 PM Yonghong Song <yhs@fb.com> wrote:
>
>
>
> On 5/10/22 3:40 PM, Yonghong Song wrote:
> >
> >
> > On 5/9/22 4:25 PM, Andrii Nakryiko wrote:
> >> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
> >>>
> >>> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
> >>> btf__add_enum_value() and introduced the following new APIs
> >>>    btf__add_enum32()
> >>>    btf__add_enum32_value()
> >>>    btf__add_enum64()
> >>>    btf__add_enum64_value()
> >>> due to new kind and introduction of kflag.
> >>>
> >>> To support old kernel with enum64, the sanitization is
> >>> added to replace BTF_KIND_ENUM64 with a bunch of
> >>> pointer-to-void types.
> >>>
> >>> The enum64 value relocation is also supported. The enum64
> >>> forward resolution, with enum type as forward declaration
> >>> and enum64 as the actual definition, is also supported.
> >>>
> >>> Signed-off-by: Yonghong Song <yhs@fb.com>
> >>> ---
> >>>   tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
> >>>   tools/lib/bpf/btf.h                           |  21 ++
> >>>   tools/lib/bpf/btf_dump.c                      |  94 ++++++--
> >>>   tools/lib/bpf/libbpf.c                        |  64 ++++-
> >>>   tools/lib/bpf/libbpf.map                      |   4 +
> >>>   tools/lib/bpf/libbpf_internal.h               |   2 +
> >>>   tools/lib/bpf/linker.c                        |   2 +
> >>>   tools/lib/bpf/relo_core.c                     |  93 ++++---
> >>>   .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
> >>>   .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
> >>>   10 files changed, 450 insertions(+), 72 deletions(-)
> >>>
> >>
> [...]
> >>
> >>
> >>> +       t->size = tsize;
> >>> +
> >>> +       return btf_commit_type(btf, sz);
> >>> +}
> >>> +
> >>> +/*
> >>> + * Append new BTF_KIND_ENUM type with:
> >>> + *   - *name* - name of the enum, can be NULL or empty for anonymous
> >>> enums;
> >>> + *   - *is_unsigned* - whether the enum values are unsigned or not;
> >>> + *
> >>> + * Enum initially has no enum values in it (and corresponds to enum
> >>> forward
> >>> + * declaration). Enumerator values can be added by
> >>> btf__add_enum64_value()
> >>> + * immediately after btf__add_enum() succeeds.
> >>> + *
> >>> + * Returns:
> >>> + *   - >0, type ID of newly added BTF type;
> >>> + *   - <0, on error.
> >>> + */
> >>> +int btf__add_enum32(struct btf *btf, const char *name, bool
> >>> is_unsigned)
> >>
> >> given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
> >> btf__add_enum()/btf__add_enum_value() and not deprecate anything.
> >> ENUM64 can be thought about as more of a special case, so I think it's
> >> ok.
> >
> > The current btf__add_enum api:
> > LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32
> > bytes_sz);
> >
> > The issue is it doesn't have signedness parameter. if the user input
> > is
> >     enum { A = -1, B = 0, C = 1 };
> > the actual printout btf format will be
> >     enum { A 4294967295, B = 0, C = 1}
> > does not match the original source.
>
> I think I found a way to keep the current btf__add_enum() API.
> Initially, the signedness will be unsigned. But during
> btf__add_enum_value() api calls, if any negative value
> is found, the signedness will change to signed. I think
> this should work.
>

Oops, didn't see this email when replying. Yeah, I guess this approach
will work for 32-bit enum. For 64-bit one we probably better specify
signedness explicitly and then accept __u64 as the value (which can be
negative value casted to __u64, in practice).

> >
> >>
> >>> +{
> >>> +       return btf_add_enum_common(btf, name, is_unsigned,
> >>> BTF_KIND_ENUM, 4);
> >>> +}
> >>> +
> >>
> >> [...]
> >>
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 01/12] bpf: Add btf enum64 support
  2022-05-10 23:18       ` Andrii Nakryiko
@ 2022-05-11  0:17         ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-11  0:17 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/10/22 4:18 PM, Andrii Nakryiko wrote:
> On Tue, May 10, 2022 at 3:06 PM Yonghong Song <yhs@fb.com> wrote:
>>
>>
>>
>> On 5/9/22 3:29 PM, Andrii Nakryiko wrote:
>>> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>>>
>>>> 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 bytes followed by the common type:
>>>
>>> you probably meant three fields, not bytes
>>
>> correct.
>>
>>>
>>>>     struct bpf_enum64 {
>>>>       __u32 nume_off;
>>>>       __u32 hi32;
>>>>       __u32 lo32;
>>>
>>> I'd like to nitpick on name here, as hi/lo of what? Maybe val_hi32 and
>>> val_lo32? Can we also reverse the order here? For x86 you'll be able
>>> to use &lo32 to get value directly if you really want, without a local
>>> copy. It also just logically seems better to have something low first,
>>> then high next.
>>
>> I can go with val_hi32, val_lo32 and put val_lo32 before val_hi32.
>> I don't have any preference for the ordering of these two fields.
>>
>>>
>>>
>>>>     };
>>>> 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 hi32 and 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, print 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 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 value size of BTF_KIND_ENUM64 is encoded to 8 to represent its intent.
>>>> The compiler ([1]) and pahole will generate BTF_KIND_ENUM64 only if the value has
>>>> to be represented with 64 bits.
>>>>
>>>>     [1] https://reviews.llvm.org/D124641
>>>>
>>>> Signed-off-by: Yonghong Song <yhs@fb.com>
[...]
>>>
>>>>           btf_show_end_type(show);
>>>>    }
>>>>
>>>> @@ -3770,6 +3779,109 @@ static struct btf_kind_operations enum_ops = {
>>>>           .show = btf_enum_show,
>>>>    };
>>>>
>>>> +static s32 btf_enum64_check_meta(struct btf_verifier_env *env,
>>>> +                                const struct btf_type *t,
>>>> +                                u32 meta_left)
>>>> +{
>>>> +       const struct btf_enum64 *enums = btf_type_enum64(t);
>>>> +       struct btf *btf = env->btf;
>>>> +       const char *fmt_str;
>>>> +       u16 i, nr_enums;
>>>> +       u32 meta_needed;
>>>> +
>>>> +       nr_enums = btf_type_vlen(t);
>>>> +       meta_needed = nr_enums * sizeof(*enums);
>>>> +
>>>> +       if (meta_left < meta_needed) {
>>>> +               btf_verifier_log_basic(env, t,
>>>> +                                      "meta_left:%u meta_needed:%u",
>>>> +                                      meta_left, meta_needed);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       if (t->size != 8) {
>>>
>>> technically there is nothing wrong with using enum64 for smaller
>>> sizes, right? Any particular reason to prevent this? We can just
>>> define that 64-bit value is sign-extended if enum is signed and has
>>> size < 8?
>>
>> My original idea is to support 64-bit enum only for ENUM64 kind.
>> But it is certainly possible to encode 32-bit enums as well for
>> ENUM64. So I will remove this restriction.
>>
>> The dwarf only generates sizes 4 (for up-to 32 bit values)
>> and 8 (for 64 bit values). But BTF_KIND_ENUM supports 1/2/4/8
>> sizes, so BTF_KIND_ENUM64 will also support 1/2/4/8 sizes.
> 
> Little known fact, but it's not true:
> 
> $ bpftool btf dump file /sys/kernel/btf/vmlinux| rg 'ENUM.*size=1' -A8
> [83476] ENUM 'hub_led_mode' size=1 vlen=8
>          'INDICATOR_AUTO' val=0
>          'INDICATOR_CYCLE' val=1
>          'INDICATOR_GREEN_BLINK' val=2
>          'INDICATOR_GREEN_BLINK_OFF' val=3
>          'INDICATOR_AMBER_BLINK' val=4
>          'INDICATOR_AMBER_BLINK_OFF' val=5
>          'INDICATOR_ALT_BLINK' val=6
>          'INDICATOR_ALT_BLINK_OFF' val=7
> 
> Defined as packed enum:
> 
> enum hub_led_mode {
>          INDICATOR_AUTO = 0,
>          INDICATOR_CYCLE,
>          /* software blinks for attention:  software, hardware, reserved */
>          INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
>          INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
>          INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
> } __attribute__ ((packed));

I am not aware of this.... Good to know.

> 
> 
>>
>>>
>>>> +               btf_verifier_log_type(env, t, "Unexpected size");
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       /* enum type either no name or a valid one */
>>>> +       if (t->name_off &&
>>>> +           !btf_name_valid_identifier(env->btf, t->name_off)) {
>>>> +               btf_verifier_log_type(env, t, "Invalid name");
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>
>>> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-10 23:38       ` Andrii Nakryiko
@ 2022-05-11  0:39         ` Yonghong Song
  2022-05-11 17:43           ` Andrii Nakryiko
  0 siblings, 1 reply; 47+ messages in thread
From: Yonghong Song @ 2022-05-11  0:39 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/10/22 4:38 PM, Andrii Nakryiko wrote:
> On Tue, May 10, 2022 at 3:40 PM Yonghong Song <yhs@fb.com> wrote:
>>
>>
>>
>> On 5/9/22 4:25 PM, Andrii Nakryiko wrote:
>>> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>>>
>>>> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
>>>> btf__add_enum_value() and introduced the following new APIs
>>>>     btf__add_enum32()
>>>>     btf__add_enum32_value()
>>>>     btf__add_enum64()
>>>>     btf__add_enum64_value()
>>>> due to new kind and introduction of kflag.
>>>>
>>>> To support old kernel with enum64, the sanitization is
>>>> added to replace BTF_KIND_ENUM64 with a bunch of
>>>> pointer-to-void types.
>>>>
>>>> The enum64 value relocation is also supported. The enum64
>>>> forward resolution, with enum type as forward declaration
>>>> and enum64 as the actual definition, is also supported.
>>>>
>>>> Signed-off-by: Yonghong Song <yhs@fb.com>
>>>> ---
>>>>    tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
>>>>    tools/lib/bpf/btf.h                           |  21 ++
>>>>    tools/lib/bpf/btf_dump.c                      |  94 ++++++--
>>>>    tools/lib/bpf/libbpf.c                        |  64 ++++-
>>>>    tools/lib/bpf/libbpf.map                      |   4 +
>>>>    tools/lib/bpf/libbpf_internal.h               |   2 +
>>>>    tools/lib/bpf/linker.c                        |   2 +
>>>>    tools/lib/bpf/relo_core.c                     |  93 ++++---
>>>>    .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
>>>>    .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
>>>>    10 files changed, 450 insertions(+), 72 deletions(-)
>>>>
>>>
> 
> [...]
> 
>>>
>>>
>>>> +       t->size = tsize;
>>>> +
>>>> +       return btf_commit_type(btf, sz);
>>>> +}
>>>> +
>>>> +/*
>>>> + * Append new BTF_KIND_ENUM type with:
>>>> + *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
>>>> + *   - *is_unsigned* - whether the enum values are unsigned or not;
>>>> + *
>>>> + * Enum initially has no enum values in it (and corresponds to enum forward
>>>> + * declaration). Enumerator values can be added by btf__add_enum64_value()
>>>> + * immediately after btf__add_enum() succeeds.
>>>> + *
>>>> + * Returns:
>>>> + *   - >0, type ID of newly added BTF type;
>>>> + *   - <0, on error.
>>>> + */
>>>> +int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned)
>>>
>>> given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
>>> btf__add_enum()/btf__add_enum_value() and not deprecate anything.
>>> ENUM64 can be thought about as more of a special case, so I think it's
>>> ok.
>>
>> The current btf__add_enum api:
>> LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32
>> bytes_sz);
>>
>> The issue is it doesn't have signedness parameter. if the user input
>> is
>>      enum { A = -1, B = 0, C = 1 };
>> the actual printout btf format will be
>>      enum { A 4294967295, B = 0, C = 1}
>> does not match the original source.
> 
> Oh, I didn't realize that's the reason. I still like btf__add_enum()
> name much better, can you please do the same macro trick that I did
> for bpf_prog_load() based on the number of arguments? We'll be able to
> preserve good API name and add extra argument. Once this lands we'll
> need to update pahole to added signedness bit, but otherwise I don't
> think there are many other users of these APIs currently (I might be
> wrong, but macro magic gives us backwards compat anyway).
> 
>>
>>>
>>>> +{
>>>> +       return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM, 4);
>>>> +}
>>>> +
>>>
>>> [...]
>>>
>>>>    /*
> 
> [...]
> 
>>>> @@ -764,8 +792,13 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
>>>>                   if (!spec)
>>>>                           return -EUCLEAN; /* request instruction poisoning */
>>>>                   t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
>>>> -               e = btf_enum(t) + spec->spec[0].idx;
>>>> -               *val = e->val;
>>>> +               if (btf_is_enum(t)) {
>>>> +                       e = btf_enum(t) + spec->spec[0].idx;
>>>> +                       *val = e->val;
>>>> +               } else {
>>>> +                       e64 = btf_enum64(t) + spec->spec[0].idx;
>>>> +                       *val = btf_enum64_value(e64);
>>>> +               }
>>>
>>> I think with sign bit we now have further complication: for 32-bit
>>> enums we need to sign extend 32-bit values to s64 and then cast as
>>> u64, no? Seems like a helper to abstract that is good to have here.
>>> Otherwise relocating enum ABC { D = -1 } will produce invalid ldimm64
>>> instruction, right?
>>
>> We should be fine here. For enum32, we have
>> struct btf_enum {
>>           __u32   name_off;
>>           __s32   val;
>> };
>> So above *val = e->val will first sign extend from __s32 to __s64
>> and then the __u64. Let me have a helper with additional comments
>> to make it clear.
>>
> 
> Ok, great! Let's just shorten this as I suggested below?

The
 >>> *val = btf_is_enum(t)
 >>>       ? btf_enum(t)[spec->spec[0].idx]
 >>>       : btf_enum64(t)[spec->spec[0].idx];
won't work, but the following should work:
    *val = btf_is_enum(t)
	? btf_enum(t)[spec->spec[0].idx].val
	: btf_enum64_value(btf_enum64(t) + spec->spec[0].idx);
> 
>>>
>>> Also keep in mind that you can use btf_enum()/btf_enum64() as an
>>> array, so above you can write just as
>>>
>>> *val = btf_is_enum(t)
>>>       ? btf_enum(t)[spec->spec[0].idx]
>>>       : btf_enum64(t)[spec->spec[0].idx];
>>>
>>> But we need sign check and extension, so better to have a separate helper.
>>>
>>>>                   break;
>>>>           default:
>>>>                   return -EOPNOTSUPP;
>>>> @@ -1034,7 +1067,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>>>                   }
>>>>
>>>>                   insn[0].imm = new_val;
>>>> -               insn[1].imm = 0; /* currently only 32-bit values are supported */
>>>> +               insn[1].imm = new_val >> 32;
>>>
>>> for 32-bit instructions (ALU/ALU32, etc) we need to make sure that
>>> new_val fits in 32 bits. And we need to be careful about
>>> signed/unsigned, because for signed case all-zero or all-one upper 32
>>> bits are ok (sign extension). Can we know the expected signed/unsigned
>>> operation from bpf_insn itself? We should be, right?
>>
>> The core relocation insn for constant is
>>     move r1, <32bit value>
>> or
>>     ldimm_64 r1, <64bit value>
>> and there are no signedness information.
>> So the 64bit value (except sign extension) can only from
>> ldimm_64. We should be okay here, but I can double check.
> 
> not sure how full 64-bit -1 should be loaded into register then. Does
> compiler generate extra sign-extending bit shifts or embedded constant
> is considered to be a signed constant always?

For ldimm64 r1, -1,
the first insn imm will be 0xffffffff, and the second insn will also be 
0xffffffff. The final value will be
   ((u64)(u32)0xffffffff << 32) | (u32)0xffffffff


> 
>>
>>>
>>>>                   pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>>>>                            prog_name, relo_idx, insn_idx,
>>>>                            (unsigned long long)imm, new_val);
>>>> @@ -1056,6 +1089,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>>>     */
> 
> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-11  0:39         ` Yonghong Song
@ 2022-05-11 17:43           ` Andrii Nakryiko
  2022-05-11 18:56             ` Yonghong Song
  0 siblings, 1 reply; 47+ messages in thread
From: Andrii Nakryiko @ 2022-05-11 17:43 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team

On Tue, May 10, 2022 at 5:39 PM Yonghong Song <yhs@fb.com> wrote:
>
>
>
> On 5/10/22 4:38 PM, Andrii Nakryiko wrote:
> > On Tue, May 10, 2022 at 3:40 PM Yonghong Song <yhs@fb.com> wrote:
> >>
> >>
> >>
> >> On 5/9/22 4:25 PM, Andrii Nakryiko wrote:
> >>> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
> >>>>
> >>>> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
> >>>> btf__add_enum_value() and introduced the following new APIs
> >>>>     btf__add_enum32()
> >>>>     btf__add_enum32_value()
> >>>>     btf__add_enum64()
> >>>>     btf__add_enum64_value()
> >>>> due to new kind and introduction of kflag.
> >>>>
> >>>> To support old kernel with enum64, the sanitization is
> >>>> added to replace BTF_KIND_ENUM64 with a bunch of
> >>>> pointer-to-void types.
> >>>>
> >>>> The enum64 value relocation is also supported. The enum64
> >>>> forward resolution, with enum type as forward declaration
> >>>> and enum64 as the actual definition, is also supported.
> >>>>
> >>>> Signed-off-by: Yonghong Song <yhs@fb.com>
> >>>> ---
> >>>>    tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
> >>>>    tools/lib/bpf/btf.h                           |  21 ++
> >>>>    tools/lib/bpf/btf_dump.c                      |  94 ++++++--
> >>>>    tools/lib/bpf/libbpf.c                        |  64 ++++-
> >>>>    tools/lib/bpf/libbpf.map                      |   4 +
> >>>>    tools/lib/bpf/libbpf_internal.h               |   2 +
> >>>>    tools/lib/bpf/linker.c                        |   2 +
> >>>>    tools/lib/bpf/relo_core.c                     |  93 ++++---
> >>>>    .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
> >>>>    .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
> >>>>    10 files changed, 450 insertions(+), 72 deletions(-)
> >>>>
> >>>
> >
> > [...]
> >
> >>>
> >>>
> >>>> +       t->size = tsize;
> >>>> +
> >>>> +       return btf_commit_type(btf, sz);
> >>>> +}
> >>>> +
> >>>> +/*
> >>>> + * Append new BTF_KIND_ENUM type with:
> >>>> + *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
> >>>> + *   - *is_unsigned* - whether the enum values are unsigned or not;
> >>>> + *
> >>>> + * Enum initially has no enum values in it (and corresponds to enum forward
> >>>> + * declaration). Enumerator values can be added by btf__add_enum64_value()
> >>>> + * immediately after btf__add_enum() succeeds.
> >>>> + *
> >>>> + * Returns:
> >>>> + *   - >0, type ID of newly added BTF type;
> >>>> + *   - <0, on error.
> >>>> + */
> >>>> +int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned)
> >>>
> >>> given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
> >>> btf__add_enum()/btf__add_enum_value() and not deprecate anything.
> >>> ENUM64 can be thought about as more of a special case, so I think it's
> >>> ok.
> >>
> >> The current btf__add_enum api:
> >> LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32
> >> bytes_sz);
> >>
> >> The issue is it doesn't have signedness parameter. if the user input
> >> is
> >>      enum { A = -1, B = 0, C = 1 };
> >> the actual printout btf format will be
> >>      enum { A 4294967295, B = 0, C = 1}
> >> does not match the original source.
> >
> > Oh, I didn't realize that's the reason. I still like btf__add_enum()
> > name much better, can you please do the same macro trick that I did
> > for bpf_prog_load() based on the number of arguments? We'll be able to
> > preserve good API name and add extra argument. Once this lands we'll
> > need to update pahole to added signedness bit, but otherwise I don't
> > think there are many other users of these APIs currently (I might be
> > wrong, but macro magic gives us backwards compat anyway).
> >
> >>
> >>>
> >>>> +{
> >>>> +       return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM, 4);
> >>>> +}
> >>>> +
> >>>
> >>> [...]
> >>>
> >>>>    /*
> >
> > [...]
> >
> >>>> @@ -764,8 +792,13 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
> >>>>                   if (!spec)
> >>>>                           return -EUCLEAN; /* request instruction poisoning */
> >>>>                   t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
> >>>> -               e = btf_enum(t) + spec->spec[0].idx;
> >>>> -               *val = e->val;
> >>>> +               if (btf_is_enum(t)) {
> >>>> +                       e = btf_enum(t) + spec->spec[0].idx;
> >>>> +                       *val = e->val;
> >>>> +               } else {
> >>>> +                       e64 = btf_enum64(t) + spec->spec[0].idx;
> >>>> +                       *val = btf_enum64_value(e64);
> >>>> +               }
> >>>
> >>> I think with sign bit we now have further complication: for 32-bit
> >>> enums we need to sign extend 32-bit values to s64 and then cast as
> >>> u64, no? Seems like a helper to abstract that is good to have here.
> >>> Otherwise relocating enum ABC { D = -1 } will produce invalid ldimm64
> >>> instruction, right?
> >>
> >> We should be fine here. For enum32, we have
> >> struct btf_enum {
> >>           __u32   name_off;
> >>           __s32   val;
> >> };
> >> So above *val = e->val will first sign extend from __s32 to __s64
> >> and then the __u64. Let me have a helper with additional comments
> >> to make it clear.
> >>
> >
> > Ok, great! Let's just shorten this as I suggested below?
>
> The
>  >>> *val = btf_is_enum(t)
>  >>>       ? btf_enum(t)[spec->spec[0].idx]
>  >>>       : btf_enum64(t)[spec->spec[0].idx];
> won't work, but the following should work:
>     *val = btf_is_enum(t)
>         ? btf_enum(t)[spec->spec[0].idx].val
>         : btf_enum64_value(btf_enum64(t) + spec->spec[0].idx);

yep, for consistency it should be btf_enum64(t)[spec->spec[0].idx],
but it's very minor, of course

> >
> >>>
> >>> Also keep in mind that you can use btf_enum()/btf_enum64() as an
> >>> array, so above you can write just as
> >>>
> >>> *val = btf_is_enum(t)
> >>>       ? btf_enum(t)[spec->spec[0].idx]
> >>>       : btf_enum64(t)[spec->spec[0].idx];
> >>>
> >>> But we need sign check and extension, so better to have a separate helper.
> >>>
> >>>>                   break;
> >>>>           default:
> >>>>                   return -EOPNOTSUPP;
> >>>> @@ -1034,7 +1067,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>>>                   }
> >>>>
> >>>>                   insn[0].imm = new_val;
> >>>> -               insn[1].imm = 0; /* currently only 32-bit values are supported */
> >>>> +               insn[1].imm = new_val >> 32;
> >>>
> >>> for 32-bit instructions (ALU/ALU32, etc) we need to make sure that
> >>> new_val fits in 32 bits. And we need to be careful about
> >>> signed/unsigned, because for signed case all-zero or all-one upper 32
> >>> bits are ok (sign extension). Can we know the expected signed/unsigned
> >>> operation from bpf_insn itself? We should be, right?
> >>
> >> The core relocation insn for constant is
> >>     move r1, <32bit value>
> >> or
> >>     ldimm_64 r1, <64bit value>
> >> and there are no signedness information.
> >> So the 64bit value (except sign extension) can only from
> >> ldimm_64. We should be okay here, but I can double check.
> >
> > not sure how full 64-bit -1 should be loaded into register then. Does
> > compiler generate extra sign-extending bit shifts or embedded constant
> > is considered to be a signed constant always?
>
> For ldimm64 r1, -1,
> the first insn imm will be 0xffffffff, and the second insn will also be
> 0xffffffff. The final value will be
>    ((u64)(u32)0xffffffff << 32) | (u32)0xffffffff

yeah, I get it for ldimm64, but I was specifically curious about move
instruction that only has 32-bit immediate value but assigns to full
64-bit r1? Is it treated as signed unconditionally?

>
>
> >
> >>
> >>>
> >>>>                   pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
> >>>>                            prog_name, relo_idx, insn_idx,
> >>>>                            (unsigned long long)imm, new_val);
> >>>> @@ -1056,6 +1089,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
> >>>>     */
> >
> > [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH bpf-next 04/12] libbpf: Add btf enum64 support
  2022-05-11 17:43           ` Andrii Nakryiko
@ 2022-05-11 18:56             ` Yonghong Song
  0 siblings, 0 replies; 47+ messages in thread
From: Yonghong Song @ 2022-05-11 18:56 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, Kernel Team



On 5/11/22 10:43 AM, Andrii Nakryiko wrote:
> On Tue, May 10, 2022 at 5:39 PM Yonghong Song <yhs@fb.com> wrote:
>>
>>
>>
>> On 5/10/22 4:38 PM, Andrii Nakryiko wrote:
>>> On Tue, May 10, 2022 at 3:40 PM Yonghong Song <yhs@fb.com> wrote:
>>>>
>>>>
>>>>
>>>> On 5/9/22 4:25 PM, Andrii Nakryiko wrote:
>>>>> On Sun, May 1, 2022 at 12:00 PM Yonghong Song <yhs@fb.com> wrote:
>>>>>>
>>>>>> Add BTF_KIND_ENUM64 support. Deprecated btf__add_enum() and
>>>>>> btf__add_enum_value() and introduced the following new APIs
>>>>>>      btf__add_enum32()
>>>>>>      btf__add_enum32_value()
>>>>>>      btf__add_enum64()
>>>>>>      btf__add_enum64_value()
>>>>>> due to new kind and introduction of kflag.
>>>>>>
>>>>>> To support old kernel with enum64, the sanitization is
>>>>>> added to replace BTF_KIND_ENUM64 with a bunch of
>>>>>> pointer-to-void types.
>>>>>>
>>>>>> The enum64 value relocation is also supported. The enum64
>>>>>> forward resolution, with enum type as forward declaration
>>>>>> and enum64 as the actual definition, is also supported.
>>>>>>
>>>>>> Signed-off-by: Yonghong Song <yhs@fb.com>
>>>>>> ---
>>>>>>     tools/lib/bpf/btf.c                           | 226 +++++++++++++++++-
>>>>>>     tools/lib/bpf/btf.h                           |  21 ++
>>>>>>     tools/lib/bpf/btf_dump.c                      |  94 ++++++--
>>>>>>     tools/lib/bpf/libbpf.c                        |  64 ++++-
>>>>>>     tools/lib/bpf/libbpf.map                      |   4 +
>>>>>>     tools/lib/bpf/libbpf_internal.h               |   2 +
>>>>>>     tools/lib/bpf/linker.c                        |   2 +
>>>>>>     tools/lib/bpf/relo_core.c                     |  93 ++++---
>>>>>>     .../selftests/bpf/prog_tests/btf_dump.c       |  10 +-
>>>>>>     .../selftests/bpf/prog_tests/btf_write.c      |   6 +-
>>>>>>     10 files changed, 450 insertions(+), 72 deletions(-)
>>>>>>
>>>>>
>>>
>>> [...]
>>>
>>>>>
>>>>>
>>>>>> +       t->size = tsize;
>>>>>> +
>>>>>> +       return btf_commit_type(btf, sz);
>>>>>> +}
>>>>>> +
>>>>>> +/*
>>>>>> + * Append new BTF_KIND_ENUM type with:
>>>>>> + *   - *name* - name of the enum, can be NULL or empty for anonymous enums;
>>>>>> + *   - *is_unsigned* - whether the enum values are unsigned or not;
>>>>>> + *
>>>>>> + * Enum initially has no enum values in it (and corresponds to enum forward
>>>>>> + * declaration). Enumerator values can be added by btf__add_enum64_value()
>>>>>> + * immediately after btf__add_enum() succeeds.
>>>>>> + *
>>>>>> + * Returns:
>>>>>> + *   - >0, type ID of newly added BTF type;
>>>>>> + *   - <0, on error.
>>>>>> + */
>>>>>> +int btf__add_enum32(struct btf *btf, const char *name, bool is_unsigned)
>>>>>
>>>>> given it's still BTF_KIND_ENUM in UAPI, let's keep 32-bit ones as just
>>>>> btf__add_enum()/btf__add_enum_value() and not deprecate anything.
>>>>> ENUM64 can be thought about as more of a special case, so I think it's
>>>>> ok.
>>>>
>>>> The current btf__add_enum api:
>>>> LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32
>>>> bytes_sz);
>>>>
>>>> The issue is it doesn't have signedness parameter. if the user input
>>>> is
>>>>       enum { A = -1, B = 0, C = 1 };
>>>> the actual printout btf format will be
>>>>       enum { A 4294967295, B = 0, C = 1}
>>>> does not match the original source.
>>>
>>> Oh, I didn't realize that's the reason. I still like btf__add_enum()
>>> name much better, can you please do the same macro trick that I did
>>> for bpf_prog_load() based on the number of arguments? We'll be able to
>>> preserve good API name and add extra argument. Once this lands we'll
>>> need to update pahole to added signedness bit, but otherwise I don't
>>> think there are many other users of these APIs currently (I might be
>>> wrong, but macro magic gives us backwards compat anyway).
>>>
>>>>
>>>>>
>>>>>> +{
>>>>>> +       return btf_add_enum_common(btf, name, is_unsigned, BTF_KIND_ENUM, 4);
>>>>>> +}
>>>>>> +
>>>>>
>>>>> [...]
>>>>>
>>>>>>     /*
>>>
>>> [...]
>>>
>>>>>> @@ -764,8 +792,13 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
>>>>>>                    if (!spec)
>>>>>>                            return -EUCLEAN; /* request instruction poisoning */
>>>>>>                    t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
>>>>>> -               e = btf_enum(t) + spec->spec[0].idx;
>>>>>> -               *val = e->val;
>>>>>> +               if (btf_is_enum(t)) {
>>>>>> +                       e = btf_enum(t) + spec->spec[0].idx;
>>>>>> +                       *val = e->val;
>>>>>> +               } else {
>>>>>> +                       e64 = btf_enum64(t) + spec->spec[0].idx;
>>>>>> +                       *val = btf_enum64_value(e64);
>>>>>> +               }
>>>>>
>>>>> I think with sign bit we now have further complication: for 32-bit
>>>>> enums we need to sign extend 32-bit values to s64 and then cast as
>>>>> u64, no? Seems like a helper to abstract that is good to have here.
>>>>> Otherwise relocating enum ABC { D = -1 } will produce invalid ldimm64
>>>>> instruction, right?
>>>>
>>>> We should be fine here. For enum32, we have
>>>> struct btf_enum {
>>>>            __u32   name_off;
>>>>            __s32   val;
>>>> };
>>>> So above *val = e->val will first sign extend from __s32 to __s64
>>>> and then the __u64. Let me have a helper with additional comments
>>>> to make it clear.
>>>>
>>>
>>> Ok, great! Let's just shorten this as I suggested below?
>>
>> The
>>   >>> *val = btf_is_enum(t)
>>   >>>       ? btf_enum(t)[spec->spec[0].idx]
>>   >>>       : btf_enum64(t)[spec->spec[0].idx];
>> won't work, but the following should work:
>>      *val = btf_is_enum(t)
>>          ? btf_enum(t)[spec->spec[0].idx].val
>>          : btf_enum64_value(btf_enum64(t) + spec->spec[0].idx);
> 
> yep, for consistency it should be btf_enum64(t)[spec->spec[0].idx],
> but it's very minor, of course
> 
>>>
>>>>>
>>>>> Also keep in mind that you can use btf_enum()/btf_enum64() as an
>>>>> array, so above you can write just as
>>>>>
>>>>> *val = btf_is_enum(t)
>>>>>        ? btf_enum(t)[spec->spec[0].idx]
>>>>>        : btf_enum64(t)[spec->spec[0].idx];
>>>>>
>>>>> But we need sign check and extension, so better to have a separate helper.
>>>>>
>>>>>>                    break;
>>>>>>            default:
>>>>>>                    return -EOPNOTSUPP;
>>>>>> @@ -1034,7 +1067,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>>>>>                    }
>>>>>>
>>>>>>                    insn[0].imm = new_val;
>>>>>> -               insn[1].imm = 0; /* currently only 32-bit values are supported */
>>>>>> +               insn[1].imm = new_val >> 32;
>>>>>
>>>>> for 32-bit instructions (ALU/ALU32, etc) we need to make sure that
>>>>> new_val fits in 32 bits. And we need to be careful about
>>>>> signed/unsigned, because for signed case all-zero or all-one upper 32
>>>>> bits are ok (sign extension). Can we know the expected signed/unsigned
>>>>> operation from bpf_insn itself? We should be, right?
>>>>
>>>> The core relocation insn for constant is
>>>>      move r1, <32bit value>
>>>> or
>>>>      ldimm_64 r1, <64bit value>
>>>> and there are no signedness information.
>>>> So the 64bit value (except sign extension) can only from
>>>> ldimm_64. We should be okay here, but I can double check.
>>>
>>> not sure how full 64-bit -1 should be loaded into register then. Does
>>> compiler generate extra sign-extending bit shifts or embedded constant
>>> is considered to be a signed constant always?
>>
>> For ldimm64 r1, -1,
>> the first insn imm will be 0xffffffff, and the second insn will also be
>> 0xffffffff. The final value will be
>>     ((u64)(u32)0xffffffff << 32) | (u32)0xffffffff
> 
> yeah, I get it for ldimm64, but I was specifically curious about move
> instruction that only has 32-bit immediate value but assigns to full
> 64-bit r1? Is it treated as signed unconditionally?

Yes, it is treated as 32-bit signed int and will do sign extension
if needed.

> 
>>
>>
>>>
>>>>
>>>>>
>>>>>>                    pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
>>>>>>                             prog_name, relo_idx, insn_idx,
>>>>>>                             (unsigned long long)imm, new_val);
>>>>>> @@ -1056,6 +1089,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
>>>>>>      */
>>>
>>> [...]

^ permalink raw reply	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2022-05-11 18:56 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-01 19:00 [PATCH bpf-next 00/12] bpf: Add 64bit enum value support Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 01/12] bpf: Add btf enum64 support Yonghong Song
2022-05-09  0:33   ` Dave Marchevsky
2022-05-09 22:29   ` Andrii Nakryiko
2022-05-10 22:06     ` Yonghong Song
2022-05-10 23:18       ` Andrii Nakryiko
2022-05-11  0:17         ` Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 02/12] libbpf: Permit 64bit relocation value Yonghong Song
2022-05-09  1:06   ` Dave Marchevsky
2022-05-10 19:35     ` Yonghong Song
2022-05-09 22:37   ` Andrii Nakryiko
2022-05-10 22:14     ` Yonghong Song
2022-05-10 23:19       ` Andrii Nakryiko
2022-05-01 19:00 ` [PATCH bpf-next 03/12] libbpf: Fix an error in 64bit relocation value computation Yonghong Song
2022-05-09  0:55   ` Dave Marchevsky
2022-05-09  0:56     ` Dave Marchevsky
2022-05-09 22:37   ` Andrii Nakryiko
2022-05-10 22:11     ` Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 04/12] libbpf: Add btf enum64 support Yonghong Song
2022-05-03 17:22   ` kernel test robot
2022-05-05 22:44     ` Yonghong Song
2022-05-09 23:25   ` Andrii Nakryiko
2022-05-10 22:40     ` Yonghong Song
2022-05-10 23:02       ` Yonghong Song
2022-05-10 23:40         ` Andrii Nakryiko
2022-05-10 23:38       ` Andrii Nakryiko
2022-05-11  0:39         ` Yonghong Song
2022-05-11 17:43           ` Andrii Nakryiko
2022-05-11 18:56             ` Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 05/12] bpftool: " Yonghong Song
2022-05-09 23:31   ` Andrii Nakryiko
2022-05-10 22:43     ` Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 06/12] selftests/bpf: Fix selftests failure Yonghong Song
2022-05-09  2:21   ` Dave Marchevsky
2022-05-10 19:40     ` Yonghong Song
2022-05-09 23:34   ` Andrii Nakryiko
2022-05-10 22:44     ` Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 07/12] selftests/bpf: Test new libbpf enum32/enum64 API functions Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 08/12] selftests/bpf: Add BTF_KIND_ENUM64 unit tests Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 09/12] selftests/bpf: Test BTF_KIND_ENUM64 for deduplication Yonghong Song
2022-05-09 23:37   ` Andrii Nakryiko
2022-05-10 22:44     ` Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 10/12] selftests/bpf: add a test for enum64 value relocation Yonghong Song
2022-05-09 23:38   ` Andrii Nakryiko
2022-05-10 22:45     ` Yonghong Song
2022-05-01 19:00 ` [PATCH bpf-next 11/12] selftests/bpf: Clarify llvm dependency with possible selftest failures Yonghong Song
2022-05-01 19:01 ` [PATCH bpf-next 12/12] docs/bpf: Update documentation for BTF_KIND_ENUM64 support Yonghong Song

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).