summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Wang <alexw@nicira.com>2014-06-30 13:48:07 -0700
committerAlex Wang <alexw@nicira.com>2014-06-30 16:58:43 -0700
commit8f037d1f76033d6398282b1fcb825b0d6c170402 (patch)
tree6d567d7f740b27ac7b4f3d5d6f005ccd13f3260b
parent3604fabbf965139e0064d7beca4dae4dd73f3497 (diff)
downloadopenvswitch-8f037d1f76033d6398282b1fcb825b0d6c170402.tar.gz
datapath: Use exact lookup for flow_get and flow_del.
Due to the race condition in userspace, there is chance that two overlapping megaflows could be installed in datapath. And this causes userspace unable to delete the less inclusive megaflow flow even after it timeout, since the flow_del logic will stop at the first match of masked flow. This commit fixes the bug by making the kernel flow_del and flow_get logic check all masks in that case. Signed-off-by: Alex Wang <alexw@nicira.com> Acked-by: Andy Zhou <azhou@nicira.com> Acked-by: Pravin B Shelar <pshelar@nicira.com>
-rw-r--r--datapath/datapath.c12
-rw-r--r--datapath/flow.c33
-rw-r--r--datapath/flow.h4
3 files changed, 29 insertions, 20 deletions
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 6d523d600..cfaa0a90c 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -1358,8 +1358,12 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
/* The unmasked key has to be the same for flow updates. */
error = -EINVAL;
if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end)) {
- OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n");
- goto err_unlock_ovs;
+ /* Look for any overlapping flow. */
+ flow = ovs_flow_lookup_exact(table, &match);
+ if (!flow) {
+ OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n");
+ goto err_unlock_ovs;
+ }
}
/* Update actions. */
@@ -1426,7 +1430,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
}
table = ovsl_dereference(dp->table);
- flow = ovs_flow_lookup_unmasked_key(table, &match);
+ flow = ovs_flow_lookup_exact(table, &match);
if (!flow) {
err = -ENOENT;
goto unlock;
@@ -1476,7 +1480,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
goto unlock;
table = ovsl_dereference(dp->table);
- flow = ovs_flow_lookup_unmasked_key(table, &match);
+ flow = ovs_flow_lookup_exact(table, &match);
if (!flow) {
err = -ENOENT;
goto unlock;
diff --git a/datapath/flow.c b/datapath/flow.c
index 2e46866e5..b3bc6838e 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -28,6 +28,7 @@
#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/llc.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/in.h>
#include <linux/rcupdate.h>
@@ -1070,20 +1071,6 @@ bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
}
-struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
- struct sw_flow_match *match)
-{
- struct sw_flow_key *unmasked = match->key;
- int key_end = match->range.end;
- struct sw_flow *flow;
-
- flow = ovs_flow_lookup(table, unmasked);
- if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_end)))
- flow = NULL;
-
- return flow;
-}
-
static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
const struct sw_flow_key *unmasked,
struct sw_flow_mask *mask)
@@ -1107,6 +1094,24 @@ static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
return NULL;
}
+struct sw_flow *ovs_flow_lookup_exact(struct flow_table *tbl,
+ struct sw_flow_match *match)
+{
+ struct sw_flow_key *unmasked = match->key;
+ struct sw_flow *flow;
+ struct sw_flow_mask *mask;
+ int key_end = match->range.end;
+
+ /* Always called under ovs-mutex. */
+ list_for_each_entry(mask, tbl->mask_list, list) {
+ flow = ovs_masked_flow_lookup(tbl, unmasked, mask);
+ if (flow && ovs_flow_cmp_unmasked_key(flow, unmasked, key_end)) /* Found */
+ return flow;
+ }
+
+ return NULL;
+}
+
struct sw_flow *ovs_flow_lookup(struct flow_table *tbl,
const struct sw_flow_key *key)
{
diff --git a/datapath/flow.h b/datapath/flow.h
index 03eae0352..cf7e2e206 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -220,8 +220,8 @@ static inline int ovs_flow_tbl_need_to_expand(struct flow_table *table)
struct sw_flow *ovs_flow_lookup(struct flow_table *,
const struct sw_flow_key *);
-struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
- struct sw_flow_match *match);
+struct sw_flow *ovs_flow_lookup_exact(struct flow_table *tbl,
+ struct sw_flow_match *match);
void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred);
struct flow_table *ovs_flow_tbl_alloc(int new_size);