datapath: Fix kernel panic on ovs_flow_free
authorAndy Zhou <azhou@nicira.com>
Wed, 22 Jan 2014 21:15:24 +0000 (13:15 -0800)
committerAndy Zhou <azhou@nicira.com>
Thu, 23 Jan 2014 09:40:37 +0000 (01:40 -0800)
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.

Reported-by: Joe Stringer <joestringer@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
datapath/datapath.c
datapath/flow.c

index bb1e282..db89a39 100644 (file)
@@ -166,7 +166,6 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
 {
        struct datapath *dp = container_of(rcu, struct datapath, rcu);
 
-       ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false);
        free_percpu(dp->stats_percpu);
        release_net(ovs_dp_get_net(dp));
        kfree(dp->ports);
@@ -1779,6 +1778,8 @@ static void __dp_destroy(struct datapath *dp)
         */
        ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 
+       ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, true);
+
        call_rcu(&dp->rcu, destroy_dp_rcu);
 }
 
index 29122af..b9ad608 100644 (file)
@@ -502,27 +502,9 @@ static struct flow_table *__flow_tbl_alloc(int new_size)
 
 static void __flow_tbl_destroy(struct flow_table *table)
 {
-       int i;
-
-       if (table->keep_flows)
-               goto skip_flows;
-
-       for (i = 0; i < table->n_buckets; i++) {
-               struct sw_flow *flow;
-               struct hlist_head *head = flex_array_get(table->buckets, i);
-               struct hlist_node *n;
-               int ver = table->node_ver;
-
-               hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
-                       hlist_del(&flow->hash_node[ver]);
-                       ovs_flow_free(flow, false);
-               }
-       }
-
        BUG_ON(!list_empty(table->mask_list));
        kfree(table->mask_list);
 
-skip_flows:
        free_buckets(table->buckets);
        kfree(table);
 }
@@ -554,9 +536,27 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
 
 void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred)
 {
+       int i;
+
        if (!table)
                return;
 
+       if (table->keep_flows)
+               goto skip_flows;
+
+       for (i = 0; i < table->n_buckets; i++) {
+               struct sw_flow *flow;
+               struct hlist_head *head = flex_array_get(table->buckets, i);
+               struct hlist_node *n;
+               int ver = table->node_ver;
+
+               hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
+                       hlist_del_rcu(&flow->hash_node[ver]);
+                       ovs_flow_free(flow, deferred);
+               }
+       }
+
+skip_flows:
        if (deferred)
                call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb);
        else