Until now, ovn-controller has been full of loops that commit a transaction to the OVS or OVN Southbound database. These blocking loops delay other work within ovn-controller. They also make it unsafe to keep pointers to database records within a single ovn-controller main loop, since calls to ovsdb_idl_run() can cause IDL records to be destroyed. This commit drops all of the blocking calls, instead doing a single commit to the databases at the end of each main loop.
Signed-off-by: Ben Pfaff <b...@nicira.com> --- ovn/controller/binding.c | 61 ++++++++------------ ovn/controller/binding.h | 4 +- ovn/controller/chassis.c | 117 ++++++++++++++------------------------ ovn/controller/chassis.h | 4 +- ovn/controller/ovn-controller.c | 123 +++++++++++++++++++++++++++++++++++++--- ovn/controller/ovn-controller.h | 4 ++ 6 files changed, 188 insertions(+), 125 deletions(-) diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c index 0a4a39e..6af216c 100644 --- a/ovn/controller/binding.c +++ b/ovn/controller/binding.c @@ -76,10 +76,12 @@ binding_run(struct controller_ctx *ctx) { const struct sbrec_chassis *chassis_rec; const struct sbrec_binding *binding_rec; - struct ovsdb_idl_txn *txn; struct sset lports, all_lports; const char *name; - int retval; + + if (!ctx->ovnsb_idl_txn) { + return; + } chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, ctx->chassis_id); if (!chassis_rec) { @@ -91,8 +93,7 @@ binding_run(struct controller_ctx *ctx) get_local_iface_ids(ctx, &lports); sset_clone(&all_lports, &lports); - txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); - ovsdb_idl_txn_add_comment(txn, + ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller: updating bindings for '%s'", ctx->chassis_id); @@ -115,14 +116,6 @@ binding_run(struct controller_ctx *ctx) } } - retval = ovsdb_idl_txn_commit_block(txn); - if (retval == TXN_ERROR) { - VLOG_INFO("Problem committing binding information: %s", - ovsdb_idl_txn_status_to_string(retval)); - } - - ovsdb_idl_txn_destroy(txn); - SSET_FOR_EACH (name, &lports) { VLOG_DBG("No binding record for lport %s", name); } @@ -130,40 +123,32 @@ binding_run(struct controller_ctx *ctx) sset_destroy(&all_lports); } -void -binding_destroy(struct controller_ctx *ctx) +/* Returns true if the database is all cleaned up, false if more work is + * required. */ +bool +binding_cleanup(struct controller_ctx *ctx) { - const struct sbrec_chassis *chassis_rec; - int retval = TXN_TRY_AGAIN; - - ovs_assert(ctx->ovnsb_idl); + if (!ctx->ovnsb_idl_txn) { + return false; + } - chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, ctx->chassis_id); + const struct sbrec_chassis *chassis_rec + = get_chassis_by_name(ctx->ovnsb_idl, ctx->chassis_id); if (!chassis_rec) { - return; + return true; } - while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { - const struct sbrec_binding *binding_rec; - struct ovsdb_idl_txn *txn; - - txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); - ovsdb_idl_txn_add_comment(txn, + ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller: removing all bindings for '%s'", ctx->chassis_id); - SBREC_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { - if (binding_rec->chassis == chassis_rec) { - sbrec_binding_set_chassis(binding_rec, NULL); - } - } - - retval = ovsdb_idl_txn_commit_block(txn); - if (retval == TXN_ERROR) { - VLOG_INFO("Problem removing bindings: %s", - ovsdb_idl_txn_status_to_string(retval)); + const struct sbrec_binding *binding_rec; + bool any_changes = false; + SBREC_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { + if (binding_rec->chassis == chassis_rec) { + sbrec_binding_set_chassis(binding_rec, NULL); + any_changes = true; } - - ovsdb_idl_txn_destroy(txn); } + return !any_changes; } diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h index 2611173..e73c1d1 100644 --- a/ovn/controller/binding.h +++ b/ovn/controller/binding.h @@ -17,10 +17,12 @@ #ifndef OVN_BINDING_H #define OVN_BINDING_H 1 +#include <stdbool.h> + struct controller_ctx; void binding_init(struct controller_ctx *); void binding_run(struct controller_ctx *); -void binding_destroy(struct controller_ctx *); +bool binding_cleanup(struct controller_ctx *); #endif /* ovn/binding.h */ diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c index cf18dd0..9d2959a 100644 --- a/ovn/controller/chassis.c +++ b/ovn/controller/chassis.c @@ -52,8 +52,6 @@ register_chassis(struct controller_ctx *ctx) const char *encap_type, *encap_ip; struct sbrec_encap *encap_rec; static bool inited = false; - int retval = TXN_TRY_AGAIN; - struct ovsdb_idl_txn *txn; chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, ctx->chassis_id); @@ -92,31 +90,22 @@ register_chassis(struct controller_ctx *ctx) } } - txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); - ovsdb_idl_txn_add_comment(txn, + ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller: registering chassis '%s'", ctx->chassis_id); if (!chassis_rec) { - chassis_rec = sbrec_chassis_insert(txn); + chassis_rec = sbrec_chassis_insert(ctx->ovnsb_idl_txn); sbrec_chassis_set_name(chassis_rec, ctx->chassis_id); } - encap_rec = sbrec_encap_insert(txn); + encap_rec = sbrec_encap_insert(ctx->ovnsb_idl_txn); sbrec_encap_set_type(encap_rec, encap_type); sbrec_encap_set_ip(encap_rec, encap_ip); sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1); - retval = ovsdb_idl_txn_commit_block(txn); - if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { - VLOG_INFO("Problem registering chassis: %s", - ovsdb_idl_txn_status_to_string(retval)); - poll_immediate_wake(); - } - ovsdb_idl_txn_destroy(txn); - inited = true; } @@ -311,7 +300,6 @@ update_encaps(struct controller_ctx *ctx) { const struct sbrec_chassis *chassis_rec; const struct ovsrec_bridge *br; - int retval; struct tunnel_ctx tc = { .tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap), @@ -319,7 +307,7 @@ update_encaps(struct controller_ctx *ctx) .br_int = ctx->br_int }; - tc.ovs_txn = ovsdb_idl_txn_create(ctx->ovs_idl); + tc.ovs_txn = ctx->ovs_idl_txn; ovsdb_idl_txn_add_comment(tc.ovs_txn, "ovn-controller: modifying OVS tunnels '%s'", ctx->chassis_id); @@ -366,81 +354,58 @@ update_encaps(struct controller_ctx *ctx) } hmap_destroy(&tc.tunnel_hmap); sset_destroy(&tc.port_names); - - retval = ovsdb_idl_txn_commit_block(tc.ovs_txn); - if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { - VLOG_INFO("Problem modifying OVS tunnels: %s", - ovsdb_idl_txn_status_to_string(retval)); - poll_immediate_wake(); - } - ovsdb_idl_txn_destroy(tc.ovs_txn); } void chassis_run(struct controller_ctx *ctx) { - register_chassis(ctx); - update_encaps(ctx); + if (ctx->ovnsb_idl_txn) { + register_chassis(ctx); + } + + if (ctx->ovs_idl_txn) { + update_encaps(ctx); + } } -void -chassis_destroy(struct controller_ctx *ctx) +/* Returns true if the database is all cleaned up, false if more work is + * required. */ +bool +chassis_cleanup(struct controller_ctx *ctx) { - int retval = TXN_TRY_AGAIN; - - ovs_assert(ctx->ovnsb_idl); - - while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { - const struct sbrec_chassis *chassis_rec; - struct ovsdb_idl_txn *txn; + if (!ctx->ovnsb_idl_txn || !ctx->ovs_idl_txn) { + return false; + } - chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, ctx->chassis_id); - if (!chassis_rec) { - break; - } + bool any_changes = false; - txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); - ovsdb_idl_txn_add_comment(txn, + /* Delete Chassis row. */ + const struct sbrec_chassis *chassis_rec + = get_chassis_by_name(ctx->ovnsb_idl, ctx->chassis_id); + if (chassis_rec) { + ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller: unregistering chassis '%s'", ctx->chassis_id); sbrec_chassis_delete(chassis_rec); - - retval = ovsdb_idl_txn_commit_block(txn); - if (retval == TXN_ERROR) { - VLOG_INFO("Problem unregistering chassis: %s", - ovsdb_idl_txn_status_to_string(retval)); - } - ovsdb_idl_txn_destroy(txn); + any_changes = true; } - retval = TXN_TRY_AGAIN; - while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { - struct ovsrec_port **ports; - struct ovsdb_idl_txn *txn; - size_t i, n; - - txn = ovsdb_idl_txn_create(ctx->ovs_idl); - ovsdb_idl_txn_add_comment(txn, - "ovn-controller: destroying tunnels"); - - /* Delete all the OVS-created tunnels from the integration - * bridge. */ - ports = xmalloc(sizeof *ctx->br_int->ports * ctx->br_int->n_ports); - for (i = n = 0; i < ctx->br_int->n_ports; i++) { - if (!smap_get(&ctx->br_int->ports[i]->external_ids, - "ovn-chassis-id")) { - ports[n++] = ctx->br_int->ports[i]; - } - } - ovsrec_bridge_verify_ports(ctx->br_int); - ovsrec_bridge_set_ports(ctx->br_int, ports, n); - free(ports); - - retval = ovsdb_idl_txn_commit_block(txn); - if (retval == TXN_ERROR) { - VLOG_INFO("Problem destroying tunnels: %s", - ovsdb_idl_txn_status_to_string(retval)); + /* Delete all the OVS-created tunnels from the integration bridge. */ + ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn, + "ovn-controller: destroying tunnels"); + struct ovsrec_port **ports + = xmalloc(sizeof *ctx->br_int->ports * ctx->br_int->n_ports); + size_t n = 0; + for (size_t i = 0; i < ctx->br_int->n_ports; i++) { + if (!smap_get(&ctx->br_int->ports[i]->external_ids, + "ovn-chassis-id")) { + ports[n++] = ctx->br_int->ports[i]; + any_changes = true; } - ovsdb_idl_txn_destroy(txn); } + ovsrec_bridge_verify_ports(ctx->br_int); + ovsrec_bridge_set_ports(ctx->br_int, ports, n); + free(ports); + + return !any_changes; } diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h index aee701b..851a300 100644 --- a/ovn/controller/chassis.h +++ b/ovn/controller/chassis.h @@ -16,10 +16,12 @@ #ifndef OVN_CHASSIS_H #define OVN_CHASSIS_H 1 +#include <stdbool.h> + struct controller_ctx; void chassis_init(struct controller_ctx *); void chassis_run(struct controller_ctx *); -void chassis_destroy(struct controller_ctx *); +bool chassis_cleanup(struct controller_ctx *); #endif /* ovn/chassis.h */ diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index cccea24..7b165a1 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -145,6 +145,78 @@ try_again: } +struct idl_loop { + struct ovsdb_idl *idl; + unsigned int skip_seqno; + + struct ovsdb_idl_txn *committing_txn; + unsigned int precommit_seqno; + + struct ovsdb_idl_txn *open_txn; +}; + +#define IDL_LOOP_INITIALIZER(IDL) { .idl = (IDL) } + +static void +idl_loop_destroy(struct idl_loop *loop) +{ + if (loop) { + ovsdb_idl_destroy(loop->idl); + } +} + +static struct ovsdb_idl_txn * +idl_loop_run(struct idl_loop *loop) +{ + ovsdb_idl_run(loop->idl); + loop->open_txn = (loop->committing_txn + || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno + ? NULL + : ovsdb_idl_txn_create(loop->idl)); + return loop->open_txn; +} + +static void +idl_loop_wait(struct idl_loop *loop) +{ + if (loop->open_txn) { + loop->committing_txn = loop->open_txn; + loop->open_txn = NULL; + + loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl); + } + + struct ovsdb_idl_txn *txn = loop->committing_txn; + if (txn) { + enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); + switch (status) { + case TXN_INCOMPLETE: + break; + + case TXN_TRY_AGAIN: + loop->skip_seqno = loop->precommit_seqno; + if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) { + poll_immediate_wake(); + } + /* Fall through. */ + case TXN_UNCHANGED: + case TXN_ABORTED: + case TXN_SUCCESS: + case TXN_NOT_LOCKED: + case TXN_ERROR: + ovsdb_idl_txn_destroy(txn); + loop->committing_txn = NULL; + break; + + case TXN_UNCOMMITTED: + OVS_NOT_REACHED(); + + } + } + + ovsdb_idl_wait(loop->idl); +} + int main(int argc, char *argv[]) { @@ -195,10 +267,14 @@ main(int argc, char *argv[]) true, true); get_initial_snapshot(ctx.ovnsb_idl); + struct idl_loop ovnsb_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovnsb_idl); + struct idl_loop ovs_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovs_idl); + + /* Main loop. */ exiting = false; while (!exiting) { - ovsdb_idl_run(ctx.ovs_idl); - ovsdb_idl_run(ctx.ovnsb_idl); + ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop); + ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop); /* xxx If run into any surprising changes, we exit. We should * xxx handle this more gracefully. */ @@ -207,7 +283,7 @@ main(int argc, char *argv[]) VLOG_ERR("Integration bridge '%s' disappeared", ctx.br_int_name); retval = EXIT_FAILURE; - break; + goto exit; } ofctrl_clear_flows(); @@ -224,20 +300,49 @@ main(int argc, char *argv[]) poll_immediate_wake(); } - ovsdb_idl_wait(ctx.ovs_idl); - ovsdb_idl_wait(ctx.ovnsb_idl); + idl_loop_wait(&ovnsb_idl_loop); + idl_loop_wait(&ovs_idl_loop); + ofctrl_wait(); poll_block(); } + /* It's time to exit. Clean up the databases. */ + bool done = false; + while (!done) { + ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop); + ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop); + + /* xxx If run into any surprising changes, we exit. We should + * xxx handle this more gracefully. */ + ctx.br_int = get_bridge(&ctx, ctx.br_int_name); + if (!ctx.br_int) { + VLOG_ERR("Integration bridge '%s' disappeared", + ctx.br_int_name); + retval = EXIT_FAILURE; + goto exit; + } + + /* Run both the binding and chassis cleanup, even if one of them + * returns false. We're done if both return true. */ + done = binding_cleanup(&ctx); + done = chassis_cleanup(&ctx) && done; + if (done) { + poll_immediate_wake(); + } + + idl_loop_wait(&ovnsb_idl_loop); + idl_loop_wait(&ovs_idl_loop); + poll_block(); + } + +exit: unixctl_server_destroy(unixctl); pipeline_destroy(&ctx); ofctrl_destroy(); - binding_destroy(&ctx); - chassis_destroy(&ctx); - ovsdb_idl_destroy(ctx.ovs_idl); - ovsdb_idl_destroy(ctx.ovnsb_idl); + idl_loop_destroy(&ovs_idl_loop); + idl_loop_destroy(&ovnsb_idl_loop); free(ctx.br_int_name); free(ctx.chassis_id); diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h index 6f98658..b72d891 100644 --- a/ovn/controller/ovn-controller.h +++ b/ovn/controller/ovn-controller.h @@ -22,8 +22,12 @@ struct controller_ctx { char *chassis_id; /* ID for this chassis. */ char *br_int_name; /* Name of local integration bridge. */ + struct ovsdb_idl *ovnsb_idl; + struct ovsdb_idl_txn *ovnsb_idl_txn; + struct ovsdb_idl *ovs_idl; + struct ovsdb_idl_txn *ovs_idl_txn; const struct ovsrec_bridge *br_int; }; -- 2.1.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev