Hi,
Thanks for your review.

Em qua., 28 de out. de 2026 às 16:16, Rosemarie O'Riorden via dev <
[email protected]> escreveu:

> Hi Lucas! This isn't really a full review, but I pointed out two things
> I noticed below.
>
> On 10/27/26 1:21 PM, Lucas Vargas Dias via dev wrote:
> > If two AZs connect ports with the same name to a transit switch,
> > ovn-ic retries a failing transaction forever. Port names are unique
> > in both the NB (Logical_Switch_Port) and the IC-SB (Port_Binding):
> > one AZ tries to create a remote LSP that clashes with its local one,
> > and the other AZ tries to bind a port name that is already bound.
> > The failed commit also blocks every other change in the same
> > transaction.
> >
> > Check the name before inserting.  On a conflict, skip the port and
> > log a rate-limited warning that names the port and AZs, so the
> > operator knows which port to rename.
> >
> > Fixes: aa3e4436c232 ("ovn-ic: Interconnection port controller.")
> > Assisted-by: Claude Opus 5.5, Claude Code
> > Signed-off-by: Lucas Vargas Dias <[email protected]>
> > ---
> >  ic/en-port-binding.c | 75 +++++++++++++++++++++++++++++++++++++++-----
> >  ic/ovn-ic.c          |  5 +++
> >  ic/ovn-ic.h          |  1 +
> >  tests/ovn-ic.at      | 54 +++++++++++++++++++++++++++++++
> >  4 files changed, 127 insertions(+), 8 deletions(-)
> >
> > diff --git a/ic/en-port-binding.c b/ic/en-port-binding.c
> > index 5a2490b6e..638f819e0 100644
> > --- a/ic/en-port-binding.c
> > +++ b/ic/en-port-binding.c
> > @@ -465,6 +465,28 @@ create_nb_lsp(struct ic_context *ctx,
> >                const struct icsbrec_port_binding *isb_pb,
> >                const struct nbrec_logical_switch *ls)
> >  {
> > +    /* The NB Logical_Switch_Port name is unique across the whole NB,
> but a
> > +     * remote AZ may bind a port whose name is already used here, e.g.
> by a
> > +     * local port with the same name on this transit switch or by a
> port of
> > +     * an unrelated logical switch.  Inserting it would violate the name
> > +     * index, abort the whole NB transaction and retry it forever, so
> skip the
> > +     * remote port instead.  A row inserted earlier in this same
> transaction
> > +     * (see lsp_create()) is also found here and is silently reused. */
> > +    const struct nbrec_logical_switch_port *existing =
> > +        get_lsp_by_ts_port_name(ctx, isb_pb->logical_port);
> > +    if (existing) {
> > +        if (!nbrec_logical_switch_port_is_new(existing)) {
> > +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1,
> 5);
> > +            VLOG_WARN_RL(&rl, "Port %s of transit switch %s in
> availability "
> > +                         "zone %s conflicts with an existing logical
> switch "
> > +                         "port with the same name, skipping it.",
> > +                         isb_pb->logical_port, isb_pb->transit_switch,
> > +                         isb_pb->availability_zone
> > +                         ? isb_pb->availability_zone->name :
> "<unknown>");
> > +        }
> > +        return;
> > +    }
> > +
> >      const struct nbrec_logical_switch_port *lsp =
> >          nbrec_logical_switch_port_insert(ctx->ovnnb_txn);
> >      nbrec_logical_switch_port_set_name(lsp, isb_pb->logical_port);
> > @@ -521,11 +543,45 @@ find_transit_router_by_name(struct ic_context
> *ctx, const char *name)
> >  }
> >
> >  static const struct icsbrec_port_binding *
> > -create_isb_pb(struct ovsdb_idl_txn *isb_txn, const char *logical_port,
> > +find_isb_pb_by_name(struct ic_context *ctx, const char *logical_port)
> > +{
> > +    const struct icsbrec_port_binding *key =
> > +        icsbrec_port_binding_index_init_row(
> > +            ctx->icsbrec_port_binding_by_name);
> > +    icsbrec_port_binding_index_set_logical_port(key, logical_port);
> > +    const struct icsbrec_port_binding *isb_pb =
> > +
> icsbrec_port_binding_index_find(ctx->icsbrec_port_binding_by_name,
> > +                                        key);
> > +    icsbrec_port_binding_index_destroy_row(key);
> > +    return isb_pb;
> > +}
> > +
> > +static const struct icsbrec_port_binding *
> > +create_isb_pb(struct ic_context *ctx, struct ovsdb_idl_txn *isb_txn,
>
> Nit: Since create_isb_pb() already accepted ctx->ovnisb_txn and now you
> added ctx as a parameter, you could just grab ctx->ovnisb_txn inside of
> the function instead of having an extra redundant argument.
>


Some calls for create_isb_pb use ctx->ovnisb_unlocked_txn
instead  ctx->ovnisb_txn.


>
> > +              const char *logical_port,
> >                const struct icsbrec_availability_zone *az, const char
> *ts_name,
> >                const struct uuid *nb_ic_uuid, const char *type,
> >                struct hmap *pb_tnlids)
> >  {
> > +    /* The IC-SB Port_Binding logical_port is unique across all AZs and
> > +     * transit switches/routers.  If another AZ (or another transit
> > +     * switch/router) already binds a port with this name, inserting
> would
> > +     * violate the index, abort the whole IC-SB transaction and retry it
> > +     * forever, so leave this port unbound instead. */
> > +    const struct icsbrec_port_binding *existing =
> > +        find_isb_pb_by_name(ctx, logical_port);
> > +    if (existing) {
> > +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > +        VLOG_WARN_RL(&rl, "Port %s of %s in availability zone %s
> conflicts "
> > +                     "with a port with the same name of %s in
> availability "
> > +                     "zone %s, not binding it.",
> > +                     logical_port, ts_name, az->name,
> > +                     existing->transit_switch,
> > +                     existing->availability_zone
> > +                     ? existing->availability_zone->name : "<unknown>");
> > +        return NULL;
> > +    }
> > +
> >      uint32_t pb_tnl_key = allocate_port_key(pb_tnlids);
> >      if (!pb_tnl_key) {
> >          return NULL;
> > @@ -720,7 +776,7 @@ port_binding_sync_ts(struct ic_context *ctx,
> >              if (ctx->ovnisb_txn && is_az_leader(ctx->ovnisb_txn)) {
> >                  if (!isb_pb) {
> >                      isb_pb = create_isb_pb(
> > -                        ctx->ovnisb_txn, tsp->name, runned_az,
> > +                        ctx, ctx->ovnisb_txn, tsp->name, runned_az,
> >                          ts->name, &ts->header_.uuid,
> "transit-switch-port",
> >                          &pb_tnlids);
> >                  }
> > @@ -730,8 +786,8 @@ port_binding_sync_ts(struct ic_context *ctx,
> >              /* Create ISB port_binding as its chassis is local. */
> >              isb_pb = shash_find_and_delete(&local_pbs, tsp->name);
> >              if (!isb_pb) {
> > -                isb_pb = create_isb_pb(ctx->ovnisb_unlocked_txn,
> tsp->name,
> > -                                       runned_az,
> > +                isb_pb = create_isb_pb(ctx, ctx->ovnisb_unlocked_txn,
> > +                                       tsp->name, runned_az,
> >                                         ts->name, &ts->header_.uuid,
> >                                         "transit-switch-port",
> &pb_tnlids);
> >              }
> > @@ -773,9 +829,12 @@ port_binding_sync_ts(struct ic_context *ctx,
> >              isb_pb = shash_find_and_delete(&local_pbs, lsp->name);
> >              if (!isb_pb) {
> >                  isb_pb = create_isb_pb(
> > -                    ctx->ovnisb_unlocked_txn, sb_pb->logical_port,
> > +                    ctx, ctx->ovnisb_unlocked_txn, sb_pb->logical_port,
> >                      runned_az, ts->name, &ts->header_.uuid,
> >                      "transit-switch-port", &pb_tnlids);
> > +                if (!isb_pb) {
> > +                    continue;
>
> I'm curious why you added a check for isb_pb being NULL here but not at
> any of the other call sites edited in this patch.
>

create_isb_pb() could already return NULL before this patch, when no
tunnel key is left.  The other call sites already handle that:
sync_tsp_pb() and sync_trp_pb() return early on NULL, and the callers
then skip the port ("if (!isb_pb) continue;" / "if (isb_pb)").  The
legacy path was the only one that dereferences the result right away
(sync_ts_isb_pb() and isb_pb->type), so it was the only one that needed
a check.

Regards,
Lucas


> > +                }
> >                  sync_ts_isb_pb(ctx, sb_pb, isb_pb);
> >              } else {
> >                  sync_local_port(ctx, isb_pb, sb_pb, lsp);
> > @@ -899,7 +958,7 @@ port_binding_sync_tr(struct ic_context *ctx,
> >
> >              if (ctx->ovnisb_txn && is_az_leader(ctx->ovnisb_txn)) {
> >                  if (!isb_pb) {
> > -                    isb_pb = create_isb_pb(ctx->ovnisb_txn, trp->name,
> > +                    isb_pb = create_isb_pb(ctx, ctx->ovnisb_txn,
> trp->name,
> >                                             runned_az,
> >                                             tr->name, &tr->header_.uuid,
> >                                             "transit-router-port",
> > @@ -912,8 +971,8 @@ port_binding_sync_tr(struct ic_context *ctx,
> >          } else {
> >              isb_pb = shash_find_and_delete(&local_pbs, trp->name);
> >              if (!isb_pb) {
> > -                isb_pb = create_isb_pb(ctx->ovnisb_unlocked_txn,
> trp->name,
> > -                                       runned_az,
> > +                isb_pb = create_isb_pb(ctx, ctx->ovnisb_unlocked_txn,
> > +                                       trp->name, runned_az,
> >                                         tr->name, &tr->header_.uuid,
> >                                         "transit-router-port",
> &pb_tnlids);
> >              }
> > diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
> > index e65256577..87a266b44 100644
> > --- a/ic/ovn-ic.c
> > +++ b/ic/ovn-ic.c
> > @@ -1191,6 +1191,10 @@ main(int argc, char *argv[])
> >          = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,
> >
> &icsbrec_port_binding_col_availability_zone);
> >
> > +    struct ovsdb_idl_index *icsbrec_port_binding_by_name
> > +        = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,
> > +
> &icsbrec_port_binding_col_logical_port);
> > +
> >      struct ovsdb_idl_index *icsbrec_port_binding_by_ts
> >          = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,
> >
> &icsbrec_port_binding_col_transit_switch);
> > @@ -1408,6 +1412,7 @@ main(int argc, char *argv[])
> >                  .icnbrec_transit_router_by_name =
> >                      icnbrec_transit_router_by_name,
> >                  .icsbrec_port_binding_by_az =
> icsbrec_port_binding_by_az,
> > +                .icsbrec_port_binding_by_name =
> icsbrec_port_binding_by_name,
> >                  .icsbrec_port_binding_by_ts =
> icsbrec_port_binding_by_ts,
> >                  .icsbrec_port_binding_by_ts_az =
> icsbrec_port_binding_by_ts_az,
> >                  .icsbrec_route_by_az = icsbrec_route_by_az,
> > diff --git a/ic/ovn-ic.h b/ic/ovn-ic.h
> > index 8adb17ad7..b1cfad08f 100644
> > --- a/ic/ovn-ic.h
> > +++ b/ic/ovn-ic.h
> > @@ -43,6 +43,7 @@ struct ic_context {
> >      struct ovsdb_idl_index *icnbrec_transit_switch_by_name;
> >      struct ovsdb_idl_index *icnbrec_transit_router_by_name;
> >      struct ovsdb_idl_index *icsbrec_port_binding_by_az;
> > +    struct ovsdb_idl_index *icsbrec_port_binding_by_name;
> >      struct ovsdb_idl_index *icsbrec_port_binding_by_ts;
> >      struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az;
> >      struct ovsdb_idl_index *icsbrec_route_by_az;
> > diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
> > index 646026817..4576c725e 100644
> > --- a/tests/ovn-ic.at
> > +++ b/tests/ovn-ic.at
> > @@ -1689,6 +1689,60 @@ OVN_CLEANUP_IC([az1], [az2])
> >  AT_CLEANUP
> >  ])
> >
> > +OVN_FOR_EACH_NORTHD([
> > +AT_SETUP([ovn-ic -- same port name in two AZs])
> > +
> > +ovn_init_ic_db
> > +ovn-ic-nbctl ts-add ts1
> > +ovn_start az1
> > +ovn_start az2
> > +check ovn-ic-nbctl --wait=sb sync
> > +
> > +dnl Each AZ connects a router to ts1 through a legacy port that has the
> > +dnl same name in both AZs.  Pause ovn-ic so that neither AZ learns
> about the
> > +dnl other's port before creating its own.
> > +for az in az1 az2; do
> > +    as $az check ovn-appctl -t ic/ovn-ic pause
> > +done
> > +for az in az1 az2; do
> > +    ovn_as $az
> > +    check ovn-nbctl lr-add lr1
> > +    check ovn-nbctl lrp-add lr1 lrp-lr1-ts1 aa:aa:aa:aa:aa:01
> 169.254.100.1/24
> > +    check ovn-nbctl --wait=sb lsp-add-router-port ts1 lsp-dup
> lrp-lr1-ts1
> > +done
> > +for az in az1 az2; do
> > +    as $az check ovn-appctl -t ic/ovn-ic resume
> > +done
> > +
> > +dnl IC-SB port names are unique, so only one AZ gets to bind the port;
> the
> > +dnl other one must give up instead of retrying the insert forever.
> > +wait_row_count ic-sb:Port_Binding 1 logical_port=lsp-dup
> > +OVS_WAIT_UNTIL([grep -q "Port lsp-dup of ts1 .* conflicts with a port" \
> > +                az1/ic/ovn-ic.log az2/ic/ovn-ic.log])
> > +
> > +dnl And the AZ that got the binding must not try to create a remote NB
> port
> > +dnl over the other AZ's local port with the same name.
> > +OVS_WAIT_UNTIL([grep -q "Port lsp-dup of transit switch ts1 .*
> conflicts" \
> > +                az1/ic/ovn-ic.log az2/ic/ovn-ic.log])
> > +for az in az1 az2; do
> > +    ovn_as $az
> > +    check_row_count nb:Logical_Switch_Port 1 name=lsp-dup type=router
> > +done
> > +
> > +dnl ovn-ic must keep committing NB transactions: a port added in az1
> shows
> > +dnl up as remote in az2.
> > +ovn_as az1
> > +check ovn-nbctl lrp-add lr1 lrp-lr1-ts1-2 aa:aa:aa:aa:aa:02
> 169.254.101.1/24
> > +check ovn-nbctl --wait=sb lsp-add-router-port ts1 lsp-az1-only
> lrp-lr1-ts1-2
> > +ovn_as az2
> > +wait_row_count nb:Logical_Switch_Port 1 name=lsp-az1-only type=remote
> > +
> > +AT_CHECK([grep -q "constraint violation" az1/ic/ovn-ic.log
> az2/ic/ovn-ic.log], [1])
> > +
> > +OVN_CLEANUP_IC([az1], [az2])
> > +AT_CLEANUP
> > +])
> > +
> >  OVN_FOR_EACH_NORTHD([
> >  AT_SETUP([ovn-ic -- address-set sync])
> >
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

-- 




_‘Esta mensagem é direcionada apenas para os endereços constantes no 
cabeçalho inicial. Se você não está listado nos endereços constantes no 
cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa 
mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão 
imediatamente anuladas e proibidas’._


* **‘Apesar do Magazine Luiza tomar 
todas as precauções razoáveis para assegurar que nenhum vírus esteja 
presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por 
quaisquer perdas ou danos causados por esse e-mail ou por seus anexos’.*



_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to