blob: c74a77c8e9bc7f72438f267d229fa65a54f2434d [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 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 Copyright (c) 2012 Eliot Dresselhaus
17
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36*/
37
38#ifndef included_clib_smp_vec_h
39#define included_clib_smp_vec_h
40
41#include <vppinfra/smp.h>
42
43#define foreach_clib_smp_fifo_data_state \
44 _ (free) \
45 _ (write_alloc) \
46 _ (write_done) \
47 _ (read_fetch)
48
Dave Barachc3799992016-08-15 11:12:27 -040049typedef enum
50{
Ed Warnickecb9cada2015-12-08 15:45:58 -070051#define _(f) CLIB_SMP_FIFO_DATA_STATE_##f,
52 foreach_clib_smp_fifo_data_state
53#undef _
Dave Barachc3799992016-08-15 11:12:27 -040054 CLIB_SMP_FIFO_N_DATA_STATE,
Ed Warnickecb9cada2015-12-08 15:45:58 -070055} clib_smp_fifo_data_state_t;
56
57/* Footer at end of each data element. */
Dave Barachc3799992016-08-15 11:12:27 -040058typedef struct
59{
Ed Warnickecb9cada2015-12-08 15:45:58 -070060 /* Magic number marking valid footer plus state encoded in low bits. */
61 u32 magic_state;
62} clib_smp_fifo_data_footer_t;
63
64#define CLIB_SMP_DATA_FOOTER_MAGIC 0xfafbfcf0
65
66always_inline clib_smp_fifo_data_state_t
67clib_smp_fifo_data_footer_get_state (clib_smp_fifo_data_footer_t * f)
68{
69 u32 s = f->magic_state - CLIB_SMP_DATA_FOOTER_MAGIC;
70
71 /* Check that magic number plus state is still valid. */
72 if (s >= CLIB_SMP_FIFO_N_DATA_STATE)
73 os_panic ();
74
75 return s;
76}
77
78always_inline void
79clib_smp_fifo_data_footer_set_state (clib_smp_fifo_data_footer_t * f,
80 clib_smp_fifo_data_state_t s)
Dave Barachc3799992016-08-15 11:12:27 -040081{
82 f->magic_state = CLIB_SMP_DATA_FOOTER_MAGIC + s;
83}
Ed Warnickecb9cada2015-12-08 15:45:58 -070084
Dave Barachc3799992016-08-15 11:12:27 -040085typedef struct
86{
Ed Warnickecb9cada2015-12-08 15:45:58 -070087 /* Read/write indices each on their own cache line.
88 Atomic incremented for each read/write. */
89 u32 read_index, write_index;
90
91 /* Power of 2 number of elements in fifo less one. */
92 u32 max_n_elts_less_one;
93
94 /* Log2 of above. */
95 u32 log2_max_n_elts;
96
97 /* Cache aligned data. */
Dave Barachc3799992016-08-15 11:12:27 -040098 void *data;
Ed Warnickecb9cada2015-12-08 15:45:58 -070099} clib_smp_fifo_t;
100
101/* External functions. */
Dave Barachc3799992016-08-15 11:12:27 -0400102clib_smp_fifo_t *clib_smp_fifo_init (uword max_n_elts, uword n_bytes_per_elt);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700103
104/* Elements are always cache-line sized; this is to avoid smp cache thrashing. */
105always_inline uword
106clib_smp_fifo_round_elt_bytes (uword n_bytes_per_elt)
Dave Barachc3799992016-08-15 11:12:27 -0400107{
108 return round_pow2 (n_bytes_per_elt, CLIB_CACHE_LINE_BYTES);
109}
Ed Warnickecb9cada2015-12-08 15:45:58 -0700110
111always_inline uword
112clib_smp_fifo_n_elts (clib_smp_fifo_t * f)
113{
114 uword n = f->write_index - f->read_index;
115 ASSERT (n <= f->max_n_elts_less_one + 1);
116 return n;
117}
118
119always_inline clib_smp_fifo_data_footer_t *
Dave Barachc3799992016-08-15 11:12:27 -0400120clib_smp_fifo_get_data_footer (void *d, uword n_bytes_per_elt)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700121{
Dave Barachc3799992016-08-15 11:12:27 -0400122 clib_smp_fifo_data_footer_t *f;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700123 f = d + clib_smp_fifo_round_elt_bytes (n_bytes_per_elt) - sizeof (f[0]);
124 return f;
125}
126
127always_inline void *
Dave Barachc3799992016-08-15 11:12:27 -0400128clib_smp_fifo_elt_at_index (clib_smp_fifo_t * f, uword n_bytes_per_elt,
129 uword i)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700130{
131 uword n_bytes_per_elt_cache_aligned;
132
133 ASSERT (i <= f->max_n_elts_less_one);
134
Dave Barachc3799992016-08-15 11:12:27 -0400135 n_bytes_per_elt_cache_aligned =
136 clib_smp_fifo_round_elt_bytes (n_bytes_per_elt);
137
Ed Warnickecb9cada2015-12-08 15:45:58 -0700138 return f->data + i * n_bytes_per_elt_cache_aligned;
139}
140
141always_inline void *
142clib_smp_fifo_write_alloc (clib_smp_fifo_t * f, uword n_bytes_per_elt)
143{
Dave Barachc3799992016-08-15 11:12:27 -0400144 void *d;
145 clib_smp_fifo_data_footer_t *t;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700146 clib_smp_fifo_data_state_t s;
147 u32 wi0, wi1;
148
149 wi0 = f->write_index;
150
151 /* Fifo full? */
152 if (wi0 - f->read_index > f->max_n_elts_less_one)
153 return 0;
154
155 while (1)
156 {
157 wi1 = wi0 + 1;
158
Dave Barachc3799992016-08-15 11:12:27 -0400159 d =
160 clib_smp_fifo_elt_at_index (f, n_bytes_per_elt,
161 wi0 & f->max_n_elts_less_one);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700162 t = clib_smp_fifo_get_data_footer (d, n_bytes_per_elt);
163
164 s = clib_smp_fifo_data_footer_get_state (t);
165 if (s != CLIB_SMP_FIFO_DATA_STATE_free)
166 {
167 d = 0;
168 break;
169 }
170
171 wi1 = clib_smp_compare_and_swap (&f->write_index, wi1, wi0);
172
173 if (wi1 == wi0)
174 {
Dave Barachc3799992016-08-15 11:12:27 -0400175 clib_smp_fifo_data_footer_set_state (t,
176 CLIB_SMP_FIFO_DATA_STATE_write_alloc);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700177 break;
178 }
179
180 /* Other cpu wrote write index first: try again. */
181 wi0 = wi1;
182 }
183
184 return d;
185}
186
187always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400188clib_smp_fifo_write_done (clib_smp_fifo_t * f, void *d, uword n_bytes_per_elt)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700189{
Dave Barachc3799992016-08-15 11:12:27 -0400190 clib_smp_fifo_data_footer_t *t;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700191
192 /* Flush out pending writes before we change state to write_done.
193 This will hold off readers until data is flushed. */
194 CLIB_MEMORY_BARRIER ();
195
196 t = clib_smp_fifo_get_data_footer (d, n_bytes_per_elt);
197
Dave Barachc3799992016-08-15 11:12:27 -0400198 ASSERT (clib_smp_fifo_data_footer_get_state (t) ==
199 CLIB_SMP_FIFO_DATA_STATE_write_alloc);
200 clib_smp_fifo_data_footer_set_state (t,
201 CLIB_SMP_FIFO_DATA_STATE_write_done);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700202}
203
204always_inline void *
205clib_smp_fifo_read_fetch (clib_smp_fifo_t * f, uword n_bytes_per_elt)
206{
Dave Barachc3799992016-08-15 11:12:27 -0400207 void *d;
208 clib_smp_fifo_data_footer_t *t;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700209 clib_smp_fifo_data_state_t s;
210 u32 ri0, ri1;
211
212 ri0 = f->read_index;
213
214 /* Fifo empty? */
215 if (f->write_index - ri0 == 0)
216 return 0;
217
218 while (1)
219 {
220 ri1 = ri0 + 1;
221
Dave Barachc3799992016-08-15 11:12:27 -0400222 d =
223 clib_smp_fifo_elt_at_index (f, n_bytes_per_elt,
224 ri0 & f->max_n_elts_less_one);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700225 t = clib_smp_fifo_get_data_footer (d, n_bytes_per_elt);
226
227 s = clib_smp_fifo_data_footer_get_state (t);
228 if (s != CLIB_SMP_FIFO_DATA_STATE_write_done)
229 {
230 d = 0;
231 break;
232 }
233
234 ri1 = clib_smp_compare_and_swap (&f->read_index, ri1, ri0);
235 if (ri1 == ri0)
236 {
Dave Barachc3799992016-08-15 11:12:27 -0400237 clib_smp_fifo_data_footer_set_state (t,
238 CLIB_SMP_FIFO_DATA_STATE_read_fetch);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700239 break;
240 }
241
242 ri0 = ri1;
243 }
244
245 return d;
246}
247
248always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400249clib_smp_fifo_read_done (clib_smp_fifo_t * f, void *d, uword n_bytes_per_elt)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700250{
Dave Barachc3799992016-08-15 11:12:27 -0400251 clib_smp_fifo_data_footer_t *t;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700252
253 t = clib_smp_fifo_get_data_footer (d, n_bytes_per_elt);
254
Dave Barachc3799992016-08-15 11:12:27 -0400255 ASSERT (clib_smp_fifo_data_footer_get_state (t) ==
256 CLIB_SMP_FIFO_DATA_STATE_read_fetch);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700257 clib_smp_fifo_data_footer_set_state (t, CLIB_SMP_FIFO_DATA_STATE_free);
258}
259
260always_inline void
261clib_smp_fifo_memcpy (uword * dst, uword * src, uword n_bytes)
262{
263 word n_bytes_left = n_bytes;
264
265 while (n_bytes_left >= 4 * sizeof (uword))
266 {
267 dst[0] = src[0];
268 dst[1] = src[1];
269 dst[2] = src[2];
270 dst[3] = src[3];
271 dst += 4;
272 src += 4;
273 n_bytes_left -= 4 * sizeof (dst[0]);
274 }
275
276 while (n_bytes_left > 0)
277 {
278 dst[0] = src[0];
279 dst += 1;
280 src += 1;
281 n_bytes_left -= 1 * sizeof (dst[0]);
282 }
283}
284
285always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400286clib_smp_fifo_write_inline (clib_smp_fifo_t * f, void *elt_to_write,
287 uword n_bytes_per_elt)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700288{
Dave Barachc3799992016-08-15 11:12:27 -0400289 uword *dst;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700290 dst = clib_smp_fifo_write_alloc (f, n_bytes_per_elt);
291 clib_smp_fifo_memcpy (dst, elt_to_write, n_bytes_per_elt);
292 clib_smp_fifo_write_done (f, dst, n_bytes_per_elt);
293}
294
295always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400296clib_smp_fifo_read_inline (clib_smp_fifo_t * f, void *elt_to_read,
297 uword n_bytes_per_elt)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700298{
Dave Barachc3799992016-08-15 11:12:27 -0400299 uword *src;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700300 src = clib_smp_fifo_read_fetch (f, n_bytes_per_elt);
301 clib_smp_fifo_memcpy (elt_to_read, src, n_bytes_per_elt);
302 clib_smp_fifo_read_done (f, src, n_bytes_per_elt);
303}
304
305#endif /* included_clib_smp_vec_h */
Dave Barachc3799992016-08-15 11:12:27 -0400306
307/*
308 * fd.io coding-style-patch-verification: ON
309 *
310 * Local Variables:
311 * eval: (c-set-style "gnu")
312 * End:
313 */