diff options
| author | Kuniyuki Iwashima <kuniyu@google.com> | 2026-10-08 03:15:26 +0000 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-10-10 16:38:13 +0000 |
| commit | 32d17fe8a9a5d83b8b4fe1fb453193221aec4a9b (patch) | |
| tree | 8974f6c0596ed613d17d098113f25a20f9b046f2 /include/uapi/linux | |
| parent | c071408db308a8f91d7be71b45bbf2da8c5e15f4 (diff) | |
bpf: tcp: Introduce bpf_tcp_ops.{enqueue,dequeue}_rcvq().
Waking up a thread per packet is expensive when an application
processes variable-length frames (e.g., RPC) that span multiple
packets.
SO_RCVLOWAT can defer wakeups, but because the frame size is
encoded in a fixed-size descriptor at the start of each frame,
the application has to:
1. wake up and recv() the descriptor,
2. raise SO_RCVLOWAT to the payload size via setsockopt(),
3. wake up and recv() the payload, and
4. reset SO_RCVLOWAT back to the descriptor size via
setsockopt() for the next frame.
This requires an extra wakeup and two setsockopt() syscalls
for every single RPC frame.
With SOCKMAP, we can parse skb and suppress wakeups in kernel,
but SOCKMAP adds overhead and also kills zerocopy.
Let's add lighter-weight opt-in callbacks to bpf_tcp_ops to
replace that.
.enqueue_rcvq(): invoked when TCP stack enqueues skb to
sk->sk_receive_queue
.dequeue_rcvq(): invoked in tcp_cleanup_rbuf() after data
is dequeued from sk->sk_receive_queue
Those callbacks can be enabled on a per-socket basis by
bpf_tcp_ops_set_flags():
bpf_tcp_ops_set_flags((struct tcp_sock *)sk,
BPF_TCP_OPS_FLAG_RCVQ, 0);
Later, we will add a new kfunc to adjust sk->sk_rcvlowat from
these callbacks.
This will allow the bpf_tcp_ops prog to parse each skb and
dynamically adjust sk->sk_rcvlowat to suppress unnecessary EPOLLIN
wakeups until sufficient data is available in the receive queue.
The placement of bpf_tcp_ops_call() in tcp_ofo_queue() and
tcp_fastopen_add_skb() is chosen to provide the same snapshot
as tcp_queue_rcv().
For example, if bpf_tcp_ops_call() were called before updating
TCP_SKB_CB(skb)->seq in tcp_fastopen_add_skb(), BPF prog would
need an extra branch for the unlikely TFO case to strip SYN.
In addition, the TCP stack can queue overlapping skbs into recvq.
Once rcv_nxt is updated with a new skb, BPF prog can no longer
infer the previous rcv_nxt from skb->len.
Lastly, dequeue_rcvq() is placed in tcp_cleanup_rbuf() rather
than __tcp_cleanup_rbuf() so that it is not called for sockets
in SOCKMAP, where calling sk->sk_data_ready() from the new
kfunc would otherwise trigger infinite recursion.
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://patch.msgid.link/20261008031604.256498-7-kuniyu@google.com
Diffstat (limited to 'include/uapi/linux')
| -rw-r--r-- | include/uapi/linux/bpf.h | 4 |
1 files changed, 3 insertions, 1 deletions
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 6963c146311e..6f70db7515dd 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -7360,7 +7360,9 @@ enum { 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, + /* .enqueue_rcvq() and .dequeue_rcvq() */ + BPF_TCP_OPS_FLAG_RCVQ = (1 << 4), + BPF_TCP_OPS_FLAG_ALL = (1 << 5) - 1, }; /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect |
