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