* [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg
@ 2026-02-14 12:40 Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 1/5] bpf: allow calling bpf_kptr_xchg while holding a lock Chengkaitao
` (5 more replies)
0 siblings, 6 replies; 8+ messages in thread
From: Chengkaitao @ 2026-02-14 12:40 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
When using bpf_kptr_xchg, we triggered the following error:
31: (85) call bpf_kptr_xchg#194
function calls are not allowed while holding a lock
bpf_kptr_xchg can now be used in lock-held contexts, so we extended
its usage scope in [patch 1/5].
When writing test cases using bpf_kptr_xchg and bpf_rbtree_*, the
following approach must be followed:
bpf_spin_lock(&lock);
rb_n = bpf_rbtree_root(&root);
while (rb_n && can_loop) {
rb_n = bpf_rbtree_remove(&root, rb_n);
if (!rb_n)
goto fail;
tnode = container_of(rb_n, struct tree_node, node);
node_data = bpf_kptr_xchg(&tnode->node_data, NULL);
if (!node_data)
goto fail;
data = node_data->data;
/* use data to do something */
node_data = bpf_kptr_xchg(&tnode->node_data, node_data);
if (node_data)
goto fail;
bpf_rbtree_add(&root, rb_n, less);
if (lookup_key < tnode->key)
rb_n = bpf_rbtree_left(&root, rb_n);
else
rb_n = bpf_rbtree_right(&root, rb_n);
}
bpf_spin_unlock(&lock);
The above illustrates a lock-remove-read-add-unlock workflow, which
exhibits lower performance. To address this, we introduced support
for a streamlined lock-read-unlock operation in [patch 2/5] and
[patch 4/5].
Changes in v7:
- Add a comma to the variable declaration in enum bpf_reg_type
- Modify the prefixes
Changes in v6:
- allow using bpf_kptr_xchg even if the MEM_RCU flag is set
- Add test case
Changes in v5:
- add lastname
Changes in v4:
- Fix the dead logic issue in the test case
Changes in v3:
- Fix compilation errors
Changes in v2:
- Allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set
- Add test case
Link to v6:
https://lore.kernel.org/all/20260208024846.18653-1-pilgrimtao@gmail.com/
Link to v5:
https://lore.kernel.org/all/20260203022712.99347-1-pilgrimtao@gmail.com/
Link to v4:
https://lore.kernel.org/all/20260202090051.87802-1-pilgrimtao@gmail.com/
Link to V3:
https://lore.kernel.org/all/20260202055818.78231-1-pilgrimtao@gmail.com/
Link to V2:
https://lore.kernel.org/all/20260201031607.32940-1-pilgrimtao@gmail.com/
Link to V1:
https://lore.kernel.org/all/20260122081426.78472-1-pilgrimtao@gmail.com/
Kaitao Cheng (5):
bpf: allow calling bpf_kptr_xchg while holding a lock
bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set
selftests/bpf: Add supplementary tests for bpf_kptr_xchg
bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set
selftests/bpf: Add test case for rbtree nodes that contain both
bpf_refcount and kptr fields.
kernel/bpf/verifier.c | 9 +-
.../testing/selftests/bpf/prog_tests/rbtree.c | 6 +
tools/testing/selftests/bpf/progs/bpf_misc.h | 4 +
.../selftests/bpf/progs/rbtree_search_kptr.c | 290 ++++++++++++++++++
4 files changed, 307 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH RESEND v7 1/5] bpf: allow calling bpf_kptr_xchg while holding a lock
2026-02-14 12:40 [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg Chengkaitao
@ 2026-02-14 12:40 ` Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 2/5] bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set Chengkaitao
` (4 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Chengkaitao @ 2026-02-14 12:40 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
For the following scenario:
struct tree_node {
struct bpf_rb_node node;
struct request __kptr *req;
u64 key;
};
struct bpf_rb_root tree_root __contains(tree_node, node);
struct bpf_spin_lock tree_lock;
If we need to traverse all nodes in the rbtree, retrieve the __kptr
pointer from each node, and read kernel data from the referenced
object, using bpf_kptr_xchg appears unavoidable.
This patch skips the BPF verifier checks for bpf_kptr_xchg when
called while holding a lock.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
kernel/bpf/verifier.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index edf5342b982f..2927a4622ff8 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -21044,7 +21044,8 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
if (env->cur_state->active_locks) {
if ((insn->src_reg == BPF_REG_0 &&
- insn->imm != BPF_FUNC_spin_unlock) ||
+ insn->imm != BPF_FUNC_spin_unlock &&
+ insn->imm != BPF_FUNC_kptr_xchg) ||
(insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
(insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) {
verbose(env,
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH RESEND v7 2/5] bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set
2026-02-14 12:40 [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 1/5] bpf: allow calling bpf_kptr_xchg while holding a lock Chengkaitao
@ 2026-02-14 12:40 ` Chengkaitao
2026-02-14 13:09 ` bot+bpf-ci
2026-02-14 12:40 ` [PATCH RESEND v7 3/5] selftests/bpf: Add supplementary tests for bpf_kptr_xchg Chengkaitao
` (3 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: Chengkaitao @ 2026-02-14 12:40 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
When traversing an rbtree using bpf_rbtree_left/right, if bpf_kptr_xchg
is used to access the __kptr pointer contained in a node, it currently
requires first removing the node with bpf_rbtree_remove and clearing the
NON_OWN_REF flag, then re-adding the node to the original rbtree with
bpf_rbtree_add after usage. This process significantly degrades rbtree
traversal performance. The patch enables accessing __kptr pointers with
the NON_OWN_REF flag set while holding the lock, eliminating the need
for this remove-read-add sequence.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
Signed-off-by: Feng Yang <yangfeng@kylinos.cn>
---
kernel/bpf/verifier.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2927a4622ff8..3536a91ff8c7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9329,7 +9329,8 @@ static const struct bpf_reg_types timer_types = { .types = { PTR_TO_MAP_VALUE }
static const struct bpf_reg_types kptr_xchg_dest_types = {
.types = {
PTR_TO_MAP_VALUE,
- PTR_TO_BTF_ID | MEM_ALLOC
+ PTR_TO_BTF_ID | MEM_ALLOC,
+ PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
}
};
static const struct bpf_reg_types dynptr_types = {
@@ -9489,6 +9490,7 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
}
case PTR_TO_BTF_ID | MEM_ALLOC:
case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
+ case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock &&
meta->func_id != BPF_FUNC_kptr_xchg) {
verifier_bug(env, "unimplemented handling of MEM_ALLOC");
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH RESEND v7 3/5] selftests/bpf: Add supplementary tests for bpf_kptr_xchg
2026-02-14 12:40 [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 1/5] bpf: allow calling bpf_kptr_xchg while holding a lock Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 2/5] bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set Chengkaitao
@ 2026-02-14 12:40 ` Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set Chengkaitao
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Chengkaitao @ 2026-02-14 12:40 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
1. Allow using bpf_kptr_xchg while holding a lock.
2. When the rb_node contains a __kptr pointer, we do not need to
perform a remove-read-add operation.
This patch implements the following workflow:
1. Construct a rbtree with 16 elements.
2. Traverse the rbtree, locate the kptr pointer in the target node,
and read the content pointed to by the pointer.
3. Remove all nodes from the rbtree.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
Signed-off-by: Feng Yang <yangfeng@kylinos.cn>
---
.../testing/selftests/bpf/prog_tests/rbtree.c | 6 +
tools/testing/selftests/bpf/progs/bpf_misc.h | 4 +
.../selftests/bpf/progs/rbtree_search_kptr.c | 167 ++++++++++++++++++
3 files changed, 177 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
diff --git a/tools/testing/selftests/bpf/prog_tests/rbtree.c b/tools/testing/selftests/bpf/prog_tests/rbtree.c
index d8f3d7a45fe9..a854fb38e418 100644
--- a/tools/testing/selftests/bpf/prog_tests/rbtree.c
+++ b/tools/testing/selftests/bpf/prog_tests/rbtree.c
@@ -9,6 +9,7 @@
#include "rbtree_btf_fail__wrong_node_type.skel.h"
#include "rbtree_btf_fail__add_wrong_type.skel.h"
#include "rbtree_search.skel.h"
+#include "rbtree_search_kptr.skel.h"
static void test_rbtree_add_nodes(void)
{
@@ -193,3 +194,8 @@ void test_rbtree_search(void)
{
RUN_TESTS(rbtree_search);
}
+
+void test_rbtree_search_kptr(void)
+{
+ RUN_TESTS(rbtree_search_kptr);
+}
diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index c9bfbe1bafc1..0904fe14ad1d 100644
--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
+++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
@@ -188,6 +188,10 @@
#define POINTER_VALUE 0xbadcafe
#define TEST_DATA_LEN 64
+#ifndef __aligned
+#define __aligned(x) __attribute__((aligned(x)))
+#endif
+
#ifndef __used
#define __used __attribute__((used))
#endif
diff --git a/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c b/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
new file mode 100644
index 000000000000..069fc64b0167
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 KylinSoft Corporation. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "bpf_experimental.h"
+
+#define NR_NODES 16
+
+struct node_data {
+ int data;
+};
+
+struct tree_node {
+ struct bpf_rb_node node;
+ u64 key;
+ struct node_data __kptr * node_data;
+};
+
+#define private(name) SEC(".data." #name) __hidden __aligned(8)
+
+private(A) struct bpf_rb_root root __contains(tree_node, node);
+private(A) struct bpf_spin_lock lock;
+
+static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct tree_node *node_a, *node_b;
+
+ node_a = container_of(a, struct tree_node, node);
+ node_b = container_of(b, struct tree_node, node);
+
+ return node_a->key < node_b->key;
+}
+
+SEC("syscall")
+__retval(0)
+long rbtree_search_kptr(void *ctx)
+{
+ struct tree_node *tnode;
+ struct bpf_rb_node *rb_n;
+ struct node_data __kptr * node_data;
+ int lookup_key = NR_NODES / 2;
+ int lookup_data = NR_NODES / 2;
+ int i, data, ret = 0;
+
+ for (i = 0; i < NR_NODES && can_loop; i++) {
+ tnode = bpf_obj_new(typeof(*tnode));
+ if (!tnode)
+ return __LINE__;
+
+ node_data = bpf_obj_new(typeof(*node_data));
+ if (!node_data) {
+ bpf_obj_drop(tnode);
+ return __LINE__;
+ }
+
+ tnode->key = i;
+ node_data->data = i;
+
+ node_data = bpf_kptr_xchg(&tnode->node_data, node_data);
+ if (node_data)
+ bpf_obj_drop(node_data);
+
+ bpf_spin_lock(&lock);
+ bpf_rbtree_add(&root, &tnode->node, less);
+ bpf_spin_unlock(&lock);
+ }
+
+ bpf_spin_lock(&lock);
+ rb_n = bpf_rbtree_root(&root);
+ while (rb_n && can_loop) {
+ tnode = container_of(rb_n, struct tree_node, node);
+ node_data = bpf_kptr_xchg(&tnode->node_data, NULL);
+ if (!node_data) {
+ ret = __LINE__;
+ goto fail;
+ }
+
+ data = node_data->data;
+ node_data = bpf_kptr_xchg(&tnode->node_data, node_data);
+ if (node_data) {
+ bpf_spin_unlock(&lock);
+ bpf_obj_drop(node_data);
+ return __LINE__;
+ }
+
+ if (lookup_key == tnode->key) {
+ if (data == lookup_data)
+ break;
+
+ ret = __LINE__;
+ goto fail;
+ }
+
+ if (lookup_key < tnode->key)
+ rb_n = bpf_rbtree_left(&root, rb_n);
+ else
+ rb_n = bpf_rbtree_right(&root, rb_n);
+ }
+ bpf_spin_unlock(&lock);
+
+ while (can_loop) {
+ bpf_spin_lock(&lock);
+ rb_n = bpf_rbtree_first(&root);
+ if (!rb_n) {
+ bpf_spin_unlock(&lock);
+ return 0;
+ }
+
+ rb_n = bpf_rbtree_remove(&root, rb_n);
+ if (!rb_n) {
+ ret = __LINE__;
+ goto fail;
+ }
+ bpf_spin_unlock(&lock);
+
+ tnode = container_of(rb_n, struct tree_node, node);
+
+ node_data = bpf_kptr_xchg(&tnode->node_data, NULL);
+ if (node_data)
+ bpf_obj_drop(node_data);
+
+ bpf_obj_drop(tnode);
+ }
+
+ return 0;
+fail:
+ bpf_spin_unlock(&lock);
+ return ret;
+}
+
+
+SEC("syscall")
+__failure __msg("R1 type=scalar expected=map_value, ptr_, ptr_")
+long non_own_ref_kptr_xchg_no_lock(void *ctx)
+{
+ struct tree_node *tnode;
+ struct bpf_rb_node *rb_n;
+ struct node_data __kptr * node_data;
+ int data;
+
+ bpf_spin_lock(&lock);
+ rb_n = bpf_rbtree_first(&root);
+ if (!rb_n) {
+ bpf_spin_unlock(&lock);
+ return __LINE__;
+ }
+ bpf_spin_unlock(&lock);
+
+ tnode = container_of(rb_n, struct tree_node, node);
+ node_data = bpf_kptr_xchg(&tnode->node_data, NULL);
+ if (!node_data)
+ return __LINE__;
+
+ data = node_data->data;
+ if (data < 0)
+ return __LINE__;
+
+ node_data = bpf_kptr_xchg(&tnode->node_data, node_data);
+ if (node_data)
+ return __LINE__;
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH RESEND v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set
2026-02-14 12:40 [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg Chengkaitao
` (2 preceding siblings ...)
2026-02-14 12:40 ` [PATCH RESEND v7 3/5] selftests/bpf: Add supplementary tests for bpf_kptr_xchg Chengkaitao
@ 2026-02-14 12:40 ` Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields Chengkaitao
2026-02-24 1:50 ` [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg patchwork-bot+netdevbpf
5 siblings, 0 replies; 8+ messages in thread
From: Chengkaitao @ 2026-02-14 12:40 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
For the following scenario:
struct tree_node {
struct bpf_refcount ref;
struct bpf_rb_node node;
struct node_data __kptr * node_data;
u64 key;
};
This means node_data would have the type PTR_TO_BTF_ID | MEM_ALLOC |
NON_OWN_REF | MEM_RCU.
When traversing an rbtree using bpf_rbtree_left/right, if we need to
use bpf_kptr_xchg to read the __kptr pointer, we still need to follow
the remove-read-add sequence.
This patch allows us to use bpf_kptr_xchg to directly read the __kptr
pointer without any prior operations.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
Signed-off-by: Feng Yang <yangfeng@kylinos.cn>
---
kernel/bpf/verifier.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 3536a91ff8c7..f673cdb55961 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9331,6 +9331,7 @@ static const struct bpf_reg_types kptr_xchg_dest_types = {
PTR_TO_MAP_VALUE,
PTR_TO_BTF_ID | MEM_ALLOC,
PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
+ PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU,
}
};
static const struct bpf_reg_types dynptr_types = {
@@ -9491,6 +9492,7 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
case PTR_TO_BTF_ID | MEM_ALLOC:
case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
+ case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock &&
meta->func_id != BPF_FUNC_kptr_xchg) {
verifier_bug(env, "unimplemented handling of MEM_ALLOC");
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH RESEND v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields.
2026-02-14 12:40 [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg Chengkaitao
` (3 preceding siblings ...)
2026-02-14 12:40 ` [PATCH RESEND v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set Chengkaitao
@ 2026-02-14 12:40 ` Chengkaitao
2026-02-24 1:50 ` [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg patchwork-bot+netdevbpf
5 siblings, 0 replies; 8+ messages in thread
From: Chengkaitao @ 2026-02-14 12:40 UTC (permalink / raw)
To: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
Allow bpf_kptr_xchg to directly operate on pointers marked with
NON_OWN_REF | MEM_RCU.
In the example demonstrated in this patch, as long as "struct
bpf_refcount ref" exists, the __kptr pointer is guaranteed to
carry the MEM_RCU flag. The ref member itself does not need to
be explicitly used.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
.../selftests/bpf/progs/rbtree_search_kptr.c | 123 ++++++++++++++++++
1 file changed, 123 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c b/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
index 069fc64b0167..610aae45e2dc 100644
--- a/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
+++ b/tools/testing/selftests/bpf/progs/rbtree_search_kptr.c
@@ -18,11 +18,21 @@ struct tree_node {
struct node_data __kptr * node_data;
};
+struct tree_node_ref {
+ struct bpf_refcount ref;
+ struct bpf_rb_node node;
+ u64 key;
+ struct node_data __kptr * node_data;
+};
+
#define private(name) SEC(".data." #name) __hidden __aligned(8)
private(A) struct bpf_rb_root root __contains(tree_node, node);
private(A) struct bpf_spin_lock lock;
+private(B) struct bpf_rb_root root_r __contains(tree_node_ref, node);
+private(B) struct bpf_spin_lock lock_r;
+
static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
{
struct tree_node *node_a, *node_b;
@@ -130,6 +140,119 @@ long rbtree_search_kptr(void *ctx)
return ret;
}
+static bool less_r(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct tree_node_ref *node_a, *node_b;
+
+ node_a = container_of(a, struct tree_node_ref, node);
+ node_b = container_of(b, struct tree_node_ref, node);
+
+ return node_a->key < node_b->key;
+}
+
+SEC("syscall")
+__retval(0)
+long rbtree_search_kptr_ref(void *ctx)
+{
+ struct tree_node_ref *tnode_r, *tnode_m;
+ struct bpf_rb_node *rb_n;
+ struct node_data __kptr * node_data;
+ int lookup_key = NR_NODES / 2;
+ int lookup_data = NR_NODES / 2;
+ int i, data, ret = 0;
+
+ for (i = 0; i < NR_NODES && can_loop; i++) {
+ tnode_r = bpf_obj_new(typeof(*tnode_r));
+ if (!tnode_r)
+ return __LINE__;
+
+ node_data = bpf_obj_new(typeof(*node_data));
+ if (!node_data) {
+ bpf_obj_drop(tnode_r);
+ return __LINE__;
+ }
+
+ tnode_r->key = i;
+ node_data->data = i;
+
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, node_data);
+ if (node_data)
+ bpf_obj_drop(node_data);
+
+ /* Unused reference */
+ tnode_m = bpf_refcount_acquire(tnode_r);
+ if (!tnode_m)
+ return __LINE__;
+
+ bpf_spin_lock(&lock_r);
+ bpf_rbtree_add(&root_r, &tnode_r->node, less_r);
+ bpf_spin_unlock(&lock_r);
+
+ bpf_obj_drop(tnode_m);
+ }
+
+ bpf_spin_lock(&lock_r);
+ rb_n = bpf_rbtree_root(&root_r);
+ while (rb_n && can_loop) {
+ tnode_r = container_of(rb_n, struct tree_node_ref, node);
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, NULL);
+ if (!node_data) {
+ ret = __LINE__;
+ goto fail;
+ }
+
+ data = node_data->data;
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, node_data);
+ if (node_data) {
+ bpf_spin_unlock(&lock_r);
+ bpf_obj_drop(node_data);
+ return __LINE__;
+ }
+
+ if (lookup_key == tnode_r->key) {
+ if (data == lookup_data)
+ break;
+
+ ret = __LINE__;
+ goto fail;
+ }
+
+ if (lookup_key < tnode_r->key)
+ rb_n = bpf_rbtree_left(&root_r, rb_n);
+ else
+ rb_n = bpf_rbtree_right(&root_r, rb_n);
+ }
+ bpf_spin_unlock(&lock_r);
+
+ while (can_loop) {
+ bpf_spin_lock(&lock_r);
+ rb_n = bpf_rbtree_first(&root_r);
+ if (!rb_n) {
+ bpf_spin_unlock(&lock_r);
+ return 0;
+ }
+
+ rb_n = bpf_rbtree_remove(&root_r, rb_n);
+ if (!rb_n) {
+ ret = __LINE__;
+ goto fail;
+ }
+ bpf_spin_unlock(&lock_r);
+
+ tnode_r = container_of(rb_n, struct tree_node_ref, node);
+
+ node_data = bpf_kptr_xchg(&tnode_r->node_data, NULL);
+ if (node_data)
+ bpf_obj_drop(node_data);
+
+ bpf_obj_drop(tnode_r);
+ }
+
+ return 0;
+fail:
+ bpf_spin_unlock(&lock_r);
+ return ret;
+}
SEC("syscall")
__failure __msg("R1 type=scalar expected=map_value, ptr_, ptr_")
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH RESEND v7 2/5] bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set
2026-02-14 12:40 ` [PATCH RESEND v7 2/5] bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set Chengkaitao
@ 2026-02-14 13:09 ` bot+bpf-ci
0 siblings, 0 replies; 8+ messages in thread
From: bot+bpf-ci @ 2026-02-14 13:09 UTC (permalink / raw)
To: pilgrimtao, ast, daniel, john.fastabend, andrii, martin.lau,
eddyz87, song, yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah,
yangfeng, alexei.starovoitov
Cc: linux-kernel, bpf, linux-kselftest, chengkaitao, ast, andrii,
daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2281 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -9309,7 +9309,8 @@
> static const struct bpf_reg_types kptr_xchg_dest_types = {
> .types = {
> PTR_TO_MAP_VALUE,
> - PTR_TO_BTF_ID | MEM_ALLOC
> + PTR_TO_BTF_ID | MEM_ALLOC,
> + PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
> }
> };
ref_set_non_owning() adds MEM_RCU to non-owning references when
the btf_record has a refcount field (refcount_off >= 0):
ref_set_non_owning() {
reg->type |= NON_OWN_REF;
if (rec->refcount_off >= 0)
reg->type |= MEM_RCU;
}
For such objects, the register type would be
PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU, which has no
matching entry in kptr_xchg_dest_types and would be rejected by
the verifier.
Should PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU also be
added here? check_func_arg_reg_off() already handles this type
combination, and Alexei Starovoitov requested this addition during
v6 review:
https://lore.kernel.org/bpf/CAADnVQJdwqeoqi+mPjQjmNbUqz2RxAjq-dVsGb7c_MCqpqmwhA@mail.gmail.com/
> @@ -9473,6 +9474,7 @@
> }
> case PTR_TO_BTF_ID | MEM_ALLOC:
> case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
> + case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
> if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock &&
> meta->func_id != BPF_FUNC_kptr_xchg) {
Similarly, this switch would also need a case for
PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU to handle the
refcounted path.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22017669804
AI-authorship-score: low
AI-authorship-explanation: The patch went through 7 revisions with genuine maintainer interaction, uses natural technical language, and shows iterative human development.
issues-found: 1
issue-severity-score: medium
issue-severity-explanation: Missing MEM_RCU type variant causes the verifier to reject valid programs using bpf_kptr_xchg on non-owning references to objects with refcount fields, making the feature incomplete for a subset of BPF object types.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg
2026-02-14 12:40 [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg Chengkaitao
` (4 preceding siblings ...)
2026-02-14 12:40 ` [PATCH RESEND v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields Chengkaitao
@ 2026-02-24 1:50 ` patchwork-bot+netdevbpf
5 siblings, 0 replies; 8+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-02-24 1:50 UTC (permalink / raw)
To: Chengkaitao
Cc: ast, daniel, john.fastabend, andrii, martin.lau, eddyz87, song,
yonghong.song, kpsingh, sdf, haoluo, jolsa, shuah, yangfeng,
alexei.starovoitov, linux-kernel, bpf, linux-kselftest,
chengkaitao
Hello:
This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Sat, 14 Feb 2026 20:40:37 +0800 you wrote:
> From: Kaitao Cheng <chengkaitao@kylinos.cn>
>
> When using bpf_kptr_xchg, we triggered the following error:
> 31: (85) call bpf_kptr_xchg#194
> function calls are not allowed while holding a lock
> bpf_kptr_xchg can now be used in lock-held contexts, so we extended
> its usage scope in [patch 1/5].
>
> [...]
Here is the summary with links:
- [RESEND,v7,1/5] bpf: allow calling bpf_kptr_xchg while holding a lock
https://git.kernel.org/bpf/bpf-next/c/964c0747686a
- [RESEND,v7,2/5] bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set
https://git.kernel.org/bpf/bpf-next/c/ee9886c40aae
- [RESEND,v7,3/5] selftests/bpf: Add supplementary tests for bpf_kptr_xchg
https://git.kernel.org/bpf/bpf-next/c/580fa3430b5d
- [RESEND,v7,4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set
https://git.kernel.org/bpf/bpf-next/c/fb1590448ff7
- [RESEND,v7,5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields.
https://git.kernel.org/bpf/bpf-next/c/ed8fa4b8894f
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-02-24 1:50 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-14 12:40 [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 1/5] bpf: allow calling bpf_kptr_xchg while holding a lock Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 2/5] bpf: allow using bpf_kptr_xchg even if the NON_OWN_REF flag is set Chengkaitao
2026-02-14 13:09 ` bot+bpf-ci
2026-02-14 12:40 ` [PATCH RESEND v7 3/5] selftests/bpf: Add supplementary tests for bpf_kptr_xchg Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 4/5] bpf: allow using bpf_kptr_xchg even if the MEM_RCU flag is set Chengkaitao
2026-02-14 12:40 ` [PATCH RESEND v7 5/5] selftests/bpf: Add test case for rbtree nodes that contain both bpf_refcount and kptr fields Chengkaitao
2026-02-24 1:50 ` [PATCH RESEND v7 0/5] bpf: Expand the usage scenarios of bpf_kptr_xchg patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox