diff --git a/SOURCES/openvswitch-3.1.0.patch b/SOURCES/openvswitch-3.1.0.patch
index b146f89..081bd36 100644
--- a/SOURCES/openvswitch-3.1.0.patch
+++ b/SOURCES/openvswitch-3.1.0.patch
@@ -566,7 +566,7 @@ index 134496ef3..5d2635946 100644
      invalidate_cache(ctx);
  }
 diff --git a/lib/dpctl.c b/lib/dpctl.c
-index d12d9b8a5..970373389 100644
+index d12d9b8a5..bfc397329 100644
 --- a/lib/dpctl.c
 +++ b/lib/dpctl.c
 @@ -1713,10 +1713,16 @@ dpctl_flush_conntrack(int argc, const char *argv[],
@@ -597,6 +597,25 @@ index d12d9b8a5..970373389 100644
          ds_put_cstr(&ds, "invalid arguments");
          error = EINVAL;
          goto error;
+@@ -2196,7 +2202,7 @@ parse_ct_limit_zones(const char *argv, struct ovs_list *zone_limits,
+     argcopy = xstrdup(argv + 5);
+     next_zone = strtok_r(argcopy, ",", &save_ptr);
+ 
+-    do {
++    while (next_zone != NULL) {
+         if (ovs_scan(next_zone, "%"SCNu16, &zone)) {
+             ct_dpif_push_zone_limit(zone_limits, zone, 0, 0);
+         } else {
+@@ -2204,7 +2210,8 @@ parse_ct_limit_zones(const char *argv, struct ovs_list *zone_limits,
+             free(argcopy);
+             return EINVAL;
+         }
+-    } while ((next_zone = strtok_r(NULL, ",", &save_ptr)) != NULL);
++        next_zone = strtok_r(NULL, ",", &save_ptr);
++    }
+ 
+     free(argcopy);
+     return 0;
 diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
 index c9f7179c3..aed2c8fbb 100644
 --- a/lib/dpif-netdev.c
@@ -883,6 +902,269 @@ index 4c78c4816..09f6393f8 100644
          return error;
      }
  
+diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
+index 4592262bd..a5fa62487 100644
+--- a/lib/netdev-offload.c
++++ b/lib/netdev-offload.c
+@@ -485,11 +485,13 @@ netdev_set_hw_info(struct netdev *netdev, int type, int val)
+ }
+ 
+ /* Protects below port hashmaps. */
+-static struct ovs_rwlock netdev_hmap_rwlock = OVS_RWLOCK_INITIALIZER;
++static struct ovs_rwlock ifindex_to_port_rwlock = OVS_RWLOCK_INITIALIZER;
++static struct ovs_rwlock port_to_netdev_rwlock
++    OVS_ACQ_BEFORE(ifindex_to_port_rwlock) = OVS_RWLOCK_INITIALIZER;
+ 
+-static struct hmap port_to_netdev OVS_GUARDED_BY(netdev_hmap_rwlock)
++static struct hmap port_to_netdev OVS_GUARDED_BY(port_to_netdev_rwlock)
+     = HMAP_INITIALIZER(&port_to_netdev);
+-static struct hmap ifindex_to_port OVS_GUARDED_BY(netdev_hmap_rwlock)
++static struct hmap ifindex_to_port OVS_GUARDED_BY(ifindex_to_port_rwlock)
+     = HMAP_INITIALIZER(&ifindex_to_port);
+ 
+ struct port_to_netdev_data {
+@@ -506,12 +508,12 @@ struct port_to_netdev_data {
+  */
+ bool
+ netdev_any_oor(void)
+-    OVS_EXCLUDED(netdev_hmap_rwlock)
++    OVS_EXCLUDED(port_to_netdev_rwlock)
+ {
+     struct port_to_netdev_data *data;
+     bool oor = false;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
+         struct netdev *dev = data->netdev;
+ 
+@@ -520,7 +522,7 @@ netdev_any_oor(void)
+             break;
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ 
+     return oor;
+ }
+@@ -594,13 +596,13 @@ netdev_ports_flow_flush(const char *dpif_type)
+ {
+     struct port_to_netdev_data *data;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
+         if (netdev_get_dpif_type(data->netdev) == dpif_type) {
+             netdev_flow_flush(data->netdev);
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ }
+ 
+ void
+@@ -610,7 +612,7 @@ netdev_ports_traverse(const char *dpif_type,
+ {
+     struct port_to_netdev_data *data;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
+         if (netdev_get_dpif_type(data->netdev) == dpif_type) {
+             if (cb(data->netdev, data->dpif_port.port_no, aux)) {
+@@ -618,7 +620,7 @@ netdev_ports_traverse(const char *dpif_type,
+             }
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ }
+ 
+ struct netdev_flow_dump **
+@@ -629,7 +631,7 @@ netdev_ports_flow_dump_create(const char *dpif_type, int *ports, bool terse)
+     int count = 0;
+     int i = 0;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
+         if (netdev_get_dpif_type(data->netdev) == dpif_type) {
+             count++;
+@@ -648,7 +650,7 @@ netdev_ports_flow_dump_create(const char *dpif_type, int *ports, bool terse)
+             i++;
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ 
+     *ports = i;
+     return dumps;
+@@ -660,15 +662,15 @@ netdev_ports_flow_del(const char *dpif_type, const ovs_u128 *ufid,
+ {
+     struct port_to_netdev_data *data;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
+         if (netdev_get_dpif_type(data->netdev) == dpif_type
+             && !netdev_flow_del(data->netdev, ufid, stats)) {
+-            ovs_rwlock_unlock(&netdev_hmap_rwlock);
++            ovs_rwlock_unlock(&port_to_netdev_rwlock);
+             return 0;
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ 
+     return ENOENT;
+ }
+@@ -681,16 +683,16 @@ netdev_ports_flow_get(const char *dpif_type, struct match *match,
+ {
+     struct port_to_netdev_data *data;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
+         if (netdev_get_dpif_type(data->netdev) == dpif_type
+             && !netdev_flow_get(data->netdev, match, actions,
+                                 ufid, stats, attrs, buf)) {
+-            ovs_rwlock_unlock(&netdev_hmap_rwlock);
++            ovs_rwlock_unlock(&port_to_netdev_rwlock);
+             return 0;
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+     return ENOENT;
+ }
+ 
+@@ -702,7 +704,7 @@ netdev_ports_hash(odp_port_t port, const char *dpif_type)
+ 
+ static struct port_to_netdev_data *
+ netdev_ports_lookup(odp_port_t port_no, const char *dpif_type)
+-    OVS_REQ_RDLOCK(netdev_hmap_rwlock)
++    OVS_REQ_RDLOCK(port_to_netdev_rwlock)
+ {
+     struct port_to_netdev_data *data;
+ 
+@@ -726,9 +728,9 @@ netdev_ports_insert(struct netdev *netdev, struct dpif_port *dpif_port)
+ 
+     ovs_assert(dpif_type);
+ 
+-    ovs_rwlock_wrlock(&netdev_hmap_rwlock);
++    ovs_rwlock_wrlock(&port_to_netdev_rwlock);
+     if (netdev_ports_lookup(dpif_port->port_no, dpif_type)) {
+-        ovs_rwlock_unlock(&netdev_hmap_rwlock);
++        ovs_rwlock_unlock(&port_to_netdev_rwlock);
+         return EEXIST;
+     }
+ 
+@@ -738,14 +740,16 @@ netdev_ports_insert(struct netdev *netdev, struct dpif_port *dpif_port)
+ 
+     if (ifindex >= 0) {
+         data->ifindex = ifindex;
++        ovs_rwlock_wrlock(&ifindex_to_port_rwlock);
+         hmap_insert(&ifindex_to_port, &data->ifindex_node, ifindex);
++        ovs_rwlock_unlock(&ifindex_to_port_rwlock);
+     } else {
+         data->ifindex = -1;
+     }
+ 
+     hmap_insert(&port_to_netdev, &data->portno_node,
+                 netdev_ports_hash(dpif_port->port_no, dpif_type));
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ 
+     netdev_init_flow_api(netdev);
+ 
+@@ -758,12 +762,12 @@ netdev_ports_get(odp_port_t port_no, const char *dpif_type)
+     struct port_to_netdev_data *data;
+     struct netdev *ret = NULL;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     data = netdev_ports_lookup(port_no, dpif_type);
+     if (data) {
+         ret = netdev_ref(data->netdev);
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ 
+     return ret;
+ }
+@@ -774,19 +778,21 @@ netdev_ports_remove(odp_port_t port_no, const char *dpif_type)
+     struct port_to_netdev_data *data;
+     int ret = ENOENT;
+ 
+-    ovs_rwlock_wrlock(&netdev_hmap_rwlock);
++    ovs_rwlock_wrlock(&port_to_netdev_rwlock);
+     data = netdev_ports_lookup(port_no, dpif_type);
+     if (data) {
+         dpif_port_destroy(&data->dpif_port);
+         netdev_close(data->netdev); /* unref and possibly close */
+         hmap_remove(&port_to_netdev, &data->portno_node);
+         if (data->ifindex >= 0) {
++            ovs_rwlock_wrlock(&ifindex_to_port_rwlock);
+             hmap_remove(&ifindex_to_port, &data->ifindex_node);
++            ovs_rwlock_unlock(&ifindex_to_port_rwlock);
+         }
+         free(data);
+         ret = 0;
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ 
+     return ret;
+ }
+@@ -798,7 +804,7 @@ netdev_ports_get_n_flows(const char *dpif_type, odp_port_t port_no,
+     struct port_to_netdev_data *data;
+     int ret = EOPNOTSUPP;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     data = netdev_ports_lookup(port_no, dpif_type);
+     if (data) {
+         uint64_t thread_n_flows[MAX_OFFLOAD_THREAD_NB] = {0};
+@@ -812,7 +818,7 @@ netdev_ports_get_n_flows(const char *dpif_type, odp_port_t port_no,
+             }
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+     return ret;
+ }
+ 
+@@ -822,14 +828,14 @@ netdev_ifindex_to_odp_port(int ifindex)
+     struct port_to_netdev_data *data;
+     odp_port_t ret = 0;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&ifindex_to_port_rwlock);
+     HMAP_FOR_EACH_WITH_HASH (data, ifindex_node, ifindex, &ifindex_to_port) {
+         if (data->ifindex == ifindex) {
+             ret = data->dpif_port.port_no;
+             break;
+         }
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&ifindex_to_port_rwlock);
+ 
+     return ret;
+ }
+@@ -847,11 +853,11 @@ netdev_ports_flow_init(void)
+ {
+     struct port_to_netdev_data *data;
+ 
+-    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
++    ovs_rwlock_rdlock(&port_to_netdev_rwlock);
+     HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
+        netdev_init_flow_api(data->netdev);
+     }
+-    ovs_rwlock_unlock(&netdev_hmap_rwlock);
++    ovs_rwlock_unlock(&port_to_netdev_rwlock);
+ }
+ 
+ void
 diff --git a/lib/netdev-windows.c b/lib/netdev-windows.c
 index 4ad45ffa1..3fad501e3 100644
 --- a/lib/netdev-windows.c
@@ -1442,10 +1724,38 @@ index e05ffe312..89df92242 100644
                  if (result != UKEY_KEEP) {
                      /* Clears 'recircs' if filled by revalidate_ukey(). */
 diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index a9cf3cbee..cffd733c5 100644
+index a9cf3cbee..10a5844d8 100644
 --- a/ofproto/ofproto-dpif-xlate.c
 +++ b/ofproto/ofproto-dpif-xlate.c
-@@ -5211,6 +5211,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
+@@ -66,6 +66,7 @@
+ #include "tunnel.h"
+ #include "util.h"
+ #include "uuid.h"
++#include "vlan-bitmap.h"
+ 
+ COVERAGE_DEFINE(xlate_actions);
+ COVERAGE_DEFINE(xlate_actions_oversize);
+@@ -1028,7 +1029,10 @@ xlate_xbundle_set(struct xbundle *xbundle,
+     xbundle->qinq_ethtype = qinq_ethtype;
+     xbundle->vlan = vlan;
+     xbundle->trunks = trunks;
+-    xbundle->cvlans = cvlans;
++    if (!vlan_bitmap_equal(xbundle->cvlans, cvlans)) {
++        free(xbundle->cvlans);
++        xbundle->cvlans = vlan_bitmap_clone(cvlans);
++    }
+     xbundle->use_priority_tags = use_priority_tags;
+     xbundle->floodable = floodable;
+     xbundle->protected = protected;
+@@ -1380,6 +1384,7 @@ xlate_xbundle_remove(struct xlate_cfg *xcfg, struct xbundle *xbundle)
+     ovs_list_remove(&xbundle->list_node);
+     bond_unref(xbundle->bond);
+     lacp_unref(xbundle->lacp);
++    free(xbundle->cvlans);
+     free(xbundle->name);
+     free(xbundle);
+ }
+@@ -5211,6 +5216,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
      }
  
      ctx->wc->masks.nw_ttl = 0xff;
@@ -1453,7 +1763,7 @@ index a9cf3cbee..cffd733c5 100644
      if (flow->nw_ttl > 1) {
          flow->nw_ttl--;
          return false;
-@@ -7128,6 +7129,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7128,6 +7134,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
          case OFPACT_SET_IPV4_SRC:
              if (flow->dl_type == htons(ETH_TYPE_IP)) {
                  memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
@@ -1461,7 +1771,7 @@ index a9cf3cbee..cffd733c5 100644
                  flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
              }
              break;
-@@ -7135,12 +7137,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7135,12 +7142,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
          case OFPACT_SET_IPV4_DST:
              if (flow->dl_type == htons(ETH_TYPE_IP)) {
                  memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
@@ -1476,7 +1786,7 @@ index a9cf3cbee..cffd733c5 100644
                  wc->masks.nw_tos |= IP_DSCP_MASK;
                  flow->nw_tos &= ~IP_DSCP_MASK;
                  flow->nw_tos |= ofpact_get_SET_IP_DSCP(a)->dscp;
-@@ -7149,6 +7153,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7149,6 +7158,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_SET_IP_ECN:
              if (is_ip_any(flow)) {
@@ -1484,7 +1794,7 @@ index a9cf3cbee..cffd733c5 100644
                  wc->masks.nw_tos |= IP_ECN_MASK;
                  flow->nw_tos &= ~IP_ECN_MASK;
                  flow->nw_tos |= ofpact_get_SET_IP_ECN(a)->ecn;
-@@ -7157,6 +7162,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7157,6 +7167,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_SET_IP_TTL:
              if (is_ip_any(flow)) {
@@ -1492,7 +1802,7 @@ index a9cf3cbee..cffd733c5 100644
                  wc->masks.nw_ttl = 0xff;
                  flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl;
              }
-@@ -7224,6 +7230,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7224,6 +7235,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
              /* Set the field only if the packet actually has it. */
              if (mf_are_prereqs_ok(mf, flow, wc)) {
@@ -1500,7 +1810,7 @@ index a9cf3cbee..cffd733c5 100644
                  mf_mask_field_masked(mf, ofpact_set_field_mask(set_field), wc);
                  mf_set_flow_value_masked(mf, set_field->value,
                                           ofpact_set_field_mask(set_field),
-@@ -7280,6 +7287,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7280,6 +7292,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_DEC_TTL:
              wc->masks.nw_ttl = 0xff;
@@ -2182,6 +2492,31 @@ index f652b5983..de2705653 100644
  AT_BANNER([flow classifier prefix lookup])
  AT_SETUP([flow classifier - prefix lookup])
  OVS_VSWITCHD_START
+diff --git a/tests/dpctl.at b/tests/dpctl.at
+index 7454a51ec..d2f1046f8 100644
+--- a/tests/dpctl.at
++++ b/tests/dpctl.at
+@@ -135,3 +135,19 @@ AT_CHECK([ovs-appctl dpctl/dump-flows dummy@br0 | sort], [0], [dnl
+ AT_CHECK([ovs-appctl dpctl/del-dp dummy@br0])
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
++
++AT_SETUP([dpctl - ct-get-limits ct-del-limits])
++OVS_VSWITCHD_START
++AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [default limit=0
++])
++AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=], [0], [default limit=0
++])
++AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=,], [0], [default limit=0
++])
++AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=x], [2], [],
++  [ovs-vswitchd: invalid zone (Invalid argument)
++ovs-appctl: ovs-vswitchd: server returned an error
++])
++AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=])
++OVS_VSWITCHD_STOP
++AT_CLEANUP
+\ No newline at end of file
 diff --git a/tests/learning-switch.at b/tests/learning-switch.at
 new file mode 100644
 index 000000000..ac2fc1b80
diff --git a/SPECS/openvswitch3.1.spec b/SPECS/openvswitch3.1.spec
index 7ec833f..8e28423 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: 23%{?dist}
+Release: 24%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -751,6 +751,14 @@ exit 0
 %endif
 
 %changelog
+* Tue May 16 2023 Open vSwitch CI <ovs-ci@redhat.com> - 3.1.0-24
+- Merging upstream branch-3.1 [RH git: 63d9d4a1d7]
+    Commit list:
+    f4cc9ca824 dpctl: Fix dereferencing null pointer in parse_ct_limit_zones().
+    9e27e8fe81 netdev-offload: Fix deadlock/recursive use of the netdev_hmap_rwlock rwlock. (#2182541)
+    087439e416 ofproto-dpif-xlate: Fix use-after-free when xlate_actions().
+
+
 * Fri May 05 2023 Open vSwitch CI <ovs-ci@redhat.com> - 3.1.0-23
 - Merging upstream branch-3.1 [RH git: b4785c720a]
     Commit list: