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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <vlib/vlib.h>
17 #include <vlib/buffer_funcs.h>
19 #define TEST_I(_cond, _comment, _args...) \
21 int _evald = (0 == (_cond)); \
24 fformat (stderr, "FAIL:%d: " _comment "\n", __LINE__, ##_args); \
28 fformat (stderr, "PASS:%d: " _comment "\n", __LINE__, ##_args); \
33 #define TEST(_cond, _comment, _args...) \
35 if (TEST_I (_cond, _comment, ##_args)) \
46 } chained_buffer_template_t;
49 build_chain (vlib_main_t *vm, const chained_buffer_template_t *tmpl, u32 n,
50 clib_random_buffer_t *randbuf, u8 **rand, vlib_buffer_t **b_,
53 vlib_buffer_t *bufs[2 * VLIB_BUFFER_LINEARIZE_MAX], **b = bufs;
54 u32 bis[2 * VLIB_BUFFER_LINEARIZE_MAX + 1], *bi = bis;
58 vec_reset_length (*rand);
60 ASSERT (n <= ARRAY_LEN (bufs));
61 n_alloc = vlib_buffer_alloc (vm, bi, n);
64 vlib_buffer_free (vm, bi, n_alloc);
68 vlib_get_buffers (vm, bis, bufs, n);
72 b[0]->next_buffer = bi[1];
73 b[0]->flags |= VLIB_BUFFER_NEXT_PRESENT;
74 b[0]->current_data = tmpl->current_data;
75 b[0]->current_length = tmpl->current_length;
76 b[0]->ref_count = 0xff == tmpl->ref_count ? 1 : tmpl->ref_count;
80 const u16 len = b[0]->current_length;
83 vec_add (*rand, clib_random_buffer_get_data (randbuf, len), len);
84 void *dst = vlib_buffer_get_current (b[0]);
86 vec_elt_at_index (*rand, vec_len (*rand) - len);
87 clib_memcpy_fast (dst, src, len);
97 b[-1]->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
105 check_chain (vlib_main_t *vm, vlib_buffer_t *b, const u8 *rand)
107 int len_chain = vlib_buffer_length_in_chain (vm, b);
110 /* check for data corruption */
111 if (clib_memcmp (vlib_buffer_get_current (b), vec_elt_at_index (rand, 0),
114 len = b->current_length;
115 while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
117 b = vlib_get_buffer (vm, b->next_buffer);
118 if (clib_memcmp (vlib_buffer_get_current (b),
119 vec_elt_at_index (rand, len), b->current_length))
121 len += b->current_length;
124 /* check for data truncation */
125 if (len != vec_len (rand))
128 /* check total length update is correct */
129 if (len != len_chain)
136 test_chain (vlib_main_t *vm, const chained_buffer_template_t *tmpl,
137 const u32 n, const int clone_off, clib_random_buffer_t *randbuf,
144 if (!build_chain (vm, tmpl, n, randbuf, rand, &b, bi))
149 if (2 != vlib_buffer_clone (vm, bi[0], bi, 2, clone_off))
151 b = vlib_get_buffer (vm, bi[0]);
154 if (!(ret = vlib_buffer_chain_linearize (vm, b)))
157 if (!check_chain (vm, b, *rand))
165 vlib_buffer_free_one (vm, bi[1]);
167 vlib_buffer_free_one (vm, bi[0]);
173 linearize_test (vlib_main_t *vm)
175 chained_buffer_template_t tmpl[VLIB_BUFFER_LINEARIZE_MAX];
176 clib_random_buffer_t randbuf;
177 u32 data_size = vlib_buffer_get_default_data_size (vm);
182 clib_random_buffer_init (&randbuf, 0);
184 clib_memset (tmpl, 0xff, sizeof (tmpl));
185 for (i = 0; i < 2; i++)
187 tmpl[i].current_data = -14;
188 tmpl[i].current_length = 14 + data_size;
190 TEST (2 == test_chain (vm, tmpl, 2, 0, &randbuf, &rand),
191 "linearize chain with negative current data");
193 clib_memset (tmpl, 0xff, sizeof (tmpl));
194 tmpl[0].current_data = 12;
195 tmpl[0].current_length = data_size - 12;
196 tmpl[1].current_data = 0;
197 tmpl[1].current_length = 0;
198 TEST (1 == test_chain (vm, tmpl, 2, 0, &randbuf, &rand),
199 "linearize chain with empty next");
201 clib_memset (tmpl, 0xff, sizeof (tmpl));
202 tmpl[0].current_data = 0;
203 tmpl[0].current_length = data_size - 17;
204 tmpl[1].current_data = -5;
205 tmpl[1].current_length = 3;
206 tmpl[2].current_data = 17;
207 tmpl[2].current_length = 9;
208 tmpl[3].current_data = 3;
209 tmpl[3].current_length = 5;
210 TEST (1 == test_chain (vm, tmpl, 4, 0, &randbuf, &rand),
211 "linearize chain into a single buffer");
213 clib_memset (tmpl, 0xff, sizeof (tmpl));
214 tmpl[0].current_data = 0;
215 tmpl[0].current_length = data_size - 2;
216 tmpl[1].current_data = -VLIB_BUFFER_PRE_DATA_SIZE;
217 tmpl[1].current_length = 20;
218 tmpl[2].current_data = data_size - 10;
219 tmpl[2].current_length = 10;
220 tmpl[3].current_data = 0;
221 tmpl[3].current_length = data_size;
222 TEST (2 == test_chain (vm, tmpl, 4, data_size - 1, &randbuf, &rand),
223 "linearize cloned chain");
225 clib_memset (tmpl, 0xff, sizeof (tmpl));
226 for (i = 0; i < 100; i++)
228 u8 *r = clib_random_buffer_get_data (&randbuf, 1);
229 int n = clib_max (r[0] % ARRAY_LEN (tmpl), 1);
231 for (j = 0; j < n; j++)
233 r = clib_random_buffer_get_data (&randbuf, 3);
234 i16 current_data = (i16) r[0] - VLIB_BUFFER_PRE_DATA_SIZE;
235 u16 current_length = *(u16 *) (r + 1) % (data_size - current_data);
236 tmpl[j].current_data = current_data;
237 tmpl[j].current_length = current_length;
239 r = clib_random_buffer_get_data (&randbuf, 1);
241 test_chain (vm, tmpl, n, r[0] > 250 ? r[0] % 128 : 0, &randbuf, &rand),
242 "linearize random chain %d", i);
247 clib_random_buffer_free (&randbuf);
252 static clib_error_t *
253 test_linearize_fn (vlib_main_t * vm, unformat_input_t * input,
254 vlib_cli_command_t * cmd)
257 if (!linearize_test (vm))
259 return clib_error_return (0, "linearize test failed");
265 VLIB_CLI_COMMAND (test_linearize_command, static) =
267 .path = "test chained-buffer-linearization",
268 .short_help = "test chained-buffer-linearization",
269 .function = test_linearize_fn,
272 static clib_error_t *
273 test_linearize_speed_fn (vlib_main_t *vm, unformat_input_t *input,
274 vlib_cli_command_t *cmd)
276 /* typical 9000-bytes TCP jumbo frames */
277 const chained_buffer_template_t tmpl[5] = { { 14, 2034, 1 },
284 for (i = 0; i < 10; i++)
287 for (j = 0; j < 100000; j++)
292 if (!build_chain (vm, tmpl, 5, 0, 0, &b, &bi))
293 return clib_error_create ("build_chain() failed");
295 CLIB_COMPILER_BARRIER ();
296 u64 start = clib_cpu_time_now ();
297 CLIB_COMPILER_BARRIER ();
299 vlib_buffer_chain_linearize (vm, b);
301 CLIB_COMPILER_BARRIER ();
302 tot += clib_cpu_time_now () - start;
303 CLIB_COMPILER_BARRIER ();
305 vlib_buffer_free_one (vm, bi);
307 vlib_cli_output (vm, "%.03f ticks/call", (f64) tot / j);
313 VLIB_CLI_COMMAND (test_linearize_speed_command, static) = {
314 .path = "test chained-buffer-linearization speed",
315 .short_help = "test chained-buffer-linearization speed",
316 .function = test_linearize_speed_fn,
320 * fd.io coding-style-patch-verification: ON
323 * eval: (c-set-style "gnu")