get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/58704/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 58704,
    "url": "https://patches.dpdk.org/api/patches/58704/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20190906030221.96093-5-xiaoyun.li@intel.com/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20190906030221.96093-5-xiaoyun.li@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20190906030221.96093-5-xiaoyun.li@intel.com",
    "date": "2019-09-06T03:02:21",
    "name": "[v2,4/4] examples/ntb: support more functions for NTB",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "ab3f6fb13e2e82612b9e6de3de8bc462e41445b2",
    "submitter": {
        "id": 798,
        "url": "https://patches.dpdk.org/api/people/798/?format=api",
        "name": "Li, Xiaoyun",
        "email": "xiaoyun.li@intel.com"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20190906030221.96093-5-xiaoyun.li@intel.com/mbox/",
    "series": [
        {
            "id": 6269,
            "url": "https://patches.dpdk.org/api/series/6269/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=6269",
            "date": "2019-09-06T03:02:17",
            "name": "enable FIFO for NTB",
            "version": 2,
            "mbox": "https://patches.dpdk.org/series/6269/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/58704/comments/",
    "check": "fail",
    "checks": "https://patches.dpdk.org/api/patches/58704/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 0C80E1F0D4;\n\tFri,  6 Sep 2019 05:02:54 +0200 (CEST)",
            "from mga01.intel.com (mga01.intel.com [192.55.52.88])\n\tby dpdk.org (Postfix) with ESMTP id CEE661F0B4\n\tfor <dev@dpdk.org>; Fri,  6 Sep 2019 05:02:50 +0200 (CEST)",
            "from fmsmga003.fm.intel.com ([10.253.24.29])\n\tby fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t05 Sep 2019 20:02:50 -0700",
            "from dpdk-xiaoyun3.sh.intel.com ([10.67.119.190])\n\tby FMSMGA003.fm.intel.com with ESMTP; 05 Sep 2019 20:02:48 -0700"
        ],
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Amp-File-Uploaded": "False",
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.64,472,1559545200\"; d=\"scan'208\";a=\"190693781\"",
        "From": "Xiaoyun Li <xiaoyun.li@intel.com>",
        "To": "jingjing.wu@intel.com, keith.wiles@intel.com, omkar.maslekar@intel.com, \n\tcunming.liang@intel.com",
        "Cc": "dev@dpdk.org,\n\tXiaoyun Li <xiaoyun.li@intel.com>",
        "Date": "Fri,  6 Sep 2019 11:02:21 +0800",
        "Message-Id": "<20190906030221.96093-5-xiaoyun.li@intel.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20190906030221.96093-1-xiaoyun.li@intel.com>",
        "References": "<20190905053933.27929-1-xiaoyun.li@intel.com>\n\t<20190906030221.96093-1-xiaoyun.li@intel.com>",
        "Subject": "[dpdk-dev] [PATCH v2 4/4] examples/ntb: support more functions for\n\tNTB",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Support to transmit files between two systems.\nSupport iofwd between one ethdev and NTB device.\nSupport rxonly and txonly for NTB device.\nSupport to set forwarding mode as file-trans, txonly,\nrxonly or iofwd.\nSupport to show/clear port stats and throughput.\n\nSigned-off-by: Xiaoyun Li <xiaoyun.li@intel.com>\n---\n doc/guides/sample_app_ug/ntb.rst |   59 +-\n examples/ntb/meson.build         |    3 +\n examples/ntb/ntb_fwd.c           | 1298 +++++++++++++++++++++++++++---\n 3 files changed, 1232 insertions(+), 128 deletions(-)",
    "diff": "diff --git a/doc/guides/sample_app_ug/ntb.rst b/doc/guides/sample_app_ug/ntb.rst\nindex 079242175..f8291d7d1 100644\n--- a/doc/guides/sample_app_ug/ntb.rst\n+++ b/doc/guides/sample_app_ug/ntb.rst\n@@ -5,8 +5,17 @@ NTB Sample Application\n ======================\n \n The ntb sample application shows how to use ntb rawdev driver.\n-This sample provides interactive mode to transmit file between\n-two hosts.\n+This sample provides interactive mode to do packet based processing\n+between two systems.\n+\n+This sample supports 4 types of packet forwarding mode.\n+\n+* ``file-trans``: transmit files between two systems. The sample will\n+  be polling to receive files from the peer and save the file as\n+  ``ntb_recv_file[N]``, [N] represents the number of received file.\n+* ``rxonly``: NTB receives packets but doesn't transmit them.\n+* ``txonly``: NTB generates and transmits packets without receiving any.\n+* ``iofwd``: iofwd between NTB device and ethdev.\n \n Compiling the Application\n -------------------------\n@@ -29,6 +38,40 @@ Refer to the *DPDK Getting Started Guide* for general information on\n running applications and the Environment Abstraction Layer (EAL)\n options.\n \n+Command-line Options\n+--------------------\n+\n+The application supports the following command-line options.\n+\n+* ``--buf-size=N``\n+\n+  Set the data size of the mbufs used to N bytes, where N < 65536.\n+  The default value is 2048.\n+\n+* ``--fwd-mode=mode``\n+\n+  Set the packet forwarding mode as ``file-trans``, ``txonly``,\n+  ``rxonly`` or ``iofwd``.\n+\n+* ``--nb-desc=N``\n+\n+  Set number of descriptors of queue as N, namely queue size,\n+  where 64 <= N <= 1024. The default value is 1024.\n+\n+* ``--txfreet=N``\n+\n+  Set the transmit free threshold of TX rings to N, where 0 <= N <=\n+  the value of ``--nb-desc``. The default value is 256.\n+\n+* ``--burst=N``\n+\n+  Set the number of packets per burst to N, where 1 <= N <= 32.\n+  The default value is 32.\n+\n+* ``--qp=N``\n+\n+  Set the number of queues as N, where qp > 0.\n+\n Using the application\n ---------------------\n \n@@ -41,7 +84,11 @@ The application is console-driven using the cmdline DPDK interface:\n From this interface the available commands and descriptions of what\n they do as as follows:\n \n-* ``send [filepath]``: Send file to the peer host.\n-* ``receive [filepath]``: Receive file to [filepath]. Need the peer\n-  to send file successfully first.\n-* ``quit``: Exit program\n+* ``send [filepath]``: Send file to the peer host. Need to be in\n+  file-trans forwarding mode first.\n+* ``start``: Start transmission.\n+* ``stop``: Stop transmission.\n+* ``show/clear port stats``: Show/Clear port stats and throughput.\n+* ``set fwd file-trans/rxonly/txonly/iofwd``: Set packet forwarding\n+  mode.\n+* ``quit``: Exit program.\ndiff --git a/examples/ntb/meson.build b/examples/ntb/meson.build\nindex 9a6288f4f..f5435fe12 100644\n--- a/examples/ntb/meson.build\n+++ b/examples/ntb/meson.build\n@@ -14,3 +14,6 @@ cflags += ['-D_FILE_OFFSET_BITS=64']\n sources = files(\n \t'ntb_fwd.c'\n )\n+if dpdk_conf.has('RTE_LIBRTE_PMD_NTB_RAWDEV')\n+\tdeps += 'rawdev_ntb'\n+endif\ndiff --git a/examples/ntb/ntb_fwd.c b/examples/ntb/ntb_fwd.c\nindex f8c970cdb..b1ea71c8f 100644\n--- a/examples/ntb/ntb_fwd.c\n+++ b/examples/ntb/ntb_fwd.c\n@@ -14,21 +14,103 @@\n #include <cmdline.h>\n #include <rte_common.h>\n #include <rte_rawdev.h>\n+#include <rte_ethdev.h>\n+#include <rte_malloc.h>\n #include <rte_lcore.h>\n+#include <rte_cycles.h>\n+#include <rte_pmd_ntb.h>\n \n-#define NTB_DRV_NAME_LEN\t7\n-static uint64_t max_file_size = 0x400000;\n+/* Per-port statistics struct */\n+struct ntb_port_statistics {\n+\tuint64_t tx;\n+\tuint64_t rx;\n+} __rte_cache_aligned;\n+/* Port 0: NTB dev, Port 1: ethdev when iofwd. */\n+struct ntb_port_statistics ntb_port_stats[2];\n+\n+struct ntb_fwd_stream {\n+\tuint16_t tx_port;\n+\tuint16_t rx_port;\n+\tuint16_t qp_id;\n+\tuint8_t tx_ntb;  /* If ntb device is tx port. */\n+};\n+\n+struct ntb_fwd_lcore_conf {\n+\tuint16_t stream_id;\n+\tuint16_t nb_stream;\n+\tuint8_t stopped;\n+};\n+\n+enum ntb_fwd_mode {\n+\tFILE_TRANS = 0,\n+\tRXONLY,\n+\tTXONLY,\n+\tIOFWD,\n+\tMAX_FWD_MODE,\n+};\n+static const char *const fwd_mode_s[] = {\n+\t\"file-trans\",\n+\t\"rxonly\",\n+\t\"txonly\",\n+\t\"iofwd\",\n+\tNULL,\n+};\n+static enum ntb_fwd_mode fwd_mode = MAX_FWD_MODE;\n+\n+static struct ntb_fwd_lcore_conf fwd_lcore_conf[RTE_MAX_LCORE];\n+static struct ntb_fwd_stream *fwd_streams;\n+\n+static struct rte_mempool *mbuf_pool;\n+\n+#define NTB_DRV_NAME_LEN 7\n+#define MEMPOOL_CACHE_SIZE 256\n+\n+static uint8_t in_test;\n static uint8_t interactive = 1;\n+static uint16_t eth_port_id = RTE_MAX_ETHPORTS;\n static uint16_t dev_id;\n \n+/* Number of queues, default set as 1 */\n+static uint16_t num_queues = 1;\n+static uint16_t ntb_buf_size = RTE_MBUF_DEFAULT_BUF_SIZE;\n+\n+/* Configurable number of descriptors */\n+#define NTB_DEFAULT_NUM_DESCS 1024\n+static uint16_t nb_desc = NTB_DEFAULT_NUM_DESCS;\n+\n+static uint16_t tx_free_thresh;\n+\n+#define NTB_MAX_PKT_BURST 32\n+#define NTB_DFLT_PKT_BURST 32\n+static uint16_t pkt_burst = NTB_DFLT_PKT_BURST;\n+\n+#define BURST_TX_RETRIES 64\n+\n+static struct rte_eth_conf eth_port_conf = {\n+\t.rxmode = {\n+\t\t.mq_mode = ETH_MQ_RX_RSS,\n+\t\t.split_hdr_size = 0,\n+\t},\n+\t.rx_adv_conf = {\n+\t\t.rss_conf = {\n+\t\t\t.rss_key = NULL,\n+\t\t\t.rss_hf = ETH_RSS_IP,\n+\t\t},\n+\t},\n+\t.txmode = {\n+\t\t.mq_mode = ETH_MQ_TX_NONE,\n+\t},\n+};\n+\n /* *** Help command with introduction. *** */\n struct cmd_help_result {\n \tcmdline_fixed_string_t help;\n };\n \n-static void cmd_help_parsed(__attribute__((unused)) void *parsed_result,\n-\t\t\t    struct cmdline *cl,\n-\t\t\t    __attribute__((unused)) void *data)\n+static void\n+cmd_help_parsed(__attribute__((unused)) void *parsed_result,\n+\t\tstruct cmdline *cl,\n+\t\t__attribute__((unused)) void *data)\n {\n \tcmdline_printf(\n \t\tcl,\n@@ -37,13 +119,17 @@ static void cmd_help_parsed(__attribute__((unused)) void *parsed_result,\n \t\t\"Control:\\n\"\n \t\t\"    quit                                      :\"\n \t\t\" Quit the application.\\n\"\n-\t\t\"\\nFile transmit:\\n\"\n+\t\t\"\\nTransmission:\\n\"\n \t\t\"    send [path]                               :\"\n-\t\t\" Send [path] file. (No more than %\"PRIu64\")\\n\"\n-\t\t\"    recv [path]                            :\"\n-\t\t\" Receive file to [path]. Make sure sending is done\"\n-\t\t\" on the other side.\\n\",\n-\t\tmax_file_size\n+\t\t\" Send [path] file. Only take effect in file-trans mode\\n\"\n+\t\t\"    start                                     :\"\n+\t\t\" Start transmissions.\\n\"\n+\t\t\"    stop                                      :\"\n+\t\t\" Stop transmissions.\\n\"\n+\t\t\"    clear/show port stats                     :\"\n+\t\t\" Clear/show port stats.\\n\"\n+\t\t\"    set fwd file-trans/rxonly/txonly/iofwd    :\"\n+\t\t\" Set packet forwarding mode.\\n\"\n \t);\n \n }\n@@ -66,13 +152,37 @@ struct cmd_quit_result {\n \tcmdline_fixed_string_t quit;\n };\n \n-static void cmd_quit_parsed(__attribute__((unused)) void *parsed_result,\n-\t\t\t    struct cmdline *cl,\n-\t\t\t    __attribute__((unused)) void *data)\n+static void\n+cmd_quit_parsed(__attribute__((unused)) void *parsed_result,\n+\t\tstruct cmdline *cl,\n+\t\t__attribute__((unused)) void *data)\n {\n+\tstruct ntb_fwd_lcore_conf *conf;\n+\tuint8_t lcore_id;\n+\n+\t/* Stop transmission first. */\n+\tRTE_LCORE_FOREACH_SLAVE(lcore_id) {\n+\t\tconf = &fwd_lcore_conf[lcore_id];\n+\n+\t\tif (!conf->nb_stream)\n+\t\t\tcontinue;\n+\n+\t\tif (conf->stopped)\n+\t\t\tcontinue;\n+\n+\t\tconf->stopped = 1;\n+\t}\n+\tprintf(\"\\nWaiting for lcores to finish...\\n\");\n+\trte_eal_mp_wait_lcore();\n+\tin_test = 0;\n+\n \t/* Stop traffic and Close port. */\n \trte_rawdev_stop(dev_id);\n \trte_rawdev_close(dev_id);\n+\tif (eth_port_id < RTE_MAX_ETHPORTS && fwd_mode == IOFWD) {\n+\t\trte_eth_dev_stop(eth_port_id);\n+\t\trte_eth_dev_close(eth_port_id);\n+\t}\n \n \tcmdline_quit(cl);\n }\n@@ -102,21 +212,19 @@ cmd_sendfile_parsed(void *parsed_result,\n \t\t    __attribute__((unused)) void *data)\n {\n \tstruct cmd_sendfile_result *res = parsed_result;\n-\tstruct rte_rawdev_buf *pkts_send[1];\n-\tuint64_t rsize, size, link;\n-\tuint8_t *buff;\n+\tstruct rte_rawdev_buf *pkts_send[NTB_MAX_PKT_BURST];\n+\tstruct rte_mbuf *mbuf_send[NTB_MAX_PKT_BURST];\n+\tuint64_t size, count, i, nb_burst;\n+\tuint16_t nb_tx, buf_size;\n+\tunsigned int nb_pkt;\n+\tsize_t queue_id = 0;\n+\tuint16_t retry = 0;\n \tuint32_t val;\n \tFILE *file;\n \n-\tif (!rte_rawdevs[dev_id].started) {\n-\t\tprintf(\"Device needs to be up first. Try later.\\n\");\n-\t\treturn;\n-\t}\n-\n-\trte_rawdev_get_attr(dev_id, \"link_status\", &link);\n-\tif (!link) {\n-\t\tprintf(\"Link is not up, cannot send file.\\n\");\n-\t\treturn;\n+\tif (num_queues != 1) {\n+\t\tprintf(\"File transmission only supports 1 queue.\\n\");\n+\t\tnum_queues = 1;\n \t}\n \n \tfile = fopen(res->filepath, \"r\");\n@@ -127,30 +235,13 @@ cmd_sendfile_parsed(void *parsed_result,\n \n \tif (fseek(file, 0, SEEK_END) < 0) {\n \t\tprintf(\"Fail to get file size.\\n\");\n+\t\tfclose(file);\n \t\treturn;\n \t}\n \tsize = ftell(file);\n \tif (fseek(file, 0, SEEK_SET) < 0) {\n \t\tprintf(\"Fail to get file size.\\n\");\n-\t\treturn;\n-\t}\n-\n-\t/**\n-\t * No FIFO now. Only test memory. Limit sending file\n-\t * size <= max_file_size.\n-\t */\n-\tif (size > max_file_size) {\n-\t\tprintf(\"Warning: The file is too large. Only send first\"\n-\t\t       \" %\"PRIu64\" bits.\\n\", max_file_size);\n-\t\tsize = max_file_size;\n-\t}\n-\n-\tbuff = (uint8_t *)malloc(size);\n-\trsize = fread(buff, size, 1, file);\n-\tif (rsize != 1) {\n-\t\tprintf(\"Fail to read file.\\n\");\n \t\tfclose(file);\n-\t\tfree(buff);\n \t\treturn;\n \t}\n \n@@ -159,22 +250,63 @@ cmd_sendfile_parsed(void *parsed_result,\n \trte_rawdev_set_attr(dev_id, \"spad_user_0\", val);\n \tval = size;\n \trte_rawdev_set_attr(dev_id, \"spad_user_1\", val);\n+\tprintf(\"Sending file, size is %\"PRIu64\"\\n\", size);\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tpkts_send[i] = (struct rte_rawdev_buf *)\n+\t\t\t\tmalloc(sizeof(struct rte_rawdev_buf));\n+\n+\tbuf_size = ntb_buf_size - RTE_PKTMBUF_HEADROOM;\n+\tcount = (size + buf_size - 1) / buf_size;\n+\tnb_burst = (count + pkt_burst - 1) / pkt_burst;\n \n-\tpkts_send[0] = (struct rte_rawdev_buf *)malloc\n-\t\t\t(sizeof(struct rte_rawdev_buf));\n-\tpkts_send[0]->buf_addr = buff;\n+\tfor (i = 0; i < nb_burst; i++) {\n+\t\tval = RTE_MIN(count, pkt_burst);\n+\t\tif (rte_mempool_get_bulk(mbuf_pool, (void **)mbuf_send,\n+\t\t\t\t\tval) == 0) {\n+\t\t\tfor (nb_pkt = 0; nb_pkt < val; nb_pkt++) {\n+\t\t\t\tmbuf_send[nb_pkt]->port = dev_id;\n+\t\t\t\tmbuf_send[nb_pkt]->data_len =\n+\t\t\t\tfread(rte_pktmbuf_mtod(mbuf_send[nb_pkt],\n+\t\t\t\t\tvoid *), 1, buf_size, file);\n+\t\t\t\tmbuf_send[nb_pkt]->pkt_len =\n+\t\t\t\t\tmbuf_send[nb_pkt]->data_len;\n+\t\t\t\tpkts_send[nb_pkt]->buf_addr = mbuf_send[nb_pkt];\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfor (nb_pkt = 0; nb_pkt < val; nb_pkt++) {\n+\t\t\t\tmbuf_send[nb_pkt] =\n+\t\t\t\t\trte_mbuf_raw_alloc(mbuf_pool);\n+\t\t\t\tif (mbuf_send[nb_pkt] == NULL)\n+\t\t\t\t\tbreak;\n+\t\t\t\tmbuf_send[nb_pkt]->port = dev_id;\n+\t\t\t\tmbuf_send[nb_pkt]->data_len =\n+\t\t\t\tfread(rte_pktmbuf_mtod(mbuf_send[nb_pkt],\n+\t\t\t\t\tvoid *), 1, buf_size, file);\n+\t\t\t\tmbuf_send[nb_pkt]->pkt_len =\n+\t\t\t\t\tmbuf_send[nb_pkt]->data_len;\n+\t\t\t\tpkts_send[nb_pkt]->buf_addr = mbuf_send[nb_pkt];\n+\t\t\t}\n+\t\t}\n \n-\tif (rte_rawdev_enqueue_buffers(dev_id, pkts_send, 1,\n-\t\t\t\t       (void *)(size_t)size)) {\n-\t\tprintf(\"Fail to enqueue.\\n\");\n-\t\tgoto clean;\n+\t\tnb_tx = rte_rawdev_enqueue_buffers(dev_id, pkts_send, nb_pkt,\n+\t\t\t\t\t\t   (void *)queue_id);\n+\t\twhile (nb_tx != nb_pkt && retry < BURST_TX_RETRIES) {\n+\t\t\trte_delay_us(1);\n+\t\t\tnb_tx += rte_rawdev_enqueue_buffers(dev_id,\n+\t\t\t\t&pkts_send[nb_tx], nb_pkt - nb_tx,\n+\t\t\t\t(void *)queue_id);\n+\t\t}\n+\t\tcount -= nb_pkt;\n \t}\n+\t/* Clear register after file sending done. */\n+\trte_rawdev_set_attr(dev_id, \"spad_user_0\", 0);\n+\trte_rawdev_set_attr(dev_id, \"spad_user_1\", 0);\n \tprintf(\"Done sending file.\\n\");\n \n-clean:\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tfree(pkts_send[i]);\n \tfclose(file);\n-\tfree(buff);\n-\tfree(pkts_send[0]);\n }\n \n cmdline_parse_token_string_t cmd_send_file_send =\n@@ -195,79 +327,680 @@ cmdline_parse_inst_t cmd_send_file = {\n \t},\n };\n \n-/* *** RECEIVE FILE PARAMETERS *** */\n-struct cmd_recvfile_result {\n-\tcmdline_fixed_string_t recv_string;\n-\tchar filepath[];\n-};\n+#define RECV_FILE_LEN 30\n+static int\n+start_polling_recv_file(void *param)\n+{\n+\tstruct rte_rawdev_buf *pkts_recv[NTB_MAX_PKT_BURST];\n+\tstruct ntb_fwd_lcore_conf *conf = param;\n+\tstruct rte_mbuf *mbuf;\n+\tchar filepath[RECV_FILE_LEN];\n+\tuint64_t val, size, file_len;\n+\tuint16_t nb_rx, i, file_no;\n+\tsize_t queue_id = 0;\n+\tFILE *file;\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tpkts_recv[i] = (struct rte_rawdev_buf *)\n+\t\t\t\tmalloc(sizeof(struct rte_rawdev_buf));\n+\n+\tfile_no = 0;\n+\twhile (!conf->stopped) {\n+\t\tsnprintf(filepath, RECV_FILE_LEN, \"ntb_recv_file%d\", file_no);\n+\t\tfile = fopen(filepath, \"w\");\n+\t\tif (file == NULL) {\n+\t\t\tprintf(\"Fail to open the file.\\n\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\trte_rawdev_get_attr(dev_id, \"spad_user_0\", &val);\n+\t\tsize = val << 32;\n+\t\trte_rawdev_get_attr(dev_id, \"spad_user_1\", &val);\n+\t\tsize |= val;\n+\n+\t\tif (!size) {\n+\t\t\tfclose(file);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfile_len = 0;\n+\t\tnb_rx = NTB_MAX_PKT_BURST;\n+\t\twhile (file_len < size && !conf->stopped) {\n+\t\t\tnb_rx = rte_rawdev_dequeue_buffers(dev_id, pkts_recv,\n+\t\t\t\t\t\tpkt_burst, (void *)queue_id);\n+\t\t\tntb_port_stats[0].rx += nb_rx;\n+\t\t\tfor (i = 0; i < nb_rx; i++) {\n+\t\t\t\tmbuf = pkts_recv[i]->buf_addr;\n+\t\t\t\tfwrite(rte_pktmbuf_mtod(mbuf, void *), 1,\n+\t\t\t\t\tmbuf->data_len, file);\n+\t\t\t\tfile_len += mbuf->data_len;\n+\t\t\t\trte_pktmbuf_free(mbuf);\n+\t\t\t\tpkts_recv[i]->buf_addr = NULL;\n+\t\t\t}\n+\t\t}\n+\n+\t\tprintf(\"Received file (size: %\" PRIu64 \") from peer to %s.\\n\",\n+\t\t\tsize, filepath);\n+\t\tfclose(file);\n+\t\tfile_no++;\n+\t}\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tfree(pkts_recv[i]);\n+\treturn 0;\n+}\n+\n+static int\n+start_iofwd_per_lcore(void *param)\n+{\n+\tstruct rte_rawdev_buf *ntb_buf[NTB_MAX_PKT_BURST];\n+\tstruct rte_mbuf *pkts_burst[NTB_MAX_PKT_BURST];\n+\tstruct ntb_fwd_lcore_conf *conf = param;\n+\tstruct ntb_fwd_stream fs;\n+\tuint16_t nb_rx, nb_tx;\n+\tint i, j;\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tntb_buf[i] = (struct rte_rawdev_buf *)\n+\t\t\t     malloc(sizeof(struct rte_rawdev_buf));\n+\n+\twhile (!conf->stopped) {\n+\t\tfor (i = 0; i < conf->nb_stream; i++) {\n+\t\t\tfs = fwd_streams[conf->stream_id + i];\n+\t\t\tif (fs.tx_ntb) {\n+\t\t\t\tnb_rx = rte_eth_rx_burst(fs.rx_port,\n+\t\t\t\t\t\tfs.qp_id, pkts_burst,\n+\t\t\t\t\t\tpkt_burst);\n+\t\t\t\tif (unlikely(nb_rx == 0))\n+\t\t\t\t\tcontinue;\n+\t\t\t\tfor (j = 0; j < nb_rx; j++)\n+\t\t\t\t\tntb_buf[j]->buf_addr = pkts_burst[j];\n+\t\t\t\tnb_tx =\n+\t\t\t\trte_rawdev_enqueue_buffers(fs.tx_port,\n+\t\t\t\t\t\tntb_buf, nb_rx,\n+\t\t\t\t\t\t(void *)(size_t)fs.qp_id);\n+\t\t\t\tntb_port_stats[0].tx += nb_tx;\n+\t\t\t\tntb_port_stats[1].rx += nb_rx;\n+\t\t\t} else {\n+\t\t\t\tnb_rx =\n+\t\t\t\trte_rawdev_dequeue_buffers(fs.rx_port,\n+\t\t\t\t\t\tntb_buf, pkt_burst,\n+\t\t\t\t\t\t(void *)(size_t)fs.qp_id);\n+\t\t\t\tif (unlikely(nb_rx == 0))\n+\t\t\t\t\tcontinue;\n+\t\t\t\tfor (j = 0; j < nb_rx; j++)\n+\t\t\t\t\tpkts_burst[j] = ntb_buf[j]->buf_addr;\n+\t\t\t\tnb_tx = rte_eth_tx_burst(fs.tx_port,\n+\t\t\t\t\tfs.qp_id, pkts_burst, nb_rx);\n+\t\t\t\tntb_port_stats[1].tx += nb_tx;\n+\t\t\t\tntb_port_stats[0].rx += nb_rx;\n+\t\t\t}\n+\t\t\tif (unlikely(nb_tx < nb_rx)) {\n+\t\t\t\tdo {\n+\t\t\t\t\trte_pktmbuf_free(pkts_burst[nb_tx]);\n+\t\t\t\t} while (++nb_tx < nb_rx);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tfree(ntb_buf[i]);\n+\n+\treturn 0;\n+}\n+\n+static int\n+start_rxonly_per_lcore(void *param)\n+{\n+\tstruct rte_rawdev_buf *ntb_buf[NTB_MAX_PKT_BURST];\n+\tstruct ntb_fwd_lcore_conf *conf = param;\n+\tstruct ntb_fwd_stream fs;\n+\tuint16_t nb_rx;\n+\tint i, j;\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tntb_buf[i] = (struct rte_rawdev_buf *)\n+\t\t\t     malloc(sizeof(struct rte_rawdev_buf));\n+\n+\twhile (!conf->stopped) {\n+\t\tfor (i = 0; i < conf->nb_stream; i++) {\n+\t\t\tfs = fwd_streams[conf->stream_id + i];\n+\t\t\tnb_rx = rte_rawdev_dequeue_buffers(fs.rx_port,\n+\t\t\t\tntb_buf, pkt_burst, (void *)(size_t)fs.qp_id);\n+\t\t\tif (unlikely(nb_rx == 0))\n+\t\t\t\tcontinue;\n+\t\t\tntb_port_stats[0].rx += nb_rx;\n+\n+\t\t\tfor (j = 0; j < nb_rx; j++)\n+\t\t\t\trte_pktmbuf_free(ntb_buf[j]->buf_addr);\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tfree(ntb_buf[i]);\n+\n+\treturn 0;\n+}\n+\n+\n+static int\n+start_txonly_per_lcore(void *param)\n+{\n+\tstruct rte_rawdev_buf *ntb_buf[NTB_MAX_PKT_BURST];\n+\tstruct rte_mbuf *pkts_burst[NTB_MAX_PKT_BURST];\n+\tstruct ntb_fwd_lcore_conf *conf = param;\n+\tstruct ntb_fwd_stream fs;\n+\tuint16_t nb_pkt, nb_tx;\n+\tint i;\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tntb_buf[i] = (struct rte_rawdev_buf *)\n+\t\t\t     malloc(sizeof(struct rte_rawdev_buf));\n+\n+\twhile (!conf->stopped) {\n+\t\tfor (i = 0; i < conf->nb_stream; i++) {\n+\t\t\tfs = fwd_streams[conf->stream_id + i];\n+\t\t\tif (rte_mempool_get_bulk(mbuf_pool, (void **)pkts_burst,\n+\t\t\t\t  pkt_burst) == 0) {\n+\t\t\t\tfor (nb_pkt = 0; nb_pkt < pkt_burst; nb_pkt++) {\n+\t\t\t\t\tpkts_burst[nb_pkt]->port = dev_id;\n+\t\t\t\t\tpkts_burst[nb_pkt]->data_len =\n+\t\t\t\t\t\tpkts_burst[nb_pkt]->buf_len -\n+\t\t\t\t\t\tRTE_PKTMBUF_HEADROOM;\n+\t\t\t\t\tpkts_burst[nb_pkt]->pkt_len =\n+\t\t\t\t\t\tpkts_burst[nb_pkt]->data_len;\n+\t\t\t\t\tntb_buf[nb_pkt]->buf_addr =\n+\t\t\t\t\t\tpkts_burst[nb_pkt];\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\tfor (nb_pkt = 0; nb_pkt < pkt_burst; nb_pkt++) {\n+\t\t\t\t\tpkts_burst[nb_pkt] =\n+\t\t\t\t\t\trte_pktmbuf_alloc(mbuf_pool);\n+\t\t\t\t\tif (pkts_burst[nb_pkt] == NULL)\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\tpkts_burst[nb_pkt]->port = dev_id;\n+\t\t\t\t\tpkts_burst[nb_pkt]->data_len =\n+\t\t\t\t\t\tpkts_burst[nb_pkt]->buf_len -\n+\t\t\t\t\t\tRTE_PKTMBUF_HEADROOM;\n+\t\t\t\t\tpkts_burst[nb_pkt]->pkt_len =\n+\t\t\t\t\t\tpkts_burst[nb_pkt]->data_len;\n+\t\t\t\t\tntb_buf[nb_pkt]->buf_addr =\n+\t\t\t\t\t\tpkts_burst[nb_pkt];\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tnb_tx = rte_rawdev_enqueue_buffers(fs.tx_port,\n+\t\t\t\tntb_buf, nb_pkt, (void *)(size_t)fs.qp_id);\n+\t\t\tntb_port_stats[0].tx += nb_tx;\n+\t\t\tif (unlikely(nb_tx < nb_pkt)) {\n+\t\t\t\tdo {\n+\t\t\t\t\trte_pktmbuf_free(\n+\t\t\t\t\t\tntb_buf[nb_tx]->buf_addr);\n+\t\t\t\t} while (++nb_tx < nb_pkt);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < NTB_MAX_PKT_BURST; i++)\n+\t\tfree(ntb_buf[i]);\n+\n+\treturn 0;\n+}\n+\n+static int\n+ntb_fwd_config_setup(void)\n+{\n+\tuint16_t i;\n+\n+\t/* Make sure iofwd has valid ethdev. */\n+\tif (fwd_mode == IOFWD && eth_port_id >= RTE_MAX_ETHPORTS) {\n+\t\tprintf(\"No ethdev, cannot be in iofwd mode.\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (fwd_mode == IOFWD) {\n+\t\tfwd_streams = rte_zmalloc(\"ntb_fwd: fwd_streams\",\n+\t\t\tsizeof(struct ntb_fwd_stream) * num_queues * 2,\n+\t\t\tRTE_CACHE_LINE_SIZE);\n+\t\tfor (i = 0; i < num_queues; i++) {\n+\t\t\tfwd_streams[i * 2].qp_id = i;\n+\t\t\tfwd_streams[i * 2].tx_port = dev_id;\n+\t\t\tfwd_streams[i * 2].rx_port = eth_port_id;\n+\t\t\tfwd_streams[i * 2].tx_ntb = 1;\n+\n+\t\t\tfwd_streams[i * 2 + 1].qp_id = i;\n+\t\t\tfwd_streams[i * 2 + 1].tx_port = eth_port_id;\n+\t\t\tfwd_streams[i * 2 + 1].rx_port = dev_id;\n+\t\t\tfwd_streams[i * 2 + 1].tx_ntb = 0;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\tif (fwd_mode == RXONLY || fwd_mode == FILE_TRANS) {\n+\t\t/* Only support 1 queue in file-trans for in order. */\n+\t\tif (fwd_mode == FILE_TRANS)\n+\t\t\tnum_queues = 1;\n+\n+\t\tfwd_streams = rte_zmalloc(\"ntb_fwd: fwd_streams\",\n+\t\t\t\tsizeof(struct ntb_fwd_stream) * num_queues,\n+\t\t\t\tRTE_CACHE_LINE_SIZE);\n+\t\tfor (i = 0; i < num_queues; i++) {\n+\t\t\tfwd_streams[i].qp_id = i;\n+\t\t\tfwd_streams[i].tx_port = RTE_MAX_ETHPORTS;\n+\t\t\tfwd_streams[i].rx_port = dev_id;\n+\t\t\tfwd_streams[i].tx_ntb = 0;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\tif (fwd_mode == TXONLY) {\n+\t\tfwd_streams = rte_zmalloc(\"ntb_fwd: fwd_streams\",\n+\t\t\t\tsizeof(struct ntb_fwd_stream) * num_queues,\n+\t\t\t\tRTE_CACHE_LINE_SIZE);\n+\t\tfor (i = 0; i < num_queues; i++) {\n+\t\t\tfwd_streams[i].qp_id = i;\n+\t\t\tfwd_streams[i].tx_port = dev_id;\n+\t\t\tfwd_streams[i].rx_port = RTE_MAX_ETHPORTS;\n+\t\t\tfwd_streams[i].tx_ntb = 1;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n \n static void\n-cmd_recvfile_parsed(void *parsed_result,\n-\t\t    __attribute__((unused)) struct cmdline *cl,\n-\t\t    __attribute__((unused)) void *data)\n+assign_stream_to_lcores(void)\n {\n-\tstruct cmd_sendfile_result *res = parsed_result;\n-\tstruct rte_rawdev_buf *pkts_recv[1];\n-\tuint8_t *buff;\n-\tuint64_t val;\n-\tsize_t size;\n-\tFILE *file;\n+\tstruct ntb_fwd_lcore_conf *conf;\n+\tstruct ntb_fwd_stream *fs;\n+\tuint16_t nb_streams, sm_per_lcore, sm_id, i;\n+\tuint8_t lcore_id, lcore_num, nb_extra;\n \n-\tif (!rte_rawdevs[dev_id].started) {\n-\t\tprintf(\"Device needs to be up first. Try later.\\n\");\n-\t\treturn;\n+\tlcore_num = rte_lcore_count();\n+\t/* Exclude master core */\n+\tlcore_num--;\n+\n+\tnb_streams = (fwd_mode == IOFWD) ? num_queues * 2 : num_queues;\n+\n+\tsm_per_lcore = nb_streams / lcore_num;\n+\tnb_extra = nb_streams % lcore_num;\n+\tsm_id = 0;\n+\ti = 0;\n+\n+\tRTE_LCORE_FOREACH_SLAVE(lcore_id) {\n+\t\tconf = &fwd_lcore_conf[lcore_id];\n+\n+\t\tif (i < nb_extra) {\n+\t\t\tconf->nb_stream = sm_per_lcore + 1;\n+\t\t\tconf->stream_id = sm_id;\n+\t\t\tsm_id = sm_id + sm_per_lcore + 1;\n+\t\t} else {\n+\t\t\tconf->nb_stream = sm_per_lcore;\n+\t\t\tconf->stream_id = sm_id;\n+\t\t\tsm_id = sm_id + sm_per_lcore;\n+\t\t}\n+\n+\t\ti++;\n+\t\tif (sm_id >= nb_streams)\n+\t\t\tbreak;\n+\t}\n+\n+\t/* Print packet forwading config. */\n+\tRTE_LCORE_FOREACH_SLAVE(lcore_id) {\n+\t\tconf = &fwd_lcore_conf[lcore_id];\n+\n+\t\tif (!conf->nb_stream)\n+\t\t\tcontinue;\n+\n+\t\tprintf(\"Streams on Lcore %u :\\n\", lcore_id);\n+\t\tfor (i = 0; i < conf->nb_stream; i++) {\n+\t\t\tfs = &fwd_streams[conf->stream_id + i];\n+\t\t\tif (fwd_mode == IOFWD)\n+\t\t\t\tprintf(\" + Stream %u : %s%u RX -> %s%u TX,\"\n+\t\t\t\t\t\" Q=%u\\n\", conf->stream_id + i,\n+\t\t\t\t\tfs->tx_ntb ? \"Eth\" : \"NTB\", fs->rx_port,\n+\t\t\t\t\tfs->tx_ntb ? \"NTB\" : \"Eth\", fs->tx_port,\n+\t\t\t\t\tfs->qp_id);\n+\t\t\tif (fwd_mode == FILE_TRANS || fwd_mode == RXONLY)\n+\t\t\t\tprintf(\" + Stream %u : %s%u RX only\\n\",\n+\t\t\t\t\tconf->stream_id, \"NTB\", fs->rx_port);\n+\t\t\tif (fwd_mode == TXONLY)\n+\t\t\t\tprintf(\" + Stream %u : %s%u TX only\\n\",\n+\t\t\t\t\tconf->stream_id, \"NTB\", fs->tx_port);\n+\t\t}\n \t}\n+}\n \n-\trte_rawdev_get_attr(dev_id, \"link_status\", &val);\n-\tif (!val) {\n-\t\tprintf(\"Link is not up, cannot receive file.\\n\");\n+static void\n+start_pkt_fwd(void)\n+{\n+\tstruct ntb_fwd_lcore_conf *conf;\n+\tstruct rte_eth_link eth_link;\n+\tuint8_t lcore_id;\n+\tint ret, i;\n+\n+\tret = ntb_fwd_config_setup();\n+\tif (ret < 0) {\n+\t\tprintf(\"Cannot start traffic. Please reset fwd mode.\\n\");\n \t\treturn;\n \t}\n \n-\tfile = fopen(res->filepath, \"w\");\n-\tif (file == NULL) {\n-\t\tprintf(\"Fail to open the file.\\n\");\n+\t/* If using iofwd, checking ethdev link status first. */\n+\tif (fwd_mode == IOFWD) {\n+\t\tprintf(\"Checking eth link status...\\n\");\n+\t\t/* Wait for eth link up at most 100 times. */\n+\t\tfor (i = 0; i < 100; i++) {\n+\t\t\trte_eth_link_get(eth_port_id, &eth_link);\n+\t\t\tif (eth_link.link_status) {\n+\t\t\t\tprintf(\"Eth%u Link Up. Speed %u Mbps - %s\\n\",\n+\t\t\t\t\teth_port_id, eth_link.link_speed,\n+\t\t\t\t\t(eth_link.link_duplex ==\n+\t\t\t\t\t ETH_LINK_FULL_DUPLEX) ?\n+\t\t\t\t\t(\"full-duplex\") : (\"half-duplex\"));\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (!eth_link.link_status) {\n+\t\t\tprintf(\"Eth%u link down. Cannot start traffic.\\n\",\n+\t\t\t\teth_port_id);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tassign_stream_to_lcores();\n+\tin_test = 1;\n+\n+\tRTE_LCORE_FOREACH_SLAVE(lcore_id) {\n+\t\tconf = &fwd_lcore_conf[lcore_id];\n+\n+\t\tif (!conf->nb_stream)\n+\t\t\tcontinue;\n+\n+\t\tconf->stopped = 0;\n+\t\tif (fwd_mode == FILE_TRANS)\n+\t\t\trte_eal_remote_launch(start_polling_recv_file,\n+\t\t\t\t\t      conf, lcore_id);\n+\t\telse if (fwd_mode == IOFWD)\n+\t\t\trte_eal_remote_launch(start_iofwd_per_lcore,\n+\t\t\t\t\t      conf, lcore_id);\n+\t\telse if (fwd_mode == RXONLY)\n+\t\t\trte_eal_remote_launch(start_rxonly_per_lcore,\n+\t\t\t\t\t      conf, lcore_id);\n+\t\telse if (fwd_mode == TXONLY)\n+\t\t\trte_eal_remote_launch(start_txonly_per_lcore,\n+\t\t\t\t\t      conf, lcore_id);\n+\t}\n+}\n+\n+/* *** START FWD PARAMETERS *** */\n+struct cmd_start_result {\n+\tcmdline_fixed_string_t start;\n+};\n+\n+static void\n+cmd_start_parsed(__attribute__((unused)) void *parsed_result,\n+\t\t\t    __attribute__((unused)) struct cmdline *cl,\n+\t\t\t    __attribute__((unused)) void *data)\n+{\n+\tstart_pkt_fwd();\n+}\n+\n+cmdline_parse_token_string_t cmd_start_start =\n+\t\tTOKEN_STRING_INITIALIZER(struct cmd_start_result, start, \"start\");\n+\n+cmdline_parse_inst_t cmd_start = {\n+\t.f = cmd_start_parsed,\n+\t.data = NULL,\n+\t.help_str = \"start pkt fwd between ntb and ethdev\",\n+\t.tokens = {\n+\t\t(void *)&cmd_start_start,\n+\t\tNULL,\n+\t},\n+};\n+\n+/* *** STOP *** */\n+struct cmd_stop_result {\n+\tcmdline_fixed_string_t stop;\n+};\n+\n+static void\n+cmd_stop_parsed(__attribute__((unused)) void *parsed_result,\n+\t\t__attribute__((unused)) struct cmdline *cl,\n+\t\t__attribute__((unused)) void *data)\n+{\n+\tstruct ntb_fwd_lcore_conf *conf;\n+\tuint8_t lcore_id;\n+\n+\tRTE_LCORE_FOREACH_SLAVE(lcore_id) {\n+\t\tconf = &fwd_lcore_conf[lcore_id];\n+\n+\t\tif (!conf->nb_stream)\n+\t\t\tcontinue;\n+\n+\t\tif (conf->stopped)\n+\t\t\tcontinue;\n+\n+\t\tconf->stopped = 1;\n+\t}\n+\tprintf(\"\\nWaiting for lcores to finish...\\n\");\n+\trte_eal_mp_wait_lcore();\n+\tin_test = 0;\n+\tprintf(\"\\nDone.\\n\");\n+}\n+\n+cmdline_parse_token_string_t cmd_stop_stop =\n+\t\tTOKEN_STRING_INITIALIZER(struct cmd_stop_result, stop, \"stop\");\n+\n+cmdline_parse_inst_t cmd_stop = {\n+\t.f = cmd_stop_parsed,\n+\t.data = NULL,\n+\t.help_str = \"stop: Stop packet forwarding\",\n+\t.tokens = {\n+\t\t(void *)&cmd_stop_stop,\n+\t\tNULL,\n+\t},\n+};\n+\n+static void\n+ntb_stats_clear(void)\n+{\n+\tint nb_ids, i;\n+\tuint32_t *ids;\n+\n+\t/* Clear NTB dev stats */\n+\tnb_ids = rte_rawdev_xstats_names_get(dev_id, NULL, 0);\n+\tif (nb_ids  < 0) {\n+\t\tprintf(\"Error: Cannot get count of xstats\\n\");\n \t\treturn;\n \t}\n+\tids = malloc(sizeof(uint32_t) * nb_ids);\n+\tfor (i = 0; i < nb_ids; i++)\n+\t\tids[i] = i;\n+\trte_rawdev_xstats_reset(dev_id, ids, nb_ids);\n+\tprintf(\"\\n  statistics for NTB port %d cleared\\n\", dev_id);\n+\n+\t/* Clear Ethdev stats if have any */\n+\tif (fwd_mode == IOFWD && eth_port_id != RTE_MAX_ETHPORTS) {\n+\t\trte_eth_stats_reset(eth_port_id);\n+\t\tprintf(\"\\n  statistics for ETH port %d cleared\\n\", eth_port_id);\n+\t}\n+}\n+\n+static inline void\n+ntb_calculate_throughput(uint16_t port) {\n+\tuint64_t diff_pkts_rx, diff_pkts_tx, diff_cycles;\n+\tuint64_t mpps_rx, mpps_tx;\n+\tstatic uint64_t prev_pkts_rx[2];\n+\tstatic uint64_t prev_pkts_tx[2];\n+\tstatic uint64_t prev_cycles[2];\n+\n+\tdiff_cycles = prev_cycles[port];\n+\tprev_cycles[port] = rte_rdtsc();\n+\tif (diff_cycles > 0)\n+\t\tdiff_cycles = prev_cycles[port] - diff_cycles;\n+\tdiff_pkts_rx = (ntb_port_stats[port].rx > prev_pkts_rx[port]) ?\n+\t\t(ntb_port_stats[port].rx - prev_pkts_rx[port]) : 0;\n+\tdiff_pkts_tx = (ntb_port_stats[port].tx > prev_pkts_tx[port]) ?\n+\t\t(ntb_port_stats[port].tx - prev_pkts_tx[port]) : 0;\n+\tprev_pkts_rx[port] = ntb_port_stats[port].rx;\n+\tprev_pkts_tx[port] = ntb_port_stats[port].tx;\n+\tmpps_rx = diff_cycles > 0 ?\n+\t\tdiff_pkts_rx * rte_get_tsc_hz() / diff_cycles : 0;\n+\tmpps_tx = diff_cycles > 0 ?\n+\t\tdiff_pkts_tx * rte_get_tsc_hz() / diff_cycles : 0;\n+\tprintf(\"  Throughput (since last show)\\n\");\n+\tprintf(\"  Rx-pps: %12\"PRIu64\"\\n  Tx-pps: %12\"PRIu64\"\\n\",\n+\t\t\tmpps_rx, mpps_tx);\n+\n+}\n+\n+static void\n+ntb_stats_display(void)\n+{\n+\tstruct rte_rawdev_xstats_name *xstats_names;\n+\tstruct rte_eth_stats stats;\n+\tuint64_t *values;\n+\tuint32_t *ids;\n+\tint nb_ids, i;\n \n-\trte_rawdev_get_attr(dev_id, \"spad_user_0\", &val);\n-\tsize = val << 32;\n-\trte_rawdev_get_attr(dev_id, \"spad_user_1\", &val);\n-\tsize |= val;\n+\tprintf(\"###### statistics for NTB port %d #######\\n\", dev_id);\n \n-\tbuff = (uint8_t *)malloc(size);\n-\tpkts_recv[0] = (struct rte_rawdev_buf *)malloc\n-\t\t\t(sizeof(struct rte_rawdev_buf));\n-\tpkts_recv[0]->buf_addr = buff;\n+\t/* Get NTB dev stats and stats names */\n+\tnb_ids = rte_rawdev_xstats_names_get(dev_id, NULL, 0);\n+\tif (nb_ids  < 0) {\n+\t\tprintf(\"Error: Cannot get count of xstats\\n\");\n+\t\treturn;\n+\t}\n+\txstats_names = malloc(sizeof(struct rte_rawdev_xstats_name) * nb_ids);\n+\tif (xstats_names == NULL) {\n+\t\tprintf(\"Cannot allocate memory for xstats lookup\\n\");\n+\t\treturn;\n+\t}\n+\tif (nb_ids != rte_rawdev_xstats_names_get(\n+\t\t\tdev_id, xstats_names, nb_ids)) {\n+\t\tprintf(\"Error: Cannot get xstats lookup\\n\");\n+\t\tfree(xstats_names);\n+\t\treturn;\n+\t}\n+\tids = malloc(sizeof(uint32_t) * nb_ids);\n+\tfor (i = 0; i < nb_ids; i++)\n+\t\tids[i] = i;\n+\tvalues = malloc(sizeof(uint64_t) * nb_ids);\n+\tif (nb_ids != rte_rawdev_xstats_get(dev_id, ids, values, nb_ids)) {\n+\t\tprintf(\"Error: Unable to get xstats\\n\");\n+\t\tfree(xstats_names);\n+\t\tfree(values);\n+\t\tfree(ids);\n+\t\treturn;\n+\t}\n+\n+\t/* Display NTB dev stats */\n+\tfor (i = 0; i < nb_ids; i++)\n+\t\tprintf(\"  %s: %\"PRIu64\"\\n\", xstats_names[i].name, values[i]);\n+\tntb_calculate_throughput(0);\n \n-\tif (rte_rawdev_dequeue_buffers(dev_id, pkts_recv, 1, (void *)size)) {\n-\t\tprintf(\"Fail to dequeue.\\n\");\n-\t\tgoto clean;\n+\t/* Get Ethdev stats if have any */\n+\tif (fwd_mode == IOFWD && eth_port_id != RTE_MAX_ETHPORTS) {\n+\t\tprintf(\"###### statistics for ETH port %d ######\\n\",\n+\t\t\teth_port_id);\n+\t\trte_eth_stats_get(eth_port_id, &stats);\n+\t\tprintf(\"  RX-packets: %\"PRIu64\"\\n\", stats.ipackets);\n+\t\tprintf(\"  RX-bytes: %\"PRIu64\"\\n\", stats.ibytes);\n+\t\tprintf(\"  RX-errors: %\"PRIu64\"\\n\", stats.ierrors);\n+\t\tprintf(\"  RX-missed: %\"PRIu64\"\\n\", stats.imissed);\n+\t\tprintf(\"  TX-packets: %\"PRIu64\"\\n\", stats.opackets);\n+\t\tprintf(\"  TX-bytes: %\"PRIu64\"\\n\", stats.obytes);\n+\t\tprintf(\"  TX-errors: %\"PRIu64\"\\n\", stats.oerrors);\n+\t\tntb_calculate_throughput(1);\n \t}\n \n-\tfwrite(buff, size, 1, file);\n-\tprintf(\"Done receiving to file.\\n\");\n+\tfree(xstats_names);\n+\tfree(values);\n+\tfree(ids);\n+}\n \n-clean:\n-\tfclose(file);\n-\tfree(buff);\n-\tfree(pkts_recv[0]);\n+/* *** SHOW/CLEAR PORT STATS *** */\n+struct cmd_stats_result {\n+\tcmdline_fixed_string_t show;\n+\tcmdline_fixed_string_t port;\n+\tcmdline_fixed_string_t stats;\n+};\n+\n+static void\n+cmd_stats_parsed(void *parsed_result,\n+\t\t __attribute__((unused)) struct cmdline *cl,\n+\t\t __attribute__((unused)) void *data)\n+{\n+\tstruct cmd_stats_result *res = parsed_result;\n+\tif (!strcmp(res->show, \"clear\"))\n+\t\tntb_stats_clear();\n+\telse\n+\t\tntb_stats_display();\n }\n \n-cmdline_parse_token_string_t cmd_recv_file_recv =\n-\tTOKEN_STRING_INITIALIZER(struct cmd_recvfile_result, recv_string,\n-\t\t\t\t \"recv\");\n-cmdline_parse_token_string_t cmd_recv_file_filepath =\n-\tTOKEN_STRING_INITIALIZER(struct cmd_recvfile_result, filepath, NULL);\n+cmdline_parse_token_string_t cmd_stats_show =\n+\tTOKEN_STRING_INITIALIZER(struct cmd_stats_result, show, \"show#clear\");\n+cmdline_parse_token_string_t cmd_stats_port =\n+\tTOKEN_STRING_INITIALIZER(struct cmd_stats_result, port, \"port\");\n+cmdline_parse_token_string_t cmd_stats_stats =\n+\tTOKEN_STRING_INITIALIZER(struct cmd_stats_result, stats, \"stats\");\n \n \n-cmdline_parse_inst_t cmd_recv_file = {\n-\t.f = cmd_recvfile_parsed,\n+cmdline_parse_inst_t cmd_stats = {\n+\t.f = cmd_stats_parsed,\n \t.data = NULL,\n-\t.help_str = \"recv <file_path>\",\n+\t.help_str = \"show|clear port stats\",\n \t.tokens = {\n-\t\t(void *)&cmd_recv_file_recv,\n-\t\t(void *)&cmd_recv_file_filepath,\n+\t\t(void *)&cmd_stats_show,\n+\t\t(void *)&cmd_stats_port,\n+\t\t(void *)&cmd_stats_stats,\n+\t\tNULL,\n+\t},\n+};\n+\n+/* *** SET FORWARDING MODE *** */\n+struct cmd_set_fwd_mode_result {\n+\tcmdline_fixed_string_t set;\n+\tcmdline_fixed_string_t fwd;\n+\tcmdline_fixed_string_t mode;\n+};\n+\n+static void\n+cmd_set_fwd_mode_parsed(__attribute__((unused)) void *parsed_result,\n+\t\t\t__attribute__((unused)) struct cmdline *cl,\n+\t\t\t__attribute__((unused)) void *data)\n+{\n+\tstruct cmd_set_fwd_mode_result *res = parsed_result;\n+\tint i;\n+\n+\tif (in_test) {\n+\t\tprintf(\"Please stop traffic first.\\n\");\n+\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < MAX_FWD_MODE; i++) {\n+\t\tif (!strcmp(res->mode, fwd_mode_s[i])) {\n+\t\t\tfwd_mode = i;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\tprintf(\"Invalid %s packet forwarding mode.\\n\", res->mode);\n+}\n+\n+cmdline_parse_token_string_t cmd_setfwd_set =\n+\tTOKEN_STRING_INITIALIZER(struct cmd_set_fwd_mode_result, set, \"set\");\n+cmdline_parse_token_string_t cmd_setfwd_fwd =\n+\tTOKEN_STRING_INITIALIZER(struct cmd_set_fwd_mode_result, fwd, \"fwd\");\n+cmdline_parse_token_string_t cmd_setfwd_mode =\n+\tTOKEN_STRING_INITIALIZER(struct cmd_set_fwd_mode_result, mode,\n+\t\t\t\t\"file-trans#iofwd#txonly#rxonly\");\n+\n+cmdline_parse_inst_t cmd_set_fwd_mode = {\n+\t.f = cmd_set_fwd_mode_parsed,\n+\t.data = NULL,\n+\t.help_str = \"set forwarding mode as file-trans|rxonly|txonly|iofwd\",\n+\t.tokens = {\n+\t\t(void *)&cmd_setfwd_set,\n+\t\t(void *)&cmd_setfwd_fwd,\n+\t\t(void *)&cmd_setfwd_mode,\n \t\tNULL,\n \t},\n };\n@@ -276,7 +1009,10 @@ cmdline_parse_inst_t cmd_recv_file = {\n cmdline_parse_ctx_t main_ctx[] = {\n \t(cmdline_parse_inst_t *)&cmd_help,\n \t(cmdline_parse_inst_t *)&cmd_send_file,\n-\t(cmdline_parse_inst_t *)&cmd_recv_file,\n+\t(cmdline_parse_inst_t *)&cmd_start,\n+\t(cmdline_parse_inst_t *)&cmd_stop,\n+\t(cmdline_parse_inst_t *)&cmd_stats,\n+\t(cmdline_parse_inst_t *)&cmd_set_fwd_mode,\n \t(cmdline_parse_inst_t *)&cmd_quit,\n \tNULL,\n };\n@@ -305,45 +1041,257 @@ signal_handler(int signum)\n \t}\n }\n \n+#define OPT_BUF_SIZE         \"buf-size\"\n+#define OPT_FWD_MODE         \"fwd-mode\"\n+#define OPT_NB_DESC          \"nb-desc\"\n+#define OPT_TXFREET          \"txfreet\"\n+#define OPT_BURST            \"burst\"\n+#define OPT_QP               \"qp\"\n+\n+enum {\n+\t/* long options mapped to a short option */\n+\tOPT_NO_ZERO_COPY_NUM = 1,\n+\tOPT_BUF_SIZE_NUM,\n+\tOPT_FWD_MODE_NUM,\n+\tOPT_NB_DESC_NUM,\n+\tOPT_TXFREET_NUM,\n+\tOPT_BURST_NUM,\n+\tOPT_QP_NUM,\n+};\n+\n+static const char short_options[] =\n+\t\"i\" /* interactive mode */\n+\t;\n+\n+static const struct option lgopts[] = {\n+\t{OPT_BUF_SIZE,     1, NULL, OPT_BUF_SIZE_NUM     },\n+\t{OPT_FWD_MODE,     1, NULL, OPT_FWD_MODE_NUM     },\n+\t{OPT_NB_DESC,      1, NULL, OPT_NB_DESC_NUM      },\n+\t{OPT_TXFREET,      1, NULL, OPT_TXFREET_NUM      },\n+\t{OPT_BURST,        1, NULL, OPT_BURST_NUM        },\n+\t{OPT_QP,           1, NULL, OPT_QP_NUM           },\n+\t{0,                0, NULL, 0                    }\n+};\n+\n static void\n ntb_usage(const char *prgname)\n {\n \tprintf(\"%s [EAL options] -- [options]\\n\"\n-\t       \"-i : run in interactive mode (default value is 1)\\n\",\n-\t       prgname);\n+\t       \"-i: run in interactive mode.\\n\"\n+\t       \"-qp=N: set number of queues as N (N > 0, default: 1).\\n\"\n+\t       \"--fwd-mode=N: set fwd mode (N: file-trans | rxonly | \"\n+\t       \"txonly | iofwd, default: file-trans)\\n\"\n+\t       \"--buf-size=N: set mbuf dataroom size as N (0 < N < 65535,\"\n+\t       \" default: 2048).\\n\"\n+\t       \"--nb-desc=N: set number of descriptors as N (%u <= N <= %u,\"\n+\t       \" default: 1024).\\n\"\n+\t       \"--txfreet=N: set tx free thresh for NTB driver as N. (N >= 0)\\n\"\n+\t       \"--burst=N: set pkt burst as N (0 < N <= %u default: 32).\\n\",\n+\t       prgname, NTB_MIN_DESC_SIZE, NTB_MAX_DESC_SIZE,\n+\t       NTB_MAX_PKT_BURST);\n }\n \n-static int\n-parse_args(int argc, char **argv)\n+static void\n+ntb_parse_args(int argc, char **argv)\n {\n \tchar *prgname = argv[0], **argvopt = argv;\n-\tint opt, ret;\n+\tint opt, opt_idx, n, i;\n \n-\t/* Only support interactive mode to send/recv file first. */\n-\twhile ((opt = getopt(argc, argvopt, \"i\")) != EOF) {\n+\twhile ((opt = getopt_long(argc, argvopt, short_options,\n+\t\t\t\tlgopts, &opt_idx)) != EOF) {\n \t\tswitch (opt) {\n \t\tcase 'i':\n-\t\t\tprintf(\"Interactive-mode selected\\n\");\n+\t\t\tprintf(\"Interactive-mode selected.\\n\");\n \t\t\tinteractive = 1;\n \t\t\tbreak;\n+\t\tcase OPT_QP_NUM:\n+\t\t\tn = atoi(optarg);\n+\t\t\tif (n > 0)\n+\t\t\t\tnum_queues = n;\n+\t\t\telse\n+\t\t\t\trte_exit(EXIT_FAILURE, \"q must be > 0.\\n\");\n+\t\t\tbreak;\n+\t\tcase OPT_BUF_SIZE_NUM:\n+\t\t\tn = atoi(optarg);\n+\t\t\tif (n > RTE_PKTMBUF_HEADROOM && n <= 0xFFFF)\n+\t\t\t\tntb_buf_size = n;\n+\t\t\telse\n+\t\t\t\trte_exit(EXIT_FAILURE, \"buf-size must be > \"\n+\t\t\t\t\t\"%u and < 65536.\\n\",\n+\t\t\t\t\tRTE_PKTMBUF_HEADROOM);\n+\t\t\tbreak;\n+\t\tcase OPT_FWD_MODE_NUM:\n+\t\t\tfor (i = 0; i < MAX_FWD_MODE; i++) {\n+\t\t\t\tif (!strcmp(optarg, fwd_mode_s[i])) {\n+\t\t\t\t\tfwd_mode = i;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (i == MAX_FWD_MODE)\n+\t\t\t\trte_exit(EXIT_FAILURE, \"Unsupported mode. \"\n+\t\t\t\t\"(Should be: file-trans | rxonly | txonly \"\n+\t\t\t\t\"| iofwd)\\n\");\n+\t\t\tbreak;\n+\t\tcase OPT_NB_DESC_NUM:\n+\t\t\tn = atoi(optarg);\n+\t\t\tif (n >= NTB_MIN_DESC_SIZE && n <= NTB_MAX_DESC_SIZE)\n+\t\t\t\tnb_desc = n;\n+\t\t\telse\n+\t\t\t\trte_exit(EXIT_FAILURE, \"nb-desc must be within\"\n+\t\t\t\t\t\" [%u, %u].\\n\", NTB_MIN_DESC_SIZE,\n+\t\t\t\t\tNTB_MAX_DESC_SIZE);\n+\t\t\tbreak;\n+\t\tcase OPT_TXFREET_NUM:\n+\t\t\tn = atoi(optarg);\n+\t\t\tif (n >= 0)\n+\t\t\t\ttx_free_thresh = n;\n+\t\t\telse\n+\t\t\t\trte_exit(EXIT_FAILURE, \"txfreet must be\"\n+\t\t\t\t\t\" >= 0\\n\");\n+\t\t\tbreak;\n+\t\tcase OPT_BURST_NUM:\n+\t\t\tn = atoi(optarg);\n+\t\t\tif (n > 0 && n <= NTB_MAX_PKT_BURST)\n+\t\t\t\tpkt_burst = n;\n+\t\t\telse\n+\t\t\t\trte_exit(EXIT_FAILURE, \"burst must be within \"\n+\t\t\t\t\t\"(0, %u].\\n\", NTB_MAX_PKT_BURST);\n+\t\t\tbreak;\n \n \t\tdefault:\n \t\t\tntb_usage(prgname);\n-\t\t\treturn -1;\n+\t\t\trte_exit(EXIT_FAILURE,\n+\t\t\t\t \"Command line is incomplete or incorrect.\\n\");\n+\t\t\tbreak;\n \t\t}\n \t}\n+}\n \n-\tif (optind >= 0)\n-\t\targv[optind-1] = prgname;\n+static void\n+ntb_mempool_mz_free(__rte_unused struct rte_mempool_memhdr *memhdr,\n+\t\tvoid *opaque)\n+{\n+\tconst struct rte_memzone *mz = opaque;\n+\trte_memzone_free(mz);\n+}\n \n-\tret = optind-1;\n-\toptind = 1; /* reset getopt lib */\n-\treturn ret;\n+static struct rte_mempool *\n+ntb_mbuf_pool_create(uint16_t mbuf_seg_size, uint32_t nb_mbuf,\n+\t\t     struct ntb_dev_info ntb_info,\n+\t\t     struct ntb_dev_config *ntb_conf,\n+\t\t     unsigned int socket_id)\n+{\n+\tsize_t mz_len, total_elt_sz, max_mz_len, left_sz;\n+\tstruct rte_pktmbuf_pool_private mbp_priv;\n+\tchar pool_name[RTE_MEMPOOL_NAMESIZE];\n+\tchar mz_name[RTE_MEMZONE_NAMESIZE];\n+\tconst struct rte_memzone *mz;\n+\tstruct rte_mempool *mp;\n+\tuint64_t align;\n+\tuint32_t mz_id;\n+\tint ret;\n+\n+\tsnprintf(pool_name, sizeof(pool_name), \"ntb_mbuf_pool_%u\", socket_id);\n+\tmp = rte_mempool_create_empty(pool_name, nb_mbuf,\n+\t\t\t\t      (mbuf_seg_size + sizeof(struct rte_mbuf)),\n+\t\t\t\t      MEMPOOL_CACHE_SIZE,\n+\t\t\t\t      sizeof(struct rte_pktmbuf_pool_private),\n+\t\t\t\t      socket_id, 0);\n+\tif (mp == NULL)\n+\t\treturn NULL;\n+\n+\tmbp_priv.mbuf_data_room_size = mbuf_seg_size;\n+\tmbp_priv.mbuf_priv_size = 0;\n+\trte_pktmbuf_pool_init(mp, &mbp_priv);\n+\n+\tntb_conf->mz_list = rte_zmalloc(\"ntb_memzone_list\",\n+\t\t\t\tsizeof(struct rte_memzone *) *\n+\t\t\t\tntb_info.mw_cnt, 0);\n+\tif (ntb_conf->mz_list == NULL)\n+\t\tgoto fail;\n+\n+\t/* Put ntb header on mw0. */\n+\tif (ntb_info.mw_size[0] < ntb_info.ntb_hdr_size) {\n+\t\tprintf(\"mw0 (size: %\" PRIu64 \") is not enough for ntb hdr\"\n+\t\t       \" (size: %u)\\n\", ntb_info.mw_size[0],\n+\t\t       ntb_info.ntb_hdr_size);\n+\t\tgoto fail;\n+\t}\n+\n+\ttotal_elt_sz = mp->header_size + mp->elt_size + mp->trailer_size;\n+\tleft_sz = total_elt_sz * nb_mbuf;\n+\tfor (mz_id = 0; mz_id < ntb_info.mw_cnt; mz_id++) {\n+\t\t/* If populated mbuf is enough, no need to reserve extra mz. */\n+\t\tif (!left_sz)\n+\t\t\tbreak;\n+\t\tsnprintf(mz_name, sizeof(mz_name), \"ntb_mw_%d\", mz_id);\n+\t\talign = ntb_info.mw_size_align ? ntb_info.mw_size[mz_id] :\n+\t\t\tRTE_CACHE_LINE_SIZE;\n+\t\t/* Reserve ntb header space on memzone 0. */\n+\t\tmax_mz_len = mz_id ? ntb_info.mw_size[mz_id] :\n+\t\t\t     ntb_info.mw_size[mz_id] - ntb_info.ntb_hdr_size;\n+\t\tmz_len = left_sz <= max_mz_len ? left_sz :\n+\t\t\t(max_mz_len / total_elt_sz * total_elt_sz);\n+\t\tif (!mz_len)\n+\t\t\tcontinue;\n+\t\tmz = rte_memzone_reserve_aligned(mz_name, mz_len, socket_id,\n+\t\t\t\t\tRTE_MEMZONE_IOVA_CONTIG, align);\n+\t\tif (mz == NULL) {\n+\t\t\tprintf(\"Cannot allocate %\" PRIu64 \" aligned memzone\"\n+\t\t\t\t\" %u\\n\", align, mz_id);\n+\t\t\tgoto fail;\n+\t\t}\n+\t\tleft_sz -= mz_len;\n+\n+\t\t/* Reserve ntb header space on memzone 0. */\n+\t\tif (mz_id)\n+\t\t\tret = rte_mempool_populate_iova(mp, mz->addr, mz->iova,\n+\t\t\t\t\tmz->len, ntb_mempool_mz_free,\n+\t\t\t\t\t(void *)(uintptr_t)mz);\n+\t\telse\n+\t\t\tret = rte_mempool_populate_iova(mp,\n+\t\t\t\t\t(void *)((size_t)mz->addr +\n+\t\t\t\t\tntb_info.ntb_hdr_size),\n+\t\t\t\t\tmz->iova + ntb_info.ntb_hdr_size,\n+\t\t\t\t\tmz->len - ntb_info.ntb_hdr_size,\n+\t\t\t\t\tntb_mempool_mz_free,\n+\t\t\t\t\t(void *)(uintptr_t)mz);\n+\t\tif (ret < 0) {\n+\t\t\trte_memzone_free(mz);\n+\t\t\trte_mempool_free(mp);\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\tntb_conf->mz_list[mz_id] = mz;\n+\t}\n+\tif (left_sz) {\n+\t\tprintf(\"mw space is not enough for mempool.\\n\");\n+\t\tgoto fail;\n+\t}\n+\n+\tntb_conf->mz_num = mz_id;\n+\trte_mempool_obj_iter(mp, rte_pktmbuf_init, NULL);\n+\n+\treturn mp;\n+fail:\n+\trte_mempool_free(mp);\n+\treturn NULL;\n }\n \n int\n main(int argc, char **argv)\n {\n+\tstruct rte_eth_conf eth_pconf = eth_port_conf;\n+\tstruct rte_rawdev_info ntb_rawdev_conf;\n+\tstruct rte_rawdev_info ntb_rawdev_info;\n+\tstruct rte_eth_dev_info ethdev_info;\n+\tstruct rte_eth_rxconf eth_rx_conf;\n+\tstruct rte_eth_txconf eth_tx_conf;\n+\tstruct ntb_queue_conf ntb_q_conf;\n+\tstruct ntb_dev_config ntb_conf;\n+\tstruct ntb_dev_info ntb_info;\n+\tuint64_t ntb_link_status;\n+\tuint32_t nb_mbuf;\n \tint ret, i;\n \n \tsignal(SIGINT, signal_handler);\n@@ -353,6 +1301,9 @@ main(int argc, char **argv)\n \tif (ret < 0)\n \t\trte_exit(EXIT_FAILURE, \"Error with EAL initialization.\\n\");\n \n+\tif (rte_lcore_count() < 2)\n+\t\trte_exit(EXIT_FAILURE, \"Need at least 2 cores\\n\");\n+\n \t/* Find 1st ntb rawdev. */\n \tfor (i = 0; i < RTE_RAWDEV_MAX_DEVS; i++)\n \t\tif (rte_rawdevs[i].driver_name &&\n@@ -368,15 +1319,118 @@ main(int argc, char **argv)\n \targc -= ret;\n \targv += ret;\n \n-\tret = parse_args(argc, argv);\n+\tntb_parse_args(argc, argv);\n+\n+\trte_rawdev_set_attr(dev_id, NTB_QUEUE_SZ_NAME, nb_desc);\n+\tprintf(\"Set queue size as %u.\\n\", nb_desc);\n+\trte_rawdev_set_attr(dev_id, NTB_QUEUE_NUM_NAME, num_queues);\n+\tprintf(\"Set queue number as %u.\\n\", num_queues);\n+\tntb_rawdev_info.dev_private = (rte_rawdev_obj_t)(&ntb_info);\n+\trte_rawdev_info_get(dev_id, &ntb_rawdev_info);\n+\n+\tnb_mbuf = nb_desc * num_queues * 2 * 2 + rte_lcore_count() *\n+\t\t  MEMPOOL_CACHE_SIZE;\n+\tmbuf_pool = ntb_mbuf_pool_create(ntb_buf_size, nb_mbuf, ntb_info,\n+\t\t\t\t\t &ntb_conf, rte_socket_id());\n+\tif (mbuf_pool == NULL)\n+\t\trte_exit(EXIT_FAILURE, \"Cannot create mbuf pool.\\n\");\n+\n+\tntb_conf.num_queues = num_queues;\n+\tntb_conf.queue_size = nb_desc;\n+\tntb_rawdev_conf.dev_private = (rte_rawdev_obj_t)(&ntb_conf);\n+\tret = rte_rawdev_configure(dev_id, &ntb_rawdev_conf);\n+\tif (ret)\n+\t\trte_exit(EXIT_FAILURE, \"Can't config ntb dev: err=%d, \"\n+\t\t\t\"port=%u\\n\", ret, dev_id);\n+\n+\tntb_q_conf.tx_free_thresh = tx_free_thresh;\n+\tntb_q_conf.nb_desc = nb_desc;\n+\tntb_q_conf.rx_mp = mbuf_pool;\n+\tfor (i = 0; i < num_queues; i++) {\n+\t\t/* Setup rawdev queue */\n+\t\tret = rte_rawdev_queue_setup(dev_id, i, &ntb_q_conf);\n+\t\tif (ret < 0)\n+\t\t\trte_exit(EXIT_FAILURE,\n+\t\t\t\t\"Failed to setup ntb queue %u.\\n\", i);\n+\t}\n+\n+\t/* Waiting for peer dev up at most 100s.*/\n+\tprintf(\"Checking ntb link status...\\n\");\n+\tfor (i = 0; i < 1000; i++) {\n+\t\trte_rawdev_get_attr(dev_id, NTB_LINK_STATUS_NAME,\n+\t\t\t\t    &ntb_link_status);\n+\t\tif (ntb_link_status) {\n+\t\t\tprintf(\"Peer dev ready, ntb link up.\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\trte_delay_ms(100);\n+\t}\n+\trte_rawdev_get_attr(dev_id, NTB_LINK_STATUS_NAME, &ntb_link_status);\n+\tif (ntb_link_status == 0)\n+\t\tprintf(\"Expire 100s. Link is not up. Please restart app.\\n\");\n+\n+\tret = rte_rawdev_start(dev_id);\n \tif (ret < 0)\n-\t\trte_exit(EXIT_FAILURE, \"Invalid arguments\\n\");\n+\t\trte_exit(EXIT_FAILURE, \"rte_rawdev_start: err=%d, port=%u\\n\",\n+\t\t\tret, dev_id);\n+\n+\t/* Find 1st ethdev */\n+\teth_port_id = rte_eth_find_next(0);\n \n-\trte_rawdev_start(dev_id);\n+\tif (eth_port_id < RTE_MAX_ETHPORTS) {\n+\t\trte_eth_dev_info_get(eth_port_id, &ethdev_info);\n+\t\teth_pconf.rx_adv_conf.rss_conf.rss_hf &=\n+\t\t\t\tethdev_info.flow_type_rss_offloads;\n+\t\tret = rte_eth_dev_configure(eth_port_id, num_queues,\n+\t\t\t\t\t    num_queues, &eth_pconf);\n+\t\tif (ret)\n+\t\t\trte_exit(EXIT_FAILURE, \"Can't config ethdev: err=%d, \"\n+\t\t\t\t\"port=%u\\n\", ret, eth_port_id);\n+\t\teth_rx_conf = ethdev_info.default_rxconf;\n+\t\teth_rx_conf.offloads = eth_pconf.rxmode.offloads;\n+\t\teth_tx_conf = ethdev_info.default_txconf;\n+\t\teth_tx_conf.offloads = eth_pconf.txmode.offloads;\n+\n+\t\t/* Setup ethdev queue if ethdev exists */\n+\t\tfor (i = 0; i < num_queues; i++) {\n+\t\t\tret = rte_eth_rx_queue_setup(eth_port_id, i, nb_desc,\n+\t\t\t\t\trte_eth_dev_socket_id(eth_port_id),\n+\t\t\t\t\t&eth_rx_conf, mbuf_pool);\n+\t\t\tif (ret < 0)\n+\t\t\t\trte_exit(EXIT_FAILURE,\n+\t\t\t\t\t\"Failed to setup eth rxq %u.\\n\", i);\n+\t\t\tret = rte_eth_tx_queue_setup(eth_port_id, i, nb_desc,\n+\t\t\t\t\trte_eth_dev_socket_id(eth_port_id),\n+\t\t\t\t\t&eth_tx_conf);\n+\t\t\tif (ret < 0)\n+\t\t\t\trte_exit(EXIT_FAILURE,\n+\t\t\t\t\t\"Failed to setup eth txq %u.\\n\", i);\n+\t\t}\n+\n+\t\tret = rte_eth_dev_start(eth_port_id);\n+\t\tif (ret < 0)\n+\t\t\trte_exit(EXIT_FAILURE, \"rte_eth_dev_start: err=%d, \"\n+\t\t\t\t\"port=%u\\n\", ret, eth_port_id);\n+\t}\n+\n+\t/* initialize port stats */\n+\tmemset(&ntb_port_stats, 0, sizeof(ntb_port_stats));\n+\n+\t/* Set default fwd mode if user doesn't set it. */\n+\tif (fwd_mode == MAX_FWD_MODE && eth_port_id < RTE_MAX_ETHPORTS) {\n+\t\tprintf(\"Set default fwd mode as iofwd.\\n\");\n+\t\tfwd_mode = IOFWD;\n+\t}\n+\tif (fwd_mode == MAX_FWD_MODE) {\n+\t\tprintf(\"Set default fwd mode as file-trans.\\n\");\n+\t\tfwd_mode = FILE_TRANS;\n+\t}\n \n \tif (interactive) {\n \t\tsleep(1);\n \t\tprompt();\n+\t} else {\n+\t\tstart_pkt_fwd();\n \t}\n \n \treturn 0;\n",
    "prefixes": [
        "v2",
        "4/4"
    ]
}