Message ID | 20240425150432.44142-3-nbd@nbd.name (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | Add TCP fraglist GRO support | expand |
Felix Fietkau wrote: > Preparation for adding TCP fraglist GRO support. It expects packets to be > combined in a similar way as UDP fraglist GSO packets. > For IPv4 packets, NAT is handled in the same way as UDP fraglist GSO. > > Signed-off-by: Felix Fietkau <nbd@nbd.name> > --- > net/ipv4/tcp_offload.c | 69 ++++++++++++++++++++++++++++++++++++++++ > net/ipv6/tcpv6_offload.c | 3 ++ > 2 files changed, 72 insertions(+) > > diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c > index fab0973f995b..e455f884190c 100644 > --- a/net/ipv4/tcp_offload.c > +++ b/net/ipv4/tcp_offload.c > @@ -28,6 +28,72 @@ static void tcp_gso_tstamp(struct sk_buff *skb, unsigned int ts_seq, > } > } > > +static void __tcpv4_gso_segment_csum(struct sk_buff *seg, > + __be32 *oldip, __be32 *newip, > + __be16 *oldport, __be16 *newport) > +{ > + struct tcphdr *th; > + struct iphdr *iph; > + > + if (*oldip == *newip && *oldport == *newport) > + return; > + > + th = tcp_hdr(seg); > + iph = ip_hdr(seg); > + > + inet_proto_csum_replace4(&th->check, seg, *oldip, *newip, true); > + inet_proto_csum_replace2(&th->check, seg, *oldport, *newport, false); > + *oldport = *newport; > + > + csum_replace4(&iph->check, *oldip, *newip); > + *oldip = *newip; > +} > + > +static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs) > +{ > + struct sk_buff *seg; > + struct tcphdr *th, *th2; > + struct iphdr *iph, *iph2; > + __be32 flags, flags2; > + > + seg = segs; > + th = tcp_hdr(seg); > + iph = ip_hdr(seg); > + flags = tcp_flag_word(th); > + flags2 = tcp_flag_word(tcp_hdr(seg->next)); Vestigial, now that flag overwrite is removed in v2? All this code is very similar to __udpv4_gso_segment_list_csum. But the zero checksum handling in __udpv4_gso_segment_csum makes it just different enough that I also do not immediately see a straightforward way to avoid duplicating.
On 25.04.24 17:17, Willem de Bruijn wrote: > Felix Fietkau wrote: >> Preparation for adding TCP fraglist GRO support. It expects packets to be >> combined in a similar way as UDP fraglist GSO packets. >> For IPv4 packets, NAT is handled in the same way as UDP fraglist GSO. >> >> Signed-off-by: Felix Fietkau <nbd@nbd.name> >> --- >> net/ipv4/tcp_offload.c | 69 ++++++++++++++++++++++++++++++++++++++++ >> net/ipv6/tcpv6_offload.c | 3 ++ >> 2 files changed, 72 insertions(+) >> >> diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c >> index fab0973f995b..e455f884190c 100644 >> --- a/net/ipv4/tcp_offload.c >> +++ b/net/ipv4/tcp_offload.c >> @@ -28,6 +28,72 @@ static void tcp_gso_tstamp(struct sk_buff *skb, unsigned int ts_seq, >> } >> } >> >> +static void __tcpv4_gso_segment_csum(struct sk_buff *seg, >> + __be32 *oldip, __be32 *newip, >> + __be16 *oldport, __be16 *newport) >> +{ >> + struct tcphdr *th; >> + struct iphdr *iph; >> + >> + if (*oldip == *newip && *oldport == *newport) >> + return; >> + >> + th = tcp_hdr(seg); >> + iph = ip_hdr(seg); >> + >> + inet_proto_csum_replace4(&th->check, seg, *oldip, *newip, true); >> + inet_proto_csum_replace2(&th->check, seg, *oldport, *newport, false); >> + *oldport = *newport; >> + >> + csum_replace4(&iph->check, *oldip, *newip); >> + *oldip = *newip; >> +} >> + >> +static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs) >> +{ >> + struct sk_buff *seg; >> + struct tcphdr *th, *th2; >> + struct iphdr *iph, *iph2; >> + __be32 flags, flags2; >> + >> + seg = segs; >> + th = tcp_hdr(seg); >> + iph = ip_hdr(seg); >> + flags = tcp_flag_word(th); >> + flags2 = tcp_flag_word(tcp_hdr(seg->next)); > > Vestigial, now that flag overwrite is removed in v2? Will fix, thanks. > All this code is very similar to __udpv4_gso_segment_list_csum. But > the zero checksum handling in __udpv4_gso_segment_csum makes it just > different enough that I also do not immediately see a straightforward > way to avoid duplicating. Also, the checksum field is in a different location in the udp header. I don't think avoiding duplication makes sense here. - Felix
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index fab0973f995b..e455f884190c 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c @@ -28,6 +28,72 @@ static void tcp_gso_tstamp(struct sk_buff *skb, unsigned int ts_seq, } } +static void __tcpv4_gso_segment_csum(struct sk_buff *seg, + __be32 *oldip, __be32 *newip, + __be16 *oldport, __be16 *newport) +{ + struct tcphdr *th; + struct iphdr *iph; + + if (*oldip == *newip && *oldport == *newport) + return; + + th = tcp_hdr(seg); + iph = ip_hdr(seg); + + inet_proto_csum_replace4(&th->check, seg, *oldip, *newip, true); + inet_proto_csum_replace2(&th->check, seg, *oldport, *newport, false); + *oldport = *newport; + + csum_replace4(&iph->check, *oldip, *newip); + *oldip = *newip; +} + +static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs) +{ + struct sk_buff *seg; + struct tcphdr *th, *th2; + struct iphdr *iph, *iph2; + __be32 flags, flags2; + + seg = segs; + th = tcp_hdr(seg); + iph = ip_hdr(seg); + flags = tcp_flag_word(th); + flags2 = tcp_flag_word(tcp_hdr(seg->next)); + + if ((tcp_hdr(seg)->dest == tcp_hdr(seg->next)->dest) && + (tcp_hdr(seg)->source == tcp_hdr(seg->next)->source) && + (ip_hdr(seg)->daddr == ip_hdr(seg->next)->daddr) && + (ip_hdr(seg)->saddr == ip_hdr(seg->next)->saddr) && + (flags == flags2)) + return segs; + + while ((seg = seg->next)) { + th2 = tcp_hdr(seg); + iph2 = ip_hdr(seg); + + __tcpv4_gso_segment_csum(seg, + &iph2->saddr, &iph->saddr, + &th2->source, &th->source); + __tcpv4_gso_segment_csum(seg, + &iph2->daddr, &iph->daddr, + &th2->dest, &th->dest); + } + + return segs; +} + +static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb, + netdev_features_t features) +{ + skb = skb_segment_list(skb, features, skb_mac_header_len(skb)); + if (IS_ERR(skb)) + return skb; + + return __tcpv4_gso_segment_list_csum(skb); +} + static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, netdev_features_t features) { @@ -37,6 +103,9 @@ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, if (!pskb_may_pull(skb, sizeof(struct tcphdr))) return ERR_PTR(-EINVAL); + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) + return __tcp4_gso_segment_list(skb, features); + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { const struct iphdr *iph = ip_hdr(skb); struct tcphdr *th = tcp_hdr(skb); diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c index 4b07d1e6c952..b3b8e1f6b92a 100644 --- a/net/ipv6/tcpv6_offload.c +++ b/net/ipv6/tcpv6_offload.c @@ -51,6 +51,9 @@ static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb, if (!pskb_may_pull(skb, sizeof(*th))) return ERR_PTR(-EINVAL); + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) + return skb_segment_list(skb, features, skb_mac_header_len(skb)); + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { const struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct tcphdr *th = tcp_hdr(skb);
Preparation for adding TCP fraglist GRO support. It expects packets to be combined in a similar way as UDP fraglist GSO packets. For IPv4 packets, NAT is handled in the same way as UDP fraglist GSO. Signed-off-by: Felix Fietkau <nbd@nbd.name> --- net/ipv4/tcp_offload.c | 69 ++++++++++++++++++++++++++++++++++++++++ net/ipv6/tcpv6_offload.c | 3 ++ 2 files changed, 72 insertions(+)