Commit dce52518 authored by Justin Iurman's avatar Justin Iurman Committed by Paolo Abeni
Browse files

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: default avatarJustin Iurman <justin.iurman@uliege.be>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 0600cf40
Loading
Loading
Loading
Loading
+37 −36
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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;

@@ -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;
@@ -352,6 +354,10 @@ 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:
@@ -359,7 +365,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
		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;

@@ -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;

@@ -387,16 +393,7 @@ 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 (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) {
		local_bh_disable();
		dst = dst_cache_get(&ilwt->cache);
		local_bh_enable();

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

@@ -407,21 +404,25 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
		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);
		cache_dst = ip6_route_output(net, NULL, &fl6);
		if (cache_dst->error) {
			err = cache_dst->error;
			dst_release(cache_dst);
			goto drop;
		}

		local_bh_disable();
			dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr);
		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, dst);

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