diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch
index 9ca8b02..73fea50 100644
--- a/SOURCES/openvswitch-2.17.0.patch
+++ b/SOURCES/openvswitch-2.17.0.patch
@@ -53648,6 +53648,22 @@ index e03cd8d0c5..474344194f 100644
+ wc->masks.nw_proto = 0xff;
+ }
+}
+diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
+index 28865f88c8..ff81fddc5e 100644
+--- a/lib/meta-flow.xml
++++ b/lib/meta-flow.xml
+@@ -4303,9 +4303,9 @@ r r c c c.
+
+
+
+-
++
+
+-
++
+
+
+
diff --git a/lib/namemap.c b/lib/namemap.c
index 785cda4c27..dd317ea52e 100644
--- a/lib/namemap.c
@@ -54232,8 +54248,267 @@ index 620a451dec..387773e927 100644
error = nl_transact(NETLINK_ROUTE, &request, &reply);
ofpbuf_uninit(&request);
if (error) {
+diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
+index b89dfdd52a..6c90946388 100644
+--- a/lib/netdev-native-tnl.c
++++ b/lib/netdev-native-tnl.c
+@@ -320,7 +320,7 @@ netdev_tnl_ip_build_header(struct ovs_action_push_tnl *data,
+ }
+
+ static void *
+-udp_build_header(struct netdev_tunnel_config *tnl_cfg,
++udp_build_header(const struct netdev_tunnel_config *tnl_cfg,
+ struct ovs_action_push_tnl *data,
+ const struct netdev_tnl_build_header_params *params)
+ {
+@@ -452,7 +452,6 @@ netdev_gre_push_header(const struct netdev *netdev,
+ const struct ovs_action_push_tnl *data)
+ {
+ struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
+ struct gre_base_hdr *greh;
+ int ip_tot_size;
+
+@@ -468,8 +467,7 @@ netdev_gre_push_header(const struct netdev *netdev,
+ int seq_ofs = gre_header_len(greh->flags) - 4;
+ ovs_16aligned_be32 *seq_opt =
+ ALIGNED_CAST(ovs_16aligned_be32 *, (char *)greh + seq_ofs);
+- tnl_cfg = &dev->tnl_cfg;
+- put_16aligned_be32(seq_opt, htonl(tnl_cfg->seqno++));
++ put_16aligned_be32(seq_opt, htonl(atomic_count_inc(&dev->gre_seqno)));
+ }
+ }
+
+@@ -478,16 +476,11 @@ netdev_gre_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct netdev_tnl_build_header_params *params)
+ {
+- struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
++ const struct netdev_tunnel_config *tnl_cfg;
+ struct gre_base_hdr *greh;
+ ovs_16aligned_be32 *options;
+ unsigned int hlen;
+
+- /* XXX: RCUfy tnl_cfg. */
+- ovs_mutex_lock(&dev->mutex);
+- tnl_cfg = &dev->tnl_cfg;
+-
+ greh = netdev_tnl_ip_build_header(data, params, IPPROTO_GRE);
+
+ if (params->flow->packet_type == htonl(PT_ETH)) {
+@@ -495,8 +488,7 @@ netdev_gre_build_header(const struct netdev *netdev,
+ } else if (pt_ns(params->flow->packet_type) == OFPHTN_ETHERTYPE) {
+ greh->protocol = pt_ns_type_be(params->flow->packet_type);
+ } else {
+- ovs_mutex_unlock(&dev->mutex);
+- return 1;
++ return EINVAL;
+ }
+ greh->flags = 0;
+
+@@ -507,6 +499,8 @@ netdev_gre_build_header(const struct netdev *netdev,
+ options++;
+ }
+
++ tnl_cfg = netdev_get_tunnel_config(netdev);
++
+ if (tnl_cfg->out_key_present) {
+ greh->flags |= htons(GRE_KEY);
+ put_16aligned_be32(options, be64_to_be32(params->flow->tunnel.tun_id));
+@@ -519,8 +513,6 @@ netdev_gre_build_header(const struct netdev *netdev,
+ options++;
+ }
+
+- ovs_mutex_unlock(&dev->mutex);
+-
+ hlen = (uint8_t *) options - (uint8_t *) greh;
+
+ data->header_len += hlen;
+@@ -605,7 +597,6 @@ netdev_erspan_push_header(const struct netdev *netdev,
+ const struct ovs_action_push_tnl *data)
+ {
+ struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
+ struct erspan_base_hdr *ersh;
+ struct gre_base_hdr *greh;
+ struct erspan_md2 *md2;
+@@ -615,9 +606,8 @@ netdev_erspan_push_header(const struct netdev *netdev,
+ data->header_len, &ip_tot_size);
+
+ /* update GRE seqno */
+- tnl_cfg = &dev->tnl_cfg;
+ ovs_16aligned_be32 *seqno = (ovs_16aligned_be32 *) (greh + 1);
+- put_16aligned_be32(seqno, htonl(tnl_cfg->seqno++));
++ put_16aligned_be32(seqno, htonl(atomic_count_inc(&dev->gre_seqno)));
+
+ /* update v2 timestamp */
+ if (greh->protocol == htons(ETH_TYPE_ERSPAN2)) {
+@@ -632,8 +622,7 @@ netdev_erspan_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct netdev_tnl_build_header_params *params)
+ {
+- struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
++ const struct netdev_tunnel_config *tnl_cfg;
+ struct gre_base_hdr *greh;
+ struct erspan_base_hdr *ersh;
+ unsigned int hlen;
+@@ -641,21 +630,19 @@ netdev_erspan_build_header(const struct netdev *netdev,
+ int erspan_ver;
+ uint16_t sid;
+
+- /* XXX: RCUfy tnl_cfg. */
+- ovs_mutex_lock(&dev->mutex);
+- tnl_cfg = &dev->tnl_cfg;
+ greh = netdev_tnl_ip_build_header(data, params, IPPROTO_GRE);
+ ersh = ERSPAN_HDR(greh);
+
+ tun_id = ntohl(be64_to_be32(params->flow->tunnel.tun_id));
+ /* ERSPAN only has 10-bit session ID */
+ if (tun_id & ~ERSPAN_SID_MASK) {
+- ovs_mutex_unlock(&dev->mutex);
+- return 1;
++ return EINVAL;
+ } else {
+ sid = (uint16_t) tun_id;
+ }
+
++ tnl_cfg = netdev_get_tunnel_config(netdev);
++
+ if (tnl_cfg->erspan_ver_flow) {
+ erspan_ver = params->flow->tunnel.erspan_ver;
+ } else {
+@@ -702,12 +689,9 @@ netdev_erspan_build_header(const struct netdev *netdev,
+ hlen = ERSPAN_GREHDR_LEN + sizeof *ersh + ERSPAN_V2_MDSIZE;
+ } else {
+ VLOG_WARN_RL(&err_rl, "ERSPAN version error %d", tnl_cfg->erspan_ver);
+- ovs_mutex_unlock(&dev->mutex);
+- return 1;
++ return EINVAL;
+ }
+
+- ovs_mutex_unlock(&dev->mutex);
+-
+ data->header_len += hlen;
+
+ if (params->is_ipv6) {
+@@ -786,7 +770,6 @@ netdev_gtpu_push_header(const struct netdev *netdev,
+ const struct ovs_action_push_tnl *data)
+ {
+ struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
+ struct udp_header *udp;
+ struct gtpuhdr *gtpuh;
+ int ip_tot_size;
+@@ -801,10 +784,9 @@ netdev_gtpu_push_header(const struct netdev *netdev,
+
+ gtpuh = ALIGNED_CAST(struct gtpuhdr *, udp + 1);
+
+- tnl_cfg = &dev->tnl_cfg;
+- if (tnl_cfg->set_seq) {
++ if (gtpuh->md.flags & GTPU_S_MASK) {
+ ovs_be16 *seqno = ALIGNED_CAST(ovs_be16 *, gtpuh + 1);
+- *seqno = htons(tnl_cfg->seqno++);
++ *seqno = htons(atomic_count_inc(&dev->gre_seqno));
+ payload_len += sizeof(struct gtpuhdr_opt);
+ }
+ gtpuh->len = htons(payload_len);
+@@ -815,13 +797,12 @@ netdev_gtpu_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct netdev_tnl_build_header_params *params)
+ {
+- struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
++ const struct netdev_tunnel_config *tnl_cfg;
+ struct gtpuhdr *gtph;
+ unsigned int gtpu_hlen;
+
+- ovs_mutex_lock(&dev->mutex);
+- tnl_cfg = &dev->tnl_cfg;
++ tnl_cfg = netdev_get_tunnel_config(netdev);
++
+ gtph = udp_build_header(tnl_cfg, data, params);
+
+ /* Set to default if not set in flow. */
+@@ -837,7 +818,6 @@ netdev_gtpu_build_header(const struct netdev *netdev,
+ gtph->md.flags |= GTPU_S_MASK;
+ gtpu_hlen += sizeof(struct gtpuhdr_opt);
+ }
+- ovs_mutex_unlock(&dev->mutex);
+
+ data->header_len += gtpu_hlen;
+ data->tnl_type = OVS_VPORT_TYPE_GTPU;
+@@ -920,13 +900,10 @@ netdev_vxlan_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct netdev_tnl_build_header_params *params)
+ {
+- struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
++ const struct netdev_tunnel_config *tnl_cfg;
+ struct vxlanhdr *vxh;
+
+- /* XXX: RCUfy tnl_cfg. */
+- ovs_mutex_lock(&dev->mutex);
+- tnl_cfg = &dev->tnl_cfg;
++ tnl_cfg = netdev_get_tunnel_config(netdev);
+
+ vxh = udp_build_header(tnl_cfg, data, params);
+
+@@ -951,10 +928,10 @@ netdev_vxlan_build_header(const struct netdev *netdev,
+ vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_ETHERNET;
+ break;
+ default:
+- goto drop;
++ return EINVAL;
+ }
+ } else {
+- goto drop;
++ return EINVAL;
+ }
+ } else {
+ put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
+@@ -962,14 +939,9 @@ netdev_vxlan_build_header(const struct netdev *netdev,
+ htonl(ntohll(params->flow->tunnel.tun_id) << 8));
+ }
+
+- ovs_mutex_unlock(&dev->mutex);
+ data->header_len += sizeof *vxh;
+ data->tnl_type = OVS_VPORT_TYPE_VXLAN;
+ return 0;
+-
+-drop:
+- ovs_mutex_unlock(&dev->mutex);
+- return 1;
+ }
+
+ struct dp_packet *
+@@ -1033,22 +1005,14 @@ netdev_geneve_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct netdev_tnl_build_header_params *params)
+ {
+- struct netdev_vport *dev = netdev_vport_cast(netdev);
+- struct netdev_tunnel_config *tnl_cfg;
+ struct genevehdr *gnh;
+ int opt_len;
+ bool crit_opt;
+
+- /* XXX: RCUfy tnl_cfg. */
+- ovs_mutex_lock(&dev->mutex);
+- tnl_cfg = &dev->tnl_cfg;
+-
+- gnh = udp_build_header(tnl_cfg, data, params);
++ gnh = udp_build_header(netdev_get_tunnel_config(netdev), data, params);
+
+ put_16aligned_be32(&gnh->vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));
+
+- ovs_mutex_unlock(&dev->mutex);
+-
+ opt_len = tun_metadata_to_geneve_header(¶ms->flow->tunnel,
+ gnh->options, &crit_opt);
+
diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
-index 94dc6a9b74..303b99daf4 100644
+index 94dc6a9b74..1ad87e5ffc 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -18,6 +18,7 @@
@@ -54332,6 +54607,21 @@ index 94dc6a9b74..303b99daf4 100644
.queue_num = netdev_n_rxq(netdev),
.queue = rss_data->queue,
.key_len = 0,
+@@ -2310,13 +2334,13 @@ netdev_offload_dpdk_flow_destroy(struct ufid_to_rte_flow_data *rte_flow_data)
+ ovsrcu_get(void *, &netdev->hw_info.offload_data);
+ data->rte_flow_counters[tid]--;
+
+- ufid_to_rte_flow_disassociate(rte_flow_data);
+ VLOG_DBG_RL(&rl, "%s/%s: rte_flow 0x%"PRIxPTR
+ " flow destroy %d ufid " UUID_FMT,
+ netdev_get_name(netdev), netdev_get_name(physdev),
+ (intptr_t) rte_flow,
+ netdev_dpdk_get_port_id(physdev),
+ UUID_ARGS((struct uuid *) ufid));
++ ufid_to_rte_flow_disassociate(rte_flow_data);
+ } else {
+ VLOG_ERR("Failed flow: %s/%s: flow destroy %d ufid " UUID_FMT,
+ netdev_get_name(netdev), netdev_get_name(physdev),
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 9845e8d3fe..6fa27d1dda 100644
--- a/lib/netdev-offload-tc.c
@@ -55543,6 +55833,460 @@ index 8237a85ddb..13ab06d116 100644
bool recirc_id_shared_with_tc; /* Indicates whever tc chains will be in
* sync with datapath recirc ids. */
+diff --git a/lib/netdev-vport-private.h b/lib/netdev-vport-private.h
+index d89a28c66c..586231057c 100644
+--- a/lib/netdev-vport-private.h
++++ b/lib/netdev-vport-private.h
+@@ -22,11 +22,17 @@
+ #include "compiler.h"
+ #include "netdev.h"
+ #include "netdev-provider.h"
++#include "ovs-atomic.h"
+ #include "ovs-thread.h"
+
+ struct netdev_vport {
+ struct netdev up;
+
++ OVSRCU_TYPE(const struct netdev_tunnel_config *) tnl_cfg;
++
++ /* Sequence number for outgoing GRE packets. */
++ atomic_count gre_seqno;
++
+ /* Protects all members below. */
+ struct ovs_mutex mutex;
+
+@@ -34,7 +40,6 @@ struct netdev_vport {
+ struct netdev_stats stats;
+
+ /* Tunnels. */
+- struct netdev_tunnel_config tnl_cfg;
+ char egress_iface[IFNAMSIZ];
+ bool carrier_status;
+
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
+index 64331f74bf..aef8cf6a9f 100644
+--- a/lib/netdev-vport.c
++++ b/lib/netdev-vport.c
+@@ -37,6 +37,7 @@
+ #include "netdev-provider.h"
+ #include "netdev-vport-private.h"
+ #include "openvswitch/dynamic-string.h"
++#include "ovs-atomic.h"
+ #include "ovs-router.h"
+ #include "packets.h"
+ #include "openvswitch/poll-loop.h"
+@@ -68,8 +69,8 @@ static int get_patch_config(const struct netdev *netdev, struct smap *args);
+ static int get_tunnel_config(const struct netdev *, struct smap *args);
+ static bool tunnel_check_status_change__(struct netdev_vport *);
+ static void update_vxlan_global_cfg(struct netdev *,
+- struct netdev_tunnel_config *,
+- struct netdev_tunnel_config *);
++ const struct netdev_tunnel_config *,
++ const struct netdev_tunnel_config *);
+
+ struct vport_class {
+ const char *dpif_port;
+@@ -90,10 +91,16 @@ vport_class_cast(const struct netdev_class *class)
+ return CONTAINER_OF(class, struct vport_class, netdev_class);
+ }
+
++static const struct netdev_tunnel_config *
++vport_tunnel_config(struct netdev_vport *netdev)
++{
++ return ovsrcu_get(const struct netdev_tunnel_config *, &netdev->tnl_cfg);
++}
++
+ static const struct netdev_tunnel_config *
+ get_netdev_tunnel_config(const struct netdev *netdev)
+ {
+- return &netdev_vport_cast(netdev)->tnl_cfg;
++ return vport_tunnel_config(netdev_vport_cast(netdev));
+ }
+
+ bool
+@@ -134,8 +141,6 @@ netdev_vport_get_dpif_port(const struct netdev *netdev,
+ }
+
+ if (netdev_vport_needs_dst_port(netdev)) {
+- const struct netdev_vport *vport = netdev_vport_cast(netdev);
+-
+ /*
+ * Note: IFNAMSIZ is 16 bytes long. Implementations should choose
+ * a dpif port name that is short enough to fit including any
+@@ -144,7 +149,7 @@ netdev_vport_get_dpif_port(const struct netdev *netdev,
+ BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ);
+ ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ);
+ snprintf(namebuf, bufsize, "%s_%d", dpif_port,
+- ntohs(vport->tnl_cfg.dst_port));
++ ntohs(netdev_get_tunnel_config(netdev)->dst_port));
+ return namebuf;
+ } else {
+ return dpif_port;
+@@ -162,12 +167,14 @@ netdev_vport_route_changed(void)
+
+ vports = netdev_get_vports(&n_vports);
+ for (i = 0; i < n_vports; i++) {
++ const struct netdev_tunnel_config *tnl_cfg;
+ struct netdev *netdev_ = vports[i];
+ struct netdev_vport *netdev = netdev_vport_cast(netdev_);
+
+ ovs_mutex_lock(&netdev->mutex);
+ /* Finds all tunnel vports. */
+- if (ipv6_addr_is_set(&netdev->tnl_cfg.ipv6_dst)) {
++ tnl_cfg = netdev_get_tunnel_config(netdev_);
++ if (tnl_cfg && ipv6_addr_is_set(&tnl_cfg->ipv6_dst)) {
+ if (tunnel_check_status_change__(netdev)) {
+ netdev_change_seq_changed(netdev_);
+ }
+@@ -198,6 +205,7 @@ netdev_vport_construct(struct netdev *netdev_)
+ uint16_t port = 0;
+
+ ovs_mutex_init(&dev->mutex);
++ atomic_count_init(&dev->gre_seqno, 0);
+ eth_addr_random(&dev->etheraddr);
+
+ if (name && dpif_port && (strlen(name) > strlen(dpif_port) + 1) &&
+@@ -206,26 +214,31 @@ netdev_vport_construct(struct netdev *netdev_)
+ port = atoi(p);
+ }
+
++ struct netdev_tunnel_config *tnl_cfg = xzalloc(sizeof *tnl_cfg);
++
+ /* If a destination port for tunnel ports is specified in the netdev
+ * name, use it instead of the default one. Otherwise, use the default
+ * destination port */
+ if (!strcmp(type, "geneve")) {
+- dev->tnl_cfg.dst_port = port ? htons(port) : htons(GENEVE_DST_PORT);
++ tnl_cfg->dst_port = port ? htons(port) : htons(GENEVE_DST_PORT);
+ } else if (!strcmp(type, "vxlan")) {
+- dev->tnl_cfg.dst_port = port ? htons(port) : htons(VXLAN_DST_PORT);
+- update_vxlan_global_cfg(netdev_, NULL, &dev->tnl_cfg);
++ tnl_cfg->dst_port = port ? htons(port) : htons(VXLAN_DST_PORT);
++ update_vxlan_global_cfg(netdev_, NULL, tnl_cfg);
+ } else if (!strcmp(type, "lisp")) {
+- dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT);
++ tnl_cfg->dst_port = port ? htons(port) : htons(LISP_DST_PORT);
+ } else if (!strcmp(type, "stt")) {
+- dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT);
++ tnl_cfg->dst_port = port ? htons(port) : htons(STT_DST_PORT);
+ } else if (!strcmp(type, "gtpu")) {
+- dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
++ tnl_cfg->dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
+ } else if (!strcmp(type, "bareudp")) {
+- dev->tnl_cfg.dst_port = htons(port);
++ tnl_cfg->dst_port = htons(port);
+ }
+
+- dev->tnl_cfg.dont_fragment = true;
+- dev->tnl_cfg.ttl = DEFAULT_TTL;
++ tnl_cfg->dont_fragment = true;
++ tnl_cfg->ttl = DEFAULT_TTL;
++
++ ovsrcu_set(&dev->tnl_cfg, tnl_cfg);
++
+ return 0;
+ }
+
+@@ -233,12 +246,15 @@ static void
+ netdev_vport_destruct(struct netdev *netdev_)
+ {
+ struct netdev_vport *netdev = netdev_vport_cast(netdev_);
++ const struct netdev_tunnel_config *tnl_cfg = vport_tunnel_config(netdev);
+ const char *type = netdev_get_type(netdev_);
+
+ if (!strcmp(type, "vxlan")) {
+- update_vxlan_global_cfg(netdev_, &netdev->tnl_cfg, NULL);
++ update_vxlan_global_cfg(netdev_, tnl_cfg, NULL);
+ }
+
++ ovsrcu_set(&netdev->tnl_cfg, NULL);
++ ovsrcu_postpone(free, CONST_CAST(struct netdev_tunnel_config *, tnl_cfg));
+ free(netdev->peer);
+ ovs_mutex_destroy(&netdev->mutex);
+ }
+@@ -281,15 +297,16 @@ static bool
+ tunnel_check_status_change__(struct netdev_vport *netdev)
+ OVS_REQUIRES(netdev->mutex)
+ {
++ const struct netdev_tunnel_config *tnl_cfg = vport_tunnel_config(netdev);
++ const struct in6_addr *route;
+ char iface[IFNAMSIZ];
+ bool status = false;
+- struct in6_addr *route;
+ struct in6_addr gw;
+ uint32_t mark;
+
+ iface[0] = '\0';
+- route = &netdev->tnl_cfg.ipv6_dst;
+- mark = netdev->tnl_cfg.egress_pkt_mark;
++ route = &tnl_cfg->ipv6_dst;
++ mark = tnl_cfg->egress_pkt_mark;
+ if (ovs_router_lookup(mark, route, iface, NULL, &gw)) {
+ struct netdev *egress_netdev;
+
+@@ -465,8 +482,8 @@ vxlan_get_port_ext_gbp_str(uint16_t port, bool gbp,
+
+ static void
+ update_vxlan_global_cfg(struct netdev *netdev,
+- struct netdev_tunnel_config *old_cfg,
+- struct netdev_tunnel_config *new_cfg)
++ const struct netdev_tunnel_config *old_cfg,
++ const struct netdev_tunnel_config *new_cfg)
+ {
+ unsigned int count;
+ char namebuf[20];
+@@ -510,19 +527,20 @@ static bool
+ is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev,
+ const struct netdev_tunnel_config *tnl_cfg)
+ {
+- char namebuf[20];
+- const char *type = netdev_get_type(&dev->up);
++ const struct netdev_tunnel_config *dev_tnl_cfg = vport_tunnel_config(dev);
+ struct vport_class *vclass = vport_class_cast(netdev_get_class(&dev->up));
++ const char *type = netdev_get_type(&dev->up);
++ char namebuf[20];
+
+ if (strcmp(type, "vxlan")) {
+ return false;
+ }
+
+- if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port &&
+- (dev->tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GBP)) ==
++ if (dev_tnl_cfg->dst_port == tnl_cfg->dst_port &&
++ (dev_tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) ==
+ (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP))) {
+
+- if (ntohs(dev->tnl_cfg.dst_port) == VXLAN_DST_PORT) {
++ if (ntohs(dev_tnl_cfg->dst_port) == VXLAN_DST_PORT) {
+ /* Special case where we kept the default port/gbp, only ok if
+ the opposite of the default does not exits */
+ vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port),
+@@ -538,9 +556,9 @@ is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev,
+ }
+
+ /* Same port: ok if no one is left with the previous configuration */
+- if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port) {
+- vxlan_get_port_ext_gbp_str(ntohs(dev->tnl_cfg.dst_port),
+- dev->tnl_cfg.exts &
++ if (dev_tnl_cfg->dst_port == tnl_cfg->dst_port) {
++ vxlan_get_port_ext_gbp_str(ntohs(dev_tnl_cfg->dst_port),
++ dev_tnl_cfg->exts &
+ (1 << OVS_VXLAN_EXT_GBP),
+ namebuf, sizeof(namebuf));
+
+@@ -568,6 +586,7 @@ static int
+ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
+ {
+ struct netdev_vport *dev = netdev_vport_cast(dev_);
++ const struct netdev_tunnel_config *curr_tnl_cfg;
+ const char *name = netdev_get_name(dev_);
+ const char *type = netdev_get_type(dev_);
+ struct ds errors = DS_EMPTY_INITIALIZER;
+@@ -858,11 +877,16 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
+ err = EEXIST;
+ goto out;
+ }
+- update_vxlan_global_cfg(dev_, &dev->tnl_cfg, &tnl_cfg);
+
+ ovs_mutex_lock(&dev->mutex);
+- if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) {
+- dev->tnl_cfg = tnl_cfg;
++
++ curr_tnl_cfg = vport_tunnel_config(dev);
++ update_vxlan_global_cfg(dev_, curr_tnl_cfg, &tnl_cfg);
++
++ if (memcmp(curr_tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) {
++ ovsrcu_set(&dev->tnl_cfg, xmemdup(&tnl_cfg, sizeof tnl_cfg));
++ ovsrcu_postpone(free, CONST_CAST(struct netdev_tunnel_config *,
++ curr_tnl_cfg));
+ tunnel_check_status_change__(dev);
+ netdev_change_seq_changed(dev_);
+ }
+@@ -887,61 +911,60 @@ out:
+ static int
+ get_tunnel_config(const struct netdev *dev, struct smap *args)
+ {
+- struct netdev_vport *netdev = netdev_vport_cast(dev);
++ const struct netdev_tunnel_config *tnl_cfg = netdev_get_tunnel_config(dev);
+ const char *type = netdev_get_type(dev);
+- struct netdev_tunnel_config tnl_cfg;
+
+- ovs_mutex_lock(&netdev->mutex);
+- tnl_cfg = netdev->tnl_cfg;
+- ovs_mutex_unlock(&netdev->mutex);
++ if (!tnl_cfg) {
++ return 0;
++ }
+
+- if (ipv6_addr_is_set(&tnl_cfg.ipv6_dst)) {
+- smap_add_ipv6(args, "remote_ip", &tnl_cfg.ipv6_dst);
+- } else if (tnl_cfg.ip_dst_flow) {
++ if (ipv6_addr_is_set(&tnl_cfg->ipv6_dst)) {
++ smap_add_ipv6(args, "remote_ip", &tnl_cfg->ipv6_dst);
++ } else if (tnl_cfg->ip_dst_flow) {
+ smap_add(args, "remote_ip", "flow");
+ }
+
+- if (ipv6_addr_is_set(&tnl_cfg.ipv6_src)) {
+- smap_add_ipv6(args, "local_ip", &tnl_cfg.ipv6_src);
+- } else if (tnl_cfg.ip_src_flow) {
++ if (ipv6_addr_is_set(&tnl_cfg->ipv6_src)) {
++ smap_add_ipv6(args, "local_ip", &tnl_cfg->ipv6_src);
++ } else if (tnl_cfg->ip_src_flow) {
+ smap_add(args, "local_ip", "flow");
+ }
+
+- if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) {
++ if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) {
+ smap_add(args, "key", "flow");
+- } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present
+- && tnl_cfg.in_key == tnl_cfg.out_key) {
+- smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key));
++ } else if (tnl_cfg->in_key_present && tnl_cfg->out_key_present
++ && tnl_cfg->in_key == tnl_cfg->out_key) {
++ smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg->in_key));
+ } else {
+- if (tnl_cfg.in_key_flow) {
++ if (tnl_cfg->in_key_flow) {
+ smap_add(args, "in_key", "flow");
+- } else if (tnl_cfg.in_key_present) {
++ } else if (tnl_cfg->in_key_present) {
+ smap_add_format(args, "in_key", "%"PRIu64,
+- ntohll(tnl_cfg.in_key));
++ ntohll(tnl_cfg->in_key));
+ }
+
+- if (tnl_cfg.out_key_flow) {
++ if (tnl_cfg->out_key_flow) {
+ smap_add(args, "out_key", "flow");
+- } else if (tnl_cfg.out_key_present) {
++ } else if (tnl_cfg->out_key_present) {
+ smap_add_format(args, "out_key", "%"PRIu64,
+- ntohll(tnl_cfg.out_key));
++ ntohll(tnl_cfg->out_key));
+ }
+ }
+
+- if (tnl_cfg.ttl_inherit) {
++ if (tnl_cfg->ttl_inherit) {
+ smap_add(args, "ttl", "inherit");
+- } else if (tnl_cfg.ttl != DEFAULT_TTL) {
+- smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl);
++ } else if (tnl_cfg->ttl != DEFAULT_TTL) {
++ smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg->ttl);
+ }
+
+- if (tnl_cfg.tos_inherit) {
++ if (tnl_cfg->tos_inherit) {
+ smap_add(args, "tos", "inherit");
+- } else if (tnl_cfg.tos) {
+- smap_add_format(args, "tos", "0x%x", tnl_cfg.tos);
++ } else if (tnl_cfg->tos) {
++ smap_add_format(args, "tos", "0x%x", tnl_cfg->tos);
+ }
+
+- if (tnl_cfg.dst_port) {
+- uint16_t dst_port = ntohs(tnl_cfg.dst_port);
++ if (tnl_cfg->dst_port) {
++ uint16_t dst_port = ntohs(tnl_cfg->dst_port);
+
+ if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
+ (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
+@@ -953,33 +976,33 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
+ }
+ }
+
+- if (tnl_cfg.csum) {
++ if (tnl_cfg->csum) {
+ smap_add(args, "csum", "true");
+ }
+
+- if (tnl_cfg.set_seq) {
++ if (tnl_cfg->set_seq) {
+ smap_add(args, "seq", "true");
+ }
+
+- enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg);
+- if (tnl_cfg.pt_mode != default_pt_mode(layers)) {
++ enum tunnel_layers layers = tunnel_supported_layers(type, tnl_cfg);
++ if (tnl_cfg->pt_mode != default_pt_mode(layers)) {
+ smap_add(args, "packet_type",
+- tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2"
+- : tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3"
++ tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2"
++ : tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3"
+ : "ptap");
+ }
+
+- if (!tnl_cfg.dont_fragment) {
++ if (!tnl_cfg->dont_fragment) {
+ smap_add(args, "df_default", "false");
+ }
+
+- if (tnl_cfg.set_egress_pkt_mark) {
++ if (tnl_cfg->set_egress_pkt_mark) {
+ smap_add_format(args, "egress_pkt_mark",
+- "%"PRIu32, tnl_cfg.egress_pkt_mark);
++ "%"PRIu32, tnl_cfg->egress_pkt_mark);
+ }
+
+ if (!strcmp("erspan", type) || !strcmp("ip6erspan", type)) {
+- if (tnl_cfg.erspan_ver_flow) {
++ if (tnl_cfg->erspan_ver_flow) {
+ /* since version number is not determined,
+ * assume print all other as flow
+ */
+@@ -988,27 +1011,27 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
+ smap_add(args, "erspan_dir", "flow");
+ smap_add(args, "erspan_hwid", "flow");
+ } else {
+- smap_add_format(args, "erspan_ver", "%d", tnl_cfg.erspan_ver);
++ smap_add_format(args, "erspan_ver", "%d", tnl_cfg->erspan_ver);
+
+- if (tnl_cfg.erspan_ver == 1) {
+- if (tnl_cfg.erspan_idx_flow) {
++ if (tnl_cfg->erspan_ver == 1) {
++ if (tnl_cfg->erspan_idx_flow) {
+ smap_add(args, "erspan_idx", "flow");
+ } else {
+ smap_add_format(args, "erspan_idx", "0x%x",
+- tnl_cfg.erspan_idx);
++ tnl_cfg->erspan_idx);
+ }
+- } else if (tnl_cfg.erspan_ver == 2) {
+- if (tnl_cfg.erspan_dir_flow) {
++ } else if (tnl_cfg->erspan_ver == 2) {
++ if (tnl_cfg->erspan_dir_flow) {
+ smap_add(args, "erspan_dir", "flow");
+ } else {
+ smap_add_format(args, "erspan_dir", "%d",
+- tnl_cfg.erspan_dir);
++ tnl_cfg->erspan_dir);
+ }
+- if (tnl_cfg.erspan_hwid_flow) {
++ if (tnl_cfg->erspan_hwid_flow) {
+ smap_add(args, "erspan_hwid", "flow");
+ } else {
+ smap_add_format(args, "erspan_hwid", "0x%x",
+- tnl_cfg.erspan_hwid);
++ tnl_cfg->erspan_hwid);
+ }
+ }
+ }
+@@ -1138,9 +1161,11 @@ netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+ static enum netdev_pt_mode
+ netdev_vport_get_pt_mode(const struct netdev *netdev)
+ {
+- struct netdev_vport *dev = netdev_vport_cast(netdev);
++ const struct netdev_tunnel_config *tnl_cfg;
++
++ tnl_cfg = netdev_get_tunnel_config(netdev);
+
+- return dev->tnl_cfg.pt_mode;
++ return tnl_cfg ? tnl_cfg->pt_mode : NETDEV_PT_UNKNOWN;
+ }
+
+
diff --git a/lib/netdev-windows.c b/lib/netdev-windows.c
index 4ad45ffa1b..3fad501e3e 100644
--- a/lib/netdev-windows.c
@@ -55633,6 +56377,28 @@ index 8305f6c427..c797783782 100644
netdev->node = shash_add(&netdev_shash, name, netdev);
/* By default enable one tx and rx queue per netdev. */
+diff --git a/lib/netdev.h b/lib/netdev.h
+index acf174927d..47c15bde7e 100644
+--- a/lib/netdev.h
++++ b/lib/netdev.h
+@@ -72,6 +72,9 @@ struct sset;
+ struct ovs_action_push_tnl;
+
+ enum netdev_pt_mode {
++ /* Unknown mode. The netdev is not configured yet. */
++ NETDEV_PT_UNKNOWN = 0,
++
+ /* The netdev is packet type aware. It can potentially carry any kind of
+ * packet. This "modern" mode is appropriate for both netdevs that handle
+ * only a single kind of packet (such as a virtual or physical Ethernet
+@@ -130,7 +133,6 @@ struct netdev_tunnel_config {
+ enum netdev_pt_mode pt_mode;
+
+ bool set_seq;
+- uint32_t seqno;
+ uint32_t erspan_idx;
+ uint8_t erspan_ver;
+ uint8_t erspan_dir;
diff --git a/lib/netlink.c b/lib/netlink.c
index 8204025a56..6215282d6f 100644
--- a/lib/netlink.c
@@ -57113,9 +57879,18 @@ index 5e646e6607..8db7bea7c9 100644
void simap_destroy(struct simap *);
void simap_swap(struct simap *, struct simap *);
diff --git a/lib/smap.c b/lib/smap.c
-index e82261497c..b23eeb52d3 100644
+index e82261497c..49f1f2a3a2 100644
--- a/lib/smap.c
+++ b/lib/smap.c
+@@ -100,7 +100,7 @@ smap_add_format(struct smap *smap, const char *key, const char *format, ...)
+ /* Adds 'key' paired with a string representation of 'addr'. It is the
+ * caller's responsibility to avoid duplicate keys if desirable. */
+ void
+-smap_add_ipv6(struct smap *smap, const char *key, struct in6_addr *addr)
++smap_add_ipv6(struct smap *smap, const char *key, const struct in6_addr *addr)
+ {
+ char buf[INET6_ADDRSTRLEN];
+ ipv6_string_mapped(buf, addr);
@@ -185,9 +185,9 @@ smap_steal(struct smap *smap, struct smap_node *node,
void
smap_clear(struct smap *smap)
@@ -57129,7 +57904,7 @@ index e82261497c..b23eeb52d3 100644
}
}
diff --git a/lib/smap.h b/lib/smap.h
-index a921159667..2fe6c540a7 100644
+index a921159667..d1d2ae6f20 100644
--- a/lib/smap.h
+++ b/lib/smap.h
@@ -45,13 +45,24 @@ struct smap_node {
@@ -57159,6 +57934,15 @@ index a921159667..2fe6c540a7 100644
/* Initializer for an immutable struct smap 'SMAP' that contains one or two
* key-value pairs, e.g.
*
+@@ -89,7 +100,7 @@ struct smap_node *smap_add_nocopy(struct smap *, char *, char *);
+ bool smap_add_once(struct smap *, const char *, const char *);
+ void smap_add_format(struct smap *, const char *key, const char *, ...)
+ OVS_PRINTF_FORMAT(3, 4);
+-void smap_add_ipv6(struct smap *, const char *, struct in6_addr *);
++void smap_add_ipv6(struct smap *, const char *, const struct in6_addr *);
+ void smap_replace(struct smap *, const char *, const char *);
+ void smap_replace_nocopy(struct smap *, const char *, char *);
+
diff --git a/lib/socket-util.c b/lib/socket-util.c
index 4f1ffecf5d..3eb3a3816b 100644
--- a/lib/socket-util.c
@@ -57412,7 +58196,7 @@ index 1c71df1a12..ec567603b1 100644
shash_delete(&stopwatches, node);
free(sw);
diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
-index f4fe3432e7..62da9febb6 100644
+index f4fe3432e7..86747e58ba 100644
--- a/lib/stream-ssl.c
+++ b/lib/stream-ssl.c
@@ -193,7 +193,9 @@ static void ssl_clear_txbuf(struct ssl_stream *);
@@ -57437,10 +58221,18 @@ index f4fe3432e7..62da9febb6 100644
if (!peer_cert) {
return NULL;
}
-@@ -1070,7 +1076,11 @@ do_ssl_init(void)
+@@ -1069,8 +1075,18 @@ do_ssl_init(void)
+ VLOG_ERR("SSL_CTX_new: %s", ERR_error_string(ERR_get_error(), NULL));
return ENOPROTOOPT;
}
- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
++
++ long options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
++#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
++ options |= SSL_OP_IGNORE_UNEXPECTED_EOF;
++#endif
++ SSL_CTX_set_options(ctx, options);
++
+#if OPENSSL_VERSION_NUMBER < 0x3000000fL
SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback);
+#else
@@ -57449,7 +58241,7 @@ index f4fe3432e7..62da9febb6 100644
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
-@@ -1081,6 +1091,7 @@ do_ssl_init(void)
+@@ -1081,6 +1097,7 @@ do_ssl_init(void)
return 0;
}
@@ -57457,7 +58249,7 @@ index f4fe3432e7..62da9febb6 100644
static DH *
tmp_dh_callback(SSL *ssl OVS_UNUSED, int is_export OVS_UNUSED, int keylength)
{
-@@ -1112,6 +1123,7 @@ tmp_dh_callback(SSL *ssl OVS_UNUSED, int is_export OVS_UNUSED, int keylength)
+@@ -1112,6 +1129,7 @@ tmp_dh_callback(SSL *ssl OVS_UNUSED, int is_export OVS_UNUSED, int keylength)
keylength);
return NULL;
}
@@ -68461,7 +69253,7 @@ index 784bada12c..3bf339582d 100644
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at
-index 86d633ac4f..f0aaae63eb 100644
+index 86d633ac4f..a9c7398e1c 100644
--- a/tests/system-kmod-macros.at
+++ b/tests/system-kmod-macros.at
@@ -200,6 +200,13 @@ m4_define([OVS_CHECK_KERNEL_EXCL],
@@ -68478,11 +69270,25 @@ index 86d633ac4f..f0aaae63eb 100644
# VSCTL_ADD_DATAPATH_TABLE()
#
# Create system datapath table "system" for kernel tests in ovsdb
+@@ -215,3 +222,13 @@ m4_define([VSCTL_ADD_DATAPATH_TABLE],
+ # or necessary for the userspace datapath as it is checking for a kernel
+ # specific regression.
+ m4_define([CHECK_L3L4_CONNTRACK_REASM])
++
++# OVS_CHECK_BAREUDP()
++#
++# The feature needs to be enabled in the kernel configuration (CONFIG_BAREUDP)
++# to work.
++m4_define([OVS_CHECK_BAREUDP],
++[
++ AT_SKIP_IF([! ip link add dev ovs_bareudp0 type bareudp dstport 6635 ethertype mpls_uc 2>&1 >/dev/null])
++ AT_CHECK([ip link del dev ovs_bareudp0])
++])
diff --git a/tests/system-layer3-tunnels.at b/tests/system-layer3-tunnels.at
-index d21fd777dd..c37852b216 100644
+index d21fd777dd..81123f7309 100644
--- a/tests/system-layer3-tunnels.at
+++ b/tests/system-layer3-tunnels.at
-@@ -147,7 +147,7 @@ AT_CHECK([tail -1 stdout], [0],
+@@ -147,14 +147,14 @@ AT_CHECK([tail -1 stdout], [0],
dnl Check GRE tunnel push
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(dst=f9:bc:12:44:34:b6,src=af:55:aa:55:00:03),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.92,proto=1,tos=0,ttl=64,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
@@ -68491,6 +69297,23 @@ index d21fd777dd..c37852b216 100644
])
OVS_VSWITCHD_STOP
+ AT_CLEANUP
+
+ AT_SETUP([layer3 - ping over MPLS Bareudp])
+-OVS_CHECK_MIN_KERNEL(5, 7)
++OVS_CHECK_BAREUDP()
+ OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])])
+ ADD_NAMESPACES(at_ns0, at_ns1)
+
+@@ -202,7 +202,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
+ AT_CLEANUP
+
+ AT_SETUP([layer3 - ping over Bareudp])
+-OVS_CHECK_MIN_KERNEL(5, 7)
++OVS_CHECK_BAREUDP()
+ OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])])
+ ADD_NAMESPACES(at_ns0, at_ns1)
+
diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
index 80bc1dd5c3..bf60e4cb27 100644
--- a/tests/system-offloads-traffic.at
@@ -69777,7 +70600,7 @@ index 406334f3e0..1a80047619 100644
+
+m4_define([CHECK_SYSTEM_TSO], [])
diff --git a/tests/system-userspace-macros.at b/tests/system-userspace-macros.at
-index f639ba53a2..da3a4caca9 100644
+index f639ba53a2..53c86ef2fd 100644
--- a/tests/system-userspace-macros.at
+++ b/tests/system-userspace-macros.at
@@ -299,6 +299,11 @@ m4_define([OVS_CHECK_KERNEL_EXCL],
@@ -69792,6 +70615,18 @@ index f639ba53a2..da3a4caca9 100644
# VSCTL_ADD_DATAPATH_TABLE()
#
# Create datapath table "netdev" for userspace tests in ovsdb
+@@ -318,3 +323,11 @@ m4_define([CHECK_L3L4_CONNTRACK_REASM],
+ [
+ AT_SKIP_IF([:])
+ ])
++
++# OVS_CHECK_BAREUDP()
++#
++# The userspace datapath does not support bareudp tunnels.
++m4_define([OVS_CHECK_BAREUDP],
++[
++ AT_SKIP_IF([:])
++])
diff --git a/tests/test-cmap.c b/tests/test-cmap.c
index 0705475606..588a5dea63 100644
--- a/tests/test-cmap.c
@@ -71246,6 +72081,23 @@ index 62928d50fc..763ece2a78 100644
diff --git a/utilities/ovs-appctl-bashcomp.bash b/utilities/ovs-appctl-bashcomp.bash
old mode 100755
new mode 100644
+index 4384be8ae1..0a9af1a18f
+--- a/utilities/ovs-appctl-bashcomp.bash
++++ b/utilities/ovs-appctl-bashcomp.bash
+@@ -223,6 +223,13 @@ printf_stderr() {
+ # The code below is taken from Peter Amidon. His change makes it more
+ # robust.
+ extract_bash_prompt() {
++ # On Bash 4.4+ just use the @P expansion
++ if ((BASH_VERSINFO[0] > 4 ||
++ (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4))); then
++ _BASH_PROMPT="${PS1@P}"
++ return
++ fi
++
+ local myPS1 v
+
+ myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")"
diff --git a/utilities/ovs-dpctl-top.in b/utilities/ovs-dpctl-top.in
index fbe6e4f560..2c1766eff5 100755
--- a/utilities/ovs-dpctl-top.in
@@ -71462,6 +72314,23 @@ index 82d1bedfa6..420c11eb8a 100755
diff --git a/utilities/ovs-vsctl-bashcomp.bash b/utilities/ovs-vsctl-bashcomp.bash
old mode 100755
new mode 100644
+index fc8245bfb5..c5ad24fb70
+--- a/utilities/ovs-vsctl-bashcomp.bash
++++ b/utilities/ovs-vsctl-bashcomp.bash
+@@ -413,6 +413,13 @@ _ovs_vsctl_get_PS1 () {
+ return;
+ fi
+
++ # On Bash 4.4+ just use the @P expansion
++ if ((BASH_VERSINFO[0] > 4 ||
++ (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4))); then
++ printf '%s\n' "${PS1@P}"
++ return
++ fi
++
+ # Original inspiration from
+ # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2,
+ # but changed quite a lot to make it more robust.
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index 37cc72d401..1032089fc2 100644
--- a/utilities/ovs-vsctl.c
diff --git a/SPECS/openvswitch2.17.spec b/SPECS/openvswitch2.17.spec
index 535ec71..049c4b8 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: 87%{?dist}
+Release: 88%{?dist}
# Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the
# lib/sflow*.[ch] files are SISSL
@@ -748,6 +748,19 @@ exit 0
%endif
%changelog
+* Mon May 29 2023 Open vSwitch CI - 2.17.0-88
+- Merging upstream branch-2.17 [RH git: c24e4da8ec]
+ Commit list:
+ 5285dad182 utilities/bashcomp: Fix PS1 generation on new bash. (#2170344)
+ ebe7bd7b68 netdev-offload-dpdk: Fix crash in debug log.
+ 4937a53410 stream-ssl: Disable alerts on unexpected EOF.
+ fe99e6b971 tests: layer3-tunnels: Skip bareudp tests if not supported by kernel.
+ a375055f2e ovs-fields: Modify the width of tpa and spa.
+ 749769be30 netdev-vport: RCU-fy tunnel config.
+ c423fa5f6e smap: Make argument of smap_add_ipv6 constant.
+ 2db06ee6f5 netdev-vport: Fix unsafe handling of GRE sequence number.
+
+
* Thu May 11 2023 Open vSwitch CI - 2.17.0-87
- Merging upstream branch-2.17 [RH git: 5ff67cbb44]
Commit list: