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 <[email protected]>
---
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

_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev

Reply via email to