Skip to content

Commit 621f85e

Browse files
simonartxavierdceara
authored andcommitted
controller: Fix bfd up too early after unexpected reboot.
If a server unexpectedly rebooted, OVS, when restarted, sets BFD UP on bfd-enabled geneve tunnels. However, if it takes time to restart OVN, an HA gw chassis would attract the traffic while being unable to handle it (as no flows), resulting in traffic loss. This is fixed by re-using ovs flow-restore-wait. If set, OVS waits (prevents upcalls, ignores bfd, ...) until reset. Once OVS receives the notification of flow-restore-wait being false, it restarts handling upcalls, bfd... and ignores any new change to flow-restore-wait. Hence, on chassis hosting ha gateways, OVN toggles flow-restore-wait: it set it to false, waits for ack from OVS and then sets it back to true. If server reboots, OVS will see flow-restore-wait being true. OVN also sets external_ids->ovn-managed-flow-restore-wait when setting flow-restore-wait. When set, it tells that OVN once set flow-restore-wait. "ovs-ctl restart" also uses flow-restore-wait: when called, it saves the flows, stops "ovs-vswitchd", sets "flow-restore-wait" to true, restarts "ovs-vswitchd", restores the flows and finally removes "flow-restore-wait". So OVS will wait either for "ovs-ctl restart" to remove "flow-restore-wait" or for OVN to set "flow-restore-wait" to false. Reported-at: https://issues.redhat.com/browse/FDP-3075 Signed-off-by: Xavier Simonart <xsimonar@redhat.com> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
1 parent 31d9441 commit 621f85e

6 files changed

Lines changed: 612 additions & 175 deletions

File tree

controller/bfd.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,14 @@ bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
117117
*
118118
* If 'our_chassis' is C5 then this function returns empty bfd set.
119119
*/
120-
void
120+
bool
121121
bfd_calculate_chassis(
122122
const struct sbrec_chassis *our_chassis,
123123
const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
124124
struct sset *bfd_chassis)
125125
{
126126
const struct sbrec_ha_chassis_group *ha_chassis_grp;
127+
bool chassis_is_ha_gw = false;
127128
SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_chassis_grp,
128129
ha_chassis_grp_table) {
129130
bool is_ha_chassis = false;
@@ -143,6 +144,7 @@ bfd_calculate_chassis(
143144
sset_add(&grp_chassis, ha_ch->chassis->name);
144145
if (our_chassis == ha_ch->chassis) {
145146
is_ha_chassis = true;
147+
chassis_is_ha_gw = true;
146148
bfd_setup_required = true;
147149
}
148150
}
@@ -178,6 +180,7 @@ bfd_calculate_chassis(
178180
}
179181
sset_destroy(&grp_chassis);
180182
}
183+
return chassis_is_ha_gw;
181184
}
182185

183186
void

controller/bfd.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#ifndef OVN_BFD_H
1717
#define OVN_BFD_H 1
1818

19+
#include <stdbool.h>
20+
1921
struct hmap;
2022
struct ovsdb_idl;
2123
struct ovsdb_idl_index;
@@ -36,7 +38,7 @@ void bfd_run(const struct ovsrec_interface_table *,
3638
const struct sbrec_sb_global_table *,
3739
const struct ovsrec_open_vswitch_table *);
3840

39-
void bfd_calculate_chassis(
41+
bool bfd_calculate_chassis(
4042
const struct sbrec_chassis *,
4143
const struct sbrec_ha_chassis_group_table *,
4244
struct sset *);

controller/ovn-controller.8.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,17 @@
531531
65535.
532532
</dd>
533533

534+
<dt>
535+
<code>external_ids:ovn-managed-flow-restore-wait</code> in the
536+
<code>Open_vSwitch</code> table
537+
</dt>
538+
<dd>
539+
When set to true, this key indicates that <code>ovn-controller</code>
540+
has set the <code>other_config:flow-restore-wait</code> option.
541+
The key is set when <code>ovn-controller</code> enables
542+
flow-restore-wait and removed when it clears it.
543+
</dd>
544+
534545
<dt>
535546
<code>external_ids:ct-zone-*</code> in the <code>Bridge</code> table
536547
</dt>

controller/ovn-controller.c

Lines changed: 169 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,152 @@ static char *get_file_system_id(void)
211211
free(filename);
212212
return ret;
213213
}
214+
215+
/* Set/unset flow-restore-wait, and inc ovs next_cfg if false
216+
* When set to true, also sets ovn-managed-flow-restore-wait to true to
217+
* indicate ownership */
218+
static void
219+
set_flow_restore_wait(struct ovsdb_idl_txn *ovs_idl_txn,
220+
const struct ovsrec_open_vswitch *cfg,
221+
const struct smap *other_config,
222+
const bool val, bool ovn_managed)
223+
{
224+
struct smap new_config;
225+
smap_clone(&new_config, other_config);
226+
smap_replace(&new_config, "flow-restore-wait", val ? "true": "false");
227+
ovsrec_open_vswitch_set_other_config(cfg, &new_config);
228+
if (val) {
229+
ovsrec_open_vswitch_update_external_ids_setkey(
230+
cfg, "ovn-managed-flow-restore-wait", "true");
231+
} else if (ovn_managed) {
232+
ovsrec_open_vswitch_update_external_ids_delkey(
233+
cfg, "ovn-managed-flow-restore-wait");
234+
}
235+
ovsdb_idl_txn_increment(ovs_idl_txn, &cfg->header_,
236+
&ovsrec_open_vswitch_col_next_cfg, true);
237+
smap_destroy(&new_config);
238+
}
239+
240+
static void
241+
manage_flow_restore_wait(struct ovsdb_idl_txn *ovs_idl_txn,
242+
const struct ovsrec_open_vswitch *cfg,
243+
uint64_t ofctrl_cur_cfg, uint64_t ovs_next_cfg,
244+
int ovs_txn_status, bool is_ha_gw)
245+
{
246+
enum flow_restore_wait_state {
247+
FRW_INIT, /* Initial state */
248+
FRW_WAIT_TXN_COMPLETE, /* Sent false, waiting txn to complete */
249+
FRW_TXN_SUCCESS, /* Txn completed. Waiting for OVS Ack. */
250+
FRW_DONE /* Everything completed */
251+
};
252+
253+
static int64_t frw_next_cfg;
254+
static enum flow_restore_wait_state frw_state;
255+
static bool ofctrl_was_connected = false;
256+
257+
bool ofctrl_connected = ofctrl_is_connected();
258+
259+
if (!ovs_idl_txn || !cfg) {
260+
return;
261+
}
262+
263+
/* If OVS is stopped/started, make sure flow-restore-wait is toggled. */
264+
if (ofctrl_connected && !ofctrl_was_connected) {
265+
frw_state = FRW_INIT;
266+
}
267+
ofctrl_was_connected = ofctrl_connected;
268+
269+
if (!ofctrl_connected) {
270+
return;
271+
}
272+
273+
bool frw = smap_get_bool(&cfg->other_config, "flow-restore-wait", false);
274+
bool ovn_managed_once = smap_get_bool(&cfg->external_ids,
275+
"ovn-managed-flow-restore-wait",
276+
false);
277+
278+
if (frw && !ovn_managed_once) {
279+
/* frw has been set by ovs-ctl. Do not touch. */
280+
return;
281+
}
282+
283+
if (!is_ha_gw) {
284+
if (frw) {
285+
/* frw has once been set by OVN. We are now not an HA chassis
286+
* anymore, unset it. */
287+
set_flow_restore_wait(ovs_idl_txn, cfg, &cfg->other_config,
288+
false, ovn_managed_once);
289+
}
290+
/* else we are not an HA chassis and frw is false. Ignore it. */
291+
return;
292+
}
293+
294+
switch (frw_state) {
295+
case FRW_INIT:
296+
if (ofctrl_cur_cfg > 0) {
297+
set_flow_restore_wait(ovs_idl_txn, cfg, &cfg->other_config,
298+
false, ovn_managed_once);
299+
frw_state = FRW_WAIT_TXN_COMPLETE;
300+
VLOG_INFO("Setting flow-restore-wait=false "
301+
"(cur_cfg=%"PRIu64")", ofctrl_cur_cfg);
302+
}
303+
break;
304+
305+
case FRW_WAIT_TXN_COMPLETE:
306+
/* if (ovs_idl_txn != NULL), the transaction completed.
307+
* When the transaction completed, it either failed
308+
* (ovs_txn_status == 0) or succeeded (ovs_txn_status != 0). */
309+
if (ovs_txn_status == 0) {
310+
/* Previous transaction failed. */
311+
set_flow_restore_wait(ovs_idl_txn, cfg, &cfg->other_config,
312+
false, ovn_managed_once);
313+
break;
314+
}
315+
/* txn succeeded, get next_cfg */
316+
frw_next_cfg = ovs_next_cfg;
317+
frw_state = FRW_TXN_SUCCESS;
318+
/* fall through */
319+
320+
case FRW_TXN_SUCCESS:
321+
if (ovs_next_cfg < frw_next_cfg) {
322+
/* DB was reset, next_cfg went backwards. */
323+
VLOG_INFO("OVS DB reset (next_cfg %"PRId64" -> %"PRIu64"), "
324+
"resetting state",
325+
frw_next_cfg, ovs_next_cfg);
326+
set_flow_restore_wait(ovs_idl_txn, cfg, &cfg->other_config,
327+
false, ovn_managed_once);
328+
frw_state = FRW_WAIT_TXN_COMPLETE;
329+
break;
330+
}
331+
332+
if (!frw) {
333+
if (cfg->cur_cfg >= frw_next_cfg) {
334+
set_flow_restore_wait(ovs_idl_txn, cfg, &cfg->other_config,
335+
true, ovn_managed_once);
336+
frw_state = FRW_DONE;
337+
VLOG_INFO("Setting flow-restore-wait=true");
338+
}
339+
} else {
340+
/* The transaction to false succeeded but frw is true.
341+
* So, another task already set it to true. */
342+
frw_state = FRW_DONE;
343+
VLOG_INFO("flow-restore-wait was already true");
344+
}
345+
break;
346+
case FRW_DONE:
347+
if (!frw) {
348+
/* frw has been removed (e.g. by ovs-ctl restart) or is false
349+
* (e.g. txn failed.) */
350+
set_flow_restore_wait(ovs_idl_txn, cfg, &cfg->other_config,
351+
false, ovn_managed_once);
352+
frw_state = FRW_WAIT_TXN_COMPLETE;
353+
VLOG_INFO("OVS frw cleared, restarting flow-restore-wait sequence "
354+
"(cur_cfg=%"PRIu64")", ofctrl_cur_cfg);
355+
}
356+
break;
357+
}
358+
}
359+
214360
/* Only set monitor conditions on tables that are available in the
215361
* server schema.
216362
*/
@@ -3381,6 +3527,7 @@ en_mac_cache_cleanup(void *data)
33813527

33823528
struct ed_type_bfd_chassis {
33833529
struct sset bfd_chassis;
3530+
bool is_ha_gw;
33843531
};
33853532

33863533
static void *
@@ -3409,8 +3556,9 @@ en_bfd_chassis_run(struct engine_node *node, void *data OVS_UNUSED)
34093556
= chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
34103557

34113558
sset_clear(&bfd_chassis->bfd_chassis);
3412-
bfd_calculate_chassis(chassis, ha_chassis_grp_table,
3413-
&bfd_chassis->bfd_chassis);
3559+
bfd_chassis->is_ha_gw = bfd_calculate_chassis(chassis,
3560+
ha_chassis_grp_table,
3561+
&bfd_chassis->bfd_chassis);
34143562
return EN_UPDATED;
34153563
}
34163564

@@ -7117,6 +7265,7 @@ main(int argc, char *argv[])
71177265
struct unixctl_server *unixctl;
71187266
struct ovn_exit_args exit_args = {0};
71197267
struct br_int_remote br_int_remote = {0};
7268+
static uint64_t next_cfg = 0;
71207269
int retval;
71217270

71227271
/* Read from system-id-override file once on startup. */
@@ -7444,6 +7593,7 @@ main(int argc, char *argv[])
74447593

74457594
/* Main loop. */
74467595
int ovnsb_txn_status = 1;
7596+
int ovs_txn_status = 1;
74477597
bool sb_monitor_all = false;
74487598
struct tracked_acl_ids *tracked_acl_ids = NULL;
74497599
while (!exit_args.exiting) {
@@ -7545,6 +7695,11 @@ main(int argc, char *argv[])
75457695
pinctrl_update_swconn(br_int_remote.target,
75467696
br_int_remote.probe_interval);
75477697

7698+
if (cfg && ovs_idl_txn && ovs_txn_status == -1) {
7699+
/* txn was in progress and is now completed */
7700+
next_cfg = cfg->next_cfg;
7701+
}
7702+
75487703
/* Enable ACL matching for double tagged traffic. */
75497704
if (ovs_idl_txn && cfg) {
75507705
int vlan_limit = smap_get_int(
@@ -7894,6 +8049,13 @@ main(int argc, char *argv[])
78948049
stopwatch_start(OFCTRL_SEQNO_RUN_STOPWATCH_NAME,
78958050
time_msec());
78968051
ofctrl_seqno_run(ofctrl_get_cur_cfg());
8052+
if (ovs_idl_txn && bfd_chassis_data) {
8053+
manage_flow_restore_wait(ovs_idl_txn, cfg,
8054+
ofctrl_get_cur_cfg(),
8055+
next_cfg, ovs_txn_status,
8056+
bfd_chassis_data->is_ha_gw);
8057+
}
8058+
78978059
stopwatch_stop(OFCTRL_SEQNO_RUN_STOPWATCH_NAME,
78988060
time_msec());
78998061
stopwatch_start(IF_STATUS_MGR_RUN_STOPWATCH_NAME,
@@ -7993,7 +8155,7 @@ main(int argc, char *argv[])
79938155
OVS_NOT_REACHED();
79948156
}
79958157

7996-
int ovs_txn_status = ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
8158+
ovs_txn_status = ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
79978159
if (!ovs_txn_status) {
79988160
/* The transaction failed. */
79998161
vif_plug_clear_deleted(
@@ -8012,6 +8174,9 @@ main(int argc, char *argv[])
80128174
&vif_plug_deleted_iface_ids);
80138175
vif_plug_finish_changed(
80148176
&vif_plug_changed_iface_ids);
8177+
if (cfg) {
8178+
next_cfg = cfg->next_cfg;
8179+
}
80158180
} else if (ovs_txn_status == -1) {
80168181
/* The commit is still in progress */
80178182
} else {
@@ -8085,7 +8250,7 @@ main(int argc, char *argv[])
80858250
}
80868251

80878252
ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
8088-
int ovs_txn_status = ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
8253+
ovs_txn_status = ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
80898254
if (!ovs_txn_status) {
80908255
/* The transaction failed. */
80918256
vif_plug_clear_deleted(

tests/multinode-macros.at

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ m4_define([M_START_TCPDUMP],
4141
]
4242
)
4343

44+
m4_define([M_START_TCPDUMPS_RECURSIVE_], [
45+
m4_if(m4_eval($# > 3), [1], [dnl
46+
names="$names $3"
47+
echo "Running podman exec $1 tcpdump -l $2 >$3.tcpdump 2>$3.stderr"
48+
podman exec $1 tcpdump -l $2 >$3.tcpdump 2>$3.stderr &
49+
echo "podman exec $1 ps -ef | grep -v grep | grep tcpdump && podman exec $1 killall tcpdump" >> cleanup
50+
M_START_TCPDUMPS_RECURSIVE_(m4_shift(m4_shift(m4_shift($@))))
51+
])
52+
]
53+
)
54+
55+
# Start Multiple tcpdump. Useful to speed up when many tcpdump
56+
# must be started as waiting for "listening" takes usually 1 second.
57+
m4_define([M_START_TCPDUMPS],
58+
[
59+
names=""
60+
M_START_TCPDUMPS_RECURSIVE_($@)
61+
for name in $names; do
62+
OVS_WAIT_UNTIL([grep -q "listening" ${name}.stderr])
63+
done
64+
]
65+
)
4466

4567
# M_FORMAT_CT([ip-addr])
4668
#
@@ -480,6 +502,30 @@ m_is_fedora() {
480502
m_central_as grep -qi fedora /etc/os-release
481503
}
482504

505+
# Run ovs-vsctl using Host socket
506+
host_ovs_vsctl() {
507+
# Discover host OVS socket on first call
508+
if [[ -z "$HOST_OVS_SOCK" ]]; then
509+
for sock in /run/openvswitch/db.sock /var/run/openvswitch/db.sock /usr/local/var/run/openvswitch/db.sock; do
510+
if [[ -S "$sock" ]]; then
511+
HOST_OVS_SOCK=$sock
512+
break
513+
fi
514+
done
515+
# Fallback on unusual prefix: discover from running process
516+
if [[ -z "$HOST_OVS_SOCK" ]]; then
517+
HOST_OVS_SOCK=$(ps aux | grep '[o]vsdb-server' | grep -oP 'punix:\K[^, ]+' | while read s; do
518+
[[ -S "$s" ]] && [[ "$s" != *"$OVS_RUNDIR"* ]] && echo "$s" && break
519+
done)
520+
fi
521+
if [[ -z "$HOST_OVS_SOCK" ]]; then
522+
echo "ERROR: Could not find host OVS socket" >&2
523+
AT_FAIL_IF([:])
524+
fi
525+
fi
526+
ovs-vsctl --db=unix:$HOST_OVS_SOCK "$@"
527+
}
528+
483529
# M_START_L4_SERVER([fake_node], [namespace], [ip_addr], [port], [reply_string], [pidfile])
484530
#
485531
# Helper to properly start l4 server in inside 'fake_node''s namespace'.

0 commit comments

Comments
 (0)