70c57e44510a17196d2322e4a96ac689d328f8d7
[dpdk.git] / examples / ip_pipeline / pipeline / pipeline_common_fe.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2016 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 <stdio.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37
38 #include <rte_common.h>
39 #include <rte_ring.h>
40 #include <rte_malloc.h>
41 #include <cmdline_rdline.h>
42 #include <cmdline_parse.h>
43 #include <cmdline_parse_num.h>
44 #include <cmdline_parse_string.h>
45 #include <cmdline.h>
46
47 #include "pipeline_common_fe.h"
48 #include "parser.h"
49
50 struct app_link_params *
51 app_pipeline_track_pktq_out_to_link(struct app_params *app,
52         uint32_t pipeline_id,
53         uint32_t pktq_out_id)
54 {
55         struct app_pipeline_params *p;
56
57         /* Check input arguments */
58         if (app == NULL)
59                 return NULL;
60
61         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
62         if (p == NULL)
63                 return NULL;
64
65         for ( ; ; ) {
66                 struct app_pktq_out_params *pktq_out =
67                         &p->pktq_out[pktq_out_id];
68
69                 switch (pktq_out->type) {
70                 case APP_PKTQ_OUT_HWQ:
71                 {
72                         struct app_pktq_hwq_out_params *hwq_out;
73
74                         hwq_out = &app->hwq_out_params[pktq_out->id];
75
76                         return app_get_link_for_txq(app, hwq_out);
77                 }
78
79                 case APP_PKTQ_OUT_SWQ:
80                 {
81                         struct pipeline_params pp;
82                         struct pipeline_type *ptype;
83                         struct app_pktq_swq_params *swq;
84                         uint32_t pktq_in_id;
85                         int status;
86
87                         swq = &app->swq_params[pktq_out->id];
88                         p = app_swq_get_reader(app, swq, &pktq_in_id);
89                         if (p == NULL)
90                                 return NULL;
91
92                         ptype = app_pipeline_type_find(app, p->type);
93                         if ((ptype == NULL) || (ptype->fe_ops->f_track == NULL))
94                                 return NULL;
95
96                         app_pipeline_params_get(app, p, &pp);
97                         status = ptype->fe_ops->f_track(&pp,
98                                 pktq_in_id,
99                                 &pktq_out_id);
100                         if (status)
101                                 return NULL;
102
103                         break;
104                 }
105
106                 case APP_PKTQ_OUT_TM:
107                 {
108                         struct pipeline_params pp;
109                         struct pipeline_type *ptype;
110                         struct app_pktq_tm_params *tm;
111                         uint32_t pktq_in_id;
112                         int status;
113
114                         tm = &app->tm_params[pktq_out->id];
115                         p = app_tm_get_reader(app, tm, &pktq_in_id);
116                         if (p == NULL)
117                                 return NULL;
118
119                         ptype = app_pipeline_type_find(app, p->type);
120                         if ((ptype == NULL) || (ptype->fe_ops->f_track == NULL))
121                                 return NULL;
122
123                         app_pipeline_params_get(app, p, &pp);
124                         status = ptype->fe_ops->f_track(&pp,
125                                 pktq_in_id,
126                                 &pktq_out_id);
127                         if (status)
128                                 return NULL;
129
130                         break;
131                 }
132
133                 case APP_PKTQ_OUT_SINK:
134                 default:
135                         return NULL;
136                 }
137         }
138 }
139
140 int
141 app_pipeline_track_default(struct pipeline_params *p,
142         uint32_t port_in,
143         uint32_t *port_out)
144 {
145         /* Check input arguments */
146         if ((p == NULL) ||
147                 (port_in >= p->n_ports_in) ||
148                 (port_out == NULL))
149                 return -1;
150
151         if (p->n_ports_out == 1) {
152                 *port_out = 0;
153                 return 0;
154         }
155
156         return -1;
157 }
158
159 int
160 app_pipeline_ping(struct app_params *app,
161         uint32_t pipeline_id)
162 {
163         struct app_pipeline_params *p;
164         struct pipeline_msg_req *req;
165         struct pipeline_msg_rsp *rsp;
166         int status = 0;
167
168         /* Check input arguments */
169         if (app == NULL)
170                 return -1;
171
172         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
173         if (p == NULL)
174                 return -1;
175
176         /* Message buffer allocation */
177         req = app_msg_alloc(app);
178         if (req == NULL)
179                 return -1;
180
181         /* Fill in request */
182         req->type = PIPELINE_MSG_REQ_PING;
183
184         /* Send request and wait for response */
185         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
186         if (rsp == NULL)
187                 return -1;
188
189         /* Check response */
190         status = rsp->status;
191
192         /* Message buffer free */
193         app_msg_free(app, rsp);
194
195         return status;
196 }
197
198 int
199 app_pipeline_stats_port_in(struct app_params *app,
200         uint32_t pipeline_id,
201         uint32_t port_id,
202         struct rte_pipeline_port_in_stats *stats)
203 {
204         struct app_pipeline_params *p;
205         struct pipeline_stats_msg_req *req;
206         struct pipeline_stats_port_in_msg_rsp *rsp;
207         int status = 0;
208
209         /* Check input arguments */
210         if ((app == NULL) ||
211                 (stats == NULL))
212                 return -1;
213
214         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
215         if ((p == NULL) ||
216                 (port_id >= p->n_pktq_in))
217                 return -1;
218
219         /* Message buffer allocation */
220         req = app_msg_alloc(app);
221         if (req == NULL)
222                 return -1;
223
224         /* Fill in request */
225         req->type = PIPELINE_MSG_REQ_STATS_PORT_IN;
226         req->id = port_id;
227
228         /* Send request and wait for response */
229         rsp = (struct pipeline_stats_port_in_msg_rsp *)
230                 app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
231         if (rsp == NULL)
232                 return -1;
233
234         /* Check response */
235         status = rsp->status;
236         if (status == 0)
237                 memcpy(stats, &rsp->stats, sizeof(rsp->stats));
238
239         /* Message buffer free */
240         app_msg_free(app, rsp);
241
242         return status;
243 }
244
245 int
246 app_pipeline_stats_port_out(struct app_params *app,
247         uint32_t pipeline_id,
248         uint32_t port_id,
249         struct rte_pipeline_port_out_stats *stats)
250 {
251         struct app_pipeline_params *p;
252         struct pipeline_stats_msg_req *req;
253         struct pipeline_stats_port_out_msg_rsp *rsp;
254         int status = 0;
255
256         /* Check input arguments */
257         if ((app == NULL) ||
258                 (pipeline_id >= app->n_pipelines) ||
259                 (stats == NULL))
260                 return -1;
261
262         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
263         if ((p == NULL) ||
264                 (port_id >= p->n_pktq_out))
265                 return -1;
266
267         /* Message buffer allocation */
268         req = app_msg_alloc(app);
269         if (req == NULL)
270                 return -1;
271
272         /* Fill in request */
273         req->type = PIPELINE_MSG_REQ_STATS_PORT_OUT;
274         req->id = port_id;
275
276         /* Send request and wait for response */
277         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
278         if (rsp == NULL)
279                 return -1;
280
281         /* Check response */
282         status = rsp->status;
283         if (status == 0)
284                 memcpy(stats, &rsp->stats, sizeof(rsp->stats));
285
286         /* Message buffer free */
287         app_msg_free(app, rsp);
288
289         return status;
290 }
291
292 int
293 app_pipeline_stats_table(struct app_params *app,
294         uint32_t pipeline_id,
295         uint32_t table_id,
296         struct rte_pipeline_table_stats *stats)
297 {
298         struct app_pipeline_params *p;
299         struct pipeline_stats_msg_req *req;
300         struct pipeline_stats_table_msg_rsp *rsp;
301         int status = 0;
302
303         /* Check input arguments */
304         if ((app == NULL) ||
305                 (stats == NULL))
306                 return -1;
307
308         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
309         if (p == NULL)
310                 return -1;
311
312         /* Message buffer allocation */
313         req = app_msg_alloc(app);
314         if (req == NULL)
315                 return -1;
316
317         /* Fill in request */
318         req->type = PIPELINE_MSG_REQ_STATS_TABLE;
319         req->id = table_id;
320
321         /* Send request and wait for response */
322         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
323         if (rsp == NULL)
324                 return -1;
325
326         /* Check response */
327         status = rsp->status;
328         if (status == 0)
329                 memcpy(stats, &rsp->stats, sizeof(rsp->stats));
330
331         /* Message buffer free */
332         app_msg_free(app, rsp);
333
334         return status;
335 }
336
337 int
338 app_pipeline_port_in_enable(struct app_params *app,
339         uint32_t pipeline_id,
340         uint32_t port_id)
341 {
342         struct app_pipeline_params *p;
343         struct pipeline_port_in_msg_req *req;
344         struct pipeline_msg_rsp *rsp;
345         int status = 0;
346
347         /* Check input arguments */
348         if (app == NULL)
349                 return -1;
350
351         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
352         if ((p == NULL) ||
353                 (port_id >= p->n_pktq_in))
354                 return -1;
355
356         /* Message buffer allocation */
357         req = app_msg_alloc(app);
358         if (req == NULL)
359                 return -1;
360
361         /* Fill in request */
362         req->type = PIPELINE_MSG_REQ_PORT_IN_ENABLE;
363         req->port_id = port_id;
364
365         /* Send request and wait for response */
366         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
367         if (rsp == NULL)
368                 return -1;
369
370         /* Check response */
371         status = rsp->status;
372
373         /* Message buffer free */
374         app_msg_free(app, rsp);
375
376         return status;
377 }
378
379 int
380 app_pipeline_port_in_disable(struct app_params *app,
381         uint32_t pipeline_id,
382         uint32_t port_id)
383 {
384         struct app_pipeline_params *p;
385         struct pipeline_port_in_msg_req *req;
386         struct pipeline_msg_rsp *rsp;
387         int status = 0;
388
389         /* Check input arguments */
390         if (app == NULL)
391                 return -1;
392
393         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
394         if ((p == NULL) ||
395                 (port_id >= p->n_pktq_in))
396                 return -1;
397
398         /* Message buffer allocation */
399         req = app_msg_alloc(app);
400         if (req == NULL)
401                 return -1;
402
403         /* Fill in request */
404         req->type = PIPELINE_MSG_REQ_PORT_IN_DISABLE;
405         req->port_id = port_id;
406
407         /* Send request and wait for response */
408         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
409         if (rsp == NULL)
410                 return -1;
411
412         /* Check response */
413         status = rsp->status;
414
415         /* Message buffer free */
416         app_msg_free(app, rsp);
417
418         return status;
419 }
420
421 int
422 app_link_set_op(struct app_params *app,
423         uint32_t link_id,
424         uint32_t pipeline_id,
425         app_link_op op,
426         void *arg)
427 {
428         struct app_pipeline_params *pp;
429         struct app_link_params *lp;
430         struct app_link_data *ld;
431         uint32_t ppos, lpos;
432
433         /* Check input arguments */
434         if ((app == NULL) ||
435                 (op == NULL))
436                 return -1;
437
438         APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, lp);
439         if (lp == NULL)
440                 return -1;
441         lpos = lp - app->link_params;
442         ld = &app->link_data[lpos];
443
444         APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, pp);
445         if (pp == NULL)
446                 return -1;
447         ppos = pp - app->pipeline_params;
448
449         ld->f_link[ppos] = op;
450         ld->arg[ppos] = arg;
451
452         return 0;
453 }
454
455 int
456 app_link_config(struct app_params *app,
457         uint32_t link_id,
458         uint32_t ip,
459         uint32_t depth)
460 {
461         struct app_link_params *p;
462         uint32_t i, netmask, host, bcast;
463
464         /* Check input arguments */
465         if (app == NULL)
466                 return -1;
467
468         APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
469         if (p == NULL) {
470                 APP_LOG(app, HIGH, "LINK%" PRIu32 " is not a valid link",
471                         link_id);
472                 return -1;
473         }
474
475         if (p->state) {
476                 APP_LOG(app, HIGH, "%s is UP, please bring it DOWN first",
477                         p->name);
478                 return -1;
479         }
480
481         netmask = (~0U) << (32 - depth);
482         host = ip & netmask;
483         bcast = host | (~netmask);
484
485         if ((ip == 0) ||
486                 (ip == UINT32_MAX) ||
487                 (ip == host) ||
488                 (ip == bcast)) {
489                 APP_LOG(app, HIGH, "Illegal IP address");
490                 return -1;
491         }
492
493         for (i = 0; i < app->n_links; i++) {
494                 struct app_link_params *link = &app->link_params[i];
495
496                 if (strcmp(p->name, link->name) == 0)
497                         continue;
498
499                 if (link->ip == ip) {
500                         APP_LOG(app, HIGH,
501                                 "%s is already assigned this IP address",
502                                 link->name);
503                         return -1;
504                 }
505         }
506
507         if ((depth == 0) || (depth > 32)) {
508                 APP_LOG(app, HIGH, "Illegal value for depth parameter "
509                         "(%" PRIu32 ")",
510                         depth);
511                 return -1;
512         }
513
514         /* Save link parameters */
515         p->ip = ip;
516         p->depth = depth;
517
518         return 0;
519 }
520
521 int
522 app_link_up(struct app_params *app,
523         uint32_t link_id)
524 {
525         struct app_link_params *p;
526         struct app_link_data *d;
527         int i;
528
529         /* Check input arguments */
530         if (app == NULL)
531                 return -1;
532
533         APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
534         if (p == NULL) {
535                 APP_LOG(app, HIGH, "LINK%" PRIu32 " is not a valid link",
536                         link_id);
537                 return -1;
538         }
539
540         d = &app->link_data[p - app->link_params];
541
542         /* Check link state */
543         if (p->state) {
544                 APP_LOG(app, HIGH, "%s is already UP", p->name);
545                 return 0;
546         }
547
548         /* Check that IP address is valid */
549         if (p->ip == 0) {
550                 APP_LOG(app, HIGH, "%s IP address is not set", p->name);
551                 return 0;
552         }
553
554         app_link_up_internal(app, p);
555
556         /* Callbacks */
557         for (i = 0; i < APP_MAX_PIPELINES; i++)
558                 if (d->f_link[i])
559                         d->f_link[i](app, link_id, 1, d->arg[i]);
560
561         return 0;
562 }
563
564 int
565 app_link_down(struct app_params *app,
566         uint32_t link_id)
567 {
568         struct app_link_params *p;
569         struct app_link_data *d;
570         uint32_t i;
571
572         /* Check input arguments */
573         if (app == NULL)
574                 return -1;
575
576         APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
577         if (p == NULL) {
578                 APP_LOG(app, HIGH, "LINK%" PRIu32 " is not a valid link",
579                         link_id);
580                 return -1;
581         }
582
583         d = &app->link_data[p - app->link_params];
584
585         /* Check link state */
586         if (p->state == 0) {
587                 APP_LOG(app, HIGH, "%s is already DOWN", p->name);
588                 return 0;
589         }
590
591         app_link_down_internal(app, p);
592
593         /* Callbacks */
594         for (i = 0; i < APP_MAX_PIPELINES; i++)
595                 if (d->f_link[i])
596                         d->f_link[i](app, link_id, 0, d->arg[i]);
597
598         return 0;
599 }
600
601 /*
602  * ping
603  */
604
605 struct cmd_ping_result {
606         cmdline_fixed_string_t p_string;
607         uint32_t pipeline_id;
608         cmdline_fixed_string_t ping_string;
609 };
610
611 static void
612 cmd_ping_parsed(
613         void *parsed_result,
614         __rte_unused struct cmdline *cl,
615         void *data)
616 {
617         struct cmd_ping_result *params = parsed_result;
618         struct app_params *app = data;
619         int status;
620
621         status = app_pipeline_ping(app, params->pipeline_id);
622         if (status != 0)
623                 printf("Command failed\n");
624 }
625
626 static cmdline_parse_token_string_t cmd_ping_p_string =
627         TOKEN_STRING_INITIALIZER(struct cmd_ping_result, p_string, "p");
628
629 static cmdline_parse_token_num_t cmd_ping_pipeline_id =
630         TOKEN_NUM_INITIALIZER(struct cmd_ping_result, pipeline_id, UINT32);
631
632 static cmdline_parse_token_string_t cmd_ping_ping_string =
633         TOKEN_STRING_INITIALIZER(struct cmd_ping_result, ping_string, "ping");
634
635 static cmdline_parse_inst_t cmd_ping = {
636         .f = cmd_ping_parsed,
637         .data = NULL,
638         .help_str = "Pipeline ping",
639         .tokens = {
640                 (void *) &cmd_ping_p_string,
641                 (void *) &cmd_ping_pipeline_id,
642                 (void *) &cmd_ping_ping_string,
643                 NULL,
644         },
645 };
646
647 /*
648  * stats port in
649  */
650
651 struct cmd_stats_port_in_result {
652         cmdline_fixed_string_t p_string;
653         uint32_t pipeline_id;
654         cmdline_fixed_string_t stats_string;
655         cmdline_fixed_string_t port_string;
656         cmdline_fixed_string_t in_string;
657         uint32_t port_in_id;
658
659 };
660
661 static void
662 cmd_stats_port_in_parsed(
663         void *parsed_result,
664         __rte_unused struct cmdline *cl,
665         void *data)
666 {
667         struct cmd_stats_port_in_result *params = parsed_result;
668         struct app_params *app = data;
669         struct rte_pipeline_port_in_stats stats;
670         int status;
671
672         status = app_pipeline_stats_port_in(app,
673                         params->pipeline_id,
674                         params->port_in_id,
675                         &stats);
676
677         if (status != 0) {
678                 printf("Command failed\n");
679                 return;
680         }
681
682         /* Display stats */
683         printf("Pipeline %" PRIu32 " - stats for input port %" PRIu32 ":\n"
684                 "\tPkts in: %" PRIu64 "\n"
685                 "\tPkts dropped by AH: %" PRIu64 "\n"
686                 "\tPkts dropped by other: %" PRIu64 "\n",
687                 params->pipeline_id,
688                 params->port_in_id,
689                 stats.stats.n_pkts_in,
690                 stats.n_pkts_dropped_by_ah,
691                 stats.stats.n_pkts_drop);
692 }
693
694 static cmdline_parse_token_string_t cmd_stats_port_in_p_string =
695         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, p_string,
696                 "p");
697
698 static cmdline_parse_token_num_t cmd_stats_port_in_pipeline_id =
699         TOKEN_NUM_INITIALIZER(struct cmd_stats_port_in_result, pipeline_id,
700                 UINT32);
701
702 static cmdline_parse_token_string_t cmd_stats_port_in_stats_string =
703         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, stats_string,
704                 "stats");
705
706 static cmdline_parse_token_string_t cmd_stats_port_in_port_string =
707         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, port_string,
708                 "port");
709
710 static cmdline_parse_token_string_t cmd_stats_port_in_in_string =
711         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, in_string,
712                 "in");
713
714         cmdline_parse_token_num_t cmd_stats_port_in_port_in_id =
715         TOKEN_NUM_INITIALIZER(struct cmd_stats_port_in_result, port_in_id,
716                 UINT32);
717
718 static cmdline_parse_inst_t cmd_stats_port_in = {
719         .f = cmd_stats_port_in_parsed,
720         .data = NULL,
721         .help_str = "Pipeline input port stats",
722         .tokens = {
723                 (void *) &cmd_stats_port_in_p_string,
724                 (void *) &cmd_stats_port_in_pipeline_id,
725                 (void *) &cmd_stats_port_in_stats_string,
726                 (void *) &cmd_stats_port_in_port_string,
727                 (void *) &cmd_stats_port_in_in_string,
728                 (void *) &cmd_stats_port_in_port_in_id,
729                 NULL,
730         },
731 };
732
733 /*
734  * stats port out
735  */
736
737 struct cmd_stats_port_out_result {
738         cmdline_fixed_string_t p_string;
739         uint32_t pipeline_id;
740         cmdline_fixed_string_t stats_string;
741         cmdline_fixed_string_t port_string;
742         cmdline_fixed_string_t out_string;
743         uint32_t port_out_id;
744 };
745
746 static void
747 cmd_stats_port_out_parsed(
748         void *parsed_result,
749         __rte_unused struct cmdline *cl,
750         void *data)
751 {
752
753         struct cmd_stats_port_out_result *params = parsed_result;
754         struct app_params *app = data;
755         struct rte_pipeline_port_out_stats stats;
756         int status;
757
758         status = app_pipeline_stats_port_out(app,
759                         params->pipeline_id,
760                         params->port_out_id,
761                         &stats);
762
763         if (status != 0) {
764                 printf("Command failed\n");
765                 return;
766         }
767
768         /* Display stats */
769         printf("Pipeline %" PRIu32 " - stats for output port %" PRIu32 ":\n"
770                 "\tPkts in: %" PRIu64 "\n"
771                 "\tPkts dropped by AH: %" PRIu64 "\n"
772                 "\tPkts dropped by other: %" PRIu64 "\n",
773                 params->pipeline_id,
774                 params->port_out_id,
775                 stats.stats.n_pkts_in,
776                 stats.n_pkts_dropped_by_ah,
777                 stats.stats.n_pkts_drop);
778 }
779
780 static cmdline_parse_token_string_t cmd_stats_port_out_p_string =
781         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, p_string,
782         "p");
783
784 static cmdline_parse_token_num_t cmd_stats_port_out_pipeline_id =
785         TOKEN_NUM_INITIALIZER(struct cmd_stats_port_out_result, pipeline_id,
786                 UINT32);
787
788 static cmdline_parse_token_string_t cmd_stats_port_out_stats_string =
789         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, stats_string,
790                 "stats");
791
792 static cmdline_parse_token_string_t cmd_stats_port_out_port_string =
793         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, port_string,
794                 "port");
795
796 static cmdline_parse_token_string_t cmd_stats_port_out_out_string =
797         TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, out_string,
798                 "out");
799
800 static cmdline_parse_token_num_t cmd_stats_port_out_port_out_id =
801         TOKEN_NUM_INITIALIZER(struct cmd_stats_port_out_result, port_out_id,
802                 UINT32);
803
804 static cmdline_parse_inst_t cmd_stats_port_out = {
805         .f = cmd_stats_port_out_parsed,
806         .data = NULL,
807         .help_str = "Pipeline output port stats",
808         .tokens = {
809                 (void *) &cmd_stats_port_out_p_string,
810                 (void *) &cmd_stats_port_out_pipeline_id,
811                 (void *) &cmd_stats_port_out_stats_string,
812                 (void *) &cmd_stats_port_out_port_string,
813                 (void *) &cmd_stats_port_out_out_string,
814                 (void *) &cmd_stats_port_out_port_out_id,
815                 NULL,
816         },
817 };
818
819 /*
820  * stats table
821  */
822
823 struct cmd_stats_table_result {
824         cmdline_fixed_string_t p_string;
825         uint32_t pipeline_id;
826         cmdline_fixed_string_t stats_string;
827         cmdline_fixed_string_t table_string;
828         uint32_t table_id;
829 };
830
831 static void
832 cmd_stats_table_parsed(
833         void *parsed_result,
834         __rte_unused struct cmdline *cl,
835         void *data)
836 {
837         struct cmd_stats_table_result *params = parsed_result;
838         struct app_params *app = data;
839         struct rte_pipeline_table_stats stats;
840         int status;
841
842         status = app_pipeline_stats_table(app,
843                         params->pipeline_id,
844                         params->table_id,
845                         &stats);
846
847         if (status != 0) {
848                 printf("Command failed\n");
849                 return;
850         }
851
852         /* Display stats */
853         printf("Pipeline %" PRIu32 " - stats for table %" PRIu32 ":\n"
854                 "\tPkts in: %" PRIu64 "\n"
855                 "\tPkts in with lookup miss: %" PRIu64 "\n"
856                 "\tPkts in with lookup hit dropped by AH: %" PRIu64 "\n"
857                 "\tPkts in with lookup hit dropped by others: %" PRIu64 "\n"
858                 "\tPkts in with lookup miss dropped by AH: %" PRIu64 "\n"
859                 "\tPkts in with lookup miss dropped by others: %" PRIu64 "\n",
860                 params->pipeline_id,
861                 params->table_id,
862                 stats.stats.n_pkts_in,
863                 stats.stats.n_pkts_lookup_miss,
864                 stats.n_pkts_dropped_by_lkp_hit_ah,
865                 stats.n_pkts_dropped_lkp_hit,
866                 stats.n_pkts_dropped_by_lkp_miss_ah,
867                 stats.n_pkts_dropped_lkp_miss);
868 }
869
870 static cmdline_parse_token_string_t cmd_stats_table_p_string =
871         TOKEN_STRING_INITIALIZER(struct cmd_stats_table_result, p_string,
872                 "p");
873
874 static cmdline_parse_token_num_t cmd_stats_table_pipeline_id =
875         TOKEN_NUM_INITIALIZER(struct cmd_stats_table_result, pipeline_id,
876                 UINT32);
877
878 static cmdline_parse_token_string_t cmd_stats_table_stats_string =
879         TOKEN_STRING_INITIALIZER(struct cmd_stats_table_result, stats_string,
880                 "stats");
881
882 static cmdline_parse_token_string_t cmd_stats_table_table_string =
883         TOKEN_STRING_INITIALIZER(struct cmd_stats_table_result, table_string,
884                 "table");
885
886 static cmdline_parse_token_num_t cmd_stats_table_table_id =
887         TOKEN_NUM_INITIALIZER(struct cmd_stats_table_result, table_id, UINT32);
888
889 static cmdline_parse_inst_t cmd_stats_table = {
890         .f = cmd_stats_table_parsed,
891         .data = NULL,
892         .help_str = "Pipeline table stats",
893         .tokens = {
894                 (void *) &cmd_stats_table_p_string,
895                 (void *) &cmd_stats_table_pipeline_id,
896                 (void *) &cmd_stats_table_stats_string,
897                 (void *) &cmd_stats_table_table_string,
898                 (void *) &cmd_stats_table_table_id,
899                 NULL,
900         },
901 };
902
903 /*
904  * port in enable
905  */
906
907 struct cmd_port_in_enable_result {
908         cmdline_fixed_string_t p_string;
909         uint32_t pipeline_id;
910         cmdline_fixed_string_t port_string;
911         cmdline_fixed_string_t in_string;
912         uint32_t port_in_id;
913         cmdline_fixed_string_t enable_string;
914 };
915
916 static void
917 cmd_port_in_enable_parsed(
918         void *parsed_result,
919         __rte_unused struct cmdline *cl,
920         void *data)
921 {
922         struct cmd_port_in_enable_result *params = parsed_result;
923         struct app_params *app = data;
924         int status;
925
926         status = app_pipeline_port_in_enable(app,
927                         params->pipeline_id,
928                         params->port_in_id);
929
930         if (status != 0)
931                 printf("Command failed\n");
932 }
933
934 static cmdline_parse_token_string_t cmd_port_in_enable_p_string =
935         TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result, p_string,
936                 "p");
937
938 static cmdline_parse_token_num_t cmd_port_in_enable_pipeline_id =
939         TOKEN_NUM_INITIALIZER(struct cmd_port_in_enable_result, pipeline_id,
940                 UINT32);
941
942 static cmdline_parse_token_string_t cmd_port_in_enable_port_string =
943         TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result, port_string,
944         "port");
945
946 static cmdline_parse_token_string_t cmd_port_in_enable_in_string =
947         TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result, in_string,
948                 "in");
949
950 static cmdline_parse_token_num_t cmd_port_in_enable_port_in_id =
951         TOKEN_NUM_INITIALIZER(struct cmd_port_in_enable_result, port_in_id,
952                 UINT32);
953
954 static cmdline_parse_token_string_t cmd_port_in_enable_enable_string =
955         TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result,
956                 enable_string, "enable");
957
958 static cmdline_parse_inst_t cmd_port_in_enable = {
959         .f = cmd_port_in_enable_parsed,
960         .data = NULL,
961         .help_str = "Pipeline input port enable",
962         .tokens = {
963                 (void *) &cmd_port_in_enable_p_string,
964                 (void *) &cmd_port_in_enable_pipeline_id,
965                 (void *) &cmd_port_in_enable_port_string,
966                 (void *) &cmd_port_in_enable_in_string,
967                 (void *) &cmd_port_in_enable_port_in_id,
968                 (void *) &cmd_port_in_enable_enable_string,
969                 NULL,
970         },
971 };
972
973 /*
974  * port in disable
975  */
976
977 struct cmd_port_in_disable_result {
978         cmdline_fixed_string_t p_string;
979         uint32_t pipeline_id;
980         cmdline_fixed_string_t port_string;
981         cmdline_fixed_string_t in_string;
982         uint32_t port_in_id;
983         cmdline_fixed_string_t disable_string;
984 };
985
986 static void
987 cmd_port_in_disable_parsed(
988         void *parsed_result,
989         __rte_unused struct cmdline *cl,
990         void *data)
991 {
992         struct cmd_port_in_disable_result *params = parsed_result;
993         struct app_params *app = data;
994         int status;
995
996         status = app_pipeline_port_in_disable(app,
997                         params->pipeline_id,
998                         params->port_in_id);
999
1000         if (status != 0)
1001                 printf("Command failed\n");
1002 }
1003
1004 static cmdline_parse_token_string_t cmd_port_in_disable_p_string =
1005         TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result, p_string,
1006                 "p");
1007
1008 static cmdline_parse_token_num_t cmd_port_in_disable_pipeline_id =
1009         TOKEN_NUM_INITIALIZER(struct cmd_port_in_disable_result, pipeline_id,
1010                 UINT32);
1011
1012 static cmdline_parse_token_string_t cmd_port_in_disable_port_string =
1013         TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result, port_string,
1014                 "port");
1015
1016 static cmdline_parse_token_string_t cmd_port_in_disable_in_string =
1017         TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result, in_string,
1018                 "in");
1019
1020 static cmdline_parse_token_num_t cmd_port_in_disable_port_in_id =
1021         TOKEN_NUM_INITIALIZER(struct cmd_port_in_disable_result, port_in_id,
1022                 UINT32);
1023
1024 static cmdline_parse_token_string_t cmd_port_in_disable_disable_string =
1025         TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result,
1026                 disable_string, "disable");
1027
1028 static cmdline_parse_inst_t cmd_port_in_disable = {
1029         .f = cmd_port_in_disable_parsed,
1030         .data = NULL,
1031         .help_str = "Pipeline input port disable",
1032         .tokens = {
1033                 (void *) &cmd_port_in_disable_p_string,
1034                 (void *) &cmd_port_in_disable_pipeline_id,
1035                 (void *) &cmd_port_in_disable_port_string,
1036                 (void *) &cmd_port_in_disable_in_string,
1037                 (void *) &cmd_port_in_disable_port_in_id,
1038                 (void *) &cmd_port_in_disable_disable_string,
1039                 NULL,
1040         },
1041 };
1042
1043 /*
1044  * link config
1045  */
1046
1047 static void
1048 print_link_info(struct app_link_params *p)
1049 {
1050         struct rte_eth_stats stats;
1051         struct ether_addr *mac_addr;
1052         uint32_t netmask = (~0U) << (32 - p->depth);
1053         uint32_t host = p->ip & netmask;
1054         uint32_t bcast = host | (~netmask);
1055
1056         memset(&stats, 0, sizeof(stats));
1057         rte_eth_stats_get(p->pmd_id, &stats);
1058
1059         mac_addr = (struct ether_addr *) &p->mac_addr;
1060
1061         if (strlen(p->pci_bdf))
1062                 printf("%s(%s): flags=<%s>\n",
1063                         p->name,
1064                         p->pci_bdf,
1065                         (p->state) ? "UP" : "DOWN");
1066         else
1067                 printf("%s: flags=<%s>\n",
1068                         p->name,
1069                         (p->state) ? "UP" : "DOWN");
1070
1071         if (p->ip)
1072                 printf("\tinet %" PRIu32 ".%" PRIu32
1073                         ".%" PRIu32 ".%" PRIu32
1074                         " netmask %" PRIu32 ".%" PRIu32
1075                         ".%" PRIu32 ".%" PRIu32 " "
1076                         "broadcast %" PRIu32 ".%" PRIu32
1077                         ".%" PRIu32 ".%" PRIu32 "\n",
1078                         (p->ip >> 24) & 0xFF,
1079                         (p->ip >> 16) & 0xFF,
1080                         (p->ip >> 8) & 0xFF,
1081                         p->ip & 0xFF,
1082                         (netmask >> 24) & 0xFF,
1083                         (netmask >> 16) & 0xFF,
1084                         (netmask >> 8) & 0xFF,
1085                         netmask & 0xFF,
1086                         (bcast >> 24) & 0xFF,
1087                         (bcast >> 16) & 0xFF,
1088                         (bcast >> 8) & 0xFF,
1089                         bcast & 0xFF);
1090
1091         printf("\tether %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
1092                 ":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
1093                 mac_addr->addr_bytes[0],
1094                 mac_addr->addr_bytes[1],
1095                 mac_addr->addr_bytes[2],
1096                 mac_addr->addr_bytes[3],
1097                 mac_addr->addr_bytes[4],
1098                 mac_addr->addr_bytes[5]);
1099
1100         printf("\tRX packets %" PRIu64
1101                 "  bytes %" PRIu64
1102                 "\n",
1103                 stats.ipackets,
1104                 stats.ibytes);
1105
1106         printf("\tRX errors %" PRIu64
1107                 "  missed %" PRIu64
1108                 "  no-mbuf %" PRIu64
1109                 "\n",
1110                 stats.ierrors,
1111                 stats.imissed,
1112                 stats.rx_nombuf);
1113
1114         printf("\tTX packets %" PRIu64
1115                 "  bytes %" PRIu64 "\n",
1116                 stats.opackets,
1117                 stats.obytes);
1118
1119         printf("\tTX errors %" PRIu64
1120                 "\n",
1121                 stats.oerrors);
1122
1123         printf("\n");
1124 }
1125
1126 /*
1127  * link
1128  *
1129  * link config:
1130  *    link <linkid> config <ipaddr> <depth>
1131  *
1132  * link up:
1133  *    link <linkid> up
1134  *
1135  * link down:
1136  *    link <linkid> down
1137  *
1138  * link ls:
1139  *    link ls
1140  */
1141
1142 struct cmd_link_result {
1143         cmdline_fixed_string_t link_string;
1144         cmdline_multi_string_t multi_string;
1145 };
1146
1147 static void
1148 cmd_link_parsed(
1149         void *parsed_result,
1150         __attribute__((unused)) struct cmdline *cl,
1151          void *data)
1152 {
1153         struct cmd_link_result *params = parsed_result;
1154         struct app_params *app = data;
1155
1156         char *tokens[16];
1157         uint32_t n_tokens = RTE_DIM(tokens);
1158         int status;
1159
1160         uint32_t link_id;
1161
1162         status = parse_tokenize_string(params->multi_string, tokens, &n_tokens);
1163         if (status != 0) {
1164                 printf(CMD_MSG_TOO_MANY_ARGS, "link");
1165                 return;
1166         }
1167
1168         /* link ls */
1169         if ((n_tokens == 1) && (strcmp(tokens[0], "ls") == 0)) {
1170                 for (link_id = 0; link_id < app->n_links; link_id++) {
1171                         struct app_link_params *p;
1172
1173                         APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
1174                         print_link_info(p);
1175                 }
1176                 return;
1177         } /* link ls */
1178
1179         if (n_tokens < 2) {
1180                 printf(CMD_MSG_MISMATCH_ARGS, "link");
1181                 return;
1182         }
1183
1184         if (parser_read_uint32(&link_id, tokens[0])) {
1185                 printf(CMD_MSG_INVALID_ARG, "linkid");
1186                 return;
1187         }
1188
1189         /* link config */
1190         if (strcmp(tokens[1], "config") == 0) {
1191                 struct in_addr ipaddr_ipv4;
1192                 uint32_t depth;
1193
1194                 if (n_tokens != 4) {
1195                         printf(CMD_MSG_MISMATCH_ARGS, "link config");
1196                         return;
1197                 }
1198
1199                 if (parse_ipv4_addr(tokens[2], &ipaddr_ipv4)) {
1200                         printf(CMD_MSG_INVALID_ARG, "ipaddr");
1201                         return;
1202                 }
1203
1204                 if (parser_read_uint32(&depth, tokens[3])) {
1205                         printf(CMD_MSG_INVALID_ARG, "depth");
1206                         return;
1207                 }
1208
1209                 status = app_link_config(app,
1210                         link_id,
1211                         rte_be_to_cpu_32(ipaddr_ipv4.s_addr),
1212                         depth);
1213                 if (status)
1214                         printf(CMD_MSG_FAIL, "link config");
1215
1216                 return;
1217         } /* link config */
1218
1219         /* link up */
1220         if (strcmp(tokens[1], "up") == 0) {
1221                 if (n_tokens != 2) {
1222                         printf(CMD_MSG_MISMATCH_ARGS, "link up");
1223                         return;
1224                 }
1225
1226                 status = app_link_up(app, link_id);
1227                 if (status)
1228                         printf(CMD_MSG_FAIL, "link up");
1229
1230                 return;
1231         } /* link up */
1232
1233         /* link down */
1234         if (strcmp(tokens[1], "down") == 0) {
1235                 if (n_tokens != 2) {
1236                         printf(CMD_MSG_MISMATCH_ARGS, "link down");
1237                         return;
1238                 }
1239
1240                 status = app_link_down(app, link_id);
1241                 if (status)
1242                         printf(CMD_MSG_FAIL, "link down");
1243
1244                 return;
1245         } /* link down */
1246
1247         printf(CMD_MSG_MISMATCH_ARGS, "link");
1248 }
1249
1250 static cmdline_parse_token_string_t cmd_link_link_string =
1251         TOKEN_STRING_INITIALIZER(struct cmd_link_result, link_string, "link");
1252
1253 static cmdline_parse_token_string_t cmd_link_multi_string =
1254         TOKEN_STRING_INITIALIZER(struct cmd_link_result, multi_string,
1255         TOKEN_STRING_MULTI);
1256
1257 static cmdline_parse_inst_t cmd_link = {
1258         .f = cmd_link_parsed,
1259         .data = NULL,
1260         .help_str = "link config / up / down / ls",
1261         .tokens = {
1262                 (void *) &cmd_link_link_string,
1263                 (void *) &cmd_link_multi_string,
1264                 NULL,
1265         },
1266 };
1267
1268 /*
1269  * quit
1270  */
1271
1272 struct cmd_quit_result {
1273         cmdline_fixed_string_t quit;
1274 };
1275
1276 static void
1277 cmd_quit_parsed(
1278         __rte_unused void *parsed_result,
1279         struct cmdline *cl,
1280         __rte_unused void *data)
1281 {
1282         cmdline_quit(cl);
1283 }
1284
1285 static cmdline_parse_token_string_t cmd_quit_quit =
1286         TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
1287
1288 static cmdline_parse_inst_t cmd_quit = {
1289         .f = cmd_quit_parsed,
1290         .data = NULL,
1291         .help_str = "Quit",
1292         .tokens = {
1293                 (void *) &cmd_quit_quit,
1294                 NULL,
1295         },
1296 };
1297
1298 /*
1299  * run
1300  *
1301  *    run <file>
1302  *    run <file> [<count> [<interval>]]
1303          <count> default is 1
1304  *       <interval> is measured in milliseconds, default is 1 second
1305  */
1306
1307 static void
1308 app_run_file(
1309         cmdline_parse_ctx_t *ctx,
1310         const char *file_name)
1311 {
1312         struct cmdline *file_cl;
1313         int fd;
1314
1315         fd = open(file_name, O_RDONLY);
1316         if (fd < 0) {
1317                 printf("Cannot open file \"%s\"\n", file_name);
1318                 return;
1319         }
1320
1321         file_cl = cmdline_new(ctx, "", fd, 1);
1322         cmdline_interact(file_cl);
1323         close(fd);
1324 }
1325
1326 struct cmd_run_result {
1327         cmdline_fixed_string_t run_string;
1328         cmdline_multi_string_t multi_string;
1329 };
1330
1331 static void
1332 cmd_run_parsed(
1333         void *parsed_result,
1334         struct cmdline *cl,
1335         __attribute__((unused)) void *data)
1336 {
1337         struct cmd_run_result *params = parsed_result;
1338
1339         char *tokens[16];
1340         uint32_t n_tokens = RTE_DIM(tokens);
1341         int status;
1342
1343         char *file_name;
1344         uint32_t count, interval, i;
1345
1346         status = parse_tokenize_string(params->multi_string, tokens, &n_tokens);
1347         if (status) {
1348                 printf(CMD_MSG_TOO_MANY_ARGS, "run");
1349                 return;
1350         }
1351
1352         switch (n_tokens) {
1353         case 0:
1354                 printf(CMD_MSG_NOT_ENOUGH_ARGS, "run");
1355                 return;
1356
1357         case 1:
1358                 file_name = tokens[0];
1359                 count = 1;
1360                 interval = 1000;
1361                 break;
1362
1363         case 2:
1364                 file_name = tokens[0];
1365
1366                 if (parser_read_uint32(&count, tokens[1]) ||
1367                         (count == 0)) {
1368                         printf(CMD_MSG_INVALID_ARG, "count");
1369                         return;
1370                 }
1371
1372                 interval = 1000;
1373                 break;
1374
1375         case 3:
1376                 file_name = tokens[0];
1377
1378                 if (parser_read_uint32(&count, tokens[1]) ||
1379                         (count == 0)) {
1380                         printf(CMD_MSG_INVALID_ARG, "count");
1381                         return;
1382                 }
1383
1384                 if (parser_read_uint32(&interval, tokens[2]) ||
1385                         (interval == 0)) {
1386                         printf(CMD_MSG_INVALID_ARG, "interval");
1387                         return;
1388                 }
1389                 break;
1390
1391         default:
1392                 printf(CMD_MSG_MISMATCH_ARGS, "run");
1393                 return;
1394         }
1395
1396         for (i = 0; i < count; i++) {
1397                 app_run_file(cl->ctx, file_name);
1398                 if (interval)
1399                         usleep(interval * 1000);
1400         }
1401 }
1402
1403 static cmdline_parse_token_string_t cmd_run_run_string =
1404         TOKEN_STRING_INITIALIZER(struct cmd_run_result, run_string, "run");
1405
1406 static cmdline_parse_token_string_t cmd_run_multi_string =
1407         TOKEN_STRING_INITIALIZER(struct cmd_run_result, multi_string,
1408         TOKEN_STRING_MULTI);
1409
1410
1411 static cmdline_parse_inst_t cmd_run = {
1412         .f = cmd_run_parsed,
1413         .data = NULL,
1414         .help_str = "Run CLI script file",
1415         .tokens = {
1416                 (void *) &cmd_run_run_string,
1417                 (void *) &cmd_run_multi_string,
1418                 NULL,
1419         },
1420 };
1421
1422 static cmdline_parse_ctx_t pipeline_common_cmds[] = {
1423         (cmdline_parse_inst_t *) &cmd_quit,
1424         (cmdline_parse_inst_t *) &cmd_run,
1425         (cmdline_parse_inst_t *) &cmd_link,
1426         (cmdline_parse_inst_t *) &cmd_ping,
1427         (cmdline_parse_inst_t *) &cmd_stats_port_in,
1428         (cmdline_parse_inst_t *) &cmd_stats_port_out,
1429         (cmdline_parse_inst_t *) &cmd_stats_table,
1430         (cmdline_parse_inst_t *) &cmd_port_in_enable,
1431         (cmdline_parse_inst_t *) &cmd_port_in_disable,
1432         NULL,
1433 };
1434
1435 int
1436 app_pipeline_common_cmd_push(struct app_params *app)
1437 {
1438         uint32_t n_cmds, i;
1439
1440         /* Check for available slots in the application commands array */
1441         n_cmds = RTE_DIM(pipeline_common_cmds) - 1;
1442         if (n_cmds > APP_MAX_CMDS - app->n_cmds)
1443                 return -ENOMEM;
1444
1445         /* Push pipeline commands into the application */
1446         memcpy(&app->cmds[app->n_cmds],
1447                 pipeline_common_cmds,
1448                 n_cmds * sizeof(cmdline_parse_ctx_t));
1449
1450         for (i = 0; i < n_cmds; i++)
1451                 app->cmds[app->n_cmds + i]->data = app;
1452
1453         app->n_cmds += n_cmds;
1454         app->cmds[app->n_cmds] = NULL;
1455
1456         return 0;
1457 }