classify: remove includes from classifier header file
[vpp.git] / src / vnet / classify / vnet_classify.h
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef __included_vnet_classify_h__
16 #define __included_vnet_classify_h__
17
18 #include <vnet/vnet.h>
19 #include <vnet/api_errno.h>     /* for API error numbers */
20
21 #include <vppinfra/error.h>
22 #include <vppinfra/hash.h>
23 #include <vppinfra/cache.h>
24 #include <vppinfra/xxhash.h>
25
26 extern vlib_node_registration_t ip4_classify_node;
27 extern vlib_node_registration_t ip6_classify_node;
28
29 #define CLASSIFY_TRACE 0
30
31 #define U32X4_ALIGNED(p) PREDICT_TRUE((((intptr_t)p) & 0xf) == 0)
32
33 /*
34  * Classify table option to process packets
35  *  CLASSIFY_FLAG_USE_CURR_DATA:
36  *   - classify packets starting from VPP node’s current data pointer
37  */
38 #define CLASSIFY_FLAG_USE_CURR_DATA              1
39
40 /*
41  * Classify session action
42  *  CLASSIFY_ACTION_SET_IP4_FIB_INDEX:
43  *   - Classified IP packets will be looked up
44  *     from the specified ipv4 fib table
45  *  CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
46  *   - Classified IP packets will be looked up
47  *     from the specified ipv6 fib table
48  */
49 typedef enum vnet_classify_action_t_
50 {
51   CLASSIFY_ACTION_SET_IP4_FIB_INDEX = 1,
52   CLASSIFY_ACTION_SET_IP6_FIB_INDEX = 2,
53   CLASSIFY_ACTION_SET_METADATA = 3,
54 } __attribute__ ((packed)) vnet_classify_action_t;
55
56 struct _vnet_classify_main;
57 typedef struct _vnet_classify_main vnet_classify_main_t;
58
59 #define foreach_size_in_u32x4                   \
60 _(1)                                            \
61 _(2)                                            \
62 _(3)                                            \
63 _(4)                                            \
64 _(5)
65
66 /* *INDENT-OFF* */
67 typedef CLIB_PACKED(struct _vnet_classify_entry {
68   /* Graph node next index */
69   u32 next_index;
70
71   /* put into vnet_buffer(b)->l2_classfy.opaque_index */
72   union {
73     struct {
74       u32 opaque_index;
75       /* advance on hit, note it's a signed quantity... */
76       i32 advance;
77     };
78     u64 opaque_count;
79   };
80
81   /* Really only need 1 bit */
82   u8 flags;
83 #define VNET_CLASSIFY_ENTRY_FREE        (1<<0)
84
85   vnet_classify_action_t action;
86   u16 metadata;
87
88   /* Hit counter, last heard time */
89   union {
90     u64 hits;
91     struct _vnet_classify_entry * next_free;
92   };
93
94   f64 last_heard;
95
96   /* Must be aligned to a 16-octet boundary */
97   u32x4 key[0];
98 }) vnet_classify_entry_t;
99 /* *INDENT-ON* */
100
101 static inline int
102 vnet_classify_entry_is_free (vnet_classify_entry_t * e)
103 {
104   return e->flags & VNET_CLASSIFY_ENTRY_FREE;
105 }
106
107 static inline int
108 vnet_classify_entry_is_busy (vnet_classify_entry_t * e)
109 {
110   return ((e->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
111 }
112
113 /* Need these to con the vector allocator */
114 /* *INDENT-OFF* */
115 #define _(size)                                 \
116 typedef CLIB_PACKED(struct {                    \
117   u32 pad0[4];                                  \
118   u64 pad1[2];                                  \
119   u32x4 key[size];                              \
120 }) vnet_classify_entry_##size##_t;
121 foreach_size_in_u32x4;
122 /* *INDENT-ON* */
123 #undef _
124
125 typedef struct
126 {
127   union
128   {
129     struct
130     {
131       u32 offset;
132       u8 linear_search;
133       u8 pad[2];
134       u8 log2_pages;
135     };
136     u64 as_u64;
137   };
138 } vnet_classify_bucket_t;
139
140 typedef struct
141 {
142   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
143   /* Mask to apply after skipping N vectors */
144   u32x4 *mask;
145   /* Buckets and entries */
146   vnet_classify_bucket_t *buckets;
147   vnet_classify_entry_t *entries;
148
149   /* Config parameters */
150   u32 match_n_vectors;
151   u32 skip_n_vectors;
152   u32 nbuckets;
153   u32 log2_nbuckets;
154   u32 linear_buckets;
155   int entries_per_page;
156   u32 active_elements;
157   u32 current_data_flag;
158   int current_data_offset;
159   u32 data_offset;
160   /* Index of next table to try */
161   u32 next_table_index;
162
163   /* Miss next index, return if next_table_index = 0 */
164   u32 miss_next_index;
165
166   /* Per-bucket working copies, one per thread */
167   vnet_classify_entry_t **working_copies;
168   int *working_copy_lengths;
169   vnet_classify_bucket_t saved_bucket;
170
171   /* Free entry freelists */
172   vnet_classify_entry_t **freelists;
173
174   u8 *name;
175
176   /* Private allocation arena, protected by the writer lock */
177   void *mheap;
178
179   /* Writer (only) lock for this table */
180   clib_spinlock_t writer_lock;
181
182 } vnet_classify_table_t;
183
184 struct _vnet_classify_main
185 {
186   /* Table pool */
187   vnet_classify_table_t *tables;
188
189   /* Registered next-index, opaque unformat fcns */
190   unformat_function_t **unformat_l2_next_index_fns;
191   unformat_function_t **unformat_ip_next_index_fns;
192   unformat_function_t **unformat_acl_next_index_fns;
193   unformat_function_t **unformat_policer_next_index_fns;
194   unformat_function_t **unformat_opaque_index_fns;
195
196   /* convenience variables */
197   vlib_main_t *vlib_main;
198   vnet_main_t *vnet_main;
199 };
200
201 extern vnet_classify_main_t vnet_classify_main;
202
203 u8 *format_classify_table (u8 * s, va_list * args);
204
205 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h);
206
207 static inline u64
208 vnet_classify_hash_packet_inline (vnet_classify_table_t * t, u8 * h)
209 {
210   u32x4 *mask;
211
212   union
213   {
214     u32x4 as_u32x4;
215     u64 as_u64[2];
216   } xor_sum __attribute__ ((aligned (sizeof (u32x4))));
217
218   ASSERT (t);
219   mask = t->mask;
220 #ifdef CLIB_HAVE_VEC128
221   if (U32X4_ALIGNED (h))
222     {                           //SSE can't handle unaligned data
223       u32x4 *data = (u32x4 *) h;
224       xor_sum.as_u32x4 = data[0 + t->skip_n_vectors] & mask[0];
225       switch (t->match_n_vectors)
226         {
227         case 5:
228           xor_sum.as_u32x4 ^= data[4 + t->skip_n_vectors] & mask[4];
229           /* FALLTHROUGH */
230         case 4:
231           xor_sum.as_u32x4 ^= data[3 + t->skip_n_vectors] & mask[3];
232           /* FALLTHROUGH */
233         case 3:
234           xor_sum.as_u32x4 ^= data[2 + t->skip_n_vectors] & mask[2];
235           /* FALLTHROUGH */
236         case 2:
237           xor_sum.as_u32x4 ^= data[1 + t->skip_n_vectors] & mask[1];
238           /* FALLTHROUGH */
239         case 1:
240           break;
241         default:
242           abort ();
243         }
244     }
245   else
246 #endif /* CLIB_HAVE_VEC128 */
247     {
248       u32 skip_u64 = t->skip_n_vectors * 2;
249       u64 *data64 = (u64 *) h;
250       xor_sum.as_u64[0] = data64[0 + skip_u64] & ((u64 *) mask)[0];
251       xor_sum.as_u64[1] = data64[1 + skip_u64] & ((u64 *) mask)[1];
252       switch (t->match_n_vectors)
253         {
254         case 5:
255           xor_sum.as_u64[0] ^= data64[8 + skip_u64] & ((u64 *) mask)[8];
256           xor_sum.as_u64[1] ^= data64[9 + skip_u64] & ((u64 *) mask)[9];
257           /* FALLTHROUGH */
258         case 4:
259           xor_sum.as_u64[0] ^= data64[6 + skip_u64] & ((u64 *) mask)[6];
260           xor_sum.as_u64[1] ^= data64[7 + skip_u64] & ((u64 *) mask)[7];
261           /* FALLTHROUGH */
262         case 3:
263           xor_sum.as_u64[0] ^= data64[4 + skip_u64] & ((u64 *) mask)[4];
264           xor_sum.as_u64[1] ^= data64[5 + skip_u64] & ((u64 *) mask)[5];
265           /* FALLTHROUGH */
266         case 2:
267           xor_sum.as_u64[0] ^= data64[2 + skip_u64] & ((u64 *) mask)[2];
268           xor_sum.as_u64[1] ^= data64[3 + skip_u64] & ((u64 *) mask)[3];
269           /* FALLTHROUGH */
270         case 1:
271           break;
272
273         default:
274           abort ();
275         }
276     }
277
278   return clib_xxhash (xor_sum.as_u64[0] ^ xor_sum.as_u64[1]);
279 }
280
281 static inline void
282 vnet_classify_prefetch_bucket (vnet_classify_table_t * t, u64 hash)
283 {
284   u32 bucket_index;
285
286   ASSERT (is_pow2 (t->nbuckets));
287
288   bucket_index = hash & (t->nbuckets - 1);
289
290   CLIB_PREFETCH (&t->buckets[bucket_index], CLIB_CACHE_LINE_BYTES, LOAD);
291 }
292
293 static inline vnet_classify_entry_t *
294 vnet_classify_get_entry (vnet_classify_table_t * t, uword offset)
295 {
296   u8 *hp = t->mheap;
297   u8 *vp = hp + offset;
298
299   return (void *) vp;
300 }
301
302 static inline uword
303 vnet_classify_get_offset (vnet_classify_table_t * t,
304                           vnet_classify_entry_t * v)
305 {
306   u8 *hp, *vp;
307
308   hp = (u8 *) t->mheap;
309   vp = (u8 *) v;
310
311   ASSERT ((vp - hp) < 0x100000000ULL);
312   return vp - hp;
313 }
314
315 static inline vnet_classify_entry_t *
316 vnet_classify_entry_at_index (vnet_classify_table_t * t,
317                               vnet_classify_entry_t * e, u32 index)
318 {
319   u8 *eu8;
320
321   eu8 = (u8 *) e;
322
323   eu8 += index * (sizeof (vnet_classify_entry_t) +
324                   (t->match_n_vectors * sizeof (u32x4)));
325
326   return (vnet_classify_entry_t *) eu8;
327 }
328
329 static inline void
330 vnet_classify_prefetch_entry (vnet_classify_table_t * t, u64 hash)
331 {
332   u32 bucket_index;
333   u32 value_index;
334   vnet_classify_bucket_t *b;
335   vnet_classify_entry_t *e;
336
337   bucket_index = hash & (t->nbuckets - 1);
338
339   b = &t->buckets[bucket_index];
340
341   if (b->offset == 0)
342     return;
343
344   hash >>= t->log2_nbuckets;
345
346   e = vnet_classify_get_entry (t, b->offset);
347   value_index = hash & ((1 << b->log2_pages) - 1);
348
349   e = vnet_classify_entry_at_index (t, e, value_index);
350
351   CLIB_PREFETCH (e, CLIB_CACHE_LINE_BYTES, LOAD);
352 }
353
354 vnet_classify_entry_t *vnet_classify_find_entry (vnet_classify_table_t * t,
355                                                  u8 * h, u64 hash, f64 now);
356
357 static inline vnet_classify_entry_t *
358 vnet_classify_find_entry_inline (vnet_classify_table_t * t,
359                                  u8 * h, u64 hash, f64 now)
360 {
361   vnet_classify_entry_t *v;
362   u32x4 *mask, *key;
363   union
364   {
365     u32x4 as_u32x4;
366     u64 as_u64[2];
367   } result __attribute__ ((aligned (sizeof (u32x4))));
368   vnet_classify_bucket_t *b;
369   u32 value_index;
370   u32 bucket_index;
371   u32 limit;
372   int i;
373
374   bucket_index = hash & (t->nbuckets - 1);
375   b = &t->buckets[bucket_index];
376   mask = t->mask;
377
378   if (b->offset == 0)
379     return 0;
380
381   hash >>= t->log2_nbuckets;
382
383   v = vnet_classify_get_entry (t, b->offset);
384   value_index = hash & ((1 << b->log2_pages) - 1);
385   limit = t->entries_per_page;
386   if (PREDICT_FALSE (b->linear_search))
387     {
388       value_index = 0;
389       limit *= (1 << b->log2_pages);
390     }
391
392   v = vnet_classify_entry_at_index (t, v, value_index);
393
394 #ifdef CLIB_HAVE_VEC128
395   if (U32X4_ALIGNED (h))
396     {
397       u32x4 *data = (u32x4 *) h;
398       for (i = 0; i < limit; i++)
399         {
400           key = v->key;
401           result.as_u32x4 = (data[0 + t->skip_n_vectors] & mask[0]) ^ key[0];
402           switch (t->match_n_vectors)
403             {
404             case 5:
405               result.as_u32x4 |=
406                 (data[4 + t->skip_n_vectors] & mask[4]) ^ key[4];
407               /* FALLTHROUGH */
408             case 4:
409               result.as_u32x4 |=
410                 (data[3 + t->skip_n_vectors] & mask[3]) ^ key[3];
411               /* FALLTHROUGH */
412             case 3:
413               result.as_u32x4 |=
414                 (data[2 + t->skip_n_vectors] & mask[2]) ^ key[2];
415               /* FALLTHROUGH */
416             case 2:
417               result.as_u32x4 |=
418                 (data[1 + t->skip_n_vectors] & mask[1]) ^ key[1];
419               /* FALLTHROUGH */
420             case 1:
421               break;
422             default:
423               abort ();
424             }
425
426           if (u32x4_zero_byte_mask (result.as_u32x4) == 0xffff)
427             {
428               if (PREDICT_TRUE (now))
429                 {
430                   v->hits++;
431                   v->last_heard = now;
432                 }
433               return (v);
434             }
435           v = vnet_classify_entry_at_index (t, v, 1);
436         }
437     }
438   else
439 #endif /* CLIB_HAVE_VEC128 */
440     {
441       u32 skip_u64 = t->skip_n_vectors * 2;
442       u64 *data64 = (u64 *) h;
443       for (i = 0; i < limit; i++)
444         {
445           key = v->key;
446
447           result.as_u64[0] =
448             (data64[0 + skip_u64] & ((u64 *) mask)[0]) ^ ((u64 *) key)[0];
449           result.as_u64[1] =
450             (data64[1 + skip_u64] & ((u64 *) mask)[1]) ^ ((u64 *) key)[1];
451           switch (t->match_n_vectors)
452             {
453             case 5:
454               result.as_u64[0] |=
455                 (data64[8 + skip_u64] & ((u64 *) mask)[8]) ^ ((u64 *) key)[8];
456               result.as_u64[1] |=
457                 (data64[9 + skip_u64] & ((u64 *) mask)[9]) ^ ((u64 *) key)[9];
458               /* FALLTHROUGH */
459             case 4:
460               result.as_u64[0] |=
461                 (data64[6 + skip_u64] & ((u64 *) mask)[6]) ^ ((u64 *) key)[6];
462               result.as_u64[1] |=
463                 (data64[7 + skip_u64] & ((u64 *) mask)[7]) ^ ((u64 *) key)[7];
464               /* FALLTHROUGH */
465             case 3:
466               result.as_u64[0] |=
467                 (data64[4 + skip_u64] & ((u64 *) mask)[4]) ^ ((u64 *) key)[4];
468               result.as_u64[1] |=
469                 (data64[5 + skip_u64] & ((u64 *) mask)[5]) ^ ((u64 *) key)[5];
470               /* FALLTHROUGH */
471             case 2:
472               result.as_u64[0] |=
473                 (data64[2 + skip_u64] & ((u64 *) mask)[2]) ^ ((u64 *) key)[2];
474               result.as_u64[1] |=
475                 (data64[3 + skip_u64] & ((u64 *) mask)[3]) ^ ((u64 *) key)[3];
476               /* FALLTHROUGH */
477             case 1:
478               break;
479             default:
480               abort ();
481             }
482
483           if (result.as_u64[0] == 0 && result.as_u64[1] == 0)
484             {
485               if (PREDICT_TRUE (now))
486                 {
487                   v->hits++;
488                   v->last_heard = now;
489                 }
490               return (v);
491             }
492
493           v = vnet_classify_entry_at_index (t, v, 1);
494         }
495     }
496   return 0;
497 }
498
499 vnet_classify_table_t *vnet_classify_new_table (vnet_classify_main_t * cm,
500                                                 u8 * mask, u32 nbuckets,
501                                                 u32 memory_size,
502                                                 u32 skip_n_vectors,
503                                                 u32 match_n_vectors);
504
505 int vnet_classify_add_del_session (vnet_classify_main_t * cm,
506                                    u32 table_index,
507                                    u8 * match,
508                                    u32 hit_next_index,
509                                    u32 opaque_index,
510                                    i32 advance,
511                                    u8 action, u32 metadata, int is_add);
512
513 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
514                                  u8 * mask,
515                                  u32 nbuckets,
516                                  u32 memory_size,
517                                  u32 skip,
518                                  u32 match,
519                                  u32 next_table_index,
520                                  u32 miss_next_index,
521                                  u32 * table_index,
522                                  u8 current_data_flag,
523                                  i16 current_data_offset,
524                                  int is_add, int del_chain);
525
526 unformat_function_t unformat_ip4_mask;
527 unformat_function_t unformat_ip6_mask;
528 unformat_function_t unformat_l3_mask;
529 unformat_function_t unformat_l2_mask;
530 unformat_function_t unformat_classify_mask;
531 unformat_function_t unformat_l2_next_index;
532 unformat_function_t unformat_ip_next_index;
533 unformat_function_t unformat_ip4_match;
534 unformat_function_t unformat_ip6_match;
535 unformat_function_t unformat_l3_match;
536 unformat_function_t unformat_l4_match;
537 unformat_function_t unformat_vlan_tag;
538 unformat_function_t unformat_l2_match;
539 unformat_function_t unformat_classify_match;
540
541 void vnet_classify_register_unformat_ip_next_index_fn
542   (unformat_function_t * fn);
543
544 void vnet_classify_register_unformat_l2_next_index_fn
545   (unformat_function_t * fn);
546
547 void vnet_classify_register_unformat_acl_next_index_fn
548   (unformat_function_t * fn);
549
550 void vnet_classify_register_unformat_policer_next_index_fn
551   (unformat_function_t * fn);
552
553 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t *
554                                                       fn);
555
556 #endif /* __included_vnet_classify_h__ */
557
558 /*
559  * fd.io coding-style-patch-verification: ON
560  *
561  * Local Variables:
562  * eval: (c-set-style "gnu")
563  * End:
564  */