* [PATCH v1 0/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory @ 2021-11-18 1:52 eagostini 2021-11-18 1:52 ` [PATCH v1 1/1] " eagostini 2021-11-18 18:56 ` [PATCH v2 0/1] " eagostini 0 siblings, 2 replies; 7+ messages in thread From: eagostini @ 2021-11-18 1:52 UTC (permalink / raw) To: dev; +Cc: Elena Agostini From: Elena Agostini <eagostini@nvidia.com> This patch introduces ethdev in test-gpudev app to provide: - an example to show how GPU memory can be used to send and receive packets - an useful tool to measure network metrics when using GPU memory with io forwarding With this feature test-gpudev can: - RX packets in CPU or GPU memory - Store packets in the gpudev communication list - TX receive packets from the communication list It's a simulation of a multi-core application. Elena Agostini (1): app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory app/test-gpudev/main.c | 471 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 452 insertions(+), 19 deletions(-) -- 2.17.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 1/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory 2021-11-18 1:52 [PATCH v1 0/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory eagostini @ 2021-11-18 1:52 ` eagostini 2021-11-18 6:17 ` Jerin Jacob 2021-11-18 18:56 ` [PATCH v2 0/1] " eagostini 1 sibling, 1 reply; 7+ messages in thread From: eagostini @ 2021-11-18 1:52 UTC (permalink / raw) To: dev; +Cc: Elena Agostini From: Elena Agostini <eagostini@nvidia.com> This patch introduces ethdev in test-gpudev app to provide: - an example to show how GPU memory can be used to send and receive packets - an useful tool to measure network metrics when using GPU memory with io forwarding With this feature test-gpudev can: - RX packets in CPU or GPU memory - Store packets in the gpudev communication list - TX receive packets from the communication list It's a simulation of a multi-core application. Signed-off-by: Elena Agostini <eagostini@nvidia.com> --- app/test-gpudev/main.c | 471 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 452 insertions(+), 19 deletions(-) diff --git a/app/test-gpudev/main.c b/app/test-gpudev/main.c index 250fba6427..daa586c64e 100644 --- a/app/test-gpudev/main.c +++ b/app/test-gpudev/main.c @@ -10,6 +10,8 @@ #include <stdarg.h> #include <errno.h> #include <getopt.h> +#include <stdbool.h> +#include <signal.h> #include <rte_common.h> #include <rte_malloc.h> @@ -19,22 +21,98 @@ #include <rte_ethdev.h> #include <rte_mempool.h> #include <rte_mbuf.h> +#include <rte_launch.h> +#include <rte_lcore.h> +#include <rte_per_lcore.h> #include <rte_gpudev.h> +#ifndef ACCESS_ONCE +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&x) +#endif + +#ifndef WRITE_ONCE +#define WRITE_ONCE(x, v) (ACCESS_ONCE(x) = (v)) +#endif + +#define GPU_PAGE_SHIFT 16 +#define GPU_PAGE_SIZE (1UL << GPU_PAGE_SHIFT) +#define GPU_PAGE_OFFSET (GPU_PAGE_SIZE-1) +#define GPU_PAGE_MASK (~GPU_PAGE_OFFSET) + +#define MAX_QUEUES 16 +#define NUM_COMM_ITEMS 2048 +#define PKT_GAP 4 + +// #define DEBUG_PRINT 1 + enum app_args { ARG_HELP, - ARG_MEMPOOL + ARG_BURST, + ARG_GPU, + ARG_MBUFD, + ARG_MEMORY, + ARG_QUEUES, + ARG_TESTAPI, +}; + +enum mem_type { + MEMORY_CPU, + MEMORY_GPU +}; + +/* Options configurable from cmd line */ +static uint32_t conf_burst = 64; +static uint16_t conf_gpu_id = 0; +static enum mem_type conf_mtype = MEMORY_CPU; +static uint32_t conf_mbuf_dataroom = 2048; +static uint32_t conf_queues = 1; +static bool conf_testapi = false; +static uint16_t conf_nb_descriptors = 2048; + +/* Options statically defined */ +static uint32_t conf_nb_mbuf = 16384; +static uint16_t conf_port_id = 0; + +/* Other variables */ +static volatile bool force_quit; +static struct rte_mempool *mpool; +static struct rte_pktmbuf_extmem ext_mem; +struct rte_gpu_comm_list *comm_list_fwd[MAX_QUEUES]; +struct rte_ether_addr port_eth_addr; +static struct rte_eth_conf port_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_RSS, + .split_hdr_size = 0, + .offloads = 0, + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + .offloads = 0, + }, + .rx_adv_conf = { + .rss_conf = { + .rss_key = NULL, + .rss_hf = ETH_RSS_IP + }, + }, }; static void usage(const char *prog_name) { - printf("%s [EAL options] --\n", + printf("%s [EAL options] --\n" + " --help\n" + " --burst N: number of packets per rx burst\n" + " --gpu N: GPU ID to use\n" + " --memory N: external mempool memory type, 0 CPU, 1 GPU\n" + " --mbufd N: mbuf dataroom size\n" + " --testapi: test gpudev function\n" + " --queues N: number of RX queues\n", prog_name); } -static void +static int args_parse(int argc, char **argv) { char **argvopt; @@ -42,7 +120,19 @@ args_parse(int argc, char **argv) int opt_idx; static struct option lgopts[] = { - { "help", 0, 0, ARG_HELP}, + { "help", 0, 0, ARG_HELP}, + /* Packets per burst. */ + { "burst", 1, 0, ARG_BURST}, + /* GPU to use. */ + { "gpu", 1, 0, ARG_GPU}, + /* Type of memory for the mempool. */ + { "memory", 1, 0, ARG_MEMORY}, + /* Size of mbufs dataroom */ + { "mbufd", 1, 0, ARG_MBUFD}, + /* Number of RX queues */ + { "queues", 1, 0, ARG_QUEUES}, + /* Test only gpudev functions */ + { "testapi", 0, 0, ARG_TESTAPI}, /* End of options */ { 0, 0, 0, 0 } }; @@ -51,6 +141,24 @@ args_parse(int argc, char **argv) while ((opt = getopt_long(argc, argvopt, "", lgopts, &opt_idx)) != EOF) { switch (opt) { + case ARG_BURST: + conf_burst = (uint32_t) atoi(optarg); + break; + case ARG_GPU: + conf_gpu_id = (uint16_t) atoi(optarg); + break; + case ARG_MEMORY: + conf_mtype = (atoi(optarg) == 1 ? MEMORY_GPU : MEMORY_CPU); + break; + case ARG_MBUFD: + conf_mbuf_dataroom = (uint32_t) atoi(optarg); + break; + case ARG_QUEUES: + conf_queues = (uint32_t) atoi(optarg); + break; + case ARG_TESTAPI: + conf_testapi = (atoi(optarg) == 1 ? true : false); + break; case ARG_HELP: usage(argv[0]); break; @@ -60,6 +168,20 @@ args_parse(int argc, char **argv) break; } } + + if (conf_queues > MAX_QUEUES) { + fprintf(stderr, "Can't support more than %d queues\n", MAX_QUEUES); + return -1; + } + + if (conf_queues * 2 > rte_lcore_count()) { + fprintf(stderr, + "Need to use at least %d cores to support %d RX/TX queues (EAL cores %d)\n", + conf_queues * 2, conf_queues, rte_lcore_count()); + return -1; + } + + return 0; } static int @@ -342,13 +464,118 @@ create_update_comm_list(uint16_t gpu_id) return -1; } +static void +signal_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + force_quit = true; + } +} + +static int +rx_core(__rte_unused void *arg) +{ + uint32_t queue_id; + uint32_t nb_rx = 0; + int ret = 0; + int comm_list_item = 0; + struct rte_mbuf *rx_mbufs[RTE_GPU_COMM_LIST_PKTS_MAX]; + + queue_id = (rte_lcore_index(rte_lcore_id()) - 1) / 2; + + printf("RX core started on queue %d.\n", queue_id); + + while (force_quit == false) { + + nb_rx = 0; + while (nb_rx < RTE_GPU_COMM_LIST_PKTS_MAX && + nb_rx < (conf_burst - PKT_GAP) && + force_quit == false) { + nb_rx += rte_eth_rx_burst(conf_port_id, queue_id, + &(rx_mbufs[nb_rx]), + (conf_burst - nb_rx)); + } + + ret = rte_gpu_comm_populate_list_pkts( + &(comm_list_fwd[queue_id][comm_list_item]), rx_mbufs, nb_rx); + if (ret) { + fprintf(stderr, + "rte_gpu_comm_populate_list_pkts error %d.\n", ret); + return -1; + } + +#ifdef DEBUG_PRINT + printf("RX %d pkts from item %d\n", + comm_list_fwd[queue_id][comm_list_item].num_pkts, + comm_list_item); +#endif + + WRITE_ONCE(comm_list_fwd[queue_id][comm_list_item].status, RTE_GPU_COMM_LIST_DONE); + + comm_list_item = (comm_list_item+1) % NUM_COMM_ITEMS; + } + + return 0; +} + +static int +tx_core(__rte_unused void *arg) +{ + uint32_t queue_id = 0; + uint32_t nb_tx = 0; + int ret = 0; + int comm_list_item = 0; + + queue_id = (rte_lcore_index(rte_lcore_id()) - 1) / 2; + printf("TX core started on queue %d.\n", queue_id); + + while (force_quit == false) { + +#ifdef DEBUG_PRINT + printf("Waiting on item %d\n", comm_list_item); +#endif + while (ACCESS_ONCE(comm_list_fwd[queue_id][comm_list_item].status) + != RTE_GPU_COMM_LIST_DONE && force_quit == false); + + nb_tx = 0; + while (nb_tx < comm_list_fwd[queue_id][comm_list_item].num_pkts) { + nb_tx += rte_eth_tx_burst(conf_port_id, queue_id, + &(comm_list_fwd[queue_id][comm_list_item].mbufs[nb_tx]), + comm_list_fwd[queue_id][comm_list_item].num_pkts - nb_tx); + } + rte_wmb(); + +#ifdef DEBUG_PRINT + printf("TX %d/%d pkts from item %d\n", + nb_tx, comm_list_fwd[queue_id][comm_list_item].num_pkts, + comm_list_item); +#endif + ret = rte_gpu_comm_cleanup_list(&(comm_list_fwd[queue_id][comm_list_item])); + if (ret) { + fprintf(stderr, "rte_gpu_comm_cleanup_list error %d.\n", ret); + return -1; + } + + rte_mb(); + + comm_list_item = (comm_list_item+1) % NUM_COMM_ITEMS; + } + + return 0; +} + int main(int argc, char **argv) { - int ret; + int ret, core_id; int nb_gpus = 0; + int nb_ports = 0; int16_t gpu_id = 0; + uint32_t idx_q = 0; struct rte_gpu_info ginfo; + struct rte_eth_dev_info dev_info; /* Init EAL. */ ret = rte_eal_init(argc, argv); @@ -356,8 +583,14 @@ main(int argc, char **argv) rte_exit(EXIT_FAILURE, "EAL init failed\n"); argc -= ret; argv += ret; - if (argc > 1) - args_parse(argc, argv); + if (argc > 1) { + ret = args_parse(argc, argv); + if (ret) { + fprintf(stderr, "Input args error.\n"); + goto exit; + } + } + argc -= ret; argv += ret; @@ -381,25 +614,225 @@ main(int argc, char **argv) if (nb_gpus == 0) { fprintf(stderr, "Need at least one GPU on the system to run the example\n"); - return EXIT_FAILURE; + goto exit; } - gpu_id = 0; + if (nb_gpus < conf_gpu_id) { + fprintf(stderr, "Not enough GPUs in the system (%d / %d).\n", nb_gpus, conf_gpu_id); + goto exit; + } - /** - * Memory tests - */ - alloc_gpu_memory(gpu_id); - register_cpu_memory(gpu_id); + if (conf_testapi == true) { + /* Memory tests */ + alloc_gpu_memory(gpu_id); + register_cpu_memory(gpu_id); - /** - * Communication items test - */ - create_update_comm_flag(gpu_id); - create_update_comm_list(gpu_id); + /* Communication items test */ + create_update_comm_flag(gpu_id); + create_update_comm_list(gpu_id); + + goto exit; + } + + force_quit = false; + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + nb_ports = rte_eth_dev_count_avail(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); + + ret = rte_eth_dev_info_get(conf_port_id, &dev_info); + if (ret) { + fprintf(stderr, "rte_eth_dev_info_get failed with %d.\n", ret); + goto exit; + } + + /* Create external memory mempool. */ + ext_mem.elt_size = conf_mbuf_dataroom + RTE_PKTMBUF_HEADROOM; + ext_mem.buf_len = RTE_ALIGN_CEIL(conf_nb_mbuf * ext_mem.elt_size, GPU_PAGE_SIZE); + + if (conf_mtype == MEMORY_CPU) { + ext_mem.buf_ptr = rte_malloc("extmem", ext_mem.buf_len, 0); + if (ext_mem.buf_ptr == NULL) { + fprintf(stderr, "Could not allocate CPU DPDK memory.\n"); + goto exit; + } + + ret = rte_gpu_mem_register(conf_gpu_id, ext_mem.buf_len, ext_mem.buf_ptr); + if (ret < 0) { + fprintf(stderr, + "rte_gpu_mem_register CPU memory returned error %d.\n", ret); + return -1; + } + } else { + ext_mem.buf_iova = RTE_BAD_IOVA; + + ext_mem.buf_ptr = rte_gpu_mem_alloc(conf_gpu_id, ext_mem.buf_len); + if (ext_mem.buf_ptr == NULL) { + fprintf(stderr, "Could not allocate GPU device memory.\n"); + goto exit; + } + + ret = rte_extmem_register(ext_mem.buf_ptr, ext_mem.buf_len, + NULL, ext_mem.buf_iova, GPU_PAGE_SIZE); + if (ret) { + fprintf(stderr, "Unable to register addr 0x%p, ret %d.\n", ext_mem.buf_ptr, ret); + goto exit; + } + } + + /* DMA map the external memory. */ + ret = rte_dev_dma_map(dev_info.device, ext_mem.buf_ptr, + ext_mem.buf_iova, ext_mem.buf_len); + if (ret) { + fprintf(stderr, "Could not DMA map EXT memory.\n"); + goto exit; + } + + /* Create external memory mempool. */ + mpool = rte_pktmbuf_pool_create_extbuf("payload_mpool", conf_nb_mbuf, + 0, 0, ext_mem.elt_size, + rte_socket_id(), &ext_mem, 1); + if (mpool == NULL) { + fprintf(stderr, "Could not create EXT memory mempool.\n"); + goto exit; + } + + /* Queues configuration. */ + ret = rte_eth_dev_configure(conf_port_id, conf_queues, + conf_queues, &port_conf); + if (ret < 0) { + fprintf(stderr, + "Cannot configure device: err=%d, port=%u queues=%u\n", + ret, conf_port_id, conf_queues); + goto exit; + } + + ret = rte_eth_dev_adjust_nb_rx_tx_desc(conf_port_id, + &conf_nb_descriptors, &conf_nb_descriptors); + if (ret) { + fprintf(stderr, + "Cannot adjust number of descriptors: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + + for (idx_q = 0; idx_q < conf_queues; idx_q++) { + + ret = rte_eth_rx_queue_setup(conf_port_id, idx_q, + conf_nb_descriptors, rte_lcore_to_socket_id(idx_q), + NULL, mpool); + + if (ret) { + fprintf(stderr, "rte_eth_rx_queue_setup: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + + ret = rte_eth_tx_queue_setup(conf_port_id, idx_q, + conf_nb_descriptors, rte_lcore_to_socket_id(idx_q), NULL); + if (ret) { + fprintf(stderr, "rte_eth_tx_queue_setup: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + } + + rte_eth_macaddr_get(conf_port_id, &port_eth_addr); + + ret = rte_eth_dev_start(conf_port_id); + if (ret) { + fprintf(stderr, "rte_eth_dev_start: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + + printf("Port %d: %02x:%02x:%02x:%02x:%02x:%02x started!\n", + conf_port_id, + (uint8_t)port_eth_addr.addr_bytes[0], + (uint8_t)port_eth_addr.addr_bytes[1], + port_eth_addr.addr_bytes[2], + port_eth_addr.addr_bytes[3], + port_eth_addr.addr_bytes[4], + port_eth_addr.addr_bytes[5]); + + rte_eth_promiscuous_enable(conf_port_id); + + /* Create communication lists, one per queue. */ + for (idx_q = 0; idx_q < MAX_QUEUES; idx_q++) { + comm_list_fwd[idx_q] = NULL; + + if (idx_q < conf_queues) { + comm_list_fwd[idx_q] = rte_gpu_comm_create_list(conf_gpu_id, NUM_COMM_ITEMS); + if (comm_list_fwd[idx_q] == NULL) { + fprintf(stderr, "rte_gpu_comm_create_list returned error %d\n", ret); + goto exit; + } + ret = rte_gpu_comm_cleanup_list(&(comm_list_fwd[idx_q][0])); + if (ret < 0) { + fprintf(stderr, "rte_gpu_comm_cleanup_list returned error %d\n", ret); + goto exit; + } + } + } + + core_id = 0; + for (idx_q = 0; idx_q < conf_queues; idx_q++) { + core_id = rte_get_next_lcore(core_id, 1, 0); + rte_eal_remote_launch(tx_core, NULL, core_id); + + core_id = rte_get_next_lcore(core_id, 1, 0); + rte_eal_remote_launch(rx_core, NULL, core_id); + } + + core_id = 0; + RTE_LCORE_FOREACH_WORKER(core_id) { + if (rte_eal_wait_lcore(core_id) < 0) { + fprintf(stderr, "bad exit for core %d.\n", + core_id); + break; + } + } + + force_quit = true; + + ret = rte_dev_dma_unmap(dev_info.device, (void *)ext_mem.buf_ptr, + RTE_BAD_IOVA, ext_mem.buf_len); + if (ret) { + fprintf(stderr, + "rte_dev_dma_unmap 0x%p -> %d (rte_errno = %d)\n", + (uint8_t *)ext_mem.buf_ptr, ret, rte_errno); + goto exit; + } + + if (conf_mtype == MEMORY_CPU) { + ret = rte_gpu_mem_unregister(conf_gpu_id, ext_mem.buf_ptr); + if (ret < 0) { + fprintf(stderr, "rte_gpu_mem_unregister returned error %d\n", ret); + goto exit; + } + + rte_free(ext_mem.buf_ptr); + + } else { + + ret = rte_extmem_unregister(ext_mem.buf_ptr, ext_mem.buf_len); + if (ret) { + fprintf(stderr, "rte_extmem_unregister failed with %d.\n", ret); + goto exit; + } + + rte_gpu_mem_free(conf_gpu_id, (void *)ext_mem.buf_ptr); + } + + rte_eth_dev_stop(conf_port_id); + rte_eth_dev_close(conf_port_id); +exit: /* clean up the EAL */ rte_eal_cleanup(); + printf("Bye...\n"); return EXIT_SUCCESS; } -- 2.17.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 1/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory 2021-11-18 1:52 ` [PATCH v1 1/1] " eagostini @ 2021-11-18 6:17 ` Jerin Jacob 2021-11-18 10:16 ` Elena Agostini 0 siblings, 1 reply; 7+ messages in thread From: Jerin Jacob @ 2021-11-18 6:17 UTC (permalink / raw) To: Elena Agostini; +Cc: dpdk-dev On Thu, Nov 18, 2021 at 12:28 AM <eagostini@nvidia.com> wrote: > > From: Elena Agostini <eagostini@nvidia.com> > > This patch introduces ethdev in test-gpudev app to provide: > - an example to show how GPU memory can be used to send and receive packets > - an useful tool to measure network metrics when using GPU memory with > io forwarding > > With this feature test-gpudev can: > - RX packets in CPU or GPU memory > - Store packets in the gpudev communication list > - TX receive packets from the communication list > > It's a simulation of a multi-core application. > > Signed-off-by: Elena Agostini <eagostini@nvidia.com> > --- > app/test-gpudev/main.c | 471 +++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 452 insertions(+), 19 deletions(-) > > diff --git a/app/test-gpudev/main.c b/app/test-gpudev/main.c > index 250fba6427..daa586c64e 100644 > --- a/app/test-gpudev/main.c > +++ b/app/test-gpudev/main.c > @@ -10,6 +10,8 @@ > #include <stdarg.h> > #include <errno.h> > #include <getopt.h> > +#include <stdbool.h> > +#include <signal.h> > > #include <rte_common.h> > #include <rte_malloc.h> > @@ -19,22 +21,98 @@ > #include <rte_ethdev.h> > #include <rte_mempool.h> > #include <rte_mbuf.h> > +#include <rte_launch.h> > +#include <rte_lcore.h> > +#include <rte_per_lcore.h> > > #include <rte_gpudev.h> > > +#ifndef ACCESS_ONCE > +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&x) > +#endif > + > +#ifndef WRITE_ONCE > +#define WRITE_ONCE(x, v) (ACCESS_ONCE(x) = (v)) > +#endif Better to have a public version of this macro as it uses just in this test application. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 1/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory 2021-11-18 6:17 ` Jerin Jacob @ 2021-11-18 10:16 ` Elena Agostini 0 siblings, 0 replies; 7+ messages in thread From: Elena Agostini @ 2021-11-18 10:16 UTC (permalink / raw) To: Jerin Jacob; +Cc: dpdk-dev [-- Attachment #1: Type: text/plain, Size: 2239 bytes --] > From: Jerin Jacob <jerinjacobk@gmail.com> > Date: Thursday, 18 November 2021 at 07:17 > To: Elena Agostini <eagostini@nvidia.com> > Cc: dpdk-dev <dev@dpdk.org> > Subject: Re: [PATCH v1 1/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory > External email: Use caution opening links or attachments> > > On Thu, Nov 18, 2021 at 12:28 AM <eagostini@nvidia.com> wrote: > > > > From: Elena Agostini <eagostini@nvidia.com> > > > > This patch introduces ethdev in test-gpudev app to provide: > > - an example to show how GPU memory can be used to send and receive packets > > - an useful tool to measure network metrics when using GPU memory with > > io forwarding > > > > With this feature test-gpudev can: > > - RX packets in CPU or GPU memory > > - Store packets in the gpudev communication list > > - TX receive packets from the communication list > > > > It's a simulation of a multi-core application. > > > > Signed-off-by: Elena Agostini <eagostini@nvidia.com> > > --- > > app/test-gpudev/main.c | 471 +++++++++++++++++++++++++++++++++++++++-- > > 1 file changed, 452 insertions(+), 19 deletions(-) > > > > diff --git a/app/test-gpudev/main.c b/app/test-gpudev/main.c > > index 250fba6427..daa586c64e 100644 > > --- a/app/test-gpudev/main.c > > +++ b/app/test-gpudev/main.c > > @@ -10,6 +10,8 @@ > > #include <stdarg.h> > > #include <errno.h> > > #include <getopt.h> > > +#include <stdbool.h> > > +#include <signal.h> > > > > #include <rte_common.h> > > #include <rte_malloc.h> > > @@ -19,22 +21,98 @@ > > #include <rte_ethdev.h> > > #include <rte_mempool.h> > > #include <rte_mbuf.h> > > +#include <rte_launch.h> > > +#include <rte_lcore.h> > > +#include <rte_per_lcore.h> > > > > #include <rte_gpudev.h> > > > > +#ifndef ACCESS_ONCE > > +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&x) > > +#endif > > + > > +#ifndef WRITE_ONCE > > +#define WRITE_ONCE(x, v) (ACCESS_ONCE(x) = (v)) > > +#endif> > Better to have a public version of this macro as it uses just in this > test application. Thanks for taking time to review this patch. I can actually use the RTE_GPU_VOLATILE macro exposed in the gpudev library to replace both of them. [-- Attachment #2: Type: text/html, Size: 8813 bytes --] ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 0/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory 2021-11-18 1:52 [PATCH v1 0/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory eagostini 2021-11-18 1:52 ` [PATCH v1 1/1] " eagostini @ 2021-11-18 18:56 ` eagostini 2021-11-18 18:56 ` [PATCH v2 1/1] " eagostini 1 sibling, 1 reply; 7+ messages in thread From: eagostini @ 2021-11-18 18:56 UTC (permalink / raw) To: dev; +Cc: Elena Agostini From: Elena Agostini <eagostini@nvidia.com> This patch introduces ethdev in test-gpudev app to provide: - an example to show how GPU memory can be used to send and receive packets - an useful tool to measure network metrics when using GPU memory with io forwarding With this feature test-gpudev can: - RX packets in CPU or GPU memory - Store packets in the gpudev communication list - TX receive packets from the communication list It's a simulation of a multi-core application. Changelog: - Address review comments - Minor improvements Elena Agostini (1): app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory app/test-gpudev/main.c | 477 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 458 insertions(+), 19 deletions(-) -- 2.17.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory 2021-11-18 18:56 ` [PATCH v2 0/1] " eagostini @ 2021-11-18 18:56 ` eagostini 2023-07-06 18:58 ` Stephen Hemminger 0 siblings, 1 reply; 7+ messages in thread From: eagostini @ 2021-11-18 18:56 UTC (permalink / raw) To: dev; +Cc: Elena Agostini From: Elena Agostini <eagostini@nvidia.com> This patch introduces ethdev in test-gpudev app to provide: - an example to show how GPU memory can be used to send and receive packets - an useful tool to measure network metrics when using GPU memory with io forwarding With this feature test-gpudev can: - RX packets in CPU or GPU memory - Store packets in the gpudev communication list - TX receive packets from the communication list It's a simulation of a multi-core application. Signed-off-by: Elena Agostini <eagostini@nvidia.com> --- app/test-gpudev/main.c | 477 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 458 insertions(+), 19 deletions(-) diff --git a/app/test-gpudev/main.c b/app/test-gpudev/main.c index 250fba6427..18de023208 100644 --- a/app/test-gpudev/main.c +++ b/app/test-gpudev/main.c @@ -10,6 +10,8 @@ #include <stdarg.h> #include <errno.h> #include <getopt.h> +#include <stdbool.h> +#include <signal.h> #include <rte_common.h> #include <rte_malloc.h> @@ -19,22 +21,90 @@ #include <rte_ethdev.h> #include <rte_mempool.h> #include <rte_mbuf.h> +#include <rte_launch.h> +#include <rte_lcore.h> +#include <rte_per_lcore.h> #include <rte_gpudev.h> +#define GPU_PAGE_SHIFT 16 +#define GPU_PAGE_SIZE (1UL << GPU_PAGE_SHIFT) +#define GPU_PAGE_OFFSET (GPU_PAGE_SIZE-1) +#define GPU_PAGE_MASK (~GPU_PAGE_OFFSET) + +#define MAX_QUEUES 16 +#define NUM_COMM_ITEMS 2048 +#define PKT_GAP 4 + +// #define DEBUG_PRINT 1 + enum app_args { ARG_HELP, - ARG_MEMPOOL + ARG_BURST, + ARG_GPU, + ARG_MBUFD, + ARG_MEMORY, + ARG_QUEUES, + ARG_TESTAPI, +}; + +enum mem_type { + MEMORY_CPU, + MEMORY_GPU +}; + +/* Options configurable from cmd line */ +static uint32_t conf_burst = 64; +static uint16_t conf_gpu_id = 0; +static enum mem_type conf_mtype = MEMORY_CPU; +static uint32_t conf_mbuf_dataroom = 2048; +static uint32_t conf_queues = 1; +static bool conf_testapi = false; +static uint16_t conf_nb_descriptors = 2048; + +/* Options statically defined */ +static uint32_t conf_nb_mbuf = 16384; +static uint16_t conf_port_id = 0; + +/* Other variables */ +static volatile bool force_quit; +static struct rte_mempool *mpool; +static struct rte_pktmbuf_extmem ext_mem; +struct rte_gpu_comm_list *comm_list_fwd[MAX_QUEUES]; +struct rte_ether_addr port_eth_addr; +static struct rte_eth_conf port_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_RSS, + .split_hdr_size = 0, + .offloads = 0, + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + .offloads = 0, + }, + .rx_adv_conf = { + .rss_conf = { + .rss_key = NULL, + .rss_hf = ETH_RSS_IP + }, + }, }; static void usage(const char *prog_name) { - printf("%s [EAL options] --\n", + printf("%s [EAL options] --\n" + " --help\n" + " --burst N: number of packets per rx burst\n" + " --gpu N: GPU ID to use\n" + " --memory N: external mempool memory type, 0 CPU, 1 GPU\n" + " --mbufd N: mbuf dataroom size\n" + " --testapi: test gpudev function\n" + " --queues N: number of RX queues\n", prog_name); } -static void +static int args_parse(int argc, char **argv) { char **argvopt; @@ -42,7 +112,19 @@ args_parse(int argc, char **argv) int opt_idx; static struct option lgopts[] = { - { "help", 0, 0, ARG_HELP}, + { "help", 0, 0, ARG_HELP}, + /* Packets per burst. */ + { "burst", 1, 0, ARG_BURST}, + /* GPU to use. */ + { "gpu", 1, 0, ARG_GPU}, + /* Type of memory for the mempool. */ + { "memory", 1, 0, ARG_MEMORY}, + /* Size of mbufs dataroom */ + { "mbufd", 1, 0, ARG_MBUFD}, + /* Number of RX queues */ + { "queues", 1, 0, ARG_QUEUES}, + /* Test only gpudev functions */ + { "testapi", 0, 0, ARG_TESTAPI}, /* End of options */ { 0, 0, 0, 0 } }; @@ -51,6 +133,24 @@ args_parse(int argc, char **argv) while ((opt = getopt_long(argc, argvopt, "", lgopts, &opt_idx)) != EOF) { switch (opt) { + case ARG_BURST: + conf_burst = (uint32_t) atoi(optarg); + break; + case ARG_GPU: + conf_gpu_id = (uint16_t) atoi(optarg); + break; + case ARG_MEMORY: + conf_mtype = (atoi(optarg) == 1 ? MEMORY_GPU : MEMORY_CPU); + break; + case ARG_MBUFD: + conf_mbuf_dataroom = (uint32_t) atoi(optarg); + break; + case ARG_QUEUES: + conf_queues = (uint32_t) atoi(optarg); + break; + case ARG_TESTAPI: + conf_testapi = (atoi(optarg) == 1 ? true : false); + break; case ARG_HELP: usage(argv[0]); break; @@ -60,6 +160,19 @@ args_parse(int argc, char **argv) break; } } + + if (conf_queues > MAX_QUEUES) { + fprintf(stderr, "Can't support more than %d queues\n", MAX_QUEUES); + return -1; + } + + if (conf_queues * 2 > rte_lcore_count()) { + fprintf(stderr, "Need to use at least %d cores to support %d RX/TX queues (EAL cores %d)\n", + conf_queues * 2, conf_queues, rte_lcore_count()); + return -1; + } + + return 0; } static int @@ -342,13 +455,130 @@ create_update_comm_list(uint16_t gpu_id) return -1; } +static void +signal_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + force_quit = true; + } +} + +static int +rx_core(__rte_unused void *arg) +{ + uint32_t queue_id; + uint32_t nb_rx = 0; + int ret = 0; + int comm_list_item = 0; + struct rte_mbuf *rx_mbufs[RTE_GPU_COMM_LIST_PKTS_MAX]; + + queue_id = (rte_lcore_index(rte_lcore_id()) - 1) / 2; + + if (queue_id > conf_queues) { + fprintf(stderr, "Please specify the right list of cores (%d cores) in EAL params to support %d queues.\n", + conf_queues*2, conf_queues); + RTE_GPU_VOLATILE(force_quit) = true; + return -1; + } + + printf("RX core started on queue %d.\n", queue_id); + + while (force_quit == false) { + + nb_rx = 0; + while (nb_rx < RTE_GPU_COMM_LIST_PKTS_MAX && + nb_rx < (conf_burst - PKT_GAP) && + force_quit == false) { + nb_rx += rte_eth_rx_burst(conf_port_id, queue_id, + &(rx_mbufs[nb_rx]), + (conf_burst - nb_rx)); + } + + ret = rte_gpu_comm_populate_list_pkts( + &(comm_list_fwd[queue_id][comm_list_item]), rx_mbufs, nb_rx); + if (ret) { + fprintf(stderr, "rte_gpu_comm_populate_list_pkts error %d.\n", ret); + return -1; + } + +#ifdef DEBUG_PRINT + printf("RX %d pkts from item %d\n", + comm_list_fwd[queue_id][comm_list_item].num_pkts, + comm_list_item); +#endif + + RTE_GPU_VOLATILE(comm_list_fwd[queue_id][comm_list_item].status) = RTE_GPU_COMM_LIST_DONE; + + comm_list_item = (comm_list_item+1) % NUM_COMM_ITEMS; + } + + return 0; +} + +static int +tx_core(__rte_unused void *arg) +{ + uint32_t queue_id = 0; + uint32_t nb_tx = 0; + int ret = 0; + int comm_list_item = 0; + + queue_id = (rte_lcore_index(rte_lcore_id()) - 1) / 2; + if (queue_id > conf_queues) { + fprintf(stderr, "Please specify the right list of cores (%d cores) in EAL params to support %d queues.\n", + conf_queues*2, conf_queues); + RTE_GPU_VOLATILE(force_quit) = true; + return -1; + } + printf("TX core started on queue %d.\n", queue_id); + + while (force_quit == false) { + +#ifdef DEBUG_PRINT + printf("Waiting on item %d\n", comm_list_item); +#endif + while (RTE_GPU_VOLATILE(comm_list_fwd[queue_id][comm_list_item].status) != + RTE_GPU_COMM_LIST_DONE && force_quit == false); + + nb_tx = 0; + while (nb_tx < comm_list_fwd[queue_id][comm_list_item].num_pkts) { + nb_tx += rte_eth_tx_burst(conf_port_id, queue_id, + &(comm_list_fwd[queue_id][comm_list_item].mbufs[nb_tx]), + comm_list_fwd[queue_id][comm_list_item].num_pkts - nb_tx); + } + rte_wmb(); + +#ifdef DEBUG_PRINT + printf("TX %d/%d pkts from item %d\n", + nb_tx, comm_list_fwd[queue_id][comm_list_item].num_pkts, + comm_list_item); +#endif + ret = rte_gpu_comm_cleanup_list(&(comm_list_fwd[queue_id][comm_list_item])); + if (ret) { + fprintf(stderr, "rte_gpu_comm_cleanup_list error %d.\n", ret); + return -1; + } + + rte_mb(); + + comm_list_item = (comm_list_item+1) % NUM_COMM_ITEMS; + } + + return 0; +} + int main(int argc, char **argv) { - int ret; + int ret, core_id; int nb_gpus = 0; + int nb_ports = 0; int16_t gpu_id = 0; + uint32_t idx_q = 0; struct rte_gpu_info ginfo; + struct rte_eth_dev_info dev_info; /* Init EAL. */ ret = rte_eal_init(argc, argv); @@ -356,8 +586,14 @@ main(int argc, char **argv) rte_exit(EXIT_FAILURE, "EAL init failed\n"); argc -= ret; argv += ret; - if (argc > 1) - args_parse(argc, argv); + if (argc > 1) { + ret = args_parse(argc, argv); + if (ret) { + fprintf(stderr, "Input args error.\n"); + goto exit; + } + } + argc -= ret; argv += ret; @@ -381,25 +617,228 @@ main(int argc, char **argv) if (nb_gpus == 0) { fprintf(stderr, "Need at least one GPU on the system to run the example\n"); - return EXIT_FAILURE; + goto exit; } - gpu_id = 0; + if (nb_gpus < conf_gpu_id) { + fprintf(stderr, "Not enough GPUs in the system (%d / %d).\n", nb_gpus, conf_gpu_id); + goto exit; + } - /** - * Memory tests - */ - alloc_gpu_memory(gpu_id); - register_cpu_memory(gpu_id); + if (conf_testapi == true) { + /* Memory tests */ + alloc_gpu_memory(gpu_id); + register_cpu_memory(gpu_id); - /** - * Communication items test - */ - create_update_comm_flag(gpu_id); - create_update_comm_list(gpu_id); + /* Communication items test */ + create_update_comm_flag(gpu_id); + create_update_comm_list(gpu_id); + + goto exit; + } + + force_quit = false; + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + nb_ports = rte_eth_dev_count_avail(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); + + ret = rte_eth_dev_info_get(conf_port_id, &dev_info); + if (ret) { + fprintf(stderr, "rte_eth_dev_info_get failed with %d.\n", ret); + goto exit; + } + + /* Create external memory mempool. */ + ext_mem.elt_size = conf_mbuf_dataroom + RTE_PKTMBUF_HEADROOM; + ext_mem.buf_len = RTE_ALIGN_CEIL(conf_nb_mbuf * ext_mem.elt_size, GPU_PAGE_SIZE); + + if (conf_mtype == MEMORY_CPU) { + ext_mem.buf_ptr = rte_malloc("extmem", ext_mem.buf_len, 0); + if (ext_mem.buf_ptr == NULL) { + fprintf(stderr, "Could not allocate CPU DPDK memory.\n"); + goto exit; + } + + ret = rte_gpu_mem_register(conf_gpu_id, ext_mem.buf_len, ext_mem.buf_ptr); + if (ret < 0) { + fprintf(stderr, + "rte_gpu_mem_register CPU memory returned error %d.\n", ret); + return -1; + } + } else { + ext_mem.buf_iova = RTE_BAD_IOVA; + + ext_mem.buf_ptr = rte_gpu_mem_alloc(conf_gpu_id, ext_mem.buf_len); + if (ext_mem.buf_ptr == NULL) { + fprintf(stderr, "Could not allocate GPU device memory.\n"); + goto exit; + } + + ret = rte_extmem_register(ext_mem.buf_ptr, ext_mem.buf_len, + NULL, ext_mem.buf_iova, GPU_PAGE_SIZE); + if (ret) { + fprintf(stderr, "Unable to register addr 0x%p, ret %d.\n", ext_mem.buf_ptr, ret); + goto exit; + } + } + + /* DMA map the external memory. */ + ret = rte_dev_dma_map(dev_info.device, ext_mem.buf_ptr, + ext_mem.buf_iova, ext_mem.buf_len); + if (ret) { + fprintf(stderr, "Could not DMA map EXT memory.\n"); + goto exit; + } + + /* Create external memory mempool. */ + mpool = rte_pktmbuf_pool_create_extbuf("payload_mpool", conf_nb_mbuf, + 0, 0, ext_mem.elt_size, + rte_socket_id(), &ext_mem, 1); + if (mpool == NULL) { + fprintf(stderr, "Could not create EXT memory mempool.\n"); + goto exit; + } + + /* Queues configuration. */ + ret = rte_eth_dev_configure(conf_port_id, conf_queues, + conf_queues, &port_conf); + if (ret < 0) { + fprintf(stderr, + "Cannot configure device: err=%d, port=%u queues=%u\n", + ret, conf_port_id, conf_queues); + goto exit; + } + + ret = rte_eth_dev_adjust_nb_rx_tx_desc(conf_port_id, + &conf_nb_descriptors, &conf_nb_descriptors); + if (ret) { + fprintf(stderr, + "Cannot adjust number of descriptors: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + + for (idx_q = 0; idx_q < conf_queues; idx_q++) { + + ret = rte_eth_rx_queue_setup(conf_port_id, idx_q, + conf_nb_descriptors, rte_lcore_to_socket_id(idx_q), + NULL, mpool); + + if (ret) { + fprintf(stderr, "rte_eth_rx_queue_setup: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + + ret = rte_eth_tx_queue_setup(conf_port_id, idx_q, + conf_nb_descriptors, rte_lcore_to_socket_id(idx_q), NULL); + if (ret) { + fprintf(stderr, "rte_eth_tx_queue_setup: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + } + + rte_eth_macaddr_get(conf_port_id, &port_eth_addr); + + ret = rte_eth_dev_start(conf_port_id); + if (ret) { + fprintf(stderr, "rte_eth_dev_start: err=%d, port=%u\n", + ret, conf_port_id); + goto exit; + } + + printf("Port %d: %02x:%02x:%02x:%02x:%02x:%02x started!\n", + conf_port_id, + (uint8_t)port_eth_addr.addr_bytes[0], + (uint8_t)port_eth_addr.addr_bytes[1], + port_eth_addr.addr_bytes[2], + port_eth_addr.addr_bytes[3], + port_eth_addr.addr_bytes[4], + port_eth_addr.addr_bytes[5]); + + rte_eth_promiscuous_enable(conf_port_id); + + /* Create communication lists, one per queue. */ + for (idx_q = 0; idx_q < MAX_QUEUES; idx_q++) { + comm_list_fwd[idx_q] = NULL; + + if (idx_q < conf_queues) { + comm_list_fwd[idx_q] = rte_gpu_comm_create_list(conf_gpu_id, + NUM_COMM_ITEMS); + if (comm_list_fwd[idx_q] == NULL) { + fprintf(stderr, "comm_create_list returned error %d\n", + ret); + goto exit; + } + ret = rte_gpu_comm_cleanup_list(&(comm_list_fwd[idx_q][0])); + if (ret < 0) { + fprintf(stderr, "comm_cleanup_list returned error %d\n", + ret); + goto exit; + } + } + } + + core_id = 0; + for (idx_q = 0; idx_q < conf_queues; idx_q++) { + core_id = rte_get_next_lcore(core_id, 1, 0); + rte_eal_remote_launch(tx_core, NULL, core_id); + + core_id = rte_get_next_lcore(core_id, 1, 0); + rte_eal_remote_launch(rx_core, NULL, core_id); + } + + core_id = 0; + RTE_LCORE_FOREACH_WORKER(core_id) { + if (rte_eal_wait_lcore(core_id) < 0) { + fprintf(stderr, "bad exit for core %d.\n", + core_id); + break; + } + } + + force_quit = true; + + ret = rte_dev_dma_unmap(dev_info.device, (void *)ext_mem.buf_ptr, + RTE_BAD_IOVA, ext_mem.buf_len); + if (ret) { + fprintf(stderr, + "rte_dev_dma_unmap 0x%p -> %d (rte_errno = %d)\n", + (uint8_t *)ext_mem.buf_ptr, ret, rte_errno); + goto exit; + } + + if (conf_mtype == MEMORY_CPU) { + ret = rte_gpu_mem_unregister(conf_gpu_id, ext_mem.buf_ptr); + if (ret < 0) { + fprintf(stderr, "rte_gpu_mem_unregister returned error %d\n", ret); + goto exit; + } + + rte_free(ext_mem.buf_ptr); + + } else { + + ret = rte_extmem_unregister(ext_mem.buf_ptr, ext_mem.buf_len); + if (ret) { + fprintf(stderr, "rte_extmem_unregister failed with %d.\n", ret); + goto exit; + } + + rte_gpu_mem_free(conf_gpu_id, (void *)ext_mem.buf_ptr); + } + + rte_eth_dev_stop(conf_port_id); + rte_eth_dev_close(conf_port_id); +exit: /* clean up the EAL */ rte_eal_cleanup(); + printf("Bye...\n"); return EXIT_SUCCESS; } -- 2.17.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory 2021-11-18 18:56 ` [PATCH v2 1/1] " eagostini @ 2023-07-06 18:58 ` Stephen Hemminger 0 siblings, 0 replies; 7+ messages in thread From: Stephen Hemminger @ 2023-07-06 18:58 UTC (permalink / raw) To: eagostini; +Cc: dev On Thu, 18 Nov 2021 18:56:13 +0000 <eagostini@nvidia.com> wrote: More tests is always good, this seemed to have gotten stalled. Probably because most people don't have Nvidia GPU and DPDK. > +static void > +signal_handler(int signum) > +{ > + if (signum == SIGINT || signum == SIGTERM) { > + printf("\n\nSignal %d received, preparing to exit...\n", > + signum); Printf in signal handler is unsafe, don't do it. We just removed this in other tests. > +exit: > /* clean up the EAL */ > rte_eal_cleanup(); > > + printf("Bye...\n"); You don't need this. Just noise, why add it. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2023-07-06 18:58 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-11-18 1:52 [PATCH v1 0/1] app/test-gpudev: introduce ethdev to rx/tx packets using GPU memory eagostini 2021-11-18 1:52 ` [PATCH v1 1/1] " eagostini 2021-11-18 6:17 ` Jerin Jacob 2021-11-18 10:16 ` Elena Agostini 2021-11-18 18:56 ` [PATCH v2 0/1] " eagostini 2021-11-18 18:56 ` [PATCH v2 1/1] " eagostini 2023-07-06 18:58 ` Stephen Hemminger
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).