diff --git a/SOURCES/openvswitch-2.15.0.patch b/SOURCES/openvswitch-2.15.0.patch index 436e7ea..f5637af 100644 --- a/SOURCES/openvswitch-2.15.0.patch +++ b/SOURCES/openvswitch-2.15.0.patch @@ -156,6 +156,19 @@ index 3a24e54f97..f5edfa756b 100644 .. note:: +diff --git a/Documentation/intro/install/general.rst b/Documentation/intro/install/general.rst +index c4300cd53e..a297aadac8 100644 +--- a/Documentation/intro/install/general.rst ++++ b/Documentation/intro/install/general.rst +@@ -169,7 +169,7 @@ other than plain text, only if you have the following: + If you are going to extensively modify Open vSwitch, consider installing the + following to obtain better warnings: + +-- "sparse" version 0.5.1 or later ++- "sparse" version 0.6.2 or later + (https://git.kernel.org/pub/scm/devel/sparse/sparse.git/). + + - GNU make. diff --git a/Documentation/topics/dpdk/pmd.rst b/Documentation/topics/dpdk/pmd.rst index caa7d97bef..9a79e43db5 100644 --- a/Documentation/topics/dpdk/pmd.rst @@ -673,7 +686,7 @@ index bc901efdb1..d51b7f9ce9 100644 --------------------- - OVSDB: diff --git a/acinclude.m4 b/acinclude.m4 -index 435685c93d..44ee2a7f82 100644 +index 435685c93d..cbaee636b4 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -209,10 +209,10 @@ dnl Configure Linux tc compat. @@ -704,6 +717,15 @@ index 435685c93d..44ee2a7f82 100644 ]) dnl OVS_CHECK_LINUX_SCTP_CT +@@ -1362,7 +1369,7 @@ AC_DEFUN([OVS_ENABLE_SPARSE], + : ${SPARSE=sparse} + AC_SUBST([SPARSE]) + AC_CONFIG_COMMANDS_PRE( +- [CC='$(if $(C:0=),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) $(SPARSE_WERROR) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')']) ++ [CC='$(if $(C:0=),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) $(SPARSE_WERROR) -I $(top_srcdir)/include/sparse -I $(top_srcdir)/include $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')']) + + AC_ARG_ENABLE( + [sparse], diff --git a/configure.ac b/configure.ac index fd82d7d270..ccfbc06639 100644 --- a/configure.ac @@ -19315,6 +19337,217 @@ index 1fa6d88fab..84670d80aa 100644 include/openvswitch/util.h \ include/openvswitch/uuid.h \ include/openvswitch/version.h \ +diff --git a/include/openvswitch/hmap.h b/include/openvswitch/hmap.h +index 4e001cc692..68c284cf14 100644 +--- a/include/openvswitch/hmap.h ++++ b/include/openvswitch/hmap.h +@@ -134,17 +134,17 @@ struct hmap_node *hmap_random_node(const struct hmap *); + * without using 'break', NODE will be NULL. This is true for all of the + * HMAP_FOR_EACH_*() macros. + */ +-#define HMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HMAP) \ +- for (INIT_CONTAINER(NODE, hmap_first_with_hash(HMAP, HASH), MEMBER); \ +- (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) \ +- || ((NODE = NULL), false); \ +- ASSIGN_CONTAINER(NODE, hmap_next_with_hash(&(NODE)->MEMBER), \ +- MEMBER)) +-#define HMAP_FOR_EACH_IN_BUCKET(NODE, MEMBER, HASH, HMAP) \ +- for (INIT_CONTAINER(NODE, hmap_first_in_bucket(HMAP, HASH), MEMBER); \ +- (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) \ +- || ((NODE = NULL), false); \ +- ASSIGN_CONTAINER(NODE, hmap_next_in_bucket(&(NODE)->MEMBER), MEMBER)) ++#define HMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HMAP) \ ++ for (INIT_MULTIVAR(NODE, MEMBER, hmap_first_with_hash(HMAP, HASH), \ ++ struct hmap_node); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, hmap_next_with_hash(ITER_VAR(NODE)))) ++ ++#define HMAP_FOR_EACH_IN_BUCKET(NODE, MEMBER, HASH, HMAP) \ ++ for (INIT_MULTIVAR(NODE, MEMBER, hmap_first_in_bucket(HMAP, HASH), \ ++ struct hmap_node); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, hmap_next_in_bucket(ITER_VAR(NODE)))) + + static inline struct hmap_node *hmap_first_with_hash(const struct hmap *, + size_t hash); +@@ -170,54 +170,62 @@ bool hmap_contains(const struct hmap *, const struct hmap_node *); + /* Iterates through every node in HMAP. */ + #define HMAP_FOR_EACH(NODE, MEMBER, HMAP) \ + HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, (void) 0) +-#define HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, ...) \ +- for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__; \ +- (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) \ +- || ((NODE = NULL), false); \ +- ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER)) ++#define HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, ...) \ ++ for (INIT_MULTIVAR_EXP(NODE, MEMBER, hmap_first(HMAP), struct hmap_node, \ ++ __VA_ARGS__); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, hmap_next(HMAP, ITER_VAR(NODE)))) + + /* Safe when NODE may be freed (not needed when NODE may be removed from the + * hash map but its members remain accessible and intact). */ + #define HMAP_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HMAP) \ +- HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, (void) 0) +-#define HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, ...) \ +- for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__; \ +- ((NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) \ +- || ((NODE = NULL), false) \ +- ? INIT_CONTAINER(NEXT, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER), 1 \ +- : 0); \ +- (NODE) = (NEXT)) ++ HMAP_FOR_EACH_SAFE_INIT (NODE, NEXT, MEMBER, HMAP, (void) NEXT) ++ ++#define HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, ...) \ ++ for (INIT_MULTIVAR_SAFE_LONG_EXP(NODE, NEXT, MEMBER, hmap_first(HMAP), \ ++ struct hmap_node, __VA_ARGS__); \ ++ CONDITION_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER, \ ++ ITER_VAR(NODE) != NULL, \ ++ ITER_VAR(NEXT) = hmap_next(HMAP, ITER_VAR(NODE)), \ ++ ITER_VAR(NEXT) != NULL); \ ++ UPDATE_MULTIVAR_SAFE_LONG(NODE, NEXT)) + + /* Continues an iteration from just after NODE. */ + #define HMAP_FOR_EACH_CONTINUE(NODE, MEMBER, HMAP) \ + HMAP_FOR_EACH_CONTINUE_INIT(NODE, MEMBER, HMAP, (void) 0) +-#define HMAP_FOR_EACH_CONTINUE_INIT(NODE, MEMBER, HMAP, ...) \ +- for (ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER), \ +- __VA_ARGS__; \ +- (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) \ +- || ((NODE = NULL), false); \ +- ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER)) ++#define HMAP_FOR_EACH_CONTINUE_INIT(NODE, MEMBER, HMAP, ...) \ ++ for (INIT_MULTIVAR_EXP(NODE, MEMBER, hmap_next(HMAP, &(NODE)->MEMBER), \ ++ struct hmap_node, __VA_ARGS__); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, hmap_next(HMAP, ITER_VAR(NODE)))) ++ ++struct hmap_pop_helper_iter__ { ++ size_t bucket; ++ struct hmap_node *node; ++}; + +-static inline struct hmap_node * +-hmap_pop_helper__(struct hmap *hmap, size_t *bucket) { ++static inline void ++hmap_pop_helper__(struct hmap *hmap, struct hmap_pop_helper_iter__ *iter) { + +- for (; *bucket <= hmap->mask; (*bucket)++) { +- struct hmap_node *node = hmap->buckets[*bucket]; ++ for (; iter->bucket <= hmap->mask; (iter->bucket)++) { ++ struct hmap_node *node = hmap->buckets[iter->bucket]; + + if (node) { + hmap_remove(hmap, node); +- return node; ++ iter->node = node; ++ return; + } + } +- +- return NULL; ++ iter->node = NULL; + } + +-#define HMAP_FOR_EACH_POP(NODE, MEMBER, HMAP) \ +- for (size_t bucket__ = 0; \ +- INIT_CONTAINER(NODE, hmap_pop_helper__(HMAP, &bucket__), MEMBER), \ +- (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) \ +- || ((NODE = NULL), false);) ++#define HMAP_FOR_EACH_POP(NODE, MEMBER, HMAP) \ ++ for (struct hmap_pop_helper_iter__ ITER_VAR(NODE) = { 0, NULL }; \ ++ hmap_pop_helper__(HMAP, &ITER_VAR(NODE)), \ ++ (ITER_VAR(NODE).node != NULL) ? \ ++ (((NODE) = OBJECT_CONTAINING(ITER_VAR(NODE).node, \ ++ NODE, MEMBER)),1): \ ++ (((NODE) = NULL), 0);) + + static inline struct hmap_node *hmap_first(const struct hmap *); + static inline struct hmap_node *hmap_next(const struct hmap *, +diff --git a/include/openvswitch/list.h b/include/openvswitch/list.h +index 8ad5eeb327..bbd2edbd0c 100644 +--- a/include/openvswitch/list.h ++++ b/include/openvswitch/list.h +@@ -72,37 +72,48 @@ static inline bool ovs_list_is_empty(const struct ovs_list *); + static inline bool ovs_list_is_singleton(const struct ovs_list *); + static inline bool ovs_list_is_short(const struct ovs_list *); + +-#define LIST_FOR_EACH(ITER, MEMBER, LIST) \ +- for (INIT_CONTAINER(ITER, (LIST)->next, MEMBER); \ +- &(ITER)->MEMBER != (LIST); \ +- ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER)) +-#define LIST_FOR_EACH_CONTINUE(ITER, MEMBER, LIST) \ +- for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER); \ +- &(ITER)->MEMBER != (LIST); \ +- ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER)) +-#define LIST_FOR_EACH_REVERSE(ITER, MEMBER, LIST) \ +- for (INIT_CONTAINER(ITER, (LIST)->prev, MEMBER); \ +- &(ITER)->MEMBER != (LIST); \ +- ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER)) +-#define LIST_FOR_EACH_REVERSE_SAFE(ITER, PREV, MEMBER, LIST) \ +- for (INIT_CONTAINER(ITER, (LIST)->prev, MEMBER); \ +- (&(ITER)->MEMBER != (LIST) \ +- ? INIT_CONTAINER(PREV, (ITER)->MEMBER.prev, MEMBER), 1 \ +- : 0); \ +- (ITER) = (PREV)) +-#define LIST_FOR_EACH_REVERSE_CONTINUE(ITER, MEMBER, LIST) \ +- for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER); \ +- &(ITER)->MEMBER != (LIST); \ +- ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER)) +-#define LIST_FOR_EACH_SAFE(ITER, NEXT, MEMBER, LIST) \ +- for (INIT_CONTAINER(ITER, (LIST)->next, MEMBER); \ +- (&(ITER)->MEMBER != (LIST) \ +- ? INIT_CONTAINER(NEXT, (ITER)->MEMBER.next, MEMBER), 1 \ +- : 0); \ +- (ITER) = (NEXT)) +-#define LIST_FOR_EACH_POP(ITER, MEMBER, LIST) \ +- while (!ovs_list_is_empty(LIST) \ +- && (INIT_CONTAINER(ITER, ovs_list_pop_front(LIST), MEMBER), 1)) ++#define LIST_FOR_EACH(VAR, MEMBER, LIST) \ ++ for (INIT_MULTIVAR(VAR, MEMBER, (LIST)->next, struct ovs_list); \ ++ CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST)); \ ++ UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->next)) ++ ++#define LIST_FOR_EACH_CONTINUE(VAR, MEMBER, LIST) \ ++ for (INIT_MULTIVAR(VAR, MEMBER, VAR->MEMBER.next, struct ovs_list); \ ++ CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST)); \ ++ UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->next)) ++ ++#define LIST_FOR_EACH_REVERSE(VAR, MEMBER, LIST) \ ++ for (INIT_MULTIVAR(VAR, MEMBER, (LIST)->prev, struct ovs_list); \ ++ CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST)); \ ++ UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->prev)) ++ ++#define LIST_FOR_EACH_REVERSE_CONTINUE(VAR, MEMBER, LIST) \ ++ for (INIT_MULTIVAR(VAR, MEMBER, VAR->MEMBER.prev, struct ovs_list); \ ++ CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST)); \ ++ UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->prev)) ++ ++#define LIST_FOR_EACH_REVERSE_SAFE(VAR, PREV, MEMBER, LIST) \ ++ for (INIT_MULTIVAR_SAFE_LONG(VAR, PREV, MEMBER, (LIST)->prev, \ ++ struct ovs_list); \ ++ CONDITION_MULTIVAR_SAFE_LONG(VAR, PREV, MEMBER, \ ++ ITER_VAR(VAR) != (LIST), \ ++ ITER_VAR(PREV) = ITER_VAR(VAR)->prev, \ ++ ITER_VAR(PREV) != (LIST)); \ ++ UPDATE_MULTIVAR_SAFE_LONG(VAR, PREV)) ++ ++#define LIST_FOR_EACH_SAFE(VAR, NEXT, MEMBER, LIST) \ ++ for (INIT_MULTIVAR_SAFE_LONG(VAR, NEXT, MEMBER, (LIST)->next, \ ++ struct ovs_list); \ ++ CONDITION_MULTIVAR_SAFE_LONG(VAR, NEXT, MEMBER, \ ++ ITER_VAR(VAR) != (LIST), \ ++ ITER_VAR(NEXT) = ITER_VAR(VAR)->next, \ ++ ITER_VAR(NEXT) != (LIST)); \ ++ UPDATE_MULTIVAR_SAFE_LONG(VAR, NEXT)) ++ ++#define LIST_FOR_EACH_POP(ITER, MEMBER, LIST) \ ++ while (!ovs_list_is_empty(LIST) ? \ ++ (INIT_CONTAINER(ITER, ovs_list_pop_front(LIST), MEMBER), 1) : \ ++ (ITER = NULL, 0)) + + /* Inline implementations. */ + diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h index 95e52e3587..045dce8f5f 100644 --- a/include/openvswitch/meta-flow.h @@ -19383,6 +19616,161 @@ index 0000000000..a49563f071 +#endif + +#endif /* usdt-probes.h */ +diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h +index 228b185c3a..96f600160b 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 *); + #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \ + ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER)) + ++/* Multi-variable container iterators. ++ * ++ * The following macros facilitate safe iteration over data structures ++ * contained in objects. It does so by using an internal iterator variable of ++ * the type of the member object pointer (i.e: pointer to the data structure). ++ */ ++ ++/* Multi-variable iterator variable name. ++ * Returns the name of the internal iterator variable. ++ */ ++#define ITER_VAR(NAME) NAME ## __iterator__ ++ ++/* Multi-variable initialization. Creates an internal iterator variable that ++ * points to the provided pointer. The type of the iterator variable is ++ * ITER_TYPE*. It must be the same type as &VAR->MEMBER. ++ * ++ * The _EXP version evaluates the extra expressions once. ++ */ ++#define INIT_MULTIVAR(VAR, MEMBER, POINTER, ITER_TYPE) \ ++ INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0) ++ ++#define INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, ...) \ ++ ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER) ++ ++/* Multi-variable condition. ++ * Evaluates the condition expression (that must be based on the internal ++ * iterator variable). Only if the result of expression is true, the OBJECT is ++ * set to the object containing the current value of the iterator variable. ++ * ++ * It is up to the caller to make sure it is safe to run OBJECT_CONTAINING on ++ * the pointers that verify the condition. ++ */ ++#define CONDITION_MULTIVAR(VAR, MEMBER, EXPR) \ ++ ((EXPR) ? \ ++ (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), 1) : \ ++ (((VAR) = NULL), 0)) ++ ++/* Multi-variable update. ++ * Sets the iterator value to NEXT_ITER. ++ */ ++#define UPDATE_MULTIVAR(VAR, NEXT_ITER) \ ++ (ITER_VAR(VAR) = NEXT_ITER) ++ ++/* In the safe version of the multi-variable container iteration, the next ++ * value of the iterator is precalculated on the condition expression. ++ * This allows for the iterator to be freed inside the loop. ++ * ++ * Two versions of the macros are provided: ++ * ++ * * In the _SHORT version, the user does not have to provide a variable to ++ * store the next value of the iterator. Instead, a second iterator variable ++ * is declared in the INIT_ macro and its name is determined by ++ * ITER_NEXT_VAR(OBJECT). ++ * ++ * * In the _LONG version, the user provides another variable of the same type ++ * as the iterator object variable to store the next containing object. ++ * We still declare an iterator variable inside the loop but in this case it's ++ * name is derived from the name of the next containing variable. ++ * The value of the next containing object will only be set ++ * (via OBJECT_CONTAINING) if an additional condition is statisfied. This ++ * second condition must ensure it is safe to call OBJECT_CONTAINING on the ++ * next iterator variable. ++ * With respect to the value of the next containing object: ++ * - Inside of the loop: the variable is either NULL or safe to use. ++ * - Outside of the loop: the variable is NULL if the loop ends normally. ++ * If the loop ends with a "break;" statement, rules of Inside the loop ++ * apply. ++ */ ++#define ITER_NEXT_VAR(NAME) NAME ## __iterator__next__ ++ ++/* Safe initialization declares both iterators. */ ++#define INIT_MULTIVAR_SAFE_SHORT(VAR, MEMBER, POINTER, ITER_TYPE) \ ++ INIT_MULTIVAR_SAFE_SHORT_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0) ++ ++#define INIT_MULTIVAR_SAFE_SHORT_EXP(VAR, MEMBER, POINTER, ITER_TYPE, ...) \ ++ ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER), \ ++ *ITER_NEXT_VAR(VAR) = NULL ++ ++/* Evaluate the condition expression and, if satisfied, update the _next_ ++ * iterator with the NEXT_EXPR. ++ * Both EXPR and NEXT_EXPR should only use ITER_VAR(VAR) and ++ * ITER_NEXT_VAR(VAR). ++ */ ++#define CONDITION_MULTIVAR_SAFE_SHORT(VAR, MEMBER, EXPR, NEXT_EXPR) \ ++ ((EXPR) ? \ ++ (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), \ ++ (NEXT_EXPR), 1) : \ ++ (((VAR) = NULL), 0)) ++ ++#define UPDATE_MULTIVAR_SAFE_SHORT(VAR) \ ++ UPDATE_MULTIVAR(VAR, ITER_NEXT_VAR(VAR)) ++ ++/* _LONG versions of the macros. */ ++ ++#define INIT_MULTIVAR_SAFE_LONG(VAR, NEXT_VAR, MEMBER, POINTER, ITER_TYPE) \ ++ INIT_MULTIVAR_SAFE_LONG_EXP(VAR, NEXT_VAR, MEMBER, POINTER, ITER_TYPE, \ ++ (void) 0) \ ++ ++#define INIT_MULTIVAR_SAFE_LONG_EXP(VAR, NEXT_VAR, MEMBER, POINTER, \ ++ ITER_TYPE, ...) \ ++ ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER), \ ++ *ITER_VAR(NEXT_VAR) = NULL ++ ++/* Evaluate the condition expression and, if satisfied, update the _next_ ++ * iterator with the NEXT_EXPR. After, evaluate the NEXT_COND and, if ++ * satisfied, set the value to NEXT_VAR. NEXT_COND must use ITER_VAR(NEXT_VAR). ++ * ++ * Both EXPR and NEXT_EXPR should only use ITER_VAR(VAR) and ++ * ITER_VAR(NEXT_VAR). ++ */ ++#define CONDITION_MULTIVAR_SAFE_LONG(VAR, NEXT_VAR, MEMBER, EXPR, NEXT_EXPR, \ ++ NEXT_COND) \ ++ ((EXPR) ? \ ++ (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), \ ++ (NEXT_EXPR), ((NEXT_COND) ? \ ++ ((NEXT_VAR) = \ ++ OBJECT_CONTAINING(ITER_VAR(NEXT_VAR), NEXT_VAR, MEMBER)) : \ ++ ((NEXT_VAR) = NULL)), 1) : \ ++ (((VAR) = NULL), ((NEXT_VAR) = NULL), 0)) ++ ++#define UPDATE_MULTIVAR_SAFE_LONG(VAR, NEXT_VAR) \ ++ UPDATE_MULTIVAR(VAR, ITER_VAR(NEXT_VAR)) ++ ++/* Helpers to allow overloading the *_SAFE iterator macros and select either ++ * the LONG or the SHORT version depending on the number of arguments. ++ */ ++#define GET_SAFE_MACRO2(_1, _2, NAME, ...) NAME ++#define GET_SAFE_MACRO3(_1, _2, _3, NAME, ...) NAME ++#define GET_SAFE_MACRO4(_1, _2, _3, _4, NAME, ...) NAME ++#define GET_SAFE_MACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME ++#define GET_SAFE_MACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME ++#define GET_SAFE_MACRO(MAX_ARGS) GET_SAFE_MACRO ## MAX_ARGS ++ ++/* MSVC treats __VA_ARGS__ as a simple token in argument lists. Introduce ++ * a level of indirection to work around that. */ ++#define EXPAND_MACRO(name, args) name args ++ ++/* Overload the LONG and the SHORT version of the macros. MAX_ARGS is the ++ * maximum number of arguments (i.e: the number of arguments of the LONG ++ * version). */ ++#define OVERLOAD_SAFE_MACRO(LONG, SHORT, MAX_ARGS, ...) \ ++ EXPAND_MACRO(GET_SAFE_MACRO(MAX_ARGS), \ ++ (__VA_ARGS__, LONG, SHORT))(__VA_ARGS__) ++ + /* Returns the number of elements in ARRAY. */ + #define ARRAY_SIZE(ARRAY) __ARRAY_SIZE(ARRAY) + diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in index 64111768b3..41ef886194 100755 --- a/ipsec/ovs-monitor-ipsec.in @@ -19625,6 +20013,53 @@ index 3c965699ac..9698576d07 100644 bfd_put_details(&ds, bfd); VLOG(level, "%s", ds_cstr(&ds)); ds_destroy(&ds); +diff --git a/lib/cmap.h b/lib/cmap.h +index d9db3c915c..8655f76d92 100644 +--- a/lib/cmap.h ++++ b/lib/cmap.h +@@ -108,6 +108,8 @@ size_t cmap_replace(struct cmap *, struct cmap_node *old_node, + * + * CMAP and HASH are evaluated only once. NODE is evaluated many times. + * ++ * After a normal exit of the loop (not through a "break;" statement) NODE is ++ * NULL. + * + * Thread-safety + * ============= +@@ -128,15 +130,15 @@ size_t cmap_replace(struct cmap *, struct cmap_node *old_node, + * CMAP_FOR_EACH_WITH_HASH_PROTECTED may only be used if CMAP is guaranteed not + * to change during iteration. It may be very slightly faster. + */ +-#define CMAP_NODE_FOR_EACH(NODE, MEMBER, CMAP_NODE) \ +- for (INIT_CONTAINER(NODE, CMAP_NODE, MEMBER); \ +- (NODE) != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ +- ASSIGN_CONTAINER(NODE, cmap_node_next(&(NODE)->MEMBER), MEMBER)) +-#define CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, CMAP_NODE) \ +- for (INIT_CONTAINER(NODE, CMAP_NODE, MEMBER); \ +- (NODE) != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ +- ASSIGN_CONTAINER(NODE, cmap_node_next_protected(&(NODE)->MEMBER), \ +- MEMBER)) ++#define CMAP_NODE_FOR_EACH(NODE, MEMBER, CMAP_NODE) \ ++ for (INIT_MULTIVAR(NODE, MEMBER, CMAP_NODE, struct cmap_node); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, cmap_node_next(ITER_VAR(NODE)))) ++#define CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, CMAP_NODE) \ ++ for (INIT_MULTIVAR(NODE, MEMBER, CMAP_NODE, struct cmap_node); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, cmap_node_next_protected(ITER_VAR(NODE)))) ++ + #define CMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, CMAP) \ + CMAP_NODE_FOR_EACH(NODE, MEMBER, cmap_find(CMAP, HASH)) + #define CMAP_FOR_EACH_WITH_HASH_PROTECTED(NODE, MEMBER, HASH, CMAP) \ +@@ -223,7 +225,7 @@ unsigned long cmap_find_batch(const struct cmap *cmap, unsigned long map, + ? (INIT_CONTAINER(NODE, (CURSOR)->node, MEMBER), \ + cmap_cursor_advance(CURSOR), \ + true) \ +- : false) ++ : (NODE = NULL, false)) + + #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 feaaec1c3f..15d1cde79d 100644 --- a/lib/conntrack.c @@ -20638,6 +21073,75 @@ index 729d59b1b3..910e52ad96 100644 } } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) { if (OVS_LIKELY(size >= UDP_HEADER_LEN)) { +diff --git a/lib/hindex.h b/lib/hindex.h +index 876c5a9e39..f7a30d511a 100644 +--- a/lib/hindex.h ++++ b/lib/hindex.h +@@ -128,18 +128,22 @@ void hindex_remove(struct hindex *, struct hindex_node *); + * Evaluates HASH only once. + */ + #define HINDEX_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HINDEX) \ +- for (INIT_CONTAINER(NODE, hindex_node_with_hash(HINDEX, HASH), MEMBER); \ +- NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ +- ASSIGN_CONTAINER(NODE, (NODE)->MEMBER.s, MEMBER)) ++ for (INIT_MULTIVAR(NODE, MEMBER, hindex_node_with_hash(HINDEX, HASH), \ ++ struct hindex_node); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, ITER_VAR(NODE)->s)) + + /* Safe when NODE may be freed (not needed when NODE may be removed from the + * hash map but its members remain accessible and intact). */ +-#define HINDEX_FOR_EACH_WITH_HASH_SAFE(NODE, NEXT, MEMBER, HASH, HINDEX) \ +- for (INIT_CONTAINER(NODE, hindex_node_with_hash(HINDEX, HASH), MEMBER); \ +- (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER) \ +- ? INIT_CONTAINER(NEXT, (NODE)->MEMBER.s, MEMBER), 1 \ +- : 0); \ +- (NODE) = (NEXT)) ++#define HINDEX_FOR_EACH_WITH_HASH_SAFE(NODE, NEXT, MEMBER, HASH, HINDEX) \ ++ for (INIT_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER, \ ++ hindex_node_with_hash(HINDEX, HASH), \ ++ struct hindex_node); \ ++ CONDITION_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER, \ ++ ITER_VAR(NODE) != NULL, \ ++ ITER_VAR(NEXT) = ITER_VAR(NODE)->s, \ ++ ITER_VAR(NEXT) != NULL); \ ++ UPDATE_MULTIVAR_SAFE_LONG(NODE, NEXT)) + + /* Returns the head node in 'hindex' with the given 'hash', or a null pointer + * if no nodes have that hash value. */ +@@ -157,19 +161,22 @@ hindex_node_with_hash(const struct hindex *hindex, size_t hash) + /* Iteration. */ + + /* Iterates through every node in HINDEX. */ +-#define HINDEX_FOR_EACH(NODE, MEMBER, HINDEX) \ +- for (INIT_CONTAINER(NODE, hindex_first(HINDEX), MEMBER); \ +- NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ +- ASSIGN_CONTAINER(NODE, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER)) ++#define HINDEX_FOR_EACH(NODE, MEMBER, HINDEX) \ ++ for (INIT_MULTIVAR(NODE, MEMBER, hindex_first(HINDEX), \ ++ struct hindex_node); \ ++ CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \ ++ UPDATE_MULTIVAR(NODE, hindex_next(HINDEX, ITER_VAR(NODE)))) + + /* Safe when NODE may be freed (not needed when NODE may be removed from the + * hash index but its members remain accessible and intact). */ +-#define HINDEX_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HINDEX) \ +- for (INIT_CONTAINER(NODE, hindex_first(HINDEX), MEMBER); \ +- (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER) \ +- ? INIT_CONTAINER(NEXT, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER), 1 \ +- : 0); \ +- (NODE) = (NEXT)) ++#define HINDEX_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HINDEX) \ ++ for (INIT_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER, hindex_first(HINDEX), \ ++ struct hindex_node); \ ++ CONDITION_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER, \ ++ ITER_VAR(NODE) != NULL, \ ++ ITER_VAR(NEXT) = hindex_next(HINDEX, ITER_VAR(NODE)), \ ++ ITER_VAR(NEXT) != NULL); \ ++ UPDATE_MULTIVAR_SAFE_LONG(NODE, NEXT)) + + struct hindex_node *hindex_first(const struct hindex *); + struct hindex_node *hindex_next(const struct hindex *, diff --git a/lib/ipf.c b/lib/ipf.c index c20bcc0b33..009f5d1e9b 100644 --- a/lib/ipf.c @@ -21759,6 +22263,22 @@ index a2778de4bc..3894cb3c33 100644
Open vSwitch 2.6 introduced nat
. Linux 4.6 was the
earliest upstream kernel that implemented ct
support for
+diff --git a/lib/ovs-numa.h b/lib/ovs-numa.h
+index 8f2ea34308..8d141b41e3 100644
+--- a/lib/ovs-numa.h
++++ b/lib/ovs-numa.h
+@@ -66,9 +66,9 @@ void ovs_numa_dump_destroy(struct ovs_numa_dump *);
+ int ovs_numa_thread_setaffinity_core(unsigned core_id);
+
+ #define FOR_EACH_CORE_ON_DUMP(ITER, DUMP) \
+- HMAP_FOR_EACH((ITER), hmap_node, &(DUMP)->cores)
++ HMAP_FOR_EACH (ITER, hmap_node, &(DUMP)->cores)
+
+ #define FOR_EACH_NUMA_ON_DUMP(ITER, DUMP) \
+- HMAP_FOR_EACH((ITER), hmap_node, &(DUMP)->numas)
++ HMAP_FOR_EACH (ITER, hmap_node, &(DUMP)->numas)
+
+ #endif /* ovs-numa.h */
diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
index ff8adaefb5..249cff0512 100644
--- a/lib/ovsdb-cs.c
@@ -22214,6 +22734,98 @@ index f0cac8e0fa..7f5561f827 100644
return NULL;
}
+diff --git a/lib/rculist.h b/lib/rculist.h
+index 1072b87af2..c0d77acf94 100644
+--- a/lib/rculist.h
++++ b/lib/rculist.h
+@@ -365,35 +365,57 @@ rculist_is_singleton_protected(const struct rculist *list)
+ return list_next == list->prev && list_next != list;
+ }
+
+-#define RCULIST_FOR_EACH(ITER, MEMBER, RCULIST) \
+- for (INIT_CONTAINER(ITER, rculist_next(RCULIST), MEMBER); \
+- &(ITER)->MEMBER != (RCULIST); \
+- ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER))
+-#define RCULIST_FOR_EACH_CONTINUE(ITER, MEMBER, RCULIST) \
+- for (ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER); \
+- &(ITER)->MEMBER != (RCULIST); \
+- ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER))
+-
+-#define RCULIST_FOR_EACH_REVERSE_PROTECTED(ITER, MEMBER, RCULIST) \
+- for (INIT_CONTAINER(ITER, (RCULIST)->prev, MEMBER); \
+- &(ITER)->MEMBER != (RCULIST); \
+- ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
+-#define RCULIST_FOR_EACH_REVERSE_PROTECTED_CONTINUE(ITER, MEMBER, RCULIST) \
+- for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER); \
+- &(ITER)->MEMBER != (RCULIST); \
+- ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
+-
+-#define RCULIST_FOR_EACH_PROTECTED(ITER, MEMBER, RCULIST) \
+- for (INIT_CONTAINER(ITER, rculist_next_protected(RCULIST), MEMBER); \
+- &(ITER)->MEMBER != (RCULIST); \
+- ASSIGN_CONTAINER(ITER, rculist_next_protected(&(ITER)->MEMBER), \
+- MEMBER))
+-
+-#define RCULIST_FOR_EACH_SAFE_PROTECTED(ITER, NEXT, MEMBER, RCULIST) \
+- for (INIT_CONTAINER(ITER, rculist_next_protected(RCULIST), MEMBER); \
+- (&(ITER)->MEMBER != (RCULIST) \
+- ? INIT_CONTAINER(NEXT, rculist_next_protected(&(ITER)->MEMBER), \
+- MEMBER), 1 : 0); \
+- (ITER) = (NEXT))
++#define RCULIST_FOR_EACH(ITER, MEMBER, RCULIST) \
++ for (INIT_MULTIVAR(ITER, MEMBER, rculist_next(RCULIST), \
++ const struct rculist); \
++ CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST)); \
++ UPDATE_MULTIVAR(ITER, rculist_next(ITER_VAR(ITER))))
++
++#define RCULIST_FOR_EACH_CONTINUE(ITER, MEMBER, RCULIST) \
++ for (INIT_MULTIVAR(ITER, MEMBER, rculist_next(&(ITER)->MEMBER), \
++ const struct rculist); \
++ CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST)); \
++ UPDATE_MULTIVAR(ITER, rculist_next(ITER_VAR(ITER))))
++
++#define RCULIST_FOR_EACH_REVERSE_PROTECTED(ITER, MEMBER, RCULIST) \
++ for (INIT_MULTIVAR(ITER, MEMBER, (RCULIST)->prev, struct rculist); \
++ CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST)); \
++ UPDATE_MULTIVAR(ITER, ITER_VAR(VAR).prev))
++
++#define RCULIST_FOR_EACH_REVERSE_PROTECTED_CONTINUE(ITER, MEMBER, RCULIST) \
++ for (INIT_MULTIVAR(ITER, MEMBER, (ITER)->MEMBER.prev, struct rculist); \
++ CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST)); \
++ UPDATE_MULTIVAR(ITER, ITER_VAR(VAR).prev))
++
++#define RCULIST_FOR_EACH_PROTECTED(ITER, MEMBER, RCULIST) \
++ for (INIT_MULTIVAR(ITER, MEMBER, rculist_next_protected(RCULIST), \
++ struct rculist); \
++ CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST)); \
++ UPDATE_MULTIVAR(ITER, rculist_next_protected(ITER_VAR(ITER))) \
++
++#define RCULIST_FOR_EACH_SAFE_SHORT_PROTECTED(ITER, MEMBER, RCULIST) \
++ for (INIT_MULTIVAR_SAFE_SHORT(ITER, MEMBER, \
++ rculist_next_protected(RCULIST), \
++ struct rculist); \
++ CONDITION_MULTIVAR_SAFE_SHORT(ITER, MEMBER, \
++ ITER_VAR(ITER) != (RCULIST), \
++ ITER_NEXT_VAR(ITER) = rculist_next_protected(ITER_VAR(VAR))); \
++ UPDATE_MULTIVAR_SHORT(ITER))
++
++#define RCULIST_FOR_EACH_SAFE_LONG_PROTECTED(ITER, NEXT, MEMBER, RCULIST) \
++ for (INIT_MULTIVAR_SAFE_LONG(ITER, NEXT, MEMBER, \
++ rculist_next_protected(RCULIST) \
++ struct rculist); \
++ CONDITION_MULTIVAR_SAFE_LONG(VAR, NEXT, MEMBER \
++ ITER_VAR(ITER) != (RCULIST), \
++ ITER_VAR(NEXT) = rculist_next_protected(ITER_VAR(VAR)), \
++ ITER_VAR(NEXT) != (RCULIST)); \
++ UPDATE_MULTIVAR_LONG(ITER))
++
++#define RCULIST_FOR_EACH_SAFE_PROTECTED(...) \
++ OVERLOAD_SAFE_MACRO(RCULIST_FOR_EACH_SAFE_LONG_PROTECTED, \
++ RCULIST_FOR_EACH_SAFE_SHORT_PROTECTED, \
++ 4, __VA_ARGS__)
++
+
+ #endif /* rculist.h */
diff --git a/lib/reconnect.c b/lib/reconnect.c
index a929ddfd2d..89a0bcaf95 100644
--- a/lib/reconnect.c
@@ -23263,7 +23875,7 @@ index 244ea0fbab..1f560a1f93 100644
AC_DEFUN([OVS_CHECK_WIN64],
[AC_CACHE_CHECK(
diff --git a/ofproto/bond.c b/ofproto/bond.c
-index 35b9caac01..a4116588f4 100644
+index 35b9caac01..2c0ad5ef84 100644
--- a/ofproto/bond.c
+++ b/ofproto/bond.c
@@ -1173,49 +1173,72 @@ bond_shift_load(struct bond_entry *hash, struct bond_member *to)
@@ -23366,6 +23978,15 @@ index 35b9caac01..a4116588f4 100644
}
/* Inserts 'member' into 'bals' so that descending order of 'tx_bytes' is
+@@ -1230,7 +1253,7 @@ insert_bal(struct ovs_list *bals, struct bond_member *member)
+ break;
+ }
+ }
+- ovs_list_insert(&pos->bal_node, &member->bal_node);
++ ovs_list_insert(pos ? &pos->bal_node : bals, &member->bal_node);
+ }
+
+ /* Removes 'member' from its current list and then inserts it into 'bals' so
@@ -1242,6 +1265,22 @@ reinsert_bal(struct ovs_list *bals, struct bond_member *member)
insert_bal(bals, member);
}
@@ -28460,6 +29081,107 @@ index 34f82cee3d..9f0d38dfb3 100644
# CHECK_CONNTRACK_TIMEOUT()
#
# Perform requirements checks for running conntrack customized timeout tests.
+diff --git a/tests/test-cmap.c b/tests/test-cmap.c
+index 0705475606..f8cc4dd80a 100644
+--- a/tests/test-cmap.c
++++ b/tests/test-cmap.c
+@@ -74,6 +74,7 @@ check_cmap(struct cmap *cmap, const int values[], size_t n,
+ cmap_values[i++] = e->value;
+ }
+ assert(i == n);
++ assert(e == NULL);
+
+ /* Here we test iteration with cmap_next_position() */
+ i = 0;
+@@ -107,6 +108,7 @@ check_cmap(struct cmap *cmap, const int values[], size_t n,
+ count += e->value == values[i];
+ }
+ assert(count == 1);
++ assert(e == NULL);
+ }
+
+ /* Check that all the values are there in batched lookup. */
+@@ -130,6 +132,7 @@ check_cmap(struct cmap *cmap, const int values[], size_t n,
+ CMAP_NODE_FOR_EACH (e, node, nodes[k]) {
+ count += e->value == values[i + k];
+ }
++ assert(e == NULL);
+ }
+ assert(count == j); /* j elements in a batch. */
+ }
+diff --git a/tests/test-hindex.c b/tests/test-hindex.c
+index af06be5fcc..95e49284ee 100644
+--- a/tests/test-hindex.c
++++ b/tests/test-hindex.c
+@@ -265,6 +265,11 @@ test_hindex_for_each_safe(hash_func *hash)
+ i = 0;
+ n_remaining = n;
+ HINDEX_FOR_EACH_SAFE (e, next, node, &hindex) {
++ if (hindex_next(&hindex, &e->node) == NULL) {
++ assert(next == NULL);
++ } else {
++ assert(&next->node == hindex_next(&hindex, &e->node));
++ }
+ assert(i < n);
+ if (pattern & (1ul << e->value)) {
+ size_t j;
+@@ -281,6 +286,7 @@ test_hindex_for_each_safe(hash_func *hash)
+ i++;
+ }
+ assert(i == n);
++ assert(next == NULL);
+
+ for (i = 0; i < n; i++) {
+ if (pattern & (1ul << i)) {
+diff --git a/tests/test-hmap.c b/tests/test-hmap.c
+index 9259b0b3fc..47b4755386 100644
+--- a/tests/test-hmap.c
++++ b/tests/test-hmap.c
+@@ -62,6 +62,7 @@ check_hmap(struct hmap *hmap, const int values[], size_t n,
+ hmap_values[i++] = e->value;
+ }
+ assert(i == n);
++ assert(e == NULL);
+
+ memcpy(sort_values, values, sizeof *sort_values * n);
+ qsort(sort_values, n, sizeof *sort_values, compare_ints);
+@@ -82,6 +83,7 @@ check_hmap(struct hmap *hmap, const int values[], size_t n,
+ count += e->value == values[i];
+ }
+ assert(count == 1);
++ assert(e == NULL);
+ }
+
+ /* Check counters. */
+@@ -243,6 +245,11 @@ test_hmap_for_each_safe(hash_func *hash)
+ i = 0;
+ n_remaining = n;
+ HMAP_FOR_EACH_SAFE (e, next, node, &hmap) {
++ if (hmap_next(&hmap, &e->node) == NULL) {
++ assert(next == NULL);
++ } else {
++ assert(&next->node == hmap_next(&hmap, &e->node));
++ }
+ assert(i < n);
+ if (pattern & (1ul << e->value)) {
+ size_t j;
+@@ -259,6 +266,8 @@ test_hmap_for_each_safe(hash_func *hash)
+ i++;
+ }
+ assert(i == n);
++ assert(next == NULL);
++ assert(e == NULL);
+
+ for (i = 0; i < n; i++) {
+ if (pattern & (1ul << i)) {
+@@ -308,6 +317,7 @@ test_hmap_for_each_pop(hash_func *hash)
+ i++;
+ }
+ assert(i == n);
++ assert(e == NULL);
+
+ hmap_destroy(&hmap);
+ }
diff --git a/tests/test-jsonrpc.py b/tests/test-jsonrpc.py
index 3eabcd78d5..1df5afa221 100644
--- a/tests/test-jsonrpc.py
@@ -28473,6 +29195,52 @@ index 3eabcd78d5..1df5afa221 100644
import argparse
import errno
import os
+diff --git a/tests/test-list.c b/tests/test-list.c
+index 6f1fb059bc..648e02a5e2 100644
+--- a/tests/test-list.c
++++ b/tests/test-list.c
+@@ -61,7 +61,7 @@ check_list(struct ovs_list *list, const int values[], size_t n)
+ assert(e->value == values[i]);
+ i++;
+ }
+- assert(&e->node == list);
++ assert(e == NULL);
+ assert(i == n);
+
+ i = 0;
+@@ -70,7 +70,7 @@ check_list(struct ovs_list *list, const int values[], size_t n)
+ assert(e->value == values[n - i - 1]);
+ i++;
+ }
+- assert(&e->node == list);
++ assert(e == NULL);
+ assert(i == n);
+
+ assert(ovs_list_is_empty(list) == !n);
+@@ -135,6 +135,13 @@ test_list_for_each_safe(void)
+ values_idx = 0;
+ n_remaining = n;
+ LIST_FOR_EACH_SAFE (e, next, node, &list) {
++ /* "next" is valid as long as it's not pointing to &list. */
++ if (&e->node == list.prev) {
++ assert(next == NULL);
++ } else {
++ assert(&next->node == e->node.next);
++ }
++
+ assert(i < n);
+ if (pattern & (1ul << i)) {
+ ovs_list_remove(&e->node);
+@@ -148,7 +155,8 @@ test_list_for_each_safe(void)
+ i++;
+ }
+ assert(i == n);
+- assert(&e->node == &list);
++ assert(e == NULL);
++ assert(next == NULL);
+
+ for (i = 0; i < n; i++) {
+ if (pattern & (1ul << i)) {
diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
index 15433e3472..c4845956ca 100644
--- a/tests/test-ovsdb.c
diff --git a/SPECS/openvswitch2.15.spec b/SPECS/openvswitch2.15.spec
index 55d0993..57fe669 100644
--- a/SPECS/openvswitch2.15.spec
+++ b/SPECS/openvswitch2.15.spec
@@ -57,7 +57,7 @@ Summary: Open vSwitch
Group: System Environment/Daemons daemon/database/utilities
URL: http://www.openvswitch.org/
Version: 2.15.0
-Release: 88%{?dist}
+Release: 89%{?dist}
# Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the
# lib/sflow*.[ch] files are SISSL
@@ -702,6 +702,21 @@ exit 0
%endif
%changelog
+* Wed Mar 30 2022 Open vSwitch CI