From 13a7f780069397976d6ef2138818b407f6e8fde8 Mon Sep 17 00:00:00 2001 From: Alfredo Moralejo Date: Jul 19 2022 08:54:20 +0000 Subject: Import openvswitch2.17-2.17.0-31 from FDP --- diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch index c624213..193d288 100644 --- a/SOURCES/openvswitch-2.17.0.patch +++ b/SOURCES/openvswitch-2.17.0.patch @@ -1,3 +1,16 @@ +diff --git a/.ci/linux-build.sh b/.ci/linux-build.sh +index 6cd38ff3ef..c4ec93a398 100755 +--- a/.ci/linux-build.sh ++++ b/.ci/linux-build.sh +@@ -220,7 +220,7 @@ fi + + if [ "$DPDK" ] || [ "$DPDK_SHARED" ]; then + if [ -z "$DPDK_VER" ]; then +- DPDK_VER="21.11" ++ DPDK_VER="21.11.1" + fi + install_dpdk $DPDK_VER + fi diff --git a/.cirrus.yml b/.cirrus.yml index a7ae793bc4..a4d2a5bbcd 100644 --- a/.cirrus.yml @@ -14,20 +27,48 @@ index a7ae793bc4..a4d2a5bbcd 100644 memory: 4G diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst -index af524251ff..530d36e25a 100644 +index af524251ff..319ee38c7d 100644 --- a/Documentation/faq/releases.rst +++ b/Documentation/faq/releases.rst -@@ -208,8 +208,8 @@ Q: What DPDK version does each Open vSwitch release work with? +@@ -208,9 +208,9 @@ Q: What DPDK version does each Open vSwitch release work with? 2.12.x 18.11.9 2.13.x 19.11.10 2.14.x 19.11.10 - 2.15.x 20.11.1 - 2.16.x 20.11.1 +- 2.17.x 21.11.0 + 2.15.x 20.11.4 + 2.16.x 20.11.4 - 2.17.x 21.11.0 ++ 2.17.x 21.11.1 ============ ======== + Q: Are all the DPDK releases that OVS versions work with maintained? +diff --git a/Documentation/intro/install/dpdk.rst b/Documentation/intro/install/dpdk.rst +index d9f44055db..f8f01bfadd 100644 +--- a/Documentation/intro/install/dpdk.rst ++++ b/Documentation/intro/install/dpdk.rst +@@ -42,7 +42,7 @@ Build requirements + In addition to the requirements described in :doc:`general`, building Open + vSwitch with DPDK will require the following: + +-- DPDK 21.11 ++- DPDK 21.11.1 + + - A `DPDK supported NIC`_ + +@@ -73,9 +73,9 @@ Install DPDK + #. Download the `DPDK sources`_, extract the file and set ``DPDK_DIR``:: + + $ cd /usr/src/ +- $ wget https://fast.dpdk.org/rel/dpdk-21.11.tar.xz +- $ tar xf dpdk-21.11.tar.xz +- $ export DPDK_DIR=/usr/src/dpdk-21.11 ++ $ wget https://fast.dpdk.org/rel/dpdk-21.11.1.tar.xz ++ $ tar xf dpdk-21.11.1.tar.xz ++ $ export DPDK_DIR=/usr/src/dpdk-stable-21.11 + $ cd $DPDK_DIR + + #. Configure and install DPDK using Meson diff --git a/Documentation/intro/install/general.rst b/Documentation/intro/install/general.rst index c4300cd53e..a297aadac8 100644 --- a/Documentation/intro/install/general.rst @@ -42,12 +83,24 @@ index c4300cd53e..a297aadac8 100644 - GNU make. diff --git a/NEWS b/NEWS -index c10e9bfacc..8cae5f7de7 100644 +index c10e9bfacc..7c71284f97 100644 --- a/NEWS +++ b/NEWS -@@ -1,3 +1,21 @@ -+v2.17.2 - xx xxx xxxx +@@ -1,3 +1,33 @@ ++v2.17.3 - xx xxx xxxx ++--------------------- ++ - OVSDB: ++ * New Local_Config schema added to support Connections (--remote) ++ configuration in a clustered databse independently for each server. ++ E.g. for listening on unique addresses. See the ovsdb.local-config.5 ++ manpage for schema details. ++ ++v2.17.2 - 15 Jun 2022 +--------------------- ++ - Bug fixes ++ - DPDK: ++ * OVS validated with DPDK 21.11.1. It is recommended to use this version ++ until further releases. + +v2.17.1 - 08 Apr 2022 +--------------------- @@ -95,7 +148,7 @@ index 0c360fd1ef..61e88105f5 100644 AC_ARG_ENABLE( [sparse], diff --git a/configure.ac b/configure.ac -index 4e9bcce272..9ba141b223 100644 +index 4e9bcce272..5cc3f4801e 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ @@ -103,7 +156,7 @@ index 4e9bcce272..9ba141b223 100644 AC_PREREQ(2.63) -AC_INIT(openvswitch, 2.17.0, bugs@openvswitch.org) -+AC_INIT(openvswitch, 2.17.2, bugs@openvswitch.org) ++AC_INIT(openvswitch, 2.17.3, bugs@openvswitch.org) AC_CONFIG_SRCDIR([datapath/datapath.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) @@ -127,16 +180,179 @@ index 70ac0a0e56..218e7db814 100644 return NDIS_STATUS_SUCCESS; } +diff --git a/datapath-windows/ovsext/PacketIO.c b/datapath-windows/ovsext/PacketIO.c +index cc0840704a..2a206305ec 100644 +--- a/datapath-windows/ovsext/PacketIO.c ++++ b/datapath-windows/ovsext/PacketIO.c +@@ -45,7 +45,9 @@ extern NDIS_STRING ovsExtFriendlyNameUC; + + static VOID OvsFinalizeCompletionList(OvsCompletionList *completionList); + static VOID OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext, +- PNET_BUFFER_LIST netBufferLists, ULONG sendCompleteFlags); ++ PNET_BUFFER_LIST netBufferLists, ++ ULONG sendCompleteFlags, ++ BOOLEAN isSendComplete); + + VOID + OvsInitCompletionList(OvsCompletionList *completionList, +@@ -155,7 +157,7 @@ OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext, + OvsReportNBLIngressError(switchContext, netBufferLists, &filterReason, + NDIS_STATUS_PAUSED); + OvsCompleteNBLIngress(switchContext, netBufferLists, +- sendCompleteFlags); ++ sendCompleteFlags, FALSE); + return; + } + +@@ -175,6 +177,79 @@ OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext, + NDIS_DEFAULT_PORT_NUMBER, sendFlags); + } + ++static __inline BOOLEAN ++OvsCheckNBLSingleSource(PNET_BUFFER_LIST netBufferLists) ++{ ++ UINT32 sourcePortId = 0; ++ BOOLEAN singleSource = TRUE; ++ PNET_BUFFER_LIST curNbl = netBufferLists; ++ PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO info; ++ ++ while (curNbl != NULL) { ++ info = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl); ++ if (info == NULL) { ++ /* We are not able to determine the source port ID */ ++ singleSource = FALSE; ++ OVS_LOG_INFO("nbl %p has no source port", curNbl); ++ break; ++ } ++ if (curNbl == netBufferLists) { ++ sourcePortId = info->SourcePortId; ++ } else if (info->SourcePortId != sourcePortId) { ++ singleSource = FALSE; ++ OVS_LOG_INFO("Source port in nbl %p is %u, not from %u", ++ curNbl, info->SourcePortId, sourcePortId); ++ break; ++ } ++ curNbl = NET_BUFFER_LIST_NEXT_NBL(curNbl); ++ } ++ ++ return singleSource; ++} ++ ++/* ++ * SendNetBufferListsCompleteHandler releases the NetBufferLists with flag ++ * NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE if all the NBLs have same ++ * source port, for cloned NBLs, source port might be changed, although the ++ * cloned NBLs have same source port, there parent NBLs may have different ++ * source ports, so we should have a check before passing the flag to ++ * NdisFSendNetBufferListsComplete. ++ */ ++static __inline VOID ++OvsCompleteUpperLayerNBL(NDIS_HANDLE ndisHandle, ++ PNET_BUFFER_LIST netBufferLists, ++ ULONG sendCompleteFlags, ++ BOOLEAN isSendComplete) ++{ ++ BOOLEAN singleSource = TRUE; ++ PNET_BUFFER_LIST curNbl, nextNbl; ++ ++ /* To check whether the NBLs are from the same source port */ ++ if (isSendComplete && ++ (sendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE)) { ++ singleSource = OvsCheckNBLSingleSource(netBufferLists); ++ } ++ ++ if (singleSource) { ++ NdisFSendNetBufferListsComplete(ndisHandle, ++ netBufferLists, ++ sendCompleteFlags); ++ } else { ++ /* ++ * Not from a single source port, releasing the NBls without flag ++ * NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE doesn't help, so ++ * let's release them one by one. ++ */ ++ for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) { ++ nextNbl = NET_BUFFER_LIST_NEXT_NBL(curNbl); ++ NET_BUFFER_LIST_NEXT_NBL(curNbl) = NULL; ++ NdisFSendNetBufferListsComplete(ndisHandle, ++ curNbl, ++ sendCompleteFlags); ++ } ++ } ++} ++ + static __inline VOID + OvsStartNBLIngressError(POVS_SWITCH_CONTEXT switchContext, + PNET_BUFFER_LIST nblList, +@@ -184,8 +259,8 @@ OvsStartNBLIngressError(POVS_SWITCH_CONTEXT switchContext, + { + ASSERT(error); + OvsReportNBLIngressError(switchContext, nblList, filterReason, error); +- NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, nblList, +- sendCompleteFlags); ++ OvsCompleteUpperLayerNBL(switchContext->NdisFilterHandle, nblList, ++ sendCompleteFlags, FALSE); + } + + static VOID +@@ -427,7 +502,8 @@ OvsExtSendNBL(NDIS_HANDLE filterModuleContext, + static VOID + OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext, + PNET_BUFFER_LIST netBufferLists, +- ULONG sendCompleteFlags) ++ ULONG sendCompleteFlags, ++ BOOLEAN isSendComplete) + { + PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL; + OvsCompletionList newList; +@@ -449,8 +525,10 @@ OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext, + + /* Complete the NBL's that were sent by the upper layer. */ + if (newList.dropNbl != NULL) { +- NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, newList.dropNbl, +- sendCompleteFlags); ++ OvsCompleteUpperLayerNBL(switchContext->NdisFilterHandle, ++ newList.dropNbl, ++ sendCompleteFlags, ++ isSendComplete); + } + } + +@@ -466,7 +544,7 @@ OvsExtSendNBLComplete(NDIS_HANDLE filterModuleContext, + ULONG sendCompleteFlags) + { + OvsCompleteNBLIngress((POVS_SWITCH_CONTEXT)filterModuleContext, +- netBufferLists, sendCompleteFlags); ++ netBufferLists, sendCompleteFlags, TRUE); + } + + +@@ -476,7 +554,8 @@ OvsFinalizeCompletionList(OvsCompletionList *completionList) + if (completionList->dropNbl != NULL) { + OvsCompleteNBLIngress(completionList->switchContext, + completionList->dropNbl, +- completionList->sendCompleteFlags); ++ completionList->sendCompleteFlags, ++ FALSE); + + completionList->dropNbl = NULL; + completionList->dropNblNext = &completionList->dropNbl; diff --git a/debian/changelog b/debian/changelog -index 3e0d3a66e3..c3b2852c28 100644 +index 3e0d3a66e3..5ddd655d6c 100644 --- a/debian/changelog +++ b/debian/changelog -@@ -1,3 +1,15 @@ +@@ -1,3 +1,21 @@ ++openvswitch (2.17.3-1) unstable; urgency=low ++ [ Open vSwitch team ] ++ * New upstream version ++ ++ -- Open vSwitch team Wed, 15 Jun 2022 12:04:07 +0200 ++ +openvswitch (2.17.2-1) unstable; urgency=low + [ Open vSwitch team ] + * New upstream version + -+ -- Open vSwitch team Fri, 08 Apr 2022 14:57:49 +0200 ++ -- Open vSwitch team Wed, 15 Jun 2022 12:04:07 +0200 + +openvswitch (2.17.1-1) unstable; urgency=low + [ Open vSwitch team ] @@ -147,6 +363,27 @@ index 3e0d3a66e3..c3b2852c28 100644 openvswitch (2.17.0-1) unstable; urgency=low * New upstream version +diff --git a/debian/openvswitch-switch.install b/debian/openvswitch-switch.install +index 6a6e9a5435..5ac3df77b1 100644 +--- a/debian/openvswitch-switch.install ++++ b/debian/openvswitch-switch.install +@@ -14,4 +14,5 @@ usr/share/openvswitch/scripts/ovs-check-dead-ifs + usr/share/openvswitch/scripts/ovs-ctl + usr/share/openvswitch/scripts/ovs-kmod-ctl + usr/share/openvswitch/scripts/ovs-save ++usr/share/openvswitch/local-config.ovsschema + usr/share/openvswitch/vswitch.ovsschema +diff --git a/debian/openvswitch-switch.manpages b/debian/openvswitch-switch.manpages +index 7fd7bc55da..088734b0dc 100644 +--- a/debian/openvswitch-switch.manpages ++++ b/debian/openvswitch-switch.manpages +@@ -1,5 +1,6 @@ + ovsdb/ovsdb-server.1 + ovsdb/ovsdb-server.5 ++ovsdb/ovsdb.local-config.5 + debian/tmp/usr/share/man/man8/ovs-ctl.8 + utilities/ovs-dpctl-top.8 + utilities/ovs-dpctl.8 diff --git a/dpdk/lib/vhost/vhost_user.c b/dpdk/lib/vhost/vhost_user.c index a781346c4d..550b0ee8b5 100644 --- a/dpdk/lib/vhost/vhost_user.c @@ -280,6 +517,37 @@ index 0000000000..6fae6f727c + +#endif /* __KERNEL__ || !HAVE_TCA_STATS_PKT64 */ +#endif /* __LINUX_GEN_STATS_WRAPPER_H */ +diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h +index 3054015d93..df10cf579e 100644 +--- a/include/openvswitch/flow.h ++++ b/include/openvswitch/flow.h +@@ -141,15 +141,14 @@ struct flow { + uint8_t nw_tos; /* IP ToS (including DSCP and ECN). */ + uint8_t nw_ttl; /* IP TTL/Hop Limit. */ + uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */ ++ /* L4 (64-bit aligned) */ + struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */ + struct eth_addr arp_sha; /* ARP/ND source hardware address. */ + struct eth_addr arp_tha; /* ARP/ND target hardware address. */ +- ovs_be16 tcp_flags; /* TCP flags/ICMPv6 ND options type. +- * With L3 to avoid matching L4. */ ++ ovs_be16 tcp_flags; /* TCP flags/ICMPv6 ND options type. */ + ovs_be16 pad2; /* Pad to 64 bits. */ + struct ovs_key_nsh nsh; /* Network Service Header keys */ + +- /* L4 (64-bit aligned) */ + ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */ + ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port/ICMP code. */ + ovs_be16 ct_tp_src; /* CT original tuple source port/ICMP type. */ +@@ -179,7 +178,7 @@ BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) + enum { + FLOW_SEGMENT_1_ENDS_AT = offsetof(struct flow, dl_dst), + FLOW_SEGMENT_2_ENDS_AT = offsetof(struct flow, nw_src), +- FLOW_SEGMENT_3_ENDS_AT = offsetof(struct flow, tp_src), ++ FLOW_SEGMENT_3_ENDS_AT = offsetof(struct flow, nd_target), + }; + BUILD_ASSERT_DECL(FLOW_SEGMENT_1_ENDS_AT % sizeof(uint64_t) == 0); + BUILD_ASSERT_DECL(FLOW_SEGMENT_2_ENDS_AT % sizeof(uint64_t) == 0); diff --git a/include/openvswitch/hmap.h b/include/openvswitch/hmap.h index 4e001cc692..beb48295b9 100644 --- a/include/openvswitch/hmap.h @@ -536,6 +804,86 @@ index 8ad5eeb327..6272d340cf 100644 /* Inline implementations. */ +diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h +index 41bcb55d20..b7231c7bb3 100644 +--- a/include/openvswitch/ofp-actions.h ++++ b/include/openvswitch/ofp-actions.h +@@ -218,7 +218,9 @@ struct ofpact *ofpact_next_flattened(const struct ofpact *); + static inline struct ofpact * + ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len) + { +- return ALIGNED_CAST(struct ofpact *, (uint8_t *) ofpacts + ofpacts_len); ++ return ofpacts ++ ? ALIGNED_CAST(struct ofpact *, (uint8_t *) ofpacts + ofpacts_len) ++ : NULL; + } + + static inline bool +diff --git a/include/openvswitch/ofpbuf.h b/include/openvswitch/ofpbuf.h +index 1136ba04c8..32f03ea837 100644 +--- a/include/openvswitch/ofpbuf.h ++++ b/include/openvswitch/ofpbuf.h +@@ -179,7 +179,11 @@ static inline void ofpbuf_delete(struct ofpbuf *b) + static inline void *ofpbuf_at(const struct ofpbuf *b, size_t offset, + size_t size) + { +- return offset + size <= b->size ? (char *) b->data + offset : NULL; ++ if (offset + size <= b->size) { ++ ovs_assert(b->data); ++ return (char *) b->data + offset; ++ } ++ return NULL; + } + + /* Returns a pointer to byte 'offset' in 'b', which must contain at least +@@ -188,20 +192,23 @@ static inline void *ofpbuf_at_assert(const struct ofpbuf *b, size_t offset, + size_t size) + { + ovs_assert(offset + size <= b->size); +- return ((char *) b->data) + offset; ++ ovs_assert(b->data); ++ return (char *) b->data + offset; + } + + /* Returns a pointer to byte following the last byte of data in use in 'b'. */ + static inline void *ofpbuf_tail(const struct ofpbuf *b) + { +- return (char *) b->data + b->size; ++ ovs_assert(b->data || !b->size); ++ return b->data ? (char *) b->data + b->size : NULL; + } + + /* Returns a pointer to byte following the last byte allocated for use (but + * not necessarily in use) in 'b'. */ + static inline void *ofpbuf_end(const struct ofpbuf *b) + { +- return (char *) b->base + b->allocated; ++ ovs_assert(b->base || !b->allocated); ++ return b->base ? (char *) b->base + b->allocated : NULL; + } + + /* Returns the number of bytes of headroom in 'b', that is, the number of bytes +@@ -249,6 +256,11 @@ static inline void *ofpbuf_pull(struct ofpbuf *b, size_t size) + { + ovs_assert(b->size >= size); + void *data = b->data; ++ ++ if (!size) { ++ return data; ++ } ++ + b->data = (char*)b->data + size; + b->size = b->size - size; + return data; +@@ -270,7 +282,7 @@ static inline struct ofpbuf *ofpbuf_from_list(const struct ovs_list *list) + static inline bool ofpbuf_equal(const struct ofpbuf *a, const struct ofpbuf *b) + { + return a->size == b->size && +- memcmp(a->data, b->data, a->size) == 0; ++ (a->size == 0 || memcmp(a->data, b->data, a->size) == 0); + } + + static inline bool ofpbuf_oversized(const struct ofpbuf *ofpacts) diff --git a/include/openvswitch/shash.h b/include/openvswitch/shash.h index c249e13e1f..4e7badd4dc 100644 --- a/include/openvswitch/shash.h @@ -568,7 +916,7 @@ index c249e13e1f..4e7badd4dc 100644 void shash_destroy(struct shash *); void shash_destroy_free_data(struct shash *); diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h -index 228b185c3a..96f600160b 100644 +index 228b185c3a..8e6c46a85f 100644 --- a/include/openvswitch/util.h +++ b/include/openvswitch/util.h @@ -145,6 +145,150 @@ OVS_NO_RETURN void ovs_assert_failure(const char *, const char *, const char *); @@ -722,6 +1070,16 @@ index 228b185c3a..96f600160b 100644 /* Returns the number of elements in ARRAY. */ #define ARRAY_SIZE(ARRAY) __ARRAY_SIZE(ARRAY) +@@ -285,6 +429,9 @@ is_pow2(uintmax_t x) + * segfault, so it is important to be aware of correct alignment. */ + #define ALIGNED_CAST(TYPE, ATTR) ((TYPE) (void *) (ATTR)) + ++#define IS_PTR_ALIGNED(OBJ) \ ++ (!(OBJ) || (uintptr_t) (OBJ) % __alignof__(OVS_TYPEOF(OBJ)) == 0) ++ + #ifdef __cplusplus + } + #endif diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in index a8b0705d9f..631a8fca80 100755 --- a/ipsec/ovs-monitor-ipsec.in @@ -754,6 +1112,28 @@ index a8b0705d9f..631a8fca80 100755 def _nss_clear_database(self): """Remove all OVS IPsec related state from the NSS database""" try: +diff --git a/lib/automake.mk b/lib/automake.mk +index a23cdc4ade..e9a5978e88 100644 +--- a/lib/automake.mk ++++ b/lib/automake.mk +@@ -38,8 +38,6 @@ lib_libopenvswitchavx512_la_CFLAGS = \ + -fPIC \ + $(AM_CFLAGS) + lib_libopenvswitchavx512_la_SOURCES = \ +- lib/cpu.c \ +- lib/cpu.h \ + lib/dpif-netdev-lookup-avx512-gather.c \ + lib/dpif-netdev-extract-avx512.c \ + lib/dpif-netdev-avx512.c +@@ -89,6 +87,8 @@ lib_libopenvswitch_la_SOURCES = \ + lib/conntrack.h \ + lib/coverage.c \ + lib/coverage.h \ ++ lib/cpu.c \ ++ lib/cpu.h \ + lib/crc32c.c \ + lib/crc32c.h \ + lib/csum.c \ diff --git a/lib/cfm.c b/lib/cfm.c index cc43e70e31..c3742f3de2 100644 --- a/lib/cfm.c @@ -840,7 +1220,7 @@ index c502d23112..72e2ec5f71 100644 #define CMAP_CURSOR_FOR_EACH(NODE, MEMBER, CURSOR, CMAP) \ for (*(CURSOR) = cmap_cursor_start(CMAP); \ diff --git a/lib/conntrack.c b/lib/conntrack.c -index 33a1a92953..08da4ddf79 100644 +index 33a1a92953..0103fb5396 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c @@ -1526,14 +1526,14 @@ set_label(struct dp_packet *pkt, struct conn *conn, @@ -860,6 +1240,15 @@ index 33a1a92953..08da4ddf79 100644 ovs_mutex_lock(&conn->lock); if (now < conn->expiration || count >= limit) { min_expiration = MIN(min_expiration, conn->expiration); +@@ -2242,7 +2242,7 @@ nat_range_hash(const struct conn *conn, uint32_t basis, + hash = ct_addr_hash_add(hash, &nat_info->min_addr); + hash = ct_addr_hash_add(hash, &nat_info->max_addr); + hash = hash_add(hash, +- (nat_info->max_port << 16) ++ ((uint32_t) nat_info->max_port << 16) + | nat_info->min_port); + hash = ct_endpoint_hash_add(hash, &conn->key.src); + hash = ct_endpoint_hash_add(hash, &conn->key.dst); @@ -2265,8 +2265,16 @@ set_sport_range(const struct nat_action_info_t *ni, const struct conn_key *k, if (((ni->nat_action & NAT_ACTION_SNAT_ALL) == NAT_ACTION_SRC) || ((ni->nat_action & NAT_ACTION_DST))) { @@ -998,10 +1387,38 @@ index d344514343..1afcc65adb 100644 } diff --git a/lib/dpif-netdev-avx512.c b/lib/dpif-netdev-avx512.c -index b7131ba3f1..11d9a00052 100644 +index b7131ba3f1..82a4138184 100644 --- a/lib/dpif-netdev-avx512.c +++ b/lib/dpif-netdev-avx512.c -@@ -159,7 +159,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, +@@ -20,7 +20,6 @@ + + #include + +-#include "cpu.h" + #include "dpif-netdev.h" + #include "dpif-netdev-perf.h" + #include "dpif-netdev-private.h" +@@ -59,19 +58,6 @@ struct dpif_userdata { + struct pkt_flow_meta pkt_meta[NETDEV_MAX_BURST]; + }; + +-int32_t +-dp_netdev_input_outer_avx512_probe(void) +-{ +- bool avx512f_available = cpu_has_isa(OVS_CPU_ISA_X86_AVX512F); +- bool bmi2_available = cpu_has_isa(OVS_CPU_ISA_X86_BMI2); +- +- if (!avx512f_available || !bmi2_available) { +- return -ENOTSUP; +- } +- +- return 0; +-} +- + int32_t + dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, + struct dp_packet_batch *packets, +@@ -159,7 +145,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, mf_mask = mfex_func(packets, keys, batch_size, in_port, pmd); } @@ -1010,7 +1427,7 @@ index b7131ba3f1..11d9a00052 100644 uint32_t iter = lookup_pkts_bitmask; while (iter) { uint32_t i = raw_ctz(iter); -@@ -183,7 +183,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, +@@ -183,7 +169,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, * classifed by vector mfex else do a scalar miniflow extract * for that packet. */ @@ -1019,7 +1436,7 @@ index b7131ba3f1..11d9a00052 100644 /* Check for a partial hardware offload match. */ if (hwol_enabled) { -@@ -204,7 +204,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, +@@ -204,7 +190,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, pkt_meta[i].bytes = dp_packet_size(packet); phwol_hits++; @@ -1028,7 +1445,7 @@ index b7131ba3f1..11d9a00052 100644 continue; } } -@@ -227,7 +227,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, +@@ -227,7 +213,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, if (f) { rules[i] = &f->cr; emc_hits++; @@ -1037,7 +1454,7 @@ index b7131ba3f1..11d9a00052 100644 continue; } } -@@ -237,7 +237,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, +@@ -237,7 +223,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, if (f) { rules[i] = &f->cr; smc_hits++; @@ -1047,10 +1464,30 @@ index b7131ba3f1..11d9a00052 100644 } } diff --git a/lib/dpif-netdev-extract-avx512.c b/lib/dpif-netdev-extract-avx512.c -index c1c1fefb6a..a0fedb1376 100644 +index c1c1fefb6a..92980ca1b9 100644 --- a/lib/dpif-netdev-extract-avx512.c +++ b/lib/dpif-netdev-extract-avx512.c -@@ -619,7 +619,7 @@ mfex_avx512_process(struct dp_packet_batch *packets, +@@ -42,7 +42,6 @@ + #include + #include + +-#include "cpu.h" + #include "flow.h" + + #include "dpif-netdev-private-dpcls.h" +@@ -544,7 +543,11 @@ mfex_avx512_process(struct dp_packet_batch *packets, + */ + __m512i v512_zeros = _mm512_setzero_si512(); + __m512i v_blk0; ++#if __GNUC__ >= 4 + if (__builtin_constant_p(use_vbmi) && use_vbmi) { ++#else ++ if (use_vbmi) { ++#endif + v_blk0 = _mm512_maskz_permutexvar_epi8_wrap(k_shuf, v_shuf, + v_pkt0); + } else { +@@ -619,7 +622,7 @@ mfex_avx512_process(struct dp_packet_batch *packets, }; /* This packet has its miniflow created, add to hitmask. */ @@ -1059,6 +1496,251 @@ index c1c1fefb6a..a0fedb1376 100644 } return hitmask; +@@ -659,47 +662,5 @@ DECLARE_MFEX_FUNC(ip_udp, PROFILE_ETH_IPV4_UDP) + DECLARE_MFEX_FUNC(ip_tcp, PROFILE_ETH_IPV4_TCP) + DECLARE_MFEX_FUNC(dot1q_ip_udp, PROFILE_ETH_VLAN_IPV4_UDP) + DECLARE_MFEX_FUNC(dot1q_ip_tcp, PROFILE_ETH_VLAN_IPV4_TCP) +- +- +-static int32_t +-avx512_isa_probe(uint32_t needs_vbmi) +-{ +- static enum ovs_cpu_isa isa_required[] = { +- OVS_CPU_ISA_X86_AVX512F, +- OVS_CPU_ISA_X86_AVX512BW, +- OVS_CPU_ISA_X86_BMI2, +- }; +- +- int32_t ret = 0; +- for (uint32_t i = 0; i < ARRAY_SIZE(isa_required); i++) { +- if (!cpu_has_isa(isa_required[i])) { +- ret = -ENOTSUP; +- } +- } +- +- if (needs_vbmi) { +- if (!cpu_has_isa(OVS_CPU_ISA_X86_AVX512VBMI)) { +- ret = -ENOTSUP; +- } +- } +- +- return ret; +-} +- +-/* Probe functions to check ISA requirements. */ +-int32_t +-mfex_avx512_probe(void) +-{ +- const uint32_t needs_vbmi = 0; +- return avx512_isa_probe(needs_vbmi); +-} +- +-int32_t +-mfex_avx512_vbmi_probe(void) +-{ +- const uint32_t needs_vbmi = 1; +- return avx512_isa_probe(needs_vbmi); +-} +- + #endif /* __CHECKER__ */ + #endif /* __x86_64__ */ +diff --git a/lib/dpif-netdev-lookup-avx512-gather.c b/lib/dpif-netdev-lookup-avx512-gather.c +index 7bc1e9e9a5..fb2084392a 100644 +--- a/lib/dpif-netdev-lookup-avx512-gather.c ++++ b/lib/dpif-netdev-lookup-avx512-gather.c +@@ -23,7 +23,6 @@ + #include "dpif-netdev-lookup.h" + + #include "cmap.h" +-#include "cpu.h" + #include "flow.h" + #include "pvector.h" + #include "openvswitch/vlog.h" +@@ -396,18 +395,11 @@ dpcls_avx512_gather_mf_any(struct dpcls_subtable *subtable, uint32_t keys_map, + } + + dpcls_subtable_lookup_func +-dpcls_subtable_avx512_gather_probe(uint32_t u0_bits, uint32_t u1_bits) ++dpcls_subtable_avx512_gather_probe__(uint32_t u0_bits, uint32_t u1_bits, ++ bool use_vpop) + { + dpcls_subtable_lookup_func f = NULL; + +- int avx512f_available = cpu_has_isa(OVS_CPU_ISA_X86_AVX512F); +- int bmi2_available = cpu_has_isa(OVS_CPU_ISA_X86_BMI2); +- if (!avx512f_available || !bmi2_available) { +- return NULL; +- } +- +- int use_vpop = cpu_has_isa(OVS_CPU_ISA_X86_VPOPCNTDQ); +- + CHECK_LOOKUP_FUNCTION(9, 4, use_vpop); + CHECK_LOOKUP_FUNCTION(9, 1, use_vpop); + CHECK_LOOKUP_FUNCTION(5, 3, use_vpop); +diff --git a/lib/dpif-netdev-lookup.c b/lib/dpif-netdev-lookup.c +index bd0a99abe7..b1d2801575 100644 +--- a/lib/dpif-netdev-lookup.c ++++ b/lib/dpif-netdev-lookup.c +@@ -18,10 +18,25 @@ + #include + #include "dpif-netdev-lookup.h" + ++#include "cpu.h" + #include "openvswitch/vlog.h" + + VLOG_DEFINE_THIS_MODULE(dpif_netdev_lookup); + ++#if (__x86_64__ && HAVE_AVX512F && HAVE_LD_AVX512_GOOD && __SSE4_2__) ++static dpcls_subtable_lookup_func ++dpcls_subtable_avx512_gather_probe(uint32_t u0_bits, uint32_t u1_bits) ++{ ++ if (!cpu_has_isa(OVS_CPU_ISA_X86_AVX512F) ++ || !cpu_has_isa(OVS_CPU_ISA_X86_BMI2)) { ++ return NULL; ++ } ++ ++ return dpcls_subtable_avx512_gather_probe__(u0_bits, u1_bits, ++ cpu_has_isa(OVS_CPU_ISA_X86_VPOPCNTDQ)); ++} ++#endif ++ + /* Actual list of implementations goes here */ + static struct dpcls_subtable_lookup_info_t subtable_lookups[] = { + /* The autovalidator implementation will not be used by default, it must +diff --git a/lib/dpif-netdev-lookup.h b/lib/dpif-netdev-lookup.h +index 59f51faa0e..5d2d845945 100644 +--- a/lib/dpif-netdev-lookup.h ++++ b/lib/dpif-netdev-lookup.h +@@ -44,7 +44,8 @@ dpcls_subtable_generic_probe(uint32_t u0_bit_count, uint32_t u1_bit_count); + + /* Probe function for AVX-512 gather implementation */ + dpcls_subtable_lookup_func +-dpcls_subtable_avx512_gather_probe(uint32_t u0_bit_cnt, uint32_t u1_bit_cnt); ++dpcls_subtable_avx512_gather_probe__(uint32_t u0_bit_cnt, uint32_t u1_bit_cnt, ++ bool use_vpop); + + + /* Subtable registration and iteration helpers */ +diff --git a/lib/dpif-netdev-private-dpif.c b/lib/dpif-netdev-private-dpif.c +index 84d4ec156e..5ae119a308 100644 +--- a/lib/dpif-netdev-private-dpif.c ++++ b/lib/dpif-netdev-private-dpif.c +@@ -22,6 +22,7 @@ + #include + #include + ++#include "cpu.h" + #include "openvswitch/dynamic-string.h" + #include "openvswitch/vlog.h" + #include "util.h" +@@ -33,6 +34,19 @@ enum dpif_netdev_impl_info_idx { + DPIF_NETDEV_IMPL_AVX512 + }; + ++#if (__x86_64__ && HAVE_AVX512F && HAVE_LD_AVX512_GOOD && __SSE4_2__) ++static int32_t ++dp_netdev_input_outer_avx512_probe(void) ++{ ++ if (!cpu_has_isa(OVS_CPU_ISA_X86_AVX512F) ++ || !cpu_has_isa(OVS_CPU_ISA_X86_BMI2)) { ++ return -ENOTSUP; ++ } ++ ++ return 0; ++} ++#endif ++ + /* Actual list of implementations goes here. */ + static struct dpif_netdev_impl_info_t dpif_impls[] = { + /* The default scalar C code implementation. */ +diff --git a/lib/dpif-netdev-private-dpif.h b/lib/dpif-netdev-private-dpif.h +index 0da639c55a..3e38630f53 100644 +--- a/lib/dpif-netdev-private-dpif.h ++++ b/lib/dpif-netdev-private-dpif.h +@@ -67,10 +67,7 @@ dp_netdev_input(struct dp_netdev_pmd_thread *pmd, + struct dp_packet_batch *packets, + odp_port_t in_port); + +-/* AVX512 enabled DPIF implementation and probe functions. */ +-int32_t +-dp_netdev_input_outer_avx512_probe(void); +- ++/* AVX512 enabled DPIF implementation function. */ + int32_t + dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd, + struct dp_packet_batch *packets, +diff --git a/lib/dpif-netdev-private-extract.c b/lib/dpif-netdev-private-extract.c +index a29bdcfa78..fe04ea80ff 100644 +--- a/lib/dpif-netdev-private-extract.c ++++ b/lib/dpif-netdev-private-extract.c +@@ -19,6 +19,7 @@ + #include + #include + ++#include "cpu.h" + #include "dp-packet.h" + #include "dpif-netdev-private-dpcls.h" + #include "dpif-netdev-private-extract.h" +@@ -33,6 +34,43 @@ VLOG_DEFINE_THIS_MODULE(dpif_netdev_extract); + /* Variable to hold the default MFEX implementation. */ + static ATOMIC(miniflow_extract_func) default_mfex_func; + ++#if (__x86_64__ && HAVE_AVX512F && HAVE_LD_AVX512_GOOD && __SSE4_2__) ++static int32_t ++avx512_isa_probe(bool needs_vbmi) ++{ ++ static enum ovs_cpu_isa isa_required[] = { ++ OVS_CPU_ISA_X86_AVX512F, ++ OVS_CPU_ISA_X86_AVX512BW, ++ OVS_CPU_ISA_X86_BMI2, ++ }; ++ ++ for (uint32_t i = 0; i < ARRAY_SIZE(isa_required); i++) { ++ if (!cpu_has_isa(isa_required[i])) { ++ return -ENOTSUP; ++ } ++ } ++ ++ if (needs_vbmi && !cpu_has_isa(OVS_CPU_ISA_X86_AVX512VBMI)) { ++ return -ENOTSUP; ++ } ++ ++ return 0; ++} ++ ++/* Probe functions to check ISA requirements. */ ++static int32_t ++mfex_avx512_probe(void) ++{ ++ return avx512_isa_probe(false); ++} ++ ++static int32_t ++mfex_avx512_vbmi_probe(void) ++{ ++ return avx512_isa_probe(true); ++} ++#endif ++ + /* Implementations of available extract options and + * the implementations are always in order of preference. + */ +diff --git a/lib/dpif-netdev-private-extract.h b/lib/dpif-netdev-private-extract.h +index f9a757ba41..3e06148c5a 100644 +--- a/lib/dpif-netdev-private-extract.h ++++ b/lib/dpif-netdev-private-extract.h +@@ -176,10 +176,8 @@ mfex_study_traffic(struct dp_packet_batch *packets, + int + mfex_set_study_pkt_cnt(uint32_t pkt_cmp_count, const char *name); + +-/* AVX512 MFEX Probe and Implementations functions. */ ++/* AVX512 MFEX Implementation functions. */ + #ifdef __x86_64__ +-int32_t mfex_avx512_probe(void); +-int32_t mfex_avx512_vbmi_probe(void); + + #define DECLARE_AVX512_MFEX_PROTOTYPE(name) \ + uint32_t \ diff --git a/lib/dpif-netdev-private-flow.h b/lib/dpif-netdev-private-flow.h index 66016eb099..7425dd44e7 100644 --- a/lib/dpif-netdev-private-flow.h @@ -1072,10 +1754,20 @@ 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..51c276b1d8 100644 +index 9f35713ef5..3d9d8929f7 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c -@@ -1932,13 +1932,13 @@ static void +@@ -93,7 +93,8 @@ VLOG_DEFINE_THIS_MODULE(dpif_netdev); + /* Auto Load Balancing Defaults */ + #define ALB_IMPROVEMENT_THRESHOLD 25 + #define ALB_LOAD_THRESHOLD 95 +-#define ALB_REBALANCE_INTERVAL 1 /* 1 Min */ ++#define ALB_REBALANCE_INTERVAL 1 /* 1 Min */ ++#define MAX_ALB_REBALANCE_INTERVAL 20000 /* 20000 Min */ + #define MIN_TO_MSEC 60000 + + #define FLOW_DUMP_MAX_BATCH 50 +@@ -1932,13 +1933,13 @@ static void dp_netdev_free(struct dp_netdev *dp) OVS_REQUIRES(dp_netdev_mutex) { @@ -1091,7 +1783,7 @@ index 9f35713ef5..51c276b1d8 100644 do_del_port(dp, port); } ovs_rwlock_unlock(&dp->port_rwlock); -@@ -3006,7 +3006,7 @@ static void +@@ -3006,7 +3007,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, @@ -1100,7 +1792,7 @@ index 9f35713ef5..51c276b1d8 100644 { struct dp_offload_thread_item *item; struct dp_offload_flow_item *flow_offload; -@@ -3021,7 +3021,7 @@ queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd, +@@ -3021,7 +3022,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; @@ -1109,7 +1801,7 @@ index 9f35713ef5..51c276b1d8 100644 item->timestamp = pmd->ctx.now; dp_netdev_offload_flow_enqueue(item); -@@ -4095,6 +4095,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd, +@@ -4095,6 +4096,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd, flow->dead = false; flow->batch = NULL; flow->mark = INVALID_FLOW_MARK; @@ -1117,7 +1809,7 @@ index 9f35713ef5..51c276b1d8 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 +4130,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd, +@@ -4129,7 +4131,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd, } queue_netdev_flow_put(pmd, flow, match, actions, actions_len, @@ -1126,7 +1818,7 @@ index 9f35713ef5..51c276b1d8 100644 log_netdev_flow_change(flow, match, NULL, actions, actions_len); return flow; -@@ -4171,7 +4172,7 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd, +@@ -4171,7 +4173,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, @@ -1135,7 +1827,70 @@ index 9f35713ef5..51c276b1d8 100644 DP_NETDEV_FLOW_OFFLOAD_OP_MOD); log_netdev_flow_change(netdev_flow, match, old_actions, put->actions, put->actions_len); -@@ -5684,23 +5685,28 @@ sched_numa_list_put_in_place(struct sched_numa_list *numa_list) +@@ -4778,8 +4780,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; +- uint8_t rebalance_load, cur_rebalance_load; +- uint8_t rebalance_improve; ++ uint8_t cur_rebalance_load; ++ uint32_t rebalance_load, rebalance_improve; + 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) + + struct pmd_auto_lb *pmd_alb = &dp->pmd_alb; + +- rebalance_intvl = smap_get_int(other_config, "pmd-auto-lb-rebal-interval", +- ALB_REBALANCE_INTERVAL); ++ rebalance_intvl = smap_get_ullong(other_config, ++ "pmd-auto-lb-rebal-interval", ++ ALB_REBALANCE_INTERVAL); ++ if (rebalance_intvl > MAX_ALB_REBALANCE_INTERVAL) { ++ rebalance_intvl = ALB_REBALANCE_INTERVAL; ++ } + + /* 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) + log_autolb = true; + } + +- rebalance_improve = smap_get_int(other_config, +- "pmd-auto-lb-improvement-threshold", +- ALB_IMPROVEMENT_THRESHOLD); ++ rebalance_improve = smap_get_uint(other_config, ++ "pmd-auto-lb-improvement-threshold", ++ ALB_IMPROVEMENT_THRESHOLD); + if (rebalance_improve > 100) { + rebalance_improve = ALB_IMPROVEMENT_THRESHOLD; + } + if (rebalance_improve != pmd_alb->rebalance_improve_thresh) { + pmd_alb->rebalance_improve_thresh = rebalance_improve; + VLOG_INFO("PMD auto load balance improvement threshold set to " +- "%"PRIu8"%%", rebalance_improve); ++ "%"PRIu32"%%", rebalance_improve); + log_autolb = true; + } + +- rebalance_load = smap_get_int(other_config, "pmd-auto-lb-load-threshold", +- ALB_LOAD_THRESHOLD); ++ rebalance_load = smap_get_uint(other_config, "pmd-auto-lb-load-threshold", ++ ALB_LOAD_THRESHOLD); + 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) + if (rebalance_load != cur_rebalance_load) { + atomic_store_relaxed(&pmd_alb->rebalance_load_thresh, + rebalance_load); +- VLOG_INFO("PMD auto load balance load threshold set to %"PRIu8"%%", ++ VLOG_INFO("PMD auto load balance load threshold set to %"PRIu32"%%", + rebalance_load); + log_autolb = true; + } +@@ -5684,23 +5690,28 @@ sched_numa_list_put_in_place(struct sched_numa_list *numa_list) } } @@ -1169,7 +1924,7 @@ index 9f35713ef5..51c276b1d8 100644 rxq->pmd->numa_id != netdev_get_numa_id(rxq->port->netdev)) { return true; -@@ -6000,10 +6006,10 @@ sched_numa_list_schedule(struct sched_numa_list *numa_list, +@@ -6000,10 +6011,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); @@ -1181,7 +1936,7 @@ index 9f35713ef5..51c276b1d8 100644 numa = NULL; } } -@@ -6111,7 +6117,7 @@ sched_numa_list_variance(struct sched_numa_list *numa_list) +@@ -6111,7 +6122,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 @@ -1190,7 +1945,7 @@ index 9f35713ef5..51c276b1d8 100644 OVS_REQ_RDLOCK(dp->port_rwlock) { struct dp_netdev_pmd_thread *pmd; -@@ -6342,11 +6348,11 @@ pmd_remove_stale_ports(struct dp_netdev *dp, +@@ -6342,11 +6353,11 @@ pmd_remove_stale_ports(struct dp_netdev *dp, OVS_EXCLUDED(pmd->port_mutex) OVS_REQ_RDLOCK(dp->port_rwlock) { @@ -1205,7 +1960,7 @@ index 9f35713ef5..51c276b1d8 100644 struct dp_netdev_port *port = poll->rxq->port; if (port->need_reconfigure -@@ -6354,7 +6360,7 @@ pmd_remove_stale_ports(struct dp_netdev *dp, +@@ -6354,7 +6365,7 @@ pmd_remove_stale_ports(struct dp_netdev *dp, dp_netdev_del_rxq_from_pmd(pmd, poll); } } @@ -1214,7 +1969,7 @@ index 9f35713ef5..51c276b1d8 100644 struct dp_netdev_port *port = tx->port; if (port->need_reconfigure -@@ -6430,8 +6436,7 @@ reconfigure_datapath(struct dp_netdev *dp) +@@ -6430,8 +6441,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. */ @@ -1224,7 +1979,7 @@ index 9f35713ef5..51c276b1d8 100644 int err; if (!port->need_reconfigure) { -@@ -6487,10 +6492,10 @@ reconfigure_datapath(struct dp_netdev *dp) +@@ -6487,10 +6497,10 @@ reconfigure_datapath(struct dp_netdev *dp) } CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { @@ -1237,7 +1992,7 @@ index 9f35713ef5..51c276b1d8 100644 if (poll->rxq->pmd != pmd) { dp_netdev_del_rxq_from_pmd(pmd, poll); -@@ -6682,7 +6687,7 @@ dpif_netdev_run(struct dpif *dpif) +@@ -6682,7 +6692,7 @@ dpif_netdev_run(struct dpif *dpif) if (pmd_rebalance && !dp_netdev_is_reconf_required(dp) && !ports_require_restart(dp) && @@ -1246,7 +2001,7 @@ index 9f35713ef5..51c276b1d8 100644 pmd_rebalance_dry_run(dp)) { VLOG_INFO("PMD auto load balance dry run. " "Requesting datapath reconfigure."); -@@ -7364,15 +7369,15 @@ static struct dp_netdev_pmd_thread * +@@ -7364,15 +7374,15 @@ static struct dp_netdev_pmd_thread * dp_netdev_get_pmd(struct dp_netdev *dp, unsigned core_id) { struct dp_netdev_pmd_thread *pmd; @@ -1268,6 +2023,68 @@ index 9f35713ef5..51c276b1d8 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) + seq_destroy(pmd->reload_seq); + ovs_mutex_destroy(&pmd->port_mutex); + ovs_mutex_destroy(&pmd->bond_mutex); ++ free(pmd->netdev_input_func_userdata); + free(pmd); + } + +diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c +index 71e35ccdda..06e1e8ca02 100644 +--- a/lib/dpif-netlink.c ++++ b/lib/dpif-netlink.c +@@ -85,7 +85,7 @@ enum { MAX_PORTS = USHRT_MAX }; + #define EPOLLEXCLUSIVE (1u << 28) + #endif + +-#define OVS_DP_F_UNSUPPORTED (1 << 31); ++#define OVS_DP_F_UNSUPPORTED (1u << 31); + + /* This PID is not used by the kernel datapath when using dispatch per CPU, + * but it is required to be set (not zero). */ +diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c +index fd0127ed17..3b4520f87c 100644 +--- a/lib/dynamic-string.c ++++ b/lib/dynamic-string.c +@@ -152,7 +152,10 @@ ds_put_format_valist(struct ds *ds, const char *format, va_list args_) + + va_copy(args, args_); + available = ds->string ? ds->allocated - ds->length + 1 : 0; +- needed = vsnprintf(&ds->string[ds->length], available, format, args); ++ needed = vsnprintf(ds->string ++ ? &ds->string[ds->length] ++ : NULL, ++ available, format, args); + va_end(args); + + if (needed < available) { +@@ -162,7 +165,8 @@ ds_put_format_valist(struct ds *ds, const char *format, va_list args_) + + va_copy(args, args_); + available = ds->allocated - ds->length + 1; +- needed = vsnprintf(&ds->string[ds->length], available, format, args); ++ needed = vsnprintf(&ds->string[ds->length], ++ available, format, args); + va_end(args); + + ovs_assert(needed < available); +@@ -198,10 +202,11 @@ ds_put_strftime_msec(struct ds *ds, const char *template, long long int when, + localtime_msec(when, &tm); + } + ++ ds_reserve(ds, 64); + for (;;) { +- size_t avail = ds->string ? ds->allocated - ds->length + 1 : 0; +- size_t used = strftime_msec(&ds->string[ds->length], avail, template, +- &tm); ++ size_t avail = ds->allocated - ds->length + 1; ++ char *dest = &ds->string[ds->length]; ++ size_t used = strftime_msec(dest, avail, template, &tm); + if (used) { + ds->length += used; + return; diff --git a/lib/fat-rwlock.c b/lib/fat-rwlock.c index d913b2088f..771ccc9737 100644 --- a/lib/fat-rwlock.c @@ -1585,10 +2402,10 @@ index 499b441746..a8c7fad098 100644 ovs_list_remove(&isid_vlan_map->m_entries); diff --git a/lib/lldp/lldpd.c b/lib/lldp/lldpd.c -index a024dc5e58..403f1f525a 100644 +index a024dc5e58..4bff7b017f 100644 --- a/lib/lldp/lldpd.c +++ b/lib/lldp/lldpd.c -@@ -134,12 +134,12 @@ lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) +@@ -134,24 +134,20 @@ lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) void lldpd_cleanup(struct lldpd *cfg) { @@ -1600,11 +2417,18 @@ index a024dc5e58..403f1f525a 100644 VLOG_DBG("cleanup all ports"); - LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware) { +- if (!hw->h_flags) { +- ovs_list_remove(&hw->h_entries); +- lldpd_remote_cleanup(hw, NULL, true); +- lldpd_hardware_cleanup(cfg, hw); +- } else { +- lldpd_remote_cleanup(hw, NULL, false); +- } + LIST_FOR_EACH_SAFE (hw, h_entries, &cfg->g_hardware) { - if (!hw->h_flags) { - ovs_list_remove(&hw->h_entries); - lldpd_remote_cleanup(hw, NULL, true); -@@ -151,7 +151,7 @@ lldpd_cleanup(struct lldpd *cfg) ++ ovs_list_remove(&hw->h_entries); ++ lldpd_remote_cleanup(hw, NULL, true); ++ lldpd_hardware_cleanup(cfg, hw); + } VLOG_DBG("cleanup all chassis"); @@ -1676,6 +2500,21 @@ index 6730301b67..029ca28558 100644 if (m->port == port) { mcast_snooping_flush_mrouter(m); ms->need_revalidate = true; +diff --git a/lib/meta-flow.c b/lib/meta-flow.c +index e03cd8d0c5..c576ae6202 100644 +--- a/lib/meta-flow.c ++++ b/lib/meta-flow.c +@@ -3442,7 +3442,9 @@ mf_get_vl_mff(const struct mf_field *mff, + const struct vl_mff_map *vl_mff_map) + { + if (mff && mff->variable_len && vl_mff_map) { +- return &mf_get_vl_mff__(mff->id, vl_mff_map)->mf; ++ struct vl_mf_field *vl_mff = mf_get_vl_mff__(mff->id, vl_mff_map); ++ ++ return vl_mff ? &vl_mff->mf : NULL; + } + + return NULL; diff --git a/lib/namemap.c b/lib/namemap.c index 785cda4c27..dd317ea52e 100644 --- a/lib/namemap.c @@ -1711,7 +2550,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..4d32781a95 100644 +index b6b29c75e3..e28e397d7e 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) @@ -1726,7 +2565,402 @@ index b6b29c75e3..4d32781a95 100644 if (!dmp->refcount && dpdk_mp_full(dmp->mp)) { VLOG_DBG("Freeing mempool \"%s\"", dmp->mp->name); ovs_list_remove(&dmp->list_node); -@@ -4686,11 +4686,11 @@ trtcm_policer_qos_construct(const struct smap *details, +@@ -2561,90 +2561,6 @@ netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev, + } + } + +-static void +-__netdev_dpdk_vhost_send(struct netdev *netdev, int qid, +- struct dp_packet **pkts, int cnt) +-{ +- struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); +- struct rte_mbuf **cur_pkts = (struct rte_mbuf **) pkts; +- struct netdev_dpdk_sw_stats sw_stats_add; +- unsigned int n_packets_to_free = cnt; +- unsigned int total_packets = cnt; +- int i, retries = 0; +- int max_retries = VHOST_ENQ_RETRY_MIN; +- int vid = netdev_dpdk_get_vid(dev); +- +- qid = dev->tx_q[qid % netdev->n_txq].map; +- +- if (OVS_UNLIKELY(vid < 0 || !dev->vhost_reconfigured || qid < 0 +- || !(dev->flags & NETDEV_UP))) { +- rte_spinlock_lock(&dev->stats_lock); +- dev->stats.tx_dropped+= cnt; +- rte_spinlock_unlock(&dev->stats_lock); +- goto out; +- } +- +- if (OVS_UNLIKELY(!rte_spinlock_trylock(&dev->tx_q[qid].tx_lock))) { +- COVERAGE_INC(vhost_tx_contention); +- rte_spinlock_lock(&dev->tx_q[qid].tx_lock); +- } +- +- sw_stats_add.tx_invalid_hwol_drops = cnt; +- if (userspace_tso_enabled()) { +- cnt = netdev_dpdk_prep_hwol_batch(dev, cur_pkts, cnt); +- } +- +- sw_stats_add.tx_invalid_hwol_drops -= cnt; +- sw_stats_add.tx_mtu_exceeded_drops = cnt; +- cnt = netdev_dpdk_filter_packet_len(dev, cur_pkts, cnt); +- sw_stats_add.tx_mtu_exceeded_drops -= cnt; +- +- /* Check has QoS has been configured for the netdev */ +- sw_stats_add.tx_qos_drops = cnt; +- cnt = netdev_dpdk_qos_run(dev, cur_pkts, cnt, true); +- sw_stats_add.tx_qos_drops -= cnt; +- +- n_packets_to_free = cnt; +- +- do { +- int vhost_qid = qid * VIRTIO_QNUM + VIRTIO_RXQ; +- unsigned int tx_pkts; +- +- tx_pkts = rte_vhost_enqueue_burst(vid, vhost_qid, cur_pkts, cnt); +- if (OVS_LIKELY(tx_pkts)) { +- /* Packets have been sent.*/ +- cnt -= tx_pkts; +- /* Prepare for possible retry.*/ +- cur_pkts = &cur_pkts[tx_pkts]; +- if (OVS_UNLIKELY(cnt && !retries)) { +- /* +- * Read max retries as there are packets not sent +- * and no retries have already occurred. +- */ +- atomic_read_relaxed(&dev->vhost_tx_retries_max, &max_retries); +- } +- } else { +- /* No packets sent - do not retry.*/ +- break; +- } +- } while (cnt && (retries++ < max_retries)); +- +- rte_spinlock_unlock(&dev->tx_q[qid].tx_lock); +- +- sw_stats_add.tx_failure_drops = cnt; +- sw_stats_add.tx_retries = MIN(retries, max_retries); +- +- rte_spinlock_lock(&dev->stats_lock); +- netdev_dpdk_vhost_update_tx_counters(dev, pkts, total_packets, +- &sw_stats_add); +- rte_spinlock_unlock(&dev->stats_lock); +- +-out: +- for (i = 0; i < n_packets_to_free; i++) { +- dp_packet_delete(pkts[i]); +- } +-} +- + static void + netdev_dpdk_extbuf_free(void *addr OVS_UNUSED, void *opaque) + { +@@ -2749,76 +2665,69 @@ dpdk_copy_dp_packet_to_mbuf(struct rte_mempool *mp, struct dp_packet *pkt_orig) + return pkt_dest; + } + +-/* Tx function. Transmit packets indefinitely */ +-static void +-dpdk_do_tx_copy(struct netdev *netdev, int qid, struct dp_packet_batch *batch) +- OVS_NO_THREAD_SAFETY_ANALYSIS +-{ +- const size_t batch_cnt = dp_packet_batch_size(batch); +-#if !defined(__CHECKER__) && !defined(_WIN32) +- const size_t PKT_ARRAY_SIZE = batch_cnt; +-#else +- /* Sparse or MSVC doesn't like variable length array. */ +- enum { PKT_ARRAY_SIZE = NETDEV_MAX_BURST }; +-#endif ++/* Replace packets in a 'batch' with their corresponding copies using ++ * DPDK memory. ++ * ++ * Returns the number of good packets in the batch. */ ++static size_t ++dpdk_copy_batch_to_mbuf(struct netdev *netdev, struct dp_packet_batch *batch) ++{ + struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); +- struct dp_packet *pkts[PKT_ARRAY_SIZE]; +- struct netdev_dpdk_sw_stats *sw_stats = dev->sw_stats; +- uint32_t cnt = batch_cnt; +- uint32_t dropped = 0; +- uint32_t tx_failure = 0; +- uint32_t mtu_drops = 0; +- uint32_t qos_drops = 0; +- +- if (dev->type != DPDK_DEV_VHOST) { +- /* Check if QoS has been configured for this netdev. */ +- cnt = netdev_dpdk_qos_run(dev, (struct rte_mbuf **) batch->packets, +- batch_cnt, false); +- qos_drops = batch_cnt - cnt; +- } +- +- uint32_t txcnt = 0; +- +- for (uint32_t i = 0; i < cnt; i++) { +- struct dp_packet *packet = batch->packets[i]; +- uint32_t size = dp_packet_size(packet); +- +- if (size > dev->max_packet_len +- && !(packet->mbuf.ol_flags & RTE_MBUF_F_TX_TCP_SEG)) { +- VLOG_WARN_RL(&rl, "Too big size %u max_packet_len %d", size, +- dev->max_packet_len); +- mtu_drops++; +- continue; +- } ++ size_t i, size = dp_packet_batch_size(batch); ++ struct dp_packet *packet; + +- pkts[txcnt] = dpdk_copy_dp_packet_to_mbuf(dev->dpdk_mp->mp, packet); +- if (OVS_UNLIKELY(!pkts[txcnt])) { +- dropped = cnt - i; +- break; +- } ++ DP_PACKET_BATCH_REFILL_FOR_EACH (i, size, packet, batch) { ++ if (OVS_UNLIKELY(packet->source == DPBUF_DPDK)) { ++ dp_packet_batch_refill(batch, packet, i); ++ } else { ++ struct dp_packet *pktcopy; + +- txcnt++; +- } ++ pktcopy = dpdk_copy_dp_packet_to_mbuf(dev->dpdk_mp->mp, packet); ++ if (pktcopy) { ++ dp_packet_batch_refill(batch, pktcopy, i); ++ } + +- if (OVS_LIKELY(txcnt)) { +- if (dev->type == DPDK_DEV_VHOST) { +- __netdev_dpdk_vhost_send(netdev, qid, pkts, txcnt); +- } else { +- tx_failure += netdev_dpdk_eth_tx_burst(dev, qid, +- (struct rte_mbuf **)pkts, +- txcnt); ++ dp_packet_delete(packet); + } + } + +- dropped += qos_drops + mtu_drops + tx_failure; +- if (OVS_UNLIKELY(dropped)) { +- rte_spinlock_lock(&dev->stats_lock); +- dev->stats.tx_dropped += dropped; +- sw_stats->tx_failure_drops += tx_failure; +- sw_stats->tx_mtu_exceeded_drops += mtu_drops; +- sw_stats->tx_qos_drops += qos_drops; +- rte_spinlock_unlock(&dev->stats_lock); ++ return dp_packet_batch_size(batch); ++} ++ ++static size_t ++netdev_dpdk_common_send(struct netdev *netdev, struct dp_packet_batch *batch, ++ struct netdev_dpdk_sw_stats *stats) ++{ ++ struct rte_mbuf **pkts = (struct rte_mbuf **) batch->packets; ++ struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); ++ size_t cnt, pkt_cnt = dp_packet_batch_size(batch); ++ ++ memset(stats, 0, sizeof *stats); ++ ++ /* Copy dp-packets to mbufs. */ ++ if (OVS_UNLIKELY(batch->packets[0]->source != DPBUF_DPDK)) { ++ 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); ++ stats->tx_mtu_exceeded_drops += pkt_cnt - cnt; ++ pkt_cnt = cnt; ++ ++ /* Prepare each mbuf for hardware offloading. */ ++ if (userspace_tso_enabled()) { ++ 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); ++ stats->tx_qos_drops += pkt_cnt - cnt; ++ ++ return cnt; + } + + static int +@@ -2826,25 +2735,92 @@ netdev_dpdk_vhost_send(struct netdev *netdev, int qid, + struct dp_packet_batch *batch, + bool concurrent_txq OVS_UNUSED) + { ++ struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); ++ int max_retries = VHOST_ENQ_RETRY_MIN; ++ int cnt, batch_cnt, vhost_batch_cnt; ++ int vid = netdev_dpdk_get_vid(dev); ++ struct netdev_dpdk_sw_stats stats; ++ struct rte_mbuf **pkts; ++ int retries; + +- if (OVS_UNLIKELY(batch->packets[0]->source != DPBUF_DPDK)) { +- dpdk_do_tx_copy(netdev, qid, batch); ++ batch_cnt = cnt = dp_packet_batch_size(batch); ++ qid = dev->tx_q[qid % netdev->n_txq].map; ++ if (OVS_UNLIKELY(vid < 0 || !dev->vhost_reconfigured || qid < 0 ++ || !(dev->flags & NETDEV_UP))) { ++ rte_spinlock_lock(&dev->stats_lock); ++ dev->stats.tx_dropped += cnt; ++ rte_spinlock_unlock(&dev->stats_lock); + dp_packet_delete_batch(batch, true); +- } else { +- __netdev_dpdk_vhost_send(netdev, qid, batch->packets, +- dp_packet_batch_size(batch)); ++ return 0; ++ } ++ ++ if (OVS_UNLIKELY(!rte_spinlock_trylock(&dev->tx_q[qid].tx_lock))) { ++ COVERAGE_INC(vhost_tx_contention); ++ rte_spinlock_lock(&dev->tx_q[qid].tx_lock); ++ } ++ ++ cnt = netdev_dpdk_common_send(netdev, batch, &stats); ++ ++ pkts = (struct rte_mbuf **) batch->packets; ++ vhost_batch_cnt = cnt; ++ retries = 0; ++ do { ++ int vhost_qid = qid * VIRTIO_QNUM + VIRTIO_RXQ; ++ int tx_pkts; ++ ++ tx_pkts = rte_vhost_enqueue_burst(vid, vhost_qid, pkts, cnt); ++ if (OVS_LIKELY(tx_pkts)) { ++ /* Packets have been sent.*/ ++ cnt -= tx_pkts; ++ /* Prepare for possible retry.*/ ++ pkts = &pkts[tx_pkts]; ++ if (OVS_UNLIKELY(cnt && !retries)) { ++ /* ++ * Read max retries as there are packets not sent ++ * and no retries have already occurred. ++ */ ++ atomic_read_relaxed(&dev->vhost_tx_retries_max, &max_retries); ++ } ++ } else { ++ /* No packets sent - do not retry.*/ ++ break; ++ } ++ } while (cnt && (retries++ < max_retries)); ++ ++ rte_spinlock_unlock(&dev->tx_q[qid].tx_lock); ++ ++ stats.tx_failure_drops += cnt; ++ stats.tx_retries = MIN(retries, max_retries); ++ ++ rte_spinlock_lock(&dev->stats_lock); ++ netdev_dpdk_vhost_update_tx_counters(dev, batch->packets, batch_cnt, ++ &stats); ++ rte_spinlock_unlock(&dev->stats_lock); ++ ++ pkts = (struct rte_mbuf **) batch->packets; ++ for (int i = 0; i < vhost_batch_cnt; i++) { ++ rte_pktmbuf_free(pkts[i]); + } ++ + return 0; + } + +-static inline void +-netdev_dpdk_send__(struct netdev_dpdk *dev, int qid, +- struct dp_packet_batch *batch, +- bool concurrent_txq) ++static int ++netdev_dpdk_eth_send(struct netdev *netdev, int qid, ++ struct dp_packet_batch *batch, bool concurrent_txq) + { ++ struct rte_mbuf **pkts = (struct rte_mbuf **) batch->packets; ++ struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); ++ int batch_cnt = dp_packet_batch_size(batch); ++ struct netdev_dpdk_sw_stats stats; ++ int cnt, dropped; ++ + if (OVS_UNLIKELY(!(dev->flags & NETDEV_UP))) { ++ rte_spinlock_lock(&dev->stats_lock); ++ dev->stats.tx_dropped += dp_packet_batch_size(batch); ++ rte_spinlock_unlock(&dev->stats_lock); + dp_packet_delete_batch(batch, true); +- return; ++ return 0; + } + + if (OVS_UNLIKELY(concurrent_txq)) { +@@ -2852,56 +2828,27 @@ netdev_dpdk_send__(struct netdev_dpdk *dev, int qid, + rte_spinlock_lock(&dev->tx_q[qid].tx_lock); + } + +- if (OVS_UNLIKELY(batch->packets[0]->source != DPBUF_DPDK)) { +- struct netdev *netdev = &dev->up; ++ cnt = netdev_dpdk_common_send(netdev, batch, &stats); + +- 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); ++ if (OVS_UNLIKELY(dropped)) { + struct netdev_dpdk_sw_stats *sw_stats = dev->sw_stats; +- int dropped; +- int tx_failure, mtu_drops, qos_drops, hwol_drops; +- int batch_cnt = dp_packet_batch_size(batch); +- struct rte_mbuf **pkts = (struct rte_mbuf **) batch->packets; + +- hwol_drops = batch_cnt; +- if (userspace_tso_enabled()) { +- batch_cnt = netdev_dpdk_prep_hwol_batch(dev, pkts, batch_cnt); +- } +- hwol_drops -= batch_cnt; +- mtu_drops = batch_cnt; +- batch_cnt = netdev_dpdk_filter_packet_len(dev, pkts, batch_cnt); +- mtu_drops -= batch_cnt; +- qos_drops = batch_cnt; +- batch_cnt = netdev_dpdk_qos_run(dev, pkts, batch_cnt, true); +- qos_drops -= batch_cnt; +- +- tx_failure = netdev_dpdk_eth_tx_burst(dev, qid, pkts, batch_cnt); +- +- dropped = tx_failure + mtu_drops + qos_drops + hwol_drops; +- if (OVS_UNLIKELY(dropped)) { +- rte_spinlock_lock(&dev->stats_lock); +- dev->stats.tx_dropped += dropped; +- sw_stats->tx_failure_drops += tx_failure; +- sw_stats->tx_mtu_exceeded_drops += mtu_drops; +- sw_stats->tx_qos_drops += qos_drops; +- sw_stats->tx_invalid_hwol_drops += hwol_drops; +- rte_spinlock_unlock(&dev->stats_lock); +- } ++ rte_spinlock_lock(&dev->stats_lock); ++ dev->stats.tx_dropped += dropped; ++ sw_stats->tx_failure_drops += stats.tx_failure_drops; ++ sw_stats->tx_mtu_exceeded_drops += stats.tx_mtu_exceeded_drops; ++ sw_stats->tx_qos_drops += stats.tx_qos_drops; ++ sw_stats->tx_invalid_hwol_drops += stats.tx_invalid_hwol_drops; ++ rte_spinlock_unlock(&dev->stats_lock); + } + + if (OVS_UNLIKELY(concurrent_txq)) { + rte_spinlock_unlock(&dev->tx_q[qid].tx_lock); + } +-} +- +-static int +-netdev_dpdk_eth_send(struct netdev *netdev, int qid, +- struct dp_packet_batch *batch, bool concurrent_txq) +-{ +- struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); + +- netdev_dpdk_send__(dev, qid, batch, concurrent_txq); + return 0; + } + +@@ -4686,11 +4633,11 @@ trtcm_policer_qos_construct(const struct smap *details, static void trtcm_policer_qos_destruct(struct qos_conf *conf) { @@ -1741,7 +2975,7 @@ index b6b29c75e3..4d32781a95 100644 free(queue); } diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c -index 620a451dec..9d125029de 100644 +index 620a451dec..2766b3f2bf 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -5331,11 +5331,11 @@ static void @@ -1758,8 +2992,84 @@ index 620a451dec..9d125029de 100644 hmap_remove(&hfsc->tc.queues, &hc->tc_queue.hmap_node); free(hc); } +@@ -6295,7 +6295,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)) { +- netdev_stats_from_rtnl_link_stats64(stats, nl_attr_get(a)); ++ const struct rtnl_link_stats64 *lstats = nl_attr_get(a); ++ struct rtnl_link_stats64 aligned_lstats; ++ ++ if (!IS_PTR_ALIGNED(lstats)) { ++ memcpy(&aligned_lstats, lstats, sizeof aligned_lstats); ++ lstats = &aligned_lstats; ++ } ++ netdev_stats_from_rtnl_link_stats64(stats, lstats); + error = 0; + } else { + a = nl_attr_find(reply, 0, IFLA_STATS); +diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c +index 94dc6a9b74..12d299603a 100644 +--- a/lib/netdev-offload-dpdk.c ++++ b/lib/netdev-offload-dpdk.c +@@ -363,6 +363,8 @@ dump_flow_pattern(struct ds *s, + + ds_put_cstr(s, "eth "); + if (eth_spec) { ++ uint32_t has_vlan_mask; ++ + if (!eth_mask) { + eth_mask = &rte_flow_item_eth_mask; + } +@@ -377,6 +379,9 @@ dump_flow_pattern(struct ds *s, + DUMP_PATTERN_ITEM(eth_mask->type, false, "type", "0x%04"PRIx16, + ntohs(eth_spec->type), + ntohs(eth_mask->type), 0); ++ has_vlan_mask = eth_mask->has_vlan ? UINT32_MAX : 0; ++ DUMP_PATTERN_ITEM(has_vlan_mask, false, "has_vlan", "%d", ++ eth_spec->has_vlan, eth_mask->has_vlan, 0); + } + ds_put_cstr(s, "/ "); + } else if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) { +@@ -1369,6 +1374,7 @@ parse_flow_match(struct netdev *netdev, + struct flow_patterns *patterns, + struct match *match) + { ++ struct rte_flow_item_eth *eth_spec = NULL, *eth_mask = NULL; + struct flow *consumed_masks; + uint8_t proto = 0; + +@@ -1414,6 +1420,11 @@ parse_flow_match(struct netdev *netdev, + memset(&consumed_masks->dl_src, 0, sizeof consumed_masks->dl_src); + consumed_masks->dl_type = 0; + ++ spec->has_vlan = 0; ++ mask->has_vlan = 1; ++ eth_spec = spec; ++ eth_mask = mask; ++ + add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_ETH, spec, mask, NULL); + } + +@@ -1427,8 +1438,14 @@ parse_flow_match(struct netdev *netdev, + spec->tci = match->flow.vlans[0].tci & ~htons(VLAN_CFI); + mask->tci = match->wc.masks.vlans[0].tci & ~htons(VLAN_CFI); + +- /* Match any protocols. */ +- mask->inner_type = 0; ++ if (eth_spec && eth_mask) { ++ eth_spec->has_vlan = 1; ++ eth_mask->has_vlan = 1; ++ spec->inner_type = eth_spec->type; ++ mask->inner_type = eth_mask->type; ++ eth_spec->type = match->flow.vlans[0].tpid; ++ eth_mask->type = match->wc.masks.vlans[0].tpid; ++ } + + add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_VLAN, spec, mask, NULL); + } diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c -index 9845e8d3fe..a41b627585 100644 +index 9845e8d3fe..262faf3c62 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -417,11 +417,11 @@ delete_chains_from_netdev(struct netdev *netdev, struct tcf_id *id) @@ -1860,6 +3170,15 @@ index 9845e8d3fe..a41b627585 100644 } if (mask->ct_zone) { +@@ -1638,7 +1640,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, + + if (mask->vlans[0].tpid && eth_type_vlan(key->vlans[0].tpid)) { + flower.key.encap_eth_type[0] = flower.key.eth_type; +- flower.mask.encap_eth_type[0] = flower.mask.eth_type; ++ flower.mask.encap_eth_type[0] = CONSTANT_HTONS(0xffff); + flower.key.eth_type = key->vlans[0].tpid; + flower.mask.eth_type = mask->vlans[0].tpid; + } @@ -1841,7 +1843,25 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, VLOG_DBG_RL(&rl, "Can't find netdev for output port %d", port); return ENODEV; @@ -1898,11 +3217,33 @@ index 9845e8d3fe..a41b627585 100644 } diff --git a/lib/odp-util.c b/lib/odp-util.c -index 9a705cffa3..3ae850b669 100644 +index 9a705cffa3..2d2a6893c6 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c -@@ -4630,6 +4630,11 @@ odp_flow_format(const struct nlattr *key, size_t key_len, - ds_put_char(ds, ','); +@@ -3429,16 +3429,16 @@ format_eth(struct ds *ds, const char *name, const struct eth_addr key, + + static void + format_be64(struct ds *ds, const char *name, ovs_be64 key, +- const ovs_be64 *mask, bool verbose) ++ const ovs_32aligned_be64 *mask_, bool verbose) + { +- bool mask_empty = mask && !*mask; ++ ovs_be64 mask = mask_ ? get_32aligned_be64(mask_) : htonll(0); + +- if (verbose || !mask_empty) { +- bool mask_full = !mask || *mask == OVS_BE64_MAX; ++ if (verbose || mask) { ++ bool mask_full = !mask_ || mask == OVS_BE64_MAX; + + ds_put_format(ds, "%s=0x%"PRIx64, name, ntohll(key)); + if (!mask_full) { /* Partially masked. */ +- ds_put_format(ds, "/%#"PRIx64, ntohll(*mask)); ++ ds_put_format(ds, "/%#"PRIx64, ntohll(mask)); + } + ds_put_char(ds, ','); + } +@@ -4630,6 +4630,11 @@ odp_flow_format(const struct nlattr *key, size_t key_len, + ds_put_char(ds, ','); } ds_put_cstr(ds, "eth()"); + } else if (attr_type == OVS_KEY_ATTR_PACKET_TYPE && is_wildcard) { @@ -1913,8 +3254,185 @@ index 9a705cffa3..3ae850b669 100644 } ofpbuf_clear(&ofp); } +@@ -7161,11 +7166,6 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + } + } + } +- } else if (src_flow->nw_proto == IPPROTO_IGMP +- && src_flow->dl_type == htons(ETH_TYPE_IP)) { +- /* OVS userspace parses the IGMP type, code, and group, but its +- * datapaths do not, so there is always missing information. */ +- return ODP_FIT_TOO_LITTLE; + } + if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) { + if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) { +@@ -7188,7 +7188,8 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + uint64_t present_attrs, int out_of_range_attr, + uint64_t expected_attrs, struct flow *flow, + const struct nlattr *key, size_t key_len, +- const struct flow *src_flow, char **errorp) ++ const struct flow *src_flow, char **errorp, ++ bool ignore_vlan_limit) + { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + bool is_mask = src_flow != flow; +@@ -7196,9 +7197,11 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + const struct nlattr *encap; + enum odp_key_fitness encap_fitness; + enum odp_key_fitness fitness = ODP_FIT_ERROR; ++ int vlan_limit; + int encaps = 0; + +- while (encaps < flow_vlan_limit && ++ vlan_limit = ignore_vlan_limit ? FLOW_MAX_VLAN_HEADERS : flow_vlan_limit; ++ while (encaps < vlan_limit && + (is_mask + ? (src_flow->vlans[encaps].tci & htons(VLAN_CFI)) != 0 + : eth_type_vlan(flow->dl_type))) { +@@ -7259,6 +7262,14 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + } + expected_attrs = 0; + ++ /* For OVS to be backward compatible with newer datapath ++ * implementations, we should ignore out of range attributes. */ ++ if (out_of_range_attr) { ++ VLOG_DBG("Flow key decode found unknown OVS_KEY_ATTR, %d", ++ out_of_range_attr); ++ out_of_range_attr = 0; ++ } ++ + if (!parse_ethertype(attrs, present_attrs, &expected_attrs, + flow, src_flow, errorp)) { + return ODP_FIT_ERROR; +@@ -7281,7 +7292,7 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + static enum odp_key_fitness + odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, + struct flow *flow, const struct flow *src_flow, +- char **errorp) ++ char **errorp, bool ignore_vlan_limit) + { + /* New "struct flow" fields that are visible to the datapath (including all + * data fields) should be translated from equivalent datapath flow fields +@@ -7308,6 +7319,14 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, + } + expected_attrs = 0; + ++ /* For OVS to be backward compatible with newer datapath implementations, ++ * we should ignore out of range attributes. */ ++ if (out_of_range_attr) { ++ VLOG_DBG("Flow key decode found unknown OVS_KEY_ATTR, %d", ++ out_of_range_attr); ++ out_of_range_attr = 0; ++ } ++ + /* Metadata. */ + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID)) { + flow->recirc_id = nl_attr_get_u32(attrs[OVS_KEY_ATTR_RECIRC_ID]); +@@ -7431,7 +7450,7 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, + : eth_type_vlan(src_flow->dl_type)) { + fitness = parse_8021q_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len, +- src_flow, errorp); ++ src_flow, errorp, ignore_vlan_limit); + } else { + if (is_mask) { + /* A missing VLAN mask means exact match on vlan_tci 0 (== no +@@ -7497,7 +7516,7 @@ enum odp_key_fitness + odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, + struct flow *flow, char **errorp) + { +- return odp_flow_key_to_flow__(key, key_len, flow, flow, errorp); ++ return odp_flow_key_to_flow__(key, key_len, flow, flow, errorp, false); + } + + /* Converts the 'mask_key_len' bytes of OVS_KEY_ATTR_* attributes in 'mask_key' +@@ -7509,14 +7528,16 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, + * If 'errorp' is nonnull, this function uses it for detailed error reports: if + * the return value is ODP_FIT_ERROR, it stores a malloc()'d error string in + * '*errorp', otherwise NULL. */ +-enum odp_key_fitness +-odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len, +- struct flow_wildcards *mask, const struct flow *src_flow, +- char **errorp) ++static enum odp_key_fitness ++odp_flow_key_to_mask__(const struct nlattr *mask_key, size_t mask_key_len, ++ struct flow_wildcards *mask, ++ const struct flow *src_flow, ++ char **errorp, bool ignore_vlan_limit) + { + if (mask_key_len) { + return odp_flow_key_to_flow__(mask_key, mask_key_len, +- &mask->masks, src_flow, errorp); ++ &mask->masks, src_flow, errorp, ++ ignore_vlan_limit); + } else { + if (errorp) { + *errorp = NULL; +@@ -7530,6 +7551,15 @@ odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len, + } + } + ++enum odp_key_fitness ++odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len, ++ struct flow_wildcards *mask, ++ const struct flow *src_flow, char **errorp) ++{ ++ return odp_flow_key_to_mask__(mask_key, mask_key_len, mask, src_flow, ++ errorp, false); ++} ++ + /* Converts the netlink formated key/mask to match. + * Fails if odp_flow_key_from_key/mask and odp_flow_key_key/mask + * disagree on the acceptable form of flow */ +@@ -7540,12 +7570,15 @@ parse_key_and_mask_to_match(const struct nlattr *key, size_t key_len, + { + enum odp_key_fitness fitness; + +- fitness = odp_flow_key_to_flow(key, key_len, &match->flow, NULL); ++ fitness = odp_flow_key_to_flow__(key, key_len, &match->flow, &match->flow, ++ NULL, true); + if (fitness) { +- /* This should not happen: it indicates that +- * odp_flow_key_from_flow() and odp_flow_key_to_flow() disagree on +- * the acceptable form of a flow. Log the problem as an error, +- * with enough details to enable debugging. */ ++ /* This will happen when the odp_flow_key_to_flow() function can't ++ * parse the netlink message to a match structure. It will return ++ * ODP_FIT_TOO_LITTLE if there is not enough information to parse the ++ * content successfully, ODP_FIT_TOO_MUCH if there is too much netlink ++ * data and we do not know how to safely ignore it, and ODP_FIT_ERROR ++ * in any other case. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + + if (!VLOG_DROP_ERR(&rl)) { +@@ -7553,20 +7586,18 @@ parse_key_and_mask_to_match(const struct nlattr *key, size_t key_len, + + ds_init(&s); + odp_flow_format(key, key_len, NULL, 0, NULL, &s, true); +- VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s)); ++ VLOG_ERR("internal error parsing flow key %s (%s)", ++ ds_cstr(&s), odp_key_fitness_to_string(fitness)); + ds_destroy(&s); + } + + return EINVAL; + } + +- fitness = odp_flow_key_to_mask(mask, mask_len, &match->wc, &match->flow, +- NULL); ++ fitness = odp_flow_key_to_mask__(mask, mask_len, &match->wc, &match->flow, ++ NULL, true); + if (fitness) { +- /* This should not happen: it indicates that +- * odp_flow_key_from_mask() and odp_flow_key_to_mask() +- * disagree on the acceptable form of a mask. Log the problem +- * as an error, with enough details to enable debugging. */ ++ /* This should not happen, see comment above. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + + if (!VLOG_DROP_ERR(&rl)) { diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c -index 006837c2e1..a0b70a89d7 100644 +index 006837c2e1..c13f97b5c9 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -853,7 +853,9 @@ decode_NXAST_RAW_CONTROLLER2(const struct ext_action_header *eah, @@ -1928,6 +3446,31 @@ index 006837c2e1..a0b70a89d7 100644 break; } +@@ -3200,16 +3202,21 @@ set_field_split_str(char *arg, char **key, char **value, char **delim) + { + char *value_end; + ++ *key = NULL; + *value = arg; +- value_end = strstr(arg, "->"); +- *key = value_end + strlen("->"); + if (delim) { +- *delim = value_end; ++ *delim = NULL; + } + ++ value_end = strstr(arg, "->"); + if (!value_end) { + return xasprintf("%s: missing `->'", arg); + } ++ ++ *key = value_end + strlen("->"); ++ if (delim) { ++ *delim = value_end; ++ } + if (strlen(value_end) <= strlen("->")) { + return xasprintf("%s: missing field name following `->'", arg); + } diff --git a/lib/ofp-msgs.c b/lib/ofp-msgs.c index fec54f75f8..93aa812978 100644 --- a/lib/ofp-msgs.c @@ -1991,8 +3534,34 @@ index 4579548ee1..9485ddfc93 100644 break; } +diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c +index 271105bdea..879275a7a3 100644 +--- a/lib/ofpbuf.c ++++ b/lib/ofpbuf.c +@@ -426,6 +426,10 @@ void + ofpbuf_reserve(struct ofpbuf *b, size_t size) + { + ovs_assert(!b->size); ++ ++ if (!size) { ++ return; ++ } + ofpbuf_prealloc_tailroom(b, size); + b->data = (char*)b->data + size; + } +@@ -436,6 +440,10 @@ ofpbuf_reserve(struct ofpbuf *b, size_t size) + void * + ofpbuf_push_uninit(struct ofpbuf *b, size_t size) + { ++ if (!size) { ++ return b->data; ++ } ++ + ofpbuf_prealloc_headroom(b, size); + b->data = (char*)b->data - size; + b->size += size; diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c -index 162311fa45..a9d205ec83 100644 +index 162311fa45..2d13e971ed 100644 --- a/lib/ovs-lldp.c +++ b/lib/ovs-lldp.c @@ -559,9 +559,9 @@ aa_mapping_unregister_mapping(struct lldp *lldp, @@ -2007,7 +3576,22 @@ index 162311fa45..a9d205ec83 100644 &hw->h_lport.p_isid_vlan_maps) { uint32_t isid = lm->isid_vlan_data.isid; -@@ -953,8 +953,8 @@ lldp_ref(const struct lldp *lldp_) +@@ -738,6 +738,14 @@ lldp_put_packet(struct lldp *lldp, struct dp_packet *packet, + ovs_mutex_unlock(&mutex); + } + ++/* Is LLDP enabled? ++ */ ++bool ++lldp_is_enabled(struct lldp *lldp) ++{ ++ return lldp ? lldp->enabled : false; ++} ++ + /* Configures the LLDP stack. + */ + bool +@@ -953,8 +961,8 @@ lldp_ref(const struct lldp *lldp_) void lldp_destroy_dummy(struct lldp *lldp) { @@ -2018,7 +3602,7 @@ index 162311fa45..a9d205ec83 100644 struct lldpd *cfg; if (!lldp) { -@@ -963,13 +963,13 @@ lldp_destroy_dummy(struct lldp *lldp) +@@ -963,13 +971,13 @@ lldp_destroy_dummy(struct lldp *lldp) cfg = lldp->lldpd; @@ -2034,6 +3618,18 @@ index 162311fa45..a9d205ec83 100644 ovs_list_remove(&chassis->list); free(chassis); } +diff --git a/lib/ovs-lldp.h b/lib/ovs-lldp.h +index 0e536e8c27..661ac4e18a 100644 +--- a/lib/ovs-lldp.h ++++ b/lib/ovs-lldp.h +@@ -86,6 +86,7 @@ void lldp_run(struct lldpd *cfg); + bool lldp_should_send_packet(struct lldp *cfg); + bool lldp_should_process_flow(struct lldp *lldp, const struct flow *flow); + bool lldp_configure(struct lldp *lldp, const struct smap *cfg); ++bool lldp_is_enabled(struct lldp *lldp); + void lldp_process_packet(struct lldp *cfg, const struct dp_packet *); + void lldp_put_packet(struct lldp *lldp, struct dp_packet *packet, + const struct eth_addr eth_src); diff --git a/lib/ovs-numa.h b/lib/ovs-numa.h index ecc251a7ff..83bd10cca5 100644 --- a/lib/ovs-numa.h @@ -2050,6 +3646,129 @@ index ecc251a7ff..83bd10cca5 100644 + HMAP_FOR_EACH (ITER, hmap_node, &(DUMP)->numas) #endif /* ovs-numa.h */ +diff --git a/lib/ovs-rcu.c b/lib/ovs-rcu.c +index 1866bd3088..946aa04d18 100644 +--- a/lib/ovs-rcu.c ++++ b/lib/ovs-rcu.c +@@ -444,3 +444,40 @@ ovsrcu_init_module(void) + ovsthread_once_done(&once); + } + } ++ ++static void ++ovsrcu_barrier_func(void *seq_) ++{ ++ struct seq *seq = (struct seq *) seq_; ++ seq_change(seq); ++} ++ ++/* Similar to the kernel rcu_barrier, ovsrcu_barrier waits for all outstanding ++ * RCU callbacks to complete. However, unlike the kernel rcu_barrier, which ++ * might return immediately if there are no outstanding RCU callbacks, ++ * this API will at least wait for a grace period. ++ * ++ * Another issue the caller might need to know is that the barrier is just ++ * for "one-shot", i.e. if inside some RCU callbacks, another RCU callback is ++ * registered, this API only guarantees the first round of RCU callbacks have ++ * been executed after it returns. ++ */ ++void ++ovsrcu_barrier(void) ++{ ++ struct seq *seq = seq_create(); ++ /* First let all threads flush their cbsets. */ ++ ovsrcu_synchronize(); ++ ++ /* Then register a new cbset, ensure this cbset ++ * is at the tail of the global list. */ ++ uint64_t seqno = seq_read(seq); ++ ovsrcu_postpone__(ovsrcu_barrier_func, (void *) seq); ++ ++ do { ++ seq_wait(seq, seqno); ++ poll_block(); ++ } while (seqno == seq_read(seq)); ++ ++ seq_destroy(seq); ++} +diff --git a/lib/ovs-rcu.h b/lib/ovs-rcu.h +index ecc4c92010..8b397b7fb0 100644 +--- a/lib/ovs-rcu.h ++++ b/lib/ovs-rcu.h +@@ -155,6 +155,19 @@ + * port_delete(id); + * } + * ++ * Use ovsrcu_barrier() to wait for all the outstanding RCU callbacks to ++ * finish. This is useful when you have to destroy some resources however ++ * these resources are referenced in the outstanding RCU callbacks. ++ * ++ * void rcu_cb(void *A) { ++ * do_something(A); ++ * } ++ * ++ * void destroy_A() { ++ * ovsrcu_postpone(rcu_cb, A); // will use A later ++ * ovsrcu_barrier(); // wait for rcu_cb done ++ * do_destroy_A(); // free A ++ * } + */ + + #include "compiler.h" +@@ -310,4 +323,6 @@ void ovsrcu_synchronize(void); + + void ovsrcu_exit(void); + ++void ovsrcu_barrier(void); ++ + #endif /* ovs-rcu.h */ +diff --git a/lib/ovs-router.c b/lib/ovs-router.c +index 09b81c6e5a..5d0fbd503e 100644 +--- a/lib/ovs-router.c ++++ b/lib/ovs-router.c +@@ -164,9 +164,10 @@ static void rt_init_match(struct match *match, uint32_t mark, + match->flow.pkt_mark = mark; + } + +-static int +-get_src_addr(const struct in6_addr *ip6_dst, +- const char output_bridge[], struct in6_addr *psrc) ++int ++ovs_router_get_netdev_source_address(const struct in6_addr *ip6_dst, ++ const char output_bridge[], ++ struct in6_addr *psrc) + { + struct in6_addr *mask, *addr6; + int err, n_in6, i, max_plen = -1; +@@ -235,9 +236,11 @@ ovs_router_insert__(uint32_t mark, uint8_t priority, bool local, + p->plen = plen; + p->local = local; + p->priority = priority; +- err = get_src_addr(ip6_dst, output_bridge, &p->src_addr); ++ err = ovs_router_get_netdev_source_address(ip6_dst, output_bridge, ++ &p->src_addr); + if (err && ipv6_addr_is_set(gw)) { +- err = get_src_addr(gw, output_bridge, &p->src_addr); ++ err = ovs_router_get_netdev_source_address(gw, output_bridge, ++ &p->src_addr); + } + if (err) { + struct ds ds = DS_EMPTY_INITIALIZER; +diff --git a/lib/ovs-router.h b/lib/ovs-router.h +index 34ea163eef..d8ce3c00de 100644 +--- a/lib/ovs-router.h ++++ b/lib/ovs-router.h +@@ -37,6 +37,10 @@ void ovs_router_flush(void); + + void ovs_router_disable_system_routing_table(void); + ++int ovs_router_get_netdev_source_address(const struct in6_addr *ip6_dst, ++ const char output_bridge[], ++ struct in6_addr *psrc); ++ + #ifdef __cplusplus + } + #endif diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c index dead31275d..9713c7dc7c 100644 --- a/lib/ovsdb-cs.c @@ -2088,6 +3807,107 @@ index dead31275d..9713c7dc7c 100644 struct sset *sset = node->data; sset_destroy(sset); free(sset); +diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c +index 6b1c20ff85..61ad7679a6 100644 +--- a/lib/ovsdb-data.c ++++ b/lib/ovsdb-data.c +@@ -1957,6 +1957,19 @@ ovsdb_datum_add_unsafe(struct ovsdb_datum *datum, + } + } + ++void ++ovsdb_datum_add_from_index_unsafe(struct ovsdb_datum *dst, ++ const struct ovsdb_datum *src, ++ size_t idx, ++ const struct ovsdb_type *type) ++{ ++ const union ovsdb_atom *key = &src->keys[idx]; ++ const union ovsdb_atom *value = type->value.type != OVSDB_TYPE_VOID ++ ? &src->values[idx] ++ : NULL; ++ ovsdb_datum_add_unsafe(dst, key, value, type, NULL); ++} ++ + /* Adds 'n' atoms starting from index 'start_idx' from 'src' to the end of + * 'dst'. 'dst' should have enough memory allocated to hold the additional + * 'n' atoms. Atoms are not cloned, i.e. 'dst' will reference the same data. +@@ -2165,12 +2178,10 @@ ovsdb_datum_added_removed(struct ovsdb_datum *added, + int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni], + type->key.type); + if (c < 0) { +- ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(removed, old, oi, type); + oi++; + } else if (c > 0) { +- ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(added, new, ni, type); + ni++; + } else { + if (type->value.type != OVSDB_TYPE_VOID && +@@ -2186,13 +2197,11 @@ ovsdb_datum_added_removed(struct ovsdb_datum *added, + } + + for (; oi < old->n; oi++) { +- ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(removed, old, oi, type); + } + + for (; ni < new->n; ni++) { +- ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(added, new, ni, type); + } + } + +@@ -2228,12 +2237,10 @@ ovsdb_datum_diff(struct ovsdb_datum *diff, + int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni], + type->key.type); + if (c < 0) { +- ovsdb_datum_add_unsafe(diff, &old->keys[oi], &old->values[oi], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(diff, old, oi, type); + oi++; + } else if (c > 0) { +- ovsdb_datum_add_unsafe(diff, &new->keys[ni], &new->values[ni], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(diff, new, ni, type); + ni++; + } else { + if (type->value.type != OVSDB_TYPE_VOID && +@@ -2247,13 +2254,11 @@ ovsdb_datum_diff(struct ovsdb_datum *diff, + } + + for (; oi < old->n; oi++) { +- ovsdb_datum_add_unsafe(diff, &old->keys[oi], &old->values[oi], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(diff, old, oi, type); + } + + for (; ni < new->n; ni++) { +- ovsdb_datum_add_unsafe(diff, &new->keys[ni], &new->values[ni], +- type, NULL); ++ ovsdb_datum_add_from_index_unsafe(diff, new, ni, type); + } + } + +diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h +index 47115a7b85..ba5d179a65 100644 +--- a/lib/ovsdb-data.h ++++ b/lib/ovsdb-data.h +@@ -280,6 +280,10 @@ void ovsdb_datum_add_unsafe(struct ovsdb_datum *, + const union ovsdb_atom *value, + const struct ovsdb_type *, + const union ovsdb_atom *range_end_atom); ++void ovsdb_datum_add_from_index_unsafe(struct ovsdb_datum *dst, ++ const struct ovsdb_datum *src, ++ size_t idx, ++ const struct ovsdb_type *type); + + /* Transactions with named-uuid row names. */ + struct json *ovsdb_datum_to_json_with_row_names(const struct ovsdb_datum *, diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c index c19128d55c..882ede7559 100644 --- a/lib/ovsdb-idl.c @@ -2280,6 +4100,22 @@ index 62c4621181..321043282e 100644 set_op_destroy(set_op, type); } hmap_destroy(&list->hmap); +diff --git a/lib/packets.c b/lib/packets.c +index d0fba81766..874066e3c6 100644 +--- a/lib/packets.c ++++ b/lib/packets.c +@@ -427,9 +427,9 @@ add_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse, + } + + if (!l3_encap) { +- ovs_be32 *header = dp_packet_push_uninit(packet, MPLS_HLEN); ++ struct mpls_hdr *header = dp_packet_push_uninit(packet, MPLS_HLEN); + +- *header = lse; ++ put_16aligned_be32(&header->mpls_lse, lse); + packet->l2_5_ofs = 0; + packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, + ntohs(ethtype)); diff --git a/lib/pcap-file.c b/lib/pcap-file.c index 41835f6f4d..3ed7ea4880 100644 --- a/lib/pcap-file.c @@ -2774,7 +4610,7 @@ index 9ccb7d4cc4..bf66393df9 100644 struct sockaddr_storage *ssp, int *fdp, uint8_t dscp); diff --git a/lib/sset.c b/lib/sset.c -index b2e3f43ec9..c3197e305f 100644 +index b2e3f43ec9..6fbaa9d60d 100644 --- a/lib/sset.c +++ b/lib/sset.c @@ -212,9 +212,9 @@ sset_add_array(struct sset *set, char **names, size_t n) @@ -2789,7 +4625,18 @@ index b2e3f43ec9..c3197e305f 100644 sset_delete(set, SSET_NODE_FROM_NAME(name)); } } -@@ -320,9 +320,9 @@ sset_at_position(const struct sset *set, struct sset_position *pos) +@@ -312,7 +312,9 @@ sset_at_position(const struct sset *set, struct sset_position *pos) + struct hmap_node *hmap_node; + + hmap_node = hmap_at_position(&set->map, &pos->pos); +- return SSET_NODE_FROM_HMAP_NODE(hmap_node); ++ return hmap_node ++ ? SSET_NODE_FROM_HMAP_NODE(hmap_node) ++ : NULL; + } + + /* Replaces 'a' by the intersection of 'a' and 'b'. That is, removes from 'a' +@@ -320,9 +322,9 @@ sset_at_position(const struct sset *set, struct sset_position *pos) void sset_intersect(struct sset *a, const struct sset *b) { @@ -2869,7 +4716,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..df73a43d4c 100644 +index adb2d3182a..6301e30365 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -568,16 +568,17 @@ nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower) @@ -2969,7 +4816,51 @@ index adb2d3182a..df73a43d4c 100644 action->type = TC_ACT_PEDIT; return 0; -@@ -1487,7 +1485,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) +@@ -1314,8 +1312,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; +- const struct tcf_t *tm; + struct tc_action *action; ++ struct tcf_t tm; + + if (!nl_parse_nested(options, gact_policy, gact_attrs, + ARRAY_SIZE(gact_policy))) { +@@ -1335,8 +1333,9 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower) + return EINVAL; + } + +- tm = nl_attr_get_unspec(gact_attrs[TCA_GACT_TM], sizeof *tm); +- nl_parse_tcf(tm, flower); ++ memcpy(&tm, nl_attr_get_unspec(gact_attrs[TCA_GACT_TM], sizeof tm), ++ sizeof tm); ++ nl_parse_tcf(&tm, flower); + + return 0; + } +@@ -1357,9 +1356,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; +- const struct tcf_t *tm; + struct nlattr *mirred_tm; + struct tc_action *action; ++ struct tcf_t tm; + + if (!nl_parse_nested(options, mirred_policy, mirred_attrs, + ARRAY_SIZE(mirred_policy))) { +@@ -1387,8 +1386,8 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower) + action->type = TC_ACT_OUTPUT; + + mirred_tm = mirred_attrs[TCA_MIRRED_TM]; +- tm = nl_attr_get_unspec(mirred_tm, sizeof *tm); +- nl_parse_tcf(tm, flower); ++ memcpy(&tm, nl_attr_get_unspec(mirred_tm, sizeof tm), sizeof tm); ++ nl_parse_tcf(&tm, flower); + + return 0; + } +@@ -1487,7 +1486,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) if (ipv4_max) { ovs_be32 addr = nl_attr_get_be32(ipv4_max); @@ -2980,7 +4871,7 @@ index adb2d3182a..df73a43d4c 100644 } } else if (ipv6_min) { action->ct.range.ip_family = AF_INET6; -@@ -1496,7 +1496,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) +@@ -1496,7 +1497,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); @@ -2991,7 +4882,7 @@ index adb2d3182a..df73a43d4c 100644 } } -@@ -1504,6 +1506,10 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) +@@ -1504,6 +1507,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); @@ -3002,7 +4893,7 @@ index adb2d3182a..df73a43d4c 100644 } } } -@@ -1702,6 +1708,9 @@ static const struct nl_policy stats_policy[] = { +@@ -1702,6 +1709,9 @@ static const struct nl_policy stats_policy[] = { [TCA_STATS_BASIC] = { .type = NL_A_UNSPEC, .min_len = sizeof(struct gnet_stats_basic), .optional = false, }, @@ -3012,7 +4903,7 @@ index adb2d3182a..df73a43d4c 100644 }; static int -@@ -1714,8 +1723,11 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, +@@ -1714,8 +1724,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)]; @@ -3020,13 +4911,11 @@ index adb2d3182a..df73a43d4c 100644 - const struct gnet_stats_basic *bs; + struct ovs_flow_stats *stats_sw = &flower->stats_sw; + struct ovs_flow_stats *stats_hw = &flower->stats_hw; -+ const struct gnet_stats_basic *bs_all = NULL; -+ const struct gnet_stats_basic *bs_hw = NULL; -+ struct gnet_stats_basic bs_sw = { .packets = 0, .bytes = 0, }; ++ struct gnet_stats_basic bs_all, bs_hw, bs_sw; int err = 0; if (!nl_parse_nested(action, act_policy, action_attrs, -@@ -1771,10 +1783,26 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, +@@ -1771,10 +1782,30 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, return EPROTO; } @@ -3034,16 +4923,19 @@ index adb2d3182a..df73a43d4c 100644 - if (bs->packets) { - put_32aligned_u64(&stats->n_packets, bs->packets); - put_32aligned_u64(&stats->n_bytes, bs->bytes); -+ bs_all = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs_all); ++ memcpy(&bs_all, ++ nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof bs_all), ++ sizeof bs_all); + if (stats_attrs[TCA_STATS_BASIC_HW]) { -+ bs_hw = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC_HW], -+ sizeof *bs_hw); ++ memcpy(&bs_hw, nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC_HW], ++ sizeof bs_hw), ++ sizeof bs_hw); + -+ bs_sw.packets = bs_all->packets - bs_hw->packets; -+ bs_sw.bytes = bs_all->bytes - bs_hw->bytes; ++ bs_sw.packets = bs_all.packets - bs_hw.packets; ++ bs_sw.bytes = bs_all.bytes - bs_hw.bytes; + } else { -+ bs_sw.packets = bs_all->packets; -+ bs_sw.bytes = bs_all->bytes; ++ bs_sw.packets = bs_all.packets; ++ bs_sw.bytes = bs_all.bytes; + } + + if (bs_sw.packets > get_32aligned_u64(&stats_sw->n_packets)) { @@ -3051,13 +4943,14 @@ index adb2d3182a..df73a43d4c 100644 + put_32aligned_u64(&stats_sw->n_bytes, bs_sw.bytes); + } + -+ if (bs_hw && bs_hw->packets > get_32aligned_u64(&stats_hw->n_packets)) { -+ put_32aligned_u64(&stats_hw->n_packets, bs_hw->packets); -+ put_32aligned_u64(&stats_hw->n_bytes, bs_hw->bytes); ++ if (stats_attrs[TCA_STATS_BASIC_HW] ++ && bs_hw.packets > get_32aligned_u64(&stats_hw->n_packets)) { ++ put_32aligned_u64(&stats_hw->n_packets, bs_hw.packets); ++ put_32aligned_u64(&stats_hw->n_bytes, bs_hw.bytes); } return 0; -@@ -2399,14 +2427,14 @@ nl_msg_put_act_flags(struct ofpbuf *request) { +@@ -2399,14 +2430,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 @@ -3075,7 +4968,7 @@ index adb2d3182a..df73a43d4c 100644 max_offset = m->offset + m->size; start_offset = ROUND_DOWN(m->offset, 4); -@@ -2473,7 +2501,8 @@ csum_update_flag(struct tc_flower *flower, +@@ -2473,7 +2504,8 @@ csum_update_flag(struct tc_flower *flower, static int nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request, @@ -3085,7 +4978,7 @@ index adb2d3182a..df73a43d4c 100644 { struct { struct tc_pedit sel; -@@ -2497,7 +2526,7 @@ nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request, +@@ -2497,12 +2529,12 @@ nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request, continue; } @@ -3094,7 +4987,14 @@ index adb2d3182a..df73a43d4c 100644 &first_word_mask, &mask, &data); for (j = 0; j < cnt; j++, mask++, data++, cur_offset += 4) { -@@ -2556,6 +2585,29 @@ nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index) +- ovs_be32 mask_word = *mask; +- ovs_be32 data_word = *data; ++ ovs_be32 mask_word = get_unaligned_be32(mask); ++ ovs_be32 data_word = get_unaligned_be32(data); + + if (j == 0) { + mask_word &= first_word_mask; +@@ -2556,6 +2588,29 @@ nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index) nl_msg_end_nested(request, act_offset); } @@ -3124,7 +5024,7 @@ index adb2d3182a..df73a43d4c 100644 static int nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) { -@@ -2572,20 +2624,22 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) +@@ -2572,20 +2627,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++) { @@ -3153,7 +5053,7 @@ index adb2d3182a..df73a43d4c 100644 } } break; -@@ -2914,13 +2968,13 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) +@@ -2914,13 +2971,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); } @@ -3172,7 +5072,7 @@ index adb2d3182a..df73a43d4c 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 +3047,79 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) +@@ -2993,12 +3050,79 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) return 0; } @@ -3253,7 +5153,7 @@ index adb2d3182a..df73a43d4c 100644 return false; } -@@ -3011,8 +3132,8 @@ cmp_tc_flower_match_action(const struct tc_flower *a, +@@ -3011,8 +3135,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) { @@ -3264,7 +5164,7 @@ index adb2d3182a..df73a43d4c 100644 return false; } } -@@ -3022,14 +3143,15 @@ cmp_tc_flower_match_action(const struct tc_flower *a, +@@ -3022,14 +3146,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) { @@ -3344,9 +5244,18 @@ index a147ca461d..d6cdddd169 100644 int tc_del_filter(struct tcf_id *id); int tc_get_flower(struct tcf_id *id, struct tc_flower *flower); diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c -index 58269d3b16..f9fee37939 100644 +index 58269d3b16..050eafa6b8 100644 --- a/lib/tnl-ports.c +++ b/lib/tnl-ports.c +@@ -71,7 +71,7 @@ tnl_port_cast(const struct cls_rule *cr) + { + BUILD_ASSERT_DECL(offsetof(struct tnl_port_in, cr) == 0); + +- return CONTAINER_OF(cr, struct tnl_port_in, cr); ++ return cr ? CONTAINER_OF(cr, struct tnl_port_in, cr) : NULL; + } + + static void @@ -259,14 +259,14 @@ ipdev_map_delete(struct ip_device *ip_dev, ovs_be16 tp_port, uint8_t nw_proto) void tnl_port_map_delete(odp_port_t port, const char type[]) @@ -3615,19 +5524,144 @@ index ed58de17de..aad9f9c77a 100644 free(nf_flow); } diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c -index 9280e008ea..fc927fe866 100644 +index 9280e008ea..742eed3998 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c -@@ -1078,7 +1078,7 @@ dpif_ipfix_set_options( +@@ -926,17 +926,21 @@ dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter) + static void + dpif_ipfix_bridge_exporter_set_options( + struct dpif_ipfix_bridge_exporter *exporter, +- const struct ofproto_ipfix_bridge_exporter_options *options) ++ const struct ofproto_ipfix_bridge_exporter_options *options, ++ bool *options_changed) + { +- bool options_changed; +- + if (!options || sset_is_empty(&options->targets)) { + /* No point in doing any work if there are no targets. */ +- dpif_ipfix_bridge_exporter_clear(exporter); ++ if (exporter->options) { ++ dpif_ipfix_bridge_exporter_clear(exporter); ++ *options_changed = true; ++ } else { ++ *options_changed = false; ++ } + return; + } + +- options_changed = ( ++ *options_changed = ( + !exporter->options + || !ofproto_ipfix_bridge_exporter_options_equal( + options, exporter->options)); +@@ -945,7 +949,7 @@ dpif_ipfix_bridge_exporter_set_options( + * shortchanged in collectors (which indicates that opening one or + * more of the configured collectors failed, so that we should + * retry). */ +- if (options_changed ++ if (*options_changed + || collectors_count(exporter->exporter.collectors) + < sset_count(&options->targets)) { + if (!dpif_ipfix_exporter_set_options( +@@ -957,7 +961,7 @@ dpif_ipfix_bridge_exporter_set_options( + } + + /* Avoid reconfiguring if options didn't change. */ +- if (!options_changed) { ++ if (!*options_changed) { + return; + } + +@@ -1015,17 +1019,21 @@ dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter) + static bool + dpif_ipfix_flow_exporter_set_options( + struct dpif_ipfix_flow_exporter *exporter, +- const struct ofproto_ipfix_flow_exporter_options *options) ++ const struct ofproto_ipfix_flow_exporter_options *options, ++ bool *options_changed) + { +- bool options_changed; +- + if (sset_is_empty(&options->targets)) { + /* No point in doing any work if there are no targets. */ +- dpif_ipfix_flow_exporter_clear(exporter); ++ if (exporter->options) { ++ dpif_ipfix_flow_exporter_clear(exporter); ++ *options_changed = true; ++ } else { ++ *options_changed = false; ++ } + return true; + } + +- options_changed = ( ++ *options_changed = ( + !exporter->options + || !ofproto_ipfix_flow_exporter_options_equal( + options, exporter->options)); +@@ -1034,7 +1042,7 @@ dpif_ipfix_flow_exporter_set_options( + * shortchanged in collectors (which indicates that opening one or + * more of the configured collectors failed, so that we should + * retry). */ +- if (options_changed ++ if (*options_changed + || collectors_count(exporter->exporter.collectors) + < sset_count(&options->targets)) { + if (!dpif_ipfix_exporter_set_options( +@@ -1046,7 +1054,7 @@ dpif_ipfix_flow_exporter_set_options( + } + + /* Avoid reconfiguring if options didn't change. */ +- if (!options_changed) { ++ if (!*options_changed) { + return true; + } + +@@ -1069,7 +1077,7 @@ remove_flow_exporter(struct dpif_ipfix *di, + free(node); + } + +-void ++bool + dpif_ipfix_set_options( + struct dpif_ipfix *di, + const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options, +@@ -1077,16 +1085,19 @@ dpif_ipfix_set_options( + size_t n_flow_exporters_options) OVS_EXCLUDED(mutex) { int i; ++ bool beo_changed, feo_changed, entry_changed; struct ofproto_ipfix_flow_exporter_options *options; - struct dpif_ipfix_flow_exporter_map_node *node, *next; + struct dpif_ipfix_flow_exporter_map_node *node; ovs_mutex_lock(&mutex); dpif_ipfix_bridge_exporter_set_options(&di->bridge_exporter, -@@ -1103,7 +1103,7 @@ dpif_ipfix_set_options( +- bridge_exporter_options); ++ bridge_exporter_options, ++ &beo_changed); + + /* Add new flow exporters and update current flow exporters. */ + options = (struct ofproto_ipfix_flow_exporter_options *) + flow_exporters_options; ++ feo_changed = false; + for (i = 0; i < n_flow_exporters_options; i++) { + node = dpif_ipfix_find_flow_exporter_map_node( + di, options->collector_set_id); +@@ -1095,15 +1106,19 @@ dpif_ipfix_set_options( + dpif_ipfix_flow_exporter_init(&node->exporter); + hmap_insert(&di->flow_exporter_map, &node->node, + hash_int(options->collector_set_id, 0)); ++ feo_changed = true; + } +- if (!dpif_ipfix_flow_exporter_set_options(&node->exporter, options)) { ++ if (!dpif_ipfix_flow_exporter_set_options(&node->exporter, ++ options, ++ &entry_changed)) { + remove_flow_exporter(di, node); + } ++ feo_changed = entry_changed ? true : feo_changed; + options++; } /* Remove dropped flow exporters, if any needs to be removed. */ @@ -3636,7 +5670,20 @@ index 9280e008ea..fc927fe866 100644 /* This is slow but doesn't take any extra memory, and * this table is not supposed to contain many rows anyway. */ options = (struct ofproto_ipfix_flow_exporter_options *) -@@ -1215,7 +1215,7 @@ static void +@@ -1117,10 +1132,12 @@ dpif_ipfix_set_options( + } + if (i == n_flow_exporters_options) { /* Not found. */ + remove_flow_exporter(di, node); ++ feo_changed = true; + } + } + + ovs_mutex_unlock(&mutex); ++ return beo_changed || feo_changed; + } + + struct dpif_ipfix * +@@ -1215,7 +1232,7 @@ static void dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex) { struct dpif_ipfix_flow_exporter_map_node *exp_node; @@ -3645,7 +5692,7 @@ index 9280e008ea..fc927fe866 100644 dpif_ipfix_bridge_exporter_clear(&di->bridge_exporter); -@@ -1224,7 +1224,7 @@ dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex) +@@ -1224,7 +1241,7 @@ dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex) free(exp_node); } @@ -3654,7 +5701,7 @@ index 9280e008ea..fc927fe866 100644 dpif_ipfix_del_port__(di, dip); } } -@@ -2799,7 +2799,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, +@@ -2799,7 +2816,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, bool forced_end, const uint64_t export_time_usec, const uint32_t export_time_sec) { @@ -3663,7 +5710,7 @@ index 9280e008ea..fc927fe866 100644 uint64_t max_flow_start_timestamp_usec; bool template_msg_sent = false; enum ipfix_flow_end_reason flow_end_reason; -@@ -2811,7 +2811,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, +@@ -2811,7 +2828,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, max_flow_start_timestamp_usec = export_time_usec - 1000000LL * exporter->cache_active_timeout; @@ -3672,6 +5719,19 @@ index 9280e008ea..fc927fe866 100644 &exporter->cache_flow_start_timestamp_list) { if (forced_end) { flow_end_reason = FORCED_END; +diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h +index 1f42cd5275..75c0ab81ac 100644 +--- a/ofproto/ofproto-dpif-ipfix.h ++++ b/ofproto/ofproto-dpif-ipfix.h +@@ -48,7 +48,7 @@ bool dpif_ipfix_get_bridge_exporter_output_sampling(const struct dpif_ipfix *); + bool dpif_ipfix_get_flow_exporter_tunnel_sampling(const struct dpif_ipfix *, + const uint32_t); + bool dpif_ipfix_is_tunnel_port(const struct dpif_ipfix *, odp_port_t); +-void dpif_ipfix_set_options( ++bool dpif_ipfix_set_options( + struct dpif_ipfix *, + const struct ofproto_ipfix_bridge_exporter_options *, + const struct ofproto_ipfix_flow_exporter_options *, size_t); diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index 30e7caf54a..e8e1de920b 100644 --- a/ofproto/ofproto-dpif-sflow.c @@ -3751,7 +5811,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..17f7e2883f 100644 +index 578cbfe581..7716c22f49 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) @@ -3835,7 +5895,25 @@ index 578cbfe581..17f7e2883f 100644 static struct xbundle * xbundle_lookup(struct xlate_cfg *xcfg, const struct ofbundle *ofbundle) { -@@ -3015,7 +3032,7 @@ xlate_normal(struct xlate_ctx *ctx) +@@ -2125,9 +2142,14 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle, + int snaplen; + + /* Get the details of the mirror represented by the rightmost 1-bit. */ +- ovs_assert(mirror_get(xbridge->mbridge, raw_ctz(mirrors), +- &vlans, &dup_mirrors, +- &out, &snaplen, &out_vlan)); ++ if (OVS_UNLIKELY(!mirror_get(xbridge->mbridge, raw_ctz(mirrors), ++ &vlans, &dup_mirrors, ++ &out, &snaplen, &out_vlan))) { ++ /* The mirror got reconfigured before we got to read it's ++ * configuration. */ ++ mirrors = zero_rightmost_1bit(mirrors); ++ continue; ++ } + + + /* If this mirror selects on the basis of VLAN, and it does not select +@@ -3015,7 +3037,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) @@ -3844,7 +5922,7 @@ index 578cbfe581..17f7e2883f 100644 ) { update_learning_table(ctx, in_xbundle, flow->dl_src, vlan, is_grat_arp); -@@ -3024,12 +3041,14 @@ xlate_normal(struct xlate_ctx *ctx) +@@ -3024,12 +3046,14 @@ xlate_normal(struct xlate_ctx *ctx) struct xc_entry *entry; /* Save just enough info to update mac learning table later. */ @@ -3865,7 +5943,15 @@ index 578cbfe581..17f7e2883f 100644 } /* Determine output bundle. */ -@@ -3523,6 +3542,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow, +@@ -3048,7 +3072,6 @@ xlate_normal(struct xlate_ctx *ctx) + */ + ctx->xout->slow |= SLOW_ACTION; + +- memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); + 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 +3546,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow, dst_flow->dl_dst = dmac; dst_flow->dl_src = smac; @@ -3875,7 +5961,37 @@ index 578cbfe581..17f7e2883f 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; -@@ -4176,6 +4198,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -3654,14 +3680,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) { ++ struct in6_addr nh_s_ip6 = in6addr_any; ++ + xlate_report(ctx, OFT_DETAIL, + "neighbor cache miss for %s on bridge %s, " + "sending %s request", + buf_dip6, out_dev->xbridge->name, d_ip ? "ARP" : "ND"); ++ ++ err = ovs_router_get_netdev_source_address(&d_ip6, ++ out_dev->xbridge->name, ++ &nh_s_ip6); ++ if (err) { ++ nh_s_ip6 = s_ip6; ++ } ++ + if (d_ip) { +- tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip); ++ ovs_be32 nh_s_ip; ++ ++ nh_s_ip = in6_addr_get_mapped_ipv4(&nh_s_ip6); ++ tnl_send_arp_request(ctx, out_dev, smac, nh_s_ip, d_ip); + } else { +- tnl_send_nd_request(ctx, out_dev, smac, &s_ip6, &d_ip6); ++ tnl_send_nd_request(ctx, out_dev, smac, &nh_s_ip6, &d_ip6); + } + return err; + } +@@ -4176,6 +4215,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)); @@ -3886,6 +6002,29 @@ index 578cbfe581..17f7e2883f 100644 } } +@@ -5622,7 +5665,8 @@ xlate_sample_action(struct xlate_ctx *ctx, + + /* Scale the probability from 16-bit to 32-bit while representing + * the same percentage. */ +- uint32_t probability = (os->probability << 16) | os->probability; ++ uint32_t probability = ++ ((uint32_t) os->probability << 16) | os->probability; + + /* If ofp_port in flow sample action is equel to ofp_port, + * this sample action is a input port action. */ +@@ -7784,6 +7828,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) + goto exit; + } + ++ if (!xin->frozen_state ++ && xin->flow.ct_state ++ && xin->flow.ct_state & CS_TRACKED) { ++ ctx.conntracked = true; ++ } ++ + /* Tunnel metadata in udpif format must be normalized before translation. */ + if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) { + const struct tun_table *tun_tab = ofproto_get_tun_tab( diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h index 851088d794..2ba90e999c 100644 --- a/ofproto/ofproto-dpif-xlate.h @@ -3899,7 +6038,7 @@ index 851088d794..2ba90e999c 100644 void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *, const char *name, enum port_vlan_mode, diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c -index 8143dd965f..6601f23464 100644 +index 8143dd965f..f9562dee87 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -215,10 +215,6 @@ struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers); @@ -3950,7 +6089,16 @@ index 8143dd965f..6601f23464 100644 OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { CLS_FOR_EACH (rule, up.cr, &table->cls) { -@@ -1945,7 +1936,7 @@ run(struct ofproto *ofproto_) +@@ -1857,6 +1848,8 @@ destruct(struct ofproto *ofproto_, bool del) + + seq_destroy(ofproto->ams_seq); + ++ /* Wait for all the meter destroy work to finish. */ ++ ovsrcu_barrier(); + close_dpif_backer(ofproto->backer, del); + } + +@@ -1945,7 +1938,7 @@ run(struct ofproto *ofproto_) new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif)); if (ofproto->dump_seq != new_dump_seq) { @@ -3959,7 +6107,7 @@ index 8143dd965f..6601f23464 100644 long long now = time_msec(); /* We know stats are relatively fresh, so now is a good time to do some -@@ -1955,7 +1946,7 @@ run(struct ofproto *ofproto_) +@@ -1955,7 +1948,7 @@ run(struct ofproto *ofproto_) /* Expire OpenFlow flows whose idle_timeout or hard_timeout * has passed. */ ovs_mutex_lock(&ofproto_mutex); @@ -3968,20 +6116,59 @@ index 8143dd965f..6601f23464 100644 &ofproto->up.expirable) { rule_expire(rule_dpif_cast(rule), now); } -@@ -2371,6 +2362,12 @@ set_ipfix( +@@ -2346,6 +2339,7 @@ set_ipfix( + struct dpif_ipfix *di = ofproto->ipfix; + bool has_options = bridge_exporter_options || flow_exporters_options; + bool new_di = false; ++ bool options_changed = false; + + if (has_options && !di) { + di = ofproto->ipfix = dpif_ipfix_create(); +@@ -2355,7 +2349,7 @@ set_ipfix( + if (di) { + /* Call set_options in any case to cleanly flush the flow + * caches in the last exporters that are to be destroyed. */ +- dpif_ipfix_set_options( ++ options_changed = dpif_ipfix_set_options( + di, bridge_exporter_options, flow_exporters_options, + n_flow_exporters_options); + +@@ -2371,6 +2365,10 @@ set_ipfix( dpif_ipfix_unref(di); ofproto->ipfix = NULL; } + -+ /* TODO: need to consider ipfix option changes more than -+ * enable/disable */ -+ if (new_di || !ofproto->ipfix) { ++ if (new_di || options_changed) { + ofproto->backer->need_revalidate = REV_RECONFIGURE; + } } return 0; -@@ -3106,11 +3103,11 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos) +@@ -2493,11 +2491,11 @@ set_lldp(struct ofport *ofport_, + { + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); ++ bool old_enable = lldp_is_enabled(ofport->lldp); + int error = 0; + +- if (cfg) { ++ if (cfg && !smap_is_empty(cfg)) { + if (!ofport->lldp) { +- ofproto->backer->need_revalidate = REV_RECONFIGURE; + ofport->lldp = lldp_create(ofport->up.netdev, ofport_->mtu, cfg); + } + +@@ -2509,6 +2507,9 @@ set_lldp(struct ofport *ofport_, + } else if (ofport->lldp) { + lldp_unref(ofport->lldp); + ofport->lldp = NULL; ++ } ++ ++ if (lldp_is_enabled(ofport->lldp) != old_enable) { + ofproto->backer->need_revalidate = REV_RECONFIGURE; + } + +@@ -3106,11 +3107,11 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos) { struct ofproto_dpif *ofproto = bundle->ofproto; struct mac_learning *ml = ofproto->ml; @@ -3995,7 +6182,7 @@ index 8143dd965f..6601f23464 100644 if (mac_entry_get_port(ml, mac) == bundle) { if (all_ofprotos) { struct ofproto_dpif *o; -@@ -3141,13 +3138,13 @@ bundle_move(struct ofbundle *old, struct ofbundle *new) +@@ -3141,13 +3142,13 @@ bundle_move(struct ofbundle *old, struct ofbundle *new) { struct ofproto_dpif *ofproto = old->ofproto; struct mac_learning *ml = ofproto->ml; @@ -4011,7 +6198,7 @@ index 8143dd965f..6601f23464 100644 if (mac_entry_get_port(ml, mac) == old) { mac_entry_set_port(ml, mac, new); } -@@ -3244,7 +3241,7 @@ static void +@@ -3244,7 +3245,7 @@ static void bundle_destroy(struct ofbundle *bundle) { struct ofproto_dpif *ofproto; @@ -4020,7 +6207,7 @@ index 8143dd965f..6601f23464 100644 if (!bundle) { return; -@@ -3257,7 +3254,7 @@ bundle_destroy(struct ofbundle *bundle) +@@ -3257,7 +3258,7 @@ bundle_destroy(struct ofbundle *bundle) xlate_bundle_remove(bundle); xlate_txn_commit(); @@ -4029,7 +6216,7 @@ index 8143dd965f..6601f23464 100644 bundle_del_port(port); } -@@ -3347,9 +3344,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, +@@ -3347,9 +3348,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, } } if (!ok || ovs_list_size(&bundle->ports) != s->n_members) { @@ -4040,7 +6227,7 @@ index 8143dd965f..6601f23464 100644 for (i = 0; i < s->n_members; i++) { if (s->members[i] == port->up.ofp_port) { goto found; -@@ -3963,6 +3958,10 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev) +@@ -3963,6 +3962,10 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev) simap_put(&ofproto->backer->tnl_backers, dp_port_name, odp_to_u32(port_no)); } @@ -4051,7 +6238,7 @@ index 8143dd965f..6601f23464 100644 } if (netdev_get_tunnel_config(netdev)) { -@@ -4471,12 +4470,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, +@@ -4471,12 +4474,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, atomic_add_relaxed(&tbl->n_matched, stats->n_packets, &orig); } if (xcache) { @@ -4071,7 +6258,7 @@ index 8143dd965f..6601f23464 100644 } return rule; } -@@ -4507,12 +4508,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, +@@ -4507,12 +4512,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, stats->n_packets, &orig); } if (xcache) { @@ -4091,7 +6278,7 @@ index 8143dd965f..6601f23464 100644 } if (rule) { goto out; /* Match. */ -@@ -5550,9 +5553,9 @@ ct_zone_timeout_policy_sweep(struct dpif_backer *backer) +@@ -5550,9 +5557,9 @@ ct_zone_timeout_policy_sweep(struct dpif_backer *backer) { if (!ovs_list_is_empty(&backer->ct_tp_kill_list) && time_msec() >= timeout_policy_cleanup_timer) { @@ -4103,7 +6290,31 @@ index 8143dd965f..6601f23464 100644 if (!ct_dpif_del_timeout_policy(backer->dpif, ct_tp->tp_id)) { ovs_list_remove(&ct_tp->list_node); ct_timeout_policy_destroy(ct_tp, backer->tp_ids); -@@ -5818,15 +5821,7 @@ ofproto_dpif_lookup_by_name(const char *name) +@@ -5594,6 +5601,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id, + ct_timeout_policy_unref(backer, ct_zone->ct_tp); + ct_zone->ct_tp = ct_tp; + ct_tp->ref_count++; ++ backer->need_revalidate = REV_RECONFIGURE; + } + } else { + struct ct_zone *new_ct_zone = ct_zone_alloc(zone_id); +@@ -5601,6 +5609,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id, + cmap_insert(&backer->ct_zones, &new_ct_zone->node, + hash_int(zone_id, 0)); + ct_tp->ref_count++; ++ backer->need_revalidate = REV_RECONFIGURE; + } + } + +@@ -5617,6 +5626,7 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id) + if (ct_zone) { + ct_timeout_policy_unref(backer, ct_zone->ct_tp); + ct_zone_remove_and_destroy(backer, ct_zone); ++ backer->need_revalidate = REV_RECONFIGURE; + } + } + +@@ -5818,15 +5828,7 @@ ofproto_dpif_lookup_by_name(const char *name) struct ofproto_dpif * ofproto_dpif_lookup_by_uuid(const struct uuid *uuid) { @@ -4134,7 +6345,7 @@ index 14b909973d..47e96e62e1 100644 void ofproto_init_tables(struct ofproto *, int n_tables); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c -index 56aeac7209..2ed1078007 100644 +index 56aeac7209..933f7de2dc 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -549,6 +549,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type, @@ -4322,6 +6533,15 @@ index 56aeac7209..2ed1078007 100644 ofproto->ofproto_class->group_dealloc(*ofgroup); } return error; +@@ -8902,7 +8963,7 @@ eviction_group_hash_rule(struct rule *rule) + hash = table->eviction_group_id_basis; + miniflow_expand(rule->cr.match.flow, &flow); + for (sf = table->eviction_fields; +- sf < &table->eviction_fields[table->n_eviction_fields]; ++ sf && sf < &table->eviction_fields[table->n_eviction_fields]; + sf++) + { + if (mf_are_prereqs_ok(sf->field, &flow, NULL)) { @@ -9138,8 +9199,8 @@ oftable_configure_eviction(struct oftable *table, unsigned int eviction, /* Destroy existing eviction groups, then destroy and recreate data @@ -4348,6 +6568,48 @@ index b0262da2df..4e15167ab7 100644 #ifdef __cplusplus } #endif +diff --git a/ovsdb/.gitignore b/ovsdb/.gitignore +index fbcefafc6e..a4f9d38f11 100644 +--- a/ovsdb/.gitignore ++++ b/ovsdb/.gitignore +@@ -1,5 +1,7 @@ + /_server.ovsschema.inc + /_server.ovsschema.stamp ++/local-config.ovsschema.stamp ++/ovsdb.local-config.5 + /ovsdb-client + /ovsdb-client.1 + /ovsdb-doc +diff --git a/ovsdb/automake.mk b/ovsdb/automake.mk +index 62cc02686f..3b3140102b 100644 +--- a/ovsdb/automake.mk ++++ b/ovsdb/automake.mk +@@ -148,4 +148,25 @@ ovsdb/ovsdb-server.5: \ + $(srcdir)/ovsdb/_server.xml > $@.tmp && \ + mv $@.tmp $@ + ++EXTRA_DIST += ovsdb/local-config.ovsschema ++pkgdata_DATA += ovsdb/local-config.ovsschema ++ ++# Version checking for local-config.ovsschema. ++ALL_LOCAL += ovsdb/local-config.ovsschema.stamp ++ovsdb/local-config.ovsschema.stamp: ovsdb/local-config.ovsschema ++ $(srcdir)/build-aux/cksum-schema-check $? $@ ++CLEANFILES += ovsdb/local-config.ovsschema.stamp ++ ++# Local_Config schema documentation ++EXTRA_DIST += ovsdb/local-config.xml ++CLEANFILES += ovsdb/ovsdb.local-config.5 ++man_MANS += ovsdb/ovsdb.local-config.5 ++ovsdb/ovsdb.local-config.5: \ ++ ovsdb/ovsdb-doc ovsdb/ ovsdb/local-config.xml ovsdb/local-config.ovsschema ++ $(AM_V_GEN)$(OVSDB_DOC) \ ++ --version=$(VERSION) \ ++ $(srcdir)/ovsdb/local-config.ovsschema \ ++ $(srcdir)/ovsdb/local-config.xml > $@.tmp && \ ++ mv $@.tmp $@ ++ + EXTRA_DIST += ovsdb/TODO.rst diff --git a/ovsdb/condition.c b/ovsdb/condition.c index 388dd54a16..9aa3788dbb 100644 --- a/ovsdb/condition.c @@ -4370,6 +6632,18 @@ index 388dd54a16..9aa3788dbb 100644 hmap_remove(&o_column->o_clauses, &c->hmap_node); free(c); } +diff --git a/ovsdb/file.c b/ovsdb/file.c +index 9f44007d97..ca80c28235 100644 +--- a/ovsdb/file.c ++++ b/ovsdb/file.c +@@ -524,6 +524,7 @@ ovsdb_file_read__(const char *filename, bool rw, + + error = ovsdb_txn_replay_commit(txn); + if (error) { ++ ovsdb_error_destroy(error); + ovsdb_storage_unread(storage); + break; + } diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c index 351c39d8aa..916a1f414e 100644 --- a/ovsdb/jsonrpc-server.c @@ -4491,6 +6765,357 @@ index 351c39d8aa..916a1f414e 100644 ovsdb_jsonrpc_monitor_destroy(m, false); } } +diff --git a/ovsdb/local-config.ovsschema b/ovsdb/local-config.ovsschema +new file mode 100644 +index 0000000000..bd86d0f4f6 +--- /dev/null ++++ b/ovsdb/local-config.ovsschema +@@ -0,0 +1,43 @@ ++{ ++ "name": "Local_Config", ++ "version": "1.0.0", ++ "cksum": "2048726482 1858", ++ "tables": { ++ "Config": { ++ "columns": { ++ "connections": { ++ "type": {"key": {"type": "uuid", ++ "refTable": "Connection"}, ++ "min": 0, ++ "max": "unlimited"}}}, ++ "maxRows": 1, ++ "isRoot": true}, ++ "Connection": { ++ "columns": { ++ "target": {"type": "string"}, ++ "max_backoff": {"type": {"key": {"type": "integer", ++ "minInteger": 1000}, ++ "min": 0, ++ "max": 1}}, ++ "inactivity_probe": {"type": {"key": "integer", ++ "min": 0, ++ "max": 1}}, ++ "read_only": {"type": "boolean"}, ++ "role": {"type": "string"}, ++ "other_config": {"type": {"key": "string", ++ "value": "string", ++ "min": 0, ++ "max": "unlimited"}}, ++ "external_ids": {"type": {"key": "string", ++ "value": "string", ++ "min": 0, ++ "max": "unlimited"}}, ++ "is_connected": {"type": "boolean", "ephemeral": true}, ++ "status": {"type": {"key": "string", ++ "value": "string", ++ "min": 0, ++ "max": "unlimited"}, ++ "ephemeral": true}}, ++ "indexes": [["target"]]} ++ } ++} +diff --git a/ovsdb/local-config.xml b/ovsdb/local-config.xml +new file mode 100644 +index 0000000000..b502aea4cf +--- /dev/null ++++ b/ovsdb/local-config.xml +@@ -0,0 +1,296 @@ ++ ++ ++

++ This database is for local configuration of an ovsdb-server. The ++ database is meant to be unique, even among multiple clustered db ++ servers, so that configuration that is local to that server can ++ be configured separately (e.g. Connection information). ++

++ ++ ++

++ The root local configuration table for an ovsdb-server. This table ++ must have exactly one row. ++

++ ++ ++ Database clients to which the Open vSwitch database server should ++ connect or on which it should listen, along with options for how these ++ connections should be configured. See the ++ table for more information. ++ ++ ++
++ ++ ++

++ Configuration for a database connection to an Open vSwitch database ++ (OVSDB) client. ++

++ ++

++ This table primarily configures the Open vSwitch database server ++ (ovsdb-server). ++

++ ++

++ The Open vSwitch database server can initiate and maintain active ++ connections to remote clients. It can also listen for database ++ connections. ++

++ ++ ++ ++

Connection methods for clients.

++

++ The following connection methods are currently supported: ++

++
++
++ ssl:host[:port] ++
++
++

++ The specified SSL port on the host at the given ++ host, which can either be a DNS name (if built with ++ unbound library) or an IP address. A valid SSL configuration must ++ be provided when this form is used, this configuration can be ++ specified via command-line options or the ++ table. ++

++

++ If port is not specified, it defaults to 6640. ++

++

++ SSL support is an optional feature that is not always ++ built as part of Open vSwitch. ++

++
++ ++
++ tcp:host[:port] ++
++
++

++ The specified TCP port on the host at the given ++ host, which can either be a DNS name (if built with ++ unbound library) or an IP address. If host is an IPv6 ++ address, wrap it in square brackets, e.g. ++ tcp:[::1]:6640. ++

++

++ If port is not specified, it defaults to 6640. ++

++
++
++ pssl:[port][:host] ++
++
++

++ Listens for SSL connections on the specified TCP port. ++ Specify 0 for port to have the kernel automatically ++ choose an available port. If host, which can either ++ be a DNS name (if built with unbound library) or an IP address, ++ is specified, then connections are restricted to the resolved or ++ specified local IPaddress (either IPv4 or IPv6 address). If ++ host is an IPv6 address, wrap in square brackets, ++ e.g. pssl:6640:[::1]. If host is not ++ specified then it listens only on IPv4 (but not IPv6) addresses. ++ A valid SSL configuration must be provided when this form is ++ used, this can be specified either via command-line options or ++ the table. ++

++

++ If port is not specified, it defaults to 6640. ++

++

++ SSL support is an optional feature that is not always built as ++ part of Open vSwitch. ++

++
++
++ ptcp:[port][:host] ++
++
++

++ Listens for connections on the specified TCP port. ++ Specify 0 for port to have the kernel automatically ++ choose an available port. If host, which can either ++ be a DNS name (if built with unbound library) or an IP address, ++ is specified, then connections are restricted to the resolved or ++ specified local IP address (either IPv4 or IPv6 address). If ++ host is an IPv6 address, wrap it in square brackets, ++ e.g. ptcp:6640:[::1]. If host is not ++ specified then it listens only on IPv4 addresses. ++

++

++ If port is not specified, it defaults to 6640. ++

++
++
++

When multiple clients are configured, the ++ values must be unique. Duplicate values yield ++ unspecified results.

++
++ ++ ++ true to restrict these connections to read-only ++ transactions, false to allow them to modify the database. ++ ++ ++ ++ String containing role name for this connection entry. ++ ++
++ ++ ++ ++ Maximum number of milliseconds to wait between connection attempts. ++ Default is implementation-specific. ++ ++ ++ ++ Maximum number of milliseconds of idle time on connection to the client ++ before sending an inactivity probe message. If Open vSwitch does not ++ communicate with the client for the specified number of seconds, it ++ will send a probe. If a response is not received for the same ++ additional amount of time, Open vSwitch assumes the connection has been ++ broken and attempts to reconnect. Default is implementation-specific. ++ A value of 0 disables inactivity probes. ++ ++ ++ ++ ++

++ Key-value pair of is always updated. ++ Other key-value pairs in the status columns may be updated depends ++ on the type. ++

++ ++

++ When specifies a connection method that ++ listens for inbound connections (e.g. ptcp: or ++ punix:), both and ++ may also be updated while the ++ remaining key-value pairs are omitted. ++

++ ++

++ On the other hand, when specifies an ++ outbound connection, all key-value pairs may be updated, except ++ the above-mentioned two key-value pairs associated with inbound ++ connection targets. They are omitted. ++

++ ++ ++ true if currently connected to this client, ++ false otherwise. ++ ++ ++ ++ A human-readable description of the last error on the connection ++ to the manager; i.e. strerror(errno). This key ++ will exist only if an error has occurred. ++ ++ ++ ++

++ The state of the connection to the manager: ++

++
++
VOID
++
Connection is disabled.
++ ++
BACKOFF
++
Attempting to reconnect at an increasing period.
++ ++
CONNECTING
++
Attempting to connect.
++ ++
ACTIVE
++
Connected, remote host responsive.
++ ++
IDLE
++
Connection is idle. Waiting for response to keep-alive.
++
++

++ These values may change in the future. They are provided only for ++ human consumption. ++

++
++ ++ ++ The amount of time since this client last successfully connected ++ to the database (in seconds). Value is empty if client has never ++ successfully been connected. ++ ++ ++ ++ The amount of time since this client last disconnected from the ++ database (in seconds). Value is empty if client has never ++ disconnected. ++ ++ ++ ++ Space-separated list of the names of OVSDB locks that the connection ++ holds. Omitted if the connection does not hold any locks. ++ ++ ++ ++ Space-separated list of the names of OVSDB locks that the connection is ++ currently waiting to acquire. Omitted if the connection is not waiting ++ for any locks. ++ ++ ++ ++ Space-separated list of the names of OVSDB locks that the connection ++ has had stolen by another OVSDB client. Omitted if no locks have been ++ stolen from this connection. ++ ++ ++ ++ When specifies a connection method that ++ listens for inbound connections (e.g. ptcp: or ++ pssl:) and more than one connection is actually active, ++ the value is the number of active connections. Otherwise, this ++ key-value pair is omitted. ++ ++ ++ ++ When is ptcp: or ++ pssl:, this is the TCP port on which the OVSDB server is ++ listening. (This is particularly useful when specifies a port of 0, allowing the kernel to ++ choose any available port.) ++ ++
++ ++ ++ ++ The Differentiated Service Code Point (DSCP) is specified using 6 bits ++ in the Type of Service (TOS) field in the IP header. DSCP provides a ++ mechanism to classify the network traffic and provide Quality of ++ Service (QoS) on IP networks. ++ ++ The DSCP value specified here is used when establishing the connection ++ between the manager and the Open vSwitch. If no value is specified, a ++ default value of 48 is chosen. Valid DSCP values must be in the range ++ 0 to 63. ++ ++ ++ ++ ++ External configuration options ++ ++ ++ External client-defined key-value pairs ++ ++ ++
++
diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c index 0f222cc992..952fa902e4 100644 --- a/ovsdb/monitor.c @@ -4785,10 +7410,10 @@ index 30760233ee..e685c8103b 100644 raft_server_destroy(s); } diff --git a/ovsdb/raft.c b/ovsdb/raft.c -index 1a3447a8dd..530c5e5a3d 100644 +index 1a3447a8dd..856d083f21 100644 --- a/ovsdb/raft.c +++ b/ovsdb/raft.c -@@ -74,6 +74,7 @@ enum raft_failure_test { +@@ -74,9 +74,12 @@ enum raft_failure_test { FT_CRASH_BEFORE_SEND_EXEC_REQ, FT_CRASH_AFTER_SEND_EXEC_REQ, FT_CRASH_AFTER_RECV_APPEND_REQ_UPDATE, @@ -4796,7 +7421,12 @@ index 1a3447a8dd..530c5e5a3d 100644 FT_DELAY_ELECTION, FT_DONT_SEND_VOTE_REQUEST, FT_STOP_RAFT_RPC, -@@ -379,12 +380,19 @@ static bool raft_handle_write_error(struct raft *, struct ovsdb_error *); ++ FT_TRANSFER_LEADERSHIP, ++ FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ, + }; + static enum raft_failure_test failure_test; + +@@ -379,12 +382,19 @@ static bool raft_handle_write_error(struct raft *, struct ovsdb_error *); static void raft_run_reconfigure(struct raft *); static void raft_set_leader(struct raft *, const struct uuid *sid); @@ -4816,7 +7446,7 @@ index 1a3447a8dd..530c5e5a3d 100644 static char * raft_make_address_passive(const char *address_) { -@@ -692,8 +700,8 @@ static void +@@ -692,8 +702,8 @@ static void raft_set_servers(struct raft *raft, const struct hmap *new_servers, enum vlog_level level) { @@ -4827,7 +7457,7 @@ index 1a3447a8dd..530c5e5a3d 100644 if (!raft_server_find(new_servers, &s->sid)) { ovs_assert(s != raft->remove_server); -@@ -703,7 +711,7 @@ raft_set_servers(struct raft *raft, const struct hmap *new_servers, +@@ -703,7 +713,7 @@ raft_set_servers(struct raft *raft, const struct hmap *new_servers, } } @@ -4836,7 +7466,7 @@ index 1a3447a8dd..530c5e5a3d 100644 if (!raft_find_server(raft, &s->sid)) { VLOG(level, "server %s added to configuration", s->nickname); -@@ -1376,8 +1384,8 @@ raft_close__(struct raft *raft) +@@ -1376,8 +1386,8 @@ raft_close__(struct raft *raft) raft->remove_server = NULL; } @@ -4847,7 +7477,7 @@ index 1a3447a8dd..530c5e5a3d 100644 raft_conn_close(conn); } } -@@ -1713,8 +1721,8 @@ raft_waiters_run(struct raft *raft) +@@ -1713,8 +1723,8 @@ raft_waiters_run(struct raft *raft) } uint64_t cur = ovsdb_log_commit_progress(raft->log); @@ -4858,7 +7488,7 @@ index 1a3447a8dd..530c5e5a3d 100644 if (cur < w->commit_ticket) { break; } -@@ -1736,8 +1744,8 @@ raft_waiters_wait(struct raft *raft) +@@ -1736,8 +1746,8 @@ raft_waiters_wait(struct raft *raft) static void raft_waiters_destroy(struct raft *raft) { @@ -4869,7 +7499,7 @@ index 1a3447a8dd..530c5e5a3d 100644 raft_waiter_destroy(w); } } -@@ -1867,6 +1875,8 @@ raft_open_conn(struct raft *raft, const char *address, const struct uuid *sid) +@@ -1867,6 +1877,8 @@ raft_open_conn(struct raft *raft, const char *address, const struct uuid *sid) static void raft_conn_close(struct raft_conn *conn) { @@ -4878,7 +7508,21 @@ index 1a3447a8dd..530c5e5a3d 100644 jsonrpc_session_close(conn->js); ovs_list_remove(&conn->list_node); free(conn->nickname); -@@ -1957,16 +1967,29 @@ raft_run(struct raft *raft) +@@ -1921,6 +1933,13 @@ raft_run(struct raft *raft) + return; + } + ++ if (failure_test == FT_TRANSFER_LEADERSHIP) { ++ /* Using this function as it conveniently implements all we need and ++ * snapshotting is the main test scenario for leadership transfer. */ ++ raft_notify_snapshot_recommended(raft); ++ failure_test = FT_NO_TEST; ++ } ++ + raft_waiters_run(raft); + + if (!raft->listener && time_msec() >= raft->listen_backoff) { +@@ -1957,16 +1976,29 @@ raft_run(struct raft *raft) } /* Close unneeded sessions. */ @@ -4911,7 +7555,7 @@ index 1a3447a8dd..530c5e5a3d 100644 HMAP_FOR_EACH (server, hmap_node, &raft->servers) { raft_open_conn(raft, server->address, &server->sid); } -@@ -2039,8 +2062,8 @@ raft_run(struct raft *raft) +@@ -2039,11 +2071,18 @@ raft_run(struct raft *raft) * commands becomes new leader: the pending commands can still complete * if the crashed leader has replicated the transactions to majority of * followers before it crashed. */ @@ -4921,19 +7565,44 @@ index 1a3447a8dd..530c5e5a3d 100644 + HMAP_FOR_EACH_SAFE (cmd, hmap_node, &raft->commands) { if (cmd->timestamp && now - cmd->timestamp > raft->election_timer * 2) { - raft_command_complete(raft, cmd, RAFT_CMD_TIMEOUT); -@@ -2243,8 +2266,8 @@ raft_command_initiate(struct raft *raft, +- raft_command_complete(raft, cmd, RAFT_CMD_TIMEOUT); ++ if (cmd->index && raft->role != RAFT_LEADER) { ++ /* This server lost leadership and command didn't complete ++ * in time. Likely, it wasn't replicated to the majority ++ * of servers before losing the leadership. */ ++ raft_command_complete(raft, cmd, RAFT_CMD_LOST_LEADERSHIP); ++ } else { ++ raft_command_complete(raft, cmd, RAFT_CMD_TIMEOUT); ++ } + } + } + raft_reset_ping_timer(raft); +@@ -2235,6 +2274,9 @@ raft_command_initiate(struct raft *raft, + if (failure_test == FT_CRASH_AFTER_SEND_APPEND_REQ) { + ovs_fatal(0, "Raft test: crash after sending append_request."); + } ++ if (failure_test == FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ) { ++ failure_test = FT_TRANSFER_LEADERSHIP; ++ } + raft_reset_ping_timer(raft); + + return cmd; +@@ -2243,8 +2285,12 @@ raft_command_initiate(struct raft *raft, static void log_all_commands(struct raft *raft) { - struct raft_command *cmd, *next; - HMAP_FOR_EACH_SAFE (cmd, next, hmap_node, &raft->commands) { ++ if (!VLOG_IS_DBG_ENABLED()) { ++ return; ++ } ++ + struct raft_command *cmd; -+ HMAP_FOR_EACH_SAFE (cmd, hmap_node, &raft->commands) { ++ HMAP_FOR_EACH (cmd, hmap_node, &raft->commands) { VLOG_DBG("raft command eid: "UUID_FMT, UUID_ARGS(&cmd->eid)); } } -@@ -2398,8 +2421,8 @@ raft_command_complete(struct raft *raft, +@@ -2398,8 +2444,8 @@ raft_command_complete(struct raft *raft, static void raft_complete_all_commands(struct raft *raft, enum raft_command_status status) { @@ -4944,7 +7613,139 @@ index 1a3447a8dd..530c5e5a3d 100644 raft_command_complete(raft, cmd, status); } } -@@ -3354,12 +3377,6 @@ raft_find_peer(struct raft *raft, const struct uuid *uuid) +@@ -2601,7 +2647,13 @@ raft_become_follower(struct raft *raft) + * configuration is already part of the log. Possibly the configuration + * log entry will not be committed, but until we know that we must use the + * new configuration. Our AppendEntries processing will properly update +- * the server configuration later, if necessary. */ ++ * the server configuration later, if necessary. ++ * ++ * Also we do not complete commands here, as they can still be completed ++ * if their log entries have already been replicated to other servers. ++ * If the entries were actually committed according to the new leader, our ++ * AppendEntries processing will complete the corresponding commands. ++ */ + struct raft_server *s; + HMAP_FOR_EACH (s, hmap_node, &raft->add_servers) { + raft_send_add_server_reply__(raft, &s->sid, s->address, false, +@@ -2615,8 +2667,6 @@ raft_become_follower(struct raft *raft) + raft_server_destroy(raft->remove_server); + raft->remove_server = NULL; + } +- +- raft_complete_all_commands(raft, RAFT_CMD_LOST_LEADERSHIP); + } + + static void +@@ -2865,61 +2915,56 @@ raft_update_commit_index(struct raft *raft, uint64_t new_commit_index) + return false; + } + +- if (raft->role == RAFT_LEADER) { +- while (raft->commit_index < new_commit_index) { +- uint64_t index = ++raft->commit_index; +- const struct raft_entry *e = raft_get_entry(raft, index); +- if (raft_entry_has_data(e)) { +- struct raft_command *cmd +- = raft_find_command_by_eid(raft, &e->eid); +- if (cmd) { +- if (!cmd->index) { +- VLOG_DBG("Command completed after role change from" +- " follower to leader "UUID_FMT, +- UUID_ARGS(&e->eid)); +- cmd->index = index; +- } +- raft_command_complete(raft, cmd, RAFT_CMD_SUCCESS); ++ while (raft->commit_index < new_commit_index) { ++ uint64_t index = ++raft->commit_index; ++ const struct raft_entry *e = raft_get_entry(raft, index); ++ ++ if (raft_entry_has_data(e)) { ++ struct raft_command *cmd = raft_find_command_by_eid(raft, &e->eid); ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); ++ ++ if (cmd) { ++ if (!cmd->index && raft->role == RAFT_LEADER) { ++ VLOG_INFO_RL(&rl, ++ "command completed after role change from " ++ "follower to leader (eid: "UUID_FMT", " ++ "commit index: %"PRIu64")", UUID_ARGS(&e->eid), index); ++ } else if (!cmd->index && raft->role != RAFT_LEADER) { ++ /* This can happen when leader fail-over before sending ++ * execute_command_reply. */ ++ VLOG_INFO_RL(&rl, ++ "command completed without reply (eid: "UUID_FMT", " ++ "commit index: %"PRIu64")", UUID_ARGS(&e->eid), index); ++ } else if (cmd->index && raft->role != RAFT_LEADER) { ++ /* This can happen if current server lost leadership after ++ * sending append requests to the majority of servers, but ++ * before receiving majority of append replies. */ ++ VLOG_INFO_RL(&rl, ++ "command completed after role change from " ++ "leader to follower (eid: "UUID_FMT", " ++ "commit index: %"PRIu64")", UUID_ARGS(&e->eid), index); ++ /* Clearing 'sid' to avoid sending cmd execution reply. */ ++ cmd->sid = UUID_ZERO; ++ } else { ++ /* (cmd->index && raft->role == RAFT_LEADER) ++ * Normal command completion on a leader. */ + } +- } +- if (e->election_timer) { +- VLOG_INFO("Election timer changed from %"PRIu64" to %"PRIu64, +- raft->election_timer, e->election_timer); +- raft->election_timer = e->election_timer; +- raft->election_timer_new = 0; +- raft_update_probe_intervals(raft); +- } +- if (e->servers) { +- /* raft_run_reconfigure() can write a new Raft entry, which can +- * reallocate raft->entries, which would invalidate 'e', so +- * this case must be last, after the one for 'e->data'. */ +- raft_run_reconfigure(raft); ++ cmd->index = index; ++ raft_command_complete(raft, cmd, RAFT_CMD_SUCCESS); + } + } +- } else { +- while (raft->commit_index < new_commit_index) { +- uint64_t index = ++raft->commit_index; +- const struct raft_entry *e = raft_get_entry(raft, index); +- if (e->election_timer) { +- VLOG_INFO("Election timer changed from %"PRIu64" to %"PRIu64, +- raft->election_timer, e->election_timer); +- raft->election_timer = e->election_timer; +- raft_update_probe_intervals(raft); +- } ++ if (e->election_timer) { ++ VLOG_INFO("Election timer changed from %"PRIu64" to %"PRIu64, ++ raft->election_timer, e->election_timer); ++ raft->election_timer = e->election_timer; ++ raft->election_timer_new = 0; ++ raft_update_probe_intervals(raft); + } +- /* Check if any pending command can be completed, and complete it. +- * This can happen when leader fail-over before sending +- * execute_command_reply. */ +- const struct uuid *eid = raft_get_eid(raft, new_commit_index); +- struct raft_command *cmd = raft_find_command_by_eid(raft, eid); +- if (cmd) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); +- VLOG_INFO_RL(&rl, +- "Command completed without reply (eid: "UUID_FMT", " +- "commit index: %"PRIu64")", +- UUID_ARGS(eid), new_commit_index); +- cmd->index = new_commit_index; +- raft_command_complete(raft, cmd, RAFT_CMD_SUCCESS); ++ if (e->servers && raft->role == RAFT_LEADER) { ++ /* raft_run_reconfigure() can write a new Raft entry, which can ++ * reallocate raft->entries, which would invalidate 'e', so ++ * this case must be last, after the one for 'e->data'. */ ++ raft_run_reconfigure(raft); + } + } + +@@ -3354,12 +3399,6 @@ raft_find_peer(struct raft *raft, const struct uuid *uuid) return s && !uuid_equals(&raft->sid, &s->sid) ? s : NULL; } @@ -4957,7 +7758,7 @@ index 1a3447a8dd..530c5e5a3d 100644 /* Figure 3.1: "If there exists an N such that N > commitIndex, a * majority of matchIndex[i] >= N, and log[N].term == currentTerm, set * commitIndex = N (sections 3.5 and 3.6)." */ -@@ -4142,6 +4159,10 @@ static void +@@ -4142,6 +4181,10 @@ static void raft_handle_install_snapshot_request( struct raft *raft, const struct raft_install_snapshot_request *rq) { @@ -4968,7 +7769,7 @@ index 1a3447a8dd..530c5e5a3d 100644 if (raft_handle_install_snapshot_request__(raft, rq)) { union raft_rpc rpy = { .install_snapshot_reply = { -@@ -4940,6 +4961,8 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED, +@@ -4940,6 +4983,8 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED, failure_test = FT_CRASH_AFTER_SEND_EXEC_REQ; } else if (!strcmp(test, "crash-after-receiving-append-request-update")) { failure_test = FT_CRASH_AFTER_RECV_APPEND_REQ_UPDATE; @@ -4977,6 +7778,18 @@ index 1a3447a8dd..530c5e5a3d 100644 } else if (!strcmp(test, "delay-election")) { failure_test = FT_DELAY_ELECTION; struct raft *raft; +@@ -4952,6 +4997,11 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED, + failure_test = FT_DONT_SEND_VOTE_REQUEST; + } else if (!strcmp(test, "stop-raft-rpc")) { + failure_test = FT_STOP_RAFT_RPC; ++ } else if (!strcmp(test, ++ "transfer-leadership-after-sending-append-request")) { ++ failure_test = FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ; ++ } else if (!strcmp(test, "transfer-leadership")) { ++ failure_test = FT_TRANSFER_LEADERSHIP; + } else if (!strcmp(test, "clear")) { + failure_test = FT_NO_TEST; + unixctl_command_reply(conn, "test dismissed"); diff --git a/ovsdb/relay.c b/ovsdb/relay.c index ef0e44d340..122ee8c52f 100644 --- a/ovsdb/relay.c @@ -5549,8 +8362,48 @@ index c4c6c87e9f..6b0d023ae3 100644 if deadline is not None: remaining = deadline - now return max(0, remaining) +diff --git a/rhel/openvswitch-fedora.spec.in b/rhel/openvswitch-fedora.spec.in +index 16ef1ac3ab..d0ae78e4ed 100644 +--- a/rhel/openvswitch-fedora.spec.in ++++ b/rhel/openvswitch-fedora.spec.in +@@ -455,6 +455,7 @@ fi + %{_datadir}/openvswitch/scripts/ovs-ctl + %{_datadir}/openvswitch/scripts/ovs-kmod-ctl + %{_datadir}/openvswitch/scripts/ovs-systemd-reload ++%config %{_datadir}/openvswitch/local-config.ovsschema + %config %{_datadir}/openvswitch/vswitch.ovsschema + %config %{_datadir}/openvswitch/vtep.ovsschema + %{_bindir}/ovs-appctl +@@ -476,6 +477,7 @@ fi + %{_mandir}/man1/ovsdb-server.1* + %{_mandir}/man1/ovsdb-tool.1* + %{_mandir}/man5/ovsdb-server.5* ++%{_mandir}/man5/ovsdb.local-config.5* + %{_mandir}/man5/ovs-vswitchd.conf.db.5* + %{_mandir}/man5/ovsdb.5* + %{_mandir}/man5/vtep.5* +diff --git a/rhel/openvswitch.spec.in b/rhel/openvswitch.spec.in +index 220e5c7472..2d8ff18bb0 100644 +--- a/rhel/openvswitch.spec.in ++++ b/rhel/openvswitch.spec.in +@@ -229,6 +229,7 @@ exit 0 + /usr/share/man/man1/ovsdb-client.1.gz + /usr/share/man/man1/ovsdb-server.1.gz + /usr/share/man/man1/ovsdb-tool.1.gz ++/usr/share/man/man5/ovsdb.local-config.5.gz + /usr/share/man/man5/ovsdb-server.5.gz + /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz + %{_mandir}/man5/ovsdb.5* +@@ -262,6 +263,7 @@ exit 0 + /usr/share/openvswitch/scripts/ovs-vtep + /usr/share/openvswitch/scripts/sysconfig.template + /usr/share/openvswitch/scripts/ovs-monitor-ipsec ++/usr/share/openvswitch/local-config.ovsschema + /usr/share/openvswitch/vswitch.ovsschema + /usr/share/openvswitch/vtep.ovsschema + %doc NOTICE README.rst NEWS rhel/README.RHEL.rst diff --git a/tests/alb.at b/tests/alb.at -index 2bef06f39c..0036bd1f29 100644 +index 2bef06f39c..922185d61d 100644 --- a/tests/alb.at +++ b/tests/alb.at @@ -96,6 +96,52 @@ OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance @@ -5606,35 +8459,658 @@ index 2bef06f39c..0036bd1f29 100644 AT_SETUP([ALB - PMD/RxQ assignment type]) OVS_VSWITCHD_START([add-port br0 p0 \ -- set Interface p0 type=dummy-pmd options:n_rxq=3 \ -diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at -index 7c2edeb9d4..ffb7208c7f 100644 ---- a/tests/ofproto-dpif.at -+++ b/tests/ofproto-dpif.at -@@ -81,11 +81,12 @@ recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=ff: - - ovs-appctl netdev-dummy/set-admin-state p1 up - ovs-appctl time/warp 100 --OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl -+OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl - ---- bond0 ---- - bond_mode: active-backup - bond may use recirculation: no, - bond-hash-basis: 0 -+lb_output action: disabled, bond-id: -1 - updelay: 0 ms - downdelay: 0 ms - lacp_status: off -@@ -99,7 +100,6 @@ member p1: enabled +@@ -197,7 +243,25 @@ get_log_next_line_num + AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="0"]) + CHECK_ALB_PARAM([interval], [1 mins], [+$LINENUM]) - member p2: enabled - may_enable: true -- - ]) +-# No check for above max as it is only a documented max value and not a hard limit ++# Set new value ++get_log_next_line_num ++AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="100"]) ++CHECK_ALB_PARAM([interval], [100 mins], [+$LINENUM]) ++ ++# Set above max value ++get_log_next_line_num ++AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="20001"]) ++CHECK_ALB_PARAM([interval], [1 mins], [+$LINENUM]) ++ ++# Set new value ++get_log_next_line_num ++AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="1000"]) ++CHECK_ALB_PARAM([interval], [1000 mins], [+$LINENUM]) ++ ++# Set Negative value ++get_log_next_line_num ++AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="-1"]) ++CHECK_ALB_PARAM([interval], [1 mins], [+$LINENUM]) OVS_VSWITCHD_STOP -@@ -129,11 +129,12 @@ ovs-appctl time/warp 100 - OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | fgrep 'member p1: disabled'`"]) - ovs-appctl netdev-dummy/set-admin-state p1 up + AT_CLEANUP +diff --git a/tests/classifier.at b/tests/classifier.at +index cdcd72c156..f652b59837 100644 +--- a/tests/classifier.at ++++ b/tests/classifier.at +@@ -129,6 +129,31 @@ Datapath actions: 3 + OVS_VSWITCHD_STOP(["/'prefixes' with incompatible field: ipv6_label/d"]) + AT_CLEANUP + ++AT_SETUP([flow classifier - ipv6 ND dependency]) ++OVS_VSWITCHD_START ++add_of_ports br0 1 2 ++AT_DATA([flows.txt], [dnl ++ table=0,priority=100,ipv6,ipv6_src=1000::/10 actions=resubmit(,1) ++ table=0,priority=0 actions=NORMAL ++ table=1,priority=110,ipv6,ipv6_dst=1000::3 actions=resubmit(,2) ++ table=1,priority=100,ipv6,ipv6_dst=1000::4 actions=resubmit(,2) ++ table=1,priority=0 actions=NORMAL ++ table=2,priority=120,icmp6,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=1000::1 actions=NORMAL ++ table=2,priority=100,tcp actions=NORMAL ++ table=2,priority=100,icmp6 actions=NORMAL ++ table=2,priority=0 actions=NORMAL ++]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++# test ICMPv6 echo request (which should have no nd_target field) ++AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,icmpv6_type=128,icmpv6_code=0"], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,icmp6,in_port=1,dl_src=f6:d2:b0:19:5e:7b,dl_dst=d2:49:19:91:78:fe,ipv6_src=1000::/10,ipv6_dst=1000::4,nw_ttl=0,nw_frag=no ++Datapath actions: 100,2 ++]) ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_BANNER([conjunctive match]) + + AT_SETUP([single conjunctive match]) +diff --git a/tests/completion.at b/tests/completion.at +index 00e3a46b8b..b6155af253 100644 +--- a/tests/completion.at ++++ b/tests/completion.at +@@ -351,22 +351,22 @@ OVS_VSWITCHD_START + TMP="$(ovs-vsctl --commands | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n') + $(ovs-vsctl --options | grep -- '--' | sed -e 's/=.*$/=/g')" + MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--db=unix:$OVS_RUNDIR/db.sock "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--db=unix:$OVS_RUNDIR/db.sock "], + [0], [dnl + ${MATCH} + ]) + # complete ovs-vsctl [TAB] +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test ""], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test ""], + [0], [dnl + ${MATCH} + ]) + + # complete on global options. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--dry-run "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--dry-run "], + [0], [dnl + ${MATCH} + ]) +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--dry-run --pretty "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--dry-run --pretty "], + [0], [dnl + ${MATCH} + ]) +@@ -374,7 +374,7 @@ ${MATCH} + # complete on local options. + TMP="$(ovs-vsctl --commands | grep -- '--may-exist' | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n' | grep -v -- '--may-exist')" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--may-exist "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--may-exist "], + [0], [dnl + ${MATCH} + ]) +@@ -385,37 +385,37 @@ ${MATCH} + # test !. no following arguments are expanded. + TMP="$(ovsdb-client --no-heading list-tables)" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set "], + [0], [dnl + ${MATCH} + ]) + # test ?. will show completions for both current and following arguments. + ovs-vsctl br-set-external-id br0 bridge-id br0 + MATCH="$(PREPARE_MATCH_SPACE(bridge-id --))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], + [0], [dnl + ${MATCH} + ]) + # test *. argument with this prefix could be completed for zero or more times. + TMP="$(ovs-vsctl --no-heading --columns=_uuid,name list Bridge | tr -d '\"')" + MATCH="$(PREPARE_MATCH_SPACE(${TMP} --))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "destroy Bridge "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "destroy Bridge "], + [0], [dnl + ${MATCH} + ]) +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "destroy Bridge br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "destroy Bridge br0 "], + [0], [dnl + ${MATCH} + ]) + # test +. the first time, an argument is required, after that, it becomes '*'. + TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | awk '/key.*value/ { print $1":"; next } { print $1; next }')" + MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 "], + [0], [dnl + ${MATCH} + ]) + MATCH="$(PREPARE_MATCH_NOSPACE(${TMP} --))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_key=123 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_key=123 "], + [0], [dnl + ${MATCH} + ]) +@@ -453,12 +453,12 @@ OVS_VSWITCHD_START( + # + TMP="$(ovsdb-client --no-heading list-tables)" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set "], + [0], [dnl + ${MATCH} + ]) + MATCH="$(PREPARE_MATCH_SPACE(Open_vSwitch))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Open"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Open"], + [0], [dnl + ${MATCH} + ]) +@@ -469,13 +469,13 @@ ${MATCH} + # + TMP="$(ovs-vsctl --no-heading --columns=_uuid list Open_vSwitch | tr -d '\"')" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Open_vSwitch "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Open_vSwitch "], + [0], [dnl + ${MATCH} + ]) + TMP="$(ovs-vsctl --no-heading --columns=_uuid,name list Bridge | tr -d '\"')" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge "], + [0], [dnl + ${MATCH} + ]) +@@ -486,13 +486,13 @@ ${MATCH} + # + TMP="$(ovs-vsctl list-br)" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-to-vlan "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-to-vlan "], + [0], [dnl + ${MATCH} + ]) + # this also helps check the '_ovs_vsctl_check_startswith_string'. + MATCH="$(PREPARE_MATCH_SPACE(--weird-br_name))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-to-vlan --"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-to-vlan --"], + [0], [dnl + ${MATCH} + ]) +@@ -503,14 +503,14 @@ ${MATCH} + # + TMP="$(ovs-vsctl --no-heading --columns=name list Port | tr -d '\"')" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "port-to-br "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "port-to-br "], + [0], [dnl + ${MATCH} + ]) + # complete on ports in particular bridge. + TMP="$(ovs-vsctl list-ports br0)" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "del-port br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "del-port br0 "], + [0], [dnl + ${MATCH} + ]) +@@ -523,7 +523,7 @@ for br in `ovs-vsctl list-br`; do + TMP="${TMP} $(ovs-vsctl list-ifaces $br)" + done + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "iface-to-br "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "iface-to-br "], + [0], [dnl + ${MATCH} + ]) +@@ -533,7 +533,7 @@ ${MATCH} + # test: _ovs_vsctl_complete_bridge_fail_mode + # + MATCH="$(PREPARE_MATCH_SPACE(standalone secure))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-fail-mode br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-fail-mode br0 "], + [0], [dnl + ${MATCH} + ]) +@@ -542,25 +542,25 @@ ${MATCH} + # + # test: _ovs_vsctl_complete_key + # +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], + [0], [dnl + + ]) + # since there is no key added yet, we will only get our own input. + MATCH="$(PREPARE_MATCH_SPACE(test_key))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 test_key"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 test_key"], + [0], [dnl + ${MATCH} + ]) + # now add a key, as we should see it. + ovs-vsctl br-set-external-id br0 bridge-id br0 + MATCH="$(PREPARE_MATCH_SPACE(bridge-id))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], + [0], [dnl + ${MATCH} + ]) + MATCH="$(PREPARE_MATCH_SPACE(bridge-id --))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], + [0], [dnl + ${MATCH} + ]) +@@ -571,7 +571,7 @@ ${MATCH} + # + # should just return the user input. + MATCH="$(PREPARE_MATCH_SPACE(test_value --))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 bridge-id test_value"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 bridge-id test_value"], + [0], [dnl + ${MATCH} + ]) +@@ -583,13 +583,13 @@ ${MATCH} + TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Open_vSwitch | tr -d ':' | cut -d' ' -f1)" + UUID="$(ovs-vsctl --no-heading --columns=_uuid list Open_vSwitch | tr -d ' ')" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "clear Open_vSwitch $UUID "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "clear Open_vSwitch $UUID "], + [0], [dnl + ${MATCH} + ]) + TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | tr -d ':' | cut -d' ' -f1)" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "clear Bridge br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "clear Bridge br0 "], + [0], [dnl + ${MATCH} + ]) +@@ -597,7 +597,7 @@ ${MATCH} + # so, with one specified COLUMN 'other_config', it should still complete on + # COLUMNs, plus '--'. + MATCH="$(PREPARE_MATCH_SPACE(${TMP} --))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "clear Bridge br0 other_config "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "clear Bridge br0 other_config "], + [0], [dnl + ${MATCH} + ]) +@@ -608,19 +608,19 @@ ${MATCH} + # + # with no key available, should always get user input. + MATCH="$(PREPARE_MATCH_NOSPACE(random_key))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key"], + [0], [dnl + ${MATCH} + ]) + MATCH="$(PREPARE_MATCH_NOSPACE(abc))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key=abc"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key=abc"], + [0], [dnl + ${MATCH} + ]) + # now add two random keys. + ovs-vsctl set Bridge br0 other_config:random_key1=abc other_config:random_val1=xyz + MATCH="$(PREPARE_MATCH_NOSPACE(random_key1= random_val1=))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config ran"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config ran"], + [0], [dnl + ${MATCH} + ]) +@@ -632,25 +632,25 @@ ${MATCH} + # at first, we should complete on column. + TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | awk '/key.*value/ { print $1":"; next } { print $1; next }')" + MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 "], + [0], [dnl + ${MATCH} + ]) + MATCH="$(PREPARE_MATCH_NOSPACE(other_config:))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other"], + [0], [dnl + ${MATCH} + ]) + # then, with the ':' we should complete on key. + TMP="$(ovs-vsctl --no-heading --columns=other_config list Bridge br0 | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1)" + MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:"], + [0], [dnl + ${MATCH} + ]) + # finally, if user fill in some value, we should just complete on user input. + MATCH="$(PREPARE_MATCH_NOSPACE(random_val1))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_val1=12345"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_val1=12345"], + [0], [dnl + ${MATCH} + ]) +@@ -661,12 +661,12 @@ ${MATCH} + # + touch private_key certificate + MATCH="$(PREPARE_MATCH_SPACE(private_key))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-ssl priva"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-ssl priva"], + [0], [dnl + ${MATCH} + ]) + MATCH="$(PREPARE_MATCH_SPACE(certificate))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-ssl private_key cer"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-ssl private_key cer"], + [0], [dnl + ${MATCH} + ]) +@@ -676,20 +676,20 @@ ${MATCH} + # test: _ovs_vsctl_complete_target + # + MATCH="$(PREPARE_MATCH_NOSPACE(pssl: ptcp: punix: ssl: tcp: unix:))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-manager "], + [0], [dnl + ${MATCH} + ]) + # filename completion on unix, punix. + MATCH="$(PREPARE_MATCH_NOSPACE(testsuite.log))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager unix:test"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-manager unix:test"], + [0], [dnl + ${MATCH} + ]) + # no completion on other type, just return available types. + # in real environment, bash will not complete on anything. + MATCH="$(PREPARE_MATCH_NOSPACE(pssl: ptcp: punix: tcp: unix:))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager ssl:something"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-manager ssl:something"], + [0], [dnl + ${MATCH} + ]) +@@ -699,14 +699,14 @@ ${MATCH} + # test: _ovs_vsctl_complete_new + # + # test 'add-br' +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-br "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-br "], + [0], [dnl + --- BEGIN MESSAGE + Enter a new bridge: + > ovs-vsctl add-br --- END MESSAGE + ]) + # user input does not change the output. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-br new-br"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-br new-br"], + [0], [dnl + --- BEGIN MESSAGE + Enter a new bridge: +@@ -715,7 +715,7 @@ Enter a new bridge: + # after specifying the new bridge name, we should complete on parent bridge. + TMP="$(ovs-vsctl list-br)" + MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-br new-br "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-br new-br "], + [0], [dnl + ${MATCH} + ]) +@@ -724,7 +724,7 @@ ${MATCH} + # of '*COLUMN?:KEY=VALUE'. + TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Port | awk '/key.*value/ { print $1":"; next } { print $1; next }')" + MATCH="$(PREPARE_MATCH_NOSPACE(${TMP} --))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-port br0 new-port "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-port br0 new-port "], + [0], [dnl + ${MATCH} + ]) +@@ -736,13 +736,13 @@ ${MATCH} + # after '--', there should be no global options available for completion. + TMP="$(ovs-vsctl --commands | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n')" + MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "init -- "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "init -- "], + [0], [dnl + ${MATCH} + ]) + TMP="$(ovs-vsctl --no-heading --columns=name,_uuid list Port | tr -d '\"')" + MATCH="$(PREPARE_MATCH_SPACE(${TMP} newp1 newp2))" +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-port br0 newp1 -- add-port br1 newp2 -- set Port "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-port br0 newp1 -- add-port br1 newp2 -- set Port "], + [0], [dnl + ${MATCH} + ]) +@@ -757,25 +757,25 @@ AT_SKIP_IF([eval 'test ${BASH_VERSINFO[[0]]} -lt 4']) + OVS_VSWITCHD_START + + # complete non-matching command. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "invalid"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "invalid"], + [0], [dnl + + ]) + + # complete after invalid command. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "invalid argu"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "invalid argu"], + [0], [dnl + + ]) + + # complete non-matching end argument. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set INVALID_"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set INVALID_"], + [0], [dnl + + ]) + + # complete after invalid intermediate argument. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set INVALID_TBL "], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set INVALID_TBL "], + [1], [dnl + --- BEGIN MESSAGE + Cannot complete 'INVALID_TBL' at index 3: +@@ -783,12 +783,12 @@ Cannot complete 'INVALID_TBL' at index 3: + + # complete ovs-vsctl --db=wrongdb [TAB] + # should return 1 and show nothing. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--db=wrongdb"], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--db=wrongdb"], + [1], []) + + OVS_VSWITCHD_STOP + # delete ovsdb-server and try again. +-AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test ""], ++AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test ""], + [1], []) + + AT_CLEANUP +diff --git a/tests/drop-stats.at b/tests/drop-stats.at +index f3e19cd83b..1d3af98dab 100644 +--- a/tests/drop-stats.at ++++ b/tests/drop-stats.at +@@ -83,6 +83,9 @@ AT_CHECK([ + ovs-ofctl -Oopenflow13 add-flows br0 flows.txt + ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [ignore]) + ++ovs-appctl time/warp 15000 ++AT_CHECK([ovs-appctl revalidator/wait]) ++ + AT_CHECK([ + ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' + ], [0], [ignore]) +diff --git a/tests/library.at b/tests/library.at +index db4997d8f0..6489be2c15 100644 +--- a/tests/library.at ++++ b/tests/library.at +@@ -252,7 +252,7 @@ AT_CHECK([ovstest test-barrier], [0], []) + AT_CLEANUP + + AT_SETUP([rcu]) +-AT_CHECK([ovstest test-rcu-quiesce], [0], []) ++AT_CHECK([ovstest test-rcu], [0], []) + AT_CLEANUP + + AT_SETUP([stopwatch module]) +diff --git a/tests/mcast-snooping.at b/tests/mcast-snooping.at +index 757cf7186e..fe475e7b38 100644 +--- a/tests/mcast-snooping.at ++++ b/tests/mcast-snooping.at +@@ -216,3 +216,70 @@ AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl + ]) + + AT_CLEANUP ++ ++ ++AT_SETUP([mcast - igmp flood for non-snoop enabled]) ++OVS_VSWITCHD_START([]) ++ ++AT_CHECK([ ++ ovs-vsctl set bridge br0 \ ++ datapath_type=dummy], [0]) ++ ++add_of_ports br0 1 2 ++ ++AT_CHECK([ovs-ofctl add-flow br0 action=normal]) ++ ++ovs-appctl time/stop ++ ++dnl Basic scenario - needs to flood for IGMP followed by unicast ICMP ++dnl in reverse direction ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ ++ '0101000c29a0aa55aa550001080046c00028000040000102d3494565eb4ae0000016940400002200f9020000000104000000e00000fb000000000000']) ++AT_CHECK([ovs-appctl netdev-dummy/receive p2 \ ++ 'aa55aa5500010101000c29a008004500001c00010000400164dc0a0101010a0101020800f7ffffffffff']) ++ ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows | grep -e .*ipv4 | sort | dnl ++ strip_stats | strip_used | strip_recirc | dnl ++ sed -e 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], ++ [0], [dnl ++recirc_id(),in_port(1),eth(src=aa:55:aa:55:00:01,dst=01:01:00:0c:29:a0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:100,2 ++recirc_id(),in_port(2),eth(src=01:01:00:0c:29:a0,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:1 ++]) ++ ++ovs-appctl time/warp 100000 ++ ++dnl Next we should clear the flows and install a complex case ++AT_CHECK([ovs-ofctl del-flows br0]) ++ ++AT_DATA([flows.txt], [dnl ++table=0, arp actions=NORMAL ++table=0, ip,in_port=1 actions=ct(table=1,zone=64000) ++table=0, in_port=2 actions=output:1 ++table=1, ip,ct_state=+trk+inv actions=drop ++table=1 ip,in_port=1,icmp,ct_state=+trk+new actions=output:2 ++table=1, in_port=1,ip,ct_state=+trk+new actions=controller(userdata=00.de.ad.be.ef.ca.fe.01) ++table=1, in_port=1,ip,ct_state=+trk+est actions=output:2 ++]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++ovs-appctl time/warp 100000 ++ ++dnl Send the IGMP, followed by a unicast ICMP - ensure we won't black hole ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ ++ '0101000c29a0aa55aa550001080046c00028000040000102d3494565eb4ae0000016940400002200f9020000000104000000e00000fb000000000000']) ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ ++ 'aa55aa550001aa55aa55000208004500001c00010000400164dc0a0101010a0101020800f7ffffffffff']) ++ ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows | grep -e .*ipv4 | sort | dnl ++ strip_stats | strip_used | strip_recirc | dnl ++ sed 's/pid=[[0-9]]*,// ++ s/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], ++ [0], [dnl ++ct_state(+new-inv+trk),recirc_id(),in_port(1),eth_type(0x0800),ipv4(proto=1,frag=no), packets:0, bytes:0, used:never, actions:2 ++ct_state(+new-inv+trk),recirc_id(),in_port(1),eth_type(0x0800),ipv4(proto=2,frag=no), packets:0, bytes:0, used:never, actions:userspace(controller(reason=1,dont_send=0,continuation=0,recirc_id=,rule_cookie=0,controller_id=0,max_len=65535)) ++recirc_id(),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ct(zone=64000),recirc() ++]) ++ ++AT_CLEANUP +diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at +index 7c2edeb9d4..c923ed6606 100644 +--- a/tests/ofproto-dpif.at ++++ b/tests/ofproto-dpif.at +@@ -29,6 +29,39 @@ AT_CHECK([ovs-appctl revalidator/wait]) + OVS_VSWITCHD_STOP + AT_CLEANUP + ++AT_SETUP([ofproto-dpif - lldp revalidator event(REV_RECONFIGURE)]) ++OVS_VSWITCHD_START( ++ [add-port br0 p1 -- set interface p1 ofport_request=1 type=dummy] ++) ++dnl first revalidation triggered by add interface ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++1 ++]) ++ ++dnl enable lldp ++AT_CHECK([ovs-vsctl set interface p1 lldp:enable=true]) ++AT_CHECK([ovs-appctl revalidator/wait]) ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++2 ++]) ++ ++dnl disable lldp ++AT_CHECK([ovs-vsctl set interface p1 lldp:enable=false]) ++AT_CHECK([ovs-appctl revalidator/wait]) ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++3 ++]) ++ ++dnl remove lldp, no revalidation as lldp was disabled ++AT_CHECK([ovs-vsctl remove interface p1 lldp enable]) ++AT_CHECK([ovs-appctl revalidator/wait]) ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++3 ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_SETUP([ofproto-dpif - active-backup bonding (with primary)]) + + dnl Create br0 with members p1, p2 and p7, creating bond0 with p1 and +@@ -81,11 +114,12 @@ recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=ff: + + ovs-appctl netdev-dummy/set-admin-state p1 up + ovs-appctl time/warp 100 +-OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl ++OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl + ---- bond0 ---- + bond_mode: active-backup + bond may use recirculation: no, + bond-hash-basis: 0 ++lb_output action: disabled, bond-id: -1 + updelay: 0 ms + downdelay: 0 ms + lacp_status: off +@@ -99,7 +133,6 @@ member p1: enabled + + member p2: enabled + may_enable: true +- + ]) + + OVS_VSWITCHD_STOP +@@ -129,11 +162,12 @@ ovs-appctl time/warp 100 + OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | fgrep 'member p1: disabled'`"]) + ovs-appctl netdev-dummy/set-admin-state p1 up ovs-appctl time/warp 100 -OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl +OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl @@ -5646,7 +9122,7 @@ index 7c2edeb9d4..ffb7208c7f 100644 updelay: 0 ms downdelay: 0 ms lacp_status: off -@@ -150,7 +151,6 @@ member p2: enabled +@@ -150,7 +184,6 @@ member p2: enabled member p3: enabled may_enable: true @@ -5654,7 +9130,7 @@ index 7c2edeb9d4..ffb7208c7f 100644 ]) dnl Now delete the primary and verify that the output shows that the -@@ -171,11 +171,12 @@ ovs-vsctl \ +@@ -171,11 +204,12 @@ ovs-vsctl \ --id=@p1 create Interface name=p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set Port bond0 interfaces="$uuids, @p1]" ovs-appctl time/warp 100 @@ -5668,7 +9144,7 @@ index 7c2edeb9d4..ffb7208c7f 100644 updelay: 0 ms downdelay: 0 ms lacp_status: off -@@ -192,17 +193,17 @@ member p2: enabled +@@ -192,17 +226,17 @@ member p2: enabled member p3: enabled may_enable: true @@ -5688,7 +9164,7 @@ index 7c2edeb9d4..ffb7208c7f 100644 updelay: 0 ms downdelay: 0 ms lacp_status: off -@@ -211,25 +212,25 @@ active-backup primary: p2 +@@ -211,25 +245,25 @@ active-backup primary: p2 member p1: enabled @@ -5717,7 +9193,7 @@ index 7c2edeb9d4..ffb7208c7f 100644 updelay: 0 ms downdelay: 0 ms lacp_status: off -@@ -238,15 +239,14 @@ active-backup primary: +@@ -238,15 +272,14 @@ active-backup primary: member p1: enabled @@ -5734,7 +9210,7 @@ index 7c2edeb9d4..ffb7208c7f 100644 ]) OVS_VSWITCHD_STOP -@@ -5573,7 +5573,36 @@ check_flows () { +@@ -5573,7 +5606,36 @@ check_flows () { echo "n_packets=$n" test "$n" = 1 } @@ -5772,11 +9248,63 @@ index 7c2edeb9d4..ffb7208c7f 100644 OVS_VSWITCHD_STOP AT_CLEANUP +@@ -7600,13 +7662,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 ++dnl first revalidation triggered by add interface ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++1 ++]) ++ + add_of_ports br0 1 2 3 ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++2 ++]) + + dnl Sample every packet using bridge-based sampling. + AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \ + --id=@fix create ipfix targets=\"127.0.0.1:4739\" \ +- sampling=1], [0], [ignore]) ++ sampling=2], [0], [ignore]) ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++3 ++]) + ++AT_CHECK([ovs-vsctl set ipfix `ovs-vsctl get bridge br0 ipfix` sampling=1], [0]) ++AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl ++4 ++]) + 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)']) diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at -index 736d9809cb..7051d95396 100644 +index 736d9809cb..b18f0fbc1e 100644 --- a/tests/ofproto-macros.at +++ b/tests/ofproto-macros.at -@@ -175,6 +175,7 @@ m4_define([_OVS_VSWITCHD_START], +@@ -134,6 +134,21 @@ strip_ufid () { + sed 's/mega_ufid:[[-0-9a-f]]* // + s/ufid:[[-0-9a-f]]* //' + } ++ ++# Strips packets: and bytes: from output ++strip_stats () { ++ sed 's/packets:[[0-9]]*/packets:0/ ++ s/bytes:[[0-9]]*/bytes:0/' ++} ++ ++# Changes all 'recirc(...)' and 'recirc=...' to say 'recirc()' and ++# 'recirc=' respectively. This should make output easier to ++# compare. ++strip_recirc() { ++ sed 's/recirc_id([[x0-9]]*)/recirc_id()/ ++ s/recirc_id=[[x0-9]]*/recirc_id=/ ++ s/recirc([[x0-9]]*)/recirc()/' ++} + m4_divert_pop([PREPARE_TESTS]) + + m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m']) +@@ -175,6 +190,7 @@ m4_define([_OVS_VSWITCHD_START], /dpdk|INFO|DPDK Disabled - Use other_config:dpdk-init to enable/d /netlink_socket|INFO|netlink: could not enable listening to all nsid/d /probe tc:/d @@ -5784,7 +9312,7 @@ index 736d9809cb..7051d95396 100644 /tc: Using policy/d']]) ]) -@@ -239,6 +240,7 @@ check_logs () { +@@ -239,6 +255,7 @@ check_logs () { /timeval.*context switches: [[0-9]]* voluntary, [[0-9]]* involuntary/d /ovs_rcu.*blocked [[0-9]]* ms waiting for .* to quiesce/d /Dropped [[0-9]]* log messages/d @@ -5850,11 +9378,119 @@ index bba4fea2bc..977b2eba1f 100644 /|WARN|/p /|ERR|/p /|EMER|/p" ovs-vswitchd.log +diff --git a/tests/ovsdb-client.at b/tests/ovsdb-client.at +index 06b671df8c..2d14f1ac26 100644 +--- a/tests/ovsdb-client.at ++++ b/tests/ovsdb-client.at +@@ -3,6 +3,7 @@ AT_BANNER([OVSDB -- ovsdb-client commands]) + AT_SETUP([ovsdb-client get-schema-version and get-schema-cksum]) + AT_KEYWORDS([ovsdb client positive]) + ordinal_schema > schema ++on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) + AT_CHECK([ovsdb-client get-schema-version unix:socket ordinals], [0], [5.1.3 +@@ -14,6 +15,7 @@ AT_CLEANUP + + AT_SETUP([ovsdb-client needs-conversion (no conversion needed)]) + AT_KEYWORDS([ovsdb client file positive]) ++on_exit 'kill `cat *.pid`' + ordinal_schema > schema + touch .db.~lock~ + AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) +@@ -27,6 +29,7 @@ AT_SETUP([ovsdb-client needs-conversion (conversion needed)]) + AT_KEYWORDS([ovsdb client file positive]) + ordinal_schema > schema + touch .db.~lock~ ++on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) + sed 's/5\.1\.3/5.1.4/' < schema > schema2 diff --git a/tests/ovsdb-cluster.at b/tests/ovsdb-cluster.at -index fc6253cfe9..ee9c7b9379 100644 +index fc6253cfe9..920b833b72 100644 --- a/tests/ovsdb-cluster.at +++ b/tests/ovsdb-cluster.at -@@ -400,6 +400,61 @@ done +@@ -1,12 +1,25 @@ + OVS_START_SHELL_HELPERS +-# ovsdb_check_cluster N_SERVERS SCHEMA_FUNC OUTPUT TRANSACTION... ++# ovsdb_check_cluster N_SERVERS SCHEMA_FUNC OUTPUT USE_LOCAL_CONFIG TRANSACTION... + ovsdb_check_cluster () { +- local n=$1 schema_func=$2 output=$3 +- shift; shift; shift ++ set -x ++ local n=$1 schema_func=$2 output=$3 local_config=$4 ++ shift; shift; shift; shift + + $schema_func > schema + schema=`ovsdb-tool schema-name schema` + AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [stderr]) ++ if test X$local_config == X"yes"; then ++ for i in `seq $n`; do ++ AT_CHECK([ovsdb-tool create c$i.db $top_srcdir/ovsdb/local-config.ovsschema], [0], [], [stderr]) ++ local ctxn="[[\"Local_Config\", ++ {\"op\": \"insert\", \"table\": \"Config\", ++ \"row\": {\"connections\": [\"named-uuid\",\"conn$n\"]}}, ++ {\"op\": \"insert\", \"table\": \"Connection\", \"uuid-name\": \"conn$n\", ++ \"row\": {\"target\": \"punix:s$i.ovsdb\"}}]]" ++ ++ AT_CHECK([ovsdb-tool transact c$i.db "$ctxn"], [0], [ignore], [stderr]) ++ done ++ fi + AT_CHECK([grep -v 'from ephemeral to persistent' stderr], [1]) + cid=`ovsdb-tool db-cid s1.db` + for i in `seq 2 $n`; do +@@ -15,7 +28,13 @@ ovsdb_check_cluster () { + + on_exit 'kill `cat *.pid`' + for i in `seq $n`; do +- AT_CHECK([ovsdb-server -vraft -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) ++ local remote=punix:s$i.ovsdb ++ local config_db= ++ if test X$local_config == X"yes"; then ++ remote=db:Local_Config,Config,connections ++ config_db=c$i.db ++ fi ++ AT_CHECK([ovsdb-server -vraft -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=$remote s$i.db $config_db]) + done + for i in `seq $n`; do + AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema connected]) +@@ -40,7 +59,7 @@ AT_BANNER([OVSDB - clustered transactions (1 server)]) + m4_define([OVSDB_CHECK_EXECUTION], + [AT_SETUP([$1 - cluster of 1]) + AT_KEYWORDS([ovsdb server positive unix cluster cluster1 $5]) +- ovsdb_check_cluster 1 "$2" '$4' m4_foreach([txn], [$3], ['txn' ]) ++ ovsdb_check_cluster 1 "$2" '$4' no m4_foreach([txn], [$3], ['txn' ]) + AT_CLEANUP]) + EXECUTION_EXAMPLES + +@@ -49,7 +68,7 @@ AT_BANNER([OVSDB - clustered transactions (3 servers)]) + m4_define([OVSDB_CHECK_EXECUTION], + [AT_SETUP([$1 - cluster of 3]) + AT_KEYWORDS([ovsdb server positive unix cluster cluster3 $5]) +- ovsdb_check_cluster 3 "$2" '$4' m4_foreach([txn], [$3], ['txn' ]) ++ ovsdb_check_cluster 3 "$2" '$4' no m4_foreach([txn], [$3], ['txn' ]) + AT_CLEANUP]) + EXECUTION_EXAMPLES + +@@ -58,7 +77,16 @@ AT_BANNER([OVSDB - clustered transactions (5 servers)]) + m4_define([OVSDB_CHECK_EXECUTION], + [AT_SETUP([$1 - cluster of 5]) + AT_KEYWORDS([ovsdb server positive unix cluster cluster5 $5]) +- ovsdb_check_cluster 5 "$2" '$4' m4_foreach([txn], [$3], ['txn' ]) ++ ovsdb_check_cluster 5 "$2" '$4' no m4_foreach([txn], [$3], ['txn' ]) ++ AT_CLEANUP]) ++EXECUTION_EXAMPLES ++ ++# Test a 3-server cluster using a Local_Config db. ++AT_BANNER([OVSDB - clustered transactions Local_Config (3 servers)]) ++m4_define([OVSDB_CHECK_EXECUTION], ++ [AT_SETUP([$1 - cluster of 3]) ++ AT_KEYWORDS([ovsdb server positive unix cluster cluster3 Local_Config $5]) ++ ovsdb_check_cluster 3 "$2" '$4' yes m4_foreach([txn], [$3], ['txn' ]) + AT_CLEANUP]) + EXECUTION_EXAMPLES + +@@ -400,6 +428,61 @@ done AT_CLEANUP @@ -5916,7 +9552,11 @@ index fc6253cfe9..ee9c7b9379 100644 OVS_START_SHELL_HELPERS -@@ -416,9 +471,8 @@ ovsdb_cluster_failure_test () { +@@ -413,12 +496,12 @@ ovsdb_cluster_failure_test () { + if test "$crash_node" == "1"; then + new_leader=$5 + fi ++ log_grep=$6 cp $top_srcdir/vswitchd/vswitch.ovsschema schema schema=`ovsdb-tool schema-name schema` @@ -5928,7 +9568,57 @@ index fc6253cfe9..ee9c7b9379 100644 n=3 join_cluster() { -@@ -629,9 +683,8 @@ ovsdb_torture_test () { +@@ -434,7 +517,7 @@ ovsdb|WARN|schema: changed 30 columns in 'Open_vSwitch' database from ephemeral + start_server() { + local i=$1 + printf "\ns$i: starting\n" +- AT_CHECK([ovsdb-server -vjsonrpc -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) ++ AT_CHECK([ovsdb-server -vjsonrpc -vraft -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) + } + connect_server() { + local i=$1 +@@ -460,14 +543,23 @@ ovsdb|WARN|schema: changed 30 columns in 'Open_vSwitch' database from ephemeral + fi + AT_CHECK([ovs-appctl -t "`pwd`"/s$delay_election_node cluster/failure-test delay-election], [0], [ignore]) + fi ++ ++ # Initializing the database separately to avoid extra 'wait' operation ++ # in later transactions. ++ AT_CHECK([ovs-vsctl -v --db="$db" --no-leader-only --no-shuffle-remotes --no-wait init], [0], [ignore], [ignore]) ++ + AT_CHECK([ovs-appctl -t "`pwd`"/s$crash_node cluster/failure-test $crash_command], [0], [ignore]) + AT_CHECK([ovs-vsctl -v --db="$db" --no-leader-only --no-shuffle-remotes --no-wait create QoS type=x], [0], [ignore], [ignore]) + +- # Make sure that the node really crashed. +- AT_CHECK([ls s$crash_node.ovsdb], [2], [ignore], [ignore]) +- # XXX: Client will fail if remotes contains unix socket that doesn't exist (killed). +- if test "$remote_1" = "$crash_node"; then +- db=unix:s$remote_2.ovsdb ++ # Make sure that the node really crashed or has specific log message. ++ if test -z "$log_grep"; then ++ AT_CHECK([ls s$crash_node.ovsdb], [2], [ignore], [ignore]) ++ # XXX: Client will fail if remotes contains unix socket that doesn't exist (killed). ++ if test "$remote_1" = "$crash_node"; then ++ db=unix:s$remote_2.ovsdb ++ fi ++ else ++ OVS_WAIT_UNTIL([grep -q "$log_grep" s${crash_node}.log]) + fi + AT_CHECK([ovs-vsctl --db="$db" --no-leader-only --no-wait --columns=type --bare list QoS], [0], [x + ]) +@@ -563,6 +655,11 @@ AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) + ovsdb_cluster_failure_test 2 2 3 crash-after-receiving-append-request-update + AT_CLEANUP + ++AT_SETUP([OVSDB cluster - txn on leader, leader transfers leadership after sending appendReq]) ++AT_KEYWORDS([ovsdb server negative unix cluster pending-txn transfer]) ++ovsdb_cluster_failure_test 1 2 1 transfer-leadership-after-sending-append-request -1 "Transferring leadership" ++AT_CLEANUP ++ + + AT_SETUP([OVSDB cluster - competing candidates]) + AT_KEYWORDS([ovsdb server negative unix cluster competing-candidates]) +@@ -629,9 +726,8 @@ ovsdb_torture_test () { local variant=$3 # 'kill' and restart or 'remove' and add cp $top_srcdir/vswitchd/vswitch.ovsschema schema schema=`ovsdb-tool schema-name schema` @@ -5940,8 +9630,367 @@ index fc6253cfe9..ee9c7b9379 100644 join_cluster() { local i=$1 +diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at +index 876cb836cd..e672c13b27 100644 +--- a/tests/ovsdb-server.at ++++ b/tests/ovsdb-server.at +@@ -4,7 +4,7 @@ m4_define([OVSDB_SERVER_SHUTDOWN], + [OVS_APP_EXIT_AND_WAIT_BY_TARGET([ovsdb-server], [ovsdb-server.pid])]) + + m4_define([OVSDB_SERVER_SHUTDOWN_N], +- [cp pid$1 savepid$1 ++ [cp $1.pid savepid$1 + AT_CHECK([ovs-appctl -t "`pwd`"/unixctl$1 -e exit], [0], [ignore], [ignore]) + OVS_WAIT_WHILE([kill -0 `cat savepid$1`], [kill `cat savepid$1`])]) + +@@ -30,14 +30,13 @@ m4_define([OVSDB_CHECK_EXECUTION], + AT_KEYWORDS([ovsdb server positive unix $5]) + $2 > schema + AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) + m4_foreach([txn], [$3], +- [AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0], [stdout], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ [AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0], [stdout], [ignore]) + cat stdout >> output + ]) +- AT_CHECK([uuidfilt output], [0], [$4], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ AT_CHECK([uuidfilt output], [0], [$4], [ignore]) + OVSDB_SERVER_SHUTDOWN + AT_CLEANUP]) + +@@ -88,8 +87,7 @@ AT_CHECK([uuidfilt output], [0], + [[[{"uuid":["uuid","<0>"]}] + [{"uuid":["uuid","<1>"]}] + [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] +-]], [], +- [test ! -e pid || kill `cat pid`]) ++]], []) + AT_CLEANUP + + AT_SETUP([truncating database log with bad transaction]) +@@ -136,8 +134,7 @@ AT_CHECK([uuidfilt output], [0], + [[[{"uuid":["uuid","<0>"]}] + [{"uuid":["uuid","<1>"]}] + [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] +-]], [], +- [test ! -e pid || kill `cat pid`]) ++]], []) + AT_CLEANUP + + dnl CHECK_DBS([databases]) +@@ -159,6 +156,7 @@ ordinal_schema > schema1 + constraint_schema > schema2 + AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore]) + AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore]) ++on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:db.sock db1 db2], [0], [ignore], [ignore]) + CHECK_DBS([constraints + ordinals +@@ -166,7 +164,7 @@ ordinals + AT_CHECK( + [[ovstest test-jsonrpc request unix:db.sock get_schema [\"nonexistent\"]]], [0], + [[{"error":{"details":"get_schema request specifies unknown database nonexistent","error":"unknown database","syntax":"[\"nonexistent\"]"},"id":0,"result":null} +-]], [], [test ! -e pid || kill `cat pid`]) ++]], []) + OVSDB_SERVER_SHUTDOWN + AT_CLEANUP + +@@ -393,7 +391,7 @@ AT_CHECK( + "table": "Manager", + "uuid-name": "x", + "row": {"target": "punix:socket2"}}]']], [0], [ignore], [ignore]) +-on_exit 'kill `cat ovsdb-server.pid`' ++on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=db:mydb,Root,managers --remote=db:mydb,Root,manager_options --log-file db], [0], [ignore], [ignore]) + ovs-appctl -t ovsdb-server time/warp 6000 1000 + AT_CHECK( +@@ -686,6 +684,7 @@ ovsdb_check_online_compaction() { + ovsdb-tool create-cluster db schema unix:s1.raft + fi]) + dnl Start ovsdb-server. ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server -vvlog:off -vconsole:off --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0]) + AT_CHECK([ovsdb_client_wait unix:socket ordinals connected]) + AT_CAPTURE_FILE([ovsdb-server.log]) +@@ -832,7 +831,7 @@ _uuid name number + <0> five 5 + <1> four 4 + <2> three 3 +-], [], [test ! -e pid || kill `cat pid`]) ++], []) + OVSDB_SERVER_SHUTDOWN + } + OVS_END_SHELL_HELPERS +@@ -1319,15 +1318,14 @@ m4_define([OVSDB_CHECK_EXECUTION], + $2 > schema + PKIDIR=$abs_top_builddir/tests + AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:127.0.0.1 db], [0], [ignore], [ignore]) + PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) + m4_foreach([txn], [$3], +- [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:127.0.0.1:$SSL_PORT 'txn'], [0], [stdout], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:127.0.0.1:$SSL_PORT 'txn'], [0], [stdout], [ignore]) + cat stdout >> output + ]) +- AT_CHECK([uuidfilt output], [0], [$4], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ AT_CHECK([uuidfilt output], [0], [$4], [ignore]) + OVSDB_SERVER_SHUTDOWN + AT_CLEANUP]) + +@@ -1356,16 +1354,15 @@ m4_define([OVSDB_CHECK_EXECUTION], + AT_SKIP_IF([test $HAVE_IPV6 = no]) + $2 > schema + PKIDIR=$abs_top_builddir/tests ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:[[::1]] db], [0], [ignore], [ignore]) + PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) + m4_foreach([txn], [$3], +- [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:[[::1]]:$SSL_PORT 'txn'], [0], [stdout], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:[[::1]]:$SSL_PORT 'txn'], [0], [stdout], [ignore]) + cat stdout >> output + ]) +- AT_CHECK([uuidfilt output], [0], [$4], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ AT_CHECK([uuidfilt output], [0], [$4], [ignore]) + OVSDB_SERVER_SHUTDOWN + AT_CLEANUP]) + +@@ -1392,16 +1389,15 @@ m4_define([OVSDB_CHECK_EXECUTION], + AT_KEYWORDS([ovsdb server positive tcp $5]) + $2 > schema + PKIDIR=$abs_top_builddir/tests ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --remote=ptcp:0:127.0.0.1 db], [0], [ignore], [ignore]) + PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) + m4_foreach([txn], [$3], +- [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT 'txn'], [0], [stdout], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT 'txn'], [0], [stdout], [ignore]) + cat stdout >> output + ]) +- AT_CHECK([uuidfilt output], [0], [$4], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ AT_CHECK([uuidfilt output], [0], [$4], [ignore]) + OVSDB_SERVER_SHUTDOWN + AT_CLEANUP]) + +@@ -1429,16 +1425,15 @@ m4_define([OVSDB_CHECK_EXECUTION], + AT_SKIP_IF([test $HAVE_IPV6 = no]) + $2 > schema + PKIDIR=$abs_top_builddir/tests ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --remote=ptcp:0:[[::1]] db], [0], [ignore], [ignore]) + PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) + m4_foreach([txn], [$3], +- [AT_CHECK([ovsdb-client transact tcp:[[::1]]:$TCP_PORT 'txn'], [0], [stdout], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ [AT_CHECK([ovsdb-client transact tcp:[[::1]]:$TCP_PORT 'txn'], [0], [stdout], [ignore]) + cat stdout >> output + ]) +- AT_CHECK([uuidfilt output], [0], [$4], [ignore], +- [test ! -e pid || kill `cat pid`]) ++ AT_CHECK([uuidfilt output], [0], [$4], [ignore]) + OVSDB_SERVER_SHUTDOWN + AT_CLEANUP]) + +@@ -1518,9 +1513,9 @@ m4_define([OVSDB_CHECK_EXECUTION], + target=4 + $2 > schema + schema_name=`ovsdb-tool schema-name schema` ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) + +- on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log dnl + --pidfile --remote=punix:db1.sock db1 + ], [0], [ignore], [ignore]) +@@ -1576,12 +1571,11 @@ m4_define([OVSDB_CHECK_EXECUTION], + AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) + ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) + i +- on_exit 'test ! -e pid || kill `cat pid`' + +- AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=pid2 --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) +- on_exit 'test ! -e pid2 || kill `cat pid2`' ++ AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) + + m4_foreach([txn], [$3], + [AT_CHECK([ovsdb-client transact 'txn'], [0], [stdout], [ignore]) +@@ -1622,11 +1616,10 @@ m4_define([OVSDB_CHECK_REPLICATION], + AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) + ++ on_exit 'kill `cat *.pid`' + AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) +- on_exit 'test ! -e pid || kill `cat pid`' + +- AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=pid2 --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock --sync-exclude-tables=mydb:b db2], [0], [ignore], [ignore]) +- on_exit 'test ! -e pid2 || kill `cat pid2`' ++ AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock --sync-exclude-tables=mydb:b db2], [0], [ignore], [ignore]) + + m4_foreach([txn], [$3], + [AT_CHECK([ ovsdb-client transact 'txn' ], [0], [stdout], [ignore]) +@@ -1694,6 +1687,7 @@ AT_CLEANUP + + #ovsdb-server/set-sync-exclude-tables command + AT_SETUP([ovsdb-server/set-sync-exclude-tables]) ++on_exit 'kill `cat *.pid`' + AT_KEYWORDS([ovsdb server replication set-exclude-tables]) + AT_SKIP_IF([test $DIFF_SUPPORTS_NORMAL_FORMAT = no]) + +@@ -1702,12 +1696,10 @@ AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) + + AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid || kill `cat pid`' + +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=pid2 --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid2 || kill `cat pid2`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) + +-AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/set-sync-exclude-tables mydb:b], [0], [ignore], [ignore], [test ! -e pid || kill `cat pid`; test ! -e pid2 || kill `cat pid2`]) ++AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/set-sync-exclude-tables mydb:b], [0], [ignore], [ignore]) + + AT_CHECK([ovsdb-client transact unix:db.sock \ + '[["mydb", +@@ -1716,11 +1708,9 @@ AT_CHECK([ovsdb-client transact unix:db.sock \ + "row": {"number": 0, "name": "zero"}}, + {"op": "insert", + "table": "b", +- "row": {"number": 1, "name": "one"}}]]'], [0], [stdout], [ignore], +- [test ! -e pid || kill `cat pid`; test ! -e pid2 || kill `cat pid2`]) ++ "row": {"number": 1, "name": "one"}}]]'], [0], [stdout], [ignore]) + +-AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout], [ignore], +- [test ! -e pid || kill `cat pid`; test ! -e pid2 || kill `cat pid2`]) ++AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout], [ignore]) + cat stdout > dump1 + OVS_WAIT_UNTIL([ ovsdb-client dump unix:db2.sock | grep zero ]) + AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout], [ignore]) +@@ -1744,16 +1734,15 @@ AT_CLEANUP + + #ovsdb-server/connect-active-ovsdb-server + AT_SETUP([ovsdb-server/connect-active-server]) ++on_exit 'kill `cat *.pid`' + AT_KEYWORDS([ovsdb server replication connect-active-server]) + replication_schema > schema + AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) + + AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid || kill `cat pid`' + +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=pid2 --remote=punix:db2.sock --unixctl=unixctl2 db2], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid2 || kill `cat pid2`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 db2], [0], [ignore], [ignore]) + + dnl Try to connect without specifying the active server. + AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/connect-active-ovsdb-server], [0], +@@ -1783,6 +1772,7 @@ AT_CLEANUP + + #ovsdb-server/disconnect-active-server command + AT_SETUP([ovsdb-server/disconnect-active-server]) ++on_exit 'kill `cat *.pid`' + AT_KEYWORDS([ovsdb server replication disconnect-active-server]) + AT_SKIP_IF([test $DIFF_SUPPORTS_NORMAL_FORMAT = no]) + +@@ -1791,10 +1781,8 @@ AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) + AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) + + AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid || kill `cat pid`' + +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=pid2 --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid2 || kill `cat pid2`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) + + AT_CHECK([ovsdb-client transact unix:db.sock \ + '[["mydb", +@@ -1840,7 +1828,7 @@ AT_CHECK([uuidfilt output], [0], [7,9c7,8 + --- + > _uuid name number + > ----- ---- ------ +-], [ignore], [test ! -e pid || kill `cat pid`; test ! -e pid2 || kill `cat pid2`]) ++], [ignore]) + + dnl The backup server now become active, and can accept write transactions. + AT_CHECK([ovsdb-client transact unix:db2.sock \ +@@ -1891,13 +1879,12 @@ dnl Start both 'db1' and 'db2' in backup mode. Let them backup from each + dnl other. This is not an supported operation state, but to simulate a start + dnl up condition where an HA manger can select which one to be an active + dnl server soon after. +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile="`pwd`"/pid --remote=punix:db.sock --unixctl="`pwd`"/unixctl db1 --sync-from=unix:db2.sock --active ], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid || kill `cat pid`' ++on_exit 'kill `cat *.pid`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock --unixctl="`pwd`"/unixctl db1 --sync-from=unix:db2.sock --active ], [0], [ignore], [ignore]) + + AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/connect-active-ovsdb-server]) + +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile="`pwd`"/pid2 --remote=punix:db2.sock --unixctl="`pwd`"/unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid2 || kill `cat pid2`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl="`pwd`"/unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) + + dnl + dnl make sure both servers reached the replication state +@@ -1965,8 +1952,8 @@ AT_CHECK([ovsdb-tool transact db \ + "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) + + dnl Start 'db', then try to be a back up server of itself. +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server.log --pidfile="`pwd`"/pid --remote=punix:db.sock --unixctl="`pwd`"/unixctl db --sync-from=unix:db.sock --active ], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid || kill `cat pid`' ++on_exit 'kill `cat *.pid`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server.log --pidfile --remote=punix:db.sock --unixctl="`pwd`"/unixctl db --sync-from=unix:db.sock --active ], [0], [ignore], [ignore]) + + dnl Save the current content + AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout]) +@@ -1984,6 +1971,7 @@ AT_CHECK([diff dump1 dump2]) + AT_CLEANUP + + AT_SETUP([ovsdb-server/read-only db:ptcp connection]) ++on_exit 'kill `cat *.pid`' + AT_KEYWORDS([ovsdb server read-only]) + AT_DATA([schema], + [[{"name": "mydb", +@@ -2072,12 +2060,10 @@ AT_CHECK([ovsdb-tool transact db2 \ + "row": {"number": 10, "name": "ten"}}]]'], [0], [ignore], [ignore]) + + dnl Start both 'db1' and 'db2'. +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile="`pwd`"/pid --remote=punix:db.sock --unixctl="`pwd`"/unixctl db1 --active ], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid || kill `cat pid`' +- ++on_exit 'kill `cat *.pid`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock --unixctl="`pwd`"/unixctl db1 --active ], [0], [ignore], [ignore]) + +-AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile="`pwd`"/pid2 --remote=punix:db2.sock --unixctl="`pwd`"/unixctl2 db2], [0], [ignore], [ignore]) +-on_exit 'test ! -e pid2 || kill `cat pid2`' ++AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl="`pwd`"/unixctl2 db2], [0], [ignore], [ignore]) + + OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status |grep active]) + OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/sync-status |grep active]) +@@ -2177,7 +2163,7 @@ dnl Starting a dummy server only to reserve some tcp port. + AT_CHECK([cp db db.tmp]) + AT_CHECK([ovsdb-server -vfile -vvlog:off --log-file=listener.log dnl + --detach --no-chdir dnl +- --pidfile=pid2 --unixctl=unixctl2 dnl ++ --pidfile=2.pid --unixctl=unixctl2 dnl + --remote=ptcp:0:127.0.0.1 dnl + db.tmp], [0], [stdout], [stderr]) + PARSE_LISTENING_PORT([listener.log], [BAD_TCP_PORT]) diff --git a/tests/pmd.at b/tests/pmd.at -index a2f9d34a2a..0a451f33c6 100644 +index a2f9d34a2a..3962dd2bd9 100644 --- a/tests/pmd.at +++ b/tests/pmd.at @@ -199,7 +199,7 @@ pmd thread numa_id core_id : @@ -5998,6 +10047,40 @@ index a2f9d34a2a..0a451f33c6 100644 AT_SETUP([PMD - stats]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 ofport_request=7 type=dummy-pmd options:n_rxq=4], [], [], [DUMMY_NUMA]) +@@ -1075,15 +1113,15 @@ AT_SETUP([PMD - dpif configuration]) + OVS_VSWITCHD_START([], [], [], [--dummy-numa 0,0]) + AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd]) + ++AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-set dpif_scalar], [0], [dnl ++DPIF implementation set to dpif_scalar. ++]) ++ + AT_CHECK([ovs-vsctl show], [], [stdout]) + AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-get | grep "dpif_scalar"], [], [dnl + dpif_scalar (pmds: 0) + ]) + +-AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-set dpif_scalar], [0], [dnl +-DPIF implementation set to dpif_scalar. +-]) +- + OVS_VSWITCHD_STOP + AT_CLEANUP + +@@ -1092,13 +1130,6 @@ OVS_VSWITCHD_START([], [], [], [--dummy-numa 0,0]) + AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd]) + + AT_CHECK([ovs-vsctl show], [], [stdout]) +-AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-get | grep generic], [], [dnl +- 1 : generic +-]) +- +-AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-get | grep autovalidator], [], [dnl +- 0 : autovalidator +-]) + + AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set autovalidator 3], [0], [dnl + Lookup priority change affected 0 dpcls ports and 0 subtables. diff --git a/tests/reconnect.at b/tests/reconnect.at index 0f74709f5a..5bca84351c 100644 --- a/tests/reconnect.at @@ -6250,7 +10333,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..1d20366280 100644 +index f22d86e466..36e10aa4a8 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -218,6 +218,7 @@ OVS_TRAFFIC_VSWITCHD_STOP @@ -6465,6 +10548,139 @@ index f22d86e466..1d20366280 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 +6807,132 @@ AT_CHECK([ovs-ofctl dump-flows br0 | grep table=2, | OFPROTO_CLEAR_DURATION_IDLE + OVS_TRAFFIC_VSWITCHD_STOP + AT_CLEANUP + ++AT_SETUP([conntrack - can match and clear ct_state from outside OVS]) ++CHECK_CONNTRACK_LOCAL_STACK() ++OVS_CHECK_TUNNEL_TSO() ++OVS_CHECK_GENEVE() ++ ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"f0:00:00:01:01:02\"]) ++ ++AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ++AT_CHECK([ovs-ofctl add-flow br-underlay "priority=100,ct_state=+trk,actions=ct_clear,resubmit(,0)"]) ++AT_CHECK([ovs-ofctl add-flow br-underlay "priority=10,actions=normal"]) ++ ++ADD_NAMESPACES(at_ns0) ++ ++dnl Set up underlay link from host into the namespace using veth pair. ++ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24", "f0:00:00:01:01:01") ++AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) ++AT_CHECK([ip link set dev br-underlay up]) ++ ++dnl Set up tunnel endpoints on OVS outside the namespace and with a native ++dnl linux device inside the namespace. ++ADD_OVS_TUNNEL([geneve], [br0], [at_gnv0], [172.31.1.1], [10.1.1.100/24]) ++ADD_NATIVE_TUNNEL([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], ++ [vni 0]) ++ ++dnl First, check the underlay ++NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 172.31.1.100 | FORMAT_PING], [0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++dnl Okay, now check the overlay ++NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PING], [0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++dnl Confirm that the ct_state and ct_clear action found its way to the dp ++AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep ct_clear | sort | dnl ++ grep 'eth(src=f0:00:00:01:01:02,dst=f0:00:00:01:01:01)' | dnl ++ strip_stats | strip_used | dnl ++ sed 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], ++ [0], [dnl ++recirc_id(0),in_port(br-underlay),ct_state(+trk),eth(src=f0:00:00:01:01:02,dst=f0:00:00:01:01:01),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ct_clear,ovs-p0 ++]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP ++ ++AT_BANNER([IGMP]) ++ ++AT_SETUP([IGMP - flood under normal action]) ++ ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_NAMESPACES(at_ns0, at_ns1) ++ ++ADD_VETH(p1, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ++ADD_VETH(p2, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") ++ ++AT_CHECK([ovs-ofctl add-flow br0 "actions=NORMAL"]) ++ ++NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 01 00 5e 01 01 03 dnl ++f0 00 00 01 01 01 08 00 46 c0 00 28 00 00 40 00 01 02 d3 49 45 65 eb 4a e0 dnl ++00 00 16 94 04 00 00 22 00 f9 02 00 00 00 01 04 00 00 00 e0 00 00 fb 00 00 dnl ++00 00 00 00 > /dev/null]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep -e .*ipv4 | sort | dnl ++ strip_stats | strip_used | strip_recirc | dnl ++ sed 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], ++ [0], [dnl ++recirc_id(),in_port(ovs-p1),eth(src=f0:00:00:01:01:01,dst=01:00:5e:01:01:03),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:br0,ovs-p2 ++]) ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP ++ ++AT_SETUP([IGMP - forward with ICMP]) ++ ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_NAMESPACES(at_ns0, at_ns1) ++ ++ADD_VETH(p1, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ++ADD_VETH(p2, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") ++ ++AT_DATA([flows.txt], [dnl ++table=0, arp actions=NORMAL ++table=0, ip,in_port=1 actions=ct(table=1,zone=64000) ++table=0, in_port=2 actions=output:1 ++table=1, ip,ct_state=+trk+inv actions=drop ++table=1 ip,in_port=1,icmp,ct_state=+trk+new actions=output:2 ++table=1, in_port=1,ip,ct_state=+trk+new actions=controller(userdata=00.de.ad.be.ef.ca.fe.01) ++table=1, in_port=1,ip,ct_state=+trk+est actions=output:2 ++]) ++AT_CHECK([ovs-ofctl del-flows br0]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++dnl Send the IGMP, followed by a unicast ICMP - ensure we won't black hole ++ ++NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 f0 00 00 01 01 02 dnl ++f0 00 00 01 01 01 08 00 46 c0 00 28 00 00 40 00 01 02 d3 49 45 65 eb 4a e0 dnl ++00 00 16 94 04 00 00 22 00 f9 02 00 00 00 01 04 00 00 00 e0 00 00 fb 00 00 dnl ++00 00 00 00 > /dev/null]) ++ ++NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 f0 00 00 01 01 02 dnl ++f0 00 00 01 01 01 08 00 45 00 00 1c 00 01 00 00 40 01 64 dc 0a 01 01 01 0a dnl ++01 01 02 08 00 f7 ff ff ff ff ff > /dev/null]) ++ ++sleep 1 ++ ++dnl Prefer the OpenFlow rules, because different datapaths will behave slightly ++dnl differently with respect to the exact dp rules. ++dnl ++dnl This is also why we clear n_bytes / n_packets - some kernels with ipv6 ++dnl enabled will bump some of these counters non-deterministically ++ ++AT_CHECK([ovs-ofctl dump-flows br0 | grep -v NXST | dnl ++ strip_duration | grep -v arp | grep -v n_packets=0 | dnl ++ grep -v 'in_port=2 actions=output:1' | dnl ++ sed 's/n_bytes=[[0-9]]*/n_bytes=0/ ++ s/idle_age=[[0-9]]*/idle_age=0/ ++ s/n_packets=[[1-9]]/n_packets=0/' | sort], [0], [dnl ++ cookie=0x0, table=0, n_packets=0, n_bytes=0, idle_age=0, ip,in_port=1 actions=ct(table=1,zone=64000) ++ cookie=0x0, table=1, n_packets=0, n_bytes=0, idle_age=0, ct_state=+new+trk,icmp,in_port=1 actions=output:2 ++ cookie=0x0, table=1, n_packets=0, n_bytes=0, idle_age=0, ct_state=+new+trk,ip,in_port=1 actions=controller(userdata=00.de.ad.be.ef.ca.fe.01) ++]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_BANNER([802.1ad]) + + AT_SETUP([802.1ad - vlan_limit]) diff --git a/tests/system-tso-macros.at b/tests/system-tso-macros.at index 406334f3e0..1a80047619 100644 --- a/tests/system-tso-macros.at @@ -6521,6 +10737,20 @@ index 0705475606..588a5dea63 100644 hmap_remove(&hmap, &e->node); } hmap_destroy(&hmap); +diff --git a/tests/test-hash.c b/tests/test-hash.c +index 5d3f8ea43f..aec5f580bb 100644 +--- a/tests/test-hash.c ++++ b/tests/test-hash.c +@@ -55,6 +55,9 @@ set_bit128(ovs_u128 *values, int bit, int n_bits) + static uint64_t + get_range128(ovs_u128 *value, int ofs, uint64_t mask) + { ++ if (ofs == 0) { ++ return value->u64.lo & mask; ++ } + return ((ofs < 64 ? (value->u64.lo >> ofs) : 0) & mask) + | ((ofs <= 64 ? (value->u64.hi << (64 - ofs)) : (value->u64.hi >> (ofs - 64)) & mask)); + } diff --git a/tests/test-hindex.c b/tests/test-hindex.c index af06be5fcc..cc2b1b8bd9 100644 --- a/tests/test-hindex.c @@ -6732,6 +10962,86 @@ index 6f1fb059bc..2c6c444488 100644 } } } +diff --git a/tests/test-rcu.c b/tests/test-rcu.c +index 965f3c49f3..bb17092bf0 100644 +--- a/tests/test-rcu.c ++++ b/tests/test-rcu.c +@@ -35,7 +35,7 @@ quiescer_main(void *aux OVS_UNUSED) + } + + static void +-test_rcu_quiesce(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) ++test_rcu_quiesce(void) + { + pthread_t quiescer; + +@@ -48,4 +48,29 @@ test_rcu_quiesce(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) + xpthread_join(quiescer, NULL); + } + +-OVSTEST_REGISTER("test-rcu-quiesce", test_rcu_quiesce); ++static void ++add_count(void *_count) ++{ ++ unsigned *count = (unsigned *)_count; ++ (*count) ++; ++} ++ ++static void ++test_rcu_barrier(void) ++{ ++ unsigned count = 0; ++ for (int i = 0; i < 10; i ++) { ++ ovsrcu_postpone(add_count, &count); ++ } ++ ++ ovsrcu_barrier(); ++ ovs_assert(count == 10); ++} ++ ++static void ++test_rcu(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { ++ test_rcu_quiesce(); ++ test_rcu_barrier(); ++} ++ ++OVSTEST_REGISTER("test-rcu", test_rcu); +diff --git a/tests/test-util.c b/tests/test-util.c +index f0fd042108..7d899fbbfd 100644 +--- a/tests/test-util.c ++++ b/tests/test-util.c +@@ -43,17 +43,16 @@ check_log_2_floor(uint32_t x, int n) + static void + test_log_2_floor(struct ovs_cmdl_context *ctx OVS_UNUSED) + { +- int n; +- +- for (n = 0; n < 32; n++) { ++ for (uint32_t n = 0; n < 32; n++) { + /* Check minimum x such that f(x) == n. */ +- check_log_2_floor(1 << n, n); ++ check_log_2_floor(UINT32_C(1) << n, n); + + /* Check maximum x such that f(x) == n. */ +- check_log_2_floor((1 << n) | ((1 << n) - 1), n); ++ check_log_2_floor((UINT32_C(1) << n) | ((UINT32_C(1) << n) - 1), n); + + /* Check a random value in the middle. */ +- check_log_2_floor((random_uint32() & ((1 << n) - 1)) | (1 << n), n); ++ check_log_2_floor((random_uint32() & ((UINT32_C(1) << n) - 1)) ++ | (UINT32_C(1) << n), n); + } + + /* log_2_floor(0) is undefined, so don't check it. */ +@@ -86,7 +85,7 @@ test_ctz(struct ovs_cmdl_context *ctx OVS_UNUSED) + + for (n = 0; n < 32; n++) { + /* Check minimum x such that f(x) == n. */ +- check_ctz32(1 << n, n); ++ check_ctz32(UINT32_C(1) << n, n); + + /* Check maximum x such that f(x) == n. */ + check_ctz32(UINT32_MAX << n, n); diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at index 57589758f4..c63344196b 100644 --- a/tests/tunnel-push-pop.at @@ -6844,6 +11154,23 @@ index b8ae7caa9b..fd482aa872 100644 ]) OVS_VSWITCHD_STOP +diff --git a/utilities/gdb/ovs_gdb.py b/utilities/gdb/ovs_gdb.py +index 62928d50fc..763ece2a78 100644 +--- a/utilities/gdb/ovs_gdb.py ++++ b/utilities/gdb/ovs_gdb.py +@@ -1391,7 +1391,8 @@ class CmdDumpPackets(gdb.Command): + print("Error, unsupported argument type: {}".format(str(val.type))) + return + +- tcpdump(pkt_list, args=tcpdump_args) ++ stdout = tcpdump(pkt_list, args=tcpdump_args, getfd=True, quiet=True) ++ gdb.write(stdout.read().decode("utf8", "replace")) + + def extract_pkt(self, pkt): + pkt_fields = pkt.type.keys() +diff --git a/utilities/ovs-appctl-bashcomp.bash b/utilities/ovs-appctl-bashcomp.bash +old mode 100755 +new mode 100644 diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index ede7f1e61a..6771973ae9 100644 --- a/utilities/ovs-ofctl.c @@ -6863,6 +11190,63 @@ index ede7f1e61a..6771973ae9 100644 const struct ofp_header *error_oh = error->data; ovs_be32 error_xid = error_oh->xid; enum ofperr ofperr; +diff --git a/utilities/ovs-save b/utilities/ovs-save +index fb2025b765..a190902f4d 100755 +--- a/utilities/ovs-save ++++ b/utilities/ovs-save +@@ -102,7 +102,7 @@ save_interfaces () { + get_highest_ofp_version() { + ovs-vsctl get bridge "$1" protocols | \ + sed 's/[][]//g' | sed 's/\ //g' | \ +- awk -F ',' '{ print (NF>1)? $(NF) : "OpenFlow14" }' ++ awk -F ',' '{ print (NF>0)? $(NF) : "OpenFlow14" }' + } + + save_flows () { +diff --git a/utilities/ovs-tcpdump.in b/utilities/ovs-tcpdump.in +index 82d1bedfa6..7fd26e4055 100755 +--- a/utilities/ovs-tcpdump.in ++++ b/utilities/ovs-tcpdump.in +@@ -165,6 +165,9 @@ class OVSDB(object): + self._idl_conn = idl.Idl(db_sock, schema) + OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB + ++ def close_idl(self): ++ self._idl_conn.close() ++ + def _get_schema(self): + error, strm = Stream.open_block(Stream.open(self._db_sock)) + if error: +@@ -403,7 +406,8 @@ def py_which(executable): + + + def main(): +- db_sock = 'unix:@RUNDIR@/db.sock' ++ rundir = os.environ.get('OVS_RUNDIR', '@RUNDIR@') ++ db_sock = 'unix:%s' % os.path.join(rundir, "db.sock") + interface = None + tcpdargs = [] + +@@ -500,6 +504,8 @@ def main(): + pass + sys.exit(1) + ++ ovsdb.close_idl() ++ + pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs)) + try: + while pipes.poll() is None: +@@ -512,6 +518,7 @@ 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: +diff --git a/utilities/ovs-vsctl-bashcomp.bash b/utilities/ovs-vsctl-bashcomp.bash +old mode 100755 +new mode 100644 diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c index 37cc72d401..1032089fc2 100644 --- a/utilities/ovs-vsctl.c @@ -7286,3 +11670,23 @@ index ab552457d9..99c4adcd53 100644 del_port(vtepctl_ctx, port); } +diff --git a/xenserver/openvswitch-xen.spec.in b/xenserver/openvswitch-xen.spec.in +index 4d21c6364f..ae22f2f5c4 100644 +--- a/xenserver/openvswitch-xen.spec.in ++++ b/xenserver/openvswitch-xen.spec.in +@@ -457,6 +457,7 @@ exit 0 + /usr/share/openvswitch/scripts/ovs-lib + /usr/share/openvswitch/scripts/ovs-vtep + /usr/share/openvswitch/vswitch.ovsschema ++/usr/share/openvswitch/local-config.ovsschema + /usr/share/openvswitch/vtep.ovsschema + /usr/sbin/ovs-bugtool + /usr/sbin/ovs-vswitchd +@@ -479,6 +480,7 @@ exit 0 + /usr/share/man/man1/ovsdb-client.1.gz + /usr/share/man/man1/ovsdb-server.1.gz + /usr/share/man/man1/ovsdb-tool.1.gz ++/usr/share/man/man5/ovsdb.local-config.5.gz + /usr/share/man/man5/ovsdb-server.5.gz + /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz + /usr/share/man/man5/vtep.5.gz diff --git a/SPECS/openvswitch2.17.spec b/SPECS/openvswitch2.17.spec index edfb655..3af1e7f 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: 15%{?dist} +Release: 31%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -155,7 +155,7 @@ BuildRequires: meson BuildRequires: python3-pyelftools %endif # DPDK driver dependencies -BuildRequires: zlib-devel numactl-devel +BuildRequires: zlib-devel numactl-devel libarchive-devel %ifarch x86_64 BuildRequires: rdma-core-devel >= 15 libmnl-devel %endif @@ -684,6 +684,7 @@ exit 0 %{_datadir}/openvswitch/scripts/ovs-ctl %{_datadir}/openvswitch/scripts/ovs-kmod-ctl %{_datadir}/openvswitch/scripts/ovs-systemd-reload +%config %{_datadir}/openvswitch/local-config.ovsschema %config %{_datadir}/openvswitch/vswitch.ovsschema %config %{_datadir}/openvswitch/vtep.ovsschema %{_bindir}/ovs-appctl @@ -701,6 +702,7 @@ exit 0 %{_mandir}/man1/ovsdb-server.1* %{_mandir}/man1/ovsdb-tool.1* %{_mandir}/man5/ovsdb.5* +%{_mandir}/man5/ovsdb.local-config.5* %{_mandir}/man5/ovsdb-server.5.* %{_mandir}/man5/ovs-vswitchd.conf.db.5* %{_mandir}/man5/vtep.5* @@ -746,6 +748,140 @@ exit 0 %endif %changelog +* Thu Jul 14 2022 Open vSwitch CI - 2.17.0-31 +- Merging upstream branch-2.17 [RH git: bdc6c6696c] + Commit list: + 13ac0bc7c6 tc: Fix misaligned access while creating pedit actions. + 2c85d737a4 utilities/bashcomp: Fix incorrect file mode. + + +* Wed Jul 06 2022 Timothy Redaelli - 2.17.0-30 +- rhel: libarchive-devel is needed for DPDK to load compressed firmwares [RH git: 95331d366d] + Reported-by: David Marchand (david.marchand@redhat.com) + + +* Fri Jul 01 2022 Open vSwitch CI - 2.17.0-29 +- Merging upstream branch-2.17 [RH git: d3c723a17e] + Commit list: + 05e9d2b7a9 Pmd.at: fix dpcls and dpif configuration test cases. + + +* Thu Jun 30 2022 Ilya Maximets - 2.17.0-28 +- Merging upstream branch-2.17 [RH git: 9738f7f756] + Commit list: + 45ecaa9e57 ovsdb: Add Local_Config schema. + + Merge: + redhat/template.spec.in updated with new files. + + Signed-off-by: Ilya Maximets + + +* Wed Jun 29 2022 Open vSwitch CI - 2.17.0-27 +- Merging upstream branch-2.17 [RH git: 62ee0c2ec8] + Commit list: + 61d64d3899 dpif-netdev: Fix leak of AVX512 DPIF scratch pad. + + +* Wed Jun 29 2022 Timothy Redaelli - 2.17.0-26 +- Fix REPO_URL [RH git: cf6a18b2bd] + + +* Wed Jun 29 2022 Open vSwitch CI - 2.17.0-25 +- Merging upstream branch-2.17 [RH git: 27d62d7d3f] + Commit list: + a77ad9693c dpif-netdev: Refactor AVX512 runtime checks. (#2100393) + + +* Tue Jun 28 2022 Open vSwitch CI - 2.17.0-24 +- Merging upstream branch-2.17 [RH git: f0e25fe3e6] + Commit list: + ccea7df578 dpif-netdev-extract-avx512: Protect GCC builtin usage. + 807f7f994a ovs-tcpdump: Default to OVS_RUNDIR if present. + ec13b03ca3 ovsdb: Fix memory leak on error path in ovsdb_file_read__(). + 8b2dff2e34 odp-util: Ignore unknown attributes in parse_key_and_mask_to_match(). (#2089331) + 13d97f6637 ofproto-dpif: Avoid unneccesary backer revalidation. + 9b4035d699 lldp: Fix lldp memory leak. + d9351febc2 ipfix: Trigger revalidation if ipfix options changes. + 5419b1de93 conntrack: Fix incorrect bit shift while hashing nat range. + 1ab5f94a11 packets: Fix misaligned write to MPLS lse. + 8e00be03c7 tc: Fix misaligned access to stats and time values. + 3a1f5341ca odp-util: Fix unaligned access to tunnel id. + 0c54c43b89 ofpbuf: Fix offsetting a NULL pointer in ofpbuf_reserve. + 98edacb40c drop-stats.at: Fix frequent failures of the recursion too deep test. + cbc13ce4f7 odp_util: Fix parse_key_and_mask_to_match() vlan parsing. + + +* Tue Jun 21 2022 Open vSwitch CI - 2.17.0-23 +- Merging upstream branch-2.17 [RH git: e2e0aac349] + Commit list: + 73e6ce4925 Prepare for 2.17.3. + 95979b0f0d Set release date for 2.17.2. + + +* Tue Jun 07 2022 Open vSwitch CI - 2.17.0-22 +- Merging upstream branch-2.17 [RH git: ce91947e61] + Commit list: + 250e1a6dd2 ofproto-dpif-xlate: Fix internal CT state for non-recirc traffic. + fe870ee072 classifier: Adjust segment boundary to execute prerequisite processing. (#2081773) + ec0ec464ba ovs-tcpdump: Fix error when stopping ovs-tcpdump. + + +* Tue May 31 2022 Open vSwitch CI - 2.17.0-21 +- Merging upstream branch-2.17 [RH git: 6ff800a303] + Commit list: + 420823e2af ofproto-dpif: Fix meter use-after-free. + c762da2623 ovs-rcu: Add ovsrcu_barrier. + cd9b6b64f4 dpif-netdev: Fix ALB 'rebalance_intvl' max hard limit. + 64f6c49d25 dpif-netdev: Fix ALB parameters type mismatch. + b11b84ea7f dpdk: Use DPDK 21.11.1 release. + d3bf48e9a9 raft: Don't use HMAP_FOR_EACH_SAFE when logging commands. + + +* Thu May 26 2022 Open vSwitch CI - 2.17.0-20 +- Merging upstream branch-2.17 [RH git: 77f2886b02] + Commit list: + e07377bb49 ovsdb: raft: Fix transaction double commit due to lost leadership. (#2046340) + 5da86cb360 dynamic-string: Fix undefined behavior due to offsetting null pointer. + 369e688908 Revert "odp-util: Always report ODP_FIT_TOO_LITTLE for IGMP." + 18341166ed ofproto-dpif-xlate: Fix netdev native tunnel neigh discovery spa. + 748e4b2b5b ovs-router: Expose the ovs_router_get_netdev_source_address function. + 34390bb35c ofproto-dpif: Trigger revalidation if ct tp changes. + + +* Wed May 25 2022 Open vSwitch CI - 2.17.0-19 +- Merging upstream branch-2.17 [RH git: 993b9ca4b4] + Commit list: + 1adb07e206 Carefully release NBL in Windows + + +* Thu May 19 2022 Open vSwitch CI - 2.17.0-18 +- Merging upstream branch-2.17 [RH git: 868b675dfd] + Commit list: + 1ccaba4484 tests: Properly kill ovsdb test processes. + 260b091c2a ovs-save: Get highest ofp version error. + 7606bb1210 netdev-linux: Properly access 32-bit aligned rtnl_link_stats64 structs. + 0688b9f27d treewide: Avoid offsetting NULL pointers. + 92bcf0a823 treewide: Fix invalid bit shift operations. + + +* Wed May 04 2022 Open vSwitch CI - 2.17.0-17 +- Merging upstream branch-2.17 [RH git: e16db3efbf] + Commit list: + 7fa76371de utilities: Handle dumping packets in GDB TUI. + 8cac8baa8f ofproto-dpif-xlate: Remove mirror assert. + e0e8f0c546 netdev-dpdk: Fix tx drops statistic for a down netdev. + f9b5f8a781 netdev-dpdk: Remove a leftover lock annotation. + 4c3976ff2a netdev-dpdk: Refactor the DPDK transmit path. + + +* Wed May 04 2022 Open vSwitch CI - 2.17.0-16 +- Merging upstream branch-2.17 [RH git: ca8c5adb3e] + Commit list: + 410b97c839 netdev-offload-dpdk: Fix ethernet type for VLANs. + 7948312feb netdev-offload-dpdk: Use has_vlan match attribute. + + * Mon May 02 2022 Open vSwitch CI - 2.17.0-15 - Merging upstream branch-2.17 [RH git: e706ea8148] Commit list: