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