Prepare include headers
[cascardo/ovs.git] / lib / flow.c
index 8319dde..2e5ca0a 100644 (file)
@@ -118,10 +118,10 @@ struct mf_ctx {
 /* miniflow_push_* macros allow filling in a miniflow data values in order.
  * Assertions are needed only when the layout of the struct flow is modified.
  * 'ofs' is a compile-time constant, which allows most of the code be optimized
- * away.  Some GCC versions gave warnigns on ALWAYS_INLINE, so these are
+ * away.  Some GCC versions gave warnings on ALWAYS_INLINE, so these are
  * defined as macros. */
 
-#if (FLOW_WC_SEQ != 26)
+#if (FLOW_WC_SEQ != 27)
 #define MINIFLOW_ASSERT(X) ovs_assert(X)
 #else
 #define MINIFLOW_ASSERT(X)
@@ -277,7 +277,7 @@ parse_icmpv6(void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
         (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
          icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
 
-        *nd_target = data_try_pull(datap, sizep, sizeof *nd_target);
+        *nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
         if (OVS_UNLIKELY(!*nd_target)) {
             return false;
         }
@@ -344,26 +344,29 @@ void
 flow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
              struct flow *flow)
 {
-    uint32_t buf[FLOW_U32S];
-    struct miniflow mf;
+    struct {
+        struct miniflow mf;
+        uint32_t buf[FLOW_U32S];
+    } m;
 
     COVERAGE_INC(flow_extract);
 
-    miniflow_initialize(&mf, buf);
-    miniflow_extract(packet, md, &mf);
-    miniflow_expand(&mf, flow);
+    miniflow_initialize(&m.mf, m.buf);
+    miniflow_extract(packet, md, &m.mf);
+    miniflow_expand(&m.mf, flow);
 }
 
-/* Caller is responsible for initializing 'dst->values' with enough storage
- * (FLOW_U64S * 8 bytes is enough). */
+/* Caller is responsible for initializing 'dst' with enough storage for
+ * FLOW_U32S * 4 bytes. */
 void
 miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
                  struct miniflow *dst)
 {
     void *data = ofpbuf_data(packet);
     size_t size = ofpbuf_size(packet);
+    uint32_t *values = miniflow_values(dst);
+    struct mf_ctx mf = { 0, values, values + FLOW_U32S };
     char *l2;
-    struct mf_ctx mf = { 0, dst->values, dst->values + FLOW_U32S };
     ovs_be16 dl_type;
     uint8_t nw_frag, nw_tos, nw_ttl, nw_proto;
 
@@ -595,6 +598,15 @@ miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
                 miniflow_push_be16(mf, tp_src, htons(icmp->icmp_type));
                 miniflow_push_be16(mf, tp_dst, htons(icmp->icmp_code));
             }
+        } else if (OVS_LIKELY(nw_proto == IPPROTO_IGMP)) {
+            if (OVS_LIKELY(size >= IGMP_HEADER_LEN)) {
+                const struct igmp_header *igmp = data;
+
+                miniflow_push_be16(mf, tp_src, htons(igmp->igmp_type));
+                miniflow_push_be16(mf, tp_dst, htons(igmp->igmp_code));
+                miniflow_push_be32(mf, igmp_group_ip4,
+                                   get_16aligned_be32(&igmp->group));
+            }
         } else if (OVS_LIKELY(nw_proto == IPPROTO_ICMPV6)) {
             if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) {
                 const struct in6_addr *nd_target = NULL;
@@ -604,12 +616,12 @@ miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
                 memset(arp_buf, 0, sizeof arp_buf);
                 if (OVS_LIKELY(parse_icmpv6(&data, &size, icmp, &nd_target,
                                             arp_buf))) {
+                    miniflow_push_words(mf, arp_sha, arp_buf,
+                                             ETH_ADDR_LEN * 2 / 4);
                     if (nd_target) {
                         miniflow_push_words(mf, nd_target, nd_target,
                                             sizeof *nd_target / 4);
                     }
-                    miniflow_push_words(mf, arp_sha, arp_buf,
-                                             ETH_ADDR_LEN * 2 / 4);
                     miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
                     miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
                 }
@@ -653,7 +665,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 26);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
 
     fmd->dp_hash = flow->dp_hash;
     fmd->recirc_id = flow->recirc_id;
@@ -684,6 +696,8 @@ flow_tun_flag_to_string(uint32_t flags)
         return "csum";
     case FLOW_TNL_F_KEY:
         return "key";
+    case FLOW_TNL_F_OAM:
+        return "oam";
     default:
         return NULL;
     }
@@ -823,65 +837,6 @@ flow_wildcards_or(struct flow_wildcards *dst,
     }
 }
 
-/* Perform a bitwise OR of miniflow 'src' flow data with the equivalent
- * fields in 'dst', storing the result in 'dst'. */
-static void
-flow_union_with_miniflow(struct flow *dst, const struct miniflow *src)
-{
-    uint32_t *dst_u32 = (uint32_t *) dst;
-    const uint32_t *p = src->values;
-    uint64_t map;
-
-    for (map = src->map; map; map = zero_rightmost_1bit(map)) {
-        dst_u32[raw_ctz(map)] |= *p++;
-    }
-}
-
-/* Fold minimask 'mask''s wildcard mask into 'wc's wildcard mask. */
-void
-flow_wildcards_fold_minimask(struct flow_wildcards *wc,
-                             const struct minimask *mask)
-{
-    flow_union_with_miniflow(&wc->masks, &mask->masks);
-}
-
-uint64_t
-miniflow_get_map_in_range(const struct miniflow *miniflow,
-                          uint8_t start, uint8_t end, unsigned int *offset)
-{
-    uint64_t map = miniflow->map;
-    *offset = 0;
-
-    if (start > 0) {
-        uint64_t msk = (UINT64_C(1) << start) - 1; /* 'start' LSBs set */
-        *offset = count_1bits(map & msk);
-        map &= ~msk;
-    }
-    if (end < FLOW_U32S) {
-        uint64_t msk = (UINT64_C(1) << end) - 1; /* 'end' LSBs set */
-        map &= msk;
-    }
-    return map;
-}
-
-/* Fold minimask 'mask''s wildcard mask into 'wc's wildcard mask
- * in range [start, end). */
-void
-flow_wildcards_fold_minimask_range(struct flow_wildcards *wc,
-                                   const struct minimask *mask,
-                                   uint8_t start, uint8_t end)
-{
-    uint32_t *dst_u32 = (uint32_t *)&wc->masks;
-    unsigned int offset;
-    uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end,
-                                             &offset);
-    const uint32_t *p = mask->masks.values + offset;
-
-    for (; map; map = zero_rightmost_1bit(map)) {
-        dst_u32[raw_ctz(map)] |= *p++;
-    }
-}
-
 /* Returns a hash of the wildcards in 'wc'. */
 uint32_t
 flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
@@ -943,23 +898,84 @@ flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
     wc->masks.regs[idx] = mask;
 }
 
+/* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
+ * (A 0-bit indicates a wildcard bit.) */
+void
+flow_wildcards_set_xreg_mask(struct flow_wildcards *wc, int idx, uint64_t mask)
+{
+    flow_set_xreg(&wc->masks, idx, mask);
+}
+
+/* Calculates the 5-tuple hash from the given miniflow.
+ * This returns the same value as flow_hash_5tuple for the corresponding
+ * flow. */
+uint32_t
+miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
+{
+    uint32_t hash = basis;
+
+    if (flow) {
+        ovs_be16 dl_type = MINIFLOW_GET_BE16(flow, dl_type);
+
+        hash = hash_add(hash, MINIFLOW_GET_U8(flow, nw_proto));
+
+        /* Separate loops for better optimization. */
+        if (dl_type == htons(ETH_TYPE_IPV6)) {
+            uint64_t map = MINIFLOW_MAP(ipv6_src) | MINIFLOW_MAP(ipv6_dst)
+                | MINIFLOW_MAP(tp_src); /* Covers both ports */
+            uint32_t value;
+
+            MINIFLOW_FOR_EACH_IN_MAP(value, flow, map) {
+                hash = hash_add(hash, value);
+            }
+        } else {
+            uint64_t map = MINIFLOW_MAP(nw_src) | MINIFLOW_MAP(nw_dst)
+                | MINIFLOW_MAP(tp_src); /* Covers both ports */
+            uint32_t value;
+
+            MINIFLOW_FOR_EACH_IN_MAP(value, flow, map) {
+                hash = hash_add(hash, value);
+            }
+        }
+        hash = hash_finish(hash, 42); /* Arbitrary number. */
+    }
+    return hash;
+}
+
+BUILD_ASSERT_DECL(offsetof(struct flow, tp_src) + 2
+                  == offsetof(struct flow, tp_dst) &&
+                  offsetof(struct flow, tp_src) / 4
+                  == offsetof(struct flow, tp_dst) / 4);
+BUILD_ASSERT_DECL(offsetof(struct flow, ipv6_src) + 16
+                  == offsetof(struct flow, ipv6_dst));
+
 /* Calculates the 5-tuple hash from the given flow. */
 uint32_t
 flow_hash_5tuple(const struct flow *flow, uint32_t basis)
 {
-    uint32_t hash = 0;
+    uint32_t hash = basis;
 
-    if (!flow) {
-        return 0;
-    }
+    if (flow) {
+        const uint32_t *flow_u32 = (const uint32_t *)flow;
 
-    hash = mhash_add(basis, (OVS_FORCE uint32_t) flow->nw_src);
-    hash = mhash_add(hash, (OVS_FORCE uint32_t) flow->nw_dst);
-    hash = mhash_add(hash, ((OVS_FORCE uint32_t) flow->tp_src << 16)
-                           | (OVS_FORCE uint32_t) flow->tp_dst);
-    hash = mhash_add(hash, flow->nw_proto);
+        hash = hash_add(hash, flow->nw_proto);
+
+        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+            int ofs = offsetof(struct flow, ipv6_src) / 4;
+            int end = ofs + 2 * sizeof flow->ipv6_src / 4;
+
+            while (ofs < end) {
+                hash = hash_add(hash, flow_u32[ofs++]);
+            }
+        } else {
+            hash = hash_add(hash, (OVS_FORCE uint32_t) flow->nw_src);
+            hash = hash_add(hash, (OVS_FORCE uint32_t) flow->nw_dst);
+        }
+        hash = hash_add(hash, flow_u32[offsetof(struct flow, tp_src) / 4]);
 
-    return mhash_finish(hash, 13);
+        hash = hash_finish(hash, 42); /* Arbitrary number. */
+    }
+    return hash;
 }
 
 /* Hashes 'flow' based on its L2 through L4 protocol information. */
@@ -1132,9 +1148,9 @@ flow_hash_in_wildcards(const struct flow *flow,
 
     hash = basis;
     for (i = 0; i < FLOW_U32S; i++) {
-        hash = mhash_add(hash, flow_u32[i] & wc_u32[i]);
+        hash = hash_add(hash, flow_u32[i] & wc_u32[i]);
     }
-    return mhash_finish(hash, 4 * FLOW_U32S);
+    return hash_finish(hash, 4 * FLOW_U32S);
 }
 
 /* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
@@ -1319,7 +1335,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
         flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
 
         /* Clear all L3 and L4 fields. */
-        BUILD_ASSERT(FLOW_WC_SEQ == 26);
+        BUILD_ASSERT(FLOW_WC_SEQ == 27);
         memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
                sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
     }
@@ -1430,6 +1446,15 @@ flow_compose_l4(struct ofpbuf *b, const struct flow *flow)
             icmp->icmp_type = ntohs(flow->tp_src);
             icmp->icmp_code = ntohs(flow->tp_dst);
             icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN);
+        } else if (flow->nw_proto == IPPROTO_IGMP) {
+            struct igmp_header *igmp;
+
+            l4_len = sizeof *igmp;
+            igmp = ofpbuf_put_zeros(b, l4_len);
+            igmp->igmp_type = ntohs(flow->tp_src);
+            igmp->igmp_code = ntohs(flow->tp_dst);
+            put_16aligned_be32(&igmp->group, flow->igmp_group_ip4);
+            igmp->igmp_csum = csum(igmp, IGMP_HEADER_LEN);
         } else if (flow->nw_proto == IPPROTO_ICMPV6) {
             struct icmp6_hdr *icmp;
 
@@ -1515,6 +1540,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
 
         l4_len = flow_compose_l4(b, flow);
 
+        ip = ofpbuf_l3(b);
         ip->ip_tot_len = htons(b->l4_ofs - b->l3_ofs + l4_len);
         ip->ip_csum = csum(ip, sizeof *ip);
     } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
@@ -1533,6 +1559,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
 
         l4_len = flow_compose_l4(b, flow);
 
+        nh = ofpbuf_l3(b);
         nh->ip6_plen = htons(l4_len);
     } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
                flow->dl_type == htons(ETH_TYPE_RARP)) {
@@ -1581,11 +1608,16 @@ miniflow_n_values(const struct miniflow *flow)
 static uint32_t *
 miniflow_alloc_values(struct miniflow *flow, int n)
 {
-    if (n <= MINI_N_INLINE) {
+    int size = MINIFLOW_VALUES_SIZE(n);
+
+    if (size <= sizeof flow->inline_values) {
+        flow->values_inline = true;
         return flow->inline_values;
     } else {
         COVERAGE_INC(miniflow_malloc);
-        return xmalloc(n * sizeof *flow->values);
+        flow->values_inline = false;
+        flow->offline_values = xmalloc(size);
+        return flow->offline_values;
     }
 }
 
@@ -1598,25 +1630,24 @@ miniflow_alloc_values(struct miniflow *flow, int n)
  * when a miniflow is initialized from a (mini)mask, the values can be zeroes,
  * so that the flow and mask always have the same maps.
  *
- * This function initializes 'dst->values' (either inline if possible or with
+ * This function initializes values (either inline if possible or with
  * malloc() otherwise) and copies the uint32_t elements of 'src' indicated by
  * 'dst->map' into it. */
 static void
 miniflow_init__(struct miniflow *dst, const struct flow *src, int n)
 {
     const uint32_t *src_u32 = (const uint32_t *) src;
-    unsigned int ofs;
+    uint32_t *dst_u32 = miniflow_alloc_values(dst, n);
     uint64_t map;
 
-    dst->values = miniflow_alloc_values(dst, n);
-    ofs = 0;
     for (map = dst->map; map; map = zero_rightmost_1bit(map)) {
-        dst->values[ofs++] = src_u32[raw_ctz(map)];
+        *dst_u32++ = src_u32[raw_ctz(map)];
     }
 }
 
 /* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
- * with miniflow_destroy(). */
+ * with miniflow_destroy().
+ * Always allocates offline storage. */
 void
 miniflow_init(struct miniflow *dst, const struct flow *src)
 {
@@ -1653,25 +1684,57 @@ miniflow_init_with_minimask(struct miniflow *dst, const struct flow *src,
 void
 miniflow_clone(struct miniflow *dst, const struct miniflow *src)
 {
-    int n = miniflow_n_values(src);
+    int size = MINIFLOW_VALUES_SIZE(miniflow_n_values(src));
+    uint32_t *values;
+
+    dst->map = src->map;
+    if (size <= sizeof dst->inline_values) {
+        dst->values_inline = true;
+        values = dst->inline_values;
+    } else {
+        dst->values_inline = false;
+        COVERAGE_INC(miniflow_malloc);
+        dst->offline_values = xmalloc(size);
+        values = dst->offline_values;
+    }
+    memcpy(values, miniflow_get_values(src), size);
+}
+
+/* Initializes 'dst' as a copy of 'src'.  The caller must have allocated
+ * 'dst' to have inline space all data in 'src'. */
+void
+miniflow_clone_inline(struct miniflow *dst, const struct miniflow *src,
+                      size_t n_values)
+{
+    dst->values_inline = true;
     dst->map = src->map;
-    dst->values = miniflow_alloc_values(dst, n);
-    memcpy(dst->values, src->values, n * sizeof *dst->values);
+    memcpy(dst->inline_values, miniflow_get_values(src),
+           MINIFLOW_VALUES_SIZE(n_values));
 }
 
 /* Initializes 'dst' with the data in 'src', destroying 'src'.
- * The caller must eventually free 'dst' with miniflow_destroy(). */
+ * The caller must eventually free 'dst' with miniflow_destroy().
+ * 'dst' must be regularly sized miniflow, but 'src' can have
+ * larger than default inline values. */
 void
 miniflow_move(struct miniflow *dst, struct miniflow *src)
 {
-    if (src->values == src->inline_values) {
-        dst->values = dst->inline_values;
-        memcpy(dst->values, src->values,
-               miniflow_n_values(src) * sizeof *dst->values);
+    int size = MINIFLOW_VALUES_SIZE(miniflow_n_values(src));
+
+    dst->map = src->map;
+    if (size <= sizeof dst->inline_values) {
+        dst->values_inline = true;
+        memcpy(dst->inline_values, miniflow_get_values(src), size);
+        miniflow_destroy(src);
+    } else if (src->values_inline) {
+        dst->values_inline = false;
+        COVERAGE_INC(miniflow_malloc);
+        dst->offline_values = xmalloc(size);
+        memcpy(dst->offline_values, src->inline_values, size);
     } else {
-        dst->values = src->values;
+        dst->values_inline = false;
+        dst->offline_values = src->offline_values;
     }
-    dst->map = src->map;
 }
 
 /* Frees any memory owned by 'flow'.  Does not free the storage in which 'flow'
@@ -1679,8 +1742,8 @@ miniflow_move(struct miniflow *dst, struct miniflow *src)
 void
 miniflow_destroy(struct miniflow *flow)
 {
-    if (flow->values != flow->inline_values) {
-        free(flow->values);
+    if (!flow->values_inline) {
+        free(flow->offline_values);
     }
 }
 
@@ -1698,7 +1761,7 @@ static uint32_t
 miniflow_get(const struct miniflow *flow, unsigned int u32_ofs)
 {
     return (flow->map & UINT64_C(1) << u32_ofs)
-        ? *(flow->values +
+        ? *(miniflow_get_u32_values(flow) +
             count_1bits(flow->map & ((UINT64_C(1) << u32_ofs) - 1)))
         : 0;
 }
@@ -1707,19 +1770,18 @@ miniflow_get(const struct miniflow *flow, unsigned int u32_ofs)
 bool
 miniflow_equal(const struct miniflow *a, const struct miniflow *b)
 {
-    const uint32_t *ap = a->values;
-    const uint32_t *bp = b->values;
+    const uint32_t *ap = miniflow_get_u32_values(a);
+    const uint32_t *bp = miniflow_get_u32_values(b);
     const uint64_t a_map = a->map;
     const uint64_t b_map = b->map;
-    uint64_t map;
 
-    if (a_map == b_map) {
-        for (map = a_map; map; map = zero_rightmost_1bit(map)) {
-            if (*ap++ != *bp++) {
-                return false;
-            }
-        }
+    if (OVS_LIKELY(a_map == b_map)) {
+        int count = miniflow_n_values(a);
+
+        return !memcmp(ap, bp, count * sizeof *ap);
     } else {
+        uint64_t map;
+
         for (map = a_map | b_map; map; map = zero_rightmost_1bit(map)) {
             uint64_t bit = rightmost_1bit(map);
             uint64_t a_value = a_map & bit ? *ap++ : 0;
@@ -1740,18 +1802,15 @@ bool
 miniflow_equal_in_minimask(const struct miniflow *a, const struct miniflow *b,
                            const struct minimask *mask)
 {
-    const uint32_t *p;
+    const uint32_t *p = miniflow_get_u32_values(&mask->masks);
     uint64_t map;
 
-    p = mask->masks.values;
-
     for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
         int ofs = raw_ctz(map);
 
-        if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p) {
+        if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p++) {
             return false;
         }
-        p++;
     }
 
     return true;
@@ -1764,112 +1823,20 @@ miniflow_equal_flow_in_minimask(const struct miniflow *a, const struct flow *b,
                                 const struct minimask *mask)
 {
     const uint32_t *b_u32 = (const uint32_t *) b;
-    const uint32_t *p;
+    const uint32_t *p = miniflow_get_u32_values(&mask->masks);
     uint64_t map;
 
-    p = mask->masks.values;
-
     for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
         int ofs = raw_ctz(map);
 
-        if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p) {
+        if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p++) {
             return false;
         }
-        p++;
     }
 
     return true;
 }
 
-/* Returns a hash value for 'flow', given 'basis'. */
-uint32_t
-miniflow_hash(const struct miniflow *flow, uint32_t basis)
-{
-    const uint32_t *p = flow->values;
-    uint32_t hash = basis;
-    uint64_t hash_map = 0;
-    uint64_t map;
-
-    for (map = flow->map; map; map = zero_rightmost_1bit(map)) {
-        if (*p) {
-            hash = mhash_add(hash, *p);
-            hash_map |= rightmost_1bit(map);
-        }
-        p++;
-    }
-    hash = mhash_add(hash, hash_map);
-    hash = mhash_add(hash, hash_map >> 32);
-
-    return mhash_finish(hash, p - flow->values);
-}
-
-/* Returns a hash value for the bits of 'flow' where there are 1-bits in
- * 'mask', given 'basis'.
- *
- * The hash values returned by this function are the same as those returned by
- * flow_hash_in_minimask(), only the form of the arguments differ. */
-uint32_t
-miniflow_hash_in_minimask(const struct miniflow *flow,
-                          const struct minimask *mask, uint32_t basis)
-{
-    const uint32_t *p = mask->masks.values;
-    uint32_t hash = basis;
-    uint32_t flow_u32;
-
-    MINIFLOW_FOR_EACH_IN_MAP(flow_u32, flow, mask->masks.map) {
-        hash = mhash_add(hash, flow_u32 & *p++);
-    }
-
-    return mhash_finish(hash, (p - mask->masks.values) * 4);
-}
-
-/* Returns a hash value for the bits of 'flow' where there are 1-bits in
- * 'mask', given 'basis'.
- *
- * The hash values returned by this function are the same as those returned by
- * miniflow_hash_in_minimask(), only the form of the arguments differ. */
-uint32_t
-flow_hash_in_minimask(const struct flow *flow, const struct minimask *mask,
-                      uint32_t basis)
-{
-    const uint32_t *flow_u32 = (const uint32_t *)flow;
-    const uint32_t *p = mask->masks.values;
-    uint32_t hash;
-    uint64_t map;
-
-    hash = basis;
-    for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
-        hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p++);
-    }
-
-    return mhash_finish(hash, (p - mask->masks.values) * 4);
-}
-
-/* Returns a hash value for the bits of range [start, end) in 'flow',
- * where there are 1-bits in 'mask', given 'hash'.
- *
- * The hash values returned by this function are the same as those returned by
- * minimatch_hash_range(), only the form of the arguments differ. */
-uint32_t
-flow_hash_in_minimask_range(const struct flow *flow,
-                            const struct minimask *mask,
-                            uint8_t start, uint8_t end, uint32_t *basis)
-{
-    const uint32_t *flow_u32 = (const uint32_t *)flow;
-    unsigned int offset;
-    uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end,
-                                             &offset);
-    const uint32_t *p = mask->masks.values + offset;
-    uint32_t hash = *basis;
-
-    for (; map; map = zero_rightmost_1bit(map)) {
-        hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p++);
-    }
-
-    *basis = hash; /* Allow continuation from the unfinished value. */
-    return mhash_finish(hash, (p - mask->masks.values) * 4);
-}
-
 \f
 /* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
  * with minimask_destroy(). */
@@ -1905,12 +1872,14 @@ minimask_combine(struct minimask *dst_,
                  uint32_t storage[FLOW_U32S])
 {
     struct miniflow *dst = &dst_->masks;
+    uint32_t *dst_values = storage;
     const struct miniflow *a = &a_->masks;
     const struct miniflow *b = &b_->masks;
     uint64_t map;
     int n = 0;
 
-    dst->values = storage;
+    dst->values_inline = false;
+    dst->offline_values = storage;
 
     dst->map = 0;
     for (map = a->map & b->map; map; map = zero_rightmost_1bit(map)) {
@@ -1919,7 +1888,7 @@ minimask_combine(struct minimask *dst_,
 
         if (mask) {
             dst->map |= rightmost_1bit(map);
-            dst->values[n++] = mask;
+            dst_values[n++] = mask;
         }
     }
 }
@@ -1954,26 +1923,17 @@ minimask_equal(const struct minimask *a, const struct minimask *b)
     return miniflow_equal(&a->masks, &b->masks);
 }
 
-/* Returns a hash value for 'mask', given 'basis'. */
-uint32_t
-minimask_hash(const struct minimask *mask, uint32_t basis)
-{
-    return miniflow_hash(&mask->masks, basis);
-}
-
-/* Returns true if at least one bit is wildcarded in 'a_' but not in 'b_',
+/* Returns true if at least one bit matched by 'b' is wildcarded by 'a',
  * false otherwise. */
 bool
-minimask_has_extra(const struct minimask *a_, const struct minimask *b_)
+minimask_has_extra(const struct minimask *a, const struct minimask *b)
 {
-    const struct miniflow *a = &a_->masks;
-    const struct miniflow *b = &b_->masks;
+    const uint32_t *p = miniflow_get_u32_values(&b->masks);
     uint64_t map;
 
-    for (map = a->map | b->map; map; map = zero_rightmost_1bit(map)) {
-        int ofs = raw_ctz(map);
-        uint32_t a_u32 = miniflow_get(a, ofs);
-        uint32_t b_u32 = miniflow_get(b, ofs);
+    for (map = b->masks.map; map; map = zero_rightmost_1bit(map)) {
+        uint32_t a_u32 = minimask_get(a, raw_ctz(map));
+        uint32_t b_u32 = *p++;
 
         if ((a_u32 & b_u32) != b_u32) {
             return true;
@@ -1982,20 +1942,3 @@ minimask_has_extra(const struct minimask *a_, const struct minimask *b_)
 
     return false;
 }
-
-/* Returns true if 'mask' matches every packet, false if 'mask' fixes any bits
- * or fields. */
-bool
-minimask_is_catchall(const struct minimask *mask_)
-{
-    const struct miniflow *mask = &mask_->masks;
-    const uint32_t *p = mask->values;
-    uint64_t map;
-
-    for (map = mask->map; map; map = zero_rightmost_1bit(map)) {
-        if (*p++) {
-            return false;
-        }
-    }
-    return true;
-}