Initial commit of vpp code.
[vpp.git] / vnet / 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 vlib_node_registration_t ip4_classify_node;
39 vlib_node_registration_t ip6_classify_node;
40
41 #define CLASSIFY_TRACE 0
42
43 struct _vnet_classify_main;
44 typedef struct _vnet_classify_main vnet_classify_main_t;
45
46 #define foreach_size_in_u32x4                   \
47 _(1)                                            \
48 _(2)                                            \
49 _(3)                                            \
50 _(4)                                            \
51 _(5)
52
53 typedef CLIB_PACKED(struct _vnet_classify_entry {
54   /* Graph node next index */
55   u32 next_index;
56
57   /* put into vnet_buffer(b)->l2_classfy.opaque_index */
58   union {
59     struct {
60       u32 opaque_index;
61       /* advance on hit, note it's a signed quantity... */
62       i32 advance;
63     };
64     u64 opaque_count;
65   };
66
67   /* Really only need 1 bit */
68   u32 flags;
69 #define VNET_CLASSIFY_ENTRY_FREE        (1<<0)
70
71   /* Hit counter, last heard time */
72   union {
73     u64 hits;
74     struct _vnet_classify_entry * next_free;
75   };
76     
77   f64 last_heard;
78
79   /* Must be aligned to a 16-octet boundary */
80   u32x4 key[0];
81 }) vnet_classify_entry_t;
82
83 static inline int vnet_classify_entry_is_free (vnet_classify_entry_t * e)
84 {
85   return e->flags & VNET_CLASSIFY_ENTRY_FREE;
86 }
87
88 static inline int vnet_classify_entry_is_busy (vnet_classify_entry_t * e)
89 {
90   return ((e->flags & VNET_CLASSIFY_ENTRY_FREE) == 0);
91 }
92
93 /* Need these to con the vector allocator */
94 #define _(size)                                 \
95 typedef CLIB_PACKED(struct {                    \
96   u32 pad0[4];                                  \
97   u64 pad1[2];                                  \
98   u32x4 key[size];                              \
99 }) vnet_classify_entry_##size##_t;
100 foreach_size_in_u32x4;
101 #undef _
102
103 typedef struct {
104   union {
105     struct {
106       u32 offset;
107       u8 pad[3];
108       u8 log2_pages;
109     };
110     u64 as_u64;
111   };
112 } vnet_classify_bucket_t;
113
114 typedef struct {
115   /* Mask to apply after skipping N vectors */
116   u32x4 *mask;
117   /* Buckets and entries */
118   vnet_classify_bucket_t * buckets;
119   vnet_classify_entry_t * entries;
120   
121   /* Config parameters */
122   u32 match_n_vectors;
123   u32 skip_n_vectors;
124   u32 nbuckets;
125   u32 log2_nbuckets;
126   int entries_per_page;
127   u32 active_elements;
128   /* Index of next table to try */
129   u32 next_table_index;
130   
131   /* Miss next index, return if next_table_index = 0 */
132   u32 miss_next_index;
133   
134   /* Per-bucket working copies, one per thread */
135   vnet_classify_entry_t ** working_copies;
136   vnet_classify_bucket_t saved_bucket;
137   
138   /* Free entry freelists */
139   vnet_classify_entry_t **freelists;
140
141   u8 * name;
142   
143   /* Private allocation arena, protected by the writer lock */
144   void * mheap;
145   
146   /* Writer (only) lock for this table */
147   volatile u32 * writer_lock;
148   
149 } vnet_classify_table_t;
150
151 struct _vnet_classify_main {
152   /* Table pool */
153   vnet_classify_table_t * tables;
154   
155   /* convenience variables */
156   vlib_main_t * vlib_main;
157   vnet_main_t * vnet_main;
158 };
159
160 vnet_classify_main_t vnet_classify_main;
161
162 u8 * format_classify_table (u8 * s, va_list * args);
163
164 u64 vnet_classify_hash_packet (vnet_classify_table_t * t, u8 * h);
165
166 static inline u64 
167 vnet_classify_hash_packet_inline (vnet_classify_table_t * t, 
168                                   u8 * h)
169 {
170   u32x4 *data, *mask;
171   
172   union {
173     u32x4 as_u32x4;
174     u64 as_u64[2];
175   } xor_sum __attribute__((aligned(sizeof(u32x4))));
176   
177   ASSERT(t);
178   
179   data = (u32x4 *)h;
180   mask = t->mask;
181   
182   ASSERT ((((u64)h) & 0xf) == 0);
183   
184   xor_sum.as_u32x4  = data[0 + t->skip_n_vectors] & mask[0];
185   
186   switch (t->match_n_vectors)
187     {
188     case 5:
189       xor_sum.as_u32x4 ^= data[4 + t->skip_n_vectors] & mask[4];
190       /* FALLTHROUGH */
191     case 4:
192       xor_sum.as_u32x4 ^= data[3 + t->skip_n_vectors] & mask[3];
193       /* FALLTHROUGH */
194     case 3:
195       xor_sum.as_u32x4 ^= data[2 + t->skip_n_vectors] & mask[2];
196       /* FALLTHROUGH */
197     case 2:
198       xor_sum.as_u32x4 ^= data[1 + t->skip_n_vectors] & mask[1];
199       /* FALLTHROUGH */
200     case 1:
201       break;
202
203     default:
204       abort();
205     }
206   
207   return clib_xxhash (xor_sum.as_u64[0] ^ xor_sum.as_u64[1]);
208 }
209
210 static inline void 
211 vnet_classify_prefetch_bucket (vnet_classify_table_t * t, u64 hash)
212 {
213   u32 bucket_index;
214   
215   ASSERT (is_pow2(t->nbuckets));
216   
217   bucket_index = hash & (t->nbuckets - 1);
218   
219   CLIB_PREFETCH(&t->buckets[bucket_index], CLIB_CACHE_LINE_BYTES, LOAD);
220 }
221
222 static inline vnet_classify_entry_t * 
223 vnet_classify_get_entry (vnet_classify_table_t * t, uword offset)
224 {
225   u8 * hp = t->mheap;
226   u8 * vp = hp + offset;
227   
228   return (void *) vp;
229 }
230
231 static inline uword vnet_classify_get_offset (vnet_classify_table_t * t, 
232                                               vnet_classify_entry_t * v)
233 {
234   u8 * hp, * vp;
235
236   hp = (u8 *) t->mheap;
237   vp = (u8 *) v;
238
239   ASSERT((vp - hp) < 0x100000000ULL);
240   return vp - hp;
241 }
242
243 static inline vnet_classify_entry_t *
244 vnet_classify_entry_at_index (vnet_classify_table_t * t, 
245                               vnet_classify_entry_t * e,
246                               u32 index)
247 {
248   u8 * eu8;
249
250   eu8 = (u8 *)e;
251
252   eu8 += index * (sizeof (vnet_classify_entry_t) +
253                   (t->match_n_vectors * sizeof (u32x4)));
254
255   return (vnet_classify_entry_t *) eu8;
256 }
257
258 static inline void
259 vnet_classify_prefetch_entry (vnet_classify_table_t * t, 
260                               u64 hash)
261 {
262   u32 bucket_index;
263   u32 value_index;
264   vnet_classify_bucket_t * b;
265   vnet_classify_entry_t * e;
266
267   bucket_index = hash & (t->nbuckets - 1);
268
269   b = &t->buckets[bucket_index];
270   
271   if (b->offset == 0)
272     return;
273
274   hash >>= t->log2_nbuckets;
275
276   e = vnet_classify_get_entry (t, b->offset);
277   value_index = hash & ((1<<b->log2_pages)-1);
278
279   e = vnet_classify_entry_at_index (t, e, value_index);
280
281   CLIB_PREFETCH(e, CLIB_CACHE_LINE_BYTES, LOAD);
282 }
283
284 vnet_classify_entry_t *
285 vnet_classify_find_entry (vnet_classify_table_t * t,
286                           u8 * h, u64 hash, f64 now);
287
288 static inline vnet_classify_entry_t *
289 vnet_classify_find_entry_inline (vnet_classify_table_t * t,
290                                  u8 * h, u64 hash, f64 now)
291   {
292   vnet_classify_entry_t * v;
293   u32x4 * mask, * data, *data_start, * key;
294   u32x4 result __attribute__((aligned(sizeof(u32x4))));
295   vnet_classify_bucket_t * b;
296   u32 value_index;
297   u32 result_mask;
298   u32 bucket_index;
299   int i;
300
301   ASSERT ((((u64)h) & 0xf) == 0);
302
303   data_start = (u32x4 *) h;
304
305   bucket_index = hash & (t->nbuckets-1);
306   b = &t->buckets[bucket_index];
307
308   if (b->offset == 0)
309     return 0;
310
311   hash >>= t->log2_nbuckets;
312
313   v = vnet_classify_get_entry (t, b->offset);
314   value_index = hash & ((1<<b->log2_pages)-1);
315
316   v = vnet_classify_entry_at_index (t, v, value_index);
317
318   for (i = 0; i < t->entries_per_page; i++)
319     {
320       mask = t->mask;
321       data = data_start;
322       key = v->key;
323       
324       switch (t->match_n_vectors)
325         {
326         case 1:
327           result = (data[0 + t->skip_n_vectors] & mask[0]) ^ key[0];
328           break;
329           
330         case 2:
331           result =  (data[0 + t->skip_n_vectors] & mask[0]) ^ key[0];
332           result |= (data[1 + t->skip_n_vectors] & mask[1]) ^ key[1];
333           break;
334
335         case 3:
336           result =  (data[0 + t->skip_n_vectors] & mask[0]) ^ key[0];
337           result |= (data[1 + t->skip_n_vectors] & mask[1]) ^ key[1];
338           result |= (data[2 + t->skip_n_vectors] & mask[2]) ^ key[2];
339           break;
340
341         case 4:
342           result =  (data[0 + t->skip_n_vectors] & mask[0]) ^ key[0];
343           result |= (data[1 + t->skip_n_vectors] & mask[1]) ^ key[1];
344           result |= (data[2 + t->skip_n_vectors] & mask[2]) ^ key[2];
345           result |= (data[3 + t->skip_n_vectors] & mask[3]) ^ key[3];
346           break;
347
348         case 5:
349           result =  (data[0 + t->skip_n_vectors] & mask[0]) ^ key[0];
350           result |= (data[1 + t->skip_n_vectors] & mask[1]) ^ key[1];
351           result |= (data[2 + t->skip_n_vectors] & mask[2]) ^ key[2];
352           result |= (data[3 + t->skip_n_vectors] & mask[3]) ^ key[3];
353           result |= (data[4 + t->skip_n_vectors] & mask[4]) ^ key[4];
354           break;
355
356         default:
357           abort();
358         }
359
360       result_mask = u32x4_zero_byte_mask (result);
361       if (result_mask == 0xffff)
362         {
363           if (PREDICT_TRUE(now))
364             {
365               v->hits++;
366               v->last_heard = now;
367             }
368           return (v);
369         }
370       v = vnet_classify_entry_at_index (t, v, 1);
371     }
372   return 0;
373 }
374
375 vnet_classify_table_t * 
376 vnet_classify_new_table (vnet_classify_main_t *cm,
377                          u8 * mask, u32 nbuckets, u32 memory_size,
378                          u32 skip_n_vectors,
379                          u32 match_n_vectors);
380
381 int vnet_classify_add_del_session (vnet_classify_main_t * cm, 
382                                    u32 table_index, 
383                                    u8 * match, 
384                                    u32 hit_next_index,
385                                    u32 opaque_index, 
386                                    i32 advance,
387                                    int is_add);
388
389 int vnet_classify_add_del_table (vnet_classify_main_t * cm,
390                                  u8 * mask, 
391                                  u32 nbuckets,
392                                  u32 memory_size,
393                                  u32 skip,
394                                  u32 match,
395                                  u32 next_table_index,
396                                  u32 miss_next_index,
397                                  u32 * table_index,
398                                  int is_add);
399
400 unformat_function_t unformat_ip4_mask;
401 unformat_function_t unformat_ip6_mask;
402 unformat_function_t unformat_l3_mask;
403 unformat_function_t unformat_l2_mask;
404 unformat_function_t unformat_classify_mask;
405 unformat_function_t unformat_l2_next_index;
406 unformat_function_t unformat_ip_next_index;
407 unformat_function_t unformat_ip4_match;
408 unformat_function_t unformat_ip6_match;
409 unformat_function_t unformat_l3_match;
410 unformat_function_t unformat_vlan_tag;
411 unformat_function_t unformat_l2_match;
412 unformat_function_t unformat_classify_match;
413
414 #endif /* __included_vnet_classify_h__ */