Commit 4a46b24e authored by Alex Wang's avatar Alex Wang Committed by Pravin B Shelar

openvswitch: 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.

Introduced by 03f0d916 (openvswitch: Mega flow implementation).
Signed-off-by: default avatarAlex Wang <alexw@nicira.com>
Acked-by: default avatarAndy Zhou <azhou@nicira.com>
Signed-off-by: default avatarPravin B Shelar <pshelar@nicira.com>
parent ad552007
...@@ -889,9 +889,12 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -889,9 +889,12 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
} }
/* The unmasked key has to be the same for flow updates. */ /* The unmasked key has to be the same for flow updates. */
if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) { if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
error = -EEXIST; flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
if (!flow) {
error = -ENOENT;
goto err_unlock_ovs; goto err_unlock_ovs;
} }
}
/* Update actions. */ /* Update actions. */
old_acts = ovsl_dereference(flow->sf_acts); old_acts = ovsl_dereference(flow->sf_acts);
rcu_assign_pointer(flow->sf_acts, acts); rcu_assign_pointer(flow->sf_acts, acts);
...@@ -981,16 +984,12 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) ...@@ -981,16 +984,12 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
goto err_unlock_ovs; goto err_unlock_ovs;
} }
/* Check that the flow exists. */ /* Check that the flow exists. */
flow = ovs_flow_tbl_lookup(&dp->table, &key); flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
if (unlikely(!flow)) { if (unlikely(!flow)) {
error = -ENOENT; error = -ENOENT;
goto err_unlock_ovs; goto err_unlock_ovs;
} }
/* The unmasked key has to be the same for flow updates. */
if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
error = -EEXIST;
goto err_unlock_ovs;
}
/* Update actions, if present. */ /* Update actions, if present. */
if (likely(acts)) { if (likely(acts)) {
old_acts = ovsl_dereference(flow->sf_acts); old_acts = ovsl_dereference(flow->sf_acts);
...@@ -1063,8 +1062,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) ...@@ -1063,8 +1062,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
goto unlock; goto unlock;
} }
flow = ovs_flow_tbl_lookup(&dp->table, &key); flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { if (!flow) {
err = -ENOENT; err = -ENOENT;
goto unlock; goto unlock;
} }
...@@ -1113,8 +1112,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -1113,8 +1112,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
goto unlock; goto unlock;
} }
flow = ovs_flow_tbl_lookup(&dp->table, &key); flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
if (unlikely(!flow || !ovs_flow_cmp_unmasked_key(flow, &match))) { if (unlikely(!flow)) {
err = -ENOENT; err = -ENOENT;
goto unlock; goto unlock;
} }
......
...@@ -456,6 +456,22 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, ...@@ -456,6 +456,22 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
return ovs_flow_tbl_lookup_stats(tbl, key, &n_mask_hit); return ovs_flow_tbl_lookup_stats(tbl, key, &n_mask_hit);
} }
struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl,
struct sw_flow_match *match)
{
struct table_instance *ti = rcu_dereference_ovsl(tbl->ti);
struct sw_flow_mask *mask;
struct sw_flow *flow;
/* Always called under ovs-mutex. */
list_for_each_entry(mask, &tbl->mask_list, list) {
flow = masked_flow_lookup(ti, match->key, mask);
if (flow && ovs_flow_cmp_unmasked_key(flow, match)) /* Found */
return flow;
}
return NULL;
}
int ovs_flow_tbl_num_masks(const struct flow_table *table) int ovs_flow_tbl_num_masks(const struct flow_table *table)
{ {
struct sw_flow_mask *mask; struct sw_flow_mask *mask;
......
...@@ -76,7 +76,8 @@ struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *, ...@@ -76,7 +76,8 @@ struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *,
u32 *n_mask_hit); u32 *n_mask_hit);
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *, struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *,
const struct sw_flow_key *); const struct sw_flow_key *);
struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl,
struct sw_flow_match *match);
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
struct sw_flow_match *match); struct sw_flow_match *match);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment