get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2383,
    "url": "https://patches.dpdk.org/api/patches/2383/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/1421671390-8560-3-git-send-email-tomaszx.kulasek@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": "<1421671390-8560-3-git-send-email-tomaszx.kulasek@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1421671390-8560-3-git-send-email-tomaszx.kulasek@intel.com",
    "date": "2015-01-19T12:43:10",
    "name": "[dpdk-dev,v2,2/2] Unit tests for mode 4",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "a1129b7c476149bfe83c71a6c05967dfc66a55b6",
    "submitter": {
        "id": 155,
        "url": "https://patches.dpdk.org/api/people/155/?format=api",
        "name": "Tomasz Kulasek",
        "email": "tomaszx.kulasek@intel.com"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/1421671390-8560-3-git-send-email-tomaszx.kulasek@intel.com/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/2383/comments/",
    "check": "pending",
    "checks": "https://patches.dpdk.org/api/patches/2383/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 5518C5A82;\n\tMon, 19 Jan 2015 13:45:30 +0100 (CET)",
            "from mga09.intel.com (mga09.intel.com [134.134.136.24])\n\tby dpdk.org (Postfix) with ESMTP id 222A05A80\n\tfor <dev@dpdk.org>; Mon, 19 Jan 2015 13:45:25 +0100 (CET)",
            "from orsmga001.jf.intel.com ([10.7.209.18])\n\tby orsmga102.jf.intel.com with ESMTP; 19 Jan 2015 04:42:31 -0800",
            "from unknown (HELO Sent) ([10.217.248.44])\n\tby orsmga001.jf.intel.com with SMTP; 19 Jan 2015 04:45:22 -0800",
            "by Sent (sSMTP sendmail emulation); Mon, 19 Jan 2015 13:45:00 +0200"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.09,426,1418112000\"; d=\"scan'208\";a=\"639304023\"",
        "From": "Tomasz Kulasek <tomaszx.kulasek@intel.com>",
        "To": "dev@dpdk.org",
        "Date": "Mon, 19 Jan 2015 13:43:10 +0100",
        "Message-Id": "<1421671390-8560-3-git-send-email-tomaszx.kulasek@intel.com>",
        "X-Mailer": "git-send-email 2.1.4",
        "In-Reply-To": "<1421671390-8560-1-git-send-email-tomaszx.kulasek@intel.com>",
        "References": "<1418375682-4703-4-git-send-email-michalx.k.jastrzebski@intel.com>\n\t<1421671390-8560-1-git-send-email-tomaszx.kulasek@intel.com>",
        "Subject": "[dpdk-dev] [PATCH v2 2/2] Unit tests for mode 4",
        "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 adds unit tests for mode 4. It is split into separate\nfile to avoid problems with other modes that does not need to\nlook into packets payload.\nThis patch includes also a modification of maximum number of ports\nused in their tests for bonding modes 0-3 from 16 to 6.\n\nSigned-off-by: Tomasz Kulasek <tomaszx.kulasek@intel.com>\nSigned-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>\n\n---\n app/test/Makefile                  |    1 +\n app/test/test_link_bonding.c       |    2 +-\n app/test/test_link_bonding_mode4.c | 1412 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 1414 insertions(+), 1 deletion(-)\n create mode 100644 app/test/test_link_bonding_mode4.c",
    "diff": "diff --git a/app/test/Makefile b/app/test/Makefile\nindex 4311f96..ee0e95a 100644\n--- a/app/test/Makefile\n+++ b/app/test/Makefile\n@@ -129,6 +129,7 @@ SRCS-y += virtual_pmd.c\n SRCS-y += packet_burst_generator.c\n SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c\n SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += test_link_bonding.c\n+SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += test_link_bonding_mode4.c\n SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c\n SRCS-$(CONFIG_RTE_LIBRTE_KVARGS) += test_kvargs.c\n \ndiff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c\nindex 4523de6..6d5b383 100644\n--- a/app/test/test_link_bonding.c\n+++ b/app/test/test_link_bonding.c\n@@ -57,7 +57,7 @@\n \n #include \"test.h\"\n \n-#define TEST_MAX_NUMBER_OF_PORTS (16)\n+#define TEST_MAX_NUMBER_OF_PORTS (6)\n \n #define RX_RING_SIZE 128\n #define RX_FREE_THRESH 32\ndiff --git a/app/test/test_link_bonding_mode4.c b/app/test/test_link_bonding_mode4.c\nnew file mode 100644\nindex 0000000..f8d0955\n--- /dev/null\n+++ b/app/test/test_link_bonding_mode4.c\n@@ -0,0 +1,1412 @@\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 <string.h>\n+#include <stdarg.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <stdint.h>\n+#include <inttypes.h>\n+#include <errno.h>\n+#include <rte_cycles.h>\n+#include <sys/queue.h>\n+\n+#include <rte_byteorder.h>\n+#include <rte_common.h>\n+#include <rte_debug.h>\n+#include <rte_ethdev.h>\n+#include <rte_log.h>\n+#include <rte_lcore.h>\n+#include <rte_memory.h>\n+\n+#include <rte_string_fns.h>\n+\n+#include <rte_eth_ring.h>\n+#include <rte_errno.h>\n+#include <rte_eth_bond.h>\n+#include <rte_eth_bond_8023ad.h>\n+\n+#include \"packet_burst_generator.h\"\n+\n+#include \"test.h\"\n+\n+#define SLAVE_COUNT (4)\n+\n+#define RX_RING_SIZE 128\n+#define TX_RING_SIZE 512\n+\n+#define MBUF_PAYLOAD_SIZE\t    (2048)\n+#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + \\\n+\tRTE_PKTMBUF_HEADROOM)\n+#define MBUF_CACHE_SIZE         (250)\n+#define BURST_SIZE              (32)\n+\n+#define DEFAULT_MBUF_DATA_SIZE  (2048)\n+#define TEST_RX_DESC_MAX        (2048)\n+#define TEST_TX_DESC_MAX        (2048)\n+#define MAX_PKT_BURST           (32)\n+#define DEF_PKT_BURST           (16)\n+\n+#define BONDED_DEV_NAME         (\"unit_test_mode4_bond_dev\")\n+\n+#define SLAVE_DEV_NAME_FMT      (\"unit_test_mode4_slave_%d\")\n+#define SLAVE_RX_QUEUE_FMT      (\"unit_test_mode4_slave_%d_rx\")\n+#define SLAVE_TX_QUEUE_FMT      (\"unit_test_mode4_slave_%d_tx\")\n+\n+#define INVALID_SOCKET_ID       (-1)\n+#define INVALID_PORT_ID         (0xFF)\n+#define INVALID_BONDING_MODE    (-1)\n+\n+static const struct ether_addr slave_mac_default = {\n+\t{ 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }\n+};\n+\n+static const struct ether_addr parnter_mac_default = {\n+\t{ 0x22, 0xBB, 0xFF, 0xBB, 0x00, 0x00 }\n+};\n+\n+static const struct ether_addr parnter_system = {\n+\t{ 0x33, 0xFF, 0xBB, 0xFF, 0x00, 0x00 }\n+};\n+\n+static const struct ether_addr slow_protocol_mac_addr = {\n+\t{ 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 }\n+};\n+\n+struct slave_conf {\n+\tstruct rte_ring *rx_queue;\n+\tstruct rte_ring *tx_queue;\n+\tuint8_t port_id;\n+\tuint8_t bonded : 1;\n+\n+\tuint8_t lacp_parnter_state;\n+};\n+\n+struct ether_vlan_hdr {\n+\tstruct ether_hdr pkt_eth_hdr;\n+\tstruct vlan_hdr vlan_hdr;\n+};\n+\n+struct link_bonding_unittest_params {\n+\tuint8_t bonded_port_id;\n+\tstruct slave_conf slave_ports[SLAVE_COUNT];\n+\n+\tstruct rte_mempool *mbuf_pool;\n+};\n+\n+#define TEST_DEFAULT_SLAVE_COUNT     RTE_DIM(test_params.slave_ports)\n+#define TEST_RX_SLAVE_COUT           TEST_DEFAULT_SLAVE_COUNT\n+#define TEST_TX_SLAVE_COUNT          TEST_DEFAULT_SLAVE_COUNT\n+#define TEST_MARKER_SLAVE_COUT       TEST_DEFAULT_SLAVE_COUNT\n+#define TEST_EXPIRED_SLAVE_COUNT     TEST_DEFAULT_SLAVE_COUNT\n+#define TEST_PROMISC_SLAVE_COUNT     TEST_DEFAULT_SLAVE_COUNT\n+\n+static struct link_bonding_unittest_params test_params  = {\n+\t.bonded_port_id = INVALID_PORT_ID,\n+\t.slave_ports = { [0 ... SLAVE_COUNT - 1] = { .port_id = INVALID_PORT_ID} },\n+\n+\t.mbuf_pool = NULL,\n+};\n+\n+static struct rte_eth_conf default_pmd_conf = {\n+\t.rxmode = {\n+\t\t.mq_mode = ETH_MQ_RX_NONE,\n+\t\t.max_rx_pkt_len = ETHER_MAX_LEN,\n+\t\t.split_hdr_size = 0,\n+\t\t.header_split   = 0, /**< Header Split disabled */\n+\t\t.hw_ip_checksum = 0, /**< IP checksum offload enabled */\n+\t\t.hw_vlan_filter = 0, /**< VLAN filtering disabled */\n+\t\t.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */\n+\t\t.hw_strip_crc   = 0, /**< CRC stripped by hardware */\n+\t},\n+\t.txmode = {\n+\t\t.mq_mode = ETH_MQ_TX_NONE,\n+\t},\n+\t.lpbk_mode = 0,\n+};\n+\n+#define FOR_EACH(_i, _item, _array, _size) \\\n+\tfor (_i = 0, _item = &_array[0]; _i < _size && (_item = &_array[_i]); _i++)\n+\n+/* Macro for iterating over every port that can be used as a slave\n+ * in this test.\n+ * _i variable used as an index in test_params->slave_ports\n+ * _slave pointer to &test_params->slave_ports[_idx]\n+ */\n+#define FOR_EACH_PORT(_i, _port) \\\n+\tFOR_EACH(_i, _port, test_params.slave_ports, \\\n+\t\tRTE_DIM(test_params.slave_ports))\n+\n+/* Macro for iterating over every port that can be used as a slave\n+ * in this test and satisfy given condition.\n+ *\n+ * _i variable used as an index in test_params->slave_ports\n+ * _slave pointer to &test_params->slave_ports[_idx]\n+ * _condition condition that need to be checked\n+ */\n+#define FOR_EACH_PORT_IF(_i, _port, _condition) FOR_EACH_PORT((_i), (_port)) \\\n+\tif (!!(_condition))\n+\n+/* Macro for iterating over every port that is currently a slave of a bonded\n+ * device.\n+ * _i variable used as an index in test_params->slave_ports\n+ * _slave pointer to &test_params->slave_ports[_idx]\n+ * */\n+#define FOR_EACH_SLAVE(_i, _slave) \\\n+\tFOR_EACH_PORT_IF(_i, _slave, (_slave)->bonded != 0)\n+\n+/*\n+ * Returns packets from slaves TX queue.\n+ * slave slave port\n+ * buffer for packets\n+ * size size of buffer\n+ * return number of packets or negative error number\n+ */\n+static int\n+slave_get_pkts(struct slave_conf *slave, struct rte_mbuf **buf, uint16_t size)\n+{\n+\treturn rte_ring_dequeue_burst(slave->tx_queue, (void **)buf, size);\n+}\n+\n+/*\n+ * Injects given packets into slaves RX queue.\n+ * slave slave port\n+ * buffer for packets\n+ * size number of packets to be injected\n+ * return number of queued packets or negative error number\n+ */\n+static int\n+slave_put_pkts(struct slave_conf *slave, struct rte_mbuf **buf, uint16_t size)\n+{\n+\treturn rte_ring_enqueue_burst(slave->rx_queue, (void **)buf, size);\n+}\n+\n+static uint16_t\n+bond_rx(struct rte_mbuf **buf, uint16_t size)\n+{\n+\treturn rte_eth_rx_burst(test_params.bonded_port_id, 0, buf, size);\n+}\n+\n+static uint16_t\n+bond_tx(struct rte_mbuf **buf, uint16_t size)\n+{\n+\treturn rte_eth_tx_burst(test_params.bonded_port_id, 0, buf, size);\n+}\n+\n+static void\n+free_pkts(struct rte_mbuf **pkts, uint16_t count)\n+{\n+\tuint16_t i;\n+\n+\tfor (i = 0; i < count; i++) {\n+\t\tif (pkts[i] != NULL)\n+\t\t\trte_pktmbuf_free(pkts[i]);\n+\t}\n+}\n+\n+static int\n+configure_ethdev(uint8_t port_id, uint8_t start)\n+{\n+\tTEST_ASSERT(rte_eth_dev_configure(port_id, 1, 1, &default_pmd_conf) == 0,\n+\t\t\"Failed to configure device %u\", port_id);\n+\n+\tTEST_ASSERT(rte_eth_rx_queue_setup(port_id, 0, RX_RING_SIZE,\n+\t\trte_eth_dev_socket_id(port_id), NULL, test_params.mbuf_pool) == 0,\n+\t\t\"Failed to setup rx queue.\");\n+\n+\tTEST_ASSERT(rte_eth_tx_queue_setup(port_id, 0, TX_RING_SIZE,\n+\t\trte_eth_dev_socket_id(port_id), NULL) == 0,\n+\t\t\"Failed to setup tx queue.\");\n+\n+\tif (start) {\n+\t\tTEST_ASSERT(rte_eth_dev_start(port_id) == 0,\n+\t\t\"Failed to start device (%d).\", port_id);\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+add_slave(struct slave_conf *slave, uint8_t start)\n+{\n+\tstruct ether_addr addr, addr_check;\n+\n+\t/* Some sanity check */\n+\tRTE_VERIFY(test_params.slave_ports <= slave &&\n+\t\tslave - test_params.slave_ports < (int)RTE_DIM(test_params.slave_ports));\n+\tRTE_VERIFY(slave->bonded == 0);\n+\tRTE_VERIFY(slave->port_id != INVALID_PORT_ID);\n+\n+\tether_addr_copy(&slave_mac_default, &addr);\n+\taddr.addr_bytes[ETHER_ADDR_LEN - 1] = slave->port_id;\n+\n+\trte_eth_dev_mac_addr_remove(slave->port_id, &addr);\n+\n+\tTEST_ASSERT_SUCCESS(rte_eth_dev_mac_addr_add(slave->port_id, &addr, 0),\n+\t\t\"Failed to set slave MAC address\");\n+\n+\tTEST_ASSERT_SUCCESS(rte_eth_bond_slave_add(test_params.bonded_port_id,\n+\t\tslave->port_id),\n+\t\t\t\"Failed to add slave (idx=%u, id=%u) to bonding (id=%u)\",\n+\t\t\t(uint8_t)(slave - test_params.slave_ports), slave->port_id,\n+\t\t\ttest_params.bonded_port_id);\n+\n+\tslave->bonded = 1;\n+\tif (start) {\n+\t\tTEST_ASSERT_SUCCESS(rte_eth_dev_start(slave->port_id),\n+\t\t\t\"Failed to start slave %u\", slave->port_id);\n+\t}\n+\n+\trte_eth_macaddr_get(slave->port_id, &addr_check);\n+\tTEST_ASSERT_EQUAL(is_same_ether_addr(&addr, &addr_check), 1,\n+\t\t\t\"Slave MAC address is not as expected\");\n+\n+\tRTE_VERIFY(slave->lacp_parnter_state == 0);\n+\treturn 0;\n+}\n+\n+static int\n+remove_slave(struct slave_conf *slave)\n+{\n+\tptrdiff_t slave_idx = slave - test_params.slave_ports;\n+\n+\tRTE_VERIFY(test_params.slave_ports <= slave &&\n+\t\tslave_idx < (ptrdiff_t)RTE_DIM(test_params.slave_ports));\n+\n+\tRTE_VERIFY(slave->bonded == 1);\n+\tRTE_VERIFY(slave->port_id != INVALID_PORT_ID);\n+\n+\tTEST_ASSERT_EQUAL(rte_ring_count(slave->rx_queue), 0,\n+\t\t\"Slave %u tx queue not empty while removing from bonding.\",\n+\t\tslave->port_id);\n+\n+\tTEST_ASSERT_EQUAL(rte_ring_count(slave->rx_queue), 0,\n+\t\t\"Slave %u tx queue not empty while removing from bonding.\",\n+\t\tslave->port_id);\n+\n+\tTEST_ASSERT_EQUAL(rte_eth_bond_slave_remove(test_params.bonded_port_id,\n+\t\t\tslave->port_id), 0,\n+\t\t\t\"Failed to remove slave (idx=%u, id=%u) from bonding (id=%u)\",\n+\t\t\t(uint8_t)slave_idx, slave->port_id,\n+\t\t\ttest_params.bonded_port_id);\n+\n+\tslave->bonded = 0;\n+\tslave->lacp_parnter_state = 0;\n+\treturn 0;\n+}\n+\n+static int\n+initialize_bonded_device_with_slaves(uint8_t slave_count, uint8_t start)\n+{\n+\tuint8_t i;\n+\n+\tRTE_VERIFY(test_params.bonded_port_id != INVALID_PORT_ID);\n+\n+\tfor (i = 0; i < slave_count; i++) {\n+\t\tTEST_ASSERT_SUCCESS(add_slave(&test_params.slave_ports[i], 1),\n+\t\t\t\"Failed to add port %u to bonded device.\\n\",\n+\t\t\ttest_params.slave_ports[i].port_id);\n+\t}\n+\n+\t/* Reset mode 4 configuration */\n+\trte_eth_bond_8023ad_setup(test_params.bonded_port_id, NULL);\n+\trte_eth_promiscuous_disable(test_params.bonded_port_id);\n+\n+\tif (start)\n+\t\tTEST_ASSERT_SUCCESS(rte_eth_dev_start(test_params.bonded_port_id),\n+\t\t\t\"Failed to start bonded device\");\n+\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+remove_slaves_and_stop_bonded_device(void)\n+{\n+\tstruct slave_conf *slave;\n+\tint retval;\n+\tuint8_t slaves[RTE_MAX_ETHPORTS];\n+\tuint8_t i;\n+\n+\trte_eth_dev_stop(test_params.bonded_port_id);\n+\n+\tFOR_EACH_SLAVE(i, slave)\n+\t\tremove_slave(slave);\n+\n+\tretval = rte_eth_bond_slaves_get(test_params.bonded_port_id, slaves,\n+\t\tRTE_DIM(slaves));\n+\n+\tTEST_ASSERT_EQUAL(retval, 0,\n+\t\t\"Expected bonded device %u have 0 slaves but returned %d.\",\n+\t\t\ttest_params.bonded_port_id, retval);\n+\n+\tFOR_EACH_PORT(i, slave) {\n+\t\trte_eth_dev_stop(slave->port_id);\n+\n+\t\tTEST_ASSERT(slave->bonded == 0,\n+\t\t\t\"Port id=%u is still marked as enslaved.\", slave->port_id);\n+\t}\n+\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+test_setup(void)\n+{\n+\tint retval, nb_mbuf_per_pool;\n+\tchar name[RTE_ETH_NAME_MAX_LEN];\n+\tstruct slave_conf *port;\n+\tconst uint8_t socket_id = rte_socket_id();\n+\tuint8_t i;\n+\n+\tif (test_params.mbuf_pool == NULL) {\n+\t\tnb_mbuf_per_pool = TEST_RX_DESC_MAX + DEF_PKT_BURST +\n+\t\t\t\t\tTEST_TX_DESC_MAX + MAX_PKT_BURST;\n+\t\ttest_params.mbuf_pool = rte_mempool_create(\"TEST_MODE4\",\n+\t\t\t\tnb_mbuf_per_pool, MBUF_SIZE, MBUF_CACHE_SIZE,\n+\t\t\t\tsizeof(struct rte_pktmbuf_pool_private),\n+\t\t\t\trte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL,\n+\t\t\t\tsocket_id, 0);\n+\n+\t\tTEST_ASSERT(test_params.mbuf_pool != NULL,\n+\t\t\t\"rte_mempool_create failed\\n\");\n+\t}\n+\n+\t/* Create / initialize ring eth devs. */\n+\tFOR_EACH_PORT(i, port) {\n+\t\tport = &test_params.slave_ports[i];\n+\n+\t\tif (port->rx_queue == NULL) {\n+\t\t\tretval = snprintf(name, RTE_DIM(name), SLAVE_RX_QUEUE_FMT, i);\n+\t\t\tTEST_ASSERT(retval <= (int)RTE_DIM(name) - 1, \"Name too long\");\n+\t\t\tport->rx_queue = rte_ring_create(name, RX_RING_SIZE, socket_id, 0);\n+\t\t\tTEST_ASSERT(port->rx_queue != NULL,\n+\t\t\t\t\"Failed to allocate rx ring '%s': %s\", name,\n+\t\t\t\trte_strerror(rte_errno));\n+\t\t}\n+\n+\t\tif (port->tx_queue == NULL) {\n+\t\t\tretval = snprintf(name, RTE_DIM(name), SLAVE_TX_QUEUE_FMT, i);\n+\t\t\tTEST_ASSERT(retval <= (int)RTE_DIM(name) - 1, \"Name too long\");\n+\t\t\tport->tx_queue = rte_ring_create(name, TX_RING_SIZE, socket_id, 0);\n+\t\t\tTEST_ASSERT_NOT_NULL(port->tx_queue,\n+\t\t\t\t\"Failed to allocate tx ring '%s': %s\", name,\n+\t\t\t\trte_strerror(rte_errno));\n+\t\t}\n+\n+\t\tif (port->port_id == INVALID_PORT_ID) {\n+\t\t\tretval = snprintf(name, RTE_DIM(name), SLAVE_DEV_NAME_FMT, i);\n+\t\t\tTEST_ASSERT(retval < (int)RTE_DIM(name) - 1, \"Name too long\");\n+\t\t\tretval = rte_eth_from_rings(name, &port->rx_queue, 1,\n+\t\t\t\t\t&port->tx_queue, 1, socket_id);\n+\t\t\tTEST_ASSERT(retval >= 0,\n+\t\t\t\t\"Failed to create ring ethdev '%s'\\n\", name);\n+\n+\t\t\tport->port_id = rte_eth_dev_count() - 1;\n+\t\t}\n+\n+\t\tretval = configure_ethdev(port->port_id, 1);\n+\t\tTEST_ASSERT_SUCCESS(retval, \"Failed to configure virtual ethdev %s\\n\",\n+\t\t\tname);\n+\t}\n+\n+\tif (test_params.bonded_port_id == INVALID_PORT_ID) {\n+\t\tretval = rte_eth_bond_create(BONDED_DEV_NAME, BONDING_MODE_8023AD,\n+\t\t\t\tsocket_id);\n+\n+\t\tTEST_ASSERT(retval >= 0, \"Failed to create bonded ethdev %s\",\n+\t\t\t\tBONDED_DEV_NAME);\n+\n+\t\ttest_params.bonded_port_id = retval;\n+\t\tTEST_ASSERT_SUCCESS(configure_ethdev(test_params.bonded_port_id, 0),\n+\t\t\t\t\"Failed to configure bonded ethdev %s\", BONDED_DEV_NAME);\n+\t} else if (rte_eth_bond_mode_get(test_params.bonded_port_id) !=\n+\t\t\tBONDING_MODE_8023AD) {\n+\t\tTEST_ASSERT(rte_eth_bond_mode_set(test_params.bonded_port_id,\n+\t\t\tBONDING_MODE_8023AD) == 0,\n+\t\t\t\"Failed to set ethdev %d to mode %d\",\n+\t\t\ttest_params.bonded_port_id, BONDING_MODE_8023AD);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+testsuite_teardown(void)\n+{\n+\tstruct slave_conf *port;\n+\tuint8_t i;\n+\n+\t/* Only stop ports.\n+\t * Any cleanup/reset state is done when particular test is\n+\t * started. */\n+\n+\trte_eth_dev_stop(test_params.bonded_port_id);\n+\n+\tFOR_EACH_PORT(i, port)\n+\t\trte_eth_dev_stop(port->port_id);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Check if given LACP packet. If it is, make make replay packet to force\n+ * COLLECTING state.\n+ * return 0 when pkt is LACP frame, 1 if it is not slow frame, 2 if it is slow\n+ * frame but not LACP\n+ */\n+static int\n+make_lacp_reply(struct slave_conf *slave, struct rte_mbuf *pkt)\n+{\n+\tstruct ether_hdr *hdr;\n+\tstruct slow_protocol_frame *slow_hdr;\n+\tstruct lacpdu *lacp;\n+\n+\t/* look for LACP */\n+\thdr = rte_pktmbuf_mtod(pkt, struct ether_hdr *);\n+\tif (hdr->ether_type != rte_cpu_to_be_16(ETHER_TYPE_SLOW))\n+\t\treturn 1;\n+\n+\tslow_hdr = rte_pktmbuf_mtod(pkt, struct slow_protocol_frame *);\n+\t/* ignore packets of other types */\n+\tif (slow_hdr->slow_protocol.subtype != SLOW_SUBTYPE_LACP)\n+\t\treturn 2;\n+\n+\tslow_hdr = rte_pktmbuf_mtod(pkt, struct slow_protocol_frame *);\n+\n+\t/* Change source address to partner address */\n+\tether_addr_copy(&parnter_mac_default, &slow_hdr->eth_hdr.s_addr);\n+\tslow_hdr->eth_hdr.s_addr.addr_bytes[ETHER_ADDR_LEN - 1] = slave->port_id;\n+\n+\tlacp = (struct lacpdu *) &slow_hdr->slow_protocol;\n+\t/* Save last received state */\n+\tslave->lacp_parnter_state = lacp->actor.state;\n+\t/* Change it into LACP replay by matching parameters. */\n+\tmemcpy(&lacp->partner.port_params, &lacp->actor.port_params,\n+\t\tsizeof(struct port_params));\n+\n+\tlacp->partner.state = lacp->actor.state;\n+\n+\tether_addr_copy(&parnter_system, &lacp->actor.port_params.system);\n+\tlacp->actor.state = STATE_LACP_ACTIVE |\n+\t\t\t\t\t\tSTATE_SYNCHRONIZATION |\n+\t\t\t\t\t\tSTATE_AGGREGATION |\n+\t\t\t\t\t\tSTATE_COLLECTING |\n+\t\t\t\t\t\tSTATE_DISTRIBUTING;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Reads packets from given slave, search for LACP packet and reply them.\n+ *\n+ * Receives burst of packets from slave. Looks for LACP packet. Drops\n+ * all other packets. Prepares response LACP and sends it back.\n+ *\n+ * return number of LACP received and replied, -1 on error.\n+ */\n+static int\n+bond_handshake_reply(struct slave_conf *slave)\n+{\n+\tint retval;\n+\tstruct rte_mbuf *rx_buf[MAX_PKT_BURST];\n+\tstruct rte_mbuf *lacp_tx_buf[MAX_PKT_BURST];\n+\tuint16_t lacp_tx_buf_cnt = 0, i;\n+\n+\tretval = slave_get_pkts(slave, rx_buf, RTE_DIM(rx_buf));\n+\tTEST_ASSERT(retval >= 0, \"Getting slave %u packets failed.\",\n+\t\t\tslave->port_id);\n+\n+\tfor (i = 0; i < (uint16_t)retval; i++) {\n+\t\tif (make_lacp_reply(slave, rx_buf[i]) == 0) {\n+\t\t\t/* reply with actor's LACP */\n+\t\t\tlacp_tx_buf[lacp_tx_buf_cnt++] = rx_buf[i];\n+\t\t} else\n+\t\t\trte_pktmbuf_free(rx_buf[i]);\n+\t}\n+\n+\tif (lacp_tx_buf_cnt == 0)\n+\t\treturn 0;\n+\n+\tretval = slave_put_pkts(slave, lacp_tx_buf, lacp_tx_buf_cnt);\n+\tif (retval <= lacp_tx_buf_cnt) {\n+\t\t/* retval might be negative */\n+\t\tfor (i = RTE_MAX(0, retval); retval < lacp_tx_buf_cnt; retval++)\n+\t\t\trte_pktmbuf_free(lacp_tx_buf[i]);\n+\t}\n+\n+\tTEST_ASSERT_EQUAL(retval, lacp_tx_buf_cnt,\n+\t\t\"Failed to equeue lacp packets into slave %u tx queue.\",\n+\t\tslave->port_id);\n+\n+\treturn lacp_tx_buf_cnt;\n+}\n+\n+/*\n+ * Function check if given slave tx queue contains packets that make mode 4\n+ * handshake complete. It will drain slave queue.\n+ * return 0 if handshake not completed, 1 if handshake was complete,\n+ */\n+static int\n+bond_handshake_done(struct slave_conf *slave)\n+{\n+\tconst uint8_t expected_state = STATE_LACP_ACTIVE | STATE_SYNCHRONIZATION |\n+\t\t\tSTATE_AGGREGATION | STATE_COLLECTING | STATE_DISTRIBUTING;\n+\n+\treturn slave->lacp_parnter_state == expected_state;\n+}\n+\n+static unsigned\n+bond_get_update_timeout_ms(void)\n+{\n+\tstruct rte_eth_bond_8023ad_conf conf;\n+\n+\trte_eth_bond_8023ad_conf_get(test_params.bonded_port_id, &conf);\n+\treturn conf.update_timeout_ms;\n+}\n+\n+/*\n+ * Exchanges LACP packets with partner to achieve dynamic port configuration.\n+ * return TEST_SUCCESS if initial handshake succeed, TEST_FAILED otherwise.\n+ */\n+static int\n+bond_handshake(void)\n+{\n+\tstruct slave_conf *slave;\n+\tstruct rte_mbuf *buf[MAX_PKT_BURST];\n+\tuint16_t nb_pkts;\n+\tuint8_t all_slaves_done, i, j;\n+\tuint8_t status[RTE_DIM(test_params.slave_ports)] = { 0 };\n+\tconst unsigned delay = bond_get_update_timeout_ms();\n+\n+\t/* Exchange LACP frames */\n+\tall_slaves_done = 0;\n+\tfor (i = 0; i < 30 && all_slaves_done == 0; ++i) {\n+\t\trte_delay_ms(delay);\n+\n+\t\tall_slaves_done = 1;\n+\t\tFOR_EACH_SLAVE(j, slave) {\n+\t\t\t/* If response already send, skip slave */\n+\t\t\tif (status[j] != 0)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (bond_handshake_reply(slave) < 0) {\n+\t\t\t\tall_slaves_done = 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tstatus[j] = bond_handshake_done(slave);\n+\t\t\tif (status[j] == 0)\n+\t\t\t\tall_slaves_done = 0;\n+\t\t}\n+\n+\t\tnb_pkts = bond_tx(NULL, 0);\n+\t\tTEST_ASSERT_EQUAL(nb_pkts, 0, \"Packets transmitted unexpectedly\");\n+\n+\t\tnb_pkts = bond_rx(buf, RTE_DIM(buf));\n+\t\tfree_pkts(buf, nb_pkts);\n+\t\tTEST_ASSERT_EQUAL(nb_pkts, 0, \"Packets received unexpectedly\");\n+\t}\n+\t/* If response didn't send - report failure */\n+\tTEST_ASSERT_EQUAL(all_slaves_done, 1, \"Bond handshake failed\\n\");\n+\n+\t/* If flags doesn't match - report failure */\n+\treturn all_slaves_done = 1 ? TEST_SUCCESS : TEST_FAILED;\n+}\n+\n+#define TEST_LACP_SLAVE_COUT RTE_DIM(test_params.slave_ports)\n+static int\n+test_mode4_lacp(void)\n+{\n+\tint retval;\n+\n+\tretval = initialize_bonded_device_with_slaves(TEST_LACP_SLAVE_COUT, 1);\n+\tTEST_ASSERT_SUCCESS(retval, \"Failed to initialize bonded device\");\n+\n+\t/* Test LACP handshake function */\n+\tretval = bond_handshake();\n+\tTEST_ASSERT_SUCCESS(retval, \"Initial handshake failed\");\n+\n+\tretval = remove_slaves_and_stop_bonded_device();\n+\tTEST_ASSERT_SUCCESS(retval, \"Test cleanup failed.\");\n+\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+generate_packets(struct ether_addr *src_mac,\n+\tstruct ether_addr *dst_mac, uint16_t count, struct rte_mbuf **buf)\n+{\n+\tuint16_t pktlen = PACKET_BURST_GEN_PKT_LEN;\n+\tuint8_t vlan_enable = 0;\n+\tuint16_t vlan_id = 0;\n+\tuint8_t ip4_type = 1; /* 0 - ipv6 */\n+\n+\tuint16_t src_port = 10, dst_port = 20;\n+\n+\tuint32_t ip_src[4] = { [0 ... 2] = 0xDEADBEEF, [3] = IPv4(192, 168, 0, 1) };\n+\tuint32_t ip_dst[4] = { [0 ... 2] = 0xFEEDFACE, [3] = IPv4(192, 168, 0, 2) };\n+\n+\tstruct ether_hdr pkt_eth_hdr;\n+\tstruct udp_hdr pkt_udp_hdr;\n+\tunion {\n+\t\tstruct ipv4_hdr v4;\n+\t\tstruct ipv6_hdr v6;\n+\t} pkt_ip_hdr;\n+\n+\tint retval;\n+\n+\tinitialize_eth_header(&pkt_eth_hdr, src_mac, dst_mac, vlan_enable, vlan_id);\n+\n+\tif (ip4_type)\n+\t\tinitialize_ipv4_header(&pkt_ip_hdr.v4, ip_src[3], ip_dst[3], pktlen);\n+\telse\n+\t\tinitialize_ipv6_header(&pkt_ip_hdr.v6, (uint8_t *)ip_src,\n+\t\t\t(uint8_t *)&ip_dst, pktlen);\n+\n+\tinitialize_udp_header(&pkt_udp_hdr, src_port, dst_port, 16);\n+\n+\tretval = generate_packet_burst(test_params.mbuf_pool, buf,\n+\t\t\t&pkt_eth_hdr, vlan_enable, &pkt_ip_hdr, 1, &pkt_udp_hdr,\n+\t\t\tcount, pktlen, 1);\n+\n+\tif (retval > 0 && retval != count)\n+\t\tfree_pkts(&buf[count - retval], retval);\n+\n+\tTEST_ASSERT_EQUAL(retval, count, \"Failed to generate %u packets\",\n+\t\tcount);\n+\n+\treturn count;\n+}\n+\n+static int\n+generate_and_put_packets(struct slave_conf *slave, struct ether_addr *src_mac,\n+\t\tstruct ether_addr *dst_mac, uint16_t count)\n+{\n+\tstruct rte_mbuf *pkts[MAX_PKT_BURST];\n+\tint retval;\n+\n+\tretval = generate_packets(src_mac, dst_mac, count, pkts);\n+\tif (retval != (int)count)\n+\t\treturn retval;\n+\n+\tretval = slave_put_pkts(slave, pkts, count);\n+\tif (retval > 0 && retval != count)\n+\t\tfree_pkts(&pkts[retval], count - retval);\n+\n+\tTEST_ASSERT_EQUAL(retval, count,\n+\t\t\"Failed to enqueue packets into slave %u RX queue\", slave->port_id);\n+\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+test_mode4_rx(void)\n+{\n+\tstruct slave_conf *slave;\n+\tuint16_t i, j;\n+\n+\tuint16_t expected_pkts_cnt;\n+\tstruct rte_mbuf *pkts[MAX_PKT_BURST];\n+\tint retval;\n+\tunsigned delay;\n+\n+\tstruct ether_hdr *hdr;\n+\n+\tstruct ether_addr src_mac = { { 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 } };\n+\tstruct ether_addr dst_mac;\n+\tstruct ether_addr bonded_mac;\n+\n+\tretval = initialize_bonded_device_with_slaves(TEST_PROMISC_SLAVE_COUNT, 1);\n+\tTEST_ASSERT_SUCCESS(retval, \"Failed to initialize bonded device\");\n+\n+\tretval = bond_handshake();\n+\tTEST_ASSERT_SUCCESS(retval, \"Initial handshake failed\");\n+\n+\trte_eth_macaddr_get(test_params.bonded_port_id, &bonded_mac);\n+\tether_addr_copy(&bonded_mac, &dst_mac);\n+\n+\t/* Assert that dst address is not bonding address */\n+\tdst_mac.addr_bytes[0]++;\n+\n+\t/* First try with promiscuous mode enabled.\n+\t * Add 2 packets to each slave. First with bonding MAC address, second with\n+\t * different. Check if we received all of them. */\n+\trte_eth_promiscuous_enable(test_params.bonded_port_id);\n+\n+\texpected_pkts_cnt = 0;\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\tretval = generate_and_put_packets(slave, &src_mac, &bonded_mac, 1);\n+\t\tTEST_ASSERT_SUCCESS(retval, \"Failed to enqueue packets to slave %u\",\n+\t\t\tslave->port_id);\n+\n+\t\tretval = generate_and_put_packets(slave, &src_mac, &dst_mac, 1);\n+\t\tTEST_ASSERT_SUCCESS(retval, \"Failed to enqueue packets to slave %u\",\n+\t\t\tslave->port_id);\n+\n+\t\t/* Expect 2 packets per slave */\n+\t\texpected_pkts_cnt += 2;\n+\t}\n+\n+\tretval = rte_eth_rx_burst(test_params.bonded_port_id, 0, pkts,\n+\t\tRTE_DIM(pkts));\n+\n+\tif (retval == expected_pkts_cnt) {\n+\t\tint cnt[2] = { 0, 0 };\n+\n+\t\tfor (i = 0; i < expected_pkts_cnt; i++) {\n+\t\t\thdr = rte_pktmbuf_mtod(pkts[i], struct ether_hdr *);\n+\t\t\tcnt[is_same_ether_addr(&hdr->d_addr, &bonded_mac)]++;\n+\t\t}\n+\n+\t\tfree_pkts(pkts, expected_pkts_cnt);\n+\n+\t\t/* For division by 2 expected_pkts_cnt must be even */\n+\t\tRTE_VERIFY((expected_pkts_cnt & 1) == 0);\n+\t\tTEST_ASSERT(cnt[0] == expected_pkts_cnt / 2 &&\n+\t\t\tcnt[1] == expected_pkts_cnt / 2,\n+\t\t\t\"Expected %u packets with the same MAC and %u with different but \"\n+\t\t\t\"got %u with the same and %u with diffrent MAC\",\n+\t\t\texpected_pkts_cnt / 2, expected_pkts_cnt / 2, cnt[1], cnt[0]);\n+\t} else if (retval > 0)\n+\t\tfree_pkts(pkts, retval);\n+\n+\tTEST_ASSERT_EQUAL(retval, expected_pkts_cnt,\n+\t\t\"Expected %u packets but received only %d\", expected_pkts_cnt, retval);\n+\n+\t/* Now, disable promiscuous mode. When promiscuous mode is disabled we\n+\t * expect to receive only packets that are directed to bonding port. */\n+\trte_eth_promiscuous_disable(test_params.bonded_port_id);\n+\n+\texpected_pkts_cnt = 0;\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\tretval = generate_and_put_packets(slave, &src_mac, &bonded_mac, 1);\n+\t\tTEST_ASSERT_SUCCESS(retval, \"Failed to enqueue packets to slave %u\",\n+\t\t\tslave->port_id);\n+\n+\t\tretval = generate_and_put_packets(slave, &src_mac, &dst_mac, 1);\n+\t\tTEST_ASSERT_SUCCESS(retval, \"Failed to enqueue packets to slave %u\",\n+\t\t\tslave->port_id);\n+\n+\t\t/* Expect only one packet per slave */\n+\t\texpected_pkts_cnt += 1;\n+\t}\n+\n+\tretval = rte_eth_rx_burst(test_params.bonded_port_id, 0, pkts,\n+\t\tRTE_DIM(pkts));\n+\n+\tif (retval == expected_pkts_cnt) {\n+\t\tint eq_cnt = 0;\n+\n+\t\tfor (i = 0; i < expected_pkts_cnt; i++) {\n+\t\t\thdr = rte_pktmbuf_mtod(pkts[i], struct ether_hdr *);\n+\t\t\teq_cnt += is_same_ether_addr(&hdr->d_addr, &bonded_mac);\n+\t\t}\n+\n+\t\tfree_pkts(pkts, expected_pkts_cnt);\n+\t\tTEST_ASSERT_EQUAL(eq_cnt, expected_pkts_cnt, \"Packet address mismatch\");\n+\t} else if (retval > 0)\n+\t\tfree_pkts(pkts, retval);\n+\n+\tTEST_ASSERT_EQUAL(retval, expected_pkts_cnt,\n+\t\t\"Expected %u packets but received only %d\", expected_pkts_cnt, retval);\n+\n+\t/* Link down test: simulate link down for first slave. */\n+\tdelay = bond_get_update_timeout_ms();\n+\n+\tuint8_t slave_down_id = INVALID_PORT_ID;\n+\n+\t/* Find first slave and make link down on it*/\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\trte_eth_dev_set_link_down(slave->port_id);\n+\t\tslave_down_id = slave->port_id;\n+\t\tbreak;\n+\t}\n+\n+\tRTE_VERIFY(slave_down_id != INVALID_PORT_ID);\n+\n+\t/* Give some time to rearrange bonding */\n+\tfor (i = 0; i < 3; i++) {\n+\t\trte_delay_ms(delay);\n+\t\tbond_handshake();\n+\t}\n+\n+\tTEST_ASSERT_SUCCESS(bond_handshake(), \"Handshake after link down failed\");\n+\n+\t/* Put packet to each slave */\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\tvoid *pkt = NULL;\n+\n+\t\tdst_mac.addr_bytes[ETHER_ADDR_LEN - 1] = slave->port_id;\n+\t\tretval = generate_and_put_packets(slave, &src_mac, &dst_mac, 1);\n+\t\tTEST_ASSERT_SUCCESS(retval, \"Failed to generate test packet burst.\");\n+\n+\t\tsrc_mac.addr_bytes[ETHER_ADDR_LEN - 1] = slave->port_id;\n+\t\tretval = generate_and_put_packets(slave, &src_mac, &bonded_mac, 1);\n+\t\tTEST_ASSERT_SUCCESS(retval, \"Failed to generate test packet burst.\");\n+\n+\t\tretval = bond_rx(pkts, RTE_DIM(pkts));\n+\n+\t\t/* Clean anything */\n+\t\tif (retval > 0)\n+\t\t\tfree_pkts(pkts, retval);\n+\n+\t\twhile (rte_ring_dequeue(slave->rx_queue, (void **)&pkt) == 0)\n+\t\t\trte_pktmbuf_free(pkt);\n+\n+\t\tif (slave_down_id == slave->port_id)\n+\t\t\tTEST_ASSERT_EQUAL(retval, 0, \"Packets received unexpectedly.\");\n+\t\telse\n+\t\t\tTEST_ASSERT_NOT_EQUAL(retval, 0,\n+\t\t\t\t\"Expected to receive some packets on slave %u.\",\n+\t\t\t\tslave->port_id);\n+\t\trte_eth_dev_start(slave->port_id);\n+\n+\t\tfor (j = 0; j < 5; j++) {\n+\t\t\tTEST_ASSERT(bond_handshake_reply(slave) >= 0,\n+\t\t\t\t\"Handshake after link up\");\n+\n+\t\t\tif (bond_handshake_done(slave) == 1)\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\tTEST_ASSERT(j < 5, \"Failed to agregate slave after link up\");\n+\t}\n+\n+\tremove_slaves_and_stop_bonded_device();\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+test_mode4_tx_burst(void)\n+{\n+\tstruct slave_conf *slave;\n+\tuint16_t i, j;\n+\n+\tuint16_t exp_pkts_cnt, pkts_cnt = 0;\n+\tstruct rte_mbuf *pkts[MAX_PKT_BURST];\n+\tint retval;\n+\tunsigned delay;\n+\n+\tstruct ether_addr dst_mac = { { 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 } };\n+\tstruct ether_addr bonded_mac;\n+\n+\tretval = initialize_bonded_device_with_slaves(TEST_TX_SLAVE_COUNT, 1);\n+\tTEST_ASSERT_SUCCESS(retval, \"Failed to initialize bonded device\");\n+\n+\tretval = bond_handshake();\n+\tTEST_ASSERT_SUCCESS(retval, \"Initial handshake failed\");\n+\n+\trte_eth_macaddr_get(test_params.bonded_port_id, &bonded_mac);\n+\n+\t/* Prepare burst */\n+\tfor (pkts_cnt = 0; pkts_cnt < RTE_DIM(pkts); pkts_cnt++) {\n+\t\tdst_mac.addr_bytes[ETHER_ADDR_LEN - 1] = pkts_cnt;\n+\t\tretval = generate_packets(&bonded_mac, &dst_mac, 1, &pkts[pkts_cnt]);\n+\n+\t\tif (retval != 1)\n+\t\t\tfree_pkts(pkts, pkts_cnt);\n+\n+\t\tTEST_ASSERT_EQUAL(retval, 1, \"Failed to generate packet %u\", pkts_cnt);\n+\t}\n+\texp_pkts_cnt = pkts_cnt;\n+\n+\t/* Transmit packets on bonded device */\n+\tretval = bond_tx(pkts, pkts_cnt);\n+\tif (retval > 0 && retval < pkts_cnt)\n+\t\tfree_pkts(&pkts[retval], pkts_cnt - retval);\n+\n+\tTEST_ASSERT_EQUAL(retval, pkts_cnt, \"TX on bonded device failed\");\n+\n+\t/* Check if packets were transmitted properly. Every slave should have\n+\t * at least one packet, and sum must match. Under normal operation\n+\t * there should be no LACP nor MARKER frames. */\n+\tpkts_cnt = 0;\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\tuint16_t normal_cnt, slow_cnt;\n+\n+\t\tretval = slave_get_pkts(slave, pkts, RTE_DIM(pkts));\n+\t\tnormal_cnt = 0;\n+\t\tslow_cnt = 0;\n+\n+\t\tfor (j = 0; j < retval; j++) {\n+\t\t\tif (make_lacp_reply(slave, pkts[j]) == 1)\n+\t\t\t\tnormal_cnt++;\n+\t\t\telse\n+\t\t\t\tslow_cnt++;\n+\t\t}\n+\n+\t\tfree_pkts(pkts, normal_cnt + slow_cnt);\n+\t\tTEST_ASSERT_EQUAL(slow_cnt, 0,\n+\t\t\t\"slave %u unexpectedly transmitted %d SLOW packets\", slave->port_id,\n+\t\t\tslow_cnt);\n+\n+\t\tTEST_ASSERT_NOT_EQUAL(normal_cnt, 0,\n+\t\t\t\"slave %u did not transmitted any packets\", slave->port_id);\n+\n+\t\tpkts_cnt += normal_cnt;\n+\t}\n+\n+\tTEST_ASSERT_EQUAL(exp_pkts_cnt, pkts_cnt,\n+\t\t\"Expected %u packets but transmitted only %d\", exp_pkts_cnt, pkts_cnt);\n+\n+\t/* Link down test:\n+\t * simulate link down for first slave. */\n+\tdelay = bond_get_update_timeout_ms();\n+\n+\tuint8_t slave_down_id = INVALID_PORT_ID;\n+\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\trte_eth_dev_set_link_down(slave->port_id);\n+\t\tslave_down_id = slave->port_id;\n+\t\tbreak;\n+\t}\n+\n+\tRTE_VERIFY(slave_down_id != INVALID_PORT_ID);\n+\n+\t/* Give some time to rearrange bonding. */\n+\tfor (i = 0; i < 3; i++) {\n+\t\tbond_handshake();\n+\t\trte_delay_ms(delay);\n+\t}\n+\n+\tTEST_ASSERT_SUCCESS(bond_handshake(), \"Handshake after link down failed\");\n+\n+\t/* Prepare burst. */\n+\tfor (pkts_cnt = 0; pkts_cnt < RTE_DIM(pkts); pkts_cnt++) {\n+\t\tdst_mac.addr_bytes[ETHER_ADDR_LEN - 1] = pkts_cnt;\n+\t\tretval = generate_packets(&bonded_mac, &dst_mac, 1, &pkts[pkts_cnt]);\n+\n+\t\tif (retval != 1)\n+\t\t\tfree_pkts(pkts, pkts_cnt);\n+\n+\t\tTEST_ASSERT_EQUAL(retval, 1, \"Failed to generate test packet %u\",\n+\t\t\tpkts_cnt);\n+\t}\n+\texp_pkts_cnt = pkts_cnt;\n+\n+\t/* Transmit packets on bonded device. */\n+\tretval = bond_tx(pkts, pkts_cnt);\n+\tif (retval > 0 && retval < pkts_cnt)\n+\t\tfree_pkts(&pkts[retval], pkts_cnt - retval);\n+\n+\tTEST_ASSERT_EQUAL(retval, pkts_cnt, \"TX on bonded device failed\");\n+\n+\t/* Check if packets was transmitted properly. Every slave should have\n+\t * at least one packet, and sum must match. Under normal operation\n+\t * there should be no LACP nor MARKER frames. */\n+\tpkts_cnt = 0;\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\tuint16_t normal_cnt, slow_cnt;\n+\n+\t\tretval = slave_get_pkts(slave, pkts, RTE_DIM(pkts));\n+\t\tnormal_cnt = 0;\n+\t\tslow_cnt = 0;\n+\n+\t\tfor (j = 0; j < retval; j++) {\n+\t\t\tif (make_lacp_reply(slave, pkts[j]) == 1)\n+\t\t\t\tnormal_cnt++;\n+\t\t\telse\n+\t\t\t\tslow_cnt++;\n+\t\t}\n+\n+\t\tfree_pkts(pkts, normal_cnt + slow_cnt);\n+\n+\t\tif (slave_down_id == slave->port_id) {\n+\t\t\tTEST_ASSERT_EQUAL(normal_cnt + slow_cnt, 0,\n+\t\t\t\t\"slave %u enexpectedly transmitted %u packets\",\n+\t\t\t\tnormal_cnt + slow_cnt, slave->port_id);\n+\t\t} else {\n+\t\t\tTEST_ASSERT_EQUAL(slow_cnt, 0,\n+\t\t\t\t\"slave %u unexpectedly transmitted %d SLOW packets\",\n+\t\t\t\tslave->port_id, slow_cnt);\n+\n+\t\t\tTEST_ASSERT_NOT_EQUAL(normal_cnt, 0,\n+\t\t\t\t\"slave %u did not transmitted any packets\", slave->port_id);\n+\t\t}\n+\n+\t\tpkts_cnt += normal_cnt;\n+\t}\n+\n+\tTEST_ASSERT_EQUAL(exp_pkts_cnt, pkts_cnt,\n+\t\t\"Expected %u packets but transmitted only %d\", exp_pkts_cnt, pkts_cnt);\n+\n+\treturn remove_slaves_and_stop_bonded_device();\n+}\n+\n+static void\n+init_marker(struct rte_mbuf *pkt, struct slave_conf *slave)\n+{\n+\tstruct marker_header *marker_hdr = rte_pktmbuf_mtod(pkt,\n+\t\t\tstruct marker_header *);\n+\n+\t/* Copy multicast destination address */\n+\tether_addr_copy(&slow_protocol_mac_addr, &marker_hdr->eth_hdr.d_addr);\n+\n+\t/* Init source address */\n+\tether_addr_copy(&parnter_mac_default, &marker_hdr->eth_hdr.s_addr);\n+\tmarker_hdr->eth_hdr.s_addr.addr_bytes[ETHER_ADDR_LEN-1] = slave->port_id;\n+\n+\tmarker_hdr->eth_hdr.ether_type = rte_cpu_to_be_16(ETHER_TYPE_SLOW);\n+\n+\tmarker_hdr->marker.subtype = SLOW_SUBTYPE_MARKER;\n+\tmarker_hdr->marker.version_number = 1;\n+\tmarker_hdr->marker.tlv_type_marker = MARKER_TLV_TYPE_INFO;\n+\tmarker_hdr->marker.info_length =\n+\t\t\toffsetof(struct marker, reserved_90) -\n+\t\t\toffsetof(struct marker, requester_port);\n+\tRTE_VERIFY(marker_hdr->marker.info_length == 16);\n+\tmarker_hdr->marker.requester_port = slave->port_id + 1;\n+\tmarker_hdr->marker.tlv_type_terminator = TLV_TYPE_TERMINATOR_INFORMATION;\n+\tmarker_hdr->marker.terminator_length = 0;\n+}\n+\n+static int\n+test_mode4_marker(void)\n+{\n+\tstruct slave_conf *slave;\n+\tstruct rte_mbuf *pkts[MAX_PKT_BURST];\n+\tstruct rte_mbuf *marker_pkt;\n+\tstruct marker_header *marker_hdr;\n+\n+\tunsigned delay;\n+\tint retval;\n+\tuint16_t nb_pkts;\n+\tuint8_t i, j;\n+\tconst uint16_t ethtype_slow_be = rte_be_to_cpu_16(ETHER_TYPE_SLOW);\n+\n+\tretval = initialize_bonded_device_with_slaves(TEST_MARKER_SLAVE_COUT, 1);\n+\tTEST_ASSERT_SUCCESS(retval, \"Failed to initialize bonded device\");\n+\n+\t/* Test LACP handshake function */\n+\tretval = bond_handshake();\n+\tTEST_ASSERT_SUCCESS(retval, \"Initial handshake failed\");\n+\n+\tdelay = bond_get_update_timeout_ms();\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\tmarker_pkt = rte_pktmbuf_alloc(test_params.mbuf_pool);\n+\t\tTEST_ASSERT_NOT_NULL(marker_pkt, \"Failed to allocate marker packet\");\n+\t\tinit_marker(marker_pkt, slave);\n+\n+\t\tretval = slave_put_pkts(slave, &marker_pkt, 1);\n+\t\tif (retval != 1)\n+\t\t\trte_pktmbuf_free(marker_pkt);\n+\n+\t\tTEST_ASSERT_EQUAL(retval, 1,\n+\t\t\t\"Failed to send marker packet to slave %u\", slave->port_id);\n+\n+\t\tfor (j = 0; j < 20; ++j) {\n+\t\t\trte_delay_ms(delay);\n+\t\t\tretval = rte_eth_rx_burst(test_params.bonded_port_id, 0, pkts,\n+\t\t\t\tRTE_DIM(pkts));\n+\n+\t\t\tif (retval > 0)\n+\t\t\t\tfree_pkts(pkts, retval);\n+\n+\t\t\tTEST_ASSERT_EQUAL(retval, 0, \"Received packets unexpectedly\");\n+\n+\t\t\tretval = rte_eth_tx_burst(test_params.bonded_port_id, 0, NULL, 0);\n+\t\t\tTEST_ASSERT_EQUAL(retval, 0,\n+\t\t\t\t\"Requested TX of 0 packets but %d transmitted\", retval);\n+\n+\t\t\t/* Check if LACP packet was send by state machines\n+\t\t\t   First and only packet must be a maker response */\n+\t\t\tretval = slave_get_pkts(slave, pkts, MAX_PKT_BURST);\n+\t\t\tif (retval == 0)\n+\t\t\t\tcontinue;\n+\t\t\tif (retval > 1)\n+\t\t\t\tfree_pkts(pkts, retval);\n+\n+\t\t\tTEST_ASSERT_EQUAL(retval, 1, \"failed to get slave packets\");\n+\t\t\tnb_pkts = retval;\n+\n+\t\t\tmarker_hdr = rte_pktmbuf_mtod(pkts[0], struct marker_header *);\n+\t\t\t/* Check if it's slow packet*/\n+\t\t\tif (marker_hdr->eth_hdr.ether_type != ethtype_slow_be)\n+\t\t\t\tretval = -1;\n+\t\t\t/* Check if it's marker packet */\n+\t\t\telse if (marker_hdr->marker.subtype != SLOW_SUBTYPE_MARKER)\n+\t\t\t\tretval = -2;\n+\t\t\telse if (marker_hdr->marker.tlv_type_marker != MARKER_TLV_TYPE_RESP)\n+\t\t\t\tretval = -3;\n+\n+\t\t\tfree_pkts(pkts, nb_pkts);\n+\n+\t\t\tTEST_ASSERT_NOT_EQUAL(retval, -1, \"Unexpected protocol type\");\n+\t\t\tTEST_ASSERT_NOT_EQUAL(retval, -2, \"Unexpected sub protocol type\");\n+\t\t\tTEST_ASSERT_NOT_EQUAL(retval, -3, \"Unexpected marker type\");\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tTEST_ASSERT(j < 20, \"Marker response not found\");\n+\t}\n+\n+\tretval = remove_slaves_and_stop_bonded_device();\n+\tTEST_ASSERT_SUCCESS(retval,\t\"Test cleanup failed.\");\n+\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+test_mode4_expired(void)\n+{\n+\tstruct slave_conf *slave, *exp_slave = NULL;\n+\tstruct rte_mbuf *pkts[MAX_PKT_BURST];\n+\tstruct rte_mbuf *pkt = NULL;\n+\tint retval;\n+\tuint32_t old_delay;\n+\n+\tuint8_t i;\n+\tuint16_t j;\n+\n+\tstruct rte_eth_bond_8023ad_conf conf;\n+\n+\tretval = initialize_bonded_device_with_slaves(TEST_EXPIRED_SLAVE_COUNT, 1);\n+\t/* Set custom timeouts to make test last shorter. */\n+\trte_eth_bond_8023ad_conf_get(test_params.bonded_port_id, &conf);\n+\tconf.fast_periodic_ms = 100;\n+\tconf.slow_periodic_ms = 600;\n+\tconf.short_timeout_ms = 300;\n+\tconf.long_timeout_ms = 900;\n+\tconf.aggregate_wait_timeout_ms = 200;\n+\tconf.tx_period_ms = 100;\n+\told_delay = conf.update_timeout_ms;\n+\tconf.update_timeout_ms = 10;\n+\trte_eth_bond_8023ad_setup(test_params.bonded_port_id, &conf);\n+\n+\t/* Wait for new settings to be applied. */\n+\tfor (i = 0; i < old_delay/conf.update_timeout_ms * 2; i++) {\n+\t\tFOR_EACH_SLAVE(j, slave)\n+\t\t\tbond_handshake_reply(slave);\n+\n+\t\trte_delay_ms(conf.update_timeout_ms);\n+\t}\n+\n+\tretval = bond_handshake();\n+\tTEST_ASSERT_SUCCESS(retval, \"Initial handshake failed\");\n+\n+\t/* Find first slave */\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\texp_slave = slave;\n+\t\tbreak;\n+\t}\n+\n+\tRTE_VERIFY(exp_slave != NULL);\n+\n+\t/* When one of partners do not send or respond to LACP frame in\n+\t * conf.long_timeout_ms time, internal state machines should detect this\n+\t * and transit to expired state. */\n+\tfor (j = 0; j < conf.long_timeout_ms/conf.update_timeout_ms + 2; j++) {\n+\t\trte_delay_ms(conf.update_timeout_ms);\n+\n+\t\tretval = bond_tx(NULL, 0);\n+\t\tTEST_ASSERT_EQUAL(retval, 0, \"Unexpectedly received %d packets\",\n+\t\t\tretval);\n+\n+\t\tFOR_EACH_SLAVE(i, slave) {\n+\t\t\tretval = bond_handshake_reply(slave);\n+\t\t\tTEST_ASSERT(retval >= 0, \"Handshake failed\");\n+\n+\t\t\t/* Remove replay for slave that supose to be expired. */\n+\t\t\tif (slave == exp_slave) {\n+\t\t\t\twhile (rte_ring_count(slave->rx_queue) > 0) {\n+\t\t\t\t\trte_ring_dequeue(slave->rx_queue, (void **)&pkt);\n+\t\t\t\t\trte_pktmbuf_free(pkt);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tretval = bond_rx(pkts, RTE_DIM(pkts));\n+\t\tif (retval > 0)\n+\t\t\tfree_pkts(pkts, retval);\n+\n+\t\tTEST_ASSERT_EQUAL(retval, 0, \"Unexpectedly received %d packets\",\n+\t\t\tretval);\n+\t}\n+\n+\t/* After test only expected slave should be in EXPIRED state */\n+\tFOR_EACH_SLAVE(i, slave) {\n+\t\tif (slave == exp_slave)\n+\t\t\tTEST_ASSERT(slave->lacp_parnter_state & STATE_EXPIRED,\n+\t\t\t\t\"Slave %u should be in expired.\", slave->port_id);\n+\t\telse\n+\t\t\tTEST_ASSERT_EQUAL(bond_handshake_done(slave), 1,\n+\t\t\t\t\"Slave %u should be operational.\", slave->port_id);\n+\t}\n+\n+\tretval = remove_slaves_and_stop_bonded_device();\n+\tTEST_ASSERT_SUCCESS(retval, \"Test cleanup failed.\");\n+\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+check_environment(void)\n+{\n+\tstruct slave_conf *port;\n+\tuint8_t i, env_state;\n+\tuint8_t slaves[RTE_DIM(test_params.slave_ports)];\n+\tint slaves_count;\n+\n+\tenv_state = 0;\n+\tFOR_EACH_PORT(i, port) {\n+\t\tif (rte_ring_count(port->rx_queue) != 0)\n+\t\t\tenv_state |= 0x01;\n+\n+\t\tif (rte_ring_count(port->tx_queue) != 0)\n+\t\t\tenv_state |= 0x02;\n+\n+\t\tif (port->bonded != 0)\n+\t\t\tenv_state |= 0x04;\n+\n+\t\tif (port->lacp_parnter_state != 0)\n+\t\t\tenv_state |= 0x08;\n+\n+\t\tif (env_state != 0)\n+\t\t\tbreak;\n+\t}\n+\n+\tslaves_count = rte_eth_bond_slaves_get(test_params.bonded_port_id,\n+\t\t\tslaves, RTE_DIM(slaves));\n+\n+\tif (slaves_count != 0)\n+\t\tenv_state |= 0x10;\n+\n+\tTEST_ASSERT_EQUAL(env_state, 0,\n+\t\t\"Environment not clean (port %u):%s%s%s%s%s\",\n+\t\tport->port_id,\n+\t\tenv_state & 0x01 ? \" slave rx queue not clean\" : \"\",\n+\t\tenv_state & 0x02 ? \" slave tx queue not clean\" : \"\",\n+\t\tenv_state & 0x04 ? \" port marked as enslaved\" : \"\",\n+\t\tenv_state & 0x80 ? \" slave state is not reset\" : \"\",\n+\t\tenv_state & 0x10 ? \" slave count not equal 0\" : \".\");\n+\n+\n+\treturn TEST_SUCCESS;\n+}\n+\n+static int\n+test_mode4_executor(int (*test_func)(void))\n+{\n+\tstruct slave_conf *port;\n+\tint test_result;\n+\tuint8_t i;\n+\tvoid *pkt;\n+\n+\t/* Check if environment is clean. Fail to launch a test if there was\n+\t * a critical error before that prevented to reset environment. */\n+\tTEST_ASSERT_SUCCESS(check_environment(),\n+\t\t\"Refusing to launch test in dirty environment.\");\n+\n+\tRTE_VERIFY(test_func != NULL);\n+\ttest_result = (*test_func)();\n+\n+\t/* If test succeed check if environment wast left in good condition. */\n+\tif (test_result == TEST_SUCCESS)\n+\t\ttest_result = check_environment();\n+\n+\t/* Reset environment in case test failed to do that. */\n+\tif (test_result != TEST_SUCCESS) {\n+\t\tTEST_ASSERT_SUCCESS(remove_slaves_and_stop_bonded_device(),\n+\t\t\t\"Failed to stop bonded device\");\n+\n+\t\tFOR_EACH_PORT(i, port) {\n+\t\t\twhile (rte_ring_count(port->rx_queue) != 0) {\n+\t\t\t\tif (rte_ring_dequeue(port->rx_queue, &pkt) == 0)\n+\t\t\t\t\trte_pktmbuf_free(pkt);\n+\t\t\t}\n+\n+\t\t\twhile (rte_ring_count(port->tx_queue) != 0) {\n+\t\t\t\tif (rte_ring_dequeue(port->tx_queue, &pkt) == 0)\n+\t\t\t\t\trte_pktmbuf_free(pkt);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn test_result;\n+}\n+\n+static int\n+test_mode4_lacp_wrapper(void)\n+{\n+\treturn test_mode4_executor(&test_mode4_lacp);\n+}\n+\n+static int\n+test_mode4_marker_wrapper(void)\n+{\n+\treturn test_mode4_executor(&test_mode4_marker);\n+}\n+\n+static int\n+test_mode4_rx_wrapper(void)\n+{\n+\treturn test_mode4_executor(&test_mode4_rx);\n+}\n+\n+static int\n+test_mode4_tx_burst_wrapper(void)\n+{\n+\treturn test_mode4_executor(&test_mode4_tx_burst);\n+}\n+\n+static int\n+test_mode4_expired_wrapper(void)\n+{\n+\treturn test_mode4_executor(&test_mode4_expired);\n+}\n+\n+static struct unit_test_suite link_bonding_mode4_test_suite  = {\n+\t.suite_name = \"Link Bonding mode 4 Unit Test Suite\",\n+\t.setup = test_setup,\n+\t.teardown = testsuite_teardown,\n+\t.unit_test_cases = {\n+\t\tTEST_CASE_NAMED(\"test_mode4_lacp\", test_mode4_lacp_wrapper),\n+\t\tTEST_CASE_NAMED(\"test_mode4_rx\", test_mode4_rx_wrapper),\n+\t\tTEST_CASE_NAMED(\"test_mode4_tx_burst\", test_mode4_tx_burst_wrapper),\n+\t\tTEST_CASE_NAMED(\"test_mode4_marker\", test_mode4_marker_wrapper),\n+\t\tTEST_CASE_NAMED(\"test_mode4_expired\", test_mode4_expired_wrapper),\n+\t\t{ NULL, NULL, NULL, NULL, NULL } /**< NULL terminate unit test array */\n+\t}\n+};\n+\n+static int\n+test_link_bonding_mode4(void)\n+{\n+\treturn unit_test_suite_runner(&link_bonding_mode4_test_suite);\n+}\n+\n+static struct test_command link_bonding_cmd = {\n+\t.command = \"link_bonding_mode4_autotest\",\n+\t.callback = test_link_bonding_mode4,\n+};\n+\n+REGISTER_TEST_COMMAND(link_bonding_cmd);\n",
    "prefixes": [
        "dpdk-dev",
        "v2",
        "2/2"
    ]
}