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