+\f
+/* Datapath Classifier. */
+
+/* A set of rules that all have the same fields wildcarded. */
+struct dpcls_subtable {
+ /* The fields are only used by writers. */
+ struct cmap_node cmap_node OVS_GUARDED; /* Within dpcls 'subtables_map'. */
+
+ /* These fields are accessed by readers. */
+ struct cmap rules; /* Contains "struct dpcls_rule"s. */
+ struct netdev_flow_key mask; /* Wildcards for fields (const). */
+ /* 'mask' must be the last field, additional space is allocated here. */
+};
+
+/* Initializes 'cls' as a classifier that initially contains no classification
+ * rules. */
+static void
+dpcls_init(struct dpcls *cls)
+{
+ cmap_init(&cls->subtables_map);
+ pvector_init(&cls->subtables);
+}
+
+static void
+dpcls_destroy_subtable(struct dpcls *cls, struct dpcls_subtable *subtable)
+{
+ pvector_remove(&cls->subtables, subtable);
+ cmap_remove(&cls->subtables_map, &subtable->cmap_node,
+ subtable->mask.hash);
+ cmap_destroy(&subtable->rules);
+ ovsrcu_postpone(free, subtable);
+}
+
+/* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the
+ * caller's responsibility.
+ * May only be called after all the readers have been terminated. */
+static void
+dpcls_destroy(struct dpcls *cls)
+{
+ if (cls) {
+ struct dpcls_subtable *subtable;
+
+ CMAP_FOR_EACH (subtable, cmap_node, &cls->subtables_map) {
+ ovs_assert(cmap_count(&subtable->rules) == 0);
+ dpcls_destroy_subtable(cls, subtable);
+ }
+ cmap_destroy(&cls->subtables_map);
+ pvector_destroy(&cls->subtables);
+ }
+}
+
+static struct dpcls_subtable *
+dpcls_create_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
+{
+ struct dpcls_subtable *subtable;
+
+ /* Need to add one. */
+ subtable = xmalloc(sizeof *subtable
+ - sizeof subtable->mask.mf + mask->len);
+ cmap_init(&subtable->rules);
+ netdev_flow_key_clone(&subtable->mask, mask);
+ cmap_insert(&cls->subtables_map, &subtable->cmap_node, mask->hash);
+ pvector_insert(&cls->subtables, subtable, 0);
+ pvector_publish(&cls->subtables);
+
+ return subtable;
+}
+
+static inline struct dpcls_subtable *
+dpcls_find_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
+{
+ struct dpcls_subtable *subtable;
+
+ CMAP_FOR_EACH_WITH_HASH (subtable, cmap_node, mask->hash,
+ &cls->subtables_map) {
+ if (netdev_flow_key_equal(&subtable->mask, mask)) {
+ return subtable;
+ }
+ }
+ return dpcls_create_subtable(cls, mask);
+}
+
+/* Insert 'rule' into 'cls'. */
+static void
+dpcls_insert(struct dpcls *cls, struct dpcls_rule *rule,
+ const struct netdev_flow_key *mask)
+{
+ struct dpcls_subtable *subtable = dpcls_find_subtable(cls, mask);
+
+ rule->mask = &subtable->mask;
+ cmap_insert(&subtable->rules, &rule->cmap_node, rule->flow.hash);
+}
+
+/* Removes 'rule' from 'cls', also destructing the 'rule'. */
+static void
+dpcls_remove(struct dpcls *cls, struct dpcls_rule *rule)
+{
+ struct dpcls_subtable *subtable;
+
+ ovs_assert(rule->mask);
+
+ INIT_CONTAINER(subtable, rule->mask, mask);
+
+ if (cmap_remove(&subtable->rules, &rule->cmap_node, rule->flow.hash)
+ == 0) {
+ dpcls_destroy_subtable(cls, subtable);
+ pvector_publish(&cls->subtables);
+ }
+}
+
+/* Returns true if 'target' satisfies 'key' in 'mask', that is, if each 1-bit
+ * in 'mask' the values in 'key' and 'target' are the same. */
+static inline bool
+dpcls_rule_matches_key(const struct dpcls_rule *rule,
+ const struct netdev_flow_key *target)
+{
+ const uint64_t *keyp = miniflow_get_values(&rule->flow.mf);
+ const uint64_t *maskp = miniflow_get_values(&rule->mask->mf);
+ uint64_t value;
+
+ NETDEV_FLOW_KEY_FOR_EACH_IN_FLOWMAP(value, target, rule->flow.mf.map) {
+ if (OVS_UNLIKELY((value & *maskp++) != *keyp++)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* For each miniflow in 'flows' performs a classifier lookup writing the result
+ * into the corresponding slot in 'rules'. If a particular entry in 'flows' is
+ * NULL it is skipped.
+ *
+ * This function is optimized for use in the userspace datapath and therefore
+ * does not implement a lot of features available in the standard
+ * classifier_lookup() function. Specifically, it does not implement
+ * priorities, instead returning any rule which matches the flow.
+ *
+ * Returns true if all flows found a corresponding rule. */
+static bool
+dpcls_lookup(const struct dpcls *cls, const struct netdev_flow_key keys[],
+ struct dpcls_rule **rules, const size_t cnt)
+{
+ /* The batch size 16 was experimentally found faster than 8 or 32. */
+ typedef uint16_t map_type;
+#define MAP_BITS (sizeof(map_type) * CHAR_BIT)
+
+#if !defined(__CHECKER__) && !defined(_WIN32)
+ const int N_MAPS = DIV_ROUND_UP(cnt, MAP_BITS);
+#else
+ enum { N_MAPS = DIV_ROUND_UP(NETDEV_MAX_BURST, MAP_BITS) };
+#endif
+ map_type maps[N_MAPS];
+ struct dpcls_subtable *subtable;
+
+ memset(maps, 0xff, sizeof maps);
+ if (cnt % MAP_BITS) {
+ maps[N_MAPS - 1] >>= MAP_BITS - cnt % MAP_BITS; /* Clear extra bits. */
+ }
+ memset(rules, 0, cnt * sizeof *rules);
+
+ PVECTOR_FOR_EACH (subtable, &cls->subtables) {
+ const struct netdev_flow_key *mkeys = keys;
+ struct dpcls_rule **mrules = rules;
+ map_type remains = 0;
+ int m;
+
+ BUILD_ASSERT_DECL(sizeof remains == sizeof *maps);
+
+ for (m = 0; m < N_MAPS; m++, mkeys += MAP_BITS, mrules += MAP_BITS) {
+ uint32_t hashes[MAP_BITS];
+ const struct cmap_node *nodes[MAP_BITS];
+ unsigned long map = maps[m];
+ int i;
+
+ if (!map) {
+ continue; /* Skip empty maps. */
+ }
+
+ /* Compute hashes for the remaining keys. */
+ ULLONG_FOR_EACH_1(i, map) {
+ hashes[i] = netdev_flow_key_hash_in_mask(&mkeys[i],
+ &subtable->mask);
+ }
+ /* Lookup. */
+ map = cmap_find_batch(&subtable->rules, map, hashes, nodes);
+ /* Check results. */
+ ULLONG_FOR_EACH_1(i, map) {
+ struct dpcls_rule *rule;
+
+ CMAP_NODE_FOR_EACH (rule, cmap_node, nodes[i]) {
+ if (OVS_LIKELY(dpcls_rule_matches_key(rule, &mkeys[i]))) {
+ mrules[i] = rule;
+ goto next;
+ }
+ }
+ ULLONG_SET0(map, i); /* Did not match. */
+ next:
+ ; /* Keep Sparse happy. */
+ }
+ maps[m] &= ~map; /* Clear the found rules. */
+ remains |= maps[m];
+ }
+ if (!remains) {
+ return true; /* All found. */
+ }
+ }
+ return false; /* Some misses. */
+}