blob: a661370a1411a01c891d89175ac6c203b746af2a [file] [log] [blame]
Damjan Marion1c229712021-04-21 12:55:15 +02001/* SPDX-License-Identifier: Apache-2.0
2 * Copyright(c) 2021 Cisco Systems, Inc.
3 */
4
Damjan Marionef0bac72021-04-22 18:08:28 +02005#include <vppinfra/clib.h>
Damjan Marion1c229712021-04-21 12:55:15 +02006#include <vlib/vlib.h>
Damjan Mariond154a172021-07-13 21:12:41 +02007#include <vppinfra/vector/mask_compare.h>
8#include <vppinfra/vector/compress.h>
Damjan Marion1c229712021-04-21 12:55:15 +02009
Damjan Marionef0bac72021-04-22 18:08:28 +020010static_always_inline u32
Damjan Marione3e35552021-05-06 17:34:49 +020011enqueue_one (vlib_main_t *vm, vlib_node_runtime_t *node, u64 *used_elt_bmp,
Damjan Marionef0bac72021-04-22 18:08:28 +020012 u16 next_index, u32 *buffers, u16 *nexts, u32 n_buffers,
13 u32 n_left, u32 *tmp)
14{
Damjan Marione3e35552021-05-06 17:34:49 +020015 u64 match_bmp[VLIB_FRAME_SIZE / 64];
Damjan Marionef0bac72021-04-22 18:08:28 +020016 vlib_frame_t *f;
17 u32 n_extracted, n_free;
18 u32 *to;
19
20 f = vlib_get_next_frame_internal (vm, node, next_index, 0);
21
22 n_free = VLIB_FRAME_SIZE - f->n_vectors;
23
24 /* if frame contains enough space for worst case scenario, we can avoid
25 * use of tmp */
26 if (n_free >= n_left)
27 to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
28 else
29 to = tmp;
30
Damjan Marione3e35552021-05-06 17:34:49 +020031 clib_mask_compare_u16 (next_index, nexts, match_bmp, n_buffers);
32
33 n_extracted = clib_compress_u32 (to, buffers, match_bmp, n_buffers);
34
35 for (int i = 0; i < ARRAY_LEN (match_bmp); i++)
36 used_elt_bmp[i] |= match_bmp[i];
Damjan Marionef0bac72021-04-22 18:08:28 +020037
38 if (to != tmp)
39 {
40 /* indices already written to frame, just close it */
41 vlib_put_next_frame (vm, node, next_index, n_free - n_extracted);
42 }
43 else if (n_free >= n_extracted)
44 {
45 /* enough space in the existing frame */
46 to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
47 vlib_buffer_copy_indices (to, tmp, n_extracted);
48 vlib_put_next_frame (vm, node, next_index, n_free - n_extracted);
49 }
50 else
51 {
52 /* full frame */
53 to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
54 vlib_buffer_copy_indices (to, tmp, n_free);
55 vlib_put_next_frame (vm, node, next_index, 0);
56
57 /* second frame */
58 u32 n_2nd_frame = n_extracted - n_free;
59 f = vlib_get_next_frame_internal (vm, node, next_index, 1);
60 to = vlib_frame_vector_args (f);
61 vlib_buffer_copy_indices (to, tmp + n_free, n_2nd_frame);
62 vlib_put_next_frame (vm, node, next_index,
63 VLIB_FRAME_SIZE - n_2nd_frame);
64 }
65
66 return n_left - n_extracted;
67}
68
Damjan Marion23c34882021-04-25 10:46:26 +020069void __clib_section (".vlib_buffer_enqueue_to_next_fn")
70CLIB_MULTIARCH_FN (vlib_buffer_enqueue_to_next_fn)
71(vlib_main_t *vm, vlib_node_runtime_t *node, u32 *buffers, u16 *nexts,
72 uword count)
Damjan Marion1c229712021-04-21 12:55:15 +020073{
Damjan Marionef0bac72021-04-22 18:08:28 +020074 u32 tmp[VLIB_FRAME_SIZE];
75 u32 n_left;
Damjan Marion1c229712021-04-21 12:55:15 +020076 u16 next_index;
77
Damjan Marionef0bac72021-04-22 18:08:28 +020078 while (count >= VLIB_FRAME_SIZE)
Damjan Marion1c229712021-04-21 12:55:15 +020079 {
Damjan Marione3e35552021-05-06 17:34:49 +020080 u64 used_elt_bmp[VLIB_FRAME_SIZE / 64] = {};
Damjan Marionef0bac72021-04-22 18:08:28 +020081 n_left = VLIB_FRAME_SIZE;
Damjan Marione3e35552021-05-06 17:34:49 +020082 u32 off = 0;
Damjan Marion1c229712021-04-21 12:55:15 +020083
Damjan Marionef0bac72021-04-22 18:08:28 +020084 next_index = nexts[0];
Damjan Marione3e35552021-05-06 17:34:49 +020085 n_left = enqueue_one (vm, node, used_elt_bmp, next_index, buffers, nexts,
Damjan Marionef0bac72021-04-22 18:08:28 +020086 VLIB_FRAME_SIZE, n_left, tmp);
Damjan Marion1c229712021-04-21 12:55:15 +020087
Damjan Marionef0bac72021-04-22 18:08:28 +020088 while (n_left)
Damjan Marion1c229712021-04-21 12:55:15 +020089 {
Damjan Marione3e35552021-05-06 17:34:49 +020090 while (PREDICT_FALSE (used_elt_bmp[off] == ~0))
Damjan Marionc0d9ca72021-05-11 09:39:24 +020091 {
92 off++;
93 ASSERT (off < ARRAY_LEN (used_elt_bmp));
94 }
Damjan Marione3e35552021-05-06 17:34:49 +020095
96 next_index =
97 nexts[off * 64 + count_trailing_zeros (~used_elt_bmp[off])];
98 n_left = enqueue_one (vm, node, used_elt_bmp, next_index, buffers,
99 nexts, VLIB_FRAME_SIZE, n_left, tmp);
Damjan Marion1c229712021-04-21 12:55:15 +0200100 }
101
Damjan Marionef0bac72021-04-22 18:08:28 +0200102 buffers += VLIB_FRAME_SIZE;
103 nexts += VLIB_FRAME_SIZE;
104 count -= VLIB_FRAME_SIZE;
Damjan Marion1c229712021-04-21 12:55:15 +0200105 }
Damjan Marionef0bac72021-04-22 18:08:28 +0200106
107 if (count)
108 {
Damjan Marione3e35552021-05-06 17:34:49 +0200109 u64 used_elt_bmp[VLIB_FRAME_SIZE / 64] = {};
Damjan Marionef0bac72021-04-22 18:08:28 +0200110 next_index = nexts[0];
111 n_left = count;
Damjan Marione3e35552021-05-06 17:34:49 +0200112 u32 off = 0;
Damjan Marionef0bac72021-04-22 18:08:28 +0200113
Damjan Marione3e35552021-05-06 17:34:49 +0200114 n_left = enqueue_one (vm, node, used_elt_bmp, next_index, buffers, nexts,
115 count, n_left, tmp);
Damjan Marionef0bac72021-04-22 18:08:28 +0200116
117 while (n_left)
118 {
Damjan Marione3e35552021-05-06 17:34:49 +0200119 while (PREDICT_FALSE (used_elt_bmp[off] == ~0))
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200120 {
121 off++;
122 ASSERT (off < ARRAY_LEN (used_elt_bmp));
123 }
Damjan Marione3e35552021-05-06 17:34:49 +0200124
125 next_index =
126 nexts[off * 64 + count_trailing_zeros (~used_elt_bmp[off])];
127 n_left = enqueue_one (vm, node, used_elt_bmp, next_index, buffers,
128 nexts, count, n_left, tmp);
Damjan Marionef0bac72021-04-22 18:08:28 +0200129 }
130 }
Damjan Marion1c229712021-04-21 12:55:15 +0200131}
Damjan Marion23c34882021-04-25 10:46:26 +0200132
Damjan Marion1c229712021-04-21 12:55:15 +0200133CLIB_MARCH_FN_REGISTRATION (vlib_buffer_enqueue_to_next_fn);
134
135void __clib_section (".vlib_buffer_enqueue_to_single_next_fn")
Damjan Marion23c34882021-04-25 10:46:26 +0200136CLIB_MULTIARCH_FN (vlib_buffer_enqueue_to_single_next_fn)
137(vlib_main_t *vm, vlib_node_runtime_t *node, u32 *buffers, u16 next_index,
138 u32 count)
Damjan Marion1c229712021-04-21 12:55:15 +0200139{
140 u32 *to_next, n_left_to_next, n_enq;
141
142 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
143
144 if (PREDICT_TRUE (n_left_to_next >= count))
145 {
146 vlib_buffer_copy_indices (to_next, buffers, count);
147 n_left_to_next -= count;
148 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
149 return;
150 }
151
152 n_enq = n_left_to_next;
153next:
154 vlib_buffer_copy_indices (to_next, buffers, n_enq);
155 n_left_to_next -= n_enq;
156
157 if (PREDICT_FALSE (count > n_enq))
158 {
159 count -= n_enq;
160 buffers += n_enq;
161
162 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
163 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
164 n_enq = clib_min (n_left_to_next, count);
165 goto next;
166 }
167 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
168}
169CLIB_MARCH_FN_REGISTRATION (vlib_buffer_enqueue_to_single_next_fn);
170
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200171static inline vlib_frame_queue_elt_t *
172vlib_get_frame_queue_elt (vlib_frame_queue_main_t *fqm, u32 index,
173 int dont_wait)
Damjan Marion1c229712021-04-21 12:55:15 +0200174{
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200175 vlib_frame_queue_t *fq;
176 u64 nelts, tail, new_tail;
Damjan Marion1c229712021-04-21 12:55:15 +0200177
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200178 fq = fqm->vlib_frame_queues[index];
179 ASSERT (fq);
180 nelts = fq->nelts;
Damjan Marion1c229712021-04-21 12:55:15 +0200181
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200182retry:
183 tail = __atomic_load_n (&fq->tail, __ATOMIC_ACQUIRE);
184 new_tail = tail + 1;
185
186 if (new_tail >= fq->head + nelts)
Damjan Marion1c229712021-04-21 12:55:15 +0200187 {
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200188 if (dont_wait)
189 return 0;
Damjan Marion1c229712021-04-21 12:55:15 +0200190
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200191 /* Wait until a ring slot is available */
192 while (new_tail >= fq->head + nelts)
193 vlib_worker_thread_barrier_check ();
Damjan Marion1c229712021-04-21 12:55:15 +0200194 }
195
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200196 if (!__atomic_compare_exchange_n (&fq->tail, &tail, new_tail, 0 /* weak */,
197 __ATOMIC_RELAXED, __ATOMIC_RELAXED))
198 goto retry;
Damjan Marion1c229712021-04-21 12:55:15 +0200199
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200200 return fq->elts + (new_tail & (nelts - 1));
201}
202
203static_always_inline u32
204vlib_buffer_enqueue_to_thread_inline (vlib_main_t *vm,
205 vlib_node_runtime_t *node,
206 vlib_frame_queue_main_t *fqm,
207 u32 *buffer_indices, u16 *thread_indices,
208 u32 n_packets, int drop_on_congestion)
209{
210 u32 drop_list[VLIB_FRAME_SIZE], n_drop = 0;
211 u64 used_elts[VLIB_FRAME_SIZE / 64] = {};
212 u64 mask[VLIB_FRAME_SIZE / 64];
213 vlib_frame_queue_elt_t *hf = 0;
214 u16 thread_index;
215 u32 n_comp, off = 0, n_left = n_packets;
216
217 thread_index = thread_indices[0];
218
219more:
220 clib_mask_compare_u16 (thread_index, thread_indices, mask, n_packets);
221 hf = vlib_get_frame_queue_elt (fqm, thread_index, drop_on_congestion);
222
223 n_comp = clib_compress_u32 (hf ? hf->buffer_index : drop_list + n_drop,
224 buffer_indices, mask, n_packets);
225
226 if (hf)
Damjan Marion1c229712021-04-21 12:55:15 +0200227 {
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200228 if (node->flags & VLIB_NODE_FLAG_TRACE)
229 hf->maybe_trace = 1;
230 hf->n_vectors = n_comp;
231 __atomic_store_n (&hf->valid, 1, __ATOMIC_RELEASE);
232 vlib_get_main_by_index (thread_index)->check_frame_queues = 1;
233 }
234 else
235 n_drop += n_comp;
236
237 n_left -= n_comp;
238
239 if (n_left)
240 {
241 for (int i = 0; i < ARRAY_LEN (used_elts); i++)
242 used_elts[i] |= mask[i];
243
244 while (PREDICT_FALSE (used_elts[off] == ~0))
Damjan Marion1c229712021-04-21 12:55:15 +0200245 {
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200246 off++;
247 ASSERT (off < ARRAY_LEN (used_elts));
Damjan Marion1c229712021-04-21 12:55:15 +0200248 }
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200249
250 thread_index =
251 thread_indices[off * 64 + count_trailing_zeros (~used_elts[off])];
252 goto more;
Damjan Marion1c229712021-04-21 12:55:15 +0200253 }
254
255 if (drop_on_congestion && n_drop)
256 vlib_buffer_free (vm, drop_list, n_drop);
257
258 return n_packets - n_drop;
259}
260
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200261u32 __clib_section (".vlib_buffer_enqueue_to_thread_fn")
262CLIB_MULTIARCH_FN (vlib_buffer_enqueue_to_thread_fn)
263(vlib_main_t *vm, vlib_node_runtime_t *node, u32 frame_queue_index,
264 u32 *buffer_indices, u16 *thread_indices, u32 n_packets,
265 int drop_on_congestion)
266{
267 vlib_thread_main_t *tm = vlib_get_thread_main ();
268 vlib_frame_queue_main_t *fqm;
269 u32 n_enq = 0;
270
271 fqm = vec_elt_at_index (tm->frame_queue_mains, frame_queue_index);
272
273 while (n_packets >= VLIB_FRAME_SIZE)
274 {
275 n_enq += vlib_buffer_enqueue_to_thread_inline (
276 vm, node, fqm, buffer_indices, thread_indices, VLIB_FRAME_SIZE,
277 drop_on_congestion);
278 buffer_indices += VLIB_FRAME_SIZE;
279 thread_indices += VLIB_FRAME_SIZE;
280 n_packets -= VLIB_FRAME_SIZE;
281 }
282
283 if (n_packets == 0)
284 return n_enq;
285
286 n_enq += vlib_buffer_enqueue_to_thread_inline (vm, node, fqm, buffer_indices,
287 thread_indices, n_packets,
288 drop_on_congestion);
289
290 return n_enq;
291}
292
Damjan Marion1c229712021-04-21 12:55:15 +0200293CLIB_MARCH_FN_REGISTRATION (vlib_buffer_enqueue_to_thread_fn);
294
Damjan Marioneee099e2021-05-01 14:56:13 +0200295u32 __clib_section (".vlib_frame_queue_dequeue_fn")
296CLIB_MULTIARCH_FN (vlib_frame_queue_dequeue_fn)
297(vlib_main_t *vm, vlib_frame_queue_main_t *fqm)
298{
299 u32 thread_id = vm->thread_index;
300 vlib_frame_queue_t *fq = fqm->vlib_frame_queues[thread_id];
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200301 u32 mask = fq->nelts - 1;
Damjan Marioneee099e2021-05-01 14:56:13 +0200302 vlib_frame_queue_elt_t *elt;
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200303 u32 n_free, n_copy, *from, *to = 0, processed = 0, vectors = 0;
304 vlib_frame_t *f = 0;
Damjan Marioneee099e2021-05-01 14:56:13 +0200305
306 ASSERT (fq);
307 ASSERT (vm == vlib_global_main.vlib_mains[thread_id]);
308
309 if (PREDICT_FALSE (fqm->node_index == ~0))
310 return 0;
311 /*
312 * Gather trace data for frame queues
313 */
314 if (PREDICT_FALSE (fq->trace))
315 {
316 frame_queue_trace_t *fqt;
317 frame_queue_nelt_counter_t *fqh;
318 u32 elix;
319
320 fqt = &fqm->frame_queue_traces[thread_id];
321
322 fqt->nelts = fq->nelts;
323 fqt->head = fq->head;
Damjan Marioneee099e2021-05-01 14:56:13 +0200324 fqt->tail = fq->tail;
325 fqt->threshold = fq->vector_threshold;
326 fqt->n_in_use = fqt->tail - fqt->head;
327 if (fqt->n_in_use >= fqt->nelts)
328 {
329 // if beyond max then use max
330 fqt->n_in_use = fqt->nelts - 1;
331 }
332
333 /* Record the number of elements in use in the histogram */
334 fqh = &fqm->frame_queue_histogram[thread_id];
335 fqh->count[fqt->n_in_use]++;
336
337 /* Record a snapshot of the elements in use */
338 for (elix = 0; elix < fqt->nelts; elix++)
339 {
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200340 elt = fq->elts + ((fq->head + 1 + elix) & (mask));
Damjan Marioneee099e2021-05-01 14:56:13 +0200341 if (1 || elt->valid)
342 {
343 fqt->n_vectors[elix] = elt->n_vectors;
344 }
345 }
346 fqt->written = 1;
347 }
348
349 while (1)
350 {
Damjan Marioneee099e2021-05-01 14:56:13 +0200351 if (fq->head == fq->tail)
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200352 break;
353
354 elt = fq->elts + ((fq->head + 1) & mask);
355
356 if (!__atomic_load_n (&elt->valid, __ATOMIC_ACQUIRE))
357 break;
358
359 from = elt->buffer_index + elt->offset;
360
361 ASSERT (elt->offset + elt->n_vectors <= VLIB_FRAME_SIZE);
362
363 if (f == 0)
Damjan Marioneee099e2021-05-01 14:56:13 +0200364 {
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200365 f = vlib_get_frame_to_node (vm, fqm->node_index);
366 to = vlib_frame_vector_args (f);
367 n_free = VLIB_FRAME_SIZE;
Damjan Marioneee099e2021-05-01 14:56:13 +0200368 }
369
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200370 if (elt->maybe_trace)
Damjan Marioneee099e2021-05-01 14:56:13 +0200371 f->frame_flags |= VLIB_NODE_FLAG_TRACE;
372
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200373 n_copy = clib_min (n_free, elt->n_vectors);
Damjan Marioneee099e2021-05-01 14:56:13 +0200374
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200375 vlib_buffer_copy_indices (to, from, n_copy);
376 to += n_copy;
377 n_free -= n_copy;
378 vectors += n_copy;
Damjan Marioneee099e2021-05-01 14:56:13 +0200379
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200380 if (n_free == 0)
Damjan Marioneee099e2021-05-01 14:56:13 +0200381 {
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200382 f->n_vectors = VLIB_FRAME_SIZE;
383 vlib_put_frame_to_node (vm, fqm->node_index, f);
384 f = 0;
Damjan Marioneee099e2021-05-01 14:56:13 +0200385 }
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200386
387 if (n_copy < elt->n_vectors)
388 {
389 /* not empty - leave it on the ring */
390 elt->n_vectors -= n_copy;
391 elt->offset += n_copy;
392 }
393 else
394 {
395 /* empty - reset and bump head */
396 u32 sz = STRUCT_OFFSET_OF (vlib_frame_queue_elt_t, end_of_reset);
397 clib_memset (elt, 0, sz);
398 __atomic_store_n (&fq->head, fq->head + 1, __ATOMIC_RELEASE);
399 processed++;
400 }
401
402 /* Limit the number of packets pushed into the graph */
403 if (vectors >= fq->vector_threshold)
404 break;
Damjan Marioneee099e2021-05-01 14:56:13 +0200405 }
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200406
407 if (f)
408 {
409 f->n_vectors = VLIB_FRAME_SIZE - n_free;
410 vlib_put_frame_to_node (vm, fqm->node_index, f);
411 }
412
Damjan Marioneee099e2021-05-01 14:56:13 +0200413 return processed;
414}
Damjan Marionc0d9ca72021-05-11 09:39:24 +0200415
Damjan Marioneee099e2021-05-01 14:56:13 +0200416CLIB_MARCH_FN_REGISTRATION (vlib_frame_queue_dequeue_fn);
417
Damjan Marion1c229712021-04-21 12:55:15 +0200418#ifndef CLIB_MARCH_VARIANT
419vlib_buffer_func_main_t vlib_buffer_func_main;
420
421static clib_error_t *
422vlib_buffer_funcs_init (vlib_main_t *vm)
423{
424 vlib_buffer_func_main_t *bfm = &vlib_buffer_func_main;
425 bfm->buffer_enqueue_to_next_fn =
426 CLIB_MARCH_FN_POINTER (vlib_buffer_enqueue_to_next_fn);
427 bfm->buffer_enqueue_to_single_next_fn =
428 CLIB_MARCH_FN_POINTER (vlib_buffer_enqueue_to_single_next_fn);
429 bfm->buffer_enqueue_to_thread_fn =
430 CLIB_MARCH_FN_POINTER (vlib_buffer_enqueue_to_thread_fn);
Damjan Marioneee099e2021-05-01 14:56:13 +0200431 bfm->frame_queue_dequeue_fn =
432 CLIB_MARCH_FN_POINTER (vlib_frame_queue_dequeue_fn);
Damjan Marion1c229712021-04-21 12:55:15 +0200433 return 0;
434}
435
436VLIB_INIT_FUNCTION (vlib_buffer_funcs_init);
437#endif