LCOV - code coverage report
Current view: top level - vnet/devices/virtio - device.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 213 622 34.2 %
Date: 2023-07-05 22:20:52 Functions: 17 34 50.0 %

          Line data    Source code
       1             : /*
       2             :  *------------------------------------------------------------------
       3             :  * Copyright (c) 2016 Cisco and/or its affiliates.
       4             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5             :  * you may not use this file except in compliance with the License.
       6             :  * You may obtain a copy of the License at:
       7             :  *
       8             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9             :  *
      10             :  * Unless required by applicable law or agreed to in writing, software
      11             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13             :  * See the License for the specific language governing permissions and
      14             :  * limitations under the License.
      15             :  *------------------------------------------------------------------
      16             :  */
      17             : 
      18             : #include <sys/types.h>
      19             : #include <sys/stat.h>
      20             : #include <fcntl.h>
      21             : 
      22             : #include <vlib/vlib.h>
      23             : #include <vlib/unix/unix.h>
      24             : #include <vnet/vnet.h>
      25             : #include <vnet/ethernet/ethernet.h>
      26             : #include <vnet/gso/gro_func.h>
      27             : #include <vnet/gso/hdr_offset_parser.h>
      28             : #include <vnet/ip/ip4_packet.h>
      29             : #include <vnet/ip/ip6_packet.h>
      30             : #include <vnet/ip/ip_psh_cksum.h>
      31             : #include <vnet/tcp/tcp_packet.h>
      32             : #include <vnet/udp/udp_packet.h>
      33             : #include <vnet/devices/virtio/virtio.h>
      34             : 
      35             : #define VIRTIO_TX_MAX_CHAIN_LEN 127
      36             : 
      37             : #define foreach_virtio_tx_func_error           \
      38             : _(NO_FREE_SLOTS, "no free tx slots")           \
      39             : _(TRUNC_PACKET, "packet > buffer size -- truncated in tx ring") \
      40             : _(PENDING_MSGS, "pending msgs in tx ring") \
      41             : _(INDIRECT_DESC_ALLOC_FAILED, "indirect descriptor allocation failed - packet drop") \
      42             : _(OUT_OF_ORDER, "out-of-order buffers in used ring") \
      43             : _(GSO_PACKET_DROP, "gso disabled on itf  -- gso packet drop") \
      44             : _(CSUM_OFFLOAD_PACKET_DROP, "checksum offload disabled on itf -- csum offload packet drop")
      45             : 
      46             : typedef enum
      47             : {
      48             : #define _(f,s) VIRTIO_TX_ERROR_##f,
      49             :   foreach_virtio_tx_func_error
      50             : #undef _
      51             :     VIRTIO_TX_N_ERROR,
      52             : } virtio_tx_func_error_t;
      53             : 
      54             : static char *virtio_tx_func_error_strings[] = {
      55             : #define _(n,s) s,
      56             :   foreach_virtio_tx_func_error
      57             : #undef _
      58             : };
      59             : 
      60             : static u8 *
      61           0 : format_virtio_device (u8 * s, va_list * args)
      62             : {
      63           0 :   u32 dev_instance = va_arg (*args, u32);
      64           0 :   int verbose = va_arg (*args, int);
      65           0 :   u32 indent = format_get_indent (s);
      66           0 :   virtio_main_t *vim = &virtio_main;
      67           0 :   virtio_if_t *vif = vec_elt_at_index (vim->interfaces, dev_instance);
      68           0 :   vnet_virtio_vring_t *vring = 0;
      69             : 
      70           0 :   s = format (s, "VIRTIO interface");
      71           0 :   if (verbose)
      72             :     {
      73           0 :       s = format (s, "\n%U instance %u", format_white_space, indent + 2,
      74             :                   dev_instance);
      75           0 :       s = format (s, "\n%U RX QUEUE : Total Packets", format_white_space,
      76             :                   indent + 4);
      77           0 :       vec_foreach (vring, vif->rxq_vrings)
      78             :         {
      79           0 :           s = format (s, "\n%U %8u : %llu", format_white_space, indent + 4,
      80           0 :                       RX_QUEUE_ACCESS (vring->queue_id), vring->total_packets);
      81             :         }
      82           0 :       s = format (s, "\n%U TX QUEUE : Total Packets", format_white_space,
      83             :                   indent + 4);
      84           0 :       vec_foreach (vring, vif->txq_vrings)
      85             :         {
      86           0 :           s = format (s, "\n%U %8u : %llu", format_white_space, indent + 4,
      87           0 :                       TX_QUEUE_ACCESS (vring->queue_id), vring->total_packets);
      88             :         }
      89             :     }
      90             : 
      91           0 :   return s;
      92             : }
      93             : 
      94             : typedef struct
      95             : {
      96             :   u32 buffer_index;
      97             :   u32 sw_if_index;
      98             :   generic_header_offset_t gho;
      99             :   vlib_buffer_t buffer;
     100             : } virtio_tx_trace_t;
     101             : 
     102             : static u8 *
     103           0 : format_virtio_tx_trace (u8 * s, va_list * va)
     104             : {
     105           0 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
     106           0 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
     107           0 :   virtio_tx_trace_t *t = va_arg (*va, virtio_tx_trace_t *);
     108           0 :   u32 indent = format_get_indent (s);
     109             : 
     110           0 :   s = format (s, "%Ubuffer 0x%x: %U\n", format_white_space, indent,
     111             :               t->buffer_index, format_vnet_buffer_no_chain, &t->buffer);
     112             :   s =
     113           0 :     format (s, "%U%U\n", format_white_space, indent,
     114             :             format_generic_header_offset, &t->gho);
     115             :   s =
     116           0 :     format (s, "%U%U", format_white_space, indent,
     117           0 :             format_ethernet_header_with_length, t->buffer.pre_data,
     118             :             sizeof (t->buffer.pre_data));
     119           0 :   return s;
     120             : }
     121             : 
     122             : static void
     123           0 : virtio_tx_trace (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *b0,
     124             :                  u32 bi, int is_tun)
     125             : {
     126             :   virtio_tx_trace_t *t;
     127           0 :   t = vlib_add_trace (vm, node, b0, sizeof (t[0]));
     128           0 :   t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
     129           0 :   t->buffer_index = bi;
     130           0 :   clib_memset (&t->gho, 0, sizeof (t->gho));
     131           0 :   if (is_tun)
     132             :     {
     133           0 :       int is_ip4 = 0, is_ip6 = 0;
     134             : 
     135           0 :       switch (((u8 *) vlib_buffer_get_current (b0))[0] & 0xf0)
     136             :         {
     137           0 :         case 0x40:
     138           0 :           is_ip4 = 1;
     139           0 :           break;
     140           0 :         case 0x60:
     141           0 :           is_ip6 = 1;
     142           0 :           break;
     143           0 :         default:
     144           0 :           break;
     145             :         }
     146           0 :       vnet_generic_header_offset_parser (b0, &t->gho, 0, is_ip4, is_ip6);
     147             :     }
     148             :   else
     149           0 :     vnet_generic_header_offset_parser (b0, &t->gho, 1,
     150           0 :                                        b0->flags &
     151             :                                        VNET_BUFFER_F_IS_IP4,
     152           0 :                                        b0->flags & VNET_BUFFER_F_IS_IP6);
     153             : 
     154           0 :   clib_memcpy_fast (&t->buffer, b0, sizeof (*b0) - sizeof (b0->pre_data));
     155           0 :   clib_memcpy_fast (t->buffer.pre_data, vlib_buffer_get_current (b0),
     156             :                     sizeof (t->buffer.pre_data));
     157           0 : }
     158             : 
     159             : static void
     160         757 : virtio_interface_drop_inline (vlib_main_t *vm, virtio_if_t *vif,
     161             :                               uword node_index, u32 *buffers, u16 n,
     162             :                               virtio_tx_func_error_t error)
     163             : {
     164         757 :   vlib_error_count (vm, node_index, error, n);
     165         757 :   vlib_increment_simple_counter (vnet_main.interface_main.sw_if_counters +
     166             :                                    VNET_INTERFACE_COUNTER_DROP,
     167             :                                  vm->thread_index, vif->sw_if_index, n);
     168         757 :   vlib_buffer_free (vm, buffers, n);
     169         757 : }
     170             : 
     171             : static void
     172     1076260 : virtio_memset_ring_u32 (u32 *ring, u32 start, u32 ring_size, u32 n_buffers)
     173             : {
     174     1076260 :   ASSERT (n_buffers <= ring_size);
     175             : 
     176     1076260 :   if (PREDICT_TRUE (start + n_buffers <= ring_size))
     177             :     {
     178      942473 :       clib_memset_u32 (ring + start, ~0, n_buffers);
     179             :     }
     180             :   else
     181             :     {
     182      133791 :       clib_memset_u32 (ring + start, ~0, ring_size - start);
     183      133791 :       clib_memset_u32 (ring, ~0, n_buffers - (ring_size - start));
     184             :     }
     185     1076260 : }
     186             : 
     187             : static void
     188     1245470 : virtio_free_used_device_desc_split (vlib_main_t *vm,
     189             :                                     vnet_virtio_vring_t *vring,
     190             :                                     uword node_index)
     191             : {
     192     1245470 :   u16 used = vring->desc_in_use;
     193     1245470 :   u16 sz = vring->queue_size;
     194     1245470 :   u16 mask = sz - 1;
     195     1245470 :   u16 last = vring->last_used_idx;
     196     1245470 :   u16 n_left = vring->used->idx - last;
     197     1245470 :   u16 out_of_order_count = 0;
     198             : 
     199     1245470 :   if (n_left == 0)
     200      169204 :     return;
     201             : 
     202     2152530 :   while (n_left)
     203             :     {
     204     1076260 :       vnet_virtio_vring_used_elem_t *e = &vring->used->ring[last & mask];
     205             :       u16 slot, n_buffers;
     206     1076260 :       slot = n_buffers = e->id;
     207             : 
     208    35368900 :       while (e->id == (n_buffers & mask))
     209             :         {
     210    35368900 :           n_left--;
     211    35368900 :           last++;
     212    35368900 :           n_buffers++;
     213    35368900 :           vnet_virtio_vring_desc_t *d = &vring->desc[e->id];
     214             :           u16 next;
     215    35368900 :           while (d->flags & VRING_DESC_F_NEXT)
     216             :             {
     217           0 :               n_buffers++;
     218           0 :               next = d->next;
     219           0 :               d = &vring->desc[next];
     220             :             }
     221    35368900 :           if (n_left == 0)
     222     1076260 :             break;
     223    34292600 :           e = &vring->used->ring[last & mask];
     224             :         }
     225     1076260 :       vlib_buffer_free_from_ring (vm, vring->buffers, slot,
     226     1076260 :                                   sz, (n_buffers - slot));
     227     1076260 :       virtio_memset_ring_u32 (vring->buffers, slot, sz, (n_buffers - slot));
     228     1076260 :       used -= (n_buffers - slot);
     229             : 
     230     1076260 :       if (n_left > 0)
     231             :         {
     232           0 :           vlib_buffer_free (vm, &vring->buffers[e->id], 1);
     233           0 :           vring->buffers[e->id] = ~0;
     234           0 :           used--;
     235           0 :           last++;
     236           0 :           n_left--;
     237           0 :           out_of_order_count++;
     238           0 :           vring->flags |= VRING_TX_OUT_OF_ORDER;
     239             :         }
     240             :     }
     241             : 
     242             :   /*
     243             :    * Some vhost-backends give buffers back in out-of-order fashion in used ring.
     244             :    * It impacts the overall virtio-performance.
     245             :    */
     246     1076260 :   if (out_of_order_count)
     247           0 :     vlib_error_count (vm, node_index, VIRTIO_TX_ERROR_OUT_OF_ORDER,
     248             :                       out_of_order_count);
     249             : 
     250     1076260 :   vring->desc_in_use = used;
     251     1076260 :   vring->last_used_idx = last;
     252             : }
     253             : 
     254             : static void
     255           0 : virtio_free_used_device_desc_packed (vlib_main_t *vm,
     256             :                                      vnet_virtio_vring_t *vring,
     257             :                                      uword node_index)
     258             : {
     259             :   vnet_virtio_vring_packed_desc_t *d;
     260           0 :   u16 sz = vring->queue_size;
     261           0 :   u16 last = vring->last_used_idx;
     262           0 :   u16 n_buffers = 0, start;
     263             :   u16 flags;
     264             : 
     265           0 :   if (vring->desc_in_use == 0)
     266           0 :     return;
     267             : 
     268           0 :   d = &vring->packed_desc[last];
     269           0 :   flags = d->flags;
     270           0 :   start = d->id;
     271             : 
     272           0 :   while ((flags & VRING_DESC_F_AVAIL) == (vring->used_wrap_counter << 7) &&
     273           0 :          (flags & VRING_DESC_F_USED) == (vring->used_wrap_counter << 15))
     274             :     {
     275           0 :       last++;
     276           0 :       n_buffers++;
     277             : 
     278           0 :       if (last >= sz)
     279             :         {
     280           0 :           last = 0;
     281           0 :           vring->used_wrap_counter ^= 1;
     282             :         }
     283           0 :       d = &vring->packed_desc[last];
     284           0 :       flags = d->flags;
     285             :     }
     286             : 
     287           0 :   if (n_buffers)
     288             :     {
     289           0 :       vlib_buffer_free_from_ring (vm, vring->buffers, start, sz, n_buffers);
     290           0 :       virtio_memset_ring_u32 (vring->buffers, start, sz, n_buffers);
     291           0 :       vring->desc_in_use -= n_buffers;
     292           0 :       vring->last_used_idx = last;
     293             :     }
     294             : }
     295             : 
     296             : static void
     297     1245470 : virtio_free_used_device_desc (vlib_main_t *vm, vnet_virtio_vring_t *vring,
     298             :                               uword node_index, int packed)
     299             : {
     300     1245470 :   if (packed)
     301           0 :     virtio_free_used_device_desc_packed (vm, vring, node_index);
     302             :   else
     303     1245470 :     virtio_free_used_device_desc_split (vm, vring, node_index);
     304             : 
     305     1245470 : }
     306             : 
     307             : static void
     308           0 : set_checksum_offsets (vlib_buffer_t *b, vnet_virtio_net_hdr_v1_t *hdr,
     309             :                       const int is_l2)
     310             : {
     311           0 :   vnet_buffer_oflags_t oflags = vnet_buffer (b)->oflags;
     312           0 :   i16 l4_hdr_offset = vnet_buffer (b)->l4_hdr_offset - b->current_data;
     313           0 :   if (b->flags & VNET_BUFFER_F_IS_IP4)
     314             :     {
     315             :       ip4_header_t *ip4;
     316           0 :       hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
     317           0 :       hdr->csum_start = l4_hdr_offset; // 0x22;
     318             : 
     319             :       /*
     320             :        * virtio devices do not support IP4 checksum offload. So driver takes
     321             :        * care of it while doing tx.
     322             :        */
     323           0 :       ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
     324           0 :       if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM)
     325           0 :         ip4->checksum = ip4_header_checksum (ip4);
     326             : 
     327             :       /*
     328             :        * virtio devices assume the l4 header is set to the checksum of the
     329             :        * l3 pseudo-header, so we compute it before tx-ing
     330             :        */
     331           0 :       if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)
     332             :         {
     333           0 :           tcp_header_t *tcp =
     334           0 :             (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
     335           0 :           tcp->checksum = ip4_pseudo_header_cksum (ip4);
     336           0 :           hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum);
     337             :         }
     338           0 :       else if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)
     339             :         {
     340           0 :           udp_header_t *udp =
     341           0 :             (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
     342           0 :           udp->checksum = ip4_pseudo_header_cksum (ip4);
     343           0 :           hdr->csum_offset = STRUCT_OFFSET_OF (udp_header_t, checksum);
     344             :         }
     345             :     }
     346           0 :   else if (b->flags & VNET_BUFFER_F_IS_IP6)
     347             :     {
     348             :       ip6_header_t *ip6;
     349           0 :       hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
     350           0 :       hdr->csum_start = l4_hdr_offset; // 0x36;
     351           0 :       ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
     352             : 
     353             :       /*
     354             :        * virtio devices assume the l4 header is set to the checksum of the
     355             :        * l3 pseudo-header, so we compute it before tx-ing
     356             :        */
     357           0 :       if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)
     358             :         {
     359           0 :           tcp_header_t *tcp =
     360           0 :             (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
     361           0 :           tcp->checksum = ip6_pseudo_header_cksum (ip6);
     362           0 :           hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum);
     363             :         }
     364           0 :       else if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)
     365             :         {
     366           0 :           udp_header_t *udp =
     367           0 :             (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
     368           0 :           udp->checksum = ip6_pseudo_header_cksum (ip6);
     369           0 :           hdr->csum_offset = STRUCT_OFFSET_OF (udp_header_t, checksum);
     370             :         }
     371             :     }
     372           0 : }
     373             : 
     374             : static void
     375     1250560 : set_gso_offsets (vlib_buffer_t *b, vnet_virtio_net_hdr_v1_t *hdr,
     376             :                  const int is_l2)
     377             : {
     378     1250560 :   vnet_buffer_oflags_t oflags = vnet_buffer (b)->oflags;
     379     1250560 :   i16 l4_hdr_offset = vnet_buffer (b)->l4_hdr_offset - b->current_data;
     380             : 
     381     1250560 :   if (b->flags & VNET_BUFFER_F_IS_IP4)
     382             :     {
     383             :       ip4_header_t *ip4;
     384      669535 :       hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
     385      669535 :       hdr->gso_size = vnet_buffer2 (b)->gso_size;
     386      669535 :       hdr->hdr_len = l4_hdr_offset + vnet_buffer2 (b)->gso_l4_hdr_sz;
     387      669535 :       hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
     388      669535 :       hdr->csum_start = l4_hdr_offset; // 0x22;
     389      669535 :       hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum);
     390      669535 :       ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
     391             :       /*
     392             :        * virtio devices do not support IP4 checksum offload. So driver takes care
     393             :        * of it while doing tx.
     394             :        */
     395      669535 :       if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM)
     396      669535 :         ip4->checksum = ip4_header_checksum (ip4);
     397             :     }
     398      581029 :   else if (b->flags & VNET_BUFFER_F_IS_IP6)
     399             :     {
     400      581029 :       hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
     401      581029 :       hdr->gso_size = vnet_buffer2 (b)->gso_size;
     402      581029 :       hdr->hdr_len = l4_hdr_offset + vnet_buffer2 (b)->gso_l4_hdr_sz;
     403      581029 :       hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
     404      581029 :       hdr->csum_start = l4_hdr_offset; // 0x36;
     405      581029 :       hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum);
     406             :     }
     407     1250560 : }
     408             : 
     409             : static u16
     410    35369200 : add_buffer_to_slot (vlib_main_t *vm, vlib_node_runtime_t *node,
     411             :                     virtio_if_t *vif, vnet_virtio_vring_t *vring, u32 bi,
     412             :                     u16 free_desc_count, u16 avail, u16 next, u16 mask,
     413             :                     int hdr_sz, int do_gso, int csum_offload, int is_pci,
     414             :                     int is_tun, int is_indirect, int is_any_layout)
     415             : {
     416    35369200 :   u16 n_added = 0;
     417             :   vnet_virtio_vring_desc_t *d;
     418    35369200 :   int is_l2 = !is_tun;
     419    35369200 :   d = &vring->desc[next];
     420    35369200 :   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     421    35369200 :   vnet_virtio_net_hdr_v1_t *hdr = vlib_buffer_get_current (b) - hdr_sz;
     422    35369200 :   u32 drop_inline = ~0;
     423             : 
     424    35369200 :   clib_memset_u8 (hdr, 0, hdr_sz);
     425             : 
     426    35369200 :   vring->total_packets++;
     427    35369200 :   if (b->flags & VNET_BUFFER_F_GSO)
     428             :     {
     429     1250560 :       if (do_gso)
     430     1250560 :         set_gso_offsets (b, hdr, is_l2);
     431             :       else
     432             :         {
     433           0 :           drop_inline = VIRTIO_TX_ERROR_GSO_PACKET_DROP;
     434           0 :           goto done;
     435             :         }
     436             :     }
     437    34118600 :   else if (b->flags & VNET_BUFFER_F_OFFLOAD)
     438             :     {
     439           0 :       if (csum_offload)
     440           0 :         set_checksum_offsets (b, hdr, is_l2);
     441             :       else
     442             :         {
     443           0 :           drop_inline = VIRTIO_TX_ERROR_CSUM_OFFLOAD_PACKET_DROP;
     444           0 :           goto done;
     445             :         }
     446             :     }
     447             : 
     448    35369200 :   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
     449             :     {
     450           0 :       virtio_tx_trace (vm, node, b, bi, is_tun);
     451             :     }
     452             : 
     453    35369200 :   if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0))
     454             :     {
     455    16750200 :       d->addr = ((is_pci) ? vlib_buffer_get_current_pa (vm, b) :
     456    33500400 :                  pointer_to_uword (vlib_buffer_get_current (b))) - hdr_sz;
     457    16750200 :       d->len = b->current_length + hdr_sz;
     458    16750200 :       d->flags = 0;
     459             :     }
     460    18619000 :   else if (is_indirect)
     461             :     {
     462             :       /*
     463             :        * We are using single vlib_buffer_t for indirect descriptor(s)
     464             :        * chain. Single descriptor is 16 bytes and vlib_buffer_t
     465             :        * has 2048 bytes space. So maximum long chain can have 128
     466             :        * (=2048/16) indirect descriptors.
     467             :        * It can easily support 65535 bytes of Jumbo frames with
     468             :        * each data buffer size of 512 bytes minimum.
     469             :        */
     470    18619000 :       u32 indirect_buffer = 0;
     471    18619000 :       if (PREDICT_FALSE (vlib_buffer_alloc (vm, &indirect_buffer, 1) == 0))
     472             :         {
     473           0 :           drop_inline = VIRTIO_TX_ERROR_INDIRECT_DESC_ALLOC_FAILED;
     474           0 :           goto done;
     475             :         }
     476             : 
     477    18619000 :       vlib_buffer_t *indirect_desc = vlib_get_buffer (vm, indirect_buffer);
     478    18619000 :       indirect_desc->current_data = 0;
     479    18619000 :       indirect_desc->flags |= VLIB_BUFFER_NEXT_PRESENT;
     480    18619000 :       indirect_desc->next_buffer = bi;
     481    18619000 :       bi = indirect_buffer;
     482             : 
     483             :       vnet_virtio_vring_desc_t *id =
     484    18619000 :         (vnet_virtio_vring_desc_t *) vlib_buffer_get_current (indirect_desc);
     485    18619000 :       u32 count = 1;
     486    18619000 :       if (is_pci)
     487             :         {
     488           0 :           d->addr = vlib_physmem_get_pa (vm, id);
     489           0 :           id->addr = vlib_buffer_get_current_pa (vm, b) - hdr_sz;
     490             : 
     491             :           /*
     492             :            * If VIRTIO_F_ANY_LAYOUT is not negotiated, then virtio_net_hdr
     493             :            * should be presented in separate descriptor and data will start
     494             :            * from next descriptor.
     495             :            */
     496           0 :           if (is_any_layout)
     497           0 :             id->len = b->current_length + hdr_sz;
     498             :           else
     499             :             {
     500           0 :               id->len = hdr_sz;
     501           0 :               id->flags = VRING_DESC_F_NEXT;
     502           0 :               id->next = count;
     503           0 :               count++;
     504           0 :               id++;
     505           0 :               id->addr = vlib_buffer_get_current_pa (vm, b);
     506           0 :               id->len = b->current_length;
     507             :             }
     508           0 :           while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     509             :             {
     510           0 :               id->flags = VRING_DESC_F_NEXT;
     511           0 :               id->next = count;
     512           0 :               count++;
     513           0 :               id++;
     514           0 :               b = vlib_get_buffer (vm, b->next_buffer);
     515           0 :               id->addr = vlib_buffer_get_current_pa (vm, b);
     516           0 :               id->len = b->current_length;
     517           0 :               if (PREDICT_FALSE (count == VIRTIO_TX_MAX_CHAIN_LEN))
     518             :                 {
     519           0 :                   if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     520           0 :                     vlib_error_count (vm, node->node_index,
     521             :                                       VIRTIO_TX_ERROR_TRUNC_PACKET, 1);
     522           0 :                   break;
     523             :                 }
     524             :             }
     525             :         }
     526             :       else                      /* VIRTIO_IF_TYPE_[TAP | TUN] */
     527             :         {
     528    18619000 :           d->addr = pointer_to_uword (id);
     529             :           /* first buffer in chain */
     530    18619000 :           id->addr = pointer_to_uword (vlib_buffer_get_current (b)) - hdr_sz;
     531    18619000 :           id->len = b->current_length + hdr_sz;
     532             : 
     533   102496000 :           while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     534             :             {
     535    83877400 :               id->flags = VRING_DESC_F_NEXT;
     536    83877400 :               id->next = count;
     537    83877400 :               count++;
     538    83877400 :               id++;
     539    83877400 :               b = vlib_get_buffer (vm, b->next_buffer);
     540    83877400 :               id->addr = pointer_to_uword (vlib_buffer_get_current (b));
     541    83877400 :               id->len = b->current_length;
     542    83877400 :               if (PREDICT_FALSE (count == VIRTIO_TX_MAX_CHAIN_LEN))
     543             :                 {
     544           0 :                   if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     545           0 :                     vlib_error_count (vm, node->node_index,
     546             :                                       VIRTIO_TX_ERROR_TRUNC_PACKET, 1);
     547           0 :                   break;
     548             :                 }
     549             :             }
     550             :         }
     551    18619000 :       id->flags = 0;
     552    18619000 :       id->next = 0;
     553    18619000 :       d->len = count * sizeof (vnet_virtio_vring_desc_t);
     554    18619000 :       d->flags = VRING_DESC_F_INDIRECT;
     555             :     }
     556           0 :   else if (is_pci)
     557             :     {
     558           0 :       u16 count = next;
     559           0 :       vlib_buffer_t *b_temp = b;
     560           0 :       u16 n_buffers_in_chain = 1;
     561             : 
     562             :       /*
     563             :        * Check the length of the chain for the required number of
     564             :        * descriptors. Return from here, retry to get more descriptors,
     565             :        * if chain length is greater than available descriptors.
     566             :        */
     567           0 :       while (b_temp->flags & VLIB_BUFFER_NEXT_PRESENT)
     568             :         {
     569           0 :           n_buffers_in_chain++;
     570           0 :           b_temp = vlib_get_buffer (vm, b_temp->next_buffer);
     571             :         }
     572             : 
     573           0 :       if (n_buffers_in_chain > free_desc_count)
     574           0 :         return n_buffers_in_chain;
     575             : 
     576           0 :       d->addr = vlib_buffer_get_current_pa (vm, b) - hdr_sz;
     577           0 :       d->len = b->current_length + hdr_sz;
     578             : 
     579           0 :       while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     580             :         {
     581           0 :           d->flags = VRING_DESC_F_NEXT;
     582           0 :           vring->buffers[count] = bi;
     583           0 :           b->flags &=
     584             :             ~(VLIB_BUFFER_NEXT_PRESENT | VLIB_BUFFER_TOTAL_LENGTH_VALID);
     585           0 :           bi = b->next_buffer;
     586           0 :           b->next_buffer = 0;
     587           0 :           n_added++;
     588           0 :           count = (count + 1) & mask;
     589           0 :           d->next = count;
     590           0 :           d = &vring->desc[count];
     591           0 :           b = vlib_get_buffer (vm, bi);
     592           0 :           d->addr = vlib_buffer_get_current_pa (vm, b);
     593           0 :           d->len = b->current_length;
     594             :         }
     595           0 :       d->flags = 0;
     596           0 :       vring->buffers[count] = bi;
     597           0 :       vring->avail->ring[avail & mask] = next;
     598           0 :       n_added++;
     599           0 :       return n_added;
     600             :     }
     601             :   else
     602             :     {
     603           0 :       ASSERT (0);
     604             :     }
     605    35369200 :   vring->buffers[next] = bi;
     606    35369200 :   vring->avail->ring[avail & mask] = next;
     607    35369200 :   n_added++;
     608             : 
     609    35369200 : done:
     610    35369200 :   if (drop_inline != ~0)
     611           0 :     virtio_interface_drop_inline (vm, vif, node->node_index, &bi, 1,
     612             :                                   drop_inline);
     613             : 
     614    35369200 :   return n_added;
     615             : }
     616             : 
     617             : static u16
     618           0 : add_buffer_to_slot_packed (vlib_main_t *vm, vlib_node_runtime_t *node,
     619             :                            virtio_if_t *vif, vnet_virtio_vring_t *vring,
     620             :                            u32 bi, u16 next, int hdr_sz, int do_gso,
     621             :                            int csum_offload, int is_pci, int is_tun,
     622             :                            int is_indirect, int is_any_layout)
     623             : {
     624           0 :   u16 n_added = 0, flags = 0;
     625           0 :   int is_l2 = !is_tun;
     626           0 :   vnet_virtio_vring_packed_desc_t *d = &vring->packed_desc[next];
     627           0 :   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     628           0 :   vnet_virtio_net_hdr_v1_t *hdr = vlib_buffer_get_current (b) - hdr_sz;
     629           0 :   u32 drop_inline = ~0;
     630             : 
     631           0 :   clib_memset (hdr, 0, hdr_sz);
     632             : 
     633           0 :   vring->total_packets++;
     634             : 
     635           0 :   if (b->flags & VNET_BUFFER_F_GSO)
     636             :     {
     637           0 :       if (do_gso)
     638           0 :         set_gso_offsets (b, hdr, is_l2);
     639             :       else
     640             :         {
     641           0 :           drop_inline = VIRTIO_TX_ERROR_GSO_PACKET_DROP;
     642           0 :           goto done;
     643             :         }
     644             :     }
     645           0 :   else if (b->flags & VNET_BUFFER_F_OFFLOAD)
     646             :     {
     647           0 :       if (csum_offload)
     648           0 :         set_checksum_offsets (b, hdr, is_l2);
     649             :       else
     650             :         {
     651           0 :           drop_inline = VIRTIO_TX_ERROR_CSUM_OFFLOAD_PACKET_DROP;
     652           0 :           goto done;
     653             :         }
     654             :     }
     655           0 :   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
     656             :     {
     657           0 :       virtio_tx_trace (vm, node, b, bi, is_tun);
     658             :     }
     659             : 
     660           0 :   if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0))
     661             :     {
     662           0 :       d->addr =
     663           0 :         ((is_pci) ? vlib_buffer_get_current_pa (vm,
     664           0 :                                                 b) :
     665           0 :          pointer_to_uword (vlib_buffer_get_current (b))) - hdr_sz;
     666           0 :       d->len = b->current_length + hdr_sz;
     667             :     }
     668           0 :   else if (is_indirect)
     669             :     {
     670             :       /*
     671             :        * We are using single vlib_buffer_t for indirect descriptor(s)
     672             :        * chain. Single descriptor is 16 bytes and vlib_buffer_t
     673             :        * has 2048 bytes space. So maximum long chain can have 128
     674             :        * (=2048/16) indirect descriptors.
     675             :        * It can easily support 65535 bytes of Jumbo frames with
     676             :        * each data buffer size of 512 bytes minimum.
     677             :        */
     678           0 :       u32 indirect_buffer = 0;
     679           0 :       if (PREDICT_FALSE (vlib_buffer_alloc (vm, &indirect_buffer, 1) == 0))
     680             :         {
     681           0 :           drop_inline = VIRTIO_TX_ERROR_INDIRECT_DESC_ALLOC_FAILED;
     682           0 :           goto done;
     683             :         }
     684             : 
     685           0 :       vlib_buffer_t *indirect_desc = vlib_get_buffer (vm, indirect_buffer);
     686           0 :       indirect_desc->current_data = 0;
     687           0 :       indirect_desc->flags |= VLIB_BUFFER_NEXT_PRESENT;
     688           0 :       indirect_desc->next_buffer = bi;
     689           0 :       bi = indirect_buffer;
     690             : 
     691             :       vnet_virtio_vring_packed_desc_t *id =
     692           0 :         (vnet_virtio_vring_packed_desc_t *) vlib_buffer_get_current (
     693             :           indirect_desc);
     694           0 :       u32 count = 1;
     695           0 :       if (is_pci)
     696             :         {
     697           0 :           d->addr = vlib_physmem_get_pa (vm, id);
     698           0 :           id->addr = vlib_buffer_get_current_pa (vm, b) - hdr_sz;
     699             : 
     700             :           /*
     701             :            * If VIRTIO_F_ANY_LAYOUT is not negotiated, then virtio_net_hdr
     702             :            * should be presented in separate descriptor and data will start
     703             :            * from next descriptor.
     704             :            */
     705           0 :           if (is_any_layout)
     706           0 :             id->len = b->current_length + hdr_sz;
     707             :           else
     708             :             {
     709           0 :               id->len = hdr_sz;
     710           0 :               id->flags = 0;
     711           0 :               id->id = 0;
     712           0 :               count++;
     713           0 :               id++;
     714           0 :               id->addr = vlib_buffer_get_current_pa (vm, b);
     715           0 :               id->len = b->current_length;
     716             :             }
     717           0 :           while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     718             :             {
     719           0 :               id->flags = 0;
     720           0 :               id->id = 0;
     721           0 :               count++;
     722           0 :               id++;
     723           0 :               b = vlib_get_buffer (vm, b->next_buffer);
     724           0 :               id->addr = vlib_buffer_get_current_pa (vm, b);
     725           0 :               id->len = b->current_length;
     726           0 :               if (PREDICT_FALSE (count == VIRTIO_TX_MAX_CHAIN_LEN))
     727             :                 {
     728           0 :                   if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     729           0 :                     vlib_error_count (vm, node->node_index,
     730             :                                       VIRTIO_TX_ERROR_TRUNC_PACKET, 1);
     731           0 :                   break;
     732             :                 }
     733             :             }
     734             :         }
     735           0 :       id->flags = 0;
     736           0 :       id->id = 0;
     737           0 :       d->len = count * sizeof (vnet_virtio_vring_packed_desc_t);
     738           0 :       flags = VRING_DESC_F_INDIRECT;
     739             :     }
     740             :   else
     741             :     {
     742           0 :       ASSERT (0);
     743             :     }
     744           0 :   if (vring->avail_wrap_counter)
     745             :     {
     746           0 :       flags |= VRING_DESC_F_AVAIL;
     747           0 :       flags &= ~VRING_DESC_F_USED;
     748             :     }
     749             :   else
     750             :     {
     751           0 :       flags &= ~VRING_DESC_F_AVAIL;
     752           0 :       flags |= VRING_DESC_F_USED;
     753             :     }
     754             : 
     755           0 :   d->id = next;
     756           0 :   d->flags = flags;
     757           0 :   vring->buffers[next] = bi;
     758           0 :   n_added++;
     759             : 
     760           0 : done:
     761           0 :   if (drop_inline != ~0)
     762           0 :     virtio_interface_drop_inline (vm, vif, node->node_index, &bi, 1,
     763             :                                   drop_inline);
     764             : 
     765           0 :   return n_added;
     766             : }
     767             : 
     768             : static uword
     769           0 : virtio_interface_tx_packed_gso_inline (
     770             :   vlib_main_t *vm, vlib_node_runtime_t *node, virtio_if_t *vif,
     771             :   virtio_if_type_t type, vnet_virtio_vring_t *vring, u32 *buffers, u16 n_left,
     772             :   const int do_gso, const int csum_offload)
     773             : {
     774           0 :   int is_pci = (type == VIRTIO_IF_TYPE_PCI);
     775           0 :   int is_tun = (type == VIRTIO_IF_TYPE_TUN);
     776           0 :   int is_indirect =
     777           0 :     ((vif->features & VIRTIO_FEATURE (VIRTIO_RING_F_INDIRECT_DESC)) != 0);
     778           0 :   int is_any_layout =
     779           0 :     ((vif->features & VIRTIO_FEATURE (VIRTIO_F_ANY_LAYOUT)) != 0);
     780           0 :   const int hdr_sz = vif->virtio_net_hdr_sz;
     781           0 :   u16 sz = vring->queue_size;
     782           0 :   u16 used, next, n_buffers = 0, n_buffers_left = 0;
     783           0 :   u16 n_vectors = n_left;
     784             : 
     785             : 
     786           0 :   used = vring->desc_in_use;
     787           0 :   next = vring->desc_next;
     788             : 
     789           0 :   if (vif->packet_buffering)
     790             :     {
     791           0 :       n_buffers = n_buffers_left = virtio_vring_n_buffers (vring->buffering);
     792             : 
     793           0 :       while (n_buffers_left && used < sz)
     794             :         {
     795           0 :           u16 n_added = 0;
     796             : 
     797           0 :           u32 bi = virtio_vring_buffering_read_from_front (vring->buffering);
     798           0 :           if (bi == ~0)
     799           0 :             break;
     800           0 :           n_added = add_buffer_to_slot_packed (
     801             :             vm, node, vif, vring, bi, next, hdr_sz, do_gso, csum_offload,
     802             :             is_pci, is_tun, is_indirect, is_any_layout);
     803           0 :           n_buffers_left--;
     804           0 :           if (PREDICT_FALSE (n_added == 0))
     805           0 :             continue;
     806             : 
     807           0 :           used++;
     808           0 :           next++;
     809           0 :           if (next >= sz)
     810             :             {
     811           0 :               next = 0;
     812           0 :               vring->avail_wrap_counter ^= 1;
     813             :             }
     814             :         }
     815           0 :       virtio_txq_clear_scheduled (vring);
     816             :     }
     817             : 
     818           0 :   while (n_left && used < sz)
     819             :     {
     820           0 :       u16 n_added = 0;
     821             : 
     822           0 :       n_added = add_buffer_to_slot_packed (
     823             :         vm, node, vif, vring, buffers[0], next, hdr_sz, do_gso, csum_offload,
     824             :         is_pci, is_tun, is_indirect, is_any_layout);
     825           0 :       buffers++;
     826           0 :       n_left--;
     827           0 :       if (PREDICT_FALSE (n_added == 0))
     828           0 :         continue;
     829             : 
     830           0 :       used++;
     831           0 :       next++;
     832           0 :       if (next >= sz)
     833             :         {
     834           0 :           next = 0;
     835           0 :           vring->avail_wrap_counter ^= 1;
     836             :         }
     837             :     }
     838             : 
     839           0 :   if (n_left != n_vectors || n_buffers != n_buffers_left)
     840             :     {
     841           0 :       CLIB_MEMORY_STORE_BARRIER ();
     842           0 :       vring->desc_next = next;
     843           0 :       vring->desc_in_use = used;
     844           0 :       CLIB_MEMORY_BARRIER ();
     845           0 :       if (vring->device_event->flags != VRING_EVENT_F_DISABLE)
     846           0 :         virtio_kick (vm, vring, vif);
     847             :     }
     848             : 
     849           0 :   return n_left;
     850             : }
     851             : 
     852             : static void
     853           0 : virtio_find_free_desc (vnet_virtio_vring_t *vring, u16 size, u16 mask, u16 req,
     854             :                        u16 next, u32 *first_free_desc_index,
     855             :                        u16 *free_desc_count)
     856             : {
     857           0 :   u16 start = 0;
     858             :   /* next is used as hint: from where to start looking */
     859           0 :   for (u16 i = 0; i < size; i++, next++)
     860             :     {
     861           0 :       if (vring->buffers[next & mask] == ~0)
     862             :         {
     863           0 :           if (*first_free_desc_index == ~0)
     864             :             {
     865           0 :               *first_free_desc_index = (next & mask);
     866           0 :               start = i;
     867           0 :               (*free_desc_count)++;
     868           0 :               req--;
     869           0 :               if (req == 0)
     870           0 :                 break;
     871             :             }
     872             :           else
     873             :             {
     874           0 :               if (start + *free_desc_count == i)
     875             :                 {
     876           0 :                   (*free_desc_count)++;
     877           0 :                   req--;
     878           0 :                   if (req == 0)
     879           0 :                     break;
     880             :                 }
     881             :               else
     882           0 :                 break;
     883             :             }
     884             :         }
     885             :     }
     886           0 : }
     887             : 
     888             : static u16
     889     1245470 : virtio_interface_tx_split_gso_inline (vlib_main_t *vm,
     890             :                                       vlib_node_runtime_t *node,
     891             :                                       virtio_if_t *vif, virtio_if_type_t type,
     892             :                                       vnet_virtio_vring_t *vring, u32 *buffers,
     893             :                                       u16 n_left, int do_gso, int csum_offload)
     894             : {
     895     1245470 :   u16 used, next, avail, n_buffers = 0, n_buffers_left = 0;
     896     1245470 :   int is_pci = (type == VIRTIO_IF_TYPE_PCI);
     897     1245470 :   int is_tun = (type == VIRTIO_IF_TYPE_TUN);
     898     1245470 :   int is_indirect =
     899     1245470 :     ((vif->features & VIRTIO_FEATURE (VIRTIO_RING_F_INDIRECT_DESC)) != 0);
     900     1245470 :   int is_any_layout =
     901     1245470 :     ((vif->features & VIRTIO_FEATURE (VIRTIO_F_ANY_LAYOUT)) != 0);
     902     1245470 :   u16 sz = vring->queue_size;
     903     1245470 :   int hdr_sz = vif->virtio_net_hdr_sz;
     904     1245470 :   u16 mask = sz - 1;
     905     1245470 :   u16 n_vectors = n_left;
     906             : 
     907     1245470 :   used = vring->desc_in_use;
     908     1245470 :   next = vring->desc_next;
     909     1245470 :   avail = vring->avail->idx;
     910             : 
     911     1245470 :   u16 free_desc_count = 0;
     912             : 
     913     1245470 :   if (PREDICT_FALSE (vring->flags & VRING_TX_OUT_OF_ORDER))
     914             :     {
     915           0 :       u32 first_free_desc_index = ~0;
     916             : 
     917           0 :       virtio_find_free_desc (vring, sz, mask, n_left, next,
     918             :                              &first_free_desc_index, &free_desc_count);
     919             : 
     920           0 :       if (free_desc_count)
     921           0 :         next = first_free_desc_index;
     922             :     }
     923             :   else
     924     1245470 :     free_desc_count = sz - used;
     925             : 
     926     1245470 :   if (vif->packet_buffering)
     927             :     {
     928           0 :       n_buffers = n_buffers_left = virtio_vring_n_buffers (vring->buffering);
     929             : 
     930           0 :       while (n_buffers_left && free_desc_count)
     931             :         {
     932           0 :           u16 n_added = 0;
     933             : 
     934           0 :           u32 bi = virtio_vring_buffering_read_from_front (vring->buffering);
     935           0 :           if (bi == ~0)
     936           0 :             break;
     937             : 
     938           0 :           n_added = add_buffer_to_slot (vm, node, vif, vring, bi,
     939             :                                         free_desc_count, avail, next, mask,
     940             :                                         hdr_sz, do_gso, csum_offload, is_pci,
     941             :                                         is_tun, is_indirect, is_any_layout);
     942           0 :           if (PREDICT_FALSE (n_added == 0))
     943             :             {
     944           0 :               n_buffers_left--;
     945           0 :               continue;
     946             :             }
     947           0 :           else if (PREDICT_FALSE (n_added > free_desc_count))
     948           0 :             break;
     949             : 
     950           0 :           avail++;
     951           0 :           next = (next + n_added) & mask;
     952           0 :           used += n_added;
     953           0 :           n_buffers_left--;
     954           0 :           free_desc_count -= n_added;
     955             :         }
     956           0 :       virtio_txq_clear_scheduled (vring);
     957             :     }
     958             : 
     959    36614700 :   while (n_left && free_desc_count)
     960             :     {
     961    35369200 :       u16 n_added = 0;
     962             : 
     963             :       n_added =
     964    35369200 :         add_buffer_to_slot (vm, node, vif, vring, buffers[0], free_desc_count,
     965             :                             avail, next, mask, hdr_sz, do_gso, csum_offload,
     966             :                             is_pci, is_tun, is_indirect, is_any_layout);
     967             : 
     968    35369200 :       if (PREDICT_FALSE (n_added == 0))
     969             :         {
     970           0 :           buffers++;
     971           0 :           n_left--;
     972           0 :           continue;
     973             :         }
     974    35369200 :       else if (PREDICT_FALSE (n_added > free_desc_count))
     975           0 :         break;
     976             : 
     977    35369200 :       avail++;
     978    35369200 :       next = (next + n_added) & mask;
     979    35369200 :       used += n_added;
     980    35369200 :       buffers++;
     981    35369200 :       n_left--;
     982    35369200 :       free_desc_count -= n_added;
     983             :     }
     984             : 
     985     1245470 :   if (n_left != n_vectors || n_buffers != n_buffers_left)
     986             :     {
     987     1243440 :       clib_atomic_store_seq_cst (&vring->avail->idx, avail);
     988     1243440 :       vring->desc_next = next;
     989     1243440 :       vring->desc_in_use = used;
     990     1243440 :       if ((clib_atomic_load_seq_cst (&vring->used->flags) &
     991             :            VRING_USED_F_NO_NOTIFY) == 0)
     992      962817 :         virtio_kick (vm, vring, vif);
     993             :     }
     994             : 
     995     1245470 :   return n_left;
     996             : }
     997             : 
     998             : static u16
     999     1245470 : virtio_interface_tx_gso_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
    1000             :                                 virtio_if_t *vif, virtio_if_type_t type,
    1001             :                                 vnet_virtio_vring_t *vring, u32 *buffers,
    1002             :                                 u16 n_left, int packed, int do_gso,
    1003             :                                 int csum_offload)
    1004             : {
    1005     1245470 :   if (packed)
    1006           0 :     return virtio_interface_tx_packed_gso_inline (vm, node, vif, type, vring,
    1007             :                                                   buffers, n_left,
    1008             :                                                   do_gso, csum_offload);
    1009             :   else
    1010     1245470 :     return virtio_interface_tx_split_gso_inline (vm, node, vif, type, vring,
    1011             :                                                  buffers, n_left,
    1012             :                                                  do_gso, csum_offload);
    1013             : }
    1014             : 
    1015             : static u16
    1016     1245470 : virtio_interface_tx_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
    1017             :                             virtio_if_t *vif, vnet_virtio_vring_t *vring,
    1018             :                             virtio_if_type_t type, u32 *buffers, u16 n_left,
    1019             :                             int packed)
    1020             : {
    1021     1245470 :   vnet_main_t *vnm = vnet_get_main ();
    1022     1245470 :   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, vif->hw_if_index);
    1023             : 
    1024     1245470 :   if (hw->caps & VNET_HW_IF_CAP_TCP_GSO)
    1025      813648 :     return virtio_interface_tx_gso_inline (vm, node, vif, type, vring,
    1026             :                                            buffers, n_left, packed,
    1027             :                                            1 /* do_gso */ ,
    1028             :                                            1 /* checksum offload */ );
    1029      431820 :   else if (hw->caps & VNET_HW_IF_CAP_L4_TX_CKSUM)
    1030           0 :     return virtio_interface_tx_gso_inline (vm, node, vif, type, vring,
    1031             :                                            buffers, n_left, packed,
    1032             :                                            0 /* no do_gso */ ,
    1033             :                                            1 /* checksum offload */ );
    1034             :   else
    1035      431820 :     return virtio_interface_tx_gso_inline (vm, node, vif, type, vring,
    1036             :                                            buffers, n_left, packed,
    1037             :                                            0 /* no do_gso */ ,
    1038             :                                            0 /* no checksum offload */ );
    1039             : }
    1040             : 
    1041     1245276 : VNET_DEVICE_CLASS_TX_FN (virtio_device_class) (vlib_main_t * vm,
    1042             :                                                vlib_node_runtime_t * node,
    1043             :                                                vlib_frame_t * frame)
    1044     1243040 : {
    1045     1243040 :   virtio_main_t *nm = &virtio_main;
    1046     1243040 :   vnet_interface_output_runtime_t *rund = (void *) node->runtime_data;
    1047     1243040 :   virtio_if_t *vif = pool_elt_at_index (nm->interfaces, rund->dev_instance);
    1048     1243040 :   vnet_hw_if_tx_frame_t *tf = vlib_frame_scalar_args (frame);
    1049     1243040 :   u16 qid = tf->queue_id;
    1050     1243040 :   vnet_virtio_vring_t *vring = vec_elt_at_index (vif->txq_vrings, qid);
    1051     1243040 :   u16 n_left = frame->n_vectors;
    1052     1243040 :   u32 *buffers = vlib_frame_vector_args (frame);
    1053     1243040 :   u32 to[GRO_TO_VECTOR_SIZE (n_left)];
    1054     1243040 :   int packed = vif->is_packed;
    1055     1243040 :   u16 n_vectors = frame->n_vectors;
    1056             : 
    1057     1243040 :   if (tf->shared_queue)
    1058           0 :     clib_spinlock_lock (&vring->lockp);
    1059             : 
    1060     1243040 :   if (vif->packet_coalesce)
    1061             :     {
    1062      517686 :       n_vectors = n_left =
    1063      517686 :         vnet_gro_inline (vm, vring->flow_table, buffers, n_left, to);
    1064      517686 :       buffers = to;
    1065      517686 :       virtio_txq_clear_scheduled (vring);
    1066             :     }
    1067             : 
    1068     1243040 :   u16 retry_count = 2;
    1069             : 
    1070     1245470 : retry:
    1071             :   /* free consumed buffers */
    1072     1245470 :   virtio_free_used_device_desc (vm, vring, node->node_index, packed);
    1073             : 
    1074     1245470 :   if (vif->type == VIRTIO_IF_TYPE_TAP)
    1075     1086710 :     n_left = virtio_interface_tx_inline (vm, node, vif, vring,
    1076             :                                          VIRTIO_IF_TYPE_TAP,
    1077     1086710 :                                          &buffers[n_vectors - n_left],
    1078             :                                          n_left, packed);
    1079      158760 :   else if (vif->type == VIRTIO_IF_TYPE_PCI)
    1080           0 :     n_left = virtio_interface_tx_inline (vm, node, vif, vring,
    1081             :                                          VIRTIO_IF_TYPE_PCI,
    1082           0 :                                          &buffers[n_vectors - n_left],
    1083             :                                          n_left, packed);
    1084      158760 :   else if (vif->type == VIRTIO_IF_TYPE_TUN)
    1085      158760 :     n_left = virtio_interface_tx_inline (vm, node, vif, vring,
    1086             :                                          VIRTIO_IF_TYPE_TUN,
    1087      158760 :                                          &buffers[n_vectors - n_left],
    1088             :                                          n_left, packed);
    1089             :   else
    1090           0 :     ASSERT (0);
    1091             : 
    1092     1245470 :   if (n_left && retry_count--)
    1093        2432 :     goto retry;
    1094             : 
    1095     1243040 :   if (vif->packet_buffering && n_left)
    1096             :     {
    1097           0 :       u16 n_buffered = virtio_vring_buffering_store_packets (vring->buffering,
    1098             :                                                              &buffers
    1099           0 :                                                              [n_vectors
    1100           0 :                                                               - n_left],
    1101             :                                                              n_left);
    1102           0 :       n_left -= n_buffered;
    1103             :     }
    1104     1243040 :   if (n_left)
    1105         757 :     virtio_interface_drop_inline (vm, vif, node->node_index,
    1106         757 :                                   &buffers[n_vectors - n_left], n_left,
    1107             :                                   VIRTIO_TX_ERROR_NO_FREE_SLOTS);
    1108             : 
    1109     1243040 :   if (tf->shared_queue)
    1110           0 :     clib_spinlock_unlock (&vring->lockp);
    1111             : 
    1112     1243040 :   return frame->n_vectors - n_left;
    1113             : }
    1114             : 
    1115             : static void
    1116           0 : virtio_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
    1117             :                                 u32 node_index)
    1118             : {
    1119           0 :   virtio_main_t *apm = &virtio_main;
    1120           0 :   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
    1121           0 :   virtio_if_t *vif = pool_elt_at_index (apm->interfaces, hw->dev_instance);
    1122             : 
    1123             :   /* Shut off redirection */
    1124           0 :   if (node_index == ~0)
    1125             :     {
    1126           0 :       vif->per_interface_next_index = node_index;
    1127           0 :       return;
    1128             :     }
    1129             : 
    1130           0 :   vif->per_interface_next_index =
    1131           0 :     vlib_node_add_next (vlib_get_main (), virtio_input_node.index,
    1132             :                         node_index);
    1133             : }
    1134             : 
    1135             : static void
    1136           0 : virtio_clear_hw_interface_counters (u32 instance)
    1137             : {
    1138             :   /* Nothing for now */
    1139           0 : }
    1140             : 
    1141             : static void
    1142           0 : virtio_set_rx_interrupt (virtio_if_t *vif, vnet_virtio_vring_t *vring)
    1143             : {
    1144           0 :   if (vif->is_packed)
    1145           0 :     vring->driver_event->flags &= ~VRING_EVENT_F_DISABLE;
    1146             :   else
    1147           0 :     vring->avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
    1148           0 : }
    1149             : 
    1150             : static void
    1151           0 : virtio_set_rx_polling (virtio_if_t *vif, vnet_virtio_vring_t *vring)
    1152             : {
    1153           0 :   if (vif->is_packed)
    1154           0 :     vring->driver_event->flags |= VRING_EVENT_F_DISABLE;
    1155             :   else
    1156           0 :     vring->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
    1157           0 : }
    1158             : 
    1159             : static clib_error_t *
    1160           0 : virtio_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid,
    1161             :                                  vnet_hw_if_rx_mode mode)
    1162             : {
    1163           0 :   virtio_main_t *mm = &virtio_main;
    1164           0 :   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
    1165           0 :   virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
    1166           0 :   vnet_virtio_vring_t *rx_vring = vec_elt_at_index (vif->rxq_vrings, qid);
    1167             : 
    1168           0 :   if (vif->type == VIRTIO_IF_TYPE_PCI && !(vif->support_int_mode))
    1169             :     {
    1170           0 :       virtio_set_rx_polling (vif, rx_vring);
    1171           0 :       return clib_error_return (0, "interrupt mode is not supported");
    1172             :     }
    1173             : 
    1174           0 :   if (mode == VNET_HW_IF_RX_MODE_POLLING)
    1175           0 :       virtio_set_rx_polling (vif, rx_vring);
    1176             :   else
    1177           0 :       virtio_set_rx_interrupt (vif, rx_vring);
    1178             : 
    1179           0 :   rx_vring->mode = mode;
    1180             : 
    1181           0 :   return 0;
    1182             : }
    1183             : 
    1184             : static clib_error_t *
    1185         600 : virtio_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
    1186             : {
    1187         600 :   virtio_main_t *mm = &virtio_main;
    1188         600 :   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
    1189         600 :   virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
    1190             : 
    1191         600 :   if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
    1192             :     {
    1193         300 :       vif->flags |= VIRTIO_IF_FLAG_ADMIN_UP;
    1194         300 :       vnet_hw_interface_set_flags (vnm, vif->hw_if_index,
    1195             :                                    VNET_HW_INTERFACE_FLAG_LINK_UP);
    1196             :     }
    1197             :   else
    1198             :     {
    1199         300 :       vif->flags &= ~VIRTIO_IF_FLAG_ADMIN_UP;
    1200         300 :       vnet_hw_interface_set_flags (vnm, vif->hw_if_index, 0);
    1201             :     }
    1202         600 :   return 0;
    1203             : }
    1204             : 
    1205             : static clib_error_t *
    1206           0 : virtio_subif_add_del_function (vnet_main_t * vnm,
    1207             :                                u32 hw_if_index,
    1208             :                                struct vnet_sw_interface_t *st, int is_add)
    1209             : {
    1210             :   /* Nothing for now */
    1211           0 :   return 0;
    1212             : }
    1213             : 
    1214             : /* *INDENT-OFF* */
    1215       11199 : VNET_DEVICE_CLASS (virtio_device_class) = {
    1216             :   .name = "virtio",
    1217             :   .format_device_name = format_virtio_device_name,
    1218             :   .format_device = format_virtio_device,
    1219             :   .format_tx_trace = format_virtio_tx_trace,
    1220             :   .tx_function_n_errors = VIRTIO_TX_N_ERROR,
    1221             :   .tx_function_error_strings = virtio_tx_func_error_strings,
    1222             :   .rx_redirect_to_node = virtio_set_interface_next_node,
    1223             :   .clear_counters = virtio_clear_hw_interface_counters,
    1224             :   .admin_up_down_function = virtio_interface_admin_up_down,
    1225             :   .subif_add_del_function = virtio_subif_add_del_function,
    1226             :   .rx_mode_change_function = virtio_interface_rx_mode_change,
    1227             : };
    1228             : 
    1229             : /* *INDENT-ON* */
    1230             : 
    1231             : /*
    1232             :  * fd.io coding-style-patch-verification: ON
    1233             :  *
    1234             :  * Local Variables:
    1235             :  * eval: (c-set-style "gnu")
    1236             :  * End:
    1237             :  */

Generated by: LCOV version 1.14