/src/openvswitch/lib/classifier.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2017 Nicira, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #ifndef CLASSIFIER_H |
18 | | #define CLASSIFIER_H 1 |
19 | | |
20 | | /* Flow classifier. |
21 | | * |
22 | | * |
23 | | * What? |
24 | | * ===== |
25 | | * |
26 | | * A flow classifier holds any number of "rules", each of which specifies |
27 | | * values to match for some fields or subfields and a priority. Each OpenFlow |
28 | | * table is implemented as a flow classifier. |
29 | | * |
30 | | * The classifier has two primary design goals. The first is obvious: given a |
31 | | * set of packet headers, as quickly as possible find the highest-priority rule |
32 | | * that matches those headers. The following section describes the second |
33 | | * goal. |
34 | | * |
35 | | * |
36 | | * "Un-wildcarding" |
37 | | * ================ |
38 | | * |
39 | | * A primary goal of the flow classifier is to produce, as a side effect of a |
40 | | * packet lookup, a wildcard mask that indicates which bits of the packet |
41 | | * headers were essential to the classification result. Ideally, a 1-bit in |
42 | | * any position of this mask means that, if the corresponding bit in the packet |
43 | | * header were flipped, then the classification result might change. A 0-bit |
44 | | * means that changing the packet header bit would have no effect. Thus, the |
45 | | * wildcarded bits are the ones that played no role in the classification |
46 | | * decision. |
47 | | * |
48 | | * Such a wildcard mask is useful with datapaths that support installing flows |
49 | | * that wildcard fields or subfields. If an OpenFlow lookup for a TCP flow |
50 | | * does not actually look at the TCP source or destination ports, for example, |
51 | | * then the switch may install into the datapath a flow that wildcards the port |
52 | | * numbers, which in turn allows the datapath to handle packets that arrive for |
53 | | * other TCP source or destination ports without additional help from |
54 | | * ovs-vswitchd. This is useful for the Open vSwitch software and, |
55 | | * potentially, for ASIC-based switches as well. |
56 | | * |
57 | | * Some properties of the wildcard mask: |
58 | | * |
59 | | * - "False 1-bits" are acceptable, that is, setting a bit in the wildcard |
60 | | * mask to 1 will never cause a packet to be forwarded the wrong way. |
61 | | * As a corollary, a wildcard mask composed of all 1-bits will always |
62 | | * yield correct (but often needlessly inefficient) behavior. |
63 | | * |
64 | | * - "False 0-bits" can cause problems, so they must be avoided. In the |
65 | | * extreme case, a mask of all 0-bits is only correct if the classifier |
66 | | * contains only a single flow that matches all packets. |
67 | | * |
68 | | * - 0-bits are desirable because they allow the datapath to act more |
69 | | * autonomously, relying less on ovs-vswitchd to process flow setups, |
70 | | * thereby improving performance. |
71 | | * |
72 | | * - We don't know a good way to generate wildcard masks with the maximum |
73 | | * (correct) number of 0-bits. We use various approximations, described |
74 | | * in later sections. |
75 | | * |
76 | | * - Wildcard masks for lookups in a given classifier yield a |
77 | | * non-overlapping set of rules. More specifically: |
78 | | * |
79 | | * Consider an classifier C1 filled with an arbitrary collection of rules |
80 | | * and an empty classifier C2. Now take a set of packet headers H and |
81 | | * look it up in C1, yielding a highest-priority matching rule R1 and |
82 | | * wildcard mask M. Form a new classifier rule R2 out of packet headers |
83 | | * H and mask M, and add R2 to C2 with a fixed priority. If one were to |
84 | | * do this for every possible set of packet headers H, then this |
85 | | * process would not attempt to add any overlapping rules to C2, that is, |
86 | | * any packet lookup using the rules generated by this process matches at |
87 | | * most one rule in C2. |
88 | | * |
89 | | * During the lookup process, the classifier starts out with a wildcard mask |
90 | | * that is all 0-bits, that is, fully wildcarded. As lookup proceeds, each |
91 | | * step tends to add constraints to the wildcard mask, that is, change |
92 | | * wildcarded 0-bits into exact-match 1-bits. We call this "un-wildcarding". |
93 | | * A lookup step that examines a particular field must un-wildcard that field. |
94 | | * In general, un-wildcarding is necessary for correctness but undesirable for |
95 | | * performance. |
96 | | * |
97 | | * |
98 | | * Basic Classifier Design |
99 | | * ======================= |
100 | | * |
101 | | * Suppose that all the rules in a classifier had the same form. For example, |
102 | | * suppose that they all matched on the source and destination Ethernet address |
103 | | * and wildcarded all the other fields. Then the obvious way to implement a |
104 | | * classifier would be a hash table on the source and destination Ethernet |
105 | | * addresses. If new classification rules came along with a different form, |
106 | | * you could add a second hash table that hashed on the fields matched in those |
107 | | * rules. With two hash tables, you look up a given flow in each hash table. |
108 | | * If there are no matches, the classifier didn't contain a match; if you find |
109 | | * a match in one of them, that's the result; if you find a match in both of |
110 | | * them, then the result is the rule with the higher priority. |
111 | | * |
112 | | * This is how the classifier works. In a "struct classifier", each form of |
113 | | * "struct cls_rule" present (based on its ->match.mask) goes into a separate |
114 | | * "struct cls_subtable". A lookup does a hash lookup in every "struct |
115 | | * cls_subtable" in the classifier and tracks the highest-priority match that |
116 | | * it finds. The subtables are kept in a descending priority order according |
117 | | * to the highest priority rule in each subtable, which allows lookup to skip |
118 | | * over subtables that can't possibly have a higher-priority match than already |
119 | | * found. Eliminating lookups through priority ordering aids both classifier |
120 | | * primary design goals: skipping lookups saves time and avoids un-wildcarding |
121 | | * fields that those lookups would have examined. |
122 | | * |
123 | | * One detail: a classifier can contain multiple rules that are identical other |
124 | | * than their priority. When this happens, only the highest priority rule out |
125 | | * of a group of otherwise identical rules is stored directly in the "struct |
126 | | * cls_subtable", with the other almost-identical rules chained off a linked |
127 | | * list inside that highest-priority rule. |
128 | | * |
129 | | * The following sub-sections describe various optimizations over this simple |
130 | | * approach. |
131 | | * |
132 | | * |
133 | | * Staged Lookup (Wildcard Optimization) |
134 | | * ------------------------------------- |
135 | | * |
136 | | * Subtable lookup is performed in ranges defined for struct flow, starting |
137 | | * from metadata (registers, in_port, etc.), then L2 header, L3, and finally |
138 | | * L4 ports. Whenever it is found that there are no matches in the current |
139 | | * subtable, the rest of the subtable can be skipped. |
140 | | * |
141 | | * Staged lookup does not reduce lookup time, and it may increase it, because |
142 | | * it changes a single hash table lookup into multiple hash table lookups. |
143 | | * It reduces un-wildcarding significantly in important use cases. |
144 | | * |
145 | | * |
146 | | * Prefix Tracking (Wildcard Optimization) |
147 | | * --------------------------------------- |
148 | | * |
149 | | * Classifier uses prefix trees ("tries") for tracking the used |
150 | | * address space, enabling skipping classifier tables containing |
151 | | * longer masks than necessary for the given address. This reduces |
152 | | * un-wildcarding for datapath flows in parts of the address space |
153 | | * without host routes, but consulting extra data structures (the |
154 | | * tries) may slightly increase lookup time. |
155 | | * |
156 | | * Trie lookup is interwoven with staged lookup, so that a trie is |
157 | | * searched only when the configured trie field becomes relevant for |
158 | | * the lookup. The trie lookup results are retained so that each trie |
159 | | * is checked at most once for each classifier lookup. |
160 | | * |
161 | | * This implementation tracks the number of rules at each address |
162 | | * prefix for the whole classifier. More aggressive table skipping |
163 | | * would be possible by maintaining lists of tables that have prefixes |
164 | | * at the lengths encountered on tree traversal, or by maintaining |
165 | | * separate tries for subsets of rules separated by metadata fields. |
166 | | * |
167 | | * Prefix tracking is configured via OVSDB "Flow_Table" table, |
168 | | * "prefixes" column. "prefixes" is a string set where each element |
169 | | * is a name of a field that should be used for prefix tracking. |
170 | | * |
171 | | * There is a maximum number of fields that can be enabled for any one |
172 | | * flow table. Currently this limit is 4. |
173 | | * |
174 | | * |
175 | | * Partitioning (Lookup Time and Wildcard Optimization) |
176 | | * ---------------------------------------------------- |
177 | | * |
178 | | * Suppose that a given classifier is being used to handle multiple stages in a |
179 | | * pipeline using "resubmit", with metadata (that is, the OpenFlow 1.1+ field |
180 | | * named "metadata") distinguishing between the different stages. For example, |
181 | | * metadata value 1 might identify ingress rules, metadata value 2 might |
182 | | * identify ACLs, and metadata value 3 might identify egress rules. Such a |
183 | | * classifier is essentially partitioned into multiple sub-classifiers on the |
184 | | * basis of the metadata value. |
185 | | * |
186 | | * The classifier has a special optimization to speed up matching in this |
187 | | * scenario: |
188 | | * |
189 | | * - Each cls_subtable that matches on metadata gets a tag derived from the |
190 | | * subtable's mask, so that it is likely that each subtable has a unique |
191 | | * tag. (Duplicate tags have a performance cost but do not affect |
192 | | * correctness.) |
193 | | * |
194 | | * - For each metadata value matched by any cls_rule, the classifier |
195 | | * constructs a "struct cls_partition" indexed by the metadata value. |
196 | | * The cls_partition has a 'tags' member whose value is the bitwise-OR of |
197 | | * the tags of each cls_subtable that contains any rule that matches on |
198 | | * the cls_partition's metadata value. In other words, struct |
199 | | * cls_partition associates metadata values with subtables that need to |
200 | | * be checked with flows with that specific metadata value. |
201 | | * |
202 | | * Thus, a flow lookup can start by looking up the partition associated with |
203 | | * the flow's metadata, and then skip over any cls_subtable whose 'tag' does |
204 | | * not intersect the partition's 'tags'. (The flow must also be looked up in |
205 | | * any cls_subtable that doesn't match on metadata. We handle that by giving |
206 | | * any such cls_subtable TAG_ALL as its 'tags' so that it matches any tag.) |
207 | | * |
208 | | * Partitioning saves lookup time by reducing the number of subtable lookups. |
209 | | * Each eliminated subtable lookup also reduces the amount of un-wildcarding. |
210 | | * |
211 | | * |
212 | | * Classifier Versioning |
213 | | * ===================== |
214 | | * |
215 | | * Classifier lookups are always done in a specific classifier version, where |
216 | | * a version is defined to be a natural number. |
217 | | * |
218 | | * When a new rule is added to a classifier, it is set to become visible in a |
219 | | * specific version. If the version number used at insert time is larger than |
220 | | * any version number currently used in lookups, the new rule is said to be |
221 | | * invisible to lookups. This means that lookups won't find the rule, but the |
222 | | * rule is immediately available to classifier iterations. |
223 | | * |
224 | | * Similarly, a rule can be marked as to be deleted in a future version. To |
225 | | * delete a rule in a way to not remove the rule before all ongoing lookups are |
226 | | * finished, the rule should be made invisible in a specific version number. |
227 | | * Then, when all the lookups use a later version number, the rule can be |
228 | | * actually removed from the classifier. |
229 | | * |
230 | | * Classifiers can hold duplicate rules (rules with the same match criteria and |
231 | | * priority) when at most one of these duplicates is visible in any given |
232 | | * lookup version. The caller responsible for classifier modifications must |
233 | | * maintain this invariant. |
234 | | * |
235 | | * The classifier supports versioning for two reasons: |
236 | | * |
237 | | * 1. Support for versioned modifications makes it possible to perform an |
238 | | * arbitrary series of classifier changes as one atomic transaction, |
239 | | * where intermediate versions of the classifier are not visible to any |
240 | | * lookups. Also, when a rule is added for a future version, or marked |
241 | | * for removal after the current version, such modifications can be |
242 | | * reverted without any visible effects to any of the current lookups. |
243 | | * |
244 | | * 2. Performance: Adding (or deleting) a large set of rules can, in |
245 | | * pathological cases, have a cost proportional to the number of rules |
246 | | * already in the classifier. When multiple rules are being added (or |
247 | | * deleted) in one go, though, this pathological case cost can be |
248 | | * typically avoided, as long as it is OK for any new rules to be |
249 | | * invisible until the batch change is complete. |
250 | | * |
251 | | * Note that the classifier_replace() function replaces a rule immediately, and |
252 | | * is therefore not safe to use with versioning. It is still available for the |
253 | | * users that do not use versioning. |
254 | | * |
255 | | * |
256 | | * Deferred Publication |
257 | | * ==================== |
258 | | * |
259 | | * Removing large number of rules from classifier can be costly, as the |
260 | | * supporting data structures are teared down, in many cases just to be |
261 | | * re-instantiated right after. In the worst case, as when each rule has a |
262 | | * different match pattern (mask), the maintenance of the match patterns can |
263 | | * have cost O(N^2), where N is the number of different match patterns. To |
264 | | * alleviate this, the classifier supports a "deferred mode", in which changes |
265 | | * in internal data structures needed for future version lookups may not be |
266 | | * fully computed yet. The computation is finalized when the deferred mode is |
267 | | * turned off. |
268 | | * |
269 | | * This feature can be used with versioning such that all changes to future |
270 | | * versions are made in the deferred mode. Then, right before making the new |
271 | | * version visible to lookups, the deferred mode is turned off so that all the |
272 | | * data structures are ready for lookups with the new version number. |
273 | | * |
274 | | * To use deferred publication, first call classifier_defer(). Then, modify |
275 | | * the classifier via additions (classifier_insert() with a specific, future |
276 | | * version number) and deletions (use cls_rule_make_removable_after_version()). |
277 | | * Then call classifier_publish(), and after that, announce the new version |
278 | | * number to be used in lookups. |
279 | | * |
280 | | * |
281 | | * Thread-safety |
282 | | * ============= |
283 | | * |
284 | | * The classifier may safely be accessed by many reader threads concurrently |
285 | | * and by a single writer, or by multiple writers when they guarantee mutually |
286 | | * exclusive access to classifier modifications. |
287 | | * |
288 | | * Since the classifier rules are RCU protected, the rule destruction after |
289 | | * removal from the classifier must be RCU postponed. Also, when versioning is |
290 | | * used, the rule removal itself needs to be typically RCU postponed. In this |
291 | | * case the rule destruction is doubly RCU postponed, i.e., the second |
292 | | * ovsrcu_postpone() call to destruct the rule is called from the first RCU |
293 | | * callback that removes the rule. |
294 | | * |
295 | | * Rules that have never been visible to lookups are an exception to the above |
296 | | * rule. Such rules can be removed immediately, but their destruction must |
297 | | * still be RCU postponed, as the rule's visibility attribute may be examined |
298 | | * parallel to the rule's removal. */ |
299 | | |
300 | | #include "cmap.h" |
301 | | #include "hmapx.h" |
302 | | #include "openvswitch/match.h" |
303 | | #include "openvswitch/meta-flow.h" |
304 | | #include "pvector.h" |
305 | | #include "rculist.h" |
306 | | #include "openvswitch/type-props.h" |
307 | | #include "versions.h" |
308 | | |
309 | | #ifdef __cplusplus |
310 | | extern "C" { |
311 | | #endif |
312 | | |
313 | | /* Classifier internal data structures. */ |
314 | | struct cls_subtable; |
315 | | struct cls_match; |
316 | | |
317 | | struct trie_node; |
318 | | typedef OVSRCU_TYPE(struct trie_node *) rcu_trie_ptr; |
319 | | |
320 | | /* Prefix trie for a 'field' */ |
321 | | struct cls_trie { |
322 | | const struct mf_field *field; /* Trie field, or NULL. */ |
323 | | rcu_trie_ptr root; /* NULL if none. */ |
324 | | }; |
325 | | |
326 | | enum { |
327 | | CLS_MAX_INDICES = 3, /* Maximum number of lookup indices per subtable. */ |
328 | | CLS_MAX_TRIES = 4, /* Maximum number of prefix trees per classifier. */ |
329 | | }; |
330 | | |
331 | | /* A flow classifier. */ |
332 | | struct classifier { |
333 | | int n_rules; /* Total number of rules. */ |
334 | | uint8_t n_flow_segments; |
335 | | uint8_t flow_segments[CLS_MAX_INDICES]; /* Flow segment boundaries to use |
336 | | * for staged lookup. */ |
337 | | struct cmap subtables_map; /* Contains "struct cls_subtable"s. */ |
338 | | struct pvector subtables; |
339 | | struct cmap partitions; /* Contains "struct cls_partition"s. */ |
340 | | struct cls_trie tries[CLS_MAX_TRIES]; /* Prefix tries. */ |
341 | | atomic_uint32_t n_tries; /* Number of tries. Also serves as a |
342 | | * memory synchronization point for trie |
343 | | * configuration. */ |
344 | | bool publish; /* Make changes visible to lookups? */ |
345 | | }; |
346 | | |
347 | | struct cls_conjunction { |
348 | | uint32_t id; |
349 | | uint8_t clause; |
350 | | uint8_t n_clauses; |
351 | | }; |
352 | | |
353 | | /* A rule to be inserted to the classifier. */ |
354 | | struct cls_rule { |
355 | | struct rculist node; /* In struct cls_subtable 'rules_list'. */ |
356 | | const int priority; /* Larger numbers are higher priorities. */ |
357 | | OVSRCU_TYPE(struct cls_match *) cls_match; /* NULL if not in a |
358 | | * classifier. */ |
359 | | const struct minimatch match; /* Matching rule. */ |
360 | | }; |
361 | | |
362 | | /* Constructor/destructor. Must run single-threaded. */ |
363 | | void classifier_init(struct classifier *, const uint8_t *flow_segments); |
364 | | void classifier_destroy(struct classifier *); |
365 | | |
366 | | /* Modifiers. Caller MUST exclude concurrent calls from other threads. */ |
367 | | bool classifier_set_prefix_fields(struct classifier *, |
368 | | const enum mf_field_id *trie_fields, |
369 | | unsigned int n_trie_fields); |
370 | | |
371 | | void cls_rule_init(struct cls_rule *, const struct match *, int priority); |
372 | | void cls_rule_init_from_minimatch(struct cls_rule *, const struct minimatch *, |
373 | | int priority); |
374 | | void cls_rule_clone(struct cls_rule *, const struct cls_rule *); |
375 | | void cls_rule_move(struct cls_rule *dst, struct cls_rule *src); |
376 | | void cls_rule_destroy(struct cls_rule *); |
377 | | |
378 | | void cls_rule_set_conjunctions(struct cls_rule *, |
379 | | const struct cls_conjunction *, size_t n); |
380 | | void cls_rule_make_invisible_in_version(const struct cls_rule *, |
381 | | ovs_version_t); |
382 | | void cls_rule_restore_visibility(const struct cls_rule *); |
383 | | |
384 | | void classifier_insert(struct classifier *, const struct cls_rule *, |
385 | | ovs_version_t, const struct cls_conjunction *, |
386 | | size_t n_conjunctions); |
387 | | const struct cls_rule *classifier_replace(struct classifier *, |
388 | | const struct cls_rule *, |
389 | | ovs_version_t, |
390 | | const struct cls_conjunction *, |
391 | | size_t n_conjunctions); |
392 | | bool classifier_remove(struct classifier *, const struct cls_rule *); |
393 | | void classifier_remove_assert(struct classifier *, const struct cls_rule *); |
394 | | static inline void classifier_defer(struct classifier *); |
395 | | static inline void classifier_publish(struct classifier *); |
396 | | |
397 | | /* Lookups. These are RCU protected and may run concurrently with modifiers |
398 | | * and each other. */ |
399 | | const struct cls_rule *classifier_lookup(const struct classifier *, |
400 | | ovs_version_t, struct flow *, |
401 | | struct flow_wildcards *, |
402 | | struct hmapx *conj_flows); |
403 | | bool classifier_rule_overlaps(const struct classifier *, |
404 | | const struct cls_rule *, ovs_version_t); |
405 | | const struct cls_rule *classifier_find_rule_exactly(const struct classifier *, |
406 | | const struct cls_rule *, |
407 | | ovs_version_t); |
408 | | const struct cls_rule *classifier_find_match_exactly(const struct classifier *, |
409 | | const struct match *, |
410 | | int priority, |
411 | | ovs_version_t); |
412 | | const struct cls_rule *classifier_find_minimatch_exactly( |
413 | | const struct classifier *, const struct minimatch *, |
414 | | int priority, ovs_version_t); |
415 | | |
416 | | bool classifier_is_empty(const struct classifier *); |
417 | | int classifier_count(const struct classifier *); |
418 | | |
419 | | /* Classifier rule properties. These are RCU protected and may run |
420 | | * concurrently with modifiers and each other. */ |
421 | | bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *); |
422 | | void cls_rule_format(const struct cls_rule *, const struct tun_table *, |
423 | | const struct ofputil_port_map *, struct ds *); |
424 | | bool cls_rule_is_catchall(const struct cls_rule *); |
425 | | bool cls_rule_is_loose_match(const struct cls_rule *rule, |
426 | | const struct minimatch *criteria); |
427 | | bool cls_rule_visible_in_version(const struct cls_rule *, ovs_version_t); |
428 | | |
429 | | /* Iteration. |
430 | | * |
431 | | * Iteration is lockless and RCU-protected. Concurrent threads may perform all |
432 | | * kinds of concurrent modifications without ruining the iteration. Obviously, |
433 | | * any modifications may or may not be visible to the concurrent iterator, but |
434 | | * all the rules not deleted are visited by the iteration. The iterating |
435 | | * thread may also modify the classifier rules itself. |
436 | | * |
437 | | * 'TARGET' iteration only iterates rules matching the 'TARGET' criteria. |
438 | | * Rather than looping through all the rules and skipping ones that can't |
439 | | * match, 'TARGET' iteration skips whole subtables, if the 'TARGET' happens to |
440 | | * be more specific than the subtable. */ |
441 | | struct cls_cursor { |
442 | | const struct classifier *cls; |
443 | | const struct cls_subtable *subtable; |
444 | | const struct cls_rule *target; |
445 | | ovs_version_t version; /* Version to iterate. */ |
446 | | struct pvector_cursor subtables; |
447 | | const struct cls_rule *rule; |
448 | | }; |
449 | | |
450 | | struct cls_cursor cls_cursor_start(const struct classifier *, |
451 | | const struct cls_rule *target, |
452 | | ovs_version_t); |
453 | | void cls_cursor_advance(struct cls_cursor *); |
454 | | |
455 | | #define CLS_FOR_EACH(RULE, MEMBER, CLS) \ |
456 | 0 | CLS_FOR_EACH_TARGET(RULE, MEMBER, CLS, NULL, OVS_VERSION_MAX) |
457 | | #define CLS_FOR_EACH_TARGET(RULE, MEMBER, CLS, TARGET, VERSION) \ |
458 | 0 | for (struct cls_cursor cursor__ = cls_cursor_start(CLS, TARGET, VERSION); \ |
459 | 0 | (cursor__.rule \ |
460 | 0 | ? (INIT_CONTAINER(RULE, cursor__.rule, MEMBER), \ |
461 | 0 | cls_cursor_advance(&cursor__), \ |
462 | 0 | true) \ |
463 | 0 | : false); \ |
464 | 0 | ) |
465 | | |
466 | | |
467 | | static inline void |
468 | | classifier_defer(struct classifier *cls) |
469 | 0 | { |
470 | 0 | cls->publish = false; |
471 | 0 | } Unexecuted instantiation: flow_extract_target.c:classifier_defer Unexecuted instantiation: ofp-util.c:classifier_defer Unexecuted instantiation: ovs-router.c:classifier_defer Unexecuted instantiation: tnl-ports.c:classifier_defer Unexecuted instantiation: classifier.c:classifier_defer Unexecuted instantiation: meta-flow.c:classifier_defer Unexecuted instantiation: nx-match.c:classifier_defer Unexecuted instantiation: miniflow_target.c:classifier_defer Unexecuted instantiation: ofctl_parse_target.c:classifier_defer |
472 | | |
473 | | static inline void |
474 | | classifier_publish(struct classifier *cls) |
475 | 0 | { |
476 | 0 | cls->publish = true; |
477 | 0 | pvector_publish(&cls->subtables); |
478 | 0 | } Unexecuted instantiation: flow_extract_target.c:classifier_publish Unexecuted instantiation: ofp-util.c:classifier_publish Unexecuted instantiation: ovs-router.c:classifier_publish Unexecuted instantiation: tnl-ports.c:classifier_publish Unexecuted instantiation: classifier.c:classifier_publish Unexecuted instantiation: meta-flow.c:classifier_publish Unexecuted instantiation: nx-match.c:classifier_publish Unexecuted instantiation: miniflow_target.c:classifier_publish Unexecuted instantiation: ofctl_parse_target.c:classifier_publish |
479 | | |
480 | | #ifdef __cplusplus |
481 | | } |
482 | | #endif |
483 | | #endif /* classifier.h */ |