forked from mirrors/linux
		
	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:
		
							parent
							
								
									0600cf40e9
								
							
						
					
					
						commit
						dce525185b
					
				
					 1 changed files with 38 additions and 37 deletions
				
			
		| 
						 | 
					@ -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 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
		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 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
		/* 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,16 +393,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
		goto drop;
 | 
							goto drop;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
 | 
						if (unlikely(!cache_dst)) {
 | 
				
			||||||
	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)) {
 | 
					 | 
				
			||||||
		struct ipv6hdr *hdr = ipv6_hdr(skb);
 | 
							struct ipv6hdr *hdr = ipv6_hdr(skb);
 | 
				
			||||||
		struct flowi6 fl6;
 | 
							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_mark = skb->mark;
 | 
				
			||||||
		fl6.flowi6_proto = hdr->nexthdr;
 | 
							fl6.flowi6_proto = hdr->nexthdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			dst = ip6_route_output(net, NULL, &fl6);
 | 
							cache_dst = ip6_route_output(net, NULL, &fl6);
 | 
				
			||||||
			if (dst->error) {
 | 
							if (cache_dst->error) {
 | 
				
			||||||
				err = dst->error;
 | 
								err = cache_dst->error;
 | 
				
			||||||
				dst_release(dst);
 | 
								dst_release(cache_dst);
 | 
				
			||||||
			goto drop;
 | 
								goto drop;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local_bh_disable();
 | 
							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();
 | 
							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_drop(skb);
 | 
				
			||||||
		skb_dst_set(skb, dst);
 | 
							skb_dst_set(skb, cache_dst);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		return dst_output(net, sk, skb);
 | 
							return dst_output(net, sk, skb);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue