diff options
Diffstat (limited to 'src/vnet/replication.c')
-rw-r--r-- | src/vnet/replication.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/vnet/replication.c b/src/vnet/replication.c new file mode 100644 index 00000000..561c86cd --- /dev/null +++ b/src/vnet/replication.c @@ -0,0 +1,293 @@ +/* + * replication.c : packet replication + * + * Copyright (c) 2013 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 <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vppinfra/error.h> +#include <vnet/ip/ip4_packet.h> +#include <vnet/replication.h> + + +replication_main_t replication_main; + + +replication_context_t * +replication_prep (vlib_main_t * vm, + vlib_buffer_t * b0, u32 recycle_node_index, u32 l2_packet) +{ + replication_main_t *rm = &replication_main; + replication_context_t *ctx; + uword cpu_number = vm->cpu_index; + ip4_header_t *ip; + u32 ctx_id; + + /* Allocate a context, reserve context 0 */ + if (PREDICT_FALSE (rm->contexts[cpu_number] == 0)) + pool_get_aligned (rm->contexts[cpu_number], ctx, CLIB_CACHE_LINE_BYTES); + + pool_get_aligned (rm->contexts[cpu_number], ctx, CLIB_CACHE_LINE_BYTES); + ctx_id = ctx - rm->contexts[cpu_number]; + + /* Save state from vlib buffer */ + ctx->saved_free_list_index = b0->free_list_index; + ctx->current_data = b0->current_data; + + /* Set up vlib buffer hooks */ + b0->recycle_count = ctx_id; + b0->free_list_index = rm->recycle_list_index; + b0->flags |= VLIB_BUFFER_RECYCLE; + + /* Save feature state */ + ctx->recycle_node_index = recycle_node_index; + + /* Save vnet state */ + clib_memcpy (ctx->vnet_buffer, vnet_buffer (b0), + sizeof (vnet_buffer_opaque_t)); + + /* Save packet contents */ + ctx->l2_packet = l2_packet; + ip = (ip4_header_t *) vlib_buffer_get_current (b0); + if (l2_packet) + { + /* Save ethernet header */ + ctx->l2_header[0] = ((u64 *) ip)[0]; + ctx->l2_header[1] = ((u64 *) ip)[1]; + ctx->l2_header[2] = ((u64 *) ip)[2]; + /* set ip to the true ip header */ + ip = (ip4_header_t *) (((u8 *) ip) + vnet_buffer (b0)->l2.l2_len); + } + + /* + * Copy L3 fields. + * We need to save TOS for ip4 and ip6 packets. + * Fortunately the TOS field is + * in the first two bytes of both the ip4 and ip6 headers. + */ + ctx->ip_tos = *((u16 *) (ip)); + + /* + * Save the ip4 checksum as well. We just blindly save the corresponding two + * bytes even for ip6 packets. + */ + ctx->ip4_checksum = ip->checksum; + + return ctx; +} + + +replication_context_t * +replication_recycle (vlib_main_t * vm, vlib_buffer_t * b0, u32 is_last) +{ + replication_main_t *rm = &replication_main; + replication_context_t *ctx; + uword cpu_number = vm->cpu_index; + ip4_header_t *ip; + + /* Get access to the replication context */ + ctx = pool_elt_at_index (rm->contexts[cpu_number], b0->recycle_count); + + /* Restore vnet buffer state */ + clib_memcpy (vnet_buffer (b0), ctx->vnet_buffer, + sizeof (vnet_buffer_opaque_t)); + + /* Restore the packet start (current_data) and length */ + vlib_buffer_advance (b0, ctx->current_data - b0->current_data); + + /* Restore packet contents */ + ip = (ip4_header_t *) vlib_buffer_get_current (b0); + if (ctx->l2_packet) + { + /* Restore ethernet header */ + ((u64 *) ip)[0] = ctx->l2_header[0]; + ((u64 *) ip)[1] = ctx->l2_header[1]; + ((u64 *) ip)[2] = ctx->l2_header[2]; + /* set ip to the true ip header */ + ip = (ip4_header_t *) (((u8 *) ip) + vnet_buffer (b0)->l2.l2_len); + } + + // Restore L3 fields + *((u16 *) (ip)) = ctx->ip_tos; + ip->checksum = ctx->ip4_checksum; + + if (is_last) + { + /* + * This is the last replication in the list. + * Restore original buffer free functionality. + */ + b0->free_list_index = ctx->saved_free_list_index; + b0->flags &= ~VLIB_BUFFER_RECYCLE; + + /* Free context back to its pool */ + pool_put (rm->contexts[cpu_number], ctx); + } + + return ctx; +} + + + +/* + * fish pkts back from the recycle queue/freelist + * un-flatten the context chains + */ +static void +replication_recycle_callback (vlib_main_t * vm, vlib_buffer_free_list_t * fl) +{ + vlib_frame_t *f = 0; + u32 n_left_from; + u32 n_left_to_next = 0; + u32 n_this_frame = 0; + u32 *from; + u32 *to_next = 0; + u32 bi0, pi0; + vlib_buffer_t *b0; + int i; + replication_main_t *rm = &replication_main; + replication_context_t *ctx; + u32 feature_node_index = 0; + uword cpu_number = vm->cpu_index; + + /* + * All buffers in the list are destined to the same recycle node. + * Pull the recycle node index from the first buffer. + * Note: this could be sped up if the node index were stuffed into + * the freelist itself. + */ + if (vec_len (fl->aligned_buffers) > 0) + { + bi0 = fl->aligned_buffers[0]; + b0 = vlib_get_buffer (vm, bi0); + ctx = pool_elt_at_index (rm->contexts[cpu_number], b0->recycle_count); + feature_node_index = ctx->recycle_node_index; + } + else if (vec_len (fl->unaligned_buffers) > 0) + { + bi0 = fl->unaligned_buffers[0]; + b0 = vlib_get_buffer (vm, bi0); + ctx = pool_elt_at_index (rm->contexts[cpu_number], b0->recycle_count); + feature_node_index = ctx->recycle_node_index; + } + + /* aligned, unaligned buffers */ + for (i = 0; i < 2; i++) + { + if (i == 0) + { + from = fl->aligned_buffers; + n_left_from = vec_len (from); + } + else + { + from = fl->unaligned_buffers; + n_left_from = vec_len (from); + } + + while (n_left_from > 0) + { + if (PREDICT_FALSE (n_left_to_next == 0)) + { + if (f) + { + f->n_vectors = n_this_frame; + vlib_put_frame_to_node (vm, feature_node_index, f); + } + + f = vlib_get_frame_to_node (vm, feature_node_index); + to_next = vlib_frame_vector_args (f); + n_left_to_next = VLIB_FRAME_SIZE; + n_this_frame = 0; + } + + bi0 = from[0]; + if (PREDICT_TRUE (n_left_from > 1)) + { + pi0 = from[1]; + vlib_prefetch_buffer_with_index (vm, pi0, LOAD); + } + + b0 = vlib_get_buffer (vm, bi0); + + /* Mark that this buffer was just recycled */ + b0->flags |= VLIB_BUFFER_IS_RECYCLED; + +#if (CLIB_DEBUG > 0) +#if DPDK == 0 + vlib_buffer_set_known_state (vm, bi0, VLIB_BUFFER_KNOWN_ALLOCATED); +#endif +#endif + + /* If buffer is traced, mark frame as traced */ + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + f->flags |= VLIB_FRAME_TRACE; + + to_next[0] = bi0; + + from++; + to_next++; + n_this_frame++; + n_left_to_next--; + n_left_from--; + } + } + + vec_reset_length (fl->aligned_buffers); + vec_reset_length (fl->unaligned_buffers); + + if (f) + { + ASSERT (n_this_frame); + f->n_vectors = n_this_frame; + vlib_put_frame_to_node (vm, feature_node_index, f); + } +} + +clib_error_t * +replication_init (vlib_main_t * vm) +{ + replication_main_t *rm = &replication_main; + vlib_buffer_main_t *bm = vm->buffer_main; + vlib_buffer_free_list_t *fl; + __attribute__ ((unused)) replication_context_t *ctx; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + rm->vlib_main = vm; + rm->vnet_main = vnet_get_main (); + rm->recycle_list_index = + vlib_buffer_create_free_list (vm, 1024 /* fictional */ , + "replication-recycle"); + + fl = pool_elt_at_index (bm->buffer_free_list_pool, rm->recycle_list_index); + + fl->buffers_added_to_freelist_function = replication_recycle_callback; + + /* Verify the replication context is the expected size */ + ASSERT (sizeof (replication_context_t) == 128); /* 2 cache lines */ + + vec_validate (rm->contexts, tm->n_vlib_mains - 1); + return 0; +} + +VLIB_INIT_FUNCTION (replication_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |