diff options
Diffstat (limited to 'src/vnet/pg/stream.c')
-rw-r--r-- | src/vnet/pg/stream.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/src/vnet/pg/stream.c b/src/vnet/pg/stream.c new file mode 100644 index 00000000..1ed7189f --- /dev/null +++ b/src/vnet/pg/stream.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2015 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. + */ +/* + * pg_stream.c: packet generator streams + * + * Copyright (c) 2008 Eliot Dresselhaus + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/ip/ip.h> +#include <vnet/mpls/mpls.h> +#include <vnet/devices/devices.h> + +/* Mark stream active or inactive. */ +void +pg_stream_enable_disable (pg_main_t * pg, pg_stream_t * s, int want_enabled) +{ + vlib_main_t *vm; + vnet_main_t *vnm = vnet_get_main (); + pg_interface_t *pi = pool_elt_at_index (pg->interfaces, s->pg_if_index); + + want_enabled = want_enabled != 0; + + if (pg_stream_is_enabled (s) == want_enabled) + /* No change necessary. */ + return; + + if (want_enabled) + s->n_packets_generated = 0; + + /* Toggle enabled flag. */ + s->flags ^= PG_STREAM_FLAGS_IS_ENABLED; + + ASSERT (!pool_is_free (pg->streams, s)); + + vec_validate (pg->enabled_streams, s->worker_index); + pg->enabled_streams[s->worker_index] = + clib_bitmap_set (pg->enabled_streams[s->worker_index], s - pg->streams, + want_enabled); + + if (want_enabled) + { + vnet_hw_interface_set_flags (vnm, pi->hw_if_index, + VNET_HW_INTERFACE_FLAG_LINK_UP); + + vnet_sw_interface_set_flags (vnm, pi->sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + } + + if (vlib_num_workers ()) + vm = vlib_get_worker_vlib_main (s->worker_index); + else + vm = vlib_get_main (); + + vlib_node_set_state (vm, pg_input_node.index, + (clib_bitmap_is_zero + (pg->enabled_streams[s->worker_index]) ? + VLIB_NODE_STATE_DISABLED : VLIB_NODE_STATE_POLLING)); + + s->packet_accumulator = 0; + s->time_last_generate = 0; +} + +static u8 * +format_pg_interface_name (u8 * s, va_list * args) +{ + pg_main_t *pg = &pg_main; + u32 if_index = va_arg (*args, u32); + pg_interface_t *pi; + + pi = pool_elt_at_index (pg->interfaces, if_index); + s = format (s, "pg%d", pi->id); + + return s; +} + +static clib_error_t * +pg_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) +{ + u32 hw_flags = 0; + + if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) + hw_flags = VNET_HW_INTERFACE_FLAG_LINK_UP; + + vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); + + return 0; +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (pg_dev_class) = { + .name = "pg", + .tx_function = pg_output, + .format_device_name = format_pg_interface_name, + .admin_up_down_function = pg_interface_admin_up_down, +}; +/* *INDENT-ON* */ + +static u8 * +pg_build_rewrite (vnet_main_t * vnm, + u32 sw_if_index, + vnet_link_t link_type, const void *dst_address) +{ + u8 *rewrite = NULL; + u16 *h; + + vec_validate (rewrite, sizeof (*h) - 1); + h = (u16 *) rewrite; + h[0] = clib_host_to_net_u16 (vnet_link_to_l3_proto (link_type)); + + return (rewrite); +} + +/* *INDENT-OFF* */ +VNET_HW_INTERFACE_CLASS (pg_interface_class,static) = { + .name = "Packet generator", + .build_rewrite = pg_build_rewrite, +}; +/* *INDENT-ON* */ + +static u32 +pg_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi, u32 flags) +{ + /* nothing for now */ + return 0; +} + +u32 +pg_interface_add_or_get (pg_main_t * pg, uword if_id) +{ + vnet_main_t *vnm = vnet_get_main (); + vlib_main_t *vm = vlib_get_main (); + pg_interface_t *pi; + vnet_hw_interface_t *hi; + uword *p; + u32 i; + + p = hash_get (pg->if_index_by_if_id, if_id); + + if (p) + { + return p[0]; + } + else + { + u8 hw_addr[6]; + f64 now = vlib_time_now (vm); + u32 rnd; + + pool_get (pg->interfaces, pi); + i = pi - pg->interfaces; + + rnd = (u32) (now * 1e6); + rnd = random_u32 (&rnd); + clib_memcpy (hw_addr + 2, &rnd, sizeof (rnd)); + hw_addr[0] = 2; + hw_addr[1] = 0xfe; + + pi->id = if_id; + ethernet_register_interface (vnm, pg_dev_class.index, i, hw_addr, + &pi->hw_if_index, pg_eth_flag_change); + hi = vnet_get_hw_interface (vnm, pi->hw_if_index); + pi->sw_if_index = hi->sw_if_index; + + hash_set (pg->if_index_by_if_id, if_id, i); + + if (vlib_num_workers ()) + { + pi->lockp = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, + CLIB_CACHE_LINE_BYTES); + *pi->lockp = 0; + } + + ip4_sw_interface_enable_disable (pi->hw_if_index, 1); + ip6_sw_interface_enable_disable (pi->hw_if_index, 1); + mpls_sw_interface_enable_disable (&mpls_main, pi->hw_if_index, 1); + } + + return i; +} + +static void +do_edit (pg_stream_t * stream, + pg_edit_group_t * g, pg_edit_t * e, uword want_commit) +{ + u32 i, i0, i1, mask, n_bits_left; + u8 *v, *s, *m; + + i0 = e->lsb_bit_offset / BITS (u8); + + /* Make space for edit in value and mask. */ + vec_validate (g->fixed_packet_data, i0); + vec_validate (g->fixed_packet_data_mask, i0); + + if (e->type != PG_EDIT_FIXED) + { + switch (e->type) + { + case PG_EDIT_RANDOM: + case PG_EDIT_INCREMENT: + e->last_increment_value = pg_edit_get_value (e, PG_EDIT_LO); + break; + + default: + break; + } + + if (want_commit) + { + ASSERT (e->type != PG_EDIT_INVALID_TYPE); + vec_add1 (g->non_fixed_edits, e[0]); + } + return; + } + + s = g->fixed_packet_data; + m = g->fixed_packet_data_mask; + + n_bits_left = e->n_bits; + i0 = e->lsb_bit_offset / BITS (u8); + i1 = e->lsb_bit_offset % BITS (u8); + + v = e->values[PG_EDIT_LO]; + i = pg_edit_n_alloc_bytes (e) - 1; + + /* Odd low order bits?. */ + if (i1 != 0 && n_bits_left > 0) + { + u32 n = clib_min (n_bits_left, BITS (u8) - i1); + + mask = pow2_mask (n) << i1; + + ASSERT (i0 < vec_len (s)); + ASSERT (i < vec_len (v)); + ASSERT ((v[i] & ~mask) == 0); + + s[i0] |= v[i] & mask; + m[i0] |= mask; + + i0--; + i--; + n_bits_left -= n; + } + + /* Even bytes. */ + while (n_bits_left >= 8) + { + ASSERT (i0 < vec_len (s)); + ASSERT (i < vec_len (v)); + + s[i0] = v[i]; + m[i0] = ~0; + + i0--; + i--; + n_bits_left -= 8; + } + + /* Odd high order bits. */ + if (n_bits_left > 0) + { + mask = pow2_mask (n_bits_left); + + ASSERT (i0 < vec_len (s)); + ASSERT (i < vec_len (v)); + ASSERT ((v[i] & ~mask) == 0); + + s[i0] |= v[i] & mask; + m[i0] |= mask; + } + + if (want_commit) + pg_edit_free (e); +} + +void +pg_edit_group_get_fixed_packet_data (pg_stream_t * s, + u32 group_index, + void *packet_data, + void *packet_data_mask) +{ + pg_edit_group_t *g = pg_stream_get_group (s, group_index); + pg_edit_t *e; + + vec_foreach (e, g->edits) do_edit (s, g, e, /* want_commit */ 0); + + clib_memcpy (packet_data, g->fixed_packet_data, + vec_len (g->fixed_packet_data)); + clib_memcpy (packet_data_mask, g->fixed_packet_data_mask, + vec_len (g->fixed_packet_data_mask)); +} + +static void +perform_fixed_edits (pg_stream_t * s) +{ + pg_edit_group_t *g; + pg_edit_t *e; + word i; + + for (i = vec_len (s->edit_groups) - 1; i >= 0; i--) + { + g = vec_elt_at_index (s->edit_groups, i); + vec_foreach (e, g->edits) do_edit (s, g, e, /* want_commit */ 1); + + /* All edits have either been performed or added to + g->non_fixed_edits. So, we can delete the vector. */ + vec_free (g->edits); + } + + vec_free (s->fixed_packet_data_mask); + vec_free (s->fixed_packet_data); + vec_foreach (g, s->edit_groups) + { + int i; + g->start_byte_offset = vec_len (s->fixed_packet_data); + + /* Relocate and copy non-fixed edits from group to stream. */ + vec_foreach (e, g->non_fixed_edits) + e->lsb_bit_offset += g->start_byte_offset * BITS (u8); + + for (i = 0; i < vec_len (g->non_fixed_edits); i++) + ASSERT (g->non_fixed_edits[i].type != PG_EDIT_INVALID_TYPE); + + vec_add (s->non_fixed_edits, + g->non_fixed_edits, vec_len (g->non_fixed_edits)); + vec_free (g->non_fixed_edits); + + vec_add (s->fixed_packet_data, + g->fixed_packet_data, vec_len (g->fixed_packet_data)); + vec_add (s->fixed_packet_data_mask, + g->fixed_packet_data_mask, vec_len (g->fixed_packet_data_mask)); + } +} + +void +pg_stream_add (pg_main_t * pg, pg_stream_t * s_init) +{ + vlib_main_t *vm = vlib_get_main (); + pg_stream_t *s; + uword *p; + + if (!pg->stream_index_by_name) + pg->stream_index_by_name + = hash_create_vec (0, sizeof (s->name[0]), sizeof (uword)); + + /* Delete any old stream with the same name. */ + if (s_init->name + && (p = hash_get_mem (pg->stream_index_by_name, s_init->name))) + { + pg_stream_del (pg, p[0]); + } + + pool_get (pg->streams, s); + s[0] = s_init[0]; + + /* Give it a name. */ + if (!s->name) + s->name = format (0, "stream%d", s - pg->streams); + else + s->name = vec_dup (s->name); + + hash_set_mem (pg->stream_index_by_name, s->name, s - pg->streams); + + /* Get fixed part of buffer data. */ + if (s->edit_groups) + perform_fixed_edits (s); + + /* Determine packet size. */ + switch (s->packet_size_edit_type) + { + case PG_EDIT_INCREMENT: + case PG_EDIT_RANDOM: + if (s->min_packet_bytes == s->max_packet_bytes) + s->packet_size_edit_type = PG_EDIT_FIXED; + break; + + default: + /* Get packet size from fixed edits. */ + s->packet_size_edit_type = PG_EDIT_FIXED; + if (!s->replay_packet_templates) + s->min_packet_bytes = s->max_packet_bytes = + vec_len (s->fixed_packet_data); + break; + } + + s->last_increment_packet_size = s->min_packet_bytes; + + { + pg_buffer_index_t *bi; + int n; + +#if DPDK > 0 + s->buffer_bytes = VLIB_BUFFER_DATA_SIZE; +#endif + + if (!s->buffer_bytes) + s->buffer_bytes = s->max_packet_bytes; + + s->buffer_bytes = vlib_buffer_round_size (s->buffer_bytes); + + n = s->max_packet_bytes / s->buffer_bytes; + n += (s->max_packet_bytes % s->buffer_bytes) != 0; + + vec_resize (s->buffer_indices, n); + + vec_foreach (bi, s->buffer_indices) + { + bi->free_list_index = + vlib_buffer_create_free_list (vm, s->buffer_bytes, + "pg stream %d buffer #%d", + s - pg->streams, + 1 + (bi - s->buffer_indices)); + } + } + + /* Find an interface to use. */ + s->pg_if_index = pg_interface_add_or_get (pg, s->if_id); + + { + pg_interface_t *pi = pool_elt_at_index (pg->interfaces, s->pg_if_index); + vlib_rx_or_tx_t rx_or_tx; + + vlib_foreach_rx_tx (rx_or_tx) + { + if (s->sw_if_index[rx_or_tx] == ~0) + s->sw_if_index[rx_or_tx] = pi->sw_if_index; + } + } + + /* Connect the graph. */ + s->next_index = vlib_node_add_next (vm, device_input_node.index, + s->node_index); +} + +void +pg_stream_del (pg_main_t * pg, uword index) +{ + vlib_main_t *vm = vlib_get_main (); + pg_stream_t *s; + pg_buffer_index_t *bi; + + s = pool_elt_at_index (pg->streams, index); + + pg_stream_enable_disable (pg, s, /* want_enabled */ 0); + hash_unset_mem (pg->stream_index_by_name, s->name); + + vec_foreach (bi, s->buffer_indices) + { + vlib_buffer_delete_free_list (vm, bi->free_list_index); + clib_fifo_free (bi->buffer_fifo); + } + + pg_stream_free (s); + pool_put (pg->streams, s); +} + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |