From 8389fb9112bcf96def69539fa1de13a7a08923f5 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Fri, 13 Oct 2017 18:29:53 +0200 Subject: virtio: fast TAP interfaces with vhost-net backend Change-Id: Ided667356d5c6fb9648eb34685aabd6b16a598b7 Signed-off-by: Damjan Marion Signed-off-by: Steven Luong --- src/vnet/devices/virtio/device.c | 328 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 src/vnet/devices/virtio/device.c (limited to 'src/vnet/devices/virtio/device.c') diff --git a/src/vnet/devices/virtio/device.c b/src/vnet/devices/virtio/device.c new file mode 100644 index 00000000000..275a3c74990 --- /dev/null +++ b/src/vnet/devices/virtio/device.c @@ -0,0 +1,328 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define foreach_virtio_tx_func_error \ +_(NO_FREE_SLOTS, "no free tx slots") \ +_(TRUNC_PACKET, "packet > buffer size -- truncated in tx ring") \ +_(PENDING_MSGS, "pending msgs in tx ring") \ +_(NO_TX_QUEUES, "no tx queues") + +typedef enum +{ +#define _(f,s) TAP_TX_ERROR_##f, + foreach_virtio_tx_func_error +#undef _ + TAP_TX_N_ERROR, +} virtio_tx_func_error_t; + +static char *virtio_tx_func_error_strings[] = { +#define _(n,s) s, + foreach_virtio_tx_func_error +#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-%s", vif->name); + } + else + s = format (s, "virtio%lu", vif->dev_instance); + + return s; +} + +static u8 * +format_virtio_device (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + int verbose = va_arg (*args, int); + u32 indent = format_get_indent (s); + + s = format (s, "VIRTIO interface"); + if (verbose) + { + s = format (s, "\n%U instance %u", format_white_space, indent + 2, + dev_instance); + } + return s; +} + +static u8 * +format_virtio_tx_trace (u8 * s, va_list * args) +{ + s = format (s, "Unimplemented..."); + return s; +} + +static_always_inline void +virtio_free_used_desc (vlib_main_t * vm, virtio_vring_t * vring) +{ + u16 used = vring->desc_in_use; + u16 sz = vring->size; + u16 mask = sz - 1; + u16 last = vring->last_used_idx; + u16 n_left = vring->used->idx - last; + + if (n_left == 0) + return; + + while (n_left) + { + struct vring_used_elem *e = &vring->used->ring[last & mask]; + u16 slot = e->id; + struct vring_desc *d = &vring->desc[slot]; + + if (PREDICT_FALSE (d->flags & VRING_DESC_F_INDIRECT)) + { + d = uword_to_pointer (d->addr, struct vring_desc *); + vec_free (d); + } + + 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 u16 +add_buffer_to_slot (vlib_main_t * vm, virtio_vring_t * vring, u32 bi, + u16 avail, u16 next, u16 mask) +{ + u16 n_added = 0; + const int hdr_sz = sizeof (struct virtio_net_hdr_v1); + struct vring_desc *d; + d = &vring->desc[next]; + vlib_buffer_t *b = vlib_get_buffer (vm, bi); + + if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0)) + { + d->addr = pointer_to_uword (vlib_buffer_get_current (b)) - hdr_sz; + d->len = b->current_length + hdr_sz; + d->flags = 0; + } + else + { + struct vring_desc *id, *descs = 0; + + /* first buffer in chain */ + vec_add2_aligned (descs, id, 1, CLIB_CACHE_LINE_BYTES); + id->addr = pointer_to_uword (vlib_buffer_get_current (b)) - hdr_sz; + id->len = b->current_length + hdr_sz; + + while (b->flags & VLIB_BUFFER_NEXT_PRESENT) + { + id->flags = VRING_DESC_F_NEXT; + id->next = vec_len (descs); + vec_add2_aligned (descs, id, 1, CLIB_CACHE_LINE_BYTES); + b = vlib_get_buffer (vm, b->next_buffer); + id->addr = pointer_to_uword (vlib_buffer_get_current (b)); + id->len = b->current_length; + } + + d->addr = pointer_to_uword (descs); + d->len = vec_len (descs) * sizeof (struct vring_desc); + d->flags = VRING_DESC_F_INDIRECT; + } + vring->buffers[next] = bi; + vring->avail->ring[avail & mask] = next; + n_added++; + return n_added; +} + + +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) +{ + u8 qid = 0; + u16 n_left = frame->n_vectors; + virtio_vring_t *vring = vec_elt_at_index (vif->vrings, (qid << 1) + 1); + u16 used, next, avail; + u16 sz = vring->size; + u16 mask = sz - 1; + u32 *buffers = vlib_frame_args (frame); + + /* free consumed buffers */ + virtio_free_used_desc (vm, vring); + + used = vring->desc_in_use; + next = vring->desc_next; + avail = vring->avail->idx; + + while (n_left && used < sz) + { + u16 n_added; + n_added = add_buffer_to_slot (vm, vring, buffers[0], avail, next, mask); + avail += n_added; + next = (next + n_added) & mask; + used += n_added; + buffers++; + n_left--; + } + + if (n_left != frame->n_vectors) + { + CLIB_MEMORY_STORE_BARRIER (); + vring->avail->idx = avail; + vring->desc_next = next; + vring->desc_in_use = used; + if ((vring->used->flags & VIRTIO_RING_FLAG_MASK_INT) == 0) + { + u64 x = 1; + CLIB_UNUSED (int r) = write (vring->kick_fd, &x, sizeof (x)); + } + } + + + if (n_left) + { + vlib_error_count (vm, node->node_index, TAP_TX_ERROR_NO_FREE_SLOTS, + n_left); + vlib_buffer_free (vm, buffers, n_left); + } + + return frame->n_vectors - n_left; +} + +static uword +virtio_interface_tx (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + 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); +} + +static void +virtio_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index, + u32 node_index) +{ + virtio_main_t *apm = &virtio_main; + vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); + virtio_if_t *vif = pool_elt_at_index (apm->interfaces, hw->dev_instance); + + /* Shut off redirection */ + if (node_index == ~0) + { + vif->per_interface_next_index = node_index; + return; + } + + vif->per_interface_next_index = + vlib_node_add_next (vlib_get_main (), virtio_input_node.index, + node_index); +} + +static void +virtio_clear_hw_interface_counters (u32 instance) +{ + /* Nothing for now */ +} + +static clib_error_t * +virtio_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid, + vnet_hw_interface_rx_mode mode) +{ + 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); + + if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING) + vring->avail->flags |= VIRTIO_RING_FLAG_MASK_INT; + else + vring->avail->flags &= ~VIRTIO_RING_FLAG_MASK_INT; + + return 0; +} + +static clib_error_t * +virtio_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) +{ + 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); + static clib_error_t *error = 0; + + if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) + vif->flags |= VIRTIO_IF_FLAG_ADMIN_UP; + else + vif->flags &= ~VIRTIO_IF_FLAG_ADMIN_UP; + + return error; + return 0; +} + +static clib_error_t * +virtio_subif_add_del_function (vnet_main_t * vnm, + u32 hw_if_index, + struct vnet_sw_interface_t *st, int is_add) +{ + /* Nothing for now */ + return 0; +} + +/* *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, + .tx_function_n_errors = TAP_TX_N_ERROR, + .tx_function_error_strings = virtio_tx_func_error_strings, + .rx_redirect_to_node = virtio_set_interface_next_node, + .clear_counters = virtio_clear_hw_interface_counters, + .admin_up_down_function = virtio_interface_admin_up_down, + .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* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg