Commit 590699d8 authored by Saket Kumar Bhaskar's avatar Saket Kumar Bhaskar Committed by Alexei Starovoitov
Browse files

selftests/bpf: Fix htab_update/reenter_update selftest failure



Since commit 31158ad0 ("rqspinlock: Add deadlock detection
and recovery") the updated path on re-entrancy now reports deadlock
via -EDEADLK instead of the previous -EBUSY.

Also, the way reentrancy was exercised (via fentry/lookup_elem_raw)
has been fragile because lookup_elem_raw may be inlined
(find_kernel_btf_id() will return -ESRCH).

To fix this fentry is attached to bpf_obj_free_fields() instead of
lookup_elem_raw() and:

- The htab map is made to use a BTF-described struct val with a
  struct bpf_timer so that check_and_free_fields() reliably calls
  bpf_obj_free_fields() on element replacement.

- The selftest is updated to do two updates to the same key (insert +
  replace) in prog_test.

- The selftest is updated to align with expected errno with the
  kernel’s current behavior.

Signed-off-by: default avatarSaket Kumar Bhaskar <skb99@linux.ibm.com>
Tested-by: default avatarVenkat Rao Bagalkote <venkat88@linux.ibm.com>
Link: https://lore.kernel.org/r/20251117060752.129648-1-skb99@linux.ibm.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 4617b306
Loading
Loading
Loading
Loading
+28 −9
Original line number Diff line number Diff line
@@ -15,17 +15,17 @@ struct htab_update_ctx {
static void test_reenter_update(void)
{
	struct htab_update *skel;
	unsigned int key, value;
	void *value = NULL;
	unsigned int key, value_size;
	int err;

	skel = htab_update__open();
	if (!ASSERT_OK_PTR(skel, "htab_update__open"))
		return;

	/* lookup_elem_raw() may be inlined and find_kernel_btf_id() will return -ESRCH */
	bpf_program__set_autoload(skel->progs.lookup_elem_raw, true);
	bpf_program__set_autoload(skel->progs.bpf_obj_free_fields, true);
	err = htab_update__load(skel);
	if (!ASSERT_TRUE(!err || err == -ESRCH, "htab_update__load") || err)
	if (!ASSERT_TRUE(!err, "htab_update__load") || err)
		goto out;

	skel->bss->pid = getpid();
@@ -33,14 +33,33 @@ static void test_reenter_update(void)
	if (!ASSERT_OK(err, "htab_update__attach"))
		goto out;

	/* Will trigger the reentrancy of bpf_map_update_elem() */
	value_size = bpf_map__value_size(skel->maps.htab);

	value = calloc(1, value_size);
	if (!ASSERT_OK_PTR(value, "calloc value"))
		goto out;
	/*
	 * First update: plain insert. This should NOT trigger the re-entrancy
	 * path, because there is no old element to free yet.
	 */
	key = 0;
	value = 0;
	err = bpf_map_update_elem(bpf_map__fd(skel->maps.htab), &key, &value, 0);
	if (!ASSERT_OK(err, "add element"))
	err = bpf_map_update_elem(bpf_map__fd(skel->maps.htab), &key, value, BPF_ANY);
	if (!ASSERT_OK(err, "first update (insert)"))
		goto out;

	/*
	 * Second update: replace existing element with same key and trigger
	 * the reentrancy of bpf_map_update_elem().
	 * check_and_free_fields() calls bpf_obj_free_fields() on the old
	 * value, which is where fentry program runs and performs a nested
	 * bpf_map_update_elem(), triggering -EDEADLK.
	 */
	memset(value, 0, value_size);
	err = bpf_map_update_elem(bpf_map__fd(skel->maps.htab), &key, value, BPF_ANY);
	if (!ASSERT_OK(err, "second update (replace)"))
		goto out;

	ASSERT_EQ(skel->bss->update_err, -EBUSY, "no reentrancy");
	ASSERT_EQ(skel->bss->update_err, -EDEADLK, "no reentrancy");
out:
	htab_update__destroy(skel);
}
+13 −6
Original line number Diff line number Diff line
@@ -6,24 +6,31 @@

char _license[] SEC("license") = "GPL";

/* Map value type: has BTF-managed field (bpf_timer) */
struct val {
	struct bpf_timer t;
	__u64 payload;
};

struct {
	__uint(type, BPF_MAP_TYPE_HASH);
	__uint(max_entries, 1);
	__uint(key_size, sizeof(__u32));
	__uint(value_size, sizeof(__u32));
	__type(key, __u32);
	__type(value, struct val);
} htab SEC(".maps");

int pid = 0;
int update_err = 0;

SEC("?fentry/lookup_elem_raw")
int lookup_elem_raw(void *ctx)
SEC("?fentry/bpf_obj_free_fields")
int bpf_obj_free_fields(void *ctx)
{
	__u32 key = 0, value = 1;
	__u32 key = 0;
	struct val value = { .payload = 1 };

	if ((bpf_get_current_pid_tgid() >> 32) != pid)
		return 0;

	update_err = bpf_map_update_elem(&htab, &key, &value, 0);
	update_err = bpf_map_update_elem(&htab, &key, &value, BPF_ANY);
	return 0;
}