Line data Source code
1 : /*
2 : * Copyright (c) 2019 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 <vlib/punt.h>
17 :
18 : #define foreach_punt_error \
19 : _(DISPATCHED, "dispatched") \
20 : _(NO_REASON, "No such punt reason") \
21 : _(NO_REG, "No registrations") \
22 : _(REP_FAIL, "Replication Failure")
23 :
24 : typedef enum punt_error_t_
25 : {
26 : #define _(v,s) PUNT_ERROR_##v,
27 : foreach_punt_error
28 : #undef _
29 : PUNT_N_ERRORS,
30 : } punt_error_t;
31 :
32 : static char *punt_error_strings[] = {
33 : #define _(v,s) [PUNT_ERROR_##v] = s,
34 : foreach_punt_error
35 : #undef _
36 : };
37 :
38 : typedef enum punt_next_t_
39 : {
40 : PUNT_NEXT_DROP,
41 : PUNT_N_NEXT,
42 : } punt_next_t;
43 :
44 : typedef struct punt_trace_t_
45 : {
46 : vlib_punt_reason_t pt_reason;
47 : } punt_trace_t;
48 :
49 : /**
50 : * Per-thread clone vectors
51 : */
52 : #ifndef CLIB_MARCH_VARIANT
53 : u32 **punt_clones;
54 : #else
55 : extern u32 **punt_clones;
56 : #endif
57 :
58 : static u8 *
59 1417 : format_punt_trace (u8 * s, va_list * args)
60 : {
61 1417 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
62 1417 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
63 1417 : punt_trace_t *t = va_arg (*args, punt_trace_t *);
64 :
65 1417 : s = format (s, "reason: %U", format_vlib_punt_reason, t->pt_reason);
66 :
67 1417 : return s;
68 : }
69 :
70 : always_inline u32
71 134 : punt_replicate (vlib_main_t * vm,
72 : vlib_node_runtime_t * node,
73 : u32 thread_index,
74 : vlib_buffer_t * b0,
75 : u32 bi0,
76 : vlib_punt_reason_t pr0,
77 : u32 * next_index,
78 : u32 * n_left_to_next, u32 ** to_next, u32 * n_dispatched)
79 : {
80 : /* multiple clients => replicate a copy to each */
81 : u16 n_clones0, n_cloned0, clone0;
82 : u32 ci0, next0;
83 :
84 134 : n_clones0 = vec_len (punt_dp_db[pr0]);
85 134 : vec_validate (punt_clones[thread_index], n_clones0);
86 :
87 134 : n_cloned0 = vlib_buffer_clone (vm, bi0,
88 134 : punt_clones[thread_index],
89 : n_clones0, 2 * CLIB_CACHE_LINE_BYTES);
90 :
91 134 : if (PREDICT_FALSE (n_cloned0 != n_clones0))
92 : {
93 0 : b0->error = node->errors[PUNT_ERROR_REP_FAIL];
94 : }
95 :
96 268 : for (clone0 = 1; clone0 < n_cloned0; clone0++)
97 : {
98 134 : ci0 = punt_clones[thread_index][clone0];
99 :
100 134 : *to_next[0] = ci0;
101 134 : *to_next += 1;
102 134 : *n_left_to_next -= 1;
103 :
104 134 : next0 = punt_dp_db[pr0][clone0];
105 :
106 134 : if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
107 : {
108 : vlib_buffer_t *c0;
109 : punt_trace_t *t;
110 :
111 134 : c0 = vlib_get_buffer (vm, ci0);
112 134 : t = vlib_add_trace (vm, node, c0, sizeof (*t));
113 134 : t->pt_reason = pr0;
114 : }
115 :
116 134 : vlib_validate_buffer_enqueue_x1 (vm, node, *next_index,
117 : *to_next, *n_left_to_next, ci0, next0);
118 :
119 : /* replications here always go to different next-nodes
120 : * so there's no need to check if the to_next frame
121 : * is full */
122 : }
123 134 : *n_dispatched = *n_dispatched + n_cloned0;
124 :
125 : /* The original buffer is the first clone */
126 134 : next0 = punt_dp_db[pr0][0];
127 : /*
128 : * Note: the original buffer is enqueued in punt_dispatch_node.
129 : * Don't do it here.
130 : *
131 : * *to_next[0] = bi0;
132 : */
133 134 : return next0;
134 : }
135 :
136 : always_inline u32
137 1672 : punt_dispatch_one (vlib_main_t * vm,
138 : vlib_node_runtime_t * node,
139 : vlib_combined_counter_main_t * cm,
140 : u32 thread_index,
141 : u32 bi0,
142 : u32 * next_index,
143 : u32 * n_left_to_next, u32 ** to_next, u32 * n_dispatched)
144 : {
145 : vlib_punt_reason_t pr0;
146 : vlib_buffer_t *b0;
147 : u32 next0;
148 :
149 1672 : b0 = vlib_get_buffer (vm, bi0);
150 1672 : pr0 = b0->punt_reason;
151 :
152 1672 : if (PREDICT_FALSE (pr0 >= vec_len (punt_dp_db)))
153 : {
154 0 : b0->error = node->errors[PUNT_ERROR_NO_REASON];
155 0 : next0 = PUNT_NEXT_DROP;
156 : }
157 : else
158 : {
159 1672 : vlib_increment_combined_counter
160 : (cm, thread_index, pr0, 1, vlib_buffer_length_in_chain (vm, b0));
161 :
162 1672 : if (PREDICT_TRUE (1 == vec_len (punt_dp_db[pr0])))
163 : {
164 : /*
165 : * one registered client => give it the packet
166 : * This is the most likely outcome.
167 : */
168 143 : next0 = punt_dp_db[pr0][0];
169 143 : *n_dispatched = *n_dispatched + 1;
170 : }
171 1529 : else if (0 == vec_len (punt_dp_db[pr0]))
172 : {
173 : /* no registered clients => drop */
174 1395 : next0 = PUNT_NEXT_DROP;
175 1395 : b0->error = node->errors[PUNT_ERROR_NO_REG];
176 : }
177 : else
178 : {
179 : /*
180 : * multiple registered clients => replicate
181 : */
182 134 : next0 = punt_replicate (vm, node, thread_index, b0, bi0, pr0,
183 : next_index, n_left_to_next, to_next,
184 : n_dispatched);
185 : }
186 : }
187 :
188 1672 : if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
189 : {
190 : punt_trace_t *t;
191 :
192 1672 : t = vlib_add_trace (vm, node, b0, sizeof (*t));
193 1672 : t->pt_reason = pr0;
194 : }
195 :
196 1672 : return (next0);
197 : }
198 :
199 2270 : VLIB_NODE_FN (punt_dispatch_node) (vlib_main_t * vm,
200 : vlib_node_runtime_t * node,
201 : vlib_frame_t * frame)
202 : {
203 : u32 n_left_from, *from, *to_next, next_index, thread_index;
204 : vlib_combined_counter_main_t *cm;
205 : u32 n_dispatched;
206 :
207 34 : cm = &punt_counters;
208 34 : from = vlib_frame_vector_args (frame);
209 34 : n_left_from = frame->n_vectors;
210 34 : next_index = node->cached_next_index;
211 34 : thread_index = vlib_get_thread_index ();
212 34 : n_dispatched = 0;
213 :
214 68 : while (n_left_from > 0)
215 : {
216 : u32 n_left_to_next;
217 :
218 34 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
219 :
220 828 : while (n_left_from > 4 && n_left_to_next > 2)
221 : {
222 : punt_next_t next0, next1;
223 : u32 bi0, bi1;
224 :
225 : {
226 : vlib_buffer_t *b2, *b3;
227 :
228 794 : b2 = vlib_get_buffer (vm, from[2]);
229 794 : b3 = vlib_get_buffer (vm, from[3]);
230 :
231 794 : vlib_prefetch_buffer_header (b2, LOAD);
232 794 : vlib_prefetch_buffer_header (b3, LOAD);
233 : }
234 :
235 794 : bi0 = to_next[0] = from[0];
236 794 : bi1 = to_next[1] = from[1];
237 794 : from += 2;
238 794 : n_left_from -= 2;
239 :
240 794 : next0 = punt_dispatch_one (vm, node, cm, thread_index, bi0,
241 : &next_index, &n_left_to_next,
242 : &to_next, &n_dispatched);
243 794 : next1 = punt_dispatch_one (vm, node, cm, thread_index, bi1,
244 : &next_index, &n_left_to_next,
245 : &to_next, &n_dispatched);
246 :
247 794 : to_next += 2;
248 794 : n_left_to_next -= 2;
249 :
250 794 : vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
251 : to_next, n_left_to_next,
252 : bi0, bi1, next0, next1);
253 : }
254 118 : while (n_left_from > 0 && n_left_to_next > 0)
255 : {
256 : punt_next_t next0;
257 : u32 bi0;
258 :
259 84 : bi0 = to_next[0] = from[0];
260 84 : from += 1;
261 84 : n_left_from -= 1;
262 :
263 84 : next0 = punt_dispatch_one (vm, node, cm, thread_index, bi0,
264 : &next_index, &n_left_to_next,
265 : &to_next, &n_dispatched);
266 :
267 84 : to_next += 1;
268 84 : n_left_to_next -= 1;
269 :
270 84 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
271 : to_next, n_left_to_next,
272 : bi0, next0);
273 : }
274 34 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
275 : }
276 :
277 34 : vlib_node_increment_counter (vm, node->node_index,
278 : PUNT_ERROR_DISPATCHED, n_dispatched);
279 :
280 34 : return frame->n_vectors;
281 : }
282 :
283 : /* *INDENT-OFF* */
284 178120 : VLIB_REGISTER_NODE (punt_dispatch_node) = {
285 : .name = "punt-dispatch",
286 : .vector_size = sizeof (u32),
287 : .format_trace = format_punt_trace,
288 : .n_errors = PUNT_N_ERRORS,
289 : .error_strings = punt_error_strings,
290 : .n_next_nodes = PUNT_N_NEXT,
291 : .next_nodes = {
292 : [PUNT_NEXT_DROP] = "drop",
293 : },
294 : };
295 :
296 : /* *INDENT-ON* */
297 :
298 : #ifndef CLIB_MARCH_VARIANT
299 : clib_error_t *
300 559 : punt_node_init (vlib_main_t * vm)
301 : {
302 559 : vec_validate (punt_clones, vlib_num_workers ());
303 :
304 559 : return NULL;
305 : }
306 :
307 3359 : VLIB_INIT_FUNCTION (punt_node_init);
308 : #endif
309 :
310 : /*
311 : * fd.io coding-style-patch-verification: ON
312 : *
313 : * Local Variables:
314 : * eval: (c-set-style "gnu")
315 : * End:
316 : */
|