diff options
Diffstat (limited to 'net/openvswitch')
-rw-r--r-- | net/openvswitch/datapath.c | 23 | ||||
-rw-r--r-- | net/openvswitch/flow_table.c | 88 | ||||
-rw-r--r-- | net/openvswitch/flow_table.h | 2 |
3 files changed, 60 insertions, 53 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index df4692826ead..e9a48baf8551 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); |
@@ -466,6 +466,14 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | |||
466 | 466 | ||
467 | skb_zerocopy(user_skb, skb, skb->len, hlen); | 467 | skb_zerocopy(user_skb, skb, skb->len, hlen); |
468 | 468 | ||
469 | /* Pad OVS_PACKET_ATTR_PACKET if linear copy was performed */ | ||
470 | if (!(dp->user_features & OVS_DP_F_UNALIGNED)) { | ||
471 | size_t plen = NLA_ALIGN(user_skb->len) - user_skb->len; | ||
472 | |||
473 | if (plen > 0) | ||
474 | memset(skb_put(user_skb, plen), 0, plen); | ||
475 | } | ||
476 | |||
469 | ((struct nlmsghdr *) user_skb->data)->nlmsg_len = user_skb->len; | 477 | ((struct nlmsghdr *) user_skb->data)->nlmsg_len = user_skb->len; |
470 | 478 | ||
471 | err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid); | 479 | err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid); |
@@ -852,11 +860,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
852 | goto err_unlock_ovs; | 860 | goto err_unlock_ovs; |
853 | 861 | ||
854 | /* The unmasked key has to be the same for flow updates. */ | 862 | /* The unmasked key has to be the same for flow updates. */ |
855 | error = -EINVAL; | 863 | if (!ovs_flow_cmp_unmasked_key(flow, &match)) |
856 | if (!ovs_flow_cmp_unmasked_key(flow, &match)) { | ||
857 | OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n"); | ||
858 | goto err_unlock_ovs; | 864 | goto err_unlock_ovs; |
859 | } | ||
860 | 865 | ||
861 | /* Update actions. */ | 866 | /* Update actions. */ |
862 | old_acts = ovsl_dereference(flow->sf_acts); | 867 | old_acts = ovsl_dereference(flow->sf_acts); |
@@ -1079,6 +1084,7 @@ static size_t ovs_dp_cmd_msg_size(void) | |||
1079 | msgsize += nla_total_size(IFNAMSIZ); | 1084 | msgsize += nla_total_size(IFNAMSIZ); |
1080 | msgsize += nla_total_size(sizeof(struct ovs_dp_stats)); | 1085 | msgsize += nla_total_size(sizeof(struct ovs_dp_stats)); |
1081 | msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats)); | 1086 | msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats)); |
1087 | msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_USER_FEATURES */ | ||
1082 | 1088 | ||
1083 | return msgsize; | 1089 | return msgsize; |
1084 | } | 1090 | } |
@@ -1279,7 +1285,7 @@ err_destroy_ports_array: | |||
1279 | err_destroy_percpu: | 1285 | err_destroy_percpu: |
1280 | free_percpu(dp->stats_percpu); | 1286 | free_percpu(dp->stats_percpu); |
1281 | err_destroy_table: | 1287 | err_destroy_table: |
1282 | ovs_flow_tbl_destroy(&dp->table); | 1288 | ovs_flow_tbl_destroy(&dp->table, false); |
1283 | err_free_dp: | 1289 | err_free_dp: |
1284 | release_net(ovs_dp_get_net(dp)); | 1290 | release_net(ovs_dp_get_net(dp)); |
1285 | kfree(dp); | 1291 | kfree(dp); |
@@ -1306,10 +1312,13 @@ static void __dp_destroy(struct datapath *dp) | |||
1306 | list_del_rcu(&dp->list_node); | 1312 | list_del_rcu(&dp->list_node); |
1307 | 1313 | ||
1308 | /* OVSP_LOCAL is datapath internal port. We need to make sure that | 1314 | /* OVSP_LOCAL is datapath internal port. We need to make sure that |
1309 | * all port in datapath are destroyed first before freeing datapath. | 1315 | * all ports in datapath are destroyed first before freeing datapath. |
1310 | */ | 1316 | */ |
1311 | ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); | 1317 | ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); |
1312 | 1318 | ||
1319 | /* RCU destroy the flow table */ | ||
1320 | ovs_flow_tbl_destroy(&dp->table, true); | ||
1321 | |||
1313 | call_rcu(&dp->rcu, destroy_dp_rcu); | 1322 | call_rcu(&dp->rcu, destroy_dp_rcu); |
1314 | } | 1323 | } |
1315 | 1324 | ||
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index c58a0fe3c889..3c268b3d71c3 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c | |||
@@ -153,29 +153,29 @@ 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) | ||
157 | { | ||
158 | if (!mask) | ||
159 | return; | ||
160 | |||
161 | BUG_ON(!mask->ref_count); | ||
162 | mask->ref_count--; | ||
163 | |||
164 | if (!mask->ref_count) { | ||
165 | list_del_rcu(&mask->list); | ||
166 | if (deferred) | ||
167 | kfree_rcu(mask, rcu); | ||
168 | else | ||
169 | kfree(mask); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | void ovs_flow_free(struct sw_flow *flow, bool deferred) | 156 | void ovs_flow_free(struct sw_flow *flow, bool deferred) |
174 | { | 157 | { |
175 | if (!flow) | 158 | if (!flow) |
176 | return; | 159 | return; |
177 | 160 | ||
178 | flow_mask_del_ref(flow->mask, deferred); | 161 | if (flow->mask) { |
162 | struct sw_flow_mask *mask = flow->mask; | ||
163 | |||
164 | /* ovs-lock is required to protect mask-refcount and | ||
165 | * mask list. | ||
166 | */ | ||
167 | ASSERT_OVSL(); | ||
168 | BUG_ON(!mask->ref_count); | ||
169 | mask->ref_count--; | ||
170 | |||
171 | if (!mask->ref_count) { | ||
172 | list_del_rcu(&mask->list); | ||
173 | if (deferred) | ||
174 | kfree_rcu(mask, rcu); | ||
175 | else | ||
176 | kfree(mask); | ||
177 | } | ||
178 | } | ||
179 | 179 | ||
180 | if (deferred) | 180 | if (deferred) |
181 | call_rcu(&flow->rcu, rcu_free_flow_callback); | 181 | call_rcu(&flow->rcu, rcu_free_flow_callback); |
@@ -188,26 +188,9 @@ static void free_buckets(struct flex_array *buckets) | |||
188 | flex_array_free(buckets); | 188 | flex_array_free(buckets); |
189 | } | 189 | } |
190 | 190 | ||
191 | |||
191 | static void __table_instance_destroy(struct table_instance *ti) | 192 | static void __table_instance_destroy(struct table_instance *ti) |
192 | { | 193 | { |
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); | 194 | free_buckets(ti->buckets); |
212 | kfree(ti); | 195 | kfree(ti); |
213 | } | 196 | } |
@@ -258,20 +241,38 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) | |||
258 | 241 | ||
259 | static void table_instance_destroy(struct table_instance *ti, bool deferred) | 242 | static void table_instance_destroy(struct table_instance *ti, bool deferred) |
260 | { | 243 | { |
244 | int i; | ||
245 | |||
261 | if (!ti) | 246 | if (!ti) |
262 | return; | 247 | return; |
263 | 248 | ||
249 | if (ti->keep_flows) | ||
250 | goto skip_flows; | ||
251 | |||
252 | for (i = 0; i < ti->n_buckets; i++) { | ||
253 | struct sw_flow *flow; | ||
254 | struct hlist_head *head = flex_array_get(ti->buckets, i); | ||
255 | struct hlist_node *n; | ||
256 | int ver = ti->node_ver; | ||
257 | |||
258 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { | ||
259 | hlist_del_rcu(&flow->hash_node[ver]); | ||
260 | ovs_flow_free(flow, deferred); | ||
261 | } | ||
262 | } | ||
263 | |||
264 | skip_flows: | ||
264 | if (deferred) | 265 | if (deferred) |
265 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); | 266 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); |
266 | else | 267 | else |
267 | __table_instance_destroy(ti); | 268 | __table_instance_destroy(ti); |
268 | } | 269 | } |
269 | 270 | ||
270 | void ovs_flow_tbl_destroy(struct flow_table *table) | 271 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) |
271 | { | 272 | { |
272 | struct table_instance *ti = ovsl_dereference(table->ti); | 273 | struct table_instance *ti = ovsl_dereference(table->ti); |
273 | 274 | ||
274 | table_instance_destroy(ti, false); | 275 | table_instance_destroy(ti, deferred); |
275 | } | 276 | } |
276 | 277 | ||
277 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, | 278 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, |
@@ -504,16 +505,11 @@ static struct sw_flow_mask *mask_alloc(void) | |||
504 | 505 | ||
505 | mask = kmalloc(sizeof(*mask), GFP_KERNEL); | 506 | mask = kmalloc(sizeof(*mask), GFP_KERNEL); |
506 | if (mask) | 507 | if (mask) |
507 | mask->ref_count = 0; | 508 | mask->ref_count = 1; |
508 | 509 | ||
509 | return mask; | 510 | return mask; |
510 | } | 511 | } |
511 | 512 | ||
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, | 513 | static bool mask_equal(const struct sw_flow_mask *a, |
518 | const struct sw_flow_mask *b) | 514 | const struct sw_flow_mask *b) |
519 | { | 515 | { |
@@ -554,9 +550,11 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, | |||
554 | mask->key = new->key; | 550 | mask->key = new->key; |
555 | mask->range = new->range; | 551 | mask->range = new->range; |
556 | list_add_rcu(&mask->list, &tbl->mask_list); | 552 | list_add_rcu(&mask->list, &tbl->mask_list); |
553 | } else { | ||
554 | BUG_ON(!mask->ref_count); | ||
555 | mask->ref_count++; | ||
557 | } | 556 | } |
558 | 557 | ||
559 | mask_add_ref(mask); | ||
560 | flow->mask = mask; | 558 | flow->mask = mask; |
561 | return 0; | 559 | return 0; |
562 | } | 560 | } |
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, |