ebpf sockops 代码解读

2032 static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)
2033 {
2034         struct bpf_sock_ops_kern sock_ops;
2035         int ret;
2036
2037         memset(&sock_ops, 0, offsetof(struct bpf_sock_ops_kern, temp));
2038         if (sk_fullsock(sk)) {
2039                 sock_ops.is_fullsock = 1;
2040                 sock_owned_by_me(sk);
2041         }
2042
2043         sock_ops.sk = sk;
2044         sock_ops.op = op;
2045         if (nargs > 0)
2046                 memcpy(sock_ops.args, args, nargs * sizeof(*args));
2047
2048         ret = BPF_CGROUP_RUN_PROG_SOCK_OPS(&sock_ops);
2049         if (ret == 0)
2050                 ret = sock_ops.reply;
2051         else
2052                 ret = -1;
2053         return ret;
2054 }
2055
2056 static inline int tcp_call_bpf_2arg(struct sock *sk, int op, u32 arg1, u32 arg2)
2057 {
2058         u32 args[2] = {arg1, arg2};
2059
2060         return tcp_call_bpf(sk, op, 2, args);
2061 }
2062
2063 static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2,
2064                                     u32 arg3)
2065 {
2066         u32 args[3] = {arg1, arg2, arg3};
2067
2068         return tcp_call_bpf(sk, op, 3, args);
2069 }

ebpf sockops 的定义的埋点函数有三个:

tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)

tcp_call_bpf_2arg(struct sock *sk, int op, u32 arg1, u32 arg2)

tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, u32 arg3)

在TCP的主路径流程中调用这个三个函数埋点,然后用户自己定义的处理函数挂载道相关的链表上,最终在这三个函数中遍历用户的定义的函数,最终实现功能。

tcp_call_bpf_2arg / tcp_call_bpf_3arg 参数个数有差异,最终参数封装为struct bpf_sock_ops_kern结构,传递给回调函数。

2032 static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)
2033 {
2034         struct bpf_sock_ops_kern sock_ops;
2035         int ret;
2036
2037         memset(&sock_ops, 0, offsetof(struct bpf_sock_ops_kern, temp));
2038         if (sk_fullsock(sk)) {
2039                 sock_ops.is_fullsock = 1;
2040                 sock_owned_by_me(sk);
2041         }
2042
2043         sock_ops.sk = sk;
2044         sock_ops.op = op;
2045         if (nargs > 0)
2046                 memcpy(sock_ops.args, args, nargs * sizeof(*args));
2047
2048         ret = BPF_CGROUP_RUN_PROG_SOCK_OPS(&sock_ops);
2049         if (ret == 0)
2050                 ret = sock_ops.reply;
2051         else
2052                 ret = -1;
2053         return ret;
2054 }

sock_ops是参数结构,2043、2044初始化记录了sk/op两个参数,2046则是拷贝了多个参数,最多支持四个参数。

2048行,BPF_CGROUP_RUN_PROG_SOCK_OPS(&sock_ops)遍历回调函数,同时将sock_ops作为参数传递给回调函数。

169 #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops)                                 \
170 ({                                                                             \
171         int __ret = 0;                                                         \
172         if (cgroup_bpf_enabled && (sock_ops)->sk) {            \
173                 typeof(sk) __sk = sk_to_full_sk((sock_ops)->sk);               \
174                 if (__sk && sk_fullsock(__sk))                                 \
175                         __ret = __cgroup_bpf_run_filter_sock_ops(__sk,         \
176                                                                  sock_ops,     \
177                                                          BPF_CGROUP_SOCK_OPS); \
178         }                                                                      \
179         __ret;                                                                 \
180 })


612 int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
613                                      struct bpf_sock_ops_kern *sock_ops,
614                                      enum bpf_attach_type type)
615 {
616         struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
617         int ret;
618
619         ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], sock_ops,
620                                  BPF_PROG_RUN);
621         return ret == 1 ? 0 : -EPERM;
622 }

392 #define BPF_PROG_RUN_ARRAY(array, ctx, func)            \
393         __BPF_PROG_RUN_ARRAY(array, ctx, func, false)


370
371 #define __BPF_PROG_RUN_ARRAY(array, ctx, func, check_non_null)  \
372         ({                                              \
373                 struct bpf_prog **_prog, *__prog;       \
374                 struct bpf_prog_array *_array;          \
375                 u32 _ret = 1;                           \
376                 preempt_disable();                      \
377                 rcu_read_lock();                        \
378                 _array = rcu_dereference(array);        \
379                 if (unlikely(check_non_null && !_array))\
380                         goto _out;                      \
381                 _prog = _array->progs;                  \
382                 while ((__prog = READ_ONCE(*_prog))) {  \
383                         _ret &= func(__prog, ctx);      \
384                         _prog++;                        \
385                 }                                       \
386 _out:                                                   \
387                 rcu_read_unlock();                      \
388                 preempt_enable_no_resched();            \
389                 _ret;                                   \
390          })

382                 while ((__prog = READ_ONCE(*_prog))) {  \
383                         _ret &= func(__prog, ctx);      \
384                         _prog++;                        \
385                 }   

上面的代码片段真正实现了注册的回调函数回调功能。ctx参数正式上面的sock_ops。

__BPF_PROG_RUN_ARRAY 宏实现中有一行代码至关重要:preempt_enable_no_resched()

区别常规的preempt_enable函数,这个函数带了no_resched,表示说执行当前代码片段后不能创造一个重新调度的抢占点。试想一下,如果preempt_enable,这时候会创造了一个重新调度抢占点,这时可能切换到其他高优先级的任务重,这时候是可能出现死锁的。 所以,最稳妥的办法是回调函数执行完后,继续回到原来的代码路径上,继续执行之前为执行的代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
eBPF python是一个框架,允许用户编写使用eBPF程序嵌入其中的Python程序。这个框架主要用于应用程序和系统分析/跟踪的场景,其中eBPF程序用于收集统计信息或生成事件,而用户空间中的对应部分则负责收集数据并以人类可读的形式进行显示。运行Python程序会生成eBPF字节码并将其加载到内核中。 在Python中使用eBPF的高级封装BCC来开发eBPF程序的步骤如下: 1. 首先,通过导入BCC库来使用它提供的功能。 2. 使用BPF类加载eBPF程序。 3. 使用attach_kprobe方法将BPF程序挂载到内核的探针上。 4. 使用trace_print方法读取内核调试文件的内容并打印到标准输出中。 在另一个例子中,也是使用BCC库来开发eBPF程序的步骤如下: 1. 导入BCC库。 2. 加载eBPF程序。 3. 使用attach_kprobe方法将BPF程序挂载到内核的探针上。 4. 使用trace_print方法读取内核调试文件的内容并打印到标准输出中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [eBPF学习笔记(二)—— eBPF开发工具](https://blog.csdn.net/qq_41988448/article/details/127813132)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [eBPF学习 - 入门](https://blog.csdn.net/hzb869168467/article/details/124239015)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值