Introduce sparse flows and masks, to reduce memory usage and improve speed.
[sliver-openvswitch.git] / tests / test-classifier.c
index 5e24dd2..a7daa94 100644 (file)
@@ -183,52 +183,54 @@ tcls_remove(struct tcls *cls, const struct test_rule *rule)
 }
 
 static bool
-match(const struct cls_rule *wild, const struct flow *fixed)
+match(const struct cls_rule *wild_, const struct flow *fixed)
 {
+    struct match wild;
     int f_idx;
 
+    minimatch_expand(&wild_->match, &wild);
     for (f_idx = 0; f_idx < CLS_N_FIELDS; f_idx++) {
         bool eq;
 
         if (f_idx == CLS_F_IDX_NW_SRC) {
-            eq = !((fixed->nw_src ^ wild->match.flow.nw_src)
-                   & wild->match.wc.masks.nw_src);
+            eq = !((fixed->nw_src ^ wild.flow.nw_src)
+                   & wild.wc.masks.nw_src);
         } else if (f_idx == CLS_F_IDX_NW_DST) {
-            eq = !((fixed->nw_dst ^ wild->match.flow.nw_dst)
-                   & wild->match.wc.masks.nw_dst);
+            eq = !((fixed->nw_dst ^ wild.flow.nw_dst)
+                   & wild.wc.masks.nw_dst);
         } else if (f_idx == CLS_F_IDX_TP_SRC) {
-            eq = !((fixed->tp_src ^ wild->match.flow.tp_src)
-                   & wild->match.wc.masks.tp_src);
+            eq = !((fixed->tp_src ^ wild.flow.tp_src)
+                   & wild.wc.masks.tp_src);
         } else if (f_idx == CLS_F_IDX_TP_DST) {
-            eq = !((fixed->tp_dst ^ wild->match.flow.tp_dst)
-                   & wild->match.wc.masks.tp_dst);
+            eq = !((fixed->tp_dst ^ wild.flow.tp_dst)
+                   & wild.wc.masks.tp_dst);
         } else if (f_idx == CLS_F_IDX_DL_SRC) {
-            eq = eth_addr_equal_except(fixed->dl_src, wild->match.flow.dl_src,
-                                       wild->match.wc.masks.dl_src);
+            eq = eth_addr_equal_except(fixed->dl_src, wild.flow.dl_src,
+                                       wild.wc.masks.dl_src);
         } else if (f_idx == CLS_F_IDX_DL_DST) {
-            eq = eth_addr_equal_except(fixed->dl_dst, wild->match.flow.dl_dst,
-                                       wild->match.wc.masks.dl_dst);
+            eq = eth_addr_equal_except(fixed->dl_dst, wild.flow.dl_dst,
+                                       wild.wc.masks.dl_dst);
         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
-            eq = !((fixed->vlan_tci ^ wild->match.flow.vlan_tci)
-                   & wild->match.wc.masks.vlan_tci);
+            eq = !((fixed->vlan_tci ^ wild.flow.vlan_tci)
+                   & wild.wc.masks.vlan_tci);
         } else if (f_idx == CLS_F_IDX_TUN_ID) {
-            eq = !((fixed->tun_id ^ wild->match.flow.tun_id)
-                   & wild->match.wc.masks.tun_id);
+            eq = !((fixed->tun_id ^ wild.flow.tun_id)
+                   & wild.wc.masks.tun_id);
         } else if (f_idx == CLS_F_IDX_METADATA) {
-            eq = !((fixed->metadata ^ wild->match.flow.metadata)
-                   & wild->match.wc.masks.metadata);
+            eq = !((fixed->metadata ^ wild.flow.metadata)
+                   & wild.wc.masks.metadata);
         } else if (f_idx == CLS_F_IDX_NW_DSCP) {
-            eq = !((fixed->nw_tos ^ wild->match.flow.nw_tos) &
-                   (wild->match.wc.masks.nw_tos & IP_DSCP_MASK));
+            eq = !((fixed->nw_tos ^ wild.flow.nw_tos) &
+                   (wild.wc.masks.nw_tos & IP_DSCP_MASK));
         } else if (f_idx == CLS_F_IDX_NW_PROTO) {
-            eq = !((fixed->nw_proto ^ wild->match.flow.nw_proto)
-                   & wild->match.wc.masks.nw_proto);
+            eq = !((fixed->nw_proto ^ wild.flow.nw_proto)
+                   & wild.wc.masks.nw_proto);
         } else if (f_idx == CLS_F_IDX_DL_TYPE) {
-            eq = !((fixed->dl_type ^ wild->match.flow.dl_type)
-                   & wild->match.wc.masks.dl_type);
+            eq = !((fixed->dl_type ^ wild.flow.dl_type)
+                   & wild.wc.masks.dl_type);
         } else if (f_idx == CLS_F_IDX_IN_PORT) {
-            eq = !((fixed->in_port ^ wild->match.flow.in_port)
-                   & wild->match.wc.masks.in_port);
+            eq = !((fixed->in_port ^ wild.flow.in_port)
+                   & wild.wc.masks.in_port);
         } else {
             NOT_REACHED();
         }
@@ -261,13 +263,17 @@ tcls_delete_matches(struct tcls *cls, const struct cls_rule *target)
 
     for (i = 0; i < cls->n_rules; ) {
         struct test_rule *pos = cls->rules[i];
-        if (!flow_wildcards_has_extra(&pos->cls_rule.match.wc,
-                                      &target->match.wc)
-            && match(target, &pos->cls_rule.match.flow)) {
-            tcls_remove(cls, pos);
-        } else {
-            i++;
+        if (!minimask_has_extra(&pos->cls_rule.match.mask,
+                                &target->match.mask)) {
+            struct flow flow;
+
+            miniflow_expand(&pos->cls_rule.match.flow, &flow);
+            if (match(target, &flow)) {
+                tcls_remove(cls, pos);
+                continue;
+            }
         }
+        i++;
     }
 }
 \f
@@ -555,7 +561,20 @@ shuffle(unsigned int *p, size_t n)
         *q = tmp;
     }
 }
+
+static void
+shuffle_u32s(uint32_t *p, size_t n)
+{
+    for (; n > 1; n--, p++) {
+        uint32_t *q = &p[rand() % n];
+        uint32_t tmp = *p;
+        *p = *q;
+        *q = tmp;
+    }
+}
 \f
+/* Classifier tests. */
+
 /* Tests an empty classifier. */
 static void
 test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
@@ -950,7 +969,301 @@ test_many_rules_in_five_tables(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     test_many_rules_in_n_tables(5);
 }
 \f
+/* Miniflow tests. */
+
+static uint32_t
+random_value(void)
+{
+    static const uint32_t values[] =
+        { 0xffffffff, 0xaaaaaaaa, 0x55555555, 0x80000000,
+          0x00000001, 0xface0000, 0x00d00d1e, 0xdeadbeef };
+
+    return values[random_uint32() % ARRAY_SIZE(values)];
+}
+
+static bool
+choose(unsigned int n, unsigned int *idxp)
+{
+    if (*idxp < n) {
+        return true;
+    } else {
+        *idxp -= n;
+        return false;
+    }
+}
+
+static bool
+init_consecutive_values(int n_consecutive, struct flow *flow,
+                        unsigned int *idxp)
+{
+    uint32_t *flow_u32 = (uint32_t *) flow;
+
+    if (choose(FLOW_U32S - n_consecutive + 1, idxp)) {
+        int i;
+
+        for (i = 0; i < n_consecutive; i++) {
+            flow_u32[*idxp + i] = random_value();
+        }
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static bool
+next_random_flow(struct flow *flow, unsigned int idx)
+{
+    uint32_t *flow_u32 = (uint32_t *) flow;
+    int i;
+
+    memset(flow, 0, sizeof *flow);
+
+    /* Empty flow. */
+    if (choose(1, &idx)) {
+        return true;
+    }
+
+    /* All flows with a small number of consecutive nonzero values. */
+    for (i = 1; i <= 4; i++) {
+        if (init_consecutive_values(i, flow, &idx)) {
+            return true;
+        }
+    }
+
+    /* All flows with a large number of consecutive nonzero values. */
+    for (i = FLOW_U32S - 4; i <= FLOW_U32S; i++) {
+        if (init_consecutive_values(i, flow, &idx)) {
+            return true;
+        }
+    }
+
+    /* All flows with exactly two nonconsecutive nonzero values. */
+    if (choose((FLOW_U32S - 1) * (FLOW_U32S - 2) / 2, &idx)) {
+        int ofs1;
+
+        for (ofs1 = 0; ofs1 < FLOW_U32S - 2; ofs1++) {
+            int ofs2;
+
+            for (ofs2 = ofs1 + 2; ofs2 < FLOW_U32S; ofs2++) {
+                if (choose(1, &idx)) {
+                    flow_u32[ofs1] = random_value();
+                    flow_u32[ofs2] = random_value();
+                    return true;
+                }
+            }
+        }
+        NOT_REACHED();
+    }
+
+    /* 16 randomly chosen flows with N >= 3 nonzero values. */
+    if (choose(16 * (FLOW_U32S - 4), &idx)) {
+        int n = idx / 16 + 3;
+        int i;
+
+        for (i = 0; i < n; i++) {
+            flow_u32[i] = random_value();
+        }
+        shuffle_u32s(flow_u32, FLOW_U32S);
+
+        return true;
+    }
+
+    return false;
+}
+
+static void
+any_random_flow(struct flow *flow)
+{
+    static unsigned int max;
+    if (!max) {
+        while (next_random_flow(flow, max)) {
+            max++;
+        }
+    }
+
+    next_random_flow(flow, random_range(max));
+}
+
+static void
+toggle_masked_flow_bits(struct flow *flow, const struct flow_wildcards *mask)
+{
+    const uint32_t *mask_u32 = (const uint32_t *) &mask->masks;
+    uint32_t *flow_u32 = (uint32_t *) flow;
+    int i;
+
+    for (i = 0; i < FLOW_U32S; i++) {
+        if (mask_u32[i] != 0) {
+            uint32_t bit;
+
+            do {
+                bit = 1u << random_range(32);
+            } while (!(bit & mask_u32[i]));
+            flow_u32[i] ^= bit;
+        }
+    }
+}
+
+static void
+wildcard_extra_bits(struct flow_wildcards *mask)
+{
+    uint32_t *mask_u32 = (uint32_t *) &mask->masks;
+    int i;
+
+    for (i = 0; i < FLOW_U32S; i++) {
+        if (mask_u32[i] != 0) {
+            uint32_t bit;
+
+            do {
+                bit = 1u << random_range(32);
+            } while (!(bit & mask_u32[i]));
+            mask_u32[i] &= ~bit;
+        }
+    }
+}
+
+static void
+test_miniflow(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct flow flow;
+    unsigned int idx;
+
+    random_set_seed(0xb3faca38);
+    for (idx = 0; next_random_flow(&flow, idx); idx++) {
+        const uint32_t *flow_u32 = (const uint32_t *) &flow;
+        struct miniflow miniflow, miniflow2, miniflow3;
+        struct flow flow2, flow3;
+        struct flow_wildcards mask;
+        struct minimask minimask;
+        int i;
+
+        /* Convert flow to miniflow. */
+        miniflow_init(&miniflow, &flow);
+
+        /* Check that the flow equals its miniflow. */
+        assert(miniflow_get_vid(&miniflow) == vlan_tci_to_vid(flow.vlan_tci));
+        for (i = 0; i < FLOW_U32S; i++) {
+            assert(miniflow_get(&miniflow, i) == flow_u32[i]);
+        }
+
+        /* Check that the miniflow equals itself. */
+        assert(miniflow_equal(&miniflow, &miniflow));
+
+        /* Convert miniflow back to flow and verify that it's the same. */
+        miniflow_expand(&miniflow, &flow2);
+        assert(flow_equal(&flow, &flow2));
+
+        /* Check that copying a miniflow works properly. */
+        miniflow_clone(&miniflow2, &miniflow);
+        assert(miniflow_equal(&miniflow, &miniflow2));
+        assert(miniflow_hash(&miniflow, 0) == miniflow_hash(&miniflow2, 0));
+        miniflow_expand(&miniflow2, &flow3);
+        assert(flow_equal(&flow, &flow3));
+
+        /* Check that masked matches work as expected for identical flows and
+         * miniflows. */
+        do {
+            next_random_flow(&mask.masks, 1);
+        } while (flow_wildcards_is_catchall(&mask));
+        minimask_init(&minimask, &mask);
+        assert(minimask_is_catchall(&minimask)
+               == flow_wildcards_is_catchall(&mask));
+        assert(miniflow_equal_in_minimask(&miniflow, &miniflow2, &minimask));
+        assert(miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
+        assert(miniflow_hash_in_minimask(&miniflow, &minimask, 0x12345678) ==
+               flow_hash_in_minimask(&flow, &minimask, 0x12345678));
+
+        /* Check that masked matches work as expected for differing flows and
+         * miniflows. */
+        toggle_masked_flow_bits(&flow2, &mask);
+        assert(!miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
+        miniflow_init(&miniflow3, &flow2);
+        assert(!miniflow_equal_in_minimask(&miniflow, &miniflow3, &minimask));
+
+        /* Clean up. */
+        miniflow_destroy(&miniflow);
+        miniflow_destroy(&miniflow2);
+        miniflow_destroy(&miniflow3);
+        minimask_destroy(&minimask);
+    }
+}
+
+static void
+test_minimask_has_extra(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct flow_wildcards catchall;
+    struct minimask minicatchall;
+    struct flow flow;
+    unsigned int idx;
+
+    flow_wildcards_init_catchall(&catchall);
+    minimask_init(&minicatchall, &catchall);
+    assert(minimask_is_catchall(&minicatchall));
+
+    random_set_seed(0x2ec7905b);
+    for (idx = 0; next_random_flow(&flow, idx); idx++) {
+        struct flow_wildcards mask;
+        struct minimask minimask;
+
+        mask.masks = flow;
+        minimask_init(&minimask, &mask);
+        assert(!minimask_has_extra(&minimask, &minimask));
+        assert(minimask_has_extra(&minicatchall, &minimask)
+               == !minimask_is_catchall(&minimask));
+        if (!minimask_is_catchall(&minimask)) {
+            struct minimask minimask2;
+
+            wildcard_extra_bits(&mask);
+            minimask_init(&minimask2, &mask);
+            assert(minimask_has_extra(&minimask2, &minimask));
+            assert(!minimask_has_extra(&minimask, &minimask2));
+            minimask_destroy(&minimask2);
+        }
+
+        minimask_destroy(&minimask);
+    }
+}
+
+static void
+test_minimask_combine(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct flow_wildcards catchall;
+    struct minimask minicatchall;
+    struct flow flow;
+    unsigned int idx;
+
+    flow_wildcards_init_catchall(&catchall);
+    minimask_init(&minicatchall, &catchall);
+    assert(minimask_is_catchall(&minicatchall));
+
+    random_set_seed(0x181bf0cd);
+    for (idx = 0; next_random_flow(&flow, idx); idx++) {
+        struct minimask minimask, minimask2, minicombined;
+        struct flow_wildcards mask, mask2, combined, combined2;
+        uint32_t storage[FLOW_U32S];
+        struct flow flow2;
+
+        mask.masks = flow;
+        minimask_init(&minimask, &mask);
+
+        minimask_combine(&minicombined, &minimask, &minicatchall, storage);
+        assert(minimask_is_catchall(&minicombined));
+
+        any_random_flow(&flow2);
+        mask2.masks = flow2;
+        minimask_init(&minimask2, &mask2);
+
+        minimask_combine(&minicombined, &minimask, &minimask2, storage);
+        flow_wildcards_combine(&combined, &mask, &mask2);
+        minimask_expand(&minicombined, &combined2);
+        assert(flow_wildcards_equal(&combined, &combined2));
+
+        minimask_destroy(&minimask);
+        minimask_destroy(&minimask2);
+    }
+}
+\f
 static const struct command commands[] = {
+    /* Classifier tests. */
     {"empty", 0, 0, test_empty},
     {"destroy-null", 0, 0, test_destroy_null},
     {"single-rule", 0, 0, test_single_rule},
@@ -959,6 +1272,12 @@ static const struct command commands[] = {
     {"many-rules-in-one-table", 0, 0, test_many_rules_in_one_table},
     {"many-rules-in-two-tables", 0, 0, test_many_rules_in_two_tables},
     {"many-rules-in-five-tables", 0, 0, test_many_rules_in_five_tables},
+
+    /* Miniflow and minimask tests. */
+    {"miniflow", 0, 0, test_miniflow},
+       {"minimask_has_extra", 0, 0, test_minimask_has_extra},
+       {"minimask_combine", 0, 0, test_minimask_combine},
+
     {NULL, 0, 0, NULL},
 };