From: Cong Wang <hidden> Date: 2021-08-05 18:57:56
From: Cong Wang <redacted>
This patchset adds 12 more tracepoints to TCP/IP stack, both
IPv4 and IPv6. The goal is to trace skb in different layers
and to measure the latency of each layer.
We only add information we need to each trace point. If any other
information is needed, it is easy to extend without breaking ABI,
see commit 3dd344ea84e1 ("net: tracepoint: exposing sk_family in all
tcp:tracepoints").
And similar to trace_qdisc_enqueue(), we only intend to trace
success cases, because most (if not all) failure cases can be traced
via kfree_skb() even if they are really interesting.
Lastly, per previous discussion, trace ring buffer is only accessible
to privileged users, it is safe to use a real kernel address with %px.
Qitao Xu (13):
net: introduce a new header file include/trace/events/ip.h
ipv4: introduce tracepoint trace_ip_queue_xmit()
tcp: introduce tracepoint trace_tcp_transmit_skb()
udp: introduce tracepoint trace_udp_send_skb()
udp: introduce tracepoint trace_udp_v6_send_skb()
ipv4: introduce tracepoint trace_ip_rcv()
ipv6: introduce tracepoint trace_ipv6_rcv()
ipv4: introduce tracepoint trace_ip_local_deliver_finish()
udp: introduce tracepoint trace_udp_rcv()
ipv6: introduce tracepoint trace_udpv6_rcv()
tcp: introduce tracepoint trace_tcp_v4_rcv()
ipv6: introduce tracepoint trace_tcp_v6_rcv()
sock: introduce tracepoint trace_sk_data_ready()
include/trace/events/ip.h | 140 ++++++++++++++++++++++++++++++++++++
include/trace/events/sock.h | 19 +++++
include/trace/events/tcp.h | 27 ++++++-
include/trace/events/udp.h | 74 +++++++++++++++++++
net/core/net-traces.c | 7 ++
net/ipv4/ip_input.c | 9 ++-
net/ipv4/ip_output.c | 10 ++-
net/ipv4/tcp_input.c | 8 ++-
net/ipv4/tcp_ipv4.c | 2 +
net/ipv4/tcp_output.c | 8 ++-
net/ipv4/udp.c | 17 ++++-
net/ipv6/ip6_input.c | 5 +-
net/ipv6/tcp_ipv6.c | 3 +
net/ipv6/udp.c | 9 ++-
14 files changed, 327 insertions(+), 11 deletions(-)
create mode 100644 include/trace/events/ip.h
--
2.27.0
@@ -41,6 +41,48 @@TP_STORE_V4MAPPED(__entry,saddr,daddr)#endif+TRACE_EVENT(ip_queue_xmit,++TP_PROTO(conststructsock*sk,conststructsk_buff*skb),++TP_ARGS(sk,skb),++TP_STRUCT__entry(+__field(constvoid*,skbaddr)+__field(constvoid*,skaddr)+__field(__u16,sport)+__field(__u16,dport)+__array(__u8,saddr,4)+__array(__u8,daddr,4)+__array(__u8,saddr_v6,16)+__array(__u8,daddr_v6,16)+),++TP_fast_assign(+structinet_sock*inet=inet_sk(sk);+__be32*p32;++__entry->skbaddr=skb;+__entry->skaddr=sk;++__entry->sport=ntohs(inet->inet_sport);+__entry->dport=ntohs(inet->inet_dport);++p32=(__be32*)__entry->saddr;+*p32=inet->inet_saddr;++p32=(__be32*)__entry->daddr;+*p32=inet->inet_daddr;++TP_STORE_ADDRS(__entry,inet->inet_saddr,inet->inet_daddr,+sk->sk_v6_rcv_saddr,sk->sk_v6_daddr);+),++TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c skbaddr=%px",+__entry->sport,__entry->dport,__entry->saddr,__entry->daddr,+__entry->saddr_v6,__entry->daddr_v6,__entry->skbaddr)+);+#endif /* _TRACE_IP_H *//* This part must be outside protection */
@@ -1420,8 +1420,14 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,staticinttcp_transmit_skb(structsock*sk,structsk_buff*skb,intclone_it,gfp_tgfp_mask){-return__tcp_transmit_skb(sk,skb,clone_it,gfp_mask,+intret;++ret=__tcp_transmit_skb(sk,skb,clone_it,gfp_mask,tcp_sk(sk)->rcv_nxt);+if(!ret)+trace_tcp_transmit_skb(sk,skb);+returnret;+}/* This routine just queues the buffer for sending.
@@ -27,6 +27,25 @@ TRACE_EVENT(udp_fail_queue_rcv_skb,TP_printk("rc=%d port=%hu",__entry->rc,__entry->lport));+TRACE_EVENT(udp_send_skb,++TP_PROTO(conststructsock*sk,conststructsk_buff*skb),++TP_ARGS(sk,skb),++TP_STRUCT__entry(+__field(constvoid*,skaddr)+__field(constvoid*,skbaddr)+),++TP_fast_assign(+__entry->skaddr=sk;+__entry->skbaddr=skb;+),++TP_printk("skaddr=%px, skbaddr=%px",__entry->skaddr,__entry->skbaddr)+);+#endif /* _TRACE_UDP_H *//* This part must be outside protection */
@@ -46,6 +46,27 @@ TRACE_EVENT(udp_send_skb,TP_printk("skaddr=%px, skbaddr=%px",__entry->skaddr,__entry->skbaddr));+#if IS_ENABLED(CONFIG_IPV6)+TRACE_EVENT(udp_v6_send_skb,++TP_PROTO(conststructsock*sk,conststructsk_buff*skb),++TP_ARGS(sk,skb),++TP_STRUCT__entry(+__field(constvoid*,skaddr)+__field(constvoid*,skbaddr)+),++TP_fast_assign(+__entry->skaddr=sk;+__entry->skbaddr=skb;+),++TP_printk("skaddr=%px, skbaddr=%px",__entry->skaddr,__entry->skbaddr)+);+#endif+#endif /* _TRACE_UDP_H *//* This part must be outside protection */
@@ -83,6 +83,22 @@ TRACE_EVENT(ip_queue_xmit,__entry->saddr_v6,__entry->daddr_v6,__entry->skbaddr));+TRACE_EVENT(ip_rcv,+TP_PROTO(conststructsk_buff*skb),++TP_ARGS(skb),++TP_STRUCT__entry(+__field(constvoid*,skbaddr)+),++TP_fast_assign(+__entry->skbaddr=skb;+),++TP_printk("skbaddr=%px",__entry->skbaddr)+);+#endif /* _TRACE_IP_H *//* This part must be outside protection */
@@ -99,6 +99,24 @@ TRACE_EVENT(ip_rcv,TP_printk("skbaddr=%px",__entry->skbaddr));+#if IS_ENABLED(CONFIG_IPV6)+TRACE_EVENT(ipv6_rcv,+TP_PROTO(conststructsk_buff*skb),++TP_ARGS(skb),++TP_STRUCT__entry(+__field(constvoid*,skbaddr)+),++TP_fast_assign(+__entry->skbaddr=skb;+),++TP_printk("skbaddr=%px",__entry->skbaddr)+);+#endif+#endif /* _TRACE_IP_H *//* This part must be outside protection */
@@ -117,6 +117,23 @@ TRACE_EVENT(ipv6_rcv,);#endif+TRACE_EVENT(ip_local_deliver_finish,++TP_PROTO(conststructsk_buff*skb),++TP_ARGS(skb),++TP_STRUCT__entry(+__field(constvoid*,skbaddr)+),++TP_fast_assign(+__entry->skbaddr=skb;+),++TP_printk("skbaddr=%px",__entry->skbaddr)+);+#endif /* _TRACE_IP_H *//* This part must be outside protection */
From: Cong Wang <hidden> Date: 2021-08-05 18:58:17
From: Qitao Xu <redacted>
Tracepoint trace_sk_data_ready is introduced to trace skb
at exit of socket layer on RX side. Here we only implement
it for UDP and TCP.
Reviewed-by: Cong Wang <redacted>
Signed-off-by: Qitao Xu <redacted>
---
include/trace/events/sock.h | 19 +++++++++++++++++++
net/ipv4/tcp_input.c | 8 +++++++-
net/ipv4/udp.c | 5 ++++-
3 files changed, 30 insertions(+), 2 deletions(-)
@@ -261,6 +261,25 @@ TRACE_EVENT(inet_sk_error_report,__entry->error));+TRACE_EVENT(sk_data_ready,++TP_PROTO(conststructsock*sk,conststructsk_buff*skb),++TP_ARGS(sk,skb),++TP_STRUCT__entry(+__field(constvoid*,skaddr)+__field(constvoid*,skbaddr)+),++TP_fast_assign(+__entry->skaddr=sk;+__entry->skbaddr=skb;+),++TP_printk("skaddr=%px, skbaddr=%px",__entry->skaddr,__entry->skbaddr)+);+#endif /* _TRACE_SOCK_H *//* This part must be outside protection */
From: Cong Wang <hidden> Date: 2021-08-06 02:22:37
On Thu, Aug 5, 2021 at 11:57 AM Cong Wang [off-list ref] wrote:
From: Cong Wang <redacted>
This patchset adds 12 more tracepoints to TCP/IP stack, both
IPv4 and IPv6. The goal is to trace skb in different layers
and to measure the latency of each layer.
It looks like we should not trace them at the end of each target function,
instead we have to trace them in the beginning, otherwise the ordering
is reversed in the output.
I will send V2 tomorrow.
Thanks.
@@ -41,6 +41,48 @@TP_STORE_V4MAPPED(__entry,saddr,daddr)#endif+TRACE_EVENT(ip_queue_xmit,++TP_PROTO(conststructsock*sk,conststructsk_buff*skb),++TP_ARGS(sk,skb),++TP_STRUCT__entry(+__field(constvoid*,skbaddr)+__field(constvoid*,skaddr)+__field(__u16,sport)+__field(__u16,dport)+__array(__u8,saddr,4)+__array(__u8,daddr,4)+__array(__u8,saddr_v6,16)+__array(__u8,daddr_v6,16)+),++TP_fast_assign(+structinet_sock*inet=inet_sk(sk);+__be32*p32;++__entry->skbaddr=skb;+__entry->skaddr=sk;++__entry->sport=ntohs(inet->inet_sport);+__entry->dport=ntohs(inet->inet_dport);++p32=(__be32*)__entry->saddr;+*p32=inet->inet_saddr;++p32=(__be32*)__entry->daddr;+*p32=inet->inet_daddr;++TP_STORE_ADDRS(__entry,inet->inet_saddr,inet->inet_daddr,+sk->sk_v6_rcv_saddr,sk->sk_v6_daddr);+),++TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c skbaddr=%px",+__entry->sport,__entry->dport,__entry->saddr,__entry->daddr,+__entry->saddr_v6,__entry->daddr_v6,__entry->skbaddr)+);+#endif /* _TRACE_IP_H *//* This part must be outside protection */
From: Cong Wang <hidden> Date: 2021-08-09 20:32:56
On Fri, Aug 6, 2021 at 3:09 AM Eric Dumazet [off-list ref] wrote:
While it is useful to have stuff like this,
ddding so many trace points has a certain cost.
I fear that you have not determined this cost
on workloads where we enter these functions with cold caches.
For instance, before this patch, compiler gives us :
2e10 <ip_queue_xmit>:
2e10: e8 00 00 00 00 callq 2e15 <ip_queue_xmit+0x5> (__fentry__-0x4)
2e15: 0f b6 8f 1c 03 00 00 movzbl 0x31c(%rdi),%ecx
2e1c: e9 ef fb ff ff jmpq 2a10 <__ip_queue_xmit>
After patch, we see the compiler had to save/restore registers, and no longer
jumps to __ip_queue_xmit. Code is bigger, even when tracepoint is not enabled.
Interesting, I didn't pay attention to the binary code generated
by compilers. Let me check it, as I have moved the trace function
before __ip_queue_xmit() (otherwise the order is reversed).
Thanks!
@@ -41,6 +41,48 @@TP_STORE_V4MAPPED(__entry,saddr,daddr)#endif+TRACE_EVENT(ip_queue_xmit,++TP_PROTO(conststructsock*sk,conststructsk_buff*skb),++TP_ARGS(sk,skb),++TP_STRUCT__entry(+__field(constvoid*,skbaddr)+__field(constvoid*,skaddr)+__field(__u16,sport)+__field(__u16,dport)+__array(__u8,saddr,4)+__array(__u8,daddr,4)+__array(__u8,saddr_v6,16)+__array(__u8,daddr_v6,16)+),++TP_fast_assign(+structinet_sock*inet=inet_sk(sk);+__be32*p32;++__entry->skbaddr=skb;+__entry->skaddr=sk;++__entry->sport=ntohs(inet->inet_sport);+__entry->dport=ntohs(inet->inet_dport);++p32=(__be32*)__entry->saddr;+*p32=inet->inet_saddr;++p32=(__be32*)__entry->daddr;+*p32=inet->inet_daddr;++TP_STORE_ADDRS(__entry,inet->inet_saddr,inet->inet_daddr,+sk->sk_v6_rcv_saddr,sk->sk_v6_daddr);+),++TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c skbaddr=%px",+__entry->sport,__entry->dport,__entry->saddr,__entry->daddr,+__entry->saddr_v6,__entry->daddr_v6,__entry->skbaddr)+);+#endif /* _TRACE_IP_H *//* This part must be outside protection */
Instead of adding tracepoints, the bpf fexit prog can be used here and
the bpf prog will have the sk, skb, and ret available (example in fexit_test.c).
Some tracepoints in this set can also be done with bpf fentry/fexit.
Does bpf fentry/fexit work for your use case?
From: Cong Wang <hidden> Date: 2021-08-11 22:48:49
On Wed, Aug 11, 2021 at 2:23 PM Martin KaFai Lau [off-list ref] wrote:
Instead of adding tracepoints, the bpf fexit prog can be used here and
the bpf prog will have the sk, skb, and ret available (example in fexit_test.c).
Some tracepoints in this set can also be done with bpf fentry/fexit.
Does bpf fentry/fexit work for your use case?
Well, kprobe works too in this perspective. The problem with kprobe
or fexit is that there is no guarantee the function still exists in kernel
during iteration. Kernel is free to delete or rename it. With tracepoint,
even if ip_queue_xmit() were renamed, the same tracepoint must
remain in the kernel.
Thanks.
From: Martin KaFai Lau <hidden> Date: 2021-08-11 23:08:37
On Wed, Aug 11, 2021 at 03:48:36PM -0700, Cong Wang wrote:
On Wed, Aug 11, 2021 at 2:23 PM Martin KaFai Lau [off-list ref] wrote:
quoted
Instead of adding tracepoints, the bpf fexit prog can be used here and
the bpf prog will have the sk, skb, and ret available (example in fexit_test.c).
Some tracepoints in this set can also be done with bpf fentry/fexit.
Does bpf fentry/fexit work for your use case?
Well, kprobe works too in this perspective. The problem with kprobe
or fexit is that there is no guarantee the function still exists in kernel
during iteration. Kernel is free to delete or rename it. With tracepoint,
even if ip_queue_xmit() were renamed, the same tracepoint must
remain in the kernel.
Some of the function names are hardly changed. Considering it is
not always cost free based on another thread, this is not a strong
enough reason to add so many tracepoints while other options
are available.
From: Cong Wang <hidden> Date: 2021-08-12 00:37:51
On Wed, Aug 11, 2021 at 4:08 PM Martin KaFai Lau [off-list ref] wrote:
Some of the function names are hardly changed.
This is obviously wrong for two reasons:
1. Kernel developers did change them. As a quick example,
tcp_retransmit_skb() has been changed, we do have reasons to only trace
__tcp_retransmit_skb() instead.
2. Even if kernel developers never did, compilers can do inline too. For
example, I see nothing to stop compiler to inline tcp_transmit_skb()
which is static and only calls __tcp_transmit_skb(). You explicitly
mark bpf_fentry_test1() as noinline, don't you?
I understand you are eager to promote ebpf, however, please keep
reasonable on facts.
Thanks.
From: Martin KaFai Lau <hidden> Date: 2021-08-12 05:47:07
On Wed, Aug 11, 2021 at 05:37:35PM -0700, Cong Wang wrote:
On Wed, Aug 11, 2021 at 4:08 PM Martin KaFai Lau [off-list ref] wrote:
quoted
Some of the function names are hardly changed.
This is obviously wrong for two reasons:
1. Kernel developers did change them. As a quick example,
tcp_retransmit_skb() has been changed, we do have reasons to only trace
__tcp_retransmit_skb() instead.
I did not say it has never changed. how often? I don't believe
it changed often enough.
2. Even if kernel developers never did, compilers can do inline too. For
example, I see nothing to stop compiler to inline tcp_transmit_skb()
which is static and only calls __tcp_transmit_skb(). You explicitly
mark bpf_fentry_test1() as noinline, don't you?
Yes, correct, inline is a legit reason.
Another one is to track some local variables in the function stack.
However, how many functions that you need here are indeed inlined by
compiler or need to track the local variables?
I understand you are eager to promote ebpf, however, please keep
reasonable on facts.
Absolutely not. There is no need. There is enough scripts
using bpf that does network tracing doing useful stuff without
adding so many tracepoints in the fast path.
I am only exploring other options and trying to understand
why it does not work in your case before adding all of
them in the fast path. It is sad that you take it
this way.