diff --git a/SOURCES/openvswitch-2.16.0.patch b/SOURCES/openvswitch-2.16.0.patch index dc7a56d..2691b90 100644 --- a/SOURCES/openvswitch-2.16.0.patch +++ b/SOURCES/openvswitch-2.16.0.patch @@ -148,6 +148,19 @@ index d8fa931fab..9ce5285c58 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 95fa7af128..c1a35eb13a 100644 --- a/Documentation/topics/dpdk/pmd.rst @@ -206,7 +219,7 @@ index 559a51ba3f..80720f2607 100644 --------------------- - Removed support for 1024-bit Diffie-Hellman key exchange, which is now diff --git a/acinclude.m4 b/acinclude.m4 -index dba365ea1a..5619ab3573 100644 +index dba365ea1a..1b957c3dcd 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -77,7 +77,7 @@ dnl Checks if compiler and binutils supports AVX512. @@ -232,6 +245,15 @@ index dba365ea1a..5619ab3573 100644 ]) dnl OVS_CHECK_LINUX_SCTP_CT +@@ -1417,7 +1424,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 16b32be965..92817b62e1 100644 --- a/configure.ac @@ -739,6 +761,133 @@ index 0000000000..6fae6f727c + +#endif /* __KERNEL__ || !HAVE_TCA_STATS_PKT64 */ +#endif /* __LINUX_GEN_STATS_WRAPPER_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/json.h b/include/openvswitch/json.h index 73b562e03d..0831a9cee1 100644 --- a/include/openvswitch/json.h @@ -787,6 +936,90 @@ index 73b562e03d..0831a9cee1 100644 struct json *json_from_file(const char *file_name); struct json *json_from_stream(FILE *stream); +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 @@ -799,6 +1032,161 @@ index 95e52e3587..045dce8f5f 100644 bool mf_is_pipeline_field(const struct mf_field *); bool mf_is_set(const struct mf_field *, const struct flow *); void mf_mask_field(const struct mf_field *, struct flow_wildcards *); +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 89a36fe17b..a8b0705d9f 100755 --- a/ipsec/ovs-monitor-ipsec.in @@ -955,6 +1343,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 c502d23112..72e2ec5f71 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/db-ctl-base.c b/lib/db-ctl-base.c index 77cc76a9f6..7074561588 100644 --- a/lib/db-ctl-base.c @@ -1751,6 +2186,75 @@ index 89837de95d..a021bc0eba 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 d9f781147a..507db2aea2 100644 --- a/lib/ipf.c @@ -2313,6 +2817,22 @@ index 4579548ee1..9485ddfc93 100644 break; } +diff --git a/lib/ovs-numa.h b/lib/ovs-numa.h +index ecc251a7ff..83bd10cca5 100644 +--- a/lib/ovs-numa.h ++++ b/lib/ovs-numa.h +@@ -68,9 +68,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 659d49dbf7..dead31275d 100644 --- a/lib/ovsdb-cs.c @@ -3121,6 +3641,98 @@ index b30a11c24b..41835f6f4d 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 @@ -4112,6 +4724,19 @@ index e4b42b0594..877bca3127 100644 int tnl_neigh_lookup(const char dev_name[IFNAMSIZ], const struct in6_addr *dst, struct eth_addr *mac); void tnl_neigh_cache_init(void); +diff --git a/ofproto/bond.c b/ofproto/bond.c +index a4116588f4..2c0ad5ef84 100644 +--- a/ofproto/bond.c ++++ b/ofproto/bond.c +@@ -1253,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 diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index 864c136b5d..0f4a61ac6b 100644 --- a/ofproto/ofproto-dpif-sflow.c @@ -8688,6 +9313,107 @@ index 406334f3e0..1a80047619 100644 ) + +m4_define([CHECK_SYSTEM_TSO], []) +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-json.c b/tests/test-json.c index a7ee595e0b..072a537252 100644 --- a/tests/test-json.c @@ -8771,6 +9497,52 @@ index a7ee595e0b..072a537252 100644 +} + +OVSTEST_REGISTER("json-string-benchmark", json_string_benchmark_main); +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 daa55dab7b..57572cd3ed 100644 --- a/tests/test-ovsdb.c diff --git a/SPECS/openvswitch2.16.spec b/SPECS/openvswitch2.16.spec index efba37d..7c886c3 100644 --- a/SPECS/openvswitch2.16.spec +++ b/SPECS/openvswitch2.16.spec @@ -57,7 +57,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 2.16.0 -Release: 64%{?dist} +Release: 65%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -699,6 +699,21 @@ exit 0 %endif %changelog +* Wed Mar 30 2022 Open vSwitch CI - 2.16.0-65 +- Merging upstream branch-2.16 [RH git: b4c45acc47] + Commit list: + 7644c924e8 sparse: bump recommended version and include headers. + 20b87feba9 rculist: use multi-variable helpers for loop macros. + 05a440fafb hindex: use multi-variable iterators. + 04dca15004 cmap: use multi-variable iterators. + 80e64f712d hmap: implement UB-safe hmap pop iterator. + 3b4b0af690 hmap: use multi-variable helpers for hmap loops. + 05e899ea8f list: use multi-variable helpers for list loops. + d2406399ae util: add helpers to overload SAFE macro. + f22f9d947a util: add safe multi-variable iterators. + 72c3e8627c util: add multi-variable loop iterator macros. + + * Wed Mar 30 2022 Open vSwitch CI - 2.16.0-64 - Merging upstream branch-2.16 [RH git: 32008eb008] Commit list: