interface: fix the offloads
[vpp.git] / src / 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 #include <vnet/ip/icmp46_packet.h>
42 #include <vnet/ethernet/packet.h>
43 #include <vnet/ip/format.h>
44 #include <vnet/ip/ip4.h>
45 #include <vnet/ip/ip6.h>
46 #include <vnet/udp/udp_packet.h>
47 #include <vnet/feature/feature.h>
48 #include <vnet/classify/pcap_classify.h>
49 #include <vnet/hash/hash.h>
50 #include <vnet/interface_output.h>
51 #include <vppinfra/vector/mask_compare.h>
52 #include <vppinfra/vector/compress.h>
53 #include <vppinfra/vector/count_equal.h>
54 #include <vppinfra/vector/array_mask.h>
55
56 typedef struct
57 {
58   u32 sw_if_index;
59   u32 flags;
60   u8 data[128 - 2 * sizeof (u32)];
61 }
62 interface_output_trace_t;
63
64 #ifndef CLIB_MARCH_VARIANT
65 u8 *
66 format_vnet_interface_output_trace (u8 * s, va_list * va)
67 {
68   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
69   vlib_node_t *node = va_arg (*va, vlib_node_t *);
70   interface_output_trace_t *t = va_arg (*va, interface_output_trace_t *);
71   vnet_main_t *vnm = vnet_get_main ();
72   vnet_sw_interface_t *si;
73   u32 indent;
74
75   if (t->sw_if_index != (u32) ~ 0)
76     {
77       indent = format_get_indent (s);
78
79       if (pool_is_free_index
80           (vnm->interface_main.sw_interfaces, t->sw_if_index))
81         {
82           /* the interface may have been deleted by the time the trace is printed */
83           s = format (s, "sw_if_index: %d ", t->sw_if_index);
84         }
85       else
86         {
87           si = vnet_get_sw_interface (vnm, t->sw_if_index);
88           s =
89             format (s, "%U ", format_vnet_sw_interface_name, vnm, si,
90                     t->flags);
91         }
92       s =
93         format (s, "\n%U%U", format_white_space, indent,
94                 node->format_buffer ? node->format_buffer : format_hex_bytes,
95                 t->data, sizeof (t->data));
96     }
97   return s;
98 }
99 #endif /* CLIB_MARCH_VARIANT */
100
101 static void
102 vnet_interface_output_trace (vlib_main_t * vm,
103                              vlib_node_runtime_t * node,
104                              vlib_frame_t * frame, uword n_buffers)
105 {
106   u32 n_left, *from;
107
108   n_left = n_buffers;
109   from = vlib_frame_vector_args (frame);
110
111   while (n_left >= 4)
112     {
113       u32 bi0, bi1;
114       vlib_buffer_t *b0, *b1;
115       interface_output_trace_t *t0, *t1;
116
117       /* Prefetch next iteration. */
118       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
119       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
120
121       bi0 = from[0];
122       bi1 = from[1];
123
124       b0 = vlib_get_buffer (vm, bi0);
125       b1 = vlib_get_buffer (vm, bi1);
126
127       if (b0->flags & VLIB_BUFFER_IS_TRACED)
128         {
129           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
130           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
131           t0->flags = b0->flags;
132           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
133                             sizeof (t0->data));
134         }
135       if (b1->flags & VLIB_BUFFER_IS_TRACED)
136         {
137           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
138           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
139           t1->flags = b1->flags;
140           clib_memcpy_fast (t1->data, vlib_buffer_get_current (b1),
141                             sizeof (t1->data));
142         }
143       from += 2;
144       n_left -= 2;
145     }
146
147   while (n_left >= 1)
148     {
149       u32 bi0;
150       vlib_buffer_t *b0;
151       interface_output_trace_t *t0;
152
153       bi0 = from[0];
154
155       b0 = vlib_get_buffer (vm, bi0);
156
157       if (b0->flags & VLIB_BUFFER_IS_TRACED)
158         {
159           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
160           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
161           t0->flags = b0->flags;
162           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
163                             sizeof (t0->data));
164         }
165       from += 1;
166       n_left -= 1;
167     }
168 }
169
170 static_always_inline void
171 vnet_interface_output_handle_offload (vlib_main_t *vm, vlib_buffer_t *b)
172 {
173   if (b->flags & VNET_BUFFER_F_GSO)
174     return;
175   vnet_calc_checksums_inline (vm, b, b->flags & VNET_BUFFER_F_IS_IP4,
176                               b->flags & VNET_BUFFER_F_IS_IP6);
177   vnet_calc_outer_checksums_inline (vm, b);
178 }
179
180 static_always_inline uword
181 vnet_interface_output_node_inline (vlib_main_t *vm, u32 sw_if_index,
182                                    vlib_combined_counter_main_t *ccm,
183                                    vlib_buffer_t **b, void **p,
184                                    u32 config_index, u8 arc, u32 n_left,
185                                    int processing_level)
186 {
187   u32 n_bytes = 0;
188   u32 n_bytes0, n_bytes1, n_bytes2, n_bytes3;
189   u32 ti = vm->thread_index;
190
191   while (n_left >= 8)
192     {
193       u32 or_flags;
194
195       /* Prefetch next iteration. */
196       vlib_prefetch_buffer_header (b[4], LOAD);
197       vlib_prefetch_buffer_header (b[5], LOAD);
198       vlib_prefetch_buffer_header (b[6], LOAD);
199       vlib_prefetch_buffer_header (b[7], LOAD);
200
201       if (processing_level >= 1)
202         or_flags = b[0]->flags | b[1]->flags | b[2]->flags | b[3]->flags;
203
204       /* Be grumpy about zero length buffers for benefit of
205          driver tx function. */
206       ASSERT (b[0]->current_length > 0);
207       ASSERT (b[1]->current_length > 0);
208       ASSERT (b[2]->current_length > 0);
209       ASSERT (b[3]->current_length > 0);
210
211       n_bytes += n_bytes0 = vlib_buffer_length_in_chain (vm, b[0]);
212       n_bytes += n_bytes1 = vlib_buffer_length_in_chain (vm, b[1]);
213       n_bytes += n_bytes2 = vlib_buffer_length_in_chain (vm, b[2]);
214       n_bytes += n_bytes3 = vlib_buffer_length_in_chain (vm, b[3]);
215
216       if (processing_level >= 3)
217         {
218           p[0] = vlib_buffer_get_current (b[0]);
219           p[1] = vlib_buffer_get_current (b[1]);
220           p[2] = vlib_buffer_get_current (b[2]);
221           p[3] = vlib_buffer_get_current (b[3]);
222           p += 4;
223         }
224
225       if (processing_level >= 2)
226         {
227           u32 tx_swif0, tx_swif1, tx_swif2, tx_swif3;
228           tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
229           tx_swif1 = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
230           tx_swif2 = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
231           tx_swif3 = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
232
233           /* update vlan subif tx counts, if required */
234           if (PREDICT_FALSE (tx_swif0 != sw_if_index))
235             vlib_increment_combined_counter (ccm, ti, tx_swif0, 1, n_bytes0);
236
237           if (PREDICT_FALSE (tx_swif1 != sw_if_index))
238             vlib_increment_combined_counter (ccm, ti, tx_swif1, 1, n_bytes1);
239
240           if (PREDICT_FALSE (tx_swif2 != sw_if_index))
241             vlib_increment_combined_counter (ccm, ti, tx_swif2, 1, n_bytes2);
242
243           if (PREDICT_FALSE (tx_swif3 != sw_if_index))
244             vlib_increment_combined_counter (ccm, ti, tx_swif3, 1, n_bytes3);
245
246           if (PREDICT_FALSE (config_index != ~0))
247             {
248               vnet_buffer (b[0])->feature_arc_index = arc;
249               b[0]->current_config_index = config_index;
250               vnet_buffer (b[1])->feature_arc_index = arc;
251               b[1]->current_config_index = config_index;
252               vnet_buffer (b[2])->feature_arc_index = arc;
253               b[2]->current_config_index = config_index;
254               vnet_buffer (b[3])->feature_arc_index = arc;
255               b[3]->current_config_index = config_index;
256             }
257         }
258
259       if (processing_level >= 1 && (or_flags & VNET_BUFFER_F_OFFLOAD))
260         {
261           vnet_interface_output_handle_offload (vm, b[0]);
262           vnet_interface_output_handle_offload (vm, b[1]);
263           vnet_interface_output_handle_offload (vm, b[2]);
264           vnet_interface_output_handle_offload (vm, b[3]);
265         }
266
267       n_left -= 4;
268       b += 4;
269     }
270
271   while (n_left)
272     {
273       /* Be grumpy about zero length buffers for benefit of
274          driver tx function. */
275       ASSERT (b[0]->current_length > 0);
276
277       n_bytes += n_bytes0 = vlib_buffer_length_in_chain (vm, b[0]);
278
279       if (processing_level >= 3)
280         {
281           p[0] = vlib_buffer_get_current (b[0]);
282           p += 1;
283         }
284
285       if (processing_level >= 2)
286         {
287           u32 tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
288
289           if (PREDICT_FALSE (config_index != ~0))
290             {
291               vnet_buffer (b[0])->feature_arc_index = arc;
292               b[0]->current_config_index = config_index;
293             }
294
295           if (PREDICT_FALSE (tx_swif0 != sw_if_index))
296             vlib_increment_combined_counter (ccm, ti, tx_swif0, 1, n_bytes0);
297         }
298
299       if (processing_level >= 1)
300         vnet_interface_output_handle_offload (vm, b[0]);
301
302       n_left -= 1;
303       b += 1;
304     }
305
306   return n_bytes;
307 }
308
309 static_always_inline void
310 vnet_interface_pcap_tx_trace (vlib_main_t *vm, vlib_node_runtime_t *node,
311                               vlib_frame_t *frame, int in_interface_ouput)
312 {
313   vnet_main_t *vnm = vnet_get_main ();
314   u32 n_left_from, *from;
315   u32 sw_if_index = ~0, hw_if_index = ~0;
316   vnet_pcap_t *pp = &vnm->pcap;
317
318   if (PREDICT_TRUE (pp->pcap_tx_enable == 0))
319     return;
320
321   if (in_interface_ouput)
322     {
323       /* interface-output is called right before interface-output-template.
324        * We only want to capture packets here if there is a per-interface
325        * filter, in case it matches the sub-interface sw_if_index.
326        * If there is no per-interface filter configured, let the
327        * interface-output-template node deal with it */
328       if (pp->pcap_sw_if_index == 0)
329         return;
330     }
331   else
332     {
333       vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
334       sw_if_index = rt->sw_if_index;
335     }
336
337   n_left_from = frame->n_vectors;
338   from = vlib_frame_vector_args (frame);
339
340   while (n_left_from > 0)
341     {
342       u32 bi0 = from[0];
343       vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
344       from++;
345       n_left_from--;
346
347       if (in_interface_ouput)
348         {
349           const u32 sii = vnet_buffer (b0)->sw_if_index[VLIB_TX];
350           if (PREDICT_FALSE (sii != sw_if_index))
351             {
352               const vnet_hw_interface_t *hi =
353                 vnet_get_sup_hw_interface (vnm, sii);
354               hw_if_index = hi->sw_if_index;
355               sw_if_index = sii;
356             }
357           if (hw_if_index == sw_if_index)
358             continue; /* defer to interface-output-template */
359         }
360
361       if (vnet_is_packet_pcaped (pp, b0, sw_if_index))
362         pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
363     }
364 }
365
366 static_always_inline void
367 hash_func_with_mask (void **p, u32 *hash, u32 n_packets, u32 *lookup_table,
368                      u32 mask, vnet_hash_fn_t hf)
369 {
370   u32 n_left_from = n_packets;
371
372   hf (p, hash, n_packets);
373
374   clib_array_mask_u32 (hash, mask, n_packets);
375
376   while (n_left_from >= 4)
377     {
378       hash[0] = lookup_table[hash[0]];
379       hash[1] = lookup_table[hash[1]];
380       hash[2] = lookup_table[hash[2]];
381       hash[3] = lookup_table[hash[3]];
382
383       hash += 4;
384       n_left_from -= 4;
385     }
386
387   while (n_left_from > 0)
388     {
389       hash[0] = lookup_table[hash[0]];
390
391       hash += 1;
392       n_left_from -= 1;
393     }
394 }
395
396 static_always_inline void
397 store_tx_frame_scalar_data (vnet_hw_if_tx_frame_t *copy_frame,
398                             vnet_hw_if_tx_frame_t *tf)
399 {
400   if (copy_frame)
401     clib_memcpy_fast (tf, copy_frame, sizeof (vnet_hw_if_tx_frame_t));
402 }
403
404 static_always_inline u32
405 enqueue_one_to_tx_node (vlib_main_t *vm, vlib_node_runtime_t *node, u32 *ppqi,
406                         u32 *from, vnet_hw_if_tx_frame_t *copy_frame,
407                         u32 n_vectors, u32 n_left, u32 next_index)
408 {
409   u32 tmp[VLIB_FRAME_SIZE];
410   vlib_frame_bitmap_t mask = {};
411   vlib_frame_t *f;
412   vnet_hw_if_tx_frame_t *tf;
413   u32 *to;
414   u32 n_copy = 0, n_free = 0;
415
416   f = vlib_get_next_frame_internal (vm, node, next_index, 0);
417   tf = vlib_frame_scalar_args (f);
418
419   if (f->n_vectors > 0 &&
420       (!copy_frame || (tf->queue_id == copy_frame->queue_id)))
421     {
422       /* append current next frame */
423       n_free = VLIB_FRAME_SIZE - f->n_vectors;
424       /*
425        * if frame contains enough space for worst case scenario,
426        * we can avoid use of tmp
427        */
428       if (n_free >= n_left)
429         to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
430       else
431         to = tmp;
432     }
433   else
434     {
435       if (f->n_vectors > 0)
436         {
437           /* current frame doesn't fit - grab empty one */
438           f = vlib_get_next_frame_internal (vm, node, next_index, 1);
439           tf = vlib_frame_scalar_args (f);
440         }
441
442       /* empty frame - store scalar data */
443       store_tx_frame_scalar_data (copy_frame, tf);
444       to = vlib_frame_vector_args (f);
445       n_free = VLIB_FRAME_SIZE;
446     }
447
448   /*
449    * per packet queue id array
450    * compare with given queue_id, if match, copy respective buffer index from
451    * -> to
452    */
453   if (ppqi)
454     {
455       clib_mask_compare_u32 (copy_frame->queue_id, ppqi, mask, n_vectors);
456       n_copy = clib_compress_u32 (to, from, mask, n_vectors);
457
458       if (n_copy == 0)
459         return n_left;
460     }
461   else
462     {
463       /*
464        * no work required, just copy all buffer indices from -> to
465        */
466       n_copy = n_left;
467       vlib_buffer_copy_indices (to, from, n_copy);
468     }
469
470   if (to != tmp)
471     {
472       /* indices already written to frame, just close it */
473       vlib_put_next_frame (vm, node, next_index, n_free - n_copy);
474     }
475   else if (n_free >= n_copy)
476     {
477       /* enough space in the existing frame */
478       to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
479       vlib_buffer_copy_indices (to, tmp, n_copy);
480       vlib_put_next_frame (vm, node, next_index, n_free - n_copy);
481     }
482   else
483     {
484       /* full frame */
485       to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
486       vlib_buffer_copy_indices (to, tmp, n_free);
487       vlib_put_next_frame (vm, node, next_index, 0);
488
489       /* second frame */
490       u32 n_2nd_frame = n_copy - n_free;
491       f = vlib_get_next_frame_internal (vm, node, next_index, 1);
492       tf = vlib_frame_scalar_args (f);
493       /* empty frame - store scalar data */
494       store_tx_frame_scalar_data (copy_frame, tf);
495       to = vlib_frame_vector_args (f);
496       vlib_buffer_copy_indices (to, tmp + n_free, n_2nd_frame);
497       vlib_put_next_frame (vm, node, next_index,
498                            VLIB_FRAME_SIZE - n_2nd_frame);
499     }
500
501   return n_left - n_copy;
502 }
503
504 static_always_inline void
505 enqueue_to_tx_node (vlib_main_t *vm, vlib_node_runtime_t *node,
506                     vnet_hw_interface_t *hi, u32 next_index,
507                     vnet_hw_if_output_node_runtime_t *r, u32 *from, void **p,
508                     u32 n_vectors)
509 {
510   u32 n_left = n_vectors;
511
512   ASSERT (n_vectors <= VLIB_FRAME_SIZE);
513
514   /*
515    * backward compatible for drivers not integrated with new tx infra.
516    */
517   if (r == 0)
518     {
519       n_left = enqueue_one_to_tx_node (vm, node, NULL, from, NULL, n_vectors,
520                                        n_left, next_index);
521     }
522   /*
523    * only 1 tx queue of given interface is available on given thread
524    */
525   else if (r->n_queues == 1)
526     {
527       n_left = enqueue_one_to_tx_node (vm, node, NULL, from, r->frame,
528                                        n_vectors, n_left, next_index);
529     }
530   /*
531    * multi tx-queues use case
532    */
533   else if (r->n_queues > 1)
534     {
535       u32 qids[VLIB_FRAME_SIZE];
536
537       hash_func_with_mask (p, qids, n_vectors, r->lookup_table,
538                            vec_len (r->lookup_table) - 1, hi->hf);
539
540       for (u32 i = 0; i < r->n_queues; i++)
541         {
542           n_left = enqueue_one_to_tx_node (vm, node, qids, from, &r->frame[i],
543                                            n_vectors, n_left, next_index);
544           if (n_left == 0)
545             break;
546         }
547     }
548   else
549     ASSERT (0);
550 }
551
552 VLIB_NODE_FN (vnet_interface_output_node)
553 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
554 {
555   vnet_main_t *vnm = vnet_get_main ();
556   vnet_interface_main_t *im = &vnm->interface_main;
557   vlib_combined_counter_main_t *ccm;
558   vnet_hw_interface_t *hi;
559   vnet_sw_interface_t *si;
560   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
561   vnet_hw_if_output_node_runtime_t *r = 0;
562   vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
563   u32 n_bytes, n_buffers = frame->n_vectors;
564   u32 config_index = ~0;
565   u32 sw_if_index = rt->sw_if_index;
566   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
567   u32 ti = vm->thread_index;
568   u8 arc = im->output_feature_arc_index;
569   int arc_or_subif = 0;
570   int do_tx_offloads = 0;
571   void *ptr[VLIB_FRAME_SIZE], **p = ptr;
572   u8 is_parr = 0;
573   u32 *from;
574
575   if (node->flags & VLIB_NODE_FLAG_TRACE)
576     vnet_interface_output_trace (vm, node, frame, n_buffers);
577
578   from = vlib_frame_vector_args (frame);
579
580   if (rt->is_deleted)
581     return vlib_error_drop_buffers (
582       vm, node, from,
583       /* buffer stride */ 1, n_buffers, VNET_INTERFACE_OUTPUT_NEXT_DROP,
584       node->node_index, VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
585
586   vnet_interface_pcap_tx_trace (vm, node, frame, 0 /* in_interface_ouput */);
587
588   vlib_get_buffers (vm, from, bufs, n_buffers);
589
590   si = vnet_get_sw_interface (vnm, sw_if_index);
591   hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
592
593   if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ||
594       !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
595     {
596       vlib_simple_counter_main_t *cm;
597
598       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
599                              VNET_INTERFACE_COUNTER_TX_ERROR);
600       vlib_increment_simple_counter (cm, ti, sw_if_index, n_buffers);
601
602       return vlib_error_drop_buffers (
603         vm, node, from,
604         /* buffer stride */ 1, n_buffers, VNET_INTERFACE_OUTPUT_NEXT_DROP,
605         node->node_index, VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
606     }
607
608   if (hi->output_node_thread_runtimes)
609     r = vec_elt_at_index (hi->output_node_thread_runtimes, vm->thread_index);
610
611   if (r)
612     {
613       /*
614        * tx queue of given interface is not available on given thread
615        */
616       if (r->n_queues == 0)
617         return vlib_error_drop_buffers (
618           vm, node, from,
619           /* buffer stride */ 1, n_buffers, VNET_INTERFACE_OUTPUT_NEXT_DROP,
620           node->node_index, VNET_INTERFACE_OUTPUT_ERROR_NO_TX_QUEUE);
621       /*
622        * multiple tx queues available on given thread
623        */
624       else if (r->n_queues > 1)
625         /* construct array of pointer */
626         is_parr = 1;
627     }
628
629   /* interface-output feature arc handling */
630   if (PREDICT_FALSE (vnet_have_features (arc, sw_if_index)))
631     {
632       vnet_feature_config_main_t *fcm;
633       fcm = vnet_feature_get_config_main (arc);
634       config_index = vnet_get_feature_config_index (arc, sw_if_index);
635       vnet_get_config_data (&fcm->config_main, &config_index, &next_index, 0);
636       arc_or_subif = 1;
637     }
638   else if (hash_elts (hi->sub_interface_sw_if_index_by_id))
639     arc_or_subif = 1;
640
641   ccm = im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX;
642
643   /* if not all three flags IP4_,TCP_,UDP_CKSUM set, do compute them
644    * here before sending to the interface */
645   if ((hi->caps & VNET_HW_IF_CAP_TX_CKSUM) != VNET_HW_IF_CAP_TX_CKSUM)
646     do_tx_offloads = 1;
647
648   // basic processing
649   if (do_tx_offloads == 0 && arc_or_subif == 0 && is_parr == 0)
650     n_bytes = vnet_interface_output_node_inline (
651       vm, sw_if_index, ccm, bufs, NULL, config_index, arc, n_buffers, 0);
652   // basic processing + tx offloads
653   else if (do_tx_offloads == 1 && arc_or_subif == 0 && is_parr == 0)
654     n_bytes = vnet_interface_output_node_inline (
655       vm, sw_if_index, ccm, bufs, NULL, config_index, arc, n_buffers, 1);
656   // basic processing + tx offloads + vlans + arcs
657   else if (do_tx_offloads == 1 && arc_or_subif == 1 && is_parr == 0)
658     n_bytes = vnet_interface_output_node_inline (
659       vm, sw_if_index, ccm, bufs, NULL, config_index, arc, n_buffers, 2);
660   // basic processing + tx offloads + vlans + arcs + multi-txqs
661   else
662     n_bytes = vnet_interface_output_node_inline (
663       vm, sw_if_index, ccm, bufs, p, config_index, arc, n_buffers, 3);
664
665   from = vlib_frame_vector_args (frame);
666   if (PREDICT_TRUE (next_index == VNET_INTERFACE_OUTPUT_NEXT_TX))
667     {
668       enqueue_to_tx_node (vm, node, hi, next_index, r, from, ptr,
669                           frame->n_vectors);
670     }
671   else
672     {
673       vlib_buffer_enqueue_to_single_next (vm, node, from, next_index,
674                                           frame->n_vectors);
675     }
676
677   /* Update main interface stats. */
678   vlib_increment_combined_counter (ccm, ti, sw_if_index, n_buffers, n_bytes);
679   return n_buffers;
680 }
681
682 VLIB_REGISTER_NODE (vnet_interface_output_node) = {
683   .name = "interface-output-template",
684   .vector_size = sizeof (u32),
685 };
686
687 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
688 VLIB_NODE_FN (vnet_per_buffer_interface_output_node) (vlib_main_t * vm,
689                                                       vlib_node_runtime_t *
690                                                       node,
691                                                       vlib_frame_t * frame)
692 {
693   vnet_main_t *vnm = vnet_get_main ();
694   u32 n_left_to_next, *from, *to_next;
695   u32 n_left_from, next_index;
696
697   vnet_interface_pcap_tx_trace (vm, node, frame, 1 /* in_interface_ouput */);
698
699   n_left_from = frame->n_vectors;
700
701   from = vlib_frame_vector_args (frame);
702   next_index = node->cached_next_index;
703
704   while (n_left_from > 0)
705     {
706       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
707
708       while (n_left_from >= 4 && n_left_to_next >= 2)
709         {
710           u32 bi0, bi1, next0, next1;
711           vlib_buffer_t *b0, *b1;
712           vnet_hw_interface_t *hi0, *hi1;
713
714           /* Prefetch next iteration. */
715           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
716           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
717
718           bi0 = from[0];
719           bi1 = from[1];
720           to_next[0] = bi0;
721           to_next[1] = bi1;
722           from += 2;
723           to_next += 2;
724           n_left_to_next -= 2;
725           n_left_from -= 2;
726
727           b0 = vlib_get_buffer (vm, bi0);
728           b1 = vlib_get_buffer (vm, bi1);
729
730           hi0 =
731             vnet_get_sup_hw_interface (vnm,
732                                        vnet_buffer (b0)->sw_if_index
733                                        [VLIB_TX]);
734           hi1 =
735             vnet_get_sup_hw_interface (vnm,
736                                        vnet_buffer (b1)->sw_if_index
737                                        [VLIB_TX]);
738
739           next0 = hi0->output_node_next_index;
740           next1 = hi1->output_node_next_index;
741
742           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
743                                            n_left_to_next, bi0, bi1, next0,
744                                            next1);
745         }
746
747       while (n_left_from > 0 && n_left_to_next > 0)
748         {
749           u32 bi0, next0;
750           vlib_buffer_t *b0;
751           vnet_hw_interface_t *hi0;
752
753           bi0 = from[0];
754           to_next[0] = bi0;
755           from += 1;
756           to_next += 1;
757           n_left_to_next -= 1;
758           n_left_from -= 1;
759
760           b0 = vlib_get_buffer (vm, bi0);
761
762           hi0 =
763             vnet_get_sup_hw_interface (vnm,
764                                        vnet_buffer (b0)->sw_if_index
765                                        [VLIB_TX]);
766
767           next0 = hi0->output_node_next_index;
768
769           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
770                                            n_left_to_next, bi0, next0);
771         }
772
773       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
774     }
775
776   return frame->n_vectors;
777 }
778
779 typedef struct vnet_error_trace_t_
780 {
781   u32 sw_if_index;
782   i8 details_valid;
783   u8 is_ip6;
784   u8 pad[2];
785   u16 mactype;
786   ip46_address_t src, dst;
787 } vnet_error_trace_t;
788
789 static u8 *
790 format_vnet_error_trace (u8 * s, va_list * va)
791 {
792   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
793   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
794   vnet_error_trace_t *t = va_arg (*va, vnet_error_trace_t *);
795
796   /* Normal, non-catchup trace */
797   if (t->details_valid == 0)
798     {
799       s = format (s, "rx:%U", format_vnet_sw_if_index_name,
800                   vnet_get_main (), t->sw_if_index);
801     }
802   else if (t->details_valid == 1)
803     {
804       /* The trace capture code didn't understant the mactype */
805       s = format (s, "mactype 0x%4x (not decoded)", t->mactype);
806     }
807   else if (t->details_valid == 2)
808     {
809       /* Dump the src/dst addresses */
810       if (t->is_ip6 == 0)
811         s = format (s, "IP4: %U -> %U",
812                     format_ip4_address, &t->src.ip4,
813                     format_ip4_address, &t->dst.ip4);
814       else
815         s = format (s, "IP6: %U -> %U",
816                     format_ip6_address, &t->src.ip6,
817                     format_ip6_address, &t->dst.ip6);
818     }
819   return s;
820 }
821
822 static void
823 interface_trace_buffers (vlib_main_t * vm,
824                          vlib_node_runtime_t * node, vlib_frame_t * frame)
825 {
826   u32 n_left, *buffers;
827
828   buffers = vlib_frame_vector_args (frame);
829   n_left = frame->n_vectors;
830
831   while (n_left >= 4)
832     {
833       u32 bi0, bi1;
834       vlib_buffer_t *b0, *b1;
835       vnet_error_trace_t *t0, *t1;
836
837       /* Prefetch next iteration. */
838       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
839       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
840
841       bi0 = buffers[0];
842       bi1 = buffers[1];
843
844       b0 = vlib_get_buffer (vm, bi0);
845       b1 = vlib_get_buffer (vm, bi1);
846
847       if (b0->flags & VLIB_BUFFER_IS_TRACED)
848         {
849           t0 = vlib_add_trace (vm, node, b0,
850                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
851           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
852           t0->details_valid = 0;
853         }
854       if (b1->flags & VLIB_BUFFER_IS_TRACED)
855         {
856           t1 = vlib_add_trace (vm, node, b1,
857                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
858           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
859           t1->details_valid = 0;
860         }
861       buffers += 2;
862       n_left -= 2;
863     }
864
865   while (n_left >= 1)
866     {
867       u32 bi0;
868       vlib_buffer_t *b0;
869       vnet_error_trace_t *t0;
870
871       bi0 = buffers[0];
872
873       b0 = vlib_get_buffer (vm, bi0);
874
875       if (b0->flags & VLIB_BUFFER_IS_TRACED)
876         {
877           t0 = vlib_add_trace (vm, node, b0,
878                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
879           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
880           t0->details_valid = 0;
881         }
882       buffers += 1;
883       n_left -= 1;
884     }
885 }
886
887 typedef enum
888 {
889   VNET_ERROR_DISPOSITION_DROP,
890   VNET_ERROR_DISPOSITION_PUNT,
891   VNET_ERROR_N_DISPOSITION,
892 } vnet_error_disposition_t;
893
894 static void
895 drop_catchup_trace (vlib_main_t * vm,
896                     vlib_node_runtime_t * node, vlib_buffer_t * b)
897 {
898   /* Can we safely rewind the buffer? If not, fagedaboudit */
899   if (b->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
900     {
901       vnet_error_trace_t *t;
902       ip4_header_t *ip4;
903       ip6_header_t *ip6;
904       ethernet_header_t *eh;
905       i16 delta;
906
907       t = vlib_add_trace (vm, node, b, sizeof (*t));
908       delta = vnet_buffer (b)->l2_hdr_offset - b->current_data;
909       vlib_buffer_advance (b, delta);
910
911       eh = vlib_buffer_get_current (b);
912       /* Save mactype */
913       t->mactype = clib_net_to_host_u16 (eh->type);
914       t->details_valid = 1;
915       switch (t->mactype)
916         {
917         case ETHERNET_TYPE_IP4:
918           ip4 = (void *) (eh + 1);
919           t->details_valid = 2;
920           t->is_ip6 = 0;
921           t->src.ip4.as_u32 = ip4->src_address.as_u32;
922           t->dst.ip4.as_u32 = ip4->dst_address.as_u32;
923           break;
924
925         case ETHERNET_TYPE_IP6:
926           ip6 = (void *) (eh + 1);
927           t->details_valid = 2;
928           t->is_ip6 = 1;
929           clib_memcpy_fast (t->src.as_u8, ip6->src_address.as_u8,
930                             sizeof (ip6_address_t));
931           clib_memcpy_fast (t->dst.as_u8, ip6->dst_address.as_u8,
932                             sizeof (ip6_address_t));
933           break;
934
935         default:
936           /* Dunno, do nothing, leave details_valid alone */
937           break;
938         }
939       /* Restore current data (probably unnecessary) */
940       vlib_buffer_advance (b, -delta);
941     }
942 }
943
944 static_always_inline uword
945 interface_drop_punt (vlib_main_t * vm,
946                      vlib_node_runtime_t * node,
947                      vlib_frame_t * frame,
948                      vnet_error_disposition_t disposition)
949 {
950   u32 *from, n_left, thread_index, *sw_if_index;
951   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
952   u32 sw_if_indices[VLIB_FRAME_SIZE];
953   vlib_simple_counter_main_t *cm;
954   u16 nexts[VLIB_FRAME_SIZE];
955   u32 n_trace;
956   vnet_main_t *vnm;
957
958   vnm = vnet_get_main ();
959   thread_index = vm->thread_index;
960   from = vlib_frame_vector_args (frame);
961   n_left = frame->n_vectors;
962   b = bufs;
963   sw_if_index = sw_if_indices;
964
965   vlib_get_buffers (vm, from, bufs, n_left);
966
967   /* "trace add error-drop NNN?" */
968   if (PREDICT_FALSE ((n_trace = vlib_get_trace_count (vm, node))))
969     {
970       /* If pkts aren't otherwise traced... */
971       if ((node->flags & VLIB_NODE_FLAG_TRACE) == 0)
972         {
973           /* Trace them from here */
974           node->flags |= VLIB_NODE_FLAG_TRACE;
975           while (n_trace && n_left)
976             {
977               if (PREDICT_TRUE
978                   (vlib_trace_buffer (vm, node, 0 /* next_index */ , b[0],
979                                       0 /* follow chain */ )))
980                 {
981                   /*
982                    * Here we have a wireshark dissector problem.
983                    * Packets may be well-formed, or not. We
984                    * must not blow chunks in any case.
985                    *
986                    * Try to produce trace records which will help
987                    * folks understand what's going on.
988                    */
989                   drop_catchup_trace (vm, node, b[0]);
990                   n_trace--;
991                 }
992               n_left--;
993               b++;
994             }
995         }
996
997       vlib_set_trace_count (vm, node, n_trace);
998       b = bufs;
999       n_left = frame->n_vectors;
1000     }
1001
1002   if (node->flags & VLIB_NODE_FLAG_TRACE)
1003     interface_trace_buffers (vm, node, frame);
1004
1005   /* All going to drop regardless, this is just a counting exercise */
1006   clib_memset (nexts, 0, sizeof (nexts));
1007
1008   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
1009                          (disposition == VNET_ERROR_DISPOSITION_PUNT
1010                           ? VNET_INTERFACE_COUNTER_PUNT
1011                           : VNET_INTERFACE_COUNTER_DROP));
1012
1013   /* collect the array of interfaces first ... */
1014   while (n_left >= 4)
1015     {
1016       if (n_left >= 12)
1017         {
1018           /* Prefetch 8 ahead - there's not much going on in each iteration */
1019           vlib_prefetch_buffer_header (b[4], LOAD);
1020           vlib_prefetch_buffer_header (b[5], LOAD);
1021           vlib_prefetch_buffer_header (b[6], LOAD);
1022           vlib_prefetch_buffer_header (b[7], LOAD);
1023         }
1024       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
1025       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
1026       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
1027       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
1028
1029       sw_if_index += 4;
1030       n_left -= 4;
1031       b += 4;
1032     }
1033   while (n_left)
1034     {
1035       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
1036
1037       sw_if_index += 1;
1038       n_left -= 1;
1039       b += 1;
1040     }
1041
1042   /* ... then count against them in blocks */
1043   n_left = frame->n_vectors;
1044
1045   while (n_left)
1046     {
1047       vnet_sw_interface_t *sw_if0;
1048       u16 off, count;
1049
1050       off = frame->n_vectors - n_left;
1051
1052       sw_if_index = sw_if_indices + off;
1053
1054       count = clib_count_equal_u32 (sw_if_index, n_left);
1055       n_left -= count;
1056
1057       vlib_increment_simple_counter (cm, thread_index, sw_if_index[0], count);
1058
1059       /* Increment super-interface drop/punt counters for
1060          sub-interfaces. */
1061       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index[0]);
1062       if (sw_if0->sup_sw_if_index != sw_if_index[0])
1063         vlib_increment_simple_counter
1064           (cm, thread_index, sw_if0->sup_sw_if_index, count);
1065     }
1066
1067   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
1068
1069   return frame->n_vectors;
1070 }
1071
1072 static inline void
1073 pcap_drop_trace (vlib_main_t * vm,
1074                  vnet_interface_main_t * im,
1075                  vnet_pcap_t * pp, vlib_frame_t * f)
1076 {
1077   u32 *from;
1078   u32 n_left = f->n_vectors;
1079   vlib_buffer_t *b0, *p1;
1080   u32 bi0;
1081   i16 save_current_data;
1082   u16 save_current_length;
1083   vlib_error_main_t *em = &vm->error_main;
1084
1085   from = vlib_frame_vector_args (f);
1086
1087   while (n_left > 0)
1088     {
1089       if (PREDICT_TRUE (n_left > 1))
1090         {
1091           p1 = vlib_get_buffer (vm, from[1]);
1092           vlib_prefetch_buffer_header (p1, LOAD);
1093         }
1094
1095       bi0 = from[0];
1096       b0 = vlib_get_buffer (vm, bi0);
1097       from++;
1098       n_left--;
1099
1100       /* See if we're pointedly ignoring this specific error */
1101       if (im->pcap_drop_filter_hash
1102           && hash_get (im->pcap_drop_filter_hash, b0->error))
1103         continue;
1104
1105       if (!vnet_is_packet_pcaped (pp, b0, ~0))
1106         continue; /* not matching, skip */
1107
1108       /* Trace all drops, or drops received on a specific interface */
1109       save_current_data = b0->current_data;
1110       save_current_length = b0->current_length;
1111
1112       /*
1113        * Typically, we'll need to rewind the buffer
1114        * if l2_hdr_offset is valid, make sure to rewind to the start of
1115        * the L2 header. This may not be the buffer start in case we pop-ed
1116        * vlan tags.
1117        * Otherwise, rewind to buffer start and hope for the best.
1118        */
1119       if (b0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
1120         {
1121           if (b0->current_data > vnet_buffer (b0)->l2_hdr_offset)
1122             vlib_buffer_advance (b0, vnet_buffer (b0)->l2_hdr_offset -
1123                                        b0->current_data);
1124         }
1125       else if (b0->current_data > 0)
1126         {
1127           vlib_buffer_advance (b0, (word) -b0->current_data);
1128         }
1129
1130       {
1131         vlib_buffer_t *last = b0;
1132         u32 error_node_index;
1133         int drop_string_len;
1134         vlib_node_t *n;
1135         /* Length of the error string */
1136         int error_string_len =
1137           clib_strnlen (em->counters_heap[b0->error].name, 128);
1138
1139         /* Dig up the drop node */
1140         error_node_index = vm->node_main.node_by_error[b0->error];
1141         n = vlib_get_node (vm, error_node_index);
1142
1143         /* Length of full drop string, w/ "nodename: " prepended */
1144         drop_string_len = error_string_len + vec_len (n->name) + 2;
1145
1146         /* Find the last buffer in the chain */
1147         while (last->flags & VLIB_BUFFER_NEXT_PRESENT)
1148           last = vlib_get_buffer (vm, last->next_buffer);
1149
1150         /*
1151          * Append <nodename>: <error-string> to the capture,
1152          * only if we can do that without allocating a new buffer.
1153          */
1154         if (PREDICT_TRUE ((last->current_data + last->current_length) <
1155                           (VLIB_BUFFER_DEFAULT_DATA_SIZE - drop_string_len)))
1156           {
1157             clib_memcpy_fast (last->data + last->current_data +
1158                                 last->current_length,
1159                               n->name, vec_len (n->name));
1160             clib_memcpy_fast (last->data + last->current_data +
1161                                 last->current_length + vec_len (n->name),
1162                               ": ", 2);
1163             clib_memcpy_fast (last->data + last->current_data +
1164                                 last->current_length + vec_len (n->name) + 2,
1165                               em->counters_heap[b0->error].name,
1166                               error_string_len);
1167             last->current_length += drop_string_len;
1168             b0->flags &= ~(VLIB_BUFFER_TOTAL_LENGTH_VALID);
1169             pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
1170             last->current_length -= drop_string_len;
1171             b0->current_data = save_current_data;
1172             b0->current_length = save_current_length;
1173             continue;
1174           }
1175       }
1176
1177       /*
1178        * Didn't have space in the last buffer, here's the dropped
1179        * packet as-is
1180        */
1181       pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
1182
1183       b0->current_data = save_current_data;
1184       b0->current_length = save_current_length;
1185     }
1186 }
1187
1188 #ifndef CLIB_MARCH_VARIANT
1189 void
1190 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
1191 {
1192   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1193
1194   if (im->pcap_drop_filter_hash == 0)
1195     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
1196
1197   if (is_add)
1198     hash_set (im->pcap_drop_filter_hash, error_index, 1);
1199   else
1200     hash_unset (im->pcap_drop_filter_hash, error_index);
1201 }
1202 #endif /* CLIB_MARCH_VARIANT */
1203
1204 VLIB_NODE_FN (interface_drop) (vlib_main_t * vm,
1205                                vlib_node_runtime_t * node,
1206                                vlib_frame_t * frame)
1207 {
1208   vnet_main_t *vnm = vnet_get_main ();
1209   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1210   vnet_pcap_t *pp = &vnm->pcap;
1211
1212   if (PREDICT_FALSE (pp->pcap_drop_enable))
1213     pcap_drop_trace (vm, im, pp, frame);
1214
1215   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
1216 }
1217
1218 VLIB_NODE_FN (interface_punt) (vlib_main_t * vm,
1219                                vlib_node_runtime_t * node,
1220                                vlib_frame_t * frame)
1221 {
1222   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
1223 }
1224
1225 /* *INDENT-OFF* */
1226 VLIB_REGISTER_NODE (interface_drop) = {
1227   .name = "error-drop",
1228   .vector_size = sizeof (u32),
1229   .format_trace = format_vnet_error_trace,
1230   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
1231   .n_next_nodes = 1,
1232   .next_nodes = {
1233     [0] = "drop",
1234   },
1235 };
1236 /* *INDENT-ON* */
1237
1238 /* *INDENT-OFF* */
1239 VLIB_REGISTER_NODE (interface_punt) = {
1240   .name = "error-punt",
1241   .vector_size = sizeof (u32),
1242   .format_trace = format_vnet_error_trace,
1243   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
1244   .n_next_nodes = 1,
1245   .next_nodes = {
1246     [0] = "punt",
1247   },
1248 };
1249 /* *INDENT-ON* */
1250
1251 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node) = {
1252   .name = "interface-output",
1253   .vector_size = sizeof (u32),
1254 };
1255
1256 VLIB_NODE_FN (vnet_interface_output_arc_end_node)
1257 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1258 {
1259   vnet_main_t *vnm = vnet_get_main ();
1260   vnet_interface_main_t *im = &vnm->interface_main;
1261   vnet_hw_interface_t *hi;
1262   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
1263   u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index = sw_if_indices;
1264   vlib_frame_bitmap_t used_elts = {}, mask = {};
1265   u32 *tmp, *from, n_left, n_comp, n_p_comp, swif, off;
1266   u16 next_index;
1267   void *ptr[VLIB_FRAME_SIZE], **p = ptr;
1268
1269   from = vlib_frame_vector_args (frame);
1270   n_left = frame->n_vectors;
1271   vlib_get_buffers (vm, from, bufs, n_left);
1272
1273   while (n_left >= 8)
1274     {
1275       vlib_prefetch_buffer_header (b[4], LOAD);
1276       vlib_prefetch_buffer_header (b[5], LOAD);
1277       vlib_prefetch_buffer_header (b[6], LOAD);
1278       vlib_prefetch_buffer_header (b[7], LOAD);
1279
1280       p[0] = vlib_buffer_get_current (b[0]);
1281       p[1] = vlib_buffer_get_current (b[1]);
1282       p[2] = vlib_buffer_get_current (b[2]);
1283       p[3] = vlib_buffer_get_current (b[3]);
1284       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
1285       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
1286       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
1287       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
1288
1289       p += 4;
1290       b += 4;
1291       sw_if_index += 4;
1292       n_left -= 4;
1293     }
1294
1295   while (n_left)
1296     {
1297       p[0] = vlib_buffer_get_current (b[0]);
1298       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
1299       p++;
1300       b++;
1301       sw_if_index++;
1302       n_left--;
1303     }
1304
1305   n_left = frame->n_vectors;
1306   swif = sw_if_indices[0];
1307   off = 0;
1308
1309   /* a bit ugly but it allows us to reuse stack space for temporary store
1310    * which may also improve memory latency */
1311   tmp = (u32 *) bufs;
1312
1313 more:
1314   next_index = vec_elt (im->if_out_arc_end_next_index_by_sw_if_index, swif);
1315   hi = vnet_get_sup_hw_interface (vnm, swif);
1316   vnet_hw_if_output_node_runtime_t *r = 0;
1317   void *ptr_tmp[VLIB_FRAME_SIZE], **p_tmp = ptr_tmp;
1318
1319   if (hi->output_node_thread_runtimes)
1320     r = vec_elt_at_index (hi->output_node_thread_runtimes, vm->thread_index);
1321
1322   /* compare and compress based on comparison mask */
1323   clib_mask_compare_u32 (swif, sw_if_indices, mask, frame->n_vectors);
1324   n_comp = clib_compress_u32 (tmp, from, mask, frame->n_vectors);
1325
1326   /*
1327    * tx queue of given interface is not available on given thread
1328    */
1329   if (r)
1330     {
1331       if (r->n_queues == 0)
1332         {
1333           vlib_error_drop_buffers (
1334             vm, node, tmp,
1335             /* buffer stride */ 1, n_comp, VNET_INTERFACE_OUTPUT_NEXT_DROP,
1336             node->node_index, VNET_INTERFACE_OUTPUT_ERROR_NO_TX_QUEUE);
1337           goto drop;
1338         }
1339       else if (r->n_queues > 1)
1340         {
1341           n_p_comp = clib_compress_u64 ((u64 *) p_tmp, (u64 *) ptr, mask,
1342                                         frame->n_vectors);
1343           ASSERT (n_p_comp == n_comp);
1344         }
1345     }
1346
1347   enqueue_to_tx_node (vm, node, hi, next_index, r, tmp, ptr_tmp, n_comp);
1348
1349 drop:
1350   n_left -= n_comp;
1351   if (n_left)
1352     {
1353       /* store comparison mask so we can find next unused element */
1354       vlib_frame_bitmap_or (used_elts, mask);
1355
1356       /* fine first unused sw_if_index by scanning trough used_elts bitmap */
1357       while (PREDICT_FALSE (used_elts[off] == ~0))
1358         off++;
1359
1360       swif =
1361         sw_if_indices[(off << 6) + count_trailing_zeros (~used_elts[off])];
1362       goto more;
1363     }
1364
1365   return frame->n_vectors;
1366 }
1367
1368 VLIB_REGISTER_NODE (vnet_interface_output_arc_end_node) = {
1369   .name = "interface-output-arc-end",
1370   .vector_size = sizeof (u32),
1371   .n_next_nodes = 1,
1372   .next_nodes = {
1373     [0] = "error-drop",
1374   },
1375 };
1376
1377 VNET_FEATURE_ARC_INIT (interface_output, static) = {
1378   .arc_name = "interface-output",
1379   .start_nodes = VNET_FEATURES (0),
1380   .last_in_arc = "interface-output-arc-end",
1381   .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index,
1382 };
1383
1384 VNET_FEATURE_INIT (span_tx, static) = {
1385   .arc_name = "interface-output",
1386   .node_name = "span-output",
1387   .runs_before = VNET_FEATURES ("interface-output-arc-end"),
1388 };
1389
1390 VNET_FEATURE_INIT (ipsec_if_tx, static) = {
1391   .arc_name = "interface-output",
1392   .node_name = "ipsec-if-output",
1393   .runs_before = VNET_FEATURES ("interface-output-arc-end"),
1394 };
1395
1396 VNET_FEATURE_INIT (interface_output_arc_end, static) = {
1397   .arc_name = "interface-output",
1398   .node_name = "interface-output-arc-end",
1399   .runs_before = 0,
1400 };
1401
1402 #ifndef CLIB_MARCH_VARIANT
1403 clib_error_t *
1404 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1405                                                        u32 hw_if_index,
1406                                                        u32 is_create)
1407 {
1408   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1409   u32 next_index;
1410
1411   if (hi->output_node_index == 0)
1412     return 0;
1413
1414   next_index = vlib_node_add_next
1415     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1416      hi->output_node_index);
1417   hi->output_node_next_index = next_index;
1418
1419   return 0;
1420 }
1421
1422 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1423   (vnet_per_buffer_interface_output_hw_interface_add_del);
1424
1425 void
1426 vnet_set_interface_output_node (vnet_main_t * vnm,
1427                                 u32 hw_if_index, u32 node_index)
1428 {
1429   ASSERT (node_index);
1430   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1431   u32 next_index = vlib_node_add_next
1432     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index);
1433   hi->output_node_next_index = next_index;
1434   hi->output_node_index = node_index;
1435 }
1436 #endif /* CLIB_MARCH_VARIANT */
1437
1438 /*
1439  * fd.io coding-style-patch-verification: ON
1440  *
1441  * Local Variables:
1442  * eval: (c-set-style "gnu")
1443  * End:
1444  */