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 }
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 })
391
392 #define BPF_PROG_RUN_ARRAY(array, ctx, func) \
393 __BPF_PROG_RUN_ARRAY(array, ctx, func, false)
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(); \
上面的代码片段真正实现了注册的回调函数回调功能。ctx参数正式上面的sock_ops。
__BPF_PROG_RUN_ARRAY 宏实现中有一行代码非常重要:
preempt_enable_no_resched()
区别常规的preempt_enable函数,这个函数带了no_resched,表示说执行当前代码片段后不能创造一个重新调度的抢占点。试想一下,如果preempt_enable,这时候会创造了一个重新调度抢占点,这时可能切换到其他高优先级的任务重,这时候是可能出现死锁的。 所以,最稳妥的办法是回调函数执行完后,继续回到原来的代码路径上,继续执行之前为执行的代码。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved