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