On Thu, Jul 14, 2022 at 6:42 AM Dumitru Ceara <[email protected]> wrote:
>
> On 7/14/22 12:37, Ales Musil wrote:
> > The ovn-controller had a race condition over MAC
> > binding table with other controllers. When multiple
> > controllers received GARP from single source usually
> > the one who was able to win the race put it into SB.
> > The others got transaction error which triggered
> > full recompute even if it's not needed.
> >
> > In order to reduce the chance of multiple controllers
> > trying to insert same row at the same time add slight
> > delay to the MAC binding processing. This delay
> > is random interval between 1-50 in ms. This greatly
> > reduces the chance that multiple controllers will try
> > to add MAC binding at exactly the same time. This applies
> > only to multicast ARP request which applies only to GARP
> > that is sent to broadcast address.
> >
> > Local testing with this delay vs without show significantly
> > reduced chance of hitting the transaction error.
> >
> > During the testing 10 GARPs was sent to two controllers
> > at the same time. Without proposed fix at least one controller
> > had multiple transaction errors present every test iteration.
> >
> > With the proposed fix the transaction error was reduced to a
> > single one when it happened which was usually in less than
> > 10% of the iterations.
> >
> > As was mentioned before the race condition can still happen,
> > but the chance is greatly reduced.
> >
> > Suggested-by: Daniel Alvarez Sanchez <[email protected]>
> > Signed-off-by: Ales Musil <[email protected]>
> > ---
> > v3: Rebase on top of current main.
> >     Change the delay to be per ARP request instead of
> >     single delay for everything. This allows the possibility
> >     to delay only multicast/broadcast AP as suggested by Han.
> > v4,v5: Address comments from Dumitru.
> > ---
>
> Thanks for this new revision!
>
> I have two very small comments but I don't think you need to send a v6
> for this, let's see if the maintainers require that or if the
> suggestions can be incorporated when the patch is accepted.
>
> Acked-by: Dumitru Ceara <[email protected]>

Thanks.  I applied this patch to the main branch.  Unfortunately I
missed addressing
Dumitru's comments before applying.  If you can send a follow up patch
that would be great.

Numan

>
> >  controller/mac-learn.c | 41 +++++++++++++++++++++++++++++++++--------
> >  controller/mac-learn.h |  9 +++++++--
> >  controller/pinctrl.c   | 27 ++++++++++++++++++---------
> >  tests/ovn.at           |  6 +++---
> >  4 files changed, 61 insertions(+), 22 deletions(-)
> >
> > diff --git a/controller/mac-learn.c b/controller/mac-learn.c
> > index 27634dca8..a27607016 100644
> > --- a/controller/mac-learn.c
> > +++ b/controller/mac-learn.c
> > @@ -18,14 +18,18 @@
> >  #include "mac-learn.h"
> >
> >  /* OpenvSwitch lib includes. */
> > +#include "openvswitch/poll-loop.h"
> >  #include "openvswitch/vlog.h"
> >  #include "lib/packets.h"
> > +#include "lib/random.h"
> >  #include "lib/smap.h"
> > +#include "lib/timeval.h"
> >
> >  VLOG_DEFINE_THIS_MODULE(mac_learn);
> >
> >  #define MAX_MAC_BINDINGS 1000
> >  #define MAX_FDB_ENTRIES  1000
> > +#define MAX_MAC_BINDING_DELAY_MSEC 50
> >
> >  static size_t mac_binding_hash(uint32_t dp_key, uint32_t port_key,
> >                                 struct in6_addr *);
> > @@ -46,25 +50,19 @@ ovn_mac_bindings_init(struct hmap *mac_bindings)
> >  }
> >
> >  void
> > -ovn_mac_bindings_flush(struct hmap *mac_bindings)
> > +ovn_mac_bindings_destroy(struct hmap *mac_bindings)
> >  {
> >      struct mac_binding *mb;
> >      HMAP_FOR_EACH_POP (mb, hmap_node, mac_bindings) {
> >          free(mb);
> >      }
> > -}
> > -
> > -void
> > -ovn_mac_bindings_destroy(struct hmap *mac_bindings)
> > -{
> > -    ovn_mac_bindings_flush(mac_bindings);
> >      hmap_destroy(mac_bindings);
> >  }
> >
> >  struct mac_binding *
> >  ovn_mac_binding_add(struct hmap *mac_bindings, uint32_t dp_key,
> >                      uint32_t port_key, struct in6_addr *ip,
> > -                    struct eth_addr mac)
> > +                    struct eth_addr mac, bool is_unicast)
> >  {
> >      uint32_t hash = mac_binding_hash(dp_key, port_key, ip);
> >
> > @@ -75,10 +73,13 @@ ovn_mac_binding_add(struct hmap *mac_bindings, uint32_t 
> > dp_key,
> >              return NULL;
> >          }
> >
> > +        uint32_t delay = is_unicast
> > +            ? 0 : random_range(MAX_MAC_BINDING_DELAY_MSEC) + 1;
>
> Nit: the coding-style doesn't seem to be extremely specific but the way
> I read it this should be indented a bit differently:
>
> uint32_t delay = is_unicast
>                  ? 0 : random_range(MAX_MAC_BINDING_DELAY_MSEC) + 1;
>
> Or maybe cleaner:
>
> uint32_t delay = !is_unicast
>                  ? random_range(MAX_MAC_BINDING_DELAY_MSEC) + 1
>                  : 0;
>
> >          mb = xmalloc(sizeof *mb);
> >          mb->dp_key = dp_key;
> >          mb->port_key = port_key;
> >          mb->ip = *ip;
> > +        mb->commit_at_ms = time_msec() + delay;
> >          hmap_insert(mac_bindings, &mb->hmap_node, hash);
> >      }
> >      mb->mac = mac;
> > @@ -86,6 +87,30 @@ ovn_mac_binding_add(struct hmap *mac_bindings, uint32_t 
> > dp_key,
> >      return mb;
> >  }
> >
> > +/* This is called from ovn-controller main context */
> > +void
> > +ovn_mac_binding_wait(struct hmap *mac_bindings)
> > +{
> > +    struct mac_binding *mb;
> > +
> > +    HMAP_FOR_EACH (mb, hmap_node, mac_bindings) {
> > +        poll_timer_wait_until(mb->commit_at_ms);
> > +    }
> > +}
> > +
> > +void
> > +ovn_mac_binding_remove(struct mac_binding *mb, struct hmap *mac_bindings)
> > +{
> > +    hmap_remove(mac_bindings, &mb->hmap_node);
> > +    free(mb);
> > +}
> > +
> > +bool
> > +ovn_mac_binding_can_commit(const struct mac_binding *mb, long long now)
> > +{
> > +    return now >= mb->commit_at_ms;
> > +}
> > +
> >  /* fdb functions. */
> >  void
> >  ovn_fdb_init(struct hmap *fdbs)
> > diff --git a/controller/mac-learn.h b/controller/mac-learn.h
> > index e7e8ba2d3..57c50c58b 100644
> > --- a/controller/mac-learn.h
> > +++ b/controller/mac-learn.h
> > @@ -31,16 +31,21 @@ struct mac_binding {
> >
> >      /* Value. */
> >      struct eth_addr mac;
> > +
> > +    /* Timestamp when to commit to SB. */
> > +    long long commit_at_ms;
> >  };
> >
> >  void ovn_mac_bindings_init(struct hmap *mac_bindings);
> > -void ovn_mac_bindings_flush(struct hmap *mac_bindings);
> >  void ovn_mac_bindings_destroy(struct hmap *mac_bindings);
> > +void ovn_mac_binding_wait(struct hmap *mac_bindings);
> > +void ovn_mac_binding_remove(struct mac_binding *mb, struct hmap 
> > *mac_bindings);
> > +bool ovn_mac_binding_can_commit(const struct mac_binding *mb, long long 
> > now);
> >
> >  struct mac_binding *ovn_mac_binding_add(struct hmap *mac_bindings,
> >                                          uint32_t dp_key, uint32_t port_key,
> >                                          struct in6_addr *ip,
> > -                                        struct eth_addr mac);
> > +                                        struct eth_addr mac, bool 
> > is_unicast);
> >
> >
> >
> > diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> > index f954362b7..beb3d3344 100644
> > --- a/controller/pinctrl.c
> > +++ b/controller/pinctrl.c
> > @@ -4134,9 +4134,13 @@ pinctrl_handle_put_mac_binding(const struct flow *md,
> >          memcpy(&ip_key, &ip6, sizeof ip_key);
> >      }
> >
> > +    /* If the ARP reply was unicast we should not delay it,
> > +     * there won't be any race. */
>
> Nit: Now that you encapsulated the delay logic in mac-learn.c, I think
> this comment can go inside ovn_mac_binding_add() before we compute 'delay'.
>
> > +    bool is_unicast = !eth_addr_is_multicast(headers->dl_dst);
> >      struct mac_binding *mb = ovn_mac_binding_add(&put_mac_bindings, dp_key,
> >                                                   port_key, &ip_key,
> > -                                                 headers->dl_src);
> > +                                                 headers->dl_src,
> > +                                                 is_unicast);
> >      if (!mb) {
> >          COVERAGE_INC(pinctrl_drop_put_mac_binding);
> >          return;
> > @@ -4296,14 +4300,18 @@ run_put_mac_bindings(struct ovsdb_idl_txn 
> > *ovnsb_idl_txn,
> >          return;
> >      }
> >
> > -    const struct mac_binding *mb;
> > -    HMAP_FOR_EACH (mb, hmap_node, &put_mac_bindings) {
> > -        run_put_mac_binding(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
> > -                            sbrec_port_binding_by_key,
> > -                            sbrec_mac_binding_by_lport_ip,
> > -                            mb);
> > +    long long now = time_msec();
> > +
> > +    struct mac_binding *mb;
> > +    HMAP_FOR_EACH_SAFE (mb, hmap_node, &put_mac_bindings) {
> > +        if (ovn_mac_binding_can_commit(mb, now)) {
> > +            run_put_mac_binding(ovnsb_idl_txn,
> > +                                sbrec_datapath_binding_by_key,
> > +                                sbrec_port_binding_by_key,
> > +                                sbrec_mac_binding_by_lport_ip, mb);
> > +            ovn_mac_binding_remove(mb, &put_mac_bindings);
> > +        }
> >      }
> > -    ovn_mac_bindings_flush(&put_mac_bindings);
> >  }
> >
> >  static void
> > @@ -4352,9 +4360,10 @@ run_buffered_binding(struct ovsdb_idl_index 
> > *sbrec_mac_binding_by_lport_ip,
> >
> >  static void
> >  wait_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn)
> > +    OVS_REQUIRES(pinctrl_mutex)
> >  {
> >      if (ovnsb_idl_txn && !hmap_is_empty(&put_mac_bindings)) {
> > -        poll_immediate_wake();
> > +        ovn_mac_binding_wait(&put_mac_bindings);
> >      }
> >  }
> >
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index c346975e6..eff18fb84 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -23002,7 +23002,7 @@ send_garp 1 1 $eth_src $eth_dst $spa $tpa
> >
> >  wait_row_count MAC_Binding 1
> >
> > -AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
> > +OVS_WAIT_UNTIL([ovn-sbctl --format=csv --bare --columns 
> > logical_port,ip,mac \
> >  list mac_binding], [0], [lr0-sw0
> >  10.0.0.30
> >  50:54:00:00:00:03
> > @@ -23049,7 +23049,7 @@ grep table_id=10 | wc -l`])
> >
> >  check_row_count MAC_Binding 1
> >
> > -AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
> > +OVS_WAIT_UNTIL([ovn-sbctl --format=csv --bare --columns 
> > logical_port,ip,mac \
> >  list mac_binding], [0], [lr0-sw0
> >  10.0.0.30
> >  50:54:00:00:00:13
> > @@ -23078,7 +23078,7 @@ OVS_WAIT_UNTIL(
> >  | wc -l`]
> >  )
> >
> > -AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
> > +OVS_WAIT_UNTIL([ovn-sbctl --format=csv --bare --columns 
> > logical_port,ip,mac \
> >  find mac_binding ip=10.0.0.50], [0], [lr0-sw0
> >  10.0.0.50
> >  50:54:00:00:00:33
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to