1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

net: ipv6: ioam6_iptunnel: mitigate 2-realloc issue

This patch mitigates the two-reallocations issue with ioam6_iptunnel by
providing the dst_entry (in the cache) to the first call to
skb_cow_head(). As a result, the very first iteration may still trigger
two reallocations (i.e., empty cache), while next iterations would only
trigger a single reallocation.

Performance tests before/after applying this patch, which clearly shows
the improvement:
- inline mode:
  - before: https://ibb.co/LhQ8V63
  - after: https://ibb.co/x5YT2bS
- encap mode:
  - before: https://ibb.co/3Cjm5m0
  - after: https://ibb.co/TwpsxTC
- encap mode with tunsrc:
  - before: https://ibb.co/Gpy9QPg
  - after: https://ibb.co/PW1bZFT

This patch also fixes an incorrect behavior: after the insertion, the
second call to skb_cow_head() makes sure that the dev has enough
headroom in the skb for layer 2 and stuff. In that case, the "old"
dst_entry was used, which is now fixed. After discussing with Paolo, it
appears that both patches can be merged into a single one -this one-
(for the sake of readability) and target net-next.

Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Justin Iurman 2024-12-03 13:49:43 +01:00 committed by Paolo Abeni
parent 0600cf40e9
commit dce525185b

View file

@ -253,14 +253,15 @@ static int ioam6_do_fill(struct net *net, struct sk_buff *skb)
} }
static int ioam6_do_inline(struct net *net, struct sk_buff *skb, static int ioam6_do_inline(struct net *net, struct sk_buff *skb,
struct ioam6_lwt_encap *tuninfo) struct ioam6_lwt_encap *tuninfo,
struct dst_entry *cache_dst)
{ {
struct ipv6hdr *oldhdr, *hdr; struct ipv6hdr *oldhdr, *hdr;
int hdrlen, err; int hdrlen, err;
hdrlen = (tuninfo->eh.hdrlen + 1) << 3; hdrlen = (tuninfo->eh.hdrlen + 1) << 3;
err = skb_cow_head(skb, hdrlen + skb->mac_len); err = skb_cow_head(skb, hdrlen + dst_dev_overhead(cache_dst, skb));
if (unlikely(err)) if (unlikely(err))
return err; return err;
@ -291,7 +292,8 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
struct ioam6_lwt_encap *tuninfo, struct ioam6_lwt_encap *tuninfo,
bool has_tunsrc, bool has_tunsrc,
struct in6_addr *tunsrc, struct in6_addr *tunsrc,
struct in6_addr *tundst) struct in6_addr *tundst,
struct dst_entry *cache_dst)
{ {
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
struct ipv6hdr *hdr, *inner_hdr; struct ipv6hdr *hdr, *inner_hdr;
@ -300,7 +302,7 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
hdrlen = (tuninfo->eh.hdrlen + 1) << 3; hdrlen = (tuninfo->eh.hdrlen + 1) << 3;
len = sizeof(*hdr) + hdrlen; len = sizeof(*hdr) + hdrlen;
err = skb_cow_head(skb, len + skb->mac_len); err = skb_cow_head(skb, len + dst_dev_overhead(cache_dst, skb));
if (unlikely(err)) if (unlikely(err))
return err; return err;
@ -334,7 +336,7 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb), *cache_dst;
struct in6_addr orig_daddr; struct in6_addr orig_daddr;
struct ioam6_lwt *ilwt; struct ioam6_lwt *ilwt;
int err = -EINVAL; int err = -EINVAL;
@ -352,6 +354,10 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
orig_daddr = ipv6_hdr(skb)->daddr; orig_daddr = ipv6_hdr(skb)->daddr;
local_bh_disable();
cache_dst = dst_cache_get(&ilwt->cache);
local_bh_enable();
switch (ilwt->mode) { switch (ilwt->mode) {
case IOAM6_IPTUNNEL_MODE_INLINE: case IOAM6_IPTUNNEL_MODE_INLINE:
do_inline: do_inline:
@ -359,7 +365,7 @@ do_inline:
if (ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP) if (ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP)
goto out; goto out;
err = ioam6_do_inline(net, skb, &ilwt->tuninfo); err = ioam6_do_inline(net, skb, &ilwt->tuninfo, cache_dst);
if (unlikely(err)) if (unlikely(err))
goto drop; goto drop;
@ -369,7 +375,7 @@ do_encap:
/* Encapsulation (ip6ip6) */ /* Encapsulation (ip6ip6) */
err = ioam6_do_encap(net, skb, &ilwt->tuninfo, err = ioam6_do_encap(net, skb, &ilwt->tuninfo,
ilwt->has_tunsrc, &ilwt->tunsrc, ilwt->has_tunsrc, &ilwt->tunsrc,
&ilwt->tundst); &ilwt->tundst, cache_dst);
if (unlikely(err)) if (unlikely(err))
goto drop; goto drop;
@ -387,41 +393,36 @@ do_encap:
goto drop; goto drop;
} }
err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); if (unlikely(!cache_dst)) {
if (unlikely(err)) struct ipv6hdr *hdr = ipv6_hdr(skb);
goto drop; struct flowi6 fl6;
if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) { memset(&fl6, 0, sizeof(fl6));
local_bh_disable(); fl6.daddr = hdr->daddr;
dst = dst_cache_get(&ilwt->cache); fl6.saddr = hdr->saddr;
local_bh_enable(); fl6.flowlabel = ip6_flowinfo(hdr);
fl6.flowi6_mark = skb->mark;
fl6.flowi6_proto = hdr->nexthdr;
if (unlikely(!dst)) { cache_dst = ip6_route_output(net, NULL, &fl6);
struct ipv6hdr *hdr = ipv6_hdr(skb); if (cache_dst->error) {
struct flowi6 fl6; err = cache_dst->error;
dst_release(cache_dst);
memset(&fl6, 0, sizeof(fl6)); goto drop;
fl6.daddr = hdr->daddr;
fl6.saddr = hdr->saddr;
fl6.flowlabel = ip6_flowinfo(hdr);
fl6.flowi6_mark = skb->mark;
fl6.flowi6_proto = hdr->nexthdr;
dst = ip6_route_output(net, NULL, &fl6);
if (dst->error) {
err = dst->error;
dst_release(dst);
goto drop;
}
local_bh_disable();
dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr);
local_bh_enable();
} }
skb_dst_drop(skb); local_bh_disable();
skb_dst_set(skb, dst); dst_cache_set_ip6(&ilwt->cache, cache_dst, &fl6.saddr);
local_bh_enable();
err = skb_cow_head(skb, LL_RESERVED_SPACE(cache_dst->dev));
if (unlikely(err))
goto drop;
}
if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) {
skb_dst_drop(skb);
skb_dst_set(skb, cache_dst);
return dst_output(net, sk, skb); return dst_output(net, sk, skb);
} }
out: out: