Adj Delegates; don't store raw pointers
[vpp.git] / src / vnet / classify / flow_classify_node.c
1 /*
2  * Copyright (c) 2016 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
16 #include <stdint.h>
17
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/classify/flow_classify.h>
22 #include <vnet/classify/vnet_classify.h>
23
24 typedef struct
25 {
26   u32 sw_if_index;
27   u32 next_index;
28   u32 table_index;
29   u32 offset;
30 } flow_classify_trace_t;
31
32 static u8 *
33 format_flow_classify_trace (u8 * s, va_list * args)
34 {
35   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
36   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
37   flow_classify_trace_t *t = va_arg (*args, flow_classify_trace_t *);
38
39   s = format (s, "FLOW_CLASSIFY: sw_if_index %d next %d table %d offset %d",
40               t->sw_if_index, t->next_index, t->table_index, t->offset);
41   return s;
42 }
43
44 #define foreach_flow_classify_error                 \
45 _(MISS, "Flow classify misses")                     \
46 _(HIT, "Flow classify hits")                        \
47 _(CHAIN_HIT, "Flow classify hits after chain walk") \
48 _(DROP, "Flow classify action drop")
49
50 typedef enum
51 {
52 #define _(sym,str) FLOW_CLASSIFY_ERROR_##sym,
53   foreach_flow_classify_error
54 #undef _
55     FLOW_CLASSIFY_N_ERROR,
56 } flow_classify_error_t;
57
58 static char *flow_classify_error_strings[] = {
59 #define _(sym,string) string,
60   foreach_flow_classify_error
61 #undef _
62 };
63
64 static inline uword
65 flow_classify_inline (vlib_main_t * vm,
66                       vlib_node_runtime_t * node,
67                       vlib_frame_t * frame, flow_classify_table_id_t tid)
68 {
69   u32 n_left_from, *from, *to_next;
70   flow_classify_next_index_t next_index;
71   flow_classify_main_t *fcm = &flow_classify_main;
72   vnet_classify_main_t *vcm = fcm->vnet_classify_main;
73   f64 now = vlib_time_now (vm);
74   u32 hits = 0;
75   u32 misses = 0;
76   u32 chain_hits = 0;
77   u32 drop = 0;
78
79   from = vlib_frame_vector_args (frame);
80   n_left_from = frame->n_vectors;
81
82   /* First pass: compute hashes */
83   while (n_left_from > 2)
84     {
85       vlib_buffer_t *b0, *b1;
86       u32 bi0, bi1;
87       u8 *h0, *h1;
88       u32 sw_if_index0, sw_if_index1;
89       u32 table_index0, table_index1;
90       vnet_classify_table_t *t0, *t1;
91
92       /* Prefetch next iteration */
93       {
94         vlib_buffer_t *p1, *p2;
95
96         p1 = vlib_get_buffer (vm, from[1]);
97         p2 = vlib_get_buffer (vm, from[2]);
98
99         vlib_prefetch_buffer_header (p1, STORE);
100         CLIB_PREFETCH (p1->data, CLIB_CACHE_LINE_BYTES, STORE);
101         vlib_prefetch_buffer_header (p2, STORE);
102         CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
103       }
104
105       bi0 = from[0];
106       b0 = vlib_get_buffer (vm, bi0);
107       h0 = b0->data;
108
109       bi1 = from[1];
110       b1 = vlib_get_buffer (vm, bi1);
111       h1 = b1->data;
112
113       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
114       table_index0 =
115         fcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
116
117       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
118       table_index1 =
119         fcm->classify_table_index_by_sw_if_index[tid][sw_if_index1];
120
121       t0 = pool_elt_at_index (vcm->tables, table_index0);
122
123       t1 = pool_elt_at_index (vcm->tables, table_index1);
124
125       vnet_buffer (b0)->l2_classify.hash =
126         vnet_classify_hash_packet (t0, (u8 *) h0);
127
128       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
129
130       vnet_buffer (b1)->l2_classify.hash =
131         vnet_classify_hash_packet (t1, (u8 *) h1);
132
133       vnet_classify_prefetch_bucket (t1, vnet_buffer (b1)->l2_classify.hash);
134
135       vnet_buffer (b0)->l2_classify.table_index = table_index0;
136
137       vnet_buffer (b1)->l2_classify.table_index = table_index1;
138
139       from += 2;
140       n_left_from -= 2;
141     }
142
143   while (n_left_from > 0)
144     {
145       vlib_buffer_t *b0;
146       u32 bi0;
147       u8 *h0;
148       u32 sw_if_index0;
149       u32 table_index0;
150       vnet_classify_table_t *t0;
151
152       bi0 = from[0];
153       b0 = vlib_get_buffer (vm, bi0);
154       h0 = b0->data;
155
156       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
157       table_index0 =
158         fcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
159
160       t0 = pool_elt_at_index (vcm->tables, table_index0);
161       vnet_buffer (b0)->l2_classify.hash =
162         vnet_classify_hash_packet (t0, (u8 *) h0);
163
164       vnet_buffer (b0)->l2_classify.table_index = table_index0;
165       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
166
167       from++;
168       n_left_from--;
169     }
170
171   next_index = node->cached_next_index;
172   from = vlib_frame_vector_args (frame);
173   n_left_from = frame->n_vectors;
174
175   while (n_left_from > 0)
176     {
177       u32 n_left_to_next;
178
179       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
180
181       /* Not enough load/store slots to dual loop... */
182       while (n_left_from > 0 && n_left_to_next > 0)
183         {
184           u32 bi0;
185           vlib_buffer_t *b0;
186           u32 next0 = FLOW_CLASSIFY_NEXT_INDEX_DROP;
187           u32 table_index0;
188           vnet_classify_table_t *t0;
189           vnet_classify_entry_t *e0;
190           u64 hash0;
191           u8 *h0;
192
193           /* Stride 3 seems to work best */
194           if (PREDICT_TRUE (n_left_from > 3))
195             {
196               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[3]);
197               vnet_classify_table_t *tp1;
198               u32 table_index1;
199               u64 phash1;
200
201               table_index1 = vnet_buffer (p1)->l2_classify.table_index;
202
203               if (PREDICT_TRUE (table_index1 != ~0))
204                 {
205                   tp1 = pool_elt_at_index (vcm->tables, table_index1);
206                   phash1 = vnet_buffer (p1)->l2_classify.hash;
207                   vnet_classify_prefetch_entry (tp1, phash1);
208                 }
209             }
210
211           /* Speculatively enqueue b0 to the current next frame */
212           bi0 = from[0];
213           to_next[0] = bi0;
214           from += 1;
215           to_next += 1;
216           n_left_from -= 1;
217           n_left_to_next -= 1;
218
219           b0 = vlib_get_buffer (vm, bi0);
220           h0 = b0->data;
221           table_index0 = vnet_buffer (b0)->l2_classify.table_index;
222           e0 = 0;
223           t0 = 0;
224
225           vnet_get_config_data (fcm->vnet_config_main[tid],
226                                 &b0->current_config_index, &next0,
227                                 /* # bytes of config data */ 0);
228
229           if (PREDICT_TRUE (table_index0 != ~0))
230             {
231               hash0 = vnet_buffer (b0)->l2_classify.hash;
232               t0 = pool_elt_at_index (vcm->tables, table_index0);
233               e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
234               if (e0)
235                 {
236                   hits++;
237                 }
238               else
239                 {
240                   misses++;
241                   vnet_classify_add_del_session (vcm, table_index0,
242                                                  h0, ~0, 0, 0, 0, 0, 1);
243                   /* increment counter */
244                   vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
245                 }
246             }
247           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
248                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
249             {
250               flow_classify_trace_t *t =
251                 vlib_add_trace (vm, node, b0, sizeof (*t));
252               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
253               t->next_index = next0;
254               t->table_index = t0 ? t0 - vcm->tables : ~0;
255               t->offset = (t0 && e0) ? vnet_classify_get_offset (t0, e0) : ~0;
256             }
257
258           /* Verify speculative enqueue, maybe switch current next frame */
259           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
260                                            n_left_to_next, bi0, next0);
261         }
262
263       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
264     }
265
266   vlib_node_increment_counter (vm, node->node_index,
267                                FLOW_CLASSIFY_ERROR_MISS, misses);
268   vlib_node_increment_counter (vm, node->node_index,
269                                FLOW_CLASSIFY_ERROR_HIT, hits);
270   vlib_node_increment_counter (vm, node->node_index,
271                                FLOW_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);
272   vlib_node_increment_counter (vm, node->node_index,
273                                FLOW_CLASSIFY_ERROR_DROP, drop);
274
275   return frame->n_vectors;
276 }
277
278 static uword
279 ip4_flow_classify (vlib_main_t * vm,
280                    vlib_node_runtime_t * node, vlib_frame_t * frame)
281 {
282   return flow_classify_inline (vm, node, frame, FLOW_CLASSIFY_TABLE_IP4);
283 }
284
285 /* *INDENT-OFF* */
286 VLIB_REGISTER_NODE (ip4_flow_classify_node) = {
287   .function = ip4_flow_classify,
288   .name = "ip4-flow-classify",
289   .vector_size = sizeof (u32),
290   .format_trace = format_flow_classify_trace,
291   .n_errors = ARRAY_LEN(flow_classify_error_strings),
292   .error_strings = flow_classify_error_strings,
293   .n_next_nodes = FLOW_CLASSIFY_NEXT_INDEX_N_NEXT,
294   .next_nodes = {
295     [FLOW_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
296   },
297 };
298 /* *INDENT-ON* */
299
300 VLIB_NODE_FUNCTION_MULTIARCH (ip4_flow_classify_node, ip4_flow_classify);
301
302 static uword
303 ip6_flow_classify (vlib_main_t * vm,
304                    vlib_node_runtime_t * node, vlib_frame_t * frame)
305 {
306   return flow_classify_inline (vm, node, frame, FLOW_CLASSIFY_TABLE_IP6);
307 }
308
309 /* *INDENT-OFF* */
310 VLIB_REGISTER_NODE (ip6_flow_classify_node) = {
311   .function = ip6_flow_classify,
312   .name = "ip6-flow-classify",
313   .vector_size = sizeof (u32),
314   .format_trace = format_flow_classify_trace,
315   .n_errors = ARRAY_LEN(flow_classify_error_strings),
316   .error_strings = flow_classify_error_strings,
317   .n_next_nodes = FLOW_CLASSIFY_NEXT_INDEX_N_NEXT,
318   .next_nodes = {
319     [FLOW_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
320   },
321 };
322
323 /* *INDENT-ON* */
324
325 VLIB_NODE_FUNCTION_MULTIARCH (ip6_flow_classify_node, ip6_flow_classify);
326
327
328 static clib_error_t *
329 flow_classify_init (vlib_main_t * vm)
330 {
331   flow_classify_main_t *fcm = &flow_classify_main;
332
333   fcm->vlib_main = vm;
334   fcm->vnet_main = vnet_get_main ();
335   fcm->vnet_classify_main = &vnet_classify_main;
336
337   return 0;
338 }
339
340 VLIB_INIT_FUNCTION (flow_classify_init);
341
342 /*
343  * fd.io coding-style-patch-verification: ON
344  *
345  * Local Variables:
346  * eval: (c-set-style "gnu")
347  * End:
348  */