Merge ~fnordahl/ubuntu/+source/ovn:ubuntu/groovy into ~ubuntu-server-dev/ubuntu/+source/ovn:ubuntu/groovy
- Git
- lp:~fnordahl/ubuntu/+source/ovn
- ubuntu/groovy
- Merge into ubuntu/groovy
Proposed by
Frode Nordahl
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | affb90c4a7bd744ef8c4f9cb30af07ebe6dab223 | ||||||||||||
Proposed branch: | ~fnordahl/ubuntu/+source/ovn:ubuntu/groovy | ||||||||||||
Merge into: | ~ubuntu-server-dev/ubuntu/+source/ovn:ubuntu/groovy | ||||||||||||
Diff against target: |
4356 lines (+4230/-0) (has conflicts) 20 files modified
debian/changelog (+16/-0) debian/patches/ovn-ctl-cluster-db-upgrades.patch (+63/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-01.patch (+63/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-02.patch (+889/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-03.patch (+718/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-04.patch (+154/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-05.patch (+481/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-06.patch (+109/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-07.patch (+233/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-08.patch (+90/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-09.patch (+216/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-10.patch (+77/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-11.patch (+215/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-12.patch (+40/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-13.patch (+416/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-14.patch (+123/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-15.patch (+66/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-16.patch (+132/-0) debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-17.patch (+108/-0) debian/patches/series (+21/-0) Conflict in debian/changelog Conflict in debian/patches/series |
||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Server Developers | Pending | ||
Review via email: mp+395221@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/debian/changelog b/debian/changelog |
2 | index ce39ab6..f41aee4 100644 |
3 | --- a/debian/changelog |
4 | +++ b/debian/changelog |
5 | @@ -1,7 +1,23 @@ |
6 | +<<<<<<< debian/changelog |
7 | ovn (20.06.2-0ubuntu1.1) groovy; urgency=medium |
8 | |
9 | * d/p/ovn-controller-ofctrl-probe-interval.patch: Cherry pick |
10 | fix to disable ofctrl probe by default (LP: #1899369). |
11 | +======= |
12 | +ovn (20.06.2-0ubuntu1.2) groovy; urgency=medium |
13 | + |
14 | + * d/p/ovn-ctl-cluster-db-upgrades.patch: Cherry pick fix for upgrading |
15 | + database schema of clustered databases on package upgrade (LP: #1907081) |
16 | + * d/p/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-*: Cherry |
17 | + pick fixes for predictable resolution for conflicting flow actions. |
18 | + (LP: #1906922) |
19 | + |
20 | + -- Frode Nordahl <frode.nordahl@canonical.com> Fri, 27 Nov 2020 09:16:07 +0000 |
21 | + |
22 | +ovn (20.06.2-0ubuntu1.1) groovy; urgency=medium |
23 | + |
24 | + * d/p: ovn-controller-ofctrl-probe-interval.patch (LP: #1899369). |
25 | +>>>>>>> debian/changelog |
26 | |
27 | -- Frode Nordahl <frode.nordahl@canonical.com> Fri, 06 Nov 2020 08:21:03 +0000 |
28 | |
29 | diff --git a/debian/patches/ovn-ctl-cluster-db-upgrades.patch b/debian/patches/ovn-ctl-cluster-db-upgrades.patch |
30 | new file mode 100644 |
31 | index 0000000..062ced3 |
32 | --- /dev/null |
33 | +++ b/debian/patches/ovn-ctl-cluster-db-upgrades.patch |
34 | @@ -0,0 +1,63 @@ |
35 | +Description: Cherry-pick fix applied to master |
36 | +Origin: https://github.com/ovn-org/ovn/commit/67e2f386cc838d0b0f9b4b5da7fe611e1113b70c |
37 | +Applied-Upstream: commit: 67e2f386cc838d0b0f9b4b5da7fe611e1113b70c |
38 | + |
39 | +From 67e2f386cc838d0b0f9b4b5da7fe611e1113b70c Mon Sep 17 00:00:00 2001 |
40 | +From: Numan Siddique <numans@ovn.org> |
41 | +Date: Wed, 9 Sep 2020 12:49:39 +0530 |
42 | +Subject: [PATCH] ovn-ctl: Handle cluster db upgrades for run_(nb/sb)_ovsdb |
43 | + |
44 | +when ovn-ctl run_(nb_sb)_ovsdb is called, the ovsdb-server is started without |
45 | +passing --detach and --monoitor and the process is exec'd. |
46 | + |
47 | +For cluster mode, upgrade_cluster is never called and hence the dbs are not upraded |
48 | +to new schema. CMS has to handle the db upgrade separately. |
49 | + |
50 | +This patch handles the db upgrade by starting ovsdb-server in background and then |
51 | +waits on ovsdb-server to complete. |
52 | + |
53 | +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1868392 |
54 | +Acked-by: Mark Michelson <mmichels@redhat.com> |
55 | +Signed-off-by: Numan Siddique <numans@ovn.org> |
56 | +--- |
57 | + utilities/ovn-ctl | 20 +++++++++++++++++++- |
58 | + 1 file changed, 19 insertions(+), 1 deletion(-) |
59 | + |
60 | +Index: ovn-20.06.2/utilities/ovn-ctl |
61 | +=================================================================== |
62 | +--- ovn-20.06.2.orig/utilities/ovn-ctl |
63 | ++++ ovn-20.06.2/utilities/ovn-ctl |
64 | +@@ -288,7 +288,21 @@ $cluster_remote_port |
65 | + set "$@" --sync-from=`cat $active_conf_file` |
66 | + fi |
67 | + |
68 | +- "$@" "$file" |
69 | ++ local run_ovsdb_in_bg="no" |
70 | ++ local process_id= |
71 | ++ if test X$detach = Xno && test $mode = cluster && test -z "$cluster_remote_addr" ; then |
72 | ++ # When detach is no (for run_nb_ovsdb/run_sb_ovsdb commands) |
73 | ++ # we want to run ovsdb-server in background rather than running it in |
74 | ++ # foreground so that the OVN dbs are upgraded for the cluster mode. |
75 | ++ # Otherwise, CMS has to take the responsibility of upgrading the dbs. |
76 | ++ # Note: We run only the ovsdb-server in backgroud which created the |
77 | ++ # cluster (i.e cluster_remote_addr is not set.). |
78 | ++ run_ovsdb_in_bg="yes" |
79 | ++ "$@" $file & |
80 | ++ process_id=$! |
81 | ++ else |
82 | ++ "$@" "$file" |
83 | ++ fi |
84 | + |
85 | + # Initialize the database if it's NOT joining a cluster. |
86 | + if test -z "$cluster_remote_addr"; then |
87 | +@@ -298,6 +312,10 @@ $cluster_remote_port |
88 | + if test $mode = cluster; then |
89 | + upgrade_cluster "$schema" "unix:$sock" |
90 | + fi |
91 | ++ |
92 | ++ if test $run_ovsdb_in_bg = yes; then |
93 | ++ wait $process_id |
94 | ++ fi |
95 | + } |
96 | + |
97 | + start_nb_ovsdb() { |
98 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-01.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-01.patch |
99 | new file mode 100644 |
100 | index 0000000..8bd1e17 |
101 | --- /dev/null |
102 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-01.patch |
103 | @@ -0,0 +1,63 @@ |
104 | +Description: Cherry-pick fix applied to master and branch-20.12 |
105 | +Origin: https://github.com/ovn-org/ovn/commit/69b56114bd253531d2dab7336a99d1c7563eb7be |
106 | +Applied-Upstream: commit: 69b56114bd253531d2dab7336a99d1c7563eb7be |
107 | + |
108 | +From 6d5adcd482b8f525e383c13e714826165d86d33f Mon Sep 17 00:00:00 2001 |
109 | +From: Han Zhou <hzhou@ovn.org> |
110 | +Date: Thu, 13 Aug 2020 12:06:09 -0700 |
111 | +Subject: [PATCH 01/15] ofctrl: change ofctrl_dup_flow to module internal |
112 | + function |
113 | + |
114 | +Acked-by: Mark Michelson <mmichels@redhat.com> |
115 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
116 | +--- |
117 | + controller/ofctrl.c | 8 +++++--- |
118 | + controller/ofctrl.h | 2 -- |
119 | + 2 files changed, 5 insertions(+), 5 deletions(-) |
120 | + |
121 | +Index: ovn-20.06.2/controller/ofctrl.c |
122 | +=================================================================== |
123 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
124 | ++++ ovn-20.06.2/controller/ofctrl.c |
125 | +@@ -169,6 +169,8 @@ static struct ofpbuf *encode_meter_mod(c |
126 | + |
127 | + static void ovn_installed_flow_table_clear(void); |
128 | + static void ovn_installed_flow_table_destroy(void); |
129 | ++static struct ovn_flow *ovn_flow_dup(struct ovn_flow *source); |
130 | ++ |
131 | + |
132 | + static void ofctrl_recv(const struct ofp_header *, enum ofptype); |
133 | + |
134 | +@@ -787,8 +789,8 @@ ovn_flow_match_hash(const struct ovn_flo |
135 | + } |
136 | + |
137 | + /* Duplicate an ovn_flow structure. */ |
138 | +-struct ovn_flow * |
139 | +-ofctrl_dup_flow(struct ovn_flow *src) |
140 | ++static struct ovn_flow * |
141 | ++ovn_flow_dup(struct ovn_flow *src) |
142 | + { |
143 | + struct ovn_flow *dst = xmalloc(sizeof *dst); |
144 | + dst->table_id = src->table_id; |
145 | +@@ -1291,7 +1293,7 @@ ofctrl_put(struct ovn_desired_flow_table |
146 | + ovn_flow_log(d, "adding installed"); |
147 | + |
148 | + /* Copy 'd' from 'flow_table' to installed_flows. */ |
149 | +- struct ovn_flow *new_node = ofctrl_dup_flow(d); |
150 | ++ struct ovn_flow *new_node = ovn_flow_dup(d); |
151 | + hmap_insert(&installed_flows, &new_node->match_hmap_node, |
152 | + new_node->match_hmap_node.hash); |
153 | + } |
154 | +Index: ovn-20.06.2/controller/ofctrl.h |
155 | +=================================================================== |
156 | +--- ovn-20.06.2.orig/controller/ofctrl.h |
157 | ++++ ovn-20.06.2/controller/ofctrl.h |
158 | +@@ -56,8 +56,6 @@ void ofctrl_wait(void); |
159 | + void ofctrl_destroy(void); |
160 | + int64_t ofctrl_get_cur_cfg(void); |
161 | + |
162 | +-struct ovn_flow *ofctrl_dup_flow(struct ovn_flow *source); |
163 | +- |
164 | + void ofctrl_ct_flush_zone(uint16_t zone_id); |
165 | + |
166 | + char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, |
167 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-02.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-02.patch |
168 | new file mode 100644 |
169 | index 0000000..32d50dd |
170 | --- /dev/null |
171 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-02.patch |
172 | @@ -0,0 +1,889 @@ |
173 | +Description: Cherry-pick fix applied to master and branch-20.12 |
174 | +Origin: https://github.com/ovn-org/ovn/commit/580aea72e26f53deb72a0d0f5516327a4af7c8fe |
175 | +Applied-Upstream: commit: 580aea72e26f53deb72a0d0f5516327a4af7c8fe |
176 | + |
177 | +From 273c8cad7bce2a2ff8569e9128f266bc0000221b Mon Sep 17 00:00:00 2001 |
178 | +From: Han Zhou <hzhou@ovn.org> |
179 | +Date: Thu, 20 Aug 2020 22:55:39 -0700 |
180 | +Subject: [PATCH 02/15] ovn-controller: Fix conjunction handling with |
181 | + incremental processing. |
182 | + |
183 | +When translating lflows to OVS flows, different lflows can refer to same OVS |
184 | +flow as a result of calling ofctrl_add_or_append_flow(), particularly for |
185 | +conjunction combinding. However, the implementation doesn't work with |
186 | +incremental processing, because when any of the lflows are removed, we rely on |
187 | +the lflow's uuid to remove the OVS flow in the desired flow table. Currently |
188 | +only one single lflow uuid is maintained in the desired flow, so removing one |
189 | +of the lflows that references to the same desired flow resulted in wrong |
190 | +behavior: either removing flows that are used by other lflows, or the existing |
191 | +flows are not updated (part of the conjunction actions should be removed from |
192 | +the flow). |
193 | + |
194 | +To solve the problem, this patch maintains the cross reference (M:N) between |
195 | +lflows (and other sb objects) and desired flows, and handles the flow removal |
196 | +and updates with a flood-removal and re-add approach. |
197 | + |
198 | +Fixes: e659bab31a9 ("Combine conjunctions with identical matches into one flow.") |
199 | +Cc: Mark Michelson <mmichels@redhat.com> |
200 | +Acked-by: Mark Michelson <mmichels@redhat.com> |
201 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
202 | +--- |
203 | + controller/lflow.c | 103 ++++++++----- |
204 | + controller/ofctrl.c | 351 +++++++++++++++++++++++++++++++++++--------- |
205 | + controller/ofctrl.h | 31 +++- |
206 | + tests/ovn.at | 91 ++++++++++++ |
207 | + 4 files changed, 470 insertions(+), 106 deletions(-) |
208 | + |
209 | +Index: ovn-20.06.2/controller/lflow.c |
210 | +=================================================================== |
211 | +--- ovn-20.06.2.orig/controller/lflow.c |
212 | ++++ ovn-20.06.2/controller/lflow.c |
213 | +@@ -342,41 +342,56 @@ lflow_handle_changed_flows(struct lflow_ |
214 | + struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts); |
215 | + nd_ra_opts_init(&nd_ra_opts); |
216 | + |
217 | +- /* Handle removed flows first, and then other flows, so that when |
218 | +- * the flows being added and removed have same match conditions |
219 | +- * can be processed in the proper order */ |
220 | ++ struct controller_event_options controller_event_opts; |
221 | ++ controller_event_opts_init(&controller_event_opts); |
222 | ++ |
223 | ++ /* Handle flow removing first (for deleted or updated lflows), and then |
224 | ++ * handle reprocessing or adding flows, so that when the flows being |
225 | ++ * removed and added with same match conditions can be processed in the |
226 | ++ * proper order */ |
227 | ++ |
228 | ++ struct hmap flood_remove_nodes = HMAP_INITIALIZER(&flood_remove_nodes); |
229 | ++ struct ofctrl_flood_remove_node *ofrn, *next; |
230 | + SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, |
231 | + l_ctx_in->logical_flow_table) { |
232 | +- /* Remove any flows that should be removed. */ |
233 | +- if (sbrec_logical_flow_is_deleted(lflow)) { |
234 | +- VLOG_DBG("handle deleted lflow "UUID_FMT, |
235 | ++ if (!sbrec_logical_flow_is_new(lflow)) { |
236 | ++ VLOG_DBG("delete lflow "UUID_FMT, |
237 | + UUID_ARGS(&lflow->header_.uuid)); |
238 | +- ofctrl_remove_flows(l_ctx_out->flow_table, &lflow->header_.uuid); |
239 | +- /* Delete entries from lflow resource reference. */ |
240 | +- lflow_resource_destroy_lflow(l_ctx_out->lfrr, |
241 | +- &lflow->header_.uuid); |
242 | ++ ofrn = xmalloc(sizeof *ofrn); |
243 | ++ ofrn->sb_uuid = lflow->header_.uuid; |
244 | ++ hmap_insert(&flood_remove_nodes, &ofrn->hmap_node, |
245 | ++ uuid_hash(&ofrn->sb_uuid)); |
246 | + } |
247 | + } |
248 | ++ ofctrl_flood_remove_flows(l_ctx_out->flow_table, &flood_remove_nodes); |
249 | ++ HMAP_FOR_EACH (ofrn, hmap_node, &flood_remove_nodes) { |
250 | ++ /* Delete entries from lflow resource reference. */ |
251 | ++ lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->sb_uuid); |
252 | ++ /* Reprocessing the lflow if the sb record is not deleted. */ |
253 | ++ lflow = sbrec_logical_flow_table_get_for_uuid( |
254 | ++ l_ctx_in->logical_flow_table, &ofrn->sb_uuid); |
255 | ++ if (lflow) { |
256 | ++ VLOG_DBG("re-add lflow "UUID_FMT, |
257 | ++ UUID_ARGS(&lflow->header_.uuid)); |
258 | ++ if (!consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts, |
259 | ++ &nd_ra_opts, &controller_event_opts, |
260 | ++ l_ctx_in, l_ctx_out)) { |
261 | ++ ret = false; |
262 | ++ break; |
263 | ++ } |
264 | ++ } |
265 | ++ } |
266 | ++ HMAP_FOR_EACH_SAFE (ofrn, next, hmap_node, &flood_remove_nodes) { |
267 | ++ hmap_remove(&flood_remove_nodes, &ofrn->hmap_node); |
268 | ++ free(ofrn); |
269 | ++ } |
270 | ++ hmap_destroy(&flood_remove_nodes); |
271 | + |
272 | +- struct controller_event_options controller_event_opts; |
273 | +- controller_event_opts_init(&controller_event_opts); |
274 | +- |
275 | ++ /* Now handle new lflows only. */ |
276 | + SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, |
277 | + l_ctx_in->logical_flow_table) { |
278 | +- if (!sbrec_logical_flow_is_deleted(lflow)) { |
279 | +- /* Now, add/modify existing flows. If the logical |
280 | +- * flow is a modification, just remove the flows |
281 | +- * for this row, and then add new flows. */ |
282 | +- if (!sbrec_logical_flow_is_new(lflow)) { |
283 | +- VLOG_DBG("handle updated lflow "UUID_FMT, |
284 | +- UUID_ARGS(&lflow->header_.uuid)); |
285 | +- ofctrl_remove_flows(l_ctx_out->flow_table, |
286 | +- &lflow->header_.uuid); |
287 | +- /* Delete entries from lflow resource reference. */ |
288 | +- lflow_resource_destroy_lflow(l_ctx_out->lfrr, |
289 | +- &lflow->header_.uuid); |
290 | +- } |
291 | +- VLOG_DBG("handle new lflow "UUID_FMT, |
292 | ++ if (sbrec_logical_flow_is_new(lflow)) { |
293 | ++ VLOG_DBG("add lflow "UUID_FMT, |
294 | + UUID_ARGS(&lflow->header_.uuid)); |
295 | + if (!consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts, |
296 | + &nd_ra_opts, &controller_event_opts, |
297 | +@@ -420,7 +435,6 @@ lflow_handle_changed_ref(enum ref_type r |
298 | + * when reparsing the lflows. */ |
299 | + LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) { |
300 | + ovs_list_remove(&lrln->lflow_list); |
301 | +- lflow_resource_destroy_lflow(l_ctx_out->lfrr, &lrln->lflow_uuid); |
302 | + } |
303 | + |
304 | + struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts); |
305 | +@@ -446,22 +460,32 @@ lflow_handle_changed_ref(enum ref_type r |
306 | + controller_event_opts_init(&controller_event_opts); |
307 | + |
308 | + /* Re-parse the related lflows. */ |
309 | ++ /* Firstly, flood remove the flows from desired flow table. */ |
310 | ++ struct hmap flood_remove_nodes = HMAP_INITIALIZER(&flood_remove_nodes); |
311 | ++ struct ofctrl_flood_remove_node *ofrn, *ofrn_next; |
312 | + LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) { |
313 | ++ VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d," |
314 | ++ " name: %s.", |
315 | ++ UUID_ARGS(&lrln->lflow_uuid), |
316 | ++ ref_type, ref_name); |
317 | ++ ofctrl_flood_remove_add_node(&flood_remove_nodes, &lrln->lflow_uuid); |
318 | ++ } |
319 | ++ ofctrl_flood_remove_flows(l_ctx_out->flow_table, &flood_remove_nodes); |
320 | ++ |
321 | ++ /* Secondly, for each lflow that is actually removed, reprocessing it. */ |
322 | ++ HMAP_FOR_EACH (ofrn, hmap_node, &flood_remove_nodes) { |
323 | ++ lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->sb_uuid); |
324 | ++ |
325 | + const struct sbrec_logical_flow *lflow = |
326 | + sbrec_logical_flow_table_get_for_uuid(l_ctx_in->logical_flow_table, |
327 | +- &lrln->lflow_uuid); |
328 | ++ &ofrn->sb_uuid); |
329 | + if (!lflow) { |
330 | +- VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d," |
331 | +- " name: %s - not found.", |
332 | +- UUID_ARGS(&lrln->lflow_uuid), |
333 | ++ VLOG_DBG("lflow "UUID_FMT" not found while reprocessing for" |
334 | ++ " resource type: %d, name: %s.", |
335 | ++ UUID_ARGS(&ofrn->sb_uuid), |
336 | + ref_type, ref_name); |
337 | + continue; |
338 | + } |
339 | +- VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d," |
340 | +- " name: %s.", |
341 | +- UUID_ARGS(&lrln->lflow_uuid), |
342 | +- ref_type, ref_name); |
343 | +- ofctrl_remove_flows(l_ctx_out->flow_table, &lrln->lflow_uuid); |
344 | + |
345 | + if (!consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts, |
346 | + &nd_ra_opts, &controller_event_opts, |
347 | +@@ -471,6 +495,11 @@ lflow_handle_changed_ref(enum ref_type r |
348 | + } |
349 | + *changed = true; |
350 | + } |
351 | ++ HMAP_FOR_EACH_SAFE (ofrn, ofrn_next, hmap_node, &flood_remove_nodes) { |
352 | ++ hmap_remove(&flood_remove_nodes, &ofrn->hmap_node); |
353 | ++ free(ofrn); |
354 | ++ } |
355 | ++ hmap_destroy(&flood_remove_nodes); |
356 | + |
357 | + LIST_FOR_EACH_SAFE (lrln, next, ref_list, &rlfn->ref_lflow_head) { |
358 | + ovs_list_remove(&lrln->ref_list); |
359 | +Index: ovn-20.06.2/controller/ofctrl.c |
360 | +=================================================================== |
361 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
362 | ++++ ovn-20.06.2/controller/ofctrl.c |
363 | +@@ -51,11 +51,45 @@ |
364 | + |
365 | + VLOG_DEFINE_THIS_MODULE(ofctrl); |
366 | + |
367 | +-/* An OpenFlow flow. */ |
368 | ++/* An OpenFlow flow. |
369 | ++ * |
370 | ++ * Links are maintained between desired flows and SB data. The relationship |
371 | ++ * is M to N. The struct sb_flow_ref is used to link a pair of desired flow |
372 | ++ * and SB UUID. The below diagram depicts the data structure. |
373 | ++ * |
374 | ++ * SB UUIDs |
375 | ++ * +-----+-----+-----+-----+-----+-----+-----+ |
376 | ++ * | | | | | | | | |
377 | ++ * +--+--+--+--+--+--+-----+--+--+--+--+--+--+ |
378 | ++ * | | | | | | |
379 | ++ * Desired Flows | | | | | | |
380 | ++ * +----+ +-+-+ | +-+-+ | +-+-+ | |
381 | ++ * | +-------+ +-------+ +-------------+ | | |
382 | ++ * +----+ +---+ | +-+-+ | +---+ | |
383 | ++ * | | | | | | |
384 | ++ * +----+ | | +-+-+ | |
385 | ++ * | +-------------------------------+ | | |
386 | ++ * +----+ +---+ | +---+ | |
387 | ++ * | +-------------+ | | | |
388 | ++ * +----+ +---+ | | |
389 | ++ * | | | | |
390 | ++ * +----+ +-+-+ +-+-+ |
391 | ++ * | +-------------------+ +-------------------+ | |
392 | ++ * +----+ +---+ +---+ |
393 | ++ * | | |
394 | ++ * +----+ |
395 | ++ * |
396 | ++ * The links are updated whenever there is a change in desired flows, which is |
397 | ++ * usually triggered by a SB data change in I-P engine. |
398 | ++ */ |
399 | + struct ovn_flow { |
400 | + struct hmap_node match_hmap_node; /* For match based hashing. */ |
401 | +- struct hindex_node uuid_hindex_node; /* For uuid based hashing. */ |
402 | + struct ovs_list list_node; /* For handling lists of flows. */ |
403 | ++ struct ovs_list references; /* A list of struct sb_flow_ref nodes, which |
404 | ++ references this flow. (There are cases |
405 | ++ that multiple SB entities share the same |
406 | ++ desired OpenFlow flow, e.g. when |
407 | ++ conjunction is used.) */ |
408 | + |
409 | + /* Key. */ |
410 | + uint8_t table_id; |
411 | +@@ -63,21 +97,34 @@ struct ovn_flow { |
412 | + struct minimatch match; |
413 | + |
414 | + /* Data. */ |
415 | +- struct uuid sb_uuid; |
416 | + struct ofpact *ofpacts; |
417 | + size_t ofpacts_len; |
418 | + uint64_t cookie; |
419 | + }; |
420 | + |
421 | ++struct sb_to_flow { |
422 | ++ struct hmap_node hmap_node; /* Node in |
423 | ++ ovn_desired_flow_table.uuid_flow_table. */ |
424 | ++ struct uuid sb_uuid; |
425 | ++ struct ovs_list flows; /* A list of struct sb_flow_ref nodes that are |
426 | ++ referenced by the sb_uuid. */ |
427 | ++}; |
428 | ++ |
429 | ++struct sb_flow_ref { |
430 | ++ struct ovs_list sb_list; /* List node in ovn_flow.references. */ |
431 | ++ struct ovs_list flow_list; /* List node in sb_to_flow.ovn_flows. */ |
432 | ++ struct ovn_flow *flow; |
433 | ++ struct uuid sb_uuid; |
434 | ++}; |
435 | ++ |
436 | + static struct ovn_flow *ovn_flow_alloc(uint8_t table_id, uint16_t priority, |
437 | + uint64_t cookie, |
438 | + const struct match *match, |
439 | +- const struct ofpbuf *actions, |
440 | +- const struct uuid *sb_uuid); |
441 | ++ const struct ofpbuf *actions); |
442 | + static uint32_t ovn_flow_match_hash(const struct ovn_flow *); |
443 | + static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table, |
444 | + const struct ovn_flow *target, |
445 | +- bool cmp_sb_uuid); |
446 | ++ const struct uuid *sb_uuid); |
447 | + static char *ovn_flow_to_string(const struct ovn_flow *); |
448 | + static void ovn_flow_log(const struct ovn_flow *, const char *action); |
449 | + static void ovn_flow_destroy(struct ovn_flow *); |
450 | +@@ -644,13 +691,46 @@ ofctrl_recv(const struct ofp_header *oh, |
451 | + } |
452 | + } |
453 | + |
454 | |
455 | ++static struct sb_to_flow * |
456 | ++sb_to_flow_find(struct hmap *uuid_flow_table, const struct uuid *sb_uuid) |
457 | ++{ |
458 | ++ struct sb_to_flow *stf; |
459 | ++ HMAP_FOR_EACH_WITH_HASH (stf, hmap_node, uuid_hash(sb_uuid), |
460 | ++ uuid_flow_table) { |
461 | ++ if (uuid_equals(sb_uuid, &stf->sb_uuid)) { |
462 | ++ return stf; |
463 | ++ } |
464 | ++ } |
465 | ++ return NULL; |
466 | ++} |
467 | ++ |
468 | ++static void |
469 | ++link_flow_to_sb(struct ovn_desired_flow_table *flow_table, |
470 | ++ struct ovn_flow *f, const struct uuid *sb_uuid) |
471 | ++{ |
472 | ++ struct sb_flow_ref *sfr = xmalloc(sizeof *sfr); |
473 | ++ sfr->flow = f; |
474 | ++ sfr->sb_uuid = *sb_uuid; |
475 | ++ ovs_list_insert(&f->references, &sfr->sb_list); |
476 | ++ struct sb_to_flow *stf = sb_to_flow_find(&flow_table->uuid_flow_table, |
477 | ++ sb_uuid); |
478 | ++ if (!stf) { |
479 | ++ stf = xmalloc(sizeof *stf); |
480 | ++ stf->sb_uuid = *sb_uuid; |
481 | ++ ovs_list_init(&stf->flows); |
482 | ++ hmap_insert(&flow_table->uuid_flow_table, &stf->hmap_node, |
483 | ++ uuid_hash(sb_uuid)); |
484 | ++ } |
485 | ++ ovs_list_insert(&stf->flows, &sfr->flow_list); |
486 | ++} |
487 | ++ |
488 | + /* Flow table interfaces to the rest of ovn-controller. */ |
489 | + |
490 | + /* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to |
491 | + * the OpenFlow table numbered 'table_id' with the given 'priority' and |
492 | + * OpenFlow 'cookie'. The caller retains ownership of 'match' and 'actions'. |
493 | + * |
494 | +- * The flow is also added to a hash index based on sb_uuid. |
495 | ++ * The flow is also linked to the sb_uuid that generates it. |
496 | + * |
497 | + * This just assembles the desired flow table in memory. Nothing is actually |
498 | + * sent to the switch until a later call to ofctrl_put(). |
499 | +@@ -665,11 +745,9 @@ ofctrl_check_and_add_flow(struct ovn_des |
500 | + bool log_duplicate_flow) |
501 | + { |
502 | + struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match, |
503 | +- actions, sb_uuid); |
504 | +- |
505 | +- ovn_flow_log(f, "ofctrl_add_flow"); |
506 | ++ actions); |
507 | + |
508 | +- if (ovn_flow_lookup(&flow_table->match_flow_table, f, true)) { |
509 | ++ if (ovn_flow_lookup(&flow_table->match_flow_table, f, sb_uuid)) { |
510 | + if (log_duplicate_flow) { |
511 | + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); |
512 | + if (!VLOG_DROP_DBG(&rl)) { |
513 | +@@ -684,31 +762,8 @@ ofctrl_check_and_add_flow(struct ovn_des |
514 | + |
515 | + hmap_insert(&flow_table->match_flow_table, &f->match_hmap_node, |
516 | + f->match_hmap_node.hash); |
517 | +- hindex_insert(&flow_table->uuid_flow_table, &f->uuid_hindex_node, |
518 | +- f->uuid_hindex_node.hash); |
519 | +-} |
520 | +- |
521 | +-/* Removes a bundles of flows from the flow table. */ |
522 | +-void |
523 | +-ofctrl_remove_flows(struct ovn_desired_flow_table *flow_table, |
524 | +- const struct uuid *sb_uuid) |
525 | +-{ |
526 | +- struct ovn_flow *f, *next; |
527 | +- HINDEX_FOR_EACH_WITH_HASH_SAFE (f, next, uuid_hindex_node, |
528 | +- uuid_hash(sb_uuid), |
529 | +- &flow_table->uuid_flow_table) { |
530 | +- if (uuid_equals(&f->sb_uuid, sb_uuid)) { |
531 | +- ovn_flow_log(f, "ofctrl_remove_flow"); |
532 | +- hmap_remove(&flow_table->match_flow_table, |
533 | +- &f->match_hmap_node); |
534 | +- hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node); |
535 | +- ovn_flow_destroy(f); |
536 | +- } |
537 | +- } |
538 | +- |
539 | +- /* remove any related group and meter info */ |
540 | +- ovn_extend_table_remove_desired(groups, sb_uuid); |
541 | +- ovn_extend_table_remove_desired(meters, sb_uuid); |
542 | ++ link_flow_to_sb(flow_table, f, sb_uuid); |
543 | ++ ovn_flow_log(f, "ofctrl_add_flow"); |
544 | + } |
545 | + |
546 | + void |
547 | +@@ -721,6 +776,9 @@ ofctrl_add_flow(struct ovn_desired_flow_ |
548 | + match, actions, sb_uuid, true); |
549 | + } |
550 | + |
551 | ++/* Either add a new flow, or append actions on an existing flow. If the |
552 | ++ * flow existed, a new link will also be created between the new sb_uuid |
553 | ++ * and the existing flow. */ |
554 | + void |
555 | + ofctrl_add_or_append_flow(struct ovn_desired_flow_table *desired_flows, |
556 | + uint8_t table_id, uint16_t priority, uint64_t cookie, |
557 | +@@ -729,12 +787,10 @@ ofctrl_add_or_append_flow(struct ovn_des |
558 | + const struct uuid *sb_uuid) |
559 | + { |
560 | + struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match, |
561 | +- actions, sb_uuid); |
562 | +- |
563 | +- ovn_flow_log(f, "ofctrl_add_or_append_flow"); |
564 | ++ actions); |
565 | + |
566 | + struct ovn_flow *existing; |
567 | +- existing = ovn_flow_lookup(&desired_flows->match_flow_table, f, false); |
568 | ++ existing = ovn_flow_lookup(&desired_flows->match_flow_table, f, NULL); |
569 | + if (existing) { |
570 | + /* There's already a flow with this particular match. Append the |
571 | + * action to that flow rather than adding a new flow |
572 | +@@ -751,11 +807,169 @@ ofctrl_add_or_append_flow(struct ovn_des |
573 | + |
574 | + ofpbuf_uninit(&compound); |
575 | + ovn_flow_destroy(f); |
576 | ++ f = existing; |
577 | + } else { |
578 | + hmap_insert(&desired_flows->match_flow_table, &f->match_hmap_node, |
579 | + f->match_hmap_node.hash); |
580 | +- hindex_insert(&desired_flows->uuid_flow_table, &f->uuid_hindex_node, |
581 | +- f->uuid_hindex_node.hash); |
582 | ++ } |
583 | ++ link_flow_to_sb(desired_flows, f, sb_uuid); |
584 | ++ |
585 | ++ if (existing) { |
586 | ++ ovn_flow_log(f, "ofctrl_add_or_append_flow (append)"); |
587 | ++ } else { |
588 | ++ ovn_flow_log(f, "ofctrl_add_or_append_flow (add)"); |
589 | ++ } |
590 | ++} |
591 | ++ |
592 | ++/* Remove ovn_flows for the given "sb_to_flow" node in the uuid_flow_table. |
593 | ++ * Optionally log the message for each flow that is acturally removed, if |
594 | ++ * log_msg is not NULL. */ |
595 | ++static void |
596 | ++remove_flows_from_sb_to_flow(struct ovn_desired_flow_table *flow_table, |
597 | ++ struct sb_to_flow *stf, |
598 | ++ const char *log_msg) |
599 | ++{ |
600 | ++ struct sb_flow_ref *sfr, *next; |
601 | ++ LIST_FOR_EACH_SAFE (sfr, next, flow_list, &stf->flows) { |
602 | ++ ovs_list_remove(&sfr->sb_list); |
603 | ++ ovs_list_remove(&sfr->flow_list); |
604 | ++ struct ovn_flow *f = sfr->flow; |
605 | ++ free(sfr); |
606 | ++ |
607 | ++ if (ovs_list_is_empty(&f->references)) { |
608 | ++ if (log_msg) { |
609 | ++ ovn_flow_log(f, log_msg); |
610 | ++ } |
611 | ++ hmap_remove(&flow_table->match_flow_table, |
612 | ++ &f->match_hmap_node); |
613 | ++ ovn_flow_destroy(f); |
614 | ++ } |
615 | ++ } |
616 | ++ hmap_remove(&flow_table->uuid_flow_table, &stf->hmap_node); |
617 | ++ free(stf); |
618 | ++} |
619 | ++ |
620 | ++void |
621 | ++ofctrl_remove_flows(struct ovn_desired_flow_table *flow_table, |
622 | ++ const struct uuid *sb_uuid) |
623 | ++{ |
624 | ++ struct sb_to_flow *stf = sb_to_flow_find(&flow_table->uuid_flow_table, |
625 | ++ sb_uuid); |
626 | ++ if (stf) { |
627 | ++ remove_flows_from_sb_to_flow(flow_table, stf, "ofctrl_remove_flow"); |
628 | ++ } |
629 | ++ |
630 | ++ /* remove any related group and meter info */ |
631 | ++ ovn_extend_table_remove_desired(groups, sb_uuid); |
632 | ++ ovn_extend_table_remove_desired(meters, sb_uuid); |
633 | ++} |
634 | ++ |
635 | ++static struct ofctrl_flood_remove_node * |
636 | ++flood_remove_find_node(struct hmap *flood_remove_nodes, struct uuid *sb_uuid) |
637 | ++{ |
638 | ++ struct ofctrl_flood_remove_node *ofrn; |
639 | ++ HMAP_FOR_EACH_WITH_HASH (ofrn, hmap_node, uuid_hash(sb_uuid), |
640 | ++ flood_remove_nodes) { |
641 | ++ if (uuid_equals(&ofrn->sb_uuid, sb_uuid)) { |
642 | ++ return ofrn; |
643 | ++ } |
644 | ++ } |
645 | ++ return NULL; |
646 | ++} |
647 | ++ |
648 | ++void |
649 | ++ofctrl_flood_remove_add_node(struct hmap *flood_remove_nodes, |
650 | ++ const struct uuid *sb_uuid) |
651 | ++{ |
652 | ++ struct ofctrl_flood_remove_node *ofrn = xmalloc(sizeof *ofrn); |
653 | ++ ofrn->sb_uuid = *sb_uuid; |
654 | ++ hmap_insert(flood_remove_nodes, &ofrn->hmap_node, uuid_hash(sb_uuid)); |
655 | ++} |
656 | ++ |
657 | ++static void |
658 | ++flood_remove_flows_for_sb_uuid(struct ovn_desired_flow_table *flow_table, |
659 | ++ const struct uuid *sb_uuid, |
660 | ++ struct hmap *flood_remove_nodes) |
661 | ++{ |
662 | ++ struct sb_to_flow *stf = sb_to_flow_find(&flow_table->uuid_flow_table, |
663 | ++ sb_uuid); |
664 | ++ if (!stf) { |
665 | ++ return; |
666 | ++ } |
667 | ++ |
668 | ++ /* ovn_flows that have other references and waiting to be removed. */ |
669 | ++ struct ovs_list to_be_removed = OVS_LIST_INITIALIZER(&to_be_removed); |
670 | ++ |
671 | ++ /* Traverse all flows for the given sb_uuid. */ |
672 | ++ struct sb_flow_ref *sfr, *next; |
673 | ++ LIST_FOR_EACH_SAFE (sfr, next, flow_list, &stf->flows) { |
674 | ++ struct ovn_flow *f = sfr->flow; |
675 | ++ ovn_flow_log(f, "flood remove"); |
676 | ++ |
677 | ++ ovs_list_remove(&sfr->sb_list); |
678 | ++ ovs_list_remove(&sfr->flow_list); |
679 | ++ free(sfr); |
680 | ++ |
681 | ++ ovs_assert(ovs_list_is_empty(&f->list_node)); |
682 | ++ if (ovs_list_is_empty(&f->references)) { |
683 | ++ /* This is to optimize the case when most flows have only |
684 | ++ * one referencing sb_uuid, so to_be_removed list should |
685 | ++ * be empty in most cases. */ |
686 | ++ hmap_remove(&flow_table->match_flow_table, |
687 | ++ &f->match_hmap_node); |
688 | ++ ovn_flow_destroy(f); |
689 | ++ } else { |
690 | ++ ovs_list_insert(&to_be_removed, &f->list_node); |
691 | ++ } |
692 | ++ } |
693 | ++ hmap_remove(&flow_table->uuid_flow_table, &stf->hmap_node); |
694 | ++ free(stf); |
695 | ++ |
696 | ++ /* Traverse other referencing sb_uuids for the flows in the to_be_removed |
697 | ++ * list. */ |
698 | ++ |
699 | ++ /* Detach the items in f->references from the sfr.flow_list lists, |
700 | ++ * so that recursive calls will not mess up the sfr.sb_list list. */ |
701 | ++ struct ovn_flow *f, *f_next; |
702 | ++ LIST_FOR_EACH (f, list_node, &to_be_removed) { |
703 | ++ ovs_assert(!ovs_list_is_empty(&f->references)); |
704 | ++ LIST_FOR_EACH (sfr, sb_list, &f->references) { |
705 | ++ ovs_list_remove(&sfr->flow_list); |
706 | ++ } |
707 | ++ } |
708 | ++ LIST_FOR_EACH_SAFE (f, f_next, list_node, &to_be_removed) { |
709 | ++ LIST_FOR_EACH_SAFE (sfr, next, sb_list, &f->references) { |
710 | ++ if (!flood_remove_find_node(flood_remove_nodes, &sfr->sb_uuid)) { |
711 | ++ ofctrl_flood_remove_add_node(flood_remove_nodes, |
712 | ++ &sfr->sb_uuid); |
713 | ++ flood_remove_flows_for_sb_uuid(flow_table, &sfr->sb_uuid, |
714 | ++ flood_remove_nodes); |
715 | ++ } |
716 | ++ ovs_list_remove(&sfr->sb_list); |
717 | ++ free(sfr); |
718 | ++ } |
719 | ++ ovs_list_remove(&f->list_node); |
720 | ++ hmap_remove(&flow_table->match_flow_table, |
721 | ++ &f->match_hmap_node); |
722 | ++ ovn_flow_destroy(f); |
723 | ++ } |
724 | ++ |
725 | ++} |
726 | ++ |
727 | ++void |
728 | ++ofctrl_flood_remove_flows(struct ovn_desired_flow_table *flow_table, |
729 | ++ struct hmap *flood_remove_nodes) |
730 | ++{ |
731 | ++ struct ofctrl_flood_remove_node *ofrn; |
732 | ++ HMAP_FOR_EACH (ofrn, hmap_node, flood_remove_nodes) { |
733 | ++ flood_remove_flows_for_sb_uuid(flow_table, &ofrn->sb_uuid, |
734 | ++ flood_remove_nodes); |
735 | ++ } |
736 | ++ |
737 | ++ /* remove any related group and meter info */ |
738 | ++ HMAP_FOR_EACH (ofrn, hmap_node, flood_remove_nodes) { |
739 | ++ ovn_extend_table_remove_desired(groups, &ofrn->sb_uuid); |
740 | ++ ovn_extend_table_remove_desired(meters, &ofrn->sb_uuid); |
741 | + } |
742 | + } |
743 | + |
744 | |
745 | +@@ -763,18 +977,17 @@ ofctrl_add_or_append_flow(struct ovn_des |
746 | + |
747 | + static struct ovn_flow * |
748 | + ovn_flow_alloc(uint8_t table_id, uint16_t priority, uint64_t cookie, |
749 | +- const struct match *match, const struct ofpbuf *actions, |
750 | +- const struct uuid *sb_uuid) |
751 | ++ const struct match *match, const struct ofpbuf *actions) |
752 | + { |
753 | + struct ovn_flow *f = xmalloc(sizeof *f); |
754 | ++ ovs_list_init(&f->references); |
755 | ++ ovs_list_init(&f->list_node); |
756 | + f->table_id = table_id; |
757 | + f->priority = priority; |
758 | + minimatch_init(&f->match, match); |
759 | + f->ofpacts = xmemdup(actions->data, actions->size); |
760 | + f->ofpacts_len = actions->size; |
761 | +- f->sb_uuid = *sb_uuid; |
762 | + f->match_hmap_node.hash = ovn_flow_match_hash(f); |
763 | +- f->uuid_hindex_node.hash = uuid_hash(&f->sb_uuid); |
764 | + f->cookie = cookie; |
765 | + |
766 | + return f; |
767 | +@@ -793,23 +1006,27 @@ static struct ovn_flow * |
768 | + ovn_flow_dup(struct ovn_flow *src) |
769 | + { |
770 | + struct ovn_flow *dst = xmalloc(sizeof *dst); |
771 | ++ ovs_list_init(&dst->references); |
772 | + dst->table_id = src->table_id; |
773 | + dst->priority = src->priority; |
774 | + minimatch_clone(&dst->match, &src->match); |
775 | + dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len); |
776 | + dst->ofpacts_len = src->ofpacts_len; |
777 | +- dst->sb_uuid = src->sb_uuid; |
778 | + dst->match_hmap_node.hash = src->match_hmap_node.hash; |
779 | +- dst->uuid_hindex_node.hash = uuid_hash(&src->sb_uuid); |
780 | + dst->cookie = src->cookie; |
781 | + return dst; |
782 | + } |
783 | + |
784 | + /* Finds and returns an ovn_flow in 'flow_table' whose key is identical to |
785 | +- * 'target''s key, or NULL if there is none. */ |
786 | ++ * 'target''s key, or NULL if there is none. |
787 | ++ * |
788 | ++ * If sb_uuid is not NULL, the function will also check if the found flow is |
789 | ++ * referenced by the sb_uuid. |
790 | ++ * |
791 | ++ * NOTE: sb_uuid can only be used for ovn_desired_flow_table lookup. */ |
792 | + static struct ovn_flow * |
793 | + ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target, |
794 | +- bool cmp_sb_uuid) |
795 | ++ const struct uuid *sb_uuid) |
796 | + { |
797 | + struct ovn_flow *f; |
798 | + |
799 | +@@ -818,9 +1035,16 @@ ovn_flow_lookup(struct hmap *flow_table, |
800 | + if (f->table_id == target->table_id |
801 | + && f->priority == target->priority |
802 | + && minimatch_equal(&f->match, &target->match)) { |
803 | +- if (!cmp_sb_uuid || uuid_equals(&target->sb_uuid, &f->sb_uuid)) { |
804 | ++ if (!sb_uuid) { |
805 | + return f; |
806 | + } |
807 | ++ ovs_assert(flow_table != &installed_flows); |
808 | ++ struct sb_flow_ref *sfr; |
809 | ++ LIST_FOR_EACH (sfr, sb_list, &f->references) { |
810 | ++ if (uuid_equals(sb_uuid, &sfr->sb_uuid)) { |
811 | ++ return f; |
812 | ++ } |
813 | ++ } |
814 | + } |
815 | + } |
816 | + return NULL; |
817 | +@@ -830,7 +1054,7 @@ static char * |
818 | + ovn_flow_to_string(const struct ovn_flow *f) |
819 | + { |
820 | + struct ds s = DS_EMPTY_INITIALIZER; |
821 | +- ds_put_format(&s, "sb_uuid="UUID_FMT", ", UUID_ARGS(&f->sb_uuid)); |
822 | ++ |
823 | + ds_put_format(&s, "cookie=%"PRIx64", ", f->cookie); |
824 | + ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id); |
825 | + ds_put_format(&s, "priority=%"PRIu16", ", f->priority); |
826 | +@@ -855,6 +1079,7 @@ static void |
827 | + ovn_flow_destroy(struct ovn_flow *f) |
828 | + { |
829 | + if (f) { |
830 | ++ ovs_assert(ovs_list_is_empty(&f->references)); |
831 | + minimatch_destroy(&f->match); |
832 | + free(f->ofpacts); |
833 | + free(f); |
834 | +@@ -866,18 +1091,16 @@ void |
835 | + ovn_desired_flow_table_init(struct ovn_desired_flow_table *flow_table) |
836 | + { |
837 | + hmap_init(&flow_table->match_flow_table); |
838 | +- hindex_init(&flow_table->uuid_flow_table); |
839 | ++ hmap_init(&flow_table->uuid_flow_table); |
840 | + } |
841 | + |
842 | + void |
843 | + ovn_desired_flow_table_clear(struct ovn_desired_flow_table *flow_table) |
844 | + { |
845 | +- struct ovn_flow *f, *next; |
846 | +- HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, |
847 | +- &flow_table->match_flow_table) { |
848 | +- hmap_remove(&flow_table->match_flow_table, &f->match_hmap_node); |
849 | +- hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node); |
850 | +- ovn_flow_destroy(f); |
851 | ++ struct sb_to_flow *stf, *next; |
852 | ++ HMAP_FOR_EACH_SAFE (stf, next, hmap_node, |
853 | ++ &flow_table->uuid_flow_table) { |
854 | ++ remove_flows_from_sb_to_flow(flow_table, stf, NULL); |
855 | + } |
856 | + } |
857 | + |
858 | +@@ -886,7 +1109,7 @@ ovn_desired_flow_table_destroy(struct ov |
859 | + { |
860 | + ovn_desired_flow_table_clear(flow_table); |
861 | + hmap_destroy(&flow_table->match_flow_table); |
862 | +- hindex_destroy(&flow_table->uuid_flow_table); |
863 | ++ hmap_destroy(&flow_table->uuid_flow_table); |
864 | + } |
865 | + |
866 | + static void |
867 | +@@ -1221,7 +1444,7 @@ ofctrl_put(struct ovn_desired_flow_table |
868 | + struct ovn_flow *i, *next; |
869 | + HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) { |
870 | + struct ovn_flow *d = ovn_flow_lookup(&flow_table->match_flow_table, |
871 | +- i, false); |
872 | ++ i, NULL); |
873 | + if (!d) { |
874 | + /* Installed flow is no longer desirable. Delete it from the |
875 | + * switch and from installed_flows. */ |
876 | +@@ -1237,10 +1460,6 @@ ofctrl_put(struct ovn_desired_flow_table |
877 | + hmap_remove(&installed_flows, &i->match_hmap_node); |
878 | + ovn_flow_destroy(i); |
879 | + } else { |
880 | +- if (!uuid_equals(&i->sb_uuid, &d->sb_uuid)) { |
881 | +- /* Update installed flow's UUID. */ |
882 | +- i->sb_uuid = d->sb_uuid; |
883 | +- } |
884 | + if (!ofpacts_equal(i->ofpacts, i->ofpacts_len, |
885 | + d->ofpacts, d->ofpacts_len) || |
886 | + i->cookie != d->cookie) { |
887 | +@@ -1277,7 +1496,7 @@ ofctrl_put(struct ovn_desired_flow_table |
888 | + * in the installed flow table. */ |
889 | + struct ovn_flow *d; |
890 | + HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) { |
891 | +- i = ovn_flow_lookup(&installed_flows, d, false); |
892 | ++ i = ovn_flow_lookup(&installed_flows, d, NULL); |
893 | + if (!i) { |
894 | + /* Send flow_mod to add flow. */ |
895 | + struct ofputil_flow_mod fm = { |
896 | +Index: ovn-20.06.2/controller/ofctrl.h |
897 | +=================================================================== |
898 | +--- ovn-20.06.2.orig/controller/ofctrl.h |
899 | ++++ ovn-20.06.2/controller/ofctrl.h |
900 | +@@ -35,8 +35,9 @@ struct ovn_desired_flow_table { |
901 | + /* Hash map flow table using flow match conditions as hash key.*/ |
902 | + struct hmap match_flow_table; |
903 | + |
904 | +- /* SB uuid index for the nodes in match_flow_table.*/ |
905 | +- struct hindex uuid_flow_table; |
906 | ++ /* SB uuid index for the cross reference nodes that link to the nodes in |
907 | ++ * match_flow_table.*/ |
908 | ++ struct hmap uuid_flow_table; |
909 | + }; |
910 | + |
911 | + /* Interface for OVN main loop. */ |
912 | +@@ -74,7 +75,30 @@ void ofctrl_add_or_append_flow(struct ov |
913 | + const struct ofpbuf *actions, |
914 | + const struct uuid *sb_uuid); |
915 | + |
916 | +-void ofctrl_remove_flows(struct ovn_desired_flow_table *, const struct uuid *); |
917 | ++/* Removes a bundles of flows from the flow table for a specific sb_uuid. The |
918 | ++ * flows are removed only if they are not referenced by any other sb_uuid(s). |
919 | ++ * For flood-removing all related flows referenced by other sb_uuid(s), use |
920 | ++ * ofctrl_flood_remove_flows(). */ |
921 | ++void ofctrl_remove_flows(struct ovn_desired_flow_table *, |
922 | ++ const struct uuid *sb_uuid); |
923 | ++ |
924 | ++/* The function ofctrl_flood_remove_flows flood-removes flows from the desired |
925 | ++ * flow table for the sb_uuids provided in the flood_remove_nodes argument. |
926 | ++ * For each given sb_uuid in flood_remove_nodes, it removes all the flows |
927 | ++ * generated by the sb_uuid, and if any of the flows are referenced by another |
928 | ++ * sb_uuid, it continues removing all the flows used by that sb_uuid as well, |
929 | ++ * and so on, recursively. |
930 | ++ * |
931 | ++ * It adds all the sb_uuids that are actually removed in the |
932 | ++ * flood_remove_nodes. */ |
933 | ++struct ofctrl_flood_remove_node { |
934 | ++ struct hmap_node hmap_node; |
935 | ++ struct uuid sb_uuid; |
936 | ++}; |
937 | ++void ofctrl_flood_remove_flows(struct ovn_desired_flow_table *, |
938 | ++ struct hmap *flood_remove_nodes); |
939 | ++void ofctrl_flood_remove_add_node(struct hmap *flood_remove_nodes, |
940 | ++ const struct uuid *sb_uuid); |
941 | + |
942 | + void ovn_desired_flow_table_init(struct ovn_desired_flow_table *); |
943 | + void ovn_desired_flow_table_clear(struct ovn_desired_flow_table *); |
944 | +@@ -86,6 +110,7 @@ void ofctrl_check_and_add_flow(struct ov |
945 | + const struct ofpbuf *ofpacts, |
946 | + const struct uuid *, bool log_duplicate_flow); |
947 | + |
948 | ++ |
949 | + bool ofctrl_is_connected(void); |
950 | + void ofctrl_set_probe_interval(int probe_interval); |
951 | + |
952 | +Index: ovn-20.06.2/tests/ovn.at |
953 | +=================================================================== |
954 | +--- ovn-20.06.2.orig/tests/ovn.at |
955 | ++++ ovn-20.06.2/tests/ovn.at |
956 | +@@ -13310,6 +13310,8 @@ AT_CHECK([cat 2.packets], [0], [expout]) |
957 | + |
958 | + OVS_WAIT_UNTIL([test 9 = `as hv1 ovs-ofctl dump-flows br-int | \ |
959 | + grep conjunction | wc -l`]) |
960 | ++OVS_WAIT_UNTIL([test 3 = `as hv1 ovs-ofctl dump-flows br-int | \ |
961 | ++grep conjunction.*conjunction | wc -l`]) |
962 | + OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \ |
963 | + grep conj_id | wc -l`]) |
964 | + |
965 | +@@ -13327,9 +13329,98 @@ sip=`ip_to_hex 10 0 0 4` |
966 | + dip=`ip_to_hex 10 0 0 7` |
967 | + |
968 | + test_ip 1 f00000000001 f00000000002 $sip $dip |
969 | ++ |
970 | + $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets |
971 | + AT_CHECK([cat 2.packets], [0], []) |
972 | + |
973 | ++# Remove the first ACL, and verify that the conjunction flows are updated |
974 | ++# properly. |
975 | ++# There should be total of 6 flows present with conjunction action and 1 flow |
976 | ++# with conj match. Eg. |
977 | ++# table=44, priority=2001,conj_id=3,metadata=0x1 actions=drop |
978 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 actions=conjunction(4,2/2) |
979 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 actions=conjunction(4,2/2) |
980 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 actions=conjunction(4,2/2) |
981 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(4,1/2) |
982 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(4,1/2) |
983 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(4,1/2) |
984 | ++ |
985 | ++ovn-nbctl acl-del ls1 to-lport 1001 \ |
986 | ++'ip4 && ip4.src == $set1 && ip4.dst == $set1' |
987 | ++ |
988 | ++OVS_WAIT_UNTIL([test 6 = `as hv1 ovs-ofctl dump-flows br-int | \ |
989 | ++grep conjunction | wc -l`]) |
990 | ++OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ |
991 | ++grep conjunction.*conjunction | wc -l`]) |
992 | ++OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | \ |
993 | ++grep conj_id | wc -l`]) |
994 | ++ |
995 | ++# Add the ACL back |
996 | ++ovn-nbctl acl-add ls1 to-lport 1001 \ |
997 | ++'ip4 && ip4.src == $set1 && ip4.dst == $set1' allow |
998 | ++# Add one more ACL with more overlapping |
999 | ++ovn-nbctl acl-add ls1 to-lport 1001 \ |
1000 | ++'ip4 && ip4.src == $set1 && ip4.dst == {10.0.0.9, 10.0.0.10}' drop |
1001 | ++ |
1002 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 actions=conjunction(4,1/2) |
1003 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 actions=conjunction(4,1/2) |
1004 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(5,1/2) |
1005 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.5 actions=conjunction(5,1/2) |
1006 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 actions=conjunction(5,1/2) |
1007 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 actions=conjunction(4,1/2),conjunction(6,1/2) |
1008 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 actions=conjunction(6,1/2) |
1009 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(4,2/2),conjunction(5,2/2),conjunction(6,2/2) |
1010 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(4,2/2),conjunction(5,2/2),conjunction(6,2/2) |
1011 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(4,2/2),conjunction(5,2/2),conjunction(6,2/2) |
1012 | ++ |
1013 | ++OVS_WAIT_UNTIL([test 10 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1014 | ++grep conjunction | wc -l`]) |
1015 | ++OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1016 | ++grep conjunction.*conjunction | wc -l`]) |
1017 | ++OVS_WAIT_UNTIL([test 3 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1018 | ++grep conjunction.*conjunction.*conjunction | wc -l`]) |
1019 | ++ |
1020 | ++# Remove 10.0.0.7 from address set2. All flows should be updated properly. |
1021 | ++ovn-nbctl set Address_Set set2 \ |
1022 | ++addresses=\"10.0.0.8\",\"10.0.0.9\" |
1023 | ++ |
1024 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(9,1/2) |
1025 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 actions=conjunction(7,1/2) |
1026 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 actions=conjunction(8,1/2) |
1027 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.5 actions=conjunction(9,1/2) |
1028 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 actions=conjunction(7,1/2),conjunction(8,1/2) |
1029 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 actions=conjunction(9,1/2) |
1030 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(7,2/2),conjunction(8,2/2),conjunction(9,2/2) |
1031 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(7,2/2),conjunction(8,2/2),conjunction(9,2/2) |
1032 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(7,2/2),conjunction(8,2/2),conjunction(9,2/2) |
1033 | ++ |
1034 | ++OVS_WAIT_UNTIL([test 9 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1035 | ++grep conjunction | wc -l`]) |
1036 | ++OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1037 | ++grep conjunction.*conjunction | wc -l`]) |
1038 | ++OVS_WAIT_UNTIL([test 3 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1039 | ++grep conjunction.*conjunction.*conjunction | wc -l`]) |
1040 | ++ |
1041 | ++# Remove an ACL again |
1042 | ++ovn-nbctl acl-del ls1 to-lport 1001 \ |
1043 | ++'ip4 && ip4.src == $set1 && ip4.dst == $set1' |
1044 | ++ |
1045 | ++ovn-nbctl --wait=hv sync |
1046 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 actions=conjunction(10,1/2) |
1047 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 actions=conjunction(11,1/2) |
1048 | ++# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 actions=conjunction(10,1/2),conjunction(11,1/2) |
1049 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(10,2/2),conjunction(11,2/2) |
1050 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(10,2/2),conjunction(11,2/2) |
1051 | ++# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(10,2/2),conjunction(11,2/2) |
1052 | ++ |
1053 | ++OVS_WAIT_UNTIL([test 6 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1054 | ++grep conjunction | wc -l`]) |
1055 | ++OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1056 | ++grep conjunction.*conjunction | wc -l`]) |
1057 | ++OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ |
1058 | ++grep conjunction.*conjunction.*conjunction | wc -l`]) |
1059 | ++ |
1060 | ++OVN_CLEANUP([hv1]) |
1061 | + AT_CLEANUP |
1062 | + |
1063 | + # 3 hypervisors, one logical switch, 3 logical ports per hypervisor |
1064 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-03.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-03.patch |
1065 | new file mode 100644 |
1066 | index 0000000..2fbfc0c |
1067 | --- /dev/null |
1068 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-03.patch |
1069 | @@ -0,0 +1,718 @@ |
1070 | +Description: Cherry-pick fix applied to master and branch-20.12 |
1071 | +Origin: https://github.com/ovn-org/ovn/commit/354d3853d40cbce89a434632f67daed7fc992d8b |
1072 | +Applied-Upstream: commit: 354d3853d40cbce89a434632f67daed7fc992d8b |
1073 | + |
1074 | +From d18e5cf1ec9ad111796889194d29d9f5648a1e5e Mon Sep 17 00:00:00 2001 |
1075 | +From: Han Zhou <hzhou@ovn.org> |
1076 | +Date: Wed, 19 Aug 2020 18:37:51 -0700 |
1077 | +Subject: [PATCH 03/15] ofctrl.c: Maintain references between installed flows |
1078 | + and desired flows. |
1079 | + |
1080 | +Currently there is no link maintained between installed flows and desired |
1081 | +flows. This patch maintains the mapping between them, which will be useful |
1082 | +for a future patch that incrementally processes the flow installation without |
1083 | +having to do the full comparison between them. |
1084 | + |
1085 | +This patch also refactors the struct ovn_flow with two different wrapper |
1086 | +types: desired_flow and installed_flow, and the related static functions, |
1087 | +to make the code easier to read and avoid misuses of the struct. |
1088 | + |
1089 | +Acked-by: Mark Michelson <mmichels@redhat.com> |
1090 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
1091 | +--- |
1092 | + controller/ofctrl.c | 428 ++++++++++++++++++++++++++++++-------------- |
1093 | + 1 file changed, 294 insertions(+), 134 deletions(-) |
1094 | + |
1095 | +Index: ovn-20.06.2/controller/ofctrl.c |
1096 | +=================================================================== |
1097 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
1098 | ++++ ovn-20.06.2/controller/ofctrl.c |
1099 | +@@ -51,7 +51,27 @@ |
1100 | + |
1101 | + VLOG_DEFINE_THIS_MODULE(ofctrl); |
1102 | + |
1103 | +-/* An OpenFlow flow. |
1104 | ++/* An OpenFlow flow. */ |
1105 | ++struct ovn_flow { |
1106 | ++ /* Key. */ |
1107 | ++ uint8_t table_id; |
1108 | ++ uint16_t priority; |
1109 | ++ struct minimatch match; |
1110 | ++ |
1111 | ++ /* Hash. */ |
1112 | ++ uint32_t hash; |
1113 | ++ |
1114 | ++ /* Data. */ |
1115 | ++ struct ofpact *ofpacts; |
1116 | ++ size_t ofpacts_len; |
1117 | ++ uint64_t cookie; |
1118 | ++}; |
1119 | ++ |
1120 | ++/* A desired flow, in struct ovn_desired_flow_table, calculated by the |
1121 | ++ * incremental processing engine. |
1122 | ++ * - They are added/removed incrementally when I-P engine is able to process |
1123 | ++ * the changes incrementally, or |
1124 | ++ * - Completely cleared and recomputed by I-P engine when recompute happens. |
1125 | + * |
1126 | + * Links are maintained between desired flows and SB data. The relationship |
1127 | + * is M to N. The struct sb_flow_ref is used to link a pair of desired flow |
1128 | +@@ -82,52 +102,92 @@ VLOG_DEFINE_THIS_MODULE(ofctrl); |
1129 | + * The links are updated whenever there is a change in desired flows, which is |
1130 | + * usually triggered by a SB data change in I-P engine. |
1131 | + */ |
1132 | +-struct ovn_flow { |
1133 | ++struct desired_flow { |
1134 | ++ struct ovn_flow flow; |
1135 | + struct hmap_node match_hmap_node; /* For match based hashing. */ |
1136 | + struct ovs_list list_node; /* For handling lists of flows. */ |
1137 | +- struct ovs_list references; /* A list of struct sb_flow_ref nodes, which |
1138 | +- references this flow. (There are cases |
1139 | +- that multiple SB entities share the same |
1140 | +- desired OpenFlow flow, e.g. when |
1141 | +- conjunction is used.) */ |
1142 | + |
1143 | +- /* Key. */ |
1144 | +- uint8_t table_id; |
1145 | +- uint16_t priority; |
1146 | +- struct minimatch match; |
1147 | ++ /* A list of struct sb_flow_ref nodes, which references this flow. (There |
1148 | ++ * are cases that multiple SB entities share the same desired OpenFlow |
1149 | ++ * flow, e.g. when conjunction is used.) */ |
1150 | ++ struct ovs_list references; |
1151 | + |
1152 | +- /* Data. */ |
1153 | +- struct ofpact *ofpacts; |
1154 | +- size_t ofpacts_len; |
1155 | +- uint64_t cookie; |
1156 | ++ /* The corresponding flow in installed table. */ |
1157 | ++ struct installed_flow *installed_flow; |
1158 | ++ |
1159 | ++ /* Node in installed_flow.desired_refs list. */ |
1160 | ++ struct ovs_list installed_ref_list_node; |
1161 | + }; |
1162 | + |
1163 | + struct sb_to_flow { |
1164 | + struct hmap_node hmap_node; /* Node in |
1165 | + ovn_desired_flow_table.uuid_flow_table. */ |
1166 | + struct uuid sb_uuid; |
1167 | +- struct ovs_list flows; /* A list of struct sb_flow_ref nodes that are |
1168 | +- referenced by the sb_uuid. */ |
1169 | ++ struct ovs_list flows; /* A list of struct sb_flow_ref nodes that |
1170 | ++ are referenced by the sb_uuid. */ |
1171 | + }; |
1172 | + |
1173 | + struct sb_flow_ref { |
1174 | +- struct ovs_list sb_list; /* List node in ovn_flow.references. */ |
1175 | +- struct ovs_list flow_list; /* List node in sb_to_flow.ovn_flows. */ |
1176 | +- struct ovn_flow *flow; |
1177 | ++ struct ovs_list sb_list; /* List node in desired_flow.references. */ |
1178 | ++ struct ovs_list flow_list; /* List node in sb_to_flow.desired_flows. */ |
1179 | ++ struct desired_flow *flow; |
1180 | + struct uuid sb_uuid; |
1181 | + }; |
1182 | + |
1183 | +-static struct ovn_flow *ovn_flow_alloc(uint8_t table_id, uint16_t priority, |
1184 | +- uint64_t cookie, |
1185 | +- const struct match *match, |
1186 | +- const struct ofpbuf *actions); |
1187 | ++/* A installed flow, in static variable installed_flows. |
1188 | ++ * |
1189 | ++ * Installed flows are updated in ofctrl_put for maintaining the flow |
1190 | ++ * installation to OVS. They are updated according to desired flows: either by |
1191 | ++ * processing the tracked desired flow changes, or by comparing desired flows |
1192 | ++ * with currently installed flows when tracked desired flows changes are not |
1193 | ++ * available. |
1194 | ++ * |
1195 | ++ * In addition, when ofctrl state machine enters S_CLEAR, the installed flows |
1196 | ++ * will be cleared. (This happens in initialization phase and also when |
1197 | ++ * ovs-vswitchd is disconnected/reconnected). |
1198 | ++ * |
1199 | ++ * Links are maintained between installed flows and desired flows. The |
1200 | ++ * relationship is 1 to N. A link is added when a flow addition is processed. |
1201 | ++ * A link is removed when a flow deletion is processed, the desired flow |
1202 | ++ * table is cleared, or the installed flow table is cleared. |
1203 | ++ */ |
1204 | ++struct installed_flow { |
1205 | ++ struct ovn_flow flow; |
1206 | ++ struct hmap_node match_hmap_node; /* For match based hashing. */ |
1207 | ++ |
1208 | ++ /* A list of desired ovn_flow nodes (linked by |
1209 | ++ * desired_flow.installed_ref_list_node), which reference this installed |
1210 | ++ * flow. (There are cases that multiple desired flows reference the same |
1211 | ++ * installed flow, e.g. when there are conflict/duplicated ACLs that |
1212 | ++ * generates same match conditions). */ |
1213 | ++ struct ovs_list desired_refs; |
1214 | ++ |
1215 | ++ /* The corresponding flow in desired table. It must be one of the flows in |
1216 | ++ * desired_refs list. If there are more than one flows in references list, |
1217 | ++ * this is the one that is actually installed. */ |
1218 | ++ struct desired_flow *desired_flow; |
1219 | ++}; |
1220 | ++ |
1221 | ++static struct desired_flow *desired_flow_alloc( |
1222 | ++ uint8_t table_id, |
1223 | ++ uint16_t priority, |
1224 | ++ uint64_t cookie, |
1225 | ++ const struct match *match, |
1226 | ++ const struct ofpbuf *actions); |
1227 | ++static struct desired_flow *desired_flow_lookup( |
1228 | ++ struct ovn_desired_flow_table *, |
1229 | ++ const struct ovn_flow *target, |
1230 | ++ const struct uuid *sb_uuid); |
1231 | ++static void desired_flow_destroy(struct desired_flow *); |
1232 | ++ |
1233 | ++static struct installed_flow *installed_flow_lookup( |
1234 | ++ const struct ovn_flow *target); |
1235 | ++static void installed_flow_destroy(struct installed_flow *); |
1236 | ++static struct installed_flow *installed_flow_dup(struct desired_flow *); |
1237 | ++ |
1238 | + static uint32_t ovn_flow_match_hash(const struct ovn_flow *); |
1239 | +-static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table, |
1240 | +- const struct ovn_flow *target, |
1241 | +- const struct uuid *sb_uuid); |
1242 | + static char *ovn_flow_to_string(const struct ovn_flow *); |
1243 | + static void ovn_flow_log(const struct ovn_flow *, const char *action); |
1244 | +-static void ovn_flow_destroy(struct ovn_flow *); |
1245 | + |
1246 | + /* OpenFlow connection to the switch. */ |
1247 | + static struct rconn *swconn; |
1248 | +@@ -216,7 +276,6 @@ static struct ofpbuf *encode_meter_mod(c |
1249 | + |
1250 | + static void ovn_installed_flow_table_clear(void); |
1251 | + static void ovn_installed_flow_table_destroy(void); |
1252 | +-static struct ovn_flow *ovn_flow_dup(struct ovn_flow *source); |
1253 | + |
1254 | + |
1255 | + static void ofctrl_recv(const struct ofp_header *, enum ofptype); |
1256 | +@@ -691,6 +750,45 @@ ofctrl_recv(const struct ofp_header *oh, |
1257 | + } |
1258 | + } |
1259 | + |
1260 | |
1261 | ++static void |
1262 | ++link_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
1263 | ++{ |
1264 | ++ if (i->desired_flow == d) { |
1265 | ++ return; |
1266 | ++ } |
1267 | ++ |
1268 | ++ if (ovs_list_is_empty(&i->desired_refs)) { |
1269 | ++ ovs_assert(!i->desired_flow); |
1270 | ++ i->desired_flow = d; |
1271 | ++ } |
1272 | ++ ovs_list_insert(&i->desired_refs, &d->installed_ref_list_node); |
1273 | ++ d->installed_flow = i; |
1274 | ++} |
1275 | ++ |
1276 | ++static void |
1277 | ++unlink_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
1278 | ++{ |
1279 | ++ ovs_assert(i && i->desired_flow && !ovs_list_is_empty(&i->desired_refs)); |
1280 | ++ ovs_assert(d && d->installed_flow == i); |
1281 | ++ ovs_list_remove(&d->installed_ref_list_node); |
1282 | ++ d->installed_flow = NULL; |
1283 | ++ if (i->desired_flow == d) { |
1284 | ++ i->desired_flow = ovs_list_is_empty(&i->desired_refs) ? NULL : |
1285 | ++ CONTAINER_OF(ovs_list_front(&i->desired_refs), |
1286 | ++ struct desired_flow, |
1287 | ++ installed_ref_list_node); |
1288 | ++ } |
1289 | ++} |
1290 | ++ |
1291 | ++static void |
1292 | ++unlink_all_refs_for_installed_flow(struct installed_flow *i) |
1293 | ++{ |
1294 | ++ struct desired_flow *d, *next; |
1295 | ++ LIST_FOR_EACH_SAFE (d, next, installed_ref_list_node, &i->desired_refs) { |
1296 | ++ unlink_installed_to_desired(i, d); |
1297 | ++ } |
1298 | ++} |
1299 | ++ |
1300 | |
1301 | + static struct sb_to_flow * |
1302 | + sb_to_flow_find(struct hmap *uuid_flow_table, const struct uuid *sb_uuid) |
1303 | + { |
1304 | +@@ -706,7 +804,7 @@ sb_to_flow_find(struct hmap *uuid_flow_t |
1305 | + |
1306 | + static void |
1307 | + link_flow_to_sb(struct ovn_desired_flow_table *flow_table, |
1308 | +- struct ovn_flow *f, const struct uuid *sb_uuid) |
1309 | ++ struct desired_flow *f, const struct uuid *sb_uuid) |
1310 | + { |
1311 | + struct sb_flow_ref *sfr = xmalloc(sizeof *sfr); |
1312 | + sfr->flow = f; |
1313 | +@@ -744,26 +842,26 @@ ofctrl_check_and_add_flow(struct ovn_des |
1314 | + const struct uuid *sb_uuid, |
1315 | + bool log_duplicate_flow) |
1316 | + { |
1317 | +- struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match, |
1318 | +- actions); |
1319 | ++ struct desired_flow *f = desired_flow_alloc(table_id, priority, cookie, |
1320 | ++ match, actions); |
1321 | + |
1322 | +- if (ovn_flow_lookup(&flow_table->match_flow_table, f, sb_uuid)) { |
1323 | ++ if (desired_flow_lookup(flow_table, &f->flow, sb_uuid)) { |
1324 | + if (log_duplicate_flow) { |
1325 | + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); |
1326 | + if (!VLOG_DROP_DBG(&rl)) { |
1327 | +- char *s = ovn_flow_to_string(f); |
1328 | ++ char *s = ovn_flow_to_string(&f->flow); |
1329 | + VLOG_DBG("dropping duplicate flow: %s", s); |
1330 | + free(s); |
1331 | + } |
1332 | + } |
1333 | +- ovn_flow_destroy(f); |
1334 | ++ desired_flow_destroy(f); |
1335 | + return; |
1336 | + } |
1337 | + |
1338 | + hmap_insert(&flow_table->match_flow_table, &f->match_hmap_node, |
1339 | +- f->match_hmap_node.hash); |
1340 | ++ f->flow.hash); |
1341 | + link_flow_to_sb(flow_table, f, sb_uuid); |
1342 | +- ovn_flow_log(f, "ofctrl_add_flow"); |
1343 | ++ ovn_flow_log(&f->flow, "ofctrl_add_flow"); |
1344 | + } |
1345 | + |
1346 | + void |
1347 | +@@ -786,11 +884,11 @@ ofctrl_add_or_append_flow(struct ovn_des |
1348 | + const struct ofpbuf *actions, |
1349 | + const struct uuid *sb_uuid) |
1350 | + { |
1351 | +- struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match, |
1352 | +- actions); |
1353 | ++ struct desired_flow *f = desired_flow_alloc(table_id, priority, cookie, |
1354 | ++ match, actions); |
1355 | + |
1356 | +- struct ovn_flow *existing; |
1357 | +- existing = ovn_flow_lookup(&desired_flows->match_flow_table, f, NULL); |
1358 | ++ struct desired_flow *existing; |
1359 | ++ existing = desired_flow_lookup(desired_flows, &f->flow, NULL); |
1360 | + if (existing) { |
1361 | + /* There's already a flow with this particular match. Append the |
1362 | + * action to that flow rather than adding a new flow |
1363 | +@@ -798,26 +896,27 @@ ofctrl_add_or_append_flow(struct ovn_des |
1364 | + uint64_t compound_stub[64 / 8]; |
1365 | + struct ofpbuf compound; |
1366 | + ofpbuf_use_stub(&compound, compound_stub, sizeof(compound_stub)); |
1367 | +- ofpbuf_put(&compound, existing->ofpacts, existing->ofpacts_len); |
1368 | +- ofpbuf_put(&compound, f->ofpacts, f->ofpacts_len); |
1369 | +- |
1370 | +- free(existing->ofpacts); |
1371 | +- existing->ofpacts = xmemdup(compound.data, compound.size); |
1372 | +- existing->ofpacts_len = compound.size; |
1373 | ++ ofpbuf_put(&compound, existing->flow.ofpacts, |
1374 | ++ existing->flow.ofpacts_len); |
1375 | ++ ofpbuf_put(&compound, f->flow.ofpacts, f->flow.ofpacts_len); |
1376 | ++ |
1377 | ++ free(existing->flow.ofpacts); |
1378 | ++ existing->flow.ofpacts = xmemdup(compound.data, compound.size); |
1379 | ++ existing->flow.ofpacts_len = compound.size; |
1380 | + |
1381 | + ofpbuf_uninit(&compound); |
1382 | +- ovn_flow_destroy(f); |
1383 | ++ desired_flow_destroy(f); |
1384 | + f = existing; |
1385 | + } else { |
1386 | + hmap_insert(&desired_flows->match_flow_table, &f->match_hmap_node, |
1387 | +- f->match_hmap_node.hash); |
1388 | ++ f->flow.hash); |
1389 | + } |
1390 | + link_flow_to_sb(desired_flows, f, sb_uuid); |
1391 | + |
1392 | + if (existing) { |
1393 | +- ovn_flow_log(f, "ofctrl_add_or_append_flow (append)"); |
1394 | ++ ovn_flow_log(&f->flow, "ofctrl_add_or_append_flow (append)"); |
1395 | + } else { |
1396 | +- ovn_flow_log(f, "ofctrl_add_or_append_flow (add)"); |
1397 | ++ ovn_flow_log(&f->flow, "ofctrl_add_or_append_flow (add)"); |
1398 | + } |
1399 | + } |
1400 | + |
1401 | +@@ -833,16 +932,19 @@ remove_flows_from_sb_to_flow(struct ovn_ |
1402 | + LIST_FOR_EACH_SAFE (sfr, next, flow_list, &stf->flows) { |
1403 | + ovs_list_remove(&sfr->sb_list); |
1404 | + ovs_list_remove(&sfr->flow_list); |
1405 | +- struct ovn_flow *f = sfr->flow; |
1406 | ++ struct desired_flow *f = sfr->flow; |
1407 | + free(sfr); |
1408 | + |
1409 | + if (ovs_list_is_empty(&f->references)) { |
1410 | + if (log_msg) { |
1411 | +- ovn_flow_log(f, log_msg); |
1412 | ++ ovn_flow_log(&f->flow, log_msg); |
1413 | + } |
1414 | + hmap_remove(&flow_table->match_flow_table, |
1415 | + &f->match_hmap_node); |
1416 | +- ovn_flow_destroy(f); |
1417 | ++ if (f->installed_flow) { |
1418 | ++ unlink_installed_to_desired(f->installed_flow, f); |
1419 | ++ } |
1420 | ++ desired_flow_destroy(f); |
1421 | + } |
1422 | + } |
1423 | + hmap_remove(&flow_table->uuid_flow_table, &stf->hmap_node); |
1424 | +@@ -903,8 +1005,8 @@ flood_remove_flows_for_sb_uuid(struct ov |
1425 | + /* Traverse all flows for the given sb_uuid. */ |
1426 | + struct sb_flow_ref *sfr, *next; |
1427 | + LIST_FOR_EACH_SAFE (sfr, next, flow_list, &stf->flows) { |
1428 | +- struct ovn_flow *f = sfr->flow; |
1429 | +- ovn_flow_log(f, "flood remove"); |
1430 | ++ struct desired_flow *f = sfr->flow; |
1431 | ++ ovn_flow_log(&f->flow, "flood remove"); |
1432 | + |
1433 | + ovs_list_remove(&sfr->sb_list); |
1434 | + ovs_list_remove(&sfr->flow_list); |
1435 | +@@ -917,7 +1019,10 @@ flood_remove_flows_for_sb_uuid(struct ov |
1436 | + * be empty in most cases. */ |
1437 | + hmap_remove(&flow_table->match_flow_table, |
1438 | + &f->match_hmap_node); |
1439 | +- ovn_flow_destroy(f); |
1440 | ++ if (f->installed_flow) { |
1441 | ++ unlink_installed_to_desired(f->installed_flow, f); |
1442 | ++ } |
1443 | ++ desired_flow_destroy(f); |
1444 | + } else { |
1445 | + ovs_list_insert(&to_be_removed, &f->list_node); |
1446 | + } |
1447 | +@@ -930,7 +1035,7 @@ flood_remove_flows_for_sb_uuid(struct ov |
1448 | + |
1449 | + /* Detach the items in f->references from the sfr.flow_list lists, |
1450 | + * so that recursive calls will not mess up the sfr.sb_list list. */ |
1451 | +- struct ovn_flow *f, *f_next; |
1452 | ++ struct desired_flow *f, *f_next; |
1453 | + LIST_FOR_EACH (f, list_node, &to_be_removed) { |
1454 | + ovs_assert(!ovs_list_is_empty(&f->references)); |
1455 | + LIST_FOR_EACH (sfr, sb_list, &f->references) { |
1456 | +@@ -951,7 +1056,10 @@ flood_remove_flows_for_sb_uuid(struct ov |
1457 | + ovs_list_remove(&f->list_node); |
1458 | + hmap_remove(&flow_table->match_flow_table, |
1459 | + &f->match_hmap_node); |
1460 | +- ovn_flow_destroy(f); |
1461 | ++ if (f->installed_flow) { |
1462 | ++ unlink_installed_to_desired(f->installed_flow, f); |
1463 | ++ } |
1464 | ++ desired_flow_destroy(f); |
1465 | + } |
1466 | + |
1467 | + } |
1468 | +@@ -973,22 +1081,32 @@ ofctrl_flood_remove_flows(struct ovn_des |
1469 | + } |
1470 | + } |
1471 | + |
1472 | |
1473 | +-/* ovn_flow. */ |
1474 | ++/* flow operations. */ |
1475 | + |
1476 | +-static struct ovn_flow * |
1477 | +-ovn_flow_alloc(uint8_t table_id, uint16_t priority, uint64_t cookie, |
1478 | +- const struct match *match, const struct ofpbuf *actions) |
1479 | ++static void |
1480 | ++ovn_flow_init(struct ovn_flow *f, uint8_t table_id, uint16_t priority, |
1481 | ++ uint64_t cookie, const struct match *match, |
1482 | ++ const struct ofpbuf *actions) |
1483 | + { |
1484 | +- struct ovn_flow *f = xmalloc(sizeof *f); |
1485 | +- ovs_list_init(&f->references); |
1486 | +- ovs_list_init(&f->list_node); |
1487 | + f->table_id = table_id; |
1488 | + f->priority = priority; |
1489 | + minimatch_init(&f->match, match); |
1490 | + f->ofpacts = xmemdup(actions->data, actions->size); |
1491 | + f->ofpacts_len = actions->size; |
1492 | +- f->match_hmap_node.hash = ovn_flow_match_hash(f); |
1493 | ++ f->hash = ovn_flow_match_hash(f); |
1494 | + f->cookie = cookie; |
1495 | ++} |
1496 | ++ |
1497 | ++static struct desired_flow * |
1498 | ++desired_flow_alloc(uint8_t table_id, uint16_t priority, uint64_t cookie, |
1499 | ++ const struct match *match, const struct ofpbuf *actions) |
1500 | ++{ |
1501 | ++ struct desired_flow *f = xmalloc(sizeof *f); |
1502 | ++ ovs_list_init(&f->references); |
1503 | ++ ovs_list_init(&f->list_node); |
1504 | ++ ovs_list_init(&f->installed_ref_list_node); |
1505 | ++ f->installed_flow = NULL; |
1506 | ++ ovn_flow_init(&f->flow, table_id, priority, cookie, match, actions); |
1507 | + |
1508 | + return f; |
1509 | + } |
1510 | +@@ -1001,48 +1119,47 @@ ovn_flow_match_hash(const struct ovn_flo |
1511 | + minimatch_hash(&f->match, 0)); |
1512 | + } |
1513 | + |
1514 | +-/* Duplicate an ovn_flow structure. */ |
1515 | +-static struct ovn_flow * |
1516 | +-ovn_flow_dup(struct ovn_flow *src) |
1517 | +-{ |
1518 | +- struct ovn_flow *dst = xmalloc(sizeof *dst); |
1519 | +- ovs_list_init(&dst->references); |
1520 | +- dst->table_id = src->table_id; |
1521 | +- dst->priority = src->priority; |
1522 | +- minimatch_clone(&dst->match, &src->match); |
1523 | +- dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len); |
1524 | +- dst->ofpacts_len = src->ofpacts_len; |
1525 | +- dst->match_hmap_node.hash = src->match_hmap_node.hash; |
1526 | +- dst->cookie = src->cookie; |
1527 | ++/* Duplicate a desired flow to an installed flow. */ |
1528 | ++static struct installed_flow * |
1529 | ++installed_flow_dup(struct desired_flow *src) |
1530 | ++{ |
1531 | ++ struct installed_flow *dst = xmalloc(sizeof *dst); |
1532 | ++ ovs_list_init(&dst->desired_refs); |
1533 | ++ dst->desired_flow = NULL; |
1534 | ++ dst->flow.table_id = src->flow.table_id; |
1535 | ++ dst->flow.priority = src->flow.priority; |
1536 | ++ minimatch_clone(&dst->flow.match, &src->flow.match); |
1537 | ++ dst->flow.ofpacts = xmemdup(src->flow.ofpacts, src->flow.ofpacts_len); |
1538 | ++ dst->flow.ofpacts_len = src->flow.ofpacts_len; |
1539 | ++ dst->flow.hash = src->flow.hash; |
1540 | ++ dst->flow.cookie = src->flow.cookie; |
1541 | + return dst; |
1542 | + } |
1543 | + |
1544 | +-/* Finds and returns an ovn_flow in 'flow_table' whose key is identical to |
1545 | ++/* Finds and returns a desired_flow in 'flow_table' whose key is identical to |
1546 | + * 'target''s key, or NULL if there is none. |
1547 | + * |
1548 | + * If sb_uuid is not NULL, the function will also check if the found flow is |
1549 | +- * referenced by the sb_uuid. |
1550 | +- * |
1551 | +- * NOTE: sb_uuid can only be used for ovn_desired_flow_table lookup. */ |
1552 | +-static struct ovn_flow * |
1553 | +-ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target, |
1554 | +- const struct uuid *sb_uuid) |
1555 | ++ * referenced by the sb_uuid. */ |
1556 | ++static struct desired_flow * |
1557 | ++desired_flow_lookup(struct ovn_desired_flow_table *flow_table, |
1558 | ++ const struct ovn_flow *target, |
1559 | ++ const struct uuid *sb_uuid) |
1560 | + { |
1561 | +- struct ovn_flow *f; |
1562 | +- |
1563 | +- HMAP_FOR_EACH_WITH_HASH (f, match_hmap_node, target->match_hmap_node.hash, |
1564 | +- flow_table) { |
1565 | ++ struct desired_flow *d; |
1566 | ++ HMAP_FOR_EACH_WITH_HASH (d, match_hmap_node, target->hash, |
1567 | ++ &flow_table->match_flow_table) { |
1568 | ++ struct ovn_flow *f = &d->flow; |
1569 | + if (f->table_id == target->table_id |
1570 | + && f->priority == target->priority |
1571 | + && minimatch_equal(&f->match, &target->match)) { |
1572 | + if (!sb_uuid) { |
1573 | +- return f; |
1574 | ++ return d; |
1575 | + } |
1576 | +- ovs_assert(flow_table != &installed_flows); |
1577 | + struct sb_flow_ref *sfr; |
1578 | +- LIST_FOR_EACH (sfr, sb_list, &f->references) { |
1579 | ++ LIST_FOR_EACH (sfr, sb_list, &d->references) { |
1580 | + if (uuid_equals(sb_uuid, &sfr->sb_uuid)) { |
1581 | +- return f; |
1582 | ++ return d; |
1583 | + } |
1584 | + } |
1585 | + } |
1586 | +@@ -1050,6 +1167,24 @@ ovn_flow_lookup(struct hmap *flow_table, |
1587 | + return NULL; |
1588 | + } |
1589 | + |
1590 | ++/* Finds and returns an installed_flow in installed_flows whose key is |
1591 | ++ * identical to 'target''s key, or NULL if there is none. */ |
1592 | ++static struct installed_flow * |
1593 | ++installed_flow_lookup(const struct ovn_flow *target) |
1594 | ++{ |
1595 | ++ struct installed_flow *i; |
1596 | ++ HMAP_FOR_EACH_WITH_HASH (i, match_hmap_node, target->hash, |
1597 | ++ &installed_flows) { |
1598 | ++ struct ovn_flow *f = &i->flow; |
1599 | ++ if (f->table_id == target->table_id |
1600 | ++ && f->priority == target->priority |
1601 | ++ && minimatch_equal(&f->match, &target->match)) { |
1602 | ++ return i; |
1603 | ++ } |
1604 | ++ } |
1605 | ++ return NULL; |
1606 | ++} |
1607 | ++ |
1608 | + static char * |
1609 | + ovn_flow_to_string(const struct ovn_flow *f) |
1610 | + { |
1611 | +@@ -1076,17 +1211,35 @@ ovn_flow_log(const struct ovn_flow *f, c |
1612 | + } |
1613 | + |
1614 | + static void |
1615 | +-ovn_flow_destroy(struct ovn_flow *f) |
1616 | ++ovn_flow_uninit(struct ovn_flow *f) |
1617 | ++{ |
1618 | ++ minimatch_destroy(&f->match); |
1619 | ++ free(f->ofpacts); |
1620 | ++} |
1621 | ++ |
1622 | ++static void |
1623 | ++desired_flow_destroy(struct desired_flow *f) |
1624 | + { |
1625 | + if (f) { |
1626 | + ovs_assert(ovs_list_is_empty(&f->references)); |
1627 | +- minimatch_destroy(&f->match); |
1628 | +- free(f->ofpacts); |
1629 | ++ ovs_assert(!f->installed_flow); |
1630 | ++ ovn_flow_uninit(&f->flow); |
1631 | ++ free(f); |
1632 | ++ } |
1633 | ++} |
1634 | ++ |
1635 | ++static void |
1636 | ++installed_flow_destroy(struct installed_flow *f) |
1637 | ++{ |
1638 | ++ if (f) { |
1639 | ++ ovs_assert(ovs_list_is_empty(&f->desired_refs)); |
1640 | ++ ovs_assert(!f->desired_flow); |
1641 | ++ ovn_flow_uninit(&f->flow); |
1642 | + free(f); |
1643 | + } |
1644 | + } |
1645 | + |
1646 | |
1647 | +-/* Flow tables of struct ovn_flow. */ |
1648 | ++/* Desired flow table operations. */ |
1649 | + void |
1650 | + ovn_desired_flow_table_init(struct ovn_desired_flow_table *flow_table) |
1651 | + { |
1652 | +@@ -1112,13 +1265,16 @@ ovn_desired_flow_table_destroy(struct ov |
1653 | + hmap_destroy(&flow_table->uuid_flow_table); |
1654 | + } |
1655 | + |
1656 | ++ |
1657 | |
1658 | ++/* Installed flow table operations. */ |
1659 | + static void |
1660 | + ovn_installed_flow_table_clear(void) |
1661 | + { |
1662 | +- struct ovn_flow *f, *next; |
1663 | ++ struct installed_flow *f, *next; |
1664 | + HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &installed_flows) { |
1665 | + hmap_remove(&installed_flows, &f->match_hmap_node); |
1666 | +- ovn_flow_destroy(f); |
1667 | ++ unlink_all_refs_for_installed_flow(f); |
1668 | ++ installed_flow_destroy(f); |
1669 | + } |
1670 | + } |
1671 | + |
1672 | +@@ -1441,81 +1597,85 @@ ofctrl_put(struct ovn_desired_flow_table |
1673 | + /* Iterate through all of the installed flows. If any of them are no |
1674 | + * longer desired, delete them; if any of them should have different |
1675 | + * actions, update them. */ |
1676 | +- struct ovn_flow *i, *next; |
1677 | ++ struct installed_flow *i, *next; |
1678 | + HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) { |
1679 | +- struct ovn_flow *d = ovn_flow_lookup(&flow_table->match_flow_table, |
1680 | +- i, NULL); |
1681 | ++ unlink_all_refs_for_installed_flow(i); |
1682 | ++ struct desired_flow *d = desired_flow_lookup(flow_table, &i->flow, |
1683 | ++ NULL); |
1684 | + if (!d) { |
1685 | + /* Installed flow is no longer desirable. Delete it from the |
1686 | + * switch and from installed_flows. */ |
1687 | + struct ofputil_flow_mod fm = { |
1688 | +- .match = i->match, |
1689 | +- .priority = i->priority, |
1690 | +- .table_id = i->table_id, |
1691 | ++ .match = i->flow.match, |
1692 | ++ .priority = i->flow.priority, |
1693 | ++ .table_id = i->flow.table_id, |
1694 | + .command = OFPFC_DELETE_STRICT, |
1695 | + }; |
1696 | + add_flow_mod(&fm, &msgs); |
1697 | +- ovn_flow_log(i, "removing installed"); |
1698 | ++ ovn_flow_log(&i->flow, "removing installed"); |
1699 | + |
1700 | + hmap_remove(&installed_flows, &i->match_hmap_node); |
1701 | +- ovn_flow_destroy(i); |
1702 | ++ installed_flow_destroy(i); |
1703 | + } else { |
1704 | +- if (!ofpacts_equal(i->ofpacts, i->ofpacts_len, |
1705 | +- d->ofpacts, d->ofpacts_len) || |
1706 | +- i->cookie != d->cookie) { |
1707 | ++ if (!ofpacts_equal(i->flow.ofpacts, i->flow.ofpacts_len, |
1708 | ++ d->flow.ofpacts, d->flow.ofpacts_len) || |
1709 | ++ i->flow.cookie != d->flow.cookie) { |
1710 | + /* Update actions in installed flow. */ |
1711 | + struct ofputil_flow_mod fm = { |
1712 | +- .match = i->match, |
1713 | +- .priority = i->priority, |
1714 | +- .table_id = i->table_id, |
1715 | +- .ofpacts = d->ofpacts, |
1716 | +- .ofpacts_len = d->ofpacts_len, |
1717 | ++ .match = i->flow.match, |
1718 | ++ .priority = i->flow.priority, |
1719 | ++ .table_id = i->flow.table_id, |
1720 | ++ .ofpacts = d->flow.ofpacts, |
1721 | ++ .ofpacts_len = d->flow.ofpacts_len, |
1722 | + .command = OFPFC_MODIFY_STRICT, |
1723 | + }; |
1724 | + /* Update cookie if it is changed. */ |
1725 | +- if (i->cookie != d->cookie) { |
1726 | ++ if (i->flow.cookie != d->flow.cookie) { |
1727 | + fm.modify_cookie = true; |
1728 | +- fm.new_cookie = htonll(d->cookie); |
1729 | ++ fm.new_cookie = htonll(d->flow.cookie); |
1730 | + /* Use OFPFC_ADD so that cookie can be updated. */ |
1731 | + fm.command = OFPFC_ADD, |
1732 | +- i->cookie = d->cookie; |
1733 | ++ i->flow.cookie = d->flow.cookie; |
1734 | + } |
1735 | + add_flow_mod(&fm, &msgs); |
1736 | +- ovn_flow_log(i, "updating installed"); |
1737 | ++ ovn_flow_log(&i->flow, "updating installed"); |
1738 | + |
1739 | + /* Replace 'i''s actions by 'd''s. */ |
1740 | +- free(i->ofpacts); |
1741 | +- i->ofpacts = xmemdup(d->ofpacts, d->ofpacts_len); |
1742 | +- i->ofpacts_len = d->ofpacts_len; |
1743 | ++ free(i->flow.ofpacts); |
1744 | ++ i->flow.ofpacts = xmemdup(d->flow.ofpacts, |
1745 | ++ d->flow.ofpacts_len); |
1746 | ++ i->flow.ofpacts_len = d->flow.ofpacts_len; |
1747 | + } |
1748 | ++ link_installed_to_desired(i, d); |
1749 | + |
1750 | + } |
1751 | + } |
1752 | + |
1753 | + /* Iterate through the desired flows and add those that aren't found |
1754 | + * in the installed flow table. */ |
1755 | +- struct ovn_flow *d; |
1756 | ++ struct desired_flow *d; |
1757 | + HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) { |
1758 | +- i = ovn_flow_lookup(&installed_flows, d, NULL); |
1759 | ++ i = installed_flow_lookup(&d->flow); |
1760 | + if (!i) { |
1761 | + /* Send flow_mod to add flow. */ |
1762 | + struct ofputil_flow_mod fm = { |
1763 | +- .match = d->match, |
1764 | +- .priority = d->priority, |
1765 | +- .table_id = d->table_id, |
1766 | +- .ofpacts = d->ofpacts, |
1767 | +- .ofpacts_len = d->ofpacts_len, |
1768 | +- .new_cookie = htonll(d->cookie), |
1769 | ++ .match = d->flow.match, |
1770 | ++ .priority = d->flow.priority, |
1771 | ++ .table_id = d->flow.table_id, |
1772 | ++ .ofpacts = d->flow.ofpacts, |
1773 | ++ .ofpacts_len = d->flow.ofpacts_len, |
1774 | ++ .new_cookie = htonll(d->flow.cookie), |
1775 | + .command = OFPFC_ADD, |
1776 | + }; |
1777 | + add_flow_mod(&fm, &msgs); |
1778 | +- ovn_flow_log(d, "adding installed"); |
1779 | ++ ovn_flow_log(&d->flow, "adding installed"); |
1780 | + |
1781 | + /* Copy 'd' from 'flow_table' to installed_flows. */ |
1782 | +- struct ovn_flow *new_node = ovn_flow_dup(d); |
1783 | +- hmap_insert(&installed_flows, &new_node->match_hmap_node, |
1784 | +- new_node->match_hmap_node.hash); |
1785 | ++ i = installed_flow_dup(d); |
1786 | ++ hmap_insert(&installed_flows, &i->match_hmap_node, |
1787 | ++ i->flow.hash); |
1788 | + } |
1789 | ++ link_installed_to_desired(i, d); |
1790 | + } |
1791 | + |
1792 | + /* Iterate through the installed groups from previous runs. If they |
1793 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-04.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-04.patch |
1794 | new file mode 100644 |
1795 | index 0000000..dcc18b8 |
1796 | --- /dev/null |
1797 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-04.patch |
1798 | @@ -0,0 +1,154 @@ |
1799 | +Description: Cherry-pick fix applied to master and branch-20.12 |
1800 | +Origin: https://github.com/ovn-org/ovn/commit/23063cf4178c05f5d6b3e4ec6d323ccc88df6101 |
1801 | +Applied-Upstream: commit: 23063cf4178c05f5d6b3e4ec6d323ccc88df6101 |
1802 | + |
1803 | +From 93c15acf2587d6d703ce0c05fd5500a7c1fcbb31 Mon Sep 17 00:00:00 2001 |
1804 | +From: Han Zhou <hzhou@ovn.org> |
1805 | +Date: Thu, 20 Aug 2020 21:38:30 -0700 |
1806 | +Subject: [PATCH 04/15] ofctrl.c: Refactor - move openflow msg construction to |
1807 | + functions. |
1808 | + |
1809 | +These functions will be reused in multiple places in a future patch. |
1810 | + |
1811 | +Acked-by: Mark Michelson <mmichels@redhat.com> |
1812 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
1813 | +--- |
1814 | + controller/ofctrl.c | 103 ++++++++++++++++++++++++++------------------ |
1815 | + 1 file changed, 60 insertions(+), 43 deletions(-) |
1816 | + |
1817 | +Index: ovn-20.06.2/controller/ofctrl.c |
1818 | +=================================================================== |
1819 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
1820 | ++++ ovn-20.06.2/controller/ofctrl.c |
1821 | +@@ -1480,6 +1480,63 @@ add_meter(struct ovn_extend_table_info * |
1822 | + free(mm.meter.bands); |
1823 | + } |
1824 | + |
1825 | ++static void |
1826 | ++installed_flow_add(struct ovn_flow *d, struct ovs_list *msgs) |
1827 | ++{ |
1828 | ++ /* Send flow_mod to add flow. */ |
1829 | ++ struct ofputil_flow_mod fm = { |
1830 | ++ .match = d->match, |
1831 | ++ .priority = d->priority, |
1832 | ++ .table_id = d->table_id, |
1833 | ++ .ofpacts = d->ofpacts, |
1834 | ++ .ofpacts_len = d->ofpacts_len, |
1835 | ++ .new_cookie = htonll(d->cookie), |
1836 | ++ .command = OFPFC_ADD, |
1837 | ++ }; |
1838 | ++ add_flow_mod(&fm, msgs); |
1839 | ++} |
1840 | ++ |
1841 | ++static void |
1842 | ++installed_flow_mod(struct ovn_flow *i, struct ovn_flow *d, |
1843 | ++ struct ovs_list *msgs) |
1844 | ++{ |
1845 | ++ /* Update actions in installed flow. */ |
1846 | ++ struct ofputil_flow_mod fm = { |
1847 | ++ .match = i->match, |
1848 | ++ .priority = i->priority, |
1849 | ++ .table_id = i->table_id, |
1850 | ++ .ofpacts = d->ofpacts, |
1851 | ++ .ofpacts_len = d->ofpacts_len, |
1852 | ++ .command = OFPFC_MODIFY_STRICT, |
1853 | ++ }; |
1854 | ++ /* Update cookie if it is changed. */ |
1855 | ++ if (i->cookie != d->cookie) { |
1856 | ++ fm.modify_cookie = true; |
1857 | ++ fm.new_cookie = htonll(d->cookie); |
1858 | ++ /* Use OFPFC_ADD so that cookie can be updated. */ |
1859 | ++ fm.command = OFPFC_ADD; |
1860 | ++ } |
1861 | ++ add_flow_mod(&fm, msgs); |
1862 | ++ |
1863 | ++ /* Replace 'i''s actions and cookie by 'd''s. */ |
1864 | ++ free(i->ofpacts); |
1865 | ++ i->ofpacts = xmemdup(d->ofpacts, d->ofpacts_len); |
1866 | ++ i->ofpacts_len = d->ofpacts_len; |
1867 | ++ i->cookie = d->cookie; |
1868 | ++} |
1869 | ++ |
1870 | ++static void |
1871 | ++installed_flow_del(struct ovn_flow *i, struct ovs_list *msgs) |
1872 | ++{ |
1873 | ++ struct ofputil_flow_mod fm = { |
1874 | ++ .match = i->match, |
1875 | ++ .priority = i->priority, |
1876 | ++ .table_id = i->table_id, |
1877 | ++ .command = OFPFC_DELETE_STRICT, |
1878 | ++ }; |
1879 | ++ add_flow_mod(&fm, msgs); |
1880 | ++} |
1881 | ++ |
1882 | + /* The flow table can be updated if the connection to the switch is up and |
1883 | + * in the correct state and not backlogged with existing flow_mods. (Our |
1884 | + * criteria for being backlogged appear very conservative, but the socket |
1885 | +@@ -1605,46 +1662,16 @@ ofctrl_put(struct ovn_desired_flow_table |
1886 | + if (!d) { |
1887 | + /* Installed flow is no longer desirable. Delete it from the |
1888 | + * switch and from installed_flows. */ |
1889 | +- struct ofputil_flow_mod fm = { |
1890 | +- .match = i->flow.match, |
1891 | +- .priority = i->flow.priority, |
1892 | +- .table_id = i->flow.table_id, |
1893 | +- .command = OFPFC_DELETE_STRICT, |
1894 | +- }; |
1895 | +- add_flow_mod(&fm, &msgs); |
1896 | ++ installed_flow_del(&i->flow, &msgs); |
1897 | + ovn_flow_log(&i->flow, "removing installed"); |
1898 | +- |
1899 | + hmap_remove(&installed_flows, &i->match_hmap_node); |
1900 | + installed_flow_destroy(i); |
1901 | + } else { |
1902 | + if (!ofpacts_equal(i->flow.ofpacts, i->flow.ofpacts_len, |
1903 | + d->flow.ofpacts, d->flow.ofpacts_len) || |
1904 | + i->flow.cookie != d->flow.cookie) { |
1905 | +- /* Update actions in installed flow. */ |
1906 | +- struct ofputil_flow_mod fm = { |
1907 | +- .match = i->flow.match, |
1908 | +- .priority = i->flow.priority, |
1909 | +- .table_id = i->flow.table_id, |
1910 | +- .ofpacts = d->flow.ofpacts, |
1911 | +- .ofpacts_len = d->flow.ofpacts_len, |
1912 | +- .command = OFPFC_MODIFY_STRICT, |
1913 | +- }; |
1914 | +- /* Update cookie if it is changed. */ |
1915 | +- if (i->flow.cookie != d->flow.cookie) { |
1916 | +- fm.modify_cookie = true; |
1917 | +- fm.new_cookie = htonll(d->flow.cookie); |
1918 | +- /* Use OFPFC_ADD so that cookie can be updated. */ |
1919 | +- fm.command = OFPFC_ADD, |
1920 | +- i->flow.cookie = d->flow.cookie; |
1921 | +- } |
1922 | +- add_flow_mod(&fm, &msgs); |
1923 | + ovn_flow_log(&i->flow, "updating installed"); |
1924 | +- |
1925 | +- /* Replace 'i''s actions by 'd''s. */ |
1926 | +- free(i->flow.ofpacts); |
1927 | +- i->flow.ofpacts = xmemdup(d->flow.ofpacts, |
1928 | +- d->flow.ofpacts_len); |
1929 | +- i->flow.ofpacts_len = d->flow.ofpacts_len; |
1930 | ++ installed_flow_mod(&i->flow, &d->flow, &msgs); |
1931 | + } |
1932 | + link_installed_to_desired(i, d); |
1933 | + |
1934 | +@@ -1657,17 +1684,7 @@ ofctrl_put(struct ovn_desired_flow_table |
1935 | + HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) { |
1936 | + i = installed_flow_lookup(&d->flow); |
1937 | + if (!i) { |
1938 | +- /* Send flow_mod to add flow. */ |
1939 | +- struct ofputil_flow_mod fm = { |
1940 | +- .match = d->flow.match, |
1941 | +- .priority = d->flow.priority, |
1942 | +- .table_id = d->flow.table_id, |
1943 | +- .ofpacts = d->flow.ofpacts, |
1944 | +- .ofpacts_len = d->flow.ofpacts_len, |
1945 | +- .new_cookie = htonll(d->flow.cookie), |
1946 | +- .command = OFPFC_ADD, |
1947 | +- }; |
1948 | +- add_flow_mod(&fm, &msgs); |
1949 | ++ installed_flow_add(&d->flow, &msgs); |
1950 | + ovn_flow_log(&d->flow, "adding installed"); |
1951 | + |
1952 | + /* Copy 'd' from 'flow_table' to installed_flows. */ |
1953 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-05.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-05.patch |
1954 | new file mode 100644 |
1955 | index 0000000..99efbc1 |
1956 | --- /dev/null |
1957 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-05.patch |
1958 | @@ -0,0 +1,481 @@ |
1959 | +Description: Cherry-pick fix applied to master and branch-20.12 |
1960 | +Origin: https://github.com/ovn-org/ovn/commit/6f0b1e02d9ab3a94048c4818f2d382938cad4b71 |
1961 | +Applied-Upstream: commit: 6f0b1e02d9ab3a94048c4818f2d382938cad4b71 |
1962 | + |
1963 | +From 2029955c4932f5e28ed37ab6570bad6178c15054 Mon Sep 17 00:00:00 2001 |
1964 | +From: Han Zhou <hzhou@ovn.org> |
1965 | +Date: Mon, 17 Aug 2020 15:49:18 -0700 |
1966 | +Subject: [PATCH 05/15] ofctrl: Incremental processing for flow installation by |
1967 | + tracking. |
1968 | +MIME-Version: 1.0 |
1969 | +Content-Type: text/plain; charset=UTF-8 |
1970 | +Content-Transfer-Encoding: 8bit |
1971 | + |
1972 | +With incremental processing for flow computation, the bottle neck of |
1973 | +ovn-controller in large scale environment is in the flow installation |
1974 | +(ofctrl_put()), which does full comparison between the two big flow tables: the |
1975 | +installed flows and desired flows. |
1976 | + |
1977 | +This patch implements tracking desired flow changes when flows are |
1978 | +incrementally computed by I-P engine, and then incrementally processing the |
1979 | +flow installation using the tracked information in ofctrl_put(). It falls back |
1980 | +to the full comparison whenever tracking information is unavailable, e.g. when |
1981 | +I-P engine triggers full recompute. |
1982 | + |
1983 | +In ovn-scale-test with 3000 HVs and 30k lports, the end-to-end latency between |
1984 | +the moment a lflow is updated in SB DB and the moment when all the 3K HVs |
1985 | +complete OVS flow updating has reduced around 60% (from 1s to 400ms). |
1986 | + |
1987 | +Below is the perf result for a ovn-controller processing a new port-binding: |
1988 | + |
1989 | +Beore: |
1990 | ++ 96.76% 0.00% ovn-controller [unknown] [k] 0xffffffffffffffff |
1991 | ++ 90.21% 0.00% ovn-controller ovn-controller [.] main |
1992 | ++ 39.93% 1.19% ovn-controller ovn-controller [.] ofctrl_put |
1993 | ++ 31.27% 12.47% ovn-controller ovn-controller [.] ovn_flow_lookup |
1994 | ++ 22.12% 3.12% ovn-controller ovn-controller [.] encaps_run |
1995 | ++ 18.69% 2.77% ovn-controller ovn-controller [.] minimatch_equal |
1996 | ++ 17.63% 4.11% ovn-controller ovn-controller [.] patch_run |
1997 | ++ 15.91% 0.00% ovn-controller ovn-controller [.] add_bridge_mappings (inlined) |
1998 | ++ 14.03% 12.08% ovn-controller ovn-controller [.] minimask_equal |
1999 | ++ 12.41% 0.00% ovn-controller ovn-controller [.] chassis_tunnel_add (inlined) |
2000 | ++ 11.40% 0.00% ovn-controller ovn-controller [.] tunnel_add (inlined) |
2001 | + |
2002 | +After: |
2003 | ++ 94.59% 0.00% ovn-controller [unknown] [k] 0xffffffffffffffff |
2004 | ++ 83.56% 0.09% ovn-controller ovn-controller [.] main |
2005 | ++ 35.37% 3.13% ovn-controller ovn-controller [.] encaps_run |
2006 | ++ 27.54% 7.53% ovn-controller ovn-controller [.] patch_run |
2007 | ++ 24.86% 0.00% ovn-controller ovn-controller [.] add_bridge_mappings (inlined) |
2008 | ++ 20.01% 0.00% ovn-controller ovn-controller [.] chassis_tunnel_add (inlined) |
2009 | ++ 18.51% 0.00% ovn-controller ovn-controller [.] tunnel_add (inlined) |
2010 | ++ 14.08% 0.17% ovn-controller ovn-controller [.] physical_run |
2011 | ++ 11.37% 11.28% ovn-controller ovn-controller [.] next_real_row |
2012 | ++ 10.50% 2.59% ovn-controller ovn-controller [.] consider_port_binding |
2013 | +... |
2014 | ++ 2.14% 0.32% ovn-controller ovn-controller [.] ofctrl_put â–’ |
2015 | + |
2016 | +Before the optimization, ofctrl_put took 40% of CPU, and now it disappears from |
2017 | +the hot spots. |
2018 | + |
2019 | +Acked-by: Mark Michelson <mmichels@redhat.com> |
2020 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
2021 | +--- |
2022 | + controller/ofctrl.c | 289 +++++++++++++++++++++++++++++++++++--------- |
2023 | + controller/ofctrl.h | 6 +- |
2024 | + 2 files changed, 240 insertions(+), 55 deletions(-) |
2025 | + |
2026 | +Index: ovn-20.06.2/controller/ofctrl.c |
2027 | +=================================================================== |
2028 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
2029 | ++++ ovn-20.06.2/controller/ofctrl.c |
2030 | +@@ -101,6 +101,39 @@ struct ovn_flow { |
2031 | + * |
2032 | + * The links are updated whenever there is a change in desired flows, which is |
2033 | + * usually triggered by a SB data change in I-P engine. |
2034 | ++ * |
2035 | ++ * ** Tracking ** |
2036 | ++ * |
2037 | ++ * A desired flow can be tracked - listed in ovn_desired_flow_table's |
2038 | ++ * tracked_flows. |
2039 | ++ * |
2040 | ++ * Tracked flows is initially empty, and stays empty after the first run of I-P |
2041 | ++ * engine when installed flows are initially populated. After that, flow |
2042 | ++ * changes are tracked when I-P engine incrementally computes flow changes. |
2043 | ++ * Tracked flows are then processed and removed completely in ofctrl_put. |
2044 | ++ * ("processed" means OpenFlow change messages are composed and sent/queued to |
2045 | ++ * OVS, which ensures flows in OVS is always in sync (eventually) with the |
2046 | ++ * installed flows table). |
2047 | ++ * |
2048 | ++ * In case of full recompute of I-P engine, tracked flows are not |
2049 | ++ * added/removed, and ofctrl_put will not rely on tracked flows. (It is I-P |
2050 | ++ * engine's responsibility to ensure the tracked flows are cleared before |
2051 | ++ * recompute). |
2052 | ++ * |
2053 | ++ * Tracked flows can be preserved across multiple I-P engine runs - if in some |
2054 | ++ * iterations ofctrl_put() is skipped. Tracked flows are cleared only when it |
2055 | ++ * is consumed or when flow recompute happens. |
2056 | ++ * |
2057 | ++ * The "change_tracked" member of desired flow table maintains the status of |
2058 | ++ * whether flow changes are tracked or not. It is always set to true when |
2059 | ++ * ofctrl_put is completed, and transition to false whenever |
2060 | ++ * ovn_desired_flow_table_clear is called. |
2061 | ++ * |
2062 | ++ * NOTE: A tracked flow is just a reference to a desired flow, instead of a new |
2063 | ++ * copy. When a desired flow is removed and tracked, it is removed from the |
2064 | ++ * match_flow_table and uuid_flow_table indexes, and added to the tracked_flows |
2065 | ++ * list, marking is_deleted = true, but not immediately destroyed. It is |
2066 | ++ * destroyed when the tracking is processed for installed flow updates. |
2067 | + */ |
2068 | + struct desired_flow { |
2069 | + struct ovn_flow flow; |
2070 | +@@ -117,6 +150,11 @@ struct desired_flow { |
2071 | + |
2072 | + /* Node in installed_flow.desired_refs list. */ |
2073 | + struct ovs_list installed_ref_list_node; |
2074 | ++ |
2075 | ++ /* For tracking. */ |
2076 | ++ struct ovs_list track_list_node; /* node in ovn_desired_flow_table's |
2077 | ++ * tracked_flows list. */ |
2078 | ++ bool is_deleted; /* If the tracked flow is deleted. */ |
2079 | + }; |
2080 | + |
2081 | + struct sb_to_flow { |
2082 | +@@ -789,6 +827,62 @@ unlink_all_refs_for_installed_flow(struc |
2083 | + } |
2084 | + } |
2085 | + |
2086 | |
2087 | ++static void |
2088 | ++track_flow_add_or_modify(struct ovn_desired_flow_table *flow_table, |
2089 | ++ struct desired_flow *f) |
2090 | ++{ |
2091 | ++ if (!flow_table->change_tracked) { |
2092 | ++ return; |
2093 | ++ } |
2094 | ++ |
2095 | ++ /* If same node (flow adding/modifying) was tracked, remove it from |
2096 | ++ * tracking first. */ |
2097 | ++ if (!ovs_list_is_empty(&f->track_list_node)) { |
2098 | ++ ovs_list_remove(&f->track_list_node); |
2099 | ++ } |
2100 | ++ f->is_deleted = false; |
2101 | ++ ovs_list_push_back(&flow_table->tracked_flows, &f->track_list_node); |
2102 | ++ |
2103 | ++} |
2104 | ++ |
2105 | ++static void |
2106 | ++track_flow_del(struct ovn_desired_flow_table *flow_table, |
2107 | ++ struct desired_flow *f) |
2108 | ++{ |
2109 | ++ if (!flow_table->change_tracked) { |
2110 | ++ return; |
2111 | ++ } |
2112 | ++ /* If same node (flow adding/modifying) was tracked, remove it from |
2113 | ++ * tracking first. */ |
2114 | ++ if (!ovs_list_is_empty(&f->track_list_node)) { |
2115 | ++ ovs_list_remove(&f->track_list_node); |
2116 | ++ if (!f->installed_flow) { |
2117 | ++ /* If it is not installed yet, simply destroy it. */ |
2118 | ++ desired_flow_destroy(f); |
2119 | ++ return; |
2120 | ++ } |
2121 | ++ } |
2122 | ++ f->is_deleted = true; |
2123 | ++ ovs_list_push_back(&flow_table->tracked_flows, &f->track_list_node); |
2124 | ++} |
2125 | ++ |
2126 | ++/* When a desired flow is being removed, depending on "change_tracked", this |
2127 | ++ * function either unlinks a desired flow from installed flow and destroy it, |
2128 | ++ * or do nothing but track it. */ |
2129 | ++static void |
2130 | ++track_or_destroy_for_flow_del(struct ovn_desired_flow_table *flow_table, |
2131 | ++ struct desired_flow *f) |
2132 | ++{ |
2133 | ++ if (flow_table->change_tracked) { |
2134 | ++ track_flow_del(flow_table, f); |
2135 | ++ } else { |
2136 | ++ if (f->installed_flow) { |
2137 | ++ unlink_installed_to_desired(f->installed_flow, f); |
2138 | ++ } |
2139 | ++ desired_flow_destroy(f); |
2140 | ++ } |
2141 | ++} |
2142 | ++ |
2143 | |
2144 | + static struct sb_to_flow * |
2145 | + sb_to_flow_find(struct hmap *uuid_flow_table, const struct uuid *sb_uuid) |
2146 | + { |
2147 | +@@ -861,6 +955,7 @@ ofctrl_check_and_add_flow(struct ovn_des |
2148 | + hmap_insert(&flow_table->match_flow_table, &f->match_hmap_node, |
2149 | + f->flow.hash); |
2150 | + link_flow_to_sb(flow_table, f, sb_uuid); |
2151 | ++ track_flow_add_or_modify(flow_table, f); |
2152 | + ovn_flow_log(&f->flow, "ofctrl_add_flow"); |
2153 | + } |
2154 | + |
2155 | +@@ -912,6 +1007,7 @@ ofctrl_add_or_append_flow(struct ovn_des |
2156 | + f->flow.hash); |
2157 | + } |
2158 | + link_flow_to_sb(desired_flows, f, sb_uuid); |
2159 | ++ track_flow_add_or_modify(desired_flows, f); |
2160 | + |
2161 | + if (existing) { |
2162 | + ovn_flow_log(&f->flow, "ofctrl_add_or_append_flow (append)"); |
2163 | +@@ -941,10 +1037,7 @@ remove_flows_from_sb_to_flow(struct ovn_ |
2164 | + } |
2165 | + hmap_remove(&flow_table->match_flow_table, |
2166 | + &f->match_hmap_node); |
2167 | +- if (f->installed_flow) { |
2168 | +- unlink_installed_to_desired(f->installed_flow, f); |
2169 | +- } |
2170 | +- desired_flow_destroy(f); |
2171 | ++ track_or_destroy_for_flow_del(flow_table, f); |
2172 | + } |
2173 | + } |
2174 | + hmap_remove(&flow_table->uuid_flow_table, &stf->hmap_node); |
2175 | +@@ -1019,10 +1112,7 @@ flood_remove_flows_for_sb_uuid(struct ov |
2176 | + * be empty in most cases. */ |
2177 | + hmap_remove(&flow_table->match_flow_table, |
2178 | + &f->match_hmap_node); |
2179 | +- if (f->installed_flow) { |
2180 | +- unlink_installed_to_desired(f->installed_flow, f); |
2181 | +- } |
2182 | +- desired_flow_destroy(f); |
2183 | ++ track_or_destroy_for_flow_del(flow_table, f); |
2184 | + } else { |
2185 | + ovs_list_insert(&to_be_removed, &f->list_node); |
2186 | + } |
2187 | +@@ -1056,10 +1146,7 @@ flood_remove_flows_for_sb_uuid(struct ov |
2188 | + ovs_list_remove(&f->list_node); |
2189 | + hmap_remove(&flow_table->match_flow_table, |
2190 | + &f->match_hmap_node); |
2191 | +- if (f->installed_flow) { |
2192 | +- unlink_installed_to_desired(f->installed_flow, f); |
2193 | +- } |
2194 | +- desired_flow_destroy(f); |
2195 | ++ track_or_destroy_for_flow_del(flow_table, f); |
2196 | + } |
2197 | + |
2198 | + } |
2199 | +@@ -1105,7 +1192,9 @@ desired_flow_alloc(uint8_t table_id, uin |
2200 | + ovs_list_init(&f->references); |
2201 | + ovs_list_init(&f->list_node); |
2202 | + ovs_list_init(&f->installed_ref_list_node); |
2203 | ++ ovs_list_init(&f->track_list_node); |
2204 | + f->installed_flow = NULL; |
2205 | ++ f->is_deleted = false; |
2206 | + ovn_flow_init(&f->flow, table_id, priority, cookie, match, actions); |
2207 | + |
2208 | + return f; |
2209 | +@@ -1245,11 +1334,27 @@ ovn_desired_flow_table_init(struct ovn_d |
2210 | + { |
2211 | + hmap_init(&flow_table->match_flow_table); |
2212 | + hmap_init(&flow_table->uuid_flow_table); |
2213 | ++ ovs_list_init(&flow_table->tracked_flows); |
2214 | ++ flow_table->change_tracked = false; |
2215 | + } |
2216 | + |
2217 | + void |
2218 | + ovn_desired_flow_table_clear(struct ovn_desired_flow_table *flow_table) |
2219 | + { |
2220 | ++ flow_table->change_tracked = false; |
2221 | ++ |
2222 | ++ struct desired_flow *f, *f_next; |
2223 | ++ LIST_FOR_EACH_SAFE (f, f_next, track_list_node, |
2224 | ++ &flow_table->tracked_flows) { |
2225 | ++ ovs_list_remove(&f->track_list_node); |
2226 | ++ if (f->is_deleted) { |
2227 | ++ if (f->installed_flow) { |
2228 | ++ unlink_installed_to_desired(f->installed_flow, f); |
2229 | ++ } |
2230 | ++ desired_flow_destroy(f); |
2231 | ++ } |
2232 | ++ } |
2233 | ++ |
2234 | + struct sb_to_flow *stf, *next; |
2235 | + HMAP_FOR_EACH_SAFE (stf, next, hmap_node, |
2236 | + &flow_table->uuid_flow_table) { |
2237 | +@@ -1537,6 +1642,117 @@ installed_flow_del(struct ovn_flow *i, s |
2238 | + add_flow_mod(&fm, msgs); |
2239 | + } |
2240 | + |
2241 | ++static void |
2242 | ++update_installed_flows_by_compare(struct ovn_desired_flow_table *flow_table, |
2243 | ++ struct ovs_list *msgs) |
2244 | ++{ |
2245 | ++ ovs_assert(ovs_list_is_empty(&flow_table->tracked_flows)); |
2246 | ++ /* Iterate through all of the installed flows. If any of them are no |
2247 | ++ * longer desired, delete them; if any of them should have different |
2248 | ++ * actions, update them. */ |
2249 | ++ struct installed_flow *i, *next; |
2250 | ++ HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) { |
2251 | ++ unlink_all_refs_for_installed_flow(i); |
2252 | ++ struct desired_flow *d = |
2253 | ++ desired_flow_lookup(flow_table, &i->flow, NULL); |
2254 | ++ if (!d) { |
2255 | ++ /* Installed flow is no longer desirable. Delete it from the |
2256 | ++ * switch and from installed_flows. */ |
2257 | ++ installed_flow_del(&i->flow, msgs); |
2258 | ++ ovn_flow_log(&i->flow, "removing installed"); |
2259 | ++ |
2260 | ++ hmap_remove(&installed_flows, &i->match_hmap_node); |
2261 | ++ installed_flow_destroy(i); |
2262 | ++ } else { |
2263 | ++ if (!ofpacts_equal(i->flow.ofpacts, i->flow.ofpacts_len, |
2264 | ++ d->flow.ofpacts, d->flow.ofpacts_len) || |
2265 | ++ i->flow.cookie != d->flow.cookie) { |
2266 | ++ installed_flow_mod(&i->flow, &d->flow, msgs); |
2267 | ++ ovn_flow_log(&i->flow, "updating installed"); |
2268 | ++ } |
2269 | ++ link_installed_to_desired(i, d); |
2270 | ++ |
2271 | ++ } |
2272 | ++ } |
2273 | ++ |
2274 | ++ /* Iterate through the desired flows and add those that aren't found |
2275 | ++ * in the installed flow table. */ |
2276 | ++ struct desired_flow *d; |
2277 | ++ HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) { |
2278 | ++ i = installed_flow_lookup(&d->flow); |
2279 | ++ if (!i) { |
2280 | ++ ovn_flow_log(&d->flow, "adding installed"); |
2281 | ++ installed_flow_add(&d->flow, msgs); |
2282 | ++ |
2283 | ++ /* Copy 'd' from 'flow_table' to installed_flows. */ |
2284 | ++ i = installed_flow_dup(d); |
2285 | ++ hmap_insert(&installed_flows, &i->match_hmap_node, i->flow.hash); |
2286 | ++ } |
2287 | ++ link_installed_to_desired(i, d); |
2288 | ++ } |
2289 | ++} |
2290 | ++ |
2291 | ++static void |
2292 | ++update_installed_flows_by_track(struct ovn_desired_flow_table *flow_table, |
2293 | ++ struct ovs_list *msgs) |
2294 | ++{ |
2295 | ++ struct desired_flow *f, *f_next; |
2296 | ++ LIST_FOR_EACH_SAFE (f, f_next, track_list_node, |
2297 | ++ &flow_table->tracked_flows) { |
2298 | ++ ovs_list_remove(&f->track_list_node); |
2299 | ++ if (f->is_deleted) { |
2300 | ++ /* The desired flow was deleted */ |
2301 | ++ if (f->installed_flow) { |
2302 | ++ struct installed_flow *i = f->installed_flow; |
2303 | ++ unlink_installed_to_desired(i, f); |
2304 | ++ |
2305 | ++ if (!i->desired_flow) { |
2306 | ++ installed_flow_del(&i->flow, msgs); |
2307 | ++ ovn_flow_log(&i->flow, "removing installed (tracked)"); |
2308 | ++ |
2309 | ++ hmap_remove(&installed_flows, &i->match_hmap_node); |
2310 | ++ installed_flow_destroy(i); |
2311 | ++ } else { |
2312 | ++ /* There are other desired flow(s) referencing this |
2313 | ++ * installed flow, so update the OVS flow for the new |
2314 | ++ * active flow (at least the cookie will be different, |
2315 | ++ * even if the actions are the same). */ |
2316 | ++ struct desired_flow *d = i->desired_flow; |
2317 | ++ ovn_flow_log(&i->flow, "updating installed (tracked)"); |
2318 | ++ installed_flow_mod(&i->flow, &d->flow, msgs); |
2319 | ++ } |
2320 | ++ } |
2321 | ++ desired_flow_destroy(f); |
2322 | ++ } else { |
2323 | ++ /* The desired flow was added or modified. */ |
2324 | ++ struct installed_flow *i = installed_flow_lookup(&f->flow); |
2325 | ++ if (!i) { |
2326 | ++ /* Adding a new flow. */ |
2327 | ++ installed_flow_add(&f->flow, msgs); |
2328 | ++ ovn_flow_log(&f->flow, "adding installed (tracked)"); |
2329 | ++ |
2330 | ++ /* Copy 'f' from 'flow_table' to installed_flows. */ |
2331 | ++ struct installed_flow *new_node = installed_flow_dup(f); |
2332 | ++ hmap_insert(&installed_flows, &new_node->match_hmap_node, |
2333 | ++ new_node->flow.hash); |
2334 | ++ link_installed_to_desired(new_node, f); |
2335 | ++ } else if (i->desired_flow == f) { |
2336 | ++ /* The installed flow is installed for f, but f has change |
2337 | ++ * tracked, so it must have been modified. */ |
2338 | ++ ovn_flow_log(&i->flow, "updating installed (tracked)"); |
2339 | ++ installed_flow_mod(&i->flow, &f->flow, msgs); |
2340 | ++ } else { |
2341 | ++ /* Adding a new flow that conflicts with an existing installed |
2342 | ++ * flow, so just add it to the link. */ |
2343 | ++ link_installed_to_desired(i, f); |
2344 | ++ } |
2345 | ++ /* The track_list_node emptyness is used to check if the node is |
2346 | ++ * already added to track list, so initialize it again here. */ |
2347 | ++ ovs_list_init(&f->track_list_node); |
2348 | ++ } |
2349 | ++ } |
2350 | ++} |
2351 | ++ |
2352 | + /* The flow table can be updated if the connection to the switch is up and |
2353 | + * in the correct state and not backlogged with existing flow_mods. (Our |
2354 | + * criteria for being backlogged appear very conservative, but the socket |
2355 | +@@ -1651,48 +1867,10 @@ ofctrl_put(struct ovn_desired_flow_table |
2356 | + } |
2357 | + } |
2358 | + |
2359 | +- /* Iterate through all of the installed flows. If any of them are no |
2360 | +- * longer desired, delete them; if any of them should have different |
2361 | +- * actions, update them. */ |
2362 | +- struct installed_flow *i, *next; |
2363 | +- HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) { |
2364 | +- unlink_all_refs_for_installed_flow(i); |
2365 | +- struct desired_flow *d = desired_flow_lookup(flow_table, &i->flow, |
2366 | +- NULL); |
2367 | +- if (!d) { |
2368 | +- /* Installed flow is no longer desirable. Delete it from the |
2369 | +- * switch and from installed_flows. */ |
2370 | +- installed_flow_del(&i->flow, &msgs); |
2371 | +- ovn_flow_log(&i->flow, "removing installed"); |
2372 | +- hmap_remove(&installed_flows, &i->match_hmap_node); |
2373 | +- installed_flow_destroy(i); |
2374 | +- } else { |
2375 | +- if (!ofpacts_equal(i->flow.ofpacts, i->flow.ofpacts_len, |
2376 | +- d->flow.ofpacts, d->flow.ofpacts_len) || |
2377 | +- i->flow.cookie != d->flow.cookie) { |
2378 | +- ovn_flow_log(&i->flow, "updating installed"); |
2379 | +- installed_flow_mod(&i->flow, &d->flow, &msgs); |
2380 | +- } |
2381 | +- link_installed_to_desired(i, d); |
2382 | +- |
2383 | +- } |
2384 | +- } |
2385 | +- |
2386 | +- /* Iterate through the desired flows and add those that aren't found |
2387 | +- * in the installed flow table. */ |
2388 | +- struct desired_flow *d; |
2389 | +- HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) { |
2390 | +- i = installed_flow_lookup(&d->flow); |
2391 | +- if (!i) { |
2392 | +- installed_flow_add(&d->flow, &msgs); |
2393 | +- ovn_flow_log(&d->flow, "adding installed"); |
2394 | +- |
2395 | +- /* Copy 'd' from 'flow_table' to installed_flows. */ |
2396 | +- i = installed_flow_dup(d); |
2397 | +- hmap_insert(&installed_flows, &i->match_hmap_node, |
2398 | +- i->flow.hash); |
2399 | +- } |
2400 | +- link_installed_to_desired(i, d); |
2401 | ++ if (flow_table->change_tracked) { |
2402 | ++ update_installed_flows_by_track(flow_table, &msgs); |
2403 | ++ } else { |
2404 | ++ update_installed_flows_by_compare(flow_table, &msgs); |
2405 | + } |
2406 | + |
2407 | + /* Iterate through the installed groups from previous runs. If they |
2408 | +@@ -1806,6 +1984,9 @@ ofctrl_put(struct ovn_desired_flow_table |
2409 | + /* We were completely up-to-date before and still are. */ |
2410 | + cur_cfg = nb_cfg; |
2411 | + } |
2412 | ++ |
2413 | ++ flow_table->change_tracked = true; |
2414 | ++ ovs_assert(ovs_list_is_empty(&flow_table->tracked_flows)); |
2415 | + } |
2416 | + |
2417 | + /* Looks up the logical port with the name 'port_name' in 'br_int_'. If |
2418 | +Index: ovn-20.06.2/controller/ofctrl.h |
2419 | +=================================================================== |
2420 | +--- ovn-20.06.2.orig/controller/ofctrl.h |
2421 | ++++ ovn-20.06.2/controller/ofctrl.h |
2422 | +@@ -38,6 +38,11 @@ struct ovn_desired_flow_table { |
2423 | + /* SB uuid index for the cross reference nodes that link to the nodes in |
2424 | + * match_flow_table.*/ |
2425 | + struct hmap uuid_flow_table; |
2426 | ++ |
2427 | ++ /* Is flow changes tracked. */ |
2428 | ++ bool change_tracked; |
2429 | ++ /* Tracked flow changes. */ |
2430 | ++ struct ovs_list tracked_flows; |
2431 | + }; |
2432 | + |
2433 | + /* Interface for OVN main loop. */ |
2434 | +@@ -99,7 +104,6 @@ void ofctrl_flood_remove_flows(struct ov |
2435 | + struct hmap *flood_remove_nodes); |
2436 | + void ofctrl_flood_remove_add_node(struct hmap *flood_remove_nodes, |
2437 | + const struct uuid *sb_uuid); |
2438 | +- |
2439 | + void ovn_desired_flow_table_init(struct ovn_desired_flow_table *); |
2440 | + void ovn_desired_flow_table_clear(struct ovn_desired_flow_table *); |
2441 | + void ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *); |
2442 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-06.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-06.patch |
2443 | new file mode 100644 |
2444 | index 0000000..ce90c33 |
2445 | --- /dev/null |
2446 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-06.patch |
2447 | @@ -0,0 +1,109 @@ |
2448 | +Description: Cherry-pick fix applied to master and branch-20.12 |
2449 | +Origin: https://github.com/ovn-org/ovn/commit/f4e508dd7a6cfbfc2e3250a8c11a8d0fdc1dfdd0 |
2450 | +Applied-Upstream: commit: f4e508dd7a6cfbfc2e3250a8c11a8d0fdc1dfdd0 |
2451 | + |
2452 | +From c9d0a9e69cbbbb43512a10c1775b21e3461e57f3 Mon Sep 17 00:00:00 2001 |
2453 | +From: Han Zhou <hzhou@ovn.org> |
2454 | +Date: Thu, 20 Aug 2020 17:04:04 -0700 |
2455 | +Subject: [PATCH 06/15] ofctrl.c: Merge opposite changes of tracked flows |
2456 | + before installing. |
2457 | + |
2458 | +This patch optimizes the previous patch that incrementally processes |
2459 | +flow installation by merging the "add-after-delete" flow changes as |
2460 | +much as possible to avoid unnecessary OpenFlow updates. |
2461 | + |
2462 | +Acked-by: Mark Michelson <mmichels@redhat.com> |
2463 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
2464 | +--- |
2465 | + controller/ofctrl.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ |
2466 | + 1 file changed, 74 insertions(+) |
2467 | + |
2468 | +Index: ovn-20.06.2/controller/ofctrl.c |
2469 | +=================================================================== |
2470 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
2471 | ++++ ovn-20.06.2/controller/ofctrl.c |
2472 | +@@ -1692,10 +1692,84 @@ update_installed_flows_by_compare(struct |
2473 | + } |
2474 | + } |
2475 | + |
2476 | ++/* Finds and returns a desired_flow in 'deleted_flows' that is exactly the |
2477 | ++ * same as 'target', including cookie and actions. |
2478 | ++ */ |
2479 | ++static struct desired_flow * |
2480 | ++deleted_flow_lookup(struct hmap *deleted_flows, struct ovn_flow *target) |
2481 | ++{ |
2482 | ++ struct desired_flow *d; |
2483 | ++ HMAP_FOR_EACH_WITH_HASH (d, match_hmap_node, target->hash, |
2484 | ++ deleted_flows) { |
2485 | ++ struct ovn_flow *f = &d->flow; |
2486 | ++ if (f->table_id == target->table_id |
2487 | ++ && f->priority == target->priority |
2488 | ++ && minimatch_equal(&f->match, &target->match) |
2489 | ++ && f->cookie == target->cookie |
2490 | ++ && ofpacts_equal(f->ofpacts, f->ofpacts_len, target->ofpacts, |
2491 | ++ target->ofpacts_len)) { |
2492 | ++ return d; |
2493 | ++ } |
2494 | ++ } |
2495 | ++ return NULL; |
2496 | ++} |
2497 | ++ |
2498 | ++/* This function scans the tracked flow changes in the order and merges "add" |
2499 | ++ * or "update" after "deleted" operations for exactly same flow (priority, |
2500 | ++ * table, match, action and cookie), to avoid unnecessary OF messages being |
2501 | ++ * sent to OVS. */ |
2502 | ++static void |
2503 | ++merge_tracked_flows(struct ovn_desired_flow_table *flow_table) |
2504 | ++{ |
2505 | ++ struct hmap deleted_flows = HMAP_INITIALIZER(&deleted_flows); |
2506 | ++ struct desired_flow *f, *next; |
2507 | ++ LIST_FOR_EACH_SAFE (f, next, track_list_node, |
2508 | ++ &flow_table->tracked_flows) { |
2509 | ++ if (f->is_deleted) { |
2510 | ++ /* reuse f->match_hmap_node field since it is already removed from |
2511 | ++ * the desired flow table's match index. */ |
2512 | ++ hmap_insert(&deleted_flows, &f->match_hmap_node, |
2513 | ++ f->flow.hash); |
2514 | ++ } else { |
2515 | ++ struct desired_flow *del_f = deleted_flow_lookup(&deleted_flows, |
2516 | ++ &f->flow); |
2517 | ++ if (!del_f) { |
2518 | ++ continue; |
2519 | ++ } |
2520 | ++ |
2521 | ++ /* del_f must have been installed, otherwise it should have been |
2522 | ++ * removed during track_flow_add_or_modify. */ |
2523 | ++ ovs_assert(del_f->installed_flow); |
2524 | ++ if (!f->installed_flow) { |
2525 | ++ /* f is not installed yet. */ |
2526 | ++ struct installed_flow *i = del_f->installed_flow; |
2527 | ++ unlink_installed_to_desired(i, del_f); |
2528 | ++ link_installed_to_desired(i, f); |
2529 | ++ } else { |
2530 | ++ /* f has been installed before, and now was updated to exact |
2531 | ++ * the same flow as del_f. */ |
2532 | ++ ovs_assert(f->installed_flow == del_f->installed_flow); |
2533 | ++ unlink_installed_to_desired(del_f->installed_flow, del_f); |
2534 | ++ } |
2535 | ++ hmap_remove(&deleted_flows, &del_f->match_hmap_node); |
2536 | ++ ovs_list_remove(&del_f->track_list_node); |
2537 | ++ desired_flow_destroy(del_f); |
2538 | ++ |
2539 | ++ ovs_list_remove(&f->track_list_node); |
2540 | ++ ovs_list_init(&f->track_list_node); |
2541 | ++ } |
2542 | ++ } |
2543 | ++ HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &deleted_flows) { |
2544 | ++ hmap_remove(&deleted_flows, &f->match_hmap_node); |
2545 | ++ } |
2546 | ++ hmap_destroy(&deleted_flows); |
2547 | ++} |
2548 | ++ |
2549 | + static void |
2550 | + update_installed_flows_by_track(struct ovn_desired_flow_table *flow_table, |
2551 | + struct ovs_list *msgs) |
2552 | + { |
2553 | ++ merge_tracked_flows(flow_table); |
2554 | + struct desired_flow *f, *f_next; |
2555 | + LIST_FOR_EACH_SAFE (f, f_next, track_list_node, |
2556 | + &flow_table->tracked_flows) { |
2557 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-07.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-07.patch |
2558 | new file mode 100644 |
2559 | index 0000000..fd05b47 |
2560 | --- /dev/null |
2561 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-07.patch |
2562 | @@ -0,0 +1,233 @@ |
2563 | +Description: Cherry-pick fix applied to master and branch-20.12 |
2564 | +Origin: https://github.com/ovn-org/ovn/commit/9d2e8d32fb9865513b70408a665184a67564390d |
2565 | +Applied-Upstream: commit: 9d2e8d32fb9865513b70408a665184a67564390d |
2566 | + |
2567 | +From 6e6ba2742f673caaba3c0fa868e0090ea5ed8466 Mon Sep 17 00:00:00 2001 |
2568 | +From: Han Zhou <hzhou@ovn.org> |
2569 | +Date: Thu, 1 Oct 2020 22:49:04 -0700 |
2570 | +Subject: [PATCH 07/15] ofctrl.c: Fix duplicated flow handling in I-P while |
2571 | + merging opposite changes. |
2572 | + |
2573 | +In a scenario in I-P when a desired flow is removed and an exactly same flow is |
2574 | +added back in the same iteration, the merge_tracked_flows() function will merge |
2575 | +the operation without firing any OpenFlow action. However, if there are |
2576 | +multiple desired flows (e.g. F1 and F2) matching the same installed flow, and |
2577 | +if the one being re-added (say, F1) is the one currently installed in OVS, the |
2578 | +current implementation will update the currently installed flow to F2 in the |
2579 | +data structure while the actual OVS flow is not updated (still F1). So far |
2580 | +there is still no problem, but the member desired_flow of the installed flow |
2581 | +is pointing to the wrong desired flow. |
2582 | + |
2583 | +Now there is only one place where the desired_flow member is consulted, in |
2584 | +update_installed_flows_by_track() for handling a tracked *modified* flow. In |
2585 | +reality flow modification happens only when conjunction flows need to be |
2586 | +combined, which would never happen together with the above scenario of |
2587 | +merge_tracked_flows(). So this wrong desired_flow pointer doesn't cause any |
2588 | +real problem yet. |
2589 | + |
2590 | +However, there is a place that can already utilize this active desired_flow |
2591 | +information, which is when handling a tracked flow deletion in |
2592 | +update_installed_flows_by_track(): we can check if the flow being deleted is |
2593 | +the currently active one installed in OVS. If not, we don't need to send and |
2594 | +OpenFlow modify to OVS. This optimization is added in this patch, apart from |
2595 | +fixing the problem of merge_tracked_flows(). Without the fix, this optimization |
2596 | +would cause the test case added in this patch fail. |
2597 | + |
2598 | +Fixes: f4e508dd7 ("ofctrl.c: Merge opposite changes of tracked flows before installing.") |
2599 | +Acked-by: Dumitru Ceara <dceara@redhat.com> |
2600 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
2601 | +--- |
2602 | + controller/ofctrl.c | 28 +++++++++++- |
2603 | + tests/ovn.at | 108 ++++++++++++++++++++++++++++++++++++++++++++ |
2604 | + 2 files changed, 134 insertions(+), 2 deletions(-) |
2605 | + |
2606 | +Index: ovn-20.06.2/controller/ofctrl.c |
2607 | +=================================================================== |
2608 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
2609 | ++++ ovn-20.06.2/controller/ofctrl.c |
2610 | +@@ -788,6 +788,24 @@ ofctrl_recv(const struct ofp_header *oh, |
2611 | + } |
2612 | + } |
2613 | + |
2614 | |
2615 | ++/* Returns true if a desired flow is active (the one currently installed |
2616 | ++ * among the list of desired flows that are linked to the same installed |
2617 | ++ * flow). */ |
2618 | ++static inline bool |
2619 | ++desired_flow_is_active(struct desired_flow *d) |
2620 | ++{ |
2621 | ++ return (d->installed_flow && d->installed_flow->desired_flow == d); |
2622 | ++} |
2623 | ++ |
2624 | ++/* Set a desired flow as the active one among the list of desired flows |
2625 | ++ * that are linked to the same installed flow. */ |
2626 | ++static inline void |
2627 | ++desired_flow_set_active(struct desired_flow *d) |
2628 | ++{ |
2629 | ++ ovs_assert(d->installed_flow); |
2630 | ++ d->installed_flow->desired_flow = d; |
2631 | ++} |
2632 | ++ |
2633 | + static void |
2634 | + link_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
2635 | + { |
2636 | +@@ -1740,6 +1758,8 @@ merge_tracked_flows(struct ovn_desired_f |
2637 | + /* del_f must have been installed, otherwise it should have been |
2638 | + * removed during track_flow_add_or_modify. */ |
2639 | + ovs_assert(del_f->installed_flow); |
2640 | ++ |
2641 | ++ bool del_f_was_active = desired_flow_is_active(del_f); |
2642 | + if (!f->installed_flow) { |
2643 | + /* f is not installed yet. */ |
2644 | + struct installed_flow *i = del_f->installed_flow; |
2645 | +@@ -1751,6 +1771,9 @@ merge_tracked_flows(struct ovn_desired_f |
2646 | + ovs_assert(f->installed_flow == del_f->installed_flow); |
2647 | + unlink_installed_to_desired(del_f->installed_flow, del_f); |
2648 | + } |
2649 | ++ if (del_f_was_active) { |
2650 | ++ desired_flow_set_active(f); |
2651 | ++ } |
2652 | + hmap_remove(&deleted_flows, &del_f->match_hmap_node); |
2653 | + ovs_list_remove(&del_f->track_list_node); |
2654 | + desired_flow_destroy(del_f); |
2655 | +@@ -1778,6 +1801,7 @@ update_installed_flows_by_track(struct o |
2656 | + /* The desired flow was deleted */ |
2657 | + if (f->installed_flow) { |
2658 | + struct installed_flow *i = f->installed_flow; |
2659 | ++ bool was_active = desired_flow_is_active(f); |
2660 | + unlink_installed_to_desired(i, f); |
2661 | + |
2662 | + if (!i->desired_flow) { |
2663 | +@@ -1786,7 +1810,7 @@ update_installed_flows_by_track(struct o |
2664 | + |
2665 | + hmap_remove(&installed_flows, &i->match_hmap_node); |
2666 | + installed_flow_destroy(i); |
2667 | +- } else { |
2668 | ++ } else if (was_active) { |
2669 | + /* There are other desired flow(s) referencing this |
2670 | + * installed flow, so update the OVS flow for the new |
2671 | + * active flow (at least the cookie will be different, |
2672 | +@@ -1810,7 +1834,7 @@ update_installed_flows_by_track(struct o |
2673 | + hmap_insert(&installed_flows, &new_node->match_hmap_node, |
2674 | + new_node->flow.hash); |
2675 | + link_installed_to_desired(new_node, f); |
2676 | +- } else if (i->desired_flow == f) { |
2677 | ++ } else if (desired_flow_is_active(f)) { |
2678 | + /* The installed flow is installed for f, but f has change |
2679 | + * tracked, so it must have been modified. */ |
2680 | + ovn_flow_log(&i->flow, "updating installed (tracked)"); |
2681 | +Index: ovn-20.06.2/tests/ovn.at |
2682 | +=================================================================== |
2683 | +--- ovn-20.06.2.orig/tests/ovn.at |
2684 | ++++ ovn-20.06.2/tests/ovn.at |
2685 | +@@ -20737,3 +20737,111 @@ AT_CHECK([ovn-sbctl find mac ip=10.0.0.2 |
2686 | + OVN_CLEANUP([hv1],[hv2]) |
2687 | + |
2688 | + AT_CLEANUP |
2689 | ++ |
2690 | ++# This test cases tests a scenario of ACL confliction with address set update. |
2691 | ++# It is to cover a corner case when flows are re-processed in the I-P |
2692 | ++# iteration, combined with the scenario of conflicting ACLs. |
2693 | ++AT_SETUP([ovn -- conflict ACLs with address set]) |
2694 | ++ovn_start |
2695 | ++ |
2696 | ++ovn-nbctl ls-add ls1 |
2697 | ++ |
2698 | ++ovn-nbctl lsp-add ls1 lsp1 \ |
2699 | ++-- lsp-set-addresses lsp1 "f0:00:00:00:00:01 10.0.0.1" |
2700 | ++ |
2701 | ++ovn-nbctl lsp-add ls1 lsp2 \ |
2702 | ++-- lsp-set-addresses lsp2 "f0:00:00:00:00:02 10.0.0.2" |
2703 | ++ |
2704 | ++net_add n1 |
2705 | ++sim_add hv1 |
2706 | ++ |
2707 | ++as hv1 |
2708 | ++ovs-vsctl add-br br-phys |
2709 | ++ovn_attach n1 br-phys 192.168.0.1 |
2710 | ++ovs-vsctl -- add-port br-int hv1-vif1 -- \ |
2711 | ++ set interface hv1-vif1 external-ids:iface-id=lsp1 \ |
2712 | ++ options:tx_pcap=hv1/vif1-tx.pcap \ |
2713 | ++ options:rxq_pcap=hv1/vif1-rx.pcap \ |
2714 | ++ ofport-request=1 |
2715 | ++ |
2716 | ++ovs-vsctl -- add-port br-int hv1-vif2 -- \ |
2717 | ++ set interface hv1-vif2 external-ids:iface-id=lsp2 \ |
2718 | ++ options:tx_pcap=hv1/vif2-tx.pcap \ |
2719 | ++ options:rxq_pcap=hv1/vif2-rx.pcap \ |
2720 | ++ ofport-request=2 |
2721 | ++ |
2722 | ++# Default drop |
2723 | ++ovn-nbctl acl-add ls1 to-lport 1000 \ |
2724 | ++'outport == "lsp1" && ip4' drop |
2725 | ++ |
2726 | ++# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT... |
2727 | ++# |
2728 | ++# This shell function causes an ip packet to be received on INPORT. |
2729 | ++# The packet's content has Ethernet destination DST and source SRC |
2730 | ++# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits). |
2731 | ++# The OUTPORTs (zero or more) list the VIFs on which the packet should |
2732 | ++# be received. INPORT and the OUTPORTs are specified as logical switch |
2733 | ++# port numbers, e.g. 11 for vif11. |
2734 | ++test_ip() { |
2735 | ++ # This packet has bad checksums but logical L3 routing doesn't check. |
2736 | ++ local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 |
2737 | ++ local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}\ |
2738 | ++${dst_ip}0035111100080000 |
2739 | ++ shift; shift; shift; shift; shift |
2740 | ++ as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $packet |
2741 | ++ for outport; do |
2742 | ++ echo $packet >> $outport.expected |
2743 | ++ done |
2744 | ++} |
2745 | ++ |
2746 | ++ip_to_hex() { |
2747 | ++ printf "%02x%02x%02x%02x" "$@" |
2748 | ++} |
2749 | ++ |
2750 | ++reset_pcap_file() { |
2751 | ++ local iface=$1 |
2752 | ++ local pcap_file=$2 |
2753 | ++ ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ |
2754 | ++options:rxq_pcap=dummy-rx.pcap |
2755 | ++ rm -f ${pcap_file}*.pcap |
2756 | ++ ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ |
2757 | ++options:rxq_pcap=${pcap_file}-rx.pcap |
2758 | ++} |
2759 | ++ |
2760 | ++# Create an address set |
2761 | ++ovn-nbctl create Address_Set name=as1 \ |
2762 | ++addresses=\"10.0.0.2\",\"10.0.0.3\" |
2763 | ++ |
2764 | ++# Create overlapping ACLs resulting in conflict desired OVS flows |
2765 | ++# Add ACL1 uses the address set |
2766 | ++ovn-nbctl --wait=hv acl-add ls1 to-lport 1001 \ |
2767 | ++'outport == "lsp1" && ip4 && ip4.src == $as1' allow |
2768 | ++ |
2769 | ++# Add ACL2 which uses a single IP, which shouldn't take effect because |
2770 | ++# when it is added incrementally there is already a conflict one installed. |
2771 | ++ovn-nbctl --wait=hv acl-add ls1 to-lport 1001 \ |
2772 | ++'outport == "lsp1" && ip4 && ip4.src == 10.0.0.2' drop |
2773 | ++ |
2774 | ++ |
2775 | ++sip=`ip_to_hex 10 0 0 2` |
2776 | ++dip=`ip_to_hex 10 0 0 1` |
2777 | ++test_ip 2 f00000000002 f00000000001 $sip $dip 1 |
2778 | ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected]) |
2779 | ++ |
2780 | ++# Update the address set which causes flow reprocessing but the OVS flow |
2781 | ++# for allowing 10.0.0.2 should keep unchanged |
2782 | ++ovn-nbctl --wait=hv set Address_Set as1 addresses=\"10.0.0.2\" |
2783 | ++ |
2784 | ++test_ip 2 f00000000002 f00000000001 $sip $dip 1 |
2785 | ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected]) |
2786 | ++ |
2787 | ++# Delete the ACL1 that has "allow" action |
2788 | ++ovn-nbctl acl-del ls1 to-lport 1001 \ |
2789 | ++'outport == "lsp1" && ip4 && ip4.src == $as1' |
2790 | ++ |
2791 | ++# ACL2 should take effect and packet should be dropped |
2792 | ++test_ip 2 f00000000002 f00000000001 $sip $dip |
2793 | ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected]) |
2794 | ++ |
2795 | ++OVN_CLEANUP([hv1]) |
2796 | ++AT_CLEANUP |
2797 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-08.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-08.patch |
2798 | new file mode 100644 |
2799 | index 0000000..b634e5d |
2800 | --- /dev/null |
2801 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-08.patch |
2802 | @@ -0,0 +1,90 @@ |
2803 | +Description: Cherry-pick fix applied to master and branch-20.12 |
2804 | +Origin: https://github.com/ovn-org/ovn/commit/7cab7bd1268ba67429954da4f73de91090acf779 |
2805 | +Applied-Upstream: commit: 7cab7bd1268ba67429954da4f73de91090acf779 |
2806 | + |
2807 | +From c48afa3159c614442c0b08a9edaf3a24bab3b515 Mon Sep 17 00:00:00 2001 |
2808 | +From: Han Zhou <hzhou@ovn.org> |
2809 | +Date: Fri, 2 Oct 2020 12:47:52 -0700 |
2810 | +Subject: [PATCH 08/15] ofctrl.c: Avoid repeatedly linking an installed flow |
2811 | + and a desired flow. |
2812 | + |
2813 | +In update_installed_flows_by_compare() there are two loops. The first |
2814 | +loop iterates the installed flows and find its peer in desired flows to: |
2815 | + |
2816 | +1. uninstall flows that are not needed anymore |
2817 | + |
2818 | +2. update flows if needed |
2819 | + |
2820 | +At the same time, it links the desired flow found for the installed flow |
2821 | +which also set the desired flow as the current active installed flow. |
2822 | + |
2823 | +The second loop iterates the desired flows and find its peer in installed |
2824 | +flows to install missing flows. At the same time it will detect if there |
2825 | +are conflict desired flows matching same installed flow then just link |
2826 | +them. |
2827 | + |
2828 | +However, currently in the second loop, it blindly link the desired flows to the |
2829 | +installed flows, without checking if it is already linked in the first loop. |
2830 | +Lucky enough, this won't cause any real problem so far, because when there are |
2831 | +conflict flows, the one found in the first loop will be set as active in the |
2832 | +installed_flow, and in the function link_installed_to_desired() checks if it is |
2833 | +already the active desired flow it just does nothing but return. However, the |
2834 | +check in the link_installed_to_desired() is confusing because a desired_flow |
2835 | +may be linked to the installed_flow already but not the active flow, and the |
2836 | +check is insufficient. It should be rather an assertion and let the caller |
2837 | +ensure that a pair of desired_flow and installed_flow is never linked twice. |
2838 | + |
2839 | +For the above reason, this patch does the following changes: |
2840 | + |
2841 | +1. Removes the check in link_installed_to_desired() and convert it to an assert. |
2842 | + |
2843 | +2. Before calling link_installed_to_desired() in the above mentioned loop, |
2844 | + check if the desired flow is already installed. |
2845 | + |
2846 | +Acked-by: Dumitru Ceara <dceara@redhat.com> |
2847 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
2848 | +--- |
2849 | + controller/ofctrl.c | 19 ++++++++++++++----- |
2850 | + 1 file changed, 14 insertions(+), 5 deletions(-) |
2851 | + |
2852 | +Index: ovn-20.06.2/controller/ofctrl.c |
2853 | +=================================================================== |
2854 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
2855 | ++++ ovn-20.06.2/controller/ofctrl.c |
2856 | +@@ -806,13 +806,18 @@ desired_flow_set_active(struct desired_f |
2857 | + d->installed_flow->desired_flow = d; |
2858 | + } |
2859 | + |
2860 | ++/* Adds the desired flow to the list of desired flows that have same match |
2861 | ++ * conditions as the installed flow. |
2862 | ++ * |
2863 | ++ * If the newly added desired flow is the first one in the list, it is also set |
2864 | ++ * as the active one. |
2865 | ++ * |
2866 | ++ * It is caller's responsibility to make sure the link between the pair didn't |
2867 | ++ * exist before. */ |
2868 | + static void |
2869 | + link_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
2870 | + { |
2871 | +- if (i->desired_flow == d) { |
2872 | +- return; |
2873 | +- } |
2874 | +- |
2875 | ++ ovs_assert(i->desired_flow != d); |
2876 | + if (ovs_list_is_empty(&i->desired_refs)) { |
2877 | + ovs_assert(!i->desired_flow); |
2878 | + i->desired_flow = d; |
2879 | +@@ -1705,8 +1710,12 @@ update_installed_flows_by_compare(struct |
2880 | + /* Copy 'd' from 'flow_table' to installed_flows. */ |
2881 | + i = installed_flow_dup(d); |
2882 | + hmap_insert(&installed_flows, &i->match_hmap_node, i->flow.hash); |
2883 | ++ link_installed_to_desired(i, d); |
2884 | ++ } else if (!d->installed_flow) { |
2885 | ++ /* This is a desired_flow that conflicts with one installed |
2886 | ++ * previously but not linked yet. */ |
2887 | ++ link_installed_to_desired(i, d); |
2888 | + } |
2889 | +- link_installed_to_desired(i, d); |
2890 | + } |
2891 | + } |
2892 | + |
2893 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-09.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-09.patch |
2894 | new file mode 100644 |
2895 | index 0000000..66afff8 |
2896 | --- /dev/null |
2897 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-09.patch |
2898 | @@ -0,0 +1,216 @@ |
2899 | +Description: Cherry-pick fix applied to master and branch-20.12 |
2900 | +Origin: https://github.com/ovn-org/ovn/commit/dadae4f800ccb1f2759378f0bd804dd002e31605 |
2901 | +Applied-Upstream: commit: dadae4f800ccb1f2759378f0bd804dd002e31605 |
2902 | + |
2903 | +From 89773054ea87ca8477e8a4ae6daea1258cf28a6a Mon Sep 17 00:00:00 2001 |
2904 | +From: Dumitru Ceara <dceara@redhat.com> |
2905 | +Date: Sun, 11 Oct 2020 14:05:31 +0200 |
2906 | +Subject: [PATCH 09/15] ofctrl.c: Only merge actions for conjunctive flows. |
2907 | + |
2908 | +In ofctrl_add_or_append_flow() when merging flow actions make sure we only |
2909 | +do that for conjunctive flows. All other actions can not be merged with |
2910 | +action "conjunction". |
2911 | + |
2912 | +CC: Mark Michelson <mmichels@redhat.com> |
2913 | +Fixes: e659bab31a91 ("Combine conjunctions with identical matches into one flow.") |
2914 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
2915 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
2916 | +--- |
2917 | + controller/ofctrl.c | 124 +++++++++++++++++++++++++++++++++++--------- |
2918 | + 1 file changed, 99 insertions(+), 25 deletions(-) |
2919 | + |
2920 | +Index: ovn-20.06.2/controller/ofctrl.c |
2921 | +=================================================================== |
2922 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
2923 | ++++ ovn-20.06.2/controller/ofctrl.c |
2924 | +@@ -206,6 +206,9 @@ struct installed_flow { |
2925 | + struct desired_flow *desired_flow; |
2926 | + }; |
2927 | + |
2928 | ++typedef bool |
2929 | ++(*desired_flow_match_cb)(const struct desired_flow *candidate, |
2930 | ++ const void *arg); |
2931 | + static struct desired_flow *desired_flow_alloc( |
2932 | + uint8_t table_id, |
2933 | + uint16_t priority, |
2934 | +@@ -214,8 +217,14 @@ static struct desired_flow *desired_flow |
2935 | + const struct ofpbuf *actions); |
2936 | + static struct desired_flow *desired_flow_lookup( |
2937 | + struct ovn_desired_flow_table *, |
2938 | ++ const struct ovn_flow *target); |
2939 | ++static struct desired_flow *desired_flow_lookup_check_uuid( |
2940 | ++ struct ovn_desired_flow_table *, |
2941 | + const struct ovn_flow *target, |
2942 | +- const struct uuid *sb_uuid); |
2943 | ++ const struct uuid *); |
2944 | ++static struct desired_flow *desired_flow_lookup_conjunctive( |
2945 | ++ struct ovn_desired_flow_table *, |
2946 | ++ const struct ovn_flow *target); |
2947 | + static void desired_flow_destroy(struct desired_flow *); |
2948 | + |
2949 | + static struct installed_flow *installed_flow_lookup( |
2950 | +@@ -806,6 +815,19 @@ desired_flow_set_active(struct desired_f |
2951 | + d->installed_flow->desired_flow = d; |
2952 | + } |
2953 | + |
2954 | ++static bool |
2955 | ++flow_action_has_conj(const struct ovn_flow *f) |
2956 | ++{ |
2957 | ++ const struct ofpact *a = NULL; |
2958 | ++ |
2959 | ++ OFPACT_FOR_EACH (a, f->ofpacts, f->ofpacts_len) { |
2960 | ++ if (a->type == OFPACT_CONJUNCTION) { |
2961 | ++ return true; |
2962 | ++ } |
2963 | ++ } |
2964 | ++ return false; |
2965 | ++} |
2966 | ++ |
2967 | + /* Adds the desired flow to the list of desired flows that have same match |
2968 | + * conditions as the installed flow. |
2969 | + * |
2970 | +@@ -962,7 +984,7 @@ ofctrl_check_and_add_flow(struct ovn_des |
2971 | + struct desired_flow *f = desired_flow_alloc(table_id, priority, cookie, |
2972 | + match, actions); |
2973 | + |
2974 | +- if (desired_flow_lookup(flow_table, &f->flow, sb_uuid)) { |
2975 | ++ if (desired_flow_lookup_check_uuid(flow_table, &f->flow, sb_uuid)) { |
2976 | + if (log_duplicate_flow) { |
2977 | + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); |
2978 | + if (!VLOG_DROP_DBG(&rl)) { |
2979 | +@@ -1002,14 +1024,15 @@ ofctrl_add_or_append_flow(struct ovn_des |
2980 | + const struct ofpbuf *actions, |
2981 | + const struct uuid *sb_uuid) |
2982 | + { |
2983 | +- struct desired_flow *f = desired_flow_alloc(table_id, priority, cookie, |
2984 | +- match, actions); |
2985 | +- |
2986 | + struct desired_flow *existing; |
2987 | +- existing = desired_flow_lookup(desired_flows, &f->flow, NULL); |
2988 | ++ struct desired_flow *f; |
2989 | ++ |
2990 | ++ f = desired_flow_alloc(table_id, priority, cookie, match, actions); |
2991 | ++ existing = desired_flow_lookup_conjunctive(desired_flows, &f->flow); |
2992 | + if (existing) { |
2993 | +- /* There's already a flow with this particular match. Append the |
2994 | +- * action to that flow rather than adding a new flow |
2995 | ++ /* There's already a flow with this particular match and action |
2996 | ++ * 'conjunction'. Append the action to that flow rather than |
2997 | ++ * adding a new flow. |
2998 | + */ |
2999 | + uint64_t compound_stub[64 / 8]; |
3000 | + struct ofpbuf compound; |
3001 | +@@ -1248,15 +1271,11 @@ installed_flow_dup(struct desired_flow * |
3002 | + return dst; |
3003 | + } |
3004 | + |
3005 | +-/* Finds and returns a desired_flow in 'flow_table' whose key is identical to |
3006 | +- * 'target''s key, or NULL if there is none. |
3007 | +- * |
3008 | +- * If sb_uuid is not NULL, the function will also check if the found flow is |
3009 | +- * referenced by the sb_uuid. */ |
3010 | + static struct desired_flow * |
3011 | +-desired_flow_lookup(struct ovn_desired_flow_table *flow_table, |
3012 | +- const struct ovn_flow *target, |
3013 | +- const struct uuid *sb_uuid) |
3014 | ++desired_flow_lookup__(struct ovn_desired_flow_table *flow_table, |
3015 | ++ const struct ovn_flow *target, |
3016 | ++ desired_flow_match_cb match_cb, |
3017 | ++ const void *arg) |
3018 | + { |
3019 | + struct desired_flow *d; |
3020 | + HMAP_FOR_EACH_WITH_HASH (d, match_hmap_node, target->hash, |
3021 | +@@ -1265,20 +1284,76 @@ desired_flow_lookup(struct ovn_desired_f |
3022 | + if (f->table_id == target->table_id |
3023 | + && f->priority == target->priority |
3024 | + && minimatch_equal(&f->match, &target->match)) { |
3025 | +- if (!sb_uuid) { |
3026 | ++ |
3027 | ++ if (!match_cb || match_cb(d, arg)) { |
3028 | + return d; |
3029 | + } |
3030 | +- struct sb_flow_ref *sfr; |
3031 | +- LIST_FOR_EACH (sfr, sb_list, &d->references) { |
3032 | +- if (uuid_equals(sb_uuid, &sfr->sb_uuid)) { |
3033 | +- return d; |
3034 | +- } |
3035 | +- } |
3036 | + } |
3037 | + } |
3038 | + return NULL; |
3039 | + } |
3040 | + |
3041 | ++/* Finds and returns a desired_flow in 'flow_table' whose key is identical to |
3042 | ++ * 'target''s key, or NULL if there is none. |
3043 | ++ */ |
3044 | ++static struct desired_flow * |
3045 | ++desired_flow_lookup(struct ovn_desired_flow_table *flow_table, |
3046 | ++ const struct ovn_flow *target) |
3047 | ++{ |
3048 | ++ return desired_flow_lookup__(flow_table, target, NULL, NULL); |
3049 | ++} |
3050 | ++ |
3051 | ++static bool |
3052 | ++flow_lookup_match_uuid_cb(const struct desired_flow *candidate, |
3053 | ++ const void *arg) |
3054 | ++{ |
3055 | ++ const struct uuid *sb_uuid = arg; |
3056 | ++ struct sb_flow_ref *sfr; |
3057 | ++ |
3058 | ++ LIST_FOR_EACH (sfr, sb_list, &candidate->references) { |
3059 | ++ if (uuid_equals(sb_uuid, &sfr->sb_uuid)) { |
3060 | ++ return true; |
3061 | ++ } |
3062 | ++ } |
3063 | ++ return false; |
3064 | ++} |
3065 | ++ |
3066 | ++/* Finds and returns a desired_flow in 'flow_table' whose key is identical to |
3067 | ++ * 'target''s key, or NULL if there is none. |
3068 | ++ * |
3069 | ++ * The function will also check if the found flow is referenced by the |
3070 | ++ * 'sb_uuid'. |
3071 | ++ */ |
3072 | ++static struct desired_flow * |
3073 | ++desired_flow_lookup_check_uuid(struct ovn_desired_flow_table *flow_table, |
3074 | ++ const struct ovn_flow *target, |
3075 | ++ const struct uuid *sb_uuid) |
3076 | ++{ |
3077 | ++ return desired_flow_lookup__(flow_table, target, flow_lookup_match_uuid_cb, |
3078 | ++ sb_uuid); |
3079 | ++} |
3080 | ++ |
3081 | ++static bool |
3082 | ++flow_lookup_match_conj_cb(const struct desired_flow *candidate, |
3083 | ++ const void *arg OVS_UNUSED) |
3084 | ++{ |
3085 | ++ return flow_action_has_conj(&candidate->flow); |
3086 | ++} |
3087 | ++ |
3088 | ++/* Finds and returns a desired_flow in 'flow_table' whose key is identical to |
3089 | ++ * 'target''s key, or NULL if there is none. |
3090 | ++ * |
3091 | ++ * The function will only return a matching flow if it contains action |
3092 | ++ * 'conjunction'. |
3093 | ++ */ |
3094 | ++static struct desired_flow * |
3095 | ++desired_flow_lookup_conjunctive(struct ovn_desired_flow_table *flow_table, |
3096 | ++ const struct ovn_flow *target) |
3097 | ++{ |
3098 | ++ return desired_flow_lookup__(flow_table, target, flow_lookup_match_conj_cb, |
3099 | ++ NULL); |
3100 | ++} |
3101 | ++ |
3102 | + /* Finds and returns an installed_flow in installed_flows whose key is |
3103 | + * identical to 'target''s key, or NULL if there is none. */ |
3104 | + static struct installed_flow * |
3105 | +@@ -1676,8 +1751,7 @@ update_installed_flows_by_compare(struct |
3106 | + struct installed_flow *i, *next; |
3107 | + HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) { |
3108 | + unlink_all_refs_for_installed_flow(i); |
3109 | +- struct desired_flow *d = |
3110 | +- desired_flow_lookup(flow_table, &i->flow, NULL); |
3111 | ++ struct desired_flow *d = desired_flow_lookup(flow_table, &i->flow); |
3112 | + if (!d) { |
3113 | + /* Installed flow is no longer desirable. Delete it from the |
3114 | + * switch and from installed_flows. */ |
3115 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-10.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-10.patch |
3116 | new file mode 100644 |
3117 | index 0000000..bb48c43 |
3118 | --- /dev/null |
3119 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-10.patch |
3120 | @@ -0,0 +1,77 @@ |
3121 | +Description: Cherry-pick fix applied to master and branch-20.12 |
3122 | +Origin: https://github.com/ovn-org/ovn/commit/e49ce9a33f38f29c44e3c30afcc189b5f6a9ef8e |
3123 | +Applied-Upstream: commit: e49ce9a33f38f29c44e3c30afcc189b5f6a9ef8e |
3124 | + |
3125 | +From 5a553b6da28d99d37a27f7e055aa6895184e0520 Mon Sep 17 00:00:00 2001 |
3126 | +From: Dumitru Ceara <dceara@redhat.com> |
3127 | +Date: Sun, 11 Oct 2020 14:05:45 +0200 |
3128 | +Subject: [PATCH 10/15] ofctrl.c: Do not change flow ordering when merging |
3129 | + opposite changes. |
3130 | + |
3131 | +Instead of removing the old desired flow from the list and inserting the new |
3132 | +one at the end we now directly replace the old flow with the new one while |
3133 | +maintaining the same ordering. |
3134 | + |
3135 | +For now order of the flows is not relevant but further commits require |
3136 | +maintaining the order of desired flows. |
3137 | + |
3138 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
3139 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
3140 | +--- |
3141 | + controller/ofctrl.c | 28 +++++++++++++++++++++------- |
3142 | + 1 file changed, 21 insertions(+), 7 deletions(-) |
3143 | + |
3144 | +Index: ovn-20.06.2/controller/ofctrl.c |
3145 | +=================================================================== |
3146 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
3147 | ++++ ovn-20.06.2/controller/ofctrl.c |
3148 | +@@ -848,6 +848,26 @@ link_installed_to_desired(struct install |
3149 | + d->installed_flow = i; |
3150 | + } |
3151 | + |
3152 | ++/* Replaces 'old_desired' with 'new_desired' in the list of desired flows |
3153 | ++ * that have same match conditions as the installed flow. |
3154 | ++ * |
3155 | ++ * If 'old_desired' was the active flow, 'new_desired' becomes the active one. |
3156 | ++ */ |
3157 | ++static void |
3158 | ++replace_installed_to_desired(struct installed_flow *i, |
3159 | ++ struct desired_flow *old_desired, |
3160 | ++ struct desired_flow *new_desired) |
3161 | ++{ |
3162 | ++ ovs_assert(old_desired->installed_flow == i); |
3163 | ++ ovs_list_replace(&new_desired->installed_ref_list_node, |
3164 | ++ &old_desired->installed_ref_list_node); |
3165 | ++ old_desired->installed_flow = NULL; |
3166 | ++ new_desired->installed_flow = i; |
3167 | ++ if (i->desired_flow == old_desired) { |
3168 | ++ i->desired_flow = new_desired; |
3169 | ++ } |
3170 | ++} |
3171 | ++ |
3172 | + static void |
3173 | + unlink_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
3174 | + { |
3175 | +@@ -1842,21 +1862,15 @@ merge_tracked_flows(struct ovn_desired_f |
3176 | + * removed during track_flow_add_or_modify. */ |
3177 | + ovs_assert(del_f->installed_flow); |
3178 | + |
3179 | +- bool del_f_was_active = desired_flow_is_active(del_f); |
3180 | + if (!f->installed_flow) { |
3181 | + /* f is not installed yet. */ |
3182 | +- struct installed_flow *i = del_f->installed_flow; |
3183 | +- unlink_installed_to_desired(i, del_f); |
3184 | +- link_installed_to_desired(i, f); |
3185 | ++ replace_installed_to_desired(del_f->installed_flow, del_f, f); |
3186 | + } else { |
3187 | + /* f has been installed before, and now was updated to exact |
3188 | + * the same flow as del_f. */ |
3189 | + ovs_assert(f->installed_flow == del_f->installed_flow); |
3190 | + unlink_installed_to_desired(del_f->installed_flow, del_f); |
3191 | + } |
3192 | +- if (del_f_was_active) { |
3193 | +- desired_flow_set_active(f); |
3194 | +- } |
3195 | + hmap_remove(&deleted_flows, &del_f->match_hmap_node); |
3196 | + ovs_list_remove(&del_f->track_list_node); |
3197 | + desired_flow_destroy(del_f); |
3198 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-11.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-11.patch |
3199 | new file mode 100644 |
3200 | index 0000000..3666718 |
3201 | --- /dev/null |
3202 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-11.patch |
3203 | @@ -0,0 +1,215 @@ |
3204 | +Description: Cherry-pick fix applied to master and branch-20.12 |
3205 | +Origin: https://github.com/ovn-org/ovn/commit/107bb25029350bd0f7dfeeb0ef3053adbd504e3e |
3206 | +Applied-Upstream: commit: 107bb25029350bd0f7dfeeb0ef3053adbd504e3e |
3207 | + |
3208 | +From f170c35c9de7af2b97764e4fa6f4bb2767f5fed9 Mon Sep 17 00:00:00 2001 |
3209 | +From: Dumitru Ceara <dceara@redhat.com> |
3210 | +Date: Sun, 11 Oct 2020 14:05:59 +0200 |
3211 | +Subject: [PATCH 11/15] ofctrl.c: Simplify active desired flow selection. |
3212 | + |
3213 | +Always consider the first "desired flow" in the list of flows that refer an |
3214 | +"installed flow" as the active flow. This simplifies the logic of the flow |
3215 | +update code and is used in a further commit to implement a partial ordering |
3216 | +of desired flows within installed flows. |
3217 | + |
3218 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
3219 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
3220 | +--- |
3221 | + controller/ofctrl.c | 91 ++++++++++++++++++--------------------------- |
3222 | + 1 file changed, 36 insertions(+), 55 deletions(-) |
3223 | + |
3224 | +Index: ovn-20.06.2/controller/ofctrl.c |
3225 | +=================================================================== |
3226 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
3227 | ++++ ovn-20.06.2/controller/ofctrl.c |
3228 | +@@ -188,6 +188,8 @@ struct sb_flow_ref { |
3229 | + * relationship is 1 to N. A link is added when a flow addition is processed. |
3230 | + * A link is removed when a flow deletion is processed, the desired flow |
3231 | + * table is cleared, or the installed flow table is cleared. |
3232 | ++ * The first desired_flow in the list is the active one, the one that is |
3233 | ++ * actually installed. |
3234 | + */ |
3235 | + struct installed_flow { |
3236 | + struct ovn_flow flow; |
3237 | +@@ -199,11 +201,6 @@ struct installed_flow { |
3238 | + * installed flow, e.g. when there are conflict/duplicated ACLs that |
3239 | + * generates same match conditions). */ |
3240 | + struct ovs_list desired_refs; |
3241 | +- |
3242 | +- /* The corresponding flow in desired table. It must be one of the flows in |
3243 | +- * desired_refs list. If there are more than one flows in references list, |
3244 | +- * this is the one that is actually installed. */ |
3245 | +- struct desired_flow *desired_flow; |
3246 | + }; |
3247 | + |
3248 | + typedef bool |
3249 | +@@ -231,6 +228,7 @@ static struct installed_flow *installed_ |
3250 | + const struct ovn_flow *target); |
3251 | + static void installed_flow_destroy(struct installed_flow *); |
3252 | + static struct installed_flow *installed_flow_dup(struct desired_flow *); |
3253 | ++static struct desired_flow *installed_flow_get_active(struct installed_flow *); |
3254 | + |
3255 | + static uint32_t ovn_flow_match_hash(const struct ovn_flow *); |
3256 | + static char *ovn_flow_to_string(const struct ovn_flow *); |
3257 | +@@ -796,24 +794,6 @@ ofctrl_recv(const struct ofp_header *oh, |
3258 | + log_openflow_rl(&rl, VLL_DBG, oh, "OpenFlow packet ignored"); |
3259 | + } |
3260 | + } |
3261 | +- |
3262 | |
3263 | +-/* Returns true if a desired flow is active (the one currently installed |
3264 | +- * among the list of desired flows that are linked to the same installed |
3265 | +- * flow). */ |
3266 | +-static inline bool |
3267 | +-desired_flow_is_active(struct desired_flow *d) |
3268 | +-{ |
3269 | +- return (d->installed_flow && d->installed_flow->desired_flow == d); |
3270 | +-} |
3271 | +- |
3272 | +-/* Set a desired flow as the active one among the list of desired flows |
3273 | +- * that are linked to the same installed flow. */ |
3274 | +-static inline void |
3275 | +-desired_flow_set_active(struct desired_flow *d) |
3276 | +-{ |
3277 | +- ovs_assert(d->installed_flow); |
3278 | +- d->installed_flow->desired_flow = d; |
3279 | +-} |
3280 | + |
3281 | + static bool |
3282 | + flow_action_has_conj(const struct ovn_flow *f) |
3283 | +@@ -831,27 +811,22 @@ flow_action_has_conj(const struct ovn_fl |
3284 | + /* Adds the desired flow to the list of desired flows that have same match |
3285 | + * conditions as the installed flow. |
3286 | + * |
3287 | +- * If the newly added desired flow is the first one in the list, it is also set |
3288 | +- * as the active one. |
3289 | +- * |
3290 | + * It is caller's responsibility to make sure the link between the pair didn't |
3291 | +- * exist before. */ |
3292 | +-static void |
3293 | ++ * exist before. |
3294 | ++ * |
3295 | ++ * Returns true if the newly added desired flow is selected to be the active |
3296 | ++ * one. |
3297 | ++ */ |
3298 | ++static bool |
3299 | + link_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
3300 | + { |
3301 | +- ovs_assert(i->desired_flow != d); |
3302 | +- if (ovs_list_is_empty(&i->desired_refs)) { |
3303 | +- ovs_assert(!i->desired_flow); |
3304 | +- i->desired_flow = d; |
3305 | +- } |
3306 | +- ovs_list_insert(&i->desired_refs, &d->installed_ref_list_node); |
3307 | + d->installed_flow = i; |
3308 | ++ ovs_list_push_back(&i->desired_refs, &d->installed_ref_list_node); |
3309 | ++ return installed_flow_get_active(i) == d; |
3310 | + } |
3311 | + |
3312 | + /* Replaces 'old_desired' with 'new_desired' in the list of desired flows |
3313 | + * that have same match conditions as the installed flow. |
3314 | +- * |
3315 | +- * If 'old_desired' was the active flow, 'new_desired' becomes the active one. |
3316 | + */ |
3317 | + static void |
3318 | + replace_installed_to_desired(struct installed_flow *i, |
3319 | +@@ -863,24 +838,22 @@ replace_installed_to_desired(struct inst |
3320 | + &old_desired->installed_ref_list_node); |
3321 | + old_desired->installed_flow = NULL; |
3322 | + new_desired->installed_flow = i; |
3323 | +- if (i->desired_flow == old_desired) { |
3324 | +- i->desired_flow = new_desired; |
3325 | +- } |
3326 | + } |
3327 | + |
3328 | +-static void |
3329 | ++/* Removes the desired flow from the list of desired flows that have the same |
3330 | ++ * match conditions as the installed flow. |
3331 | ++ * |
3332 | ++ * Returns true if the desired flow was the previously active flow. |
3333 | ++ */ |
3334 | ++static bool |
3335 | + unlink_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
3336 | + { |
3337 | +- ovs_assert(i && i->desired_flow && !ovs_list_is_empty(&i->desired_refs)); |
3338 | ++ struct desired_flow *old_active = installed_flow_get_active(i); |
3339 | ++ |
3340 | + ovs_assert(d && d->installed_flow == i); |
3341 | + ovs_list_remove(&d->installed_ref_list_node); |
3342 | + d->installed_flow = NULL; |
3343 | +- if (i->desired_flow == d) { |
3344 | +- i->desired_flow = ovs_list_is_empty(&i->desired_refs) ? NULL : |
3345 | +- CONTAINER_OF(ovs_list_front(&i->desired_refs), |
3346 | +- struct desired_flow, |
3347 | +- installed_ref_list_node); |
3348 | +- } |
3349 | ++ return old_active == d; |
3350 | + } |
3351 | + |
3352 | + static void |
3353 | +@@ -1280,7 +1253,6 @@ installed_flow_dup(struct desired_flow * |
3354 | + { |
3355 | + struct installed_flow *dst = xmalloc(sizeof *dst); |
3356 | + ovs_list_init(&dst->desired_refs); |
3357 | +- dst->desired_flow = NULL; |
3358 | + dst->flow.table_id = src->flow.table_id; |
3359 | + dst->flow.priority = src->flow.priority; |
3360 | + minimatch_clone(&dst->flow.match, &src->flow.match); |
3361 | +@@ -1292,6 +1264,17 @@ installed_flow_dup(struct desired_flow * |
3362 | + } |
3363 | + |
3364 | + static struct desired_flow * |
3365 | ++installed_flow_get_active(struct installed_flow *f) |
3366 | ++{ |
3367 | ++ if (!ovs_list_is_empty(&f->desired_refs)) { |
3368 | ++ return CONTAINER_OF(ovs_list_front(&f->desired_refs), |
3369 | ++ struct desired_flow, |
3370 | ++ installed_ref_list_node); |
3371 | ++ } |
3372 | ++ return NULL; |
3373 | ++} |
3374 | ++ |
3375 | ++static struct desired_flow * |
3376 | + desired_flow_lookup__(struct ovn_desired_flow_table *flow_table, |
3377 | + const struct ovn_flow *target, |
3378 | + desired_flow_match_cb match_cb, |
3379 | +@@ -1439,8 +1422,7 @@ static void |
3380 | + installed_flow_destroy(struct installed_flow *f) |
3381 | + { |
3382 | + if (f) { |
3383 | +- ovs_assert(ovs_list_is_empty(&f->desired_refs)); |
3384 | +- ovs_assert(!f->desired_flow); |
3385 | ++ ovs_assert(!installed_flow_get_active(f)); |
3386 | + ovn_flow_uninit(&f->flow); |
3387 | + free(f); |
3388 | + } |
3389 | +@@ -1898,10 +1880,10 @@ update_installed_flows_by_track(struct o |
3390 | + /* The desired flow was deleted */ |
3391 | + if (f->installed_flow) { |
3392 | + struct installed_flow *i = f->installed_flow; |
3393 | +- bool was_active = desired_flow_is_active(f); |
3394 | +- unlink_installed_to_desired(i, f); |
3395 | ++ bool was_active = unlink_installed_to_desired(i, f); |
3396 | ++ struct desired_flow *d = installed_flow_get_active(i); |
3397 | + |
3398 | +- if (!i->desired_flow) { |
3399 | ++ if (!d) { |
3400 | + installed_flow_del(&i->flow, msgs); |
3401 | + ovn_flow_log(&i->flow, "removing installed (tracked)"); |
3402 | + |
3403 | +@@ -1912,7 +1894,6 @@ update_installed_flows_by_track(struct o |
3404 | + * installed flow, so update the OVS flow for the new |
3405 | + * active flow (at least the cookie will be different, |
3406 | + * even if the actions are the same). */ |
3407 | +- struct desired_flow *d = i->desired_flow; |
3408 | + ovn_flow_log(&i->flow, "updating installed (tracked)"); |
3409 | + installed_flow_mod(&i->flow, &d->flow, msgs); |
3410 | + } |
3411 | +@@ -1931,7 +1912,7 @@ update_installed_flows_by_track(struct o |
3412 | + hmap_insert(&installed_flows, &new_node->match_hmap_node, |
3413 | + new_node->flow.hash); |
3414 | + link_installed_to_desired(new_node, f); |
3415 | +- } else if (desired_flow_is_active(f)) { |
3416 | ++ } else if (installed_flow_get_active(i) == f) { |
3417 | + /* The installed flow is installed for f, but f has change |
3418 | + * tracked, so it must have been modified. */ |
3419 | + ovn_flow_log(&i->flow, "updating installed (tracked)"); |
3420 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-12.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-12.patch |
3421 | new file mode 100644 |
3422 | index 0000000..c78d964 |
3423 | --- /dev/null |
3424 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-12.patch |
3425 | @@ -0,0 +1,40 @@ |
3426 | +Description: Cherry-pick fix applied to master and branch-20.12 |
3427 | +Origin: https://github.com/ovn-org/ovn/commit/33c15c145988daa6172928dc870f3a0225515f50 |
3428 | +Applied-Upstream: commit: 33c15c145988daa6172928dc870f3a0225515f50 |
3429 | + |
3430 | +From 063c1fc0858c462719b2d3b33b3fcfdef18bb9a5 Mon Sep 17 00:00:00 2001 |
3431 | +From: Dumitru Ceara <dceara@redhat.com> |
3432 | +Date: Tue, 13 Oct 2020 11:04:03 +0200 |
3433 | +Subject: [PATCH 12/15] ofctrl.c: Always log the most recent flow changes. |
3434 | + |
3435 | +Fixes: 6f0b1e02d9ab ("ofctrl: Incremental processing for flow installation by tracking.") |
3436 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
3437 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
3438 | +--- |
3439 | + controller/ofctrl.c | 4 ++-- |
3440 | + 1 file changed, 2 insertions(+), 2 deletions(-) |
3441 | + |
3442 | +Index: ovn-20.06.2/controller/ofctrl.c |
3443 | +=================================================================== |
3444 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
3445 | ++++ ovn-20.06.2/controller/ofctrl.c |
3446 | +@@ -1894,8 +1894,8 @@ update_installed_flows_by_track(struct o |
3447 | + * installed flow, so update the OVS flow for the new |
3448 | + * active flow (at least the cookie will be different, |
3449 | + * even if the actions are the same). */ |
3450 | +- ovn_flow_log(&i->flow, "updating installed (tracked)"); |
3451 | + installed_flow_mod(&i->flow, &d->flow, msgs); |
3452 | ++ ovn_flow_log(&i->flow, "updating installed (tracked)"); |
3453 | + } |
3454 | + } |
3455 | + desired_flow_destroy(f); |
3456 | +@@ -1915,8 +1915,8 @@ update_installed_flows_by_track(struct o |
3457 | + } else if (installed_flow_get_active(i) == f) { |
3458 | + /* The installed flow is installed for f, but f has change |
3459 | + * tracked, so it must have been modified. */ |
3460 | +- ovn_flow_log(&i->flow, "updating installed (tracked)"); |
3461 | + installed_flow_mod(&i->flow, &f->flow, msgs); |
3462 | ++ ovn_flow_log(&i->flow, "updating installed (tracked)"); |
3463 | + } else { |
3464 | + /* Adding a new flow that conflicts with an existing installed |
3465 | + * flow, so just add it to the link. */ |
3466 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-13.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-13.patch |
3467 | new file mode 100644 |
3468 | index 0000000..3b273f2 |
3469 | --- /dev/null |
3470 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-13.patch |
3471 | @@ -0,0 +1,416 @@ |
3472 | +Description: Cherry-pick fix applied to master and branch-20.12 |
3473 | +Origin: https://github.com/ovn-org/ovn/commit/986b3d5e4ad6f05245d021ba699c957246294a22 |
3474 | +Applied-Upstream: commit: 986b3d5e4ad6f05245d021ba699c957246294a22 |
3475 | + |
3476 | +From 7f27cf96b8d16c5dd960ceb620a7269a66bc9cc4 Mon Sep 17 00:00:00 2001 |
3477 | +From: Dumitru Ceara <dceara@redhat.com> |
3478 | +Date: Thu, 15 Oct 2020 11:12:30 +0200 |
3479 | +Subject: [PATCH 13/15] ofctrl.c: Add a predictable resolution for conflicting |
3480 | + flow actions. |
3481 | + |
3482 | +Until now, in case the ACL configuration generates openflows that have |
3483 | +the same match but different actions, ovn-controller was using the |
3484 | +following approach: |
3485 | +1. If the flow being added contains conjunctive actions, merge its |
3486 | + actions with the already existing flow. |
3487 | +2. Otherwise, if the flow is being added incrementally |
3488 | + (update_installed_flows_by_track), don't install the new flow but |
3489 | + instead keep the old one. |
3490 | +3. Otherwise, (update_installed_flows_by_compare), don't install the |
3491 | + new flow but instead keep the old one. |
3492 | + |
3493 | +Even though one can argue that having an ACL with a match that includes |
3494 | +the match of another ACL is a misconfiguration, it can happen that the |
3495 | +users provision OVN like this. Depending on the order of reading and |
3496 | +installing the logical flows, the above operations can yield |
3497 | +unpredictable results, e.g., allow specific traffic but then after |
3498 | +ovn-controller is restarted (or a recompute happens) that specific |
3499 | +traffic starts being dropped. |
3500 | + |
3501 | +A simple example of ACL configuration is: |
3502 | +ovn-nbctl acl-add ls to-lport 3 '(ip4.src==10.0.0.1 || |
3503 | +ip4.src==10.0.0.2) && (ip4.dst == 10.0.0.3 || ip4.dst == 10.0.0.4)' allow |
3504 | +ovn-nbctl acl-add ls to-lport 3 'ip4.src==10.0.0.1' allow |
3505 | +ovn-nbctl acl-add ls to-lport 2 'arp' allow |
3506 | +ovn-nbctl acl-add ls to-lport 1 'ip4' drop |
3507 | + |
3508 | +This is a pattern used by most CMSs: |
3509 | +- define a default deny policy. |
3510 | +- punch holes in the default deny policy based on user specific |
3511 | + configurations. |
3512 | + |
3513 | +Without this commit the behavior for traffic from 10.0.0.1 to 10.0.0.5 |
3514 | +is unpredictable. Depending on the order of operations traffic might be |
3515 | +dropped or allowed. |
3516 | + |
3517 | +It's also quite hard to force the CMS to ensure that such match overlaps |
3518 | +never occur. |
3519 | + |
3520 | +To address this issue we now ensure that all desired flows refering the |
3521 | +same installed flow are partially sorted in the following way: |
3522 | +- first all flows with action "allow". |
3523 | +- then all flows with action "drop". |
3524 | +- then a single flow with action "conjunction" (resulting from merging |
3525 | + all flows with the same match and action conjunction). |
3526 | + |
3527 | +This ensures that "allow" flows have precedence over "drop" flows which |
3528 | +in turn have precedence over "conjunction" flows. Essentially less |
3529 | +restrictive flows are always preferred over more restrictive flows whenever a match |
3530 | +conflict happens. |
3531 | + |
3532 | +CC: Daniel Alvarez <dalvarez@redhat.com> |
3533 | +Reported-at: https://bugzilla.redhat.com/1871931 |
3534 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
3535 | +Acked-by: Mark Gray <mark.d.gray@redhat.com> |
3536 | +Signed-off-by: Han Zhou <hzhou@ovn.org> |
3537 | +--- |
3538 | + controller/ofctrl.c | 74 +++++++++++++-- |
3539 | + tests/ovn.at | 214 ++++++++++++++++++++++++++++++++++++++++++++ |
3540 | + 2 files changed, 283 insertions(+), 5 deletions(-) |
3541 | + |
3542 | +Index: ovn-20.06.2/controller/ofctrl.c |
3543 | +=================================================================== |
3544 | +--- ovn-20.06.2.orig/controller/ofctrl.c |
3545 | ++++ ovn-20.06.2/controller/ofctrl.c |
3546 | +@@ -188,6 +188,14 @@ struct sb_flow_ref { |
3547 | + * relationship is 1 to N. A link is added when a flow addition is processed. |
3548 | + * A link is removed when a flow deletion is processed, the desired flow |
3549 | + * table is cleared, or the installed flow table is cleared. |
3550 | ++ * |
3551 | ++ * To ensure predictable behavior, the list of desired flows is maintained |
3552 | ++ * partially sorted in the following way (from least restrictive to most |
3553 | ++ * restrictive wrt. match): |
3554 | ++ * - allow flows without action conjunction. |
3555 | ++ * - drop flows without action conjunction. |
3556 | ++ * - a single flow with action conjunction. |
3557 | ++ * |
3558 | + * The first desired_flow in the list is the active one, the one that is |
3559 | + * actually installed. |
3560 | + */ |
3561 | +@@ -796,6 +804,12 @@ ofctrl_recv(const struct ofp_header *oh, |
3562 | + } |
3563 | + |
3564 | + static bool |
3565 | ++flow_action_has_drop(const struct ovn_flow *f) |
3566 | ++{ |
3567 | ++ return f->ofpacts_len == 0; |
3568 | ++} |
3569 | ++ |
3570 | ++static bool |
3571 | + flow_action_has_conj(const struct ovn_flow *f) |
3572 | + { |
3573 | + const struct ofpact *a = NULL; |
3574 | +@@ -808,6 +822,33 @@ flow_action_has_conj(const struct ovn_fl |
3575 | + return false; |
3576 | + } |
3577 | + |
3578 | ++static bool |
3579 | ++flow_action_has_allow(const struct ovn_flow *f) |
3580 | ++{ |
3581 | ++ return !flow_action_has_drop(f) && !flow_action_has_conj(f); |
3582 | ++} |
3583 | ++ |
3584 | ++/* Returns true if flow 'a' is preferred over flow 'b'. */ |
3585 | ++static bool |
3586 | ++flow_is_preferred(const struct ovn_flow *a, const struct ovn_flow *b) |
3587 | ++{ |
3588 | ++ if (flow_action_has_allow(b)) { |
3589 | ++ return false; |
3590 | ++ } |
3591 | ++ if (flow_action_has_allow(a)) { |
3592 | ++ return true; |
3593 | ++ } |
3594 | ++ if (flow_action_has_drop(b)) { |
3595 | ++ return false; |
3596 | ++ } |
3597 | ++ if (flow_action_has_drop(a)) { |
3598 | ++ return true; |
3599 | ++ } |
3600 | ++ |
3601 | ++ /* Flows 'a' and 'b' should never both have action conjunction. */ |
3602 | ++ OVS_NOT_REACHED(); |
3603 | ++} |
3604 | ++ |
3605 | + /* Adds the desired flow to the list of desired flows that have same match |
3606 | + * conditions as the installed flow. |
3607 | + * |
3608 | +@@ -820,8 +861,18 @@ flow_action_has_conj(const struct ovn_fl |
3609 | + static bool |
3610 | + link_installed_to_desired(struct installed_flow *i, struct desired_flow *d) |
3611 | + { |
3612 | ++ struct desired_flow *f; |
3613 | ++ |
3614 | ++ /* Find first 'f' such that 'd' is preferred over 'f'. If no such desired |
3615 | ++ * flow exists then 'f' will point after the last element of the list. |
3616 | ++ */ |
3617 | ++ LIST_FOR_EACH (f, installed_ref_list_node, &i->desired_refs) { |
3618 | ++ if (flow_is_preferred(&d->flow, &f->flow)) { |
3619 | ++ break; |
3620 | ++ } |
3621 | ++ } |
3622 | ++ ovs_list_insert(&f->installed_ref_list_node, &d->installed_ref_list_node); |
3623 | + d->installed_flow = i; |
3624 | +- ovs_list_push_back(&i->desired_refs, &d->installed_ref_list_node); |
3625 | + return installed_flow_get_active(i) == d; |
3626 | + } |
3627 | + |
3628 | +@@ -1789,8 +1840,14 @@ update_installed_flows_by_compare(struct |
3629 | + link_installed_to_desired(i, d); |
3630 | + } else if (!d->installed_flow) { |
3631 | + /* This is a desired_flow that conflicts with one installed |
3632 | +- * previously but not linked yet. */ |
3633 | +- link_installed_to_desired(i, d); |
3634 | ++ * previously but not linked yet. However, if this flow becomes |
3635 | ++ * active, e.g., it is less restrictive than the previous active |
3636 | ++ * flow then modify the installed flow. |
3637 | ++ */ |
3638 | ++ if (link_installed_to_desired(i, d)) { |
3639 | ++ installed_flow_mod(&i->flow, &d->flow, msgs); |
3640 | ++ ovn_flow_log(&i->flow, "updating installed (conflict)"); |
3641 | ++ } |
3642 | + } |
3643 | + } |
3644 | + } |
3645 | +@@ -1919,8 +1976,15 @@ update_installed_flows_by_track(struct o |
3646 | + ovn_flow_log(&i->flow, "updating installed (tracked)"); |
3647 | + } else { |
3648 | + /* Adding a new flow that conflicts with an existing installed |
3649 | +- * flow, so just add it to the link. */ |
3650 | +- link_installed_to_desired(i, f); |
3651 | ++ * flow, so add it to the link. If this flow becomes active, |
3652 | ++ * e.g., it is less restrictive than the previous active flow |
3653 | ++ * then modify the installed flow. |
3654 | ++ */ |
3655 | ++ if (link_installed_to_desired(i, f)) { |
3656 | ++ installed_flow_mod(&i->flow, &f->flow, msgs); |
3657 | ++ ovn_flow_log(&i->flow, |
3658 | ++ "updating installed (tracked conflict)"); |
3659 | ++ } |
3660 | + } |
3661 | + /* The track_list_node emptyness is used to check if the node is |
3662 | + * already added to track list, so initialize it again here. */ |
3663 | +Index: ovn-20.06.2/tests/ovn.at |
3664 | +=================================================================== |
3665 | +--- ovn-20.06.2.orig/tests/ovn.at |
3666 | ++++ ovn-20.06.2/tests/ovn.at |
3667 | +@@ -13423,6 +13423,220 @@ grep conjunction.*conjunction.*conjuncti |
3668 | + OVN_CLEANUP([hv1]) |
3669 | + AT_CLEANUP |
3670 | + |
3671 | ++AT_SETUP([ovn -- Superseeding ACLs with conjunction]) |
3672 | ++ovn_start |
3673 | ++ |
3674 | ++ovn-nbctl ls-add ls1 |
3675 | ++ |
3676 | ++ovn-nbctl lsp-add ls1 ls1-lp1 \ |
3677 | ++-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01" |
3678 | ++ |
3679 | ++ovn-nbctl lsp-add ls1 ls1-lp2 \ |
3680 | ++-- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02" |
3681 | ++ |
3682 | ++net_add n1 |
3683 | ++sim_add hv1 |
3684 | ++ |
3685 | ++as hv1 |
3686 | ++ovs-vsctl add-br br-phys |
3687 | ++ovn_attach n1 br-phys 192.168.0.1 |
3688 | ++ovs-vsctl -- add-port br-int hv1-vif1 -- \ |
3689 | ++ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ |
3690 | ++ options:tx_pcap=hv1/vif1-tx.pcap \ |
3691 | ++ options:rxq_pcap=hv1/vif1-rx.pcap \ |
3692 | ++ ofport-request=1 |
3693 | ++ |
3694 | ++ovs-vsctl -- add-port br-int hv1-vif2 -- \ |
3695 | ++ set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \ |
3696 | ++ options:tx_pcap=hv1/vif2-tx.pcap \ |
3697 | ++ options:rxq_pcap=hv1/vif2-rx.pcap \ |
3698 | ++ ofport-request=2 |
3699 | ++ |
3700 | ++# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT... |
3701 | ++# |
3702 | ++# This shell function causes an ip packet to be received on INPORT. |
3703 | ++# The packet's content has Ethernet destination DST and source SRC |
3704 | ++# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits). |
3705 | ++# The OUTPORTs (zero or more) list the VIFs on which the packet should |
3706 | ++# be received. INPORT and the OUTPORTs are specified as logical switch |
3707 | ++# port numbers, e.g. 11 for vif11. |
3708 | ++test_ip() { |
3709 | ++ # This packet has bad checksums but logical L3 routing doesn't check. |
3710 | ++ local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 |
3711 | ++ local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}\ |
3712 | ++${dst_ip}0035111100080000 |
3713 | ++ shift; shift; shift; shift; shift |
3714 | ++ as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet |
3715 | ++ for outport; do |
3716 | ++ echo $packet >> $outport.expected |
3717 | ++ done |
3718 | ++} |
3719 | ++ |
3720 | ++ip_to_hex() { |
3721 | ++ printf "%02x%02x%02x%02x" "$@" |
3722 | ++} |
3723 | ++ |
3724 | ++reset_pcap_file() { |
3725 | ++ local iface=$1 |
3726 | ++ local pcap_file=$2 |
3727 | ++ ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ |
3728 | ++options:rxq_pcap=dummy-rx.pcap |
3729 | ++ rm -f ${pcap_file}*.pcap |
3730 | ++ ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ |
3731 | ++options:rxq_pcap=${pcap_file}-rx.pcap |
3732 | ++} |
3733 | ++ |
3734 | ++# Add a default deny ACL and an allow ACL for specific IP traffic. |
3735 | ++ovn-nbctl acl-add ls1 to-lport 2 'arp' allow |
3736 | ++ovn-nbctl acl-add ls1 to-lport 1 'ip4' drop |
3737 | ++ovn-nbctl acl-add ls1 to-lport 3 '(ip4.src==10.0.0.1 || ip4.src==10.0.0.2) && (ip4.dst == 10.0.0.3 || ip4.dst == 10.0.0.4)' allow |
3738 | ++ovn-nbctl acl-add ls1 to-lport 3 '(ip4.src==10.0.0.1 || ip4.src==10.0.0.42) && (ip4.dst == 10.0.0.3 || ip4.dst == 10.0.0.4)' allow |
3739 | ++ovn-nbctl --wait=hv sync |
3740 | ++ |
3741 | ++# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
3742 | ++for src in `seq 1 2`; do |
3743 | ++ for dst in `seq 3 4`; do |
3744 | ++ sip=`ip_to_hex 10 0 0 $src` |
3745 | ++ dip=`ip_to_hex 10 0 0 $dst` |
3746 | ++ |
3747 | ++ test_ip 1 f00000000001 f00000000002 $sip $dip 2 |
3748 | ++ done |
3749 | ++done |
3750 | ++ |
3751 | ++# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.5 should be dropped. |
3752 | ++dip=`ip_to_hex 10 0 0 5` |
3753 | ++for src in `seq 1 2`; do |
3754 | ++ sip=`ip_to_hex 10 0 0 $src` |
3755 | ++ |
3756 | ++ test_ip 1 f00000000001 f00000000002 $sip $dip |
3757 | ++done |
3758 | ++ |
3759 | ++cat 2.expected > expout |
3760 | ++$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets |
3761 | ++AT_CHECK([cat 2.packets], [0], [expout]) |
3762 | ++reset_pcap_file hv1-vif2 hv1/vif2 |
3763 | ++rm -f 2.packets |
3764 | ++> 2.expected |
3765 | ++ |
3766 | ++# Add two less restrictive allow ACLs for src IP 10.0.0.1. |
3767 | ++ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1 || ip4.src==10.0.0.1' allow |
3768 | ++ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow |
3769 | ++ovn-nbctl --wait=hv sync |
3770 | ++ |
3771 | ++# Check OVS flows, the less restrictive flows should have been installed. |
3772 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3773 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3774 | ++priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3775 | ++priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3776 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3777 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3778 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
3779 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
3780 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
3781 | ++]) |
3782 | ++ |
3783 | ++# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
3784 | ++for src in `seq 1 2`; do |
3785 | ++ for dst in `seq 3 4`; do |
3786 | ++ sip=`ip_to_hex 10 0 0 $src` |
3787 | ++ dip=`ip_to_hex 10 0 0 $dst` |
3788 | ++ |
3789 | ++ test_ip 1 f00000000001 f00000000002 $sip $dip 2 |
3790 | ++ done |
3791 | ++done |
3792 | ++ |
3793 | ++# Traffic 10.0.0.2 -> 10.0.0.5 should be dropped. |
3794 | ++sip=`ip_to_hex 10 0 0 2` |
3795 | ++dip=`ip_to_hex 10 0 0 5` |
3796 | ++test_ip 1 f00000000001 f00000000002 $sip $dip |
3797 | ++ |
3798 | ++# Traffic 10.0.0.1 -> 10.0.0.5 should be allowed. |
3799 | ++sip=`ip_to_hex 10 0 0 1` |
3800 | ++dip=`ip_to_hex 10 0 0 5` |
3801 | ++test_ip 1 f00000000001 f00000000002 $sip $dip 2 |
3802 | ++ |
3803 | ++cat 2.expected > expout |
3804 | ++$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets |
3805 | ++AT_CHECK([cat 2.packets], [0], [expout]) |
3806 | ++reset_pcap_file hv1-vif2 hv1/vif2 |
3807 | ++rm -f 2.packets |
3808 | ++> 2.expected |
3809 | ++ |
3810 | ++#sleep infinity |
3811 | ++ |
3812 | ++# Remove the first less restrictive allow ACL. |
3813 | ++ovn-nbctl acl-del ls1 to-lport 3 'ip4.src==10.0.0.1 || ip4.src==10.0.0.1' |
3814 | ++ovn-nbctl --wait=hv sync |
3815 | ++ |
3816 | ++# Check OVS flows, the second less restrictive allow ACL should have been installed. |
3817 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3818 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3819 | ++priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3820 | ++priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3821 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3822 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3823 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
3824 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
3825 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
3826 | ++]) |
3827 | ++ |
3828 | ++# Remove the less restrictive allow ACL. |
3829 | ++ovn-nbctl acl-del ls1 to-lport 3 'ip4.src==10.0.0.1' |
3830 | ++ovn-nbctl --wait=hv sync |
3831 | ++ |
3832 | ++# Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled. |
3833 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3834 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3835 | ++priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3836 | ++priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3837 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3838 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3839 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(2,2/2),conjunction(3,2/2) |
3840 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
3841 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
3842 | ++]) |
3843 | ++ |
3844 | ++# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
3845 | ++for src in `seq 1 2`; do |
3846 | ++ for dst in `seq 3 4`; do |
3847 | ++ sip=`ip_to_hex 10 0 0 $src` |
3848 | ++ dip=`ip_to_hex 10 0 0 $dst` |
3849 | ++ |
3850 | ++ test_ip 1 f00000000001 f00000000002 $sip $dip 2 |
3851 | ++ done |
3852 | ++done |
3853 | ++ |
3854 | ++# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.5 should be dropped. |
3855 | ++dip=`ip_to_hex 10 0 0 5` |
3856 | ++for src in `seq 1 2`; do |
3857 | ++ sip=`ip_to_hex 10 0 0 $src` |
3858 | ++ |
3859 | ++ test_ip 1 f00000000001 f00000000002 $sip $dip |
3860 | ++done |
3861 | ++ |
3862 | ++cat 2.expected > expout |
3863 | ++$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets |
3864 | ++AT_CHECK([cat 2.packets], [0], [expout]) |
3865 | ++ |
3866 | ++# Re-add the less restrictive allow ACL for src IP 10.0.0.1 |
3867 | ++ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow |
3868 | ++ovn-nbctl --wait=hv sync |
3869 | ++ |
3870 | ++# Check OVS flows, the less restrictive flows should have been installed. |
3871 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3872 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3873 | ++priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3874 | ++priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3875 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3876 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3877 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
3878 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
3879 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
3880 | ++]) |
3881 | ++ |
3882 | ++OVN_CLEANUP([hv1]) |
3883 | ++AT_CLEANUP |
3884 | ++ |
3885 | + # 3 hypervisors, one logical switch, 3 logical ports per hypervisor |
3886 | + AT_SETUP([ovn -- L2 Drop and Allow ACL w/ Stateful ACL]) |
3887 | + ovn_start |
3888 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-14.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-14.patch |
3889 | new file mode 100644 |
3890 | index 0000000..534e886 |
3891 | --- /dev/null |
3892 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-14.patch |
3893 | @@ -0,0 +1,123 @@ |
3894 | +Description: Cherry-pick fix applied to master and branch-20.12 |
3895 | +Origin: https://github.com/ovn-org/ovn/commit/d6594a4695084e1b0f4fb3170547942876d3a81a |
3896 | +Applied-Upstream: commit: d6594a4695084e1b0f4fb3170547942876d3a81a |
3897 | + |
3898 | +From 0389d5deadba2df04a33178d2b6ef55f615dba79 Mon Sep 17 00:00:00 2001 |
3899 | +From: Dumitru Ceara <dceara@redhat.com> |
3900 | +Date: Tue, 10 Nov 2020 10:37:34 +0100 |
3901 | +Subject: [PATCH 14/15] ovn.at: Make some of the tests more predictable. |
3902 | + |
3903 | +Fix some of the race conditions that are present in the current OVN test |
3904 | +suite. |
3905 | + |
3906 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
3907 | +Signed-off-by: Ben Pfaff <blp@ovn.org> |
3908 | +--- |
3909 | + tests/ovn.at | 48 +++++++++++++++++++++++++++--------------------- |
3910 | + 1 file changed, 27 insertions(+), 21 deletions(-) |
3911 | + |
3912 | +Index: ovn-20.06.2/tests/ovn.at |
3913 | +=================================================================== |
3914 | +--- ovn-20.06.2.orig/tests/ovn.at |
3915 | ++++ ovn-20.06.2/tests/ovn.at |
3916 | +@@ -4065,6 +4065,7 @@ for i in 1 2 3; do |
3917 | + ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr" |
3918 | + ovn-nbctl lsp-set-port-security lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr" |
3919 | + fi |
3920 | ++ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp$i$j` = xup]) |
3921 | + done |
3922 | + done |
3923 | + |
3924 | +@@ -13525,14 +13526,15 @@ ovn-nbctl --wait=hv sync |
3925 | + |
3926 | + # Check OVS flows, the less restrictive flows should have been installed. |
3927 | + AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3928 | +- grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3929 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | \ |
3930 | ++ sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
3931 | + priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3932 | + priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3933 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3934 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3935 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
3936 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
3937 | + priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
3938 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
3939 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
3940 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
3941 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
3942 | + ]) |
3943 | + |
3944 | + # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
3945 | +@@ -13570,14 +13572,15 @@ ovn-nbctl --wait=hv sync |
3946 | + |
3947 | + # Check OVS flows, the second less restrictive allow ACL should have been installed. |
3948 | + AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3949 | +- grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3950 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | \ |
3951 | ++ sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
3952 | + priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3953 | + priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3954 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3955 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3956 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
3957 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
3958 | + priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
3959 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
3960 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
3961 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
3962 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
3963 | + ]) |
3964 | + |
3965 | + # Remove the less restrictive allow ACL. |
3966 | +@@ -13586,14 +13589,15 @@ ovn-nbctl --wait=hv sync |
3967 | + |
3968 | + # Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled. |
3969 | + AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3970 | +- grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3971 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | \ |
3972 | ++ sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
3973 | + priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3974 | + priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3975 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3976 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3977 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(2,2/2),conjunction(3,2/2) |
3978 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
3979 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
3980 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
3981 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
3982 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction() |
3983 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
3984 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
3985 | + ]) |
3986 | + |
3987 | + # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
3988 | +@@ -13624,14 +13628,15 @@ ovn-nbctl --wait=hv sync |
3989 | + |
3990 | + # Check OVS flows, the less restrictive flows should have been installed. |
3991 | + AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
3992 | +- grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl |
3993 | ++ grep "priority=1003" | awk '{print $7 " " $8}' | \ |
3994 | ++ sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
3995 | + priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
3996 | + priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
3997 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) |
3998 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) |
3999 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4000 | ++priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4001 | + priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4002 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) |
4003 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) |
4004 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4005 | ++priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4006 | + ]) |
4007 | + |
4008 | + OVN_CLEANUP([hv1]) |
4009 | +@@ -14229,6 +14234,7 @@ ovn-nbctl set Logical_Switch_Port ls1-lr |
4010 | + # Create HA chassis group |
4011 | + ovn-nbctl ha-chassis-group-add hagrp1 |
4012 | + ovn-nbctl ha-chassis-group-add-chassis hagrp1 hv1 30 |
4013 | ++ovn-nbctl --wait=sb sync |
4014 | + |
4015 | + hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group name="hagrp1"` |
4016 | + |
4017 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-15.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-15.patch |
4018 | new file mode 100644 |
4019 | index 0000000..cb13bb7 |
4020 | --- /dev/null |
4021 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-15.patch |
4022 | @@ -0,0 +1,66 @@ |
4023 | +Description: Cherry-pick fix applied to master |
4024 | +Origin: https://github.com/ovn-org/ovn/commit/0879a0aa9f332bdc689769a1bc2f156f1c3149a5 |
4025 | +Applied-Upstream: commit: 0879a0aa9f332bdc689769a1bc2f156f1c3149a5 |
4026 | + |
4027 | +From 0879a0aa9f332bdc689769a1bc2f156f1c3149a5 Mon Sep 17 00:00:00 2001 |
4028 | +From: Dumitru Ceara <dceara@redhat.com> |
4029 | +Date: Mon, 7 Dec 2020 21:30:52 +0100 |
4030 | +Subject: [PATCH] tests: Add ofctl_strip_all() to filter OVS flow outputs. |
4031 | + |
4032 | +Extend the already existing ofctl_strip() to also ignore n_packets, |
4033 | +n_bytes, cookie. Use some helper functions from |
4034 | +tests/system-userspace-packet-type-aware.at, which will be removed |
4035 | +by a future commit. |
4036 | + |
4037 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
4038 | +Signed-off-by: Numan Siddique <numans@ovn.org> |
4039 | +--- |
4040 | + tests/ofproto-macros.at | 23 ++++++++++++++++++++++- |
4041 | + 1 file changed, 22 insertions(+), 1 deletion(-) |
4042 | + |
4043 | +diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at |
4044 | +index a6e89a951..dd5d3848d 100644 |
4045 | +--- a/tests/ofproto-macros.at |
4046 | ++++ b/tests/ofproto-macros.at |
4047 | +@@ -1,12 +1,27 @@ |
4048 | + m4_divert_push([PREPARE_TESTS]) |
4049 | + [ |
4050 | ++# Strips 'n_packets=...' from ovs-ofctl output. |
4051 | ++strip_n_packets () { |
4052 | ++ sed 's/ n_packets=[0-9]*,//' |
4053 | ++} |
4054 | ++ |
4055 | ++# Strips 'n_bytes=...' from ovs-ofctl output. |
4056 | ++strip_n_bytes () { |
4057 | ++ sed 's/ n_bytes=[0-9]*,//' |
4058 | ++} |
4059 | ++ |
4060 | ++# Strips 'cookie=...' from ovs-ofctl output. |
4061 | ++strip_cookie () { |
4062 | ++ sed 's/ cookie=0x[0-9a-fA-F]*,//' |
4063 | ++} |
4064 | ++ |
4065 | + # Strips out uninteresting parts of ovs-ofctl output, as well as parts |
4066 | + # that vary from one run to another. |
4067 | + ofctl_strip () { |
4068 | + sed ' |
4069 | + s/ (xid=0x[0-9a-fA-F]*)// |
4070 | + s/ duration=[0-9.]*s,// |
4071 | +-s/ cookie=0x0,// |
4072 | ++s/ cookie=0,// |
4073 | + s/ table=0,// |
4074 | + s/ n_packets=0,// |
4075 | + s/ n_bytes=0,// |
4076 | +@@ -19,6 +34,12 @@ s/dir\/[0-9]*\/br0.mgmt/dir\/XXXX\/br0.mgmt/ |
4077 | + ' |
4078 | + } |
4079 | + |
4080 | ++# Strips out uninteresting parts of ovs-ofctl output, including n_packets=.. |
4081 | ++# n_bytes=.. |
4082 | ++ofctl_strip_all () { |
4083 | ++ ofctl_strip | strip_n_packets | strip_n_bytes | strip_cookie |
4084 | ++} |
4085 | ++ |
4086 | + # Filter (multiline) vconn debug messages from ovs-vswitchd.log. |
4087 | + # Use with vconn_sub() and ofctl_strip() |
4088 | + print_vconn_debug () { awk -F\| < ovs-vswitchd.log ' |
4089 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-16.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-16.patch |
4090 | new file mode 100644 |
4091 | index 0000000..3b00c75 |
4092 | --- /dev/null |
4093 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-16.patch |
4094 | @@ -0,0 +1,132 @@ |
4095 | +Description: Cherry-pick fix applied to master and branch-20.12 |
4096 | +Origin: https://github.com/ovn-org/ovn/commit/6e3d69e6b5153e26c869a03ec7dd1aaa40488005 |
4097 | +Applied-Upstream: commit: 6e3d69e6b5153e26c869a03ec7dd1aaa40488005 |
4098 | + |
4099 | +From f144e0e33af8ca8e1748546c379f0b22b86576b7 Mon Sep 17 00:00:00 2001 |
4100 | +From: Dumitru Ceara <dceara@redhat.com> |
4101 | +Date: Mon, 7 Dec 2020 21:31:25 +0100 |
4102 | +Subject: [PATCH 15/15] tests: Fix test "ovn -- Superseding ACLs with |
4103 | + conjunction". |
4104 | + |
4105 | +The test was checking the output of "ovs-ofctl dump-flows" without |
4106 | +taking into account that some fields don't have predictable values, |
4107 | +e.g., hard_age. |
4108 | + |
4109 | +Instead, use ofctl_strip_all(). |
4110 | + |
4111 | +Fixes: 986b3d5e4ad6 ("ofctrl.c: Add a predictable resolution for conflicting flow actions.") |
4112 | +Reported-by: Numan Siddique <numans@ovn.org> |
4113 | +Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
4114 | +Signed-off-by: Numan Siddique <numans@ovn.org> |
4115 | +--- |
4116 | + tests/ovn.at | 72 ++++++++++++++++++++++++++-------------------------- |
4117 | + 1 file changed, 36 insertions(+), 36 deletions(-) |
4118 | + |
4119 | +Index: ovn-20.06.2/tests/ovn.at |
4120 | +=================================================================== |
4121 | +--- ovn-20.06.2.orig/tests/ovn.at |
4122 | ++++ ovn-20.06.2/tests/ovn.at |
4123 | +@@ -13525,16 +13525,16 @@ ovn-nbctl acl-add ls1 to-lport 3 'ip4.sr |
4124 | + ovn-nbctl --wait=hv sync |
4125 | + |
4126 | + # Check OVS flows, the less restrictive flows should have been installed. |
4127 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
4128 | +- grep "priority=1003" | awk '{print $7 " " $8}' | \ |
4129 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4130 | ++ grep "priority=1003" | \ |
4131 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4132 | +-priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4133 | +-priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4134 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4135 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4136 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4137 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4138 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4139 | ++ table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4140 | ++ table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4141 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4142 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4143 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4144 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4145 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4146 | + ]) |
4147 | + |
4148 | + # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
4149 | +@@ -13571,16 +13571,16 @@ ovn-nbctl acl-del ls1 to-lport 3 'ip4.sr |
4150 | + ovn-nbctl --wait=hv sync |
4151 | + |
4152 | + # Check OVS flows, the second less restrictive allow ACL should have been installed. |
4153 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
4154 | +- grep "priority=1003" | awk '{print $7 " " $8}' | \ |
4155 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4156 | ++ grep "priority=1003" | \ |
4157 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4158 | +-priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4159 | +-priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4160 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4161 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4162 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4163 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4164 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4165 | ++ table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4166 | ++ table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4167 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4168 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4169 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4170 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4171 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4172 | + ]) |
4173 | + |
4174 | + # Remove the less restrictive allow ACL. |
4175 | +@@ -13588,16 +13588,16 @@ ovn-nbctl acl-del ls1 to-lport 3 'ip4.sr |
4176 | + ovn-nbctl --wait=hv sync |
4177 | + |
4178 | + # Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled. |
4179 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
4180 | +- grep "priority=1003" | awk '{print $7 " " $8}' | \ |
4181 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4182 | ++ grep "priority=1003" | \ |
4183 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4184 | +-priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4185 | +-priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4186 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4187 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4188 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction() |
4189 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4190 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4191 | ++ table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4192 | ++ table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4193 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4194 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4195 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction() |
4196 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4197 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4198 | + ]) |
4199 | + |
4200 | + # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
4201 | +@@ -13627,16 +13627,16 @@ ovn-nbctl acl-add ls1 to-lport 3 'ip4.sr |
4202 | + ovn-nbctl --wait=hv sync |
4203 | + |
4204 | + # Check OVS flows, the less restrictive flows should have been installed. |
4205 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ |
4206 | +- grep "priority=1003" | awk '{print $7 " " $8}' | \ |
4207 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4208 | ++ grep "priority=1003" | \ |
4209 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4210 | +-priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4211 | +-priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4212 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4213 | +-priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4214 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4215 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4216 | +-priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4217 | ++ table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4218 | ++ table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4219 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4220 | ++ table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4221 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4222 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4223 | ++ table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4224 | + ]) |
4225 | + |
4226 | + OVN_CLEANUP([hv1]) |
4227 | diff --git a/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-17.patch b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-17.patch |
4228 | new file mode 100644 |
4229 | index 0000000..8f73b65 |
4230 | --- /dev/null |
4231 | +++ b/debian/patches/ovn-ofctrl-predictable-resolution-conflicting-flow-actions-17.patch |
4232 | @@ -0,0 +1,108 @@ |
4233 | +Description: Adapt test suite to 20.03 |
4234 | +Author: Frode Nordahl <frode.nordahl@canonical.com> |
4235 | +Forwarded: not-needed |
4236 | + |
4237 | +Index: ovn-20.06.2/tests/ovn.at |
4238 | +=================================================================== |
4239 | +--- ovn-20.06.2.orig/tests/ovn.at |
4240 | ++++ ovn-20.06.2/tests/ovn.at |
4241 | +@@ -13525,16 +13525,16 @@ ovn-nbctl acl-add ls1 to-lport 3 'ip4.sr |
4242 | + ovn-nbctl --wait=hv sync |
4243 | + |
4244 | + # Check OVS flows, the less restrictive flows should have been installed. |
4245 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4246 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \ |
4247 | + grep "priority=1003" | \ |
4248 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4249 | +- table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4250 | +- table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4251 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4252 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4253 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4254 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4255 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4256 | ++ table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45) |
4257 | ++ table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45) |
4258 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4259 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4260 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45) |
4261 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4262 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4263 | + ]) |
4264 | + |
4265 | + # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
4266 | +@@ -13571,16 +13571,16 @@ ovn-nbctl acl-del ls1 to-lport 3 'ip4.sr |
4267 | + ovn-nbctl --wait=hv sync |
4268 | + |
4269 | + # Check OVS flows, the second less restrictive allow ACL should have been installed. |
4270 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4271 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \ |
4272 | + grep "priority=1003" | \ |
4273 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4274 | +- table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4275 | +- table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4276 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4277 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4278 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4279 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4280 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4281 | ++ table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45) |
4282 | ++ table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45) |
4283 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4284 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4285 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45) |
4286 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4287 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4288 | + ]) |
4289 | + |
4290 | + # Remove the less restrictive allow ACL. |
4291 | +@@ -13588,16 +13588,16 @@ ovn-nbctl acl-del ls1 to-lport 3 'ip4.sr |
4292 | + ovn-nbctl --wait=hv sync |
4293 | + |
4294 | + # Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled. |
4295 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4296 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \ |
4297 | + grep "priority=1003" | \ |
4298 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4299 | +- table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4300 | +- table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4301 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4302 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4303 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction() |
4304 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4305 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4306 | ++ table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45) |
4307 | ++ table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45) |
4308 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4309 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4310 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction() |
4311 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4312 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4313 | + ]) |
4314 | + |
4315 | + # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. |
4316 | +@@ -13627,16 +13627,16 @@ ovn-nbctl acl-add ls1 to-lport 3 'ip4.sr |
4317 | + ovn-nbctl --wait=hv sync |
4318 | + |
4319 | + # Check OVS flows, the less restrictive flows should have been installed. |
4320 | +-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \ |
4321 | ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \ |
4322 | + grep "priority=1003" | \ |
4323 | + sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl |
4324 | +- table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) |
4325 | +- table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) |
4326 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4327 | +- table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4328 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) |
4329 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4330 | +- table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4331 | ++ table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45) |
4332 | ++ table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45) |
4333 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction() |
4334 | ++ table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction() |
4335 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45) |
4336 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction() |
4337 | ++ table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction() |
4338 | + ]) |
4339 | + |
4340 | + OVN_CLEANUP([hv1]) |
4341 | diff --git a/debian/patches/series b/debian/patches/series |
4342 | index d55d9eb..0948af0 100644 |
4343 | --- a/debian/patches/series |
4344 | +++ b/debian/patches/series |
4345 | @@ -1 +1,22 @@ |
4346 | ovn-controller-ofctrl-probe-interval.patch |
4347 | +<<<<<<< debian/patches/series |
4348 | +======= |
4349 | +ovn-ctl-cluster-db-upgrades.patch |
4350 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-01.patch |
4351 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-02.patch |
4352 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-03.patch |
4353 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-04.patch |
4354 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-05.patch |
4355 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-06.patch |
4356 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-07.patch |
4357 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-08.patch |
4358 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-09.patch |
4359 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-10.patch |
4360 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-11.patch |
4361 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-12.patch |
4362 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-13.patch |
4363 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-14.patch |
4364 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-15.patch |
4365 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-16.patch |
4366 | +ovn-ofctrl-predictable-resolution-conflicting-flow-actions-17.patch |
4367 | +>>>>>>> debian/patches/series |