FD.io VPP  v17.04-9-g99c0734
Vector Packet Processing
svm_fifo.c
Go to the documentation of this file.
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,
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;
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 
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 
69 
70  return s;
71 }
72 
73 always_inline void
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
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 
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 */
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 
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 
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 */
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 
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
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? */
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
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
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
423  int pid,
424  u32 offset,
425  u32 required_bytes, u8 * copy_from_here)
426 {
428  (f, pid, offset, required_bytes, copy_from_here);
429 }
430 
431 
432 static int
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
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  */
int svm_fifo_enqueue_nowait(svm_fifo_t *f, int pid, u32 max_bytes, u8 *copy_from_here)
Definition: svm_fifo.c:359
#define PREDICT_TRUE(x)
Definition: clib.h:98
u32 prev
Previous linked-list element pool index.
Definition: svm_fifo.h:37
static int ooo_segment_try_collect(svm_fifo_t *f, u32 n_bytes_enqueued)
Removes segments that can now be enqueued because the fifo&#39;s tail has advanced.
Definition: svm_fifo.c:253
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:200
u32 tail
Definition: svm_fifo.h:64
pthread_cond_t condvar
Definition: svm_fifo.h:48
static void ooo_segment_add(svm_fifo_t *f, u32 offset, u32 length)
Add segment to fifo&#39;s out-of-order segment list.
Definition: svm_fifo.c:103
static u32 ooo_segment_end_offset(svm_fifo_t *f, ooo_segment_t *s)
Definition: svm_fifo.h:144
#define always_inline
Definition: clib.h:84
static int svm_fifo_enqueue_internal(svm_fifo_t *f, int pid, u32 max_bytes, u8 *copy_from_here)
Definition: svm_fifo.c:302
u32 ooos_newest
Last segment to have been updated.
Definition: svm_fifo.h:68
volatile u32 cursize
Definition: svm_fifo.h:51
ooo_segment_t * ooo_segments
Pool of ooo segments.
Definition: svm_fifo.h:66
static int svm_fifo_dequeue_internal2(svm_fifo_t *f, int pid, u32 max_bytes, u8 *copy_here)
Definition: svm_fifo.c:433
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:397
static u32 ooo_segment_offset(svm_fifo_t *f, ooo_segment_t *s)
Definition: svm_fifo.h:138
int svm_fifo_dequeue_drop(svm_fifo_t *f, int pid, u32 max_bytes)
Definition: svm_fifo.c:527
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:241
#define PREDICT_FALSE(x)
Definition: clib.h:97
pthread_mutex_t mutex
Definition: svm_fifo.h:47
#define clib_memcpy(a, b, c)
Definition: string.h:69
static int svm_fifo_enqueue_with_offset_internal2(svm_fifo_t *f, int pid, u32 offset, u32 required_bytes, u8 *copy_from_here)
Enqueue a future segment.
Definition: svm_fifo.c:372
static void ooo_segment_del(svm_fifo_t *f, u32 index)
Definition: svm_fifo.c:74
#define OOO_SEGMENT_INVALID_INDEX
Definition: svm_fifo.h:43
static void * clib_mem_alloc_aligned_or_null(uword size, uword align)
Definition: mem.h:133
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
int svm_fifo_enqueue_with_offset(svm_fifo_t *f, int pid, u32 offset, u32 required_bytes, u8 *copy_from_here)
Definition: svm_fifo.c:422
u32 fifo_position
Start of segment, normalized.
Definition: svm_fifo.h:39
Out-of-order segment.
Definition: svm_fifo.h:34
u32 ooos_list_head
Head of out-of-order linked-list.
Definition: svm_fifo.h:67
u32 length
Length of segment.
Definition: svm_fifo.h:40
u32 next
Next linked-list element pool index.
Definition: svm_fifo.h:36
template key/value backing page structure
Definition: bihash_doc.h:44
u32 nitems
Definition: svm_fifo.h:52
unsigned char u8
Definition: types.h:56
int svm_fifo_peek(svm_fifo_t *f, int pid, u32 offset, u32 max_bytes, u8 *copy_here)
Definition: svm_fifo.c:491
static ooo_segment_t * ooo_segment_new(svm_fifo_t *f, u32 start, u32 length)
Definition: svm_fifo.c:59
#define clib_unix_warning(format, args...)
Definition: error.h:68
struct clib_bihash_value offset
template key/value backing page structure
int svm_fifo_dequeue_nowait(svm_fifo_t *f, int pid, u32 max_bytes, u8 *copy_here)
Definition: svm_fifo.c:484
u32 head
Definition: svm_fifo.h:60
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
svm_fifo_t * svm_fifo_create(u32 data_size_in_bytes)
create an svm fifo, in the current heap.
Definition: svm_fifo.c:20