diff --git a/SOURCES/openvswitch-2.16.0.patch b/SOURCES/openvswitch-2.16.0.patch
index 7df258c..356509d 100644
--- a/SOURCES/openvswitch-2.16.0.patch
+++ b/SOURCES/openvswitch-2.16.0.patch
@@ -1,3 +1,18 @@
+diff --git a/.ci/linux-build.sh b/.ci/linux-build.sh
+index 863f023888..1e7565b8bb 100755
+--- a/.ci/linux-build.sh
++++ b/.ci/linux-build.sh
+@@ -246,8 +246,8 @@ if [ "$ASAN" ]; then
+     export ASAN_OPTIONS='detect_leaks=1'
+     # -O2 generates few false-positive memory leak reports in test-ovsdb
+     # application, so lowering optimizations to -O1 here.
+-    CLFAGS_ASAN="-O1 -fno-omit-frame-pointer -fno-common -fsanitize=address"
+-    CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} ${CLFAGS_ASAN}"
++    CFLAGS_ASAN="-O1 -fno-omit-frame-pointer -fno-common -fsanitize=address"
++    CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} ${CFLAGS_ASAN}"
+ fi
+ 
+ save_OPTS="${OPTS} $*"
 diff --git a/.ci/linux-prepare.sh b/.ci/linux-prepare.sh
 index c55125cf78..c0b7473eda 100755
 --- a/.ci/linux-prepare.sh
@@ -657,6 +672,122 @@ index 89a36fe17b..a8b0705d9f 100755
                                          (tunnel.conf["remote_ip"],
                                          tunnel.conf["private_key"]))
              if tunnel.conf["remote_cert"]:
+diff --git a/lib/bfd.c b/lib/bfd.c
+index 3c965699ac..9698576d07 100644
+--- a/lib/bfd.c
++++ b/lib/bfd.c
+@@ -131,16 +131,17 @@ enum diag {
+  * |                 Required Min Echo RX Interval                 |
+  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+ struct msg {
+-    uint8_t vers_diag;    /* Version and diagnostic. */
+-    uint8_t flags;        /* 2bit State field followed by flags. */
+-    uint8_t mult;         /* Fault detection multiplier. */
+-    uint8_t length;       /* Length of this BFD message. */
+-    ovs_be32 my_disc;     /* My discriminator. */
+-    ovs_be32 your_disc;   /* Your discriminator. */
+-    ovs_be32 min_tx;      /* Desired minimum tx interval. */
+-    ovs_be32 min_rx;      /* Required minimum rx interval. */
+-    ovs_be32 min_rx_echo; /* Required minimum echo rx interval. */
++    uint8_t vers_diag;              /* Version and diagnostic. */
++    uint8_t flags;                  /* 2bit State field followed by flags. */
++    uint8_t mult;                   /* Fault detection multiplier. */
++    uint8_t length;                 /* Length of this BFD message. */
++    ovs_16aligned_be32 my_disc;     /* My discriminator. */
++    ovs_16aligned_be32 your_disc;   /* Your discriminator. */
++    ovs_16aligned_be32 min_tx;      /* Desired minimum tx interval. */
++    ovs_16aligned_be32 min_rx;      /* Required minimum rx interval. */
++    ovs_16aligned_be32 min_rx_echo; /* Required minimum echo rx interval. */
+ };
++
+ BUILD_ASSERT_DECL(BFD_PACKET_LEN == sizeof(struct msg));
+ 
+ #define DIAG_MASK 0x1f
+@@ -634,9 +635,9 @@ bfd_put_packet(struct bfd *bfd, struct dp_packet *p,
+ 
+     msg->mult = bfd->mult;
+     msg->length = BFD_PACKET_LEN;
+-    msg->my_disc = htonl(bfd->disc);
+-    msg->your_disc = htonl(bfd->rmt_disc);
+-    msg->min_rx_echo = htonl(0);
++    put_16aligned_be32(&msg->my_disc, htonl(bfd->disc));
++    put_16aligned_be32(&msg->your_disc, htonl(bfd->rmt_disc));
++    put_16aligned_be32(&msg->min_rx_echo, htonl(0));
+ 
+     if (bfd_in_poll(bfd)) {
+         min_tx = bfd->poll_min_tx;
+@@ -646,8 +647,8 @@ bfd_put_packet(struct bfd *bfd, struct dp_packet *p,
+         min_rx = bfd->min_rx;
+     }
+ 
+-    msg->min_tx = htonl(min_tx * 1000);
+-    msg->min_rx = htonl(min_rx * 1000);
++    put_16aligned_be32(&msg->min_tx, htonl(min_tx * 1000));
++    put_16aligned_be32(&msg->min_rx, htonl(min_rx * 1000));
+ 
+     bfd->flags &= ~FLAG_FINAL;
+     *oam = bfd->oam;
+@@ -781,12 +782,12 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
+         goto out;
+     }
+ 
+-    if (!msg->my_disc) {
++    if (!get_16aligned_be32(&msg->my_disc)) {
+         log_msg(VLL_WARN, msg, "NULL my_disc", bfd);
+         goto out;
+     }
+ 
+-    pkt_your_disc = ntohl(msg->your_disc);
++    pkt_your_disc = ntohl(get_16aligned_be32(&msg->your_disc));
+     if (pkt_your_disc) {
+         /* Technically, we should use the your discriminator field to figure
+          * out which 'struct bfd' this packet is destined towards.  That way a
+@@ -806,7 +807,7 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
+         bfd_status_changed(bfd);
+     }
+ 
+-    bfd->rmt_disc = ntohl(msg->my_disc);
++    bfd->rmt_disc = ntohl(get_16aligned_be32(&msg->my_disc));
+     bfd->rmt_state = rmt_state;
+     bfd->rmt_flags = flags;
+     bfd->rmt_diag = msg->vers_diag & DIAG_MASK;
+@@ -834,7 +835,7 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
+         bfd->rmt_mult = msg->mult;
+     }
+ 
+-    rmt_min_rx = MAX(ntohl(msg->min_rx) / 1000, 1);
++    rmt_min_rx = MAX(ntohl(get_16aligned_be32(&msg->min_rx)) / 1000, 1);
+     if (bfd->rmt_min_rx != rmt_min_rx) {
+         bfd->rmt_min_rx = rmt_min_rx;
+         if (bfd->next_tx) {
+@@ -843,7 +844,7 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
+         log_msg(VLL_INFO, msg, "New remote min_rx", bfd);
+     }
+ 
+-    bfd->rmt_min_tx = MAX(ntohl(msg->min_tx) / 1000, 1);
++    bfd->rmt_min_tx = MAX(ntohl(get_16aligned_be32(&msg->min_tx)) / 1000, 1);
+     bfd->detect_time = bfd_rx_interval(bfd) * bfd->rmt_mult + time_msec();
+ 
+     if (bfd->state == STATE_ADMIN_DOWN) {
+@@ -1105,10 +1106,14 @@ log_msg(enum vlog_level level, const struct msg *p, const char *message,
+                   bfd_diag_str(p->vers_diag & DIAG_MASK),
+                   bfd_state_str(p->flags & STATE_MASK),
+                   p->mult, p->length, bfd_flag_str(p->flags & FLAGS_MASK),
+-                  ntohl(p->my_disc), ntohl(p->your_disc),
+-                  ntohl(p->min_tx), ntohl(p->min_tx) / 1000,
+-                  ntohl(p->min_rx), ntohl(p->min_rx) / 1000,
+-                  ntohl(p->min_rx_echo), ntohl(p->min_rx_echo) / 1000);
++                  ntohl(get_16aligned_be32(&p->my_disc)),
++                  ntohl(get_16aligned_be32(&p->your_disc)),
++                  ntohl(get_16aligned_be32(&p->min_tx)),
++                  ntohl(get_16aligned_be32(&p->min_tx)) / 1000,
++                  ntohl(get_16aligned_be32(&p->min_rx)),
++                  ntohl(get_16aligned_be32(&p->min_rx)) / 1000,
++                  ntohl(get_16aligned_be32(&p->min_rx_echo)),
++                  ntohl(get_16aligned_be32(&p->min_rx_echo)) / 1000);
+     bfd_put_details(&ds, bfd);
+     VLOG(level, "%s", ds_cstr(&ds));
+     ds_destroy(&ds);
 diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c
 index 77cc76a9f6..7074561588 100644
 --- a/lib/db-ctl-base.c
@@ -755,6 +886,19 @@ index 77cc76a9f6..7074561588 100644
          ovsdb_datum_destroy(&add, type);
      }
      if (old.n > type->n_max) {
+diff --git a/lib/dp-packet.c b/lib/dp-packet.c
+index 72f6d09ac7..35c72542a2 100644
+--- a/lib/dp-packet.c
++++ b/lib/dp-packet.c
+@@ -294,7 +294,7 @@ dp_packet_resize(struct dp_packet *b, size_t new_headroom, size_t new_tailroom)
+ void
+ dp_packet_prealloc_tailroom(struct dp_packet *b, size_t size)
+ {
+-    if (size > dp_packet_tailroom(b)) {
++    if ((size && !dp_packet_base(b)) || (size > dp_packet_tailroom(b))) {
+         dp_packet_resize(b, dp_packet_headroom(b), MAX(size, 64));
+     }
+ }
 diff --git a/lib/dp-packet.h b/lib/dp-packet.h
 index 08d93c2779..3dc582fbfd 100644
 --- a/lib/dp-packet.h
@@ -1064,6 +1208,23 @@ index 92092ebec9..3dfc91f0fe 100644
   * If dp_netdev_input is not called from a pmd thread, a mutex is used.
   */
  
+diff --git a/lib/dpif-netdev-private-dpcls.h b/lib/dpif-netdev-private-dpcls.h
+index 7c4a840cb1..0d5da73c7a 100644
+--- a/lib/dpif-netdev-private-dpcls.h
++++ b/lib/dpif-netdev-private-dpcls.h
+@@ -83,8 +83,10 @@ struct dpcls_subtable {
+     /* The lookup function to use for this subtable. If there is a known
+      * property of the subtable (eg: only 3 bits of miniflow metadata is
+      * used for the lookup) then this can point at an optimized version of
+-     * the lookup function for this particular subtable. */
+-    dpcls_subtable_lookup_func lookup_func;
++     * the lookup function for this particular subtable. The lookup function
++     * can be used at any time by a PMD thread, so it's declared as an atomic
++     * here to prevent garbage from being read. */
++    ATOMIC(dpcls_subtable_lookup_func) lookup_func;
+ 
+     /* Caches the masks to match a packet to, reducing runtime calculations. */
+     uint64_t *mf_masks;
 diff --git a/lib/dpif-netdev-private-thread.h b/lib/dpif-netdev-private-thread.h
 index a782d9678a..ac4885538c 100644
 --- a/lib/dpif-netdev-private-thread.h
@@ -1084,10 +1245,20 @@ index a782d9678a..ac4885538c 100644
  
      /* Flow-Table and classifiers
 diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
-index bddce75b63..d6bee2a5a9 100644
+index bddce75b63..0b47c1a486 100644
 --- a/lib/dpif-netdev.c
 +++ b/lib/dpif-netdev.c
-@@ -4061,7 +4061,10 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
+@@ -984,7 +984,9 @@ dpif_netdev_subtable_lookup_set(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                 if (!cls) {
+                     continue;
+                 }
++                ovs_mutex_lock(&pmd->flow_mutex);
+                 uint32_t subtbl_changes = dpcls_subtable_lookup_reprobe(cls);
++                ovs_mutex_unlock(&pmd->flow_mutex);
+                 if (subtbl_changes) {
+                     lookup_dpcls_changed++;
+                     lookup_subtable_changed += subtbl_changes;
+@@ -4061,7 +4063,10 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
                                 flow_hash_5tuple(execute->flow, 0));
      }
  
@@ -1099,7 +1270,7 @@ index bddce75b63..d6bee2a5a9 100644
      dp_netdev_execute_actions(pmd, &pp, false, execute->flow,
                                execute->actions, execute->actions_len);
      dp_netdev_pmd_flush_output_packets(pmd, true);
-@@ -4071,6 +4074,24 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
+@@ -4071,6 +4076,24 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
          dp_netdev_pmd_unref(pmd);
      }
  
@@ -1124,6 +1295,48 @@ index bddce75b63..d6bee2a5a9 100644
      return 0;
  }
  
+@@ -8942,9 +8965,12 @@ dpcls_create_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
+ 
+     /* Get the preferred subtable search function for this (u0,u1) subtable.
+      * The function is guaranteed to always return a valid implementation, and
+-     * possibly an ISA optimized, and/or specialized implementation.
++     * possibly an ISA optimized, and/or specialized implementation. Initialize
++     * the subtable search function atomically to avoid garbage data being read
++     * by the PMD thread.
+      */
+-    subtable->lookup_func = dpcls_subtable_get_best_impl(unit0, unit1);
++    atomic_init(&subtable->lookup_func,
++                dpcls_subtable_get_best_impl(unit0, unit1));
+ 
+     cmap_insert(&cls->subtables_map, &subtable->cmap_node, mask->hash);
+     /* Add the new subtable at the end of the pvector (with no hits yet) */
+@@ -8973,6 +8999,10 @@ dpcls_find_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
+ /* Checks for the best available implementation for each subtable lookup
+  * function, and assigns it as the lookup function pointer for each subtable.
+  * Returns the number of subtables that have changed lookup implementation.
++ * This function requires holding a flow_mutex when called. This is to make
++ * sure modifications done by this function are not overwritten. This could
++ * happen if dpcls_sort_subtable_vector() is called at the same time as this
++ * function.
+  */
+ static uint32_t
+ dpcls_subtable_lookup_reprobe(struct dpcls *cls)
+@@ -8985,10 +9015,13 @@ dpcls_subtable_lookup_reprobe(struct dpcls *cls)
+         uint32_t u0_bits = subtable->mf_bits_set_unit0;
+         uint32_t u1_bits = subtable->mf_bits_set_unit1;
+         void *old_func = subtable->lookup_func;
+-        subtable->lookup_func = dpcls_subtable_get_best_impl(u0_bits, u1_bits);
++
++        /* Set the subtable lookup function atomically to avoid garbage data
++         * being read by the PMD thread. */
++        atomic_store_relaxed(&subtable->lookup_func,
++                    dpcls_subtable_get_best_impl(u0_bits, u1_bits));
+         subtables_changed += (old_func != subtable->lookup_func);
+     }
+-    pvector_publish(pvec);
+ 
+     return subtables_changed;
+ }
 diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
 index 34fc042373..5f4b60c5a6 100644
 --- a/lib/dpif-netlink.c
@@ -1496,6 +1709,21 @@ index 32d25003b8..0baf7c622c 100644
 +    }
      ds_put_char(ds, '"');
  }
+diff --git a/lib/lldp/lldp.c b/lib/lldp/lldp.c
+index 18afbab9a7..dfeb2a8002 100644
+--- a/lib/lldp/lldp.c
++++ b/lib/lldp/lldp.c
+@@ -146,7 +146,9 @@ static void
+ lldp_tlv_end(struct dp_packet *p, unsigned int start)
+ {
+     ovs_be16 *tlv = dp_packet_at_assert(p, start, 2);
+-    *tlv |= htons((dp_packet_size(p) - (start + 2)) & 0x1ff);
++    put_unaligned_be16(tlv,
++                       get_unaligned_be16(tlv)
++                       | htons((dp_packet_size(p) - (start + 2)) & 0x1ff));
+ }
+ 
+ int
 diff --git a/lib/meta-flow.c b/lib/meta-flow.c
 index c808d205d5..e03cd8d0c5 100644
 --- a/lib/meta-flow.c
@@ -2433,6 +2661,81 @@ index b30a11c24b..41835f6f4d 100644
          return NULL;
      }
  
+diff --git a/lib/stopwatch.c b/lib/stopwatch.c
+index f5602163bc..1c71df1a12 100644
+--- a/lib/stopwatch.c
++++ b/lib/stopwatch.c
+@@ -114,7 +114,6 @@ static void
+ calc_percentile(unsigned long long n_samples, struct percentile *pctl,
+                 unsigned long long new_sample)
+ {
+-
+     if (n_samples < P_SQUARE_MIN) {
+         pctl->samples[n_samples - 1] = new_sample;
+     }
+@@ -228,13 +227,12 @@ add_sample(struct stopwatch *sw, unsigned long long new_sample)
+         sw->min = new_sample;
+     }
+ 
+-    calc_percentile(sw->n_samples, &sw->pctl, new_sample);
+-
+     if (sw->n_samples++ == 0) {
+         sw->short_term.average = sw->long_term.average = new_sample;
+         return;
+     }
+ 
++    calc_percentile(sw->n_samples, &sw->pctl, new_sample);
+     calc_average(&sw->short_term, new_sample);
+     calc_average(&sw->long_term, new_sample);
+ }
+diff --git a/lib/stp.c b/lib/stp.c
+index 809b405a52..a869b5f390 100644
+--- a/lib/stp.c
++++ b/lib/stp.c
+@@ -737,7 +737,7 @@ void
+ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
+ {
+     struct stp *stp = p->stp;
+-    const struct stp_bpdu_header *header;
++    struct stp_bpdu_header header;
+ 
+     ovs_mutex_lock(&mutex);
+     if (p->state == STP_DISABLED) {
+@@ -750,19 +750,19 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
+         goto out;
+     }
+ 
+-    header = bpdu;
+-    if (header->protocol_id != htons(STP_PROTOCOL_ID)) {
++    memcpy(&header, bpdu, sizeof header);
++    if (header.protocol_id != htons(STP_PROTOCOL_ID)) {
+         VLOG_WARN("%s: received BPDU with unexpected protocol ID %"PRIu16,
+-                  stp->name, ntohs(header->protocol_id));
++                  stp->name, ntohs(header.protocol_id));
+         p->error_count++;
+         goto out;
+     }
+-    if (header->protocol_version != STP_PROTOCOL_VERSION) {
++    if (header.protocol_version != STP_PROTOCOL_VERSION) {
+         VLOG_DBG("%s: received BPDU with unexpected protocol version %"PRIu8,
+-                 stp->name, header->protocol_version);
++                 stp->name, header.protocol_version);
+     }
+ 
+-    switch (header->bpdu_type) {
++    switch (header.bpdu_type) {
+     case STP_TYPE_CONFIG:
+         if (bpdu_size < sizeof(struct stp_config_bpdu)) {
+             VLOG_WARN("%s: received config BPDU with invalid size %"PRIuSIZE,
+@@ -785,7 +785,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
+ 
+     default:
+         VLOG_WARN("%s: received BPDU of unexpected type %"PRIu8,
+-                  stp->name, header->bpdu_type);
++                  stp->name, header.bpdu_type);
+         p->error_count++;
+         goto out;
+     }
 diff --git a/lib/tc.c b/lib/tc.c
 index 38a1dfc0eb..adb2d3182a 100644
 --- a/lib/tc.c
@@ -3222,7 +3525,7 @@ index 56edc5f000..03d1c3499e 100644
              break;
  
 diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
-index 61cded16d3..78ebf2dc14 100755
+index 61cded16d3..a2ee10af1b 100755
 --- a/ovsdb/ovsdb-idlc.in
 +++ b/ovsdb/ovsdb-idlc.in
 @@ -551,20 +551,20 @@ static void
@@ -3462,6 +3765,15 @@ index 61cded16d3..78ebf2dc14 100755
              print("}")
  
  # Index table related functions
+@@ -1272,7 +1265,7 @@ struct ovsdb_idl_cursor
+     struct ovsdb_idl_index *index, const struct %(s)s *target)
+ {
+     ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
+-    return ovsdb_idl_cursor_first_ge(index, &target->header_);
++    return ovsdb_idl_cursor_first_ge(index, target ? &target->header_ : NULL);
+ }
+ 
+ struct %(s)s *
 @@ -1309,8 +1302,8 @@ struct %(s)s *
  
          i = 0;
diff --git a/SPECS/openvswitch2.16.spec b/SPECS/openvswitch2.16.spec
index 39ef635..816847f 100644
--- a/SPECS/openvswitch2.16.spec
+++ b/SPECS/openvswitch2.16.spec
@@ -57,7 +57,7 @@ Summary: Open vSwitch
 Group: System Environment/Daemons daemon/database/utilities
 URL: http://www.openvswitch.org/
 Version: 2.16.0
-Release: 51%{?dist}
+Release: 52%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -699,6 +699,17 @@ exit 0
 %endif
 
 %changelog
+* Wed Feb 16 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.16.0-52
+- Merging upstream branch-2.16 [RH git: bba08b5363]
+    Commit list:
+    8e23c06f24 dpif-netdev-dpcls: Make subtable reprobe thread-safe.
+    ac0e3dd3ba ci: Fix typo in variable name.
+    fc25e0397a dp-packet: Ensure packet base is always non-NULL.
+    dbae56e702 bfd: lldp: stp: Fix misaligned packet field access.
+    ee17b06cf9 ovsdb-idlc: Avoid accessing member within NULL idl index cursors.
+    1d799a5d17 stopwatch: Fix buffer underflow when computing percentiles.
+
+
 * Wed Feb 09 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.16.0-51
 - Merging upstream branch-2.16 [RH git: 7b6570c65f]
     Commit list: