get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 17028,
    "url": "https://patches.dpdk.org/api/patches/17028/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/15fd4a920abe08af278d1dc5dcaa25b88e37ab57.1479309720.git.adrien.mazarguil@6wind.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": "<15fd4a920abe08af278d1dc5dcaa25b88e37ab57.1479309720.git.adrien.mazarguil@6wind.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/15fd4a920abe08af278d1dc5dcaa25b88e37ab57.1479309720.git.adrien.mazarguil@6wind.com",
    "date": "2016-11-16T16:23:30",
    "name": "[dpdk-dev,04/22] app/testpmd: implement basic support for rte_flow",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "afb3ca153de41b1134091fd6fceddfb313c1cecf",
    "submitter": {
        "id": 165,
        "url": "https://patches.dpdk.org/api/people/165/?format=api",
        "name": "Adrien Mazarguil",
        "email": "adrien.mazarguil@6wind.com"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/15fd4a920abe08af278d1dc5dcaa25b88e37ab57.1479309720.git.adrien.mazarguil@6wind.com/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/17028/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/17028/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 C714E58EC;\n\tWed, 16 Nov 2016 17:25:03 +0100 (CET)",
            "from mail-wm0-f43.google.com (mail-wm0-f43.google.com\n\t[74.125.82.43]) by dpdk.org (Postfix) with ESMTP id 374F8559C\n\tfor <dev@dpdk.org>; Wed, 16 Nov 2016 17:24:10 +0100 (CET)",
            "by mail-wm0-f43.google.com with SMTP id t79so83402949wmt.0\n\tfor <dev@dpdk.org>; Wed, 16 Nov 2016 08:24:10 -0800 (PST)",
            "from 6wind.com (guy78-3-82-239-227-177.fbx.proxad.net.\n\t[82.239.227.177]) by smtp.gmail.com with ESMTPSA id\n\tf126sm10903949wme.22.2016.11.16.08.24.06\n\t(version=TLS1_2 cipher=AES128-SHA bits=128/128);\n\tWed, 16 Nov 2016 08:24:09 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=6wind-com.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=xG2qVibXiZ+5uRujbgfR/Cd1AkEWEPemLXXEQ4yFZdE=;\n\tb=QCXY/F86DUJtlLgQbGAyGxytOL1hNwBRhJHexuJNRzzC8uWErzNCqlicqWJwIPWNPb\n\tsLHnq1BdAiQqM1xqUd7lyP89P2GExKtefqnXt3lFosO5ODSPK+3MiCDLdSsheTNz5Gj8\n\t/OAAOgj4Bi2ISMcf0GLhQdEYd7XaSBXSYuk5ULzh7sZVMSlNFBQIubsD4DfM6CSxk5ee\n\tYVMilCwDbs9cZ6p33X5I1Xi+Irqy2At12D8wrLHBuOh9r2mm6wg/GIZvB7IBhEvpzpTP\n\tAgv80KLq8IuYzREIkwjeLfB4nKcDRmMVYaLR9AxQSlJbm8IfGScRK8K6gLQTxeospgSG\n\t52ow==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20130820;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=xG2qVibXiZ+5uRujbgfR/Cd1AkEWEPemLXXEQ4yFZdE=;\n\tb=Ce2NZhGCLj6ksQxDoDvaeZW6/V8upmV9VyCYGjBNq7lVUP7LlDi4PeNKIMk9pTDtsu\n\t/+9pos0E7tnxexXwNT37vZFanrpZV0zsRy7taQbr3uBvWI3RgxdjIse2l1HCgvFbSrfW\n\tvXYDjHM8GYsFUZIS9rAM+hMBJabaHLNwM2sGtVJKsnodpbI/mhXghkZKKpxH2Cgsknb0\n\tz0nxb9vz1+XIZdGkexagKgQJu2Usa6ArVdDsAXBQYdb5vTr6+2MssQZF9CfRUxLwSzz+\n\tXNLDw7KnTxcjsXGBUXxUMFOQUWExWMl5rUPnfZt8poWCCDE50nzv09mxqXCnXT1mCWnu\n\tk3Nw==",
        "X-Gm-Message-State": "ABUngvcVZhppELTLw/2p7OzRY1MQoWW2RJ8TlHzdZ87jP7QRaQsomCKHmpAaIsHPl8QQncr7",
        "X-Received": "by 10.194.89.41 with SMTP id bl9mr2513326wjb.81.1479313450278;\n\tWed, 16 Nov 2016 08:24:10 -0800 (PST)",
        "From": "Adrien Mazarguil <adrien.mazarguil@6wind.com>",
        "To": "dev@dpdk.org",
        "Cc": "Thomas Monjalon <thomas.monjalon@6wind.com>,\n\tPablo de Lara <pablo.de.lara.guarch@intel.com>,\n\tOlivier Matz <olivier.matz@6wind.com>",
        "Date": "Wed, 16 Nov 2016 17:23:30 +0100",
        "Message-Id": "<15fd4a920abe08af278d1dc5dcaa25b88e37ab57.1479309720.git.adrien.mazarguil@6wind.com>",
        "X-Mailer": "git-send-email 2.1.4",
        "In-Reply-To": "<cover.1479309719.git.adrien.mazarguil@6wind.com>",
        "References": "<cover.1471632644.git.adrien.mazarguil@6wind.com>\n\t<cover.1479309719.git.adrien.mazarguil@6wind.com>",
        "Subject": "[dpdk-dev] [PATCH 04/22] app/testpmd: implement basic support for\n\trte_flow",
        "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": "Add basic management functions for the generic flow API (validate, create,\ndestroy, flush, query and list). Flow rule objects and properties are\narranged in lists associated with each port.\n\nSigned-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>\n---\n app/test-pmd/cmdline.c     |   1 +\n app/test-pmd/config.c      | 484 ++++++++++++++++++++++++++++++++++++++++\n app/test-pmd/csumonly.c    |   1 +\n app/test-pmd/flowgen.c     |   1 +\n app/test-pmd/icmpecho.c    |   1 +\n app/test-pmd/ieee1588fwd.c |   1 +\n app/test-pmd/iofwd.c       |   1 +\n app/test-pmd/macfwd.c      |   1 +\n app/test-pmd/macswap.c     |   1 +\n app/test-pmd/parameters.c  |   1 +\n app/test-pmd/rxonly.c      |   1 +\n app/test-pmd/testpmd.c     |   6 +\n app/test-pmd/testpmd.h     |  27 +++\n app/test-pmd/txonly.c      |   1 +\n 14 files changed, 528 insertions(+)",
    "diff": "diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c\nindex 63b55dc..c5b015c 100644\n--- a/app/test-pmd/cmdline.c\n+++ b/app/test-pmd/cmdline.c\n@@ -75,6 +75,7 @@\n #include <rte_string_fns.h>\n #include <rte_devargs.h>\n #include <rte_eth_ctrl.h>\n+#include <rte_flow.h>\n \n #include <cmdline_rdline.h>\n #include <cmdline_parse.h>\ndiff --git a/app/test-pmd/config.c b/app/test-pmd/config.c\nindex 36c47ab..c9dc872 100644\n--- a/app/test-pmd/config.c\n+++ b/app/test-pmd/config.c\n@@ -92,6 +92,8 @@\n #include <rte_ethdev.h>\n #include <rte_string_fns.h>\n #include <rte_cycles.h>\n+#include <rte_flow.h>\n+#include <rte_errno.h>\n \n #include \"testpmd.h\"\n \n@@ -750,6 +752,488 @@ port_mtu_set(portid_t port_id, uint16_t mtu)\n \tprintf(\"Set MTU failed. diag=%d\\n\", diag);\n }\n \n+/* Generic flow management functions. */\n+\n+/** Generate flow_item[] entry. */\n+#define MK_FLOW_ITEM(t, s) \\\n+\t[RTE_FLOW_ITEM_TYPE_ ## t] = { \\\n+\t\t.name = # t, \\\n+\t\t.size = s, \\\n+\t}\n+\n+/** Information about known flow pattern items. */\n+static const struct {\n+\tconst char *name;\n+\tsize_t size;\n+} flow_item[] = {\n+\tMK_FLOW_ITEM(END, 0),\n+\tMK_FLOW_ITEM(VOID, 0),\n+\tMK_FLOW_ITEM(INVERT, 0),\n+\tMK_FLOW_ITEM(ANY, sizeof(struct rte_flow_item_any)),\n+\tMK_FLOW_ITEM(PF, 0),\n+\tMK_FLOW_ITEM(VF, sizeof(struct rte_flow_item_vf)),\n+\tMK_FLOW_ITEM(PORT, sizeof(struct rte_flow_item_port)),\n+\tMK_FLOW_ITEM(RAW, sizeof(struct rte_flow_item_raw)), /* +pattern[] */\n+\tMK_FLOW_ITEM(ETH, sizeof(struct rte_flow_item_eth)),\n+\tMK_FLOW_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)),\n+\tMK_FLOW_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)),\n+\tMK_FLOW_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)),\n+\tMK_FLOW_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)),\n+\tMK_FLOW_ITEM(UDP, sizeof(struct rte_flow_item_udp)),\n+\tMK_FLOW_ITEM(TCP, sizeof(struct rte_flow_item_tcp)),\n+\tMK_FLOW_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)),\n+\tMK_FLOW_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)),\n+};\n+\n+/** Compute storage space needed by item specification. */\n+static void\n+flow_item_spec_size(const struct rte_flow_item *item,\n+\t\t    size_t *size, size_t *pad)\n+{\n+\tif (!item->spec)\n+\t\tgoto empty;\n+\tswitch (item->type) {\n+\t\tunion {\n+\t\t\tconst struct rte_flow_item_raw *raw;\n+\t\t} spec;\n+\n+\tcase RTE_FLOW_ITEM_TYPE_RAW:\n+\t\tspec.raw = item->spec;\n+\t\t*size = offsetof(struct rte_flow_item_raw, pattern) +\n+\t\t\tspec.raw->length * sizeof(*spec.raw->pattern);\n+\t\tbreak;\n+\tdefault:\n+empty:\n+\t\t*size = 0;\n+\t\tbreak;\n+\t}\n+\t*pad = RTE_ALIGN_CEIL(*size, sizeof(double)) - *size;\n+}\n+\n+/** Generate flow_action[] entry. */\n+#define MK_FLOW_ACTION(t, s) \\\n+\t[RTE_FLOW_ACTION_TYPE_ ## t] = { \\\n+\t\t.name = # t, \\\n+\t\t.size = s, \\\n+\t}\n+\n+/** Information about known flow actions. */\n+static const struct {\n+\tconst char *name;\n+\tsize_t size;\n+} flow_action[] = {\n+\tMK_FLOW_ACTION(END, 0),\n+\tMK_FLOW_ACTION(VOID, 0),\n+\tMK_FLOW_ACTION(PASSTHRU, 0),\n+\tMK_FLOW_ACTION(MARK, sizeof(struct rte_flow_action_mark)),\n+\tMK_FLOW_ACTION(FLAG, 0),\n+\tMK_FLOW_ACTION(QUEUE, sizeof(struct rte_flow_action_queue)),\n+\tMK_FLOW_ACTION(DROP, 0),\n+\tMK_FLOW_ACTION(COUNT, 0),\n+\tMK_FLOW_ACTION(DUP, sizeof(struct rte_flow_action_dup)),\n+\tMK_FLOW_ACTION(RSS, sizeof(struct rte_flow_action_rss)), /* +queue[] */\n+\tMK_FLOW_ACTION(PF, 0),\n+\tMK_FLOW_ACTION(VF, sizeof(struct rte_flow_action_vf)),\n+};\n+\n+/** Compute storage space needed by action configuration. */\n+static void\n+flow_action_conf_size(const struct rte_flow_action *action,\n+\t\t      size_t *size, size_t *pad)\n+{\n+\tif (!action->conf)\n+\t\tgoto empty;\n+\tswitch (action->type) {\n+\t\tunion {\n+\t\t\tconst struct rte_flow_action_rss *rss;\n+\t\t} conf;\n+\n+\tcase RTE_FLOW_ACTION_TYPE_RSS:\n+\t\tconf.rss = action->conf;\n+\t\t*size = offsetof(struct rte_flow_action_rss, queue) +\n+\t\t\tconf.rss->queues * sizeof(*conf.rss->queue);\n+\t\tbreak;\n+\tdefault:\n+empty:\n+\t\t*size = 0;\n+\t\tbreak;\n+\t}\n+\t*pad = RTE_ALIGN_CEIL(*size, sizeof(double)) - *size;\n+}\n+\n+/** Generate a port_flow entry from attributes/pattern/actions. */\n+static struct port_flow *\n+port_flow_new(const struct rte_flow_attr *attr,\n+\t      const struct rte_flow_item *pattern,\n+\t      const struct rte_flow_action *actions)\n+{\n+\tconst struct rte_flow_item *item;\n+\tconst struct rte_flow_action *action;\n+\tstruct port_flow *pf = NULL;\n+\tsize_t tmp;\n+\tsize_t pad;\n+\tsize_t off1 = 0;\n+\tsize_t off2 = 0;\n+\tint err = ENOTSUP;\n+\n+store:\n+\titem = pattern;\n+\tif (pf)\n+\t\tpf->pattern = (void *)&pf->data[off1];\n+\tdo {\n+\t\tstruct rte_flow_item *dst = NULL;\n+\n+\t\tif ((unsigned int)item->type > RTE_DIM(flow_item) ||\n+\t\t    !flow_item[item->type].name)\n+\t\t\tgoto notsup;\n+\t\tif (pf)\n+\t\t\tdst = memcpy(pf->data + off1, item, sizeof(*item));\n+\t\toff1 += sizeof(*item);\n+\t\tflow_item_spec_size(item, &tmp, &pad);\n+\t\tif (item->spec) {\n+\t\t\tif (pf)\n+\t\t\t\tdst->spec = memcpy(pf->data + off2,\n+\t\t\t\t\t\t   item->spec, tmp);\n+\t\t\toff2 += tmp + pad;\n+\t\t}\n+\t\tif (item->last) {\n+\t\t\tif (pf)\n+\t\t\t\tdst->last = memcpy(pf->data + off2,\n+\t\t\t\t\t\t   item->last, tmp);\n+\t\t\toff2 += tmp + pad;\n+\t\t}\n+\t\tif (item->mask) {\n+\t\t\tif (pf)\n+\t\t\t\tdst->mask = memcpy(pf->data + off2,\n+\t\t\t\t\t\t   item->mask, tmp);\n+\t\t\toff2 += tmp + pad;\n+\t\t}\n+\t\toff2 = RTE_ALIGN_CEIL(off2, sizeof(double));\n+\t} while ((item++)->type != RTE_FLOW_ITEM_TYPE_END);\n+\toff1 = RTE_ALIGN_CEIL(off1, sizeof(double));\n+\taction = actions;\n+\tif (pf)\n+\t\tpf->actions = (void *)&pf->data[off1];\n+\tdo {\n+\t\tstruct rte_flow_action *dst = NULL;\n+\n+\t\tif ((unsigned int)action->type > RTE_DIM(flow_action) ||\n+\t\t    !flow_action[action->type].name)\n+\t\t\tgoto notsup;\n+\t\tif (pf)\n+\t\t\tdst = memcpy(pf->data + off1, action, sizeof(*action));\n+\t\toff1 += sizeof(*action);\n+\t\tflow_action_conf_size(action, &tmp, &pad);\n+\t\tif (action->conf) {\n+\t\t\tif (pf)\n+\t\t\t\tdst->conf = memcpy(pf->data + off2,\n+\t\t\t\t\t\t   action->conf, tmp);\n+\t\t\toff2 += tmp + pad;\n+\t\t}\n+\t\toff2 = RTE_ALIGN_CEIL(off2, sizeof(double));\n+\t} while ((action++)->type != RTE_FLOW_ACTION_TYPE_END);\n+\tif (pf != NULL)\n+\t\treturn pf;\n+\toff1 = RTE_ALIGN_CEIL(off1, sizeof(double));\n+\ttmp = RTE_ALIGN_CEIL(offsetof(struct port_flow, data), sizeof(double));\n+\tpf = calloc(1, tmp + off1 + off2);\n+\tif (pf == NULL)\n+\t\terr = errno;\n+\telse {\n+\t\t*pf = (const struct port_flow){\n+\t\t\t.size = tmp + off1 + off2,\n+\t\t\t.attr = *attr,\n+\t\t};\n+\t\ttmp -= offsetof(struct port_flow, data);\n+\t\toff2 = tmp + off1;\n+\t\toff1 = tmp;\n+\t\tgoto store;\n+\t}\n+notsup:\n+\trte_errno = err;\n+\treturn NULL;\n+}\n+\n+/** Print a message out of a flow error. */\n+static int\n+port_flow_complain(struct rte_flow_error *error)\n+{\n+\tstatic const char *const errstrlist[] = {\n+\t\t[RTE_FLOW_ERROR_TYPE_NONE] = \"no error\",\n+\t\t[RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = \"cause unspecified\",\n+\t\t[RTE_FLOW_ERROR_TYPE_HANDLE] = \"flow rule (handle)\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = \"group field\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = \"priority field\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = \"ingress field\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = \"egress field\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ATTR] = \"attributes structure\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ITEM_NUM] = \"pattern length\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ITEM] = \"specific pattern item\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ACTION_NUM] = \"number of actions\",\n+\t\t[RTE_FLOW_ERROR_TYPE_ACTION] = \"specific action\",\n+\t};\n+\tconst char *errstr;\n+\tchar buf[32];\n+\tint err = rte_errno;\n+\n+\tif ((unsigned int)error->type > RTE_DIM(errstrlist) ||\n+\t    !errstrlist[error->type])\n+\t\terrstr = \"unknown type\";\n+\telse\n+\t\terrstr = errstrlist[error->type];\n+\tprintf(\"Caught error type %d (%s): %s%s\\n\",\n+\t       error->type, errstr,\n+\t       error->cause ? (snprintf(buf, sizeof(buf), \"cause: %p, \",\n+\t\t\t\t\terror->cause), buf) : \"\",\n+\t       error->message ? error->message : \"(no stated reason)\");\n+\treturn -err;\n+}\n+\n+/** Validate flow rule. */\n+int\n+port_flow_validate(portid_t port_id,\n+\t\t   const struct rte_flow_attr *attr,\n+\t\t   const struct rte_flow_item *pattern,\n+\t\t   const struct rte_flow_action *actions)\n+{\n+\tstruct rte_flow_error error;\n+\n+\tif (rte_flow_validate(port_id, attr, pattern, actions, &error))\n+\t\treturn port_flow_complain(&error);\n+\tprintf(\"Flow rule validated\\n\");\n+\treturn 0;\n+}\n+\n+/** Create flow rule. */\n+int\n+port_flow_create(portid_t port_id,\n+\t\t const struct rte_flow_attr *attr,\n+\t\t const struct rte_flow_item *pattern,\n+\t\t const struct rte_flow_action *actions)\n+{\n+\tstruct rte_flow *flow;\n+\tstruct rte_port *port;\n+\tstruct port_flow *pf;\n+\tuint32_t id;\n+\tstruct rte_flow_error error;\n+\n+\tflow = rte_flow_create(port_id, attr, pattern, actions, &error);\n+\tif (!flow)\n+\t\treturn port_flow_complain(&error);\n+\tport = &ports[port_id];\n+\tif (port->flow_list) {\n+\t\tif (port->flow_list->id == UINT32_MAX) {\n+\t\t\tprintf(\"Highest rule ID is already assigned, delete\"\n+\t\t\t       \" it first\");\n+\t\t\trte_flow_destroy(port_id, flow, NULL);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t\tid = port->flow_list->id + 1;\n+\t} else\n+\t\tid = 0;\n+\tpf = port_flow_new(attr, pattern, actions);\n+\tif (!pf) {\n+\t\tint err = rte_errno;\n+\n+\t\tprintf(\"Cannot allocate flow: %s\\n\", rte_strerror(err));\n+\t\trte_flow_destroy(port_id, flow, NULL);\n+\t\treturn -err;\n+\t}\n+\tpf->next = port->flow_list;\n+\tpf->id = id;\n+\tport->flow_list = pf;\n+\tprintf(\"Flow rule #%u created\\n\", pf->id);\n+\treturn 0;\n+}\n+\n+/** Destroy a number of flow rules. */\n+int\n+port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule)\n+{\n+\tstruct rte_port *port;\n+\tstruct port_flow **tmp;\n+\tuint32_t c = 0;\n+\tint ret = 0;\n+\n+\tif (port_id_is_invalid(port_id, ENABLED_WARN) ||\n+\t    port_id == (portid_t)RTE_PORT_ALL)\n+\t\treturn -EINVAL;\n+\tport = &ports[port_id];\n+\ttmp = &port->flow_list;\n+\twhile (*tmp) {\n+\t\tuint32_t i;\n+\n+\t\tfor (i = 0; i != n; ++i) {\n+\t\t\tstruct rte_flow_error error;\n+\t\t\tstruct port_flow *pf = *tmp;\n+\n+\t\t\tif (rule[i] != pf->id)\n+\t\t\t\tcontinue;\n+\t\t\tif (rte_flow_destroy(port_id, pf->flow, &error)) {\n+\t\t\t\tret = port_flow_complain(&error);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tprintf(\"Flow rule #%u destroyed\\n\", pf->id);\n+\t\t\t*tmp = pf->next;\n+\t\t\tfree(pf);\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (i == n)\n+\t\t\ttmp = &(*tmp)->next;\n+\t\t++c;\n+\t}\n+\treturn ret;\n+}\n+\n+/** Remove all flow rules. */\n+int\n+port_flow_flush(portid_t port_id)\n+{\n+\tstruct rte_flow_error error;\n+\tstruct rte_port *port;\n+\tint ret = 0;\n+\n+\tif (rte_flow_flush(port_id, &error)) {\n+\t\tret = port_flow_complain(&error);\n+\t\tif (port_id_is_invalid(port_id, DISABLED_WARN) ||\n+\t\t    port_id == (portid_t)RTE_PORT_ALL)\n+\t\t\treturn ret;\n+\t}\n+\tport = &ports[port_id];\n+\twhile (port->flow_list) {\n+\t\tstruct port_flow *pf = port->flow_list->next;\n+\n+\t\tfree(port->flow_list);\n+\t\tport->flow_list = pf;\n+\t}\n+\treturn ret;\n+}\n+\n+/** Query a flow rule. */\n+int\n+port_flow_query(portid_t port_id, uint32_t rule,\n+\t\tenum rte_flow_action_type action)\n+{\n+\tstruct rte_flow_error error;\n+\tstruct rte_port *port;\n+\tstruct port_flow *pf;\n+\tconst char *name;\n+\tunion {\n+\t\tstruct rte_flow_query_count count;\n+\t} query;\n+\n+\tif (port_id_is_invalid(port_id, ENABLED_WARN) ||\n+\t    port_id == (portid_t)RTE_PORT_ALL)\n+\t\treturn -EINVAL;\n+\tport = &ports[port_id];\n+\tfor (pf = port->flow_list; pf; pf = pf->next)\n+\t\tif (pf->id == rule)\n+\t\t\tbreak;\n+\tif (!pf) {\n+\t\tprintf(\"Flow rule #%u not found\\n\", rule);\n+\t\treturn -ENOENT;\n+\t}\n+\tif ((unsigned int)action > RTE_DIM(flow_action) ||\n+\t    !flow_action[action].name)\n+\t\tname = \"unknown\";\n+\telse\n+\t\tname = flow_action[action].name;\n+\tswitch (action) {\n+\tcase RTE_FLOW_ACTION_TYPE_COUNT:\n+\t\tbreak;\n+\tdefault:\n+\t\tprintf(\"Cannot query action type %d (%s)\\n\", action, name);\n+\t\treturn -ENOTSUP;\n+\t}\n+\tmemset(&query, 0, sizeof(query));\n+\tif (rte_flow_query(port_id, pf->flow, action, &query, &error))\n+\t\treturn port_flow_complain(&error);\n+\tswitch (action) {\n+\tcase RTE_FLOW_ACTION_TYPE_COUNT:\n+\t\tprintf(\"%s:\\n\"\n+\t\t       \" hits_set: %u\\n\"\n+\t\t       \" bytes_set: %u\\n\"\n+\t\t       \" hits: %\" PRIu64 \"\\n\"\n+\t\t       \" bytes: %\" PRIu64 \"\\n\",\n+\t\t       name,\n+\t\t       query.count.hits_set,\n+\t\t       query.count.bytes_set,\n+\t\t       query.count.hits,\n+\t\t       query.count.bytes);\n+\t\tbreak;\n+\tdefault:\n+\t\tprintf(\"Cannot display result for action type %d (%s).\\n\",\n+\t\t       action, name);\n+\t\tbreak;\n+\t}\n+\treturn 0;\n+}\n+\n+/** List flow rules. */\n+void\n+port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])\n+{\n+\tstruct rte_port *port;\n+\tstruct port_flow *pf;\n+\tstruct port_flow *list = NULL;\n+\tuint32_t i;\n+\n+\tif (port_id_is_invalid(port_id, ENABLED_WARN) ||\n+\t    port_id == (portid_t)RTE_PORT_ALL)\n+\t\treturn;\n+\tport = &ports[port_id];\n+\tif (!port->flow_list)\n+\t\treturn;\n+\t/* Sort flows by group, priority and ID. */\n+\tfor (pf = port->flow_list; pf != NULL; pf = pf->next) {\n+\t\tstruct port_flow **tmp;\n+\n+\t\tif (n) {\n+\t\t\t/* Filter out unwanted groups. */\n+\t\t\tfor (i = 0; i != n; ++i)\n+\t\t\t\tif (pf->attr.group == group[i])\n+\t\t\t\t\tbreak;\n+\t\t\tif (i == n)\n+\t\t\t\tcontinue;\n+\t\t}\n+\t\ttmp = &list;\n+\t\twhile (*tmp &&\n+\t\t       (pf->attr.group > (*tmp)->attr.group ||\n+\t\t\t(pf->attr.group == (*tmp)->attr.group &&\n+\t\t\t pf->attr.priority > (*tmp)->attr.priority) ||\n+\t\t\t(pf->attr.group == (*tmp)->attr.group &&\n+\t\t\t pf->attr.priority == (*tmp)->attr.priority &&\n+\t\t\t pf->id > (*tmp)->id)))\n+\t\t\ttmp = &(*tmp)->tmp;\n+\t\tpf->tmp = *tmp;\n+\t\t*tmp = pf;\n+\t}\n+\tprintf(\"ID\\tGroup\\tPrio\\tAttr\\tRule\\n\");\n+\tfor (pf = list; pf != NULL; pf = pf->tmp) {\n+\t\tconst struct rte_flow_item *item = pf->pattern;\n+\t\tconst struct rte_flow_action *action = pf->actions;\n+\n+\t\tprintf(\"%\" PRIu32 \"\\t%\" PRIu32 \"\\t%\" PRIu32 \"\\t%c%c\\t\",\n+\t\t       pf->id,\n+\t\t       pf->attr.group,\n+\t\t       pf->attr.priority,\n+\t\t       pf->attr.ingress ? 'i' : '-',\n+\t\t       pf->attr.egress ? 'e' : '-');\n+\t\twhile (item->type != RTE_FLOW_ITEM_TYPE_END) {\n+\t\t\tif (item->type != RTE_FLOW_ITEM_TYPE_VOID)\n+\t\t\t\tprintf(\"%s \", flow_item[item->type].name);\n+\t\t\t++item;\n+\t\t}\n+\t\tprintf(\"=>\");\n+\t\twhile (action->type != RTE_FLOW_ACTION_TYPE_END) {\n+\t\t\tif (action->type != RTE_FLOW_ACTION_TYPE_VOID)\n+\t\t\t\tprintf(\" %s\", flow_action[action->type].name);\n+\t\t\t++action;\n+\t\t}\n+\t\tprintf(\"\\n\");\n+\t}\n+}\n+\n /*\n  * RX/TX ring descriptors display functions.\n  */\ndiff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c\nindex 57e6ae2..dd67ebf 100644\n--- a/app/test-pmd/csumonly.c\n+++ b/app/test-pmd/csumonly.c\n@@ -70,6 +70,7 @@\n #include <rte_sctp.h>\n #include <rte_prefetch.h>\n #include <rte_string_fns.h>\n+#include <rte_flow.h>\n #include \"testpmd.h\"\n \n #define IP_DEFTTL  64   /* from RFC 1340. */\ndiff --git a/app/test-pmd/flowgen.c b/app/test-pmd/flowgen.c\nindex b13ff89..13b4f90 100644\n--- a/app/test-pmd/flowgen.c\n+++ b/app/test-pmd/flowgen.c\n@@ -68,6 +68,7 @@\n #include <rte_tcp.h>\n #include <rte_udp.h>\n #include <rte_string_fns.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/icmpecho.c b/app/test-pmd/icmpecho.c\nindex 6a4e750..f25a8f5 100644\n--- a/app/test-pmd/icmpecho.c\n+++ b/app/test-pmd/icmpecho.c\n@@ -61,6 +61,7 @@\n #include <rte_ip.h>\n #include <rte_icmp.h>\n #include <rte_string_fns.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/ieee1588fwd.c b/app/test-pmd/ieee1588fwd.c\nindex 0d3b37a..51170ee 100644\n--- a/app/test-pmd/ieee1588fwd.c\n+++ b/app/test-pmd/ieee1588fwd.c\n@@ -34,6 +34,7 @@\n \n #include <rte_cycles.h>\n #include <rte_ethdev.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/iofwd.c b/app/test-pmd/iofwd.c\nindex 26936b7..15cb4a2 100644\n--- a/app/test-pmd/iofwd.c\n+++ b/app/test-pmd/iofwd.c\n@@ -64,6 +64,7 @@\n #include <rte_ether.h>\n #include <rte_ethdev.h>\n #include <rte_string_fns.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/macfwd.c b/app/test-pmd/macfwd.c\nindex 86e01de..d361db1 100644\n--- a/app/test-pmd/macfwd.c\n+++ b/app/test-pmd/macfwd.c\n@@ -65,6 +65,7 @@\n #include <rte_ethdev.h>\n #include <rte_ip.h>\n #include <rte_string_fns.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/macswap.c b/app/test-pmd/macswap.c\nindex 36e139f..f996039 100644\n--- a/app/test-pmd/macswap.c\n+++ b/app/test-pmd/macswap.c\n@@ -65,6 +65,7 @@\n #include <rte_ethdev.h>\n #include <rte_ip.h>\n #include <rte_string_fns.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c\nindex 08e5a76..28db8cd 100644\n--- a/app/test-pmd/parameters.c\n+++ b/app/test-pmd/parameters.c\n@@ -76,6 +76,7 @@\n #ifdef RTE_LIBRTE_PMD_BOND\n #include <rte_eth_bond.h>\n #endif\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/rxonly.c b/app/test-pmd/rxonly.c\nindex fff815c..cf00576 100644\n--- a/app/test-pmd/rxonly.c\n+++ b/app/test-pmd/rxonly.c\n@@ -67,6 +67,7 @@\n #include <rte_ip.h>\n #include <rte_udp.h>\n #include <rte_net.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \ndiff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c\nindex a0332c2..bfb2f8e 100644\n--- a/app/test-pmd/testpmd.c\n+++ b/app/test-pmd/testpmd.c\n@@ -78,6 +78,7 @@\n #ifdef RTE_LIBRTE_PDUMP\n #include <rte_pdump.h>\n #endif\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \n@@ -1545,6 +1546,8 @@ close_port(portid_t pid)\n \t\t\tcontinue;\n \t\t}\n \n+\t\tif (port->flow_list)\n+\t\t\tport_flow_flush(pi);\n \t\trte_eth_dev_close(pi);\n \n \t\tif (rte_atomic16_cmpset(&(port->port_status),\n@@ -1599,6 +1602,9 @@ detach_port(uint8_t port_id)\n \t\treturn;\n \t}\n \n+\tif (ports[port_id].flow_list)\n+\t\tport_flow_flush(port_id);\n+\n \tif (rte_eth_dev_detach(port_id, name))\n \t\treturn;\n \ndiff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h\nindex 9c1e703..22ce2d6 100644\n--- a/app/test-pmd/testpmd.h\n+++ b/app/test-pmd/testpmd.h\n@@ -144,6 +144,19 @@ struct fwd_stream {\n /** Insert double VLAN header in forward engine */\n #define TESTPMD_TX_OFFLOAD_INSERT_QINQ       0x0080\n \n+/** Descriptor for a single flow. */\n+struct port_flow {\n+\tsize_t size; /**< Allocated space including data[]. */\n+\tstruct port_flow *next; /**< Next flow in list. */\n+\tstruct port_flow *tmp; /**< Temporary linking. */\n+\tuint32_t id; /**< Flow rule ID. */\n+\tstruct rte_flow *flow; /**< Opaque flow object returned by PMD. */\n+\tstruct rte_flow_attr attr; /**< Attributes. */\n+\tstruct rte_flow_item *pattern; /**< Pattern. */\n+\tstruct rte_flow_action *actions; /**< Actions. */\n+\tuint8_t data[]; /**< Storage for pattern/actions. */\n+};\n+\n /**\n  * The data structure associated with each port.\n  */\n@@ -177,6 +190,7 @@ struct rte_port {\n \tstruct ether_addr       *mc_addr_pool; /**< pool of multicast addrs */\n \tuint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */\n \tuint8_t                 slave_flag; /**< bonding slave port */\n+\tstruct port_flow        *flow_list; /**< Associated flows. */\n };\n \n extern portid_t __rte_unused\n@@ -504,6 +518,19 @@ void port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,\n \t\t\t    uint8_t bit1_pos, uint8_t bit2_pos, uint32_t value);\n void port_reg_display(portid_t port_id, uint32_t reg_off);\n void port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t value);\n+int port_flow_validate(portid_t port_id,\n+\t\t       const struct rte_flow_attr *attr,\n+\t\t       const struct rte_flow_item *pattern,\n+\t\t       const struct rte_flow_action *actions);\n+int port_flow_create(portid_t port_id,\n+\t\t     const struct rte_flow_attr *attr,\n+\t\t     const struct rte_flow_item *pattern,\n+\t\t     const struct rte_flow_action *actions);\n+int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);\n+int port_flow_flush(portid_t port_id);\n+int port_flow_query(portid_t port_id, uint32_t rule,\n+\t\t    enum rte_flow_action_type action);\n+void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);\n \n void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id);\n void tx_ring_desc_display(portid_t port_id, queueid_t txq_id, uint16_t txd_id);\ndiff --git a/app/test-pmd/txonly.c b/app/test-pmd/txonly.c\nindex 8513a06..e996f35 100644\n--- a/app/test-pmd/txonly.c\n+++ b/app/test-pmd/txonly.c\n@@ -68,6 +68,7 @@\n #include <rte_tcp.h>\n #include <rte_udp.h>\n #include <rte_string_fns.h>\n+#include <rte_flow.h>\n \n #include \"testpmd.h\"\n \n",
    "prefixes": [
        "dpdk-dev",
        "04/22"
    ]
}