3f919b4adb0b012df85b3e216f7a79c688dcff68
[o-du/phy.git] / fhi_lib / lib / ethernet / ethdi.c
1 /******************************************************************************
2 *
3 *   Copyright (c) 2019 Intel.
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 *
17 *******************************************************************************/
18
19
20 /**
21  * @brief This file has all definitions for the Ethernet Data Interface Layer
22  * @file ethdi.c
23  * @ingroup group_lte_source_auxlib
24  * @author Intel Corporation
25  **/
26
27 #define _GNU_SOURCE
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdint.h>
31 #include <errno.h>
32 #include <sys/queue.h>
33 #include <err.h>
34 #include <assert.h>
35 #include <linux/limits.h>
36 #include <sys/types.h>
37 #include <stdlib.h>
38 #include <sys/time.h>
39 #include <time.h>
40 #include <unistd.h>
41
42 #include <rte_config.h>
43 #include <rte_common.h>
44 #include <rte_log.h>
45 #include <rte_memory.h>
46 #include <rte_memcpy.h>
47 #include <rte_memzone.h>
48 #include <rte_eal.h>
49 #include <rte_per_lcore.h>
50 #include <rte_launch.h>
51 #include <rte_atomic.h>
52 #include <rte_cycles.h>
53 #include <rte_prefetch.h>
54 #include <rte_lcore.h>
55 #include <rte_per_lcore.h>
56 #include <rte_branch_prediction.h>
57 #include <rte_interrupts.h>
58 #include <rte_pci.h>
59 #include <rte_debug.h>
60 #include <rte_ethdev.h>
61 #include <rte_ring.h>
62 #include <rte_mbuf.h>
63 #include <rte_timer.h>
64
65 #include "ethernet.h"
66 #include "ethdi.h"
67 #include "xran_fh_o_du.h"
68 #include "xran_mlog_lnx.h"
69 #include "xran_printf.h"
70
71 #include "../src/xran_lib_mlog_tasks_id.h"
72
73 #define BURST_RX_IO_SIZE 48
74
75 struct xran_ethdi_ctx g_ethdi_ctx = { 0 };
76 enum xran_if_state xran_if_current_state = XRAN_STOPPED;
77
78 struct rte_mbuf *xran_ethdi_mbuf_alloc(void)
79 {
80     return rte_pktmbuf_alloc(_eth_mbuf_pool);
81 }
82
83 int xran_ethdi_mbuf_send(struct rte_mbuf *mb, uint16_t ethertype)
84 {
85     struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
86     int res = 0;
87
88     mb->port = ctx->io_cfg.port[ETHDI_UP_VF];
89     xran_add_eth_hdr_vlan(&ctx->entities[ID_RU], ethertype, mb, ctx->up_vtag);
90
91     res = xran_enqueue_mbuf(mb, ctx->tx_ring[ETHDI_UP_VF]);
92     return res;
93 }
94
95 int xran_ethdi_mbuf_send_cp(struct rte_mbuf *mb, uint16_t ethertype)
96 {
97     struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
98     int res = 0;
99
100     mb->port = ctx->io_cfg.port[ETHDI_CP_VF];
101     xran_add_eth_hdr_vlan(&ctx->entities[ID_RU], ethertype, mb, ctx->cp_vtag);
102
103     res = xran_enqueue_mbuf(mb, ctx->tx_ring[ETHDI_CP_VF]);
104     return res;
105 }
106 #if 0
107 void xran_ethdi_stop_tx()
108 {
109     struct xran_ethdi_ctx *const ctx = xran_ethdi_get_ctx();
110     rte_timer_stop_sync(&ctx->timer_tx);
111 }
112 #endif
113
114 struct {
115     uint16_t ethertype;
116     ethertype_handler fn;
117 } xran_ethertype_handlers[] = {
118     { ETHER_TYPE_ETHDI, NULL },
119     { ETHER_TYPE_ECPRI, NULL },
120     { ETHER_TYPE_START_TX, NULL }
121 };
122
123
124
125 int xran_register_ethertype_handler(uint16_t ethertype, ethertype_handler callback)
126 {
127     int i;
128
129     for (i = 0; i < RTE_DIM(xran_ethertype_handlers); ++i)
130         if (xran_ethertype_handlers[i].ethertype == ethertype) {
131             xran_ethertype_handlers[i].fn = callback;
132
133             return 1;
134         }
135
136     elog("support for ethertype %u not found", ethertype);
137
138     return 0;
139 }
140
141 int xran_handle_ether(uint16_t ethertype, struct rte_mbuf *pkt, uint64_t rx_time)
142 {
143     int i;
144
145     for (i = 0; i < RTE_DIM(xran_ethertype_handlers); ++i)
146         if (xran_ethertype_handlers[i].ethertype == ethertype)
147             if (xran_ethertype_handlers[i].fn)
148                 return xran_ethertype_handlers[i].fn(pkt, rx_time);
149
150     wlog("Packet with unrecognized ethertype '%.4X' dropped", ethertype);
151
152     return 0;
153 };
154
155
156 /* Process vlan tag. Cut the ethernet header. Call the etherype handlers. */
157 int xran_ethdi_filter_packet(struct rte_mbuf *pkt, uint64_t rx_time)
158 {
159     struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
160
161 #ifdef VLAN_SUPPORT
162     if (rte_vlan_strip(pkt) == 0) {
163         if (pkt->vlan_tci == ctx->cp_vtag) {
164             dlog("VLAN tci matches %d", pkt->vlan_tci);
165         } else {
166             wlog("packet with wrong VLAN tag %d, dropping",
167                     pkt->vlan_tci);
168             return 0;
169         }
170     } else
171         dlog("Packet not vlan tagged");
172 #endif
173
174     const struct ether_hdr *eth_hdr = rte_pktmbuf_mtod(pkt, void *);
175
176 #if defined(DPDKIO_DEBUG) && DPDKIO_DEBUG > 1
177     nlog("*** processing RX'ed packet of size %d ***",
178             rte_pktmbuf_data_len(pkt));
179     /* TODO: just dump ethernet header in readable format? */
180 #endif
181
182 #if defined(DPDKIO_DEBUG) && DPDKIO_DEBUG > 1
183     {
184         char dst[ETHER_ADDR_FMT_SIZE] = "(empty)";
185         char src[ETHER_ADDR_FMT_SIZE] = "(empty)";
186
187         ether_format_addr(dst, sizeof(dst), &eth_hdr->d_addr);
188         ether_format_addr(src, sizeof(src), &eth_hdr->s_addr);
189         nlog("src: %s dst: %s ethertype: %.4X", dst, src,
190                 rte_be_to_cpu_16(eth_hdr->ether_type));
191     }
192 #endif
193
194     /* Cut out the ethernet header. It's not needed anymore. */
195     if (rte_pktmbuf_adj(pkt, sizeof(*eth_hdr)) == NULL) {
196         wlog("Packet too short, dropping");
197         return 0;
198     }
199
200
201     return xran_handle_ether(rte_be_to_cpu_16(eth_hdr->ether_type), pkt, rx_time);
202 }
203
204 #if 0
205 //-------------------------------------------------------------------------------------------
206 /** @ingroup xran
207  *
208  *  @param[in]  port - DPDK ETH port id
209  *
210  *  @return  void
211  *
212  *  @description
213  *  Prints statistics of usage of DPDK port
214  *
215 **/
216 //-------------------------------------------------------------------------------------------
217 void xran_ethdi_ports_stats(void)
218 {
219     struct rte_eth_stats stats;
220     struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
221     int32_t i = 0;
222
223     for(i = 0; i < ETHDI_VF_MAX; i++){
224         /* Get stats (extended stats includes common stats) */
225         rte_eth_stats_get(ctx->io_cfg.port[i], &stats);
226         printf("DPDK stats:\n");
227         printf("** Port %hhu **\n", ctx->io_cfg.port[i]);
228         printf("ierrors:\t%lu\n",   stats.ierrors);
229         printf("oerrors:\t%lu\n",   stats.oerrors);
230         printf("ipackets:\t%lu\n",  stats.ipackets);
231         printf("opackets:\t%lu\n",  stats.opackets);
232         printf("imissed:\t%lu\n",   stats.imissed);
233         printf("rx_nombuf:\t%lu\n", stats.rx_nombuf);
234     }
235     return ;
236 }
237 #endif
238 /* Check the link status of all ports in up to 9s, and print them finally */
239 static void check_port_link_status(uint8_t portid)
240 {
241 #define CHECK_INTERVAL 100 /* 100ms */
242 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
243     uint8_t count, all_ports_up, print_flag = 0;
244     struct rte_eth_link link;
245
246     printf("\nChecking link status");
247     fflush(stdout);
248     for (count = 0; count <= MAX_CHECK_TIME; count++) {
249         all_ports_up = 1;
250         memset(&link, 0, sizeof(link));
251         rte_eth_link_get_nowait(portid, &link);
252
253         /* print link status if flag set */
254         if (print_flag == 1) {
255             if (link.link_status)
256                 printf("Port %d Link Up - speed %u "
257                         "Mbps - %s\n", (uint8_t)portid,
258                         (unsigned)link.link_speed,
259                         (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
260                         ("full-duplex") : ("half-duplex\n"));
261             else
262                 printf("Port %d Link Down\n",
263                         (uint8_t)portid);
264         }
265         /* clear all_ports_up flag if any link down */
266         if (link.link_status == ETH_LINK_DOWN) {
267             all_ports_up = 0;
268             break;
269         }
270         /* after finally printing all link status, get out */
271         if (print_flag == 1)
272             break;
273
274         if (all_ports_up == 0) {
275             printf(".");
276             fflush(stdout);
277             rte_delay_ms(CHECK_INTERVAL);
278         }
279
280         /* set the print_flag if all ports up or timeout */
281         if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
282             print_flag = 1;
283             printf(" ... done\n");
284         }
285     }
286 }
287
288
289 int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg,
290     int *lcore_id, struct ether_addr *p_lls_cu_addr, struct ether_addr *p_ru_addr,
291     uint16_t cp_vlan, uint16_t up_vlan)
292 {
293     uint16_t port[2] = {0xffff, 0xffff};
294     struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
295     int i;
296     char core_mask[20];
297     char bbdev_wdev[32] = "";
298     char bbdev_vdev[32] = "";
299
300     char *argv[] = { name, "-c ffffffff", "-n2", "--socket-mem=8192", "--proc-type=auto",
301         "--file-prefix", name, "-w", "0000:00:00.0", bbdev_wdev, bbdev_vdev};
302
303     if (io_cfg == NULL)
304         return 0;
305     if(io_cfg->bbdev_mode != XRAN_BBDEV_NOT_USED){
306         printf("BBDEV_FEC_ACCL_NR5G\n");
307         if (io_cfg->bbdev_mode == XRAN_BBDEV_MODE_HW_ON){
308             // hw-accelerated bbdev
309             printf("hw-accelerated bbdev %s\n", io_cfg->bbdev_dev[0]);
310             sprintf(bbdev_wdev, "-w %s", io_cfg->bbdev_dev[0]);
311         } else if (io_cfg->bbdev_mode == XRAN_BBDEV_MODE_HW_OFF){
312             // hw-accelerated bbdev disable
313             if(io_cfg->bbdev_dev[0]){
314                 printf("hw-accelerated bbdev disable %s\n", io_cfg->bbdev_dev[0]);
315                 sprintf(bbdev_wdev, "-b %s", io_cfg->bbdev_dev[0]);
316             }
317             sprintf(bbdev_wdev, "%s", "--vdev=baseband_turbo_sw");
318         } else {
319             rte_panic("Cannot init DPDK incorrect [bbdev_mode %d]\n", io_cfg->bbdev_mode);
320         }
321     }
322
323     snprintf(core_mask, sizeof(core_mask), "-c%x",
324             (1 << io_cfg->core) |
325             (1 << io_cfg->system_core) |
326             (1 << io_cfg->pkt_proc_core) |
327             (1 << io_cfg->pkt_aux_core) |
328             (1 << io_cfg->timing_core));
329
330     ctx->io_cfg = *io_cfg;
331     ctx->ping_state           = PING_IDLE;
332     ctx->known_peers          = 1;
333     ctx->busy_poll_till = rte_rdtsc();
334     ctx->cp_vtag = cp_vlan;
335     ctx->up_vtag = up_vlan;
336
337     for (i = 0; i <= ID_BROADCAST; i++)     /* Initialize all as broadcast */
338         memset(&ctx->entities[i], 0xFF, sizeof(ctx->entities[0]));
339
340     printf("%s: Calling rte_eal_init:", __FUNCTION__);
341     for (i = 0; i < RTE_DIM(argv); i++)
342     {
343         printf("%s ", argv[i]);
344     }
345     printf("\n");
346
347
348     /* This will return on system_core, which is not necessarily the
349      * one we're on right now. */
350     if (rte_eal_init(RTE_DIM(argv), argv) < 0)
351         rte_panic("Cannot init EAL: %s\n", rte_strerror(rte_errno));
352
353     xran_init_mbuf_pool();
354
355
356     /* Timers. */
357     rte_timer_subsystem_init();
358     rte_timer_init(&ctx->timer_ping);
359     rte_timer_init(&ctx->timer_sync);
360     rte_timer_init(&ctx->timer_tx);
361
362     *lcore_id = rte_get_next_lcore(rte_lcore_id(), 0, 0);
363
364     PANIC_ON(*lcore_id == RTE_MAX_LCORE, "out of lcores for io_loop()");
365
366     if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
367         for (i = 0; i < ETHDI_VF_MAX; i ++){
368             if(io_cfg->dpdk_dev[i]){
369                 if (rte_eth_dev_attach(io_cfg->dpdk_dev[i], &port[i]) != 0 ||
370                     rte_eth_dev_count_avail() == 0)
371                     errx(1, "Network port doesn't exist.");
372                 xran_init_port(port[i], p_lls_cu_addr);
373             } else {
374                 printf("no DPDK port provided\n");
375             }
376             if(i==0){
377                 ctx->tx_ring[i] = rte_ring_create("tx_ring_up", NUM_MBUFS,
378                     rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ);
379                 ctx->rx_ring[i] = rte_ring_create("rx_ring_up", NUM_MBUFS,
380                     rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ);
381                 ctx->pkt_dump_ring[i] = rte_ring_create("pkt_dump_ring_up", NUM_MBUFS,
382                     rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ);
383             }else {
384                 ctx->tx_ring[i] = rte_ring_create("tx_ring_cp", NUM_MBUFS,
385                     rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ);
386                 ctx->rx_ring[i] = rte_ring_create("rx_ring_cp", NUM_MBUFS,
387                     rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ);
388                 ctx->pkt_dump_ring[i] = rte_ring_create("pkt_dump_ring_cp", NUM_MBUFS,
389                     rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ);
390             }
391             if(io_cfg->dpdk_dev[i])
392                 check_port_link_status(port[i]);
393         }
394     } else {
395         rte_panic("ethdi_dpdk_io_loop() failed to start  with RTE_PROC_SECONDARY\n");
396     }
397     PANIC_ON(ctx->tx_ring == NULL, "failed to allocate tx ring");
398     PANIC_ON(ctx->rx_ring == NULL, "failed to allocate rx ring");
399     PANIC_ON(ctx->pkt_dump_ring == NULL, "failed to allocate pkt dumping ring");
400     for (i = 0; i < ETHDI_VF_MAX; i++){
401         ctx->io_cfg.port[i] = port[i];
402         print_dbg("port_id 0x%04x\n", ctx->io_cfg.port[i]);
403     }
404
405     if(io_cfg->dpdk_dev[ETHDI_UP_VF]){
406         rte_eth_macaddr_get(port[ETHDI_UP_VF], &ctx->entities[io_cfg->id]);
407         ether_addr_copy(p_ru_addr,  &ctx->entities[ID_RU]);
408     }
409
410     return 1;
411 }
412
413 static inline uint16_t xran_tx_from_ring(int port, struct rte_ring *r)
414 {
415     struct rte_mbuf *mbufs[BURST_SIZE];
416     uint16_t dequeued, sent = 0;
417     uint32_t remaining;
418     int i;
419     long t1 = MLogTick();
420
421     dequeued = rte_ring_dequeue_burst(r, (void **)mbufs, BURST_SIZE,
422             &remaining);
423     if (!dequeued)
424         return 0;   /* Nothing to send. */
425
426     while (1) {     /* When tx queue is full it is trying again till succeed */
427         t1 = MLogTick();
428         sent += rte_eth_tx_burst(port, 0, &mbufs[sent], dequeued - sent);
429         MLogTask(PID_RADIO_ETH_TX_BURST, t1, MLogTick());
430
431         if (sent == dequeued)
432             return remaining;
433     }
434 }
435
436 int32_t process_dpdk_io(void)
437 {
438     struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
439     const struct xran_io_loop_cfg *const cfg = &(xran_ethdi_get_ctx()->io_cfg);
440     const int port[ETHDI_VF_MAX] = {cfg->port[ETHDI_UP_VF], cfg->port[ETHDI_CP_VF]};
441     int port_id = 0;
442
443     for (port_id = 0; port_id < ETHDI_VF_MAX; port_id++){
444         struct rte_mbuf *mbufs[BURST_RX_IO_SIZE];
445         if(port[port_id] == 0xFF);
446         /* RX */
447         const uint16_t rxed = rte_eth_rx_burst(port[port_id], 0, mbufs, BURST_RX_IO_SIZE);
448         if (rxed != 0){
449             unsigned enq_n = 0;
450             long t1 = MLogTick();
451             enq_n =  rte_ring_enqueue_burst(ctx->rx_ring[port_id], (void*)mbufs, rxed, NULL);
452             if(rxed - enq_n)
453                 rte_panic("error enq\n");
454             MLogTask(PID_RADIO_RX_VALIDATE, t1, MLogTick());
455         }
456
457         /* TX */
458         const uint16_t sent = xran_tx_from_ring(port[port_id], ctx->tx_ring[port_id]);
459
460         if (XRAN_STOPPED == xran_if_current_state)
461             return -1;
462     }
463
464     if (XRAN_STOPPED == xran_if_current_state)
465             return -1;
466
467     return 0;
468 }
469
470 #if 0
471 static inline void xran_process_rx_burst(struct rte_mbuf *mbufs[], uint16_t n_mbufs,
472         uint64_t rx_time)
473 {
474         int i;
475
476         if (!n_mbufs)
477             return;
478
479         for (i = 0; i < n_mbufs; ++i)
480         {
481             if (xran_ethdi_filter_packet(mbufs[i], rx_time) == MBUF_FREE)
482                 rte_pktmbuf_free(mbufs[i]);
483         }
484
485 #ifdef DPDKIO_LATENCY_DEBUG
486        struct timeval tv_now, tv_diff;
487
488        gettimeofday(&tv_now, NULL);
489        if (n_mbufs > 1)
490            nlog("Warning - received %d mbufs in a row", n_mbufs);
491
492        timersub(&tv_now, &rx_time, &tv_diff);
493        nlog("rx processing took %d usec", tv_diff.tv_usec);
494 #endif
495 }
496
497 /*
498  * This is the main DPDK-IO loop.
499  * This will sleep if there's no packets incoming and there's
500  * no work enqueued, sleep lenth is defined in IDLE_SLEEP_MICROSECS
501  */
502 int xran_ethdi_dpdk_io_loop(void *io_loop_cfg)
503 {
504     struct sched_param sched_param;
505     int res = 0;
506     struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
507     const struct xran_io_loop_cfg *const cfg = &(xran_ethdi_get_ctx()->io_cfg);
508     const int port[ETHDI_VF_MAX] = {cfg->port[ETHDI_UP_VF], cfg->port[ETHDI_CP_VF]};
509
510     printf("%s [PORT: %d %d] [CPU %2d] [PID: %6d]\n", __FUNCTION__, port[ETHDI_UP_VF], port[ETHDI_CP_VF] , rte_lcore_id(), getpid());
511
512     printf("%s [CPU %2d] [PID: %6d]\n", __FUNCTION__,  rte_lcore_id(), getpid());
513     sched_param.sched_priority = XRAN_THREAD_DEFAULT_PRIO;
514     if ((res = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param))) {
515         printf("priority is not changed: coreId = %d, result1 = %d\n",rte_lcore_id(), res);
516     }
517
518     for (;;){
519         if(process_dpdk_io()!=0)
520             break;
521     }
522
523     fflush(stderr);
524     fflush(stdout);
525     puts("IO loop finished");
526
527     return 0;
528 }
529 #endif
530