app/testpmd: rework softnic forward mode
[dpdk.git] / app / test-pmd / softnicfwd.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2017 Intel Corporation
3  */
4 #include <stdio.h>
5 #include <sys/stat.h>
6
7 #include <rte_cycles.h>
8 #include <rte_mbuf.h>
9 #include <rte_malloc.h>
10 #include <rte_ethdev.h>
11 #include <rte_flow.h>
12 #include <rte_meter.h>
13 #include <rte_eth_softnic.h>
14 #include <rte_tm.h>
15
16 #include "testpmd.h"
17
18 #define SUBPORT_NODES_PER_PORT          1
19 #define PIPE_NODES_PER_SUBPORT          4096
20 #define TC_NODES_PER_PIPE                       4
21 #define QUEUE_NODES_PER_TC                      4
22
23 #define NUM_PIPE_NODES                                          \
24         (SUBPORT_NODES_PER_PORT * PIPE_NODES_PER_SUBPORT)
25
26 #define NUM_TC_NODES                                            \
27         (NUM_PIPE_NODES * TC_NODES_PER_PIPE)
28
29 #define ROOT_NODE_ID                            1000000
30 #define SUBPORT_NODES_START_ID          900000
31 #define PIPE_NODES_START_ID                     800000
32 #define TC_NODES_START_ID                       700000
33
34 #define STATS_MASK_DEFAULT                                      \
35         (RTE_TM_STATS_N_PKTS |                                  \
36         RTE_TM_STATS_N_BYTES |                                  \
37         RTE_TM_STATS_N_PKTS_GREEN_DROPPED |                     \
38         RTE_TM_STATS_N_BYTES_GREEN_DROPPED)
39
40 #define STATS_MASK_QUEUE                                        \
41         (STATS_MASK_DEFAULT |                                   \
42         RTE_TM_STATS_N_PKTS_QUEUED)
43
44 #define BYTES_IN_MBPS                           (1000 * 1000 / 8)
45 #define TOKEN_BUCKET_SIZE                       1000000
46
47 /* TM Hierarchy Levels */
48 enum tm_hierarchy_level {
49         TM_NODE_LEVEL_PORT = 0,
50         TM_NODE_LEVEL_SUBPORT,
51         TM_NODE_LEVEL_PIPE,
52         TM_NODE_LEVEL_TC,
53         TM_NODE_LEVEL_QUEUE,
54         TM_NODE_LEVEL_MAX,
55 };
56
57 struct tm_hierarchy {
58         /* TM Nodes */
59         uint32_t root_node_id;
60         uint32_t subport_node_id[SUBPORT_NODES_PER_PORT];
61         uint32_t pipe_node_id[SUBPORT_NODES_PER_PORT][PIPE_NODES_PER_SUBPORT];
62         uint32_t tc_node_id[NUM_PIPE_NODES][TC_NODES_PER_PIPE];
63         uint32_t queue_node_id[NUM_TC_NODES][QUEUE_NODES_PER_TC];
64
65         /* TM Hierarchy Nodes Shaper Rates */
66         uint32_t root_node_shaper_rate;
67         uint32_t subport_node_shaper_rate;
68         uint32_t pipe_node_shaper_rate;
69         uint32_t tc_node_shaper_rate;
70         uint32_t tc_node_shared_shaper_rate;
71
72         uint32_t n_shapers;
73 };
74
75 static struct fwd_lcore *softnic_fwd_lcore;
76 static uint16_t softnic_port_id;
77 struct fwd_engine softnic_fwd_engine;
78
79 /*
80  * Softnic packet forward
81  */
82 static void
83 softnic_fwd(struct fwd_stream *fs)
84 {
85         struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
86         uint16_t nb_rx;
87         uint16_t nb_tx;
88         uint32_t retry;
89
90 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
91         uint64_t start_tsc;
92         uint64_t end_tsc;
93         uint64_t core_cycles;
94 #endif
95
96 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
97         start_tsc = rte_rdtsc();
98 #endif
99
100         /*  Packets Receive */
101         nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue,
102                         pkts_burst, nb_pkt_per_burst);
103         fs->rx_packets += nb_rx;
104
105 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
106         fs->rx_burst_stats.pkt_burst_spread[nb_rx]++;
107 #endif
108
109         nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue,
110                         pkts_burst, nb_rx);
111
112         /* Retry if necessary */
113         if (unlikely(nb_tx < nb_rx) && fs->retry_enabled) {
114                 retry = 0;
115                 while (nb_tx < nb_rx && retry++ < burst_tx_retry_num) {
116                         rte_delay_us(burst_tx_delay_time);
117                         nb_tx += rte_eth_tx_burst(fs->tx_port, fs->tx_queue,
118                                         &pkts_burst[nb_tx], nb_rx - nb_tx);
119                 }
120         }
121         fs->tx_packets += nb_tx;
122
123 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
124         fs->tx_burst_stats.pkt_burst_spread[nb_tx]++;
125 #endif
126
127         if (unlikely(nb_tx < nb_rx)) {
128                 fs->fwd_dropped += (nb_rx - nb_tx);
129                 do {
130                         rte_pktmbuf_free(pkts_burst[nb_tx]);
131                 } while (++nb_tx < nb_rx);
132         }
133 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
134         end_tsc = rte_rdtsc();
135         core_cycles = (end_tsc - start_tsc);
136         fs->core_cycles = (uint64_t) (fs->core_cycles + core_cycles);
137 #endif
138 }
139
140 static void
141 softnic_fwd_run(struct fwd_stream *fs)
142 {
143         rte_pmd_softnic_run(softnic_port_id);
144         softnic_fwd(fs);
145 }
146
147 /**
148  * Softnic init
149  */
150 static int
151 softnic_begin(void *arg __rte_unused)
152 {
153         for (;;) {
154                 if (!softnic_fwd_lcore->stopped)
155                         break;
156         }
157
158         do {
159                 /* Run softnic */
160                 rte_pmd_softnic_run(softnic_port_id);
161         } while (!softnic_fwd_lcore->stopped);
162
163         return 0;
164 }
165
166 static void
167 set_tm_hiearchy_nodes_shaper_rate(portid_t port_id,
168         struct tm_hierarchy *h)
169 {
170         struct rte_eth_link link_params;
171         uint64_t tm_port_rate;
172
173         memset(&link_params, 0, sizeof(link_params));
174
175         rte_eth_link_get(port_id, &link_params);
176         tm_port_rate = (uint64_t)ETH_SPEED_NUM_10G * BYTES_IN_MBPS;
177
178         if (tm_port_rate > UINT32_MAX)
179                 tm_port_rate = UINT32_MAX;
180
181         /* Set tm hierarchy shapers rate */
182         h->root_node_shaper_rate = tm_port_rate;
183         h->subport_node_shaper_rate =
184                 tm_port_rate / SUBPORT_NODES_PER_PORT;
185         h->pipe_node_shaper_rate
186                 = h->subport_node_shaper_rate / PIPE_NODES_PER_SUBPORT;
187         h->tc_node_shaper_rate = h->pipe_node_shaper_rate;
188         h->tc_node_shared_shaper_rate = h->subport_node_shaper_rate;
189 }
190
191 static int
192 softport_tm_root_node_add(portid_t port_id, struct tm_hierarchy *h,
193         struct rte_tm_error *error)
194 {
195         struct rte_tm_node_params rnp;
196         struct rte_tm_shaper_params rsp;
197         uint32_t priority, weight, level_id, shaper_profile_id;
198
199         memset(&rsp, 0, sizeof(struct rte_tm_shaper_params));
200         memset(&rnp, 0, sizeof(struct rte_tm_node_params));
201
202         /* Shaper profile Parameters */
203         rsp.peak.rate = h->root_node_shaper_rate;
204         rsp.peak.size = TOKEN_BUCKET_SIZE;
205         rsp.pkt_length_adjust = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
206         shaper_profile_id = 0;
207
208         if (rte_tm_shaper_profile_add(port_id, shaper_profile_id,
209                 &rsp, error)) {
210                 printf("%s ERROR(%d)-%s!(shaper_id %u)\n ",
211                         __func__, error->type, error->message,
212                         shaper_profile_id);
213                 return -1;
214         }
215
216         /* Root Node Parameters */
217         h->root_node_id = ROOT_NODE_ID;
218         weight = 1;
219         priority = 0;
220         level_id = TM_NODE_LEVEL_PORT;
221         rnp.shaper_profile_id = shaper_profile_id;
222         rnp.nonleaf.n_sp_priorities = 1;
223         rnp.stats_mask = STATS_MASK_DEFAULT;
224
225         /* Add Node to TM Hierarchy */
226         if (rte_tm_node_add(port_id, h->root_node_id, RTE_TM_NODE_ID_NULL,
227                 priority, weight, level_id, &rnp, error)) {
228                 printf("%s ERROR(%d)-%s!(node_id %u, parent_id %u, level %u)\n",
229                         __func__, error->type, error->message,
230                         h->root_node_id, RTE_TM_NODE_ID_NULL,
231                         level_id);
232                 return -1;
233         }
234         /* Update */
235         h->n_shapers++;
236
237         printf("  Root node added (Start id %u, Count %u, level %u)\n",
238                 h->root_node_id, 1, level_id);
239
240         return 0;
241 }
242
243 static int
244 softport_tm_subport_node_add(portid_t port_id,
245         struct tm_hierarchy *h,
246         struct rte_tm_error *error)
247 {
248         uint32_t subport_parent_node_id, subport_node_id = 0;
249         struct rte_tm_node_params snp;
250         struct rte_tm_shaper_params ssp;
251         uint32_t priority, weight, level_id, shaper_profile_id;
252         uint32_t i;
253
254         memset(&ssp, 0, sizeof(struct rte_tm_shaper_params));
255         memset(&snp, 0, sizeof(struct rte_tm_node_params));
256
257         shaper_profile_id = h->n_shapers;
258
259         /* Add Shaper Profile to TM Hierarchy */
260         for (i = 0; i < SUBPORT_NODES_PER_PORT; i++) {
261                 ssp.peak.rate = h->subport_node_shaper_rate;
262                 ssp.peak.size = TOKEN_BUCKET_SIZE;
263                 ssp.pkt_length_adjust = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
264
265                 if (rte_tm_shaper_profile_add(port_id, shaper_profile_id,
266                         &ssp, error)) {
267                         printf("%s ERROR(%d)-%s!(shaper_id %u)\n ",
268                                 __func__, error->type, error->message,
269                                 shaper_profile_id);
270                         return -1;
271                 }
272
273                 /* Node Parameters */
274                 h->subport_node_id[i] = SUBPORT_NODES_START_ID + i;
275                 subport_parent_node_id = h->root_node_id;
276                 weight = 1;
277                 priority = 0;
278                 level_id = TM_NODE_LEVEL_SUBPORT;
279                 snp.shaper_profile_id = shaper_profile_id;
280                 snp.nonleaf.n_sp_priorities = 1;
281                 snp.stats_mask = STATS_MASK_DEFAULT;
282
283                 /* Add Node to TM Hiearchy */
284                 if (rte_tm_node_add(port_id,
285                                 h->subport_node_id[i],
286                                 subport_parent_node_id,
287                                 priority, weight,
288                                 level_id,
289                                 &snp,
290                                 error)) {
291                         printf("%s ERROR(%d)-%s!(node %u,parent %u,level %u)\n",
292                                         __func__,
293                                         error->type,
294                                         error->message,
295                                         h->subport_node_id[i],
296                                         subport_parent_node_id,
297                                         level_id);
298                         return -1;
299                 }
300                 shaper_profile_id++;
301                 subport_node_id++;
302         }
303         /* Update */
304         h->n_shapers = shaper_profile_id;
305
306         printf("  Subport nodes added (Start id %u, Count %u, level %u)\n",
307                 h->subport_node_id[0], SUBPORT_NODES_PER_PORT, level_id);
308
309         return 0;
310 }
311
312 static int
313 softport_tm_pipe_node_add(portid_t port_id,
314         struct tm_hierarchy *h,
315         struct rte_tm_error *error)
316 {
317         uint32_t pipe_parent_node_id;
318         struct rte_tm_node_params pnp;
319         struct rte_tm_shaper_params psp;
320         uint32_t priority, weight, level_id, shaper_profile_id;
321         uint32_t i, j;
322
323         memset(&psp, 0, sizeof(struct rte_tm_shaper_params));
324         memset(&pnp, 0, sizeof(struct rte_tm_node_params));
325
326         shaper_profile_id = h->n_shapers;
327
328         /* Shaper Profile Parameters */
329         psp.peak.rate = h->pipe_node_shaper_rate;
330         psp.peak.size = TOKEN_BUCKET_SIZE;
331         psp.pkt_length_adjust = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
332
333         /* Pipe Node Parameters */
334         weight = 1;
335         priority = 0;
336         level_id = TM_NODE_LEVEL_PIPE;
337         pnp.nonleaf.n_sp_priorities = 4;
338         pnp.stats_mask = STATS_MASK_DEFAULT;
339
340         /* Add Shaper Profiles and Nodes to TM Hierarchy */
341         for (i = 0; i < SUBPORT_NODES_PER_PORT; i++) {
342                 for (j = 0; j < PIPE_NODES_PER_SUBPORT; j++) {
343                         if (rte_tm_shaper_profile_add(port_id,
344                                 shaper_profile_id, &psp, error)) {
345                                 printf("%s ERROR(%d)-%s!(shaper_id %u)\n ",
346                                         __func__, error->type, error->message,
347                                         shaper_profile_id);
348                                 return -1;
349                         }
350                         pnp.shaper_profile_id = shaper_profile_id;
351                         pipe_parent_node_id = h->subport_node_id[i];
352                         h->pipe_node_id[i][j] = PIPE_NODES_START_ID +
353                                 (i * PIPE_NODES_PER_SUBPORT) + j;
354
355                         if (rte_tm_node_add(port_id,
356                                         h->pipe_node_id[i][j],
357                                         pipe_parent_node_id,
358                                         priority, weight, level_id,
359                                         &pnp,
360                                         error)) {
361                                 printf("%s ERROR(%d)-%s!(node %u,parent %u )\n",
362                                         __func__,
363                                         error->type,
364                                         error->message,
365                                         h->pipe_node_id[i][j],
366                                         pipe_parent_node_id);
367
368                                 return -1;
369                         }
370                         shaper_profile_id++;
371                 }
372         }
373         /* Update */
374         h->n_shapers = shaper_profile_id;
375
376         printf("  Pipe nodes added (Start id %u, Count %u, level %u)\n",
377                 h->pipe_node_id[0][0], NUM_PIPE_NODES, level_id);
378
379         return 0;
380 }
381
382 static int
383 softport_tm_tc_node_add(portid_t port_id,
384         struct tm_hierarchy *h,
385         struct rte_tm_error *error)
386 {
387         uint32_t tc_parent_node_id;
388         struct rte_tm_node_params tnp;
389         struct rte_tm_shaper_params tsp, tssp;
390         uint32_t shared_shaper_profile_id[TC_NODES_PER_PIPE];
391         uint32_t priority, weight, level_id, shaper_profile_id;
392         uint32_t pos, n_tc_nodes, i, j, k;
393
394         memset(&tsp, 0, sizeof(struct rte_tm_shaper_params));
395         memset(&tssp, 0, sizeof(struct rte_tm_shaper_params));
396         memset(&tnp, 0, sizeof(struct rte_tm_node_params));
397
398         shaper_profile_id = h->n_shapers;
399
400         /* Private Shaper Profile (TC) Parameters */
401         tsp.peak.rate = h->tc_node_shaper_rate;
402         tsp.peak.size = TOKEN_BUCKET_SIZE;
403         tsp.pkt_length_adjust = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
404
405         /* Shared Shaper Profile (TC) Parameters */
406         tssp.peak.rate = h->tc_node_shared_shaper_rate;
407         tssp.peak.size = TOKEN_BUCKET_SIZE;
408         tssp.pkt_length_adjust = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
409
410         /* TC Node Parameters */
411         weight = 1;
412         level_id = TM_NODE_LEVEL_TC;
413         tnp.n_shared_shapers = 1;
414         tnp.nonleaf.n_sp_priorities = 1;
415         tnp.stats_mask = STATS_MASK_DEFAULT;
416
417         /* Add Shared Shaper Profiles to TM Hierarchy */
418         for (i = 0; i < TC_NODES_PER_PIPE; i++) {
419                 shared_shaper_profile_id[i] = shaper_profile_id;
420
421                 if (rte_tm_shaper_profile_add(port_id,
422                         shared_shaper_profile_id[i], &tssp, error)) {
423                         printf("%s ERROR(%d)-%s!(Shared shaper profileid %u)\n",
424                                 __func__, error->type, error->message,
425                                 shared_shaper_profile_id[i]);
426
427                         return -1;
428                 }
429                 if (rte_tm_shared_shaper_add_update(port_id,  i,
430                         shared_shaper_profile_id[i], error)) {
431                         printf("%s ERROR(%d)-%s!(Shared shaper id %u)\n",
432                                 __func__, error->type, error->message, i);
433
434                         return -1;
435                 }
436                 shaper_profile_id++;
437         }
438
439         /* Add Shaper Profiles and Nodes to TM Hierarchy */
440         n_tc_nodes = 0;
441         for (i = 0; i < SUBPORT_NODES_PER_PORT; i++) {
442                 for (j = 0; j < PIPE_NODES_PER_SUBPORT; j++) {
443                         for (k = 0; k < TC_NODES_PER_PIPE ; k++) {
444                                 priority = k;
445                                 tc_parent_node_id = h->pipe_node_id[i][j];
446                                 tnp.shared_shaper_id =
447                                         (uint32_t *)calloc(1, sizeof(uint32_t));
448                                 if (tnp.shared_shaper_id == NULL) {
449                                         printf("Shared shaper mem alloc err\n");
450                                         return -1;
451                                 }
452                                 tnp.shared_shaper_id[0] = k;
453                                 pos = j + (i * PIPE_NODES_PER_SUBPORT);
454                                 h->tc_node_id[pos][k] =
455                                         TC_NODES_START_ID + n_tc_nodes;
456
457                                 if (rte_tm_shaper_profile_add(port_id,
458                                         shaper_profile_id, &tsp, error)) {
459                                         printf("%s ERROR(%d)-%s!(shaper %u)\n",
460                                                 __func__, error->type,
461                                                 error->message,
462                                                 shaper_profile_id);
463
464                                         return -1;
465                                 }
466                                 tnp.shaper_profile_id = shaper_profile_id;
467                                 if (rte_tm_node_add(port_id,
468                                                 h->tc_node_id[pos][k],
469                                                 tc_parent_node_id,
470                                                 priority, weight,
471                                                 level_id,
472                                                 &tnp, error)) {
473                                         printf("%s ERROR(%d)-%s!(node id %u)\n",
474                                                 __func__,
475                                                 error->type,
476                                                 error->message,
477                                                 h->tc_node_id[pos][k]);
478
479                                         return -1;
480                                 }
481                                 shaper_profile_id++;
482                                 n_tc_nodes++;
483                         }
484                 }
485         }
486         /* Update */
487         h->n_shapers = shaper_profile_id;
488
489         printf("  TC nodes added (Start id %u, Count %u, level %u)\n",
490                 h->tc_node_id[0][0], n_tc_nodes, level_id);
491
492         return 0;
493 }
494
495 static int
496 softport_tm_queue_node_add(portid_t port_id, struct tm_hierarchy *h,
497         struct rte_tm_error *error)
498 {
499         uint32_t queue_parent_node_id;
500         struct rte_tm_node_params qnp;
501         uint32_t priority, weight, level_id, pos;
502         uint32_t n_queue_nodes, i, j, k;
503
504         memset(&qnp, 0, sizeof(struct rte_tm_node_params));
505
506         /* Queue Node Parameters */
507         priority = 0;
508         weight = 1;
509         level_id = TM_NODE_LEVEL_QUEUE;
510         qnp.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE;
511         qnp.leaf.cman = RTE_TM_CMAN_TAIL_DROP;
512         qnp.stats_mask = STATS_MASK_QUEUE;
513
514         /* Add Queue Nodes to TM Hierarchy */
515         n_queue_nodes = 0;
516         for (i = 0; i < NUM_PIPE_NODES; i++) {
517                 for (j = 0; j < TC_NODES_PER_PIPE; j++) {
518                         queue_parent_node_id = h->tc_node_id[i][j];
519                         for (k = 0; k < QUEUE_NODES_PER_TC; k++) {
520                                 pos = j + (i * TC_NODES_PER_PIPE);
521                                 h->queue_node_id[pos][k] = n_queue_nodes;
522                                 if (rte_tm_node_add(port_id,
523                                                 h->queue_node_id[pos][k],
524                                                 queue_parent_node_id,
525                                                 priority,
526                                                 weight,
527                                                 level_id,
528                                                 &qnp, error)) {
529                                         printf("%s ERROR(%d)-%s!(node %u)\n",
530                                                 __func__,
531                                                 error->type,
532                                                 error->message,
533                                                 h->queue_node_id[pos][k]);
534
535                                         return -1;
536                                 }
537                                 n_queue_nodes++;
538                         }
539                 }
540         }
541         printf("  Queue nodes added (Start id %u, Count %u, level %u)\n",
542                 h->queue_node_id[0][0], n_queue_nodes, level_id);
543
544         return 0;
545 }
546
547 static int
548 softport_tm_hierarchy_specify(portid_t port_id,
549         struct rte_tm_error *error)
550 {
551
552         struct tm_hierarchy h;
553         int status;
554
555         memset(&h, 0, sizeof(struct tm_hierarchy));
556
557         /* TM hierarchy shapers rate */
558         set_tm_hiearchy_nodes_shaper_rate(port_id, &h);
559
560         /* Add root node (level 0) */
561         status = softport_tm_root_node_add(port_id, &h, error);
562         if (status)
563                 return status;
564
565         /* Add subport node (level 1) */
566         status = softport_tm_subport_node_add(port_id, &h, error);
567         if (status)
568                 return status;
569
570         /* Add pipe nodes (level 2) */
571         status = softport_tm_pipe_node_add(port_id, &h, error);
572         if (status)
573                 return status;
574
575         /* Add traffic class nodes (level 3) */
576         status = softport_tm_tc_node_add(port_id, &h, error);
577         if (status)
578                 return status;
579
580         /* Add queue nodes (level 4) */
581         status = softport_tm_queue_node_add(port_id, &h, error);
582         if (status)
583                 return status;
584
585         return 0;
586 }
587
588 /*
589  * Softnic TM default configuration
590  */
591 static void
592 softnic_tm_default_config(portid_t pi)
593 {
594         struct rte_port *port = &ports[pi];
595         struct rte_tm_error error;
596         int status;
597
598         /* Stop port */
599         rte_eth_dev_stop(pi);
600
601         /* TM hierarchy specification */
602         status = softport_tm_hierarchy_specify(pi, &error);
603         if (status) {
604                 printf("  TM Hierarchy built error(%d) - %s\n",
605                         error.type, error.message);
606                 return;
607         }
608         printf("\n  TM Hierarchy Specified!\n");
609
610         /* TM hierarchy commit */
611         status = rte_tm_hierarchy_commit(pi, 0, &error);
612         if (status) {
613                 printf("  Hierarchy commit error(%d) - %s\n",
614                         error.type, error.message);
615                 return;
616         }
617         printf("  Hierarchy Committed (port %u)!\n", pi);
618
619         /* Start port */
620         status = rte_eth_dev_start(pi);
621         if (status) {
622                 printf("\n  Port %u start error!\n", pi);
623                 return;
624         }
625
626         /* Reset the default hierarchy flag */
627         port->softport.default_tm_hierarchy_enable = 0;
628 }
629
630 /*
631  * Softnic forwarding init
632  */
633 static void
634 softnic_fwd_begin(portid_t pi)
635 {
636         struct rte_port *port = &ports[pi];
637         uint32_t lcore, fwd_core_present = 0, softnic_run_launch = 0;
638         int     status;
639
640         softnic_fwd_lcore = port->softport.fwd_lcore_arg[0];
641         softnic_port_id = pi;
642
643         /* Launch softnic_run function on lcores */
644         for (lcore = 0; lcore < RTE_MAX_LCORE; lcore++) {
645                 if (!rte_lcore_is_enabled(lcore))
646                         continue;
647
648                 if (lcore == rte_get_master_lcore())
649                         continue;
650
651                 if (fwd_core_present == 0) {
652                         fwd_core_present++;
653                         continue;
654                 }
655
656                 status = rte_eal_remote_launch(softnic_begin, NULL, lcore);
657                 if (status)
658                         printf("softnic launch on lcore %u failed (%d)\n",
659                                        lcore, status);
660
661                 softnic_run_launch = 1;
662         }
663
664         if (!softnic_run_launch)
665                 softnic_fwd_engine.packet_fwd = softnic_fwd_run;
666
667         /* Softnic TM default configuration */
668         if (port->softport.default_tm_hierarchy_enable == 1)
669                 softnic_tm_default_config(pi);
670 }
671
672 struct fwd_engine softnic_fwd_engine = {
673         .fwd_mode_name  = "softnic",
674         .port_fwd_begin = softnic_fwd_begin,
675         .port_fwd_end   = NULL,
676         .packet_fwd     = softnic_fwd,
677 };