[ovs-dev] [PATCH 10/10] lib/classifier: Support variable sized miniflows.

Kmindg G kmindg at gmail.com
Sun Apr 20 04:51:02 UTC 2014


On Sat, Apr 19, 2014 at 3:42 AM, Jarno Rajahalme <jrajahalme at nicira.com> wrote:
> Change the classifier to allocate variable sized miniflows and
> minimasks in cls_match and cls_subtable, respectively.  Do not
> duplicate the mask in cls_rule any more.
>
> miniflow_clone and miniflow_move can now take variably sized miniflows
> as source.  The destination is assumed to be regularly sized miniflow.
>
> Inlining miniflow and mask values reduces memory indirection and helps
> reduce cache misses.
>
> Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
> ---
>  lib/classifier.c |   82 +++++++++++++++++++++++++++++++++++++++---------------
>  lib/flow.c       |   55 +++++++++++++++++++++++++++++++-----
>  lib/flow.h       |   30 ++++++++++++++++++--
>  3 files changed, 134 insertions(+), 33 deletions(-)
>
> diff --git a/lib/classifier.c b/lib/classifier.c
> index 75befad..1198a76 100644
> --- a/lib/classifier.c
> +++ b/lib/classifier.c
> @@ -40,7 +40,6 @@ struct cls_trie {
>
>  struct cls_subtable_entry {
>      struct cls_subtable *subtable;
> -    const uint32_t *mask_values;
>      tag_type tag;
>      unsigned int max_priority;
>  };
> @@ -72,7 +71,6 @@ struct cls_subtable {
>      struct hmap_node hmap_node; /* Within struct cls_classifier 'subtables'
>                                   * hmap. */
>      struct hmap rules;          /* Contains "struct cls_rule"s. */
> -    struct minimask mask;       /* Wildcards for fields. */
>      int n_rules;                /* Number of rules, including duplicates. */
>      unsigned int max_priority;  /* Max priority of any rule in the subtable. */
>      unsigned int max_count;     /* Count of max_priority rules. */
> @@ -81,6 +79,8 @@ struct cls_subtable {
>      uint8_t index_ofs[CLS_MAX_INDICES]; /* u32 flow segment boundaries. */
>      struct hindex indices[CLS_MAX_INDICES]; /* Staged lookup indices. */
>      unsigned int trie_plen[CLS_MAX_TRIES];  /* Trie prefix length in 'mask'. */
> +    struct minimask mask;       /* Wildcards for fields. */
> +    /* 'mask' must be the last field. */
>  };
>
>  /* Associates a metadata value (that is, a value of the OpenFlow 1.1+ metadata
> @@ -103,16 +103,21 @@ struct cls_match {
>      unsigned int priority;      /* Larger numbers are higher priorities. */
>      struct cls_partition *partition;
>      struct list list;           /* List of identical, lower-priority rules. */
> -    struct minimatch match;     /* Matching rule. */
> +    struct miniflow flow;       /* Matching rule. Mask is in the subtable. */
> +    /* 'flow' must be the last field. */
>  };
>
>  static struct cls_match *
>  cls_match_alloc(struct cls_rule *rule)
>  {
> -    struct cls_match *cls_match = xmalloc(sizeof *cls_match);
> +    int count = count_1bits(rule->match.flow.map);
> +
> +    struct cls_match *cls_match
> +        = xmalloc(sizeof *cls_match - sizeof cls_match->flow.inline_values
> +                  + MINIFLOW_VALUES_SIZE(count));

Would it lead to a potential array access violation problem when
'sizeof cls_match->flow.inline_values' is bigger than
'MINIFLOW_VALUES_SIZE(count)'?

>
>      cls_match->cls_rule = rule;
> -    minimatch_clone(&cls_match->match, &rule->match);
> +    miniflow_clone_inline(&cls_match->flow, &rule->match.flow, count);
>      cls_match->priority = rule->priority;
>      rule->cls_match = cls_match;
>
> @@ -875,7 +880,6 @@ static inline void
>  lookahead_subtable(const struct cls_subtable_entry *subtables)
>  {
>      ovs_prefetch_range(subtables->subtable, sizeof *subtables->subtable);
> -    ovs_prefetch_range(subtables->mask_values, 1);
>  }
>
>  /* Finds and returns the highest-priority rule in 'cls' that matches 'flow'.
> @@ -969,16 +973,19 @@ classifier_lookup(const struct classifier *cls_, const struct flow *flow,
>  }
>
>  /* Returns true if 'target' satisifies 'match', that is, if each bit for which
> - * 'match' specifies a particular value has the correct value in 'target'. */
> + * 'match' specifies a particular value has the correct value in 'target'.
> + *
> + * 'flow' and 'mask' have the same mask! */
>  static bool
> -minimatch_matches_miniflow(const struct minimatch *match,
> -                           const struct miniflow *target)
> +miniflow_and_mask_matches_miniflow(const struct miniflow *flow,
> +                                   const struct minimask *mask,
> +                                   const struct miniflow *target)
>  {
> -    const uint32_t *flowp = miniflow_get_u32_values(&match->flow);
> -    const uint32_t *maskp = miniflow_get_u32_values(&match->mask.masks);
> +    const uint32_t *flowp = miniflow_get_u32_values(flow);
> +    const uint32_t *maskp = miniflow_get_u32_values(&mask->masks);
>      uint32_t target_u32;
>
> -    MINIFLOW_FOR_EACH_IN_MAP(target_u32, target, match->mask.masks.map) {
> +    MINIFLOW_FOR_EACH_IN_MAP(target_u32, target, mask->masks.map) {
>          if ((*flowp++ ^ target_u32) & *maskp++) {
>              return false;
>          }
> @@ -995,7 +1002,8 @@ find_match_miniflow(const struct cls_subtable *subtable,
>      struct cls_match *rule;
>
>      HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &subtable->rules) {
> -        if (minimatch_matches_miniflow(&rule->match, flow)) {
> +        if (miniflow_and_mask_matches_miniflow(&rule->flow, &subtable->mask,
> +                                               flow)) {
>              return rule;
>          }
>      }
> @@ -1113,7 +1121,7 @@ classifier_rule_overlaps(const struct classifier *cls_,
>                  }
>                  if (rule->priority == target->priority
>                      && miniflow_equal_in_minimask(&target->match.flow,
> -                                                  &rule->match.flow, &mask)) {
> +                                                  &rule->flow, &mask)) {
>                      return true;
>                  }
>              }
> @@ -1171,7 +1179,7 @@ static bool
>  rule_matches(const struct cls_match *rule, const struct cls_rule *target)
>  {
>      return (!target
> -            || miniflow_equal_in_minimask(&rule->match.flow,
> +            || miniflow_equal_in_minimask(&rule->flow,
>                                            &target->match.flow,
>                                            &target->match.mask));
>  }
> @@ -1285,10 +1293,12 @@ insert_subtable(struct cls_classifier *cls, const struct minimask *mask)
>      struct flow_wildcards old, new;
>      uint8_t prev;
>      struct cls_subtable_entry elem;
> +    int count = count_1bits(mask->masks.map);
>
> -    subtable = xzalloc(sizeof *subtable);
> +    subtable = xzalloc(sizeof *subtable - sizeof mask->masks.inline_values
> +                       + MINIFLOW_VALUES_SIZE(count));
>      hmap_init(&subtable->rules);
> -    minimask_clone(&subtable->mask, mask);
> +    miniflow_clone_inline(&subtable->mask.masks, &mask->masks, count);
>
>      /* Init indices for segmented lookup, if any. */
>      flow_wildcards_init_catchall(&new);
> @@ -1329,7 +1339,6 @@ insert_subtable(struct cls_classifier *cls, const struct minimask *mask)
>
>      hmap_insert(&cls->subtables, &subtable->hmap_node, hash);
>      elem.subtable = subtable;
> -    elem.mask_values = miniflow_get_values(&subtable->mask.masks);
>      elem.tag = subtable->tag;
>      elem.max_priority = subtable->max_priority;
>      cls_subtable_cache_push_back(&cls->subtables_priority, elem);
> @@ -1525,6 +1534,31 @@ check_tries(struct trie_ctx trie_ctx[CLS_MAX_TRIES], unsigned int n_tries,
>      return false;
>  }
>
> +/* Returns true if 'target' satisifies 'flow'/'mask', that is, if each bit
> + * for which 'flow', for which 'mask' has a bit set, specifies a particular
> + * value has the correct value in 'target'.
> + *
> + * This function is equivalent to miniflow_equal_flow_in_minimask(flow,
> + * target, mask) but it is faster because of the invariant that
> + * flow->map and mask->masks.map are the same. */
> +static inline bool
> +miniflow_and_mask_matches_flow(const struct miniflow *flow,
> +                               const struct minimask *mask,
> +                               const struct flow *target)
> +{
> +    const uint32_t *flowp = miniflow_get_u32_values(flow);
> +    const uint32_t *maskp = miniflow_get_u32_values(&mask->masks);
> +    uint32_t target_u32;
> +
> +    FLOW_FOR_EACH_IN_MAP(target_u32, target, mask->masks.map) {
> +        if ((*flowp++ ^ target_u32) & *maskp++) {
> +            return false;
> +        }
> +    }
> +
> +    return true;
> +}
> +
>  static inline struct cls_match *
>  find_match(const struct cls_subtable *subtable, const struct flow *flow,
>             uint32_t hash)
> @@ -1532,7 +1566,8 @@ find_match(const struct cls_subtable *subtable, const struct flow *flow,
>      struct cls_match *rule;
>
>      HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &subtable->rules) {
> -        if (minimatch_matches_flow(&rule->match, flow)) {
> +        if (miniflow_and_mask_matches_flow(&rule->flow, &subtable->mask,
> +                                           flow)) {
>              return rule;
>          }
>      }
> @@ -1580,8 +1615,8 @@ find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
>           * not match, then we know that we will never get a match, but we do
>           * not yet know how many wildcards we need to fold into 'wc' so we
>           * continue iterating through indices to find that out.  (We won't
> -         * waste time calling minimatch_matches_flow() again because we've set
> -         * 'rule' nonnull.)
> +         * waste time calling miniflow_and_mask_matches_flow() again because
> +         * we've set 'rule' nonnull.)
>           *
>           * This check shows a measurable benefit with non-trivial flow tables.
>           *
> @@ -1589,7 +1624,8 @@ find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
>           * optimization. */
>          if (!inode->s && !rule) {
>              ASSIGN_CONTAINER(rule, inode - i, index_nodes);
> -            if (minimatch_matches_flow(&rule->match, flow)) {
> +            if (miniflow_and_mask_matches_flow(&rule->flow, &subtable->mask,
> +                                               flow)) {
>                  goto out;
>              }
>          }
> @@ -1629,7 +1665,7 @@ find_equal(struct cls_subtable *subtable, const struct miniflow *flow,
>      struct cls_match *head;
>
>      HMAP_FOR_EACH_WITH_HASH (head, hmap_node, hash, &subtable->rules) {
> -        if (miniflow_equal(&head->match.flow, flow)) {
> +        if (miniflow_equal(&head->flow, flow)) {
>              return head;
>          }
>      }
> diff --git a/lib/flow.c b/lib/flow.c
> index 8e8e67e..1f65001 100644
> --- a/lib/flow.c
> +++ b/lib/flow.c
> @@ -1581,13 +1581,15 @@ miniflow_n_values(const struct miniflow *flow)
>  static uint32_t *
>  miniflow_alloc_values(struct miniflow *flow, int n)
>  {
> -    if (n <= sizeof flow->inline_values / sizeof(uint32_t)) {
> +    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);
>          flow->values_inline = false;
> -        flow->offline_values = xmalloc(n * sizeof(uint32_t));
> +        flow->offline_values = xmalloc(size);
>          return flow->offline_values;
>      }
>  }
> @@ -1655,18 +1657,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;
> -    memcpy(miniflow_alloc_values(dst, n), miniflow_get_values(src),
> -           n * sizeof(uint32_t));
> +    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;
> +    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)
>  {
> -    *dst = *src;
> +    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_inline = false;
> +        dst->offline_values = src->offline_values;
> +    }
>  }
>
>  /* Frees any memory owned by 'flow'.  Does not free the storage in which 'flow'
> diff --git a/lib/flow.h b/lib/flow.h
> index c508e1e..324997f 100644
> --- a/lib/flow.h
> +++ b/lib/flow.h
> @@ -358,14 +358,18 @@ struct miniflow {
>      };
>  };
>
> +#define MINIFLOW_VALUES_SIZE(COUNT) ((COUNT) * sizeof(uint32_t))
> +
>  static inline uint32_t *miniflow_values(struct miniflow *mf)
>  {
> -    return mf->values_inline ? mf->inline_values : mf->offline_values;
> +    return OVS_LIKELY(mf->values_inline)
> +        ? mf->inline_values : mf->offline_values;
>  }
>
>  static inline const uint32_t *miniflow_get_values(const struct miniflow *mf)
>  {
> -    return mf->values_inline ? mf->inline_values : mf->offline_values;
> +    return OVS_LIKELY(mf->values_inline)
> +        ? mf->inline_values : mf->offline_values;
>  }
>
>  static inline const uint32_t *miniflow_get_u32_values(const struct miniflow *mf)
> @@ -400,11 +404,31 @@ void miniflow_init(struct miniflow *, const struct flow *);
>  void miniflow_init_with_minimask(struct miniflow *, const struct flow *,
>                                   const struct minimask *);
>  void miniflow_clone(struct miniflow *, const struct miniflow *);
> +void miniflow_clone_inline(struct miniflow *, const struct miniflow *,
> +                           size_t n_values);
>  void miniflow_move(struct miniflow *dst, struct miniflow *);
>  void miniflow_destroy(struct miniflow *);
>
>  void miniflow_expand(const struct miniflow *, struct flow *);
>
> +static inline uint32_t
> +flow_get_next_in_map(const struct flow *flow, uint64_t map, uint32_t *value)
> +{
> +    if (map) {
> +        *value = ((const uint32_t *)flow)[raw_ctz(map)];
> +        return true;
> +    }
> +    return false;
> +}
> +
> +/* Iterate through all flow u32 values specified by 'MAP'.
> + * This works as the first statement in a block.*/
> +#define FLOW_FOR_EACH_IN_MAP(VALUE, FLOW, MAP)                          \
> +    uint64_t map_;                                                      \
> +    for (map_ = (MAP);                                                  \
> +         flow_get_next_in_map(FLOW, map_, &(VALUE));                    \
> +         map_ = zero_rightmost_1bit(map_))
> +
>  #define FLOW_U32_SIZE(FIELD)                                            \
>      DIV_ROUND_UP(sizeof(((struct flow *)0)->FIELD), sizeof(uint32_t))
>
> @@ -429,7 +453,7 @@ mf_get_next_in_map(uint64_t *fmap, uint64_t rm1bit, const uint32_t **fp,
>      return rm1bit != 0;
>  }
>
> -/* Iterate through all miniflow u32 values specified by the 'MAP'.
> +/* Iterate through all miniflow u32 values specified by 'MAP'.
>   * This works as the first statement in a block.*/
>  #define MINIFLOW_FOR_EACH_IN_MAP(VALUE, FLOW, MAP)                      \
>      const uint32_t *fp_ = miniflow_get_u32_values(FLOW);                \
> --
> 1.7.10.4
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list