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,
|
||||
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 @@ do_inline:
|
|||
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 @@ do_encap:
|
|||
/* 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,41 +393,36 @@ do_encap:
|
|||
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;
|
||||
|
||||
if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) {
|
||||
local_bh_disable();
|
||||
dst = dst_cache_get(&ilwt->cache);
|
||||
local_bh_enable();
|
||||
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;
|
||||
|
||||
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();
|
||||
cache_dst = ip6_route_output(net, NULL, &fl6);
|
||||
if (cache_dst->error) {
|
||||
err = cache_dst->error;
|
||||
dst_release(cache_dst);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
skb_dst_drop(skb);
|
||||
skb_dst_set(skb, dst);
|
||||
local_bh_disable();
|
||||
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);
|
||||
}
|
||||
out:
|
||||
|
|
Loading…
Add table
Reference in a new issue