Advertisement
hemant4047

DPDK HW Timestamping (Multi-process)

May 17th, 2024
221
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.45 KB | Software | 0 0
  1. /* SPDX-License-Identifier: BSD-3-Clause
  2.  * Copyright(c) 2010-2014 Intel Corporation
  3.  */
  4.  
  5. /*
  6.  * Sample application demonstrating how to do packet I/O in a multi-process
  7.  * environment. The same code can be run as a primary process and as a
  8.  * secondary process, just with a different proc-id parameter in each case
  9.  * (apart from the EAL flag to indicate a secondary process).
  10.  *
  11.  * Each process will read from the same ports, given by the port-mask
  12.  * parameter, which should be the same in each case, just using a different
  13.  * queue per port as determined by the proc-id parameter.
  14.  */
  15.  
  16. /**
  17.  * NIC: Mellanox ConnectX-6 Dx
  18.  * driver: mlx5_core
  19.  *
  20.  * Modified to use HW timestamping
  21.  * each instance attempts to calculate 3 params
  22.  * nic_freq
  23.  * base_time
  24.  * base_clock - cycle count at base_time
  25.  *
  26.  * Observation, error if this is done in a secondary process
  27.  */
  28.  
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <stdint.h>
  32. #include <stdlib.h>
  33. #include <stdarg.h>
  34. #include <errno.h>
  35. #include <sys/queue.h>
  36. #include <getopt.h>
  37. #include <signal.h>
  38. #include <inttypes.h>
  39.  
  40. #include <rte_common.h>
  41. #include <rte_log.h>
  42. #include <rte_memory.h>
  43. #include <rte_launch.h>
  44. #include <rte_eal.h>
  45. #include <rte_per_lcore.h>
  46. #include <rte_lcore.h>
  47. #include <rte_branch_prediction.h>
  48. #include <rte_debug.h>
  49. #include <rte_interrupts.h>
  50. #include <rte_ether.h>
  51. #include <rte_ethdev.h>
  52. #include <rte_mempool.h>
  53. #include <rte_memcpy.h>
  54. #include <rte_mbuf.h>
  55. #include <rte_string_fns.h>
  56. #include <rte_cycles.h>
  57.  
  58. #define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
  59. #define RTE_LOGTYPE_MLNX_DPDK RTE_LOGTYPE_USER1
  60.  
  61. #define NB_MBUFS 64 * 1024 /* use 64k mbufs */
  62. #define MBUF_CACHE_SIZE 256
  63. #define PKT_BURST 32
  64. #define RX_RING_SIZE 1024
  65. #define TX_RING_SIZE 1024
  66.  
  67. #define PARAM_PROC_ID "proc-id"
  68. #define PARAM_NUM_PROCS "num-procs"
  69.  
  70. static int hwts_dynfield_offset = -1; // for hw timestamping.
  71.  
  72. /* NIC clock frequency */
  73. double nic_freq = 0; // cycles per ns
  74.  
  75. /* Base Time and base clock value (HW timestamping) */
  76. uint64_t base_time_ns = 0;
  77. uint64_t base_nic_clock = 0;
  78.  
  79. /**
  80.  * Returns pkt timestamp (hw clock value)
  81.  */
  82. static inline rte_mbuf_timestamp_t *
  83. hwts_field(struct rte_mbuf *mbuf)
  84. {
  85.     return RTE_MBUF_DYNFIELD(mbuf,
  86.                              hwts_dynfield_offset, rte_mbuf_timestamp_t *);
  87. }
  88.  
  89. /**
  90.  * Calculate NIC's clock frequency
  91.  */
  92. static inline int
  93. calc_hw_freq(uint16_t portid)
  94. {
  95.     uint64_t start, end;
  96.     int retval = rte_eth_read_clock(portid, &start);
  97.     if (retval != 0)
  98.     {
  99.         RTE_LOG(ERR, MLNX_DPDK, "Cannot read device clock, err: %s\n",
  100.                 rte_strerror(retval));
  101.         return 1;
  102.     }
  103.     rte_delay_ms(100);
  104.     retval = rte_eth_read_clock(portid, &end);
  105.     if (retval != 0)
  106.     {
  107.         RTE_LOG(ERR, MLNX_DPDK, "Cannot read device clock, err: %s\n",
  108.                 rte_strerror(retval));
  109.         return 1;
  110.     }
  111.     nic_freq = (end - start) / (double)1e8;
  112.     return 0;
  113. }
  114.  
  115. static inline int
  116. calc_base_clock_params(void)
  117. {
  118.     struct timespec cur_time;
  119.     clock_gettime(CLOCK_REALTIME, &cur_time);
  120.     base_time_ns = cur_time.tv_sec * 1e9 + cur_time.tv_nsec;
  121.     int retval = rte_eth_read_clock(0, &base_nic_clock);
  122.     if (retval != 0)
  123.     {
  124.         RTE_LOG(ERR, MLNX_DPDK, "Cannot read device clock, err: %s\n",
  125.                 rte_strerror(retval));
  126.         return 1;
  127.     }
  128.     return 0;
  129. }
  130.  
  131. /* for each lcore, record the elements of the ports array to use */
  132. struct lcore_ports
  133. {
  134.     unsigned start_port;
  135.     unsigned num_ports;
  136. };
  137.  
  138. /* structure to record the rx and tx packets. Put two per cache line as ports
  139.  * used in pairs */
  140. struct port_stats
  141. {
  142.     unsigned rx;
  143.     unsigned tx;
  144.     unsigned drop;
  145. } __rte_aligned(RTE_CACHE_LINE_SIZE / 2);
  146.  
  147. static int proc_id = -1;
  148. static unsigned num_procs = 0;
  149.  
  150. static uint16_t ports[RTE_MAX_ETHPORTS];
  151. static unsigned num_ports = 0;
  152.  
  153. static struct lcore_ports lcore_ports[RTE_MAX_LCORE];
  154. static struct port_stats pstats[RTE_MAX_ETHPORTS];
  155.  
  156. /* prints the usage statement and quits with an error message */
  157. static void
  158. smp_usage(const char *prgname, const char *errmsg)
  159. {
  160.     printf("\nError: %s\n", errmsg);
  161.     printf("\n%s [EAL options] -- -p <port mask> "
  162.            "--" PARAM_NUM_PROCS " <n>"
  163.            " --" PARAM_PROC_ID " <id>\n"
  164.            "-p         : a hex bitmask indicating what ports are to be used\n"
  165.            "--num-procs: the number of processes which will be used\n"
  166.            "--proc-id  : the id of the current process (id < num-procs)\n"
  167.            "\n",
  168.            prgname);
  169.     exit(1);
  170. }
  171.  
  172. /* signal handler configured for SIGTERM and SIGINT to print stats on exit */
  173. static void
  174. print_stats(int signum)
  175. {
  176.     unsigned i;
  177.     printf("\nExiting on signal %d\n\n", signum);
  178.     for (i = 0; i < num_ports; i++)
  179.     {
  180.         const uint8_t p_num = ports[i];
  181.         printf("Port %u: RX - %u, TX - %u, Drop - %u\n", (unsigned)p_num,
  182.                pstats[p_num].rx, pstats[p_num].tx, pstats[p_num].drop);
  183.     }
  184.     exit(0);
  185. }
  186.  
  187. /* Parse the argument given in the command line of the application */
  188. static int
  189. smp_parse_args(int argc, char **argv)
  190. {
  191.     int opt, ret;
  192.     char **argvopt;
  193.     int option_index;
  194.     uint16_t i, port_mask = 0;
  195.     char *prgname = argv[0];
  196.     static struct option lgopts[] = {
  197.         {PARAM_NUM_PROCS, 1, 0, 0},
  198.         {PARAM_PROC_ID, 1, 0, 0},
  199.         {NULL, 0, 0, 0}};
  200.  
  201.     argvopt = argv;
  202.  
  203.     while ((opt = getopt_long(argc, argvopt, "p:",
  204.                               lgopts, &option_index)) != EOF)
  205.     {
  206.  
  207.         switch (opt)
  208.         {
  209.         case 'p':
  210.             port_mask = strtoull(optarg, NULL, 16);
  211.             break;
  212.             /* long options */
  213.         case 0:
  214.             if (strncmp(lgopts[option_index].name, PARAM_NUM_PROCS, 8) == 0)
  215.                 num_procs = atoi(optarg);
  216.             else if (strncmp(lgopts[option_index].name, PARAM_PROC_ID, 7) == 0)
  217.                 proc_id = atoi(optarg);
  218.             break;
  219.  
  220.         default:
  221.             smp_usage(prgname, "Cannot parse all command-line arguments\n");
  222.         }
  223.     }
  224.  
  225.     if (optind >= 0)
  226.         argv[optind - 1] = prgname;
  227.  
  228.     if (proc_id < 0)
  229.         smp_usage(prgname, "Invalid or missing proc-id parameter\n");
  230.     if (rte_eal_process_type() == RTE_PROC_PRIMARY && num_procs == 0)
  231.         smp_usage(prgname, "Invalid or missing num-procs parameter\n");
  232.     if (port_mask == 0)
  233.         smp_usage(prgname, "Invalid or missing port mask\n");
  234.  
  235.     /* get the port numbers from the port mask */
  236.     RTE_ETH_FOREACH_DEV(i)
  237.     if (port_mask & (1 << i))
  238.         ports[num_ports++] = (uint8_t)i;
  239.  
  240.     ret = optind - 1;
  241.     optind = 1; /* reset getopt lib */
  242.  
  243.     return ret;
  244. }
  245.  
  246. /*
  247.  * Initialises a given port using global settings and with the rx buffers
  248.  * coming from the mbuf_pool passed as parameter
  249.  */
  250. static inline int
  251. smp_port_init(uint16_t port, struct rte_mempool *mbuf_pool,
  252.               uint16_t num_queues)
  253. {
  254.     struct rte_eth_conf port_conf = {
  255.         .rxmode = {
  256.             .mq_mode = RTE_ETH_MQ_RX_RSS,
  257.             .offloads = RTE_ETH_RX_OFFLOAD_CHECKSUM,
  258.         },
  259.         .rx_adv_conf = {
  260.             .rss_conf = {
  261.                 .rss_key = NULL,
  262.                 .rss_hf = RTE_ETH_RSS_IP,
  263.             },
  264.         },
  265.         .txmode = {
  266.             .mq_mode = RTE_ETH_MQ_TX_NONE,
  267.         }};
  268.     const uint16_t rx_rings = num_queues, tx_rings = num_queues;
  269.     struct rte_eth_dev_info info;
  270.     struct rte_eth_rxconf rxq_conf;
  271.     struct rte_eth_txconf txq_conf;
  272.     int retval;
  273.     uint16_t q;
  274.     uint16_t nb_rxd = RX_RING_SIZE;
  275.     uint16_t nb_txd = TX_RING_SIZE;
  276.     uint64_t rss_hf_tmp;
  277.  
  278.     if (rte_eal_process_type() == RTE_PROC_SECONDARY)
  279.         return 0;
  280.  
  281.     if (!rte_eth_dev_is_valid_port(port))
  282.         return -1;
  283.  
  284.     printf("# Initialising port %u... ", port);
  285.     fflush(stdout);
  286.  
  287.     retval = rte_eth_dev_info_get(port, &info);
  288.     if (retval != 0)
  289.     {
  290.         printf("Error during getting device (port %u) info: %s\n",
  291.                port, strerror(-retval));
  292.         return retval;
  293.     }
  294.  
  295.     info.default_rxconf.rx_drop_en = 1;
  296.  
  297.     if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE)
  298.         port_conf.txmode.offloads |=
  299.             RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;
  300.  
  301.     // Hardware Timestamping
  302.     if (!(info.rx_offload_capa & RTE_ETH_RX_OFFLOAD_TIMESTAMP))
  303.     {
  304.         rte_panic("\nERROR: Port %u does not support hardware timestamping\n", port);
  305.         // return -1;
  306.     }
  307.     port_conf.rxmode.offloads |= RTE_ETH_RX_OFFLOAD_TIMESTAMP;
  308.     rte_mbuf_dyn_rx_timestamp_register(&hwts_dynfield_offset, NULL);
  309.     if (hwts_dynfield_offset < 0)
  310.     {
  311.         printf("ERROR: Failed to register timestamp field\n");
  312.         return -rte_errno;
  313.     }
  314.  
  315.     rss_hf_tmp = port_conf.rx_adv_conf.rss_conf.rss_hf;
  316.     port_conf.rx_adv_conf.rss_conf.rss_hf &= info.flow_type_rss_offloads;
  317.     if (port_conf.rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp)
  318.     {
  319.         printf("Port %u modified RSS hash function based on hardware support,"
  320.                "requested:%#" PRIx64 " configured:%#" PRIx64 "\n",
  321.                port,
  322.                rss_hf_tmp,
  323.                port_conf.rx_adv_conf.rss_conf.rss_hf);
  324.     }
  325.  
  326.     retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
  327.     if (retval == -EINVAL)
  328.     {
  329.         printf("Port %u configuration failed. Re-attempting with HW checksum disabled.\n",
  330.                port);
  331.         port_conf.rxmode.offloads &= ~(RTE_ETH_RX_OFFLOAD_CHECKSUM);
  332.         retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
  333.     }
  334.  
  335.     if (retval == -ENOTSUP)
  336.     {
  337.         printf("Port %u configuration failed. Re-attempting with HW RSS disabled.\n",
  338.                port);
  339.         port_conf.rxmode.mq_mode &= ~(RTE_ETH_MQ_RX_RSS);
  340.         retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
  341.     }
  342.  
  343.     if (retval < 0)
  344.         return retval;
  345.  
  346.     retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
  347.     if (retval < 0)
  348.         return retval;
  349.  
  350.     rxq_conf = info.default_rxconf;
  351.     rxq_conf.offloads = port_conf.rxmode.offloads;
  352.     for (q = 0; q < rx_rings; q++)
  353.     {
  354.         retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
  355.                                         rte_eth_dev_socket_id(port),
  356.                                         &rxq_conf,
  357.                                         mbuf_pool);
  358.         if (retval < 0)
  359.             return retval;
  360.     }
  361.  
  362.     txq_conf = info.default_txconf;
  363.     txq_conf.offloads = port_conf.txmode.offloads;
  364.     for (q = 0; q < tx_rings; q++)
  365.     {
  366.         retval = rte_eth_tx_queue_setup(port, q, nb_txd,
  367.                                         rte_eth_dev_socket_id(port),
  368.                                         &txq_conf);
  369.         if (retval < 0)
  370.             return retval;
  371.     }
  372.  
  373.     retval = rte_eth_promiscuous_enable(port);
  374.     if (retval != 0)
  375.         return retval;
  376.  
  377.     retval = rte_eth_dev_start(port);
  378.     if (retval < 0)
  379.         return retval;
  380.  
  381.     return 0;
  382. }
  383.  
  384. /* Goes through each of the lcores and calculates what ports should
  385.  * be used by that core. Fills in the global lcore_ports[] array.
  386.  */
  387. static void
  388. assign_ports_to_cores(void)
  389. {
  390.  
  391.     const unsigned int lcores = rte_lcore_count();
  392.     const unsigned port_pairs = num_ports / 2;
  393.     const unsigned pairs_per_lcore = port_pairs / lcores;
  394.     unsigned extra_pairs = port_pairs % lcores;
  395.     unsigned ports_assigned = 0;
  396.     unsigned i;
  397.  
  398.     RTE_LCORE_FOREACH(i)
  399.     {
  400.         lcore_ports[i].start_port = ports_assigned;
  401.         lcore_ports[i].num_ports = pairs_per_lcore * 2;
  402.         if (extra_pairs > 0)
  403.         {
  404.             lcore_ports[i].num_ports += 2;
  405.             extra_pairs--;
  406.         }
  407.         ports_assigned += lcore_ports[i].num_ports;
  408.     }
  409. }
  410.  
  411. /* Main function used by the processing threads.
  412.  * Prints out some configuration details for the thread and then begins
  413.  * performing packet RX and TX.
  414.  */
  415. static int
  416. lcore_main(void *arg __rte_unused)
  417. {
  418.     const unsigned id = rte_lcore_id();
  419.     const unsigned start_port = lcore_ports[id].start_port;
  420.     const unsigned end_port = start_port + lcore_ports[id].num_ports;
  421.     const uint16_t q_id = (uint16_t)proc_id;
  422.     unsigned p, i;
  423.     char msgbuf[256];
  424.     int msgbufpos = 0;
  425.  
  426.     if (start_port == end_port)
  427.     {
  428.         printf("Lcore %u has nothing to do\n", id);
  429.         return 0;
  430.     }
  431.  
  432.     /* build up message in msgbuf before printing to decrease likelihood
  433.      * of multi-core message interleaving.
  434.      */
  435.     msgbufpos += snprintf(msgbuf, sizeof(msgbuf) - msgbufpos,
  436.                           "Lcore %u using ports ", id);
  437.     for (p = start_port; p < end_port; p++)
  438.     {
  439.         msgbufpos += snprintf(msgbuf + msgbufpos, sizeof(msgbuf) - msgbufpos,
  440.                               "%u ", (unsigned)ports[p]);
  441.     }
  442.     printf("%s\n", msgbuf);
  443.     printf("lcore %u using queue %u of each port\n", id, (unsigned)q_id);
  444.  
  445.     /* handle packet I/O from the ports, reading and writing to the
  446.      * queue number corresponding to our process number (not lcore id)
  447.      */
  448.  
  449.     for (;;)
  450.     {
  451.         struct rte_mbuf *buf[PKT_BURST];
  452.  
  453.         for (p = start_port; p < end_port; p++)
  454.         {
  455.             const uint8_t src = ports[p];
  456.             const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
  457.             const uint16_t rx_c = rte_eth_rx_burst(src, q_id, buf, PKT_BURST);
  458.             if (rx_c == 0)
  459.                 continue;
  460.             pstats[src].rx += rx_c;
  461.  
  462.             const uint16_t tx_c = rte_eth_tx_burst(dst, q_id, buf, rx_c);
  463.             pstats[dst].tx += tx_c;
  464.             if (tx_c != rx_c)
  465.             {
  466.                 pstats[dst].drop += (rx_c - tx_c);
  467.                 for (i = tx_c; i < rx_c; i++)
  468.                     rte_pktmbuf_free(buf[i]);
  469.             }
  470.         }
  471.     }
  472. }
  473.  
  474. /* Check the link status of all ports in up to 9s, and print them finally */
  475. static void
  476. check_all_ports_link_status(uint16_t port_num, uint32_t port_mask)
  477. {
  478. #define CHECK_INTERVAL 100 /* 100ms */
  479. #define MAX_CHECK_TIME 90  /* 9s (90 * 100ms) in total */
  480.     uint16_t portid;
  481.     uint8_t count, all_ports_up, print_flag = 0;
  482.     struct rte_eth_link link;
  483.     int ret;
  484.     char link_status_text[RTE_ETH_LINK_MAX_STR_LEN];
  485.  
  486.     printf("\nChecking link status");
  487.     fflush(stdout);
  488.     for (count = 0; count <= MAX_CHECK_TIME; count++)
  489.     {
  490.         all_ports_up = 1;
  491.         for (portid = 0; portid < port_num; portid++)
  492.         {
  493.             if ((port_mask & (1 << portid)) == 0)
  494.                 continue;
  495.             memset(&link, 0, sizeof(link));
  496.             ret = rte_eth_link_get_nowait(portid, &link);
  497.             if (ret < 0)
  498.             {
  499.                 all_ports_up = 0;
  500.                 if (print_flag == 1)
  501.                     printf("Port %u link get failed: %s\n",
  502.                            portid, rte_strerror(-ret));
  503.                 continue;
  504.             }
  505.             /* print link status if flag set */
  506.             if (print_flag == 1)
  507.             {
  508.                 rte_eth_link_to_str(link_status_text,
  509.                                     sizeof(link_status_text), &link);
  510.                 printf("Port %d %s\n", portid,
  511.                        link_status_text);
  512.                 continue;
  513.             }
  514.             /* clear all_ports_up flag if any link down */
  515.             if (link.link_status == RTE_ETH_LINK_DOWN)
  516.             {
  517.                 all_ports_up = 0;
  518.                 break;
  519.             }
  520.         }
  521.         /* after finally printing all link status, get out */
  522.         if (print_flag == 1)
  523.             break;
  524.  
  525.         if (all_ports_up == 0)
  526.         {
  527.             printf(".");
  528.             fflush(stdout);
  529.             rte_delay_ms(CHECK_INTERVAL);
  530.         }
  531.  
  532.         /* set the print_flag if all ports up or timeout */
  533.         if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1))
  534.         {
  535.             print_flag = 1;
  536.             printf("done\n");
  537.         }
  538.     }
  539. }
  540.  
  541. /* Main function.
  542.  * Performs initialisation and then calls the lcore_main on each core
  543.  * to do the packet-processing work.
  544.  */
  545. int main(int argc, char **argv)
  546. {
  547.     static const char *_SMP_MBUF_POOL = "SMP_MBUF_POOL";
  548.     int ret;
  549.     unsigned i;
  550.     enum rte_proc_type_t proc_type;
  551.     struct rte_mempool *mp;
  552.  
  553.     /* set up signal handlers to print stats on exit */
  554.     signal(SIGINT, print_stats);
  555.     signal(SIGTERM, print_stats);
  556.  
  557.     /* initialise the EAL for all */
  558.     ret = rte_eal_init(argc, argv);
  559.     if (ret < 0)
  560.         rte_exit(EXIT_FAILURE, "Cannot init EAL\n");
  561.     argc -= ret;
  562.     argv += ret;
  563.  
  564.     /* determine the NIC devices available */
  565.     if (rte_eth_dev_count_avail() == 0)
  566.         rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
  567.  
  568.     /* parse application arguments (those after the EAL ones) */
  569.     smp_parse_args(argc, argv);
  570.  
  571.     proc_type = rte_eal_process_type();
  572.     mp = (proc_type == RTE_PROC_SECONDARY) ? rte_mempool_lookup(_SMP_MBUF_POOL) : rte_pktmbuf_pool_create(_SMP_MBUF_POOL, NB_MBUFS, MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
  573.     if (mp == NULL)
  574.         rte_exit(EXIT_FAILURE, "Cannot get memory pool for buffers\n");
  575.  
  576.     /* Primary instance initialized. 8< */
  577.     if (num_ports & 1)
  578.         rte_exit(EXIT_FAILURE, "Application must use an even number of ports\n");
  579.     for (i = 0; i < num_ports; i++)
  580.     {
  581.         if (proc_type == RTE_PROC_PRIMARY)
  582.             if (smp_port_init(ports[i], mp, (uint16_t)num_procs) < 0)
  583.                 rte_exit(EXIT_FAILURE, "Error initialising ports\n");
  584.     }
  585.     /* >8 End of primary instance initialization. */
  586.  
  587.     if (proc_type == RTE_PROC_PRIMARY)
  588.         check_all_ports_link_status((uint8_t)num_ports, (~0x0));
  589.  
  590.     RTE_LOG(INFO, APP, "Finished Process Init.\n");
  591.  
  592.     // calculating freq and base time params for HW timestamping
  593.     uint16_t portid;
  594.     RTE_ETH_FOREACH_DEV(portid)
  595.     {
  596.         if (nic_freq == 0 && calc_hw_freq(portid) != 0)
  597.             rte_exit(EXIT_FAILURE, "Cannot configure dpdk system\n");
  598.     }
  599.     double avg_freq = 0;
  600.     for (int i = 0; i < 10; i++)
  601.     {
  602.         if (calc_hw_freq(0) != 0)
  603.             rte_exit(EXIT_FAILURE, "Cannot configure dpdk system\n");
  604.         // printf("iteration %d, hw freq: %.9lf\n", i, nic_freq);
  605.         avg_freq += nic_freq;
  606.     }
  607.     avg_freq /= 10;
  608.     nic_freq = avg_freq;
  609.     if (calc_base_clock_params() != 0)
  610.     {
  611.         rte_exit(EXIT_FAILURE, "Cannot configure base paramteres for HW Timestamping\n");
  612.     }
  613.  
  614.     printf("avg HW freq: %.9lf cycles/ns\nBase Time: %lu ns\nBase HW clock val: %lu\n", nic_freq, base_time_ns, base_nic_clock);
  615.  
  616.     assign_ports_to_cores();
  617.     rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MAIN);
  618.  
  619.     /* clean up the EAL */
  620.     rte_eal_cleanup();
  621.  
  622.     return 0;
  623. }
  624.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement