[ovs-dev] [PATCH 13/16] datapath: Inner And Outer Flows

Simon Horman horms at verge.net.au
Fri Jan 25 07:22:19 UTC 2013


Allow a flow to be visible in two forms, an outer flow and an inner flow.
This may occur if the actions allow further decoding of a packet to
provide a more fine-grained match. In this case the first-pass
coarse-grained match will hit the outer-flow and the second-pass
fined-grained match will hit the inner-flow.

Inner-flows are not visible to user-space. Rather, they are just an
internal representation to handle the case of actions allowing further
packet decoding. An inner-flow is associated with its outer-flow and
deleted when the outer-flow is deleted. An outer-flow may have more than
one inner-flow but an inner-flow may only have one outer-flow.

For example:

In the case of MPLS, L3 and L4 information may not initially be decoded
from the frame as the ethernet type of the frame is an MPLS type and no
information is known about the type of the inner frame.

However, the type of the inner frame may be provided by an mpls_pop action
in which case L3 and L4 information may be decoded providing a finer
grained match than is otherwise possible.

Signed-off-by: Simon Horman <horms at verge.net.au>

---

v2.17
* Rebase
* Do not allocate and free actions for outer flows,
  the actions of an inner flow may be shared.

v2.16
* No change

v2.15
* Rebase

v2.14
* No change

v2.13
* Merge "datapath: Allow inner_flows" and
  "datapath: re-lookup a flow if actions allow a more fine grained match"
  and rename the resulting patch "datapath: Inner and Outer Flows".
* As suggested by Jarno Rajahalme
  - Add encap_eth_type to struct sw_flow and use it to store the
    ethernet type supplied by actions of a flow if any. This is
    an optimisation to avoid scanning the actions for each packet.

v2.12
* No change

v2.11
* First post
---
 datapath/actions.c  |   14 +++
 datapath/datapath.c |  262 +++++++++++++++++++++++++++++++++++----------------
 datapath/datapath.h |    1 +
 datapath/flow.c     |   72 ++++++++++----
 datapath/flow.h     |   42 ++++++++-
 5 files changed, 291 insertions(+), 100 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index 60522be..eefbab4 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -629,6 +629,20 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 	return 0;
 }
 
+__be16 ovs_actions_allow_l3_extraction(struct sw_flow *flow)
+{
+	struct sw_flow_actions *acts = rcu_dereference(flow->sf_acts);
+	const struct nlattr *a;
+	int rem;
+
+	for (a = acts->actions, rem = acts->actions_len; rem > 0;
+	     a = nla_next(a, &rem))
+		if (nla_type(a) == OVS_ACTION_ATTR_POP_MPLS)
+			return *(__be16 *)nla_data(a);
+
+	return 0;
+}
+
 /* We limit the number of times that we pass into execute_actions()
  * to avoid blowing out the stack in the event that we have a loop. */
 #define MAX_LOOPS 5
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 578fbef..0284bea 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -239,7 +239,7 @@ void ovs_dp_detach_port(struct vport *p)
 void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 {
 	struct datapath *dp = p->dp;
-	struct sw_flow *flow;
+	struct sw_flow *flow, *outer_flow = NULL;
 	struct dp_stats_percpu *stats;
 	u64 *stats_counter;
 	int error;
@@ -249,6 +249,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 	if (!OVS_CB(skb)->flow) {
 		struct sw_flow_key key;
 		int key_len;
+		struct flow_table *table = rcu_dereference(dp->table);
 
 		/* Extract flow from 'skb' into 'key'. */
 		error = ovs_flow_extract(skb, p->port_no, &key, &key_len);
@@ -258,8 +259,27 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 		}
 
 		/* Look up flow. */
-		flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table),
-					   &key, key_len);
+		flow = ovs_flow_tbl_lookup(table, &key, key_len);
+
+		/* We have a flow? Superb.
+		 * See if the actions in the flow supply information to
+		 * allow more information to be included in the flow's match
+		 */
+		if (likely(flow)) {
+			__be16 encap_eth_type;
+			encap_eth_type = flow->encap_eth_type;
+			if (unlikely(encap_eth_type)) {
+				error = ovs_flow_extract_l3_onwards(skb, &key, &key_len,
+								    encap_eth_type);
+				if (unlikely(error)) {
+					kfree_skb(skb);
+					return;
+				}
+				outer_flow = flow;
+				flow = ovs_flow_tbl_lookup(table, &key, key_len);
+			}
+		}
+
 		if (unlikely(!flow)) {
 			struct dp_upcall_info upcall;
 
@@ -277,6 +297,8 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 	}
 
 	stats_counter = &stats->n_hit;
+	if (unlikely(outer_flow))
+		ovs_flow_used(outer_flow, skb);
 	ovs_flow_used(OVS_CB(skb)->flow, skb);
 	ovs_execute_actions(dp, skb);
 
@@ -1228,54 +1250,28 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
 	return skb;
 }
 
-static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
-{
-	struct nlattr **a = info->attrs;
-	struct ovs_header *ovs_header = info->userhdr;
-	struct sw_flow_key key;
-	struct sw_flow *flow;
-	struct sk_buff *reply;
-	struct datapath *dp;
-	struct flow_table *table;
-	struct sw_flow_actions *acts = NULL;
-	int error;
-	int key_len;
-
-	/* Extract key. */
-	error = -EINVAL;
-	if (!a[OVS_FLOW_ATTR_KEY])
-		goto error;
-	error = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
-	if (error)
-		goto error;
-
-	/* Validate actions. */
-	if (a[OVS_FLOW_ATTR_ACTIONS]) {
-		acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
-		error = PTR_ERR(acts);
-		if (IS_ERR(acts))
-			goto error;
-
-		error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0, &acts);
-		if (error)
-			goto err_kfree;
-	} else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
-		error = -EINVAL;
-		goto error;
-	}
+enum flow_type {
+	flow_inner,
+	flow_outer,
+	flow_singleton,
+};
 
-	dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-	error = -ENODEV;
-	if (!dp)
-		goto err_kfree;
+static int ovs_flow_cmd_new_or_set__(struct sk_buff *skb,
+				     struct genl_info *info,
+				     struct sw_flow_actions *acts,
+				     struct nlattr **a, struct datapath *dp,
+				     struct sw_flow_key key, int key_len,
+				     enum flow_type flow_type,
+				     struct sw_flow **flowp)
+{
+	struct sk_buff *reply = NULL;
+	struct flow_table *table = genl_dereference(dp->table);
+	struct sw_flow *flow = ovs_flow_tbl_lookup(table, &key, key_len);
 
-	table = genl_dereference(dp->table);
-	flow = ovs_flow_tbl_lookup(table, &key, key_len);
 	if (!flow) {
 		/* Bail out if we're not allowed to create a new flow. */
-		error = -ENOENT;
 		if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
-			goto err_kfree;
+			return -ENOENT;
 
 		/* Expand table, if necessary, to make room. */
 		if (ovs_flow_tbl_need_to_expand(table)) {
@@ -1291,23 +1287,36 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
 		/* Allocate flow. */
 		flow = ovs_flow_alloc();
-		if (IS_ERR(flow)) {
-			error = PTR_ERR(flow);
-			goto err_kfree;
-		}
+		if (IS_ERR(flow))
+			return PTR_ERR(flow);
 		clear_stats(flow);
 
 		rcu_assign_pointer(flow->sf_acts, acts);
 
+		/* Add inner flow without locking */
+		if (flow_type == flow_outer) {
+			flow->encap_eth_type = ovs_actions_allow_l3_extraction(flow);
+			list_add_rcu(&(*flowp)->inner_flows,
+				     &flow->inner_flows);
+			flow->flags = SW_FLOW_F_NO_FREE_SF_ACTS;
+		} else if (flow_type == flow_inner) {
+
+			flow->flags = SW_FLOW_F_NO_DUMP;
+			*flowp = flow;
+		}
+
 		/* Put flow in bucket. */
 		ovs_flow_tbl_insert(table, flow, &key, key_len);
 
-		reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
-						info->snd_seq,
-						OVS_FLOW_CMD_NEW);
+		if (flow_type == flow_inner)
+			reply = ovs_flow_cmd_build_info(flow, dp,
+							info->snd_portid,
+							info->snd_seq,
+							OVS_FLOW_CMD_NEW);
 	} else {
 		/* We found a matching flow. */
 		struct sw_flow_actions *old_acts;
+		bool may_clear_stats = true;
 
 		/* Bail out if we're not allowed to modify an existing flow.
 		 * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL
@@ -1315,28 +1324,45 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 		 * request.  We also accept NLM_F_EXCL in case that bug ever
 		 * gets fixed.
 		 */
-		error = -EEXIST;
 		if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW &&
-		    info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
-			goto err_kfree;
-
-		/* Update actions. */
-		old_acts = rcu_dereference_protected(flow->sf_acts,
-						     lockdep_genl_is_held());
-		rcu_assign_pointer(flow->sf_acts, acts);
-		ovs_flow_deferred_free_acts(old_acts);
-
-		reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
-					       info->snd_seq, OVS_FLOW_CMD_NEW);
+		    info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) {
+			/* If this is an outer_flow then it may duplicate
+			 * the outer_flow of another flow's inner_flow.
+			 * Just accept this and more on.
+			 */
+			if (flow_type != flow_outer)
+				return -EEXIST;
+			may_clear_stats = false;
+		} else {
+			/* Update actions. */
+			old_acts = rcu_dereference_protected(flow->sf_acts,
+							     lockdep_genl_is_held());
+			rcu_assign_pointer(flow->sf_acts, acts);
+			if (!(flow->flags | SW_FLOW_F_NO_FREE_SF_ACTS))
+				ovs_flow_deferred_free_acts(old_acts);
+
+			reply = ovs_flow_cmd_build_info(flow, dp,
+							info->snd_portid,
+							info->snd_seq,
+							OVS_FLOW_CMD_NEW);
+		}
 
-		/* Clear stats. */
-		if (a[OVS_FLOW_ATTR_CLEAR]) {
+		if (a[OVS_FLOW_ATTR_CLEAR] || flow_type == flow_outer) {
 			spin_lock_bh(&flow->lock);
-			clear_stats(flow);
+			/* Clear stats. */
+			if (a[OVS_FLOW_ATTR_CLEAR] && may_clear_stats)
+				clear_stats(flow);
+			/* Add inner flow */
+			if (flow_type == flow_outer)
+				list_add_rcu(&(*flowp)->inner_flows,
+					     &flow->inner_flows);
 			spin_unlock_bh(&flow->lock);
 		}
 	}
 
+	if (!reply)
+		return 0;
+
 	if (!IS_ERR(reply))
 		genl_notify(reply, genl_info_net(info), info->snd_portid,
 			   ovs_dp_flow_multicast_group.id, info->nlhdr,
@@ -1345,6 +1371,68 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 		netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
 				ovs_dp_flow_multicast_group.id,	PTR_ERR(reply));
 	return 0;
+}
+
+static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr **a = info->attrs;
+	struct ovs_header *ovs_header = info->userhdr;
+	struct sw_flow_key key;
+	struct sw_flow *inner_flow = NULL;
+	struct datapath *dp;
+	struct sw_flow_actions *acts = NULL;
+	int error;
+	int key_len[2];
+
+	/* Extract key. */
+	error = -EINVAL;
+	if (!a[OVS_FLOW_ATTR_KEY])
+		goto error;
+	error = ovs_flow_from_nlattrs(&key, key_len, a[OVS_FLOW_ATTR_KEY]);
+	if (error)
+		goto error;
+
+	/* Validate actions. */
+	if (a[OVS_FLOW_ATTR_ACTIONS]) {
+		acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+		error = PTR_ERR(acts);
+		if (IS_ERR(acts))
+			goto error;
+
+		error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0, &acts);
+		if (error)
+			goto err_kfree;
+	} else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
+		error = -EINVAL;
+		goto error;
+	}
+
+	dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+	error = -ENODEV;
+	if (!dp)
+		goto err_kfree;
+
+	if (key_len[1]) {
+		error = ovs_flow_cmd_new_or_set__(skb, info, acts, a, dp,
+						  key, key_len[1], flow_inner,
+						  &inner_flow);
+		if (error)
+			goto err_kfree;
+	}
+
+	error = ovs_flow_cmd_new_or_set__(skb, info, acts, a, dp, key,
+					  key_len[0], key_len[1] ? flow_outer :
+					  flow_singleton, &inner_flow);
+	if (error) {
+		if (key_len[1]) {
+			struct flow_table *table = genl_dereference(dp->table);
+			ovs_flow_tbl_remove(table, inner_flow);
+			ovs_flow_deferred_free(inner_flow);
+		}
+		goto err_kfree;
+	}
+
+	return 0;
 
 err_kfree:
 	kfree(acts);
@@ -1362,11 +1450,11 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
 	struct datapath *dp;
 	struct flow_table *table;
 	int err;
-	int key_len;
+	int key_len[2];
 
 	if (!a[OVS_FLOW_ATTR_KEY])
 		return -EINVAL;
-	err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+	err = ovs_flow_from_nlattrs(&key, key_len, a[OVS_FLOW_ATTR_KEY]);
 	if (err)
 		return err;
 
@@ -1375,7 +1463,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
 		return -ENODEV;
 
 	table = genl_dereference(dp->table);
-	flow = ovs_flow_tbl_lookup(table, &key, key_len);
+	flow = ovs_flow_tbl_lookup(table, &key,
+				   key_len[1] ? key_len[1] : key_len[0]);
 	if (!flow)
 		return -ENOENT;
 
@@ -1393,11 +1482,11 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	struct ovs_header *ovs_header = info->userhdr;
 	struct sw_flow_key key;
 	struct sk_buff *reply;
-	struct sw_flow *flow;
+	struct sw_flow *notify_flow, *outer_flow, *inner_flow;
 	struct datapath *dp;
 	struct flow_table *table;
 	int err;
-	int key_len;
+	int key_len[2];
 
 	dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
 	if (!dp)
@@ -1406,26 +1495,39 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	if (!a[OVS_FLOW_ATTR_KEY])
 		return flush_flows(dp);
 
-	err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+	err = ovs_flow_from_nlattrs(&key, key_len, a[OVS_FLOW_ATTR_KEY]);
 	if (err)
 		return err;
 
 	table = genl_dereference(dp->table);
-	flow = ovs_flow_tbl_lookup(table, &key, key_len);
-	if (!flow)
+	notify_flow = outer_flow = ovs_flow_tbl_lookup(table, &key, key_len[0]);
+	if (!outer_flow)
 		return -ENOENT;
 
-	reply = ovs_flow_cmd_alloc_info(flow);
+	if (key_len[1]) {
+		notify_flow = inner_flow = ovs_flow_tbl_lookup(table, &key,
+							       key_len[1]);
+		if (!inner_flow)
+			return -ENOENT;
+	} else {
+		inner_flow = NULL;
+	}
+
+	reply = ovs_flow_cmd_alloc_info(notify_flow);
 	if (!reply)
 		return -ENOMEM;
 
-	ovs_flow_tbl_remove(table, flow);
+	ovs_flow_tbl_remove(table, outer_flow);
+	if (inner_flow)
+		ovs_flow_tbl_remove(table, inner_flow);
 
-	err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
+	err = ovs_flow_cmd_fill_info(notify_flow, dp, reply, info->snd_portid,
 				     info->snd_seq, 0, OVS_FLOW_CMD_DEL);
 	BUG_ON(err < 0);
 
-	ovs_flow_deferred_free(flow);
+	ovs_flow_deferred_free(outer_flow);
+	if (inner_flow)
+		ovs_flow_deferred_free(inner_flow);
 
 	genl_notify(reply, genl_info_net(info), info->snd_portid,
 		    ovs_dp_flow_multicast_group.id, info->nlhdr, GFP_KERNEL);
diff --git a/datapath/datapath.h b/datapath/datapath.h
index 11c908e..c969b88 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -194,6 +194,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 portid, u32 seq,
 					 u8 cmd);
 
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
+__be16 ovs_actions_allow_l3_extraction(struct sw_flow *flow);
 
 void skb_cb_set_l2_size(struct sk_buff *skb);
 unsigned char *skb_cb_mpls_bos(const struct sk_buff *skb);
diff --git a/datapath/flow.c b/datapath/flow.c
index 75f85b4..b59e818 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -224,7 +224,9 @@ struct sw_flow *ovs_flow_alloc(void)
 		return ERR_PTR(-ENOMEM);
 
 	spin_lock_init(&flow->lock);
+	INIT_LIST_HEAD(&flow->inner_flows);
 	flow->sf_acts = NULL;
+	flow->flags = 0;
 
 	return flow;
 }
@@ -341,7 +343,7 @@ struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *la
 		i = 0;
 		head = flex_array_get(table->buckets, *bucket);
 		hlist_for_each_entry_rcu(flow, n, head, hash_node[ver]) {
-			if (i < *last) {
+			if (i < *last || flow->flags | SW_FLOW_F_NO_DUMP) {
 				i++;
 				continue;
 			}
@@ -413,7 +415,8 @@ void ovs_flow_free(struct sw_flow *flow)
 	if (unlikely(!flow))
 		return;
 
-	kfree((struct sf_flow_acts __force *)flow->sf_acts);
+	if (!(flow->flags | SW_FLOW_F_NO_FREE_SF_ACTS))
+		kfree((struct sf_flow_acts __force *)flow->sf_acts);
 	kmem_cache_free(flow_cache, flow);
 }
 
@@ -873,13 +876,25 @@ void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
 	__flow_tbl_insert(table, flow);
 }
 
-void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
+static void __flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
 {
 	hlist_del_rcu(&flow->hash_node[table->node_ver]);
 	table->count--;
 	BUG_ON(table->count < 0);
 }
 
+void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
+{
+	struct sw_flow *inner_flow;
+
+	__flow_tbl_remove(table, flow);
+
+	list_for_each_entry_rcu(inner_flow, &flow->inner_flows, inner_flows) {
+		__flow_tbl_remove(table, inner_flow);
+		ovs_flow_deferred_free(inner_flow);
+	}
+}
+
 /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute.  */
 const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 	[OVS_KEY_ATTR_ENCAP] = -1,
@@ -1145,7 +1160,7 @@ int ipv4_tun_to_nlattr(struct sk_buff *skb,
  * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
  * sequence.
  */
-int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
+int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int key_lenp[2],
 		      const struct nlattr *attr)
 {
 	const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
@@ -1153,6 +1168,7 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 	int key_len;
 	u64 attrs;
 	int err;
+	__be16 eth_type;
 
 	memset(swkey, 0, sizeof(struct sw_flow_key));
 	key_len = SW_FLOW_KEY_OFFSET(eth);
@@ -1260,7 +1276,29 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 		swkey->eth.type = htons(ETH_P_802_2);
 	}
 
-	if (swkey->eth.type == htons(ETH_P_IP)) {
+	eth_type = swkey->eth.type;
+	if (eth_p_mpls(eth_type)) {
+		const struct ovs_key_mpls *mpls_key;
+
+		if (!(attrs & (1ULL << OVS_KEY_ATTR_MPLS)))
+			return -EINVAL;
+		attrs &= ~(1ULL << OVS_KEY_ATTR_MPLS);
+
+		key_len = SW_FLOW_KEY_OFFSET(mpls.top_lse);
+		mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]);
+		swkey->mpls.top_lse = mpls_key->mpls_top_lse;
+
+		if (attrs & (1 << OVS_KEY_ATTR_IPV4))
+			eth_type = htons(ETH_P_IP);
+		if (attrs & (1 << OVS_KEY_ATTR_IPV6))
+			eth_type = htons(ETH_P_IPV6);
+		if (attrs & (1 << OVS_KEY_ATTR_ARP))
+			eth_type = htons(ETH_P_ARP);
+	}
+
+	key_lenp[0] = key_len;
+
+	if (eth_type == htons(ETH_P_IP)) {
 		const struct ovs_key_ipv4 *ipv4_key;
 
 		if (!(attrs & (1 << OVS_KEY_ATTR_IPV4)))
@@ -1283,7 +1321,7 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 			if (err)
 				return err;
 		}
-	} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+	} else if (eth_type == htons(ETH_P_IPV6)) {
 		const struct ovs_key_ipv6 *ipv6_key;
 
 		if (!(attrs & (1 << OVS_KEY_ATTR_IPV6)))
@@ -1309,8 +1347,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 			if (err)
 				return err;
 		}
-	} else if (swkey->eth.type == htons(ETH_P_ARP) ||
-		   swkey->eth.type == htons(ETH_P_RARP)) {
+	} else if (eth_type == htons(ETH_P_ARP) ||
+		   eth_type == htons(ETH_P_RARP)) {
 		const struct ovs_key_arp *arp_key;
 
 		if (!(attrs & (1 << OVS_KEY_ATTR_ARP)))
@@ -1326,21 +1364,17 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 		swkey->ip.proto = ntohs(arp_key->arp_op);
 		memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN);
 		memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN);
-	} else if (eth_p_mpls(swkey->eth.type)) {
-		const struct ovs_key_mpls *mpls_key;
-
-		if (!(attrs & (1ULL << OVS_KEY_ATTR_MPLS)))
-			return -EINVAL;
-		attrs &= ~(1ULL << OVS_KEY_ATTR_MPLS);
-
-		key_len = SW_FLOW_KEY_OFFSET(mpls.top_lse);
-		mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]);
-		swkey->mpls.top_lse = mpls_key->mpls_top_lse;
 	}
 
 	if (attrs)
 		return -EINVAL;
-	*key_lenp = key_len;
+
+	if (eth_type == swkey->eth.type) {
+		key_lenp[0] = key_len;
+		key_lenp[1] = 0;
+	} else {
+		key_lenp[1] = key_len;
+	}
 
 	return 0;
 }
diff --git a/datapath/flow.h b/datapath/flow.h
index 3969d7c..d162302 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -112,6 +112,29 @@ struct sw_flow_key {
 	};
 };
 
+#define SW_FLOW_F_NO_DUMP         0x01
+#define SW_FLOW_F_NO_FREE_SF_ACTS 0x02
+
+/* Flows are divided into three types:
+ * Singleton Flow: The match and actions of this flow are self-contained
+ *     and actions are applied to packets which match.
+ * Outer Flow: The actions of the flow include information that
+ *     may be used to create a more fine-grained match. This match
+ *     is constructed and a second lookup is made for the inner flow that
+ *     matches a packet.
+ * Inner Flow: This has a more fine-grained match constructed using
+ *     information contained in the actions of the inner flow.
+ *
+ * The actions of the inner and outer flow are identical but as a match on
+ * an outer flow will always result in the lookup of an inner flow the
+ * matches of an outer flow are never applied.
+ *
+ * An outer_flow is denoted by encap_eth_type being set to a non-zero
+ * value. This element is used as an optimisation to avoid scanning the
+ * actions of the outer flow each time it matches a packet.
+ *
+ * On deletion, outer_flow will delete all its inner flows, which
+ * are found by iterating the inner_flows element. */
 struct sw_flow {
 	struct rcu_head rcu;
 	struct hlist_node hash_node[2];
@@ -121,10 +144,27 @@ struct sw_flow {
 	struct sw_flow_actions __rcu *sf_acts;
 
 	spinlock_t lock;	/* Lock for values below. */
+
+	struct list_head inner_flows;	/* Inner flows.
+					 * For an inner flow this is its
+					 * entry in the hlist of its outer
+					 * flow.
+					 * For an outer flow this is the
+					 * list of inner flows
+					 * For a singleton flow this is
+					 * unused */
+	__be16 encap_eth_type;		/* Set to the ethernet type supplied
+					 * by action for outer flows,
+					 * zero otherwise. */
+
 	unsigned long used;	/* Last used time (in jiffies). */
 	u64 packet_count;	/* Number of packets matched. */
 	u64 byte_count;		/* Number of bytes matched. */
 	u8 tcp_flags;		/* Union of seen TCP flags. */
+	/* End values that need loc*/
+
+	u8 flags;		/* SW_FLOW_F_* */
+
 };
 
 struct arp_eth_header {
@@ -156,7 +196,7 @@ struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
 void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
 
 int ovs_flow_extract_l3_onwards(struct sk_buff *, struct sw_flow_key *,
-				int *key_lenp, __be16 eth_type);
+				int key_lenp[2], __be16 eth_type);
 int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
 		     int *key_lenp);
 void ovs_flow_used(struct sw_flow *, struct sk_buff *);
-- 
1.7.10.4




More information about the dev mailing list