61d3e344ec5c3c471506cf511026d6de7fbfad62
[dpdk.git] / examples / vmdq_dcb / main.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 <stdint.h>
35 #include <sys/queue.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <assert.h>
40 #include <errno.h>
41 #include <signal.h>
42 #include <stdarg.h>
43 #include <inttypes.h>
44 #include <getopt.h>
45
46 #include <rte_common.h>
47 #include <rte_log.h>
48 #include <rte_memory.h>
49 #include <rte_memcpy.h>
50 #include <rte_memzone.h>
51 #include <rte_tailq.h>
52 #include <rte_eal.h>
53 #include <rte_per_lcore.h>
54 #include <rte_launch.h>
55 #include <rte_atomic.h>
56 #include <rte_cycles.h>
57 #include <rte_prefetch.h>
58 #include <rte_lcore.h>
59 #include <rte_per_lcore.h>
60 #include <rte_branch_prediction.h>
61 #include <rte_interrupts.h>
62 #include <rte_pci.h>
63 #include <rte_random.h>
64 #include <rte_debug.h>
65 #include <rte_ether.h>
66 #include <rte_ethdev.h>
67 #include <rte_ring.h>
68 #include <rte_log.h>
69 #include <rte_mempool.h>
70 #include <rte_mbuf.h>
71 #include <rte_memcpy.h>
72
73 /* basic constants used in application */
74 #define NUM_QUEUES 128
75
76 #define NUM_MBUFS 64*1024
77 #define MBUF_CACHE_SIZE 64
78 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
79
80 #define INVALID_PORT_ID 0xFF
81
82 /* mask of enabled ports */
83 static uint32_t enabled_port_mask = 0;
84
85 /* number of pools (if user does not specify any, 16 by default */
86 static enum rte_eth_nb_pools num_pools = ETH_16_POOLS;
87
88 /* empty vmdq+dcb configuration structure. Filled in programatically */
89 static const struct rte_eth_conf vmdq_dcb_conf_default = {
90         .rxmode = {
91                 .mq_mode        = ETH_MQ_RX_VMDQ_DCB,
92                 .split_hdr_size = 0,
93                 .header_split   = 0, /**< Header Split disabled */
94                 .hw_ip_checksum = 0, /**< IP checksum offload disabled */
95                 .hw_vlan_filter = 0, /**< VLAN filtering disabled */
96                 .jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
97         },
98         .txmode = {
99                 .mq_mode = ETH_MQ_TX_NONE,
100         },
101         .rx_adv_conf = {
102                 /*
103                  * should be overridden separately in code with
104                  * appropriate values
105                  */
106                 .vmdq_dcb_conf = {
107                         .nb_queue_pools = ETH_16_POOLS,
108                         .enable_default_pool = 0,
109                         .default_pool = 0,
110                         .nb_pool_maps = 0,
111                         .pool_map = {{0, 0},},
112                         .dcb_queue = {0},
113                 },
114         },
115 };
116
117 static uint8_t ports[RTE_MAX_ETHPORTS];
118 static unsigned num_ports = 0;
119
120 /* array used for printing out statistics */
121 volatile unsigned long rxPackets[ NUM_QUEUES ] = {0};
122
123 const uint16_t vlan_tags[] = {
124         0,  1,  2,  3,  4,  5,  6,  7,
125         8,  9, 10, 11,  12, 13, 14, 15,
126         16, 17, 18, 19, 20, 21, 22, 23,
127         24, 25, 26, 27, 28, 29, 30, 31
128 };
129
130 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
131  * given above, and the number of traffic classes available for use. */
132 static inline int
133 get_eth_conf(struct rte_eth_conf *eth_conf, enum rte_eth_nb_pools num_pools)
134 {
135         struct rte_eth_vmdq_dcb_conf conf;
136         unsigned i;
137
138         if (num_pools != ETH_16_POOLS && num_pools != ETH_32_POOLS ) return -1;
139
140         conf.nb_queue_pools = num_pools;
141         conf.enable_default_pool = 0;
142         conf.default_pool = 0; /* set explicit value, even if not used */
143         conf.nb_pool_maps = sizeof( vlan_tags )/sizeof( vlan_tags[ 0 ]);
144         for (i = 0; i < conf.nb_pool_maps; i++){
145                 conf.pool_map[i].vlan_id = vlan_tags[ i ];
146                 conf.pool_map[i].pools = 1 << (i % num_pools);
147         }
148         for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
149                 conf.dcb_queue[i] = (uint8_t)(i % (NUM_QUEUES/num_pools));
150         }
151         (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
152         (void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
153                    sizeof(eth_conf->rx_adv_conf.vmdq_dcb_conf)));
154         return 0;
155 }
156
157 /*
158  * Initialises a given port using global settings and with the rx buffers
159  * coming from the mbuf_pool passed as parameter
160  */
161 static inline int
162 port_init(uint8_t port, struct rte_mempool *mbuf_pool)
163 {
164         struct rte_eth_conf port_conf;
165         const uint16_t rxRings = ETH_VMDQ_DCB_NUM_QUEUES,
166                 txRings = (uint16_t)rte_lcore_count();
167         const uint16_t rxRingSize = 128, txRingSize = 512;
168         int retval;
169         uint16_t q;
170
171         retval = get_eth_conf(&port_conf, num_pools);
172         if (retval < 0)
173                 return retval;
174
175         if (port >= rte_eth_dev_count()) return -1;
176
177         retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
178         if (retval != 0)
179                 return retval;
180
181         for (q = 0; q < rxRings; q ++) {
182                 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
183                                                 rte_eth_dev_socket_id(port),
184                                                 NULL,
185                                                 mbuf_pool);
186                 if (retval < 0)
187                         return retval;
188         }
189
190         for (q = 0; q < txRings; q ++) {
191                 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
192                                                 rte_eth_dev_socket_id(port),
193                                                 NULL);
194                 if (retval < 0)
195                         return retval;
196         }
197
198         retval  = rte_eth_dev_start(port);
199         if (retval < 0)
200                 return retval;
201
202         struct ether_addr addr;
203         rte_eth_macaddr_get(port, &addr);
204         printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
205                         " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
206                         (unsigned)port,
207                         addr.addr_bytes[0], addr.addr_bytes[1], addr.addr_bytes[2],
208                         addr.addr_bytes[3], addr.addr_bytes[4], addr.addr_bytes[5]);
209
210         return 0;
211 }
212
213 /* Check num_pools parameter and set it if OK*/
214 static int
215 vmdq_parse_num_pools(const char *q_arg)
216 {
217         char *end = NULL;
218         int n;
219
220         /* parse number string */
221         n = strtol(q_arg, &end, 10);
222         if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
223                 return -1;
224         if (n != 16 && n != 32)
225                 return -1;
226         if (n == 16)
227                 num_pools = ETH_16_POOLS;
228         else
229                 num_pools = ETH_32_POOLS;
230
231         return 0;
232 }
233
234 static int
235 parse_portmask(const char *portmask)
236 {
237         char *end = NULL;
238         unsigned long pm;
239
240         /* parse hexadecimal string */
241         pm = strtoul(portmask, &end, 16);
242         if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
243                 return -1;
244
245         if (pm == 0)
246                 return -1;
247
248         return pm;
249 }
250
251 /* Display usage */
252 static void
253 vmdq_usage(const char *prgname)
254 {
255         printf("%s [EAL options] -- -p PORTMASK]\n"
256                "  --nb-pools NP: number of pools (16 default, 32)\n",
257                prgname);
258 }
259
260 /*  Parse the argument (num_pools) given in the command line of the application */
261 static int
262 vmdq_parse_args(int argc, char **argv)
263 {
264         int opt;
265         int option_index;
266         unsigned i;
267         const char *prgname = argv[0];
268         static struct option long_option[] = {
269                 {"nb-pools", required_argument, NULL, 0},
270                 {NULL, 0, 0, 0}
271         };
272
273         /* Parse command line */
274         while ((opt = getopt_long(argc, argv, "p:",long_option,&option_index)) != EOF) {
275                 switch (opt) {
276                 /* portmask */
277                 case 'p':
278                         enabled_port_mask = parse_portmask(optarg);
279                         if (enabled_port_mask == 0) {
280                                 printf("invalid portmask\n");
281                                 vmdq_usage(prgname);
282                                 return -1;
283                         }
284                         break;
285                 case 0:
286                         if (vmdq_parse_num_pools(optarg) == -1){
287                                 printf("invalid number of pools\n");
288                                 vmdq_usage(prgname);
289                                 return -1;
290                         }
291                         break;
292                 default:
293                         vmdq_usage(prgname);
294                         return -1;
295                 }
296         }
297
298         for(i = 0; i < RTE_MAX_ETHPORTS; i++)
299         {
300                 if (enabled_port_mask & (1 << i))
301                         ports[num_ports++] = (uint8_t)i;
302         }
303
304         if (num_ports < 2 || num_ports % 2) {
305                 printf("Current enabled port number is %u,"
306                         "but it should be even and at least 2\n",num_ports);
307                 return -1;
308         }
309
310         return 0;
311 }
312
313
314 /* When we receive a HUP signal, print out our stats */
315 static void
316 sighup_handler(int signum)
317 {
318         unsigned q;
319         for (q = 0; q < NUM_QUEUES; q ++) {
320                 if (q % (NUM_QUEUES/num_pools) == 0)
321                         printf("\nPool %u: ", q/(NUM_QUEUES/num_pools));
322                 printf("%lu ", rxPackets[ q ]);
323         }
324         printf("\nFinished handling signal %d\n", signum);
325 }
326
327 /*
328  * Main thread that does the work, reading from INPUT_PORT
329  * and writing to OUTPUT_PORT
330  */
331 static  __attribute__((noreturn)) int
332 lcore_main(void *arg)
333 {
334         const uintptr_t core_num = (uintptr_t)arg;
335         const unsigned num_cores = rte_lcore_count();
336         uint16_t startQueue = (uint16_t)(core_num * (NUM_QUEUES/num_cores));
337         uint16_t endQueue = (uint16_t)(startQueue + (NUM_QUEUES/num_cores));
338         uint16_t q, i, p;
339
340         printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
341                rte_lcore_id(), startQueue, endQueue - 1);
342
343         for (;;) {
344                 struct rte_mbuf *buf[32];
345                 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
346                 for (p = 0; p < num_ports; p++) {
347                         const uint8_t src = ports[p];
348                         const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
349
350                         if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
351                                 continue;
352
353                         for (q = startQueue; q < endQueue; q++) {
354                                 const uint16_t rxCount = rte_eth_rx_burst(src,
355                                         q, buf, buf_size);
356                                 if (rxCount == 0)
357                                         continue;
358                                 rxPackets[q] += rxCount;
359
360                                 const uint16_t txCount = rte_eth_tx_burst(dst,
361                                         (uint16_t)core_num, buf, rxCount);
362                                 if (txCount != rxCount) {
363                                         for (i = txCount; i < rxCount; i++)
364                                                 rte_pktmbuf_free(buf[i]);
365                                 }
366                         }
367                 }
368         }
369 }
370
371 /*
372  * Update the global var NUM_PORTS and array PORTS according to system ports number
373  * and return valid ports number
374  */
375 static unsigned check_ports_num(unsigned nb_ports)
376 {
377         unsigned valid_num_ports = num_ports;
378         unsigned portid;
379
380         if (num_ports > nb_ports) {
381                 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
382                         num_ports, nb_ports);
383                 num_ports = nb_ports;
384         }
385
386         for (portid = 0; portid < num_ports; portid ++) {
387                 if (ports[portid] >= nb_ports) {
388                         printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
389                                 ports[portid], (nb_ports - 1));
390                         ports[portid] = INVALID_PORT_ID;
391                         valid_num_ports --;
392                 }
393         }
394         return valid_num_ports;
395 }
396
397
398 /* Main function, does initialisation and calls the per-lcore functions */
399 int
400 main(int argc, char *argv[])
401 {
402         unsigned cores;
403         struct rte_mempool *mbuf_pool;
404         unsigned lcore_id;
405         uintptr_t i;
406         int ret;
407         unsigned nb_ports, valid_num_ports;
408         uint8_t portid;
409
410         signal(SIGHUP, sighup_handler);
411
412         /* init EAL */
413         ret = rte_eal_init(argc, argv);
414         if (ret < 0)
415                 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
416         argc -= ret;
417         argv += ret;
418
419         /* parse app arguments */
420         ret = vmdq_parse_args(argc, argv);
421         if (ret < 0)
422                 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
423
424         cores = rte_lcore_count();
425         if ((cores & (cores - 1)) != 0 || cores > 128) {
426                 rte_exit(EXIT_FAILURE,"This program can only run on an even"
427                                 "number of cores(1-128)\n\n");
428         }
429
430         nb_ports = rte_eth_dev_count();
431         if (nb_ports > RTE_MAX_ETHPORTS)
432                 nb_ports = RTE_MAX_ETHPORTS;
433
434         /*
435          * Update the global var NUM_PORTS and global array PORTS
436          * and get value of var VALID_NUM_PORTS according to system ports number
437          */
438         valid_num_ports = check_ports_num(nb_ports);
439
440         if (valid_num_ports < 2 || valid_num_ports % 2) {
441                 printf("Current valid ports number is %u\n", valid_num_ports);
442                 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
443         }
444
445         mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
446                                        MBUF_SIZE, MBUF_CACHE_SIZE,
447                                        sizeof(struct rte_pktmbuf_pool_private),
448                                        rte_pktmbuf_pool_init, NULL,
449                                        rte_pktmbuf_init, NULL,
450                                        rte_socket_id(), 0);
451         if (mbuf_pool == NULL)
452                 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
453
454         /* initialize all ports */
455         for (portid = 0; portid < nb_ports; portid++) {
456                 /* skip ports that are not enabled */
457                 if ((enabled_port_mask & (1 << portid)) == 0) {
458                         printf("\nSkipping disabled port %d\n", portid);
459                         continue;
460                 }
461                 if (port_init(portid, mbuf_pool) != 0)
462                         rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
463         }
464
465         /* call lcore_main() on every slave lcore */
466         i = 0;
467         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
468                 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
469         }
470         /* call on master too */
471         (void) lcore_main((void*)i);
472
473         return 0;
474 }