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/vlib.h>
17 : #include <vlib/buffer_funcs.h>
18 :
19 : #define TEST_I(_cond, _comment, _args...) \
20 : ({ \
21 : int _evald = (0 == (_cond)); \
22 : if (_evald) \
23 : { \
24 : fformat (stderr, "FAIL:%d: " _comment "\n", __LINE__, ##_args); \
25 : } \
26 : else \
27 : { \
28 : fformat (stderr, "PASS:%d: " _comment "\n", __LINE__, ##_args); \
29 : } \
30 : _evald; \
31 : })
32 :
33 : #define TEST(_cond, _comment, _args...) \
34 : { \
35 : if (TEST_I (_cond, _comment, ##_args)) \
36 : { \
37 : goto err; \
38 : } \
39 : }
40 :
41 : typedef struct
42 : {
43 : i16 current_data;
44 : u16 current_length;
45 : u8 ref_count;
46 : } chained_buffer_template_t;
47 :
48 : static int
49 104 : 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_,
51 : u32 *bi_)
52 : {
53 104 : vlib_buffer_t *bufs[2 * VLIB_BUFFER_LINEARIZE_MAX], **b = bufs;
54 104 : u32 bis[2 * VLIB_BUFFER_LINEARIZE_MAX + 1], *bi = bis;
55 : u32 n_alloc;
56 :
57 104 : if (rand)
58 104 : vec_reset_length (*rand);
59 :
60 104 : ASSERT (n <= ARRAY_LEN (bufs));
61 104 : n_alloc = vlib_buffer_alloc (vm, bi, n);
62 104 : if (n_alloc != n)
63 : {
64 0 : vlib_buffer_free (vm, bi, n_alloc);
65 0 : return 0;
66 : }
67 :
68 104 : vlib_get_buffers (vm, bis, bufs, n);
69 :
70 3220 : while (n > 0)
71 : {
72 3116 : b[0]->next_buffer = bi[1];
73 3116 : b[0]->flags |= VLIB_BUFFER_NEXT_PRESENT;
74 3116 : b[0]->current_data = tmpl->current_data;
75 3116 : b[0]->current_length = tmpl->current_length;
76 3116 : b[0]->ref_count = 0xff == tmpl->ref_count ? 1 : tmpl->ref_count;
77 :
78 3116 : if (rand)
79 : {
80 3116 : const u16 len = b[0]->current_length;
81 3116 : if (len)
82 : {
83 3115 : vec_add (*rand, clib_random_buffer_get_data (randbuf, len), len);
84 3115 : void *dst = vlib_buffer_get_current (b[0]);
85 3115 : const void *src =
86 3115 : vec_elt_at_index (*rand, vec_len (*rand) - len);
87 3115 : clib_memcpy_fast (dst, src, len);
88 : }
89 : }
90 :
91 3116 : b++;
92 3116 : bi++;
93 3116 : tmpl++;
94 3116 : n--;
95 : }
96 :
97 104 : b[-1]->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
98 :
99 104 : *b_ = bufs[0];
100 104 : *bi_ = bis[0];
101 104 : return 1;
102 : }
103 :
104 : static int
105 104 : check_chain (vlib_main_t *vm, vlib_buffer_t *b, const u8 *rand)
106 : {
107 104 : int len_chain = vlib_buffer_length_in_chain (vm, b);
108 : int len;
109 :
110 : /* check for data corruption */
111 104 : if (clib_memcmp (vlib_buffer_get_current (b), vec_elt_at_index (rand, 0),
112 : b->current_length))
113 0 : return 0;
114 104 : len = b->current_length;
115 1571 : while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
116 : {
117 1467 : b = vlib_get_buffer (vm, b->next_buffer);
118 1467 : if (clib_memcmp (vlib_buffer_get_current (b),
119 : vec_elt_at_index (rand, len), b->current_length))
120 0 : return 0;
121 1467 : len += b->current_length;
122 : }
123 :
124 : /* check for data truncation */
125 104 : if (len != vec_len (rand))
126 0 : return 0;
127 :
128 : /* check total length update is correct */
129 104 : if (len != len_chain)
130 0 : return 0;
131 :
132 104 : return 1;
133 : }
134 :
135 : static int
136 104 : 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,
138 : u8 **rand)
139 : {
140 : vlib_buffer_t *b;
141 : u32 bi[2];
142 104 : int ret = 0;
143 :
144 104 : if (!build_chain (vm, tmpl, n, randbuf, rand, &b, bi))
145 0 : goto err0;
146 :
147 104 : if (clone_off)
148 : {
149 1 : if (2 != vlib_buffer_clone (vm, bi[0], bi, 2, clone_off))
150 0 : goto err1;
151 1 : b = vlib_get_buffer (vm, bi[0]);
152 : }
153 :
154 104 : if (!(ret = vlib_buffer_chain_linearize (vm, b)))
155 0 : goto err2;
156 :
157 104 : if (!check_chain (vm, b, *rand))
158 : {
159 0 : ret = 0;
160 0 : goto err2;
161 : }
162 :
163 104 : err2:
164 104 : if (clone_off)
165 1 : vlib_buffer_free_one (vm, bi[1]);
166 103 : err1:
167 104 : vlib_buffer_free_one (vm, bi[0]);
168 104 : err0:
169 104 : return ret;
170 : }
171 :
172 : static int
173 1 : linearize_test (vlib_main_t *vm)
174 : {
175 : chained_buffer_template_t tmpl[VLIB_BUFFER_LINEARIZE_MAX];
176 : clib_random_buffer_t randbuf;
177 1 : u32 data_size = vlib_buffer_get_default_data_size (vm);
178 1 : u8 *rand = 0;
179 1 : int ret = 0;
180 : int i;
181 :
182 1 : clib_random_buffer_init (&randbuf, 0);
183 :
184 1 : clib_memset (tmpl, 0xff, sizeof (tmpl));
185 3 : for (i = 0; i < 2; i++)
186 : {
187 2 : tmpl[i].current_data = -14;
188 2 : tmpl[i].current_length = 14 + data_size;
189 : }
190 1 : TEST (2 == test_chain (vm, tmpl, 2, 0, &randbuf, &rand),
191 : "linearize chain with negative current data");
192 :
193 1 : clib_memset (tmpl, 0xff, sizeof (tmpl));
194 1 : tmpl[0].current_data = 12;
195 1 : tmpl[0].current_length = data_size - 12;
196 1 : tmpl[1].current_data = 0;
197 1 : tmpl[1].current_length = 0;
198 1 : TEST (1 == test_chain (vm, tmpl, 2, 0, &randbuf, &rand),
199 : "linearize chain with empty next");
200 :
201 1 : clib_memset (tmpl, 0xff, sizeof (tmpl));
202 1 : tmpl[0].current_data = 0;
203 1 : tmpl[0].current_length = data_size - 17;
204 1 : tmpl[1].current_data = -5;
205 1 : tmpl[1].current_length = 3;
206 1 : tmpl[2].current_data = 17;
207 1 : tmpl[2].current_length = 9;
208 1 : tmpl[3].current_data = 3;
209 1 : tmpl[3].current_length = 5;
210 1 : TEST (1 == test_chain (vm, tmpl, 4, 0, &randbuf, &rand),
211 : "linearize chain into a single buffer");
212 :
213 1 : clib_memset (tmpl, 0xff, sizeof (tmpl));
214 1 : tmpl[0].current_data = 0;
215 1 : tmpl[0].current_length = data_size - 2;
216 1 : tmpl[1].current_data = -VLIB_BUFFER_PRE_DATA_SIZE;
217 1 : tmpl[1].current_length = 20;
218 1 : tmpl[2].current_data = data_size - 10;
219 1 : tmpl[2].current_length = 10;
220 1 : tmpl[3].current_data = 0;
221 1 : tmpl[3].current_length = data_size;
222 1 : TEST (2 == test_chain (vm, tmpl, 4, data_size - 1, &randbuf, &rand),
223 : "linearize cloned chain");
224 :
225 1 : clib_memset (tmpl, 0xff, sizeof (tmpl));
226 101 : for (i = 0; i < 100; i++)
227 : {
228 100 : u8 *r = clib_random_buffer_get_data (&randbuf, 1);
229 100 : int n = clib_max (r[0] % ARRAY_LEN (tmpl), 1);
230 : int j;
231 3204 : for (j = 0; j < n; j++)
232 : {
233 3104 : r = clib_random_buffer_get_data (&randbuf, 3);
234 3104 : i16 current_data = (i16) r[0] - VLIB_BUFFER_PRE_DATA_SIZE;
235 3104 : u16 current_length = *(u16 *) (r + 1) % (data_size - current_data);
236 3104 : tmpl[j].current_data = current_data;
237 3104 : tmpl[j].current_length = current_length;
238 : }
239 100 : r = clib_random_buffer_get_data (&randbuf, 1);
240 100 : TEST (
241 : test_chain (vm, tmpl, n, r[0] > 250 ? r[0] % 128 : 0, &randbuf, &rand),
242 : "linearize random chain %d", i);
243 : }
244 :
245 1 : ret = 1;
246 1 : err:
247 1 : clib_random_buffer_free (&randbuf);
248 1 : vec_free (rand);
249 1 : return ret;
250 : }
251 :
252 : static clib_error_t *
253 1 : test_linearize_fn (vlib_main_t * vm, unformat_input_t * input,
254 : vlib_cli_command_t * cmd)
255 : {
256 :
257 1 : if (!linearize_test (vm))
258 : {
259 0 : return clib_error_return (0, "linearize test failed");
260 : }
261 :
262 1 : return 0;
263 : }
264 :
265 : /* *INDENT-OFF* */
266 16239 : VLIB_CLI_COMMAND (test_linearize_command, static) =
267 : {
268 : .path = "test chained-buffer-linearization",
269 : .short_help = "test chained-buffer-linearization",
270 : .function = test_linearize_fn,
271 : };
272 : /* *INDENT-ON* */
273 :
274 : static clib_error_t *
275 0 : test_linearize_speed_fn (vlib_main_t *vm, unformat_input_t *input,
276 : vlib_cli_command_t *cmd)
277 : {
278 : /* typical 9000-bytes TCP jumbo frames */
279 0 : const chained_buffer_template_t tmpl[5] = { { 14, 2034, 1 },
280 : { 0, 2048, 1 },
281 : { 0, 2048, 1 },
282 : { 0, 2048, 1 },
283 : { 0, 808, 1 } };
284 : int i, j;
285 :
286 0 : for (i = 0; i < 10; i++)
287 : {
288 0 : u64 tot = 0;
289 0 : for (j = 0; j < 100000; j++)
290 : {
291 : vlib_buffer_t *b;
292 : u32 bi;
293 :
294 0 : if (!build_chain (vm, tmpl, 5, 0, 0, &b, &bi))
295 0 : return clib_error_create ("build_chain() failed");
296 :
297 0 : CLIB_COMPILER_BARRIER ();
298 0 : u64 start = clib_cpu_time_now ();
299 0 : CLIB_COMPILER_BARRIER ();
300 :
301 0 : vlib_buffer_chain_linearize (vm, b);
302 :
303 0 : CLIB_COMPILER_BARRIER ();
304 0 : tot += clib_cpu_time_now () - start;
305 0 : CLIB_COMPILER_BARRIER ();
306 :
307 0 : vlib_buffer_free_one (vm, bi);
308 : }
309 0 : vlib_cli_output (vm, "%.03f ticks/call", (f64) tot / j);
310 : }
311 :
312 0 : return 0;
313 : }
314 :
315 16239 : VLIB_CLI_COMMAND (test_linearize_speed_command, static) = {
316 : .path = "test chained-buffer-linearization speed",
317 : .short_help = "test chained-buffer-linearization speed",
318 : .function = test_linearize_speed_fn,
319 : };
320 :
321 : /*
322 : * fd.io coding-style-patch-verification: ON
323 : *
324 : * Local Variables:
325 : * eval: (c-set-style "gnu")
326 : * End:
327 : */
|