Merge ~fnordahl/ubuntu/+source/ovn:ubuntu/groovy into ~ubuntu-server-dev/ubuntu/+source/ovn: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
Reviewer Review Type Date Requested Status
Ubuntu Server Developers Pending
Review via email: mp+395221@code.launchpad.net
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
1diff --git a/debian/changelog b/debian/changelog
2index 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
29diff --git a/debian/patches/ovn-ctl-cluster-db-upgrades.patch b/debian/patches/ovn-ctl-cluster-db-upgrades.patch
30new file mode 100644
31index 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() {
98diff --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
99new file mode 100644
100index 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,
167diff --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
168new file mode 100644
169index 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
1064diff --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
1065new file mode 100644
1066index 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
1793diff --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
1794new file mode 100644
1795index 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. */
1953diff --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
1954new file mode 100644
1955index 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 *);
2442diff --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
2443new file mode 100644
2444index 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) {
2557diff --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
2558new file mode 100644
2559index 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
2797diff --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
2798new file mode 100644
2799index 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+
2893diff --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
2894new file mode 100644
2895index 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. */
3115diff --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
3116new file mode 100644
3117index 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);
3198diff --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
3199new file mode 100644
3200index 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)");
3420diff --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
3421new file mode 100644
3422index 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. */
3466diff --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
3467new file mode 100644
3468index 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
3888diff --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
3889new file mode 100644
3890index 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+
4017diff --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
4018new file mode 100644
4019index 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 '
4089diff --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
4090new file mode 100644
4091index 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])
4227diff --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
4228new file mode 100644
4229index 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])
4341diff --git a/debian/patches/series b/debian/patches/series
4342index 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

Subscribers

People subscribed via source and target branches