diff --git a/SOURCES/openvswitch-2.15.0.patch b/SOURCES/openvswitch-2.15.0.patch
index b67f9aa..44c997c 100644
--- a/SOURCES/openvswitch-2.15.0.patch
+++ b/SOURCES/openvswitch-2.15.0.patch
@@ -19110,6 +19110,47 @@ index c20bcc0b33..9c83f1913a 100644
atomic_count_dec(&ipf->nfrag);
ipf_list->last_sent_idx++;
}
+diff --git a/lib/jsonrpc.c b/lib/jsonrpc.c
+index 8c5126ffcb..df0396815a 100644
+--- a/lib/jsonrpc.c
++++ b/lib/jsonrpc.c
+@@ -1261,6 +1261,24 @@ jsonrpc_session_force_reconnect(struct jsonrpc_session *s)
+ reconnect_force_reconnect(s->reconnect, time_msec());
+ }
+
++/* Resets the reconnect backoff for 's' by allowing as many free tries as the
++ * number of configured remotes. This is to be used by upper layers before
++ * calling jsonrpc_session_force_reconnect() if backoff is undesirable.
++ */
++void
++jsonrpc_session_reset_backoff(struct jsonrpc_session *s)
++{
++ unsigned int free_tries = s->remotes.n;
++
++ if (jsonrpc_session_is_connected(s)) {
++ /* The extra free try will be consumed when the current remote
++ * is disconnected.
++ */
++ free_tries++;
++ }
++ reconnect_set_backoff_free_tries(s->reconnect, free_tries);
++}
++
+ /* Sets 'max_backoff' as the maximum time, in milliseconds, to wait after a
+ * connection attempt fails before attempting to connect again. */
+ void
+diff --git a/lib/jsonrpc.h b/lib/jsonrpc.h
+index d75d66b863..ba096dd0c8 100644
+--- a/lib/jsonrpc.h
++++ b/lib/jsonrpc.h
+@@ -136,6 +136,7 @@ void jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *,
+
+ void jsonrpc_session_enable_reconnect(struct jsonrpc_session *);
+ void jsonrpc_session_force_reconnect(struct jsonrpc_session *);
++void jsonrpc_session_reset_backoff(struct jsonrpc_session *);
+
+ void jsonrpc_session_set_max_backoff(struct jsonrpc_session *,
+ int max_backoff);
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 6be23dbeed..0ab511b60d 100644
--- a/lib/netdev-linux.c
@@ -19686,10 +19727,27 @@ index a2778de4bc..3894cb3c33 100644
Open vSwitch 2.6 introduced nat
. Linux 4.6 was the
earliest upstream kernel that implemented ct
support for
diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
-index ff8adaefb5..911b71dd4f 100644
+index ff8adaefb5..7c78056956 100644
--- a/lib/ovsdb-cs.c
+++ b/lib/ovsdb-cs.c
-@@ -903,8 +903,27 @@ ovsdb_cs_db_set_condition(struct ovsdb_cs_db *db, const char *table,
+@@ -712,6 +712,16 @@ void
+ ovsdb_cs_force_reconnect(struct ovsdb_cs *cs)
+ {
+ if (cs->session) {
++ if (cs->state == CS_S_MONITORING) {
++ /* The ovsdb-cs was in MONITORING state, so we either had data
++ * inconsistency on this server, or it stopped being the cluster
++ * leader, or the user requested to re-connect. Avoiding backoff
++ * in these cases, as we need to re-connect as soon as possible.
++ * Connections that are not in MONITORING state should have their
++ * backoff to avoid constant flood of re-connection attempts in
++ * case there is no suitable database server. */
++ jsonrpc_session_reset_backoff(cs->session);
++ }
+ jsonrpc_session_force_reconnect(cs->session);
+ }
+ }
+@@ -903,8 +913,27 @@ ovsdb_cs_db_set_condition(struct ovsdb_cs_db *db, const char *table,
}
/* Conditions will be up to date when we receive replies for already
@@ -19719,7 +19777,7 @@ index ff8adaefb5..911b71dd4f 100644
}
/* Sets the replication condition for 'tc' in 'cs' to 'condition' and arranges
-@@ -1367,7 +1386,7 @@ ovsdb_cs_send_transaction(struct ovsdb_cs *cs, struct json *operations)
+@@ -1367,7 +1396,7 @@ ovsdb_cs_send_transaction(struct ovsdb_cs *cs, struct json *operations)
sizeof *cs->txns);
}
cs->txns[cs->n_txns++] = request_id;
@@ -19728,7 +19786,7 @@ index ff8adaefb5..911b71dd4f 100644
}
/* Makes 'cs' drop its record of transaction 'request_id'. If a reply arrives
-@@ -1380,6 +1399,7 @@ ovsdb_cs_forget_transaction(struct ovsdb_cs *cs, const struct json *request_id)
+@@ -1380,6 +1409,7 @@ ovsdb_cs_forget_transaction(struct ovsdb_cs *cs, const struct json *request_id)
{
for (size_t i = 0; i < cs->n_txns; i++) {
if (json_equal(request_id, cs->txns[i])) {
@@ -20707,7 +20765,7 @@ index 8aec6bbac1..ba55566926 100644
from collections import Sequence, MutableSequence
from functools import wraps
diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py
-index 5850ac7abf..4cf79cf94e 100644
+index 5850ac7abf..6cb5bef10b 100644
--- a/python/ovs/db/idl.py
+++ b/python/ovs/db/idl.py
@@ -12,6 +12,7 @@
@@ -20729,7 +20787,15 @@ index 5850ac7abf..4cf79cf94e 100644
class Idl(object):
"""Open vSwitch Database Interface Definition Language (OVSDB IDL).
-@@ -241,6 +246,7 @@ class Idl(object):
+@@ -96,6 +101,7 @@ class Idl(object):
+ IDL_S_SERVER_MONITOR_REQUESTED = 2
+ IDL_S_DATA_MONITOR_REQUESTED = 3
+ IDL_S_DATA_MONITOR_COND_REQUESTED = 4
++ IDL_S_MONITORING = 5
+
+ def __init__(self, remote, schema_helper, probe_interval=None,
+ leader_only=True):
+@@ -241,6 +247,7 @@ class Idl(object):
i = 0
while i < 50:
i += 1
@@ -20737,7 +20803,7 @@ index 5850ac7abf..4cf79cf94e 100644
if not self._session.is_connected():
break
-@@ -269,7 +275,7 @@ class Idl(object):
+@@ -269,7 +276,7 @@ class Idl(object):
if msg.params[0] == str(self.server_monitor_uuid):
self.__parse_update(msg.params[1], OVSDB_UPDATE,
tables=self.server_tables)
@@ -20746,7 +20812,15 @@ index 5850ac7abf..4cf79cf94e 100644
if not self.__check_server_db():
self.force_reconnect()
break
-@@ -312,7 +318,7 @@ class Idl(object):
+@@ -288,6 +295,7 @@ class Idl(object):
+ else:
+ assert self.state == self.IDL_S_DATA_MONITOR_REQUESTED
+ self.__parse_update(msg.result, OVSDB_UPDATE)
++ self.state = self.IDL_S_MONITORING
+
+ except error.Error as e:
+ vlog.err("%s: parse error in received schema: %s"
+@@ -312,7 +320,7 @@ class Idl(object):
self.__error()
break
else:
@@ -20755,7 +20829,7 @@ index 5850ac7abf..4cf79cf94e 100644
self.__send_monitor_request()
elif (msg.type == ovs.jsonrpc.Message.T_REPLY
and self._server_monitor_request_id is not None
-@@ -322,7 +328,7 @@ class Idl(object):
+@@ -322,7 +330,7 @@ class Idl(object):
self._server_monitor_request_id = None
self.__parse_update(msg.result, OVSDB_UPDATE,
tables=self.server_tables)
@@ -20764,7 +20838,7 @@ index 5850ac7abf..4cf79cf94e 100644
if self.__check_server_db():
self.__send_monitor_request()
self.__send_db_change_aware()
-@@ -336,7 +342,7 @@ class Idl(object):
+@@ -336,7 +344,7 @@ class Idl(object):
self.__error()
break
else:
@@ -20773,7 +20847,7 @@ index 5850ac7abf..4cf79cf94e 100644
self.__send_monitor_request()
elif (msg.type == ovs.jsonrpc.Message.T_REPLY
and self._db_change_aware_request_id is not None
-@@ -372,7 +378,7 @@ class Idl(object):
+@@ -372,7 +380,7 @@ class Idl(object):
self.force_reconnect()
break
else:
@@ -20782,7 +20856,23 @@ index 5850ac7abf..4cf79cf94e 100644
self.__send_monitor_request()
elif (msg.type in (ovs.jsonrpc.Message.T_ERROR,
ovs.jsonrpc.Message.T_REPLY)
-@@ -614,6 +620,7 @@ class Idl(object):
+@@ -435,6 +443,15 @@ class Idl(object):
+ def force_reconnect(self):
+ """Forces the IDL to drop its connection to the database and reconnect.
+ In the meantime, the contents of the IDL will not change."""
++ if self.state == self.IDL_S_MONITORING:
++ # The IDL was in MONITORING state, so we either had data
++ # inconsistency on this server, or it stopped being the cluster
++ # leader, or the user requested to re-connect. Avoiding backoff
++ # in these cases, as we need to re-connect as soon as possible.
++ # Connections that are not in MONITORING state should have their
++ # backoff to avoid constant flood of re-connection attempts in
++ # case there is no suitable database server.
++ self._session.reset_backoff()
+ self._session.force_reconnect()
+
+ def session_name(self):
+@@ -614,6 +631,7 @@ class Idl(object):
raise error.Error(" is not an object",
table_updates)
@@ -20790,7 +20880,7 @@ index 5850ac7abf..4cf79cf94e 100644
for table_name, table_update in table_updates.items():
table = tables.get(table_name)
if not table:
-@@ -639,7 +646,9 @@ class Idl(object):
+@@ -639,7 +657,9 @@ class Idl(object):
% (table_name, uuid_string))
if version == OVSDB_UPDATE2:
@@ -20801,7 +20891,7 @@ index 5850ac7abf..4cf79cf94e 100644
self.change_seqno += 1
continue
-@@ -652,17 +661,20 @@ class Idl(object):
+@@ -652,17 +672,20 @@ class Idl(object):
raise error.Error(' missing "old" and '
'"new" members', row_update)
@@ -20826,7 +20916,7 @@ index 5850ac7abf..4cf79cf94e 100644
else:
# XXX rate-limit
vlog.warn("cannot delete missing row %s from table"
-@@ -681,29 +693,27 @@ class Idl(object):
+@@ -681,29 +704,27 @@ class Idl(object):
changed = self.__row_update(table, row, row_update)
table.rows[uuid] = row
if changed:
@@ -20861,7 +20951,7 @@ index 5850ac7abf..4cf79cf94e 100644
else:
# XXX rate-limit
vlog.warn("cannot delete missing row %s from table %s"
-@@ -723,7 +733,7 @@ class Idl(object):
+@@ -723,7 +744,7 @@ class Idl(object):
if op == ROW_CREATE:
table.rows[uuid] = row
if changed:
@@ -20870,7 +20960,7 @@ index 5850ac7abf..4cf79cf94e 100644
else:
op = ROW_UPDATE
if not row:
-@@ -737,8 +747,8 @@ class Idl(object):
+@@ -737,8 +758,8 @@ class Idl(object):
if op == ROW_CREATE:
table.rows[uuid] = row
if changed:
@@ -20881,6 +20971,29 @@ index 5850ac7abf..4cf79cf94e 100644
def __check_server_db(self):
"""Returns True if this is a valid server database, False otherwise."""
+diff --git a/python/ovs/jsonrpc.py b/python/ovs/jsonrpc.py
+index bf32f8c87c..d5127268aa 100644
+--- a/python/ovs/jsonrpc.py
++++ b/python/ovs/jsonrpc.py
+@@ -612,5 +612,18 @@ class Session(object):
+ def force_reconnect(self):
+ self.reconnect.force_reconnect(ovs.timeval.msec())
+
++ def reset_backoff(self):
++ """ Resets the reconnect backoff by allowing as many free tries as the
++ number of configured remotes. This is to be used by upper layers
++ before calling force_reconnect() if backoff is undesirable."""
++ free_tries = len(self.remotes)
++
++ if self.is_connected():
++ # The extra free try will be consumed when the current remote
++ # is disconnected.
++ free_tries += 1
++
++ self.reconnect.set_backoff_free_tries(free_tries)
++
+ def get_num_of_remotes(self):
+ return len(self.remotes)
diff --git a/python/ovstest/rpcserver.py b/python/ovstest/rpcserver.py
index c4aab70207..05b6b1be20 100644
--- a/python/ovstest/rpcserver.py
@@ -21521,7 +21634,7 @@ index 92aa427093..cf43e9cf86 100644
# Start collecting raft_is_connected logs for $target before shutting down
# any servers.
diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
-index 4b4791a7da..62181dd4de 100644
+index 4b4791a7da..d5cdf7e8b0 100644
--- a/tests/ovsdb-idl.at
+++ b/tests/ovsdb-idl.at
@@ -141,7 +141,7 @@ m4_define([OVSDB_CHECK_IDL_REGISTER_COLUMNS_PY],
@@ -22631,7 +22744,78 @@ index 4b4791a7da..62181dd4de 100644
005: After delete
007: check simple4: empty
008: End test
-@@ -1989,11 +2274,11 @@ OVSDB_CHECK_CLUSTER_IDL_C([simple idl, monitor_cond_since, cluster disconnect],
+@@ -1942,11 +2227,29 @@ m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY],
+ OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL connects to leader], 3, ['remote'])
+ OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL reconnects to leader], 3, ['remote' '+remotestop' 'remote'])
+
+-# same as OVSDB_CHECK_IDL but uses C IDL implementation with tcp
+-# with multiple remotes.
++# OVSDB_CHECK_CLUSTER_IDL_C(TITLE, N_SERVERS, [PRE-IDL-TXN], TRANSACTIONS,
++# OUTPUT, [KEYWORDS], [FILTER], [LOG_FILTER])
++#
++# Creates a clustered database with a schema derived from idltest.ovsidl, runs
++# each PRE-IDL-TXN (if any), starts N_SERVERS ovsdb-server instances in RAFT,
++# on that database, and runs "test-ovsdb idl" passing each of the TRANSACTIONS
++# along.
++#
++# Checks that the overall output is OUTPUT. Before comparison, the
++# output is sorted (using "sort") and UUIDs in the output are replaced
++# by markers of the form where N is a number. The first unique
++# UUID is replaced by <0>, the next by <1>, and so on. If a given
++# UUID appears more than once it is always replaced by the same
++# marker. If FILTER is supplied then the output is also filtered
++# through the specified program.
++#
++# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
++#
++# If LOG_FILTER is provided, checks that the contents of LOG_FILTER
++# are not matched by grep in the test-ovsdb logs.
+ m4_define([OVSDB_CHECK_CLUSTER_IDL_C],
+ [AT_SETUP([$1 - C - tcp])
+- AT_KEYWORDS([ovsdb server idl positive tcp socket $5])
++ AT_KEYWORDS([ovsdb server idl tcp $6])
+ m4_define([LPBK],[127.0.0.1])
+ OVSDB_CLUSTER_START_IDLTEST([$2], ["ptcp:0:"LPBK])
+ PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1])
+@@ -1957,11 +2260,36 @@ m4_define([OVSDB_CHECK_CLUSTER_IDL_C],
+ m4_if([$3], [], [],
+ [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])])
+ AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:LPBK:$TCP_PORT_1 $4],
+- [0], [stdout], [ignore])
++ [0], [stdout], [stderr])
++ AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]),
++ [0], [$5])
++ m4_ifval([$8], [AT_CHECK([grep '$8' stderr], [1])], [], [])
++ AT_CLEANUP])
++
++# Same as OVSDB_CHECK_CLUSTER_IDL_C but uses the Python IDL implementation.
++m4_define([OVSDB_CHECK_CLUSTER_IDL_PY],
++ [AT_SETUP([$1 - Python3 - tcp])
++ AT_KEYWORDS([ovsdb server idl tcp $6])
++ m4_define([LPBK],[127.0.0.1])
++ OVSDB_CLUSTER_START_IDLTEST([$2], ["ptcp:0:"LPBK])
++ PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1])
++ PARSE_LISTENING_PORT([s2.log], [TCP_PORT_2])
++ PARSE_LISTENING_PORT([s3.log], [TCP_PORT_3])
++ remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3
++
++ m4_if([$3], [], [],
++ [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])])
++ AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema tcp:LPBK:$TCP_PORT_1 $4],
++ [0], [stdout], [stderr])
+ AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]),
+ [0], [$5])
++ m4_if([$8], [AT_CHECK([grep '$8' stderr], [1])], [], [])
+ AT_CLEANUP])
+
++m4_define([OVSDB_CHECK_CLUSTER_IDL],
++ [OVSDB_CHECK_CLUSTER_IDL_C($@)
++ OVSDB_CHECK_CLUSTER_IDL_PY($@)])
++
+ # Checks that monitor_cond_since works fine when disconnects happen
+ # with cond_change requests in flight (i.e., IDL is properly updated).
+ OVSDB_CHECK_CLUSTER_IDL_C([simple idl, monitor_cond_since, cluster disconnect],
+@@ -1989,11 +2317,34 @@ OVSDB_CHECK_CLUSTER_IDL_C([simple idl, monitor_cond_since, cluster disconnect],
[[000: change conditions
001: empty
002: change conditions
@@ -22646,6 +22830,29 @@ index 4b4791a7da..62181dd4de 100644
+008: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2>
009: done
]])
++
++dnl This test checks that forceful reconnects triggered by the IDL
++dnl happen immediately (they should not use backoff).
++OVSDB_CHECK_CLUSTER_IDL([simple idl, initially empty, force reconnect],
++ 3,
++ [],
++ [['+reconnect' \
++ 'reconnect' \
++ 'reconnect' \
++ 'reconnect']],
++ [[000: reconnect
++001: empty
++002: reconnect
++003: empty
++004: reconnect
++005: empty
++006: reconnect
++007: empty
++008: done
++]],
++[],
++[],
++reconnect.*waiting .* seconds before reconnect)
diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at
index 15628a7c6f..86d633ac4f 100644
--- a/tests/system-kmod-macros.at
diff --git a/SPECS/openvswitch2.15.spec b/SPECS/openvswitch2.15.spec
index 03aeac2..6a365f4 100644
--- a/SPECS/openvswitch2.15.spec
+++ b/SPECS/openvswitch2.15.spec
@@ -57,7 +57,7 @@ Summary: Open vSwitch
Group: System Environment/Daemons daemon/database/utilities
URL: http://www.openvswitch.org/
Version: 2.15.0
-Release: 28%{?dist}
+Release: 29%{?dist}
# Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the
# lib/sflow*.[ch] files are SISSL
@@ -697,6 +697,12 @@ exit 0
%endif
%changelog
+* Fri Jul 23 2021 Open vSwitch CI - 2.15.0-29
+- Merging upstream branch-2.15 [RH gerrit: 60c8b2a15b]
+ Commit list:
+ 8aa0f03747 ovsdb-cs: Perform forced reconnects without a backoff.
+
+
* Wed Jul 21 2021 Open vSwitch CI - 2.15.0-28
- Merging upstream branch-2.15 [RH gerrit: 48a90081e8]
Commit list: