svm: fifo ooo reads/writes with multiple chunks
[vpp.git] / src / svm / svm_fifo.h
1 /*
2  * Copyright (c) 2016-2019 Cisco and/or its affiliates.
3  * Copyright (c) 2019 Arm Limited
4  * Copyright (c) 2010-2017 Intel Corporation and/or its affiliates.
5  * Copyright (c) 2007-2009 Kip Macy kmacy@freebsd.org
6  * Inspired from DPDK rte_ring.h (SPSC only) (derived from freebsd bufring.h).
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at:
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 #ifndef __included_ssvm_fifo_h__
20 #define __included_ssvm_fifo_h__
21
22 #include <vppinfra/clib.h>
23 #include <vppinfra/vec.h>
24 #include <vppinfra/pool.h>
25 #include <vppinfra/format.h>
26 #include <vppinfra/rbtree.h>
27
28 /** Out-of-order segment */
29 typedef struct
30 {
31   u32 next;     /**< Next linked-list element pool index */
32   u32 prev;     /**< Previous linked-list element pool index */
33
34   u32 start;    /**< Start of segment, normalized*/
35   u32 length;   /**< Length of segment */
36 } ooo_segment_t;
37
38 #define SVM_FIFO_TRACE                  (0)
39 #define OOO_SEGMENT_INVALID_INDEX       ((u32)~0)
40 #define SVM_FIFO_INVALID_SESSION_INDEX  ((u32)~0)
41 #define SVM_FIFO_INVALID_INDEX          ((u32)~0)
42 #define SVM_FIFO_MAX_EVT_SUBSCRIBERS    7
43
44 enum svm_fifo_tx_ntf_
45 {
46   SVM_FIFO_NO_TX_NOTIF = 0,
47   SVM_FIFO_WANT_TX_NOTIF = 1,
48   SVM_FIFO_WANT_TX_NOTIF_IF_FULL = 2,
49 };
50
51 typedef struct
52 {
53   u32 offset;
54   u32 len;
55   u32 action;
56 } svm_fifo_trace_elem_t;
57
58 typedef struct svm_fifo_chunk_
59 {
60   u32 start_byte;
61   u32 length;
62   struct svm_fifo_chunk_ *next;
63   u8 data[0];
64 } svm_fifo_chunk_t;
65
66 typedef enum svm_fifo_flag_
67 {
68   SVM_FIFO_F_SIZE_UPDATE = 1 << 0,
69   SVM_FIFO_F_MULTI_CHUNK = 1 << 1,
70 } svm_fifo_flag_t;
71
72 typedef struct _svm_fifo
73 {
74   CLIB_CACHE_LINE_ALIGN_MARK (shared_first);
75   u32 size;                     /**< size of the fifo */
76   u32 nitems;                   /**< usable size(size-1) */
77   u8 flags;                     /**< fifo flags */
78   svm_fifo_chunk_t *start_chunk;/**< first chunk in fifo chunk list */
79   svm_fifo_chunk_t *end_chunk;  /**< end chunk in fifo chunk list */
80   svm_fifo_chunk_t *new_chunks; /**< chunks yet to be added to list */
81   rb_tree_t chunk_lookup;
82
83     CLIB_CACHE_LINE_ALIGN_MARK (shared_second);
84   volatile u32 has_event;       /**< non-zero if deq event exists */
85
86   u32 master_session_index;
87   u32 client_session_index;
88   u8 master_thread_index;
89   u8 client_thread_index;
90   u32 segment_manager;
91   u32 segment_index;
92   u32 ct_session_index;         /**< Local session index for vpp */
93   u32 freelist_index;           /**< aka log2(allocated_size) - const. */
94   i8 refcnt;                    /**< reference count  */
95   struct _svm_fifo *next;       /**< next in freelist/active chain */
96   struct _svm_fifo *prev;       /**< prev in active chain */
97
98     CLIB_CACHE_LINE_ALIGN_MARK (consumer);
99   u32 head;                     /**< fifo head position/byte */
100   svm_fifo_chunk_t *head_chunk; /**< tracks chunk where head lands */
101   svm_fifo_chunk_t *ooo_deq;    /**< last chunk used for ooo dequeue */
102   volatile u32 want_tx_ntf;     /**< producer wants nudge */
103   volatile u32 has_tx_ntf;
104
105     CLIB_CACHE_LINE_ALIGN_MARK (producer);
106   u32 tail;                     /**< fifo tail position/byte */
107   u32 ooos_list_head;           /**< Head of out-of-order linked-list */
108   svm_fifo_chunk_t *tail_chunk; /**< tracks chunk where tail lands */
109   svm_fifo_chunk_t *ooo_enq;    /**< last chunk used for ooo enqueue */
110   ooo_segment_t *ooo_segments;  /**< Pool of ooo segments */
111   u32 ooos_newest;              /**< Last segment to have been updated */
112   volatile u8 n_subscribers;
113   u8 subscribers[SVM_FIFO_MAX_EVT_SUBSCRIBERS];
114
115 #if SVM_FIFO_TRACE
116   svm_fifo_trace_elem_t *trace;
117 #endif
118
119   svm_fifo_chunk_t default_chunk;
120 } svm_fifo_t;
121
122 typedef enum
123 {
124   SVM_FIFO_FULL = -2,
125 } svm_fifo_err_t;
126
127 typedef struct svm_fifo_segment_
128 {
129   u8 *data;
130   u32 len;
131 } svm_fifo_segment_t;
132
133 #if SVM_FIFO_TRACE
134 #define svm_fifo_trace_add(_f, _s, _l, _t)              \
135 {                                                       \
136   svm_fifo_trace_elem_t *trace_elt;                     \
137   vec_add2(_f->trace, trace_elt, 1);                    \
138   trace_elt->offset = _s;                               \
139   trace_elt->len = _l;                                  \
140   trace_elt->action = _t;                               \
141 }
142 #else
143 #define svm_fifo_trace_add(_f, _s, _l, _t)
144 #endif
145
146 u8 *svm_fifo_dump_trace (u8 * s, svm_fifo_t * f);
147 u8 *svm_fifo_replay (u8 * s, svm_fifo_t * f, u8 no_read, u8 verbose);
148
149 /* internal function */
150 static inline void
151 f_load_head_tail_cons (svm_fifo_t * f, u32 * head, u32 * tail)
152 {
153   /* load-relaxed: consumer owned index */
154   *head = f->head;
155   /* load-acq: consumer foreign index (paired with store-rel in producer) */
156   *tail = clib_atomic_load_acq_n (&f->tail);
157 }
158
159 /* internal function */
160 static inline void
161 f_load_head_tail_prod (svm_fifo_t * f, u32 * head, u32 * tail)
162 {
163   /* load relaxed: producer owned index */
164   *tail = f->tail;
165   /* load-acq: producer foreign index (paired with store-rel in consumer) */
166   *head = clib_atomic_load_acq_n (&f->head);
167 }
168
169 /* producer consumer role independent */
170 /* internal function */
171 static inline void
172 f_load_head_tail_all_acq (svm_fifo_t * f, u32 * head, u32 * tail)
173 {
174   /* load-acq : consumer foreign index (paired with store-rel) */
175   *tail = clib_atomic_load_acq_n (&f->tail);
176   /* load-acq : producer foriegn index (paired with store-rel) */
177   *head = clib_atomic_load_acq_n (&f->head);
178 }
179
180 /* internal function */
181 static inline u32
182 f_free_count (svm_fifo_t * f, u32 head, u32 tail)
183 {
184   return (f->nitems + head - tail);
185 }
186
187 /* internal function */
188 static inline u32
189 f_cursize (svm_fifo_t * f, u32 head, u32 tail)
190 {
191   return (f->nitems - f_free_count (f, head, tail));
192 }
193
194 /* used by consumer */
195 static inline u32
196 svm_fifo_max_dequeue_cons (svm_fifo_t * f)
197 {
198   u32 tail, head;
199   f_load_head_tail_cons (f, &head, &tail);
200   return f_cursize (f, head, tail);
201 }
202
203 /* used by producer*/
204 static inline u32
205 svm_fifo_max_dequeue_prod (svm_fifo_t * f)
206 {
207   u32 tail, head;
208   f_load_head_tail_prod (f, &head, &tail);
209   return f_cursize (f, head, tail);
210 }
211
212 /* use producer or consumer specific functions for perfomance.
213  * svm_fifo_max_dequeue_cons (svm_fifo_t *f)
214  * svm_fifo_max_dequeue_prod (svm_fifo_t *f)
215  */
216 static inline u32
217 svm_fifo_max_dequeue (svm_fifo_t * f)
218 {
219   u32 tail, head;
220   f_load_head_tail_all_acq (f, &head, &tail);
221   return f_cursize (f, head, tail);
222 }
223
224 /* used by producer */
225 static inline int
226 svm_fifo_is_full_prod (svm_fifo_t * f)
227 {
228   return (svm_fifo_max_dequeue_prod (f) == f->nitems);
229 }
230
231 /* use producer or consumer specific functions for perfomance.
232  * svm_fifo_is_full_prod (svm_fifo_t * f)
233  * add cons version if needed
234  */
235 static inline int
236 svm_fifo_is_full (svm_fifo_t * f)
237 {
238   return (svm_fifo_max_dequeue (f) == f->nitems);
239 }
240
241 /* used by consumer */
242 static inline int
243 svm_fifo_is_empty_cons (svm_fifo_t * f)
244 {
245   return (svm_fifo_max_dequeue_cons (f) == 0);
246 }
247
248 /* used by producer */
249 static inline int
250 svm_fifo_is_empty_prod (svm_fifo_t * f)
251 {
252   return (svm_fifo_max_dequeue_prod (f) == 0);
253 }
254
255 /* use producer or consumer specific functions for perfomance.
256  * svm_fifo_is_empty_cons (svm_fifo_t * f)
257  * svm_fifo_is_empty_prod (svm_fifo_t * f)
258  */
259 static inline int
260 svm_fifo_is_empty (svm_fifo_t * f)
261 {
262   return (svm_fifo_max_dequeue (f) == 0);
263 }
264
265 static inline u8
266 svm_fifo_is_wrapped (svm_fifo_t * f)
267 {
268   u32 head, tail;
269   f_load_head_tail_all_acq (f, &head, &tail);
270   return head % f->size > tail % f->size;
271 }
272
273 /* used by producer*/
274 static inline u32
275 svm_fifo_max_enqueue_prod (svm_fifo_t * f)
276 {
277   u32 head, tail;
278   f_load_head_tail_prod (f, &head, &tail);
279   return f_free_count (f, head, tail);
280 }
281
282 /* use producer or consumer specfic functions for perfomance.
283  * svm_fifo_max_enqueue_prod (svm_fifo_t *f)
284  * add consumer specific version if needed.
285  */
286 static inline u32
287 svm_fifo_max_enqueue (svm_fifo_t * f)
288 {
289   u32 head, tail;
290   f_load_head_tail_all_acq (f, &head, &tail);
291   return f_free_count (f, head, tail);
292 }
293
294 static inline int
295 svm_fifo_has_event (svm_fifo_t * f)
296 {
297   return f->has_event;
298 }
299
300 static inline u8
301 svm_fifo_has_ooo_data (svm_fifo_t * f)
302 {
303   return f->ooos_list_head != OOO_SEGMENT_INVALID_INDEX;
304 }
305
306 /**
307  * Sets fifo event flag.
308  *
309  * Also acts as a release ordering.
310  *
311  * @return 1 if flag was not set.
312  */
313 always_inline u8
314 svm_fifo_set_event (svm_fifo_t * f)
315 {
316   /* return __sync_lock_test_and_set (&f->has_event, 1) == 0;
317      return __sync_bool_compare_and_swap (&f->has_event, 0, 1); */
318   return !clib_atomic_swap_rel_n (&f->has_event, 1);
319 }
320
321 /**
322  * Unsets fifo event flag.
323  *
324  * Also acts as an acquire barrier.
325  */
326 always_inline void
327 svm_fifo_unset_event (svm_fifo_t * f)
328 {
329   clib_atomic_swap_acq_n (&f->has_event, 0);
330 }
331
332 svm_fifo_t *svm_fifo_create (u32 data_size_in_bytes);
333 void svm_fifo_init (svm_fifo_t * f, u32 size);
334 /**
335  * Grow fifo size by adding chunk to chunk list
336  *
337  * If fifos are allocated on a segment, this should be called with
338  * the segment's heap pushed.
339  *
340  * @param f     fifo to be extended
341  * @param c     chunk or linked list of chunks to be added
342  */
343 void svm_fifo_add_chunk (svm_fifo_t * f, svm_fifo_chunk_t * c);
344 void svm_fifo_free (svm_fifo_t * f);
345
346 int svm_fifo_enqueue_nowait (svm_fifo_t * f, u32 max_bytes,
347                              const u8 * copy_from_here);
348 int svm_fifo_enqueue_with_offset (svm_fifo_t * f, u32 offset,
349                                   u32 required_bytes, u8 * copy_from_here);
350 int svm_fifo_dequeue_nowait (svm_fifo_t * f, u32 max_bytes, u8 * copy_here);
351
352 int svm_fifo_peek (svm_fifo_t * f, u32 offset, u32 max_bytes, u8 * copy_here);
353 int svm_fifo_dequeue_drop (svm_fifo_t * f, u32 max_bytes);
354 void svm_fifo_dequeue_drop_all (svm_fifo_t * f);
355 int svm_fifo_segments (svm_fifo_t * f, svm_fifo_segment_t * fs);
356 void svm_fifo_segments_free (svm_fifo_t * f, svm_fifo_segment_t * fs);
357 void svm_fifo_init_pointers (svm_fifo_t * f, u32 head, u32 tail);
358 void svm_fifo_clone (svm_fifo_t * df, svm_fifo_t * sf);
359 void svm_fifo_overwrite_head (svm_fifo_t * f, u8 * data, u32 len);
360 void svm_fifo_add_subscriber (svm_fifo_t * f, u8 subscriber);
361 void svm_fifo_del_subscriber (svm_fifo_t * f, u8 subscriber);
362 format_function_t format_svm_fifo;
363
364 /**
365  * Max contiguous chunk of data that can be read
366  */
367 always_inline u32
368 svm_fifo_max_read_chunk (svm_fifo_t * f)
369 {
370   u32 head, tail;
371   u32 head_idx, tail_idx;
372   f_load_head_tail_cons (f, &head, &tail);
373   head_idx = head % f->size;
374   tail_idx = tail % f->size;
375   return tail_idx > head_idx ? (tail_idx - head_idx) : (f->size - head_idx);
376 }
377
378 /**
379  * Max contiguous chunk of data that can be written
380  */
381 always_inline u32
382 svm_fifo_max_write_chunk (svm_fifo_t * f)
383 {
384   u32 head, tail;
385   u32 head_idx, tail_idx;
386   f_load_head_tail_prod (f, &head, &tail);
387   head_idx = head % f->size;
388   tail_idx = tail % f->size;
389   return tail_idx >= head_idx ? (f->size - tail_idx) : (head_idx - tail_idx);
390 }
391
392 /**
393  * Advance tail pointer
394  *
395  * Useful for moving tail pointer after external enqueue.
396  */
397 always_inline void
398 svm_fifo_enqueue_nocopy (svm_fifo_t * f, u32 bytes)
399 {
400   ASSERT (bytes <= svm_fifo_max_enqueue_prod (f));
401   /* load-relaxed: producer owned index */
402   u32 tail = f->tail;
403   tail += bytes;
404   /* store-rel: producer owned index (paired with load-acq in consumer) */
405   clib_atomic_store_rel_n (&f->tail, tail);
406 }
407
408 always_inline u8 *
409 svm_fifo_head (svm_fifo_t * f)
410 {
411   /* load-relaxed: consumer owned index */
412   return (f->head_chunk->data
413           + ((f->head % f->size) - f->head_chunk->start_byte));
414 }
415
416 always_inline u8 *
417 svm_fifo_tail (svm_fifo_t * f)
418 {
419   /* load-relaxed: producer owned index */
420   return (f->tail_chunk->data
421           + ((f->tail % f->size) - f->tail_chunk->start_byte));
422 }
423
424 static inline void
425 svm_fifo_add_want_tx_ntf (svm_fifo_t * f, u8 ntf_type)
426 {
427   f->want_tx_ntf |= ntf_type;
428 }
429
430 static inline void
431 svm_fifo_del_want_tx_ntf (svm_fifo_t * f, u8 ntf_type)
432 {
433   f->want_tx_ntf &= ~ntf_type;
434 }
435
436 static inline void
437 svm_fifo_clear_tx_ntf (svm_fifo_t * f)
438 {
439   /* Set the flag if want_tx_notif_if_full was the only ntf requested */
440   f->has_tx_ntf = f->want_tx_ntf == SVM_FIFO_WANT_TX_NOTIF_IF_FULL;
441   svm_fifo_del_want_tx_ntf (f, SVM_FIFO_WANT_TX_NOTIF);
442 }
443
444 static inline void
445 svm_fifo_reset_tx_ntf (svm_fifo_t * f)
446 {
447   f->has_tx_ntf = 0;
448 }
449
450 static inline u8
451 svm_fifo_needs_tx_ntf (svm_fifo_t * f, u32 n_last_deq)
452 {
453   u8 want_ntf = f->want_tx_ntf;
454
455   if (PREDICT_TRUE (want_ntf == SVM_FIFO_NO_TX_NOTIF))
456     return 0;
457   else if (want_ntf & SVM_FIFO_WANT_TX_NOTIF)
458     return 1;
459   else if (want_ntf & SVM_FIFO_WANT_TX_NOTIF_IF_FULL)
460     {
461       u32 max_deq = svm_fifo_max_dequeue_cons (f);
462       u32 nitems = f->nitems;
463       if (!f->has_tx_ntf && max_deq < nitems
464           && max_deq + n_last_deq >= nitems)
465         return 1;
466
467       return 0;
468     }
469   return 0;
470 }
471
472 always_inline u8
473 svm_fifo_n_subscribers (svm_fifo_t * f)
474 {
475   return f->n_subscribers;
476 }
477
478 u32 svm_fifo_number_ooo_segments (svm_fifo_t * f);
479 ooo_segment_t *svm_fifo_first_ooo_segment (svm_fifo_t * f);
480
481 always_inline ooo_segment_t *
482 svm_fifo_newest_ooo_segment (svm_fifo_t * f)
483 {
484   if (f->ooos_newest == OOO_SEGMENT_INVALID_INDEX)
485     return 0;
486   return pool_elt_at_index (f->ooo_segments, f->ooos_newest);
487 }
488
489 always_inline void
490 svm_fifo_newest_ooo_segment_reset (svm_fifo_t * f)
491 {
492   f->ooos_newest = OOO_SEGMENT_INVALID_INDEX;
493 }
494
495 always_inline u32
496 ooo_segment_distance_from_tail (svm_fifo_t * f, u32 pos, u32 tail)
497 {
498   return ((pos - tail) % f->size);
499 }
500
501 always_inline u32
502 ooo_segment_distance_to_tail (svm_fifo_t * f, u32 pos, u32 tail)
503 {
504   return ((tail - pos) % f->size);
505 }
506
507 always_inline u32
508 ooo_segment_offset_prod (svm_fifo_t * f, ooo_segment_t * s)
509 {
510   u32 tail;
511   /* load-relaxed: producer owned index */
512   tail = f->tail;
513
514   return ooo_segment_distance_from_tail (f, s->start, tail);
515 }
516
517 always_inline u32
518 ooo_segment_length (svm_fifo_t * f, ooo_segment_t * s)
519 {
520   return s->length;
521 }
522
523 always_inline ooo_segment_t *
524 ooo_segment_get_prev (svm_fifo_t * f, ooo_segment_t * s)
525 {
526   if (s->prev == OOO_SEGMENT_INVALID_INDEX)
527     return 0;
528   return pool_elt_at_index (f->ooo_segments, s->prev);
529 }
530
531 always_inline ooo_segment_t *
532 ooo_segment_next (svm_fifo_t * f, ooo_segment_t * s)
533 {
534   if (s->next == OOO_SEGMENT_INVALID_INDEX)
535     return 0;
536   return pool_elt_at_index (f->ooo_segments, s->next);
537 }
538
539 #endif /* __included_ssvm_fifo_h__ */
540
541 /*
542  * fd.io coding-style-patch-verification: ON
543  *
544  * Local Variables:
545  * eval: (c-set-style "gnu")
546  * End:
547  */