Cleanup URI code and TCP bugfixing
[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_fifo.h"
17
18 /** create an svm fifo, in the current heap. Fails vs blow up the process */
19 svm_fifo_t *
20 svm_fifo_create (u32 data_size_in_bytes)
21 {
22   svm_fifo_t *f;
23   pthread_mutexattr_t attr;
24   pthread_condattr_t cattr;
25
26   f = clib_mem_alloc_aligned_or_null (sizeof (*f) + data_size_in_bytes,
27                                       CLIB_CACHE_LINE_BYTES);
28   if (f == 0)
29     return 0;
30
31   memset (f, 0, sizeof (*f) + data_size_in_bytes);
32   f->nitems = data_size_in_bytes;
33   f->ooos_list_head = OOO_SEGMENT_INVALID_INDEX;
34
35   memset (&attr, 0, sizeof (attr));
36   memset (&cattr, 0, sizeof (cattr));
37
38   if (pthread_mutexattr_init (&attr))
39     clib_unix_warning ("mutexattr_init");
40   if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED))
41     clib_unix_warning ("pthread_mutexattr_setpshared");
42   if (pthread_mutex_init (&f->mutex, &attr))
43     clib_unix_warning ("mutex_init");
44   if (pthread_mutexattr_destroy (&attr))
45     clib_unix_warning ("mutexattr_destroy");
46   if (pthread_condattr_init (&cattr))
47     clib_unix_warning ("condattr_init");
48   if (pthread_condattr_setpshared (&cattr, PTHREAD_PROCESS_SHARED))
49     clib_unix_warning ("condattr_setpshared");
50   if (pthread_cond_init (&f->condvar, &cattr))
51     clib_unix_warning ("cond_init1");
52   if (pthread_condattr_destroy (&cattr))
53     clib_unix_warning ("cond_init2");
54
55   return (f);
56 }
57
58 always_inline ooo_segment_t *
59 ooo_segment_new (svm_fifo_t * f, u32 start, u32 length)
60 {
61   ooo_segment_t *s;
62
63   pool_get (f->ooo_segments, s);
64
65   s->fifo_position = start;
66   s->length = length;
67
68   s->prev = s->next = OOO_SEGMENT_INVALID_INDEX;
69
70   return s;
71 }
72
73 always_inline void
74 ooo_segment_del (svm_fifo_t * f, u32 index)
75 {
76   ooo_segment_t *cur, *prev = 0, *next = 0;
77   cur = pool_elt_at_index (f->ooo_segments, index);
78
79   if (cur->next != OOO_SEGMENT_INVALID_INDEX)
80     {
81       next = pool_elt_at_index (f->ooo_segments, cur->next);
82       next->prev = cur->prev;
83     }
84
85   if (cur->prev != OOO_SEGMENT_INVALID_INDEX)
86     {
87       prev = pool_elt_at_index (f->ooo_segments, cur->prev);
88       prev->next = cur->next;
89     }
90   else
91     {
92       f->ooos_list_head = cur->next;
93     }
94
95   pool_put (f->ooo_segments, cur);
96 }
97
98 /**
99  * Add segment to fifo's out-of-order segment list. Takes care of merging
100  * adjacent segments and removing overlapping ones.
101  */
102 static void
103 ooo_segment_add (svm_fifo_t * f, u32 offset, u32 length)
104 {
105   ooo_segment_t *s, *new_s, *prev, *next, *it;
106   u32 new_index, position, end_offset, s_sof, s_eof, s_index;
107
108   position = (f->tail + offset) % f->nitems;
109   end_offset = offset + length;
110
111   if (f->ooos_list_head == OOO_SEGMENT_INVALID_INDEX)
112     {
113       s = ooo_segment_new (f, position, length);
114       f->ooos_list_head = s - f->ooo_segments;
115       f->ooos_newest = f->ooos_list_head;
116       return;
117     }
118
119   /* Find first segment that starts after new segment */
120   s = pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
121   while (s->next != OOO_SEGMENT_INVALID_INDEX
122          && ooo_segment_offset (f, s) <= offset)
123     s = pool_elt_at_index (f->ooo_segments, s->next);
124
125   s_index = s - f->ooo_segments;
126   s_sof = ooo_segment_offset (f, s);
127   s_eof = ooo_segment_end_offset (f, s);
128
129   /* No overlap, add before current segment */
130   if (end_offset < s_sof)
131     {
132       new_s = ooo_segment_new (f, position, length);
133       new_index = new_s - f->ooo_segments;
134
135       /* Pool might've moved, get segment again */
136       s = pool_elt_at_index (f->ooo_segments, s_index);
137
138       if (s->prev != OOO_SEGMENT_INVALID_INDEX)
139         {
140           new_s->prev = s->prev;
141
142           prev = pool_elt_at_index (f->ooo_segments, new_s->prev);
143           prev->next = new_index;
144         }
145       else
146         {
147           /* New head */
148           f->ooos_list_head = new_index;
149         }
150
151       new_s->next = s - f->ooo_segments;
152       s->prev = new_index;
153       f->ooos_newest = new_index;
154       return;
155     }
156   /* No overlap, add after current segment */
157   else if (s_eof < offset)
158     {
159       new_s = ooo_segment_new (f, position, length);
160       new_index = new_s - f->ooo_segments;
161
162       /* Pool might've moved, get segment again */
163       s = pool_elt_at_index (f->ooo_segments, s_index);
164
165       if (s->next != OOO_SEGMENT_INVALID_INDEX)
166         {
167           new_s->next = s->next;
168
169           next = pool_elt_at_index (f->ooo_segments, new_s->next);
170           next->prev = new_index;
171         }
172
173       new_s->prev = s - f->ooo_segments;
174       s->next = new_index;
175       f->ooos_newest = new_index;
176
177       return;
178     }
179
180   /*
181    * Merge needed
182    */
183
184   /* Merge at head */
185   if (offset <= s_sof)
186     {
187       /* If we have a previous, check if we overlap */
188       if (s->prev != OOO_SEGMENT_INVALID_INDEX)
189         {
190           prev = pool_elt_at_index (f->ooo_segments, s->prev);
191
192           /* New segment merges prev and current. Remove previous and
193            * update position of current. */
194           if (ooo_segment_end_offset (f, prev) >= offset)
195             {
196               s->fifo_position = prev->fifo_position;
197               s->length = s_eof - ooo_segment_offset (f, prev);
198               ooo_segment_del (f, s->prev);
199             }
200         }
201       else
202         {
203           s->fifo_position = position;
204           s->length = s_eof - ooo_segment_offset (f, s);
205         }
206
207       /* The new segment's tail may cover multiple smaller ones */
208       if (s_eof < end_offset)
209         {
210           /* Remove segments completely covered */
211           it = (s->next != OOO_SEGMENT_INVALID_INDEX) ?
212             pool_elt_at_index (f->ooo_segments, s->next) : 0;
213           while (it && ooo_segment_end_offset (f, it) < end_offset)
214             {
215               next = (it->next != OOO_SEGMENT_INVALID_INDEX) ?
216                 pool_elt_at_index (f->ooo_segments, it->next) : 0;
217               ooo_segment_del (f, it - f->ooo_segments);
218               it = next;
219             }
220
221           /* Update length. Segment's start might have changed. */
222           s->length = end_offset - ooo_segment_offset (f, s);
223
224           /* If partial overlap with last, merge */
225           if (it && ooo_segment_offset (f, it) < end_offset)
226             {
227               s->length +=
228                 it->length - (ooo_segment_offset (f, it) - end_offset);
229               ooo_segment_del (f, it - f->ooo_segments);
230             }
231         }
232     }
233   /* Last but overlapping previous */
234   else if (s_eof <= end_offset)
235     {
236       s->length = end_offset - ooo_segment_offset (f, s);
237     }
238   /* New segment completely covered by current one */
239   else
240     {
241       /* Do Nothing */
242     }
243
244   /* Most recently updated segment */
245   f->ooos_newest = s - f->ooo_segments;
246 }
247
248 /**
249  * Removes segments that can now be enqueued because the fifo's tail has
250  * advanced. Returns the number of bytes added to tail.
251  */
252 static int
253 ooo_segment_try_collect (svm_fifo_t * f, u32 n_bytes_enqueued)
254 {
255   ooo_segment_t *s;
256   u32 index, bytes = 0, diff;
257
258   s = pool_elt_at_index (f->ooo_segments, f->ooos_list_head);
259
260   /* If last tail update overlaps one/multiple ooo segments, remove them */
261   diff = (f->nitems + f->tail - s->fifo_position) % f->nitems;
262   while (0 < diff && diff < n_bytes_enqueued)
263     {
264       /* Segment end is beyond the tail. Advance tail and be done */
265       if (diff < s->length)
266         {
267           f->tail += s->length - diff;
268           f->tail %= f->nitems;
269           break;
270         }
271       /* If we have next go on */
272       else if (s->next != OOO_SEGMENT_INVALID_INDEX)
273         {
274           index = s - f->ooo_segments;
275           s = pool_elt_at_index (f->ooo_segments, s->next);
276           diff = (f->nitems + f->tail - s->fifo_position) % f->nitems;
277           ooo_segment_del (f, index);
278         }
279       /* End of search */
280       else
281         {
282           break;
283         }
284     }
285
286   /* If tail is adjacent to an ooo segment, 'consume' it */
287   if (diff == 0)
288     {
289       bytes = ((f->nitems - f->cursize) >= s->length) ? s->length :
290         f->nitems - f->cursize;
291
292       f->tail += bytes;
293       f->tail %= f->nitems;
294
295       ooo_segment_del (f, s - f->ooo_segments);
296     }
297
298   return bytes;
299 }
300
301 static int
302 svm_fifo_enqueue_internal (svm_fifo_t * f,
303                            int pid, u32 max_bytes, u8 * copy_from_here)
304 {
305   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
306   u32 cursize, nitems;
307
308   if (PREDICT_FALSE (f->cursize == f->nitems))
309     return -2;                  /* fifo stuffed */
310
311   /* read cursize, which can only decrease while we're working */
312   cursize = f->cursize;
313   nitems = f->nitems;
314
315   /* Number of bytes we're going to copy */
316   total_copy_bytes = (nitems - cursize) < max_bytes ?
317     (nitems - cursize) : max_bytes;
318
319   if (PREDICT_TRUE (copy_from_here != 0))
320     {
321       /* Number of bytes in first copy segment */
322       first_copy_bytes = ((nitems - f->tail) < total_copy_bytes)
323         ? (nitems - f->tail) : total_copy_bytes;
324
325       clib_memcpy (&f->data[f->tail], copy_from_here, first_copy_bytes);
326       f->tail += first_copy_bytes;
327       f->tail = (f->tail == nitems) ? 0 : f->tail;
328
329       /* Number of bytes in second copy segment, if any */
330       second_copy_bytes = total_copy_bytes - first_copy_bytes;
331       if (second_copy_bytes)
332         {
333           clib_memcpy (&f->data[f->tail], copy_from_here + first_copy_bytes,
334                        second_copy_bytes);
335           f->tail += second_copy_bytes;
336           f->tail = (f->tail == nitems) ? 0 : f->tail;
337         }
338     }
339   else
340     {
341       /* Account for a zero-copy enqueue done elsewhere */
342       ASSERT (max_bytes <= (nitems - cursize));
343       f->tail += max_bytes;
344       f->tail = f->tail % nitems;
345       total_copy_bytes = max_bytes;
346     }
347
348   /* Any out-of-order segments to collect? */
349   if (PREDICT_FALSE (f->ooos_list_head != OOO_SEGMENT_INVALID_INDEX))
350     total_copy_bytes += ooo_segment_try_collect (f, total_copy_bytes);
351
352   /* Atomically increase the queue length */
353   __sync_fetch_and_add (&f->cursize, total_copy_bytes);
354
355   return (total_copy_bytes);
356 }
357
358 int
359 svm_fifo_enqueue_nowait (svm_fifo_t * f,
360                          int pid, u32 max_bytes, u8 * copy_from_here)
361 {
362   return svm_fifo_enqueue_internal (f, pid, max_bytes, copy_from_here);
363 }
364
365 /** Enqueue a future segment.
366  * Two choices: either copies the entire segment, or copies nothing
367  * Returns 0 of the entire segment was copied
368  * Returns -1 if none of the segment was copied due to lack of space
369  */
370
371 static int
372 svm_fifo_enqueue_with_offset_internal2 (svm_fifo_t * f,
373                                         int pid,
374                                         u32 offset,
375                                         u32 required_bytes,
376                                         u8 * copy_from_here)
377 {
378   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
379   u32 cursize, nitems;
380   u32 tail_plus_offset;
381
382   ASSERT (offset > 0);
383
384   /* read cursize, which can only decrease while we're working */
385   cursize = f->cursize;
386   nitems = f->nitems;
387
388   /* Will this request fit? */
389   if ((required_bytes + offset) > (nitems - cursize))
390     return -1;
391
392   ooo_segment_add (f, offset, required_bytes);
393
394   /* Number of bytes we're going to copy */
395   total_copy_bytes = required_bytes;
396   tail_plus_offset = (f->tail + offset) % nitems;
397
398   /* Number of bytes in first copy segment */
399   first_copy_bytes = ((nitems - tail_plus_offset) < total_copy_bytes)
400     ? (nitems - tail_plus_offset) : total_copy_bytes;
401
402   clib_memcpy (&f->data[tail_plus_offset], copy_from_here, first_copy_bytes);
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       tail_plus_offset += first_copy_bytes;
409       tail_plus_offset %= nitems;
410
411       ASSERT (tail_plus_offset == 0);
412
413       clib_memcpy (&f->data[tail_plus_offset],
414                    copy_from_here + first_copy_bytes, second_copy_bytes);
415     }
416
417   return (0);
418 }
419
420
421 int
422 svm_fifo_enqueue_with_offset (svm_fifo_t * f,
423                               int pid,
424                               u32 offset,
425                               u32 required_bytes, u8 * copy_from_here)
426 {
427   return svm_fifo_enqueue_with_offset_internal2
428     (f, pid, offset, required_bytes, copy_from_here);
429 }
430
431
432 static int
433 svm_fifo_dequeue_internal2 (svm_fifo_t * f,
434                             int pid, u32 max_bytes, u8 * copy_here)
435 {
436   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
437   u32 cursize, nitems;
438
439   if (PREDICT_FALSE (f->cursize == 0))
440     return -2;                  /* nothing in the fifo */
441
442   /* read cursize, which can only increase while we're working */
443   cursize = f->cursize;
444   nitems = f->nitems;
445
446   /* Number of bytes we're going to copy */
447   total_copy_bytes = (cursize < max_bytes) ? cursize : max_bytes;
448
449   if (PREDICT_TRUE (copy_here != 0))
450     {
451       /* Number of bytes in first copy segment */
452       first_copy_bytes = ((nitems - f->head) < total_copy_bytes)
453         ? (nitems - f->head) : total_copy_bytes;
454       clib_memcpy (copy_here, &f->data[f->head], first_copy_bytes);
455       f->head += first_copy_bytes;
456       f->head = (f->head == nitems) ? 0 : f->head;
457
458       /* Number of bytes in second copy segment, if any */
459       second_copy_bytes = total_copy_bytes - first_copy_bytes;
460       if (second_copy_bytes)
461         {
462           clib_memcpy (copy_here + first_copy_bytes,
463                        &f->data[f->head], second_copy_bytes);
464           f->head += second_copy_bytes;
465           f->head = (f->head == nitems) ? 0 : f->head;
466         }
467     }
468   else
469     {
470       /* Account for a zero-copy dequeue done elsewhere */
471       ASSERT (max_bytes <= cursize);
472       f->head += max_bytes;
473       f->head = f->head % nitems;
474       cursize -= max_bytes;
475       total_copy_bytes = max_bytes;
476     }
477
478   __sync_fetch_and_sub (&f->cursize, total_copy_bytes);
479
480   return (total_copy_bytes);
481 }
482
483 int
484 svm_fifo_dequeue_nowait (svm_fifo_t * f,
485                          int pid, u32 max_bytes, u8 * copy_here)
486 {
487   return svm_fifo_dequeue_internal2 (f, pid, max_bytes, copy_here);
488 }
489
490 int
491 svm_fifo_peek (svm_fifo_t * f, int pid, u32 offset, u32 max_bytes,
492                u8 * copy_here)
493 {
494   u32 total_copy_bytes, first_copy_bytes, second_copy_bytes;
495   u32 cursize, nitems;
496
497   if (PREDICT_FALSE (f->cursize == 0))
498     return -2;                  /* nothing in the fifo */
499
500   /* read cursize, which can only increase while we're working */
501   cursize = f->cursize;
502   nitems = f->nitems;
503
504   /* Number of bytes we're going to copy */
505   total_copy_bytes = (cursize < max_bytes) ? cursize : max_bytes;
506
507   if (PREDICT_TRUE (copy_here != 0))
508     {
509       /* Number of bytes in first copy segment */
510       first_copy_bytes =
511         ((nitems - f->head + offset) < total_copy_bytes) ?
512         (nitems - f->head + offset) : total_copy_bytes;
513       clib_memcpy (copy_here, &f->data[f->head + offset], first_copy_bytes);
514
515       /* Number of bytes in second copy segment, if any */
516       second_copy_bytes = total_copy_bytes - first_copy_bytes;
517       if (second_copy_bytes)
518         {
519           clib_memcpy (copy_here + first_copy_bytes, &f->data[0],
520                        second_copy_bytes);
521         }
522     }
523   return total_copy_bytes;
524 }
525
526 int
527 svm_fifo_dequeue_drop (svm_fifo_t * f, int pid, u32 max_bytes)
528 {
529   u32 total_drop_bytes, first_drop_bytes, second_drop_bytes;
530   u32 cursize, nitems;
531
532   if (PREDICT_FALSE (f->cursize == 0))
533     return -2;                  /* nothing in the fifo */
534
535   /* read cursize, which can only increase while we're working */
536   cursize = f->cursize;
537   nitems = f->nitems;
538
539   /* Number of bytes we're going to drop */
540   total_drop_bytes = (cursize < max_bytes) ? cursize : max_bytes;
541
542   /* Number of bytes in first copy segment */
543   first_drop_bytes =
544     ((nitems - f->head) < total_drop_bytes) ?
545     (nitems - f->head) : total_drop_bytes;
546   f->head += first_drop_bytes;
547   f->head = (f->head == nitems) ? 0 : f->head;
548
549   /* Number of bytes in second drop segment, if any */
550   second_drop_bytes = total_drop_bytes - first_drop_bytes;
551   if (second_drop_bytes)
552     {
553       f->head += second_drop_bytes;
554       f->head = (f->head == nitems) ? 0 : f->head;
555     }
556
557   __sync_fetch_and_sub (&f->cursize, total_drop_bytes);
558
559   return total_drop_bytes;
560 }
561
562 /*
563  * fd.io coding-style-patch-verification: ON
564  *
565  * Local Variables:
566  * eval: (c-set-style "gnu")
567  * End:
568  */