Add support for multiple microarchitectures in single binary
[vpp.git] / vnet / vnet / policer / node_funcs.c
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
16 #include <stdint.h>
17
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/policer/policer.h>
21
22 /* Dispatch functions meant to be instantiated elsewhere */
23
24 typedef struct {
25   u32 next_index;
26   u32 sw_if_index;
27   u32 policer_index;
28 } vnet_policer_trace_t;
29
30 /* packet trace format function */
31 static u8 * format_policer_trace (u8 * s, va_list * args)
32 {
33   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
34   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
35   vnet_policer_trace_t * t = va_arg (*args, vnet_policer_trace_t *);
36   
37   s = format (s, "VNET_POLICER: sw_if_index %d policer_index %d next %d",
38               t->sw_if_index, t->policer_index, t->next_index);
39   return s;
40 }
41
42 #define foreach_vnet_policer_error              \
43 _(TRANSMIT, "Packets Transmitted")              \
44 _(DROP, "Packets Dropped")
45
46 typedef enum {
47 #define _(sym,str) VNET_POLICER_ERROR_##sym,
48   foreach_vnet_policer_error
49 #undef _
50   VNET_POLICER_N_ERROR,
51 } vnet_policer_error_t;
52
53 static char * vnet_policer_error_strings[] = {
54 #define _(sym,string) string,
55   foreach_vnet_policer_error
56 #undef _
57 };
58
59 static inline
60 uword vnet_policer_inline (vlib_main_t * vm,
61                            vlib_node_runtime_t * node,
62                            vlib_frame_t * frame,
63                            vnet_policer_index_t which)
64 {
65   u32 n_left_from, * from, * to_next;
66   vnet_policer_next_t next_index;
67   vnet_policer_main_t * pm = &vnet_policer_main;
68   u64 time_in_policer_periods;
69   u32 transmitted = 0;
70
71   time_in_policer_periods = 
72     clib_cpu_time_now() >> POLICER_TICKS_PER_PERIOD_SHIFT;
73
74   from = vlib_frame_vector_args (frame);
75   n_left_from = frame->n_vectors;
76   next_index = node->cached_next_index;
77
78   while (n_left_from > 0)
79     {
80       u32 n_left_to_next;
81
82       vlib_get_next_frame (vm, node, next_index,
83                            to_next, n_left_to_next);
84
85       while (n_left_from >= 4 && n_left_to_next >= 2)
86         {
87           u32 bi0, bi1;
88           vlib_buffer_t * b0, * b1;
89           u32 next0, next1;
90           u32 sw_if_index0, sw_if_index1;
91           u32 pi0 = 0, pi1 = 0;
92           u32 len0, len1;
93           u32 col0, col1;
94           policer_read_response_type_st * pol0, * pol1;
95           
96           /* Prefetch next iteration. */
97           {
98             vlib_buffer_t * b2, * b3;
99             
100             b2 = vlib_get_buffer (vm, from[2]);
101             b3 = vlib_get_buffer (vm, from[3]);
102             
103             vlib_prefetch_buffer_header (b2, LOAD);
104             vlib_prefetch_buffer_header (b3, LOAD);
105           }
106
107           /* speculatively enqueue b0 and b1 to the current next frame */
108           to_next[0] = bi0 = from[0];
109           to_next[1] = bi1 = from[1];
110           from += 2;
111           to_next += 2;
112           n_left_from -= 2;
113           n_left_to_next -= 2;
114
115           b0 = vlib_get_buffer (vm, bi0);
116           b1 = vlib_get_buffer (vm, bi1);
117
118           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
119           next0 = VNET_POLICER_NEXT_TRANSMIT;
120
121           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
122           next1 = VNET_POLICER_NEXT_TRANSMIT;
123
124
125           if (which == VNET_POLICER_INDEX_BY_SW_IF_INDEX)
126             {
127               pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
128               pi1 = pm->policer_index_by_sw_if_index[sw_if_index1];
129             }
130
131           if (which == VNET_POLICER_INDEX_BY_OPAQUE)
132             {
133               pi0 = vnet_buffer(b0)->policer.index;
134               pi1 = vnet_buffer(b1)->policer.index;
135             }
136
137           if (which == VNET_POLICER_INDEX_BY_EITHER)
138             {
139               pi0 = vnet_buffer(b0)->policer.index;
140               pi0 = (pi0 != ~0) ? pi0 : 
141                 pm->policer_index_by_sw_if_index [sw_if_index0];
142               pi1 = vnet_buffer(b1)->policer.index;
143               pi1 = (pi1 != ~0) ? pi1 : 
144                 pm->policer_index_by_sw_if_index [sw_if_index1];
145             }
146
147           len0 = vlib_buffer_length_in_chain (vm, b0);
148           pol0 = &pm->policers [pi0];
149           col0 = vnet_police_packet (pol0, len0, 
150                                      POLICE_CONFORM /* no chaining */,
151                                      time_in_policer_periods);
152
153           len1 = vlib_buffer_length_in_chain (vm, b1);
154           pol1 = &pm->policers [pi1];
155           col1 = vnet_police_packet (pol1, len1, 
156                                      POLICE_CONFORM /* no chaining */,
157                                      time_in_policer_periods);
158           
159           if (PREDICT_FALSE(col0 > 0))
160             {
161               next0 = VNET_POLICER_NEXT_DROP;
162               b0->error = node->errors[VNET_POLICER_ERROR_DROP];
163             }
164           else
165             transmitted++;
166           
167           if (PREDICT_FALSE(col1 > 0))
168             {
169               next1 = VNET_POLICER_NEXT_DROP;
170               b1->error = node->errors[VNET_POLICER_ERROR_DROP];
171             }
172           else
173             transmitted++;
174
175
176           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
177             {
178               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
179                 {
180                   vnet_policer_trace_t *t = 
181                     vlib_add_trace (vm, node, b0, sizeof (*t));
182                   t->sw_if_index = sw_if_index0;
183                   t->next_index = next0;
184                 }
185               if (b1->flags & VLIB_BUFFER_IS_TRACED) 
186                 {
187                   vnet_policer_trace_t *t = 
188                     vlib_add_trace (vm, node, b1, sizeof (*t));
189                   t->sw_if_index = sw_if_index1;
190                   t->next_index = next1;
191                 }
192             }
193             
194           /* verify speculative enqueues, maybe switch current next frame */
195           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
196                                            to_next, n_left_to_next,
197                                            bi0, bi1, next0, next1);
198         }
199       
200       while (n_left_from > 0 && n_left_to_next > 0)
201         {
202           u32 bi0;
203           vlib_buffer_t * b0;
204           u32 next0;
205           u32 sw_if_index0;
206           u32 pi0 = 0;
207           u32 len0;
208           u32 col0;
209           policer_read_response_type_st * pol0;
210
211           bi0 = from[0];
212           to_next[0] = bi0;
213           from += 1;
214           to_next += 1;
215           n_left_from -= 1;
216           n_left_to_next -= 1;
217
218           b0 = vlib_get_buffer (vm, bi0);
219
220           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
221           next0 = VNET_POLICER_NEXT_TRANSMIT;
222
223           if (which == VNET_POLICER_INDEX_BY_SW_IF_INDEX)
224             pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
225
226           if (which == VNET_POLICER_INDEX_BY_OPAQUE)
227             pi0 = vnet_buffer(b0)->policer.index;
228
229           if (which == VNET_POLICER_INDEX_BY_EITHER)
230             {
231               pi0 = vnet_buffer(b0)->policer.index;
232               pi0 = (pi0 != ~0) ? pi0 : 
233                 pm->policer_index_by_sw_if_index [sw_if_index0];
234             }
235
236           len0 = vlib_buffer_length_in_chain (vm, b0);
237           pol0 = &pm->policers [pi0];
238           col0 = vnet_police_packet (pol0, len0, 
239                                      POLICE_CONFORM /* no chaining */,
240                                      time_in_policer_periods);
241           
242           if (PREDICT_FALSE(col0 > 0))
243             {
244               next0 = VNET_POLICER_NEXT_DROP;
245               b0->error = node->errors[VNET_POLICER_ERROR_DROP];
246             }
247           else
248             {
249               transmitted++;
250             }
251           
252           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
253                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
254             {
255               vnet_policer_trace_t *t = 
256                 vlib_add_trace (vm, node, b0, sizeof (*t));
257               t->sw_if_index = sw_if_index0;
258               t->next_index = next0;
259               t->policer_index = pi0;
260             }
261             
262           /* verify speculative enqueue, maybe switch current next frame */
263           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
264                                            to_next, n_left_to_next,
265                                            bi0, next0);
266         }
267
268       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
269     }
270
271   vlib_node_increment_counter (vm, node->node_index, 
272                                VNET_POLICER_ERROR_TRANSMIT, 
273                                transmitted);
274   return frame->n_vectors;
275 }
276
277 uword vnet_policer_by_sw_if_index (vlib_main_t * vm,
278                                    vlib_node_runtime_t * node,
279                                    vlib_frame_t * frame)
280 {
281   return vnet_policer_inline (vm, node, frame, 
282                               VNET_POLICER_INDEX_BY_SW_IF_INDEX);
283 }
284
285 uword vnet_policer_by_opaque (vlib_main_t * vm,
286                               vlib_node_runtime_t * node,
287                               vlib_frame_t * frame)
288 {
289   return vnet_policer_inline (vm, node, frame, 
290                               VNET_POLICER_INDEX_BY_OPAQUE);
291 }
292
293 uword vnet_policer_by_either (vlib_main_t * vm,
294                               vlib_node_runtime_t * node,
295                               vlib_frame_t * frame)
296 {
297   return vnet_policer_inline (vm, node, frame, 
298                               VNET_POLICER_INDEX_BY_EITHER);
299 }
300
301 void vnet_policer_node_funcs_reference (void) { }
302
303
304 #define TEST_CODE 1
305
306 #ifdef TEST_CODE
307
308 VLIB_REGISTER_NODE (policer_by_sw_if_index_node, static) = {
309   .function = vnet_policer_by_sw_if_index,
310   .name = "policer-by-sw-if-index",
311   .vector_size = sizeof (u32),
312   .format_trace = format_policer_trace,
313   .type = VLIB_NODE_TYPE_INTERNAL,
314   
315   .n_errors = ARRAY_LEN(vnet_policer_error_strings),
316   .error_strings = vnet_policer_error_strings,
317   
318   .n_next_nodes = VNET_POLICER_N_NEXT,
319
320   /* edit / add dispositions here */
321   .next_nodes = {
322     [VNET_POLICER_NEXT_TRANSMIT] = "ethernet-input",
323     [VNET_POLICER_NEXT_DROP] = "error-drop",
324   },
325 };
326
327 VLIB_NODE_FUNCTION_MULTIARCH (policer_by_sw_if_index_node,
328                               vnet_policer_by_sw_if_index);
329
330
331 int test_policer_add_del (u32 rx_sw_if_index, u8 *config_name,
332                           int is_add)
333 {
334   vnet_policer_main_t * pm = &vnet_policer_main;  
335   policer_read_response_type_st * template;
336   policer_read_response_type_st * policer;
337   vnet_hw_interface_t * rxhi;
338   uword * p;
339
340   rxhi = vnet_get_sup_hw_interface (pm->vnet_main, rx_sw_if_index);
341
342   /* Make sure caller didn't pass a vlan subif, etc. */
343   if (rxhi->sw_if_index != rx_sw_if_index)
344     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
345
346   if (is_add)
347     {
348       
349       p = hash_get_mem (pm->policer_config_by_name, config_name);
350
351       if (p == 0)
352         return -2;
353
354       template = pool_elt_at_index (pm->policer_templates, p[0]);
355
356       vnet_hw_interface_rx_redirect_to_node 
357         (pm->vnet_main, 
358          rxhi->hw_if_index,
359          policer_by_sw_if_index_node.index);
360
361       pool_get_aligned (pm->policers, policer, CLIB_CACHE_LINE_BYTES);
362
363       policer[0] = template[0];
364
365       vec_validate (pm->policer_index_by_sw_if_index, rx_sw_if_index);
366       pm->policer_index_by_sw_if_index[rx_sw_if_index] 
367           = policer - pm->policers;
368     }
369   else
370     {
371       u32 pi;
372       vnet_hw_interface_rx_redirect_to_node (pm->vnet_main, 
373                                              rxhi->hw_if_index,
374                                              ~0 /* disable */);
375
376       pi = pm->policer_index_by_sw_if_index[rx_sw_if_index];
377       pm->policer_index_by_sw_if_index[rx_sw_if_index] = ~0;
378       pool_put_index (pm->policers, pi);
379     }
380   
381   return 0;
382 }
383
384 static clib_error_t *
385 test_policer_command_fn (vlib_main_t * vm,
386                          unformat_input_t * input,
387                          vlib_cli_command_t * cmd)
388 {
389   vnet_policer_main_t * pm = &vnet_policer_main;  
390   unformat_input_t _line_input, * line_input = &_line_input;
391   u32 rx_sw_if_index;
392   int rv;
393   u8 * config_name = 0;
394   int rx_set = 0;
395   int is_add = 1;
396   int is_show = 0;
397
398   /* Get a line of input. */
399   if (! unformat_user (input, unformat_line_input, line_input))
400     return 0;
401
402   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
403     {
404       if (unformat (line_input, "intfc %U", unformat_vnet_sw_interface,
405                     pm->vnet_main, &rx_sw_if_index))
406         rx_set = 1;
407       else if (unformat (line_input, "show"))
408         is_show=1;
409       else if (unformat (line_input, "policer %s", &config_name))
410         ;
411       else if (unformat (line_input, "del"))
412         is_add = 0;
413       else break;
414     }
415
416   if (rx_set == 0)
417     return clib_error_return (0, "interface not set");
418
419   if (is_show)
420     {
421       u32 pi = pm->policer_index_by_sw_if_index[rx_sw_if_index];
422       policer_read_response_type_st * policer;
423       policer = pool_elt_at_index (pm->policers, pi);
424       
425       vlib_cli_output (vm, "%U", format_policer_instance, policer);
426       return 0;
427     }
428
429   if (is_add && config_name == 0)
430     {
431       return clib_error_return (0, "policer config name required");
432     }
433
434   rv = test_policer_add_del (rx_sw_if_index, config_name, is_add);
435
436   switch (rv)
437     {
438     case 0:
439       break;
440
441     default:
442       return clib_error_return 
443         (0, "WARNING: vnet_vnet_policer_add_del returned %d", rv);
444     }
445   
446   return 0;
447 }
448
449 VLIB_CLI_COMMAND (test_patch_command, static) = {
450     .path = "test policer",
451     .short_help = 
452     "intfc <intfc> policer <policer-config-name> [del]",
453     .function = test_policer_command_fn,
454 };
455
456
457 #endif /* TEST_CODE */