On Wed, 2023-05-24 at 11:12 +0200, Ihtisham ul Haq via dev wrote:
> Using cache improves performance of recomputation of lflows(by
> about 30%)
>
> Exising lflow cache for `matches` and `expressions` is adopted
> to include `actions` as well.
>
> Co-authored-by: Felix Huettner <[email protected]>
> Signed-off-by: Felix Huettner <[email protected]>
> Signed-off-by: Ihtisham ul Haq <[email protected]>
> ---
> controller/lflow-cache.c | 12 ++++++++--
> controller/lflow-cache.h | 8 +++++--
> controller/lflow.c | 41 +++++++++++++++++++++-------------
> controller/test-lflow-cache.c | 42 +++++++++++++++++++++++++++--------
> tests/ovn-lflow-cache.at | 41 ++++++++++++++++++++++++++++++++++
> 5 files changed, 115 insertions(+), 29 deletions(-)
>
> diff --git a/controller/lflow-cache.c b/controller/lflow-cache.c
> index f723eab8a..a648d77af 100644
> --- a/controller/lflow-cache.c
> +++ b/controller/lflow-cache.c
> @@ -25,7 +25,9 @@
> #include "lflow-cache.h"
> #include "lib/uuid.h"
> #include "memory-trim.h"
> +#include "openvswitch/ofpbuf.h"
> #include "openvswitch/vlog.h"
> +#include "ovn/actions.h"
> #include "ovn/expr.h"
>
> VLOG_DEFINE_THIS_MODULE(lflow_cache);
> @@ -209,7 +211,8 @@ 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,
> - struct expr *expr, size_t expr_sz)
> + struct expr *expr, size_t expr_sz,
> + struct ofpbuf *actions)
> {
> struct lflow_cache_value *lcv =
> lflow_cache_add__(lc, lflow_uuid, LCACHE_T_EXPR, expr_sz);
> @@ -220,12 +223,14 @@ lflow_cache_add_expr(struct lflow_cache *lc, const
> struct uuid *lflow_uuid,
> }
> COVERAGE_INC(lflow_cache_add_expr);
> lcv->expr = expr;
> + lcv->actions = actions;
Since the function consumes the action, you need to free & uninit the
actions in the !lcv case above this chunk, like happens for '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 hmap *matches, size_t matches_sz,
> + struct ofpbuf *actions)
> {
> struct lflow_cache_value *lcv =
> lflow_cache_add__(lc, lflow_uuid, LCACHE_T_MATCHES, matches_sz);
> @@ -239,6 +244,7 @@ lflow_cache_add_matches(struct lflow_cache *lc, const
> struct uuid *lflow_uuid,
> lcv->expr_matches = matches;
> lcv->n_conjs = n_conjs;
> lcv->conj_id_ofs = conj_id_ofs;
> + lcv->actions = actions;
Similarly, since the function consumes the action, you need to free &
uninit the actions in the !lcv case above this chunk, like happens for
'matches'.
> }
>
> struct lflow_cache_value *
> @@ -380,11 +386,13 @@ lflow_cache_delete__(struct lflow_cache *lc, struct
> lflow_cache_entry *lce)
> case LCACHE_T_EXPR:
> COVERAGE_INC(lflow_cache_free_expr);
> expr_destroy(lce->value.expr);
> + ovnacts_free((*lce->value.actions).data, (*lce->value.actions).size);
ovnacts_free(lce->value.actions->data, lce->value.actions->size);
seems a bit more canonical (eg, actions->data instead of dereferencing actions
and using '.')
> break;
> case LCACHE_T_MATCHES:
> COVERAGE_INC(lflow_cache_free_matches);
> expr_matches_destroy(lce->value.expr_matches);
> free(lce->value.expr_matches);
> + ovnacts_free((*lce->value.actions).data, (*lce->value.actions).size);
Same here.
> break;
> }
>
> diff --git a/controller/lflow-cache.h b/controller/lflow-cache.h
> index 300a56077..9d542beec 100644
> --- a/controller/lflow-cache.h
> +++ b/controller/lflow-cache.h
> @@ -50,6 +50,8 @@ struct lflow_cache_value {
> uint32_t n_conjs;
> uint32_t conj_id_ofs;
>
> + struct ofpbuf *actions;
> +
> union {
> struct hmap *expr_matches;
> struct expr *expr;
> @@ -66,11 +68,13 @@ 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,
> - struct expr *expr, size_t expr_sz);
> + struct expr *expr, size_t expr_sz,
> + struct ofpbuf *actions);
> 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 hmap *matches, size_t matches_sz,
> + struct ofpbuf *actions);
>
> struct lflow_cache_value *lflow_cache_get(struct lflow_cache *,
> const struct uuid *lflow_uuid);
> diff --git a/controller/lflow.c b/controller/lflow.c
> index 0b071138d..5ca676ee8 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -1078,14 +1078,23 @@ consider_logical_flow__(const struct
> sbrec_logical_flow *lflow,
> struct sset template_vars_ref = SSET_INITIALIZER(&template_vars_ref);
> struct expr *prereqs = NULL;
>
> - if (!lflow_parse_actions(lflow, l_ctx_in, &template_vars_ref,
> - &ovnacts, &prereqs)) {
> - ovnacts_free(ovnacts.data, ovnacts.size);
> - ofpbuf_uninit(&ovnacts);
> - store_lflow_template_refs(l_ctx_out->lflow_deps_mgr,
> - &template_vars_ref, lflow);
> - sset_destroy(&template_vars_ref);
> - return;
> + struct lflow_cache_value *lcv =
> + lflow_cache_get(l_ctx_out->lflow_cache, &lflow->header_.uuid);
> + enum lflow_cache_type lcv_type =
> + lcv ? lcv->type : LCACHE_T_NONE;
> +
> + if (lcv_type != LCACHE_T_NONE) {
> + ovnacts = *ofpbuf_clone(lcv->actions);
Nothing actually modifies lcv->actions so we can optimize memory usage
for the cached case by never cloning if we have a cache hit. (we could
also avoid cloning the uncached actions if it was heap-allocated, but
that's another patch perhaps).
Since you'd have to track the cached actions separately to ensure they
don't get freed, you could:
uint64_t ovnacts_stub[1024 / 8];
struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub);
struct ofpbuf *ovnacts_cached = NULL;
...
if (lcv_type != LCACHE_T_NONE) {
ovnacts_cached = lcv->actions;
} else {
if (!lflow_parse_actions(lflow, l_ctx_in, &template_vars_ref,
&ovnacts, &prereqs)) {
ovnacts_free(ovnacts.data, ovnacts.size);
ofpbuf_uninit(&ovnacts);
store_lflow_template_refs(l_ctx_out->lflow_deps_mgr,
&template_vars_ref, lflow);
sset_destroy(&template_vars_ref);
return;
}
}
...
if (lcv_type == LCACHE_T_MATCHES
&& lcv->n_conjs
&& !lflow_conj_ids_alloc_specified(l_ctx_out->conj_ids,
&lflow->header_.uuid,
&dp->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));
/* Clone actions as they'll get freed when the cache entry is deleted */
ovnacts_cached = ofpbuf_clone(ovnacts_cached);
lflow_cache_delete(l_ctx_out->lflow_cache, &lflow->header_.uuid);
lcv_type = LCACHE_T_NONE;
}
...
add_matches_to_flow_table(lflow, ldp, matches, ptable, output_ptable,
ovnacts_cached ? ovnacts_cached : ovnacts,
ingress, l_ctx_in, l_ctx_out);
...
/* Cache new entry if caching is enabled. */
if (lflow_cache_is_enabled(l_ctx_out->lflow_cache)) {
struct ofpbuf *lcv_actions = ovnacts_cached
? ovnacts_cached
: ofpbuf_clone(&ovnacts);
...
done:
ovnacts_free(ovnacts_uncached.data, ovnacts_uncached.size);
ofpbuf_uninit(&ovnacts_uncached);
You wouldn't need to free the cached actions (since they're still owned
by the cache), just the ones created by lflow_parse_actions() in
ovnact_uncached.
> + } else {
> + if (!lflow_parse_actions(lflow, l_ctx_in, &template_vars_ref,
> + &ovnacts, &prereqs)) {
> + ovnacts_free(ovnacts.data, ovnacts.size);
> + ofpbuf_uninit(&ovnacts);
> + store_lflow_template_refs(l_ctx_out->lflow_deps_mgr,
> + &template_vars_ref, lflow);
> + sset_destroy(&template_vars_ref);
> + return;
> + }
> }
>
> struct lookup_port_aux aux = {
> @@ -1105,11 +1114,6 @@ consider_logical_flow__(const struct
> sbrec_logical_flow *lflow,
> .deps_mgr = l_ctx_out->lflow_deps_mgr,
> };
>
> - struct lflow_cache_value *lcv =
> - lflow_cache_get(l_ctx_out->lflow_cache, &lflow->header_.uuid);
> - enum lflow_cache_type lcv_type =
> - lcv ? lcv->type : LCACHE_T_NONE;
> -
> struct expr *cached_expr = NULL, *expr = NULL;
> struct hmap *matches = NULL;
> size_t matches_size = 0;
> @@ -1211,17 +1215,20 @@ consider_logical_flow__(const struct
> sbrec_logical_flow *lflow,
> case LCACHE_T_NONE:
> /* Cache new entry if caching is enabled. */
> if (lflow_cache_is_enabled(l_ctx_out->lflow_cache)) {
> + struct ofpbuf *lcv_actions = ofpbuf_clone(&ovnacts);
If you go with the above suggestion, then we wouldn't need to clone if
ovnacts_cached != NULL since it was already cloned by the cache drop
case.
> if (cached_expr
> && !objdep_mgr_contains_obj(l_ctx_out->lflow_deps_mgr,
> &lflow->header_.uuid)) {
> lflow_cache_add_matches(l_ctx_out->lflow_cache,
> &lflow->header_.uuid, start_conj_id,
> - n_conjs, matches, matches_size);
> + n_conjs, matches, matches_size,
> + lcv_actions);
> matches = NULL;
> } else if (cached_expr) {
> lflow_cache_add_expr(l_ctx_out->lflow_cache,
> &lflow->header_.uuid,
> - cached_expr, expr_size(cached_expr));
> + cached_expr, expr_size(cached_expr),
> + lcv_actions);
> cached_expr = NULL;
> }
If neither of the if() statements happened the cloned lcv_actions gets
leaked.
You could add:
} else {
ovnacts_free(lcv_actions->data, lcv_actions->size);
ofpbuf_uninit(lcv_actions);
}
> }
> @@ -1236,7 +1243,9 @@ consider_logical_flow__(const struct sbrec_logical_flow
> *lflow,
>
> done:
> expr_destroy(prereqs);
> - ovnacts_free(ovnacts.data, ovnacts.size);
> + if (!lflow_cache_is_enabled(l_ctx_out->lflow_cache)) {
> + ovnacts_free(ovnacts.data, ovnacts.size);
Don't we want to always free here? Since it's getting cloned before
being added to the cache we should still free the original data.
And we have to clone the uncached ovnacts before caching them, because
it's a stack buffer. So I think we always want to free here, cached or
uncached.
Dan
> + }
> ofpbuf_uninit(&ovnacts);
> expr_destroy(expr);
> expr_destroy(cached_expr);
> diff --git a/controller/test-lflow-cache.c b/controller/test-lflow-cache.c
> index 7ce999360..03e216290 100644
> --- a/controller/test-lflow-cache.c
> +++ b/controller/test-lflow-cache.c
> @@ -16,6 +16,8 @@
> #include <config.h>
>
> #include "lib/uuid.h"
> +#include "openvswitch/ofpbuf.h"
> +#include "ovn/actions.h"
> #include "ovn/expr.h"
> #include "tests/ovstest.h"
> #include "tests/test-utils.h"
> @@ -39,22 +41,26 @@ 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)
> + struct expr *e, struct ofpbuf *a)
> {
> + struct ds ovnacts_s = DS_EMPTY_INITIALIZER;
> + ovnacts_format(a->data, a->size, &ovnacts_s);
> +
> printf("ADD %s:\n", op_type);
> printf(" conj-id-ofs: %u\n", conj_id_ofs);
> printf(" n_conjs: %u\n", n_conjs);
> + printf(" action: %s\n", ds_cstr(&ovnacts_s));
>
> if (!strcmp(op_type, "expr")) {
> lflow_cache_add_expr(lc, lflow_uuid, expr_clone(e),
> - TEST_LFLOW_CACHE_VALUE_SIZE);
> + TEST_LFLOW_CACHE_VALUE_SIZE, ofpbuf_clone(a));
> } 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,
> conj_id_ofs, n_conjs, matches,
> - TEST_LFLOW_CACHE_VALUE_SIZE);
> + TEST_LFLOW_CACHE_VALUE_SIZE,
> ofpbuf_clone(a));
> } else {
> OVS_NOT_REACHED();
> }
> @@ -72,8 +78,12 @@ test_lflow_cache_lookup__(struct lflow_cache *lc,
> return;
> }
>
> + struct ds ovnacts_s = DS_EMPTY_INITIALIZER;
> + ovnacts_format(lcv->actions->data, lcv->actions->size, &ovnacts_s);
> +
> printf(" conj_id_ofs: %"PRIu32"\n", lcv->conj_id_ofs);
> printf(" n_conjs: %"PRIu32"\n", lcv->n_conjs);
> + printf(" action: %s\n", ds_cstr(&ovnacts_s));
> switch (lcv->type) {
> case LCACHE_T_EXPR:
> printf(" type: expr\n");
> @@ -110,6 +120,13 @@ test_lflow_cache_operations(struct ovs_cmdl_context *ctx)
> {
> struct lflow_cache *lc = lflow_cache_create();
> struct expr *e = expr_create_boolean(true);
> + struct ofpbuf *a = ofpbuf_new(0);
> + struct ovnact_next *next = ovnact_put_NEXT(a);
> + next->pipeline = 1;
> + next->ltable = 2;
> + next->src_pipeline = OVNACT_P_INGRESS;
> + next->src_ltable = 3;
> +
> bool enabled = !strcmp(ctx->argv[1], "true");
> struct uuid *lflow_uuids = NULL;
> size_t n_allocated_lflow_uuids = 0;
> @@ -160,7 +177,7 @@ test_lflow_cache_operations(struct ovs_cmdl_context *ctx)
>
> uuid_generate(lflow_uuid);
> test_lflow_cache_add__(lc, op_type, lflow_uuid, conj_id_ofs,
> - n_conjs, e);
> + n_conjs, e, a);
> test_lflow_cache_lookup__(lc, lflow_uuid);
> } else if (!strcmp(op, "add-del")) {
> const char *op_type = test_read_value(ctx, shift++, "op_type");
> @@ -183,7 +200,7 @@ test_lflow_cache_operations(struct ovs_cmdl_context *ctx)
> struct uuid lflow_uuid;
> uuid_generate(&lflow_uuid);
> test_lflow_cache_add__(lc, op_type, &lflow_uuid, conj_id_ofs,
> - n_conjs, e);
> + n_conjs, e, a);
> test_lflow_cache_lookup__(lc, &lflow_uuid);
> test_lflow_cache_delete__(lc, &lflow_uuid);
> test_lflow_cache_lookup__(lc, &lflow_uuid);
> @@ -264,16 +281,23 @@ test_lflow_cache_negative(struct ovs_cmdl_context *ctx
> OVS_UNUSED)
>
> for (size_t i = 0; i < ARRAY_SIZE(lcs); i++) {
> struct expr *e = expr_create_boolean(true);
> + struct ofpbuf *a = ofpbuf_new(0);
> + struct ovnact_next *next = ovnact_put_NEXT(a);
> + next->pipeline = 1;
> + next->ltable = 2;
> + next->src_pipeline = OVNACT_P_INGRESS;
> + next->src_ltable = 3;
> +
> 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_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_expr(lcs[i], NULL, NULL, 0, NULL);
> + lflow_cache_add_expr(lcs[i], NULL, e, expr_size(e), a);
> + lflow_cache_add_matches(lcs[i], NULL, 0, 0, NULL, 0, NULL);
> lflow_cache_add_matches(lcs[i], NULL, 0, 0, matches,
> - TEST_LFLOW_CACHE_VALUE_SIZE);
> + TEST_LFLOW_CACHE_VALUE_SIZE, a);
> lflow_cache_destroy(lcs[i]);
> }
> }
> diff --git a/tests/ovn-lflow-cache.at b/tests/ovn-lflow-cache.at
> index 593d3eaac..b1f79cb72 100644
> --- a/tests/ovn-lflow-cache.at
> +++ b/tests/ovn-lflow-cache.at
> @@ -19,9 +19,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 2
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -32,9 +34,11 @@ trim count : 0
> ADD matches:
> conj-id-ofs: 3
> n_conjs: 2
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 3
> n_conjs: 2
> + action: next(pipeline=egress, table=2);
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -61,9 +65,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 2
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> DELETE
> LOOKUP:
> @@ -77,9 +83,11 @@ trim count : 0
> ADD matches:
> conj-id-ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> type: matches
> DELETE
> LOOKUP:
> @@ -109,6 +117,7 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 2
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> not found
> Enabled: false
> @@ -120,6 +129,7 @@ trim count : 0
> ADD matches:
> conj-id-ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> not found
> Enabled: false
> @@ -154,9 +164,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 2
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -167,9 +179,11 @@ trim count : 0
> ADD matches:
> conj-id-ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -188,6 +202,7 @@ trim count : 1
> ADD expr:
> conj-id-ofs: 5
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> not found
> Enabled: false
> @@ -199,6 +214,7 @@ trim count : 1
> ADD matches:
> conj-id-ofs: 6
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> not found
> Enabled: false
> @@ -217,9 +233,11 @@ trim count : 1
> ADD expr:
> conj-id-ofs: 8
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -230,9 +248,11 @@ trim count : 1
> ADD matches:
> conj-id-ofs: 9
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 9
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -273,9 +293,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 2
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -286,9 +308,11 @@ trim count : 0
> ADD matches:
> conj-id-ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> type: matches
> Enabled: true
> high-watermark : 2
> @@ -309,9 +333,11 @@ trim count : 1
> ADD expr:
> conj-id-ofs: 5
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -322,9 +348,11 @@ trim count : 1
> ADD matches:
> conj-id-ofs: 6
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 6
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> type: matches
> dnl
> dnl Cache is full but we can evict the expr entry because we're adding
> @@ -339,6 +367,7 @@ trim count : 1
> ADD expr:
> conj-id-ofs: 7
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> not found
> dnl
> @@ -365,6 +394,7 @@ trim count : 2
> ADD expr:
> conj-id-ofs: 9
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> not found
> dnl
> @@ -380,6 +410,7 @@ trim count : 2
> ADD matches:
> conj-id-ofs: 10
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> not found
> dnl
> @@ -428,9 +459,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 1
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 1
> @@ -441,9 +474,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 2
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 2
> @@ -454,9 +489,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 3
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 3
> @@ -467,9 +504,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 4
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 4
> @@ -480,9 +519,11 @@ trim count : 0
> ADD expr:
> conj-id-ofs: 5
> n_conjs: 1
> + action: next(pipeline=egress, table=2);
> LOOKUP:
> conj_id_ofs: 0
> n_conjs: 0
> + action: next(pipeline=egress, table=2);
> type: expr
> Enabled: true
> high-watermark : 5
> --
> 2.34.1
>
> Diese E Mail enthält möglicherweise vertrauliche Inhalte und ist nur für die
> Verwertung durch den vorgesehenen Empfänger bestimmt.
> Sollten Sie nicht der vorgesehene Empfänger sein, setzen Sie den Absender
> bitte unverzüglich in Kenntnis und löschen diese E Mail.
>
> Hinweise zum Datenschutz finden Sie hier<https://www.datenschutz.schwarz>.
>
>
> This e-mail may contain confidential content and is intended only for the
> specified recipient/s.
> If you are not the intended recipient, please inform the sender immediately
> and delete this e-mail.
>
> Information on data protection can be found
> here<https://www.datenschutz.schwarz>.
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev