X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Fdevices%2Fvirtio%2Fdevice.c;h=ecc8b8c1aa79d4e4f7203c746a440bc775df70bc;hb=6d4af8918;hp=4eeb2caf8a1ed36c700b64bd87d2846bb3d3fc66;hpb=d6c15af33f3f153e084f14484e884f3ca68dbc23;p=vpp.git diff --git a/src/vnet/devices/virtio/device.c b/src/vnet/devices/virtio/device.c index 4eeb2caf8a1..ecc8b8c1aa7 100644 --- a/src/vnet/devices/virtio/device.c +++ b/src/vnet/devices/virtio/device.c @@ -18,14 +18,15 @@ #include #include #include -#include -#include #include #include #include +#include #include #include +#include +#include #include #define foreach_virtio_tx_func_error \ @@ -48,25 +49,6 @@ static char *virtio_tx_func_error_strings[] = { #undef _ }; -u8 * -format_virtio_device_name (u8 * s, va_list * args) -{ - u32 dev_instance = va_arg (*args, u32); - virtio_main_t *mm = &virtio_main; - virtio_if_t *vif = pool_elt_at_index (mm->interfaces, dev_instance); - - if (vif->type == VIRTIO_IF_TYPE_TAP) - s = format (s, "tap%u", vif->id); - else if (vif->type == VIRTIO_IF_TYPE_PCI) - s = format (s, "virtio-%x/%x/%x/%x", vif->pci_addr.domain, - vif->pci_addr.bus, vif->pci_addr.slot, - vif->pci_addr.function); - else - s = format (s, "virtio-%lu", vif->dev_instance); - - return s; -} - static u8 * format_virtio_device (u8 * s, va_list * args) { @@ -90,8 +72,8 @@ format_virtio_tx_trace (u8 * s, va_list * args) return s; } -inline void -virtio_free_used_desc (vlib_main_t * vm, virtio_vring_t * vring) +static_always_inline void +virtio_free_used_device_desc (vlib_main_t * vm, virtio_vring_t * vring) { u16 used = vring->desc_in_use; u16 sz = vring->size; @@ -105,21 +87,76 @@ virtio_free_used_desc (vlib_main_t * vm, virtio_vring_t * vring) while (n_left) { struct vring_used_elem *e = &vring->used->ring[last & mask]; - u16 slot = e->id; + u16 slot, n_buffers; + slot = n_buffers = e->id; - vlib_buffer_free (vm, &vring->buffers[slot], 1); - used--; - last++; - n_left--; + while (e->id == n_buffers) + { + n_left--; + last++; + n_buffers++; + if (n_left == 0) + break; + e = &vring->used->ring[last & mask]; + } + vlib_buffer_free_from_ring (vm, vring->buffers, slot, + sz, (n_buffers - slot)); + used -= (n_buffers - slot); + + if (n_left > 0) + { + slot = e->id; + + vlib_buffer_free (vm, &vring->buffers[slot], 1); + used--; + last++; + n_left--; + } } vring->desc_in_use = used; vring->last_used_idx = last; } +static_always_inline void +set_checksum_offsets (vlib_main_t * vm, virtio_if_t * vif, vlib_buffer_t * b, + struct virtio_net_hdr_v1 *hdr) +{ + if (b->flags & VNET_BUFFER_F_IS_IP4) + { + ip4_header_t *ip4; + gso_header_offset_t gho = vnet_gso_header_offset_parser (b, 0); + hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + hdr->csum_start = gho.l4_hdr_offset; // 0x22; + if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM) + hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum); + else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM) + hdr->csum_offset = STRUCT_OFFSET_OF (udp_header_t, checksum); + + /* + * virtio devices do not support IP4 checksum offload. So driver takes care + * of it while doing tx. + */ + ip4 = + (ip4_header_t *) (vlib_buffer_get_current (b) + gho.l3_hdr_offset); + if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM) + ip4->checksum = ip4_header_checksum (ip4); + } + else if (b->flags & VNET_BUFFER_F_IS_IP6) + { + gso_header_offset_t gho = vnet_gso_header_offset_parser (b, 1); + hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + hdr->csum_start = gho.l4_hdr_offset; // 0x36; + if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM) + hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum); + else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM) + hdr->csum_offset = STRUCT_OFFSET_OF (udp_header_t, checksum); + } +} + static_always_inline u16 add_buffer_to_slot (vlib_main_t * vm, virtio_if_t * vif, virtio_vring_t * vring, u32 bi, u16 avail, u16 next, - u16 mask) + u16 mask, int do_gso, int csum_offload) { u16 n_added = 0; int hdr_sz = vif->virtio_net_hdr_sz; @@ -130,6 +167,46 @@ add_buffer_to_slot (vlib_main_t * vm, virtio_if_t * vif, clib_memset (hdr, 0, hdr_sz); + if (do_gso && (b->flags & VNET_BUFFER_F_GSO)) + { + if (b->flags & VNET_BUFFER_F_IS_IP4) + { + ip4_header_t *ip4; + gso_header_offset_t gho = vnet_gso_header_offset_parser (b, 0); + hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + hdr->gso_size = vnet_buffer2 (b)->gso_size; + hdr->hdr_len = gho.l4_hdr_offset + gho.l4_hdr_sz; + hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + hdr->csum_start = gho.l4_hdr_offset; // 0x22; + hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum); + ip4 = + (ip4_header_t *) (vlib_buffer_get_current (b) + + gho.l3_hdr_offset); + /* + * virtio devices do not support IP4 checksum offload. So driver takes care + * of it while doing tx. + */ + if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM) + ip4->checksum = ip4_header_checksum (ip4); + } + else if (b->flags & VNET_BUFFER_F_IS_IP6) + { + gso_header_offset_t gho = vnet_gso_header_offset_parser (b, 1); + hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + hdr->gso_size = vnet_buffer2 (b)->gso_size; + hdr->hdr_len = gho.l4_hdr_offset + gho.l4_hdr_sz; + hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + hdr->csum_start = gho.l4_hdr_offset; // 0x36; + hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum); + } + } + else if (csum_offload + && (b->flags & (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM | + VNET_BUFFER_F_OFFLOAD_UDP_CKSUM))) + { + set_checksum_offsets (vm, vif, b, hdr); + } + if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0)) { d->addr = @@ -149,9 +226,15 @@ add_buffer_to_slot (vlib_main_t * vm, virtio_if_t * vif, * It can easily support 65535 bytes of Jumbo frames with * each data buffer size of 512 bytes minimum. */ - vlib_buffer_t *indirect_desc = - vlib_get_buffer (vm, vring->indirect_buffers[next]); + u32 indirect_buffer = 0; + if (PREDICT_FALSE (vlib_buffer_alloc (vm, &indirect_buffer, 1) == 0)) + return n_added; + + vlib_buffer_t *indirect_desc = vlib_get_buffer (vm, indirect_buffer); indirect_desc->current_data = 0; + indirect_desc->flags |= VLIB_BUFFER_NEXT_PRESENT; + indirect_desc->next_buffer = bi; + bi = indirect_buffer; struct vring_desc *id = (struct vring_desc *) vlib_buffer_get_current (indirect_desc); @@ -221,24 +304,26 @@ add_buffer_to_slot (vlib_main_t * vm, virtio_if_t * vif, static_always_inline uword virtio_interface_tx_inline (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame, virtio_if_t * vif) + vlib_frame_t * frame, virtio_if_t * vif, + int do_gso, int csum_offload) { - u8 qid = 0; u16 n_left = frame->n_vectors; - virtio_vring_t *vring = vec_elt_at_index (vif->vrings, (qid << 1) + 1); + virtio_vring_t *vring; + u16 qid = vm->thread_index % vif->num_txqs; + vring = vec_elt_at_index (vif->txq_vrings, qid); u16 used, next, avail; u16 sz = vring->size; u16 mask = sz - 1; u32 *buffers = vlib_frame_vector_args (frame); - clib_spinlock_lock_if_init (&vif->lockp); + clib_spinlock_lock_if_init (&vring->lockp); if ((vring->used->flags & VIRTIO_RING_FLAG_MASK_INT) == 0 && (vring->last_kick_avail_idx != vring->avail->idx)) virtio_kick (vm, vring, vif); /* free consumed buffers */ - virtio_free_used_desc (vm, vring); + virtio_free_used_device_desc (vm, vring); used = vring->desc_in_use; next = vring->desc_next; @@ -248,7 +333,8 @@ virtio_interface_tx_inline (vlib_main_t * vm, vlib_node_runtime_t * node, { u16 n_added = 0; n_added = - add_buffer_to_slot (vm, vif, vring, buffers[0], avail, next, mask); + add_buffer_to_slot (vm, vif, vring, buffers[0], avail, next, mask, + do_gso, csum_offload); if (!n_added) break; avail += n_added; @@ -275,20 +361,30 @@ virtio_interface_tx_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_free (vm, buffers, n_left); } - clib_spinlock_unlock_if_init (&vif->lockp); + clib_spinlock_unlock_if_init (&vring->lockp); return frame->n_vectors - n_left; } -static uword -virtio_interface_tx (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) +VNET_DEVICE_CLASS_TX_FN (virtio_device_class) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) { + vnet_main_t *vnm = vnet_get_main (); virtio_main_t *nm = &virtio_main; vnet_interface_output_runtime_t *rund = (void *) node->runtime_data; virtio_if_t *vif = pool_elt_at_index (nm->interfaces, rund->dev_instance); - - return virtio_interface_tx_inline (vm, node, frame, vif); + vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, vif->hw_if_index); + + if (hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO) + return virtio_interface_tx_inline (vm, node, frame, vif, 1 /* do_gso */ , + 1); + else if (hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD) + return virtio_interface_tx_inline (vm, node, frame, vif, + 0 /* no do_gso */ , 1); + else + return virtio_interface_tx_inline (vm, node, frame, vif, + 0 /* no do_gso */ , 0); } static void @@ -324,7 +420,13 @@ virtio_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid, virtio_main_t *mm = &virtio_main; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance); - virtio_vring_t *vring = vec_elt_at_index (vif->vrings, qid); + virtio_vring_t *vring = vec_elt_at_index (vif->rxq_vrings, qid); + + if (vif->type == VIRTIO_IF_TYPE_PCI && !(vif->support_int_mode)) + { + vring->avail->flags |= VIRTIO_RING_FLAG_MASK_INT; + return clib_error_return (0, "interrupt mode is not supported"); + } if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING) vring->avail->flags |= VIRTIO_RING_FLAG_MASK_INT; @@ -361,7 +463,6 @@ virtio_subif_add_del_function (vnet_main_t * vnm, /* *INDENT-OFF* */ VNET_DEVICE_CLASS (virtio_device_class) = { .name = "virtio", - .tx_function = virtio_interface_tx, .format_device_name = format_virtio_device_name, .format_device = format_virtio_device, .format_tx_trace = format_virtio_tx_trace, @@ -373,9 +474,6 @@ VNET_DEVICE_CLASS (virtio_device_class) = { .subif_add_del_function = virtio_subif_add_del_function, .rx_mode_change_function = virtio_interface_rx_mode_change, }; - -VLIB_DEVICE_TX_FUNCTION_MULTIARCH(virtio_device_class, - virtio_interface_tx) /* *INDENT-ON* */ /*