examples/pipeline: add new example application
[dpdk.git] / examples / pipeline / obj.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <rte_mempool.h>
9 #include <rte_mbuf.h>
10 #include <rte_ethdev.h>
11 #include <rte_swx_port_ethdev.h>
12 #include <rte_swx_port_source_sink.h>
13 #include <rte_swx_table_em.h>
14 #include <rte_swx_pipeline.h>
15 #include <rte_swx_ctl.h>
16
17 #include "obj.h"
18
19 /*
20  * mempool
21  */
22 TAILQ_HEAD(mempool_list, mempool);
23
24 /*
25  * link
26  */
27 TAILQ_HEAD(link_list, link);
28
29 /*
30  * pipeline
31  */
32 TAILQ_HEAD(pipeline_list, pipeline);
33
34 /*
35  * obj
36  */
37 struct obj {
38         struct mempool_list mempool_list;
39         struct link_list link_list;
40         struct pipeline_list pipeline_list;
41 };
42
43 /*
44  * mempool
45  */
46 #define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
47
48 struct mempool *
49 mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
50 {
51         struct mempool *mempool;
52         struct rte_mempool *m;
53
54         /* Check input params */
55         if ((name == NULL) ||
56                 mempool_find(obj, name) ||
57                 (params == NULL) ||
58                 (params->buffer_size < BUFFER_SIZE_MIN) ||
59                 (params->pool_size == 0))
60                 return NULL;
61
62         /* Resource create */
63         m = rte_pktmbuf_pool_create(
64                 name,
65                 params->pool_size,
66                 params->cache_size,
67                 0,
68                 params->buffer_size - sizeof(struct rte_mbuf),
69                 params->cpu_id);
70
71         if (m == NULL)
72                 return NULL;
73
74         /* Node allocation */
75         mempool = calloc(1, sizeof(struct mempool));
76         if (mempool == NULL) {
77                 rte_mempool_free(m);
78                 return NULL;
79         }
80
81         /* Node fill in */
82         strlcpy(mempool->name, name, sizeof(mempool->name));
83         mempool->m = m;
84         mempool->buffer_size = params->buffer_size;
85
86         /* Node add to list */
87         TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
88
89         return mempool;
90 }
91
92 struct mempool *
93 mempool_find(struct obj *obj, const char *name)
94 {
95         struct mempool *mempool;
96
97         if (!obj || !name)
98                 return NULL;
99
100         TAILQ_FOREACH(mempool, &obj->mempool_list, node)
101                 if (strcmp(mempool->name, name) == 0)
102                         return mempool;
103
104         return NULL;
105 }
106
107 /*
108  * link
109  */
110 static struct rte_eth_conf port_conf_default = {
111         .link_speeds = 0,
112         .rxmode = {
113                 .mq_mode = ETH_MQ_RX_NONE,
114                 .max_rx_pkt_len = 9000, /* Jumbo frame max packet len */
115                 .split_hdr_size = 0, /* Header split buffer size */
116         },
117         .rx_adv_conf = {
118                 .rss_conf = {
119                         .rss_key = NULL,
120                         .rss_key_len = 40,
121                         .rss_hf = 0,
122                 },
123         },
124         .txmode = {
125                 .mq_mode = ETH_MQ_TX_NONE,
126         },
127         .lpbk_mode = 0,
128 };
129
130 #define RETA_CONF_SIZE     (ETH_RSS_RETA_SIZE_512 / RTE_RETA_GROUP_SIZE)
131
132 static int
133 rss_setup(uint16_t port_id,
134         uint16_t reta_size,
135         struct link_params_rss *rss)
136 {
137         struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
138         uint32_t i;
139         int status;
140
141         /* RETA setting */
142         memset(reta_conf, 0, sizeof(reta_conf));
143
144         for (i = 0; i < reta_size; i++)
145                 reta_conf[i / RTE_RETA_GROUP_SIZE].mask = UINT64_MAX;
146
147         for (i = 0; i < reta_size; i++) {
148                 uint32_t reta_id = i / RTE_RETA_GROUP_SIZE;
149                 uint32_t reta_pos = i % RTE_RETA_GROUP_SIZE;
150                 uint32_t rss_qs_pos = i % rss->n_queues;
151
152                 reta_conf[reta_id].reta[reta_pos] =
153                         (uint16_t) rss->queue_id[rss_qs_pos];
154         }
155
156         /* RETA update */
157         status = rte_eth_dev_rss_reta_update(port_id,
158                 reta_conf,
159                 reta_size);
160
161         return status;
162 }
163
164 struct link *
165 link_create(struct obj *obj, const char *name, struct link_params *params)
166 {
167         struct rte_eth_dev_info port_info;
168         struct rte_eth_conf port_conf;
169         struct link *link;
170         struct link_params_rss *rss;
171         struct mempool *mempool;
172         uint32_t cpu_id, i;
173         int status;
174         uint16_t port_id;
175
176         /* Check input params */
177         if ((name == NULL) ||
178                 link_find(obj, name) ||
179                 (params == NULL) ||
180                 (params->rx.n_queues == 0) ||
181                 (params->rx.queue_size == 0) ||
182                 (params->tx.n_queues == 0) ||
183                 (params->tx.queue_size == 0))
184                 return NULL;
185
186         port_id = params->port_id;
187         if (params->dev_name) {
188                 status = rte_eth_dev_get_port_by_name(params->dev_name,
189                         &port_id);
190
191                 if (status)
192                         return NULL;
193         } else
194                 if (!rte_eth_dev_is_valid_port(port_id))
195                         return NULL;
196
197         if (rte_eth_dev_info_get(port_id, &port_info) != 0)
198                 return NULL;
199
200         mempool = mempool_find(obj, params->rx.mempool_name);
201         if (mempool == NULL)
202                 return NULL;
203
204         rss = params->rx.rss;
205         if (rss) {
206                 if ((port_info.reta_size == 0) ||
207                         (port_info.reta_size > ETH_RSS_RETA_SIZE_512))
208                         return NULL;
209
210                 if ((rss->n_queues == 0) ||
211                         (rss->n_queues >= LINK_RXQ_RSS_MAX))
212                         return NULL;
213
214                 for (i = 0; i < rss->n_queues; i++)
215                         if (rss->queue_id[i] >= port_info.max_rx_queues)
216                                 return NULL;
217         }
218
219         /**
220          * Resource create
221          */
222         /* Port */
223         memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
224         if (rss) {
225                 port_conf.rxmode.mq_mode = ETH_MQ_RX_RSS;
226                 port_conf.rx_adv_conf.rss_conf.rss_hf =
227                         (ETH_RSS_IP | ETH_RSS_TCP | ETH_RSS_UDP) &
228                         port_info.flow_type_rss_offloads;
229         }
230
231         cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
232         if (cpu_id == (uint32_t) SOCKET_ID_ANY)
233                 cpu_id = 0;
234
235         status = rte_eth_dev_configure(
236                 port_id,
237                 params->rx.n_queues,
238                 params->tx.n_queues,
239                 &port_conf);
240
241         if (status < 0)
242                 return NULL;
243
244         if (params->promiscuous) {
245                 status = rte_eth_promiscuous_enable(port_id);
246                 if (status != 0)
247                         return NULL;
248         }
249
250         /* Port RX */
251         for (i = 0; i < params->rx.n_queues; i++) {
252                 status = rte_eth_rx_queue_setup(
253                         port_id,
254                         i,
255                         params->rx.queue_size,
256                         cpu_id,
257                         NULL,
258                         mempool->m);
259
260                 if (status < 0)
261                         return NULL;
262         }
263
264         /* Port TX */
265         for (i = 0; i < params->tx.n_queues; i++) {
266                 status = rte_eth_tx_queue_setup(
267                         port_id,
268                         i,
269                         params->tx.queue_size,
270                         cpu_id,
271                         NULL);
272
273                 if (status < 0)
274                         return NULL;
275         }
276
277         /* Port start */
278         status = rte_eth_dev_start(port_id);
279         if (status < 0)
280                 return NULL;
281
282         if (rss) {
283                 status = rss_setup(port_id, port_info.reta_size, rss);
284
285                 if (status) {
286                         rte_eth_dev_stop(port_id);
287                         return NULL;
288                 }
289         }
290
291         /* Port link up */
292         status = rte_eth_dev_set_link_up(port_id);
293         if ((status < 0) && (status != -ENOTSUP)) {
294                 rte_eth_dev_stop(port_id);
295                 return NULL;
296         }
297
298         /* Node allocation */
299         link = calloc(1, sizeof(struct link));
300         if (link == NULL) {
301                 rte_eth_dev_stop(port_id);
302                 return NULL;
303         }
304
305         /* Node fill in */
306         strlcpy(link->name, name, sizeof(link->name));
307         link->port_id = port_id;
308         rte_eth_dev_get_name_by_port(port_id, link->dev_name);
309         link->n_rxq = params->rx.n_queues;
310         link->n_txq = params->tx.n_queues;
311
312         /* Node add to list */
313         TAILQ_INSERT_TAIL(&obj->link_list, link, node);
314
315         return link;
316 }
317
318 int
319 link_is_up(struct obj *obj, const char *name)
320 {
321         struct rte_eth_link link_params;
322         struct link *link;
323
324         /* Check input params */
325         if (!obj || !name)
326                 return 0;
327
328         link = link_find(obj, name);
329         if (link == NULL)
330                 return 0;
331
332         /* Resource */
333         if (rte_eth_link_get(link->port_id, &link_params) < 0)
334                 return 0;
335
336         return (link_params.link_status == ETH_LINK_DOWN) ? 0 : 1;
337 }
338
339 struct link *
340 link_find(struct obj *obj, const char *name)
341 {
342         struct link *link;
343
344         if (!obj || !name)
345                 return NULL;
346
347         TAILQ_FOREACH(link, &obj->link_list, node)
348                 if (strcmp(link->name, name) == 0)
349                         return link;
350
351         return NULL;
352 }
353
354 struct link *
355 link_next(struct obj *obj, struct link *link)
356 {
357         return (link == NULL) ?
358                 TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
359 }
360
361 /*
362  * pipeline
363  */
364 #ifndef PIPELINE_MSGQ_SIZE
365 #define PIPELINE_MSGQ_SIZE                                 64
366 #endif
367
368 struct pipeline *
369 pipeline_create(struct obj *obj, const char *name, int numa_node)
370 {
371         struct pipeline *pipeline;
372         struct rte_swx_pipeline *p = NULL;
373         int status;
374
375         /* Check input params */
376         if ((name == NULL) ||
377                 pipeline_find(obj, name))
378                 return NULL;
379
380         /* Resource create */
381         status = rte_swx_pipeline_config(&p, numa_node);
382         if (status)
383                 goto error;
384
385         status = rte_swx_pipeline_port_in_type_register(p,
386                 "ethdev",
387                 &rte_swx_port_ethdev_reader_ops);
388         if (status)
389                 goto error;
390
391         status = rte_swx_pipeline_port_out_type_register(p,
392                 "ethdev",
393                 &rte_swx_port_ethdev_writer_ops);
394         if (status)
395                 goto error;
396
397 #ifdef RTE_PORT_PCAP
398         status = rte_swx_pipeline_port_in_type_register(p,
399                 "source",
400                 &rte_swx_port_source_ops);
401         if (status)
402                 goto error;
403 #endif
404
405         status = rte_swx_pipeline_port_out_type_register(p,
406                 "sink",
407                 &rte_swx_port_sink_ops);
408         if (status)
409                 goto error;
410
411         status = rte_swx_pipeline_table_type_register(p,
412                 "exact",
413                 RTE_SWX_TABLE_MATCH_EXACT,
414                 &rte_swx_table_exact_match_ops);
415         if (status)
416                 goto error;
417
418         /* Node allocation */
419         pipeline = calloc(1, sizeof(struct pipeline));
420         if (pipeline == NULL)
421                 goto error;
422
423         /* Node fill in */
424         strlcpy(pipeline->name, name, sizeof(pipeline->name));
425         pipeline->p = p;
426         pipeline->timer_period_ms = 10;
427
428         /* Node add to list */
429         TAILQ_INSERT_TAIL(&obj->pipeline_list, pipeline, node);
430
431         return pipeline;
432
433 error:
434         rte_swx_pipeline_free(p);
435         return NULL;
436 }
437
438 struct pipeline *
439 pipeline_find(struct obj *obj, const char *name)
440 {
441         struct pipeline *pipeline;
442
443         if (!obj || !name)
444                 return NULL;
445
446         TAILQ_FOREACH(pipeline, &obj->pipeline_list, node)
447                 if (strcmp(name, pipeline->name) == 0)
448                         return pipeline;
449
450         return NULL;
451 }
452
453 /*
454  * obj
455  */
456 struct obj *
457 obj_init(void)
458 {
459         struct obj *obj;
460
461         obj = calloc(1, sizeof(struct obj));
462         if (!obj)
463                 return NULL;
464
465         TAILQ_INIT(&obj->mempool_list);
466         TAILQ_INIT(&obj->link_list);
467         TAILQ_INIT(&obj->pipeline_list);
468
469         return obj;
470 }