get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 12172,
    "url": "https://patches.dpdk.org/api/patches/12172/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/1461192195-104070-2-git-send-email-zhihong.wang@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": "<1461192195-104070-2-git-send-email-zhihong.wang@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1461192195-104070-2-git-send-email-zhihong.wang@intel.com",
    "date": "2016-04-20T22:43:14",
    "name": "[dpdk-dev,RFC,1/2] testpmd: add portfwd engine",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "2e3d86528720d42c3c42f815a8e0bb2f45e2ae8f",
    "submitter": {
        "id": 156,
        "url": "https://patches.dpdk.org/api/people/156/?format=api",
        "name": "Zhihong Wang",
        "email": "zhihong.wang@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/1461192195-104070-2-git-send-email-zhihong.wang@intel.com/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/12172/comments/",
    "check": "pending",
    "checks": "https://patches.dpdk.org/api/patches/12172/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 [IPv6:::1])\n\tby dpdk.org (Postfix) with ESMTP id 60BEE2C18;\n\tThu, 21 Apr 2016 07:48:36 +0200 (CEST)",
            "from mga11.intel.com (mga11.intel.com [192.55.52.93])\n\tby dpdk.org (Postfix) with ESMTP id 7E1CD2BF2\n\tfor <dev@dpdk.org>; Thu, 21 Apr 2016 07:48:32 +0200 (CEST)",
            "from fmsmga004.fm.intel.com ([10.253.24.48])\n\tby fmsmga102.fm.intel.com with ESMTP; 20 Apr 2016 22:48:31 -0700",
            "from unknown (HELO dpdk5.sh.intel.com) ([10.239.129.244])\n\tby fmsmga004.fm.intel.com with ESMTP; 20 Apr 2016 22:48:32 -0700"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.24,512,1455004800\"; d=\"scan'208\";a=\"89108542\"",
        "From": "Zhihong Wang <zhihong.wang@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "Zhihong Wang <zhihong.wang@intel.com>",
        "Date": "Wed, 20 Apr 2016 18:43:14 -0400",
        "Message-Id": "<1461192195-104070-2-git-send-email-zhihong.wang@intel.com>",
        "X-Mailer": "git-send-email 2.5.0",
        "In-Reply-To": "<1461192195-104070-1-git-send-email-zhihong.wang@intel.com>",
        "References": "<1461192195-104070-1-git-send-email-zhihong.wang@intel.com>",
        "Subject": "[dpdk-dev] [RFC PATCH 1/2] testpmd: add portfwd engine",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "patches and discussions about DPDK <dev.dpdk.org>",
        "List-Unsubscribe": "<http://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<http://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "This patch implements a general purpose forwarding engine in testpmd namely\n\"portfwd\", to enable performance analysis and tuning for poll mode drivers\nin vSwitching scenarios.\n\nFeatures of portfwd:\n\n   1) Build up traffic from simple rx/tx to complex scenarios easily\n\n   2) Rich performance statistics for all ports\n\n   3) Core affinity manipulation\n\n   4) Commands for run time configuration\n\nTo enable flexible traffic flow setup, each port has 2 ways to forward\npackets in portfwd:\n\n   1) Forward based on dst ip\n\n      For ip based forwarding, portfwd scans each packet to get the dst ip\n      for dst port mapping.\n\n      A simple suffix mapping method is used for dst ip based forwarding, a\n      macro IPV4_ROUTE_MASK is used to specify how many (last) bits of dst\n      ip will be used for hashing.\n\n      It is recommended to make sure there's no conflict by setting proper\n      IPV4_ROUTE_MASK and/or different ip ends for each port, otherwise it\n      may hurt performance.\n\n   2) Forward to a fixed port\n\n      For fixed port forwarding, portfwd still scans each packet on purpose\n      to simulate the impact of packet analysis behavior in real scenarios.\n\nAfter dst ports are identified, packets are enqueued to a buffer which will\nbe burst sent when full. Packet buffers are built at each src port, so no\ncontention at enqueue stage.\n\nThere is a timeout interval to drain all buffers, which can be configured\nor disabled.\n\nSpinlock is used at dst port & queue to solve conflicts.\n\nNotice that portfwd has fair performance, but it's not for getting the\n\"maximum\" numbers:\n\n   1) It buffers packets for burst send efficiency analysis, which increase\n   latency\n\n   2) It touches the packet header and collect performance statistics which\n   adds overheads\n\nThese \"extra\" overheads are actually what happens in real applications.\n\nModifications are:\n   1) Add the portfwd engine in portfwd.c\n   2) Add related data structures\n   3) Add support functions\n\n\nSigned-off-by: Zhihong Wang <zhihong.wang@intel.com>\n---\n app/test-pmd/Makefile  |   1 +\n app/test-pmd/config.c  | 408 ++++++++++++++++++++++++++++++++++++++++++++++-\n app/test-pmd/portfwd.c | 418 +++++++++++++++++++++++++++++++++++++++++++++++++\n app/test-pmd/testpmd.c |  19 +++\n app/test-pmd/testpmd.h |  62 ++++++++\n 5 files changed, 900 insertions(+), 8 deletions(-)\n create mode 100644 app/test-pmd/portfwd.c",
    "diff": "diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile\nindex 72426f3..0352feb 100644\n--- a/app/test-pmd/Makefile\n+++ b/app/test-pmd/Makefile\n@@ -49,6 +49,7 @@ SRCS-y += parameters.c\n SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline.c\n SRCS-y += config.c\n SRCS-y += iofwd.c\n+SRCS-y += portfwd.c\n SRCS-y += macfwd.c\n SRCS-y += macfwd-retry.c\n SRCS-y += macswap.c\ndiff --git a/app/test-pmd/config.c b/app/test-pmd/config.c\nindex b1bbec6..9754229 100644\n--- a/app/test-pmd/config.c\n+++ b/app/test-pmd/config.c\n@@ -92,6 +92,8 @@\n #include <rte_ether.h>\n #include <rte_ethdev.h>\n #include <rte_string_fns.h>\n+#include <rte_cycles.h>\n+#include <rte_malloc.h>\n \n #include \"testpmd.h\"\n \n@@ -150,6 +152,11 @@ print_ethaddr(const char *name, struct ether_addr *eth_addr)\n void\n nic_stats_display(portid_t port_id)\n {\n+\tstatic uint64_t cnt_rx[RTE_MAX_ETHPORTS];\n+\tstatic uint64_t cnt_tx[RTE_MAX_ETHPORTS];\n+\tstatic uint64_t cycle[RTE_MAX_ETHPORTS];\n+\tuint64_t crx, ctx, c;\n+\n \tstruct rte_eth_stats stats;\n \tstruct rte_port *port = &ports[port_id];\n \tuint8_t i;\n@@ -209,6 +216,20 @@ nic_stats_display(portid_t port_id)\n \t\t}\n \t}\n \n+\tc = cycle[port_id];\n+\tcycle[port_id] = rte_rdtsc();\n+\tif (c > 0)\n+\t\tc = cycle[port_id] - c;\n+\n+\tcrx = stats.ipackets - cnt_rx[port_id];\n+\tctx = stats.opackets - cnt_tx[port_id];\n+\tcnt_rx[port_id] = stats.ipackets;\n+\tcnt_tx[port_id] = stats.opackets;\n+\tprintf(\"  Throughput (since last show):\\n\");\n+\tprintf(\"  RX PPS: %12\"PRIu64\"\\n  TX PPS: %12\"PRIu64\"\\n\",\n+\t\t\tc > 0 ? crx * rte_get_tsc_hz() / c : 0,\n+\t\t\tc > 0 ? ctx * rte_get_tsc_hz() / c : 0);\n+\n \tprintf(\"  %s############################%s\\n\",\n \t       nic_stats_border, nic_stats_border);\n }\n@@ -1087,6 +1108,178 @@ setup_fwd_config_of_each_lcore(struct fwd_config *cfg)\n }\n \n static void\n+copy_fwd_stream(struct fwd_stream *src, struct fwd_stream *dst)\n+{\n+\trte_memcpy(dst, src, sizeof(struct fwd_stream));\n+}\n+\n+int\n+set_fwd_stream_affinity(unsigned int idx, unsigned int core)\n+{\n+\tstruct fwd_stream **fwd_streams_tmp;\n+\tstruct fwd_stream *fs;\n+\tunsigned int lc_id_dst;\n+\tunsigned int lc_id_src;\n+\tunsigned int fs_id;\n+\tunsigned int i, j, ci, cj;\n+\n+\tif (cur_fwd_eng != &port_fwd_engine)\n+\t\treturn 0;\n+\tif (test_done == 0) {\n+\t\tprintf(\"please stop forwarding first\\n\");\n+\t\treturn 0;\n+\t}\n+\tfor (i = 0; i < cur_fwd_config.nb_fwd_lcores; i++) {\n+\t\tif (fwd_lcores_cpuids[i] == core) {\n+\t\t\tlc_id_dst = i;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (i >= cur_fwd_config.nb_fwd_lcores)\n+\t\treturn -1;\n+\tfor (i = 0; i < cur_fwd_config.nb_fwd_streams; i++) {\n+\t\tif (fwd_streams[i]->idx == idx) {\n+\t\t\tfs_id = i;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (i >= cur_fwd_config.nb_fwd_streams)\n+\t\treturn -1;\n+\tfor (i = 0; i < cur_fwd_config.nb_fwd_lcores; i++) {\n+\t\tfor (j = 0; j < fwd_lcores[i]->stream_nb; j++) {\n+\t\t\tfs = fwd_streams[fwd_lcores[i]->stream_idx + j];\n+\t\t\tif (idx == fs->idx) {\n+\t\t\t\tlc_id_src = i;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (j < fwd_lcores[i]->stream_nb)\n+\t\t\tbreak;\n+\t}\n+\tif (i >= cur_fwd_config.nb_fwd_lcores)\n+\t\treturn -1;\n+\tif (lc_id_src == lc_id_dst)\n+\t\treturn 0;\n+\tfwd_streams_tmp = rte_zmalloc(\"testpmd: fwd_streams_tmp\",\n+\t\tsizeof(struct fwd_stream *) * cur_fwd_config.nb_fwd_streams,\n+\t\tRTE_CACHE_LINE_SIZE);\n+\tif (fwd_streams_tmp == NULL)\n+\t\trte_exit(EXIT_FAILURE,\n+\t\t\t\t\"rte_zmalloc(%d (struct fwd_stream *)) \"\n+\t\t\t\t\"failed\\n\", nb_fwd_streams);\n+\n+\tfor (i = 0; i < cur_fwd_config.nb_fwd_streams; i++) {\n+\t\tfwd_streams_tmp[i] =\n+\t\t\trte_zmalloc(\"testpmd: struct fwd_stream\",\n+\t\t\t\tsizeof(struct fwd_stream),\n+\t\t\t\tRTE_CACHE_LINE_SIZE);\n+\t\tif (fwd_streams_tmp[i] == NULL)\n+\t\t\trte_exit(EXIT_FAILURE,\n+\t\t\t\t\t\"rte_zmalloc(struct fwd_stream)\"\n+\t\t\t\t\t\" failed\\n\");\n+\t}\n+\tci = 0;\n+\tcj = 0;\n+\tfor (i = 0; i < cur_fwd_config.nb_fwd_lcores; i++) {\n+\t\tif (i == lc_id_src) {\n+\t\t\tfwd_lcores[i]->stream_idx = cj;\n+\t\t\tfor (j = 0; j < fwd_lcores[i]->stream_nb; j++) {\n+\t\t\t\tif (ci == fs_id) {\n+\t\t\t\t\tci++;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\tcopy_fwd_stream(fwd_streams[ci],\n+\t\t\t\t\t\tfwd_streams_tmp[cj]);\n+\t\t\t\tci++;\n+\t\t\t\tcj++;\n+\t\t\t}\n+\t\t\tfwd_lcores[i]->stream_nb -= 1;\n+\t\t} else if (i == lc_id_dst) {\n+\t\t\tfwd_lcores[i]->stream_idx = cj;\n+\t\t\tfor (j = 0; j < fwd_lcores[i]->stream_nb; j++) {\n+\t\t\t\tcopy_fwd_stream(fwd_streams[ci],\n+\t\t\t\t\t\tfwd_streams_tmp[cj]);\n+\t\t\t\tci++;\n+\t\t\t\tcj++;\n+\t\t\t}\n+\t\t\tcopy_fwd_stream(fwd_streams[fs_id],\n+\t\t\t\t\tfwd_streams_tmp[cj]);\n+\t\t\tcj++;\n+\t\t\tfwd_lcores[i]->stream_nb += 1;\n+\t\t} else {\n+\t\t\tfwd_lcores[i]->stream_idx = cj;\n+\t\t\tfor (j = 0; j < fwd_lcores[i]->stream_nb; j++) {\n+\t\t\t\tcopy_fwd_stream(fwd_streams[ci],\n+\t\t\t\t\t\tfwd_streams_tmp[cj]);\n+\t\t\t\tci++;\n+\t\t\t\tcj++;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tif (fwd_streams != NULL) {\n+\t\tfor (i = 0; i < nb_fwd_streams; i++) {\n+\t\t\tif (fwd_streams[i] == NULL)\n+\t\t\t\tcontinue;\n+\t\t\trte_free(fwd_streams[i]);\n+\t\t\tfwd_streams[i] = NULL;\n+\t\t}\n+\t\trte_free(fwd_streams);\n+\t\tfwd_streams = NULL;\n+\t}\n+\tfwd_streams = fwd_streams_tmp;\n+\n+\treturn 0;\n+}\n+\n+static void\n+port_fwd_config_setup(void)\n+{\n+\tportid_t   rxp;\n+\tqueueid_t  rxq;\n+\tlcoreid_t  lc_id;\n+\n+\tif (fwd_config_init == 1)\n+\t\t\treturn;\n+\n+\tfwd_config_init = 1;\n+\tcur_fwd_config.nb_fwd_lcores = (lcoreid_t) nb_fwd_lcores;\n+\tcur_fwd_config.nb_fwd_ports = nb_fwd_ports;\n+\tcur_fwd_config.nb_fwd_streams =\n+\t\t(streamid_t) (nb_rxq * nb_fwd_ports);\n+\tif (cur_fwd_config.nb_fwd_lcores > cur_fwd_config.nb_fwd_streams)\n+\t\tcur_fwd_config.nb_fwd_lcores =\n+\t\t\t(lcoreid_t)cur_fwd_config.nb_fwd_streams;\n+\tinit_fwd_streams();\n+\tsetup_fwd_config_of_each_lcore(&cur_fwd_config);\n+\n+\tfor (rxp = 0; rxp < nb_ports; rxp++)\n+\t\tfwd_ports_ids[rxp] = rxp;\n+\n+\trxp = 0;\n+\trxq = 0;\n+\tfor (lc_id = 0; lc_id < cur_fwd_config.nb_fwd_streams; lc_id++) {\n+\t\tstruct fwd_stream *fs = fwd_streams[lc_id];\n+\n+\t\tfs->idx = lc_id;\n+\t\tfs->rx_port = fwd_ports_ids[rxp];\n+\t\tfs->rx_queue = rxq;\n+\t\tfs->tx_port = 0;\n+\t\tfs->tx_queue = 0;\n+\t\tfs->peer_addr = fs->tx_port;\n+\t\trxq = (queueid_t) (rxq + 1);\n+\t\tif (rxq < nb_rxq)\n+\t\t\tcontinue;\n+\t\trxq = 0;\n+\t\tif (numa_support && (nb_fwd_ports <= (nb_ports >> 1)))\n+\t\t\trxp = (portid_t) (rxp +\n+\t\t\t\t\t((nb_ports >> 1) / nb_fwd_ports));\n+\t\telse\n+\t\t\trxp = (portid_t) (rxp + 1);\n+\t}\n+\tclear_perf_stats();\n+}\n+\n+static void\n simple_fwd_config_setup(void)\n {\n \tportid_t i;\n@@ -1371,14 +1564,17 @@ fwd_config_setup(void)\n \t\ticmp_echo_config_setup();\n \t\treturn;\n \t}\n-\tif ((nb_rxq > 1) && (nb_txq > 1)){\n-\t\tif (dcb_config)\n-\t\t\tdcb_fwd_config_setup();\n-\t\telse\n-\t\t\trss_fwd_config_setup();\n+\tif (cur_fwd_eng == &port_fwd_engine)\n+\t\tport_fwd_config_setup();\n+\telse {\n+\t\tif ((nb_rxq > 1) && (nb_txq > 1)) {\n+\t\t\tif (dcb_config)\n+\t\t\t\tdcb_fwd_config_setup();\n+\t\t\telse\n+\t\t\t\trss_fwd_config_setup();\n+\t\t} else\n+\t\t\tsimple_fwd_config_setup();\n \t}\n-\telse\n-\t\tsimple_fwd_config_setup();\n }\n \n static void\n@@ -1406,8 +1602,9 @@ pkt_fwd_config_display(struct fwd_config *cfg)\n \t\t       fwd_lcores[lc_id]->stream_nb);\n \t\tfor (sm_id = 0; sm_id < fwd_lcores[lc_id]->stream_nb; sm_id++) {\n \t\t\tfs = fwd_streams[fwd_lcores[lc_id]->stream_idx + sm_id];\n-\t\t\tprintf(\"\\n  RX P=%d/Q=%d (socket %u) -> TX \"\n+\t\t\tprintf(\"\\n  %2u: RX P=%d/Q=%d (socket %u) -> TX \"\n \t\t\t       \"P=%d/Q=%d (socket %u) \",\n+\t\t\t\t   fs->idx,\n \t\t\t       fs->rx_port, fs->rx_queue,\n \t\t\t       ports[fs->rx_port].socket_id,\n \t\t\t       fs->tx_port, fs->tx_queue,\n@@ -1688,12 +1885,17 @@ set_pkt_forwarding_mode(const char *fwd_mode_name)\n \tstruct fwd_engine *fwd_eng;\n \tunsigned i;\n \n+\tif (test_done == 0) {\n+\t\tprintf(\"Please stop forwarding first\\n\");\n+\t\treturn;\n+\t}\n \ti = 0;\n \twhile ((fwd_eng = fwd_engines[i]) != NULL) {\n \t\tif (! strcmp(fwd_eng->fwd_mode_name, fwd_mode_name)) {\n \t\t\tprintf(\"Set %s packet forwarding mode\\n\",\n \t\t\t       fwd_mode_name);\n \t\t\tcur_fwd_eng = fwd_eng;\n+\t\t\tfwd_config_init = 0;\n \t\t\treturn;\n \t\t}\n \t\ti++;\n@@ -2479,3 +2681,193 @@ port_dcb_info_display(uint8_t port_id)\n \t\tprintf(\"\\t%4d\", dcb_info.tc_queue.tc_txq[0][i].nb_queue);\n \tprintf(\"\\n\");\n }\n+\n+void set_fixed_route(uint16_t srcp, uint16_t dstp)\n+{\n+\tuint32_t error = 0;\n+\n+\tif (cur_fwd_eng != &port_fwd_engine)\n+\t\tprintf(\"warning: current forward engine is not portfwd!\\n\");\n+\tif (srcp >= nb_fwd_ports) {\n+\t\tprintf(\"error: invalid srcport!\\n\");\n+\t\terror = 1;\n+\t}\n+\tif (dstp >= nb_fwd_ports) {\n+\t\tprintf(\"error: invalid dstport!\\n\");\n+\t\terror = 1;\n+\t}\n+\tif (error == 0)\n+\t\troute_table[srcp] = dstp;\n+}\n+\n+void set_ip_route(uint16_t srcp)\n+{\n+\tif (cur_fwd_eng != &port_fwd_engine)\n+\t\tprintf(\"warning: current forward engine is not portfwd!\\n\");\n+\tif (srcp >= nb_fwd_ports) {\n+\t\tprintf(\"error: invalid srcport!\\n\");\n+\t\treturn;\n+\t}\n+\troute_table[srcp] = PORT_ROUTE_IP;\n+}\n+\n+void show_route(void)\n+{\n+\tportid_t i;\n+\n+\tif (cur_fwd_eng != &port_fwd_engine)\n+\t\tprintf(\"warning: current forward engine is not portfwd!\\n\");\n+\tif (1 != ipv4_route_available) {\n+\t\tprintf(\"warning: ipv4 route not available!\\n\");\n+\t\tprintf(\"         try increase IPV4_ROUTE_MASK\\n\");\n+\t\tprintf(\"         and/or set different ip ends\\n\");\n+\t}\n+\tprintf(\"route table:\\n\");\n+\tprintf(\" srcport  dstport\\n\");\n+\tfor (i = 0; i < nb_fwd_ports; ++i) {\n+\t\tif (route_table[i] == PORT_ROUTE_IP)\n+\t\t\tprintf(\"%8d       ip\\n\", i);\n+\t\telse\n+\t\t\tprintf(\"%8d %8d\\n\", i, route_table[i]);\n+\t}\n+}\n+\n+void\n+init_txq_lock(void)\n+{\n+\tuint16_t portid, queueid;\n+\n+\tfor (portid = 0; portid < RTE_MAX_ETHPORTS; ++portid)\n+\t\tfor (queueid = 0; queueid < MAX_TX_QUEUE; ++queueid)\n+\t\t\trte_spinlock_init(\n+\t\t\t\t&(txq_lock[portid][queueid].spinlock));\n+}\n+\n+void build_ipv4_route(void)\n+{\n+\tuint16_t i, j;\n+\n+\tprintf(\"--------------------------------\\n\");\n+\tprintf(\"building ipv4 route table...\\n\");\n+\tprintf(\"ipv4 route mask: 0x%x\\n\", IPV4_ROUTE_MASK);\n+\tfor (i = 0; i < MAX_IP_DIFF; ++i)\n+\t\tipv4_route[i] = MAX_IP_DIFF;\n+\tfor (i = 0; i < nb_fwd_ports; ++i) {\n+\t\tj = ipv4_table[i] & IPV4_ROUTE_MASK;\n+\t\tif (ipv4_route[j] < MAX_IP_DIFF) {\n+\t\t\tipv4_route_available = 0;\n+\t\t\tprintf(\"warning: ipv4 route failed\\n\");\n+\t\t\tprintf(\"         try increase IPV4_ROUTE_MASK\\n\");\n+\t\t\tprintf(\"         and/or set different ip ends\\n\");\n+\t\t\tprintf(\"--------------------------------\\n\");\n+\t\t\treturn;\n+\t\t}\n+\t\tipv4_route[j] = i;\n+\t\tprintf(\"route: suffix: %d -> port: %d\\n\", j, i);\n+\t}\n+\tipv4_route_available = 1;\n+\tfor (i = 0; i < MAX_IP_DIFF; ++i)\n+\t\tif (ipv4_route[i] == MAX_IP_DIFF)\n+\t\t\tipv4_route[i] = 0;\n+\tprintf(\"ipv4 route available\\n\");\n+\tprintf(\"--------------------------------\\n\");\n+}\n+\n+void add_port_addr(void)\n+{\n+\tunsigned long i, j, k;\n+\n+\tprintf(\"--------------------------------\\n\");\n+\tprintf(\"adding port addr...\\n\");\n+\tprintf(\"port number: %d, rxq nubmer: %d, txq number: %d\\n\",\n+\t\t\tnb_fwd_ports, nb_rxq, nb_txq);\n+\tfor (i = 0; i < nb_fwd_streams; ++i)\n+\t\tfwd_streams[i]->rx_port = i;\n+\tfor (i = 0; i < nb_fwd_ports; ++i) {\n+\t\t/* assume less than 256 ports */\n+\t\tipv4_table[i] = (192 << 24) + (168 << 16) + (1 << 8) + (1 + i);\n+\t\tprintf(\"port %lu: ipv4: %d.%d.%d.%d\\n\",\n+\t\t\t\ti,\n+\t\t\t\t(ipv4_table[i] & 0xFF000000) >> 24,\n+\t\t\t\t(ipv4_table[i] & 0x00FF0000) >> 16,\n+\t\t\t\t(ipv4_table[i] & 0x0000FF00) >> 8,\n+\t\t\t\t(ipv4_table[i] & 0x000000FF));\n+\t\tfor (j = 0; j < nb_rxq; ++j) {\n+\t\t\tfor (k = 0; k < nb_fwd_ports; ++k)\n+\t\t\t\tfs_buf[i][j].tx_mbufs[k].len = 0;\n+\t\t\tfs_buf[i][j].dst_queue = (i * nb_rxq + j) % nb_txq;\n+\t\t\tprintf(\"port %lu: rxq: %lu -> txq: %d\\n\",\n+\t\t\t\t\ti, j, fs_buf[i][j].dst_queue);\n+\t\t}\n+\t\troute_table[i] = PORT_ROUTE_IP;\n+\t}\n+\tprintf(\"--------------------------------\\n\");\n+\tfor (i = 0; i < RTE_MAX_ETHPORTS; ++i)\n+\t\tfor (j = 0; j < MAX_RX_QUEUE; ++j) {\n+\t\t\tdrainer[i][j].cycle_last = 0;\n+\t\t\tdrainer[i][j].cycle_now = 0;\n+\t\t}\n+\n+\tbuild_ipv4_route();\n+}\n+\n+void set_ip(uint32_t srcp, uint32_t num0, uint32_t num1,\n+\t\tuint32_t num2, uint32_t num3)\n+{\n+\tif (cur_fwd_eng != &port_fwd_engine)\n+\t\tprintf(\"warning: current forward engine is not portfwd!\\n\");\n+\tif (srcp >= nb_fwd_ports) {\n+\t\tprintf(\"error: invalid srcport!\\n\");\n+\t\treturn;\n+\t}\n+\tif (num0 > 255 || num1 > 255 || num2 > 255 || num3 > 255) {\n+\t\tprintf(\"error: invalid ip address!\\n\");\n+\t\treturn;\n+\t}\n+\tipv4_table[srcp] = (num0 << 24) + (num1 << 16) + (num2 << 8) + num3;\n+\tprintf(\"port %u: ipv4: %d.%d.%d.%d\\n\",\n+\t\t\tsrcp,\n+\t\t\t(ipv4_table[srcp] & 0xFF000000) >> 24,\n+\t\t\t(ipv4_table[srcp] & 0x00FF0000) >> 16,\n+\t\t\t(ipv4_table[srcp] & 0x0000FF00) >> 8,\n+\t\t\t(ipv4_table[srcp] & 0x000000FF));\n+\n+\tbuild_ipv4_route();\n+}\n+\n+void set_drain_interval_ns(unsigned long drain_ns)\n+{\n+\tif (cur_fwd_eng != &port_fwd_engine)\n+\t\tprintf(\"warning: current forward engine is not portfwd!\\n\");\n+\tdrain_cycle = rte_get_tsc_hz() / 1E9 * drain_ns;\n+\tif (drain_cycle > 0)\n+\t\tprintf(\"portfwd drain interval: %lu ns, %lu cycles\\n\",\n+\t\t\t\tdrain_ns, drain_cycle);\n+\telse\n+\t\tprintf(\"portfwd drain disabled\\n\");\n+}\n+\n+void print_port_info(void)\n+{\n+\tportid_t pid;\n+\tstruct rte_port *port;\n+\n+\tprintf(\"port  mac                ip               name\\n\");\n+\tFOREACH_PORT(pid, ports) {\n+\t\tport = &ports[pid];\n+\t\tprintf(\"%4u\", pid);\n+\t\tprintf(\"  %02x:%02x:%02x:%02x:%02x:%02x\",\n+\t\t\t\t(unsigned int)port->eth_addr.addr_bytes[0],\n+\t\t\t\t(unsigned int)port->eth_addr.addr_bytes[1],\n+\t\t\t\t(unsigned int)port->eth_addr.addr_bytes[2],\n+\t\t\t\t(unsigned int)port->eth_addr.addr_bytes[3],\n+\t\t\t\t(unsigned int)port->eth_addr.addr_bytes[4],\n+\t\t\t\t(unsigned int)port->eth_addr.addr_bytes[5]);\n+\t\tprintf(\"  %3d.%3d.%3d.%3d\"\n+\t\t\t\t, (ipv4_table[pid] & 0xFF000000) >> 24\n+\t\t\t\t, (ipv4_table[pid] & 0x00FF0000) >> 16\n+\t\t\t\t, (ipv4_table[pid] & 0x0000FF00) >> 8\n+\t\t\t\t, (ipv4_table[pid] & 0x000000FF));\n+\t\tprintf(\"  %s\\n\", port->dev_info.driver_name);\n+\t}\n+}\ndiff --git a/app/test-pmd/portfwd.c b/app/test-pmd/portfwd.c\nnew file mode 100644\nindex 0000000..52b0b95\n--- /dev/null\n+++ b/app/test-pmd/portfwd.c\n@@ -0,0 +1,418 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.\n+ *   All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of Intel Corporation nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <stdarg.h>\n+#include <stdio.h>\n+#include <string.h>\n+#include <errno.h>\n+#include <stdint.h>\n+#include <unistd.h>\n+#include <inttypes.h>\n+#include <sys/queue.h>\n+#include <sys/stat.h>\n+#include <rte_common.h>\n+#include <rte_byteorder.h>\n+#include <rte_log.h>\n+#include <rte_debug.h>\n+#include <rte_cycles.h>\n+#include <rte_memory.h>\n+#include <rte_memzone.h>\n+#include <rte_launch.h>\n+#include <rte_eal.h>\n+#include <rte_per_lcore.h>\n+#include <rte_lcore.h>\n+#include <rte_atomic.h>\n+#include <rte_branch_prediction.h>\n+#include <rte_ring.h>\n+#include <rte_memory.h>\n+#include <rte_memcpy.h>\n+#include <rte_mempool.h>\n+#include <rte_mbuf.h>\n+#include <rte_interrupts.h>\n+#include <rte_pci.h>\n+#include <rte_ether.h>\n+#include <rte_ethdev.h>\n+#include <rte_string_fns.h>\n+#include <rte_ip.h>\n+#include \"testpmd.h\"\n+\n+#define PORTFWD_PERF_STATS 1\n+\n+struct op_stat {\n+\tuint64_t run[DEF_PKT_BURST+1];\n+\tuint64_t cycle[DEF_PKT_BURST+1];\n+\tuint64_t cycle_min[DEF_PKT_BURST+1];\n+\tuint64_t cycle_max[DEF_PKT_BURST+1];\n+} __rte_cache_aligned;\n+\n+struct port_perf_stat {\n+\tstruct op_stat rx_stat[MAX_RX_QUEUE];\n+\tstruct op_stat tx_stat[MAX_TX_QUEUE];\n+\tuint64_t pkt_loss[MAX_TX_QUEUE];\n+};\n+\n+struct port_perf_stat port_stat[RTE_MAX_ETHPORTS];\n+uint64_t print_stat_cycle = 1e10;\n+uint64_t last_stat_cycle;\n+\n+inline void __attribute__((always_inline))\n+clear_perf_stats(void)\n+{\n+\tuint16_t i, j, k;\n+\tstruct op_stat *ops;\n+\n+\tfor (i = 0; i < nb_fwd_ports; ++i) {\n+\t\tfor (j = 0; j < nb_rxq; ++j) {\n+\t\t\tfor (k = 0; k <= DEF_PKT_BURST; ++k) {\n+\t\t\t\tops = &port_stat[i].rx_stat[j];\n+\t\t\t\tops->run[k] = 0;\n+\t\t\t\tops->cycle[k] = 0;\n+\t\t\t\tops->cycle_min[k] = print_stat_cycle;\n+\t\t\t\tops->cycle_max[k] = 0;\n+\t\t\t}\n+\t\t}\n+\t\tfor (j = 0; j < nb_txq; ++j) {\n+\t\t\tfor (k = 0; k <= DEF_PKT_BURST; ++k) {\n+\t\t\t\tops = &port_stat[i].tx_stat[j];\n+\t\t\t\tops->run[k] = 0;\n+\t\t\t\tops->cycle[k] = 0;\n+\t\t\t\tops->cycle_min[k] = print_stat_cycle;\n+\t\t\t\tops->cycle_max[k] = 0;\n+\t\t\t}\n+\t\t}\n+\t\tfor (j = 0; j < nb_txq; ++j)\n+\t\t\tport_stat[i].pkt_loss[j] = 0;\n+\t}\n+}\n+\n+inline void __attribute__((always_inline))\n+print_perf_stats(void)\n+{\n+#if PORTFWD_PERF_STATS\n+\tuint16_t i, j, k;\n+\tstruct op_stat *ops;\n+\n+\tprintf(\"\\ncycle stat (since last show)\\n\");\n+\tprintf(\"--------------------------------\\n\");\n+\tfor (i = 0; i < nb_fwd_ports; ++i) {\n+\t\tprintf(\"port %3d, burst %3d,\\n\", i, DEF_PKT_BURST);\n+\t\tprintf(\"          rx,\");\n+\t\tfor (j = 0; j < nb_txq; ++j)\n+\t\t\tprintf(\"         run,         min,\"\n+\t\t\t\t\"         avg,         max,\");\n+\t\tprintf(\"\\n\");\n+\t\tfor (k = 0; k <= DEF_PKT_BURST; ++k) {\n+\t\t\tprintf(\"%12d,\", k);\n+\t\t\tfor (j = 0; j < nb_rxq; ++j) {\n+\t\t\t\tops = &port_stat[i].rx_stat[j];\n+\t\t\t\tprintf(\"%12\"PRIu64\",%12\"PRIu64\",\"\n+\t\t\t\t\t\"%12\"PRIu64\",%12\"PRIu64\",\",\n+\t\t\t\t\tops->run[k],\n+\t\t\t\t\tops->run[k] > 0 ?\n+\t\t\t\t\tops->cycle_min[k] : 0,\n+\t\t\t\t\tops->run[k] > 0 ?\n+\t\t\t\t\tops->cycle[k] /\n+\t\t\t\t\tops->run[k] : 0,\n+\t\t\t\t\tops->cycle_max[k]);\n+\t\t\t}\n+\t\t\tprintf(\"\\n\");\n+\t\t}\n+\t\tprintf(\"          tx,\");\n+\t\tfor (j = 0; j < nb_txq; ++j)\n+\t\t\tprintf(\"         run,         min,\"\n+\t\t\t\t\"         avg,         max,\");\n+\t\tprintf(\"\\n\");\n+\t\tfor (k = 0; k <= DEF_PKT_BURST; ++k) {\n+\t\t\tprintf(\"%12d,\", k);\n+\t\t\tfor (j = 0; j < nb_txq; ++j) {\n+\t\t\t\tops = &port_stat[i].tx_stat[j];\n+\t\t\t\tprintf(\"%12\"PRIu64\",%12\"PRIu64\",\"\n+\t\t\t\t\t\"%12\"PRIu64\",%12\"PRIu64\",\",\n+\t\t\t\t\tops->run[k],\n+\t\t\t\t\tops->run[k] > 0 ?\n+\t\t\t\t\tops->cycle_min[k] : 0,\n+\t\t\t\t\tops->run[k] > 0 ?\n+\t\t\t\t\tops->cycle[k] /\n+\t\t\t\t\tops->run[k] : 0,\n+\t\t\t\t\tops->cycle_max[k]);\n+\t\t\t}\n+\t\t\tprintf(\"\\n\");\n+\t\t}\n+\t}\n+\tprintf(\"\\ntx pktloss (since last show)\\n\");\n+\tprintf(\"--------------------------------\\n\");\n+\tprintf(\"         \");\n+\tfor (i = 0; i < nb_txq; ++i)\n+\t\tprintf(\"     txq %3d,\", i);\n+\tprintf(\"\\n\");\n+\tfor (i = 0; i < nb_fwd_ports; ++i) {\n+\t\tprintf(\"port %3d,\", i);\n+\t\tfor (j = 0; j < nb_txq; ++j)\n+\t\t\tprintf(\"%12\"PRIu64\",\", port_stat[i].pkt_loss[j]);\n+\t\tprintf(\"\\n\");\n+\t}\n+\tprintf(\"\\n\");\n+\tclear_perf_stats();\n+#else\n+\tprintf(\"warning: portfwd perf stats not enabled!\\n\");\n+#endif\n+}\n+\n+static inline uint32_t __attribute__((always_inline))\n+get_ipv4_dst_port(void *ipv4_hdr,  uint16_t portid)\n+{\n+\tuint32_t route;\n+\tuint32_t ip;\n+\tuint32_t i;\n+\n+\troute = route_table[portid];\n+\tif (route == PORT_ROUTE_IP) {\n+\t\tip = rte_be_to_cpu_32(((struct ipv4_hdr *)ipv4_hdr)->dst_addr);\n+\t\ti = ipv4_route[ip & IPV4_ROUTE_MASK];\n+\t\tif (likely(ipv4_route_available == 1 && ipv4_table[i] == ip))\n+\t\t\treturn i;\n+\t\tif (likely(ipv4_route_available == 1))\n+\t\t\treturn portid;\n+\t\tfor (i = 0; i < nb_fwd_ports; ++i)\n+\t\t\tif (ip == ipv4_table[i])\n+\t\t\t\treturn i;\n+\t\treturn portid;\n+\t}\n+\treturn route;\n+}\n+\n+static inline void __attribute__((always_inline))\n+log_stat(uint16_t port, uint16_t queue, uint64_t pkt,\n+\t\tuint64_t pktloss, uint64_t t, uint16_t is_tx)\n+{\n+\tif (is_tx) {\n+\t\tport_stat[port].pkt_loss[queue] += pktloss;\n+\t\tport_stat[port].tx_stat[queue].run[pkt]++;\n+\t\tport_stat[port].tx_stat[queue].cycle[pkt] += t;\n+\t\tif (port_stat[port].tx_stat[queue].cycle_min[pkt] > t)\n+\t\t\tport_stat[port].tx_stat[queue].cycle_min[pkt] = t;\n+\t\tif (port_stat[port].tx_stat[queue].cycle_max[pkt] < t)\n+\t\t\tport_stat[port].tx_stat[queue].cycle_max[pkt] = t;\n+\t} else {\n+\t\tport_stat[port].rx_stat[queue].run[pkt]++;\n+\t\tport_stat[port].rx_stat[queue].cycle[pkt] += t;\n+\t\tif (port_stat[port].rx_stat[queue].cycle_min[pkt] > t)\n+\t\t\tport_stat[port].rx_stat[queue].cycle_min[pkt] = t;\n+\t\tif (port_stat[port].rx_stat[queue].cycle_max[pkt] < t)\n+\t\t\tport_stat[port].rx_stat[queue].cycle_max[pkt] = t;\n+\t}\n+}\n+\n+static inline uint32_t __attribute__((always_inline))\n+send_single_packet(uint16_t dst_port, uint16_t src_port,\n+\t\tuint16_t src_queue, struct rte_mbuf *m)\n+{\n+\tstruct rte_mbuf **pkts_burst;\n+\tstruct fwd_stream_buffer *fsb;\n+\tuint32_t len;\n+\tuint16_t dst_queue;\n+\tuint32_t nb_tx;\n+\tuint32_t ret;\n+#if PORTFWD_PERF_STATS\n+\tuint64_t t[2];\n+#endif\n+\n+\tret = 0;\n+\tfsb = &fs_buf[src_port][src_queue];\n+\tlen = fsb->tx_mbufs[dst_port].len;\n+\tfsb->tx_mbufs[dst_port].m_table[len] = m;\n+\t++len;\n+\tif (len == DEF_PKT_BURST) {\n+\t\tdst_queue = fsb->dst_queue;\n+\t\tpkts_burst = fsb->tx_mbufs[dst_port].m_table;\n+\t\trte_spinlock_lock(&(txq_lock[dst_port][dst_queue].spinlock));\n+#if PORTFWD_PERF_STATS\n+\t\tt[0] = rte_rdtsc();\n+#endif\n+\t\tnb_tx = rte_eth_tx_burst(dst_port, dst_queue,\n+\t\t\t\tpkts_burst, DEF_PKT_BURST);\n+#if PORTFWD_PERF_STATS\n+\t\tt[1] = rte_rdtsc();\n+\t\tlog_stat(dst_port, dst_queue, nb_tx, DEF_PKT_BURST - nb_tx,\n+\t\t\t\tt[1] - t[0], 1);\n+#endif\n+\t\trte_spinlock_unlock(&(txq_lock[dst_port][dst_queue].spinlock));\n+\t\tret = nb_tx;\n+\t\tif (nb_tx < DEF_PKT_BURST)\n+\t\t\tdo {\n+\t\t\t\trte_pktmbuf_free(pkts_burst[nb_tx]);\n+\t\t\t} while (++nb_tx < DEF_PKT_BURST);\n+\t\tlen = 0;\n+\t}\n+\tfsb->tx_mbufs[dst_port].len = len;\n+\treturn ret;\n+}\n+\n+static inline uint32_t __attribute__((always_inline))\n+drain_packets(uint16_t src_port, uint16_t src_queue)\n+{\n+\tstruct rte_mbuf **pkts_burst;\n+\tstruct fwd_stream_buffer *fsb;\n+\tuint16_t dst_port;\n+\tuint16_t dst_queue;\n+\tuint16_t len;\n+\tuint16_t nb_tx;\n+\tuint32_t ret;\n+#if PORTFWD_PERF_STATS\n+\tuint64_t t[2];\n+#endif\n+\n+\tret = 0;\n+\tfor (dst_port = 0; dst_port < nb_fwd_ports; ++dst_port) {\n+\t\tfsb = &fs_buf[src_port][src_queue];\n+\t\tlen = fsb->tx_mbufs[dst_port].len;\n+\t\tif (len > 0) {\n+\t\t\tdst_queue = fsb->dst_queue;\n+\t\t\tpkts_burst =\n+\t\t\t\tfsb->tx_mbufs[dst_port].m_table;\n+\t\t\trte_spinlock_lock(\n+\t\t\t\t&(txq_lock[dst_port][dst_queue].spinlock));\n+#if PORTFWD_PERF_STATS\n+\t\t\tt[0] = rte_rdtsc();\n+#endif\n+\t\t\tnb_tx = rte_eth_tx_burst(dst_port, dst_queue,\n+\t\t\t\t\tpkts_burst, len);\n+#if PORTFWD_PERF_STATS\n+\t\t\tt[1] = rte_rdtsc();\n+\t\t\tlog_stat(dst_port, dst_queue, nb_tx, len - nb_tx,\n+\t\t\t\t\tt[1] - t[0], 1);\n+#endif\n+\t\t\trte_spinlock_unlock(\n+\t\t\t\t&(txq_lock[dst_port][dst_queue].spinlock));\n+\t\t\tret += nb_tx;\n+\t\t\tif (nb_tx < len)\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 < len);\n+\t\t\tfsb->tx_mbufs[dst_port].len = 0;\n+\t\t}\n+\t}\n+\treturn ret;\n+}\n+\n+static inline uint32_t __attribute__((always_inline))\n+batch_send_ipv4_packets(uint16_t src_port, uint16_t src_queue,\n+\t\tstruct rte_mbuf *m[], uint16_t len)\n+{\n+\tstruct ipv4_hdr *ipv4_hdr[2];\n+\tstruct rte_mbuf **mp;\n+\tuint16_t dst_port[2];\n+\tuint16_t nb_tx;\n+\n+\tnb_tx = 0;\n+\tmp = m;\n+\twhile (len >= 4) {\n+\t\trte_prefetch0((void *)mp[2]);\n+\t\trte_prefetch0((void *)mp[3]);\n+\t\tipv4_hdr[0] = rte_pktmbuf_mtod_offset(mp[0], struct ipv4_hdr *,\n+\t\t\t\tsizeof(struct ether_hdr));\n+\t\tipv4_hdr[1] = rte_pktmbuf_mtod_offset(mp[1], struct ipv4_hdr *,\n+\t\t\t\tsizeof(struct ether_hdr));\n+\t\tdst_port[0] = get_ipv4_dst_port(ipv4_hdr[0], src_port);\n+\t\tdst_port[1] = get_ipv4_dst_port(ipv4_hdr[1], src_port);\n+\t\tnb_tx += send_single_packet(dst_port[0], src_port,\n+\t\t\t\tsrc_queue, mp[0]);\n+\t\tnb_tx += send_single_packet(dst_port[1], src_port,\n+\t\t\t\tsrc_queue, mp[1]);\n+\t\tmp += 2;\n+\t\tlen -= 2;\n+\t}\n+\twhile (len > 0) {\n+\t\tipv4_hdr[0] = rte_pktmbuf_mtod_offset(mp[0], struct ipv4_hdr *,\n+\t\t\t\tsizeof(struct ether_hdr));\n+\t\tdst_port[0] = get_ipv4_dst_port(ipv4_hdr[0], src_port);\n+\t\tnb_tx += send_single_packet(dst_port[0], src_port,\n+\t\t\t\tsrc_queue, mp[0]);\n+\t\tmp += 1;\n+\t\tlen -= 1;\n+\t}\n+\n+\treturn nb_tx;\n+}\n+\n+static void\n+pkt_burst_ip_forward(struct fwd_stream *fs)\n+{\n+\tstruct rte_mbuf *pkts_burst[DEF_PKT_BURST];\n+\tuint32_t nb_rx;\n+\tuint32_t nb_tx;\n+\tuint16_t src_port = fs->rx_port;\n+\tuint16_t src_queue = fs->rx_queue;\n+#if PORTFWD_PERF_STATS\n+\tuint64_t t[2];\n+#endif\n+\n+\t/* send timeout packets */\n+\tif (drain_cycle > 0) {\n+\t\tdrainer[src_port][src_queue].cycle_now = rte_rdtsc();\n+\t\tif (drainer[src_port][src_queue].cycle_now -\n+\t\t\t\tdrainer[src_port][src_queue].cycle_last >\n+\t\t\t\tdrain_cycle) {\n+\t\t\tnb_tx = drain_packets(src_port, src_queue);\n+\t\t\tfs->tx_packets += nb_tx;\n+\t\t\tdrainer[src_port][src_queue].cycle_last =\n+\t\t\t\tdrainer[src_port][src_queue].cycle_now;\n+\t\t}\n+\t}\n+\n+#if PORTFWD_PERF_STATS\n+\tt[0] = rte_rdtsc();\n+#endif\n+\tnb_rx = rte_eth_rx_burst(src_port, src_queue,\n+\t\t\tpkts_burst, nb_pkt_per_burst);\n+#if PORTFWD_PERF_STATS\n+\tt[1] = rte_rdtsc();\n+\tlog_stat(src_port, src_queue, nb_rx, 0, t[1] - t[0], 0);\n+#endif\n+\tif (unlikely(nb_rx == 0))\n+\t\treturn;\n+\tfs->rx_packets += nb_rx;\n+\tnb_tx = 0;\n+\t/* assume ipv4 packet */\n+\tnb_tx += batch_send_ipv4_packets(src_port, src_queue,\n+\t\t\tpkts_burst, nb_rx);\n+\tfs->tx_packets += nb_tx;\n+\tfs->fwd_dropped = fs->rx_packets - fs->tx_packets;\n+}\n+\n+struct fwd_engine port_fwd_engine = {\n+\t.fwd_mode_name  = \"port\",\n+\t.port_fwd_begin = NULL,\n+\t.port_fwd_end   = NULL,\n+\t.packet_fwd     = pkt_burst_ip_forward,\n+};\ndiff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c\nindex 26a174c..3316400 100644\n--- a/app/test-pmd/testpmd.c\n+++ b/app/test-pmd/testpmd.c\n@@ -138,11 +138,26 @@ portid_t fwd_ports_ids[RTE_MAX_ETHPORTS];      /**< Port ids configuration. */\n struct fwd_stream **fwd_streams; /**< For each RX queue of each port. */\n streamid_t nb_fwd_streams;       /**< Is equal to (nb_ports * nb_rxq). */\n \n+unsigned long drain_cycle = 10000;\n+struct drain_counter drainer[RTE_MAX_ETHPORTS][MAX_RX_QUEUE];\n+\n+uint32_t ipv4_table[RTE_MAX_ETHPORTS];\n+\n+uint32_t ipv4_route[MAX_IP_DIFF];\n+uint32_t ipv4_route_available;\n+uint32_t route_table[RTE_MAX_ETHPORTS];\n+\n+struct fwd_stream_buffer fs_buf[RTE_MAX_ETHPORTS][MAX_RX_QUEUE];\n+struct queue_lock txq_lock[RTE_MAX_ETHPORTS][MAX_TX_QUEUE];\n+\n+volatile int fwd_config_init;\n+\n /*\n  * Forwarding engines.\n  */\n struct fwd_engine * fwd_engines[] = {\n \t&io_fwd_engine,\n+\t&port_fwd_engine,\n \t&mac_fwd_engine,\n \t&mac_retry_fwd_engine,\n \t&mac_swap_engine,\n@@ -1002,6 +1017,7 @@ start_packet_forwarding(int with_tx_first)\n \tif(!no_flush_rx)\n \t\tflush_fwd_rx_queues();\n \n+\tinit_txq_lock();\n \tfwd_config_setup();\n \trxtx_config_display();\n \n@@ -2063,6 +2079,9 @@ main(int argc, char** argv)\n \t\t       \"but nb_txq=%d will prevent to fully test it.\\n\",\n \t\t       nb_rxq, nb_txq);\n \n+\tadd_port_addr();\n+\tset_drain_interval_ns(DRAIN_INTERVAL_NS);\n+\n \tinit_config();\n \tif (start_port(RTE_PORT_ALL) != 0)\n \t\trte_exit(EXIT_FAILURE, \"Start ports failed\\n\");\ndiff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h\nindex 0f72ca1..7cbfc5f 100644\n--- a/app/test-pmd/testpmd.h\n+++ b/app/test-pmd/testpmd.h\n@@ -34,6 +34,8 @@\n #ifndef _TESTPMD_H_\n #define _TESTPMD_H_\n \n+#include <rte_spinlock.h>\n+\n #define RTE_PORT_ALL            (~(portid_t)0x0)\n \n #define RTE_TEST_RX_DESC_MAX    2048\n@@ -103,6 +105,8 @@ struct fwd_stream {\n \tqueueid_t  tx_queue;  /**< TX queue to send forwarded packets */\n \tstreamid_t peer_addr; /**< index of peer ethernet address of packets */\n \n+\tunsigned int idx;\n+\n \t/* \"read-write\" results */\n \tunsigned int rx_packets;  /**< received packets */\n \tunsigned int tx_packets;  /**< received packets transmitted */\n@@ -221,6 +225,7 @@ struct fwd_engine {\n };\n \n extern struct fwd_engine io_fwd_engine;\n+extern struct fwd_engine port_fwd_engine;\n extern struct fwd_engine mac_fwd_engine;\n extern struct fwd_engine mac_retry_fwd_engine;\n extern struct fwd_engine mac_swap_engine;\n@@ -329,6 +334,48 @@ extern portid_t nb_fwd_ports; /**< Number of forwarding ports. */\n extern portid_t fwd_ports_ids[RTE_MAX_ETHPORTS];\n extern struct rte_port *ports;\n \n+#define MAX_RX_QUEUE 32\n+#define MAX_TX_QUEUE 32\n+#define PORT_ROUTE_IP 65536\n+#define IPV4_ROUTE_MASK\t0x3F\n+#define MAX_IP_DIFF\t((IPV4_ROUTE_MASK) + 1)\n+#define DRAIN_INTERVAL_NS 5000\n+\n+struct mbuf_table {\n+\tuint16_t len;\n+\tstruct rte_mbuf *m_table[DEF_PKT_BURST];\n+};\n+\n+struct fwd_stream_buffer {\n+\tstruct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS];\n+\tuint16_t dst_queue;\n+} __rte_cache_aligned;\n+\n+struct drain_counter {\n+\tunsigned long cycle_now;\n+\tunsigned long cycle_last;\n+} __rte_cache_aligned;\n+\n+struct queue_lock {\n+\trte_spinlock_t spinlock;\n+} __rte_cache_aligned;\n+\n+extern unsigned long drain_cycle;\n+extern struct drain_counter drainer[RTE_MAX_ETHPORTS][MAX_RX_QUEUE];\n+\n+extern streamid_t nb_fwd_streams;\n+\n+extern uint32_t ipv4_table[RTE_MAX_ETHPORTS];\n+\n+extern uint32_t route_table[RTE_MAX_ETHPORTS];\n+extern uint32_t ipv4_route[MAX_IP_DIFF];\n+extern uint32_t ipv4_route_available;\n+\n+extern struct fwd_stream_buffer fs_buf[RTE_MAX_ETHPORTS][MAX_RX_QUEUE];\n+extern struct queue_lock txq_lock[RTE_MAX_ETHPORTS][MAX_TX_QUEUE];\n+\n+extern volatile int fwd_config_init;\n+\n extern struct rte_eth_rxmode rx_mode;\n extern uint64_t rss_hf;\n \n@@ -575,6 +622,21 @@ void mcast_addr_add(uint8_t port_id, struct ether_addr *mc_addr);\n void mcast_addr_remove(uint8_t port_id, struct ether_addr *mc_addr);\n void port_dcb_info_display(uint8_t port_id);\n \n+void set_fixed_route(uint16_t srcp, uint16_t dstp);\n+void set_ip_route(uint16_t srcp);\n+void show_route(void);\n+void init_txq_lock(void);\n+void set_ip(uint32_t srcp, uint32_t num0, uint32_t num1,\n+\t\tuint32_t num2, uint32_t num3);\n+void add_port_addr(void);\n+void build_ipv4_route(void);\n+void set_drain_interval_ns(unsigned long drain_ns);\n+void print_perf_stats(void);\n+void clear_perf_stats(void);\n+int set_fwd_stream_affinity(unsigned int idx, unsigned int core);\n+void print_port_info(void);\n+\n+\n enum print_warning {\n \tENABLED_WARN = 0,\n \tDISABLED_WARN\n",
    "prefixes": [
        "dpdk-dev",
        "RFC",
        "1/2"
    ]
}