[ovs-dev] [PATCH ovn v3 4/4] lflow: Consistent conjunction id generation.
Numan Siddique
numans at ovn.org
Fri Nov 19 19:13:40 UTC 2021
On Thu, Nov 11, 2021 at 5:39 PM Han Zhou <hzhou at ovn.org> wrote:
>
> When a logical flow translation uses conjunction ids, it is better to
> keep the conjuntion ids consistent between iterations, so that the
> generated OVS flows don't change unnecessarily and avoid reinstalling
> unchanged flows to OVS. The problem was partially solved when
> lflow-cache was enabled but there was a bug for a corner case so the
> lflow-cache based approach was reverted in a previous commit.
>
> This patch takes an approach that generates conjunction ids based on the
> logical flow's uuid, which ensures the same ids being used when
> processing the same lflow in most cases. There is a very little chance
> that different logial flows happen to have the same 32-bit prefix in
> their uuids, but the probability is really low. For the very unlikely
> situation, the conflicts are handled properly and correctness is
> ensured.
>
> A new module lflow-conj-ids is created to manage the id allocations and
> maintaining the usage of each ids and their owner lflows.
>
> Unlike the previously lflow-cache based approach, this approach works
> equally well when lflow-cache is not in use.
>
> Signed-off-by: Han Zhou <hzhou at ovn.org>
Hi Han,
Few minor comments.
1. I think it would be nice to reorganize the newly added file -
controller/lflow-conj-ids.c to
be organized with public functions first and then static functions as
per the coding
guidelines - https://github.com/ovn-org/ovn/blob/main/Documentation/internals/contributing/coding-style.rst
2. I'd suggest to add a debug function in
controller/lflow-conj-ids.c which would dump
the allocated conj ids and the lflow conj_id mapping. And the
test file can verify the dump is as expected.
What do you think ? I'm fine if you think this can be a follow up patch.
Also if required, we can add appctl command to dump the conj id
allocations.
Thanks
Numan
> ---
> controller/automake.mk | 2 +
> controller/lflow-cache.c | 6 +-
> controller/lflow-cache.h | 9 +-
> controller/lflow-conj-ids.c | 259 +++++++++++++++++++++++++++++++
> controller/lflow-conj-ids.h | 41 +++++
> controller/lflow.c | 147 ++++++++----------
> controller/lflow.h | 4 +-
> controller/ovn-controller.c | 32 +---
> controller/test-lflow-cache.c | 36 +++--
> controller/test-lflow-conj-ids.c | 128 +++++++++++++++
> tests/automake.mk | 3 +
> tests/ovn-lflow-cache.at | 123 ++++++++++-----
> tests/ovn-lflow-conj-ids.at | 112 +++++++++++++
> tests/ovn.at | 49 ++++--
> tests/testsuite.at | 1 +
> 15 files changed, 772 insertions(+), 180 deletions(-)
> create mode 100644 controller/lflow-conj-ids.c
> create mode 100644 controller/lflow-conj-ids.h
> create mode 100644 controller/test-lflow-conj-ids.c
> create mode 100644 tests/ovn-lflow-conj-ids.at
>
> diff --git a/controller/automake.mk b/controller/automake.mk
> index 9f9b49fe0..c2ab1bbe6 100644
> --- a/controller/automake.mk
> +++ b/controller/automake.mk
> @@ -18,6 +18,8 @@ controller_ovn_controller_SOURCES = \
> controller/lflow.h \
> controller/lflow-cache.c \
> controller/lflow-cache.h \
> + controller/lflow-conj-ids.c \
> + controller/lflow-conj-ids.h \
> controller/lport.c \
> controller/lport.h \
> controller/ofctrl.c \
> diff --git a/controller/lflow-cache.c b/controller/lflow-cache.c
> index 26228c960..6db85fc12 100644
> --- a/controller/lflow-cache.c
> +++ b/controller/lflow-cache.c
> @@ -203,7 +203,7 @@ lflow_cache_get_stats(const struct lflow_cache *lc, struct ds *output)
>
> void
> lflow_cache_add_expr(struct lflow_cache *lc, const struct uuid *lflow_uuid,
> - uint32_t conj_id_ofs, struct expr *expr, size_t expr_sz)
> + struct expr *expr, size_t expr_sz)
> {
> struct lflow_cache_value *lcv =
> lflow_cache_add__(lc, lflow_uuid, LCACHE_T_EXPR, expr_sz);
> @@ -213,12 +213,12 @@ lflow_cache_add_expr(struct lflow_cache *lc, const struct uuid *lflow_uuid,
> return;
> }
> COVERAGE_INC(lflow_cache_add_expr);
> - lcv->conj_id_ofs = conj_id_ofs;
> lcv->expr = expr;
> }
>
> void
> lflow_cache_add_matches(struct lflow_cache *lc, const struct uuid *lflow_uuid,
> + uint32_t conj_id_ofs, uint32_t n_conjs,
> struct hmap *matches, size_t matches_sz)
> {
> struct lflow_cache_value *lcv =
> @@ -231,6 +231,8 @@ lflow_cache_add_matches(struct lflow_cache *lc, const struct uuid *lflow_uuid,
> }
> COVERAGE_INC(lflow_cache_add_matches);
> lcv->expr_matches = matches;
> + lcv->n_conjs = n_conjs;
> + lcv->conj_id_ofs = conj_id_ofs;
> }
>
> struct lflow_cache_value *
> diff --git a/controller/lflow-cache.h b/controller/lflow-cache.h
> index d42bdeaa3..faad32bb6 100644
> --- a/controller/lflow-cache.h
> +++ b/controller/lflow-cache.h
> @@ -43,6 +43,11 @@ enum lflow_cache_type {
>
> struct lflow_cache_value {
> enum lflow_cache_type type;
> +
> + /* n_conjs and conj_id_ofs are used only for LCACHE_T_MATCHES.
> + * They are saved together with the match, so that we can re-allocate the
> + * same ids when reusing the cached match for a given lflow. */
> + uint32_t n_conjs;
> uint32_t conj_id_ofs;
>
> union {
> @@ -61,10 +66,10 @@ bool lflow_cache_is_enabled(const struct lflow_cache *);
> void lflow_cache_get_stats(const struct lflow_cache *, struct ds *output);
>
> void lflow_cache_add_expr(struct lflow_cache *, const struct uuid *lflow_uuid,
> - uint32_t conj_id_ofs, struct expr *expr,
> - size_t expr_sz);
> + struct expr *expr, size_t expr_sz);
> void lflow_cache_add_matches(struct lflow_cache *,
> const struct uuid *lflow_uuid,
> + uint32_t conj_id_ofs, uint32_t n_conjs,
> struct hmap *matches, size_t matches_sz);
>
> struct lflow_cache_value *lflow_cache_get(struct lflow_cache *,
> diff --git a/controller/lflow-conj-ids.c b/controller/lflow-conj-ids.c
> new file mode 100644
> index 000000000..2678c4205
> --- /dev/null
> +++ b/controller/lflow-conj-ids.c
> @@ -0,0 +1,259 @@
> +/*
> + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +#include <config.h>
> +#include "coverage.h"
> +#include "lflow-conj-ids.h"
> +#include "util.h"
> +
> +COVERAGE_DEFINE(lflow_conj_conflict);
> +COVERAGE_DEFINE(lflow_conj_alloc);
> +COVERAGE_DEFINE(lflow_conj_alloc_specified);
> +COVERAGE_DEFINE(lflow_conj_free);
> +COVERAGE_DEFINE(lflow_conj_free_unexpected);
> +
> +/* Node in struct conj_ids.conj_id_allocations. */
> +struct conj_id_node {
> + struct hmap_node hmap_node;
> + uint32_t conj_id;
> +};
> +
> +/* Node in struct conj_ids.lflow_conj_ids. */
> +struct lflow_conj_node {
> + struct hmap_node hmap_node;
> + struct uuid lflow_uuid;
> + uint32_t start_conj_id;
> + uint32_t n_conjs;
> +};
> +
> +/* Insert n_conjs conjuntion ids starting from start_conj_id into the conj_ids,
> + * assuming the ids are confirmed to be available. */
> +static void
> +lflow_conj_ids_insert_(struct conj_ids *conj_ids,
> + const struct uuid *lflow_uuid,
> + uint32_t start_conj_id, uint32_t n_conjs)
> +{
> + ovs_assert(n_conjs);
> + uint32_t conj_id = start_conj_id;
> + for (uint32_t i = 0; i < n_conjs; i++) {
> + ovs_assert(conj_id);
> + struct conj_id_node *node = xzalloc(sizeof *node);
> + node->conj_id = conj_id;
> + hmap_insert(&conj_ids->conj_id_allocations, &node->hmap_node, conj_id);
> + conj_id++;
> + }
> +
> + struct lflow_conj_node *lflow_conj = xzalloc(sizeof *lflow_conj);
> + lflow_conj->lflow_uuid = *lflow_uuid;
> + lflow_conj->start_conj_id = start_conj_id;
> + lflow_conj->n_conjs = n_conjs;
> + hmap_insert(&conj_ids->lflow_conj_ids, &lflow_conj->hmap_node,
> + uuid_hash(lflow_uuid));
> +}
> +
> +static void
> +lflow_conj_ids_free_(struct conj_ids *conj_ids, const struct uuid *lflow_uuid,
> + bool expected)
> +{
> + struct lflow_conj_node *lflow_conj;
> + bool found = false;
> + HMAP_FOR_EACH_WITH_HASH (lflow_conj, hmap_node, uuid_hash(lflow_uuid),
> + &conj_ids->lflow_conj_ids) {
> + if (uuid_equals(&lflow_conj->lflow_uuid, lflow_uuid)) {
> + found = true;
> + break;
> + }
> + }
> + if (!found) {
> + return;
> + }
> + ovs_assert(lflow_conj->n_conjs);
> + COVERAGE_INC(lflow_conj_free);
> + if (!expected) {
> + /* It is unexpected that an entry is found when calling from
> + * alloc/alloc_specified. Something may be wrong in the lflow module.
> + */
> + COVERAGE_INC(lflow_conj_free_unexpected);
> + }
> + uint32_t conj_id = lflow_conj->start_conj_id;
> + for (uint32_t i = 0; i < lflow_conj->n_conjs; i++) {
> + ovs_assert(conj_id);
> + struct conj_id_node *conj_id_node;
> + HMAP_FOR_EACH_WITH_HASH (conj_id_node, hmap_node, conj_id,
> + &conj_ids->conj_id_allocations) {
> + if (conj_id_node->conj_id == conj_id) {
> + hmap_remove(&conj_ids->conj_id_allocations,
> + &conj_id_node->hmap_node);
> + free(conj_id_node);
> + break;
> + }
> + }
> + conj_id++;
> + }
> +
> + hmap_remove(&conj_ids->lflow_conj_ids, &lflow_conj->hmap_node);
> + free(lflow_conj);
> +}
> +
> +/* Allocate n_conjs continuous conjuction ids from the conj_ids for the given
> + * lflow_uuid. (0 is never included in an allocated range)
> + *
> + * The first conjunction id is returned. If no conjunction ids available, or if
> + * the input is invalid (n_conjs == 0), then 0 is returned.
> + *
> + * The algorithm tries to allocate the parts[0] of the input uuid as the first
> + * conjunction id. If it is unavailable, or any of the subsequent n_conjs - 1
> + * ids are unavailable, iterate until the next available n_conjs ids are found.
> + * Given that n_conjs is very small (in most cases will be 1), the algorithm
> + * should be efficient enough and in most cases just return the lflow_uuid's
> + * part[0], which ensures conjunction ids are consistent for the same logical
> + * flow in most cases.
> + *
> + * The performance will degrade if most of the uint32_t are allocated because
> + * conflicts will happen a lot. In practice this is not expected to happen in
> + * reasonable scale. Otherwise, if the amount of logical flows is close to
> + * this (4G logical flows that need conjunction ids) there are other parts of
> + * the system expected to be suffered even before reaching to a scale much
> + * smaller than this. */
> +uint32_t
> +lflow_conj_ids_alloc(struct conj_ids *conj_ids, const struct uuid *lflow_uuid,
> + uint32_t n_conjs)
> +{
> + if (!n_conjs) {
> + return 0;
> + }
> + lflow_conj_ids_free_(conj_ids, lflow_uuid, false);
> +
> + COVERAGE_INC(lflow_conj_alloc);
> +
> + uint32_t start_conj_id = lflow_uuid->parts[0];
> + uint32_t initial_id = start_conj_id;
> + bool initial = true;
> + while (true) {
> + if (start_conj_id == 0) {
> + start_conj_id++;
> + }
> + bool available = true;
> + uint32_t conj_id = start_conj_id;
> + for (uint32_t i = 0; i < n_conjs; i++) {
> + if (conj_id == 0) {
> + /* Overflow. Consider the current range as unavailable because
> + * we need a continuous range. Start over from 1 (0 is
> + * skipped). */
> + available = false;
> + break;
> + }
> + if (!initial && conj_id == initial_id) {
> + /* It has checked all ids (extreme situation, not expected in
> + * real environment). */
> + return 0;
> + }
> + initial = false;
> + struct conj_id_node *conj_id_node;
> + /* conj_id is both the key and the hash */
> + HMAP_FOR_EACH_WITH_HASH (conj_id_node, hmap_node, conj_id,
> + &conj_ids->conj_id_allocations) {
> + if (conj_id_node->conj_id == conj_id) {
> + available = false;
> + COVERAGE_INC(lflow_conj_conflict);
> + break;
> + }
> + }
> + if (!available) {
> + break;
> + }
> + conj_id++;
> + }
> + if (available) {
> + break;
> + }
> + start_conj_id = conj_id + 1;
> + }
> + lflow_conj_ids_insert_(conj_ids, lflow_uuid, start_conj_id, n_conjs);
> + return start_conj_id;
> +}
> +
> +/* Similar to lflow_conj_ids_alloc, except that it takes an extra parameter
> + * start_conj_id, which specifies the desired conjunction ids to be allocated,
> + * and if they are unavailable, return false directly without trying to find
> + * the next available ones. It returns true if the specified range is
> + * allocated successfully. */
> +bool
> +lflow_conj_ids_alloc_specified(struct conj_ids *conj_ids,
> + const struct uuid *lflow_uuid,
> + uint32_t start_conj_id, uint32_t n_conjs)
> +{
> + if (!n_conjs) {
> + return false;
> + }
> + lflow_conj_ids_free_(conj_ids, lflow_uuid, false);
> +
> + uint32_t conj_id = start_conj_id;
> + for (uint32_t i = 0; i < n_conjs; i++) {
> + if (!conj_id) {
> + return false;
> + }
> + struct conj_id_node *conj_id_node;
> + HMAP_FOR_EACH_WITH_HASH (conj_id_node, hmap_node, conj_id,
> + &conj_ids->conj_id_allocations) {
> + if (conj_id_node->conj_id == conj_id) {
> + return false;
> + }
> + }
> + conj_id++;
> + }
> + lflow_conj_ids_insert_(conj_ids, lflow_uuid, start_conj_id, n_conjs);
> + COVERAGE_INC(lflow_conj_alloc_specified);
> +
> + return true;
> +}
> +
> +/* Frees the conjunction IDs used by lflow_uuid. */
> +void
> +lflow_conj_ids_free(struct conj_ids *conj_ids, const struct uuid *lflow_uuid)
> +{
> + lflow_conj_ids_free_(conj_ids, lflow_uuid, true);
> +}
> +
> +void
> +lflow_conj_ids_init(struct conj_ids *conj_ids)
> +{
> + hmap_init(&conj_ids->conj_id_allocations);
> + hmap_init(&conj_ids->lflow_conj_ids);
> +}
> +
> +void
> +lflow_conj_ids_destroy(struct conj_ids *conj_ids) {
> + struct conj_id_node *conj_id_node, *next;
> + HMAP_FOR_EACH_SAFE (conj_id_node, next, hmap_node,
> + &conj_ids->conj_id_allocations) {
> + hmap_remove(&conj_ids->conj_id_allocations, &conj_id_node->hmap_node);
> + free(conj_id_node);
> + }
> + hmap_destroy(&conj_ids->conj_id_allocations);
> +
> + struct lflow_conj_node *lflow_conj, *l_c_next;
> + HMAP_FOR_EACH_SAFE (lflow_conj, l_c_next, hmap_node,
> + &conj_ids->lflow_conj_ids) {
> + hmap_remove(&conj_ids->lflow_conj_ids, &lflow_conj->hmap_node);
> + free(lflow_conj);
> + }
> + hmap_destroy(&conj_ids->lflow_conj_ids);
> +}
> +
> +void lflow_conj_ids_clear(struct conj_ids *conj_ids) {
> + lflow_conj_ids_destroy(conj_ids);
> + lflow_conj_ids_init(conj_ids);
> +}
> diff --git a/controller/lflow-conj-ids.h b/controller/lflow-conj-ids.h
> new file mode 100644
> index 000000000..d333fa8d5
> --- /dev/null
> +++ b/controller/lflow-conj-ids.h
> @@ -0,0 +1,41 @@
> +/*
> + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef LFLOW_CONJ_IDS_H
> +#define LFLOW_CONJ_IDS_H 1
> +
> +#include "openvswitch/hmap.h"
> +#include "uuid.h"
> +
> +struct conj_ids {
> + /* Allocated conjunction ids. Contains struct conj_id_node. */
> + struct hmap conj_id_allocations;
> + /* A map from lflows to the conjunction ids used. Contains struct
> + * lflow_conj_node. */
> + struct hmap lflow_conj_ids;
> +};
> +
> +uint32_t lflow_conj_ids_alloc(struct conj_ids *, const struct uuid *lflow_uuid,
> + uint32_t n_conjs);
> +bool lflow_conj_ids_alloc_specified(struct conj_ids *,
> + const struct uuid *lflow_uuid,
> + uint32_t start_conj_id, uint32_t n_conjs);
> +void lflow_conj_ids_free(struct conj_ids *, const struct uuid *lflow_uuid);
> +void lflow_conj_ids_init(struct conj_ids *);
> +void lflow_conj_ids_destroy(struct conj_ids *);
> +void lflow_conj_ids_clear(struct conj_ids *);
> +
> +#endif /* controller/lflow-conj-ids.h */
> diff --git a/controller/lflow.c b/controller/lflow.c
> index 59f5d3a07..d74924e3a 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -73,7 +73,7 @@ struct condition_aux {
> struct lflow_resource_ref *lfrr;
> };
>
> -static bool
> +static void
> consider_logical_flow(const struct sbrec_logical_flow *lflow,
> struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
> struct hmap *nd_ra_opts,
> @@ -365,14 +365,9 @@ add_logical_flows(struct lflow_ctx_in *l_ctx_in,
> controller_event_opts_init(&controller_event_opts);
>
> SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, l_ctx_in->logical_flow_table) {
> - if (!consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts,
> - &nd_ra_opts, &controller_event_opts,
> - l_ctx_in, l_ctx_out)) {
> - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> - VLOG_ERR_RL(&rl, "Conjunction id overflow when processing lflow "
> - UUID_FMT, UUID_ARGS(&lflow->header_.uuid));
> - l_ctx_out->conj_id_overflow = true;
> - }
> + consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts,
> + &nd_ra_opts, &controller_event_opts,
> + l_ctx_in, l_ctx_out);
> }
>
> dhcp_opts_destroy(&dhcp_opts);
> @@ -434,18 +429,17 @@ lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in,
> HMAP_FOR_EACH (ofrn, hmap_node, &flood_remove_nodes) {
> /* Delete entries from lflow resource reference. */
> lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->sb_uuid);
> + /* Delete conj_ids owned by the lflow. */
> + lflow_conj_ids_free(l_ctx_out->conj_ids, &ofrn->sb_uuid);
> /* Reprocessing the lflow if the sb record is not deleted. */
> lflow = sbrec_logical_flow_table_get_for_uuid(
> l_ctx_in->logical_flow_table, &ofrn->sb_uuid);
> if (lflow) {
> VLOG_DBG("re-add lflow "UUID_FMT,
> UUID_ARGS(&lflow->header_.uuid));
> - if (!consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts,
> - &nd_ra_opts, &controller_event_opts,
> - l_ctx_in, l_ctx_out)) {
> - ret = false;
> - break;
> - }
> + consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts,
> + &nd_ra_opts, &controller_event_opts,
> + l_ctx_in, l_ctx_out);
> }
> }
> HMAP_FOR_EACH_SAFE (ofrn, next, hmap_node, &flood_remove_nodes) {
> @@ -528,6 +522,7 @@ lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
> /* Secondly, for each lflow that is actually removed, reprocessing it. */
> HMAP_FOR_EACH (ofrn, hmap_node, &flood_remove_nodes) {
> lflow_resource_destroy_lflow(l_ctx_out->lfrr, &ofrn->sb_uuid);
> + lflow_conj_ids_free(l_ctx_out->conj_ids, &ofrn->sb_uuid);
>
> const struct sbrec_logical_flow *lflow =
> sbrec_logical_flow_table_get_for_uuid(l_ctx_in->logical_flow_table,
> @@ -540,13 +535,9 @@ lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
> continue;
> }
>
> - if (!consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts,
> - &nd_ra_opts, &controller_event_opts,
> - l_ctx_in, l_ctx_out)) {
> - ret = false;
> - l_ctx_out->conj_id_overflow = true;
> - break;
> - }
> + consider_logical_flow(lflow, &dhcp_opts, &dhcpv6_opts,
> + &nd_ra_opts, &controller_event_opts,
> + l_ctx_in, l_ctx_out);
> *changed = true;
> }
> HMAP_FOR_EACH_SAFE (ofrn, ofrn_next, hmap_node, &flood_remove_nodes) {
> @@ -568,17 +559,6 @@ lflow_handle_changed_ref(enum ref_type ref_type, const char *ref_name,
> return ret;
> }
>
> -static bool
> -update_conj_id_ofs(uint32_t *conj_id_ofs, uint32_t n_conjs)
> -{
> - if (*conj_id_ofs + n_conjs < *conj_id_ofs) {
> - /* overflow */
> - return true;
> - }
> - *conj_id_ofs += n_conjs;
> - return false;
> -}
> -
> static void
> lflow_parse_ctrl_meter(const struct sbrec_logical_flow *lflow,
> struct ovn_extend_table *meter_table,
> @@ -762,7 +742,7 @@ convert_match_to_expr(const struct sbrec_logical_flow *lflow,
> return expr_simplify(e);
> }
>
> -static bool
> +static void
> consider_logical_flow__(const struct sbrec_logical_flow *lflow,
> const struct sbrec_datapath_binding *dp,
> struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
> @@ -774,7 +754,7 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
> if (!get_local_datapath(l_ctx_in->local_datapaths, dp->tunnel_key)) {
> VLOG_DBG("lflow "UUID_FMT" is not for local datapath, skip",
> UUID_ARGS(&lflow->header_.uuid));
> - return true;
> + return;
> }
>
> const char *io_port = smap_get(&lflow->tags, "in_out_port");
> @@ -787,14 +767,14 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
> if (!pb) {
> VLOG_DBG("lflow "UUID_FMT" matches inport/outport %s that's not "
> "found, skip", UUID_ARGS(&lflow->header_.uuid), io_port);
> - return true;
> + return;
> }
> char buf[16];
> get_unique_lport_key(dp->tunnel_key, pb->tunnel_key, buf, sizeof buf);
> if (!sset_contains(l_ctx_in->related_lport_ids, buf)) {
> VLOG_DBG("lflow "UUID_FMT" matches inport/outport %s that's not "
> "local, skip", UUID_ARGS(&lflow->header_.uuid), io_port);
> - return true;
> + return;
> }
> }
>
> @@ -837,7 +817,7 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
> free(error);
> ovnacts_free(ovnacts.data, ovnacts.size);
> ofpbuf_uninit(&ovnacts);
> - return true;
> + return;
> }
>
> struct lookup_port_aux aux = {
> @@ -859,8 +839,6 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
>
> struct lflow_cache_value *lcv =
> lflow_cache_get(l_ctx_out->lflow_cache, &lflow->header_.uuid);
> - uint32_t conj_id_ofs =
> - lcv ? lcv->conj_id_ofs : *l_ctx_out->conj_id_ofs;
> enum lflow_cache_type lcv_type =
> lcv ? lcv->type : LCACHE_T_NONE;
>
> @@ -869,9 +847,19 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
> size_t matches_size = 0;
>
> bool pg_addr_set_ref = false;
> - uint32_t n_conjs = 0;
>
> - bool conj_id_overflow = false;
> + if (lcv_type == LCACHE_T_MATCHES
> + && lcv->n_conjs
> + && !lflow_conj_ids_alloc_specified(l_ctx_out->conj_ids,
> + &lflow->header_.uuid,
> + lcv->conj_id_ofs, lcv->n_conjs)) {
> + /* This should happen very rarely. */
> + VLOG_DBG("lflow "UUID_FMT" match cached with conjunctions, but the"
> + " cached ids are not available anymore. Drop the cache.",
> + UUID_ARGS(&lflow->header_.uuid));
> + lflow_cache_delete(l_ctx_out->lflow_cache, &lflow->header_.uuid);
> + lcv_type = LCACHE_T_NONE;
> + }
>
> /* Get match expr, either from cache or from lflow match. */
> switch (lcv_type) {
> @@ -912,17 +900,28 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
> }
>
> /* Get matches, either from cache or from expr computed above. */
> + uint32_t start_conj_id = 0;
> + uint32_t n_conjs = 0;
> switch (lcv_type) {
> case LCACHE_T_NONE:
> case LCACHE_T_EXPR:
> matches = xmalloc(sizeof *matches);
> n_conjs = expr_to_matches(expr, lookup_port_cb, &aux, matches);
> - matches_size = expr_matches_prepare(matches, conj_id_ofs);
> if (hmap_is_empty(matches)) {
> VLOG_DBG("lflow "UUID_FMT" matches are empty, skip",
> - UUID_ARGS(&lflow->header_.uuid));
> + UUID_ARGS(&lflow->header_.uuid));
> goto done;
> }
> + if (n_conjs) {
> + start_conj_id = lflow_conj_ids_alloc(l_ctx_out->conj_ids,
> + &lflow->header_.uuid,
> + n_conjs);
> + if (!start_conj_id) {
> + VLOG_ERR("32-bit conjunction ids exhausted!");
> + goto done;
> + }
> + matches_size = expr_matches_prepare(matches, start_conj_id - 1);
> + }
> break;
> case LCACHE_T_MATCHES:
> matches = lcv->expr_matches;
> @@ -935,23 +934,18 @@ consider_logical_flow__(const struct sbrec_logical_flow *lflow,
> /* Update cache if needed. */
> switch (lcv_type) {
> case LCACHE_T_NONE:
> - /* Entry not already in cache, update conjunction id offset and
> - * add the entry to the cache.
> - */
> - conj_id_overflow = update_conj_id_ofs(l_ctx_out->conj_id_ofs, n_conjs);
> -
> /* Cache new entry if caching is enabled. */
> if (lflow_cache_is_enabled(l_ctx_out->lflow_cache)) {
> if (cached_expr
> && !lflow_ref_lookup(&l_ctx_out->lfrr->lflow_ref_table,
> &lflow->header_.uuid)) {
> lflow_cache_add_matches(l_ctx_out->lflow_cache,
> - &lflow->header_.uuid, matches,
> - matches_size);
> + &lflow->header_.uuid, start_conj_id,
> + n_conjs, matches, matches_size);
> matches = NULL;
> } else if (cached_expr) {
> lflow_cache_add_expr(l_ctx_out->lflow_cache,
> - &lflow->header_.uuid, conj_id_ofs,
> + &lflow->header_.uuid,
> cached_expr, expr_size(cached_expr));
> cached_expr = NULL;
> }
> @@ -973,10 +967,9 @@ done:
> expr_destroy(cached_expr);
> expr_matches_destroy(matches);
> free(matches);
> - return !conj_id_overflow;
> }
>
> -static bool
> +static void
> consider_logical_flow(const struct sbrec_logical_flow *lflow,
> struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
> struct hmap *nd_ra_opts,
> @@ -986,30 +979,27 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow,
> {
> const struct sbrec_logical_dp_group *dp_group = lflow->logical_dp_group;
> const struct sbrec_datapath_binding *dp = lflow->logical_datapath;
> - bool ret = true;
>
> if (!dp_group && !dp) {
> VLOG_DBG("lflow "UUID_FMT" has no datapath binding, skip",
> UUID_ARGS(&lflow->header_.uuid));
> - return true;
> + return;
> }
> ovs_assert(!dp_group || !dp);
>
> - if (dp && !consider_logical_flow__(lflow, dp,
> - dhcp_opts, dhcpv6_opts, nd_ra_opts,
> - controller_event_opts,
> - l_ctx_in, l_ctx_out)) {
> - ret = false;
> + if (dp) {
> + consider_logical_flow__(lflow, dp,
> + dhcp_opts, dhcpv6_opts, nd_ra_opts,
> + controller_event_opts,
> + l_ctx_in, l_ctx_out);
> + return;
> }
> for (size_t i = 0; dp_group && i < dp_group->n_datapaths; i++) {
> - if (!consider_logical_flow__(lflow, dp_group->datapaths[i],
> - dhcp_opts, dhcpv6_opts, nd_ra_opts,
> - controller_event_opts,
> - l_ctx_in, l_ctx_out)) {
> - ret = false;
> - }
> + consider_logical_flow__(lflow, dp_group->datapaths[i],
> + dhcp_opts, dhcpv6_opts, nd_ra_opts,
> + controller_event_opts,
> + l_ctx_in, l_ctx_out);
> }
> - return ret;
> }
>
> static void
> @@ -1933,13 +1923,9 @@ lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *dp,
> const struct sbrec_logical_flow *lflow;
> SBREC_LOGICAL_FLOW_FOR_EACH_EQUAL (
> lflow, lf_row, l_ctx_in->sbrec_logical_flow_by_logical_datapath) {
> - if (!consider_logical_flow__(lflow, dp, &dhcp_opts, &dhcpv6_opts,
> - &nd_ra_opts, &controller_event_opts,
> - l_ctx_in, l_ctx_out)) {
> - handled = false;
> - l_ctx_out->conj_id_overflow = true;
> - goto lflow_processing_end;
> - }
> + consider_logical_flow__(lflow, dp, &dhcp_opts, &dhcpv6_opts,
> + &nd_ra_opts, &controller_event_opts,
> + l_ctx_in, l_ctx_out);
> }
> sbrec_logical_flow_index_destroy_row(lf_row);
>
> @@ -1963,16 +1949,11 @@ lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *dp,
> sbrec_logical_flow_index_set_logical_dp_group(lf_row, ldpg);
> SBREC_LOGICAL_FLOW_FOR_EACH_EQUAL (
> lflow, lf_row, l_ctx_in->sbrec_logical_flow_by_logical_dp_group) {
> - if (!consider_logical_flow__(lflow, dp, &dhcp_opts, &dhcpv6_opts,
> - &nd_ra_opts, &controller_event_opts,
> - l_ctx_in, l_ctx_out)) {
> - handled = false;
> - l_ctx_out->conj_id_overflow = true;
> - goto lflow_processing_end;
> - }
> + consider_logical_flow__(lflow, dp, &dhcp_opts, &dhcpv6_opts,
> + &nd_ra_opts, &controller_event_opts,
> + l_ctx_in, l_ctx_out);
> }
> }
> -lflow_processing_end:
> sbrec_logical_flow_index_destroy_row(lf_row);
>
> dhcp_opts_destroy(&dhcp_opts);
> diff --git a/controller/lflow.h b/controller/lflow.h
> index 61ffbc0cc..b2f3385e5 100644
> --- a/controller/lflow.h
> +++ b/controller/lflow.h
> @@ -35,6 +35,7 @@
>
> #include <stdint.h>
> #include "lflow-cache.h"
> +#include "lflow-conj-ids.h"
> #include "openvswitch/hmap.h"
> #include "openvswitch/uuid.h"
> #include "openvswitch/list.h"
> @@ -156,8 +157,7 @@ struct lflow_ctx_out {
> struct ovn_extend_table *meter_table;
> struct lflow_resource_ref *lfrr;
> struct lflow_cache *lflow_cache;
> - uint32_t *conj_id_ofs;
> - bool conj_id_overflow;
> + struct conj_ids *conj_ids;
> struct simap *hairpin_lb_ids;
> struct id_pool *hairpin_id_pool;
> };
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index f223e0f09..661e51f0b 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -39,6 +39,7 @@
> #include "openvswitch/hmap.h"
> #include "lflow.h"
> #include "lflow-cache.h"
> +#include "lflow-conj-ids.h"
> #include "lib/vswitch-idl.h"
> #include "local_data.h"
> #include "lport.h"
> @@ -2150,7 +2151,6 @@ non_vif_data_ovs_iface_handler(struct engine_node *node, void *data OVS_UNUSED)
> }
>
> struct lflow_output_persistent_data {
> - uint32_t conj_id_ofs;
> struct lflow_cache *lflow_cache;
> };
>
> @@ -2168,6 +2168,8 @@ struct ed_type_lflow_output {
> struct ovn_extend_table meter_table;
> /* lflow resource cross reference */
> struct lflow_resource_ref lflow_resource_ref;
> + /* conjunciton ID usage information of lflows */
> + struct conj_ids conj_ids;
>
> /* Data which is persistent and not cleared during
> * full recompute. */
> @@ -2293,9 +2295,8 @@ init_lflow_ctx(struct engine_node *node,
> l_ctx_out->group_table = &fo->group_table;
> l_ctx_out->meter_table = &fo->meter_table;
> l_ctx_out->lfrr = &fo->lflow_resource_ref;
> - l_ctx_out->conj_id_ofs = &fo->pd.conj_id_ofs;
> + l_ctx_out->conj_ids = &fo->conj_ids;
> l_ctx_out->lflow_cache = fo->pd.lflow_cache;
> - l_ctx_out->conj_id_overflow = false;
> l_ctx_out->hairpin_id_pool = fo->hd.pool;
> l_ctx_out->hairpin_lb_ids = &fo->hd.ids;
> }
> @@ -2308,8 +2309,8 @@ en_lflow_output_init(struct engine_node *node OVS_UNUSED,
> ovn_desired_flow_table_init(&data->flow_table);
> ovn_extend_table_init(&data->group_table);
> ovn_extend_table_init(&data->meter_table);
> - data->pd.conj_id_ofs = 1;
> lflow_resource_init(&data->lflow_resource_ref);
> + lflow_conj_ids_init(&data->conj_ids);
> simap_init(&data->hd.ids);
> data->hd.pool = id_pool_create(1, UINT32_MAX - 1);
> return data;
> @@ -2323,6 +2324,7 @@ en_lflow_output_cleanup(void *data)
> ovn_extend_table_destroy(&flow_output_data->group_table);
> ovn_extend_table_destroy(&flow_output_data->meter_table);
> lflow_resource_destroy(&flow_output_data->lflow_resource_ref);
> + lflow_conj_ids_destroy(&flow_output_data->conj_ids);
> lflow_cache_destroy(flow_output_data->pd.lflow_cache);
> simap_destroy(&flow_output_data->hd.ids);
> id_pool_destroy(flow_output_data->hd.pool);
> @@ -2371,37 +2373,18 @@ en_lflow_output_run(struct engine_node *node, void *data)
> ovn_extend_table_clear(group_table, false /* desired */);
> ovn_extend_table_clear(meter_table, false /* desired */);
> lflow_resource_clear(lfrr);
> + lflow_conj_ids_clear(&fo->conj_ids);
> }
>
> struct controller_engine_ctx *ctrl_ctx = engine_get_context()->client_ctx;
>
> fo->pd.lflow_cache = ctrl_ctx->lflow_cache;
>
> - if (!lflow_cache_is_enabled(fo->pd.lflow_cache)) {
> - fo->pd.conj_id_ofs = 1;
> - }
> -
> struct lflow_ctx_in l_ctx_in;
> struct lflow_ctx_out l_ctx_out;
> init_lflow_ctx(node, rt_data, non_vif_data, fo, &l_ctx_in, &l_ctx_out);
> lflow_run(&l_ctx_in, &l_ctx_out);
>
> - if (l_ctx_out.conj_id_overflow) {
> - /* Conjunction ids overflow. There can be many holes in between.
> - * Destroy lflow cache and call lflow_run() again. */
> - ovn_desired_flow_table_clear(lflow_table);
> - ovn_extend_table_clear(group_table, false /* desired */);
> - ovn_extend_table_clear(meter_table, false /* desired */);
> - lflow_resource_clear(lfrr);
> - fo->pd.conj_id_ofs = 1;
> - lflow_cache_flush(fo->pd.lflow_cache);
> - l_ctx_out.conj_id_overflow = false;
> - lflow_run(&l_ctx_in, &l_ctx_out);
> - if (l_ctx_out.conj_id_overflow) {
> - VLOG_WARN("Conjunction id overflow.");
> - }
> - }
> -
> engine_set_node_state(node, EN_UPDATED);
> }
>
> @@ -4158,7 +4141,6 @@ lflow_cache_flush_cmd(struct unixctl_conn *conn OVS_UNUSED,
> VLOG_INFO("User triggered lflow cache flush.");
> struct lflow_output_persistent_data *fo_pd = arg_;
> lflow_cache_flush(fo_pd->lflow_cache);
> - fo_pd->conj_id_ofs = 1;
> engine_set_force_recompute(true);
> poll_immediate_wake();
> unixctl_command_reply(conn, NULL);
> diff --git a/controller/test-lflow-cache.c b/controller/test-lflow-cache.c
> index 95e619ee0..6f7a3c389 100644
> --- a/controller/test-lflow-cache.c
> +++ b/controller/test-lflow-cache.c
> @@ -36,19 +36,22 @@ static void
> test_lflow_cache_add__(struct lflow_cache *lc, const char *op_type,
> const struct uuid *lflow_uuid,
> unsigned int conj_id_ofs,
> + unsigned int n_conjs,
> struct expr *e)
> {
> printf("ADD %s:\n", op_type);
> printf(" conj-id-ofs: %u\n", conj_id_ofs);
> + printf(" n_conjs: %u\n", n_conjs);
>
> if (!strcmp(op_type, "expr")) {
> - lflow_cache_add_expr(lc, lflow_uuid, conj_id_ofs, expr_clone(e),
> + lflow_cache_add_expr(lc, lflow_uuid, expr_clone(e),
> TEST_LFLOW_CACHE_VALUE_SIZE);
> } else if (!strcmp(op_type, "matches")) {
> struct hmap *matches = xmalloc(sizeof *matches);
> ovs_assert(expr_to_matches(e, NULL, NULL, matches) == 0);
> ovs_assert(hmap_count(matches) == 1);
> - lflow_cache_add_matches(lc, lflow_uuid, matches,
> + lflow_cache_add_matches(lc, lflow_uuid,
> + conj_id_ofs, n_conjs, matches,
> TEST_LFLOW_CACHE_VALUE_SIZE);
> } else {
> OVS_NOT_REACHED();
> @@ -68,6 +71,7 @@ test_lflow_cache_lookup__(struct lflow_cache *lc,
> }
>
> printf(" conj_id_ofs: %"PRIu32"\n", lcv->conj_id_ofs);
> + printf(" n_conjs: %"PRIu32"\n", lcv->n_conjs);
> switch (lcv->type) {
> case LCACHE_T_EXPR:
> printf(" type: expr\n");
> @@ -139,6 +143,12 @@ test_lflow_cache_operations(struct ovs_cmdl_context *ctx)
> goto done;
> }
>
> + unsigned int n_conjs;
> + if (!test_read_uint_value(ctx, shift++, "n_conjs",
> + &n_conjs)) {
> + goto done;
> + }
> +
> if (n_lflow_uuids == n_allocated_lflow_uuids) {
> lflow_uuids = x2nrealloc(lflow_uuids, &n_allocated_lflow_uuids,
> sizeof *lflow_uuids);
> @@ -146,7 +156,8 @@ test_lflow_cache_operations(struct ovs_cmdl_context *ctx)
> struct uuid *lflow_uuid = &lflow_uuids[n_lflow_uuids++];
>
> uuid_generate(lflow_uuid);
> - test_lflow_cache_add__(lc, op_type, lflow_uuid, conj_id_ofs, e);
> + test_lflow_cache_add__(lc, op_type, lflow_uuid, conj_id_ofs,
> + n_conjs, e);
> test_lflow_cache_lookup__(lc, lflow_uuid);
> } else if (!strcmp(op, "add-del")) {
> const char *op_type = test_read_value(ctx, shift++, "op_type");
> @@ -160,9 +171,16 @@ test_lflow_cache_operations(struct ovs_cmdl_context *ctx)
> goto done;
> }
>
> + unsigned int n_conjs;
> + if (!test_read_uint_value(ctx, shift++, "n_conjs",
> + &n_conjs)) {
> + goto done;
> + }
> +
> struct uuid lflow_uuid;
> uuid_generate(&lflow_uuid);
> - test_lflow_cache_add__(lc, op_type, &lflow_uuid, conj_id_ofs, e);
> + test_lflow_cache_add__(lc, op_type, &lflow_uuid, conj_id_ofs,
> + n_conjs, e);
> test_lflow_cache_lookup__(lc, &lflow_uuid);
> test_lflow_cache_delete__(lc, &lflow_uuid);
> test_lflow_cache_lookup__(lc, &lflow_uuid);
> @@ -246,10 +264,10 @@ test_lflow_cache_negative(struct ovs_cmdl_context *ctx OVS_UNUSED)
> ovs_assert(expr_to_matches(e, NULL, NULL, matches) == 0);
> ovs_assert(hmap_count(matches) == 1);
>
> - lflow_cache_add_expr(lcs[i], NULL, 0, NULL, 0);
> - lflow_cache_add_expr(lcs[i], NULL, 0, e, expr_size(e));
> - lflow_cache_add_matches(lcs[i], NULL, NULL, 0);
> - lflow_cache_add_matches(lcs[i], NULL, matches,
> + lflow_cache_add_expr(lcs[i], NULL, NULL, 0);
> + lflow_cache_add_expr(lcs[i], NULL, e, expr_size(e));
> + lflow_cache_add_matches(lcs[i], NULL, 0, 0, NULL, 0);
> + lflow_cache_add_matches(lcs[i], NULL, 0, 0, matches,
> TEST_LFLOW_CACHE_VALUE_SIZE);
> lflow_cache_destroy(lcs[i]);
> }
> @@ -260,7 +278,7 @@ test_lflow_cache_main(int argc, char *argv[])
> {
> set_program_name(argv[0]);
> static const struct ovs_cmdl_command commands[] = {
> - {"lflow_cache_operations", NULL, 3, INT_MAX,
> + {"lflow_cache_operations", NULL, 4, INT_MAX,
> test_lflow_cache_operations, OVS_RO},
> {"lflow_cache_negative", NULL, 0, 0,
> test_lflow_cache_negative, OVS_RO},
> diff --git a/controller/test-lflow-conj-ids.c b/controller/test-lflow-conj-ids.c
> new file mode 100644
> index 000000000..1273f9a4c
> --- /dev/null
> +++ b/controller/test-lflow-conj-ids.c
> @@ -0,0 +1,128 @@
> +/*
> + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "tests/ovstest.h"
> +#include "tests/test-utils.h"
> +#include "util.h"
> +#include "lib/uuid.h"
> +
> +#include "lflow-conj-ids.h"
> +
> +static bool
> +parse_lflow_uuid(struct ovs_cmdl_context *ctx, unsigned int shift,
> + struct uuid *uuid)
> +{
> + const char *uuid_s = test_read_value(ctx, shift++, "lflow_uuid");
> + if (!uuid_s) {
> + return false;
> + }
> + if (!uuid_from_string(uuid, uuid_s)) {
> + printf("Expected uuid, got %s.\n", uuid_s);
> + return false;
> + }
> + return true;
> +}
> +
> +static void
> +test_conj_ids_operations(struct ovs_cmdl_context *ctx)
> +{
> + unsigned int shift = 1;
> + unsigned int n_ops;
> + struct conj_ids conj_ids;
> + lflow_conj_ids_init(&conj_ids);
> +
> + if (!test_read_uint_value(ctx, shift++, "n_ops", &n_ops)) {
> + goto done;
> + }
> +
> + for (unsigned int i = 0; i < n_ops; i++) {
> + const char *op = test_read_value(ctx, shift++, "op");
> +
> + if (!op) {
> + goto done;
> + }
> +
> + if (!strcmp(op, "alloc")) {
> + struct uuid uuid;
> + if (!parse_lflow_uuid(ctx, shift++, &uuid)) {
> + goto done;
> + }
> +
> + unsigned int n_conjs;
> + if (!test_read_uint_value(ctx, shift++, "n_conjs", &n_conjs)) {
> + goto done;
> + }
> +
> + uint32_t start_conj_id = lflow_conj_ids_alloc(&conj_ids, &uuid,
> + n_conjs);
> + printf("alloc("UUID_FMT", %"PRIu32"): 0x%"PRIx32"\n",
> + UUID_ARGS(&uuid), n_conjs, start_conj_id);
> + } else if (!strcmp(op, "alloc-specified")) {
> + struct uuid uuid;
> + if (!parse_lflow_uuid(ctx, shift++, &uuid)) {
> + goto done;
> + }
> +
> + unsigned int start_conj_id;
> + if (!test_read_uint_hex_value(ctx, shift++, "start_conj_id",
> + &start_conj_id)) {
> + goto done;
> + }
> +
> + unsigned int n_conjs;
> + if (!test_read_uint_value(ctx, shift++, "n_conjs", &n_conjs)) {
> + goto done;
> + }
> +
> + bool ret = lflow_conj_ids_alloc_specified(&conj_ids, &uuid,
> + start_conj_id, n_conjs);
> + printf("alloc_specified("UUID_FMT", 0x%"PRIx32", %"PRIu32"): %s\n",
> + UUID_ARGS(&uuid), start_conj_id, n_conjs,
> + ret ? "true" : "false");
> + } else if (!strcmp(op, "free")) {
> + struct uuid uuid;
> + if (!parse_lflow_uuid(ctx, shift++, &uuid)) {
> + goto done;
> + }
> + lflow_conj_ids_free(&conj_ids, &uuid);
> + printf("free("UUID_FMT")\n", UUID_ARGS(&uuid));
> + } else {
> + printf("Unknown operation: %s\n", op);
> + goto done;
> + }
> + }
> +done:
> + lflow_conj_ids_destroy(&conj_ids);
> +}
> +
> +static void
> +test_lflow_conj_ids_main(int argc, char *argv[])
> +{
> + set_program_name(argv[0]);
> + static const struct ovs_cmdl_command commands[] = {
> + {"operations", NULL, 1, INT_MAX,
> + test_conj_ids_operations, OVS_RO},
> + {NULL, NULL, 0, 0, NULL, OVS_RO},
> + };
> + struct ovs_cmdl_context ctx;
> + ctx.argc = argc - 1;
> + ctx.argv = argv + 1;
> + ovs_cmdl_run_command(&ctx, commands);
> +}
> +
> +OVSTEST_REGISTER("test-lflow-conj-ids", test_lflow_conj_ids_main);
> diff --git a/tests/automake.mk b/tests/automake.mk
> index 685d78c5b..a08dfa632 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -38,6 +38,7 @@ TESTSUITE_AT = \
> tests/ovn-ipam.at \
> tests/ovn-features.at \
> tests/ovn-lflow-cache.at \
> + tests/ovn-lflow-conj-ids.at \
> tests/ovn-ipsec.at \
> tests/ovn-vif-plug.at
>
> @@ -243,6 +244,7 @@ tests_ovstest_SOURCES = \
> tests/test-utils.h \
> tests/test-ovn.c \
> controller/test-lflow-cache.c \
> + controller/test-lflow-conj-ids.c \
> controller/test-ofctrl-seqno.c \
> controller/test-vif-plug.c \
> lib/test-ovn-features.c \
> @@ -255,6 +257,7 @@ tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \
> controller/ha-chassis.$(OBJEXT) \
> controller/if-status.$(OBJEXT) \
> controller/lflow-cache.$(OBJEXT) \
> + controller/lflow-conj-ids.$(OBJEXT) \
> controller/local_data.$(OBJEXT) \
> controller/lport.$(OBJEXT) \
> controller/ofctrl-seqno.$(OBJEXT) \
> diff --git a/tests/ovn-lflow-cache.at b/tests/ovn-lflow-cache.at
> index 97d24d0dc..593d3eaac 100644
> --- a/tests/ovn-lflow-cache.at
> +++ b/tests/ovn-lflow-cache.at
> @@ -7,8 +7,8 @@ AT_SETUP([unit test -- lflow-cache single add/lookup])
> AT_CHECK(
> [ovstest test-lflow-cache lflow_cache_operations \
> true 2 \
> - add expr 2 \
> - add matches 3 | grep -v 'Mem usage (KB)'],
> + add expr 2 1 \
> + add matches 3 2 | grep -v 'Mem usage (KB)'],
> [0], [dnl
> Enabled: true
> high-watermark : 0
> @@ -18,8 +18,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 2
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 2
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -29,8 +31,10 @@ cache-matches : 0
> trim count : 0
> ADD matches:
> conj-id-ofs: 3
> + n_conjs: 2
> LOOKUP:
> - conj_id_ofs: 0
> + conj_id_ofs: 3
> + n_conjs: 2
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -45,8 +49,8 @@ AT_SETUP([unit test -- lflow-cache single add/lookup/del])
> AT_CHECK(
> [ovstest test-lflow-cache lflow_cache_operations \
> true 2 \
> - add-del expr 2 \
> - add-del matches 3 | grep -v 'Mem usage (KB)'],
> + add-del expr 2 1 \
> + add-del matches 3 1 | grep -v 'Mem usage (KB)'],
> [0], [dnl
> Enabled: true
> high-watermark : 0
> @@ -56,8 +60,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 2
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 2
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> DELETE
> LOOKUP:
> @@ -70,8 +76,10 @@ cache-matches : 0
> trim count : 0
> ADD matches:
> conj-id-ofs: 3
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 0
> + conj_id_ofs: 3
> + n_conjs: 1
> type: matches
> DELETE
> LOOKUP:
> @@ -89,8 +97,8 @@ AT_SETUP([unit test -- lflow-cache disabled single add/lookup/del])
> AT_CHECK(
> [ovstest test-lflow-cache lflow_cache_operations \
> false 2 \
> - add expr 2 \
> - add matches 3 | grep -v 'Mem usage (KB)'],
> + add expr 2 1 \
> + add matches 3 1 | grep -v 'Mem usage (KB)'],
> [0], [dnl
> Enabled: false
> high-watermark : 0
> @@ -100,6 +108,7 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 2
> + n_conjs: 1
> LOOKUP:
> not found
> Enabled: false
> @@ -110,6 +119,7 @@ cache-matches : 0
> trim count : 0
> ADD matches:
> conj-id-ofs: 3
> + n_conjs: 1
> LOOKUP:
> not found
> Enabled: false
> @@ -125,14 +135,14 @@ AT_SETUP([unit test -- lflow-cache disable/enable/flush])
> AT_CHECK(
> [ovstest test-lflow-cache lflow_cache_operations \
> true 9 \
> - add expr 2 \
> - add matches 3 \
> + add expr 2 1 \
> + add matches 3 1 \
> disable \
> - add expr 5 \
> - add matches 6 \
> + add expr 5 1 \
> + add matches 6 1 \
> enable 1000 1024 \
> - add expr 8 \
> - add matches 9 \
> + add expr 8 1 \
> + add matches 9 1 \
> flush | grep -v 'Mem usage (KB)'],
> [0], [dnl
> Enabled: true
> @@ -143,8 +153,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 2
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 2
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -154,8 +166,10 @@ cache-matches : 0
> trim count : 0
> ADD matches:
> conj-id-ofs: 3
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 0
> + conj_id_ofs: 3
> + n_conjs: 1
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -173,6 +187,7 @@ dnl At "disable" the cache was flushed.
> trim count : 1
> ADD expr:
> conj-id-ofs: 5
> + n_conjs: 1
> LOOKUP:
> not found
> Enabled: false
> @@ -183,6 +198,7 @@ cache-matches : 0
> trim count : 1
> ADD matches:
> conj-id-ofs: 6
> + n_conjs: 1
> LOOKUP:
> not found
> Enabled: false
> @@ -200,8 +216,10 @@ cache-matches : 0
> trim count : 1
> ADD expr:
> conj-id-ofs: 8
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 8
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -211,8 +229,10 @@ cache-matches : 0
> trim count : 1
> ADD matches:
> conj-id-ofs: 9
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 0
> + conj_id_ofs: 9
> + n_conjs: 1
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -234,15 +254,15 @@ AT_SETUP([unit test -- lflow-cache set limit])
> AT_CHECK(
> [ovstest test-lflow-cache lflow_cache_operations \
> true 9 \
> - add expr 2 \
> - add matches 3 \
> + add expr 2 1 \
> + add matches 3 1 \
> enable 1 1024 \
> - add expr 5 \
> - add matches 6 \
> - add expr 7 \
> + add expr 5 1 \
> + add matches 6 1 \
> + add expr 7 1 \
> enable 1 1 \
> - add expr 9 \
> - add matches 10 | grep -v 'Mem usage (KB)'],
> + add expr 9 1 \
> + add matches 10 1 | grep -v 'Mem usage (KB)'],
> [0], [dnl
> Enabled: true
> high-watermark : 0
> @@ -252,8 +272,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 2
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 2
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -263,8 +285,10 @@ cache-matches : 0
> trim count : 0
> ADD matches:
> conj-id-ofs: 3
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 0
> + conj_id_ofs: 3
> + n_conjs: 1
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -284,8 +308,10 @@ cache-matches : 0
> trim count : 1
> ADD expr:
> conj-id-ofs: 5
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 5
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -295,8 +321,10 @@ cache-matches : 0
> trim count : 1
> ADD matches:
> conj-id-ofs: 6
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 0
> + conj_id_ofs: 6
> + n_conjs: 1
> type: matches
> dnl
> dnl Cache is full but we can evict the expr entry because we're adding
> @@ -310,6 +338,7 @@ cache-matches : 1
> trim count : 1
> ADD expr:
> conj-id-ofs: 7
> + n_conjs: 1
> LOOKUP:
> not found
> dnl
> @@ -335,6 +364,7 @@ cache-matches : 0
> trim count : 2
> ADD expr:
> conj-id-ofs: 9
> + n_conjs: 1
> LOOKUP:
> not found
> dnl
> @@ -349,6 +379,7 @@ cache-matches : 0
> trim count : 2
> ADD matches:
> conj-id-ofs: 10
> + n_conjs: 1
> LOOKUP:
> not found
> dnl
> @@ -369,11 +400,11 @@ AT_CHECK(
> [ovstest test-lflow-cache lflow_cache_operations \
> true 12 \
> enable 1000 1024 trim-limit 100 trim-wmark-perc 100 \
> - add expr 1 \
> - add expr 2 \
> - add expr 3 \
> - add expr 4 \
> - add expr 5 \
> + add expr 1 1 \
> + add expr 2 1 \
> + add expr 3 1 \
> + add expr 4 1 \
> + add expr 5 1 \
> del \
> enable 1000 1024 trim-limit 0 trim-wmark-perc 100 \
> del \
> @@ -396,8 +427,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 1
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 1
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -407,8 +440,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 2
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 2
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 2
> @@ -418,8 +453,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 3
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 3
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 3
> @@ -429,8 +466,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 4
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 4
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 4
> @@ -440,8 +479,10 @@ cache-matches : 0
> trim count : 0
> ADD expr:
> conj-id-ofs: 5
> + n_conjs: 1
> LOOKUP:
> - conj_id_ofs: 5
> + conj_id_ofs: 0
> + n_conjs: 0
> type: expr
> Enabled: true
> high-watermark : 5
> diff --git a/tests/ovn-lflow-conj-ids.at b/tests/ovn-lflow-conj-ids.at
> new file mode 100644
> index 000000000..818d67324
> --- /dev/null
> +++ b/tests/ovn-lflow-conj-ids.at
> @@ -0,0 +1,112 @@
> +#
> +# Unit tests for the controller/lflow-conj-ids.c module.
> +#
> +AT_BANNER([OVN unit tests - lflow-conj-ids])
> +
> +AT_SETUP([unit test -- lflow-conj-ids basic-alloc])
> +
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 3 \
> + alloc aaaaaaaa-1111-1111-1111-111111111111 10 \
> + alloc bbbbbbbb-1111-1111-1111-111111111111 10 \
> + alloc cccccccc-1111-1111-1111-111111111111 10],
> + [0], [dnl
> +alloc(aaaaaaaa-1111-1111-1111-111111111111, 10): 0xaaaaaaaa
> +alloc(bbbbbbbb-1111-1111-1111-111111111111, 10): 0xbbbbbbbb
> +alloc(cccccccc-1111-1111-1111-111111111111, 10): 0xcccccccc
> +])
> +
> +AT_CLEANUP
> +
> +AT_SETUP([unit test -- lflow-conj-ids alloc-with-conflict])
> +
> +# Conflict of the same prefix
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 2 \
> + alloc aaaaaaaa-1111-1111-1111-111111111111 1 \
> + alloc aaaaaaaa-2222-1111-1111-111111111111 1],
> + [0], [dnl
> +alloc(aaaaaaaa-1111-1111-1111-111111111111, 1): 0xaaaaaaaa
> +alloc(aaaaaaaa-2222-1111-1111-111111111111, 1): 0xaaaaaaab
> +])
> +
> +# Conflict of the different prefix but overlapping range, the second allocation
> +# should get the next available slot.
> +# Free the first range, then a new allocation should get the uuid prefix.
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 4 \
> + alloc aaaaaaaa-1111-1111-1111-111111111111 16 \
> + alloc aaaaaaab-1111-1111-1111-111111111111 1 \
> + free aaaaaaaa-1111-1111-1111-111111111111 \
> + alloc aaaaaaab-2222-1111-1111-111111111111 1],
> + [0], [dnl
> +alloc(aaaaaaaa-1111-1111-1111-111111111111, 16): 0xaaaaaaaa
> +alloc(aaaaaaab-1111-1111-1111-111111111111, 1): 0xaaaaaaba
> +free(aaaaaaaa-1111-1111-1111-111111111111)
> +alloc(aaaaaaab-2222-1111-1111-111111111111, 1): 0xaaaaaaab
> +])
> +
> +# Conflict at the tail of the range.
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 2 \
> + alloc aaaaaaaa-1111-1111-1111-111111111111 1 \
> + alloc aaaaaaa0-1111-1111-1111-111111111111 11],
> + [0], [dnl
> +alloc(aaaaaaaa-1111-1111-1111-111111111111, 1): 0xaaaaaaaa
> +alloc(aaaaaaa0-1111-1111-1111-111111111111, 11): 0xaaaaaaab
> +])
> +
> +# Realloc for the same lflow should get the same id, with the old allocations
> +# freeed (this is not supposed to be used but implemented for safety)
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 3 \
> + alloc aaaaaaaa-1111-1111-1111-111111111111 16 \
> + alloc aaaaaaaa-1111-1111-1111-111111111111 1 \
> + alloc aaaaaaab-1111-1111-1111-111111111111 1],
> + [0], [dnl
> +alloc(aaaaaaaa-1111-1111-1111-111111111111, 16): 0xaaaaaaaa
> +alloc(aaaaaaaa-1111-1111-1111-111111111111, 1): 0xaaaaaaaa
> +alloc(aaaaaaab-1111-1111-1111-111111111111, 1): 0xaaaaaaab
> +])
> +
> +AT_CLEANUP
> +
> +AT_SETUP([unit test -- lflow-conj-ids rewind])
> +
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 3 \
> + alloc ffffffff-1111-1111-1111-111111111111 2 \
> + free ffffffff-1111-1111-1111-111111111111 \
> + alloc 00000000-2222-1111-1111-111111111111 1],
> + [0], [dnl
> +alloc(ffffffff-1111-1111-1111-111111111111, 2): 0x1
> +free(ffffffff-1111-1111-1111-111111111111)
> +alloc(00000000-2222-1111-1111-111111111111, 1): 0x1
> +])
> +
> +AT_CLEANUP
> +
> +AT_SETUP([unit test -- lflow-conj-ids alloc-specified])
> +
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 4 \
> + alloc 00000001-1111-1111-1111-111111111111 16 \
> + alloc-specified 0000000a-1111-1111-1111-111111111111 0xa 1 \
> + free 00000001-1111-1111-1111-111111111111 \
> + alloc-specified 0000000a-1111-1111-1111-111111111111 0xa 1],
> + [0], [dnl
> +alloc(00000001-1111-1111-1111-111111111111, 16): 0x1
> +alloc_specified(0000000a-1111-1111-1111-111111111111, 0xa, 1): false
> +free(00000001-1111-1111-1111-111111111111)
> +alloc_specified(0000000a-1111-1111-1111-111111111111, 0xa, 1): true
> +])
> +
> +# alloc_specified for a range including 0 should always fail.
> +AT_CHECK(
> + [ovstest test-lflow-conj-ids operations 1 \
> + alloc-specified fee1dead-1111-1111-1111-111111111111 0xffffffff 2],
> + [0], [dnl
> +alloc_specified(fee1dead-1111-1111-1111-111111111111, 0xffffffff, 2): false
> +])
> +
> +AT_CLEANUP
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 3a361b33f..c0ffa8254 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -14936,6 +14936,18 @@ grep conjunction.*conjunction | wc -l`])
> OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \
> grep conjunction.*conjunction.*conjunction | wc -l`])
>
> +# Verify that conjunction IDs are consistent between recopmutes
> +old_conj_ids=`as hv1 ovs-ofctl dump-flows br-int | grep conj_id= | \
> + awk -F 'conj_id=' '{ print $2 }' | awk -F ',' '{ print $1 }' | sort`
> +echo $old_conj_ids
> +
> +as hv1 ovn-appctl -t ovn-controller recompute
> +ovn-nbctl --wait=hv sync
> +new_conj_ids=`as hv1 ovs-ofctl dump-flows br-int | grep conj_id= | \
> + awk -F 'conj_id=' '{ print $2 }' | awk -F ',' '{ print $1 }' | sort`
> +echo $new_conj_ids
> +AT_CHECK([test x"$old_conj_ids" = x"$new_conj_ids"])
> +
> OVN_CLEANUP([hv1])
> AT_CLEANUP
> ])
> @@ -15076,9 +15088,10 @@ check ovn-nbctl --wait=hv sync
> # Check OVS flows, the less restrictive flows should have been installed.
> AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
> grep "priority=1003" | \
> - sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
> - table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
> - table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
> + sed 's/conjunction([[^)]]*)/conjunction()/g' | \
> + sed 's/conj_id=[[0-9]]*,/conj_id=xxx,/g' | sort], [0], [dnl
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
> @@ -15120,9 +15133,10 @@ check ovn-nbctl --wait=hv sync
> # Check OVS flows, the second less restrictive allow ACL should have been installed.
> AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
> grep "priority=1003" | \
> - sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
> - table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
> - table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
> + sed 's/conjunction([[^)]]*)/conjunction()/g' | \
> + sed 's/conj_id=[[0-9]]*,/conj_id=xxx,/g' | sort], [0], [dnl
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
> @@ -15137,9 +15151,10 @@ check ovn-nbctl --wait=hv sync
> # Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled.
> AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
> grep "priority=1003" | \
> - sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
> - table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
> - table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
> + sed 's/conjunction([[^)]]*)/conjunction()/g' | \
> + sed 's/conj_id=[[0-9]]*,/conj_id=xxx,/g' | sort], [0], [dnl
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction()
> @@ -15176,9 +15191,10 @@ check ovn-nbctl --wait=hv sync
> # Check OVS flows, the less restrictive flows should have been installed.
> AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
> grep "priority=1003" | \
> - sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
> - table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
> - table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
> + sed 's/conjunction([[^)]]*)/conjunction()/g' | \
> + sed 's/conj_id=[[0-9]]*,/conj_id=xxx,/g' | sort], [0], [dnl
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
> @@ -15196,10 +15212,11 @@ check ovn-nbctl --wait=hv sync
> # New non-conjunctive flows should be added to match on 'udp'.
> AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
> grep "priority=1003" | \
> - sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
> - table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
> - table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
> - table=44, priority=1003,conj_id=4,ip,metadata=0x1 actions=resubmit(,45)
> + sed 's/conjunction([[^)]]*)/conjunction()/g' | \
> + sed 's/conj_id=[[0-9]]*,/conj_id=xxx,/g' | sort], [0], [dnl
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> + table=44, priority=1003,conj_id=xxx,ip,metadata=0x1 actions=resubmit(,45)
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction(),conjunction()
> table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
> diff --git a/tests/testsuite.at b/tests/testsuite.at
> index b716a1ad9..479e786bd 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -29,6 +29,7 @@ m4_include([tests/ovn-northd.at])
> m4_include([tests/ovn-nbctl.at])
> m4_include([tests/ovn-features.at])
> m4_include([tests/ovn-lflow-cache.at])
> +m4_include([tests/ovn-lflow-conj-ids.at])
> m4_include([tests/ovn-ofctrl-seqno.at])
> m4_include([tests/ovn-sbctl.at])
> m4_include([tests/ovn-ic-nbctl.at])
> --
> 2.30.2
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
More information about the dev
mailing list