Horizontal (nSessions) scaling draft
[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 u8 *
54 format_ooo_segment (u8 * s, va_list * args)
55 {
56   ooo_segment_t *seg = va_arg (*args, ooo_segment_t *);
57
58   s = format (s, "pos %u, len %u, next %d, prev %d",
59               seg->start, seg->length, seg->next, seg->prev);
60   return s;
61 }
62
63 u8 *
64 format_ooo_list (u8 * s, va_list * args)
65 {
66   svm_fifo_t *f = va_arg (*args, svm_fifo_t *);
67   u32 ooo_segment_index = f->ooos_list_head;
68   ooo_segment_t *seg;
69
70   while (ooo_segment_index != OOO_SEGMENT_INVALID_INDEX)
71     {
72       seg = pool_elt_at_index (f->ooo_segments, ooo_segment_index);
73       s = format (s, "  %U\n", format_ooo_segment, seg);
74       ooo_segment_index = seg->next;
75     }
76   return s;
77 }
78
79 u8 *
80 format_svm_fifo (u8 * s, va_list * args)
81 {
82   svm_fifo_t *f = va_arg (*args, svm_fifo_t *);
83   int verbose = va_arg (*args, int);
84
85   s = format (s, "cursize %u nitems %u has_event %d\n",
86               f->cursize, f->nitems, f->has_event);
87   s = format (s, " head %d tail %d\n", f->head, f->tail);
88
89   if (verbose > 1)
90     s = format
91       (s, " server session %d thread %d client session %d thread %d\n",
92        f->master_session_index, f->master_thread_index,
93        f->client_session_index, f->client_thread_index);
94
95   if (verbose)
96     {
97       s = format (s, " ooo pool %d active elts\n",
98                   pool_elts (f->ooo_segments));
99       if (svm_fifo_has_ooo_data (f))
100         s = format (s, " %U", format_ooo_list, f);
101     }
102   return s;
103 }
104
105 /** create an svm fifo, in the current heap. Fails vs blow up the process */
106 svm_fifo_t *
107 svm_fifo_create (u32 data_size_in_bytes)
108 {
109   svm_fifo_t *f;
110
111   f = clib_mem_alloc_aligned_or_null (sizeof (*f) + data_size_in_bytes,
112                                       CLIB_CACHE_LINE_BYTES);
113   if (f == 0)
114     return 0;
115
116   memset (f, 0, sizeof (*f));
117   f->nitems = data_size_in_bytes;
118   f->ooos_list_head = OOO_SEGMENT_INVALID_INDEX;
119
120   return (f);
121 }
122
123 void
124 svm_fifo_free (svm_fifo_t * f)
125 {
126   pool_free (f->ooo_segments);
127   clib_mem_free (f);
128 }
129
130 always_inline ooo_segment_t *
131 ooo_segment_new (svm_fifo_t * f, u32 start, u32 length)
132 {
133   ooo_segment_t *s;
134
135   pool_get (f->ooo_segments, s);
136
137   s->start = start;
138   s->length = length;
139
140   s->prev = s->next = OOO_SEGMENT_INVALID_INDEX;
141
142   return s;
143 }
144
145 always_inline void
146 ooo_segment_del (svm_fifo_t * f, u32 index)
147 {
148   ooo_segment_t *cur, *prev = 0, *next = 0;
149   cur = pool_elt_at_index (f->ooo_segments, index);
150
151   if (cur->next != OOO_SEGMENT_INVALID_INDEX)
152     {
153       next = pool_elt_at_index (f->ooo_segments, cur->next);
154       next->prev = cur->prev;
155     }
156
157   if (cur->prev != OOO_SEGMENT_INVALID_INDEX)
158     {
159       prev = pool_elt_at_index (f->ooo_segments, cur->prev);
160       prev->next = cur->next;
161     }
162   else
163     {
164       f->ooos_list_head = cur->next;
165     }
166
167   pool_put (f->ooo_segments, cur);
168 }
169
170 /**
171  * Add segment to fifo's out-of-order segment list. Takes care of merging
172  * adjacent segments and removing overlapping ones.
173  */
174 static void
175 ooo_segment_add (svm_fifo_t * f, u32 offset, u32 length)
176 {
177   ooo_segment_t *s, *new_s, *prev, *next, *it;
178   u32 new_index, s_end_pos, s_index;
179   u32 normalized_position, normalized_end_position;
180
181   normalized_position = (f->tail + offset) % f->nitems;
182   normalized_end_position = (f->tail + offset + length) % f->nitems;
183
184   f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
185
186   if (f->ooos_list_head == OOO_SEGMENT_INVALID_INDEX)
187     {
188       s = ooo_segment_new (f, normalized_position, length);
189       f->ooos_list_head = s - f->ooo_segments;
190       f->ooos_newest = f->ooos_list_head;
191       return;
192     }
193
194   /* Find first segment that starts after new segment */
195   s = pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
196   while (s->next != OOO_SEGMENT_INVALID_INDEX
197          && position_lt (f, s->start, normalized_position))
198     s = pool_elt_at_index (f->ooo_segments, s->next);
199
200   /* If we have a previous and we overlap it, use it as starting point */
201   prev = ooo_segment_get_prev (f, s);
202   if (prev
203       && position_leq (f, normalized_position, ooo_segment_end_pos (f, prev)))
204     {
205       s = prev;
206       s_end_pos = ooo_segment_end_pos (f, s);
207
208       /* Check head and tail now since segment may be wider at both ends so
209        * merge tests lower won't work */
210       if (position_lt (f, normalized_position, s->start))
211         {
212           s->start = normalized_position;
213           s->length = position_diff (f, s_end_pos, s->start);
214         }
215       if (position_gt (f, normalized_end_position, s_end_pos))
216         {
217           s->length = position_diff (f, normalized_end_position, s->start);
218         }
219       goto check_tail;
220     }
221
222   s_index = s - f->ooo_segments;
223   s_end_pos = ooo_segment_end_pos (f, s);
224
225   /* No overlap, add before current segment */
226   if (position_lt (f, normalized_end_position, s->start))
227     {
228       new_s = ooo_segment_new (f, normalized_position, length);
229       new_index = new_s - f->ooo_segments;
230
231       /* Pool might've moved, get segment again */
232       s = pool_elt_at_index (f->ooo_segments, s_index);
233       if (s->prev != OOO_SEGMENT_INVALID_INDEX)
234         {
235           new_s->prev = s->prev;
236           prev = pool_elt_at_index (f->ooo_segments, new_s->prev);
237           prev->next = new_index;
238         }
239       else
240         {
241           /* New head */
242           f->ooos_list_head = new_index;
243         }
244
245       new_s->next = s_index;
246       s->prev = new_index;
247       f->ooos_newest = new_index;
248       return;
249     }
250   /* No overlap, add after current segment */
251   else if (position_gt (f, normalized_position, s_end_pos))
252     {
253       new_s = ooo_segment_new (f, normalized_position, length);
254       new_index = new_s - f->ooo_segments;
255
256       /* Pool might've moved, get segment again */
257       s = pool_elt_at_index (f->ooo_segments, s_index);
258
259       ASSERT (s->next == OOO_SEGMENT_INVALID_INDEX);
260
261       new_s->prev = s_index;
262       s->next = new_index;
263       f->ooos_newest = new_index;
264
265       return;
266     }
267
268   /*
269    * Merge needed
270    */
271
272   /* Merge at head */
273   if (position_lt (f, normalized_position, s->start))
274     {
275       s->start = normalized_position;
276       s->length = position_diff (f, s_end_pos, s->start);
277     }
278   /* Overlapping tail */
279   else if (position_gt (f, normalized_end_position, s_end_pos))
280     {
281       s->length = position_diff (f, normalized_end_position, s->start);
282     }
283   /* New segment completely covered by current one */
284   else
285     {
286       /* Do Nothing */
287       s = 0;
288       goto done;
289     }
290
291 check_tail:
292   /* The new segment's tail may cover multiple smaller ones */
293   if (position_gt (f, normalized_end_position, s_end_pos))
294     {
295       /* Remove the completely overlapped segments */
296       it = (s->next != OOO_SEGMENT_INVALID_INDEX) ?
297         pool_elt_at_index (f->ooo_segments, s->next) : 0;
298       while (it && position_leq (f, ooo_segment_end_pos (f, it),
299                                  normalized_end_position))
300         {
301           next = (it->next != OOO_SEGMENT_INVALID_INDEX) ?
302             pool_elt_at_index (f->ooo_segments, it->next) : 0;
303           ooo_segment_del (f, it - f->ooo_segments);
304           it = next;
305         }
306
307       /* If partial overlap with last, merge */
308       if (it && position_leq (f, it->start, normalized_end_position))
309         {
310           s->length =
311             position_diff (f, ooo_segment_end_pos (f, it), s->start);
312           ooo_segment_del (f, it - f->ooo_segments);
313         }
314     }
315
316 done:
317   /* Most recently updated segment */
318   if (s)
319     f->ooos_newest = s - f->ooo_segments;
320 }
321
322 /**
323  * Removes segments that can now be enqueued because the fifo's tail has
324  * advanced. Returns the number of bytes added to tail.
325  */
326 static int
327 ooo_segment_try_collect (svm_fifo_t * f, u32 n_bytes_enqueued)
328 {
329   ooo_segment_t *s;
330   u32 index, bytes = 0;
331   i32 diff;
332
333   s = pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
334   diff = ooo_segment_distance_to_tail (f, s->start);
335
336   ASSERT (diff != n_bytes_enqueued);
337
338   if (diff > n_bytes_enqueued)
339     return 0;
340
341   /* If last tail update overlaps one/multiple ooo segments, remove them */
342   while (0 <= diff && diff < n_bytes_enqueued)
343     {
344       index = s - f->ooo_segments;
345
346       /* Segment end is beyond the tail. Advance tail and remove segment */
347       if (s->length > diff)
348         {
349           bytes = s->length - diff;
350           f->tail += bytes;
351           f->tail %= f->nitems;
352           ooo_segment_del (f, index);
353           break;
354         }
355
356       /* If we have next go on */
357       if (s->next != OOO_SEGMENT_INVALID_INDEX)
358         {
359           s = pool_elt_at_index (f->ooo_segments, s->next);
360           diff = ooo_segment_distance_to_tail (f, s->start);
361           ooo_segment_del (f, index);
362         }
363       /* End of search */
364       else
365         {
366           ooo_segment_del (f, index);
367           break;
368         }
369     }
370
371   ASSERT (bytes >= 0 && bytes <= f->nitems);
372   return bytes;
373 }
374
375 static int
376 svm_fifo_enqueue_internal (svm_fifo_t * f, u32 max_bytes, u8 * copy_from_here)
377 {
378   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
379   u32 cursize, nitems;
380
381   /* read cursize, which can only increase while we're working */
382   cursize = svm_fifo_max_dequeue (f);
383   f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
384
385   if (PREDICT_FALSE (cursize == f->nitems))
386     return -2;                  /* fifo stuffed */
387
388   nitems = f->nitems;
389
390   /* Number of bytes we're going to copy */
391   total_copy_bytes = (nitems - cursize) < max_bytes ?
392     (nitems - cursize) : max_bytes;
393
394   if (PREDICT_TRUE (copy_from_here != 0))
395     {
396       /* Number of bytes in first copy segment */
397       first_copy_bytes = ((nitems - f->tail) < total_copy_bytes)
398         ? (nitems - f->tail) : total_copy_bytes;
399
400       clib_memcpy (&f->data[f->tail], copy_from_here, first_copy_bytes);
401       f->tail += first_copy_bytes;
402       f->tail = (f->tail == nitems) ? 0 : f->tail;
403
404       /* Number of bytes in second copy segment, if any */
405       second_copy_bytes = total_copy_bytes - first_copy_bytes;
406       if (second_copy_bytes)
407         {
408           clib_memcpy (&f->data[f->tail], copy_from_here + first_copy_bytes,
409                        second_copy_bytes);
410           f->tail += second_copy_bytes;
411           f->tail = (f->tail == nitems) ? 0 : f->tail;
412         }
413     }
414   else
415     {
416       ASSERT (0);
417
418       /* Account for a zero-copy enqueue done elsewhere */
419       ASSERT (max_bytes <= (nitems - cursize));
420       f->tail += max_bytes;
421       f->tail = f->tail % nitems;
422       total_copy_bytes = max_bytes;
423     }
424
425   /* Any out-of-order segments to collect? */
426   if (PREDICT_FALSE (f->ooos_list_head != OOO_SEGMENT_INVALID_INDEX))
427     total_copy_bytes += ooo_segment_try_collect (f, total_copy_bytes);
428
429   /* Atomically increase the queue length */
430   ASSERT (cursize + total_copy_bytes <= nitems);
431   __sync_fetch_and_add (&f->cursize, total_copy_bytes);
432
433   return (total_copy_bytes);
434 }
435
436 #define SVM_ENQUEUE_CLONE_TEMPLATE(arch, fn, tgt)                       \
437   uword                                                                 \
438   __attribute__ ((flatten))                                             \
439   __attribute__ ((target (tgt)))                                        \
440   CLIB_CPU_OPTIMIZED                                                    \
441   fn ## _ ## arch ( svm_fifo_t * f, u32 max_bytes, u8 * copy_from_here) \
442   { return fn (f, max_bytes, copy_from_here);}
443
444 static int
445 svm_fifo_enqueue_nowait_ma (svm_fifo_t * f, u32 max_bytes,
446                             u8 * copy_from_here)
447 {
448   return svm_fifo_enqueue_internal (f, max_bytes, copy_from_here);
449 }
450
451 foreach_march_variant (SVM_ENQUEUE_CLONE_TEMPLATE,
452                        svm_fifo_enqueue_nowait_ma);
453 CLIB_MULTIARCH_SELECT_FN (svm_fifo_enqueue_nowait_ma);
454
455 int
456 svm_fifo_enqueue_nowait (svm_fifo_t * f, u32 max_bytes, u8 * copy_from_here)
457 {
458 #if CLIB_DEBUG > 0
459   return svm_fifo_enqueue_nowait_ma (f, max_bytes, copy_from_here);
460 #else
461   static int (*fp) (svm_fifo_t *, u32, u8 *);
462
463   if (PREDICT_FALSE (fp == 0))
464     fp = (void *) svm_fifo_enqueue_nowait_ma_multiarch_select ();
465
466   return (*fp) (f, max_bytes, copy_from_here);
467 #endif
468 }
469
470 /**
471  * Enqueue a future segment.
472  *
473  * Two choices: either copies the entire segment, or copies nothing
474  * Returns 0 of the entire segment was copied
475  * Returns -1 if none of the segment was copied due to lack of space
476  */
477 static int
478 svm_fifo_enqueue_with_offset_internal (svm_fifo_t * f,
479                                        u32 offset,
480                                        u32 required_bytes,
481                                        u8 * copy_from_here)
482 {
483   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
484   u32 cursize, nitems, normalized_offset;
485   u32 offset_from_tail;
486
487   f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
488
489   /* read cursize, which can only increase while we're working */
490   cursize = svm_fifo_max_dequeue (f);
491   nitems = f->nitems;
492
493   ASSERT (required_bytes < nitems);
494
495   normalized_offset = (f->tail + offset) % nitems;
496
497   /* Will this request fit? */
498   offset_from_tail = (nitems + normalized_offset - f->tail) % nitems;
499   if ((required_bytes + offset_from_tail) > (nitems - cursize))
500     return -1;
501
502   ooo_segment_add (f, offset, required_bytes);
503
504   /* Number of bytes we're going to copy */
505   total_copy_bytes = required_bytes;
506
507   /* Number of bytes in first copy segment */
508   first_copy_bytes = ((nitems - normalized_offset) < total_copy_bytes)
509     ? (nitems - normalized_offset) : total_copy_bytes;
510
511   clib_memcpy (&f->data[normalized_offset], copy_from_here, first_copy_bytes);
512
513   /* Number of bytes in second copy segment, if any */
514   second_copy_bytes = total_copy_bytes - first_copy_bytes;
515   if (second_copy_bytes)
516     {
517       normalized_offset += first_copy_bytes;
518       normalized_offset %= nitems;
519
520       ASSERT (normalized_offset == 0);
521
522       clib_memcpy (&f->data[normalized_offset],
523                    copy_from_here + first_copy_bytes, second_copy_bytes);
524     }
525
526   return (0);
527 }
528
529
530 int
531 svm_fifo_enqueue_with_offset (svm_fifo_t * f,
532                               u32 offset,
533                               u32 required_bytes, u8 * copy_from_here)
534 {
535   return svm_fifo_enqueue_with_offset_internal (f, offset, required_bytes,
536                                                 copy_from_here);
537 }
538
539
540 static int
541 svm_fifo_dequeue_internal (svm_fifo_t * f, u32 max_bytes, u8 * copy_here)
542 {
543   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
544   u32 cursize, nitems;
545
546   /* read cursize, which can only increase while we're working */
547   cursize = svm_fifo_max_dequeue (f);
548   if (PREDICT_FALSE (cursize == 0))
549     return -2;                  /* nothing in the fifo */
550
551   nitems = f->nitems;
552
553   /* Number of bytes we're going to copy */
554   total_copy_bytes = (cursize < max_bytes) ? cursize : max_bytes;
555
556   if (PREDICT_TRUE (copy_here != 0))
557     {
558       /* Number of bytes in first copy segment */
559       first_copy_bytes = ((nitems - f->head) < total_copy_bytes)
560         ? (nitems - f->head) : total_copy_bytes;
561       clib_memcpy (copy_here, &f->data[f->head], first_copy_bytes);
562       f->head += first_copy_bytes;
563       f->head = (f->head == nitems) ? 0 : f->head;
564
565       /* Number of bytes in second copy segment, if any */
566       second_copy_bytes = total_copy_bytes - first_copy_bytes;
567       if (second_copy_bytes)
568         {
569           clib_memcpy (copy_here + first_copy_bytes,
570                        &f->data[f->head], second_copy_bytes);
571           f->head += second_copy_bytes;
572           f->head = (f->head == nitems) ? 0 : f->head;
573         }
574     }
575   else
576     {
577       ASSERT (0);
578       /* Account for a zero-copy dequeue done elsewhere */
579       ASSERT (max_bytes <= cursize);
580       f->head += max_bytes;
581       f->head = f->head % nitems;
582       cursize -= max_bytes;
583       total_copy_bytes = max_bytes;
584     }
585
586   ASSERT (f->head <= nitems);
587   ASSERT (cursize >= total_copy_bytes);
588   __sync_fetch_and_sub (&f->cursize, total_copy_bytes);
589
590   return (total_copy_bytes);
591 }
592
593 static int
594 svm_fifo_dequeue_nowait_ma (svm_fifo_t * f, u32 max_bytes, u8 * copy_here)
595 {
596   return svm_fifo_dequeue_internal (f, max_bytes, copy_here);
597 }
598
599 #define SVM_FIFO_DEQUEUE_CLONE_TEMPLATE(arch, fn, tgt)          \
600   uword                                                         \
601   __attribute__ ((flatten))                                     \
602   __attribute__ ((target (tgt)))                                \
603   CLIB_CPU_OPTIMIZED                                            \
604   fn ## _ ## arch ( svm_fifo_t * f, u32 max_bytes,              \
605                     u8 * copy_here)                             \
606   { return fn (f, max_bytes, copy_here);}
607
608 foreach_march_variant (SVM_FIFO_DEQUEUE_CLONE_TEMPLATE,
609                        svm_fifo_dequeue_nowait_ma);
610 CLIB_MULTIARCH_SELECT_FN (svm_fifo_dequeue_nowait_ma);
611
612 int
613 svm_fifo_dequeue_nowait (svm_fifo_t * f, u32 max_bytes, u8 * copy_here)
614 {
615 #if CLIB_DEBUG > 0
616   return svm_fifo_dequeue_nowait_ma (f, max_bytes, copy_here);
617 #else
618   static int (*fp) (svm_fifo_t *, u32, u8 *);
619
620   if (PREDICT_FALSE (fp == 0))
621     fp = (void *) svm_fifo_dequeue_nowait_ma_multiarch_select ();
622
623   return (*fp) (f, max_bytes, copy_here);
624 #endif
625 }
626
627 static int
628 svm_fifo_peek_ma (svm_fifo_t * f, u32 relative_offset, u32 max_bytes,
629                   u8 * copy_here)
630 {
631   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
632   u32 cursize, nitems, real_head;
633
634   /* read cursize, which can only increase while we're working */
635   cursize = svm_fifo_max_dequeue (f);
636   if (PREDICT_FALSE (cursize < relative_offset))
637     return -2;                  /* nothing in the fifo */
638
639   nitems = f->nitems;
640   real_head = f->head + relative_offset;
641   real_head = real_head >= nitems ? real_head - nitems : real_head;
642
643   /* Number of bytes we're going to copy */
644   total_copy_bytes = (cursize - relative_offset < max_bytes) ?
645     cursize - relative_offset : max_bytes;
646
647   if (PREDICT_TRUE (copy_here != 0))
648     {
649       /* Number of bytes in first copy segment */
650       first_copy_bytes =
651         ((nitems - real_head) < total_copy_bytes) ?
652         (nitems - real_head) : total_copy_bytes;
653       clib_memcpy (copy_here, &f->data[real_head], first_copy_bytes);
654
655       /* Number of bytes in second copy segment, if any */
656       second_copy_bytes = total_copy_bytes - first_copy_bytes;
657       if (second_copy_bytes)
658         {
659           clib_memcpy (copy_here + first_copy_bytes, &f->data[0],
660                        second_copy_bytes);
661         }
662     }
663   return total_copy_bytes;
664 }
665
666 #define SVM_FIFO_PEEK_CLONE_TEMPLATE(arch, fn, tgt)                     \
667   uword                                                                 \
668   __attribute__ ((flatten))                                             \
669   __attribute__ ((target (tgt)))                                        \
670   CLIB_CPU_OPTIMIZED                                                    \
671   fn ## _ ## arch ( svm_fifo_t * f, u32 relative_offset, u32 max_bytes, \
672                     u8 * copy_here)                                     \
673   { return fn (f, relative_offset, max_bytes, copy_here);}
674
675 foreach_march_variant (SVM_FIFO_PEEK_CLONE_TEMPLATE, svm_fifo_peek_ma);
676 CLIB_MULTIARCH_SELECT_FN (svm_fifo_peek_ma);
677
678 int
679 svm_fifo_peek (svm_fifo_t * f, u32 relative_offset, u32 max_bytes,
680                u8 * copy_here)
681 {
682 #if CLIB_DEBUG > 0
683   return svm_fifo_peek_ma (f, relative_offset, max_bytes, copy_here);
684 #else
685   static int (*fp) (svm_fifo_t *, u32, u32, u8 *);
686
687   if (PREDICT_FALSE (fp == 0))
688     fp = (void *) svm_fifo_peek_ma_multiarch_select ();
689
690   return (*fp) (f, relative_offset, max_bytes, copy_here);
691 #endif
692 }
693
694 int
695 svm_fifo_dequeue_drop (svm_fifo_t * f, u32 max_bytes)
696 {
697   u32 total_drop_bytes, first_drop_bytes, second_drop_bytes;
698   u32 cursize, nitems;
699
700   /* read cursize, which can only increase while we're working */
701   cursize = svm_fifo_max_dequeue (f);
702   if (PREDICT_FALSE (cursize == 0))
703     return -2;                  /* nothing in the fifo */
704
705   nitems = f->nitems;
706
707   /* Number of bytes we're going to drop */
708   total_drop_bytes = (cursize < max_bytes) ? cursize : max_bytes;
709
710   /* Number of bytes in first copy segment */
711   first_drop_bytes =
712     ((nitems - f->head) < total_drop_bytes) ?
713     (nitems - f->head) : total_drop_bytes;
714   f->head += first_drop_bytes;
715   f->head = (f->head == nitems) ? 0 : f->head;
716
717   /* Number of bytes in second drop segment, if any */
718   second_drop_bytes = total_drop_bytes - first_drop_bytes;
719   if (second_drop_bytes)
720     {
721       f->head += second_drop_bytes;
722       f->head = (f->head == nitems) ? 0 : f->head;
723     }
724
725   ASSERT (f->head <= nitems);
726   ASSERT (cursize >= total_drop_bytes);
727   __sync_fetch_and_sub (&f->cursize, total_drop_bytes);
728
729   return total_drop_bytes;
730 }
731
732 u32
733 svm_fifo_number_ooo_segments (svm_fifo_t * f)
734 {
735   return pool_elts (f->ooo_segments);
736 }
737
738 ooo_segment_t *
739 svm_fifo_first_ooo_segment (svm_fifo_t * f)
740 {
741   return pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
742 }
743
744 /**
745  * Set fifo pointers to requested offset
746  */
747 void
748 svm_fifo_init_pointers (svm_fifo_t * f, u32 pointer)
749 {
750   f->head = f->tail = pointer % f->nitems;
751 }
752
753 /*
754  * fd.io coding-style-patch-verification: ON
755  *
756  * Local Variables:
757  * eval: (c-set-style "gnu")
758  * End:
759  */