From: Kaitao Cheng <[email protected]>

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 <[email protected]>
---
 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_node 
*prev,
        return __bpf_list_add_after(p, n, meta ? meta->record : NULL, off);
 }
 
+__bpf_kfunc bool bpf_list_is_first(struct bpf_list_head *head, struct 
bpf_list_node *node)
+{
+       struct list_head *h = (struct list_head *)head;
+       struct bpf_list_node_kern *n = (struct bpf_list_node_kern *)node;
+
+       if (unlikely(!h->next) || list_empty(h))
+               return false;
+
+       if (READ_ONCE(n->owner) != head)
+               return false;
+
+       return h->next == &n->list_head;
+}
+
+__bpf_kfunc bool bpf_list_is_last(struct bpf_list_head *head, struct 
bpf_list_node *node)
+{
+       struct list_head *h = (struct list_head *)head;
+       struct bpf_list_node_kern *n = (struct bpf_list_node_kern *)node;
+
+       if (unlikely(!h->next) || list_empty(h))
+               return false;
+
+       if (READ_ONCE(n->owner) != head)
+               return false;
+
+       return h->prev == &n->list_head;
+}
+
+__bpf_kfunc bool bpf_list_empty(struct bpf_list_head *head)
+{
+       struct list_head *h = (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 923deb73683b..149eac5f70e3 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12435,6 +12435,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,
@@ -12497,6 +12500,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)
@@ -12973,7 +12979,10 @@ static bool is_bpf_list_api_kfunc(u32 btf_id)
               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_add_impl];
+              btf_id == special_kfunc_list[KF_bpf_list_add_impl] ||
+              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)
@@ -13096,7 +13105,9 @@ static bool check_kfunc_is_graph_node_api(struct 
bpf_verifier_env *env,
                ret = (kfunc_btf_id == 
special_kfunc_list[KF_bpf_list_push_front_impl] ||
                       kfunc_btf_id == 
special_kfunc_list[KF_bpf_list_push_back_impl] ||
                       kfunc_btf_id == special_kfunc_list[KF_bpf_list_del] ||
-                      kfunc_btf_id == 
special_kfunc_list[KF_bpf_list_add_impl]);
+                      kfunc_btf_id == special_kfunc_list[KF_bpf_list_add_impl] 
||
+                      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 = (kfunc_btf_id == special_kfunc_list[KF_bpf_rbtree_remove] 
||
-- 
2.50.1 (Apple Git-155)


Reply via email to