xref: /dpdk/app/test/test_table_pipeline.c (revision f541aa2d41f8939b687e4d145a5dd18bd16f593e)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 
5 #ifndef RTE_EXEC_ENV_WINDOWS
6 
7 #include <string.h>
8 #include <rte_pipeline.h>
9 #include <inttypes.h>
10 #include <rte_hexdump.h>
11 #include "test_table.h"
12 #include "test_table_pipeline.h"
13 
14 #if 0
15 
16 static rte_pipeline_port_out_action_handler port_action_0x00
17 	(struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg);
18 static rte_pipeline_port_out_action_handler port_action_0xFF
19 	(struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg);
20 static rte_pipeline_port_out_action_handler port_action_stub
21 	(struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg);
22 
23 
24 rte_pipeline_port_out_action_handler port_action_0x00(struct rte_mbuf **pkts,
25 	uint32_t n,
26 	uint64_t *pkts_mask,
27 	void *arg)
28 {
29 	RTE_SET_USED(pkts);
30 	RTE_SET_USED(n);
31 	RTE_SET_USED(arg);
32 	printf("Port Action 0x00\n");
33 	*pkts_mask = 0x00;
34 	return 0;
35 }
36 
37 rte_pipeline_port_out_action_handler port_action_0xFF(struct rte_mbuf **pkts,
38 	uint32_t n,
39 	uint64_t *pkts_mask,
40 	void *arg)
41 {
42 	RTE_SET_USED(pkts);
43 	RTE_SET_USED(n);
44 	RTE_SET_USED(arg);
45 	printf("Port Action 0xFF\n");
46 	*pkts_mask = 0xFF;
47 	return 0;
48 }
49 
50 rte_pipeline_port_out_action_handler port_action_stub(struct rte_mbuf **pkts,
51 	uint32_t n,
52 	uint64_t *pkts_mask,
53 	void *arg)
54 {
55 	RTE_SET_USED(pkts);
56 	RTE_SET_USED(n);
57 	RTE_SET_USED(pkts_mask);
58 	RTE_SET_USED(arg);
59 	printf("Port Action stub\n");
60 	return 0;
61 }
62 
63 #endif
64 
65 rte_pipeline_table_action_handler_hit
66 table_action_0x00(struct rte_pipeline *p, struct rte_mbuf **pkts,
67 	uint64_t pkts_mask, struct rte_pipeline_table_entry **entry, void *arg);
68 
69 rte_pipeline_table_action_handler_hit
70 table_action_stub_hit(struct rte_pipeline *p, struct rte_mbuf **pkts,
71 	uint64_t pkts_mask, struct rte_pipeline_table_entry **entry, void *arg);
72 
73 static int
74 table_action_stub_miss(struct rte_pipeline *p, struct rte_mbuf **pkts,
75 	uint64_t pkts_mask, struct rte_pipeline_table_entry *entry, void *arg);
76 
77 rte_pipeline_table_action_handler_hit
table_action_0x00(__rte_unused struct rte_pipeline * p,__rte_unused struct rte_mbuf ** pkts,uint64_t pkts_mask,__rte_unused struct rte_pipeline_table_entry ** entry,__rte_unused void * arg)78 table_action_0x00(__rte_unused struct rte_pipeline *p,
79 	__rte_unused struct rte_mbuf **pkts,
80 	uint64_t pkts_mask,
81 	__rte_unused struct rte_pipeline_table_entry **entry,
82 	__rte_unused void *arg)
83 {
84 	printf("Table Action, setting pkts_mask to 0x00\n");
85 	pkts_mask = ~0x00;
86 	rte_pipeline_ah_packet_drop(p, pkts_mask);
87 	return 0;
88 }
89 
90 rte_pipeline_table_action_handler_hit
table_action_stub_hit(__rte_unused struct rte_pipeline * p,__rte_unused struct rte_mbuf ** pkts,uint64_t pkts_mask,__rte_unused struct rte_pipeline_table_entry ** entry,__rte_unused void * arg)91 table_action_stub_hit(__rte_unused struct rte_pipeline *p,
92 	__rte_unused struct rte_mbuf **pkts,
93 	uint64_t pkts_mask,
94 	__rte_unused struct rte_pipeline_table_entry **entry,
95 	__rte_unused void *arg)
96 {
97 	printf("STUB Table Action Hit - doing nothing\n");
98 	printf("STUB Table Action Hit - setting mask to 0x%"PRIx64"\n",
99 		override_hit_mask);
100 	pkts_mask = (~override_hit_mask) & 0x3;
101 	rte_pipeline_ah_packet_drop(p, pkts_mask);
102 	return 0;
103 }
104 
105 static int
table_action_stub_miss(struct rte_pipeline * p,__rte_unused struct rte_mbuf ** pkts,uint64_t pkts_mask,__rte_unused struct rte_pipeline_table_entry * entry,__rte_unused void * arg)106 table_action_stub_miss(struct rte_pipeline *p,
107 	__rte_unused struct rte_mbuf **pkts,
108 	uint64_t pkts_mask,
109 	__rte_unused struct rte_pipeline_table_entry *entry,
110 	__rte_unused void *arg)
111 {
112 	printf("STUB Table Action Miss - setting mask to 0x%"PRIx64"\n",
113 		override_miss_mask);
114 	pkts_mask = (~override_miss_mask) & 0x3;
115 	rte_pipeline_ah_packet_drop(p, pkts_mask);
116 	return 0;
117 }
118 
119 enum e_test_type {
120 	e_TEST_STUB = 0,
121 	e_TEST_LPM,
122 	e_TEST_LPM6,
123 	e_TEST_HASH_LRU_8,
124 	e_TEST_HASH_LRU_16,
125 	e_TEST_HASH_LRU_32,
126 	e_TEST_HASH_EXT_8,
127 	e_TEST_HASH_EXT_16,
128 	e_TEST_HASH_EXT_32
129 };
130 
131 char pipeline_test_names[][64] = {
132 	"Stub",
133 	"LPM",
134 	"LPMv6",
135 	"8-bit LRU Hash",
136 	"16-bit LRU Hash",
137 	"32-bit LRU Hash",
138 	"16-bit Ext Hash",
139 	"8-bit Ext Hash",
140 	"32-bit Ext Hash",
141 	""
142 };
143 
144 
145 static int
cleanup_pipeline(void)146 cleanup_pipeline(void)
147 {
148 
149 	rte_pipeline_free(p);
150 
151 	return 0;
152 }
153 
154 
155 static int check_pipeline_invalid_params(void);
156 
157 static int
check_pipeline_invalid_params(void)158 check_pipeline_invalid_params(void)
159 {
160 	struct rte_pipeline_params pipeline_params_1 = {
161 		.name = NULL,
162 		.socket_id = 0,
163 	};
164 	struct rte_pipeline_params pipeline_params_2 = {
165 		.name = "PIPELINE",
166 		.socket_id = -1,
167 	};
168 	struct rte_pipeline_params pipeline_params_3 = {
169 		.name = "PIPELINE",
170 		.socket_id = 127,
171 	};
172 
173 	p = rte_pipeline_create(NULL);
174 	if (p != NULL) {
175 		fprintf(stderr,
176 			"%s: configured pipeline with null params\n",
177 			__func__);
178 		goto fail;
179 	}
180 	p = rte_pipeline_create(&pipeline_params_1);
181 	if (p != NULL) {
182 		fprintf(stderr,
183 			"%s: Configure pipeline with NULL name\n", __func__);
184 		goto fail;
185 	}
186 
187 	p = rte_pipeline_create(&pipeline_params_2);
188 	if (p != NULL) {
189 		fprintf(stderr,
190 			"%s: Configure pipeline with invalid socket\n", __func__);
191 		goto fail;
192 	}
193 
194 	if (rte_eal_has_hugepages()) {
195 		p = rte_pipeline_create(&pipeline_params_3);
196 		if (p != NULL) {
197 			fprintf(stderr,
198 				"%s: Configure pipeline with invalid socket\n",
199 				__func__);
200 			goto fail;
201 		}
202 	}
203 
204 	/* Check pipeline consistency */
205 	if (!rte_pipeline_check(p)) {
206 		rte_panic("Pipeline consistency reported as OK\n");
207 		goto fail;
208 	}
209 
210 
211 	return 0;
212 fail:
213 	return -1;
214 }
215 
216 
217 static int
setup_pipeline(int test_type)218 setup_pipeline(int test_type)
219 {
220 	int ret;
221 	int i;
222 	struct rte_pipeline_params pipeline_params = {
223 		.name = "PIPELINE",
224 		.socket_id = 0,
225 	};
226 
227 	fprintf(stderr, "%s: **** Setting up %s test\n",
228 		__func__, pipeline_test_names[test_type]);
229 
230 	/* Pipeline configuration */
231 	p = rte_pipeline_create(&pipeline_params);
232 	if (p == NULL) {
233 		fprintf(stderr, "%s: Failed to configure pipeline\n",
234 			__func__);
235 		goto fail;
236 	}
237 
238 	ret = rte_pipeline_free(p);
239 	if (ret != 0) {
240 		fprintf(stderr, "%s: Failed to free pipeline\n",
241 			__func__);
242 		goto fail;
243 	}
244 
245 	/* Pipeline configuration */
246 	p = rte_pipeline_create(&pipeline_params);
247 	if (p == NULL) {
248 		fprintf(stderr, "%s: Failed to configure pipeline\n",
249 			__func__);
250 		goto fail;
251 	}
252 
253 
254 	/* Input port configuration */
255 	for (i = 0; i < N_PORTS; i++) {
256 		struct rte_port_ring_reader_params port_ring_params = {
257 			.ring = rings_rx[i],
258 		};
259 
260 		struct rte_pipeline_port_in_params port_params = {
261 			.ops = &rte_port_ring_reader_ops,
262 			.arg_create = (void *) &port_ring_params,
263 			.f_action = NULL,
264 			.burst_size = BURST_SIZE,
265 		};
266 
267 		/* Put in action for some ports */
268 		if (i)
269 			port_params.f_action = NULL;
270 
271 		ret = rte_pipeline_port_in_create(p, &port_params,
272 			&port_in_id[i]);
273 		if (ret) {
274 			rte_panic("Unable to configure input port %d, ret:%d\n",
275 				i, ret);
276 			goto fail;
277 		}
278 	}
279 
280 	/* output Port configuration */
281 	for (i = 0; i < N_PORTS; i++) {
282 		struct rte_port_ring_writer_params port_ring_params = {
283 			.ring = rings_tx[i],
284 			.tx_burst_sz = BURST_SIZE,
285 		};
286 
287 		struct rte_pipeline_port_out_params port_params = {
288 			.ops = &rte_port_ring_writer_ops,
289 			.arg_create = (void *) &port_ring_params,
290 			.f_action = NULL,
291 			.arg_ah = NULL,
292 		};
293 
294 		if (i)
295 			port_params.f_action = port_out_action;
296 
297 		if (rte_pipeline_port_out_create(p, &port_params,
298 			&port_out_id[i])) {
299 			rte_panic("Unable to configure output port %d\n", i);
300 			goto fail;
301 		}
302 	}
303 
304 	/* Table configuration  */
305 	for (i = 0; i < N_PORTS; i++) {
306 		struct rte_pipeline_table_params table_params = {
307 				.ops = &rte_table_stub_ops,
308 				.arg_create = NULL,
309 				.f_action_hit = action_handler_hit,
310 				.f_action_miss = action_handler_miss,
311 				.action_data_size = 0,
312 		};
313 
314 		if (rte_pipeline_table_create(p, &table_params, &table_id[i])) {
315 			rte_panic("Unable to configure table %u\n", i);
316 			goto fail;
317 		}
318 
319 		if (connect_miss_action_to_table)
320 			if (rte_pipeline_table_create(p, &table_params,
321 				&table_id[i+2])) {
322 				rte_panic("Unable to configure table %u\n", i);
323 				goto fail;
324 			}
325 	}
326 
327 	for (i = 0; i < N_PORTS; i++)
328 		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
329 			table_id[i])) {
330 			rte_panic("Unable to connect input port %u to "
331 				"table %u\n", port_in_id[i],  table_id[i]);
332 			goto fail;
333 		}
334 
335 	/* Add entries to tables */
336 	for (i = 0; i < N_PORTS; i++) {
337 		struct rte_pipeline_table_entry default_entry = {
338 			.action = (enum rte_pipeline_action)
339 				table_entry_default_action,
340 			{.port_id = port_out_id[i^1]},
341 		};
342 		struct rte_pipeline_table_entry *default_entry_ptr;
343 
344 		if (connect_miss_action_to_table) {
345 			printf("Setting first table to output to next table\n");
346 			default_entry.action = RTE_PIPELINE_ACTION_TABLE;
347 			default_entry.table_id = table_id[i+2];
348 		}
349 
350 		/* Add the default action for the table. */
351 		ret = rte_pipeline_table_default_entry_add(p, table_id[i],
352 			&default_entry, &default_entry_ptr);
353 		if (ret < 0) {
354 			rte_panic("Unable to add default entry to table %u "
355 				"code %d\n", table_id[i], ret);
356 			goto fail;
357 		} else
358 			printf("Added default entry to table id %d with "
359 				"action %x\n",
360 				table_id[i], default_entry.action);
361 
362 		if (connect_miss_action_to_table) {
363 			/* We create a second table so the first can pass
364 			traffic into it */
365 			struct rte_pipeline_table_entry default_entry = {
366 				.action = RTE_PIPELINE_ACTION_PORT,
367 				{.port_id = port_out_id[i^1]},
368 			};
369 			printf("Setting second table to output to port\n");
370 
371 			/* Add the default action for the table. */
372 			ret = rte_pipeline_table_default_entry_add(p,
373 				table_id[i+2],
374 				&default_entry, &default_entry_ptr);
375 			if (ret < 0) {
376 				rte_panic("Unable to add default entry to "
377 					"table %u code %d\n",
378 					table_id[i], ret);
379 				goto fail;
380 			} else
381 				printf("Added default entry to table id %d "
382 					"with action %x\n",
383 					table_id[i], default_entry.action);
384 		}
385 	}
386 
387 	/* Enable input ports */
388 	for (i = 0; i < N_PORTS ; i++)
389 		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
390 			rte_panic("Unable to enable input port %u\n",
391 				port_in_id[i]);
392 
393 	/* Check pipeline consistency */
394 	if (rte_pipeline_check(p) < 0) {
395 		rte_panic("Pipeline consistency check failed\n");
396 		goto fail;
397 	} else
398 		printf("Pipeline Consistency OK!\n");
399 
400 	return 0;
401 fail:
402 
403 	return -1;
404 }
405 
406 static int
test_pipeline_single_filter(int test_type,int expected_count)407 test_pipeline_single_filter(int test_type, int expected_count)
408 {
409 	int i;
410 	int j;
411 	int ret;
412 	int tx_count;
413 
414 	fprintf(stderr, "%s: **** Running %s test\n",
415 		__func__, pipeline_test_names[test_type]);
416 	/* Run pipeline once */
417 	for (i = 0; i < N_PORTS; i++)
418 		rte_pipeline_run(p);
419 
420 
421 	ret = rte_pipeline_flush(NULL);
422 	if (ret != -EINVAL) {
423 		fprintf(stderr,
424 			"%s: No pipeline flush error NULL pipeline (%d)\n",
425 			__func__, ret);
426 		goto fail;
427 	}
428 
429 	/*
430 	 * Allocate a few mbufs and manually insert into the rings. */
431 	for (i = 0; i < N_PORTS; i++)
432 		for (j = 0; j < N_PORTS; j++) {
433 			struct rte_mbuf *m;
434 			uint8_t *key;
435 			uint32_t *k32;
436 
437 			m = rte_pktmbuf_alloc(pool);
438 			if (m == NULL) {
439 				rte_panic("Failed to alloc mbuf from pool\n");
440 				return -1;
441 			}
442 			key = RTE_MBUF_METADATA_UINT8_PTR(m,
443 					APP_METADATA_OFFSET(32));
444 
445 			k32 = (uint32_t *) key;
446 			k32[0] = 0xadadadad >> (j % 2);
447 
448 			fprintf(stderr, "%s: Enqueue onto ring %d\n",
449 				__func__, i);
450 			rte_ring_enqueue(rings_rx[i], m);
451 		}
452 
453 	/* Run pipeline once */
454 	for (i = 0; i < N_PORTS; i++)
455 		rte_pipeline_run(p);
456 
457    /*
458 	* need to flush the pipeline, as there may be less hits than the burst
459 	size and they will not have been flushed to the tx rings. */
460 	rte_pipeline_flush(p);
461 
462    /*
463 	* Now we'll see what we got back on the tx rings. We should see whatever
464 	* packets we had hits on that were destined for the output ports.
465 	*/
466 	tx_count = 0;
467 
468 	for (i = 0; i < N_PORTS; i++) {
469 		void *objs[RING_TX_SIZE];
470 		struct rte_mbuf *mbuf;
471 
472 		ret = rte_ring_sc_dequeue_burst(rings_tx[i], objs, 10, NULL);
473 		if (ret <= 0)
474 			printf("Got no objects from ring %d - error code %d\n",
475 				i, ret);
476 		else {
477 			printf("Got %d object(s) from ring %d!\n", ret, i);
478 			for (j = 0; j < ret; j++) {
479 				mbuf = objs[j];
480 				rte_hexdump(stdout, "Object:",
481 					rte_pktmbuf_mtod(mbuf, char *),
482 					mbuf->data_len);
483 				rte_pktmbuf_free(mbuf);
484 			}
485 			tx_count += ret;
486 		}
487 	}
488 
489 	if (tx_count != expected_count) {
490 		fprintf(stderr,
491 			"%s: Unexpected packets out for %s test, expected %d, got %d\n",
492 			__func__, pipeline_test_names[test_type],
493 			expected_count, tx_count);
494 		goto fail;
495 	}
496 
497 	cleanup_pipeline();
498 
499 	return 0;
500 fail:
501 	return -1;
502 
503 }
504 
505 int
test_table_pipeline(void)506 test_table_pipeline(void)
507 {
508 	/* TEST - All packets dropped */
509 	action_handler_hit = NULL;
510 	action_handler_miss = NULL;
511 	table_entry_default_action = RTE_PIPELINE_ACTION_DROP;
512 	setup_pipeline(e_TEST_STUB);
513 	if (test_pipeline_single_filter(e_TEST_STUB, 0) < 0)
514 		return -1;
515 
516 	/* TEST - All packets passed through */
517 	table_entry_default_action = RTE_PIPELINE_ACTION_PORT;
518 	setup_pipeline(e_TEST_STUB);
519 	if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0)
520 		return -1;
521 
522 	/* TEST - one packet per port */
523 	action_handler_hit = NULL;
524 	action_handler_miss = table_action_stub_miss;
525 	table_entry_default_action = RTE_PIPELINE_ACTION_PORT;
526 	override_miss_mask = 0x01; /* one packet per port */
527 	setup_pipeline(e_TEST_STUB);
528 	if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0)
529 		return -1;
530 
531 	/* TEST - one packet per port */
532 	override_miss_mask = 0x02; /*all per port */
533 	setup_pipeline(e_TEST_STUB);
534 	if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0)
535 		return -1;
536 
537 	/* TEST - all packets per port */
538 	override_miss_mask = 0x03; /*all per port */
539 	setup_pipeline(e_TEST_STUB);
540 	if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0)
541 		return -1;
542 
543    /*
544 	* This test will set up two tables in the pipeline. the first table
545 	* will forward to another table on miss, and the second table will
546 	* forward to port.
547 	*/
548 	connect_miss_action_to_table = 1;
549 	table_entry_default_action = RTE_PIPELINE_ACTION_TABLE;
550 	action_handler_hit = NULL;  /* not for stub, hitmask always zero */
551 	action_handler_miss = NULL;
552 	setup_pipeline(e_TEST_STUB);
553 	if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0)
554 		return -1;
555 	connect_miss_action_to_table = 0;
556 
557 	printf("TEST - two tables, hitmask override to 0x01\n");
558 	connect_miss_action_to_table = 1;
559 	action_handler_miss = table_action_stub_miss;
560 	override_miss_mask = 0x01;
561 	setup_pipeline(e_TEST_STUB);
562 	if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0)
563 		return -1;
564 	connect_miss_action_to_table = 0;
565 
566 	if (check_pipeline_invalid_params()) {
567 		fprintf(stderr, "%s: Check pipeline invalid params failed.\n",
568 			__func__);
569 		return -1;
570 	}
571 
572 	return 0;
573 }
574 
575 #endif /* !RTE_EXEC_ENV_WINDOWS */
576