[ovs-dev] [PATCHv4] datapath: Cache netlink flow key, mask on flow_dump.
Joe Stringer
joestringer at nicira.com
Mon Jul 21 02:30:38 UTC 2014
Converting the flow key and mask back into netlink format during each
flow dump is fairly expensive. By caching the netlink versions of these
the first time a flow is dumped, and copying the memory directly during
subsequent dumps, we are able to support up to 15% more flows in the
datapath.
Perf of single revalidator thread before, many flows in datapath:
14.59% ovs-vswitchd [kernel.kallsyms] [k] memcpy
8.21% ovs-vswitchd [kernel.kallsyms] [k] memset
3.67% ovs-vswitchd [kernel.kallsyms] [k] __nla_reserve
3.55% ovs-vswitchd ovs-vswitchd [.] revalidate.isra.14
2.89% ovs-vswitchd [kernel.kallsyms] [k] _raw_spin_lock_bh
...
Perf of single revalidator thread after:
8.70% ovs-vswitchd [kernel.kallsyms] [k] memcpy
5.09% ovs-vswitchd ovs-vswitchd [.] revalidate.isra.14
4.36% ovs-vswitchd [kernel.kallsyms] [k] ovs_nla_put_flow
4.27% ovs-vswitchd [kernel.kallsyms] [k] memset
3.41% ovs-vswitchd [kernel.kallsyms] [k] _raw_spin_lock_bh
...
Signed-off-by: Joe Stringer <joestringer at nicira.com>
---
v4: Construct the cache during the first flow dump.
v3: Add error checking to ovs_nla_copy_flow() and its users.
Allow nl_match_cache to fail allocation in ovs_flow_alloc().
v2: Copy mask from userspace rather than constructing from sw_flow.
RFC: First post.
---
datapath/datapath.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
datapath/flow.h | 1 +
datapath/flow_table.c | 2 ++
3 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 065356f..b6161ce 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -684,11 +684,16 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
}
}
+static size_t ovs_nl_match_size(void)
+{
+ return nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */
+ + nla_total_size(key_attr_size()); /* OVS_FLOW_ATTR_MASK */
+}
+
static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
{
return NLMSG_ALIGN(sizeof(struct ovs_header))
- + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */
- + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_MASK */
+ + ovs_nl_match_size()
+ nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */
+ nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
+ nla_total_size(8) /* OVS_FLOW_ATTR_USED */
@@ -703,6 +708,15 @@ static int ovs_flow_cmd_fill_match(struct datapath *dp,
struct nlattr *nla;
int err;
+ if (flow->nl_match_cache) {
+ if (skb_tailroom(skb) < flow->nl_match_cache->len)
+ return -EMSGSIZE;
+
+ return skb_copy_bits(flow->nl_match_cache, 0,
+ skb_put(skb, flow->nl_match_cache->len),
+ flow->nl_match_cache->len);
+ }
+
/* Fill flow key. */
nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
if (!nla)
@@ -826,6 +840,29 @@ error:
return err;
}
+/* Must be called with RCU read lock. */
+static void ovs_flow_fill_nl_cache(struct datapath *dp, struct sw_flow *flow)
+{
+ struct sk_buff *nl_cache;
+ int retval;
+
+ nl_cache = alloc_skb(ovs_nl_match_size(), GFP_KERNEL);
+ if (!nl_cache)
+ return;
+
+ retval = ovs_flow_cmd_fill_match(dp, flow, nl_cache);
+ BUG_ON(retval < 0);
+
+ ovs_lock();
+ if (likely(!flow->nl_match_cache)) {
+ flow->nl_match_cache = nl_cache;
+ nl_cache = NULL;
+ }
+ ovs_unlock();
+
+ kfree_skb(nl_cache);
+}
+
/* May not be called with RCU read lock. */
static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts,
struct genl_info *info,
@@ -1269,6 +1306,9 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
if (!flow)
break;
+ if (!flow->nl_match_cache)
+ ovs_flow_fill_nl_cache(dp, flow);
+
if (ovs_flow_cmd_fill_info(dp, flow, ovs_header->dp_ifindex, skb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
diff --git a/datapath/flow.h b/datapath/flow.h
index f6afa48..f1bf289 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -189,6 +189,7 @@ struct sw_flow {
struct sw_flow_key unmasked_key;
struct sw_flow_mask *mask;
struct sw_flow_actions __rcu *sf_acts;
+ struct sk_buff *nl_match_cache;
struct flow_stats __rcu *stats[]; /* One for each NUMA node. First one
* is allocated at flow creation time,
* the rest are allocated on demand
diff --git a/datapath/flow_table.c b/datapath/flow_table.c
index 9ab1020..e97d749 100644
--- a/datapath/flow_table.c
+++ b/datapath/flow_table.c
@@ -92,6 +92,7 @@ struct sw_flow *ovs_flow_alloc(void)
flow->sf_acts = NULL;
flow->mask = NULL;
+ flow->nl_match_cache = NULL;
flow->stats_last_writer = NUMA_NO_NODE;
/* Initialize the default stat node. */
@@ -146,6 +147,7 @@ static void flow_free(struct sw_flow *flow)
{
int node;
+ kfree_skb(flow->nl_match_cache);
kfree((struct sw_flow_actions __force *)flow->sf_acts);
for_each_node(node)
if (flow->stats[node])
--
1.7.10.4
More information about the dev
mailing list