svm: march svm_fifo take 2
[vpp.git] / src / svm / svm_fifo.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <svm/svm_fifo.h>
17 #include <vppinfra/cpu.h>
18
19 static inline u8
20 position_lt (svm_fifo_t * f, u32 a, u32 b)
21 {
22   return (ooo_segment_distance_from_tail (f, a)
23           < ooo_segment_distance_from_tail (f, b));
24 }
25
26 static inline u8
27 position_leq (svm_fifo_t * f, u32 a, u32 b)
28 {
29   return (ooo_segment_distance_from_tail (f, a)
30           <= ooo_segment_distance_from_tail (f, b));
31 }
32
33 static inline u8
34 position_gt (svm_fifo_t * f, u32 a, u32 b)
35 {
36   return (ooo_segment_distance_from_tail (f, a)
37           > ooo_segment_distance_from_tail (f, b));
38 }
39
40 static inline u32
41 position_diff (svm_fifo_t * f, u32 posa, u32 posb)
42 {
43   return ooo_segment_distance_from_tail (f, posa)
44     - ooo_segment_distance_from_tail (f, posb);
45 }
46
47 static inline u32
48 ooo_segment_end_pos (svm_fifo_t * f, ooo_segment_t * s)
49 {
50   return (s->start + s->length) % f->nitems;
51 }
52
53 #ifndef CLIB_MARCH_VARIANT
54
55 u8 *
56 format_ooo_segment (u8 * s, va_list * args)
57 {
58   svm_fifo_t *f = va_arg (*args, svm_fifo_t *);
59   ooo_segment_t *seg = va_arg (*args, ooo_segment_t *);
60   u32 normalized_start = (seg->start + f->nitems - f->tail) % f->nitems;
61   s = format (s, "[%u, %u], len %u, next %d, prev %d", normalized_start,
62               (normalized_start + seg->length) % f->nitems, seg->length,
63               seg->next, seg->prev);
64   return s;
65 }
66
67 u8 *
68 svm_fifo_dump_trace (u8 * s, svm_fifo_t * f)
69 {
70 #if SVM_FIFO_TRACE
71   svm_fifo_trace_elem_t *seg = 0;
72   int i = 0;
73
74   if (f->trace)
75     {
76       vec_foreach (seg, f->trace)
77       {
78         s = format (s, "{%u, %u, %u}, ", seg->offset, seg->len, seg->action);
79         i++;
80         if (i % 5 == 0)
81           s = format (s, "\n");
82       }
83       s = format (s, "\n");
84     }
85   return s;
86 #else
87   return 0;
88 #endif
89 }
90
91 u8 *
92 svm_fifo_replay (u8 * s, svm_fifo_t * f, u8 no_read, u8 verbose)
93 {
94   int i, trace_len;
95   u8 *data = 0;
96   svm_fifo_trace_elem_t *trace;
97   u32 offset;
98   svm_fifo_t *dummy_fifo;
99
100   if (!f)
101     return s;
102
103 #if SVM_FIFO_TRACE
104   trace = f->trace;
105   trace_len = vec_len (trace);
106 #else
107   trace = 0;
108   trace_len = 0;
109 #endif
110
111   dummy_fifo = svm_fifo_create (f->nitems);
112   memset (f->data, 0xFF, f->nitems);
113
114   vec_validate (data, f->nitems);
115   for (i = 0; i < vec_len (data); i++)
116     data[i] = i;
117
118   for (i = 0; i < trace_len; i++)
119     {
120       offset = trace[i].offset;
121       if (trace[i].action == 1)
122         {
123           if (verbose)
124             s = format (s, "adding [%u, %u]:", trace[i].offset,
125                         (trace[i].offset +
126                          trace[i].len) % dummy_fifo->nitems);
127           svm_fifo_enqueue_with_offset (dummy_fifo, trace[i].offset,
128                                         trace[i].len, &data[offset]);
129         }
130       else if (trace[i].action == 2)
131         {
132           if (verbose)
133             s = format (s, "adding [%u, %u]:", 0, trace[i].len);
134           svm_fifo_enqueue_nowait (dummy_fifo, trace[i].len, &data[offset]);
135         }
136       else if (!no_read)
137         {
138           if (verbose)
139             s = format (s, "read: %u", trace[i].len);
140           svm_fifo_dequeue_drop (dummy_fifo, trace[i].len);
141         }
142       if (verbose)
143         s = format (s, "%U", format_svm_fifo, dummy_fifo, 1);
144     }
145
146   s = format (s, "result: %U", format_svm_fifo, dummy_fifo, 1);
147
148   return s;
149 }
150
151 u8 *
152 format_ooo_list (u8 * s, va_list * args)
153 {
154   svm_fifo_t *f = va_arg (*args, svm_fifo_t *);
155   u32 ooo_segment_index = f->ooos_list_head;
156   ooo_segment_t *seg;
157
158   while (ooo_segment_index != OOO_SEGMENT_INVALID_INDEX)
159     {
160       seg = pool_elt_at_index (f->ooo_segments, ooo_segment_index);
161       s = format (s, "  %U\n", format_ooo_segment, f, seg);
162       ooo_segment_index = seg->next;
163     }
164
165   return s;
166 }
167
168 u8 *
169 format_svm_fifo (u8 * s, va_list * args)
170 {
171   svm_fifo_t *f = va_arg (*args, svm_fifo_t *);
172   int verbose = va_arg (*args, int);
173
174   if (!s)
175     return s;
176
177   s = format (s, "cursize %u nitems %u has_event %d\n",
178               f->cursize, f->nitems, f->has_event);
179   s = format (s, " head %d tail %d segment manager %u\n", f->head, f->tail,
180               f->segment_manager);
181
182   if (verbose > 1)
183     s = format
184       (s, " vpp session %d thread %d app session %d thread %d\n",
185        f->master_session_index, f->master_thread_index,
186        f->client_session_index, f->client_thread_index);
187
188   if (verbose)
189     {
190       s = format (s, " ooo pool %d active elts newest %u\n",
191                   pool_elts (f->ooo_segments), f->ooos_newest);
192       if (svm_fifo_has_ooo_data (f))
193         s = format (s, " %U", format_ooo_list, f, verbose);
194     }
195   return s;
196 }
197
198 /** create an svm fifo, in the current heap. Fails vs blow up the process */
199 svm_fifo_t *
200 svm_fifo_create (u32 data_size_in_bytes)
201 {
202   svm_fifo_t *f;
203   u32 rounded_data_size;
204
205   /* always round fifo data size to the next highest power-of-two */
206   rounded_data_size = (1 << (max_log2 (data_size_in_bytes)));
207   f = clib_mem_alloc_aligned_or_null (sizeof (*f) + rounded_data_size,
208                                       CLIB_CACHE_LINE_BYTES);
209   if (f == 0)
210     return 0;
211
212   memset (f, 0, sizeof (*f));
213   f->nitems = data_size_in_bytes;
214   f->ooos_list_head = OOO_SEGMENT_INVALID_INDEX;
215   f->refcnt = 1;
216   return (f);
217 }
218
219 void
220 svm_fifo_free (svm_fifo_t * f)
221 {
222   ASSERT (f->refcnt > 0);
223
224   if (--f->refcnt == 0)
225     {
226       pool_free (f->ooo_segments);
227       clib_mem_free (f);
228     }
229 }
230 #endif
231
232 always_inline ooo_segment_t *
233 ooo_segment_new (svm_fifo_t * f, u32 start, u32 length)
234 {
235   ooo_segment_t *s;
236
237   pool_get (f->ooo_segments, s);
238
239   s->start = start;
240   s->length = length;
241
242   s->prev = s->next = OOO_SEGMENT_INVALID_INDEX;
243
244   return s;
245 }
246
247 always_inline void
248 ooo_segment_del (svm_fifo_t * f, u32 index)
249 {
250   ooo_segment_t *cur, *prev = 0, *next = 0;
251   cur = pool_elt_at_index (f->ooo_segments, index);
252
253   if (cur->next != OOO_SEGMENT_INVALID_INDEX)
254     {
255       next = pool_elt_at_index (f->ooo_segments, cur->next);
256       next->prev = cur->prev;
257     }
258
259   if (cur->prev != OOO_SEGMENT_INVALID_INDEX)
260     {
261       prev = pool_elt_at_index (f->ooo_segments, cur->prev);
262       prev->next = cur->next;
263     }
264   else
265     {
266       f->ooos_list_head = cur->next;
267     }
268
269   pool_put (f->ooo_segments, cur);
270 }
271
272 /**
273  * Add segment to fifo's out-of-order segment list. Takes care of merging
274  * adjacent segments and removing overlapping ones.
275  */
276 static void
277 ooo_segment_add (svm_fifo_t * f, u32 offset, u32 length)
278 {
279   ooo_segment_t *s, *new_s, *prev, *next, *it;
280   u32 new_index, s_end_pos, s_index;
281   u32 normalized_position, normalized_end_position;
282
283   ASSERT (offset + length <= ooo_segment_distance_from_tail (f, f->head));
284   normalized_position = (f->tail + offset) % f->nitems;
285   normalized_end_position = (f->tail + offset + length) % f->nitems;
286
287   f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
288
289   if (f->ooos_list_head == OOO_SEGMENT_INVALID_INDEX)
290     {
291       s = ooo_segment_new (f, normalized_position, length);
292       f->ooos_list_head = s - f->ooo_segments;
293       f->ooos_newest = f->ooos_list_head;
294       return;
295     }
296
297   /* Find first segment that starts after new segment */
298   s = pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
299   while (s->next != OOO_SEGMENT_INVALID_INDEX
300          && position_lt (f, s->start, normalized_position))
301     s = pool_elt_at_index (f->ooo_segments, s->next);
302
303   /* If we have a previous and we overlap it, use it as starting point */
304   prev = ooo_segment_get_prev (f, s);
305   if (prev
306       && position_leq (f, normalized_position, ooo_segment_end_pos (f, prev)))
307     {
308       s = prev;
309       s_end_pos = ooo_segment_end_pos (f, s);
310
311       /* Since we have previous, normalized start position cannot be smaller
312        * than prev->start. Check tail */
313       ASSERT (position_lt (f, s->start, normalized_position));
314       goto check_tail;
315     }
316
317   s_index = s - f->ooo_segments;
318   s_end_pos = ooo_segment_end_pos (f, s);
319
320   /* No overlap, add before current segment */
321   if (position_lt (f, normalized_end_position, s->start))
322     {
323       new_s = ooo_segment_new (f, normalized_position, length);
324       new_index = new_s - f->ooo_segments;
325
326       /* Pool might've moved, get segment again */
327       s = pool_elt_at_index (f->ooo_segments, s_index);
328       if (s->prev != OOO_SEGMENT_INVALID_INDEX)
329         {
330           new_s->prev = s->prev;
331           prev = pool_elt_at_index (f->ooo_segments, new_s->prev);
332           prev->next = new_index;
333         }
334       else
335         {
336           /* New head */
337           f->ooos_list_head = new_index;
338         }
339
340       new_s->next = s_index;
341       s->prev = new_index;
342       f->ooos_newest = new_index;
343       return;
344     }
345   /* No overlap, add after current segment */
346   else if (position_gt (f, normalized_position, s_end_pos))
347     {
348       new_s = ooo_segment_new (f, normalized_position, length);
349       new_index = new_s - f->ooo_segments;
350
351       /* Pool might've moved, get segment again */
352       s = pool_elt_at_index (f->ooo_segments, s_index);
353
354       /* Needs to be last */
355       ASSERT (s->next == OOO_SEGMENT_INVALID_INDEX);
356
357       new_s->prev = s_index;
358       s->next = new_index;
359       f->ooos_newest = new_index;
360
361       return;
362     }
363
364   /*
365    * Merge needed
366    */
367
368   /* Merge at head */
369   if (position_lt (f, normalized_position, s->start))
370     {
371       s->start = normalized_position;
372       s->length = position_diff (f, s_end_pos, s->start);
373       f->ooos_newest = s - f->ooo_segments;
374     }
375
376 check_tail:
377
378   /* Overlapping tail */
379   if (position_gt (f, normalized_end_position, s_end_pos))
380     {
381       s->length = position_diff (f, normalized_end_position, s->start);
382
383       /* Remove the completely overlapped segments in the tail */
384       it = ooo_segment_next (f, s);
385       while (it && position_leq (f, ooo_segment_end_pos (f, it),
386                                  normalized_end_position))
387         {
388           next = ooo_segment_next (f, it);
389           ooo_segment_del (f, it - f->ooo_segments);
390           it = next;
391         }
392
393       /* If partial overlap with last, merge */
394       if (it && position_leq (f, it->start, normalized_end_position))
395         {
396           s->length = position_diff (f, ooo_segment_end_pos (f, it),
397                                      s->start);
398           ooo_segment_del (f, it - f->ooo_segments);
399         }
400       f->ooos_newest = s - f->ooo_segments;
401     }
402 }
403
404 /**
405  * Removes segments that can now be enqueued because the fifo's tail has
406  * advanced. Returns the number of bytes added to tail.
407  */
408 static int
409 ooo_segment_try_collect (svm_fifo_t * f, u32 n_bytes_enqueued)
410 {
411   ooo_segment_t *s;
412   u32 index, bytes = 0;
413   i32 diff;
414
415   s = pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
416   diff = ooo_segment_distance_to_tail (f, s->start);
417
418   ASSERT (diff != n_bytes_enqueued);
419
420   if (diff > n_bytes_enqueued)
421     return 0;
422
423   /* If last tail update overlaps one/multiple ooo segments, remove them */
424   while (0 <= diff && diff < n_bytes_enqueued)
425     {
426       index = s - f->ooo_segments;
427
428       /* Segment end is beyond the tail. Advance tail and remove segment */
429       if (s->length > diff)
430         {
431           bytes = s->length - diff;
432           f->tail += bytes;
433           f->tail %= f->nitems;
434           ooo_segment_del (f, index);
435           break;
436         }
437
438       /* If we have next go on */
439       if (s->next != OOO_SEGMENT_INVALID_INDEX)
440         {
441           s = pool_elt_at_index (f->ooo_segments, s->next);
442           diff = ooo_segment_distance_to_tail (f, s->start);
443           ooo_segment_del (f, index);
444         }
445       /* End of search */
446       else
447         {
448           ooo_segment_del (f, index);
449           break;
450         }
451     }
452
453   ASSERT (bytes <= f->nitems);
454   return bytes;
455 }
456
457 CLIB_MARCH_FN (svm_fifo_enqueue_nowait, int, svm_fifo_t * f, u32 max_bytes,
458                const u8 * copy_from_here)
459 {
460   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
461   u32 cursize, nitems;
462
463   /* read cursize, which can only increase while we're working */
464   cursize = svm_fifo_max_dequeue (f);
465   f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
466
467   if (PREDICT_FALSE (cursize == f->nitems))
468     return SVM_FIFO_FULL;
469
470   nitems = f->nitems;
471
472   /* Number of bytes we're going to copy */
473   total_copy_bytes = (nitems - cursize) < max_bytes ?
474     (nitems - cursize) : max_bytes;
475
476   if (PREDICT_TRUE (copy_from_here != 0))
477     {
478       /* Number of bytes in first copy segment */
479       first_copy_bytes = ((nitems - f->tail) < total_copy_bytes)
480         ? (nitems - f->tail) : total_copy_bytes;
481
482       clib_memcpy (&f->data[f->tail], copy_from_here, first_copy_bytes);
483       f->tail += first_copy_bytes;
484       f->tail = (f->tail == nitems) ? 0 : f->tail;
485
486       /* Number of bytes in second copy segment, if any */
487       second_copy_bytes = total_copy_bytes - first_copy_bytes;
488       if (second_copy_bytes)
489         {
490           clib_memcpy (&f->data[f->tail], copy_from_here + first_copy_bytes,
491                        second_copy_bytes);
492           f->tail += second_copy_bytes;
493           f->tail = (f->tail == nitems) ? 0 : f->tail;
494         }
495     }
496   else
497     {
498       ASSERT (0);
499
500       /* Account for a zero-copy enqueue done elsewhere */
501       ASSERT (max_bytes <= (nitems - cursize));
502       f->tail += max_bytes;
503       f->tail = f->tail % nitems;
504       total_copy_bytes = max_bytes;
505     }
506
507   svm_fifo_trace_add (f, f->head, total_copy_bytes, 2);
508
509   /* Any out-of-order segments to collect? */
510   if (PREDICT_FALSE (f->ooos_list_head != OOO_SEGMENT_INVALID_INDEX))
511     total_copy_bytes += ooo_segment_try_collect (f, total_copy_bytes);
512
513   /* Atomically increase the queue length */
514   ASSERT (cursize + total_copy_bytes <= nitems);
515   __sync_fetch_and_add (&f->cursize, total_copy_bytes);
516
517   return (total_copy_bytes);
518 }
519
520 #ifndef CLIB_MARCH_VARIANT
521 int
522 svm_fifo_enqueue_nowait (svm_fifo_t * f, u32 max_bytes,
523                          const u8 * copy_from_here)
524 {
525   return CLIB_MARCH_FN_SELECT (svm_fifo_enqueue_nowait) (f, max_bytes,
526                                                          copy_from_here);
527 }
528 #endif
529
530 /**
531  * Enqueue a future segment.
532  *
533  * Two choices: either copies the entire segment, or copies nothing
534  * Returns 0 of the entire segment was copied
535  * Returns -1 if none of the segment was copied due to lack of space
536  */
537 CLIB_MARCH_FN (svm_fifo_enqueue_with_offset, int, svm_fifo_t * f,
538                u32 offset, u32 required_bytes, u8 * copy_from_here)
539 {
540   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
541   u32 cursize, nitems, normalized_offset;
542
543   f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
544
545   /* read cursize, which can only increase while we're working */
546   cursize = svm_fifo_max_dequeue (f);
547   nitems = f->nitems;
548
549   ASSERT (required_bytes < nitems);
550
551   normalized_offset = (f->tail + offset) % nitems;
552
553   /* Will this request fit? */
554   if ((required_bytes + offset) > (nitems - cursize))
555     return -1;
556
557   svm_fifo_trace_add (f, offset, required_bytes, 1);
558
559   ooo_segment_add (f, offset, required_bytes);
560
561   /* Number of bytes we're going to copy */
562   total_copy_bytes = required_bytes;
563
564   /* Number of bytes in first copy segment */
565   first_copy_bytes = ((nitems - normalized_offset) < total_copy_bytes)
566     ? (nitems - normalized_offset) : total_copy_bytes;
567
568   clib_memcpy (&f->data[normalized_offset], copy_from_here, first_copy_bytes);
569
570   /* Number of bytes in second copy segment, if any */
571   second_copy_bytes = total_copy_bytes - first_copy_bytes;
572   if (second_copy_bytes)
573     {
574       normalized_offset += first_copy_bytes;
575       normalized_offset %= nitems;
576
577       ASSERT (normalized_offset == 0);
578
579       clib_memcpy (&f->data[normalized_offset],
580                    copy_from_here + first_copy_bytes, second_copy_bytes);
581     }
582
583   return (0);
584 }
585
586 #ifndef CLIB_MARCH_VARIANT
587
588 int
589 svm_fifo_enqueue_with_offset (svm_fifo_t * f, u32 offset, u32 required_bytes,
590                               u8 * copy_from_here)
591 {
592   return CLIB_MARCH_FN_SELECT (svm_fifo_enqueue_with_offset) (f, offset,
593                                                               required_bytes,
594                                                               copy_from_here);
595 }
596
597 void
598 svm_fifo_overwrite_head (svm_fifo_t * f, u8 * data, u32 len)
599 {
600   u32 first_chunk;
601   first_chunk = f->nitems - f->head;
602   ASSERT (len <= f->nitems);
603   if (len <= first_chunk)
604     clib_memcpy (&f->data[f->head], data, len);
605   else
606     {
607       clib_memcpy (&f->data[f->head], data, first_chunk);
608       clib_memcpy (&f->data[0], data + first_chunk, len - first_chunk);
609     }
610 }
611 #endif
612
613 CLIB_MARCH_FN (svm_fifo_dequeue_nowait, int, svm_fifo_t * f, u32 max_bytes,
614                u8 * copy_here)
615 {
616   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
617   u32 cursize, nitems;
618
619   /* read cursize, which can only increase while we're working */
620   cursize = svm_fifo_max_dequeue (f);
621   if (PREDICT_FALSE (cursize == 0))
622     return -2;                  /* nothing in the fifo */
623
624   nitems = f->nitems;
625
626   /* Number of bytes we're going to copy */
627   total_copy_bytes = (cursize < max_bytes) ? cursize : max_bytes;
628
629   if (PREDICT_TRUE (copy_here != 0))
630     {
631       /* Number of bytes in first copy segment */
632       first_copy_bytes = ((nitems - f->head) < total_copy_bytes)
633         ? (nitems - f->head) : total_copy_bytes;
634       clib_memcpy (copy_here, &f->data[f->head], first_copy_bytes);
635       f->head += first_copy_bytes;
636       f->head = (f->head == nitems) ? 0 : f->head;
637
638       /* Number of bytes in second copy segment, if any */
639       second_copy_bytes = total_copy_bytes - first_copy_bytes;
640       if (second_copy_bytes)
641         {
642           clib_memcpy (copy_here + first_copy_bytes,
643                        &f->data[f->head], second_copy_bytes);
644           f->head += second_copy_bytes;
645           f->head = (f->head == nitems) ? 0 : f->head;
646         }
647     }
648   else
649     {
650       ASSERT (0);
651       /* Account for a zero-copy dequeue done elsewhere */
652       ASSERT (max_bytes <= cursize);
653       f->head += max_bytes;
654       f->head = f->head % nitems;
655       cursize -= max_bytes;
656       total_copy_bytes = max_bytes;
657     }
658
659   ASSERT (f->head <= nitems);
660   ASSERT (cursize >= total_copy_bytes);
661   __sync_fetch_and_sub (&f->cursize, total_copy_bytes);
662
663   return (total_copy_bytes);
664 }
665
666 #ifndef CLIB_MARCH_VARIANT
667
668 int
669 svm_fifo_dequeue_nowait (svm_fifo_t * f, u32 max_bytes, u8 * copy_here)
670 {
671   return CLIB_MARCH_FN_SELECT (svm_fifo_dequeue_nowait) (f, max_bytes,
672                                                          copy_here);
673 }
674 #endif
675
676 CLIB_MARCH_FN (svm_fifo_peek, int, svm_fifo_t * f, u32 relative_offset,
677                u32 max_bytes, u8 * copy_here)
678 {
679   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
680   u32 cursize, nitems, real_head;
681
682   /* read cursize, which can only increase while we're working */
683   cursize = svm_fifo_max_dequeue (f);
684   if (PREDICT_FALSE (cursize < relative_offset))
685     return -2;                  /* nothing in the fifo */
686
687   nitems = f->nitems;
688   real_head = f->head + relative_offset;
689   real_head = real_head >= nitems ? real_head - nitems : real_head;
690
691   /* Number of bytes we're going to copy */
692   total_copy_bytes = (cursize - relative_offset < max_bytes) ?
693     cursize - relative_offset : max_bytes;
694
695   if (PREDICT_TRUE (copy_here != 0))
696     {
697       /* Number of bytes in first copy segment */
698       first_copy_bytes =
699         ((nitems - real_head) < total_copy_bytes) ?
700         (nitems - real_head) : total_copy_bytes;
701       clib_memcpy (copy_here, &f->data[real_head], first_copy_bytes);
702
703       /* Number of bytes in second copy segment, if any */
704       second_copy_bytes = total_copy_bytes - first_copy_bytes;
705       if (second_copy_bytes)
706         {
707           clib_memcpy (copy_here + first_copy_bytes, &f->data[0],
708                        second_copy_bytes);
709         }
710     }
711   return total_copy_bytes;
712 }
713
714 #ifndef CLIB_MARCH_VARIANT
715
716 int
717 svm_fifo_peek (svm_fifo_t * f, u32 relative_offset, u32 max_bytes,
718                u8 * copy_here)
719 {
720   return CLIB_MARCH_FN_SELECT (svm_fifo_peek) (f, relative_offset, max_bytes,
721                                                copy_here);
722 }
723
724 int
725 svm_fifo_dequeue_drop (svm_fifo_t * f, u32 max_bytes)
726 {
727   u32 total_drop_bytes, first_drop_bytes, second_drop_bytes;
728   u32 cursize, nitems;
729
730   /* read cursize, which can only increase while we're working */
731   cursize = svm_fifo_max_dequeue (f);
732   if (PREDICT_FALSE (cursize == 0))
733     return -2;                  /* nothing in the fifo */
734
735   nitems = f->nitems;
736
737   /* Number of bytes we're going to drop */
738   total_drop_bytes = (cursize < max_bytes) ? cursize : max_bytes;
739
740   svm_fifo_trace_add (f, f->tail, total_drop_bytes, 3);
741
742   /* Number of bytes in first copy segment */
743   first_drop_bytes =
744     ((nitems - f->head) < total_drop_bytes) ?
745     (nitems - f->head) : total_drop_bytes;
746   f->head += first_drop_bytes;
747   f->head = (f->head == nitems) ? 0 : f->head;
748
749   /* Number of bytes in second drop segment, if any */
750   second_drop_bytes = total_drop_bytes - first_drop_bytes;
751   if (second_drop_bytes)
752     {
753       f->head += second_drop_bytes;
754       f->head = (f->head == nitems) ? 0 : f->head;
755     }
756
757   ASSERT (f->head <= nitems);
758   ASSERT (cursize >= total_drop_bytes);
759   __sync_fetch_and_sub (&f->cursize, total_drop_bytes);
760
761   return total_drop_bytes;
762 }
763
764 void
765 svm_fifo_dequeue_drop_all (svm_fifo_t * f)
766 {
767   f->head = f->tail;
768   __sync_fetch_and_sub (&f->cursize, f->cursize);
769 }
770
771 int
772 svm_fifo_segments (svm_fifo_t * f, svm_fifo_segment_t * fs)
773 {
774   u32 cursize, nitems;
775
776   /* read cursize, which can only increase while we're working */
777   cursize = svm_fifo_max_dequeue (f);
778   if (PREDICT_FALSE (cursize == 0))
779     return -2;
780
781   nitems = f->nitems;
782
783   fs[0].len = ((nitems - f->head) < cursize) ? (nitems - f->head) : cursize;
784   fs[0].data = f->data + f->head;
785
786   if (fs[0].len < cursize)
787     {
788       fs[1].len = cursize - fs[0].len;
789       fs[1].data = f->data;
790     }
791   else
792     {
793       fs[1].len = 0;
794       fs[1].data = 0;
795     }
796   return cursize;
797 }
798
799 void
800 svm_fifo_segments_free (svm_fifo_t * f, svm_fifo_segment_t * fs)
801 {
802   u32 total_drop_bytes;
803
804   ASSERT (fs[0].data == f->data + f->head);
805   if (fs[1].len)
806     {
807       f->head = fs[1].len;
808       total_drop_bytes = fs[0].len + fs[1].len;
809     }
810   else
811     {
812       f->head = (f->head + fs[0].len) % f->nitems;
813       total_drop_bytes = fs[0].len;
814     }
815   __sync_fetch_and_sub (&f->cursize, total_drop_bytes);
816 }
817
818 u32
819 svm_fifo_number_ooo_segments (svm_fifo_t * f)
820 {
821   return pool_elts (f->ooo_segments);
822 }
823
824 ooo_segment_t *
825 svm_fifo_first_ooo_segment (svm_fifo_t * f)
826 {
827   return pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
828 }
829
830 /**
831  * Set fifo pointers to requested offset
832  */
833 void
834 svm_fifo_init_pointers (svm_fifo_t * f, u32 pointer)
835 {
836   f->head = f->tail = pointer % f->nitems;
837 }
838
839 #endif
840 /*
841  * fd.io coding-style-patch-verification: ON
842  *
843  * Local Variables:
844  * eval: (c-set-style "gnu")
845  * End:
846  */