diff options
| author | Kuniyuki Iwashima <kuniyu@google.com> | 2026-10-08 03:15:22 +0000 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-10-10 16:38:12 +0000 |
| commit | 4e2385abecb03998922afbfb91caff273fe6cbee (patch) | |
| tree | cd78253e9569450090ee90935861d5978b0737c2 /include | |
| parent | 114ce944d01500ffcbf9159a72806d7f633ad9ae (diff) | |
| download | linux-next-4e2385abecb03998922afbfb91caff273fe6cbee.tar.gz linux-next-4e2385abecb03998922afbfb91caff273fe6cbee.zip | |
bpf: tcp: Add a new per-socket flag and kfunc for bpf_tcp_ops.
The legacy SOCK_OPS guards some hooks with a per-socket flag,
tp->bpf_sock_ops_cb_flags.
In contrast, bpf_tcp_ops was initially designed without per-socket
flags so that users can simply define only the callbacks they need.
However, it turned out that even attaching a bpf_tcp_ops with a NULL
callback incurs measurable overhead in the fast path. [0]
We could reuse tp->bpf_sock_ops_cb_flags, but it is u8 and has only
1 bit left for a new opt-in callback, while not all of the 7 existing
opt-in hooks are in the fast path and really need a flag guard.
Moreover, tp->bpf_sock_ops_cb_flags has two bpf helper functions to
update it, both of which require sock_owned_by_me(sk):
* bpf_sock_ops_cb_flags_set()
* bpf_setsockopt(TCP_BPF_SOCK_OPS_CB_FLAGS)
Both helpers only overwrite the field, which leads to reading and
modifying the flags in the BPF prog and then writing them back
via the helper. This prevents use from the fast path (tc or
cgroup_skb) hooks where bh_lock_sock() alone cannot prevent races
with process context.
Let's add a new u32 field, tp->bpf_tcp_ops_flags, at the end of the
tcp_sock_read_txrx cacheline group, along with a new kfunc,
bpf_tcp_ops_set_flags().
bpf_tcp_ops_set_flags() takes bitmasks of flags to enable and
disable and updates tp->bpf_tcp_ops_flags atomically via
try_cmpxchg() without relying on lock_sock().
The kfunc is exposed to bpf_tcp_ops, BPF_PROG_TYPE_CGROUP_SOCKOPT,
and BPF_PROG_TYPE_CGROUP_SKB.
The first argument is struct tcp_sock * so that bpf_tcp_sock() etc
is required for cgroup hooks, but not for bpf_tcp_ops where struct
sock * is promoted to struct tcp_sock * automatically.
The fast-path callbacks will be guarded by the new flags in a later
patch after updating the existing selftest to avoid breaking bisection.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Reviewed-by: Amery Hung <ameryhung@gmail.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://lore.kernel.org/netdev/CAMB2axMwBuz3X4Uwn5uzZUqm91EbiMnbQX832wUFOQ44fOwDnQ@mail.gmail.com/ #[0]
Link: https://patch.msgid.link/20261008031604.256498-3-kuniyu@google.com
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/tcp.h | 7 | ||||
| -rw-r--r-- | include/net/tcp.h | 11 | ||||
| -rw-r--r-- | include/uapi/linux/bpf.h | 23 |
3 files changed, 40 insertions, 1 deletions
diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 6a8c77719322..24bb751cc65a 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -233,6 +233,13 @@ struct tcp_sock { is_sack_reneg:1, /* in recovery from loss with SACK reneg? */ is_cwnd_limited:1,/* forward progress limited by snd_cwnd? */ recvmsg_inq : 1;/* Indicate # of bytes in queue upon recvmsg */ +#ifdef CONFIG_BPF + u32 bpf_tcp_ops_flags; +#define BPF_TCP_OPS_TEST_FLAG(TP, ARG) \ + (READ_ONCE((TP)->bpf_tcp_ops_flags) & BPF_TCP_OPS_FLAG_ ## ARG) +#else +#define BPF_TCP_OPS_TEST_FLAG(TP, ARG) (0) +#endif __cacheline_group_end(tcp_sock_read_txrx); /* RX read-mostly hotpath cache lines */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 4ecabf4989de..55ad9db99596 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2934,6 +2934,7 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk) { tcp_sk(sk)->bpf_sock_ops_cb_flags = 0; + WRITE_ONCE(tcp_sk(sk)->bpf_tcp_ops_flags, 0); } #else @@ -2989,7 +2990,7 @@ struct bpf_tcp_ops { /* Called when the retransmission timer fires. */ void (*rto)(struct sock *sk); - /* Called on every RTT sample. + /* Called on every RTT sample if BPF_TCP_OPS_FLAG_RTT is enabled. * @mrtt: the measured RTT, in microseconds. * @srtt: the updated smoothed RTT. */ @@ -3016,6 +3017,10 @@ struct bpf_tcp_ops { * Parse the TCP header options of an incoming skb received on an * established connection. Use bpf_dynptr_from_skb()/bpf_skb_load_bytes() * to access the options. + * + * Called if BPF_TCP_OPS_FLAG_PARSE_HDR_OPT_ALL is enabled, or if + * BPF_TCP_OPS_FLAG_PARSE_HDR_OPT_UNKNOWN is enabled and an unknown + * option is received. */ void (*parse_hdr)(struct sock *sk, struct sk_buff *skb); @@ -3023,6 +3028,8 @@ struct bpf_tcp_ops { * Reserve space in the outgoing TCP header for options to be written * later by write_hdr_opt(). Call bpf_reserve_hdr_opt() to reserve bytes. * + * Called if BPF_TCP_OPS_FLAG_WRITE_HDR_OPT is enabled. + * * @skb: outgoing packet. NULL when called from tcp_current_mss() * (MSS sizing). * @req: request_sock on the synack path; NULL otherwise. @@ -3041,6 +3048,8 @@ struct bpf_tcp_ops { * Use bpf_store_hdr_opt() to write; it appends within the reserved window * shared with legacy SOCKOPS. * + * Called if BPF_TCP_OPS_FLAG_WRITE_HDR_OPT is enabled. + * * @skb: outgoing packet. * @req: request_sock on the synack path; NULL otherwise. * @syn_skb: incoming SYN on the synack path; NULL otherwise. diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index e0ed44b1bbcb..6963c146311e 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -7340,6 +7340,29 @@ enum { */ }; +/* + * Most callbacks of struct bpf_tcp_ops are placed in the slow + * path (e.g., one-shot connection setup or unlikely events like + * timers) and are invoked simply by defining non-NULL callbacks. + * + * Callbacks in the fast path, however, would incur noticeable + * overhead even when set to NULL, so they are disabled by default + * and must be explicitly enabled per socket via bpf_tcp_ops_set_flags(). + * + * The flags are per-socket and shared by all effective bpf_tcp_ops + * programs. + */ +enum { + /* .rtt() */ + BPF_TCP_OPS_FLAG_RTT = (1 << 0), + /* .parse_hdr() */ + BPF_TCP_OPS_FLAG_PARSE_HDR_OPT_ALL = (1 << 1), + BPF_TCP_OPS_FLAG_PARSE_HDR_OPT_UNKNOWN = (1 << 2), + /* .hdr_opt_len() and .write_hdr_opt() */ + BPF_TCP_OPS_FLAG_WRITE_HDR_OPT = (1 << 3), + BPF_TCP_OPS_FLAG_ALL = (1 << 4) - 1, +}; + /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect * changes between the TCP and BPF versions. Ideally this should never happen. * If it does, we need to add code to convert them before calling |
