+ svm_fifo_chunk_t *cur;
+ u32 actual_len = 0;
+
+ /* Abort if trying to reduce by more than fifo size or if
+ * fifo is undergoing resizing already */
+ if (len >= f->size || f->size > f->nitems + 1
+ || (f->flags & SVM_FIFO_F_SHRINK) || (f->flags & SVM_FIFO_F_GROW))
+ return 0;
+
+ /* last chunk that will not be removed */
+ cur = svm_fifo_find_chunk (f, f->nitems - len);
+
+ /* sum length of chunks that will be removed */
+ cur = cur->next;
+ while (cur != f->start_chunk)
+ {
+ actual_len += cur->length;
+ cur = cur->next;
+ }
+
+ ASSERT (actual_len <= len);
+ if (!actual_len)
+ return 0;
+
+ f->size_decrement = actual_len;
+ f->flags |= SVM_FIFO_F_SHRINK;
+
+ if (try_shrink)
+ {
+ u32 head, tail;
+ f_load_head_tail_prod (f, &head, &tail);
+ svm_fifo_try_shrink (f, head, tail);
+ }
+
+ return actual_len;
+}
+
+void
+svm_fifo_free_chunk_lookup (svm_fifo_t * f)
+{
+ rb_tree_free_nodes (&f->chunk_lookup);
+}
+
+void
+svm_fifo_free (svm_fifo_t * f)
+{
+ ASSERT (f->refcnt > 0);
+
+ if (--f->refcnt == 0)
+ {
+ /* ooo data is not allocated on segment heap */
+ svm_fifo_free_chunk_lookup (f);
+ clib_mem_free (f);
+ }
+}
+
+void
+svm_fifo_overwrite_head (svm_fifo_t * f, u8 * src, u32 len)
+{
+ u32 n_chunk;
+ u32 head, tail, head_idx;
+ svm_fifo_chunk_t *c;
+
+ ASSERT (len <= f->nitems);
+
+ f_load_head_tail_cons (f, &head, &tail);
+ c = f->head_chunk;
+ head_idx = head - c->start_byte;
+ n_chunk = c->length - head_idx;
+ if (len <= n_chunk)
+ clib_memcpy_fast (&c->data[head_idx], src, len);
+ else
+ {
+ clib_memcpy_fast (&c->data[head_idx], src, n_chunk);
+ clib_memcpy_fast (&c->next->data[0], src + n_chunk, len - n_chunk);
+ }
+}
+
+int
+svm_fifo_enqueue (svm_fifo_t * f, u32 len, const u8 * src)
+{
+ u32 tail, head, free_count;
+
+ f_load_head_tail_prod (f, &head, &tail);
+
+ /* free space in fifo can only increase during enqueue: SPSC */
+ free_count = f_free_count (f, head, tail);
+
+ f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
+
+ if (PREDICT_FALSE (free_count == 0))
+ return SVM_FIFO_EFULL;
+
+ /* number of bytes we're going to copy */
+ len = clib_min (free_count, len);
+ svm_fifo_copy_to_chunk (f, f->tail_chunk, tail, src, len, &f->tail_chunk);
+ tail = (tail + len) % f->size;
+
+ svm_fifo_trace_add (f, head, len, 2);
+
+ /* collect out-of-order segments */
+ if (PREDICT_FALSE (f->ooos_list_head != OOO_SEGMENT_INVALID_INDEX))
+ {
+ len += ooo_segment_try_collect (f, len, &tail);
+ if (!svm_fifo_chunk_includes_pos (f->tail_chunk, tail))
+ f->tail_chunk = svm_fifo_find_chunk (f, tail);
+ }
+
+ /* store-rel: producer owned index (paired with load-acq in consumer) */
+ clib_atomic_store_rel_n (&f->tail, tail);
+
+ return len;