Merge branch 'CO-RE relocation selftests fixes'
Andrii Nakryiko says: ==================== Lorenz Bauer noticed that core_reloc selftest has two inverted CHECK() conditions, allowing failing tests to pass unnoticed. Fixing that opened up few long-standing (field existence and direct memory bitfields) and one recent failures (BTF_KIND_FLOAT relos). This patch set fixes core_reloc selftest to capture such failures reliably in the future. It also fixes all the newly failing tests. See individual patches for details. This patch set also completes a set of ASSERT_xxx() macros, so now there should be a very little reason to use verbose and error-prone generic CHECK() macro. v1->v2: - updated bpf_core_fields_are_compat() comment to mention FLOAT (Lorenz). Cc: Lorenz Bauer <lmb@cloudflare.com> ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
9273150418
|
@ -88,11 +88,19 @@ enum bpf_enum_value_kind {
|
|||
const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \
|
||||
unsigned long long val; \
|
||||
\
|
||||
/* This is a so-called barrier_var() operation that makes specified \
|
||||
* variable "a black box" for optimizing compiler. \
|
||||
* It forces compiler to perform BYTE_OFFSET relocation on p and use \
|
||||
* its calculated value in the switch below, instead of applying \
|
||||
* the same relocation 4 times for each individual memory load. \
|
||||
*/ \
|
||||
asm volatile("" : "=r"(p) : "0"(p)); \
|
||||
\
|
||||
switch (__CORE_RELO(s, field, BYTE_SIZE)) { \
|
||||
case 1: val = *(const unsigned char *)p; \
|
||||
case 2: val = *(const unsigned short *)p; \
|
||||
case 4: val = *(const unsigned int *)p; \
|
||||
case 8: val = *(const unsigned long long *)p; \
|
||||
case 1: val = *(const unsigned char *)p; break; \
|
||||
case 2: val = *(const unsigned short *)p; break; \
|
||||
case 4: val = *(const unsigned int *)p; break; \
|
||||
case 8: val = *(const unsigned long long *)p; break; \
|
||||
} \
|
||||
val <<= __CORE_RELO(s, field, LSHIFT_U64); \
|
||||
if (__CORE_RELO(s, field, SIGNED)) \
|
||||
|
|
|
@ -5115,6 +5115,7 @@ err_out:
|
|||
* least one of enums should be anonymous;
|
||||
* - for ENUMs, check sizes, names are ignored;
|
||||
* - for INT, size and signedness are ignored;
|
||||
* - any two FLOATs are always compatible;
|
||||
* - for ARRAY, dimensionality is ignored, element types are checked for
|
||||
* compatibility recursively;
|
||||
* - everything else shouldn't be ever a target of relocation.
|
||||
|
@ -5141,6 +5142,7 @@ recur:
|
|||
|
||||
switch (btf_kind(local_type)) {
|
||||
case BTF_KIND_PTR:
|
||||
case BTF_KIND_FLOAT:
|
||||
return 1;
|
||||
case BTF_KIND_FWD:
|
||||
case BTF_KIND_ENUM: {
|
||||
|
@ -6245,8 +6247,8 @@ patch_insn:
|
|||
/* bpf_core_patch_insn() should know how to handle missing targ_spec */
|
||||
err = bpf_core_patch_insn(prog, relo, relo_idx, &targ_res);
|
||||
if (err) {
|
||||
pr_warn("prog '%s': relo #%d: failed to patch insn at offset %d: %d\n",
|
||||
prog->name, relo_idx, relo->insn_off, err);
|
||||
pr_warn("prog '%s': relo #%d: failed to patch insn #%zu: %d\n",
|
||||
prog->name, relo_idx, relo->insn_off / BPF_INSN_SZ, err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
|
|||
|
||||
snprintf(out_file, sizeof(out_file), "/tmp/%s.output.XXXXXX", t->file);
|
||||
fd = mkstemp(out_file);
|
||||
if (CHECK(fd < 0, "create_tmp", "failed to create file: %d\n", fd)) {
|
||||
if (!ASSERT_GE(fd, 0, "create_tmp")) {
|
||||
err = fd;
|
||||
goto done;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include <test_progs.h>
|
||||
#include <bpf/btf.h>
|
||||
|
||||
static int duration = 0;
|
||||
|
||||
void test_btf_endian() {
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
enum btf_endianness endian = BTF_LITTLE_ENDIAN;
|
||||
|
@ -71,7 +69,7 @@ void test_btf_endian() {
|
|||
|
||||
/* now modify original BTF */
|
||||
var_id = btf__add_var(btf, "some_var", BTF_VAR_GLOBAL_ALLOCATED, 1);
|
||||
CHECK(var_id <= 0, "var_id", "failed %d\n", var_id);
|
||||
ASSERT_GT(var_id, 0, "var_id");
|
||||
|
||||
btf__free(swap_btf);
|
||||
swap_btf = NULL;
|
||||
|
|
|
@ -54,7 +54,7 @@ void test_cgroup_link(void)
|
|||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
cgs[i].fd = create_and_get_cgroup(cgs[i].path);
|
||||
if (CHECK(cgs[i].fd < 0, "cg_create", "fail: %d\n", cgs[i].fd))
|
||||
if (!ASSERT_GE(cgs[i].fd, 0, "cg_create"))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
|
@ -210,11 +210,6 @@ static int duration = 0;
|
|||
.bpf_obj_file = "test_core_reloc_existence.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o" \
|
||||
|
||||
#define FIELD_EXISTS_ERR_CASE(name) { \
|
||||
FIELD_EXISTS_CASE_COMMON(name), \
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
#define BITFIELDS_CASE_COMMON(objfile, test_name_prefix, name) \
|
||||
.case_name = test_name_prefix#name, \
|
||||
.bpf_obj_file = objfile, \
|
||||
|
@ -222,7 +217,7 @@ static int duration = 0;
|
|||
|
||||
#define BITFIELDS_CASE(name, ...) { \
|
||||
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \
|
||||
"direct:", name), \
|
||||
"probed:", name), \
|
||||
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
|
||||
|
@ -230,7 +225,7 @@ static int duration = 0;
|
|||
.output_len = sizeof(struct core_reloc_bitfields_output), \
|
||||
}, { \
|
||||
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \
|
||||
"probed:", name), \
|
||||
"direct:", name), \
|
||||
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
|
||||
|
@ -551,8 +546,7 @@ static struct core_reloc_test_case test_cases[] = {
|
|||
ARRAYS_ERR_CASE(arrays___err_too_small),
|
||||
ARRAYS_ERR_CASE(arrays___err_too_shallow),
|
||||
ARRAYS_ERR_CASE(arrays___err_non_array),
|
||||
ARRAYS_ERR_CASE(arrays___err_wrong_val_type1),
|
||||
ARRAYS_ERR_CASE(arrays___err_wrong_val_type2),
|
||||
ARRAYS_ERR_CASE(arrays___err_wrong_val_type),
|
||||
ARRAYS_ERR_CASE(arrays___err_bad_zero_sz_arr),
|
||||
|
||||
/* enum/ptr/int handling scenarios */
|
||||
|
@ -643,13 +637,25 @@ static struct core_reloc_test_case test_cases[] = {
|
|||
},
|
||||
.output_len = sizeof(struct core_reloc_existence_output),
|
||||
},
|
||||
|
||||
FIELD_EXISTS_ERR_CASE(existence__err_int_sz),
|
||||
FIELD_EXISTS_ERR_CASE(existence__err_int_type),
|
||||
FIELD_EXISTS_ERR_CASE(existence__err_int_kind),
|
||||
FIELD_EXISTS_ERR_CASE(existence__err_arr_kind),
|
||||
FIELD_EXISTS_ERR_CASE(existence__err_arr_value_type),
|
||||
FIELD_EXISTS_ERR_CASE(existence__err_struct_type),
|
||||
{
|
||||
FIELD_EXISTS_CASE_COMMON(existence___wrong_field_defs),
|
||||
.input = STRUCT_TO_CHAR_PTR(core_reloc_existence___wrong_field_defs) {
|
||||
},
|
||||
.input_len = sizeof(struct core_reloc_existence___wrong_field_defs),
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) {
|
||||
.a_exists = 0,
|
||||
.b_exists = 0,
|
||||
.c_exists = 0,
|
||||
.arr_exists = 0,
|
||||
.s_exists = 0,
|
||||
.a_value = 0xff000001u,
|
||||
.b_value = 0xff000002u,
|
||||
.c_value = 0xff000003u,
|
||||
.arr_value = 0xff000004u,
|
||||
.s_value = 0xff000005u,
|
||||
},
|
||||
.output_len = sizeof(struct core_reloc_existence_output),
|
||||
},
|
||||
|
||||
/* bitfield relocation checks */
|
||||
BITFIELDS_CASE(bitfields, {
|
||||
|
@ -858,13 +864,20 @@ void test_core_reloc(void)
|
|||
"prog '%s' not found\n", probe_name))
|
||||
goto cleanup;
|
||||
|
||||
|
||||
if (test_case->btf_src_file) {
|
||||
err = access(test_case->btf_src_file, R_OK);
|
||||
if (!ASSERT_OK(err, "btf_src_file"))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
load_attr.obj = obj;
|
||||
load_attr.log_level = 0;
|
||||
load_attr.target_btf_path = test_case->btf_src_file;
|
||||
err = bpf_object__load_xattr(&load_attr);
|
||||
if (err) {
|
||||
if (!test_case->fails)
|
||||
CHECK(false, "obj_load", "failed to load prog '%s': %d\n", probe_name, err);
|
||||
ASSERT_OK(err, "obj_load");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
@ -903,10 +916,8 @@ void test_core_reloc(void)
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if (test_case->fails) {
|
||||
CHECK(false, "obj_load_fail", "should fail to load prog '%s'\n", probe_name);
|
||||
if (!ASSERT_FALSE(test_case->fails, "obj_load_should_fail"))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
equal = memcmp(data->out, test_case->output,
|
||||
test_case->output_len) == 0;
|
||||
|
|
|
@ -134,7 +134,7 @@ void test_kfree_skb(void)
|
|||
/* make sure kfree_skb program was triggered
|
||||
* and it sent expected skb into ring buffer
|
||||
*/
|
||||
CHECK_FAIL(!passed);
|
||||
ASSERT_TRUE(passed, "passed");
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(global_data), &zero, test_ok);
|
||||
if (CHECK(err, "get_result",
|
||||
|
|
|
@ -160,11 +160,8 @@ int test_resolve_btfids(void)
|
|||
break;
|
||||
|
||||
if (i > 0) {
|
||||
ret = CHECK(test_set.ids[i - 1] > test_set.ids[i],
|
||||
"sort_check",
|
||||
"test_set is not sorted\n");
|
||||
if (ret)
|
||||
break;
|
||||
if (!ASSERT_LE(test_set.ids[i - 1], test_set.ids[i], "sort_check"))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ void test_snprintf_btf(void)
|
|||
* and it set expected return values from bpf_trace_printk()s
|
||||
* and all tests ran.
|
||||
*/
|
||||
if (CHECK(bss->ret <= 0,
|
||||
"bpf_snprintf_btf: got return value",
|
||||
"ret <= 0 %ld test %d\n", bss->ret, bss->ran_subtests))
|
||||
if (!ASSERT_GT(bss->ret, 0, "bpf_snprintf_ret"))
|
||||
goto cleanup;
|
||||
|
||||
if (CHECK(bss->ran_subtests == 0, "check if subtests ran",
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_existence___err_wrong_arr_kind x) {}
|
|
@ -1,3 +0,0 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_existence___err_wrong_arr_value_type x) {}
|
|
@ -1,3 +0,0 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_existence___err_wrong_int_kind x) {}
|
|
@ -1,3 +0,0 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_existence___err_wrong_int_sz x) {}
|
|
@ -1,3 +0,0 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_existence___err_wrong_int_type x) {}
|
|
@ -1,3 +0,0 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_existence___err_wrong_struct_type x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_existence___wrong_field_defs x) {}
|
|
@ -700,27 +700,11 @@ struct core_reloc_existence___minimal {
|
|||
int a;
|
||||
};
|
||||
|
||||
struct core_reloc_existence___err_wrong_int_sz {
|
||||
short a;
|
||||
};
|
||||
|
||||
struct core_reloc_existence___err_wrong_int_type {
|
||||
struct core_reloc_existence___wrong_field_defs {
|
||||
void *a;
|
||||
int b[1];
|
||||
};
|
||||
|
||||
struct core_reloc_existence___err_wrong_int_kind {
|
||||
struct{ int x; } c;
|
||||
};
|
||||
|
||||
struct core_reloc_existence___err_wrong_arr_kind {
|
||||
int arr;
|
||||
};
|
||||
|
||||
struct core_reloc_existence___err_wrong_arr_value_type {
|
||||
short arr[1];
|
||||
};
|
||||
|
||||
struct core_reloc_existence___err_wrong_struct_type {
|
||||
int s;
|
||||
};
|
||||
|
||||
|
|
|
@ -130,6 +130,20 @@ extern int test__join_cgroup(const char *path);
|
|||
#define CHECK_ATTR(condition, tag, format...) \
|
||||
_CHECK(condition, tag, tattr.duration, format)
|
||||
|
||||
#define ASSERT_TRUE(actual, name) ({ \
|
||||
static int duration = 0; \
|
||||
bool ___ok = (actual); \
|
||||
CHECK(!___ok, (name), "unexpected %s: got FALSE\n", (name)); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_FALSE(actual, name) ({ \
|
||||
static int duration = 0; \
|
||||
bool ___ok = !(actual); \
|
||||
CHECK(!___ok, (name), "unexpected %s: got TRUE\n", (name)); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_EQ(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
typeof(actual) ___act = (actual); \
|
||||
|
@ -163,6 +177,39 @@ extern int test__join_cgroup(const char *path);
|
|||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_LE(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
typeof(actual) ___act = (actual); \
|
||||
typeof(expected) ___exp = (expected); \
|
||||
bool ___ok = ___act <= ___exp; \
|
||||
CHECK(!___ok, (name), \
|
||||
"unexpected %s: actual %lld > expected %lld\n", \
|
||||
(name), (long long)(___act), (long long)(___exp)); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_GT(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
typeof(actual) ___act = (actual); \
|
||||
typeof(expected) ___exp = (expected); \
|
||||
bool ___ok = ___act > ___exp; \
|
||||
CHECK(!___ok, (name), \
|
||||
"unexpected %s: actual %lld <= expected %lld\n", \
|
||||
(name), (long long)(___act), (long long)(___exp)); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_GE(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
typeof(actual) ___act = (actual); \
|
||||
typeof(expected) ___exp = (expected); \
|
||||
bool ___ok = ___act >= ___exp; \
|
||||
CHECK(!___ok, (name), \
|
||||
"unexpected %s: actual %lld < expected %lld\n", \
|
||||
(name), (long long)(___act), (long long)(___exp)); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_STREQ(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
const char *___act = actual; \
|
||||
|
@ -178,7 +225,8 @@ extern int test__join_cgroup(const char *path);
|
|||
static int duration = 0; \
|
||||
long long ___res = (res); \
|
||||
bool ___ok = ___res == 0; \
|
||||
CHECK(!___ok, (name), "unexpected error: %lld\n", ___res); \
|
||||
CHECK(!___ok, (name), "unexpected error: %lld (errno %d)\n", \
|
||||
___res, errno); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue