From nobody Thu Apr 9 12:06:55 2026 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5CD3D374730 for ; Mon, 2 Mar 2026 12:40:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455251; cv=none; b=HP/uObRVnr03emYziqvU47rUWj4m5XTax3A4z9vCyKmhiITL9KU1nWj0NdS3sr7bcuxKRXi1tU3gk++UyK4GYyRwi5qBElPTzQZXQ8eQEJX4yU5+RYy3gkdS2aVwcPQEHubbkeyyVYJo5PQFDm3Pcbaai29+emZjL/q1oaYAxgE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455251; c=relaxed/simple; bh=M355Sbdzqhu36DBOFyHzileJa0ZyXN2c/SHzhAr6JW8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=glAPLpA3FYq7t9yFef51WijtJUkkrw9da04+mnATETq+mFT5LIuW0A0DlUcOcGFIRNUFglFO+Z7DNqVu/7OWJxCHcWuZCiEZAXkeccN+Ff7r5H34nGR74KIV29r318j02fRo/VCfahe6l13/rfHTu7XiBiQrPwpWmL5pZQOFcZo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fWTQUbu+; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fWTQUbu+" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-2ae527552acso2752185ad.0 for ; Mon, 02 Mar 2026 04:40:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772455248; x=1773060048; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2scSrgN6MVzwM6/GDhWSPA9nuDZaPKI6bEHUsTrO/gw=; b=fWTQUbu+RczJrsikNd0z5rGla4dbwwxPUk29zKBPNZxJCBkV3hzhpPXeBoN+mCD06g 7SWPcgrYG4qjDfdXAXux+4Arf3xEzWsVuNnvpsmrzWT45J/je7dfTPXXUKiNzPDRV18F 13goRjy85LLApdnwMEIva3LmEYJ0Fez72OPtZEU409YmXoF3zZYfz+4o9eeOnWtYYOAz j72+h0Bqbq+A8vaT+pn6FtphpBQmus2NLKn+OAk/EF1DsFstIw0WiGfaVO/DJFzPoPLN HF9iod+eMmbvDXmUEkfnNk9cFI5h4Gz9zoDCjxetEoEL/u/edr8YGhbFLMwq2qdI4FeJ 5W2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772455248; x=1773060048; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=2scSrgN6MVzwM6/GDhWSPA9nuDZaPKI6bEHUsTrO/gw=; b=gitfeBGjHNNc6M7hG+Lduvig/WQweDZczoxpqv3k3WM0FCeVzaLoXZct6OOWwkhzrg tlR4Zaq3fXE/Mzn94pm/rdpdnDn6ZOkS2mkkhnz97yLR3FCjH+zfLY2FxUjqPYfR8Xs5 rzUB2azM8Z/0/NAd+rOrfavs3dW0fG+6CwZ1mflpctyX1IEGyrSvmf+VtJxGaMwLm2g9 ucScYHKaGlji1Czd69ybaMZX7TpthYY0fYf5ruhNQwpBoUcOC5/ykd00GXzGzaFBenfy kE+P1/W5n81ykqooKLnBXurYr9n7kk1YJvU/O0N2XzNa5Rz8gefxUVU5AUfMdAKj0DOD 7HzQ== X-Forwarded-Encrypted: i=1; AJvYcCW+vgM56ycbBHlwrOA6B5Vb7pC7eF+zkWHO5q0Wpt55moXVqPw3RF12tnhudvG5Kt0XC/dPHAa4zXjAcRk=@vger.kernel.org X-Gm-Message-State: AOJu0YwvsWFXwT+QH/xrr7XI1Fv2JeSc1+oj5Eusc98JMA9tGOu80cbk o3gPs6mxS+I2K3C03uyr3yoTBlY6qQHS0DF3Ni/IqSL6DI04lSdkWw8u X-Gm-Gg: ATEYQzzhsD0xERkwFC1oSm+LDpJvDM+7Y6rv65LoW2bGmlM7jNoLe8SvPjEb5wX8tdC /czikcaPCm0vdAmTM3IV9J/Q8HT8rn8A0hkKk/f0u8akoklZD2gdOyIsV25PQKsJEY3gBa2+zYd 2xMhZ0akzYhwHFESyMhSFWtqWkRtFTLj0T4upjSh7wk3otMjDQGX+wwpyvL7VrJF0Pgx3obmqGG z1aungNl3ha12PGFmYqOme79MgKpRN7q54nNKKesxDKum1K75nGlMwQJepxCItMFktofWlmTLFE sFkuR+4pMgOf6zkz1gC/jVrIAF9O0bg7WnTvOULBlb8tN4PIlG07WPgJRNEEDP+xM8Ms1g/6C4t ujFlq/HuME0RqV8nZl7vx1r0zvn6IGqJvj8dV7RIqJeyhrLPl1hA3FGe13hkLcFPvTdDnNM6Lvl K7iFgL6hmbzO2YDKAnI2O29f0X5JU+fQuTtndctTkWuhAzFK/JD1qXRQ== X-Received: by 2002:a17:903:2449:b0:2ae:57cc:63d7 with SMTP id d9443c01a7336-2ae57cc65femr12287425ad.7.1772455247611; Mon, 02 Mar 2026 04:40:47 -0800 (PST) Received: from localhost.localdomain ([116.128.244.171]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ae3d1956absm67910225ad.14.2026.03.02.04.40.40 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 02 Mar 2026 04:40:47 -0800 (PST) From: Chengkaitao To: martin.lau@linux.dev, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com, jolsa@kernel.org, shuah@kernel.org, chengkaitao@kylinos.cn, linux-kselftest@vger.kernel.org Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 1/6] bpf: Introduce the bpf_list_del kfunc. Date: Mon, 2 Mar 2026 20:40:23 +0800 Message-ID: <20260302124028.82420-2-pilgrimtao@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260302124028.82420-1-pilgrimtao@gmail.com> References: <20260302124028.82420-1-pilgrimtao@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kaitao Cheng If a user holds ownership of a node in the middle of a list, they can directly remove it from the list without strictly adhering to deletion rules from the head or tail. When a kfunc has only one bpf_list_node parameter, supplement the initialization of the corresponding btf_field. Add a new lock_rec member to struct bpf_reference_state for lock holding detection. This is typically paired with bpf_refcount. After calling bpf_list_del, it is generally necessary to drop the reference to the list node twice to prevent reference count leaks. Signed-off-by: Kaitao Cheng --- include/linux/bpf_verifier.h | 4 +++ kernel/bpf/btf.c | 33 +++++++++++++++++++--- kernel/bpf/helpers.c | 17 ++++++++++++ kernel/bpf/verifier.c | 54 ++++++++++++++++++++++++++++++++++-- 4 files changed, 101 insertions(+), 7 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index ef8e45a362d9..e1358b62d6cc 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -261,6 +261,10 @@ struct bpf_reference_state { * it matches on unlock. */ void *ptr; + /* For REF_TYPE_LOCK_*: btf_record of the locked object, used for lock + * checking in kfuncs such as bpf_list_del. + */ + struct btf_record *lock_rec; }; =20 struct bpf_retval_range { diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 4872d2a6c42d..8a977c793d56 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3785,7 +3785,6 @@ static int btf_find_field_one(const struct btf *btf, case BPF_RES_SPIN_LOCK: case BPF_TIMER: case BPF_WORKQUEUE: - case BPF_LIST_NODE: case BPF_RB_NODE: case BPF_REFCOUNT: case BPF_TASK_WORK: @@ -3794,6 +3793,27 @@ static int btf_find_field_one(const struct btf *btf, if (ret < 0) return ret; break; + case BPF_LIST_NODE: + ret =3D btf_find_struct(btf, var_type, off, sz, field_type, + info_cnt ? &info[0] : &tmp); + if (ret < 0) + return ret; + /* graph_root for verifier: container type and node member name */ + if (info_cnt && var_idx >=3D 0 && (u32)var_idx < btf_type_vlen(var)) { + u32 id; + const struct btf_member *member; + + for (id =3D 1; id < btf_nr_types(btf); id++) { + if (btf_type_by_id(btf, id) =3D=3D var) { + info[0].graph_root.value_btf_id =3D id; + member =3D btf_type_member(var) + var_idx; + info[0].graph_root.node_name =3D + __btf_name_by_offset(btf, member->name_off); + break; + } + } + } + break; case BPF_KPTR_UNREF: case BPF_KPTR_REF: case BPF_KPTR_PERCPU: @@ -4138,6 +4158,7 @@ struct btf_record *btf_parse_fields(const struct btf = *btf, const struct btf_type if (ret < 0) goto end; break; + case BPF_LIST_NODE: case BPF_LIST_HEAD: ret =3D btf_parse_list_head(btf, &rec->fields[i], &info_arr[i]); if (ret < 0) @@ -4148,7 +4169,6 @@ struct btf_record *btf_parse_fields(const struct btf = *btf, const struct btf_type if (ret < 0) goto end; break; - case BPF_LIST_NODE: case BPF_RB_NODE: break; default: @@ -4192,20 +4212,25 @@ int btf_check_and_fixup_fields(const struct btf *bt= f, struct btf_record *rec) int i; =20 /* There are three types that signify ownership of some other type: - * kptr_ref, bpf_list_head, bpf_rb_root. + * kptr_ref, bpf_list_head/node, bpf_rb_root. * kptr_ref only supports storing kernel types, which can't store * references to program allocated local types. * * Hence we only need to ensure that bpf_{list_head,rb_root} ownership * does not form cycles. */ - if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & (BPF_GRAPH_ROOT | BPF_UPTR= ))) + if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & + (BPF_GRAPH_ROOT | BPF_GRAPH_NODE | BPF_UPTR))) return 0; + for (i =3D 0; i < rec->cnt; i++) { struct btf_struct_meta *meta; const struct btf_type *t; u32 btf_id; =20 + if (rec->fields[i].type & BPF_GRAPH_NODE) + rec->fields[i].graph_root.value_rec =3D rec; + if (rec->fields[i].type =3D=3D BPF_UPTR) { /* The uptr only supports pinning one page and cannot * point to a kernel struct diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 6eb6c82ed2ee..577af62a9f7a 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2459,6 +2459,22 @@ __bpf_kfunc struct bpf_list_node *bpf_list_pop_back(= struct bpf_list_head *head) return __bpf_list_del(head, true); } =20 +__bpf_kfunc struct bpf_list_node *bpf_list_del(struct bpf_list_node *node) +{ + struct bpf_list_node_kern *knode =3D (struct bpf_list_node_kern *)node; + + if (unlikely(!knode)) + return NULL; + + if (WARN_ON_ONCE(!READ_ONCE(knode->owner))) + return NULL; + + list_del_init(&knode->list_head); + WRITE_ONCE(knode->owner, NULL); + + return node; +} + __bpf_kfunc struct bpf_list_node *bpf_list_front(struct bpf_list_head *hea= d) { struct list_head *h =3D (struct list_head *)head; @@ -4545,6 +4561,7 @@ BTF_ID_FLAGS(func, bpf_list_push_front_impl) BTF_ID_FLAGS(func, bpf_list_push_back_impl) BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_list_del, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_front, KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_back, KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a3390190c26e..8a782772dd36 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1536,7 +1536,7 @@ static int acquire_reference(struct bpf_verifier_env = *env, int insn_idx) } =20 static int acquire_lock_state(struct bpf_verifier_env *env, int insn_idx, = enum ref_state_type type, - int id, void *ptr) + int id, void *ptr, struct btf_record *lock_rec) { struct bpf_verifier_state *state =3D env->cur_state; struct bpf_reference_state *s; @@ -1547,6 +1547,7 @@ static int acquire_lock_state(struct bpf_verifier_env= *env, int insn_idx, enum r s->type =3D type; s->id =3D id; s->ptr =3D ptr; + s->lock_rec =3D lock_rec; =20 state->active_locks++; state->active_lock_id =3D id; @@ -1662,6 +1663,23 @@ static struct bpf_reference_state *find_lock_state(s= truct bpf_verifier_state *st return NULL; } =20 +static bool rec_has_list_matching_node_type(struct bpf_verifier_env *env, + const struct btf_record *rec, + const struct btf *node_btf, u32 node_btf_id) +{ + u32 i; + + for (i =3D 0; i < rec->cnt; i++) { + if (!(rec->fields[i].type & BPF_LIST_HEAD)) + continue; + if (btf_struct_ids_match(&env->log, node_btf, node_btf_id, 0, + rec->fields[i].graph_root.btf, + rec->fields[i].graph_root.value_btf_id, true)) + return true; + } + return false; +} + static void update_peak_states(struct bpf_verifier_env *env) { u32 cur_states; @@ -8576,7 +8594,8 @@ static int process_spin_lock(struct bpf_verifier_env = *env, int regno, int flags) type =3D REF_TYPE_RES_LOCK; else type =3D REF_TYPE_LOCK; - err =3D acquire_lock_state(env, env->insn_idx, type, reg->id, ptr); + err =3D acquire_lock_state(env, env->insn_idx, type, reg->id, ptr, + reg_btf_record(reg)); if (err < 0) { verbose(env, "Failed to acquire lock state\n"); return err; @@ -12431,6 +12450,7 @@ enum special_kfunc_type { KF_bpf_list_push_back_impl, KF_bpf_list_pop_front, KF_bpf_list_pop_back, + KF_bpf_list_del, KF_bpf_list_front, KF_bpf_list_back, KF_bpf_cast_to_kern_ctx, @@ -12491,6 +12511,7 @@ BTF_ID(func, bpf_list_push_front_impl) BTF_ID(func, bpf_list_push_back_impl) BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_back) +BTF_ID(func, bpf_list_del) BTF_ID(func, bpf_list_front) BTF_ID(func, bpf_list_back) BTF_ID(func, bpf_cast_to_kern_ctx) @@ -12966,6 +12987,7 @@ static bool is_bpf_list_api_kfunc(u32 btf_id) btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_back_impl] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_pop_front] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_pop_back] || + btf_id =3D=3D special_kfunc_list[KF_bpf_list_del] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_front] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_back]; } @@ -13088,7 +13110,8 @@ static bool check_kfunc_is_graph_node_api(struct bp= f_verifier_env *env, switch (node_field_type) { case BPF_LIST_NODE: ret =3D (kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_front_i= mpl] || - kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_back_impl= ]); + kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_back_impl= ] || + kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_del]); break; case BPF_RB_NODE: ret =3D (kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_rbtree_remove] || @@ -13211,6 +13234,9 @@ __process_kf_arg_ptr_to_graph_node(struct bpf_verif= ier_env *env, return -EINVAL; } =20 + if (!*node_field) + *node_field =3D field; + field =3D *node_field; =20 et =3D btf_type_by_id(field->graph_root.btf, field->graph_root.value_btf_= id); @@ -13237,6 +13263,28 @@ __process_kf_arg_ptr_to_graph_node(struct bpf_veri= fier_env *env, return -EINVAL; } =20 + /* bpf_list_del: require list head's lock. Use refs[] REF_TYPE_LOCK_MASK + * only. At lock time we stored the locked object's btf_record in ref-> + * lock_rec, so we can get the list value type from the ref directly. + */ + if (node_field_type =3D=3D BPF_LIST_NODE && + meta->func_id =3D=3D special_kfunc_list[KF_bpf_list_del]) { + struct bpf_verifier_state *cur =3D env->cur_state; + + for (int i =3D 0; i < cur->acquired_refs; i++) { + struct bpf_reference_state *s =3D &cur->refs[i]; + + if (!(s->type & REF_TYPE_LOCK_MASK) || !s->lock_rec) + continue; + + if (rec_has_list_matching_node_type(env, s->lock_rec, + reg->btf, reg->btf_id)) + return 0; + } + verbose(env, "bpf_spin_lock must be held for bpf_list_del\n"); + return -EINVAL; + } + return 0; } =20 --=20 2.50.1 (Apple Git-155) From nobody Thu Apr 9 12:06:55 2026 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 33EEB375AA9 for ; Mon, 2 Mar 2026 12:40:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455258; cv=none; b=TbSWOerzunL8FvaY9yHk+zihHnyhFSTO8IuygNC1+G0g+NIE6kPm4kzInho9KxLJRePEgGWMnBxAZjWc9gY/sYwEiOBTOkS4RB0m79dDBZ2WLxKgez/FV4HPymGtHHG4b3LH2m9CpuG7nimIc3/xrjvMr1vA++12fGYWC4eKp68= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455258; c=relaxed/simple; bh=sOe9arj46SNWwf/pIF8963vr5xEYDR3pc1hBPuALohY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BWRGwyB/nAfS1tjBlHZFt4tQUse9dGGXxr09wuRcNj/xnajXv9xYLNQnkWwgd+B8dJIPbE/MsHmxPJjk2KIoT21z+Y2ketyxNJSZSi9++uZygnl1WtNdwsw2rzvnqwsRCArFn1trL9QHpFskuwlFtjvmk0tMsbxuLvVg8SJkA2I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UW7f/9tl; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UW7f/9tl" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2ae4e538abdso9871915ad.3 for ; Mon, 02 Mar 2026 04:40:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772455256; x=1773060056; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uX4WAvxsCrnf+I/DBX5XaVur9b97LAwNil7wzBRZi20=; b=UW7f/9tlWtJ3TM5LHAMxWUipm3FjEpFbCpEnjckH3Eb3vANq4Ac6F/zY/6hdEg68M9 JHRh5FsLpvoSLneyQ07RVDDiyXmFMJtYdpoaDJRP1kMGwI0UVNPT5Kk2R0/teWcgod0A 442ukMb/EnDeMT5XRVaYVDJOe2bulhCjuHmOLbUzBvzlQSlhsdndHACH58HgeTr4I3W/ nF3uc36K1MuyyoqgKnReZLdXZmyGCPEt2ft+TRLxamfZdP84jFj9Sa3Kt3O7Krk5MZGf apBSkQmrZbY9dsJK3gZl4Lsz8LcY7Axli7672l8EskcBb9+V9vrGcx8IHIP7evzzYA17 Xw0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772455256; x=1773060056; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=uX4WAvxsCrnf+I/DBX5XaVur9b97LAwNil7wzBRZi20=; b=Ixa6ASd1d1gRr4gPIy2E6+H9Wpf3JNCc6C/QzZxYzl8Bl3lhhuJes72vr4H2f4vWok RBn1AtmoB3wB5P7DGJ8xYmQU44eGGWZBG83ncPfm79fI0Bv81GaQy4jO8wegVFeMAghE kIc+rXNxMOpQtwOzDZzVz4kPHgvGVPm2AC7hhRwCzzc0Wmav8L8JOIQ+1liCEaRYERV6 pYEM6Wt+duxeoC1wUA/r5vSoAYgnRRME8lO9BWHvmyczmfj7g6R8gbeKhuZbyvFxsQK8 g5lfpfaW6e9mzvoVmR9cRci9/GCRnwEWbtwcPGXgw+8cqWcuTIjG0c2idj8oiDhLGZi+ X93w== X-Forwarded-Encrypted: i=1; AJvYcCV/l0VBqwO3Naul/zYRUMBjebdrwC7olluFjY1CuX0SOeUUxlymRFcRCnTq4dswzd2fbeTZMDJ0kJO/VYs=@vger.kernel.org X-Gm-Message-State: AOJu0YyBBVlOkmBi3vZ/ZtH35YHymrKbpGeszp46HR2kBKJU1np9jBrU 0O4oth4vW69zMaHbxSNf8ypESDzbLDAy8fndnKfZF6gQMlqHXYIv3ST2V2MhKQ== X-Gm-Gg: ATEYQzwlV+qGtziYJdwegKXGDAFfzb7raSdANLyzxPRZRycDk/M2QA+TbczoSPSxukz HsEC56pfH6JF9NZe7Mrdtjg2BKFlGbJ7iMSC/q6evSOzKW/6y2OjHBSojCN6Mx8kiRwykSaltWb fVAU3RXNmLskKx+VPysaRX23HQA7Wzv0uYjN/UD/Bn9s5kD5qqyN/YFSvk8DHAoedlgbR7dOdtJ zBnGITAI6cRzoHgjwqx8syo5M4a+HrRPzOhqZMlGm9ReWKBwH8BnAdJJyyhevtLCpOXDKIlXJYf EmD9CcFHgBaXbkUeQ4PQC6O8T4PpHCTN0u/hZo3tAlZnkFfqC7FQRytoPkYbgS6Hbj/JV8hvmeD d7ez/IT+cCAhlFQZhSS2nZ7+QM5N+vcs8SCETJcsOFah+ce0N12AUnpoRkQ5ADwzfWy2Eqa3t1a vSuCzRaVUvMBgrmT5+EF1vp7JB5kcE9Y4tPK/pVa8Ei0QO1zJiF6q3ZQ== X-Received: by 2002:a17:903:37d0:b0:2ad:dfb8:8ed3 with SMTP id d9443c01a7336-2ae2e3ecea7mr116397005ad.8.1772455256520; Mon, 02 Mar 2026 04:40:56 -0800 (PST) Received: from localhost.localdomain ([116.128.244.171]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ae3d1956absm67910225ad.14.2026.03.02.04.40.48 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 02 Mar 2026 04:40:56 -0800 (PST) From: Chengkaitao To: martin.lau@linux.dev, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com, jolsa@kernel.org, shuah@kernel.org, chengkaitao@kylinos.cn, linux-kselftest@vger.kernel.org Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 2/6] selftests/bpf: Add test cases for bpf_list_del Date: Mon, 2 Mar 2026 20:40:24 +0800 Message-ID: <20260302124028.82420-3-pilgrimtao@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260302124028.82420-1-pilgrimtao@gmail.com> References: <20260302124028.82420-1-pilgrimtao@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kaitao Cheng Add a node to both an rbtree and a list, retrieve the node from the rbtree, use the obtained node pointer to remove it from the list, and finally free the node. To verify the validity of bpf_list_del, also expect the verifier to reject calls to bpf_list_del made without holding the spin_lock. Signed-off-by: Kaitao Cheng --- .../testing/selftests/bpf/bpf_experimental.h | 10 +++ .../selftests/bpf/progs/refcounted_kptr.c | 71 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing= /selftests/bpf/bpf_experimental.h index 4b7210c318dd..b4fb0459f11f 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -99,6 +99,16 @@ extern struct bpf_list_node *bpf_list_pop_front(struct b= pf_list_head *head) __ks */ extern struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head)= __ksym; =20 +/* Description + * Remove 'node' from its BPF linked list. + * The node must be in the list. Caller receives ownership of the + * removed node and must release it with bpf_obj_drop. + * Returns + * Pointer to the removed bpf_list_node, or NULL if 'node' is NULL + * or not in the list. + */ +extern struct bpf_list_node *bpf_list_del(struct bpf_list_node *node) __ks= ym; + /* Description * Remove 'node' from rbtree with root 'root' * Returns diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr.c b/tools/te= sting/selftests/bpf/progs/refcounted_kptr.c index 1aca85d86aeb..c4fb5615d08b 100644 --- a/tools/testing/selftests/bpf/progs/refcounted_kptr.c +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr.c @@ -367,6 +367,77 @@ long insert_rbtree_and_stash__del_tree_##rem_tree(void= *ctx) \ INSERT_STASH_READ(true, "insert_stash_read: remove from tree"); INSERT_STASH_READ(false, "insert_stash_read: don't remove from tree"); =20 +/* Insert node_data into both rbtree and list, remove from tree, then remo= ve + * from list via bpf_list_del using the node obtained from the tree. + */ +SEC("tc") +__description("test_bpf_list_del: remove an arbitrary node from the list") +__success __retval(0) +long test_bpf_list_del(void *ctx) +{ + long err; + struct bpf_rb_node *rb; + struct bpf_list_node *l; + struct node_data *n; + + err =3D __insert_in_tree_and_list(&head, &root, &lock); + if (err) + return err; + + bpf_spin_lock(&lock); + rb =3D bpf_rbtree_first(&root); + if (!rb) { + bpf_spin_unlock(&lock); + return -4; + } + + rb =3D bpf_rbtree_remove(&root, rb); + if (!rb) { + bpf_spin_unlock(&lock); + return -5; + } + + n =3D container_of(rb, struct node_data, r); + l =3D bpf_list_del(&n->l); + bpf_spin_unlock(&lock); + bpf_obj_drop(n); + if (!l) + return -6; + + bpf_obj_drop(container_of(l, struct node_data, l)); + return 0; +} + +SEC("?tc") +__failure __msg("bpf_spin_lock must be held for bpf_list_del") +long list_del_without_lock_fail(void *ctx) +{ + struct bpf_rb_node *rb; + struct bpf_list_node *l; + struct node_data *n; + + bpf_spin_lock(&lock); + rb =3D bpf_rbtree_first(&root); + if (!rb) { + bpf_spin_unlock(&lock); + return -4; + } + + rb =3D bpf_rbtree_remove(&root, rb); + bpf_spin_unlock(&lock); + if (!rb) + return -5; + + n =3D container_of(rb, struct node_data, r); + l =3D bpf_list_del(&n->l); + bpf_obj_drop(n); + if (!l) + return -6; + + bpf_obj_drop(container_of(l, struct node_data, l)); + return 0; +} + SEC("tc") __success long rbtree_refcounted_node_ref_escapes(void *ctx) --=20 2.50.1 (Apple Git-155) From nobody Thu Apr 9 12:06:55 2026 Received: from mail-pl1-f179.google.com (mail-pl1-f179.google.com [209.85.214.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 46829375ADD for ; Mon, 2 Mar 2026 12:41:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455265; cv=none; b=rIE+rP8eX88YT04VJRETmNZVjM5sx9fvq2u1QxQ2pte+k9N0GR6lhV7Kdgdx6B/5HL3Z8LmHIr9FEVOaRbxWk1tKQ1X6R30IrPqaOapDjrI1ySIJq2CKxNngDLhIJb3Ro6GaQh7YHVYoOETei+ym1jemdyJiA6A3F4yJ4jcHZNc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455265; c=relaxed/simple; bh=OARCp2c0bNabt09S6boIe4Fa5ckg0ng3LQnP7arffbo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lP1Jh/yYpPv5YfbEXNGiDhg0TkeSFiwA8Foi6zp4psISGejjlgy3wlvhNX7ZAKjA3wDZ2FG2sirnHpgv2T5Ls5X+s3AbZjjuBfpGeRNR0ceLJFztz4w1caodnFhYYEv+JdxgVJsV2Y+yeq1/A8isiDT0S6ATDR16T5pybRnMxjI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fakBtF/D; arc=none smtp.client-ip=209.85.214.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fakBtF/D" Received: by mail-pl1-f179.google.com with SMTP id d9443c01a7336-2ae5636ab04so6492435ad.3 for ; Mon, 02 Mar 2026 04:41:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772455264; x=1773060064; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Bd0q35RQwe48UzQhss6Qsj2Pe6t56hcfaCjZ9c765uA=; b=fakBtF/DxUwYGww3SkpQS04cAsQ70E6W5diFDhAcN74/OvkRwAk7mJXeB3YQdYEJq5 eMX3F+ebA1yGyIlzvYAddXSGvfV2lEDs36/Ak3kqiWS8F1K5ZI1WDPiL/E4jjgv2ike0 Vr3K2MsDxOBTvd/THm5blqgwSVdJ7HNysRjdfJLbbtdOz/GmchRD0lbIzesHmrg++Rwj ZQ/y2sYuttJfAUYx524EyM10+BIL7AX4QK/LVE59KCHzJsDk8iJsA4exyDWuFFapimdg HGJQy/nn2lronDgei6C6LOIJC8n2cEN3G4cxvOBPWPhRvAsq4labj1WbawDv8JmyLHDK Zejg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772455264; x=1773060064; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Bd0q35RQwe48UzQhss6Qsj2Pe6t56hcfaCjZ9c765uA=; b=sl6DVXMfMrEP8r2nPxtJVCKwZo+Oou3zsUBii8sQMnZ5UeWQA9Euqx8xnrdduai6vL 3NjUJFCB31FJaMXaVcOH3ytrsOkfXuBoWmBodM7YTeRRJHhU8++RIy915CNWYlXyhujQ NfbvOLdPLWIzFliF3sxmkydMX5FP2NoomjiQrspcOvdWsTgEX/geJrU1eF998U79zjqm TDEbBMLXf/0ZDObI+8XzOfRvdoi9CWJMNuDXJlSRwlJjGz3W8Z+pcqGrugL+jwgcEawN Hb7rqFHgJ1gpoo0lNp8BEHJj7g6IF78oWz95oQX7TmEtN+hDoFeLhqmd8U+iNpvPUAji DY5w== X-Forwarded-Encrypted: i=1; AJvYcCXqb0l1sLUBDy0cJOzE+MkEe5H9mSddWx0KgjfGsBUCEl5q6Yk2Rkmq8g2IZBU4DqAdsazlV+kShJDjCfw=@vger.kernel.org X-Gm-Message-State: AOJu0YwGjW2atIuFQxwc7GxSPzRyYxQRX6g5a+KAWCCeu2WTIPmDsBcX f+q3q/clJFWiP6YKajJV8DAdMcA2q/ZsNOGyMOHcXr3qh4eTsKKLIP4o X-Gm-Gg: ATEYQzx16c07CjTKS/OSyr/UXug77SkZdGepiED+jai+5s8vzALl0oZWlJbNM4ZFyjn yrcDKDerej6Htcn9UBuMkNEzyH2hDLETiBCim6a+HWWbd/NjWcUX6VByn/ebJFl39f+O+9rirpM s+PtJrXFbMDtRd+dqsp6d72km1p4MLL14AENiknW6rJO8Mi4LMW+gd8ClVZ8OqerPnbrfhfKMhI B2uCXpcfUoPDTWHUvfQou2xEHDy6fWYefuX7cd4QXhy+h9nsrqm7U9oy5xqKYD4QFJc6/RO/GrF b2oTIBgRtg6NljYfOGFOzZ2LZ5zqi/kp4WxPWeBvJI70vOZ+qKmM3A/+H+6sYGILdLj3M2UImeb OKrPQvxrhgBZ0O2MWkCl3nr19t1zT7mYZeL99+Gnc6Lj0w7XpIx5Vr4ZpcgpmIwJzyUlyfIgrzg RTcrIyhxW0mM61eFosPfG7ianFg7dPbQnzjFqD0Nmjb2KisVPnABcJAg== X-Received: by 2002:a17:903:244e:b0:2ae:5039:a075 with SMTP id d9443c01a7336-2ae5039a3bfmr31249185ad.53.1772455263609; Mon, 02 Mar 2026 04:41:03 -0800 (PST) Received: from localhost.localdomain ([116.128.244.171]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ae3d1956absm67910225ad.14.2026.03.02.04.40.56 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 02 Mar 2026 04:41:03 -0800 (PST) From: Chengkaitao To: martin.lau@linux.dev, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com, jolsa@kernel.org, shuah@kernel.org, chengkaitao@kylinos.cn, linux-kselftest@vger.kernel.org Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 3/6] bpf: add bpf_list_add_impl to insert node after a given list node Date: Mon, 2 Mar 2026 20:40:25 +0800 Message-ID: <20260302124028.82420-4-pilgrimtao@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260302124028.82420-1-pilgrimtao@gmail.com> References: <20260302124028.82420-1-pilgrimtao@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kaitao Cheng Add a new kfunc bpf_list_add_impl(prev, node, meta, off) that inserts 'node' after 'prev' in the BPF linked list. Both must be in the same list; 'prev' must already be in the list. The new node must be an owning reference (e.g. from bpf_obj_new); the kfunc consumes that reference and the node becomes non-owning once inserted. Returns 0 on success, -EINVAL if prev is not in a list or node is already in a list (or duplicate insertion). On failure, the kernel drops the passed-in node. Signed-off-by: Kaitao Cheng --- kernel/bpf/helpers.c | 34 ++++++++++++++++++++++++++++++++++ kernel/bpf/verifier.c | 23 ++++++++++++++++------- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 577af62a9f7a..d212962d4ed6 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2495,6 +2495,39 @@ __bpf_kfunc struct bpf_list_node *bpf_list_back(stru= ct bpf_list_head *head) return (struct bpf_list_node *)h->prev; } =20 +static int __bpf_list_add_after(struct bpf_list_node_kern *prev, + struct bpf_list_node_kern *node, + struct btf_record *rec, u64 off) +{ + struct bpf_list_head *head; + struct list_head *n =3D &node->list_head, *p =3D &prev->list_head; + + head =3D READ_ONCE(prev->owner); + if (unlikely(!head)) + goto fail; + + if (cmpxchg(&node->owner, NULL, BPF_PTR_POISON)) + goto fail; + + list_add(n, p); + WRITE_ONCE(node->owner, head); + return 0; + +fail: + __bpf_obj_drop_impl((void *)n - off, rec, false); + return -EINVAL; +} + +__bpf_kfunc int bpf_list_add_impl(struct bpf_list_node *prev, + struct bpf_list_node *node, + void *meta__ign, u64 off) +{ + struct bpf_list_node_kern *n =3D (void *)node, *p =3D (void *)prev; + struct btf_struct_meta *meta =3D meta__ign; + + return __bpf_list_add_after(p, n, meta ? meta->record : NULL, off); +} + __bpf_kfunc struct bpf_rb_node *bpf_rbtree_remove(struct bpf_rb_root *root, struct bpf_rb_node *node) { @@ -4564,6 +4597,7 @@ BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF= _RET_NULL) BTF_ID_FLAGS(func, bpf_list_del, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_front, KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_back, KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_list_add_impl) BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) BTF_ID_FLAGS(func, bpf_rbtree_remove, KF_ACQUIRE | KF_RET_NULL) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8a782772dd36..f5ee11779a5c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12453,6 +12453,7 @@ enum special_kfunc_type { KF_bpf_list_del, KF_bpf_list_front, KF_bpf_list_back, + KF_bpf_list_add_impl, KF_bpf_cast_to_kern_ctx, KF_bpf_rdonly_cast, KF_bpf_rcu_read_lock, @@ -12514,6 +12515,7 @@ BTF_ID(func, bpf_list_pop_back) BTF_ID(func, bpf_list_del) BTF_ID(func, bpf_list_front) BTF_ID(func, bpf_list_back) +BTF_ID(func, bpf_list_add_impl) BTF_ID(func, bpf_cast_to_kern_ctx) BTF_ID(func, bpf_rdonly_cast) BTF_ID(func, bpf_rcu_read_lock) @@ -12989,7 +12991,8 @@ static bool is_bpf_list_api_kfunc(u32 btf_id) btf_id =3D=3D special_kfunc_list[KF_bpf_list_pop_back] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_del] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_front] || - btf_id =3D=3D special_kfunc_list[KF_bpf_list_back]; + btf_id =3D=3D special_kfunc_list[KF_bpf_list_back] || + btf_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl]; } =20 static bool is_bpf_rbtree_api_kfunc(u32 btf_id) @@ -13111,7 +13114,8 @@ static bool check_kfunc_is_graph_node_api(struct bp= f_verifier_env *env, case BPF_LIST_NODE: ret =3D (kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_front_i= mpl] || kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_back_impl= ] || - kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_del]); + kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_del] || + kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl]); break; case BPF_RB_NODE: ret =3D (kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_rbtree_remove] || @@ -13263,12 +13267,15 @@ __process_kf_arg_ptr_to_graph_node(struct bpf_ver= ifier_env *env, return -EINVAL; } =20 - /* bpf_list_del: require list head's lock. Use refs[] REF_TYPE_LOCK_MASK - * only. At lock time we stored the locked object's btf_record in ref-> - * lock_rec, so we can get the list value type from the ref directly. + /* When there is no bpf_list_head in the parameter list, to prevent BPF + * programs from calling bpf_list APIs without holding the spinlock, + * we need to acquire the list head's lock. At lock time we stored the + * locked object's btf_record in ref->lock_rec, so we can get the list + * value type from the ref directly. */ if (node_field_type =3D=3D BPF_LIST_NODE && - meta->func_id =3D=3D special_kfunc_list[KF_bpf_list_del]) { + (meta->func_id =3D=3D special_kfunc_list[KF_bpf_list_del] || + meta->func_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl])) { struct bpf_verifier_state *cur =3D env->cur_state; =20 for (int i =3D 0; i < cur->acquired_refs; i++) { @@ -13281,7 +13288,7 @@ __process_kf_arg_ptr_to_graph_node(struct bpf_verif= ier_env *env, reg->btf, reg->btf_id)) return 0; } - verbose(env, "bpf_spin_lock must be held for bpf_list_del\n"); + verbose(env, "bpf_spin_lock must be held for bpf_list api\n"); return -EINVAL; } =20 @@ -14278,6 +14285,7 @@ static int check_kfunc_call(struct bpf_verifier_env= *env, struct bpf_insn *insn, =20 if (meta.func_id =3D=3D special_kfunc_list[KF_bpf_list_push_front_impl] || meta.func_id =3D=3D special_kfunc_list[KF_bpf_list_push_back_impl] || + meta.func_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl] || meta.func_id =3D=3D special_kfunc_list[KF_bpf_rbtree_add_impl]) { release_ref_obj_id =3D regs[BPF_REG_2].ref_obj_id; insn_aux->insert_off =3D regs[BPF_REG_2].off; @@ -23244,6 +23252,7 @@ static int fixup_kfunc_call(struct bpf_verifier_env= *env, struct bpf_insn *insn, *cnt =3D 3; } else if (desc->func_id =3D=3D special_kfunc_list[KF_bpf_list_push_back_= impl] || desc->func_id =3D=3D special_kfunc_list[KF_bpf_list_push_front_impl] = || + desc->func_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl] || desc->func_id =3D=3D special_kfunc_list[KF_bpf_rbtree_add_impl]) { struct btf_struct_meta *kptr_struct_meta =3D env->insn_aux_data[insn_idx= ].kptr_struct_meta; int struct_meta_reg =3D BPF_REG_3; --=20 2.50.1 (Apple Git-155) From nobody Thu Apr 9 12:06:55 2026 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 246F138CFFA for ; Mon, 2 Mar 2026 12:41:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455273; cv=none; b=EHSk38isPEvhkcyHNMUXTO7sY4oRYT3qeO5G7lgF9qSBYxihMVqgwPf0Dobg8q7+0935Yf5c6UEa+gZpOuFK1RaLUWA/l7uzdrIuGvwDnNnudpSpXmAAYr9XgoFiacQ0gZfGEUjSOcJoQUtAhHDOuINll2U3MiBFpRk/3k0UF6Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455273; c=relaxed/simple; bh=jtGIjlDuLik+f2Igu18fW3dt4LdjvlRBZPoRTTUs1G0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QdHvdupmWtvsb4uKDRTtkioY8J85MrXpkVPPWEXcJxmUNKkLUf9McRvfhYlG91genm33vbvzexRpjsRuTImIs/86yPvtusuMkKo7bEFOh8mKdEMBc1+qncilj7FzvECWsXSvTranfbdxoIpgOcCxbIGIskzYd1zKCxUcQvmHp7Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=eUIjTelB; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="eUIjTelB" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2ad9516a653so18248615ad.0 for ; Mon, 02 Mar 2026 04:41:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772455271; x=1773060071; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9Eyq8baO3YE83K4ftV/rGILwVlXJCLjJ+D4XOH4RDSs=; b=eUIjTelB/AJ7fRjWkqUGTDs/F87KHVkjklwGXnDrxjZzPbgbQ96c88sAZRIPUgPHJ/ 5cC8A2ddandV1hao6rhaKQv7j8GR8BqFA51Fe9DrT0I593OskRR3wt829/Fvf2dHsCzX jF6VKpmGoptFDkLR9uHSf2QiJqGn9ZS8Y1ZdDkpo2pyBBuhwgNF1AzwsmeDwy1ZKlqQ+ I3ETzhS8cBZgHMkdjXanAJc3LtFPFmCI96QRm2oO8jFlvxShvgvwHh8QtJAwrWXJhRrp E9jDDq4468H1BgxdC3CatEwlk0VafdNioDHSsl/9xf2Ptye4KhMI46LiaWJGaDEZAsSK EQLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772455271; x=1773060071; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=9Eyq8baO3YE83K4ftV/rGILwVlXJCLjJ+D4XOH4RDSs=; b=AMsriA4FpkQNNnEVG2tqMZwEMMvAsvEZD6g6T8ZeRB87ul/R5kAYGiUhg9P9jiBTI5 KParS1GWMfp7InLSHdeCxC1+RmGiDAYK9vb1ymWrZCnusAo9oUKG1ts/TWUhq/qVmQZb Kdr93RcvKjD52gS31kbCAWDF4n19sbqzRuCWp6fOpk+YcoK5zGp3+TWzrCV1g9a5t+X/ jQEUOTXdGeY879SFnmHoV/Yh4CAKbIHLE9rt/qm7eaKnHG+X2AVa9i8ZzIj9gVlU2dJT HaNzremhWqxgby3va+EY4PpeVNAEDVUwfkFjEgksVGz12QjWM0phpHuS5mYj4rxyDHaL eMxA== X-Forwarded-Encrypted: i=1; AJvYcCUCvvYxJ0aAyd+LnFBa2FJodEdexH4bi7RJ4RWgjYSRZ5TOIUTX3F9ZRHtp1xfe8ZdlZQ26Y6KKkc74ZjQ=@vger.kernel.org X-Gm-Message-State: AOJu0YwWWaaWMyshHjR4qMn6j7jQeyDGEGJKtv6TaA+dnwDYhxnItmay RTonAEAUaaCKzGPIRnXhfuNLX2OAqh51eOeF51VJqqjo+Cj+sjIA0o87 X-Gm-Gg: ATEYQzyRMM+FJ/dVhgH0txp+mdSO8bOwThSl46hbKJaK1A+BfsuhHz0GwvmnuXuGdtl QMAkZ3ukW9A9d5bNJu4QoT0gQZp28riurZToHKfesvH9udL60uejBch8y0e/8GS9Ol0DPblEabH t0SergOX8ZN2QRfHjeFjRGO1weZQUEBxrx26nr/O8xzkOrmMwZD9zyTFUXWr47iMfm/SCy6+nIU f5yBYjTcGjUZrh7k/0xnT7OPyNcH7H4DJ9yQh35qqecLvIFIOYvD+q+LLejxIAss0z1Rlgg7Pri J7XmmwBr6Itr6qbV9GFPOLvGTxcCxEJBh9VZEsuiKWB1dBlgBvRclBG+FvIrggRBDj2sGrcoglL Zywl1Rs1j+4ruebTtVepSga/d2eKfjddBtkW5hVWWiPOZpuHPw5t83VBs8XxRu8IhG37uTsqmu3 7W7pTZmX9vvhdHSzUndUaQi4wQBNJ3ORYYH5+OyF2fRRsiUOvm56ytZa62h8TTJVyM X-Received: by 2002:a17:902:d592:b0:2ad:d0ff:2ed4 with SMTP id d9443c01a7336-2ae2e3e3c91mr110237765ad.6.1772455271425; Mon, 02 Mar 2026 04:41:11 -0800 (PST) Received: from localhost.localdomain ([116.128.244.171]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ae3d1956absm67910225ad.14.2026.03.02.04.41.04 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 02 Mar 2026 04:41:11 -0800 (PST) From: Chengkaitao To: martin.lau@linux.dev, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com, jolsa@kernel.org, shuah@kernel.org, chengkaitao@kylinos.cn, linux-kselftest@vger.kernel.org Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 4/6] selftests/bpf: Add test case for bpf_list_add_impl Date: Mon, 2 Mar 2026 20:40:26 +0800 Message-ID: <20260302124028.82420-5-pilgrimtao@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260302124028.82420-1-pilgrimtao@gmail.com> References: <20260302124028.82420-1-pilgrimtao@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kaitao Cheng Extend refcounted_kptr test (test_list_add_del) to exercise bpf_list_add: add a second node after the first, then bpf_list_del both nodes. Signed-off-by: Kaitao Cheng --- .../testing/selftests/bpf/bpf_experimental.h | 14 +++ .../selftests/bpf/progs/refcounted_kptr.c | 105 ++++++++++++++---- 2 files changed, 97 insertions(+), 22 deletions(-) diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing= /selftests/bpf/bpf_experimental.h index b4fb0459f11f..48106ea5dda8 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -109,6 +109,20 @@ extern struct bpf_list_node *bpf_list_pop_back(struct = bpf_list_head *head) __ksy */ extern struct bpf_list_node *bpf_list_del(struct bpf_list_node *node) __ks= ym; =20 +/* Description + * Insert 'node' after 'prev' in the BPF linked list. 'prev' must already + * be in a list; 'node' must not be in any list. The 'meta' and 'off' + * parameters are rewritten by the verifier, no need for BPF programs to + * set them. + * Returns + * 0 on success, -EINVAL if prev is not in a list or node is already in a = list. + */ +extern int bpf_list_add_impl(struct bpf_list_node *prev, struct bpf_list_n= ode *node, + void *meta, __u64 off) __ksym; + +/* Convenience macro to wrap over bpf_list_add_impl */ +#define bpf_list_add(prev, node) bpf_list_add_impl(prev, node, NULL, 0) + /* Description * Remove 'node' from rbtree with root 'root' * Returns diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr.c b/tools/te= sting/selftests/bpf/progs/refcounted_kptr.c index c4fb5615d08b..4d979f5ad9e8 100644 --- a/tools/testing/selftests/bpf/progs/refcounted_kptr.c +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr.c @@ -367,18 +367,19 @@ long insert_rbtree_and_stash__del_tree_##rem_tree(voi= d *ctx) \ INSERT_STASH_READ(true, "insert_stash_read: remove from tree"); INSERT_STASH_READ(false, "insert_stash_read: don't remove from tree"); =20 -/* Insert node_data into both rbtree and list, remove from tree, then remo= ve - * from list via bpf_list_del using the node obtained from the tree. +/* Insert one node in tree and list, remove it from tree, add a second + * node after it in list with bpf_list_add, then remove both nodes from + * list via bpf_list_del. */ SEC("tc") -__description("test_bpf_list_del: remove an arbitrary node from the list") +__description("test_list_add_del: test bpf_list_add/del") __success __retval(0) -long test_bpf_list_del(void *ctx) +long test_list_add_del(void *ctx) { - long err; + long err =3D 0; struct bpf_rb_node *rb; - struct bpf_list_node *l; - struct node_data *n; + struct bpf_list_node *l, *l_1; + struct node_data *n, *n_1, *m_1; =20 err =3D __insert_in_tree_and_list(&head, &root, &lock); if (err) @@ -392,29 +393,62 @@ long test_bpf_list_del(void *ctx) } =20 rb =3D bpf_rbtree_remove(&root, rb); - if (!rb) { - bpf_spin_unlock(&lock); + bpf_spin_unlock(&lock); + if (!rb) return -5; - } =20 n =3D container_of(rb, struct node_data, r); + n_1 =3D bpf_obj_new(typeof(*n_1)); + if (!n_1) { + bpf_obj_drop(n); + return -1; + } + m_1 =3D bpf_refcount_acquire(n_1); + if (!m_1) { + bpf_obj_drop(n); + bpf_obj_drop(n_1); + return -1; + } + + bpf_spin_lock(&lock); + if (bpf_list_add(&n->l, &n_1->l)) { + bpf_spin_unlock(&lock); + bpf_obj_drop(n); + bpf_obj_drop(m_1); + return -8; + } + l =3D bpf_list_del(&n->l); + l_1 =3D bpf_list_del(&m_1->l); bpf_spin_unlock(&lock); bpf_obj_drop(n); - if (!l) - return -6; + bpf_obj_drop(m_1); =20 - bpf_obj_drop(container_of(l, struct node_data, l)); - return 0; + if (l) + bpf_obj_drop(container_of(l, struct node_data, l)); + else + err =3D -6; + + if (l_1) + bpf_obj_drop(container_of(l_1, struct node_data, l)); + else + err =3D -6; + + return err; } =20 SEC("?tc") -__failure __msg("bpf_spin_lock must be held for bpf_list_del") -long list_del_without_lock_fail(void *ctx) +__failure __msg("bpf_spin_lock must be held for bpf_list api") +long list_add_del_without_lock_fail(void *ctx) { + long err =3D 0; struct bpf_rb_node *rb; - struct bpf_list_node *l; - struct node_data *n; + struct bpf_list_node *l, *l_1; + struct node_data *n, *n_1, *m_1; + + err =3D __insert_in_tree_and_list(&head, &root, &lock); + if (err) + return err; =20 bpf_spin_lock(&lock); rb =3D bpf_rbtree_first(&root); @@ -429,13 +463,40 @@ long list_del_without_lock_fail(void *ctx) return -5; =20 n =3D container_of(rb, struct node_data, r); + n_1 =3D bpf_obj_new(typeof(*n_1)); + if (!n_1) { + bpf_obj_drop(n); + return -1; + } + m_1 =3D bpf_refcount_acquire(n_1); + if (!m_1) { + bpf_obj_drop(n); + bpf_obj_drop(n_1); + return -1; + } + + if (bpf_list_add(&n->l, &n_1->l)) { + bpf_obj_drop(n); + bpf_obj_drop(m_1); + return -8; + } + l =3D bpf_list_del(&n->l); + l_1 =3D bpf_list_del(&m_1->l); bpf_obj_drop(n); - if (!l) - return -6; + bpf_obj_drop(m_1); =20 - bpf_obj_drop(container_of(l, struct node_data, l)); - return 0; + if (l) + bpf_obj_drop(container_of(l, struct node_data, l)); + else + err =3D -6; + + if (l_1) + bpf_obj_drop(container_of(l_1, struct node_data, l)); + else + err =3D -6; + + return err; } =20 SEC("tc") --=20 2.50.1 (Apple Git-155) From nobody Thu Apr 9 12:06:55 2026 Received: from mail-pg1-f179.google.com (mail-pg1-f179.google.com [209.85.215.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E53D437755B for ; Mon, 2 Mar 2026 12:41:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455281; cv=none; b=qFFa6BVVnDeB4j7uB9pi+BcABnUn6RBqKrCmlJWhAj8ce3wHXEIsGc2WqEmQuIBGdX+eyVdIWOt/RVzJURo3gQtOqnuLaQre+AcBNPRuIxob6H1YVtWJ7wliTCgysa+TEWEKxm2X1Zy/XC+mYic4RzK+VyOwvGCa7N0RQ+bP1Fk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455281; c=relaxed/simple; bh=2pc+l+8zW6c1fnoyVeTRp6x7p+qsm1qsJPaPbldYX2w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OEyu3j+/FAnt0hWenF0lMdNtdF94JEUDA4mWygO9mFrE8K7XBHs69MdSAfdp5cx+NY/LgrLLbjnF/wiPQh8aPSeYGhv85KGyeccRsbx5wWUwoIHNlmRFIpP6qmhoyEmkPUeDxoBjNkgp2w5gNEJ++2ey0/7LkCAsoHKLJlSOVn4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jR9tBpKj; arc=none smtp.client-ip=209.85.215.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jR9tBpKj" Received: by mail-pg1-f179.google.com with SMTP id 41be03b00d2f7-c6e1f417918so1243185a12.3 for ; Mon, 02 Mar 2026 04:41:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772455279; x=1773060079; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SQj9PZXDyoiFhstulTRfahJNDAtfPbHGLUSOWnLuMe0=; b=jR9tBpKj8cRU6K9RpXtRAuQEyhSV9jqj1exq8LFo4CkRZREXMMEdMU30MhOpF9Ht0z zdI8LC5yeYrOBHX9wsi3FsuAu8xIw5WHtzIZWzDlwWCmYBAwtyIukzrWsjTWipHf/ksL BYiNXVFF1AklWLUWwNOYPCqPTMA0OzYRGKENI2rvaucmiLaqjhDazhEW4IktCLAyJly/ KKyrq0DVzuyMA5Em+P8uwcbDndL+j17B1EjMvUaDsTzWcPfrtD+onmUv7kvbl++dLPXR demAhesCeF3Z3cDVWZILuWKaEjrgfdKgASPcMA2Ko7t5TyFcUpo0eKIsAv4XSyUQ+FR+ ww+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772455279; x=1773060079; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=SQj9PZXDyoiFhstulTRfahJNDAtfPbHGLUSOWnLuMe0=; b=k9V83ec+bbxUyGMgO3rb7BU9zeNT8A76UIObyN4Qx42Yh3L4ApBnGUPwd5FX9pg8Mh 8WhwtZ11nBwdy96kcIjTh3+Zru44o9T15x0jWwIQlIba8X+mUlNIdiJ+6Rd1KRWQRfdy kikPEhWm742D/TwBhX5v9/29VIyL/hFTCHm19UfLyWgXJM4JmLliOlzSY6R8tlYxk4dV BWK199I0NCER4oum2OtYyHgdp4dAho901PWxpxCEpyW2CbYH4wpmtvwejR9fGuRJHFeP QE2ZuZhKbyhM3E0FwTq7JHRCoJjWhG2kR5bIQOJFcMPFZglIdspZhEv3B2IY1/ZAggku JuXA== X-Forwarded-Encrypted: i=1; AJvYcCWlm84iCMY37G0t+uGP2TR1qqBJYp0lAx/NdB4txVt79K8rmWfLAYX3UrMNziGrFnrM8iu1/H4e57zfW60=@vger.kernel.org X-Gm-Message-State: AOJu0YzpZDe38d8kE7XF/o3RqlegXyhzR7DGe3cFrpQ1lwWB8r9LWhit FIQvzwSReZtC2IgFd/rEpK4sN43FJx0HMbkGOHMW/EgFxaUTVc9/HFBC X-Gm-Gg: ATEYQzzcRQg6Qe7wChUQ7ptZ0/DrAovTBr1sZn2UtedQsWIb//DSCPFr8I0MbB3LP4C y9jUjsLUgx3sRdzYSvyAHyepl8MaC9XuKVnwZ2yW1p9YPqdYTw980v+hVq8/KrKPlVzRdH1tz6H HPjBwBOTfE/dfFdoY/JQtqwW/VvLgXv4WlHIJEvKORUq1s3iEui+39OgT/sIh50x9o1X6sAPV1i MQPtyce0DdVzezzdj25dHVUH4ZQHfXyhXolPSS9CWkDbitfe3R6vZ11lyKOcbjG6XNsACk7sB0K AAXom4564jQzupEgkMS2T9fTvXV51jSznFhlepaUvL7ZGlBJP6TPbyhu3QwSjbj1WudBTSPp9YF VLQSU25+70pdBweGTEiXNkyYi/BY1T46azem5VNFbZOlhUaBbm1yeW7t0UiX7Ko5KIbSaJVK2GR 2e1TpMoAkRFLOTV7msScg6AJeCMw823eG6oJZpAE7uUibPlRQC4e60jg== X-Received: by 2002:a17:903:35c6:b0:2ae:54b2:27d1 with SMTP id d9443c01a7336-2ae54b24ab8mr21528065ad.44.1772455279208; Mon, 02 Mar 2026 04:41:19 -0800 (PST) Received: from localhost.localdomain ([116.128.244.171]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ae3d1956absm67910225ad.14.2026.03.02.04.41.11 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 02 Mar 2026 04:41:18 -0800 (PST) From: Chengkaitao To: martin.lau@linux.dev, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com, jolsa@kernel.org, shuah@kernel.org, chengkaitao@kylinos.cn, linux-kselftest@vger.kernel.org Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 5/6] bpf: add bpf_list_is_first/last/empty kfuncs Date: Mon, 2 Mar 2026 20:40:27 +0800 Message-ID: <20260302124028.82420-6-pilgrimtao@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260302124028.82420-1-pilgrimtao@gmail.com> References: <20260302124028.82420-1-pilgrimtao@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kaitao Cheng Add three kfuncs for BPF linked list queries: - bpf_list_is_first(head, node): true if node is the first in the list. - bpf_list_is_last(head, node): true if node is the last in the list. - bpf_list_empty(head): true if the list has no entries. In previous versions, to implement the above functionality, it was necessary to first call bpf_list_pop_front/back to retrieve the first or last node before checking whether the passed-in node was the first or last one. After the check, the node had to be pushed back into the list using bpf_list_push_front/back, which was very inefficient. Now, with the bpf_list_is_first/last/empty kfuncs, we can directly check whether a node is the first, last, or whether the list is empty, without having to first retrieve the node. Signed-off-by: Kaitao Cheng --- kernel/bpf/helpers.c | 41 +++++++++++++++++++++++++++++++++++++++++ kernel/bpf/verifier.c | 15 +++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index d212962d4ed6..ada14eca58ab 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2528,6 +2528,44 @@ __bpf_kfunc int bpf_list_add_impl(struct bpf_list_no= de *prev, return __bpf_list_add_after(p, n, meta ? meta->record : NULL, off); } =20 +__bpf_kfunc bool bpf_list_is_first(struct bpf_list_head *head, struct bpf_= list_node *node) +{ + struct list_head *h =3D (struct list_head *)head; + struct bpf_list_node_kern *n =3D (struct bpf_list_node_kern *)node; + + if (unlikely(!h->next) || list_empty(h)) + return false; + + if (READ_ONCE(n->owner) !=3D head) + return false; + + return h->next =3D=3D &n->list_head; +} + +__bpf_kfunc bool bpf_list_is_last(struct bpf_list_head *head, struct bpf_l= ist_node *node) +{ + struct list_head *h =3D (struct list_head *)head; + struct bpf_list_node_kern *n =3D (struct bpf_list_node_kern *)node; + + if (unlikely(!h->next) || list_empty(h)) + return false; + + if (READ_ONCE(n->owner) !=3D head) + return false; + + return h->prev =3D=3D &n->list_head; +} + +__bpf_kfunc bool bpf_list_empty(struct bpf_list_head *head) +{ + struct list_head *h =3D (struct list_head *)head; + + if (unlikely(!h->next)) + return true; + + return list_empty(h); +} + __bpf_kfunc struct bpf_rb_node *bpf_rbtree_remove(struct bpf_rb_root *root, struct bpf_rb_node *node) { @@ -4598,6 +4636,9 @@ BTF_ID_FLAGS(func, bpf_list_del, KF_ACQUIRE | KF_RET_= NULL) BTF_ID_FLAGS(func, bpf_list_front, KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_back, KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_add_impl) +BTF_ID_FLAGS(func, bpf_list_is_first) +BTF_ID_FLAGS(func, bpf_list_is_last) +BTF_ID_FLAGS(func, bpf_list_empty) BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) BTF_ID_FLAGS(func, bpf_rbtree_remove, KF_ACQUIRE | KF_RET_NULL) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f5ee11779a5c..1c36b0938da7 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12454,6 +12454,9 @@ enum special_kfunc_type { KF_bpf_list_front, KF_bpf_list_back, KF_bpf_list_add_impl, + KF_bpf_list_is_first, + KF_bpf_list_is_last, + KF_bpf_list_empty, KF_bpf_cast_to_kern_ctx, KF_bpf_rdonly_cast, KF_bpf_rcu_read_lock, @@ -12516,6 +12519,9 @@ BTF_ID(func, bpf_list_del) BTF_ID(func, bpf_list_front) BTF_ID(func, bpf_list_back) BTF_ID(func, bpf_list_add_impl) +BTF_ID(func, bpf_list_is_first) +BTF_ID(func, bpf_list_is_last) +BTF_ID(func, bpf_list_empty) BTF_ID(func, bpf_cast_to_kern_ctx) BTF_ID(func, bpf_rdonly_cast) BTF_ID(func, bpf_rcu_read_lock) @@ -12992,7 +12998,10 @@ static bool is_bpf_list_api_kfunc(u32 btf_id) btf_id =3D=3D special_kfunc_list[KF_bpf_list_del] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_front] || btf_id =3D=3D special_kfunc_list[KF_bpf_list_back] || - btf_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl]; + btf_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl] || + btf_id =3D=3D special_kfunc_list[KF_bpf_list_is_first] || + btf_id =3D=3D special_kfunc_list[KF_bpf_list_is_last] || + btf_id =3D=3D special_kfunc_list[KF_bpf_list_empty]; } =20 static bool is_bpf_rbtree_api_kfunc(u32 btf_id) @@ -13115,7 +13124,9 @@ static bool check_kfunc_is_graph_node_api(struct bp= f_verifier_env *env, ret =3D (kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_front_i= mpl] || kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_push_back_impl= ] || kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_del] || - kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl]); + kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_add_impl] || + kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_is_first] || + kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_list_is_last]); break; case BPF_RB_NODE: ret =3D (kfunc_btf_id =3D=3D special_kfunc_list[KF_bpf_rbtree_remove] || --=20 2.50.1 (Apple Git-155) From nobody Thu Apr 9 12:06:55 2026 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F2EA9375ADD for ; Mon, 2 Mar 2026 12:41:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455288; cv=none; b=F7P41R+jnD3opQB/Wt/mbX1ecfgu5qlhRBfj7qf2uyqA/2dy7wrIPZ8TuaBy2tMvL09LJLznpiBbJ7B8qWnfvjoGQpqEn5oaS3ld4Pwj8k3dpVk/UEUQWdk3dIA3ihP6VPeU8SMxk6BF6Ffe0DOMDIyoXwEsKuxD5sODWLbn69E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772455288; c=relaxed/simple; bh=7sRo4nVSCezbQYCJvvf3hIL9zJ9NiAOafmugyoPklZM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=M1ED91aV3ZHnHH0UOC+/HDixsJBdlW3UT1ioNOyKKXUiXusmEHHay06tKtZ9K7plHAJePUnd4XmacJ8PnZDXLwD550y9myEF2RN/nknZPP+3o64FmWcO5+YEczI3O9G1vyHh7aEhQwoxtD8hpCZnsaTv4itRQr6cv0VW5ppuWKA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Tq6iNcMw; arc=none smtp.client-ip=209.85.214.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Tq6iNcMw" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2ad4d639db3so19928235ad.0 for ; Mon, 02 Mar 2026 04:41:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772455286; x=1773060086; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aWf2v8BqqxkSzBu1cxjzyfYlLbJQrEJW5Zx/O8qgXUk=; b=Tq6iNcMw2e6aKqPi21Jz3tBpnP7q2rxyoK0WIKF23o5ZIDIv/HT3oYT3IRRxo6sMvM SrL0vDbXdkQTDN/EAKPJsRHJrA8pco+LIuimKgJTjSy/3NlVdX8ytlhax9XJtBQ8ktch bxSrQEI8s3kOuD0/+FDJjn7UfAXKJPz85mgABK7b1ZHgDPRgew8R3gpa1/s8L/Lw9gsN WV/TuY3OeSLXxX41H4KZU3uG8X4qgt12qXXX117G/V+42k3FVVbmfdE+l+9bnAXPOJBQ Xk3ObOlo4hHt2wWExAL3Uvv9WqSKEgbkOjr8CjtCZOGi2dBIA5MtRVBxW0LQO4KTx9ew VB8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772455286; x=1773060086; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=aWf2v8BqqxkSzBu1cxjzyfYlLbJQrEJW5Zx/O8qgXUk=; b=imElGWCLylaUVf8F6AOsPCOV0dHzENfLiz/IBI152SF0ffQhf5mm15jIYABn3om8FZ 0EIMvActtNFQMwj3YjppeaaYAoOq58ZRiTbZ6pZH+mXUBMLotP5X8Hea8ghhEVFIJzzv TIXT9wYm38LMVBf0FUwg6C33lPzkOuvS0ipkciQt5DfWk4doBapemmqRlwScYivIXSlt MHEP2sUFEirhqSjmW6O0pLiSHmS6/QgMvv13jIH26eN4ZU2Tjeeag3ND6wd+mJnyGDJS oxEVPwNsX9jY1F/NzqxTm5Kx+8SdCds+MKxztXKB5M/IwDrdhZFjZPAjv+upVwM8UcUu FbRA== X-Forwarded-Encrypted: i=1; AJvYcCWbVhXrgWZ/NNLKBtcgJ954GkNfxDw2wczjOtywNZLeRWt/hR4IojUgNmwi6m/p7haVstfg+LtiUxXjLG8=@vger.kernel.org X-Gm-Message-State: AOJu0Yyk95vS21hGMRCUimKI/9h6MjG8VhAxgzR8pFaUPPuPhyFbecAY lYyp2bww2Ns6Ydvr1e/555Suh3eWWbANoU8obQpgUXvaJ8Qdk2Sl4Yl8 X-Gm-Gg: ATEYQzxZpT0aUDM0DujhMiqtilY5zD2lQV6I/CmCLldHXcvnERBzkN4qgV66PduXDqP iiY4cha3CDZ0dilBAhc84IcgM+lExXD5Kw7hCeZNUGTDuntkrRlR2+AFXPF4c04LIerfzOvndZO BZA9So9v1hzFm221+tWBxNNMMx3PeWEXUIl2VCft+I454K+pRQ0dJIX9rHdTcGMc1ndwhqbE6E7 S1zrt+psXfQqtHg8d+IpFK7oCcpoxh3KCTA2tyRqAur1hLmhcxOG5aKTGBUPatCJa07SeU/pEay TUaU9qmCib+/xZjc2qCfVAHUbDilMVmOqv98GoUpEk/42YeptIZ2Tt9sxgol33eWdDV6bsUFxhS NEpOOe1dyLChNexAzJYq7JmZplmzRwXObLWGxjtsgTJoouL9lc/fl232Dc42a9JOOYPVazqw+S3 0a0iTD3TiBctMhD6HKZOteo+i05wT1c089toHDbsok2N040Wb+IRruGFRJsPKurSdU X-Received: by 2002:a17:903:2289:b0:2a0:e223:f6e6 with SMTP id d9443c01a7336-2ae2e4d6622mr132178515ad.46.1772455286289; Mon, 02 Mar 2026 04:41:26 -0800 (PST) Received: from localhost.localdomain ([116.128.244.171]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ae3d1956absm67910225ad.14.2026.03.02.04.41.19 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 02 Mar 2026 04:41:26 -0800 (PST) From: Chengkaitao To: martin.lau@linux.dev, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com, jolsa@kernel.org, shuah@kernel.org, chengkaitao@kylinos.cn, linux-kselftest@vger.kernel.org Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 6/6] selftests/bpf: Add test cases for bpf_list_is_first/is_last/empty Date: Mon, 2 Mar 2026 20:40:28 +0800 Message-ID: <20260302124028.82420-7-pilgrimtao@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260302124028.82420-1-pilgrimtao@gmail.com> References: <20260302124028.82420-1-pilgrimtao@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kaitao Cheng Rename test_list_add_del to list_add_del_and_check and extend it to cover the new kfuncs: assert list non-empty after insert, assert is_first(n) and is_last(m_1) after bpf_list_add, and assert list empty after removing both nodes. Signed-off-by: Kaitao Cheng --- .../testing/selftests/bpf/bpf_experimental.h | 15 +++++++++++ .../selftests/bpf/progs/refcounted_kptr.c | 27 +++++++++++++++---- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing= /selftests/bpf/bpf_experimental.h index 48106ea5dda8..2eb107e771ad 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -123,6 +123,21 @@ extern int bpf_list_add_impl(struct bpf_list_node *pre= v, struct bpf_list_node *n /* Convenience macro to wrap over bpf_list_add_impl */ #define bpf_list_add(prev, node) bpf_list_add_impl(prev, node, NULL, 0) =20 +/* Description + * Return true if 'node' is the first node in the list with head 'head'. + */ +extern bool bpf_list_is_first(struct bpf_list_head *head, struct bpf_list_= node *node) __ksym; + +/* Description + * Return true if 'node' is the last node in the list with head 'head'. + */ +extern bool bpf_list_is_last(struct bpf_list_head *head, struct bpf_list_n= ode *node) __ksym; + +/* Description + * Return true if the list with head 'head' has no entries. + */ +extern bool bpf_list_empty(struct bpf_list_head *head) __ksym; + /* Description * Remove 'node' from rbtree with root 'root' * Returns diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr.c b/tools/te= sting/selftests/bpf/progs/refcounted_kptr.c index 4d979f5ad9e8..6797c53f550e 100644 --- a/tools/testing/selftests/bpf/progs/refcounted_kptr.c +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr.c @@ -367,14 +367,14 @@ long insert_rbtree_and_stash__del_tree_##rem_tree(voi= d *ctx) \ INSERT_STASH_READ(true, "insert_stash_read: remove from tree"); INSERT_STASH_READ(false, "insert_stash_read: don't remove from tree"); =20 -/* Insert one node in tree and list, remove it from tree, add a second - * node after it in list with bpf_list_add, then remove both nodes from - * list via bpf_list_del. +/* Insert one node in tree and list, remove it from tree, add a second node + * after it with bpf_list_add, check bpf_list_is_first/is_last/empty, then + * remove both nodes from list via bpf_list_del. */ SEC("tc") -__description("test_list_add_del: test bpf_list_add/del") +__description("list_add_del_and_check: test bpf_list_add/del/is_first/is_l= ast/empty") __success __retval(0) -long test_list_add_del(void *ctx) +long list_add_del_and_check(void *ctx) { long err =3D 0; struct bpf_rb_node *rb; @@ -386,6 +386,11 @@ long test_list_add_del(void *ctx) return err; =20 bpf_spin_lock(&lock); + if (bpf_list_empty(&head)) { + bpf_spin_unlock(&lock); + return -7; + } + rb =3D bpf_rbtree_first(&root); if (!rb) { bpf_spin_unlock(&lock); @@ -418,6 +423,14 @@ long test_list_add_del(void *ctx) return -8; } =20 + if (!bpf_list_is_first(&head, &n->l) || + !bpf_list_is_last(&head, &m_1->l)) { + bpf_spin_unlock(&lock); + bpf_obj_drop(n); + bpf_obj_drop(m_1); + return -9; + } + l =3D bpf_list_del(&n->l); l_1 =3D bpf_list_del(&m_1->l); bpf_spin_unlock(&lock); @@ -434,6 +447,10 @@ long test_list_add_del(void *ctx) else err =3D -6; =20 + bpf_spin_lock(&lock); + if (!bpf_list_empty(&head)) + err =3D -7; + bpf_spin_unlock(&lock); return err; } =20 --=20 2.50.1 (Apple Git-155)