diff options
author | David S. Miller <davem@davemloft.net> | 2015-12-15 23:25:20 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-12-15 23:25:20 -0500 |
commit | fec65bd4e855c417b8408ed87824f9234fd0d795 (patch) | |
tree | 207ff2184419083aa1a70fc37ba8a5ce54135ae9 /net/ipv6/ila/ila_lwt.c | |
parent | Merge branch 'stmmac-mdio-compat' (diff) | |
parent | ila: Add generic ILA translation facility (diff) | |
download | linux-dev-fec65bd4e855c417b8408ed87824f9234fd0d795.tar.xz linux-dev-fec65bd4e855c417b8408ed87824f9234fd0d795.zip |
Merge branch 'ila-early-demux'
Tom Herbert says:
====================
ila: Optimization to preserve value of early demux
In the current implementation of ILA, LWT is used to perform
translation on both the input and output paths. This is functional,
however there is a big performance hit in the receive path. Early
demux occurs before the routing lookup (a hit actually obviates the
route lookup). Therefore the stack currently performs early
demux before translation so that a local connection with ILA
addresses is never matched. Note that this issue is not just
with ILA, but pretty much any translated or encapsulated packet
handled by LWT would miss the opportunity for early demux. Solving
the general problem seems non trivial since we would need to move
the route lookup before early demx thereby mitigating the value.
This patch set addresses the issue for ILA by adding a fast locator
lookup that occurs before early demux. This done by hooking in to
NF_INET_PRE_ROUTING
For the backend we implement an rhashtable that contains identifier
to locator to mappings. The table also allows more specific matches
that include original locator and interface.
This patch set:
- Add an rhashtable function to atomically replace and element.
This is useful to implement sub-trees from a table entry
without needing to use a special anchor structure as the
table entry.
- Add a start callback for starting a netlink dump.
- Creates an ila directory under net/ipv6 and moves ila.c to it.
ila.c is split into ila_common.c and ila_lwt.c.
- Implement a table to do identifier->locator mapping. This is
an rhashtable (in ila_xlat.c).
- Configuration for the table with netlink.
- Add a hook into NF_INET_PRE_ROUTING to perform ILA translation
before early demux.
Changes in v2:
- Use iptables targets instead of a new xfrm function
Changes in v3:
- Add __rcu to next pointer in struct ila_map
Changes in v4:
- Use hook for NF_INET_PRE_ROUTING
Changed in v5:
- Register hooks per namespace using nf_register_net_hooks
- Only register hooks when first mapping is actually added
Changed in v6:
- Remove gfp argument in alloc_ila_locks, it is unnecessary
- Set registered_hooks properly when hooks are registered
Testing:
Running 200 netperf TCP_RR streams
No ILA, baseline
79.26% CPU utilization
1678282 tps
104/189/390 50/90/99% latencies
ILA before fix (LWT on both input and output)
81.91% CPU utilization
1464723 tps (-14.5% from baseline)
121/215/411 50/90/99% latencies
ILA after fix
80.62% CPU utilization
1622985 (-3.4% from baseline)
110/191/347 50/90/99% latencies
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/ila/ila_lwt.c')
-rw-r--r-- | net/ipv6/ila/ila_lwt.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c new file mode 100644 index 000000000000..2ae3c4fd8aab --- /dev/null +++ b/net/ipv6/ila/ila_lwt.c @@ -0,0 +1,152 @@ +#include <linux/errno.h> +#include <linux/ip.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/skbuff.h> +#include <linux/socket.h> +#include <linux/types.h> +#include <net/checksum.h> +#include <net/ip.h> +#include <net/ip6_fib.h> +#include <net/lwtunnel.h> +#include <net/protocol.h> +#include <uapi/linux/ila.h> +#include "ila.h" + +static inline struct ila_params *ila_params_lwtunnel( + struct lwtunnel_state *lwstate) +{ + return (struct ila_params *)lwstate->data; +} + +static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + + if (skb->protocol != htons(ETH_P_IPV6)) + goto drop; + + update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); + + return dst->lwtstate->orig_output(net, sk, skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +static int ila_input(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + + if (skb->protocol != htons(ETH_P_IPV6)) + goto drop; + + update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); + + return dst->lwtstate->orig_input(skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { + [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, +}; + +static int ila_build_state(struct net_device *dev, struct nlattr *nla, + unsigned int family, const void *cfg, + struct lwtunnel_state **ts) +{ + struct ila_params *p; + struct nlattr *tb[ILA_ATTR_MAX + 1]; + size_t encap_len = sizeof(*p); + struct lwtunnel_state *newts; + const struct fib6_config *cfg6 = cfg; + int ret; + + if (family != AF_INET6) + return -EINVAL; + + ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, + ila_nl_policy); + if (ret < 0) + return ret; + + if (!tb[ILA_ATTR_LOCATOR]) + return -EINVAL; + + newts = lwtunnel_state_alloc(encap_len); + if (!newts) + return -ENOMEM; + + newts->len = encap_len; + p = ila_params_lwtunnel(newts); + + p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); + + if (cfg6->fc_dst_len > sizeof(__be64)) { + /* Precompute checksum difference for translation since we + * know both the old locator and the new one. + */ + p->locator_match = *(__be64 *)&cfg6->fc_dst; + p->csum_diff = compute_csum_diff8( + (__be32 *)&p->locator_match, (__be32 *)&p->locator); + } + + newts->type = LWTUNNEL_ENCAP_ILA; + newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | + LWTUNNEL_STATE_INPUT_REDIRECT; + + *ts = newts; + + return 0; +} + +static int ila_fill_encap_info(struct sk_buff *skb, + struct lwtunnel_state *lwtstate) +{ + struct ila_params *p = ila_params_lwtunnel(lwtstate); + + if (nla_put_u64(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static int ila_encap_nlsize(struct lwtunnel_state *lwtstate) +{ + /* No encapsulation overhead */ + return 0; +} + +static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) +{ + struct ila_params *a_p = ila_params_lwtunnel(a); + struct ila_params *b_p = ila_params_lwtunnel(b); + + return (a_p->locator != b_p->locator); +} + +static const struct lwtunnel_encap_ops ila_encap_ops = { + .build_state = ila_build_state, + .output = ila_output, + .input = ila_input, + .fill_encap = ila_fill_encap_info, + .get_encap_size = ila_encap_nlsize, + .cmp_encap = ila_encap_cmp, +}; + +int ila_lwt_init(void) +{ + return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA); +} + +void ila_lwt_fini(void) +{ + lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA); +} |