Add support for multiple microarchitectures in single binary
[vpp.git] / vnet / vnet / interface_output.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  * interface_output.c: interface output node
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vnet/vnet.h>
41
42 typedef struct {
43   u32 sw_if_index;
44   u8 data[128 - sizeof (u32)];
45 } interface_output_trace_t;
46
47 u8 * format_vnet_interface_output_trace (u8 * s, va_list * va)
48 {
49   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
50   vlib_node_t * node = va_arg (*va, vlib_node_t *);
51   interface_output_trace_t * t = va_arg (*va, interface_output_trace_t *);
52   vnet_main_t * vnm = vnet_get_main();
53   vnet_sw_interface_t * si;
54   uword indent;
55
56   if (t->sw_if_index != (u32)~0)
57     {
58       si = vnet_get_sw_interface (vnm, t->sw_if_index);
59       indent = format_get_indent (s);
60       
61       s = format (s, "%U\n%U%U",
62                   format_vnet_sw_interface_name, vnm, si,
63                   format_white_space, indent,
64                   node->format_buffer ? node->format_buffer : format_hex_bytes,
65                   t->data, sizeof (t->data));
66     }
67   return s;
68 }
69
70 static void
71 vnet_interface_output_trace (vlib_main_t * vm,
72                              vlib_node_runtime_t * node,
73                              vlib_frame_t * frame,
74                              uword n_buffers)
75 {
76   u32 n_left, * from;
77
78   n_left = n_buffers;
79   from = vlib_frame_args (frame);
80   
81   while (n_left >= 4)
82     {
83       u32 bi0, bi1;
84       vlib_buffer_t * b0, * b1;
85       interface_output_trace_t * t0, * t1;
86
87       /* Prefetch next iteration. */
88       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
89       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
90
91       bi0 = from[0];
92       bi1 = from[1];
93
94       b0 = vlib_get_buffer (vm, bi0);
95       b1 = vlib_get_buffer (vm, bi1);
96
97       if (b0->flags & VLIB_BUFFER_IS_TRACED)
98         {
99           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
100           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
101           clib_memcpy (t0->data, vlib_buffer_get_current (b0),
102                   sizeof (t0->data));
103         }
104       if (b1->flags & VLIB_BUFFER_IS_TRACED)
105         {
106           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
107           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
108           clib_memcpy (t1->data, vlib_buffer_get_current (b1),
109                   sizeof (t1->data));
110         }
111       from += 2;
112       n_left -= 2;
113     }
114
115   while (n_left >= 1)
116     {
117       u32 bi0;
118       vlib_buffer_t * b0;
119       interface_output_trace_t * t0;
120
121       bi0 = from[0];
122
123       b0 = vlib_get_buffer (vm, bi0);
124
125       if (b0->flags & VLIB_BUFFER_IS_TRACED)
126         {
127           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
128           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
129           clib_memcpy (t0->data, vlib_buffer_get_current (b0),
130                   sizeof (t0->data));
131         }
132       from += 1;
133       n_left -= 1;
134     }
135 }
136
137 static never_inline u32
138 slow_path (vlib_main_t * vm,
139            u32 bi,
140            vlib_buffer_t * b,
141            u32 n_left_to_tx,
142            u32 * to_tx,
143            u32 * n_slow_bytes_result)
144 {
145   /* We've already enqueued a single buffer. */
146   u32 n_buffers = 0;
147   u32 n_slow_bytes = 0;
148
149   while (n_left_to_tx > 0)
150     {
151       to_tx[0] = bi;
152       to_tx += 1;
153       n_left_to_tx -= 1;
154       n_buffers += 1;
155       n_slow_bytes += vlib_buffer_length_in_chain (vm, b);
156
157       /* Be grumpy about zero length buffers for benefit of
158          driver tx function. */
159       ASSERT (b->current_length > 0);
160
161       if (! (b->flags & VLIB_BUFFER_NEXT_PRESENT))
162         break;
163
164       bi = b->next_buffer;
165       b = vlib_get_buffer (vm, bi);
166     }
167
168   /* Ran out of space in next frame trying to enqueue buffers? */
169   if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
170     return 0;
171
172   *n_slow_bytes_result = n_slow_bytes;
173   return n_buffers;
174 }
175
176 /* 
177  * Increment TX stats. Roll up consecutive increments to the same sw_if_index 
178  * into one increment.
179  */
180 static_always_inline
181 void incr_output_stats (vnet_main_t * vnm,
182                         u32 cpu_index, 
183                         u32 length, 
184                         u32 sw_if_index,
185                         u32 * last_sw_if_index, 
186                         u32 * n_packets, 
187                         u32 * n_bytes) {
188   vnet_interface_main_t * im;
189
190   if (PREDICT_TRUE (sw_if_index == *last_sw_if_index)) {
191     *n_packets += 1;
192     *n_bytes += length;
193   } else {
194     if (PREDICT_TRUE (*last_sw_if_index != ~0)) {
195       im = &vnm->interface_main;
196
197       vlib_increment_combined_counter (im->combined_sw_if_counters
198                                        + VNET_INTERFACE_COUNTER_TX,
199                                        cpu_index, 
200                                        *last_sw_if_index,
201                                        *n_packets,
202                                        *n_bytes);
203     }
204     *last_sw_if_index = sw_if_index;
205     *n_packets = 1;
206     *n_bytes = length;
207   }
208 }
209
210
211 /* Interface output functions. */
212 uword
213 vnet_interface_output_node (vlib_main_t * vm,
214                             vlib_node_runtime_t * node,
215                             vlib_frame_t * frame)
216 {
217   vnet_main_t * vnm = vnet_get_main();
218   vnet_interface_output_runtime_t * rt = (void *) node->runtime_data;
219   vnet_sw_interface_t * si;
220   vnet_hw_interface_t * hi;
221   u32 n_left_to_tx, * from, * from_end, * to_tx;
222   u32 n_bytes, n_buffers, n_packets;
223   u32 last_sw_if_index;
224   u32 cpu_index = vm->cpu_index;
225
226   n_buffers = frame->n_vectors;
227
228   if (node->flags & VLIB_NODE_FLAG_TRACE)
229     vnet_interface_output_trace (vm, node, frame, n_buffers);
230
231   from = vlib_frame_args (frame);
232
233   if (rt->is_deleted)
234     return vlib_error_drop_buffers (vm, node,
235                                     from,
236                                     /* buffer stride */ 1,
237                                     n_buffers,
238                                     VNET_INTERFACE_OUTPUT_NEXT_DROP,
239                                     node->node_index,
240                                     VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
241
242   si = vnet_get_sw_interface (vnm, rt->sw_if_index);
243   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
244   if (! (si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ||
245       ! (hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
246     {
247       vlib_simple_counter_main_t * cm;
248      
249       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
250                              VNET_INTERFACE_COUNTER_TX_ERROR);
251       vlib_increment_simple_counter (cm, cpu_index,
252                                      rt->sw_if_index, n_buffers);
253       return vlib_error_drop_buffers (vm, node,
254                                       from,
255                                       /* buffer stride */ 1,
256                                       n_buffers,
257                                       VNET_INTERFACE_OUTPUT_NEXT_DROP,
258                                       node->node_index,
259                                    VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
260     }
261
262   from_end = from + n_buffers;
263
264   /* Total byte count of all buffers. */
265   n_bytes = 0;
266   n_packets = 0;
267   last_sw_if_index = ~0;
268
269   while (from < from_end)
270     {
271       /* Get new next frame since previous incomplete frame may have less
272          than VNET_FRAME_SIZE vectors in it. */
273       vlib_get_new_next_frame (vm, node, VNET_INTERFACE_OUTPUT_NEXT_TX,
274                                to_tx, n_left_to_tx);
275
276       while (from + 4 <= from_end && n_left_to_tx >= 2)
277         {
278           u32 bi0, bi1;
279           vlib_buffer_t * b0, * b1;
280
281           /* Prefetch next iteration. */
282           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
283           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
284
285           bi0 = from[0];
286           bi1 = from[1];
287           to_tx[0] = bi0;
288           to_tx[1] = bi1;
289           from += 2;
290           to_tx += 2;
291           n_left_to_tx -= 2;
292
293           b0 = vlib_get_buffer (vm, bi0);
294           b1 = vlib_get_buffer (vm, bi1);
295
296           /* Be grumpy about zero length buffers for benefit of
297              driver tx function. */
298           ASSERT (b0->current_length > 0);
299           ASSERT (b1->current_length > 0);
300
301           if (PREDICT_FALSE ((b0->flags | b1->flags) & VLIB_BUFFER_NEXT_PRESENT))
302             {
303               u32 n_buffers, n_slow_bytes, i;
304
305               /* Undo. */
306               from -= 2;
307               to_tx -= 2;
308               n_left_to_tx += 2;
309
310               /* Do slow path two times. */
311               for (i = 0; i < 2; i++)
312                 {
313                   u32 bi = i ? bi1 : bi0;
314                   vlib_buffer_t * b = i ? b1 : b0;
315
316                   n_buffers = slow_path (vm, bi, b,
317                                          n_left_to_tx, to_tx, &n_slow_bytes);
318
319                   /* Not enough room for single packet? */
320                   if (n_buffers == 0)
321                     goto put;
322
323                   from += 1;
324                   to_tx += n_buffers;
325                   n_left_to_tx -= n_buffers;
326                   incr_output_stats (vnm, cpu_index, n_slow_bytes,
327                                      vnet_buffer(b)->sw_if_index[VLIB_TX],
328                                      &last_sw_if_index, &n_packets, &n_bytes);
329                 }
330             } else {
331               incr_output_stats (vnm, cpu_index, 
332                                  vlib_buffer_length_in_chain (vm, b0),
333                                  vnet_buffer(b0)->sw_if_index[VLIB_TX],
334                                  &last_sw_if_index, &n_packets, &n_bytes);
335               incr_output_stats (vnm, cpu_index, 
336                                  vlib_buffer_length_in_chain (vm, b0),
337                                  vnet_buffer(b1)->sw_if_index[VLIB_TX],
338                                  &last_sw_if_index, &n_packets, &n_bytes);
339             }
340         }
341
342       while (from + 1 <= from_end && n_left_to_tx >= 1)
343         {
344           u32 bi0;
345           vlib_buffer_t * b0;
346
347           bi0 = from[0];
348           to_tx[0] = bi0;
349           from += 1;
350           to_tx += 1;
351           n_left_to_tx -= 1;
352
353           b0 = vlib_get_buffer (vm, bi0);
354
355           /* Be grumpy about zero length buffers for benefit of
356              driver tx function. */
357           ASSERT (b0->current_length > 0);
358
359           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_NEXT_PRESENT))
360             {
361               u32 n_buffers, n_slow_bytes;
362
363               /* Undo. */
364               from -= 1;
365               to_tx -= 1;
366               n_left_to_tx += 1;
367
368               n_buffers = slow_path (vm, bi0, b0,
369                                      n_left_to_tx, to_tx, &n_slow_bytes);
370
371               /* Not enough room for single packet? */
372               if (n_buffers == 0)
373                 goto put;
374
375               from += 1;
376               to_tx += n_buffers;
377               n_left_to_tx -= n_buffers;
378             }
379           incr_output_stats (vnm, cpu_index, 
380                              vlib_buffer_length_in_chain (vm, b0),
381                              vnet_buffer(b0)->sw_if_index[VLIB_TX],
382                              &last_sw_if_index, &n_packets, &n_bytes);
383         }
384
385     put:
386       vlib_put_next_frame (vm, node, VNET_INTERFACE_OUTPUT_NEXT_TX, n_left_to_tx);
387     }
388
389   /* Final update of interface stats. */
390   incr_output_stats (vnm, cpu_index, 0, ~0, /* ~0 will flush stats */
391                      &last_sw_if_index, &n_packets, &n_bytes); 
392
393   return n_buffers;
394 }
395
396
397 uword
398 vnet_interface_output_node_no_flatten (vlib_main_t * vm,
399                                        vlib_node_runtime_t * node,
400                                        vlib_frame_t * frame)
401 {
402   vnet_main_t * vnm = vnet_get_main();
403   vnet_interface_output_runtime_t * rt = (void *) node->runtime_data;
404   vnet_sw_interface_t * si;
405   vnet_hw_interface_t * hi;
406   u32 n_left_to_tx, * from, * from_end, * to_tx;
407   u32 n_bytes, n_buffers, n_packets;
408   u32 n_bytes_b0, n_bytes_b1;
409   u32 cpu_index = vm->cpu_index;
410   vnet_interface_main_t * im = &vnm->interface_main;
411   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
412
413   n_buffers = frame->n_vectors;
414
415   if (node->flags & VLIB_NODE_FLAG_TRACE)
416     vnet_interface_output_trace (vm, node, frame, n_buffers);
417
418   from = vlib_frame_args (frame);
419
420   if (rt->is_deleted)
421     return vlib_error_drop_buffers (vm, node,
422                                     from,
423                                     /* buffer stride */ 1,
424                                     n_buffers,
425                                     VNET_INTERFACE_OUTPUT_NEXT_DROP,
426                                     node->node_index,
427                                     VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
428
429   si = vnet_get_sw_interface (vnm, rt->sw_if_index);
430   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
431   if (! (si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ||
432       ! (hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
433     {
434       vlib_simple_counter_main_t * cm;
435      
436       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
437                              VNET_INTERFACE_COUNTER_TX_ERROR);
438       vlib_increment_simple_counter (cm, cpu_index,
439                                      rt->sw_if_index, n_buffers);
440       
441       return vlib_error_drop_buffers (vm, node,
442                                       from,
443                                       /* buffer stride */ 1,
444                                       n_buffers,
445                                       VNET_INTERFACE_OUTPUT_NEXT_DROP,
446                                       node->node_index,
447                                       VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
448     }
449
450   from_end = from + n_buffers;
451
452   /* Total byte count of all buffers. */
453   n_bytes = 0;
454   n_packets = 0;
455
456   while (from < from_end)
457     {
458       /* Get new next frame since previous incomplete frame may have less
459          than VNET_FRAME_SIZE vectors in it. */
460       vlib_get_new_next_frame (vm, node, next_index,
461                                to_tx, n_left_to_tx);
462
463       while (from + 4 <= from_end && n_left_to_tx >= 2)
464         {
465           u32 bi0, bi1;
466           vlib_buffer_t * b0, * b1;
467           u32 tx_swif0, tx_swif1;
468
469           /* Prefetch next iteration. */
470           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
471           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
472
473           bi0 = from[0];
474           bi1 = from[1];
475           to_tx[0] = bi0;
476           to_tx[1] = bi1;
477           from += 2;
478           to_tx += 2;
479           n_left_to_tx -= 2;
480
481           b0 = vlib_get_buffer (vm, bi0);
482           b1 = vlib_get_buffer (vm, bi1);
483
484           /* Be grumpy about zero length buffers for benefit of
485              driver tx function. */
486           ASSERT (b0->current_length > 0);
487           ASSERT (b1->current_length > 0);
488
489           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b0);
490           n_bytes_b1 = vlib_buffer_length_in_chain (vm, b1);
491           tx_swif0 = vnet_buffer(b0)->sw_if_index[VLIB_TX];
492           tx_swif1 = vnet_buffer(b1)->sw_if_index[VLIB_TX];
493
494           n_bytes += n_bytes_b0 + n_bytes_b1;
495           n_packets += 2;
496
497           if (PREDICT_FALSE(si->output_feature_bitmap &&
498               ((b0->flags & BUFFER_OUTPUT_FEAT_DONE) == 0)))
499             {
500               u32 next0;
501               b0->flags |= BUFFER_OUTPUT_FEAT_DONE;
502               vnet_buffer(b0)->output_features.bitmap = si->output_feature_bitmap;
503               count_trailing_zeros(next0, vnet_buffer(b0)->output_features.bitmap);
504               vnet_buffer(b0)->output_features.bitmap &= ~(1 << next0);
505               vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_tx,
506                                                n_left_to_tx, bi0, next0);
507             }
508           else
509             {
510               vnet_buffer(b0)->output_features.bitmap = 0;
511
512               if (PREDICT_FALSE(tx_swif0 != rt->sw_if_index))
513                 {
514                   /* update vlan subif tx counts, if required */
515                   vlib_increment_combined_counter (im->combined_sw_if_counters
516                                                    + VNET_INTERFACE_COUNTER_TX,
517                                                    cpu_index,
518                                                    tx_swif0,
519                                                    1,
520                                                    n_bytes_b0);
521                 }
522             }
523
524           if (PREDICT_FALSE(si->output_feature_bitmap &&
525               ((b1->flags & BUFFER_OUTPUT_FEAT_DONE) == 0)))
526             {
527               u32 next1;
528               b1->flags |= BUFFER_OUTPUT_FEAT_DONE;
529               vnet_buffer(b1)->output_features.bitmap = si->output_feature_bitmap;
530               count_trailing_zeros(next1, vnet_buffer(b1)->output_features.bitmap);
531               vnet_buffer(b1)->output_features.bitmap &= ~(1 << next1);
532               vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_tx,
533                                                n_left_to_tx, bi1, next1);
534             }
535           else
536             {
537               vnet_buffer(b1)->output_features.bitmap = 0;
538
539               /* update vlan subif tx counts, if required */
540               if (PREDICT_FALSE(tx_swif1 != rt->sw_if_index))
541                 {
542
543                   vlib_increment_combined_counter (im->combined_sw_if_counters
544                                                    + VNET_INTERFACE_COUNTER_TX,
545                                                    cpu_index,
546                                                    tx_swif1,
547                                                    1,
548                                                    n_bytes_b1);
549                 }
550             }
551
552         }
553
554       while (from + 1 <= from_end && n_left_to_tx >= 1)
555         {
556           u32 bi0;
557           vlib_buffer_t * b0;
558           u32 tx_swif0;
559
560           bi0 = from[0];
561           to_tx[0] = bi0;
562           from += 1;
563           to_tx += 1;
564           n_left_to_tx -= 1;
565
566           b0 = vlib_get_buffer (vm, bi0);
567
568           /* Be grumpy about zero length buffers for benefit of
569              driver tx function. */
570           ASSERT (b0->current_length > 0);
571
572           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b0);
573           tx_swif0 = vnet_buffer(b0)->sw_if_index[VLIB_TX];
574           n_bytes += n_bytes_b0;
575           n_packets += 1;
576
577           if (PREDICT_FALSE(si->output_feature_bitmap &&
578               ((b0->flags & BUFFER_OUTPUT_FEAT_DONE) == 0)))
579             {
580               u32 next0;
581               b0->flags |= BUFFER_OUTPUT_FEAT_DONE;
582               vnet_buffer(b0)->output_features.bitmap = si->output_feature_bitmap;
583               count_trailing_zeros(next0, vnet_buffer(b0)->output_features.bitmap);
584               vnet_buffer(b0)->output_features.bitmap &= ~(1 << next0);
585               vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_tx,
586                                                n_left_to_tx, bi0, next0);
587             }
588           else
589             {
590               vnet_buffer(b0)->output_features.bitmap = 0;
591
592               if (PREDICT_FALSE(tx_swif0 != rt->sw_if_index))
593                 {
594
595                   vlib_increment_combined_counter (im->combined_sw_if_counters
596                                                    + VNET_INTERFACE_COUNTER_TX,
597                                                    cpu_index,
598                                                    tx_swif0,
599                                                    1,
600                                                    n_bytes_b0);
601                 }
602             }
603         }
604
605       vlib_put_next_frame (vm, node, next_index,
606                            n_left_to_tx);
607     }
608
609   /* Update main interface stats. */
610   vlib_increment_combined_counter (im->combined_sw_if_counters
611                                    + VNET_INTERFACE_COUNTER_TX,
612                                    cpu_index,
613                                    rt->sw_if_index,
614                                    n_packets,
615                                    n_bytes);
616   return n_buffers;
617 }
618
619
620 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
621 static uword
622 vnet_per_buffer_interface_output (vlib_main_t * vm,
623                                   vlib_node_runtime_t * node,
624                                   vlib_frame_t * frame)
625 {
626   vnet_main_t * vnm = vnet_get_main();
627   u32 n_left_to_next, * from, * to_next;
628   u32 n_left_from, next_index;
629
630   n_left_from = frame->n_vectors;
631
632   from = vlib_frame_args (frame);
633   next_index = node->cached_next_index;
634
635   while (n_left_from > 0)
636     {
637       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
638
639       while (n_left_from >= 4 && n_left_to_next >= 2)
640         {
641           u32 bi0, bi1, next0, next1;
642           vlib_buffer_t * b0, * b1;
643           vnet_hw_interface_t * hi0, * hi1;
644
645           /* Prefetch next iteration. */
646           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
647           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
648
649           bi0 = from[0];
650           bi1 = from[1];
651           to_next[0] = bi0;
652           to_next[1] = bi1;
653           from += 2;
654           to_next += 2;
655           n_left_to_next -= 2;
656           n_left_from -= 2;
657
658           b0 = vlib_get_buffer (vm, bi0);
659           b1 = vlib_get_buffer (vm, bi1);
660
661           hi0 = vnet_get_sup_hw_interface (vnm, vnet_buffer (b0)->sw_if_index[VLIB_TX]);
662           hi1 = vnet_get_sup_hw_interface (vnm, vnet_buffer (b1)->sw_if_index[VLIB_TX]);
663
664           next0 = hi0->hw_if_index;
665           next1 = hi1->hw_if_index;
666
667           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next, n_left_to_next,
668                                            bi0, bi1, next0, next1);
669         }
670
671       while (n_left_from > 0 && n_left_to_next > 0)
672         {
673           u32 bi0, next0;
674           vlib_buffer_t * b0;
675           vnet_hw_interface_t * hi0;
676
677           bi0 = from[0];
678           to_next[0] = bi0;
679           from += 1;
680           to_next += 1;
681           n_left_to_next -= 1;
682           n_left_from -= 1;
683
684           b0 = vlib_get_buffer (vm, bi0);
685
686           hi0 = vnet_get_sup_hw_interface (vnm, vnet_buffer (b0)->sw_if_index[VLIB_TX]);
687
688           next0 = hi0->hw_if_index;
689
690           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next,
691                                            bi0, next0);
692         }
693
694       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
695     }
696
697   return frame->n_vectors;
698 }
699
700 always_inline u32
701 counter_index (vlib_main_t * vm, vlib_error_t e)
702 {
703   vlib_node_t * n;
704   u32 ci, ni;
705
706   ni = vlib_error_get_node (e);
707   n = vlib_get_node (vm, ni);
708
709   ci = vlib_error_get_code (e);
710   ASSERT (ci < n->n_errors);
711
712   ci += n->error_heap_index;
713
714   return ci;
715 }
716
717 static u8 * format_vnet_error_trace (u8 * s, va_list * va)
718 {
719   vlib_main_t * vm = va_arg (*va, vlib_main_t *);
720   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
721   vlib_error_t * e = va_arg (*va, vlib_error_t *);
722   vlib_node_t * error_node;
723   vlib_error_main_t * em = &vm->error_main;
724   u32 i;
725
726   error_node = vlib_get_node (vm, vlib_error_get_node (e[0]));
727   i = counter_index (vm, e[0]);
728   s = format (s, "%v: %s", error_node->name, em->error_strings_heap[i]);
729
730   return s;
731 }
732
733 static void
734 trace_errors_with_buffers (vlib_main_t * vm,
735                            vlib_node_runtime_t * node,
736                            vlib_frame_t * frame)
737 {
738   u32 n_left, * buffers;
739
740   buffers = vlib_frame_vector_args (frame);
741   n_left = frame->n_vectors;
742   
743   while (n_left >= 4)
744     {
745       u32 bi0, bi1;
746       vlib_buffer_t * b0, * b1;
747       vlib_error_t * t0, * t1;
748
749       /* Prefetch next iteration. */
750       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
751       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
752
753       bi0 = buffers[0];
754       bi1 = buffers[1];
755
756       b0 = vlib_get_buffer (vm, bi0);
757       b1 = vlib_get_buffer (vm, bi1);
758
759       if (b0->flags & VLIB_BUFFER_IS_TRACED)
760         {
761           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
762           t0[0] = b0->error;
763         }
764       if (b1->flags & VLIB_BUFFER_IS_TRACED)
765         {
766           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
767           t1[0] = b1->error;
768         }
769       buffers += 2;
770       n_left -= 2;
771     }
772
773   while (n_left >= 1)
774     {
775       u32 bi0;
776       vlib_buffer_t * b0;
777       vlib_error_t * t0;
778
779       bi0 = buffers[0];
780
781       b0 = vlib_get_buffer (vm, bi0);
782
783       if (b0->flags & VLIB_BUFFER_IS_TRACED)
784         {
785           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
786           t0[0] = b0->error;
787         }
788       buffers += 1;
789       n_left -= 1;
790     }
791 }
792
793 static u8 *
794 validate_error (vlib_main_t * vm, vlib_error_t * e, u32 index)
795 {
796   uword node_index = vlib_error_get_node (e[0]);
797   uword code = vlib_error_get_code (e[0]);
798   vlib_node_t * n;
799
800   if (node_index >= vec_len (vm->node_main.nodes))
801     return format (0, "[%d], node index out of range 0x%x, error 0x%x",
802                    index, node_index, e[0]);
803
804   n = vlib_get_node (vm, node_index);
805   if (code >= n->n_errors)
806     return format (0, "[%d], code %d out of range for node %v",
807                    index, code, n->name);
808
809   return 0;
810 }
811
812 static u8 *
813 validate_error_frame (vlib_main_t * vm,
814                       vlib_node_runtime_t * node,
815                       vlib_frame_t * f)
816 {
817   u32 * buffers = vlib_frame_args (f);
818   vlib_buffer_t * b;
819   u8 * msg = 0;
820   uword i;
821
822   for (i = 0; i < f->n_vectors; i++)
823     {
824       b = vlib_get_buffer (vm, buffers[i]);
825       msg = validate_error (vm, &b->error, i);
826       if (msg)
827         return msg;
828     }
829
830   return msg;
831 }
832
833 typedef enum {
834   VNET_ERROR_DISPOSITION_DROP,
835   VNET_ERROR_DISPOSITION_PUNT,
836   VNET_ERROR_N_DISPOSITION,
837 } vnet_error_disposition_t;
838
839 always_inline void
840 do_packet (vlib_main_t * vm, vlib_error_t a)
841 {
842   vlib_error_main_t * em = &vm->error_main;
843   u32 i = counter_index (vm, a);
844   em->counters[i] += 1;
845   vlib_error_elog_count (vm, i, 1);
846 }
847     
848 static_always_inline uword
849 process_drop_punt (vlib_main_t * vm,
850                    vlib_node_runtime_t * node,
851                    vlib_frame_t * frame,
852                    vnet_error_disposition_t disposition)
853 {
854   vnet_main_t * vnm = vnet_get_main();
855   vlib_error_main_t * em = &vm->error_main;
856   u32 * buffers, * first_buffer;
857   vlib_error_t current_error;
858   u32 current_counter_index, n_errors_left;
859   u32 current_sw_if_index, n_errors_current_sw_if_index;
860   u64 current_counter;
861   vlib_simple_counter_main_t * cm;
862   u32 cpu_index = vm->cpu_index;
863
864   static vlib_error_t memory[VNET_ERROR_N_DISPOSITION];
865   static char memory_init[VNET_ERROR_N_DISPOSITION];
866
867   buffers = vlib_frame_args (frame);
868   first_buffer = buffers;
869
870   {
871     vlib_buffer_t * b = vlib_get_buffer (vm, first_buffer[0]);
872
873     if (! memory_init[disposition])
874       {
875         memory_init[disposition] = 1;
876         memory[disposition] = b->error;
877       }
878
879     current_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
880     n_errors_current_sw_if_index = 0;
881   }
882
883   current_error = memory[disposition];
884   current_counter_index = counter_index (vm, memory[disposition]);
885   current_counter = em->counters[current_counter_index];
886
887   if (node->flags & VLIB_NODE_FLAG_TRACE)
888     trace_errors_with_buffers (vm, node, frame);
889   
890   n_errors_left = frame->n_vectors;
891   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
892                          (disposition == VNET_ERROR_DISPOSITION_PUNT
893                           ? VNET_INTERFACE_COUNTER_PUNT
894                           : VNET_INTERFACE_COUNTER_DROP));
895
896   while (n_errors_left >= 2)
897     {
898       vlib_buffer_t * b0, * b1;
899       vnet_sw_interface_t * sw_if0, * sw_if1;
900       vlib_error_t e0, e1;
901       u32 bi0, bi1;
902       u32 sw_if_index0, sw_if_index1;
903
904       bi0 = buffers[0];
905       bi1 = buffers[1];
906
907       buffers += 2;
908       n_errors_left -= 2;
909
910       b0 = vlib_get_buffer (vm, bi0);
911       b1 = vlib_get_buffer (vm, bi1);
912
913       e0 = b0->error;
914       e1 = b1->error;
915
916       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
917       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
918
919       /* Speculate that sw_if_index == sw_if_index[01]. */
920       n_errors_current_sw_if_index += 2;
921
922       /* Speculatively assume all 2 (node, code) pairs are equal
923          to current (node, code). */
924       current_counter += 2;
925
926       if (PREDICT_FALSE (e0 != current_error
927                          || e1 != current_error
928                          || sw_if_index0 != current_sw_if_index
929                          || sw_if_index1 != current_sw_if_index))
930         {
931           current_counter -= 2;
932           n_errors_current_sw_if_index -= 2;
933
934           vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
935           vlib_increment_simple_counter (cm, cpu_index, sw_if_index1, 1);
936
937           /* Increment super-interface drop/punt counters for
938              sub-interfaces. */
939           sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
940           vlib_increment_simple_counter
941               (cm, cpu_index, sw_if0->sup_sw_if_index,
942                sw_if0->sup_sw_if_index != sw_if_index0);
943
944           sw_if1 = vnet_get_sw_interface (vnm, sw_if_index1);
945           vlib_increment_simple_counter
946               (cm, cpu_index, sw_if1->sup_sw_if_index, 
947                sw_if1->sup_sw_if_index != sw_if_index1);
948
949           em->counters[current_counter_index] = current_counter;
950           do_packet (vm, e0);
951           do_packet (vm, e1);
952
953           /* For 2 repeated errors, change current error. */
954           if (e0 == e1 && e1 != current_error)
955             {
956               current_error = e0;
957               current_counter_index = counter_index (vm, e0);
958             }
959           current_counter = em->counters[current_counter_index];
960         }
961     }
962
963   while (n_errors_left >= 1)
964     {
965       vlib_buffer_t * b0;
966       vnet_sw_interface_t * sw_if0;
967       vlib_error_t e0;
968       u32 bi0, sw_if_index0;
969
970       bi0 = buffers[0];
971
972       buffers += 1;
973       n_errors_left -= 1;
974       current_counter += 1;
975
976       b0 = vlib_get_buffer (vm, bi0);
977       e0 = b0->error;
978
979       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
980
981       /* Increment drop/punt counters. */
982       vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
983
984       /* Increment super-interface drop/punt counters for sub-interfaces. */
985       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
986       vlib_increment_simple_counter (cm, cpu_index, sw_if0->sup_sw_if_index, 
987                                      sw_if0->sup_sw_if_index != sw_if_index0);
988
989       if (PREDICT_FALSE (e0 != current_error))
990         {
991           current_counter -= 1;
992
993           vlib_error_elog_count (vm, current_counter_index,
994                                  (current_counter
995                                   - em->counters[current_counter_index]));
996             
997           em->counters[current_counter_index] = current_counter;
998
999           do_packet (vm, e0);
1000           current_error = e0;
1001           current_counter_index = counter_index (vm, e0);
1002           current_counter = em->counters[current_counter_index];
1003         }
1004     }
1005
1006   if (n_errors_current_sw_if_index > 0)
1007     {
1008       vnet_sw_interface_t * si;
1009
1010       vlib_increment_simple_counter (cm, cpu_index, current_sw_if_index,
1011                                      n_errors_current_sw_if_index);
1012
1013       si = vnet_get_sw_interface (vnm, current_sw_if_index);
1014       if (si->sup_sw_if_index != current_sw_if_index)
1015           vlib_increment_simple_counter (cm, cpu_index, si->sup_sw_if_index,
1016                                          n_errors_current_sw_if_index);
1017     }
1018
1019   vlib_error_elog_count (vm, current_counter_index,
1020                          (current_counter
1021                           - em->counters[current_counter_index]));
1022
1023   /* Return cached counter. */
1024   em->counters[current_counter_index] = current_counter;
1025
1026   /* Save memory for next iteration. */
1027   memory[disposition] = current_error;
1028
1029   if (disposition == VNET_ERROR_DISPOSITION_DROP
1030       || ! vm->os_punt_frame)
1031     {
1032       vlib_buffer_free
1033         (vm,
1034          first_buffer,
1035          frame->n_vectors);
1036
1037       /* If there is no punt function, free the frame as well. */
1038       if (disposition == VNET_ERROR_DISPOSITION_PUNT && ! vm->os_punt_frame)
1039         vlib_frame_free (vm, node, frame);
1040     }
1041   else
1042     vm->os_punt_frame (vm, node, frame);
1043
1044   return frame->n_vectors;
1045 }
1046
1047 static inline void 
1048 pcap_drop_trace (vlib_main_t * vm, 
1049                  vnet_interface_main_t * im, 
1050                  vlib_frame_t * f)
1051 {
1052   u32 * from;
1053   u32 n_left = f->n_vectors;
1054   vlib_buffer_t * b0, * p1;
1055   u32 bi0;
1056   i16 save_current_data;
1057   u16 save_current_length;
1058
1059   from = vlib_frame_vector_args (f);
1060
1061   while (n_left > 0)
1062     {
1063       if (PREDICT_TRUE (n_left > 1))
1064         {
1065           p1 = vlib_get_buffer (vm, from[1]);
1066           vlib_prefetch_buffer_header (p1, LOAD);
1067         }
1068       
1069       bi0 = from[0];
1070       b0 = vlib_get_buffer (vm, bi0);
1071       from++;
1072       n_left--;
1073       
1074       /* See if we're pointedly ignoring this specific error */
1075       if (im->pcap_drop_filter_hash 
1076           && hash_get (im->pcap_drop_filter_hash, b0->error))
1077         continue;
1078
1079       /* Trace all drops, or drops received on a specific interface */
1080       if (im->pcap_sw_if_index == 0 ||
1081           im->pcap_sw_if_index == vnet_buffer(b0)->sw_if_index [VLIB_RX])
1082         {
1083           save_current_data = b0->current_data;
1084           save_current_length = b0->current_length;
1085           
1086           /* 
1087            * Typically, we'll need to rewind the buffer
1088            */
1089           if (b0->current_data > 0)
1090             vlib_buffer_advance (b0, (word) -b0->current_data);
1091
1092           pcap_add_buffer (&im->pcap_main, vm, bi0, 512);
1093
1094           b0->current_data = save_current_data;
1095           b0->current_length = save_current_length;
1096         }
1097     }
1098 }
1099
1100 void vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
1101 {
1102   vnet_interface_main_t * im = &vnet_get_main()->interface_main;
1103
1104   if (im->pcap_drop_filter_hash == 0)
1105       im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
1106
1107   if (is_add)
1108     hash_set (im->pcap_drop_filter_hash, error_index, 1);
1109   else
1110     hash_unset (im->pcap_drop_filter_hash, error_index);
1111 }
1112
1113 static uword
1114 process_drop (vlib_main_t * vm,
1115               vlib_node_runtime_t * node,
1116               vlib_frame_t * frame)
1117 {
1118   vnet_interface_main_t * im = &vnet_get_main()->interface_main;
1119
1120   if (PREDICT_FALSE (im->drop_pcap_enable))
1121     pcap_drop_trace (vm, im, frame);
1122
1123   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
1124 }
1125
1126 static uword
1127 process_punt (vlib_main_t * vm,
1128               vlib_node_runtime_t * node,
1129               vlib_frame_t * frame)
1130 {
1131   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
1132 }
1133
1134 VLIB_REGISTER_NODE (drop_buffers,static) = {
1135   .function = process_drop,
1136   .name = "error-drop",
1137   .flags = VLIB_NODE_FLAG_IS_DROP,
1138   .vector_size = sizeof (u32),
1139   .format_trace = format_vnet_error_trace,
1140   .validate_frame = validate_error_frame,
1141 };
1142
1143 VLIB_NODE_FUNCTION_MULTIARCH (drop_buffers, process_drop)
1144
1145 VLIB_REGISTER_NODE (punt_buffers,static) = {
1146   .function = process_punt,
1147   .flags = (VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH
1148             | VLIB_NODE_FLAG_IS_PUNT),
1149   .name = "error-punt",
1150   .vector_size = sizeof (u32),
1151   .format_trace = format_vnet_error_trace,
1152   .validate_frame = validate_error_frame,
1153 };
1154
1155 VLIB_NODE_FUNCTION_MULTIARCH (punt_buffers, process_punt)
1156
1157 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node,static) = {
1158   .function = vnet_per_buffer_interface_output,
1159   .name = "interface-output",
1160   .vector_size = sizeof (u32),
1161 };
1162
1163 VLIB_NODE_FUNCTION_MULTIARCH (vnet_per_buffer_interface_output_node, vnet_per_buffer_interface_output)
1164
1165 clib_error_t *
1166 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1167                                                        u32 hw_if_index,
1168                                                        u32 is_create)
1169 {
1170   vnet_hw_interface_t * hi = vnet_get_hw_interface (vnm, hw_if_index);
1171   u32 next_index;
1172
1173   next_index = vlib_node_add_next_with_slot
1174     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1175      hi->output_node_index,
1176      /* next_index */ hw_if_index);
1177
1178   ASSERT (next_index == hw_if_index);
1179
1180   return 0;
1181 }
1182
1183 VNET_HW_INTERFACE_ADD_DEL_FUNCTION 
1184 (vnet_per_buffer_interface_output_hw_interface_add_del);
1185
1186 static clib_error_t *
1187 pcap_drop_trace_command_fn (vlib_main_t * vm,
1188                             unformat_input_t * input,
1189                             vlib_cli_command_t * cmd)
1190 {
1191   vnet_main_t * vnm = vnet_get_main();
1192   vnet_interface_main_t * im = &vnm->interface_main;
1193   u8 * filename;
1194   u32 max;
1195   int matched = 0;
1196   clib_error_t * error = 0;
1197
1198   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
1199     {
1200       if (unformat (input, "on"))
1201         {
1202           if (im->drop_pcap_enable == 0)
1203             {
1204               if (im->pcap_filename == 0)
1205                 im->pcap_filename = format (0, "/tmp/drop.pcap%c", 0);
1206               
1207               memset (&im->pcap_main, 0, sizeof (im->pcap_main));
1208               im->pcap_main.file_name = (char *) im->pcap_filename;
1209               im->pcap_main.n_packets_to_capture = 100;
1210               if (im->pcap_pkts_to_capture)
1211                 im->pcap_main.n_packets_to_capture = im->pcap_pkts_to_capture;
1212
1213               im->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
1214               im->drop_pcap_enable = 1;
1215               matched = 1;
1216               vlib_cli_output (vm, "pcap drop capture on...");
1217             }
1218           else
1219             {
1220               vlib_cli_output (vm, "pcap drop capture already on...");
1221             }
1222           matched = 1;
1223         }
1224       else if (unformat (input, "off"))
1225         {
1226           matched = 1;
1227
1228           if (im->drop_pcap_enable)
1229             {
1230               vlib_cli_output (vm, "captured %d pkts...", 
1231                                im->pcap_main.n_packets_captured);
1232               if (im->pcap_main.n_packets_captured)
1233                 {
1234                   im->pcap_main.n_packets_to_capture = 
1235                     im->pcap_main.n_packets_captured;
1236                   error = pcap_write (&im->pcap_main);
1237                   if (error)
1238                     clib_error_report (error);
1239                   else
1240                     vlib_cli_output (vm, "saved to %s...", im->pcap_filename);
1241                 }
1242             }
1243           else
1244             {
1245               vlib_cli_output (vm, "pcap drop capture already off...");
1246             }
1247
1248           im->drop_pcap_enable = 0;
1249         }
1250       else if (unformat (input, "max %d", &max))
1251         {
1252           im->pcap_pkts_to_capture = max;
1253           matched = 1;
1254         }
1255
1256       else if (unformat (input, "intfc %U", 
1257                          unformat_vnet_sw_interface, vnm,
1258                          &im->pcap_sw_if_index))
1259         matched = 1;
1260       else if (unformat (input, "intfc any"))
1261         {
1262           im->pcap_sw_if_index = 0;
1263           matched = 1;
1264         }
1265       else if (unformat (input, "file %s", &filename))
1266         {
1267           u8 * chroot_filename;
1268           /* Brain-police user path input */
1269           if (strstr((char *)filename, "..") || index((char *)filename, '/'))
1270             {
1271               vlib_cli_output (vm, "illegal characters in filename '%s'", 
1272                                filename);
1273               continue;
1274             }
1275
1276           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1277           vec_free (filename);
1278           
1279           if (im->pcap_filename)
1280             vec_free (im->pcap_filename);
1281           vec_add1 (filename, 0);
1282           im->pcap_filename = chroot_filename;
1283           matched = 1;
1284         }
1285       else if (unformat (input, "status"))
1286         {
1287           if (im->drop_pcap_enable == 0)
1288             {
1289               vlib_cli_output (vm, "pcap drop capture is off...");
1290               continue;
1291             }
1292
1293           vlib_cli_output (vm, "pcap drop capture: %d of %d pkts...",
1294                            im->pcap_main.n_packets_captured,
1295                            im->pcap_main.n_packets_to_capture);
1296           matched = 1;
1297         }
1298
1299       else
1300         break;
1301     }
1302
1303   if (matched == 0)
1304     return clib_error_return (0, "unknown input `%U'", 
1305                               format_unformat_error, input);
1306
1307   return 0;
1308 }
1309
1310 VLIB_CLI_COMMAND (pcap_trace_command, static) = {
1311     .path = "pcap drop trace",
1312     .short_help = 
1313     "pcap drop trace on off max <nn> intfc <intfc> file <name> status",
1314     .function = pcap_drop_trace_command_fn,
1315 };