With the small nits below:

Acked-by: Jarno Rajahalme <jrajaha...@nicira.com> 


On Jan 9, 2015, at 11:42 AM, Ben Pfaff <b...@nicira.com> wrote:

> A "conjunctive match" allows higher-level matches in the flow table, such
> as set membership matches, without causing a cross-product explosion for
> multidimensional matches.  Please refer to the documentation that this
> commit adds to ovs-ofctl(8) for a better explanation, including an example.
> 
> Signed-off-by: Ben Pfaff <b...@nicira.com>
> ---
> v1->v2:
>  - Use 1-based dimension numbers in formatted syntax, e.g. 1/2 and 2/2,
>    not 0/2 and 1/2.
>  - Add new conj_id field instead of overwriting reg0.
>  - Since priority is now an 'int', get rid of awkward +1s and comparisons
>    on priorities in classifier_lookup().
>  - Fix memory leak in classifier_replace().
>  - Modify conj_id in-place in classifier_lookup() instead of copying
>    entire flow.
>  - Remove prototype for nonexistent cls_rule_init_conjunction().
>  - Fix memory leak in classifier_lookup(), and eliminate memory allocation
>    in the common case of few conjunctive matches.
> 
> v2->v2.1:
>  - Rebase.
> 
> v2.1->v3:
>  - Nontrivial rebase due to upstream changes.
>  - Fix race condition modifying rules to add or remove conjunctions.
>  - Fix race condition adding rules with conjunctions.
>  - Document where one should specify extra constraints.
>  - Add some tests.
> —
> 

(snip)

> +static bool
> +find_conjunctive_match(const struct cls_conjunction_set *set,
> +                       unsigned int max_n_clauses, struct hmap *matches,
> +                       struct conjunctive_match *cm_stubs, size_t n_cm_stubs,
> +                       uint32_t *idp)
> +{
> +    const struct cls_conjunction *c;
> +
> +    if (max_n_clauses < set->min_n_clauses) {
> +        return false;
> +    }
> +
> +    for (c = set->conj; c < &set->conj[set->n]; c++) {
> +        uint32_t hash = hash_int(c->id, 0);

Could compute the hash after the “continue” to not compute it if not needed?

> +        struct conjunctive_match *cm;
> +
> +        if (c->n_clauses > max_n_clauses) {
> +            continue;
> +        }
> +
> +        cm = find_conjunctive_match__(matches, c->id, hash);
> +        if (!cm) {
> +            size_t n = hmap_count(matches);

Empty line here would help seeing the scope of ’n'.

> +            cm = n < n_cm_stubs ? &cm_stubs[n++] : xmalloc(sizeof *cm);

Post-increment seems to be wasted as the scope of ’n’ is this block only?

> +            hmap_insert(matches, &cm->hmap_node, hash);
> +            cm->id = c->id;
> +            cm->clauses = UINT64_MAX << (c->n_clauses & 63);
> +        }
> +        cm->clauses |= UINT64_C(1) << c->clause;
> +        if (cm->clauses == UINT64_MAX) {
> +            *idp = cm->id;
> +            return true;
> +        }
> +    }
> +    return false;
> +}
> +
> +static void
> +free_conjunctive_matches(struct hmap *matches,
> +                         struct conjunctive_match *cm_stubs, size_t 
> n_cm_stubs)
> +{
> +    if (hmap_count(matches) > n_cm_stubs) {
> +        struct conjunctive_match *cm, *next;
> +
> +        HMAP_FOR_EACH_SAFE (cm, next, hmap_node, matches) {
> +            if (!(cm >= cm_stubs && cm < &cm_stubs[n_cm_stubs])) {
> +                hmap_remove(matches, &cm->hmap_node);

Since the stub entires are not removed from the hmap, is it necessary to remove 
the malloced ones either?

> +                free(cm);
> +            }
> +        }
> +    }
> +    hmap_destroy(matches);
> +}
> +

(snip)

> 
> diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
> index 8362aa8..5458583 100644
> --- a/lib/ofp-actions.h
> +++ b/lib/ofp-actions.h
> @@ -19,6 +19,7 @@
> 
> #include <stddef.h>
> #include <stdint.h>
> +#include “classifier.h"

I do not see why this is needed here.

> #include "meta-flow.h"
> #include "ofp-errors.h"
> #include "ofp-util.h"
> @@ -96,6 +97,7 @@
>     /* Flow table interaction. */                                       \
>     OFPACT(RESUBMIT,        ofpact_resubmit,    ofpact, "resubmit")     \
>     OFPACT(LEARN,           ofpact_learn,       specs, "learn")         \
> +    OFPACT(CONJUNCTION,     ofpact_conjunction, ofpact, "conjunction")  \
>                                                                         \
>     /* Arithmetic. */                                                   \
>     OFPACT(MULTIPATH,       ofpact_multipath,   ofpact, "multipath")    \
> @@ -611,6 +613,16 @@ enum nx_mp_algorithm {
>     NX_MP_ALG_ITER_HASH = 3,
> };
> 
> +/* OFPACT_CONJUNCTION.
> + *
> + * Used for NXAST_CONJUNCTION. */
> +struct ofpact_conjunction {
> +    struct ofpact ofpact;
> +    uint8_t clause;
> +    uint8_t n_clauses;
> +    uint32_t id;
> +};
> +
> /* OFPACT_MULTIPATH.
>  *
>  * Used for NXAST_MULTIPATH. */
> diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h
> index 238fded..56b7652 100644
> --- a/lib/ofp-errors.h
> +++ b/lib/ofp-errors.h
> @@ -230,6 +230,14 @@ enum ofperr {
>      * value. */
>     OFPERR_NXBAC_MUST_BE_ZERO,
> 
> +    /* NX1.0-1.1(2,526), NX1.2+(15).  Conjunction action must be only action
> +     * present.  Conjunction action must have at least one clause. */
> +    OFPERR_NXBAC_BAD_CONJUNCTION,
> +
> +    /* NX1.0-1.1(2,527), NX1.2+(16).  Conjunction actions may not be 
> modified.
> +     * (Instead, remove the flow and add a new one in its place.) */
> +    OFPERR_NXBAC_READONLY_CONJUNCTION,
> +

I do not see this latter error code being used, is it not needed after all?

> /* ## --------------------- ## */
> /* ## OFPET_BAD_INSTRUCTION ## */
> /* ## --------------------- ## */
> diff --git a/lib/ofp-util.c b/lib/ofp-util.c
> index 53982d5..2270a93 100644
> --- a/lib/ofp-util.c
> +++ b/lib/ofp-util.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
> + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
>  *
>  * Licensed under the Apache License, Version 2.0 (the "License");
>  * you may not use this file except in compliance with the License.
> @@ -186,7 +186,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
> void
> ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
> {
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 30);
> 
>     /* Initialize most of wc. */
>     flow_wildcards_init_catchall(wc);
> diff --git a/lib/ovs-router.c b/lib/ovs-router.c
> index a121354..8de2e2d 100644
> --- a/lib/ovs-router.c
> +++ b/lib/ovs-router.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2014 Nicira, Inc.
> + * Copyright (c) 2014, 2015 Nicira, Inc.
>  *
>  * Licensed under the Apache License, Version 2.0 (the "License");
>  * you may not use this file except in compliance with the License.
> @@ -118,7 +118,7 @@ ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, 
> uint8_t plen,
>     cls_rule_init(&p->cr, &match, priority); /* Longest prefix matches first. 
> */
> 
>     ovs_mutex_lock(&mutex);
> -    cr = classifier_replace(&cls, &p->cr);
> +    cr = classifier_replace(&cls, &p->cr, NULL, 0);
>     ovs_mutex_unlock(&mutex);
> 
>     if (cr) {
> diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
> index ff616a2..e6739b9 100644
> --- a/lib/tnl-ports.c
> +++ b/lib/tnl-ports.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2014 Nicira, Inc.
> + * Copyright (c) 2014, 2015 Nicira, Inc.
>  *
>  * Licensed under the Apache License, Version 2.0 (the "License");
>  * you may not use this file except in compliance with the License.
> @@ -103,7 +103,7 @@ tnl_port_map_insert(odp_port_t port, ovs_be32 ip_dst, 
> ovs_be16 udp_port,
>         ovs_refcount_init(&p->ref_cnt);
>         strncpy(p->dev_name, dev_name, IFNAMSIZ);
> 
> -        classifier_insert(&cls, &p->cr);
> +        classifier_insert(&cls, &p->cr, NULL, 0);
>     }
>     ovs_mutex_unlock(&mutex);
> }
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 4a5b7fd..2efcbb9 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -1,4 +1,4 @@
> -/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
> +/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
>  *
>  * Licensed under the Apache License, Version 2.0 (the "License");
>  * you may not use this file except in compliance with the License.
> @@ -2635,7 +2635,7 @@ compose_output_action__(struct xlate_ctx *ctx, 
> ofp_port_t ofp_port,
> 
>     /* If 'struct flow' gets additional metadata, we'll need to zero it out
>      * before traversing a patch port. */
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 30);
>     memset(&flow_tnl, 0, sizeof flow_tnl);
> 
>     if (!xport) {
> @@ -3742,6 +3742,7 @@ ofpact_needs_recirculation_after_mpls(const struct 
> ofpact *a, struct xlate_ctx *
>     case OFPACT_SET_TUNNEL:
>     case OFPACT_SET_QUEUE:
>     case OFPACT_POP_QUEUE:
> +    case OFPACT_CONJUNCTION:
>     case OFPACT_NOTE:
>     case OFPACT_OUTPUT_REG:
>     case OFPACT_EXIT:
> @@ -4055,6 +4056,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
> ofpacts_len,
>             xlate_learn_action(ctx, ofpact_get_LEARN(a));
>             break;
> 
> +        case OFPACT_CONJUNCTION:
> +            break;
> +

This should never happen, do you consider OVS_NOT_REACHED() too risky?

>         case OFPACT_EXIT:
>             ctx->exit = true;
>             break;

(snip)

> diff --git a/tests/classifier.at b/tests/classifier.at
> index eb97022..9489da6 100644
> --- a/tests/classifier.at
> +++ b/tests/classifier.at
> @@ -124,3 +124,161 @@ Datapath actions: 3
> ])
> OVS_VSWITCHD_STOP(["/'prefixes' with incompatible field: ipv6_label/d"])
> AT_CLEANUP
> +
> +AT_BANNER([conjunctive match])
> +
> +AT_SETUP([single conjunctive match])
> +OVS_VSWITCHD_START
> +ADD_OF_PORTS([br0], 1, 2, 3, 4, 5)
> +AT_DATA([flows.txt], [dnl
> +conj_id=1,actions=3
> +priority=100,ip,ip_src=10.0.0.1,actions=conjunction(1,1/2)
> +priority=100,ip,ip_src=10.0.0.4,actions=conjunction(1,1/2)
> +priority=100,ip,ip_src=10.0.0.6,actions=conjunction(1,1/2)
> +priority=100,ip,ip_src=10.0.0.7,actions=conjunction(1,1/2)
> +priority=100,ip,ip_dst=10.0.0.2,actions=conjunction(1,2/2)
> +priority=100,ip,ip_dst=10.0.0.5,actions=conjunction(1,2/2)
> +priority=100,ip,ip_dst=10.0.0.7,actions=conjunction(1,2/2)
> +priority=100,ip,ip_dst=10.0.0.8,actions=conjunction(1,2/2)
> +priority=100,ip,ip_src=10.0.0.1,ip_dst=10.0.0.4,actions=4
> +priority=100,ip,ip_src=10.0.0.3,ip_dst=10.0.0.5,actions=5
> +
> +priority=0 actions=2
> +])
> +AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
> +for src in 0 1 2 3 4 5 6 7; do
> +    for dst in 0 1 2 3 4 5 6 7; do
> +        if test $src$dst = 14; then
> +            out=4
> +        elif test $src$dst = 35; then
> +            out=5
> +        else
> +            out=2
> +            case $src in [[1467]]) case $dst in [[2578]]) out=3 ;; esac ;; 
> esac
> +        fi
> +        AT_CHECK([ovs-appctl ofproto/trace br0 
> "in_port=1,dl_type=0x0800,nw_src=10.0.0.$src,nw_dst=10.0.0.$dst"], [0], 
> [stdout])
> +        AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $out
> +])
> +    done
> +done
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +AT_SETUP([multiple conjunctive match])
> +OVS_VSWITCHD_START
> +ADD_OF_PORTS([br0], 1, 2, 3, 4, 5)
> +AT_DATA([flows.txt], [dnl
> +conj_id=1,actions=1
> +conj_id=2,actions=2
> +conj_id=3,actions=3
> +
> +priority=5,ip,ip_src=20.0.0.0/8,actions=conjunction(1,1/2),conjunction(2,1/2)
> +priority=5,ip,ip_src=10.1.0.0/16,actions=conjunction(1,1/2),conjunction(3,2/3)
> +priority=5,ip,ip_src=10.2.0.0/16,actions=conjunction(1,1/2),conjunction(2,1/2)
> +priority=5,ip,ip_src=10.1.3.0/24,actions=conjunction(1,1/2),conjunction(3,2/3)
> +priority=5,ip,ip_src=10.1.4.5/32,actions=conjunction(1,1/2),conjunction(2,1/2)
> +
> +priority=5,ip,ip_dst=20.0.0.0/8,actions=conjunction(1,2/2)
> +priority=5,ip,ip_dst=10.1.0.0/16,actions=conjunction(1,2/2)
> +priority=5,ip,ip_dst=10.2.0.0/16,actions=conjunction(1,2/2)
> +priority=5,ip,ip_dst=10.1.3.0/24,actions=conjunction(1,2/2)
> +priority=5,ip,ip_dst=10.1.4.5/32,actions=conjunction(1,2/2)
> +priority=5,ip,ip_dst=30.0.0.0/8,actions=conjunction(2,2/2),conjunction(3,1/3)
> +priority=5,ip,ip_dst=40.5.0.0/16,actions=conjunction(2,2/2),conjunction(3,1/3)
> +
> +priority=5,tcp,tcp_dst=80,actions=conjunction(3,3/3)
> +priority=5,tcp,tcp_dst=443,actions=conjunction(3,3/3)
> +
> +priority=5,tcp,tcp_src=80,actions=conjunction(3,3/3)
> +priority=5,tcp,tcp_src=443,actions=conjunction(3,3/3)
> +
> +priority=0,actions=4
> +])
> +AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
> +for a0 in \
> +    '1 20.0.0.1' \
> +    '2 10.1.0.1' \
> +    '3 10.2.0.1' \
> +    '4 10.1.3.1' \
> +    '5 10.1.4.5' \
> +    '6 1.2.3.4'
> +do
> +    for b0 in \
> +        '1 20.0.0.1' \
> +        '2 10.1.0.1' \
> +        '3 10.2.0.1' \
> +        '4 10.1.3.1' \
> +        '5 10.1.4.5' \
> +        '6 30.0.0.1' \
> +        '7 40.5.0.1' \
> +        '8 1.2.3.4'
> +    do
> +        for c0 in '1 80' '2 443' '3 8080'; do
> +            for d0 in '1 80' '2 443' '3 8080'; do
> +                set $a0; a=$1 ip_src=$2
> +                set $b0; b=$1 ip_dst=$2
> +                set $c0; c=$1 tcp_src=$2
> +                set $d0; d=$1 tcp_dst=$2
> +                case $a$b$c$d in
> +                    [[12345]][[12345]]??) out=1 ;;
> +                    [[135]][[67]]??) out=2 ;;
> +                    [[24]][[67]][[12]]? | [[24]][[67]]?[[12]]) out=3 ;;
> +                    *) out=4
> +                esac
> +                AT_CHECK([ovs-appctl ofproto/trace br0 
> "in_port=5,dl_type=0x0800,nw_proto=6,nw_src=$ip_src,nw_dst=$ip_dst,tcp_src=$tcp_src,tcp_dst=$tcp_dst"],
>  [0], [stdout])
> +                AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 
> $out
> +])
> +            done
> +        done
> +    done
> +done
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +# In conjunctive match, we can find some soft matches that turn out not to 
> be a
> +# real match.  Usually, that's the end of the road--there is no real match.
> +# But if there is a flow identical to one of the flows that was a soft match,
> +# except with a lower priority, then we have to try again with that lower
> +# priority flow.  This test checks this special case.
> +AT_SETUP([conjunctive match priority fallback])
> +OVS_VSWITCHD_START
> +ADD_OF_PORTS([br0], 1, 2, 3, 4, 5, 6)
> +AT_DATA([flows.txt], [dnl
> +conj_id=1,actions=1
> +conj_id=3,actions=3
> +
> +priority=5,ip,ip_src=10.0.0.1,actions=conjunction(1,1/2)
> +priority=5,ip,ip_src=10.0.0.2,actions=conjunction(1,1/2)
> +priority=5,ip,ip_dst=10.0.0.1,actions=conjunction(1,2/2)
> +priority=5,ip,ip_dst=10.0.0.2,actions=conjunction(1,2/2)
> +priority=5,ip,ip_dst=10.0.0.3,actions=conjunction(1,2/2)
> +
> +priority=4,ip,ip_src=10.0.0.3,ip_dst=10.0.0.2,actions=2
> +
> +priority=3,ip,ip_src=10.0.0.1,actions=conjunction(3,1/2)
> +priority=3,ip,ip_src=10.0.0.3,actions=conjunction(3,1/2)
> +priority=3,ip,ip_dst=10.0.0.2,actions=conjunction(3,2/2)
> +priority=3,ip,ip_dst=10.0.0.3,actions=conjunction(3,2/2)
> +priority=3,ip,ip_dst=10.0.0.4,actions=conjunction(3,2/2)
> +
> +priority=2,ip,ip_src=10.0.0.1,ip_dst=10.0.0.5,actions=4
> +

It would be good to also have an identical lower priority rule that is a hard 
match.

> +priority=0,actions=5
> +])
> +AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
> +for src in 0 1 2 3; do
> +    for dst in 0 1 2 3 4 5; do
> +        case $src$dst in
> +            [[12]][[123]]) out=1 ;;
> +            32) out=2 ;;
> +            [[13]][[234]]) out=3 ;;
> +            15) out=4 ;;
> +            *) out=5
> +        esac
> +        AT_CHECK([ovs-appctl ofproto/trace br0 
> "in_port=6,dl_type=0x0800,nw_src=10.0.0.$src,nw_dst=10.0.0.$dst"], [0], 
> [stdout])
> +        AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $out
> +])
> +    done
> +done
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> 

(snip)

> diff --git a/tests/test-conjunction b/tests/test-conjunction
> new file mode 100755
> index 0000000..83dbe37
> --- /dev/null
> +++ b/tests/test-conjunction
> @@ -0,0 +1,22 @@
> +#! /bin/sh
> +ovs-vsctl --may-exist add-br br0
> +ovs-ofctl del-flows br0
> +ovs-ofctl add-flows br0 - <<EOF
> +conj_id=1,ip,actions=mod_dl_src:00:11:22:33:44:55,local
> +ip,ip_src=10.0.0.1,actions=conjunction(1,1/2)
> +ip,ip_src=10.0.0.4,actions=conjunction(1,1/2)
> +ip,ip_src=10.0.0.6,actions=conjunction(1,1/2)
> +ip,ip_src=10.0.0.7,actions=conjunction(1,1/2)
> +ip,ip_dst=10.0.0.2,actions=conjunction(1,2/2)
> +ip,ip_dst=10.0.0.5,actions=conjunction(1,2/2)
> +ip,ip_dst=10.0.0.7,actions=conjunction(1,2/2)
> +ip,ip_dst=10.0.0.8,actions=conjunction(1,2/2)
> +EOF
> +
> +# This should match the conjunctive flow and thus change the Ethernet
> +# source address and output to local.
> +ovs-appctl ofproto/trace br0 tcp,ip_src=10.0.0.1,ip_dst=10.0.0.5
> +printf "%s\n\n" 
> '------------------------------------------------------------'
> +
> +# This should not match anything and thus get dropped.
> +ovs-appctl ofproto/trace br0 tcp,ip_src=10.0.0.2,ip_dst=10.0.0.5

Are we running this script as part of the test suite?

> diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
> index 7ffbeaa..aa6da29 100644
> --- a/utilities/ovs-ofctl.8.in
> +++ b/utilities/ovs-ofctl.8.in
> @@ -1141,6 +1141,11 @@ output group in the OpenFlow action set), then the 
> value will be
> .IP
> This field was introduced in Open vSwitch 2.4 to conform with the
> OpenFlow 1.5 (draft) specification.
> +.
> +.IP \fBconj_id=\fIvalue\fR
> +Matches the given 32-bit \fIvalue\fR against the conjunction ID.  This
> +is used only with the \fBconjunction\fR action, documented later.

Better say “below” than “later”, as the latter could be understood as “in 
future”.

  Jarno

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to