[ovs-dev] [ovs flow free panic v2] datapath: Fix kernel panic on ovs_flow_free
Andy Zhou
azhou at nicira.com
Sat Jan 11 00:24:51 UTC 2014
Both mega flow mask's reference counter and per flow table mask list
should only be accessed when holding ovs_mutex() lock. However
this is not true with ovs_flow_table_flush(). The patch fixes this bug.
Signed-off-by: Andy Zhou <azhou at nicira.com>
---
v1->v2:
Atomic ops can protect mask's reference counter, but not mask list.
rework ovs_flow_table_flush() implementation instead.
---
datapath/flow_table.c | 61 ++++++++++++++++++++++++-------------------------
1 file changed, 30 insertions(+), 31 deletions(-)
diff --git a/datapath/flow_table.c b/datapath/flow_table.c
index 4232b82..f26c43f 100644
--- a/datapath/flow_table.c
+++ b/datapath/flow_table.c
@@ -162,29 +162,26 @@ static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu)
kfree(mask);
}
-static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
-{
- if (!mask)
- return;
-
- BUG_ON(!mask->ref_count);
- mask->ref_count--;
-
- if (!mask->ref_count) {
- list_del_rcu(&mask->list);
- if (deferred)
- call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb);
- else
- kfree(mask);
- }
-}
-
void ovs_flow_free(struct sw_flow *flow, bool deferred)
{
if (!flow)
return;
- flow_mask_del_ref(flow->mask, deferred);
+ if (flow->mask) {
+ struct sw_flow_mask *mask = flow->mask;
+
+ BUG_ON(!mask->ref_count);
+ mask->ref_count--;
+
+ if (!mask->ref_count) {
+ ASSERT_OVSL();
+ list_del_rcu(&mask->list);
+ if (deferred)
+ call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb);
+ else
+ kfree(mask);
+ }
+ }
if (deferred)
call_rcu(&flow->rcu, rcu_free_flow_callback);
@@ -197,12 +194,12 @@ static void free_buckets(struct flex_array *buckets)
flex_array_free(buckets);
}
-static void __table_instance_destroy(struct table_instance *ti)
+static void table_instance_free_flows(struct table_instance *ti, bool deferred)
{
int i;
if (ti->keep_flows)
- goto skip_flows;
+ return;
for (i = 0; i < ti->n_buckets; i++) {
struct sw_flow *flow;
@@ -211,12 +208,14 @@ static void __table_instance_destroy(struct table_instance *ti)
int ver = ti->node_ver;
hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
- hlist_del(&flow->hash_node[ver]);
- ovs_flow_free(flow, false);
+ hlist_del_rcu(&flow->hash_node[ver]);
+ ovs_flow_free(flow, deferred);
}
}
+}
-skip_flows:
+static void __table_instance_destroy(struct table_instance *ti)
+{
free_buckets(ti->buckets);
kfree(ti);
}
@@ -262,7 +261,8 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
{
struct table_instance *ti = container_of(rcu, struct table_instance, rcu);
- __table_instance_destroy(ti);
+ free_buckets(ti->buckets);
+ kfree(ti);
}
static void table_instance_destroy(struct table_instance *ti, bool deferred)
@@ -270,6 +270,8 @@ static void table_instance_destroy(struct table_instance *ti, bool deferred)
if (!ti)
return;
+ table_instance_free_flows(ti, deferred);
+
if (deferred)
call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb);
else
@@ -513,16 +515,11 @@ static struct sw_flow_mask *mask_alloc(void)
mask = kmalloc(sizeof(*mask), GFP_KERNEL);
if (mask)
- mask->ref_count = 0;
+ mask->ref_count = 1;
return mask;
}
-static void mask_add_ref(struct sw_flow_mask *mask)
-{
- mask->ref_count++;
-}
-
static bool mask_equal(const struct sw_flow_mask *a,
const struct sw_flow_mask *b)
{
@@ -563,9 +560,11 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
mask->key = new->key;
mask->range = new->range;
list_add_rcu(&mask->list, &tbl->mask_list);
+ } else {
+ BUG_ON(!mask->ref_count);
+ mask->ref_count++;
}
- mask_add_ref(mask);
flow->mask = mask;
return 0;
}
--
1.7.9.5
More information about the dev
mailing list