diff --git a/SOURCES/openvswitch-2.16.0.patch b/SOURCES/openvswitch-2.16.0.patch
index 1241304..c9bf905 100644
--- a/SOURCES/openvswitch-2.16.0.patch
+++ b/SOURCES/openvswitch-2.16.0.patch
@@ -3179,6 +3179,82 @@ index ecc251a7ff..83bd10cca5 100644
 +    HMAP_FOR_EACH (ITER, hmap_node, &(DUMP)->numas)
  
  #endif /* ovs-numa.h */
+diff --git a/lib/ovs-rcu.c b/lib/ovs-rcu.c
+index 1866bd3088..946aa04d18 100644
+--- a/lib/ovs-rcu.c
++++ b/lib/ovs-rcu.c
+@@ -444,3 +444,40 @@ ovsrcu_init_module(void)
+         ovsthread_once_done(&once);
+     }
+ }
++
++static void
++ovsrcu_barrier_func(void *seq_)
++{
++    struct seq *seq = (struct seq *) seq_;
++    seq_change(seq);
++}
++
++/* Similar to the kernel rcu_barrier, ovsrcu_barrier waits for all outstanding
++ * RCU callbacks to complete. However, unlike the kernel rcu_barrier, which
++ * might return immediately if there are no outstanding RCU callbacks,
++ * this API will at least wait for a grace period.
++ *
++ * Another issue the caller might need to know is that the barrier is just
++ * for "one-shot", i.e. if inside some RCU callbacks, another RCU callback is
++ * registered, this API only guarantees the first round of RCU callbacks have
++ * been executed after it returns.
++ */
++void
++ovsrcu_barrier(void)
++{
++    struct seq *seq = seq_create();
++    /* First let all threads flush their cbsets. */
++    ovsrcu_synchronize();
++
++    /* Then register a new cbset, ensure this cbset
++     * is at the tail of the global list. */
++    uint64_t seqno = seq_read(seq);
++    ovsrcu_postpone__(ovsrcu_barrier_func, (void *) seq);
++
++    do {
++        seq_wait(seq, seqno);
++        poll_block();
++    } while (seqno == seq_read(seq));
++
++    seq_destroy(seq);
++}
+diff --git a/lib/ovs-rcu.h b/lib/ovs-rcu.h
+index ecc4c92010..8b397b7fb0 100644
+--- a/lib/ovs-rcu.h
++++ b/lib/ovs-rcu.h
+@@ -155,6 +155,19 @@
+  *         port_delete(id);
+  *     }
+  *
++ * Use ovsrcu_barrier() to wait for all the outstanding RCU callbacks to
++ * finish. This is useful when you have to destroy some resources however
++ * these resources are referenced in the outstanding RCU callbacks.
++ *
++ *     void rcu_cb(void *A) {
++ *         do_something(A);
++ *     }
++ *
++ *     void destroy_A() {
++ *         ovsrcu_postpone(rcu_cb, A); // will use A later
++ *         ovsrcu_barrier(); // wait for rcu_cb done
++ *         do_destroy_A(); // free A
++ *     }
+  */
+ 
+ #include "compiler.h"
+@@ -310,4 +323,6 @@ void ovsrcu_synchronize(void);
+ 
+ void ovsrcu_exit(void);
+ 
++void ovsrcu_barrier(void);
++
+ #endif /* ovs-rcu.h */
 diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
 index 659d49dbf7..dead31275d 100644
 --- a/lib/ovsdb-cs.c
@@ -5519,7 +5595,7 @@ index 851088d794..2ba90e999c 100644
  void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
                        const char *name, enum port_vlan_mode,
 diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
-index cba49a99e1..13f00e82d2 100644
+index cba49a99e1..ed92d3731f 100644
 --- a/ofproto/ofproto-dpif.c
 +++ b/ofproto/ofproto-dpif.c
 @@ -215,10 +215,6 @@ struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
@@ -5552,7 +5628,16 @@ index cba49a99e1..13f00e82d2 100644
  
      OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
          CLS_FOR_EACH (rule, up.cr, &table->cls) {
-@@ -2333,6 +2324,12 @@ set_ipfix(
+@@ -1819,6 +1810,8 @@ destruct(struct ofproto *ofproto_, bool del)
+ 
+     seq_destroy(ofproto->ams_seq);
+ 
++    /* Wait for all the meter destroy work to finish. */
++    ovsrcu_barrier();
+     close_dpif_backer(ofproto->backer, del);
+ }
+ 
+@@ -2333,6 +2326,12 @@ set_ipfix(
              dpif_ipfix_unref(di);
              ofproto->ipfix = NULL;
          }
@@ -5565,7 +5650,7 @@ index cba49a99e1..13f00e82d2 100644
      }
  
      return 0;
-@@ -4433,12 +4430,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto,
+@@ -4433,12 +4432,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto,
                  atomic_add_relaxed(&tbl->n_matched, stats->n_packets, &orig);
              }
              if (xcache) {
@@ -5585,7 +5670,7 @@ index cba49a99e1..13f00e82d2 100644
              }
              return rule;
          }
-@@ -4469,12 +4468,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto,
+@@ -4469,12 +4470,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto,
                                 stats->n_packets, &orig);
          }
          if (xcache) {
@@ -5605,7 +5690,7 @@ index cba49a99e1..13f00e82d2 100644
          }
          if (rule) {
              goto out;   /* Match. */
-@@ -5556,6 +5557,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id,
+@@ -5556,6 +5559,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id,
              ct_timeout_policy_unref(backer, ct_zone->ct_tp);
              ct_zone->ct_tp = ct_tp;
              ct_tp->ref_count++;
@@ -5613,7 +5698,7 @@ index cba49a99e1..13f00e82d2 100644
          }
      } else {
          struct ct_zone *new_ct_zone = ct_zone_alloc(zone_id);
-@@ -5563,6 +5565,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id,
+@@ -5563,6 +5567,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id,
          cmap_insert(&backer->ct_zones, &new_ct_zone->node,
                      hash_int(zone_id, 0));
          ct_tp->ref_count++;
@@ -5621,7 +5706,7 @@ index cba49a99e1..13f00e82d2 100644
      }
  }
  
-@@ -5579,6 +5582,7 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
+@@ -5579,6 +5584,7 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
      if (ct_zone) {
          ct_timeout_policy_unref(backer, ct_zone->ct_tp);
          ct_zone_remove_and_destroy(backer, ct_zone);
@@ -5629,7 +5714,7 @@ index cba49a99e1..13f00e82d2 100644
      }
  }
  
-@@ -5779,15 +5783,7 @@ ofproto_dpif_lookup_by_name(const char *name)
+@@ -5779,15 +5785,7 @@ ofproto_dpif_lookup_by_name(const char *name)
  struct ofproto_dpif *
  ofproto_dpif_lookup_by_uuid(const struct uuid *uuid)
  {
@@ -5646,7 +5731,7 @@ index cba49a99e1..13f00e82d2 100644
  }
  
  static void
-@@ -6496,6 +6492,7 @@ ofproto_unixctl_dpif_show_dp_features(struct unixctl_conn *conn,
+@@ -6496,6 +6494,7 @@ ofproto_unixctl_dpif_show_dp_features(struct unixctl_conn *conn,
  
      dpif_show_support(&ofproto->backer->bt_support, &ds);
      unixctl_command_reply(conn, ds_cstr(&ds));
@@ -8768,6 +8853,19 @@ index 7ef32d13cb..cb0e9df388 100755
      print()
  
      flows.write(struct.pack('>LH',
+diff --git a/tests/library.at b/tests/library.at
+index 1702b7556b..e27d9e8bce 100644
+--- a/tests/library.at
++++ b/tests/library.at
+@@ -247,7 +247,7 @@ AT_CHECK([ovstest test-ofpbuf], [0], [])
+ AT_CLEANUP
+ 
+ AT_SETUP([rcu])
+-AT_CHECK([ovstest test-rcu-quiesce], [0], [])
++AT_CHECK([ovstest test-rcu], [0], [])
+ AT_CLEANUP
+ 
+ AT_SETUP([stopwatch module])
 diff --git a/tests/mcast-snooping.at b/tests/mcast-snooping.at
 index 757cf7186e..fe475e7b38 100644
 --- a/tests/mcast-snooping.at
@@ -11479,6 +11577,50 @@ index 5bc0bf6814..853264f22b 100644
              step += 1
  
          seqno = idl.change_seqno
+diff --git a/tests/test-rcu.c b/tests/test-rcu.c
+index 965f3c49f3..bb17092bf0 100644
+--- a/tests/test-rcu.c
++++ b/tests/test-rcu.c
+@@ -35,7 +35,7 @@ quiescer_main(void *aux OVS_UNUSED)
+ }
+ 
+ static void
+-test_rcu_quiesce(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
++test_rcu_quiesce(void)
+ {
+     pthread_t quiescer;
+ 
+@@ -48,4 +48,29 @@ test_rcu_quiesce(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+     xpthread_join(quiescer, NULL);
+ }
+ 
+-OVSTEST_REGISTER("test-rcu-quiesce", test_rcu_quiesce);
++static void
++add_count(void *_count)
++{
++    unsigned *count = (unsigned *)_count;
++    (*count) ++;
++}
++
++static void
++test_rcu_barrier(void)
++{
++    unsigned count = 0;
++    for (int i = 0; i < 10; i ++) {
++        ovsrcu_postpone(add_count, &count);
++    }
++
++    ovsrcu_barrier();
++    ovs_assert(count == 10);
++}
++
++static void
++test_rcu(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) {
++    test_rcu_quiesce();
++    test_rcu_barrier();
++}
++
++OVSTEST_REGISTER("test-rcu", test_rcu);
 diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
 index 59723e63b8..c7665a1aeb 100644
 --- a/tests/tunnel-push-pop-ipv6.at
diff --git a/SPECS/openvswitch2.16.spec b/SPECS/openvswitch2.16.spec
index 50275a8..68ff286 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: 78%{?dist}
+Release: 79%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -699,6 +699,13 @@ exit 0
 %endif
 
 %changelog
+* Tue May 31 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.16.0-79
+- Merging upstream branch-2.16 [RH git: c224775aed]
+    Commit list:
+    840c3fcb12 ofproto-dpif: Fix meter use-after-free.
+    77c89b0d25 ovs-rcu: Add ovsrcu_barrier.
+
+
 * Thu May 26 2022 Ilya Maximets <i.maximets@redhat.com> - 2.16.0-78
 - Merging upstream branch-2.16 [RH git: d7d5f09849]
     Commit list: