+/**
+ * Allocate a new buffer and build a new tcp segment
+ *
+ * @param wrk tcp worker
+ * @param tc connection for which the segment will be allocated
+ * @param offset offset of the first byte in the tx fifo
+ * @param max_deq_byte segment size
+ * @param[out] b pointer to buffer allocated
+ *
+ * @return the number of bytes in the segment or 0 if buffer cannot be
+ * allocated or no data available
+ */
+static int
+tcp_prepare_segment (tcp_worker_ctx_t * wrk, tcp_connection_t * tc,
+ u32 offset, u32 max_deq_bytes, vlib_buffer_t ** b)
+{
+ u32 bytes_per_buffer = vnet_get_tcp_main ()->bytes_per_buffer;
+ u32 bi, seg_size;
+ vlib_main_t *vm = wrk->vm;
+ int n_bytes = 0;
+ u8 *data;
+
+ seg_size = max_deq_bytes + MAX_HDRS_LEN;
+
+ /*
+ * Prepare options
+ */
+ tc->snd_opts_len = tcp_make_options (tc, &tc->snd_opts, tc->state);
+
+ /*
+ * Allocate and fill in buffer(s)
+ */
+
+ /* Easy case, buffer size greater than mss */
+ if (PREDICT_TRUE (seg_size <= bytes_per_buffer))
+ {
+ if (PREDICT_FALSE (tcp_get_free_buffer_index (wrk, &bi)))
+ return 0;
+ *b = vlib_get_buffer (vm, bi);
+ data = tcp_init_buffer (vm, *b);
+ n_bytes = stream_session_peek_bytes (&tc->connection, data, offset,
+ max_deq_bytes);
+ ASSERT (n_bytes == max_deq_bytes);
+ b[0]->current_length = n_bytes;
+ tcp_push_hdr_i (tc, *b, tc->state, /* compute opts */ 0, /* burst */ 0);
+ if (seq_gt (tc->snd_nxt, tc->snd_una_max))
+ tc->snd_una_max = tc->snd_nxt;
+ }
+ /* Split mss into multiple buffers */
+ else
+ {
+ u32 chain_bi = ~0, n_bufs_per_seg;
+ u16 n_peeked, len_to_deq, available_bufs;
+ vlib_buffer_t *chain_b, *prev_b;
+ int i;
+
+ /* Make sure we have enough buffers */
+ n_bufs_per_seg = ceil ((double) seg_size / bytes_per_buffer);
+ available_bufs = vec_len (wrk->tx_buffers);
+ if (n_bufs_per_seg > available_bufs)
+ {
+ tcp_alloc_tx_buffers (wrk, &available_bufs, VLIB_FRAME_SIZE);
+ if (n_bufs_per_seg > available_bufs)
+ {
+ *b = 0;
+ return 0;
+ }
+ }
+
+ (void) tcp_get_free_buffer_index (wrk, &bi);
+ ASSERT (bi != (u32) ~ 0);
+ *b = vlib_get_buffer (vm, bi);
+ data = tcp_init_buffer (vm, *b);
+ n_bytes = stream_session_peek_bytes (&tc->connection, data, offset,
+ bytes_per_buffer - MAX_HDRS_LEN);
+ b[0]->current_length = n_bytes;
+ b[0]->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
+ b[0]->total_length_not_including_first_buffer = 0;
+ max_deq_bytes -= n_bytes;
+
+ chain_b = *b;
+ for (i = 1; i < n_bufs_per_seg; i++)
+ {
+ prev_b = chain_b;
+ len_to_deq = clib_min (max_deq_bytes, bytes_per_buffer);
+ tcp_get_free_buffer_index (wrk, &chain_bi);
+ ASSERT (chain_bi != (u32) ~ 0);
+ chain_b = vlib_get_buffer (vm, chain_bi);
+ chain_b->current_data = 0;
+ data = vlib_buffer_get_current (chain_b);
+ n_peeked = stream_session_peek_bytes (&tc->connection, data,
+ offset + n_bytes, len_to_deq);
+ ASSERT (n_peeked == len_to_deq);
+ n_bytes += n_peeked;
+ chain_b->current_length = n_peeked;
+ chain_b->next_buffer = 0;
+
+ /* update previous buffer */
+ prev_b->next_buffer = chain_bi;
+ prev_b->flags |= VLIB_BUFFER_NEXT_PRESENT;
+
+ max_deq_bytes -= n_peeked;
+ b[0]->total_length_not_including_first_buffer += n_peeked;
+ }
+
+ tcp_push_hdr_i (tc, *b, tc->state, /* compute opts */ 0, /* burst */ 0);
+ if (seq_gt (tc->snd_nxt, tc->snd_una_max))
+ tc->snd_una_max = tc->snd_nxt;
+ }
+
+ ASSERT (n_bytes > 0);
+ ASSERT (((*b)->current_data + (*b)->current_length) <= bytes_per_buffer);
+
+ return n_bytes;
+}
+
+/**
+ * Build a retransmit segment