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 @@ 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:
|
||||||
|
|
Loading…
Add table
Reference in a new issue