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