On Tue, Jul 28, 2015 at 8:44 AM, Ben Pfaff <[email protected]> wrote:
> 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 <[email protected]>
> Acked-by: Russell Bryant <[email protected]>
> ---
> v1->v2: Rename idl_loop_wait() to idl_loop_commit_and_wait().
> ---
> 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 6f94187..962d6de 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_commit_and_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);
> +}
>
Curious here, if we could run into the following race (in main function):
1. first run of while loop: we have some change from ovnsb~
2. ovn-controller execute all *_run() functions to update~
3. the txn commit to ovsdb is INCOMPLETE~ and at the same time, there is
another
change from ovnsb~
4. second run of while loop: the next call to
idl_loop_run()->ovsdb_idl_run() will update the
local idl. however, since the 'idl_loop' has 'committing_txn', the
*_run() will be no-op.
5. assume this time, the commit to ovsdb finishes. since the local idl has
already been
updated, ovn-controller will call poll_block() with no immediate
wakeup...
6. so, the second change to ovnsb will only be checked the next time there
is a new update
from ovnsb.
Hope this make sense,
Thanks,
Alex Wang,
> +
> 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_commit_and_wait(&ovnsb_idl_loop);
> + idl_loop_commit_and_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_commit_and_wait(&ovnsb_idl_loop);
> + idl_loop_commit_and_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
> [email protected]
> http://openvswitch.org/mailman/listinfo/dev
>
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev