Skip to content

Commit

Permalink
net: ipv6: ioam6_iptunnel: mitigate 2-realloc issue
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Justin Iurman authored and Paolo Abeni committed Dec 5, 2024
1 parent 0600cf4 commit dce5251
Showing 1 changed file with 37 additions and 36 deletions.
73 changes: 37 additions & 36 deletions net/ipv6/ioam6_iptunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
struct ioam6_lwt_encap *tuninfo)
struct ioam6_lwt_encap *tuninfo,
struct dst_entry *cache_dst)
{
struct ipv6hdr *oldhdr, *hdr;
int hdrlen, err;

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))
return err;

Expand Down Expand Up @@ -291,7 +292,8 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
struct ioam6_lwt_encap *tuninfo,
bool has_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 ipv6hdr *hdr, *inner_hdr;
Expand All @@ -300,7 +302,7 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
hdrlen = (tuninfo->eh.hdrlen + 1) << 3;
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))
return err;

Expand Down Expand Up @@ -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)
{
struct dst_entry *dst = skb_dst(skb);
struct dst_entry *dst = skb_dst(skb), *cache_dst;
struct in6_addr orig_daddr;
struct ioam6_lwt *ilwt;
int err = -EINVAL;
Expand All @@ -352,14 +354,18 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)

orig_daddr = ipv6_hdr(skb)->daddr;

local_bh_disable();
cache_dst = dst_cache_get(&ilwt->cache);
local_bh_enable();

switch (ilwt->mode) {
case IOAM6_IPTUNNEL_MODE_INLINE:
do_inline:
/* Direct insertion - if there is no Hop-by-Hop yet */
if (ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP)
goto out;

err = ioam6_do_inline(net, skb, &ilwt->tuninfo);
err = ioam6_do_inline(net, skb, &ilwt->tuninfo, cache_dst);
if (unlikely(err))
goto drop;

Expand All @@ -369,7 +375,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
/* Encapsulation (ip6ip6) */
err = ioam6_do_encap(net, skb, &ilwt->tuninfo,
ilwt->has_tunsrc, &ilwt->tunsrc,
&ilwt->tundst);
&ilwt->tundst, cache_dst);
if (unlikely(err))
goto drop;

Expand All @@ -387,41 +393,36 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
goto drop;
}

err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
if (unlikely(err))
goto drop;
if (unlikely(!cache_dst)) {
struct ipv6hdr *hdr = ipv6_hdr(skb);
struct flowi6 fl6;

memset(&fl6, 0, sizeof(fl6));
fl6.daddr = hdr->daddr;
fl6.saddr = hdr->saddr;
fl6.flowlabel = ip6_flowinfo(hdr);
fl6.flowi6_mark = skb->mark;
fl6.flowi6_proto = hdr->nexthdr;

cache_dst = ip6_route_output(net, NULL, &fl6);
if (cache_dst->error) {
err = cache_dst->error;
dst_release(cache_dst);
goto drop;
}

if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) {
local_bh_disable();
dst = dst_cache_get(&ilwt->cache);
dst_cache_set_ip6(&ilwt->cache, cache_dst, &fl6.saddr);
local_bh_enable();

if (unlikely(!dst)) {
struct ipv6hdr *hdr = ipv6_hdr(skb);
struct flowi6 fl6;

memset(&fl6, 0, sizeof(fl6));
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();
}
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, dst);

skb_dst_set(skb, cache_dst);
return dst_output(net, sk, skb);
}
out:
Expand Down

0 comments on commit dce5251

Please sign in to comment.