net: Add skb->mono_delivery_time to distinguish mono delivery_time from (rcv) timestamp
authorMartin KaFai Lau <kafai@fb.com>
Wed, 2 Mar 2022 19:55:25 +0000 (11:55 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 3 Mar 2022 14:38:48 +0000 (14:38 +0000)
skb->tstamp was first used as the (rcv) timestamp.
The major usage is to report it to the user (e.g. SO_TIMESTAMP).

Later, skb->tstamp is also set as the (future) delivery_time (e.g. EDT in TCP)
during egress and used by the qdisc (e.g. sch_fq) to make decision on when
the skb can be passed to the dev.

Currently, there is no way to tell skb->tstamp having the (rcv) timestamp
or the delivery_time, so it is always reset to 0 whenever forwarded
between egress and ingress.

While it makes sense to always clear the (rcv) timestamp in skb->tstamp
to avoid confusing sch_fq that expects the delivery_time, it is a
performance issue [0] to clear the delivery_time if the skb finally
egress to a fq@phy-dev.  For example, when forwarding from egress to
ingress and then finally back to egress:

            tcp-sender => veth@netns => veth@hostns => fq@eth0@hostns
                                     ^              ^
                                     reset          rest

This patch adds one bit skb->mono_delivery_time to flag the skb->tstamp
is storing the mono delivery_time (EDT) instead of the (rcv) timestamp.

The current use case is to keep the TCP mono delivery_time (EDT) and
to be used with sch_fq.  A latter patch will also allow tc-bpf@ingress
to read and change the mono delivery_time.

In the future, another bit (e.g. skb->user_delivery_time) can be added
for the SCM_TXTIME where the clock base is tracked by sk->sk_clockid.

[ This patch is a prep work.  The following patches will
  get the other parts of the stack ready first.  Then another patch
  after that will finally set the skb->mono_delivery_time. ]

skb_set_delivery_time() function is added.  It is used by the tcp_output.c
and during ip[6] fragmentation to assign the delivery_time to
the skb->tstamp and also set the skb->mono_delivery_time.

A note on the change in ip_send_unicast_reply() in ip_output.c.
It is only used by TCP to send reset/ack out of a ctl_sk.
Like the new skb_set_delivery_time(), this patch sets
the skb->mono_delivery_time to 0 for now as a place
holder.  It will be enabled in a latter patch.
A similar case in tcp_ipv6 can be done with
skb_set_delivery_time() in tcp_v6_send_response().

[0] (slide 22): https://linuxplumbersconf.org/event/11/contributions/953/attachments/867/1658/LPC_2021_BPF_Datapath_Extensions.pdf

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/skbuff.h
net/bridge/netfilter/nf_conntrack_bridge.c
net/ipv4/ip_output.c
net/ipv4/tcp_output.c
net/ipv6/ip6_output.c
net/ipv6/netfilter.c
net/ipv6/tcp_ipv6.c

index d67941f..803ffa6 100644 (file)
@@ -795,6 +795,10 @@ typedef unsigned char *sk_buff_data_t;
  *     @dst_pending_confirm: need to confirm neighbour
  *     @decrypted: Decrypted SKB
  *     @slow_gro: state present at GRO time, slower prepare step required
+ *     @mono_delivery_time: When set, skb->tstamp has the
+ *             delivery_time in mono clock base (i.e. EDT).  Otherwise, the
+ *             skb->tstamp has the (rcv) timestamp at ingress and
+ *             delivery_time at egress.
  *     @napi_id: id of the NAPI struct this skb came from
  *     @sender_cpu: (aka @napi_id) source CPU in XPS
  *     @secmark: security marking
@@ -965,6 +969,7 @@ struct sk_buff {
        __u8                    decrypted:1;
 #endif
        __u8                    slow_gro:1;
+       __u8                    mono_delivery_time:1;
 
 #ifdef CONFIG_NET_SCHED
        __u16                   tc_index;       /* traffic control index */
@@ -3983,6 +3988,14 @@ static inline ktime_t net_timedelta(ktime_t t)
        return ktime_sub(ktime_get_real(), t);
 }
 
+static inline void skb_set_delivery_time(struct sk_buff *skb, ktime_t kt,
+                                        bool mono)
+{
+       skb->tstamp = kt;
+       /* Setting mono_delivery_time will be enabled later */
+       skb->mono_delivery_time = 0;
+}
+
 static inline u8 skb_metadata_len(const struct sk_buff *skb)
 {
        return skb_shinfo(skb)->meta_len;
index fdbed31..ebfb2a5 100644 (file)
@@ -32,6 +32,7 @@ static int nf_br_ip_fragment(struct net *net, struct sock *sk,
                                           struct sk_buff *))
 {
        int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
+       bool mono_delivery_time = skb->mono_delivery_time;
        unsigned int hlen, ll_rs, mtu;
        ktime_t tstamp = skb->tstamp;
        struct ip_frag_state state;
@@ -81,7 +82,7 @@ static int nf_br_ip_fragment(struct net *net, struct sock *sk,
                        if (iter.frag)
                                ip_fraglist_prepare(skb, &iter);
 
-                       skb->tstamp = tstamp;
+                       skb_set_delivery_time(skb, tstamp, mono_delivery_time);
                        err = output(net, sk, data, skb);
                        if (err || !iter.frag)
                                break;
@@ -112,7 +113,7 @@ slow_path:
                        goto blackhole;
                }
 
-               skb2->tstamp = tstamp;
+               skb_set_delivery_time(skb2, tstamp, mono_delivery_time);
                err = output(net, sk, data, skb2);
                if (err)
                        goto blackhole;
index 6df3545..a9588e0 100644 (file)
@@ -761,6 +761,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
 {
        struct iphdr *iph;
        struct sk_buff *skb2;
+       bool mono_delivery_time = skb->mono_delivery_time;
        struct rtable *rt = skb_rtable(skb);
        unsigned int mtu, hlen, ll_rs;
        struct ip_fraglist_iter iter;
@@ -852,7 +853,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                                }
                        }
 
-                       skb->tstamp = tstamp;
+                       skb_set_delivery_time(skb, tstamp, mono_delivery_time);
                        err = output(net, sk, skb);
 
                        if (!err)
@@ -908,7 +909,7 @@ slow_path:
                /*
                 *      Put this fragment into the sending queue.
                 */
-               skb2->tstamp = tstamp;
+               skb_set_delivery_time(skb2, tstamp, mono_delivery_time);
                err = output(net, sk, skb2);
                if (err)
                        goto fail;
@@ -1727,6 +1728,8 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,
                          arg->csumoffset) = csum_fold(csum_add(nskb->csum,
                                                                arg->csum));
                nskb->ip_summed = CHECKSUM_NONE;
+               /* Setting mono_delivery_time will be enabled later */
+               nskb->mono_delivery_time = 0;
                ip_push_pending_frames(sk, &fl4);
        }
 out:
index e76bf1e..2319531 100644 (file)
@@ -1253,7 +1253,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
        tp = tcp_sk(sk);
        prior_wstamp = tp->tcp_wstamp_ns;
        tp->tcp_wstamp_ns = max(tp->tcp_wstamp_ns, tp->tcp_clock_cache);
-       skb->skb_mstamp_ns = tp->tcp_wstamp_ns;
+       skb_set_delivery_time(skb, tp->tcp_wstamp_ns, true);
        if (clone_it) {
                oskb = skb;
 
@@ -1589,7 +1589,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue,
 
        skb_split(skb, buff, len);
 
-       buff->tstamp = skb->tstamp;
+       skb_set_delivery_time(buff, skb->tstamp, true);
        tcp_fragment_tstamp(skb, buff);
 
        old_factor = tcp_skb_pcount(skb);
@@ -2616,7 +2616,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
 
                if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) {
                        /* "skb_mstamp_ns" is used as a start point for the retransmit timer */
-                       skb->skb_mstamp_ns = tp->tcp_wstamp_ns = tp->tcp_clock_cache;
+                       tp->tcp_wstamp_ns = tp->tcp_clock_cache;
+                       skb_set_delivery_time(skb, tp->tcp_wstamp_ns, true);
                        list_move_tail(&skb->tcp_tsorted_anchor, &tp->tsorted_sent_queue);
                        tcp_init_tso_segs(skb, mss_now);
                        goto repair; /* Skip network transmission */
@@ -3541,11 +3542,12 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
        now = tcp_clock_ns();
 #ifdef CONFIG_SYN_COOKIES
        if (unlikely(synack_type == TCP_SYNACK_COOKIE && ireq->tstamp_ok))
-               skb->skb_mstamp_ns = cookie_init_timestamp(req, now);
+               skb_set_delivery_time(skb, cookie_init_timestamp(req, now),
+                                     true);
        else
 #endif
        {
-               skb->skb_mstamp_ns = now;
+               skb_set_delivery_time(skb, now, true);
                if (!tcp_rsk(req)->snt_synack) /* Timestamp first SYNACK */
                        tcp_rsk(req)->snt_synack = tcp_skb_timestamp_us(skb);
        }
@@ -3594,7 +3596,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
        bpf_skops_write_hdr_opt((struct sock *)sk, skb, req, syn_skb,
                                synack_type, &opts);
 
-       skb->skb_mstamp_ns = now;
+       skb_set_delivery_time(skb, now, true);
        tcp_add_tx_delay(skb, tp);
 
        return skb;
@@ -3771,7 +3773,7 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
 
        err = tcp_transmit_skb(sk, syn_data, 1, sk->sk_allocation);
 
-       syn->skb_mstamp_ns = syn_data->skb_mstamp_ns;
+       skb_set_delivery_time(syn, syn_data->skb_mstamp_ns, true);
 
        /* Now full SYN+DATA was cloned and sent (or not),
         * remove the SYN from the original skb (syn_data)
index c5edc86..dad4e3d 100644 (file)
@@ -813,6 +813,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
        struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
        struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
                                inet6_sk(skb->sk) : NULL;
+       bool mono_delivery_time = skb->mono_delivery_time;
        struct ip6_frag_state state;
        unsigned int mtu, hlen, nexthdr_offset;
        ktime_t tstamp = skb->tstamp;
@@ -903,7 +904,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                        if (iter.frag)
                                ip6_fraglist_prepare(skb, &iter);
 
-                       skb->tstamp = tstamp;
+                       skb_set_delivery_time(skb, tstamp, mono_delivery_time);
                        err = output(net, sk, skb);
                        if (!err)
                                IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
@@ -962,7 +963,7 @@ slow_path:
                /*
                 *      Put this fragment into the sending queue.
                 */
-               frag->tstamp = tstamp;
+               skb_set_delivery_time(frag, tstamp, mono_delivery_time);
                err = output(net, sk, frag);
                if (err)
                        goto fail;
index 6ab710b..1da3324 100644 (file)
@@ -121,6 +121,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                                  struct sk_buff *))
 {
        int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
+       bool mono_delivery_time = skb->mono_delivery_time;
        ktime_t tstamp = skb->tstamp;
        struct ip6_frag_state state;
        u8 *prevhdr, nexthdr = 0;
@@ -186,7 +187,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                        if (iter.frag)
                                ip6_fraglist_prepare(skb, &iter);
 
-                       skb->tstamp = tstamp;
+                       skb_set_delivery_time(skb, tstamp, mono_delivery_time);
                        err = output(net, sk, data, skb);
                        if (err || !iter.frag)
                                break;
@@ -219,7 +220,7 @@ slow_path:
                        goto blackhole;
                }
 
-               skb2->tstamp = tstamp;
+               skb_set_delivery_time(skb2, tstamp, mono_delivery_time);
                err = output(net, sk, data, skb2);
                if (err)
                        goto blackhole;
index e98af86..cb2bb7d 100644 (file)
@@ -940,7 +940,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
                } else {
                        mark = sk->sk_mark;
                }
-               buff->tstamp = tcp_transmit_time(sk);
+               skb_set_delivery_time(buff, tcp_transmit_time(sk), true);
        }
        fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark) ?: mark;
        fl6.fl6_dport = t1->dest;