diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch
index fc7cc38..8cd41b8 100644
--- a/SOURCES/openvswitch-2.17.0.patch
+++ b/SOURCES/openvswitch-2.17.0.patch
@@ -61128,9 +61128,18 @@ index 57f94df544..53b47073ad 100644
   * netdev, flow state (offloaded/kernel path), flow_pps_rate.
   */
 diff --git a/ofproto/ofproto-dpif-xlate-cache.c b/ofproto/ofproto-dpif-xlate-cache.c
-index dcc91cb380..9224ee2e6d 100644
+index dcc91cb380..2e1fcb3a6f 100644
 --- a/ofproto/ofproto-dpif-xlate-cache.c
 +++ b/ofproto/ofproto-dpif-xlate-cache.c
+@@ -125,7 +125,7 @@ xlate_push_stats_entry(struct xc_entry *entry,
+     case XC_LEARN: {
+         enum ofperr error;
+         error = ofproto_flow_mod_learn(entry->learn.ofm, true,
+-                                       entry->learn.limit, NULL);
++                                       entry->learn.limit, NULL, stats->used);
+         if (error) {
+             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+             VLOG_WARN_RL(&rl, "xcache LEARN action execution failed.");
 @@ -209,6 +209,7 @@ xlate_cache_clear_entry(struct xc_entry *entry)
  {
      switch (entry->type) {
@@ -61164,7 +61173,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..2b1611c912 100644
+index 578cbfe581..b91b5757fd 100644
 --- a/ofproto/ofproto-dpif-xlate.c
 +++ b/ofproto/ofproto-dpif-xlate.c
 @@ -66,6 +66,7 @@
@@ -61749,7 +61758,25 @@ index 578cbfe581..2b1611c912 100644
      } 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,
+@@ -5525,8 +5671,16 @@ xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn)
+         if (!error) {
+             bool success = true;
+             if (ctx->xin->allow_side_effects) {
++                long long int last_used;
++
++                if (ctx->xin->resubmit_stats) {
++                    last_used = ctx->xin->resubmit_stats->used;
++                } else {
++                    last_used = time_msec();
++                }
+                 error = ofproto_flow_mod_learn(ofm, ctx->xin->xcache != NULL,
+-                                               learn->limit, &success);
++                                               learn->limit, &success,
++                                               last_used);
+             } else if (learn->limit) {
+                 if (!ofm->temp_rule
+                     || ofm->temp_rule->state != RULE_INSERTED) {
+@@ -5622,7 +5776,8 @@ xlate_sample_action(struct xlate_ctx *ctx,
  
      /* Scale the probability from 16-bit to 32-bit while representing
       * the same percentage. */
@@ -61759,7 +61786,7 @@ index 578cbfe581..2b1611c912 100644
  
      /* If ofp_port in flow sample action is equel to ofp_port,
       * this sample action is a input port action. */
-@@ -5656,13 +5803,15 @@ xlate_sample_action(struct xlate_ctx *ctx,
+@@ -5656,13 +5811,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)) {
@@ -61777,7 +61804,7 @@ index 578cbfe581..2b1611c912 100644
              }
          } else {
              xlate_report_error(ctx,
-@@ -5772,21 +5921,12 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len,
+@@ -5772,21 +5929,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)
  {
@@ -61802,7 +61829,7 @@ index 578cbfe581..2b1611c912 100644
          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,
+@@ -5801,7 +5949,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);
@@ -61812,7 +61839,7 @@ index 578cbfe581..2b1611c912 100644
      bool old_was_mpls = ctx->was_mpls;
      bool old_conntracked = ctx->conntracked;
  
-@@ -5858,14 +5999,10 @@ dp_clone_done:
+@@ -5858,14 +6007,10 @@ dp_clone_done:
      ctx->was_mpls = old_was_mpls;
  
      /* Restore the 'base_flow' for the next action.  */
@@ -61829,7 +61856,7 @@ index 578cbfe581..2b1611c912 100644
  }
  
  static void
-@@ -6241,8 +6378,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
+@@ -6241,8 +6386,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
  {
      uint16_t zone;
      if (ofc->zone_src.field) {
@@ -61840,7 +61867,7 @@ index 578cbfe581..2b1611c912 100644
  
          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,
+@@ -6252,12 +6397,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)) {
@@ -61856,7 +61883,7 @@ index 578cbfe581..2b1611c912 100644
      } else {
          zone = ofc->zone_imm;
      }
-@@ -6347,16 +6485,16 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+@@ -6347,16 +6493,16 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
                         const struct ofpact *remaining_acts,
                         size_t remaining_acts_len)
  {
@@ -61877,7 +61904,7 @@ index 578cbfe581..2b1611c912 100644
                                 &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,
+@@ -6366,22 +6512,17 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
           * the packet length. This results in wrong actions being applied.
           */
          ctx->xout->slow |= SLOW_ACTION;
@@ -61905,7 +61932,7 @@ index 578cbfe581..2b1611c912 100644
      bool old_was_mpls = ctx->was_mpls;
      bool old_conntracked = ctx->conntracked;
  
-@@ -6391,8 +6524,8 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+@@ -6391,8 +6532,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);
@@ -61916,7 +61943,7 @@ index 578cbfe581..2b1611c912 100644
      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,
+@@ -6402,10 +6543,10 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
      }
      nl_msg_end_nested(ctx->odp_actions, offset_attr);
  
@@ -61929,7 +61956,7 @@ index 578cbfe581..2b1611c912 100644
  
      /* 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,
+@@ -6416,8 +6557,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);
@@ -61940,7 +61967,7 @@ index 578cbfe581..2b1611c912 100644
      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,
+@@ -6428,15 +6569,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);
  
@@ -61959,7 +61986,7 @@ index 578cbfe581..2b1611c912 100644
  }
  
  static void
-@@ -6887,6 +7017,107 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx,
+@@ -6887,6 +7025,107 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx,
                   "cookie=%#"PRIx64, a->rule_table_id, a->rule_cookie);
  }
  
@@ -62067,7 +62094,7 @@ index 578cbfe581..2b1611c912 100644
  static void
  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                   struct xlate_ctx *ctx, bool is_last_action,
-@@ -6928,6 +7159,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -6928,6 +7167,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
              break;
          }
  
@@ -62076,7 +62103,7 @@ index 578cbfe581..2b1611c912 100644
          if (OVS_UNLIKELY(ctx->xin->trace)) {
              struct ds s = DS_EMPTY_INITIALIZER;
              struct ofpact_format_params fp = { .s = &s };
-@@ -7027,6 +7260,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7027,6 +7268,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);
@@ -62084,7 +62111,7 @@ index 578cbfe581..2b1611c912 100644
                  flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
              }
              break;
-@@ -7034,12 +7268,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7034,12 +7276,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);
@@ -62099,7 +62126,7 @@ index 578cbfe581..2b1611c912 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 +7284,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7048,6 +7292,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_SET_IP_ECN:
              if (is_ip_any(flow)) {
@@ -62107,7 +62134,7 @@ index 578cbfe581..2b1611c912 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 +7293,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7056,6 +7301,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_SET_IP_TTL:
              if (is_ip_any(flow)) {
@@ -62115,7 +62142,7 @@ index 578cbfe581..2b1611c912 100644
                  wc->masks.nw_ttl = 0xff;
                  flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl;
              }
-@@ -7123,6 +7361,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7123,6 +7369,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)) {
@@ -62123,7 +62150,7 @@ index 578cbfe581..2b1611c912 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 +7418,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7179,6 +7426,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          case OFPACT_DEC_TTL:
              wc->masks.nw_ttl = 0xff;
@@ -62131,7 +62158,7 @@ index 578cbfe581..2b1611c912 100644
              if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
                  return;
              }
-@@ -7609,6 +7849,43 @@ xlate_wc_finish(struct xlate_ctx *ctx)
+@@ -7609,6 +7857,43 @@ xlate_wc_finish(struct xlate_ctx *ctx)
              ctx->wc->masks.vlans[i].tci = 0;
          }
      }
@@ -62175,7 +62202,7 @@ index 578cbfe581..2b1611c912 100644
  }
  
  /* Translates the flow, actions, or rule in 'xin' into datapath actions in
-@@ -7784,6 +8061,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
+@@ -7784,6 +8069,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
          goto exit;
      }
  
@@ -62188,7 +62215,7 @@ index 578cbfe581..2b1611c912 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 +8313,10 @@ exit:
+@@ -8030,6 +8321,10 @@ exit:
          if (xin->odp_actions) {
              ofpbuf_clear(xin->odp_actions);
          }
@@ -62212,7 +62239,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 8143dd965f..bd235ce2b8 100644
+index 8143dd965f..06f6d00b39 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);
@@ -62544,6 +62571,15 @@ index 8143dd965f..bd235ce2b8 100644
          }
          if (rule) {
              goto out;   /* Match. */
+@@ -4879,7 +4880,7 @@ packet_xlate(struct ofproto *ofproto_, struct ofproto_packet_out *opo)
+             if (entry->type == XC_LEARN) {
+                 struct ofproto_flow_mod *ofm = entry->learn.ofm;
+ 
+-                error = ofproto_flow_mod_learn_refresh(ofm);
++                error = ofproto_flow_mod_learn_refresh(ofm, time_msec());
+                 if (error) {
+                     goto error_out;
+                 }
 @@ -5550,9 +5551,9 @@ ct_zone_timeout_policy_sweep(struct dpif_backer *backer)
  {
      if (!ovs_list_is_empty(&backer->ct_tp_kill_list)
@@ -62598,7 +62634,7 @@ index 8143dd965f..bd235ce2b8 100644
  
  static void
 diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
-index 14b909973d..e64ca5b805 100644
+index 14b909973d..4cdae27bc1 100644
 --- a/ofproto/ofproto-provider.h
 +++ b/ofproto/ofproto-provider.h
 @@ -143,6 +143,8 @@ struct ofproto {
@@ -62622,8 +62658,22 @@ index 14b909973d..e64ca5b805 100644
  /* Number of upcall handler and revalidator threads. Only affects the
   * ofproto-dpif implementation. */
  extern uint32_t n_handlers, n_revalidators;
+@@ -2015,9 +2022,11 @@ enum ofperr ofproto_flow_mod_init_for_learn(struct ofproto *,
+                                             struct ofproto_flow_mod *)
+     OVS_EXCLUDED(ofproto_mutex);
+ enum ofperr ofproto_flow_mod_learn(struct ofproto_flow_mod *, bool keep_ref,
+-                                   unsigned limit, bool *below_limit)
++                                   unsigned limit, bool *below_limit,
++                                   long long int last_used)
+     OVS_EXCLUDED(ofproto_mutex);
+-enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm);
++enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm,
++                                           long long int last_used);
+ enum ofperr ofproto_flow_mod_learn_start(struct ofproto_flow_mod *ofm)
+     OVS_REQUIRES(ofproto_mutex);
+ void ofproto_flow_mod_learn_revert(struct ofproto_flow_mod *ofm)
 diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
-index 56aeac7209..8569ce94cb 100644
+index 56aeac7209..609627d99e 100644
 --- a/ofproto/ofproto.c
 +++ b/ofproto/ofproto.c
 @@ -310,6 +310,7 @@ unsigned ofproto_flow_limit = OFPROTO_FLOW_LIMIT_DEFAULT;
@@ -62801,7 +62851,120 @@ index 56aeac7209..8569ce94cb 100644
          VLOG_WARN_RL(&rl, "%s: failed to allocate a rule.", ofproto->name);
          return OFPERR_OFPFMFC_UNKNOWN;
      }
-@@ -6797,9 +6862,9 @@ static void
+@@ -5377,7 +5442,8 @@ ofproto_flow_mod_init_for_learn(struct ofproto *ofproto,
+ }
+ 
+ enum ofperr
+-ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm)
++ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm,
++                               long long int last_used)
+ {
+     enum ofperr error = 0;
+ 
+@@ -5398,9 +5464,37 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm)
+      * this function is executed the rule will be reinstated. */
+     if (rule->state == RULE_REMOVED) {
+         struct cls_rule cr;
++        struct oftable *table = &rule->ofproto->tables[rule->table_id];
++        ovs_version_t tables_version = rule->ofproto->tables_version;
++
++        if (!cls_rule_visible_in_version(&rule->cr, tables_version)) {
++            const struct cls_rule *curr_cls_rule;
++
++            /* Only check for matching classifier rules and their modified
++             * time, instead of also checking all rule metadata, with the goal
++             * of suppressing a learn action update that would replace a more
++             * recent rule in the classifier. */
++            curr_cls_rule = classifier_find_rule_exactly(&table->cls,
++                                                         &rule->cr,
++                                                         tables_version);
++            if (curr_cls_rule) {
++                struct rule *curr_rule = rule_from_cls_rule(curr_cls_rule);
++                long long int curr_last_used;
++
++                ovs_mutex_lock(&curr_rule->mutex);
++                curr_last_used = curr_rule->modified;
++                ovs_mutex_unlock(&curr_rule->mutex);
++
++                if (curr_last_used > last_used) {
++                    /* In the case of a newer visible rule, don't recreate the
++                     *  current rule. */
++                    return 0;
++                }
++            }
++        }
+ 
+-        cls_rule_clone(&cr, &rule->cr);
+         ovs_mutex_lock(&rule->mutex);
++        cls_rule_clone(&cr, &rule->cr);
+         error = ofproto_rule_create(rule->ofproto, &cr, rule->table_id,
+                                     rule->flow_cookie,
+                                     rule->idle_timeout,
+@@ -5411,6 +5505,7 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm)
+                                     rule->match_tlv_bitmap,
+                                     rule->ofpacts_tlv_bitmap,
+                                     &ofm->temp_rule);
++        ofm->temp_rule->modified = last_used;
+         ovs_mutex_unlock(&rule->mutex);
+         if (!error) {
+             ofproto_rule_unref(rule);   /* Release old reference. */
+@@ -5418,7 +5513,7 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm)
+     } else {
+         /* Refresh the existing rule. */
+         ovs_mutex_lock(&rule->mutex);
+-        rule->modified = time_msec();
++        rule->modified = last_used;
+         ovs_mutex_unlock(&rule->mutex);
+     }
+     return error;
+@@ -5470,10 +5565,16 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm,
+ 
+ /* Refresh 'ofm->temp_rule', for which the caller holds a reference, if already
+  * in the classifier, insert it otherwise.  If the rule has already been
+- * removed from the classifier, a new rule is created using 'ofm->temp_rule' as
+- * a template and the reference to the old 'ofm->temp_rule' is freed.  If
+- * 'keep_ref' is true, then a reference to the current rule is held, otherwise
+- * it is released and 'ofm->temp_rule' is set to NULL.
++ * removed from the classifier and replaced by another rule, the 'last_used'
++ * parameter is used to determine whether the newer rule is replaced or kept.
++ * If 'last_used' is greater than the last modified time of an identical rule
++ * in the classifier, then a new rule is created using 'ofm->temp_rule' as a
++ * template and the reference to the old 'ofm->temp_rule' is freed.  If the
++ * rule has been removed but another identical rule doesn't exist in the
++ * classifier, then it will be recreated.  If the rule hasn't been removed
++ * from the classifier, then 'last_used' is used to update the rules modified
++ * time.  If 'keep_ref' is true, then a reference to the current rule is held,
++ * otherwise it is released and 'ofm->temp_rule' is set to NULL.
+  *
+  * If 'limit' != 0, insertion will fail if there are more than 'limit' rules
+  * in the same table with the same cookie.  If insertion succeeds,
+@@ -5484,10 +5585,11 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm,
+  * during the call. */
+ enum ofperr
+ ofproto_flow_mod_learn(struct ofproto_flow_mod *ofm, bool keep_ref,
+-                       unsigned limit, bool *below_limitp)
++                       unsigned limit, bool *below_limitp,
++                       long long int last_used)
+     OVS_EXCLUDED(ofproto_mutex)
+ {
+-    enum ofperr error = ofproto_flow_mod_learn_refresh(ofm);
++    enum ofperr error = ofproto_flow_mod_learn_refresh(ofm, last_used);
+     struct rule *rule = ofm->temp_rule;
+     bool below_limit = true;
+ 
+@@ -5520,6 +5622,11 @@ ofproto_flow_mod_learn(struct ofproto_flow_mod *ofm, bool keep_ref,
+ 
+             error = ofproto_flow_mod_learn_start(ofm);
+             if (!error) {
++                /* ofproto_flow_mod_learn_start may have overwritten
++                 * modified with current time. */
++                ovs_mutex_lock(&ofm->temp_rule->mutex);
++                ofm->temp_rule->modified = last_used;
++                ovs_mutex_unlock(&ofm->temp_rule->mutex);
+                 error = ofproto_flow_mod_learn_finish(ofm, NULL);
+             }
+         } else {
+@@ -6797,9 +6904,9 @@ static void
  meter_delete_all(struct ofproto *ofproto)
      OVS_REQUIRES(ofproto_mutex)
  {
@@ -62813,7 +62976,7 @@ index 56aeac7209..8569ce94cb 100644
          hmap_remove(&ofproto->meters, &meter->node);
          meter_destroy(ofproto, meter);
      }
-@@ -7339,8 +7404,13 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm,
+@@ -7339,8 +7446,13 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm,
          return OFPERR_OFPGMFC_BAD_TYPE;
      }
  
@@ -62827,7 +62990,7 @@ index 56aeac7209..8569ce94cb 100644
          VLOG_WARN_RL(&rl, "%s: failed to allocate group", ofproto->name);
          return OFPERR_OFPGMFC_OUT_OF_GROUPS;
      }
-@@ -7377,6 +7447,7 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm,
+@@ -7377,6 +7489,7 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm,
                                                      &(*ofgroup)->props));
          ofputil_bucket_list_destroy(CONST_CAST(struct ovs_list *,
                                                 &(*ofgroup)->buckets));
@@ -62835,7 +62998,7 @@ index 56aeac7209..8569ce94cb 100644
          ofproto->ofproto_class->group_dealloc(*ofgroup);
      }
      return error;
-@@ -8902,7 +8973,7 @@ eviction_group_hash_rule(struct rule *rule)
+@@ -8902,7 +9015,7 @@ eviction_group_hash_rule(struct rule *rule)
      hash = table->eviction_group_id_basis;
      miniflow_expand(rule->cr.match.flow, &flow);
      for (sf = table->eviction_fields;
@@ -62844,7 +63007,7 @@ index 56aeac7209..8569ce94cb 100644
           sf++)
      {
          if (mf_are_prereqs_ok(sf->field, &flow, NULL)) {
-@@ -9138,8 +9209,8 @@ oftable_configure_eviction(struct oftable *table, unsigned int eviction,
+@@ -9138,8 +9251,8 @@ oftable_configure_eviction(struct oftable *table, unsigned int eviction,
  
      /* Destroy existing eviction groups, then destroy and recreate data
       * structures to recover memory. */
@@ -66644,7 +66807,7 @@ index f3e19cd83b..1d3af98dab 100644
      ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
  ], [0], [ignore])
 diff --git a/tests/learn.at b/tests/learn.at
-index 5f1d6df9de..d127fed348 100644
+index 5f1d6df9de..d0bcc83633 100644
 --- a/tests/learn.at
 +++ b/tests/learn.at
 @@ -6,7 +6,7 @@ actions=learn()
@@ -66665,6 +66828,70 @@ index 5f1d6df9de..d127fed348 100644
  OFPT_FLOW_MOD (xid=0x6): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
  OFPT_FLOW_MOD (xid=0x7): ADD actions=learn(table=1,limit=4096)
  OFPT_FLOW_MOD (xid=0x8): ADD actions=learn(table=1,limit=4096,result_dst=NXM_NX_REG0[0])
+@@ -836,3 +836,63 @@ AT_CHECK([ovs-vsctl add-br br1 -- set b br1 datapath_type=dummy])
+ 
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
++
++AT_SETUP([learning action - flapping learn rule])
++OVS_VSWITCHD_START
++add_of_ports br0 1 2 3
++
++AT_CHECK([ovs-appctl time/stop], [0], [ignore])
++AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=1,actions=resubmit(,2)']])
++AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=2,actions=resubmit(,2)']])
++AT_CHECK([[ovs-ofctl add-flow br0 'table=2,actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:OXM_OF_IN_PORT[]),output:3']])
++
++packet="eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)"
++
++dnl Run this test a few times in a loop to reduce the likelyhood that it passes by chance.
++for i in 1 2 3; do
++    AT_CHECK([ovs-appctl revalidator/pause], [0])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++
++    AT_CHECK([ovs-appctl revalidator/resume], [0])
++    AT_CHECK([ovs-appctl revalidator/wait], [0])
++
++    AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl
++ cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:1
++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3
++])
++
++    AT_CHECK([ovs-appctl revalidator/pause], [0])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++    AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0])
++    AT_CHECK([ovs-appctl time/warp 75], [0], [ignore])
++
++    AT_CHECK([ovs-appctl revalidator/resume], [0])
++    AT_CHECK([ovs-appctl revalidator/wait], [0])
++
++    AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl
++ cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:2
++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3
++])
++done
++
++dnl Wait and check for learned rule eviction due to hard timeout.
++AT_CHECK([ovs-appctl time/warp 3200], [0], [ignore])
++
++AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | grep 0x123], [0], [dnl
++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
 diff --git a/tests/learning-switch.at b/tests/learning-switch.at
 new file mode 100644
 index 0000000000..ac2fc1b801
diff --git a/SPECS/openvswitch2.17.spec b/SPECS/openvswitch2.17.spec
index b3ec986..d37d2a7 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: 106%{?dist}
+Release: 107%{?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 Sep 06 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-107
+- Merging upstream branch-2.17 [RH git: 9ed4710854]
+    Commit list:
+    a141b62c2f ofproto-dpif-xlate: Don't reinstall removed XC_LEARN rule. (#2213892)
+
+
 * Wed Aug 30 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-106
 - Merging upstream branch-2.17 [RH git: 8da11aaa9d]
     Commit list: