mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
bpf: add bpf_list_is_first/last/empty kfuncs
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. Currently, without these kfuncs, to implement the above functionality it is 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 <chengkaitao@kylinos.cn> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> Link: https://lore.kernel.org/r/20260521032306.97118-8-kaitao.cheng@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
a3493ca504
commit
745515d386
|
|
@ -2656,6 +2656,43 @@ __bpf_kfunc struct bpf_list_node *bpf_list_back(struct bpf_list_head *head)
|
|||
return (struct bpf_list_node *)h->prev;
|
||||
}
|
||||
|
||||
__bpf_kfunc bool bpf_list_is_first(struct bpf_list_head *head,
|
||||
struct bpf_list_node *node__nonown_allowed)
|
||||
{
|
||||
struct list_head *h = (struct list_head *)head;
|
||||
struct bpf_list_node_kern *kn = (struct bpf_list_node_kern *)node__nonown_allowed;
|
||||
|
||||
if (READ_ONCE(kn->owner) != head)
|
||||
return false;
|
||||
|
||||
return list_is_first(&kn->list_head, h);
|
||||
}
|
||||
|
||||
__bpf_kfunc bool bpf_list_is_last(struct bpf_list_head *head,
|
||||
struct bpf_list_node *node__nonown_allowed)
|
||||
{
|
||||
struct list_head *h = (struct list_head *)head;
|
||||
struct bpf_list_node_kern *kn = (struct bpf_list_node_kern *)node__nonown_allowed;
|
||||
|
||||
if (READ_ONCE(kn->owner) != head)
|
||||
return false;
|
||||
|
||||
return list_is_last(&kn->list_head, h);
|
||||
}
|
||||
|
||||
__bpf_kfunc bool bpf_list_empty(struct bpf_list_head *head)
|
||||
{
|
||||
struct list_head *h = (struct list_head *)head;
|
||||
|
||||
/* If list_head was 0-initialized by map, bpf_obj_init_field wasn't
|
||||
* called on its fields, so init here
|
||||
*/
|
||||
if (unlikely(!h->next))
|
||||
INIT_LIST_HEAD(h);
|
||||
|
||||
return list_empty(h);
|
||||
}
|
||||
|
||||
__bpf_kfunc struct bpf_rb_node *bpf_rbtree_remove(struct bpf_rb_root *root,
|
||||
struct bpf_rb_node *node)
|
||||
{
|
||||
|
|
@ -4772,6 +4809,9 @@ 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_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)
|
||||
|
|
|
|||
|
|
@ -10965,6 +10965,9 @@ enum special_kfunc_type {
|
|||
KF_bpf_list_del,
|
||||
KF_bpf_list_front,
|
||||
KF_bpf_list_back,
|
||||
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,
|
||||
|
|
@ -11035,6 +11038,9 @@ 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_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)
|
||||
|
|
@ -11556,7 +11562,10 @@ static bool is_bpf_list_api_kfunc(u32 btf_id)
|
|||
btf_id == special_kfunc_list[KF_bpf_list_pop_back] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_list_del] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_list_front] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_list_back];
|
||||
btf_id == special_kfunc_list[KF_bpf_list_back] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_list_is_first] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_list_is_last] ||
|
||||
btf_id == special_kfunc_list[KF_bpf_list_empty];
|
||||
}
|
||||
|
||||
static bool is_bpf_rbtree_api_kfunc(u32 btf_id)
|
||||
|
|
@ -11678,7 +11687,9 @@ static bool check_kfunc_is_graph_node_api(struct bpf_verifier_env *env,
|
|||
switch (node_field_type) {
|
||||
case BPF_LIST_NODE:
|
||||
ret = is_bpf_list_push_kfunc(kfunc_btf_id) ||
|
||||
kfunc_btf_id == special_kfunc_list[KF_bpf_list_del];
|
||||
kfunc_btf_id == special_kfunc_list[KF_bpf_list_del] ||
|
||||
kfunc_btf_id == special_kfunc_list[KF_bpf_list_is_first] ||
|
||||
kfunc_btf_id == special_kfunc_list[KF_bpf_list_is_last];
|
||||
break;
|
||||
case BPF_RB_NODE:
|
||||
ret = (is_bpf_rbtree_add_kfunc(kfunc_btf_id) ||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user