diff options
| -rw-r--r-- | net/openvswitch/datapath.c | 9 | ||||
| -rw-r--r-- | net/openvswitch/flow_table.c | 84 | ||||
| -rw-r--r-- | net/openvswitch/flow_table.h | 2 |
3 files changed, 47 insertions, 48 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index d1a73a6102f8..e1b337e0bf4d 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
| @@ -55,6 +55,7 @@ | |||
| 55 | 55 | ||
| 56 | #include "datapath.h" | 56 | #include "datapath.h" |
| 57 | #include "flow.h" | 57 | #include "flow.h" |
| 58 | #include "flow_table.h" | ||
| 58 | #include "flow_netlink.h" | 59 | #include "flow_netlink.h" |
| 59 | #include "vport-internal_dev.h" | 60 | #include "vport-internal_dev.h" |
| 60 | #include "vport-netdev.h" | 61 | #include "vport-netdev.h" |
| @@ -160,7 +161,6 @@ static void destroy_dp_rcu(struct rcu_head *rcu) | |||
| 160 | { | 161 | { |
| 161 | struct datapath *dp = container_of(rcu, struct datapath, rcu); | 162 | struct datapath *dp = container_of(rcu, struct datapath, rcu); |
| 162 | 163 | ||
| 163 | ovs_flow_tbl_destroy(&dp->table); | ||
| 164 | free_percpu(dp->stats_percpu); | 164 | free_percpu(dp->stats_percpu); |
| 165 | release_net(ovs_dp_get_net(dp)); | 165 | release_net(ovs_dp_get_net(dp)); |
| 166 | kfree(dp->ports); | 166 | kfree(dp->ports); |
| @@ -1287,7 +1287,7 @@ err_destroy_ports_array: | |||
| 1287 | err_destroy_percpu: | 1287 | err_destroy_percpu: |
| 1288 | free_percpu(dp->stats_percpu); | 1288 | free_percpu(dp->stats_percpu); |
| 1289 | err_destroy_table: | 1289 | err_destroy_table: |
| 1290 | ovs_flow_tbl_destroy(&dp->table); | 1290 | ovs_flow_tbl_destroy(&dp->table, false); |
| 1291 | err_free_dp: | 1291 | err_free_dp: |
| 1292 | release_net(ovs_dp_get_net(dp)); | 1292 | release_net(ovs_dp_get_net(dp)); |
| 1293 | kfree(dp); | 1293 | kfree(dp); |
| @@ -1314,10 +1314,13 @@ static void __dp_destroy(struct datapath *dp) | |||
| 1314 | list_del_rcu(&dp->list_node); | 1314 | list_del_rcu(&dp->list_node); |
| 1315 | 1315 | ||
| 1316 | /* OVSP_LOCAL is datapath internal port. We need to make sure that | 1316 | /* OVSP_LOCAL is datapath internal port. We need to make sure that |
| 1317 | * all port in datapath are destroyed first before freeing datapath. | 1317 | * all ports in datapath are destroyed first before freeing datapath. |
| 1318 | */ | 1318 | */ |
| 1319 | ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); | 1319 | ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); |
| 1320 | 1320 | ||
| 1321 | /* RCU destroy the flow table */ | ||
| 1322 | ovs_flow_tbl_destroy(&dp->table, true); | ||
| 1323 | |||
| 1321 | call_rcu(&dp->rcu, destroy_dp_rcu); | 1324 | call_rcu(&dp->rcu, destroy_dp_rcu); |
| 1322 | } | 1325 | } |
| 1323 | 1326 | ||
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index c58a0fe3c889..bd14052ed342 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c | |||
| @@ -153,29 +153,27 @@ static void rcu_free_flow_callback(struct rcu_head *rcu) | |||
| 153 | flow_free(flow); | 153 | flow_free(flow); |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred) | 156 | void ovs_flow_free(struct sw_flow *flow, bool deferred) |
| 157 | { | 157 | { |
| 158 | if (!mask) | 158 | if (!flow) |
| 159 | return; | 159 | return; |
| 160 | 160 | ||
| 161 | BUG_ON(!mask->ref_count); | 161 | ASSERT_OVSL(); |
| 162 | mask->ref_count--; | ||
| 163 | 162 | ||
| 164 | if (!mask->ref_count) { | 163 | if (flow->mask) { |
| 165 | list_del_rcu(&mask->list); | 164 | struct sw_flow_mask *mask = flow->mask; |
| 166 | if (deferred) | ||
| 167 | kfree_rcu(mask, rcu); | ||
| 168 | else | ||
| 169 | kfree(mask); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | 165 | ||
| 173 | void ovs_flow_free(struct sw_flow *flow, bool deferred) | 166 | BUG_ON(!mask->ref_count); |
| 174 | { | 167 | mask->ref_count--; |
| 175 | if (!flow) | ||
| 176 | return; | ||
| 177 | 168 | ||
| 178 | flow_mask_del_ref(flow->mask, deferred); | 169 | if (!mask->ref_count) { |
| 170 | list_del_rcu(&mask->list); | ||
| 171 | if (deferred) | ||
| 172 | kfree_rcu(mask, rcu); | ||
| 173 | else | ||
| 174 | kfree(mask); | ||
| 175 | } | ||
| 176 | } | ||
| 179 | 177 | ||
| 180 | if (deferred) | 178 | if (deferred) |
| 181 | call_rcu(&flow->rcu, rcu_free_flow_callback); | 179 | call_rcu(&flow->rcu, rcu_free_flow_callback); |
| @@ -188,26 +186,9 @@ static void free_buckets(struct flex_array *buckets) | |||
| 188 | flex_array_free(buckets); | 186 | flex_array_free(buckets); |
| 189 | } | 187 | } |
| 190 | 188 | ||
| 189 | |||
| 191 | static void __table_instance_destroy(struct table_instance *ti) | 190 | static void __table_instance_destroy(struct table_instance *ti) |
| 192 | { | 191 | { |
| 193 | int i; | ||
| 194 | |||
| 195 | if (ti->keep_flows) | ||
| 196 | goto skip_flows; | ||
| 197 | |||
| 198 | for (i = 0; i < ti->n_buckets; i++) { | ||
| 199 | struct sw_flow *flow; | ||
| 200 | struct hlist_head *head = flex_array_get(ti->buckets, i); | ||
| 201 | struct hlist_node *n; | ||
| 202 | int ver = ti->node_ver; | ||
| 203 | |||
| 204 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { | ||
| 205 | hlist_del(&flow->hash_node[ver]); | ||
| 206 | ovs_flow_free(flow, false); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | skip_flows: | ||
| 211 | free_buckets(ti->buckets); | 192 | free_buckets(ti->buckets); |
| 212 | kfree(ti); | 193 | kfree(ti); |
| 213 | } | 194 | } |
| @@ -258,20 +239,38 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) | |||
| 258 | 239 | ||
| 259 | static void table_instance_destroy(struct table_instance *ti, bool deferred) | 240 | static void table_instance_destroy(struct table_instance *ti, bool deferred) |
| 260 | { | 241 | { |
| 242 | int i; | ||
| 243 | |||
| 261 | if (!ti) | 244 | if (!ti) |
| 262 | return; | 245 | return; |
| 263 | 246 | ||
| 247 | if (ti->keep_flows) | ||
| 248 | goto skip_flows; | ||
| 249 | |||
| 250 | for (i = 0; i < ti->n_buckets; i++) { | ||
| 251 | struct sw_flow *flow; | ||
| 252 | struct hlist_head *head = flex_array_get(ti->buckets, i); | ||
| 253 | struct hlist_node *n; | ||
| 254 | int ver = ti->node_ver; | ||
| 255 | |||
| 256 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { | ||
| 257 | hlist_del_rcu(&flow->hash_node[ver]); | ||
| 258 | ovs_flow_free(flow, deferred); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | skip_flows: | ||
| 264 | if (deferred) | 263 | if (deferred) |
| 265 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); | 264 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); |
| 266 | else | 265 | else |
| 267 | __table_instance_destroy(ti); | 266 | __table_instance_destroy(ti); |
| 268 | } | 267 | } |
| 269 | 268 | ||
| 270 | void ovs_flow_tbl_destroy(struct flow_table *table) | 269 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) |
| 271 | { | 270 | { |
| 272 | struct table_instance *ti = ovsl_dereference(table->ti); | 271 | struct table_instance *ti = ovsl_dereference(table->ti); |
| 273 | 272 | ||
| 274 | table_instance_destroy(ti, false); | 273 | table_instance_destroy(ti, deferred); |
| 275 | } | 274 | } |
| 276 | 275 | ||
| 277 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, | 276 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, |
| @@ -504,16 +503,11 @@ static struct sw_flow_mask *mask_alloc(void) | |||
| 504 | 503 | ||
| 505 | mask = kmalloc(sizeof(*mask), GFP_KERNEL); | 504 | mask = kmalloc(sizeof(*mask), GFP_KERNEL); |
| 506 | if (mask) | 505 | if (mask) |
| 507 | mask->ref_count = 0; | 506 | mask->ref_count = 1; |
| 508 | 507 | ||
| 509 | return mask; | 508 | return mask; |
| 510 | } | 509 | } |
| 511 | 510 | ||
| 512 | static void mask_add_ref(struct sw_flow_mask *mask) | ||
| 513 | { | ||
| 514 | mask->ref_count++; | ||
| 515 | } | ||
| 516 | |||
| 517 | static bool mask_equal(const struct sw_flow_mask *a, | 511 | static bool mask_equal(const struct sw_flow_mask *a, |
| 518 | const struct sw_flow_mask *b) | 512 | const struct sw_flow_mask *b) |
| 519 | { | 513 | { |
| @@ -554,9 +548,11 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, | |||
| 554 | mask->key = new->key; | 548 | mask->key = new->key; |
| 555 | mask->range = new->range; | 549 | mask->range = new->range; |
| 556 | list_add_rcu(&mask->list, &tbl->mask_list); | 550 | list_add_rcu(&mask->list, &tbl->mask_list); |
| 551 | } else { | ||
| 552 | BUG_ON(!mask->ref_count); | ||
| 553 | mask->ref_count++; | ||
| 557 | } | 554 | } |
| 558 | 555 | ||
| 559 | mask_add_ref(mask); | ||
| 560 | flow->mask = mask; | 556 | flow->mask = mask; |
| 561 | return 0; | 557 | return 0; |
| 562 | } | 558 | } |
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h index 1996e34c0fd8..baaeb101924d 100644 --- a/net/openvswitch/flow_table.h +++ b/net/openvswitch/flow_table.h | |||
| @@ -60,7 +60,7 @@ void ovs_flow_free(struct sw_flow *, bool deferred); | |||
| 60 | 60 | ||
| 61 | int ovs_flow_tbl_init(struct flow_table *); | 61 | int ovs_flow_tbl_init(struct flow_table *); |
| 62 | int ovs_flow_tbl_count(struct flow_table *table); | 62 | int ovs_flow_tbl_count(struct flow_table *table); |
| 63 | void ovs_flow_tbl_destroy(struct flow_table *table); | 63 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred); |
| 64 | int ovs_flow_tbl_flush(struct flow_table *flow_table); | 64 | int ovs_flow_tbl_flush(struct flow_table *flow_table); |
| 65 | 65 | ||
| 66 | int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, | 66 | int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, |
