diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch
index 9d2ccdc..b95502d 100644
--- a/SOURCES/openvswitch-2.17.0.patch
+++ b/SOURCES/openvswitch-2.17.0.patch
@@ -60765,7 +60765,7 @@ index 114aff8ea3..0fc6d2ea60 100644
      enum xc_type type;
      union {
 diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index 578cbfe581..4b4d5c7a76 100644
+index 578cbfe581..2b1611c912 100644
 --- a/ofproto/ofproto-dpif-xlate.c
 +++ b/ofproto/ofproto-dpif-xlate.c
 @@ -66,6 +66,7 @@
@@ -60776,7 +60776,92 @@ index 578cbfe581..4b4d5c7a76 100644
  
  COVERAGE_DEFINE(xlate_actions);
  COVERAGE_DEFINE(xlate_actions_oversize);
-@@ -865,7 +866,7 @@ xlate_xbridge_init(struct xlate_cfg *xcfg, struct xbridge *xbridge)
+@@ -500,6 +501,84 @@ ctx_cancel_freeze(struct xlate_ctx *ctx)
+ 
+ static void finish_freezing(struct xlate_ctx *ctx);
+ 
++/* These functions and structure are used to save stack space in actions that
++ * need to retain a large amount of xlate_ctx state. */
++struct xretained_state {
++    union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
++    uint64_t actset_stub[1024 / 8];
++    struct ofpbuf old_stack;
++    struct ofpbuf old_action_set;
++    struct flow old_flow;
++    struct flow old_base;
++    struct flow_tnl flow_tnl_mask;
++};
++
++/* The return of this function must be freed by
++ * xretain_state_restore_and_free(). */
++static struct xretained_state *
++xretain_state_save(struct xlate_ctx *ctx)
++{
++    struct xretained_state *retained = xmalloc(sizeof *retained);
++
++    retained->old_flow = ctx->xin->flow;
++    retained->old_stack = ctx->stack;
++    retained->old_action_set = ctx->action_set;
++    ofpbuf_use_stub(&ctx->stack, retained->new_stack,
++                    sizeof retained->new_stack);
++    ofpbuf_use_stub(&ctx->action_set, retained->actset_stub,
++                sizeof retained->actset_stub);
++
++    return retained;
++}
++
++static void
++xretain_tunnel_mask_save(const struct xlate_ctx *ctx,
++                         struct xretained_state *retained)
++{
++    retained->flow_tnl_mask = ctx->wc->masks.tunnel;
++}
++
++static void
++xretain_base_flow_save(const struct xlate_ctx *ctx,
++                       struct xretained_state *retained)
++{
++    retained->old_base = ctx->base_flow;
++}
++
++static void
++xretain_base_flow_restore(struct xlate_ctx *ctx,
++                          const struct xretained_state *retained)
++{
++    ctx->base_flow = retained->old_base;
++}
++
++static void
++xretain_flow_restore(struct xlate_ctx *ctx,
++                     const struct xretained_state *retained)
++{
++    ctx->xin->flow = retained->old_flow;
++}
++
++static void
++xretain_tunnel_mask_restore(struct xlate_ctx *ctx,
++                            const struct xretained_state *retained)
++{
++    ctx->wc->masks.tunnel = retained->flow_tnl_mask;
++}
++
++static void
++xretain_state_restore_and_free(struct xlate_ctx *ctx,
++                               struct xretained_state *retained)
++{
++    ctx->xin->flow = retained->old_flow;
++    ofpbuf_uninit(&ctx->action_set);
++    ctx->action_set = retained->old_action_set;
++    ofpbuf_uninit(&ctx->stack);
++    ctx->stack = retained->old_stack;
++
++    free(retained);
++}
++
+ /* A controller may use OFPP_NONE as the ingress port to indicate that
+  * it did not arrive on a "real" port.  'ofpp_none_bundle' exists for
+  * when an input bundle is needed for validation (e.g., mirroring or
+@@ -865,7 +944,7 @@ xlate_xbridge_init(struct xlate_cfg *xcfg, struct xbridge *xbridge)
      ovs_list_init(&xbridge->xbundles);
      hmap_init(&xbridge->xports);
      hmap_insert(&xcfg->xbridges, &xbridge->hmap_node,
@@ -60785,7 +60870,7 @@ index 578cbfe581..4b4d5c7a76 100644
  }
  
  static void
-@@ -1017,7 +1018,10 @@ xlate_xbundle_set(struct xbundle *xbundle,
+@@ -1017,7 +1096,10 @@ xlate_xbundle_set(struct xbundle *xbundle,
      xbundle->qinq_ethtype = qinq_ethtype;
      xbundle->vlan = vlan;
      xbundle->trunks = trunks;
@@ -60797,7 +60882,7 @@ index 578cbfe581..4b4d5c7a76 100644
      xbundle->use_priority_tags = use_priority_tags;
      xbundle->floodable = floodable;
      xbundle->protected = protected;
-@@ -1222,13 +1226,13 @@ xlate_txn_start(void)
+@@ -1222,13 +1304,13 @@ xlate_txn_start(void)
  static void
  xlate_xcfg_free(struct xlate_cfg *xcfg)
  {
@@ -60813,7 +60898,7 @@ index 578cbfe581..4b4d5c7a76 100644
          xlate_xbridge_remove(xcfg, xbridge);
      }
  
-@@ -1282,18 +1286,18 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
+@@ -1282,18 +1364,18 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
  static void
  xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
  {
@@ -60836,7 +60921,7 @@ index 578cbfe581..4b4d5c7a76 100644
          xlate_xbundle_remove(xcfg, xbundle);
      }
  
-@@ -1369,6 +1373,7 @@ xlate_xbundle_remove(struct xlate_cfg *xcfg, struct xbundle *xbundle)
+@@ -1369,6 +1451,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);
@@ -60844,7 +60929,7 @@ index 578cbfe581..4b4d5c7a76 100644
      free(xbundle->name);
      free(xbundle);
  }
-@@ -1515,7 +1520,7 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer,
+@@ -1515,7 +1598,7 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer,
          if (OVS_UNLIKELY(!recirc_id_node)) {
              if (errorp) {
                  *errorp = xasprintf("no recirculation data for recirc_id "
@@ -60853,7 +60938,7 @@ index 578cbfe581..4b4d5c7a76 100644
              }
              return NULL;
          }
-@@ -1556,8 +1561,8 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer,
+@@ -1556,8 +1639,8 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer,
          if (errorp) {
              *errorp = (tnl_port_should_receive(flow)
                         ? xstrdup("no OpenFlow tunnel port for this packet")
@@ -60864,7 +60949,7 @@ index 578cbfe581..4b4d5c7a76 100644
          }
          return NULL;
      }
-@@ -1639,7 +1644,7 @@ xbridge_lookup(struct xlate_cfg *xcfg, const struct ofproto_dpif *ofproto)
+@@ -1639,7 +1722,7 @@ xbridge_lookup(struct xlate_cfg *xcfg, const struct ofproto_dpif *ofproto)
  
      xbridges = &xcfg->xbridges;
  
@@ -60873,7 +60958,7 @@ index 578cbfe581..4b4d5c7a76 100644
                               xbridges) {
          if (xbridge->ofproto == ofproto) {
              return xbridge;
-@@ -1661,6 +1666,23 @@ xbridge_lookup_by_uuid(struct xlate_cfg *xcfg, const struct uuid *uuid)
+@@ -1661,6 +1744,23 @@ xbridge_lookup_by_uuid(struct xlate_cfg *xcfg, const struct uuid *uuid)
      return NULL;
  }
  
@@ -60897,7 +60982,7 @@ index 578cbfe581..4b4d5c7a76 100644
  static struct xbundle *
  xbundle_lookup(struct xlate_cfg *xcfg, const struct ofbundle *ofbundle)
  {
-@@ -1894,8 +1916,8 @@ group_is_alive(const struct xlate_ctx *ctx, uint32_t group_id, int depth)
+@@ -1894,8 +1994,8 @@ group_is_alive(const struct xlate_ctx *ctx, uint32_t group_id, int depth)
  #define MAX_LIVENESS_RECURSION 128 /* Arbitrary limit */
  
  static bool
@@ -60908,7 +60993,7 @@ index 578cbfe581..4b4d5c7a76 100644
  {
      if (depth >= MAX_LIVENESS_RECURSION) {
          xlate_report_error(ctx, "bucket chaining exceeded %d links",
-@@ -1903,6 +1925,12 @@ bucket_is_alive(const struct xlate_ctx *ctx,
+@@ -1903,6 +2003,12 @@ bucket_is_alive(const struct xlate_ctx *ctx,
          return false;
      }
  
@@ -60921,7 +61006,7 @@ index 578cbfe581..4b4d5c7a76 100644
      return (!ofputil_bucket_has_liveness(bucket)
              || (bucket->watch_port != OFPP_ANY
                 && bucket->watch_port != OFPP_CONTROLLER
-@@ -1943,7 +1971,7 @@ group_first_live_bucket(const struct xlate_ctx *ctx,
+@@ -1943,7 +2049,7 @@ group_first_live_bucket(const struct xlate_ctx *ctx,
  {
      struct ofputil_bucket *bucket;
      LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
@@ -60930,7 +61015,7 @@ index 578cbfe581..4b4d5c7a76 100644
              return bucket;
          }
          xlate_report_bucket_not_live(ctx, bucket);
-@@ -1962,7 +1990,7 @@ group_best_live_bucket(const struct xlate_ctx *ctx,
+@@ -1962,7 +2068,7 @@ group_best_live_bucket(const struct xlate_ctx *ctx,
  
      struct ofputil_bucket *bucket;
      LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
@@ -60939,7 +61024,7 @@ index 578cbfe581..4b4d5c7a76 100644
              uint32_t score =
                  (hash_int(bucket->bucket_id, basis) & 0xffff) * bucket->weight;
              if (score >= best_score) {
-@@ -2125,9 +2153,14 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
+@@ -2125,9 +2231,14 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
          int snaplen;
  
          /* Get the details of the mirror represented by the rightmost 1-bit. */
@@ -60957,7 +61042,7 @@ index 578cbfe581..4b4d5c7a76 100644
  
  
          /* If this mirror selects on the basis of VLAN, and it does not select
-@@ -2444,9 +2477,18 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
+@@ -2444,9 +2555,18 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
              /* In case recirculation is not actually in use, 'xr.recirc_id'
               * will be set to '0', since a valid 'recirc_id' can
               * not be zero.  */
@@ -60979,7 +61064,7 @@ index 578cbfe581..4b4d5c7a76 100644
              if (xr.recirc_id) {
                  /* Use recirculation instead of output. */
                  use_recirc = true;
-@@ -3015,7 +3057,7 @@ xlate_normal(struct xlate_ctx *ctx)
+@@ -3015,7 +3135,7 @@ xlate_normal(struct xlate_ctx *ctx)
      bool is_grat_arp = is_gratuitous_arp(flow, wc);
      if (ctx->xin->allow_side_effects
          && flow->packet_type == htonl(PT_ETH)
@@ -60988,7 +61073,7 @@ index 578cbfe581..4b4d5c7a76 100644
      ) {
          update_learning_table(ctx, in_xbundle, flow->dl_src, vlan,
                                is_grat_arp);
-@@ -3024,12 +3066,14 @@ xlate_normal(struct xlate_ctx *ctx)
+@@ -3024,12 +3144,14 @@ xlate_normal(struct xlate_ctx *ctx)
          struct xc_entry *entry;
  
          /* Save just enough info to update mac learning table later. */
@@ -61009,7 +61094,7 @@ index 578cbfe581..4b4d5c7a76 100644
      }
  
      /* Determine output bundle. */
-@@ -3048,7 +3092,6 @@ xlate_normal(struct xlate_ctx *ctx)
+@@ -3048,7 +3170,6 @@ xlate_normal(struct xlate_ctx *ctx)
               */
              ctx->xout->slow |= SLOW_ACTION;
  
@@ -61017,7 +61102,7 @@ index 578cbfe581..4b4d5c7a76 100644
              if (mcast_snooping_is_membership(flow->tp_src) ||
                  mcast_snooping_is_query(flow->tp_src)) {
                  if (ctx->xin->allow_side_effects && ctx->xin->packet) {
-@@ -3523,6 +3566,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow,
+@@ -3523,6 +3644,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow,
      dst_flow->dl_dst = dmac;
      dst_flow->dl_src = smac;
  
@@ -61027,7 +61112,7 @@ index 578cbfe581..4b4d5c7a76 100644
      dst_flow->packet_type = htonl(PT_ETH);
      dst_flow->nw_dst = src_flow->tunnel.ip_dst;
      dst_flow->nw_src = src_flow->tunnel.ip_src;
-@@ -3654,14 +3700,27 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct xport *xport,
+@@ -3654,14 +3778,27 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct xport *xport,
  
      err = tnl_neigh_lookup(out_dev->xbridge->name, &d_ip6, &dmac);
      if (err) {
@@ -61057,7 +61142,76 @@ index 578cbfe581..4b4d5c7a76 100644
          }
          return err;
      }
-@@ -4099,6 +4158,16 @@ xport_has_ip(const struct xport *xport)
+@@ -3827,20 +3964,17 @@ static void
+ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
+                   struct xport *out_dev, bool is_last_action)
+ {
++    bool old_was_mpls = ctx->was_mpls;
+     struct flow *flow = &ctx->xin->flow;
+-    struct flow old_flow = ctx->xin->flow;
+-    struct flow_tnl old_flow_tnl_wc = ctx->wc->masks.tunnel;
+     bool old_conntrack = ctx->conntracked;
+-    bool old_was_mpls = ctx->was_mpls;
+-    ovs_version_t old_version = ctx->xin->tables_version;
+-    struct ofpbuf old_stack = ctx->stack;
+-    uint8_t new_stack[1024];
+-    struct ofpbuf old_action_set = ctx->action_set;
++    struct xretained_state *retained_state;
+     struct ovs_list *old_trace = ctx->xin->trace;
+-    uint64_t actset_stub[1024 / 8];
++    ovs_version_t old_version = ctx->xin->tables_version;
++
++    retained_state = xretain_state_save(ctx);
++
++    xretain_tunnel_mask_save(ctx, retained_state);
+ 
+-    ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
+-    ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
+     flow->in_port.ofp_port = out_dev->ofp_port;
+     flow->metadata = htonll(0);
+     memset(&flow->tunnel, 0, sizeof flow->tunnel);
+@@ -3879,14 +4013,15 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
+         } else {
+             /* Forwarding is disabled by STP and RSTP.  Let OFPP_NORMAL and
+              * the learning action look at the packet, then drop it. */
+-            struct flow old_base_flow = ctx->base_flow;
+             size_t old_size = ctx->odp_actions->size;
++
++            xretain_base_flow_save(ctx, retained_state);
+             mirror_mask_t old_mirrors2 = ctx->mirrors;
+ 
+             xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true,
+                                false, is_last_action, clone_xlate_actions);
+             ctx->mirrors = old_mirrors2;
+-            ctx->base_flow = old_base_flow;
++            xretain_base_flow_restore(ctx, retained_state);
+             ctx->odp_actions->size = old_size;
+ 
+             /* Undo changes that may have been done for freezing. */
+@@ -3898,18 +4033,15 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
+     if (independent_mirrors) {
+         ctx->mirrors = old_mirrors;
+     }
+-    ctx->xin->flow = old_flow;
+     ctx->xbridge = in_dev->xbridge;
+-    ofpbuf_uninit(&ctx->action_set);
+-    ctx->action_set = old_action_set;
+-    ofpbuf_uninit(&ctx->stack);
+-    ctx->stack = old_stack;
+ 
+     /* Restore calling bridge's lookup version. */
+     ctx->xin->tables_version = old_version;
+ 
+-    /* Restore to calling bridge tunneling information */
+-    ctx->wc->masks.tunnel = old_flow_tnl_wc;
++    /* Restore to calling bridge tunneling information; the ctx flow, actions,
++     * and stack. And free the retained state. */
++    xretain_tunnel_mask_restore(ctx, retained_state);
++    xretain_state_restore_and_free(ctx, retained_state);
+ 
+     /* The out bridge popping MPLS should have no effect on the original
+      * bridge. */
+@@ -4099,6 +4231,16 @@ xport_has_ip(const struct xport *xport)
      return n_in6 ? true : false;
  }
  
@@ -61074,7 +61228,7 @@ index 578cbfe581..4b4d5c7a76 100644
  static bool
  terminate_native_tunnel(struct xlate_ctx *ctx, const struct xport *xport,
                          struct flow *flow, struct flow_wildcards *wc,
-@@ -4119,9 +4188,7 @@ terminate_native_tunnel(struct xlate_ctx *ctx, const struct xport *xport,
+@@ -4119,9 +4261,7 @@ terminate_native_tunnel(struct xlate_ctx *ctx, const struct xport *xport,
          /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
           * do tunnel neighbor snooping. */
          if (*tnl_port == ODPP_NONE &&
@@ -61085,7 +61239,24 @@ index 578cbfe581..4b4d5c7a76 100644
              tnl_neigh_snoop(flow, wc, ctx->xbridge->name,
                              ctx->xin->allow_side_effects);
          } else if (*tnl_port != ODPP_NONE &&
-@@ -4176,6 +4243,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+@@ -4151,7 +4291,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+     const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
+     struct flow_wildcards *wc = ctx->wc;
+     struct flow *flow = &ctx->xin->flow;
+-    struct flow_tnl flow_tnl;
++    struct flow_tnl *flow_tnl = NULL;
+     union flow_vlan_hdr flow_vlans[FLOW_MAX_VLAN_HEADERS];
+     uint8_t flow_nw_tos;
+     odp_port_t out_port, odp_port, odp_tnl_port;
+@@ -4165,7 +4305,6 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+     /* If 'struct flow' gets additional metadata, we'll need to zero it out
+      * before traversing a patch port. */
+     BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
+-    memset(&flow_tnl, 0, sizeof flow_tnl);
+ 
+     if (!check_output_prerequisites(ctx, xport, flow, check_stp)) {
+         return;
+@@ -4176,6 +4315,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
          if (xport->pt_mode == NETDEV_PT_LEGACY_L3) {
              flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
                                                 ntohs(flow->dl_type));
@@ -61096,7 +61267,52 @@ index 578cbfe581..4b4d5c7a76 100644
          }
      }
  
-@@ -4678,7 +4749,7 @@ pick_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
+@@ -4205,7 +4348,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+           * the Logical (tunnel) Port are not visible for any further
+           * matches, while explicit set actions on tunnel metadata are.
+           */
+-        flow_tnl = flow->tunnel;
++        flow_tnl = xmemdup(&flow->tunnel, sizeof *flow_tnl);
+         odp_port = tnl_port_send(xport->ofport, flow, ctx->wc);
+         if (odp_port == ODPP_NONE) {
+             xlate_report(ctx, OFT_WARN, "Tunneling decided against output");
+@@ -4236,7 +4379,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+             tnl_type = tnl_port_get_type(xport->ofport);
+             commit_odp_tunnel_action(flow, &ctx->base_flow,
+                                      ctx->odp_actions, tnl_type);
+-            flow->tunnel = flow_tnl; /* Restore tunnel metadata */
++            flow->tunnel = *flow_tnl; /* Restore tunnel metadata. */
+         }
+     } else {
+         odp_port = xport->odp_port;
+@@ -4280,7 +4423,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+             /* Output to native tunnel port. */
+             native_tunnel_output(ctx, xport, flow, odp_port, truncate,
+                                  is_last_action);
+-            flow->tunnel = flow_tnl; /* Restore tunnel metadata */
++            ovs_assert(flow_tnl);
++            flow->tunnel = *flow_tnl; /* Restore tunnel metadata. */
+ 
+         } else if (terminate_native_tunnel(ctx, xport, flow, wc,
+                                            &odp_tnl_port)) {
+@@ -4323,7 +4467,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+                                          xport->xbundle));
+     }
+ 
+- out:
++out:
+     /* Restore flow */
+     memcpy(flow->vlans, flow_vlans, sizeof flow->vlans);
+     flow->nw_tos = flow_nw_tos;
+@@ -4331,6 +4475,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+     flow->dl_src = flow_dl_src;
+     flow->packet_type = flow_packet_type;
+     flow->dl_type = flow_dl_type;
++    free(flow_tnl);
+ }
+ 
+ static void
+@@ -4678,7 +4823,7 @@ pick_dp_hash_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
          for (int i = 0; i <= hash_mask; i++) {
              struct ofputil_bucket *b =
                      group->hash_map[(dp_hash + i) & hash_mask];
@@ -61105,7 +61321,7 @@ index 578cbfe581..4b4d5c7a76 100644
                  return b;
              }
          }
-@@ -5120,6 +5191,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
+@@ -5120,6 +5265,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
      }
  
      ctx->wc->masks.nw_ttl = 0xff;
@@ -61113,7 +61329,28 @@ index 578cbfe581..4b4d5c7a76 100644
      if (flow->nw_ttl > 1) {
          flow->nw_ttl--;
          return false;
-@@ -5622,7 +5694,8 @@ xlate_sample_action(struct xlate_ctx *ctx,
+@@ -5308,15 +5454,15 @@ xlate_output_reg_action(struct xlate_ctx *ctx,
+ {
+     uint64_t port = mf_get_subfield(&or->src, &ctx->xin->flow);
+     if (port <= UINT16_MAX) {
+-        xlate_report(ctx, OFT_DETAIL, "output port is %"PRIu64, port);
++        union mf_subvalue *value = xmalloc(sizeof *value);
+ 
+-        union mf_subvalue value;
+-
+-        memset(&value, 0xff, sizeof value);
+-        mf_write_subfield_flow(&or->src, &value, &ctx->wc->masks);
++        xlate_report(ctx, OFT_DETAIL, "output port is %"PRIu64, port);
++        memset(value, 0xff, sizeof *value);
++        mf_write_subfield_flow(&or->src, value, &ctx->wc->masks);
+         xlate_output_action(ctx, u16_to_ofp(port), or->max_len,
+                             false, is_last_action, false,
+                             group_bucket_action);
++        free(value);
+     } else {
+         xlate_report(ctx, OFT_WARN, "output port %"PRIu64" is out of range",
+                      port);
+@@ -5622,7 +5768,8 @@ xlate_sample_action(struct xlate_ctx *ctx,
  
      /* Scale the probability from 16-bit to 32-bit while representing
       * the same percentage. */
@@ -61123,7 +61360,207 @@ index 578cbfe581..4b4d5c7a76 100644
  
      /* If ofp_port in flow sample action is equel to ofp_port,
       * this sample action is a input port action. */
-@@ -6887,6 +6960,107 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx,
+@@ -5656,13 +5803,15 @@ xlate_sample_action(struct xlate_ctx *ctx,
+             struct flow *flow = &ctx->xin->flow;
+             tnl_port_send(xport->ofport, flow, ctx->wc);
+             if (!ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
+-                struct flow_tnl flow_tnl = flow->tunnel;
++                struct flow_tnl *flow_tnl;
+                 const char *tnl_type;
+ 
++                flow_tnl = xmemdup(&flow->tunnel, sizeof *flow_tnl);
+                 tnl_type = tnl_port_get_type(xport->ofport);
+                 commit_odp_tunnel_action(flow, &ctx->base_flow,
+                                          ctx->odp_actions, tnl_type);
+-                flow->tunnel = flow_tnl;
++                flow->tunnel = *flow_tnl;
++                free(flow_tnl);
+             }
+         } else {
+             xlate_report_error(ctx,
+@@ -5772,21 +5921,12 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
+                     struct xlate_ctx *ctx, bool is_last_action,
+                     bool group_bucket_action OVS_UNUSED)
+ {
+-    struct ofpbuf old_stack = ctx->stack;
+-    union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
+-    ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
+-    ofpbuf_put(&ctx->stack, old_stack.data, old_stack.size);
+-
+-    struct ofpbuf old_action_set = ctx->action_set;
+-    uint64_t actset_stub[1024 / 8];
+-    ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
+-    ofpbuf_put(&ctx->action_set, old_action_set.data, old_action_set.size);
+-
++    struct xretained_state *retained_state;
+     size_t offset, ac_offset;
+-    struct flow old_flow = ctx->xin->flow;
++
++    retained_state = xretain_state_save(ctx);
+ 
+     if (reversible_actions(actions, actions_len) || is_last_action) {
+-        old_flow = ctx->xin->flow;
+         do_xlate_actions(actions, actions_len, ctx, is_last_action, false);
+         if (!ctx->freezing) {
+             xlate_action_set(ctx);
+@@ -5801,7 +5941,8 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
+      * avoid emitting those actions twice. Once inside
+      * the clone, another time for the action after clone.  */
+     xlate_commit_actions(ctx);
+-    struct flow old_base = ctx->base_flow;
++    xretain_base_flow_save(ctx, retained_state);
++
+     bool old_was_mpls = ctx->was_mpls;
+     bool old_conntracked = ctx->conntracked;
+ 
+@@ -5858,14 +5999,10 @@ dp_clone_done:
+     ctx->was_mpls = old_was_mpls;
+ 
+     /* Restore the 'base_flow' for the next action.  */
+-    ctx->base_flow = old_base;
++    xretain_base_flow_restore(ctx, retained_state);
+ 
+ xlate_done:
+-    ofpbuf_uninit(&ctx->action_set);
+-    ctx->action_set = old_action_set;
+-    ofpbuf_uninit(&ctx->stack);
+-    ctx->stack = old_stack;
+-    ctx->xin->flow = old_flow;
++    xretain_state_restore_and_free(ctx, retained_state);
+ }
+ 
+ static void
+@@ -6241,8 +6378,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
+ {
+     uint16_t zone;
+     if (ofc->zone_src.field) {
+-        union mf_subvalue value;
+-        memset(&value, 0xff, sizeof(value));
++        union mf_subvalue *value = xmalloc(sizeof *value);
++        memset(value, 0xff, sizeof *value);
+ 
+         zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow);
+         if (ctx->xin->frozen_state) {
+@@ -6252,12 +6389,13 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
+              * which will invalidate the megaflow with old the recirc_id.
+              */
+             if (!mf_is_frozen_metadata(ofc->zone_src.field)) {
+-                mf_write_subfield_flow(&ofc->zone_src, &value,
++                mf_write_subfield_flow(&ofc->zone_src, value,
+                                        &ctx->wc->masks);
+             }
+         } else {
+-            mf_write_subfield_flow(&ofc->zone_src, &value, &ctx->wc->masks);
++            mf_write_subfield_flow(&ofc->zone_src, value, &ctx->wc->masks);
+         }
++        free(value);
+     } else {
+         zone = ofc->zone_imm;
+     }
+@@ -6347,16 +6485,16 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+                        const struct ofpact *remaining_acts,
+                        size_t remaining_acts_len)
+ {
+-    union mf_subvalue value;
+-    memset(&value, 0, sizeof value);
++    union mf_subvalue *value = xmalloc(sizeof *value);
++    memset(value, 0, sizeof *value);
+     if (!ctx->xbridge->support.check_pkt_len) {
+         uint8_t is_pkt_larger = 0;
+         if (ctx->xin->packet) {
+             is_pkt_larger =
+                 dp_packet_size(ctx->xin->packet) > check_pkt_larger->pkt_len;
+         }
+-        value.u8_val = is_pkt_larger;
+-        mf_write_subfield_flow(&check_pkt_larger->dst, &value,
++        value->u8_val = is_pkt_larger;
++        mf_write_subfield_flow(&check_pkt_larger->dst, value,
+                                &ctx->xin->flow);
+         /* If datapath doesn't support check_pkt_len action, then set the
+          * SLOW_ACTION flag. If we don't set SLOW_ACTION, we
+@@ -6366,22 +6504,17 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+          * the packet length. This results in wrong actions being applied.
+          */
+         ctx->xout->slow |= SLOW_ACTION;
++        free(value);
+         return;
+     }
+ 
+-    struct ofpbuf old_stack = ctx->stack;
+-    union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
+-    ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
+-    ofpbuf_put(&ctx->stack, old_stack.data, old_stack.size);
++    struct xretained_state *retained_state;
+ 
+-    struct ofpbuf old_action_set = ctx->action_set;
+-    uint64_t actset_stub[1024 / 8];
+-    ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
+-    ofpbuf_put(&ctx->action_set, old_action_set.data, old_action_set.size);
++    retained_state = xretain_state_save(ctx);
+ 
+-    struct flow old_flow = ctx->xin->flow;
+     xlate_commit_actions(ctx);
+-    struct flow old_base = ctx->base_flow;
++    xretain_base_flow_save(ctx, retained_state);
++
+     bool old_was_mpls = ctx->was_mpls;
+     bool old_conntracked = ctx->conntracked;
+ 
+@@ -6391,8 +6524,8 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+                    check_pkt_larger->pkt_len);
+     size_t offset_attr = nl_msg_start_nested(
+         ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
+-    value.u8_val = 1;
+-    mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow);
++    value->u8_val = 1;
++    mf_write_subfield_flow(&check_pkt_larger->dst, value, &ctx->xin->flow);
+     do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false);
+     if (!ctx->freezing) {
+         xlate_action_set(ctx);
+@@ -6402,10 +6535,10 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+     }
+     nl_msg_end_nested(ctx->odp_actions, offset_attr);
+ 
+-    ctx->base_flow = old_base;
++    xretain_base_flow_restore(ctx, retained_state);
++    xretain_flow_restore(ctx, retained_state);
+     ctx->was_mpls = old_was_mpls;
+     ctx->conntracked = old_conntracked;
+-    ctx->xin->flow = old_flow;
+ 
+     /* If the flow translation for the IF_GREATER case requires freezing,
+      * then ctx->exit would be true. Reset to false so that we can
+@@ -6416,8 +6549,8 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+ 
+     offset_attr = nl_msg_start_nested(
+         ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
+-    value.u8_val = 0;
+-    mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow);
++    value->u8_val = 0;
++    mf_write_subfield_flow(&check_pkt_larger->dst, value, &ctx->xin->flow);
+     do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false);
+     if (!ctx->freezing) {
+         xlate_action_set(ctx);
+@@ -6428,15 +6561,12 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+     nl_msg_end_nested(ctx->odp_actions, offset_attr);
+     nl_msg_end_nested(ctx->odp_actions, offset);
+ 
+-    ofpbuf_uninit(&ctx->action_set);
+-    ctx->action_set = old_action_set;
+-    ofpbuf_uninit(&ctx->stack);
+-    ctx->stack = old_stack;
+-    ctx->base_flow = old_base;
+     ctx->was_mpls = old_was_mpls;
+     ctx->conntracked = old_conntracked;
+-    ctx->xin->flow = old_flow;
+     ctx->exit = old_exit;
++    xretain_base_flow_restore(ctx, retained_state);
++    xretain_state_restore_and_free(ctx, retained_state);
++    free(value);
+ }
+ 
+ static void
+@@ -6887,6 +7017,107 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx,
                   "cookie=%#"PRIx64, a->rule_table_id, a->rule_cookie);
  }
  
@@ -61231,7 +61668,7 @@ index 578cbfe581..4b4d5c7a76 100644
  static void
  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                   struct xlate_ctx *ctx, bool is_last_action,
-@@ -6928,6 +7102,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -6928,6 +7159,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
              break;
          }
  
@@ -61240,7 +61677,7 @@ index 578cbfe581..4b4d5c7a76 100644
          if (OVS_UNLIKELY(ctx->xin->trace)) {
              struct ds s = DS_EMPTY_INITIALIZER;
              struct ofpact_format_params fp = { .s = &s };
-@@ -7027,6 +7203,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7027,6 +7260,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);
@@ -61248,7 +61685,7 @@ index 578cbfe581..4b4d5c7a76 100644
                  flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
              }
              break;
-@@ -7034,12 +7211,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7034,12 +7268,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);
@@ -61263,7 +61700,7 @@ index 578cbfe581..4b4d5c7a76 100644
                  wc->masks.nw_tos |= IP_DSCP_MASK;
                  flow->nw_tos &= ~IP_DSCP_MASK;
                  flow->nw_tos |= ofpact_get_SET_IP_DSCP(a)->dscp;
-@@ -7048,6 +7227,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7048,6 +7284,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_SET_IP_ECN:
              if (is_ip_any(flow)) {
@@ -61271,7 +61708,7 @@ index 578cbfe581..4b4d5c7a76 100644
                  wc->masks.nw_tos |= IP_ECN_MASK;
                  flow->nw_tos &= ~IP_ECN_MASK;
                  flow->nw_tos |= ofpact_get_SET_IP_ECN(a)->ecn;
-@@ -7056,6 +7236,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7056,6 +7293,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_SET_IP_TTL:
              if (is_ip_any(flow)) {
@@ -61279,7 +61716,7 @@ index 578cbfe581..4b4d5c7a76 100644
                  wc->masks.nw_ttl = 0xff;
                  flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl;
              }
-@@ -7123,6 +7304,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7123,6 +7361,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)) {
@@ -61287,7 +61724,7 @@ index 578cbfe581..4b4d5c7a76 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),
-@@ -7179,6 +7361,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7179,6 +7418,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_DEC_TTL:
              wc->masks.nw_ttl = 0xff;
@@ -61295,7 +61732,7 @@ index 578cbfe581..4b4d5c7a76 100644
              if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
                  return;
              }
-@@ -7609,6 +7792,43 @@ xlate_wc_finish(struct xlate_ctx *ctx)
+@@ -7609,6 +7849,43 @@ xlate_wc_finish(struct xlate_ctx *ctx)
              ctx->wc->masks.vlans[i].tci = 0;
          }
      }
@@ -61339,7 +61776,7 @@ index 578cbfe581..4b4d5c7a76 100644
  }
  
  /* Translates the flow, actions, or rule in 'xin' into datapath actions in
-@@ -7784,6 +8004,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
+@@ -7784,6 +8061,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
          goto exit;
      }
  
@@ -61352,7 +61789,7 @@ index 578cbfe581..4b4d5c7a76 100644
      /* Tunnel metadata in udpif format must be normalized before translation. */
      if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
          const struct tun_table *tun_tab = ofproto_get_tun_tab(
-@@ -8030,6 +8256,10 @@ exit:
+@@ -8030,6 +8313,10 @@ exit:
          if (xin->odp_actions) {
              ofpbuf_clear(xin->odp_actions);
          }
diff --git a/SPECS/openvswitch2.17.spec b/SPECS/openvswitch2.17.spec
index ba53a09..f48020b 100644
--- a/SPECS/openvswitch2.17.spec
+++ b/SPECS/openvswitch2.17.spec
@@ -57,7 +57,7 @@ Summary: Open vSwitch
 Group: System Environment/Daemons daemon/database/utilities
 URL: http://www.openvswitch.org/
 Version: 2.17.0
-Release: 99%{?dist}
+Release: 100%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -751,6 +751,12 @@ exit 0
 %endif
 
 %changelog
+* Wed Aug 09 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-100
+- Merging upstream branch-2.17 [RH git: 23753e82f9]
+    Commit list:
+    f04bfd5e47 ofproto-dpif-xlate: Reduce stack usage in recursive xlate functions. (#2104779)
+
+
 * Wed Aug 09 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-99
 - Merging upstream branch-2.17 [RH git: e11afe5dde]
     Commit list: