diff --git a/SOURCES/openvswitch-3.1.0.patch b/SOURCES/openvswitch-3.1.0.patch
index 99cb675..06ef9d6 100644
--- a/SOURCES/openvswitch-3.1.0.patch
+++ b/SOURCES/openvswitch-3.1.0.patch
@@ -3860,7 +3860,7 @@ index 62da9febb..86747e58b 100644
      SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback);
  #else
 diff --git a/lib/tc.c b/lib/tc.c
-index 4c07e2216..270dc95ce 100644
+index 4c07e2216..0c0f07767 100644
 --- a/lib/tc.c
 +++ b/lib/tc.c
 @@ -36,6 +36,7 @@
@@ -3978,7 +3978,48 @@ index 4c07e2216..270dc95ce 100644
      return tc_transact(&request, NULL);
  }
  
-@@ -3821,8 +3836,15 @@ tc_replace_flower(struct tcf_id *id, struct tc_flower *flower)
+@@ -3720,15 +3735,13 @@ log_tc_flower_match(const char *msg,
+ 
+         ds_put_cstr(&s, "\nExpected Actions:\n");
+         for (i = 0, action = a->actions; i < a->action_count; i++, action++) {
+-            ds_put_cstr(&s, " - ");
+-            ds_put_hex(&s, action, sizeof *action);
+-            ds_put_cstr(&s, "\n");
++            ds_put_format(&s, " - %d -\n", i);
++            ds_put_sparse_hex_dump(&s, action, sizeof *action, 0, false);
+         }
+-        ds_put_cstr(&s, "Received Actions:\n");
++        ds_put_cstr(&s, "\nReceived Actions:\n");
+         for (i = 0, action = b->actions; i < b->action_count; i++, action++) {
+-            ds_put_cstr(&s, " - ");
+-            ds_put_hex(&s, action, sizeof *action);
+-            ds_put_cstr(&s, "\n");
++            ds_put_format(&s, " - %d -\n", i);
++            ds_put_sparse_hex_dump(&s, action, sizeof *action, 0, false);
+         }
+     } else {
+         /* Only dump the delta in actions. */
+@@ -3737,12 +3750,13 @@ log_tc_flower_match(const char *msg,
+ 
+         for (int i = 0; i < a->action_count; i++, action_a++, action_b++) {
+             if (memcmp(action_a, action_b, sizeof *action_a)) {
+-                ds_put_format(&s,
+-                              "\nAction %d mismatch:\n - Expected Action: ",
+-                              i);
+-                ds_put_hex(&s, action_a, sizeof *action_a);
+-                ds_put_cstr(&s, "\n - Received Action: ");
+-                ds_put_hex(&s, action_b, sizeof *action_b);
++                ds_put_format(&s, "\nAction %d mismatch:\n"
++                                  " - Expected Action:\n", i);
++                ds_put_sparse_hex_dump(&s, action_a, sizeof *action_a,
++                                       0, false);
++                ds_put_cstr(&s, " - Received Action:\n");
++                ds_put_sparse_hex_dump(&s, action_b, sizeof *action_b,
++                                       0, false);
+             }
+         }
+     }
+@@ -3821,8 +3835,15 @@ tc_replace_flower(struct tcf_id *id, struct tc_flower *flower)
  
      error = tc_transact(&request, &reply);
      if (!error) {
@@ -4363,7 +4404,7 @@ index 742eed399..f13478a88 100644
      uint64_t export_time_usec;
      uint32_t export_time_sec;
 diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
-index e05ffe312..b44c72969 100644
+index e05ffe312..0fceeba18 100644
 --- a/ofproto/ofproto-dpif-upcall.c
 +++ b/ofproto/ofproto-dpif-upcall.c
 @@ -47,17 +47,20 @@
@@ -4415,7 +4456,33 @@ index e05ffe312..b44c72969 100644
          atomic_store_relaxed(&udpif->n_flows, flow_count);
          ovs_mutex_unlock(&udpif->n_flows_mutex);
      } else {
-@@ -1766,6 +1781,7 @@ ukey_create__(const struct nlattr *key, size_t key_len,
+@@ -972,7 +987,7 @@ udpif_revalidator(void *arg)
+             udpif->reval_exit = latch_is_set(&udpif->exit_latch);
+ 
+             start_time = time_msec();
+-            if (!udpif->reval_exit) {
++            if (!udpif->reval_exit && !udpif->pause) {
+                 bool terse_dump;
+ 
+                 terse_dump = udpif_use_ufid(udpif);
+@@ -981,10 +996,15 @@ udpif_revalidator(void *arg)
+             }
+         }
+ 
+-        /* Wait for the leader to start the flow dump. */
++        /* Wait for the leader to reach this point. */
+         ovs_barrier_block(&udpif->reval_barrier);
+         if (udpif->pause) {
+             revalidator_pause(revalidator);
++            if (!udpif->reval_exit) {
++                /* The main thread resumed all validators, but the leader
++                 * didn't start the dump, go to next iteration. */
++                continue;
++            }
+         }
+ 
+         if (udpif->reval_exit) {
+@@ -1766,6 +1786,7 @@ ukey_create__(const struct nlattr *key, size_t key_len,
      ukey->created = ukey->flow_time = time_msec();
      memset(&ukey->stats, 0, sizeof ukey->stats);
      ukey->stats.used = used;
@@ -4423,7 +4490,7 @@ index e05ffe312..b44c72969 100644
      ukey->xcache = NULL;
  
      ukey->offloaded = false;
-@@ -2095,10 +2111,12 @@ ukey_delete(struct umap *umap, struct udpif_key *ukey)
+@@ -2095,10 +2116,12 @@ ukey_delete(struct umap *umap, struct udpif_key *ukey)
  }
  
  static bool
@@ -4438,7 +4505,7 @@ index e05ffe312..b44c72969 100644
  
      if (!used) {
          /* Always revalidate the first time a flow is dumped. */
-@@ -2125,8 +2143,12 @@ should_revalidate(const struct udpif *udpif, uint64_t packets,
+@@ -2125,8 +2148,12 @@ should_revalidate(const struct udpif *udpif, uint64_t packets,
      duration = now - used;
      metric = duration / packets;
  
@@ -4453,7 +4520,7 @@ index e05ffe312..b44c72969 100644
          return true;
      }
      return false;
-@@ -2302,6 +2324,27 @@ exit:
+@@ -2302,6 +2329,27 @@ exit:
      return result;
  }
  
@@ -4481,7 +4548,7 @@ index e05ffe312..b44c72969 100644
  /* Verifies that the datapath actions of 'ukey' are still correct, and pushes
   * 'stats' for it.
   *
-@@ -2324,7 +2367,7 @@ static enum reval_result
+@@ -2324,7 +2372,7 @@ static enum reval_result
  revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
                  const struct dpif_flow_stats *stats,
                  struct ofpbuf *odp_actions, uint64_t reval_seq,
@@ -4490,7 +4557,7 @@ index e05ffe312..b44c72969 100644
      OVS_REQUIRES(ukey->mutex)
  {
      bool need_revalidate = ukey->reval_seq != reval_seq;
-@@ -2335,15 +2378,19 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
+@@ -2335,15 +2383,19 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
  
      push.used = stats->used;
      push.tcp_flags = stats->tcp_flags;
@@ -4517,7 +4584,7 @@ index e05ffe312..b44c72969 100644
              if (!ukey->xcache) {
                  ukey->xcache = xlate_cache_new();
              } else {
-@@ -2359,7 +2406,7 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
+@@ -2359,7 +2411,7 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
  
      /* Stats for deleted flows will be attributed upon flow deletion. Skip. */
      if (result != UKEY_DELETE) {
@@ -4526,7 +4593,7 @@ index e05ffe312..b44c72969 100644
          ukey->stats = *stats;
          ukey->reval_seq = reval_seq;
      }
-@@ -2455,6 +2502,15 @@ push_dp_ops(struct udpif *udpif, struct ukey_op *ops, size_t n_ops)
+@@ -2455,6 +2507,15 @@ push_dp_ops(struct udpif *udpif, struct ukey_op *ops, size_t n_ops)
              push->tcp_flags = stats->tcp_flags | op->ukey->stats.tcp_flags;
              push->n_packets = stats->n_packets - op->ukey->stats.n_packets;
              push->n_bytes = stats->n_bytes - op->ukey->stats.n_bytes;
@@ -4542,7 +4609,7 @@ index e05ffe312..b44c72969 100644
              ovs_mutex_unlock(&op->ukey->mutex);
          } else {
              push = stats;
-@@ -2759,6 +2815,22 @@ revalidate(struct revalidator *revalidator)
+@@ -2759,6 +2820,22 @@ revalidate(struct revalidator *revalidator)
                  continue;
              }
  
@@ -4565,7 +4632,7 @@ index e05ffe312..b44c72969 100644
              already_dumped = ukey->dump_seq == dump_seq;
              if (already_dumped) {
                  /* The flow has already been handled during this flow dump
-@@ -2790,8 +2862,7 @@ revalidate(struct revalidator *revalidator)
+@@ -2790,8 +2867,7 @@ revalidate(struct revalidator *revalidator)
                  result = UKEY_DELETE;
              } else {
                  result = revalidate_ukey(udpif, ukey, &stats, &odp_actions,
@@ -4575,7 +4642,7 @@ index e05ffe312..b44c72969 100644
              }
              ukey->dump_seq = dump_seq;
  
-@@ -2876,7 +2947,7 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge)
+@@ -2876,7 +2952,7 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge)
                      COVERAGE_INC(revalidate_missed_dp_flow);
                      memcpy(&stats, &ukey->stats, sizeof stats);
                      result = revalidate_ukey(udpif, ukey, &stats, &odp_actions,
@@ -4584,6 +4651,26 @@ index e05ffe312..b44c72969 100644
                  }
                  if (result != UKEY_KEEP) {
                      /* Clears 'recircs' if filled by revalidate_ukey(). */
+@@ -3111,11 +3187,19 @@ upcall_unixctl_purge(struct unixctl_conn *conn, int argc OVS_UNUSED,
+     struct udpif *udpif;
+ 
+     LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
++        bool wake_up = false;
+         int n;
+ 
++        if (!latch_is_set(&udpif->pause_latch)) {
++            udpif_pause_revalidators(udpif);
++            wake_up = true;
++        }
+         for (n = 0; n < udpif->n_revalidators; n++) {
+             revalidator_purge(&udpif->revalidators[n]);
+         }
++        if (wake_up) {
++            udpif_resume_revalidators(udpif);
++        }
+     }
+     unixctl_command_reply(conn, "");
+ }
 diff --git a/ofproto/ofproto-dpif-xlate-cache.c b/ofproto/ofproto-dpif-xlate-cache.c
 index 9224ee2e6..2e1fcb3a6 100644
 --- a/ofproto/ofproto-dpif-xlate-cache.c
@@ -7872,10 +7959,10 @@ index c37852b21..81123f730 100644
  ADD_NAMESPACES(at_ns0, at_ns1)
  
 diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
-index d36da0580..8dd3bdf88 100644
+index d36da0580..1d02ce6c0 100644
 --- a/tests/system-offloads-traffic.at
 +++ b/tests/system-offloads-traffic.at
-@@ -742,3 +742,118 @@ recirc_id(<recirc>),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:29, bytes
+@@ -742,3 +742,116 @@ recirc_id(<recirc>),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:29, bytes
  
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
@@ -7988,8 +8075,6 @@ index d36da0580..8dd3bdf88 100644
 +
 +OVS_TRAFFIC_VSWITCHD_STOP(["/could not open network device ovs-p0/d
 +/on nonexistent port/d
-+/failed to flow_get/d
-+/Failed to acquire udpif_key/d
 +/No such device/d
 +/failed to offload flow/d
 +"])
diff --git a/SPECS/openvswitch3.1.spec b/SPECS/openvswitch3.1.spec
index f552ba6..672225d 100644
--- a/SPECS/openvswitch3.1.spec
+++ b/SPECS/openvswitch3.1.spec
@@ -57,7 +57,7 @@ Summary: Open vSwitch
 Group: System Environment/Daemons daemon/database/utilities
 URL: http://www.openvswitch.org/
 Version: 3.1.0
-Release: 60%{?dist}
+Release: 61%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -754,6 +754,13 @@ exit 0
 %endif
 
 %changelog
+* Fri Oct 20 2023 Open vSwitch CI <ovs-ci@redhat.com> - 3.1.0-61
+- Merging upstream branch-3.1 [RH git: e6d98d829e]
+    Commit list:
+    32ca3bd2c2 tc: Improve logging of mismatched actions.
+    f330d7a311 ofproto-dpif-upcall: Pause revalidators when purging.
+
+
 * Wed Oct 18 2023 Open vSwitch CI <ovs-ci@redhat.com> - 3.1.0-60
 - Merging upstream branch-3.1 [RH git: eca528248c]
     Commit list: