diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch
index 864a2a4..d744d04 100644
--- a/SOURCES/openvswitch-2.17.0.patch
+++ b/SOURCES/openvswitch-2.17.0.patch
@@ -50766,6 +50766,21 @@ index c4790ee6ba..0a89626cc3 100644
              if (!(cm >= cm_stubs && cm < &cm_stubs[n_cm_stubs])) {
                  free(cm);
              }
+diff --git a/lib/cmap.c b/lib/cmap.c
+index c9eef3f4ae..8ca893b0b2 100644
+--- a/lib/cmap.c
++++ b/lib/cmap.c
+@@ -598,7 +598,9 @@ cmap_set_bucket(struct cmap_bucket *b, int i,
+     uint32_t c;
+ 
+     atomic_read_explicit(&b->counter, &c, memory_order_acquire);
+-    atomic_store_explicit(&b->counter, c + 1, memory_order_release);
++    atomic_store_explicit(&b->counter, c + 1, memory_order_relaxed);
++    /* Need to make sure setting hash is not moved up before counter update. */
++    atomic_thread_fence(memory_order_release);
+     ovsrcu_set(&b->nodes[i].next, node); /* Also atomic. */
+     b->hashes[i] = hash;
+     atomic_store_explicit(&b->counter, c + 2, memory_order_release);
 diff --git a/lib/cmap.h b/lib/cmap.h
 index c502d23112..72e2ec5f71 100644
 --- a/lib/cmap.h
@@ -51476,7 +51491,7 @@ index 66016eb099..7425dd44e7 100644
      /* Statistics. */
      struct dp_netdev_flow_stats stats;
 diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
-index 9f35713ef5..3d9d8929f7 100644
+index 9f35713ef5..24b64b1686 100644
 --- a/lib/dpif-netdev.c
 +++ b/lib/dpif-netdev.c
 @@ -93,7 +93,8 @@ VLOG_DEFINE_THIS_MODULE(dpif_netdev);
@@ -51489,7 +51504,15 @@ index 9f35713ef5..3d9d8929f7 100644
  #define MIN_TO_MSEC                  60000
  
  #define FLOW_DUMP_MAX_BATCH 50
-@@ -1932,13 +1933,13 @@ static void
+@@ -430,7 +431,6 @@ struct dp_netdev_rxq {
+     unsigned intrvl_idx;               /* Write index for 'cycles_intrvl'. */
+     struct dp_netdev_pmd_thread *pmd;  /* pmd thread that polls this queue. */
+     bool is_vhost;                     /* Is rxq of a vhost port. */
+-    bool hw_miss_api_supported;        /* hw_miss_packet_recover() supported.*/
+ 
+     /* Counters of cycles spent successfully polling and processing pkts. */
+     atomic_ullong cycles[RXQ_N_CYCLES];
+@@ -1932,13 +1932,13 @@ static void
  dp_netdev_free(struct dp_netdev *dp)
      OVS_REQUIRES(dp_netdev_mutex)
  {
@@ -51505,7 +51528,7 @@ index 9f35713ef5..3d9d8929f7 100644
          do_del_port(dp, port);
      }
      ovs_rwlock_unlock(&dp->port_rwlock);
-@@ -3006,7 +3007,7 @@ static void
+@@ -3006,7 +3006,7 @@ static void
  queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd,
                        struct dp_netdev_flow *flow, struct match *match,
                        const struct nlattr *actions, size_t actions_len,
@@ -51514,7 +51537,7 @@ index 9f35713ef5..3d9d8929f7 100644
  {
      struct dp_offload_thread_item *item;
      struct dp_offload_flow_item *flow_offload;
-@@ -3021,7 +3022,7 @@ queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd,
+@@ -3021,7 +3021,7 @@ queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd,
      flow_offload->actions = xmalloc(actions_len);
      memcpy(flow_offload->actions, actions, actions_len);
      flow_offload->actions_len = actions_len;
@@ -51523,7 +51546,7 @@ index 9f35713ef5..3d9d8929f7 100644
  
      item->timestamp = pmd->ctx.now;
      dp_netdev_offload_flow_enqueue(item);
-@@ -4095,6 +4096,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
+@@ -4095,6 +4095,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
      flow->dead = false;
      flow->batch = NULL;
      flow->mark = INVALID_FLOW_MARK;
@@ -51531,7 +51554,7 @@ index 9f35713ef5..3d9d8929f7 100644
      *CONST_CAST(unsigned *, &flow->pmd_id) = pmd->core_id;
      *CONST_CAST(struct flow *, &flow->flow) = match->flow;
      *CONST_CAST(ovs_u128 *, &flow->ufid) = *ufid;
-@@ -4129,7 +4131,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
+@@ -4129,7 +4130,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
      }
  
      queue_netdev_flow_put(pmd, flow, match, actions, actions_len,
@@ -51540,7 +51563,7 @@ index 9f35713ef5..3d9d8929f7 100644
      log_netdev_flow_change(flow, match, NULL, actions, actions_len);
  
      return flow;
-@@ -4171,7 +4173,7 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd,
+@@ -4171,7 +4172,7 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd,
              ovsrcu_set(&netdev_flow->actions, new_actions);
  
              queue_netdev_flow_put(pmd, netdev_flow, match,
@@ -51549,7 +51572,7 @@ index 9f35713ef5..3d9d8929f7 100644
                                    DP_NETDEV_FLOW_OFFLOAD_OP_MOD);
              log_netdev_flow_change(netdev_flow, match, old_actions,
                                     put->actions, put->actions_len);
-@@ -4778,8 +4780,8 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
+@@ -4778,8 +4779,8 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
      uint32_t insert_min, cur_min;
      uint32_t tx_flush_interval, cur_tx_flush_interval;
      uint64_t rebalance_intvl;
@@ -51560,7 +51583,7 @@ index 9f35713ef5..3d9d8929f7 100644
      bool log_autolb = false;
      enum sched_assignment_type pmd_rxq_assign_type;
  
-@@ -4880,8 +4882,12 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
+@@ -4880,8 +4881,12 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
  
      struct pmd_auto_lb *pmd_alb = &dp->pmd_alb;
  
@@ -51575,7 +51598,7 @@ index 9f35713ef5..3d9d8929f7 100644
  
      /* Input is in min, convert it to msec. */
      rebalance_intvl =
-@@ -4894,21 +4900,21 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
+@@ -4894,21 +4899,21 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
          log_autolb = true;
      }
  
@@ -51603,7 +51626,7 @@ index 9f35713ef5..3d9d8929f7 100644
      if (rebalance_load > 100) {
          rebalance_load = ALB_LOAD_THRESHOLD;
      }
-@@ -4916,7 +4922,7 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
+@@ -4916,7 +4921,7 @@ dpif_netdev_set_config(struct dpif *dpif, const struct smap *other_config)
      if (rebalance_load != cur_rebalance_load) {
          atomic_store_relaxed(&pmd_alb->rebalance_load_thresh,
                               rebalance_load);
@@ -51612,7 +51635,15 @@ index 9f35713ef5..3d9d8929f7 100644
                    rebalance_load);
          log_autolb = true;
      }
-@@ -5684,23 +5690,28 @@ sched_numa_list_put_in_place(struct sched_numa_list *numa_list)
+@@ -5425,7 +5430,6 @@ port_reconfigure(struct dp_netdev_port *port)
+ 
+         port->rxqs[i].port = port;
+         port->rxqs[i].is_vhost = !strncmp(port->type, "dpdkvhost", 9);
+-        port->rxqs[i].hw_miss_api_supported = true;
+ 
+         err = netdev_rxq_open(netdev, &port->rxqs[i].rx, i);
+         if (err) {
+@@ -5684,23 +5688,28 @@ sched_numa_list_put_in_place(struct sched_numa_list *numa_list)
      }
  }
  
@@ -51646,7 +51677,7 @@ index 9f35713ef5..3d9d8929f7 100644
                      rxq->pmd->numa_id !=
                          netdev_get_numa_id(rxq->port->netdev)) {
                      return true;
-@@ -6000,10 +6011,10 @@ sched_numa_list_schedule(struct sched_numa_list *numa_list,
+@@ -6000,10 +6009,10 @@ sched_numa_list_schedule(struct sched_numa_list *numa_list,
              /* Find any numa with available PMDs. */
              for (int j = 0; j < n_numa; j++) {
                  numa = sched_numa_list_next(numa_list, last_cross_numa);
@@ -51658,7 +51689,7 @@ index 9f35713ef5..3d9d8929f7 100644
                  numa = NULL;
              }
          }
-@@ -6111,7 +6122,7 @@ sched_numa_list_variance(struct sched_numa_list *numa_list)
+@@ -6111,7 +6120,7 @@ sched_numa_list_variance(struct sched_numa_list *numa_list)
   * pmd_rebalance_dry_run() can be avoided when it is not needed.
   */
  static bool
@@ -51667,7 +51698,7 @@ index 9f35713ef5..3d9d8929f7 100644
      OVS_REQ_RDLOCK(dp->port_rwlock)
  {
      struct dp_netdev_pmd_thread *pmd;
-@@ -6342,11 +6353,11 @@ pmd_remove_stale_ports(struct dp_netdev *dp,
+@@ -6342,11 +6351,11 @@ pmd_remove_stale_ports(struct dp_netdev *dp,
      OVS_EXCLUDED(pmd->port_mutex)
      OVS_REQ_RDLOCK(dp->port_rwlock)
  {
@@ -51682,7 +51713,7 @@ index 9f35713ef5..3d9d8929f7 100644
          struct dp_netdev_port *port = poll->rxq->port;
  
          if (port->need_reconfigure
-@@ -6354,7 +6365,7 @@ pmd_remove_stale_ports(struct dp_netdev *dp,
+@@ -6354,7 +6363,7 @@ pmd_remove_stale_ports(struct dp_netdev *dp,
              dp_netdev_del_rxq_from_pmd(pmd, poll);
          }
      }
@@ -51691,7 +51722,7 @@ index 9f35713ef5..3d9d8929f7 100644
          struct dp_netdev_port *port = tx->port;
  
          if (port->need_reconfigure
-@@ -6430,8 +6441,7 @@ reconfigure_datapath(struct dp_netdev *dp)
+@@ -6430,8 +6439,7 @@ reconfigure_datapath(struct dp_netdev *dp)
      /* We only reconfigure the ports that we determined above, because they're
       * not being used by any pmd thread at the moment.  If a port fails to
       * reconfigure we remove it from the datapath. */
@@ -51701,7 +51732,7 @@ index 9f35713ef5..3d9d8929f7 100644
          int err;
  
          if (!port->need_reconfigure) {
-@@ -6487,10 +6497,10 @@ reconfigure_datapath(struct dp_netdev *dp)
+@@ -6487,10 +6495,10 @@ reconfigure_datapath(struct dp_netdev *dp)
      }
  
      CMAP_FOR_EACH (pmd, node, &dp->poll_threads) {
@@ -51714,7 +51745,7 @@ index 9f35713ef5..3d9d8929f7 100644
              if (poll->rxq->pmd != pmd) {
                  dp_netdev_del_rxq_from_pmd(pmd, poll);
  
-@@ -6682,7 +6692,7 @@ dpif_netdev_run(struct dpif *dpif)
+@@ -6682,7 +6690,7 @@ dpif_netdev_run(struct dpif *dpif)
              if (pmd_rebalance &&
                  !dp_netdev_is_reconf_required(dp) &&
                  !ports_require_restart(dp) &&
@@ -51723,7 +51754,7 @@ index 9f35713ef5..3d9d8929f7 100644
                  pmd_rebalance_dry_run(dp)) {
                  VLOG_INFO("PMD auto load balance dry run. "
                            "Requesting datapath reconfigure.");
-@@ -7364,15 +7374,15 @@ static struct dp_netdev_pmd_thread *
+@@ -7364,15 +7372,15 @@ static struct dp_netdev_pmd_thread *
  dp_netdev_get_pmd(struct dp_netdev *dp, unsigned core_id)
  {
      struct dp_netdev_pmd_thread *pmd;
@@ -51745,7 +51776,7 @@ index 9f35713ef5..3d9d8929f7 100644
  }
  
  /* Sets the 'struct dp_netdev_pmd_thread' for non-pmd threads. */
-@@ -7505,6 +7515,7 @@ dp_netdev_destroy_pmd(struct dp_netdev_pmd_thread *pmd)
+@@ -7505,6 +7513,7 @@ dp_netdev_destroy_pmd(struct dp_netdev_pmd_thread *pmd)
      seq_destroy(pmd->reload_seq);
      ovs_mutex_destroy(&pmd->port_mutex);
      ovs_mutex_destroy(&pmd->bond_mutex);
@@ -51753,6 +51784,31 @@ index 9f35713ef5..3d9d8929f7 100644
      free(pmd);
  }
  
+@@ -8020,17 +8029,15 @@ dp_netdev_hw_flow(const struct dp_netdev_pmd_thread *pmd,
+ #ifdef ALLOW_EXPERIMENTAL_API /* Packet restoration API required. */
+     /* Restore the packet if HW processing was terminated before completion. */
+     struct dp_netdev_rxq *rxq = pmd->ctx.last_rxq;
++    bool miss_api_supported;
+ 
+-    if (rxq->hw_miss_api_supported) {
++    atomic_read_relaxed(&rxq->port->netdev->hw_info.miss_api_supported,
++                        &miss_api_supported);
++    if (miss_api_supported) {
+         int err = netdev_hw_miss_packet_recover(rxq->port->netdev, packet);
+-        if (err) {
+-            if (err != EOPNOTSUPP) {
+-                COVERAGE_INC(datapath_drop_hw_miss_recover);
+-                return -1;
+-            } else {
+-                /* API unsupported by the port; avoid subsequent calls. */
+-                rxq->hw_miss_api_supported = false;
+-            }
++        if (err && err != EOPNOTSUPP) {
++            COVERAGE_INC(datapath_drop_hw_miss_recover);
++            return -1;
+         }
+     }
+ #endif
 diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
 index 71e35ccdda..484545cfb8 100644
 --- a/lib/dpif-netlink.c
@@ -52521,10 +52577,34 @@ index a024dc5e58..4bff7b017f 100644
              ovs_list_remove(&chassis->list);
              lldpd_chassis_cleanup(chassis, 1);
 diff --git a/lib/mac-learning.c b/lib/mac-learning.c
-index 3fcd7d9b77..a60794fb26 100644
+index 3fcd7d9b77..5932e2709d 100644
 --- a/lib/mac-learning.c
 +++ b/lib/mac-learning.c
-@@ -244,10 +244,10 @@ void
+@@ -176,12 +176,18 @@ get_lru(struct mac_learning *ml, struct mac_entry **e)
+     OVS_REQ_RDLOCK(ml->rwlock)
+ {
+     if (!ovs_list_is_empty(&ml->lrus)) {
+-        *e = mac_entry_from_lru_node(ml->lrus.next);
+-        return true;
+-    } else {
+-        *e = NULL;
+-        return false;
++        struct mac_entry *entry;
++
++        LIST_FOR_EACH (entry, lru_node, &ml->lrus) {
++            if (entry->expires != MAC_ENTRY_AGE_STATIC_ENTRY) {
++                *e = entry;
++                return true;
++            }
++        }
+     }
++
++    *e = NULL;
++    return false;
+ }
+ 
+ static unsigned int
+@@ -244,10 +250,10 @@ void
  mac_learning_unref(struct mac_learning *ml)
  {
      if (ml && ovs_refcount_unref(&ml->ref_cnt) == 1) {
@@ -52537,6 +52617,35 @@ index 3fcd7d9b77..a60794fb26 100644
              mac_learning_expire(ml, e);
          }
          hmap_destroy(&ml->table);
+@@ -618,25 +624,10 @@ mac_learning_expire(struct mac_learning *ml, struct mac_entry *e)
+ void
+ mac_learning_flush(struct mac_learning *ml)
+ {
+-    struct mac_entry *e, *first_static_mac = NULL;
+-
+-    while (get_lru(ml, &e) && (e != first_static_mac)) {
+-
+-        /* Static mac should not be evicted. */
+-        if (MAC_ENTRY_AGE_STATIC_ENTRY == e->expires) {
+-
+-            /* Make note of first static-mac encountered, so that this while
+-             * loop will break on visting this mac again via get_lru(). */
+-            if (!first_static_mac) {
+-                first_static_mac = e;
+-            }
++    struct mac_entry *e;
+ 
+-            /* Remove from lru head and append it to tail. */
+-            ovs_list_remove(&e->lru_node);
+-            ovs_list_push_back(&ml->lrus, &e->lru_node);
+-        } else {
+-            mac_learning_expire(ml, e);
+-        }
++    while (get_lru(ml, &e)) {
++        mac_learning_expire(ml, e);
+     }
+     hmap_shrink(&ml->table);
+ }
 diff --git a/lib/match.c b/lib/match.c
 index 2ad03e044e..0b9dc4278c 100644
 --- a/lib/match.c
@@ -52646,7 +52755,7 @@ index 482400d8d1..ca3f2431ea 100644
          count = umem_pool_count(&pool->umem_info->mpool);
          ovs_assert(count + pool->lost_in_rings <= NUM_FRAMES);
 diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
-index b6b29c75e3..e28e397d7e 100644
+index b6b29c75e3..39a1fd388d 100644
 --- a/lib/netdev-dpdk.c
 +++ b/lib/netdev-dpdk.c
 @@ -622,9 +622,9 @@ dpdk_mp_full(const struct rte_mempool *mp) OVS_REQUIRES(dpdk_mp_mutex)
@@ -52862,7 +52971,7 @@ index b6b29c75e3..e28e397d7e 100644
 +        cnt = dpdk_copy_batch_to_mbuf(netdev, batch);
 +        stats->tx_failure_drops += pkt_cnt - cnt;
 +        pkt_cnt = cnt;
-     }
++    }
 +
 +    /* Drop oversized packets. */
 +    cnt = netdev_dpdk_filter_packet_len(dev, pkts, pkt_cnt);
@@ -52874,7 +52983,7 @@ index b6b29c75e3..e28e397d7e 100644
 +        cnt = netdev_dpdk_prep_hwol_batch(dev, pkts, pkt_cnt);
 +        stats->tx_invalid_hwol_drops += pkt_cnt - cnt;
 +        pkt_cnt = cnt;
-+    }
+     }
 +
 +    /* Apply Quality of Service policy. */
 +    cnt = netdev_dpdk_qos_run(dev, pkts, pkt_cnt, true);
@@ -52998,9 +53107,9 @@ index b6b29c75e3..e28e397d7e 100644
 -        dpdk_do_tx_copy(netdev, qid, batch);
 -        dp_packet_delete_batch(batch, true);
 -    } else {
-+    dropped = batch_cnt - cnt;
-+
-+    dropped += netdev_dpdk_eth_tx_burst(dev, qid, pkts, cnt);
++    dropped = netdev_dpdk_eth_tx_burst(dev, qid, pkts, cnt);
++    stats.tx_failure_drops += dropped;
++    dropped += batch_cnt - cnt;
 +    if (OVS_UNLIKELY(dropped)) {
          struct netdev_dpdk_sw_stats *sw_stats = dev->sw_stats;
 -        int dropped;
@@ -53071,7 +53180,7 @@ index b6b29c75e3..e28e397d7e 100644
          free(queue);
      }
 diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
-index 620a451dec..1b9c2874ac 100644
+index 620a451dec..5aa8a8ca3c 100644
 --- a/lib/netdev-linux.c
 +++ b/lib/netdev-linux.c
 @@ -247,6 +247,14 @@ enum {
@@ -53110,7 +53219,26 @@ index 620a451dec..1b9c2874ac 100644
      nl_msg_end_nested(request, offset);
      nl_msg_end_nested(request, act_offset);
  }
-@@ -5331,11 +5342,11 @@ static void
+@@ -2970,12 +2981,18 @@ netdev_linux_set_qos(struct netdev *netdev_,
+         /* Delete existing qdisc. */
+         error = tc_del_qdisc(netdev_);
+         if (error) {
++            VLOG_WARN_RL(&rl, "%s: Failed to delete existing qdisc: %s",
++                         netdev_get_name(netdev_), ovs_strerror(error));
+             goto exit;
+         }
+         ovs_assert(netdev->tc == NULL);
+ 
+         /* Install new qdisc. */
+         error = new_ops->tc_install(netdev_, details);
++        if (error) {
++            VLOG_WARN_RL(&rl, "%s: Failed to install new qdisc: %s",
++                         netdev_get_name(netdev_), ovs_strerror(error));
++        }
+         ovs_assert((error == 0) == (netdev->tc != NULL));
+     }
+ 
+@@ -5331,11 +5348,11 @@ static void
  hfsc_tc_destroy(struct tc *tc)
  {
      struct hfsc *hfsc;
@@ -53124,7 +53252,24 @@ index 620a451dec..1b9c2874ac 100644
          hmap_remove(&hfsc->tc.queues, &hc->tc_queue.hmap_node);
          free(hc);
      }
-@@ -6295,7 +6306,14 @@ get_stats_via_netlink(const struct netdev *netdev_, struct netdev_stats *stats)
+@@ -5966,13 +5983,12 @@ tc_del_qdisc(struct netdev *netdev_)
+     if (!tcmsg) {
+         return ENODEV;
+     }
+-    tcmsg->tcm_handle = tc_make_handle(1, 0);
+     tcmsg->tcm_parent = TC_H_ROOT;
+ 
+     error = tc_transact(&request, NULL);
+-    if (error == EINVAL) {
+-        /* EINVAL probably means that the default qdisc was in use, in which
+-         * case we've accomplished our purpose. */
++    if (error == EINVAL || error == ENOENT) {
++        /* EINVAL or ENOENT probably means that the default qdisc was in use,
++         * in which case we've accomplished our purpose. */
+         error = 0;
+     }
+     if (!error && netdev->tc) {
+@@ -6295,7 +6311,14 @@ get_stats_via_netlink(const struct netdev *netdev_, struct netdev_stats *stats)
      if (ofpbuf_try_pull(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg))) {
          const struct nlattr *a = nl_attr_find(reply, 0, IFLA_STATS64);
          if (a && nl_attr_get_size(a) >= sizeof(struct rtnl_link_stats64)) {
@@ -53140,7 +53285,7 @@ index 620a451dec..1b9c2874ac 100644
              error = 0;
          } else {
              a = nl_attr_find(reply, 0, IFLA_STATS);
-@@ -6411,6 +6429,9 @@ netdev_linux_update_via_netlink(struct netdev_linux *netdev)
+@@ -6411,6 +6434,9 @@ netdev_linux_update_via_netlink(struct netdev_linux *netdev)
      if (netdev_linux_netnsid_is_remote(netdev)) {
          nl_msg_put_u32(&request, IFLA_IF_NETNSID, netdev->netnsid);
      }
@@ -53731,11 +53876,81 @@ index 9845e8d3fe..93321989a9 100644
          }
      }
  
+diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
+index fb108c0d50..eea8fadc0e 100644
+--- a/lib/netdev-offload.c
++++ b/lib/netdev-offload.c
+@@ -182,6 +182,7 @@ netdev_assign_flow_api(struct netdev *netdev)
+     CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) {
+         if (!rfa->flow_api->init_flow_api(netdev)) {
+             ovs_refcount_ref(&rfa->refcnt);
++            atomic_store_relaxed(&netdev->hw_info.miss_api_supported, true);
+             ovsrcu_set(&netdev->flow_api, rfa->flow_api);
+             VLOG_INFO("%s: Assigned flow API '%s'.",
+                       netdev_get_name(netdev), rfa->flow_api->type);
+@@ -190,6 +191,7 @@ netdev_assign_flow_api(struct netdev *netdev)
+         VLOG_DBG("%s: flow API '%s' is not suitable.",
+                  netdev_get_name(netdev), rfa->flow_api->type);
+     }
++    atomic_store_relaxed(&netdev->hw_info.miss_api_supported, false);
+     VLOG_INFO("%s: No suitable flow API found.", netdev_get_name(netdev));
+ 
+     return -1;
+@@ -263,12 +265,28 @@ int
+ netdev_hw_miss_packet_recover(struct netdev *netdev,
+                               struct dp_packet *packet)
+ {
+-    const struct netdev_flow_api *flow_api =
+-        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
++    const struct netdev_flow_api *flow_api;
++    bool miss_api_supported;
++    int rv;
++
++    atomic_read_relaxed(&netdev->hw_info.miss_api_supported,
++                        &miss_api_supported);
++    if (!miss_api_supported) {
++        return EOPNOTSUPP;
++    }
++
++    flow_api = ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
++    if (!flow_api || !flow_api->hw_miss_packet_recover) {
++        return EOPNOTSUPP;
++    }
++
++    rv = flow_api->hw_miss_packet_recover(netdev, packet);
++    if (rv == EOPNOTSUPP) {
++        /* API unsupported by the port; avoid subsequent calls. */
++        atomic_store_relaxed(&netdev->hw_info.miss_api_supported, false);
++    }
+ 
+-    return (flow_api && flow_api->hw_miss_packet_recover)
+-            ? flow_api->hw_miss_packet_recover(netdev, packet)
+-            : EOPNOTSUPP;
++    return rv;
+ }
+ 
+ int
 diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
-index 8237a85ddb..93eb2df48b 100644
+index 8237a85ddb..13ab06d116 100644
 --- a/lib/netdev-offload.h
 +++ b/lib/netdev-offload.h
-@@ -65,9 +65,6 @@ struct netdev_flow_dump {
+@@ -20,6 +20,7 @@
+ 
+ #include "openvswitch/netdev.h"
+ #include "openvswitch/types.h"
++#include "ovs-atomic.h"
+ #include "ovs-rcu.h"
+ #include "ovs-thread.h"
+ #include "packets.h"
+@@ -45,6 +46,7 @@ struct ovs_action_push_tnl;
+ /* Offload-capable (HW) netdev information */
+ struct netdev_hw_info {
+     bool oor;		/* Out of Offload Resources ? */
++    atomic_bool miss_api_supported;  /* hw_miss_packet_recover() supported.*/
+     int offload_count;  /* Pending (non-offloaded) flow count */
+     int pending_count;  /* Offloaded flow count */
+     OVSRCU_TYPE(void *) offload_data; /* Offload metadata. */
+@@ -65,9 +67,6 @@ struct netdev_flow_dump {
  
  /* Flow offloading. */
  struct offload_info {
@@ -53746,7 +53961,7 @@ index 8237a85ddb..93eb2df48b 100644
                                       * sync with datapath recirc ids. */
  
 diff --git a/lib/netdev.c b/lib/netdev.c
-index 8305f6c427..ce0d4117ac 100644
+index 8305f6c427..c797783782 100644
 --- a/lib/netdev.c
 +++ b/lib/netdev.c
 @@ -387,25 +387,30 @@ netdev_open(const char *name, const char *type, struct netdev **netdevp)
@@ -53798,10 +54013,27 @@ index 8305f6c427..ce0d4117ac 100644
          }
      }
  
+@@ -426,6 +431,7 @@ netdev_open(const char *name, const char *type, struct netdev **netdevp)
+                     seq_read(netdev->reconfigure_seq);
+                 ovsrcu_set(&netdev->flow_api, NULL);
+                 netdev->hw_info.oor = false;
++                atomic_init(&netdev->hw_info.miss_api_supported, false);
+                 netdev->node = shash_add(&netdev_shash, name, netdev);
+ 
+                 /* By default enable one tx and rx queue per netdev. */
 diff --git a/lib/odp-util.c b/lib/odp-util.c
-index 9a705cffa3..2d2a6893c6 100644
+index 9a705cffa3..3bbc9a1010 100644
 --- a/lib/odp-util.c
 +++ b/lib/odp-util.c
+@@ -1003,7 +1003,7 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
+             ds_put_format(ds, "helper=%s,", helper);
+         }
+         if (timeout) {
+-            ds_put_format(ds, "timeout=%s", timeout);
++            ds_put_format(ds, "timeout=%s,", timeout);
+         }
+         if (nat) {
+             format_odp_ct_nat(ds, nat);
 @@ -3429,16 +3429,16 @@ format_eth(struct ds *ds, const char *name, const struct eth_addr key,
  
  static void
@@ -55511,7 +55743,7 @@ index fcaddf10ad..71039e24f1 100644
  
  /* Attempts to guess the content type of a stream whose first few bytes were
 diff --git a/lib/tc.c b/lib/tc.c
-index adb2d3182a..276cc54b35 100644
+index adb2d3182a..900a631553 100644
 --- a/lib/tc.c
 +++ b/lib/tc.c
 @@ -395,8 +395,14 @@ static const struct nl_policy tca_flower_policy[] = {
@@ -55668,7 +55900,32 @@ index adb2d3182a..276cc54b35 100644
      size_t keys_ex_size, left;
      int type, i = 0, err;
  
-@@ -1092,7 +1112,6 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
+@@ -1068,7 +1088,7 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
+                 int diff = flower_off + (keys->off - mf);
+                 ovs_be32 *dst = (void *) (rewrite_key + diff);
+                 ovs_be32 *dst_m = (void *) (rewrite_mask + diff);
+-                ovs_be32 mask, mask_word, data_word;
++                ovs_be32 mask, mask_word, data_word, val;
+                 uint32_t zero_bits;
+ 
+                 mask_word = htonl(ntohl(keys->mask) << m->boundary_shift);
+@@ -1083,8 +1103,13 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
+                     mask &= htonl(UINT32_MAX << zero_bits);
+                 }
+ 
+-                *dst_m |= mask;
+-                *dst |= data_word & mask;
++                val = get_unaligned_be32(dst_m);
++                val |= mask;
++                put_unaligned_be32(dst_m, val);
++
++                val = get_unaligned_be32(dst);
++                val |= data_word & mask;
++                put_unaligned_be32(dst, val);
+             }
+         }
+ 
+@@ -1092,7 +1117,6 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
          i++;
      }
  
@@ -55676,7 +55933,7 @@ index adb2d3182a..276cc54b35 100644
      action->type = TC_ACT_PEDIT;
  
      return 0;
-@@ -1314,8 +1333,8 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
+@@ -1314,8 +1338,8 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
      struct nlattr *gact_attrs[ARRAY_SIZE(gact_policy)];
      const struct tc_gact *p;
      struct nlattr *gact_parms;
@@ -55686,7 +55943,7 @@ index adb2d3182a..276cc54b35 100644
  
      if (!nl_parse_nested(options, gact_policy, gact_attrs,
                           ARRAY_SIZE(gact_policy))) {
-@@ -1335,8 +1354,9 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
+@@ -1335,8 +1359,9 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
          return EINVAL;
      }
  
@@ -55698,7 +55955,7 @@ index adb2d3182a..276cc54b35 100644
  
      return 0;
  }
-@@ -1357,9 +1377,9 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
+@@ -1357,9 +1382,9 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
      struct nlattr *mirred_attrs[ARRAY_SIZE(mirred_policy)];
      const struct tc_mirred *m;
      const struct nlattr *mirred_parms;
@@ -55709,7 +55966,7 @@ index adb2d3182a..276cc54b35 100644
  
      if (!nl_parse_nested(options, mirred_policy, mirred_attrs,
                           ARRAY_SIZE(mirred_policy))) {
-@@ -1387,8 +1407,8 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
+@@ -1387,8 +1412,8 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
      action->type = TC_ACT_OUTPUT;
  
      mirred_tm = mirred_attrs[TCA_MIRRED_TM];
@@ -55720,7 +55977,7 @@ index adb2d3182a..276cc54b35 100644
  
      return 0;
  }
-@@ -1487,7 +1507,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
+@@ -1487,7 +1512,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
                  if (ipv4_max) {
                      ovs_be32 addr = nl_attr_get_be32(ipv4_max);
  
@@ -55731,7 +55988,7 @@ index adb2d3182a..276cc54b35 100644
                  }
              } else if (ipv6_min) {
                  action->ct.range.ip_family = AF_INET6;
-@@ -1496,7 +1518,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
+@@ -1496,7 +1523,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
                  if (ipv6_max) {
                      struct in6_addr addr = nl_attr_get_in6_addr(ipv6_max);
  
@@ -55742,7 +55999,7 @@ index adb2d3182a..276cc54b35 100644
                  }
              }
  
-@@ -1504,6 +1528,10 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
+@@ -1504,6 +1533,10 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
                  action->ct.range.port.min = nl_attr_get_be16(port_min);
                  if (port_max) {
                      action->ct.range.port.max = nl_attr_get_be16(port_max);
@@ -55753,7 +56010,7 @@ index adb2d3182a..276cc54b35 100644
                  }
              }
          }
-@@ -1702,6 +1730,9 @@ static const struct nl_policy stats_policy[] = {
+@@ -1702,6 +1735,9 @@ static const struct nl_policy stats_policy[] = {
      [TCA_STATS_BASIC] = { .type = NL_A_UNSPEC,
                            .min_len = sizeof(struct gnet_stats_basic),
                            .optional = false, },
@@ -55763,7 +56020,7 @@ index adb2d3182a..276cc54b35 100644
  };
  
  static int
-@@ -1714,8 +1745,9 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
+@@ -1714,8 +1750,9 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
      const char *act_kind;
      struct nlattr *action_attrs[ARRAY_SIZE(act_policy)];
      struct nlattr *stats_attrs[ARRAY_SIZE(stats_policy)];
@@ -55775,7 +56032,7 @@ index adb2d3182a..276cc54b35 100644
      int err = 0;
  
      if (!nl_parse_nested(action, act_policy, action_attrs,
-@@ -1771,10 +1803,30 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
+@@ -1771,10 +1808,30 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
          return EPROTO;
      }
  
@@ -55810,7 +56067,7 @@ index adb2d3182a..276cc54b35 100644
      }
  
      return 0;
-@@ -2399,14 +2451,14 @@ nl_msg_put_act_flags(struct ofpbuf *request) {
+@@ -2399,14 +2456,14 @@ nl_msg_put_act_flags(struct ofpbuf *request) {
   * first_word_mask/last_word_mask - the mask to use for the first/last read
   * (as we read entire words). */
  static void
@@ -55828,7 +56085,7 @@ index adb2d3182a..276cc54b35 100644
  
      max_offset = m->offset + m->size;
      start_offset = ROUND_DOWN(m->offset, 4);
-@@ -2473,7 +2525,8 @@ csum_update_flag(struct tc_flower *flower,
+@@ -2473,7 +2530,8 @@ csum_update_flag(struct tc_flower *flower,
  
  static int
  nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
@@ -55838,7 +56095,7 @@ index adb2d3182a..276cc54b35 100644
  {
      struct {
          struct tc_pedit sel;
-@@ -2497,12 +2550,12 @@ nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
+@@ -2497,12 +2555,12 @@ nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
              continue;
          }
  
@@ -55854,7 +56111,7 @@ index adb2d3182a..276cc54b35 100644
  
              if (j == 0) {
                  mask_word &= first_word_mask;
-@@ -2556,6 +2609,29 @@ nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index)
+@@ -2556,6 +2614,29 @@ nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index)
      nl_msg_end_nested(request, act_offset);
  }
  
@@ -55884,7 +56141,7 @@ index adb2d3182a..276cc54b35 100644
  static int
  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
  {
-@@ -2572,20 +2648,22 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
+@@ -2572,20 +2653,22 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
  
          action = flower->actions;
          for (i = 0; i < flower->action_count; i++, action++) {
@@ -55913,7 +56170,7 @@ index adb2d3182a..276cc54b35 100644
                  }
              }
              break;
-@@ -2792,13 +2870,16 @@ nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
+@@ -2792,13 +2875,16 @@ nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
      struct in6_addr *ipv6_dst_mask = &flower->mask.tunnel.ipv6.ipv6_dst;
      struct in6_addr *ipv6_src = &flower->key.tunnel.ipv6.ipv6_src;
      struct in6_addr *ipv6_dst = &flower->key.tunnel.ipv6.ipv6_dst;
@@ -55931,7 +56188,7 @@ index adb2d3182a..276cc54b35 100644
  
      if (ipv4_dst_mask || ipv4_src_mask) {
          nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,
-@@ -2824,8 +2905,15 @@ nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
+@@ -2824,8 +2910,15 @@ nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
          nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TTL, ttl);
          nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TTL_MASK, ttl_mask);
      }
@@ -55948,7 +56205,7 @@ index adb2d3182a..276cc54b35 100644
      }
      if (id_mask) {
          nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_KEY_ID, id);
-@@ -2914,13 +3002,13 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
+@@ -2914,13 +3007,13 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
              FLOWER_PUT_MASKED_VALUE(icmp_code, TCA_FLOWER_KEY_ICMPV6_CODE);
              FLOWER_PUT_MASKED_VALUE(icmp_type, TCA_FLOWER_KEY_ICMPV6_TYPE);
          }
@@ -55967,7 +56224,7 @@ index adb2d3182a..276cc54b35 100644
      if (host_eth_type == ETH_P_IP) {
              FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_src, TCA_FLOWER_KEY_IPV4_SRC);
              FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_dst, TCA_FLOWER_KEY_IPV4_DST);
-@@ -2993,12 +3081,79 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
+@@ -2993,12 +3086,79 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
      return 0;
  }
  
@@ -56048,7 +56305,7 @@ index adb2d3182a..276cc54b35 100644
          return false;
      }
  
-@@ -3011,8 +3166,8 @@ cmp_tc_flower_match_action(const struct tc_flower *a,
+@@ -3011,8 +3171,8 @@ cmp_tc_flower_match_action(const struct tc_flower *a,
          uint8_t key_b = ((uint8_t *)&b->key)[i] & mask;
  
          if (key_a != key_b) {
@@ -56059,7 +56316,7 @@ index adb2d3182a..276cc54b35 100644
              return false;
          }
      }
-@@ -3022,14 +3177,15 @@ cmp_tc_flower_match_action(const struct tc_flower *a,
+@@ -3022,14 +3182,15 @@ cmp_tc_flower_match_action(const struct tc_flower *a,
      const struct tc_action *action_b = b->actions;
  
      if (a->action_count != b->action_count) {
@@ -56208,6 +56465,44 @@ index 58269d3b16..050eafa6b8 100644
          char dev_name[IFNAMSIZ];
  
          if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
+diff --git a/lib/unaligned.h b/lib/unaligned.h
+index f40e4e10df..15334e3c76 100644
+--- a/lib/unaligned.h
++++ b/lib/unaligned.h
+@@ -95,7 +95,7 @@ GCC_UNALIGNED_ACCESSORS(ovs_be64, be64);
+ static inline uint16_t get_unaligned_u16(const uint16_t *p_)
+ {
+     const uint8_t *p = (const uint8_t *) p_;
+-    return ntohs((p[0] << 8) | p[1]);
++    return ntohs(((uint16_t) p[0] << 8) | (uint16_t) p[1]);
+ }
+ 
+ static inline void put_unaligned_u16(uint16_t *p_, uint16_t x_)
+@@ -110,7 +110,8 @@ static inline void put_unaligned_u16(uint16_t *p_, uint16_t x_)
+ static inline uint32_t get_unaligned_u32(const uint32_t *p_)
+ {
+     const uint8_t *p = (const uint8_t *) p_;
+-    return ntohl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
++    return ntohl(((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16) |
++                 ((uint32_t) p[2] <<  8) | (uint32_t) p[3]);
+ }
+ 
+ static inline void put_unaligned_u32(uint32_t *p_, uint32_t x_)
+@@ -131,10 +132,10 @@ static inline uint64_t get_unaligned_u64__(const uint64_t *p_)
+                   | ((uint64_t) p[1] << 48)
+                   | ((uint64_t) p[2] << 40)
+                   | ((uint64_t) p[3] << 32)
+-                  | (p[4] << 24)
+-                  | (p[5] << 16)
+-                  | (p[6] << 8)
+-                  | p[7]);
++                  | ((uint64_t) p[4] << 24)
++                  | ((uint64_t) p[5] << 16)
++                  | ((uint64_t) p[6] << 8)
++                  | (uint64_t)  p[7]);
+ }
+ 
+ static inline void put_unaligned_u64__(uint64_t *p_, uint64_t x_)
 diff --git a/lib/unixctl.c b/lib/unixctl.c
 index 69aed6722c..103357ee91 100644
 --- a/lib/unixctl.c
@@ -56394,7 +56689,7 @@ index 4c3bace6ef..09134feca0 100644
       AC_DEFINE([HAVE_LD_AVX512_GOOD], [1],
                 [Define to 1 if binutils correctly supports AVX512.])
 diff --git a/ofproto/bond.c b/ofproto/bond.c
-index cdfdf0b9d8..6ecd6e1c9f 100644
+index cdfdf0b9d8..a7c859b909 100644
 --- a/ofproto/bond.c
 +++ b/ofproto/bond.c
 @@ -185,10 +185,14 @@ static struct bond_member *choose_output_member(const struct bond *,
@@ -56456,6 +56751,15 @@ index cdfdf0b9d8..6ecd6e1c9f 100644
      revalidate = bond->bond_revalidate;
      bond->bond_revalidate = false;
      ovs_rwlock_unlock(&rwlock);
+@@ -876,7 +892,7 @@ bond_check_admissibility(struct bond *bond, const void *member_,
+         if (!member->enabled && member->may_enable) {
+             VLOG_DBG_RL(&rl, "bond %s: member %s: "
+                         "main thread has not yet enabled member",
+-                        bond->name, bond->active_member->name);
++                        bond->name, member->name);
+         }
+         goto out;
+     case LACP_CONFIGURED:
 @@ -1038,7 +1054,7 @@ bond_may_recirc(const struct bond *bond)
  }
  
@@ -57001,7 +57305,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..e804c4d887 100644
+index 578cbfe581..8a28b29d4c 100644
 --- a/ofproto/ofproto-dpif-xlate.c
 +++ b/ofproto/ofproto-dpif-xlate.c
 @@ -865,7 +865,7 @@ xlate_xbridge_init(struct xlate_cfg *xcfg, struct xbridge *xbridge)
@@ -57105,7 +57409,49 @@ index 578cbfe581..e804c4d887 100644
  static struct xbundle *
  xbundle_lookup(struct xlate_cfg *xcfg, const struct ofbundle *ofbundle)
  {
-@@ -2125,9 +2142,14 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
+@@ -1894,8 +1911,8 @@ group_is_alive(const struct xlate_ctx *ctx, uint32_t group_id, int depth)
+ #define MAX_LIVENESS_RECURSION 128 /* Arbitrary limit */
+ 
+ static bool
+-bucket_is_alive(const struct xlate_ctx *ctx,
+-                struct ofputil_bucket *bucket, int depth)
++bucket_is_alive(const struct xlate_ctx *ctx, const struct group_dpif *group,
++                const struct ofputil_bucket *bucket, int depth)
+ {
+     if (depth >= MAX_LIVENESS_RECURSION) {
+         xlate_report_error(ctx, "bucket chaining exceeded %d links",
+@@ -1903,6 +1920,12 @@ bucket_is_alive(const struct xlate_ctx *ctx,
+         return false;
+     }
+ 
++    /* In "select" groups, buckets with weight 0 are not used.
++     * In other kinds of groups, weight does not matter. */
++    if (group->up.type == OFPGT11_SELECT && bucket->weight == 0) {
++        return false;
++    }
++
+     return (!ofputil_bucket_has_liveness(bucket)
+             || (bucket->watch_port != OFPP_ANY
+                && bucket->watch_port != OFPP_CONTROLLER
+@@ -1943,7 +1966,7 @@ group_first_live_bucket(const struct xlate_ctx *ctx,
+ {
+     struct ofputil_bucket *bucket;
+     LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
+-        if (bucket_is_alive(ctx, bucket, depth)) {
++        if (bucket_is_alive(ctx, group, bucket, depth)) {
+             return bucket;
+         }
+         xlate_report_bucket_not_live(ctx, bucket);
+@@ -1962,7 +1985,7 @@ group_best_live_bucket(const struct xlate_ctx *ctx,
+ 
+     struct ofputil_bucket *bucket;
+     LIST_FOR_EACH (bucket, list_node, &group->up.buckets) {
+-        if (bucket_is_alive(ctx, bucket, 0)) {
++        if (bucket_is_alive(ctx, group, bucket, 0)) {
+             uint32_t score =
+                 (hash_int(bucket->bucket_id, basis) & 0xffff) * bucket->weight;
+             if (score >= best_score) {
+@@ -2125,9 +2148,14 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
          int snaplen;
  
          /* Get the details of the mirror represented by the rightmost 1-bit. */
@@ -57123,7 +57469,7 @@ index 578cbfe581..e804c4d887 100644
  
  
          /* If this mirror selects on the basis of VLAN, and it does not select
-@@ -2444,9 +2466,18 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
+@@ -2444,9 +2472,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.  */
@@ -57145,7 +57491,7 @@ index 578cbfe581..e804c4d887 100644
              if (xr.recirc_id) {
                  /* Use recirculation instead of output. */
                  use_recirc = true;
-@@ -3015,7 +3046,7 @@ xlate_normal(struct xlate_ctx *ctx)
+@@ -3015,7 +3052,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)
@@ -57154,7 +57500,7 @@ index 578cbfe581..e804c4d887 100644
      ) {
          update_learning_table(ctx, in_xbundle, flow->dl_src, vlan,
                                is_grat_arp);
-@@ -3024,12 +3055,14 @@ xlate_normal(struct xlate_ctx *ctx)
+@@ -3024,12 +3061,14 @@ xlate_normal(struct xlate_ctx *ctx)
          struct xc_entry *entry;
  
          /* Save just enough info to update mac learning table later. */
@@ -57175,7 +57521,7 @@ index 578cbfe581..e804c4d887 100644
      }
  
      /* Determine output bundle. */
-@@ -3048,7 +3081,6 @@ xlate_normal(struct xlate_ctx *ctx)
+@@ -3048,7 +3087,6 @@ xlate_normal(struct xlate_ctx *ctx)
               */
              ctx->xout->slow |= SLOW_ACTION;
  
@@ -57183,7 +57529,7 @@ index 578cbfe581..e804c4d887 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 +3555,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow,
+@@ -3523,6 +3561,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow,
      dst_flow->dl_dst = dmac;
      dst_flow->dl_src = smac;
  
@@ -57193,7 +57539,7 @@ index 578cbfe581..e804c4d887 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 +3689,27 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct xport *xport,
+@@ -3654,14 +3695,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) {
@@ -57223,7 +57569,35 @@ index 578cbfe581..e804c4d887 100644
          }
          return err;
      }
-@@ -4176,6 +4224,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+@@ -4099,6 +4153,16 @@ xport_has_ip(const struct xport *xport)
+     return n_in6 ? true : false;
+ }
+ 
++static bool check_neighbor_reply(struct xlate_ctx *ctx, struct flow *flow)
++{
++    if (flow->dl_type == htons(ETH_TYPE_ARP) ||
++        flow->nw_proto == IPPROTO_ICMPV6) {
++        return is_neighbor_reply_correct(ctx, flow);
++    }
++
++    return false;
++}
++
+ static bool
+ terminate_native_tunnel(struct xlate_ctx *ctx, const struct xport *xport,
+                         struct flow *flow, struct flow_wildcards *wc,
+@@ -4119,9 +4183,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 &&
+-            (flow->dl_type == htons(ETH_TYPE_ARP) ||
+-             flow->nw_proto == IPPROTO_ICMPV6) &&
+-             is_neighbor_reply_correct(ctx, flow)) {
++            (check_neighbor_reply(ctx, flow) || is_garp(flow, wc))) {
+             tnl_neigh_snoop(flow, wc, ctx->xbridge->name,
+                             ctx->xin->allow_side_effects);
+         } else if (*tnl_port != ODPP_NONE &&
+@@ -4176,6 +4238,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));
@@ -57234,7 +57608,16 @@ index 578cbfe581..e804c4d887 100644
          }
      }
  
-@@ -5622,7 +5674,8 @@ xlate_sample_action(struct xlate_ctx *ctx,
+@@ -4678,7 +4744,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];
+-            if (bucket_is_alive(ctx, b, 0)) {
++            if (bucket_is_alive(ctx, group, b, 0)) {
+                 return b;
+             }
+         }
+@@ -5622,7 +5688,8 @@ xlate_sample_action(struct xlate_ctx *ctx,
  
      /* Scale the probability from 16-bit to 32-bit while representing
       * the same percentage. */
@@ -57244,7 +57627,7 @@ index 578cbfe581..e804c4d887 100644
  
      /* If ofp_port in flow sample action is equel to ofp_port,
       * this sample action is a input port action. */
-@@ -7609,6 +7662,10 @@ xlate_wc_finish(struct xlate_ctx *ctx)
+@@ -7609,6 +7676,10 @@ xlate_wc_finish(struct xlate_ctx *ctx)
              ctx->wc->masks.vlans[i].tci = 0;
          }
      }
@@ -57255,7 +57638,7 @@ index 578cbfe581..e804c4d887 100644
  }
  
  /* Translates the flow, actions, or rule in 'xin' into datapath actions in
-@@ -7784,6 +7841,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
+@@ -7784,6 +7855,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
          goto exit;
      }
  
@@ -60448,9 +60831,47 @@ index 00e3a46b8b..b6155af253 100644
  
  AT_CLEANUP
 diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
-index a79ebdb618..3179e1645d 100644
+index a79ebdb618..6aff1eda7b 100644
 --- a/tests/dpif-netdev.at
 +++ b/tests/dpif-netdev.at
+@@ -6,8 +6,8 @@ m4_divert_push([PREPARE_TESTS])
+ # that vary from one run to another (e.g., timing and bond actions).
+ strip_timers () {
+     sed '
+-    s/duration:[0-9]*\.[0-9]*/duration:0.0/
+-    s/used:[0-9]*\.[0-9]*/used:0.0/
++    s/duration:[0-9\.][0-9\.]*/duration:0.0/
++    s/used:[0-9\.][0-9\.]*/used:0.0/
+ '
+ }
+ 
+@@ -15,7 +15,7 @@ strip_xout () {
+     sed '
+     s/mega_ufid:[-0-9a-f]* //
+     s/ufid:[-0-9a-f]* //
+-    s/used:[0-9]*\.[0-9]*/used:0.0/
++    s/used:[0-9\.][0-9\.]*/used:0.0/
+     s/actions:.*/actions: <del>/
+     s/packets:[0-9]*/packets:0/
+     s/bytes:[0-9]*/bytes:0/
+@@ -26,7 +26,7 @@ strip_xout_keep_actions () {
+     sed '
+     s/mega_ufid:[-0-9a-f]* //
+     s/ufid:[-0-9a-f]* //
+-    s/used:[0-9]*\.[0-9]*/used:0.0/
++    s/used:[0-9\.][0-9\.]*/used:0.0/
+     s/packets:[0-9]*/packets:0/
+     s/bytes:[0-9]*/bytes:0/
+ ' | sort
+@@ -51,7 +51,7 @@ filter_hw_packet_netdev_dummy () {
+ filter_flow_dump () {
+     grep 'flow_dump ' | sed '
+         s/.*flow_dump //
+-        s/used:[0-9]*\.[0-9]*/used:0.0/
++        s/used:[0-9\.][0-9\.]*/used:0.0/
+     ' | sort | uniq
+ }
+ 
 @@ -439,7 +439,7 @@ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), a
  
     # Check for succesfull packet matching with installed offloaded flow.
@@ -60680,6 +61101,20 @@ index 4d49f12017..91ded1445d 100644
  
  bridge("br0")
  -------------
+diff --git a/tests/odp.at b/tests/odp.at
+index 4d08c59ca6..fce6a4f2bc 100644
+--- a/tests/odp.at
++++ b/tests/odp.at
+@@ -348,7 +348,9 @@ ct(commit,helper=tftp)
+ ct(commit,timeout=ovs_tp_1_tcp4)
+ ct(nat)
+ ct(commit,nat(src))
++ct(commit,timeout=ovs_tp_1_tcp4,nat(src))
+ ct(commit,nat(dst))
++ct(commit,timeout=ovs_tp_1_tcp4,nat(dst))
+ ct(commit,nat(src=10.0.0.240,random))
+ ct(commit,nat(src=10.0.0.240:32768-65535,random))
+ ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))
 diff --git a/tests/ofp-print.at b/tests/ofp-print.at
 index 2c7e163bd6..7be6628c34 100644
 --- a/tests/ofp-print.at
@@ -60784,7 +61219,7 @@ index 2c7e163bd6..7be6628c34 100644
  AT_CLEANUP
  
 diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
-index 7c2edeb9d4..1a8de7398c 100644
+index 7c2edeb9d4..73a5e8c3d0 100644
 --- a/tests/ofproto-dpif.at
 +++ b/tests/ofproto-dpif.at
 @@ -29,6 +29,39 @@ AT_CHECK([ovs-appctl revalidator/wait])
@@ -62057,7 +62492,37 @@ index 7c2edeb9d4..1a8de7398c 100644
  OVS_VSWITCHD_STOP
  AT_CLEANUP
  
-@@ -7031,7 +7173,7 @@ dnl An 170 byte packet
+@@ -7004,6 +7146,29 @@ AT_CHECK([ovs-appctl coverage/read-counter mac_learning_static_none_move], [0],
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
++AT_SETUP([ofproto-dpif - static-mac learned mac age out])
++OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone -- set bridge br0 other_config:mac-aging-time=5])
++add_of_ports br0 1 2
++
++dnl Add some static mac entries.
++AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:01:01])
++AT_CHECK([ovs-appctl fdb/add br0 p2 0 50:54:00:00:02:02])
++
++dnl Generate some dynamic fdb entries on some ports.
++OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=60:54:00:00:00:01)], [-generate], [100,2])
++OFPROTO_TRACE([ovs-dummy], [in_port(2),eth(src=60:54:00:00:00:02)], [-generate], [100,1])
++
++dnl Waiting for aging out.
++ovs-appctl time/warp 20000
++
++dnl Count number of static entries remaining.
++AT_CHECK_UNQUOTED([ovs-appctl fdb/stats-show br0 | grep expired], [0], [dnl
++  Total number of expired MAC entries     : 2
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
+ AT_SETUP([ofproto-dpif - basic truncate action])
+ OVS_VSWITCHD_START
+ add_of_ports br0 1 2 3 4 5
+@@ -7031,7 +7196,7 @@ dnl An 170 byte packet
  AT_CHECK([ovs-appctl netdev-dummy/receive p1 '000c29c8a0a4005056c0000808004500009cb4a6000040019003c0a8da01c0a8da640800cb5fa762000556f431ad0009388e08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f'])
  
  AT_CHECK([ovs-ofctl parse-pcap p1.pcap], [0], [dnl
@@ -62066,7 +62531,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  AT_CHECK([ovs-appctl revalidator/purge], [0])
-@@ -7600,13 +7742,28 @@ dnl configure bridge IPFIX and ensure that sample action generation works at the
+@@ -7600,13 +7765,28 @@ dnl configure bridge IPFIX and ensure that sample action generation works at the
  dnl datapath level.
  AT_SETUP([ofproto-dpif - Bridge IPFIX sanity check])
  OVS_VSWITCHD_START
@@ -62096,7 +62561,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  dnl Send some packets that should be sampled.
  for i in `seq 1 3`; do
      AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
-@@ -8666,7 +8823,7 @@ recirc_id(0),in_port(100),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.1
+@@ -8666,7 +8846,7 @@ recirc_id(0),in_port(100),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.1
  ])
  
  AT_CHECK([grep -e '|ofproto_dpif_xlate|WARN|' ovs-vswitchd.log | sed "s/^.*|WARN|//"], [0], [dnl
@@ -62105,7 +62570,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP(["/stack underflow/d"])
-@@ -9855,7 +10012,7 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
+@@ -9855,7 +10035,7 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
  
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via no_match) data_len=86 (unbuffered)
@@ -62114,7 +62579,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -9906,7 +10063,7 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
+@@ -9906,7 +10086,7 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
  
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via action) data_len=86 (unbuffered)
@@ -62123,7 +62588,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10166,10 +10323,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10166,10 +10346,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output. We only see the latter two packets, not the first.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x0 total_len=106 reg0=0x1,reg1=0x4d2,reg2=0x1,reg3=0x1,reg4=0x1,in_port=1 (via action) data_len=106 (unbuffered)
@@ -62136,7 +62601,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log])
-@@ -10187,10 +10344,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10187,10 +10367,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output. We should see both packets
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x0 total_len=106 reg0=0x1,reg1=0x4d2,reg2=0x1,reg3=0x1,reg4=0x1,in_port=1 (via action) data_len=106 (unbuffered)
@@ -62149,7 +62614,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10239,10 +10396,10 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
+@@ -10239,10 +10419,10 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
  dnl happens because the ct_state field is available only after recirc.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered)
@@ -62162,7 +62627,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log])
-@@ -10261,10 +10418,10 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
+@@ -10261,10 +10441,10 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
  dnl happens because the ct_state field is available only after recirc.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered)
@@ -62175,7 +62640,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  dnl
-@@ -10320,9 +10477,9 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
+@@ -10320,9 +10500,9 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
  dnl happens because the ct_state field is available only after recirc.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=126 in_port=1 (via action) data_len=126 (unbuffered)
@@ -62187,7 +62652,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10433,7 +10590,7 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10433,7 +10613,7 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output. Only one reply must be there
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered)
@@ -62196,7 +62661,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  dnl
  OFPT_ECHO_REQUEST (xid=0x0): 0 bytes of payload
  ])
-@@ -10467,7 +10624,7 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10467,7 +10647,7 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 ct_state=inv|trk,ipv6,in_port=2 (via action) data_len=86 (unbuffered)
@@ -62205,7 +62670,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10523,16 +10680,16 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10523,16 +10703,16 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output. We only see the latter two packets (for each zone), not the first.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered)
@@ -62226,7 +62691,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10579,10 +10736,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10579,10 +10759,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output. We only see the latter two packets, not the first.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered)
@@ -62239,7 +62704,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10629,10 +10786,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10629,10 +10809,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output. We only see the first and the last packet
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=47 ct_state=new|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,ip,in_port=1 (via action) data_len=47 (unbuffered)
@@ -62252,7 +62717,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10681,19 +10838,19 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10681,19 +10861,19 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered)
@@ -62277,7 +62742,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -10738,10 +10895,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -10738,10 +10918,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_label=0x1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered)
@@ -62290,7 +62755,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  OVS_VSWITCHD_STOP
-@@ -11152,16 +11309,16 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
+@@ -11152,16 +11332,16 @@ dnl Note that the first packet doesn't have the ct_state bits set. This
  dnl happens because the ct_state field is available only after recirc.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered)
@@ -62311,7 +62776,7 @@ index 7c2edeb9d4..1a8de7398c 100644
  ])
  
  dnl The next test verifies that ct_clear at the datapath only gets executed
-@@ -11235,13 +11392,13 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+@@ -11235,13 +11415,13 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
  dnl Check this output.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.2.100,ct_nw_dst=10.1.2.200,ct_nw_proto=17,ct_tp_src=6,ct_tp_dst=6,ip,in_port=2 (via action) data_len=106 (unbuffered)
@@ -63593,6 +64058,19 @@ index 0f74709f5a..5bca84351c 100644
  run
    should send probe
    in IDLE for 0 ms (1000 ms backoff)
+diff --git a/tests/stp.at b/tests/stp.at
+index 7ddacfc3a0..69475843e5 100644
+--- a/tests/stp.at
++++ b/tests/stp.at
+@@ -368,7 +368,7 @@ AT_CLEANUP
+ # Strips out uninteresting parts of flow output, as well as parts
+ # that vary from one run to another (e.g., timing and bond actions).
+ m4_define([STRIP_USED], [[sed '
+-    s/used:[0-9]*\.[0-9]*/used:0.0/
++    s/used:[0-9\.][0-9\.]*/used:0.0/
+     s/duration=[0-9.]*s*/duration=Xs/
+     s/idle_age=[0-9]*,/idle_age=X,/
+ ']])
 diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at
 index 19a0b125b9..8b9f5c7525 100644
 --- a/tests/system-common-macros.at
@@ -63710,7 +64188,7 @@ index 1714273e35..270956d13f 100644
  dnl Delete ip address.
  AT_CHECK([ip addr del 10.0.0.17/24 dev p1-route], [0], [stdout])
 diff --git a/tests/system-traffic.at b/tests/system-traffic.at
-index f22d86e466..69de604fa0 100644
+index f22d86e466..9d1c96bb43 100644
 --- a/tests/system-traffic.at
 +++ b/tests/system-traffic.at
 @@ -192,6 +192,46 @@ NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -w 2 fc00:1::2 | FORMAT_PI
@@ -64138,7 +64616,7 @@ index f22d86e466..69de604fa0 100644
  
  
  OVS_TRAFFIC_VSWITCHD_STOP
-@@ -1933,13 +2037,13 @@ dnl p1(at_ns1) interface
+@@ -1933,15 +2037,51 @@ dnl p1(at_ns1) interface
  NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37  > /dev/null])
  
  dnl Check the expected decapsulated on the egress interface
@@ -64158,8 +64636,46 @@ index f22d86e466..69de604fa0 100644
 +OVS_WAIT_UNTIL([cat p1.pcap | grep -E "0x0060:  *3637" 2>&1 1>/dev/null])
  
  
++OVS_TRAFFIC_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_BANNER([QoS])
++
++AT_SETUP([QoS - basic configuration])
++AT_SKIP_IF([test $HAVE_TC = no])
++OVS_TRAFFIC_VSWITCHD_START()
++
++ADD_NAMESPACES(at_ns0, at_ns1)
++
++ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
++ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
++
++dnl Adding a custom qdisc to ovs-p1, ovs-p0 will have the default qdisc.
++AT_CHECK([tc qdisc add dev ovs-p1 root noqueue])
++AT_CHECK([tc qdisc show dev ovs-p1 | grep -q noqueue])
++
++dnl Configure the same QoS for both ports.
++AT_CHECK([ovs-vsctl set port ovs-p0 qos=@qos -- set port ovs-p1 qos=@qos dnl
++            -- --id=@qos create qos dnl
++               type=linux-htb other-config:max-rate=3000000 queues:0=@queue dnl
++            -- --id=@queue create queue dnl
++               other_config:min-rate=2000000 other_config:max-rate=3000000 dnl
++               other_config:burst=3000000],
++         [ignore], [ignore])
++
++dnl Wait for qdiscs to be applied.
++OVS_WAIT_UNTIL([tc qdisc show dev ovs-p0 | grep -q htb])
++OVS_WAIT_UNTIL([tc qdisc show dev ovs-p1 | grep -q htb])
++
++dnl Check the configuration.
++m4_define([HTB_CONF], [rate 2Mbit ceil 3Mbit burst 375000b cburst 375000b])
++AT_CHECK([tc class show dev ovs-p0 | grep -q 'class htb .* HTB_CONF'])
++AT_CHECK([tc class show dev ovs-p1 | grep -q 'class htb .* HTB_CONF'])
++
  OVS_TRAFFIC_VSWITCHD_STOP
-@@ -1985,9 +2089,9 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
+ AT_CLEANUP
+ 
+@@ -1985,9 +2125,9 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
  dnl Check this output. We only see the latter two packets, not the first.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN2 (xid=0x0): total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
@@ -64171,7 +64687,7 @@ index f22d86e466..69de604fa0 100644
  ])
  
  OVS_TRAFFIC_VSWITCHD_STOP
-@@ -2033,9 +2137,9 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
+@@ -2033,9 +2173,9 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
  dnl Check this output. We only see the latter two packets, not the first.
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
@@ -64183,7 +64699,7 @@ index f22d86e466..69de604fa0 100644
  ])
  
  dnl
-@@ -2980,6 +3084,15 @@ NXST_FLOW reply:
+@@ -2980,6 +3120,15 @@ NXST_FLOW reply:
   table=1, priority=100,ct_state=+est+trk,in_port=1 actions=output:2
  ])
  
@@ -64199,7 +64715,7 @@ index f22d86e466..69de604fa0 100644
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
  
-@@ -3140,11 +3253,11 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
+@@ -3140,11 +3289,11 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
  dnl Check this output. We only see the latter two packets, not the first.
  AT_CHECK([cat ofctl_monitor.log | grep -v ff02 | grep -v fe80 | grep -v no_match], [0], [dnl
  NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=inv|trk,ip,in_port=2 (via action) data_len=75 (unbuffered)
@@ -64214,7 +64730,7 @@ index f22d86e466..69de604fa0 100644
  ])
  
  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.1)], [0], [dnl
-@@ -3345,6 +3458,11 @@ AT_CHECK([ovs-ofctl bundle br0 bundle.txt])
+@@ -3345,6 +3494,11 @@ AT_CHECK([ovs-ofctl bundle br0 bundle.txt])
  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
  ])
  
@@ -64226,7 +64742,7 @@ index f22d86e466..69de604fa0 100644
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
  
-@@ -4100,15 +4218,15 @@ action=normal
+@@ -4100,15 +4254,15 @@ action=normal
  
  AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
  
@@ -64245,7 +64761,7 @@ index f22d86e466..69de604fa0 100644
  "1616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610a, actions=ct(table=1)"])
  
  AT_CHECK([ovs-appctl dpctl/dump-flows | head -2 | tail -1 | grep -q -e ["]udp[(]src=5001["]])
-@@ -5384,7 +5502,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=
+@@ -5384,7 +5538,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=
  udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.2XX,sport=<cleared>,dport=<cleared>),mark=1
  ])
  
@@ -64254,7 +64770,7 @@ index f22d86e466..69de604fa0 100644
  
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
-@@ -6134,7 +6252,7 @@ sleep 1
+@@ -6134,7 +6288,7 @@ sleep 1
  dnl UDP packets from ns0->ns1 should solicit "destination unreachable" response.
  NS_CHECK_EXEC([at_ns0], [bash -c "echo a | nc -6 $NC_EOF_OPT -u fc00::2 1"])
  
@@ -64263,7 +64779,7 @@ index f22d86e466..69de604fa0 100644
  
  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl
  udp,orig=(src=fc00::1,dst=fc00::2,sport=<cleared>,dport=<cleared>),reply=(src=fc00::2,dst=fc00::240,sport=<cleared>,dport=<cleared>)
-@@ -6454,7 +6572,7 @@ on_exit 'ovs-appctl revalidator/purge'
+@@ -6454,7 +6608,7 @@ on_exit 'ovs-appctl revalidator/purge'
  on_exit 'ovs-appctl dpif/dump-flows br0'
  
  dnl Should work with the virtual IP address through NAT
@@ -64272,7 +64788,7 @@ index f22d86e466..69de604fa0 100644
      echo Request $i
      NS_CHECK_EXEC([at_ns1], [wget 10.1.1.64 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
  done
-@@ -6743,6 +6861,132 @@ AT_CHECK([ovs-ofctl dump-flows br0 | grep table=2, | OFPROTO_CLEAR_DURATION_IDLE
+@@ -6743,6 +6897,132 @@ AT_CHECK([ovs-ofctl dump-flows br0 | grep table=2, | OFPROTO_CLEAR_DURATION_IDLE
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
  
@@ -64405,7 +64921,7 @@ index f22d86e466..69de604fa0 100644
  AT_BANNER([802.1ad])
  
  AT_SETUP([802.1ad - vlan_limit])
-@@ -7007,12 +7251,12 @@ dnl p1(at_ns1) interface
+@@ -7007,12 +7287,12 @@ dnl p1(at_ns1) interface
  NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null])
  
  dnl Check the expected nsh encapsulated packet on the egress interface
@@ -64424,7 +64940,7 @@ index f22d86e466..69de604fa0 100644
  
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
-@@ -7039,10 +7283,10 @@ dnl p1(at_ns1) interface
+@@ -7039,10 +7319,10 @@ dnl p1(at_ns1) interface
  NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 ff 00 00 00 02 f2 ff 00 00 00 01 89 4f 02 06 01 03 00 00 64 03 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null])
  
  dnl Check the expected de-capsulated TCP packet on the egress interface
@@ -64439,7 +64955,7 @@ index f22d86e466..69de604fa0 100644
  
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
-@@ -7072,12 +7316,12 @@ dnl p1(at_ns1) interface
+@@ -7072,12 +7352,12 @@ dnl p1(at_ns1) interface
  NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 ff 00 00 00 02 f2 ff 00 00 00 01 89 4f 02 06 01 03 00 01 00 03 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null])
  
  dnl Check the expected NSH packet with new fields in the header
@@ -64458,7 +64974,7 @@ index f22d86e466..69de604fa0 100644
  
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
-@@ -7106,23 +7350,23 @@ dnl First send packet from at_ns0 --> OVS with SPI=0x100 and SI=2
+@@ -7106,23 +7386,23 @@ dnl First send packet from at_ns0 --> OVS with SPI=0x100 and SI=2
  NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 ff 00 00 00 02 f2 ff 00 00 00 01 89 4f 02 06 01 03 00 01 00 02 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null])
  
  dnl Check for the above packet on p1 interface
@@ -65348,10 +65864,37 @@ index 3f58e3e8fd..8c5af459e9 100644
  
  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
 diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
-index 57589758f4..c9a04c76bd 100644
+index 57589758f4..50f90815a1 100644
 --- a/tests/tunnel-push-pop.at
 +++ b/tests/tunnel-push-pop.at
-@@ -546,6 +546,28 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
+@@ -369,6 +369,26 @@ AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
+ 1.1.2.92                                      f8:bc:12:44:34:b6   br0
+ ])
+ 
++dnl Receiving Gratuitous ARP request with correct VLAN id should alter tunnel neighbor cache
++AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.92,op=1,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00))'])
++
++ovs-appctl time/warp 1000
++ovs-appctl time/warp 1000
++
++AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
++1.1.2.92                                      f8:bc:12:44:34:c8   br0
++])
++
++dnl Receiving Gratuitous ARP reply with correct VLAN id should alter tunnel neighbor cache
++AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b2,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.92,op=2,sha=f8:bc:12:44:34:b2,tha=f8:bc:12:44:34:b2))'])
++
++ovs-appctl time/warp 1000
++ovs-appctl time/warp 1000
++
++AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
++1.1.2.92                                      f8:bc:12:44:34:b2   br0
++])
++
+ dnl Receive ARP reply without VLAN header
+ AT_CHECK([ovs-vsctl set port br0 tag=0])
+ AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
+@@ -546,6 +566,28 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
    port  7: rx pkts=5, bytes=434, drop=?, errs=?, frame=?, over=?, crc=?
  ])
  
@@ -65380,7 +65923,7 @@ index 57589758f4..c9a04c76bd 100644
  dnl Check decapsulation of Geneve packet with options
  AT_CAPTURE_FILE([ofctl_monitor.log])
  AT_CHECK([ovs-ofctl monitor int-br 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
-@@ -559,14 +581,14 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
+@@ -559,14 +601,14 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
  
  AT_CHECK([cat ofctl_monitor.log], [0], [dnl
  NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=98 tun_id=0x7b,tun_src=1.1.2.92,tun_dst=1.1.2.88,tun_metadata0=0xa,in_port=5 (via action) data_len=98 (unbuffered)
@@ -65398,7 +65941,7 @@ index 57589758f4..c9a04c76bd 100644
  ])
  
  dnl Receive VXLAN with different MAC and verify that the neigh cache gets updated
-@@ -718,14 +740,14 @@ dnl Output to tunnel from a int-br internal port.
+@@ -718,14 +760,14 @@ dnl Output to tunnel from a int-br internal port.
  dnl Checking that the packet arrived and it was correctly encapsulated.
  AT_CHECK([ovs-ofctl add-flow int-br "in_port=LOCAL,actions=debug_slow,output:2"])
  AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4"])
@@ -65416,7 +65959,7 @@ index 57589758f4..c9a04c76bd 100644
  
  dnl Datapath actions should not have tunnel push action.
  AT_CHECK([ovs-appctl dpctl/dump-flows | grep -q tnl_push], [1])
-@@ -842,3 +864,54 @@ Datapath actions: 7
+@@ -842,3 +884,54 @@ Datapath actions: 7
  
  OVS_VSWITCHD_STOP
  AT_CLEANUP
@@ -65556,10 +66099,18 @@ index fb2025b765..67092ecf7e 100755
          awk '/^  *0x/ {if (cnt != 0) printf ","; \
               cnt++;printf "{class="$1",type="$2",len="$3"}->"$4}'
 diff --git a/utilities/ovs-tcpdump.in b/utilities/ovs-tcpdump.in
-index 82d1bedfa6..e12bab8895 100755
+index 82d1bedfa6..a49ec9f942 100755
 --- a/utilities/ovs-tcpdump.in
 +++ b/utilities/ovs-tcpdump.in
-@@ -165,6 +165,9 @@ class OVSDB(object):
+@@ -44,6 +44,7 @@ try:
+     from ovs import jsonrpc
+     from ovs.poller import Poller
+     from ovs.stream import Stream
++    from ovs.fatal_signal import add_hook
+ except Exception:
+     print("ERROR: Please install the correct Open vSwitch python support")
+     print("       libraries (version @VERSION@).")
+@@ -165,6 +166,9 @@ class OVSDB(object):
          self._idl_conn = idl.Idl(db_sock, schema)
          OVSDB.wait_for_db_change(self._idl_conn)  # Initial Sync with DB
  
@@ -65569,7 +66120,7 @@ index 82d1bedfa6..e12bab8895 100755
      def _get_schema(self):
          error, strm = Stream.open_block(Stream.open(self._db_sock))
          if error:
-@@ -222,6 +225,13 @@ class OVSDB(object):
+@@ -222,6 +226,13 @@ class OVSDB(object):
      def interface_mtu(self, intf_name):
          try:
              intf = self._find_row_by_name('Interface', intf_name)
@@ -65583,9 +66134,28 @@ index 82d1bedfa6..e12bab8895 100755
              return intf.mtu[0]
          except Exception:
              return None
-@@ -403,7 +413,8 @@ def py_which(executable):
+@@ -402,8 +413,27 @@ def py_which(executable):
+                for path in os.environ["PATH"].split(os.pathsep))
  
  
++def teardown(db_sock, interface, mirror_interface, tap_created):
++    def cleanup_mirror():
++        try:
++            ovsdb = OVSDB(db_sock)
++            ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
++            ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
++            if tap_created is True:
++                _del_taps[sys.platform](mirror_interface)
++        except Exception:
++            print("Unable to tear down the ports and mirrors.")
++            print("Please use ovs-vsctl to remove the ports and mirrors"
++                  " created.")
++            print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
++                                                          mirror_interface))
++
++    add_hook(cleanup_mirror, None, True)
++
++
  def main():
 -    db_sock = 'unix:@RUNDIR@/db.sock'
 +    rundir = os.environ.get('OVS_RUNDIR', '@RUNDIR@')
@@ -65593,8 +66163,26 @@ index 82d1bedfa6..e12bab8895 100755
      interface = None
      tcpdargs = []
  
-@@ -500,6 +511,8 @@ def main():
-             pass
+@@ -485,6 +515,9 @@ def main():
+         print("ERROR: Mirror port (%s) exists for port %s." %
+               (mirror_interface, interface))
+         sys.exit(1)
++
++    teardown(db_sock, interface, mirror_interface, tap_created)
++
+     try:
+         ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
+         ovsdb.bridge_mirror(interface, mirror_interface,
+@@ -492,14 +525,10 @@ def main():
+                             mirror_select_all)
+     except OVSDBException as oe:
+         print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
+-        try:
+-            ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
+-            if tap_created is True:
+-                _del_taps[sys.platform](mirror_interface)
+-        except Exception:
+-            pass
          sys.exit(1)
  
 +    ovsdb.close_idl()
@@ -65602,14 +66190,24 @@ index 82d1bedfa6..e12bab8895 100755
      pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
      try:
          while pipes.poll() is None:
-@@ -512,6 +525,7 @@ def main():
+@@ -512,17 +541,6 @@ def main():
          if pipes.poll() is None:
              pipes.terminate()
  
-+        ovsdb = OVSDB(db_sock)
-         ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
-         ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
-         if tap_created is True:
+-        ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
+-        ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
+-        if tap_created is True:
+-            _del_taps[sys.platform](mirror_interface)
+-    except Exception:
+-        print("Unable to tear down the ports and mirrors.")
+-        print("Please use ovs-vsctl to remove the ports and mirrors created.")
+-        print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
+-                                                      mirror_interface))
+-        sys.exit(1)
+-
+     sys.exit(0)
+ 
+ 
 diff --git a/utilities/ovs-vsctl-bashcomp.bash b/utilities/ovs-vsctl-bashcomp.bash
 old mode 100755
 new mode 100644
@@ -65984,6 +66582,19 @@ index 5223aa8970..e328d8ead1 100644
          union ovsdb_atom atom;
  
          atom.uuid = m->uuid;
+diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
+index 0c66326171..87d8f3e67a 100644
+--- a/vswitchd/vswitch.xml
++++ b/vswitchd/vswitch.xml
+@@ -2312,7 +2312,7 @@
+           lowest port-id is elected as the root.
+         </column>
+ 
+-        <column name="other_config" key="rstp-port-path-cost"
++        <column name="other_config" key="rstp-path-cost"
+                 type='{"type": "integer"}'>
+           The port path cost.  The Port's contribution, when it is
+           the Root Port, to the Root Path Cost for the Bridge.  By default the
 diff --git a/vtep/vtep-ctl.c b/vtep/vtep-ctl.c
 index ab552457d9..99c4adcd53 100644
 --- a/vtep/vtep-ctl.c
diff --git a/SPECS/openvswitch2.17.spec b/SPECS/openvswitch2.17.spec
index c052166..b72036e 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: 59%{?dist}
+Release: 61%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -743,6 +743,30 @@ exit 0
 %endif
 
 %changelog
+* Wed Nov 02 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-61
+- Merging upstream branch-2.17 [RH git: bf6774bd2c]
+    Commit list:
+    fa95bf9621 ovs-tcpdump: Cleanup mirror port on SIGHUP/SIGTERM.
+    7ebef81f91 netdev-linux: Fix inability to apply QoS on ports with custom qdiscs. (#2138339)
+    037ef6301b tc: Fix misaligned writes while parsing pedit.
+    869e2e1ba0 odp-util: Add missing separator in format_odp_conntrack_action().
+    0aa55709fc vswitch.xml: Fix the name of rstp-path-cost option.
+    af459fa370 mac-learning: Fix learned fdb entries not age out issue.
+    c4336a1f12 ofproto-dpif-xlate: Update tunnel neighbor when receive gratuitous ARP.
+    683508cd4e bond: Fix crash while logging not yet enabled member.
+    41b178d525 netdev-dpdk: Fix tx_dropped counters value.
+
+
+* Wed Oct 26 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-60
+- Merging upstream branch-2.17 [RH git: 4ad2c662f3]
+    Commit list:
+    d0276481a1 unaligned: Correct the stats of packet_count and byte_count on Windows.
+    71401199ff tests: Fix filtering of whole-second durations.
+    3c1c034e58 netdev-offload: Set 'miss_api_supported' to be under netdev.
+    35615cd37a cmap: Add thread fence for slot update.
+    5f8ba216a4 ofproto-dpif-xlate: Do not use zero-weight buckets in select groups.
+
+
 * Thu Oct 13 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-59
 - Merging upstream branch-2.17 [RH git: ff2d81a848]
     Commit list: