Show a patch.

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

{
    "id": 44068,
    "url": "http://patches.dpdk.org/api/patches/44068/",
    "web_url": "http://patches.dpdk.org/patch/44068/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/",
        "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"
    },
    "msgid": "<20180831085337.21419-2-adrien.mazarguil@6wind.com>",
    "date": "2018-08-31T09:01:00",
    "name": "[v3,1/7] ethdev: add flow API object converter",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "57c5dbaa40222b6adeb262798223823a478ecebf",
    "submitter": {
        "id": 165,
        "url": "http://patches.dpdk.org/api/people/165/",
        "name": "Adrien Mazarguil",
        "email": "adrien.mazarguil@6wind.com"
    },
    "delegate": {
        "id": 319,
        "url": "http://patches.dpdk.org/api/users/319/",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@intel.com"
    },
    "mbox": "http://patches.dpdk.org/patch/44068/mbox/",
    "series": [
        {
            "id": 1123,
            "url": "http://patches.dpdk.org/api/series/1123/",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=1123",
            "date": "2018-08-31T09:00:57",
            "name": "ethdev: add flow API object converter",
            "version": 3,
            "mbox": "http://patches.dpdk.org/series/1123/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/44068/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/44068/checks/",
    "tags": {},
    "headers": {
        "X-Mailman-Version": "2.1.15",
        "In-Reply-To": "<20180831085337.21419-1-adrien.mazarguil@6wind.com>",
        "Errors-To": "dev-bounces@dpdk.org",
        "X-Mailer": "git-send-email 2.11.0",
        "Received": [
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id BAD874C9D;\n\tFri, 31 Aug 2018 11:01:19 +0200 (CEST)",
            "from mail-wm0-f65.google.com (mail-wm0-f65.google.com\n\t[74.125.82.65]) by dpdk.org (Postfix) with ESMTP id B87B94C90\n\tfor <dev@dpdk.org>; Fri, 31 Aug 2018 11:01:17 +0200 (CEST)",
            "by mail-wm0-f65.google.com with SMTP id t25-v6so4568239wmi.3\n\tfor <dev@dpdk.org>; Fri, 31 Aug 2018 02:01:17 -0700 (PDT)",
            "from 6wind.com (host.78.145.23.62.rev.coltfrance.com.\n\t[62.23.145.78]) by smtp.gmail.com with ESMTPSA id\n\th82-v6sm3198390wme.11.2018.08.31.02.01.16\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tFri, 31 Aug 2018 02:01:16 -0700 (PDT)"
        ],
        "References": "<20180803132032.29038-1-adrien.mazarguil@6wind.com>\n\t<20180831085337.21419-1-adrien.mazarguil@6wind.com>",
        "X-Google-Smtp-Source": "ANB0VdaEmyORj5Wtv0/nWzb+zcVm7n6pLReldnX9N6G+Qq4VYoSGgKEUuO3oFLFsy8gFk7YgchvGYQ==",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=6wind-com.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:in-reply-to;\n\tbh=g7sdEZQUr7esOW41/mfdEuP/I0JW2i7OXw6w4kCWNtE=;\n\tb=ZjyjC2+Iw1cw5R/nLYC0DvYUwrF6Ywb9uxtjd5VeRxcVlEE9ySdZSuBwXJqMg4Tn3X\n\t6IEtwDx8bQjpMRejhzK+34wI4F7GiIxJbzYppMihxCxmWjBhNIPfRvbSF/3WoLt+4lnx\n\taVbg8Oi7Di6D2MGra9Yv6jOaAUqpvgvF3oat3mTM9o98kL34pP/0zYR3Vngvaxlz9yKf\n\tVBjK5h5hX1H7GNWmdgU4VZnU2V6XwIA/d7j/DCpdNglFJ+zreybQHdbFEZOlCkaCcmk1\n\tgzrqZJQ7v72kjZZlvbPTo7EROO076i4IXBfM1yGGcAAHTwi5QaURRZnDr2B8S/ZC3xy3\n\tBipg==",
        "MIME-Version": "1.0",
        "X-Received": "by 2002:a1c:c642:: with SMTP id\n\tw63-v6mr2146567wmf.3.1535706077149; \n\tFri, 31 Aug 2018 02:01:17 -0700 (PDT)",
        "Message-ID": "<20180831085337.21419-2-adrien.mazarguil@6wind.com>",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:in-reply-to;\n\tbh=g7sdEZQUr7esOW41/mfdEuP/I0JW2i7OXw6w4kCWNtE=;\n\tb=pyqqXUr4Ic4aTlvxEnqpPHd19svVbI2Ns12bQtwthD7m33HK1kydRFvoJ+O726tt2J\n\tzuxcnHj5hN6bmeS8EtJI/YupjmBkyhZS3TNWJZ/M/nz/nfAkoaeahAUVPlDAbFg/1BWK\n\tC2ZGUYdGKoK4MhryERHgJSi22pcMjop8+f3ZFopMnoYy7Y527EWUV9il4HFwmzXBFETy\n\tVXxhx+VWM+HXUcTv02UtBMCu53NhUt+u4bHNggXSkMwmoyzNku1aEOF8e/brJGUoTieM\n\tEaLOhRIZ3fCAHUu7VsFow+W4CBy0GsnwnLbO15lxKCOdZNb/6y0P176UpHmwAwAOUAcv\n\tKgbw==",
        "Delivered-To": "patchwork@dpdk.org",
        "Precedence": "list",
        "From": "Adrien Mazarguil <adrien.mazarguil@6wind.com>",
        "X-Original-To": "patchwork@dpdk.org",
        "Content-Type": "text/plain; charset=us-ascii",
        "Content-Disposition": "inline",
        "Return-Path": "<dev-bounces@dpdk.org>",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "To": "Ferruh Yigit <ferruh.yigit@intel.com>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "Date": "Fri, 31 Aug 2018 11:01:00 +0200",
        "X-Gm-Message-State": "APzg51BUl8IPoXZYQvCTF2B2D2Kro7SHLiWqVisJd2qx54+fLSgv6TaM\n\tZsyMiY3UpyoLBxR2FBf6QjxBLQ==",
        "Cc": "dev@dpdk.org, Thomas Monjalon <thomas@monjalon.net>,\n\tAndrew Rybchenko <arybchenko@solarflare.com>,\n\tGaetan Rivet <gaetan.rivet@6wind.com>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "X-BeenThere": "dev@dpdk.org",
        "Subject": "[dpdk-dev] [PATCH v3 1/7] ethdev: add flow API object converter"
    },
    "content": "rte_flow_copy() is bound to duplicate flow rule descriptions (attributes,\npattern and list of actions, all at once), however applications sometimes\nneed more flexibility, for instance the ability to duplicate only one of\nthe underlying objects (a single pattern item or action) or retrieve other\nproperties such as their names.\n\nInstead of adding dedicated functions to handle each possible use case,\nthis patch introduces rte_flow_conv(), which supports any number of object\nconversion operations in an extensible manner.\n\nThis patch re-implements rte_flow_copy() as a wrapper to rte_flow_conv().\n\nSigned-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>\nCc: Thomas Monjalon <thomas@monjalon.net>\nCc: Ferruh Yigit <ferruh.yigit@intel.com>\nCc: Andrew Rybchenko <arybchenko@solarflare.com>\nCc: Gaetan Rivet <gaetan.rivet@6wind.com>\n--\nv3 changes:\n\n- Worked around compilation issue on ARM where rte_memcpy() is a macro that\n  chokes on commas.\n\n- Marked rte_flow_conv() as experimental.\n\nv2 changes:\n\n- Modified patch to keep rte_flow_copy() around instead of removing it\n  entirely. Reworded commit log accordingly.\n\n- Moved failsafe PMD changes to a subsequent patch.\n\n- Re-implemented rte_flow_copy() as a wrapper to rte_flow_conv() to reduce\n  code duplication.\n\n- Tweaked semantics of rte_flow_conv() to return the required number of\n  bytes regardless of the size parameter; a buffer not large enough is not\n  considered to be an error anymore. This change removes the need for a\n  \"store\" pass in underlying helper functions.\n\n- Renamed and properly documented internal helper functions.\n---\n doc/guides/prog_guide/rte_flow.rst       |  19 +\n lib/librte_ethdev/rte_ethdev_version.map |   1 +\n lib/librte_ethdev/rte_flow.c             | 553 ++++++++++++++++++--------\n lib/librte_ethdev/rte_flow.h             | 170 +++++++-\n 4 files changed, 582 insertions(+), 161 deletions(-)",
    "diff": "diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst\nindex b305a72a5..964cf9ceb 100644\n--- a/doc/guides/prog_guide/rte_flow.rst\n+++ b/doc/guides/prog_guide/rte_flow.rst\n@@ -2419,6 +2419,25 @@ This function initializes ``error`` (if non-NULL) with the provided\n parameters and sets ``rte_errno`` to ``code``. A negative error ``code`` is\n then returned.\n \n+Object conversion\n+~~~~~~~~~~~~~~~~~\n+\n+.. code-block:: c\n+\n+   int\n+   rte_flow_conv(enum rte_flow_conv_op op,\n+                 void *dst,\n+                 size_t size,\n+                 const void *src,\n+                 struct rte_flow_error *error);\n+\n+Convert ``src`` to ``dst`` according to operation ``op``. Possible\n+operations include:\n+\n+- Attributes, pattern item or action duplication.\n+- Duplication of an entire pattern or list of actions.\n+- Duplication of a complete flow rule description.\n+\n Caveats\n -------\n \ndiff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map\nindex 38f117f01..2ee9173a1 100644\n--- a/lib/librte_ethdev/rte_ethdev_version.map\n+++ b/lib/librte_ethdev/rte_ethdev_version.map\n@@ -239,6 +239,7 @@ EXPERIMENTAL {\n \trte_eth_dev_tx_offload_name;\n \trte_eth_switch_domain_alloc;\n \trte_eth_switch_domain_free;\n+\trte_flow_conv;\n \trte_flow_expand_rss;\n \trte_mtr_capabilities_get;\n \trte_mtr_create;\ndiff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c\nindex cff4b5209..4fd6cfa76 100644\n--- a/lib/librte_ethdev/rte_flow.c\n+++ b/lib/librte_ethdev/rte_flow.c\n@@ -288,26 +288,41 @@ rte_flow_error_set(struct rte_flow_error *error,\n }\n \n /** Pattern item specification types. */\n-enum item_spec_type {\n-\tITEM_SPEC,\n-\tITEM_LAST,\n-\tITEM_MASK,\n+enum rte_flow_conv_item_spec_type {\n+\tRTE_FLOW_CONV_ITEM_SPEC,\n+\tRTE_FLOW_CONV_ITEM_LAST,\n+\tRTE_FLOW_CONV_ITEM_MASK,\n };\n \n-/** Compute storage space needed by item specification and copy it. */\n+/**\n+ * Copy pattern item specification.\n+ *\n+ * @param[out] buf\n+ *   Output buffer. Can be NULL if @p size is zero.\n+ * @param size\n+ *   Size of @p buf in bytes.\n+ * @param[in] item\n+ *   Pattern item to copy specification from.\n+ * @param type\n+ *   Specification selector for either @p spec, @p last or @p mask.\n+ *\n+ * @return\n+ *   Number of bytes needed to store pattern item specification regardless\n+ *   of @p size. @p buf contents are truncated to @p size if not large\n+ *   enough.\n+ */\n static size_t\n-flow_item_spec_copy(void *buf, const struct rte_flow_item *item,\n-\t\t    enum item_spec_type type)\n+rte_flow_conv_item_spec(void *buf, const size_t size,\n+\t\t\tconst struct rte_flow_item *item,\n+\t\t\tenum rte_flow_conv_item_spec_type type)\n {\n-\tsize_t size = 0;\n+\tsize_t off;\n \tconst void *data =\n-\t\ttype == ITEM_SPEC ? item->spec :\n-\t\ttype == ITEM_LAST ? item->last :\n-\t\ttype == ITEM_MASK ? item->mask :\n+\t\ttype == RTE_FLOW_CONV_ITEM_SPEC ? item->spec :\n+\t\ttype == RTE_FLOW_CONV_ITEM_LAST ? item->last :\n+\t\ttype == RTE_FLOW_CONV_ITEM_MASK ? item->mask :\n \t\tNULL;\n \n-\tif (!item->spec || !data)\n-\t\tgoto empty;\n \tswitch (item->type) {\n \t\tunion {\n \t\t\tconst struct rte_flow_item_raw *raw;\n@@ -324,7 +339,7 @@ flow_item_spec_copy(void *buf, const struct rte_flow_item *item,\n \t\tunion {\n \t\t\tstruct rte_flow_item_raw *raw;\n \t\t} dst;\n-\t\tsize_t off;\n+\t\tsize_t tmp;\n \n \tcase RTE_FLOW_ITEM_TYPE_RAW:\n \t\tspec.raw = item->spec;\n@@ -332,41 +347,62 @@ flow_item_spec_copy(void *buf, const struct rte_flow_item *item,\n \t\tmask.raw = item->mask ? item->mask : &rte_flow_item_raw_mask;\n \t\tsrc.raw = data;\n \t\tdst.raw = buf;\n-\t\toff = RTE_ALIGN_CEIL(sizeof(struct rte_flow_item_raw),\n-\t\t\t\t     sizeof(*src.raw->pattern));\n-\t\tif (type == ITEM_SPEC ||\n-\t\t    (type == ITEM_MASK &&\n+\t\trte_memcpy(dst.raw,\n+\t\t\t   (&(struct rte_flow_item_raw){\n+\t\t\t\t.relative = src.raw->relative,\n+\t\t\t\t.search = src.raw->search,\n+\t\t\t\t.reserved = src.raw->reserved,\n+\t\t\t\t.offset = src.raw->offset,\n+\t\t\t\t.limit = src.raw->limit,\n+\t\t\t\t.length = src.raw->length,\n+\t\t\t   }),\n+\t\t\t   size > sizeof(*dst.raw) ? sizeof(*dst.raw) : size);\n+\t\toff = sizeof(*dst.raw);\n+\t\tif (type == RTE_FLOW_CONV_ITEM_SPEC ||\n+\t\t    (type == RTE_FLOW_CONV_ITEM_MASK &&\n \t\t     ((spec.raw->length & mask.raw->length) >=\n \t\t      (last.raw->length & mask.raw->length))))\n-\t\t\tsize = spec.raw->length & mask.raw->length;\n+\t\t\ttmp = spec.raw->length & mask.raw->length;\n \t\telse\n-\t\t\tsize = last.raw->length & mask.raw->length;\n-\t\tsize = off + size * sizeof(*src.raw->pattern);\n-\t\tif (dst.raw) {\n-\t\t\tmemcpy(dst.raw, src.raw, sizeof(*src.raw));\n-\t\t\tdst.raw->pattern = memcpy((uint8_t *)dst.raw + off,\n-\t\t\t\t\t\t  src.raw->pattern,\n-\t\t\t\t\t\t  size - off);\n+\t\t\ttmp = last.raw->length & mask.raw->length;\n+\t\tif (tmp) {\n+\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(*dst.raw->pattern));\n+\t\t\tif (size >= off + tmp)\n+\t\t\t\tdst.raw->pattern = rte_memcpy\n+\t\t\t\t\t((void *)((uintptr_t)dst.raw + off),\n+\t\t\t\t\t src.raw->pattern, tmp);\n+\t\t\toff += tmp;\n \t\t}\n \t\tbreak;\n \tdefault:\n-\t\tsize = rte_flow_desc_item[item->type].size;\n-\t\tif (buf)\n-\t\t\tmemcpy(buf, data, size);\n+\t\toff = rte_flow_desc_item[item->type].size;\n+\t\trte_memcpy(buf, data, (size > off ? off : size));\n \t\tbreak;\n \t}\n-empty:\n-\treturn RTE_ALIGN_CEIL(size, sizeof(double));\n+\treturn off;\n }\n \n-/** Compute storage space needed by action configuration and copy it. */\n+/**\n+ * Copy action configuration.\n+ *\n+ * @param[out] buf\n+ *   Output buffer. Can be NULL if @p size is zero.\n+ * @param size\n+ *   Size of @p buf in bytes.\n+ * @param[in] action\n+ *   Action to copy configuration from.\n+ *\n+ * @return\n+ *   Number of bytes needed to store pattern item specification regardless\n+ *   of @p size. @p buf contents are truncated to @p size if not large\n+ *   enough.\n+ */\n static size_t\n-flow_action_conf_copy(void *buf, const struct rte_flow_action *action)\n+rte_flow_conv_action_conf(void *buf, const size_t size,\n+\t\t\t  const struct rte_flow_action *action)\n {\n-\tsize_t size = 0;\n+\tsize_t off;\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@@ -374,49 +410,308 @@ flow_action_conf_copy(void *buf, const struct rte_flow_action *action)\n \t\tunion {\n \t\t\tstruct rte_flow_action_rss *rss;\n \t\t} dst;\n-\t\tsize_t off;\n+\t\tsize_t tmp;\n \n \tcase RTE_FLOW_ACTION_TYPE_RSS:\n \t\tsrc.rss = action->conf;\n \t\tdst.rss = buf;\n-\t\toff = 0;\n-\t\tif (dst.rss)\n-\t\t\t*dst.rss = (struct rte_flow_action_rss){\n+\t\trte_memcpy(dst.rss,\n+\t\t\t   (&(struct rte_flow_action_rss){\n \t\t\t\t.func = src.rss->func,\n \t\t\t\t.level = src.rss->level,\n \t\t\t\t.types = src.rss->types,\n \t\t\t\t.key_len = src.rss->key_len,\n \t\t\t\t.queue_num = src.rss->queue_num,\n-\t\t\t};\n-\t\toff += sizeof(*src.rss);\n+\t\t\t   }),\n+\t\t\t   size > sizeof(*dst.rss) ? sizeof(*dst.rss) : size);\n+\t\toff = sizeof(*dst.rss);\n \t\tif (src.rss->key_len) {\n-\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n-\t\t\tsize = sizeof(*src.rss->key) * src.rss->key_len;\n-\t\t\tif (dst.rss)\n-\t\t\t\tdst.rss->key = memcpy\n+\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(*dst.rss->key));\n+\t\t\ttmp = sizeof(*src.rss->key) * src.rss->key_len;\n+\t\t\tif (size >= off + tmp)\n+\t\t\t\tdst.rss->key = rte_memcpy\n \t\t\t\t\t((void *)((uintptr_t)dst.rss + off),\n-\t\t\t\t\t src.rss->key, size);\n-\t\t\toff += size;\n+\t\t\t\t\t src.rss->key, tmp);\n+\t\t\toff += tmp;\n \t\t}\n \t\tif (src.rss->queue_num) {\n-\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n-\t\t\tsize = sizeof(*src.rss->queue) * src.rss->queue_num;\n-\t\t\tif (dst.rss)\n-\t\t\t\tdst.rss->queue = memcpy\n+\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(*dst.rss->queue));\n+\t\t\ttmp = sizeof(*src.rss->queue) * src.rss->queue_num;\n+\t\t\tif (size >= off + tmp)\n+\t\t\t\tdst.rss->queue = rte_memcpy\n \t\t\t\t\t((void *)((uintptr_t)dst.rss + off),\n-\t\t\t\t\t src.rss->queue, size);\n-\t\t\toff += size;\n+\t\t\t\t\t src.rss->queue, tmp);\n+\t\t\toff += tmp;\n \t\t}\n-\t\tsize = off;\n \t\tbreak;\n \tdefault:\n-\t\tsize = rte_flow_desc_action[action->type].size;\n-\t\tif (buf)\n-\t\t\tmemcpy(buf, action->conf, size);\n+\t\toff = rte_flow_desc_action[action->type].size;\n+\t\trte_memcpy(buf, action->conf, (size > off ? off : size));\n \t\tbreak;\n \t}\n-empty:\n-\treturn RTE_ALIGN_CEIL(size, sizeof(double));\n+\treturn off;\n+}\n+\n+/**\n+ * Copy a list of pattern items.\n+ *\n+ * @param[out] dst\n+ *   Destination buffer. Can be NULL if @p size is zero.\n+ * @param size\n+ *   Size of @p dst in bytes.\n+ * @param[in] src\n+ *   Source pattern items.\n+ * @param num\n+ *   Maximum number of pattern items to process from @p src or 0 to process\n+ *   the entire list. In both cases, processing stops after\n+ *   RTE_FLOW_ITEM_TYPE_END is encountered.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL.\n+ *\n+ * @return\n+ *   A positive value representing the number of bytes needed to store\n+ *   pattern items regardless of @p size on success (@p buf contents are\n+ *   truncated to @p size if not large enough), a negative errno value\n+ *   otherwise and rte_errno is set.\n+ */\n+static int\n+rte_flow_conv_pattern(struct rte_flow_item *dst,\n+\t\t      const size_t size,\n+\t\t      const struct rte_flow_item *src,\n+\t\t      unsigned int num,\n+\t\t      struct rte_flow_error *error)\n+{\n+\tuintptr_t data = (uintptr_t)dst;\n+\tsize_t off;\n+\tsize_t ret;\n+\tunsigned int i;\n+\n+\tfor (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {\n+\t\tif ((size_t)src->type >= RTE_DIM(rte_flow_desc_item) ||\n+\t\t    !rte_flow_desc_item[src->type].name)\n+\t\t\treturn rte_flow_error_set\n+\t\t\t\t(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, src,\n+\t\t\t\t \"cannot convert unknown item type\");\n+\t\tif (size >= off + sizeof(*dst))\n+\t\t\t*dst = (struct rte_flow_item){\n+\t\t\t\t.type = src->type,\n+\t\t\t};\n+\t\toff += sizeof(*dst);\n+\t\tif (!src->type)\n+\t\t\tnum = i + 1;\n+\t}\n+\tnum = i;\n+\tsrc -= num;\n+\tdst -= num;\n+\tdo {\n+\t\tif (src->spec) {\n+\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n+\t\t\tret = rte_flow_conv_item_spec\n+\t\t\t\t((void *)(data + off),\n+\t\t\t\t size > off ? size - off : 0, src,\n+\t\t\t\t RTE_FLOW_CONV_ITEM_SPEC);\n+\t\t\tif (size && size >= off + ret)\n+\t\t\t\tdst->spec = (void *)(data + off);\n+\t\t\toff += ret;\n+\n+\t\t}\n+\t\tif (src->last) {\n+\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n+\t\t\tret = rte_flow_conv_item_spec\n+\t\t\t\t((void *)(data + off),\n+\t\t\t\t size > off ? size - off : 0, src,\n+\t\t\t\t RTE_FLOW_CONV_ITEM_LAST);\n+\t\t\tif (size && size >= off + ret)\n+\t\t\t\tdst->last = (void *)(data + off);\n+\t\t\toff += ret;\n+\t\t}\n+\t\tif (src->mask) {\n+\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n+\t\t\tret = rte_flow_conv_item_spec\n+\t\t\t\t((void *)(data + off),\n+\t\t\t\t size > off ? size - off : 0, src,\n+\t\t\t\t RTE_FLOW_CONV_ITEM_MASK);\n+\t\t\tif (size && size >= off + ret)\n+\t\t\t\tdst->mask = (void *)(data + off);\n+\t\t\toff += ret;\n+\t\t}\n+\t\t++src;\n+\t\t++dst;\n+\t} while (--num);\n+\treturn off;\n+}\n+\n+/**\n+ * Copy a list of actions.\n+ *\n+ * @param[out] dst\n+ *   Destination buffer. Can be NULL if @p size is zero.\n+ * @param size\n+ *   Size of @p dst in bytes.\n+ * @param[in] src\n+ *   Source actions.\n+ * @param num\n+ *   Maximum number of actions to process from @p src or 0 to process the\n+ *   entire list. In both cases, processing stops after\n+ *   RTE_FLOW_ACTION_TYPE_END is encountered.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL.\n+ *\n+ * @return\n+ *   A positive value representing the number of bytes needed to store\n+ *   actions regardless of @p size on success (@p buf contents are truncated\n+ *   to @p size if not large enough), a negative errno value otherwise and\n+ *   rte_errno is set.\n+ */\n+static int\n+rte_flow_conv_actions(struct rte_flow_action *dst,\n+\t\t      const size_t size,\n+\t\t      const struct rte_flow_action *src,\n+\t\t      unsigned int num,\n+\t\t      struct rte_flow_error *error)\n+{\n+\tuintptr_t data = (uintptr_t)dst;\n+\tsize_t off;\n+\tsize_t ret;\n+\tunsigned int i;\n+\n+\tfor (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {\n+\t\tif ((size_t)src->type >= RTE_DIM(rte_flow_desc_action) ||\n+\t\t    !rte_flow_desc_action[src->type].name)\n+\t\t\treturn rte_flow_error_set\n+\t\t\t\t(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t src, \"cannot convert unknown action type\");\n+\t\tif (size >= off + sizeof(*dst))\n+\t\t\t*dst = (struct rte_flow_action){\n+\t\t\t\t.type = src->type,\n+\t\t\t};\n+\t\toff += sizeof(*dst);\n+\t\tif (!src->type)\n+\t\t\tnum = i + 1;\n+\t}\n+\tnum = i;\n+\tsrc -= num;\n+\tdst -= num;\n+\tdo {\n+\t\tif (src->conf) {\n+\t\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n+\t\t\tret = rte_flow_conv_action_conf\n+\t\t\t\t((void *)(data + off),\n+\t\t\t\t size > off ? size - off : 0, src);\n+\t\t\tif (size && size >= off + ret)\n+\t\t\t\tdst->conf = (void *)(data + off);\n+\t\t\toff += ret;\n+\t\t}\n+\t\t++src;\n+\t\t++dst;\n+\t} while (--num);\n+\treturn off;\n+}\n+\n+/**\n+ * Copy flow rule components.\n+ *\n+ * This comprises the flow rule descriptor itself, attributes, pattern and\n+ * actions list. NULL components in @p src are skipped.\n+ *\n+ * @param[out] dst\n+ *   Destination buffer. Can be NULL if @p size is zero.\n+ * @param size\n+ *   Size of @p dst in bytes.\n+ * @param[in] src\n+ *   Source flow rule descriptor.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL.\n+ *\n+ * @return\n+ *   A positive value representing the number of bytes needed to store all\n+ *   components including the descriptor regardless of @p size on success\n+ *   (@p buf contents are truncated to @p size if not large enough), a\n+ *   negative errno value otherwise and rte_errno is set.\n+ */\n+static int\n+rte_flow_conv_rule(struct rte_flow_conv_rule *dst,\n+\t\t   const size_t size,\n+\t\t   const struct rte_flow_conv_rule *src,\n+\t\t   struct rte_flow_error *error)\n+{\n+\tsize_t off;\n+\tint ret;\n+\n+\trte_memcpy(dst,\n+\t\t   (&(struct rte_flow_conv_rule){\n+\t\t\t.attr = NULL,\n+\t\t\t.pattern = NULL,\n+\t\t\t.actions = NULL,\n+\t\t   }),\n+\t\t   size > sizeof(*dst) ? sizeof(*dst) : size);\n+\toff = sizeof(*dst);\n+\tif (src->attr_ro) {\n+\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n+\t\tif (size && size >= off + sizeof(*dst->attr))\n+\t\t\tdst->attr = rte_memcpy\n+\t\t\t\t((void *)((uintptr_t)dst + off),\n+\t\t\t\t src->attr_ro, sizeof(*dst->attr));\n+\t\toff += sizeof(*dst->attr);\n+\t}\n+\tif (src->pattern_ro) {\n+\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n+\t\tret = rte_flow_conv_pattern((void *)((uintptr_t)dst + off),\n+\t\t\t\t\t    size > off ? size - off : 0,\n+\t\t\t\t\t    src->pattern_ro, 0, error);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\tif (size && size >= off + (size_t)ret)\n+\t\t\tdst->pattern = (void *)((uintptr_t)dst + off);\n+\t\toff += ret;\n+\t}\n+\tif (src->actions_ro) {\n+\t\toff = RTE_ALIGN_CEIL(off, sizeof(double));\n+\t\tret = rte_flow_conv_actions((void *)((uintptr_t)dst + off),\n+\t\t\t\t\t    size > off ? size - off : 0,\n+\t\t\t\t\t    src->actions_ro, 0, error);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\tif (size >= off + (size_t)ret)\n+\t\t\tdst->actions = (void *)((uintptr_t)dst + off);\n+\t\toff += ret;\n+\t}\n+\treturn off;\n+}\n+\n+/** Helper function to convert flow API objects. */\n+int\n+rte_flow_conv(enum rte_flow_conv_op op,\n+\t      void *dst,\n+\t      size_t size,\n+\t      const void *src,\n+\t      struct rte_flow_error *error)\n+{\n+\tswitch (op) {\n+\t\tconst struct rte_flow_attr *attr;\n+\n+\tcase RTE_FLOW_CONV_OP_NONE:\n+\t\treturn 0;\n+\tcase RTE_FLOW_CONV_OP_ATTR:\n+\t\tattr = src;\n+\t\tif (size > sizeof(*attr))\n+\t\t\tsize = sizeof(*attr);\n+\t\trte_memcpy(dst, attr, size);\n+\t\treturn sizeof(*attr);\n+\tcase RTE_FLOW_CONV_OP_ITEM:\n+\t\treturn rte_flow_conv_pattern(dst, size, src, 1, error);\n+\tcase RTE_FLOW_CONV_OP_ACTION:\n+\t\treturn rte_flow_conv_actions(dst, size, src, 1, error);\n+\tcase RTE_FLOW_CONV_OP_PATTERN:\n+\t\treturn rte_flow_conv_pattern(dst, size, src, 0, error);\n+\tcase RTE_FLOW_CONV_OP_ACTIONS:\n+\t\treturn rte_flow_conv_actions(dst, size, src, 0, error);\n+\tcase RTE_FLOW_CONV_OP_RULE:\n+\t\treturn rte_flow_conv_rule(dst, size, src, error);\n+\t}\n+\treturn rte_flow_error_set\n+\t\t(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,\n+\t\t \"unknown object conversion operation\");\n }\n \n /** Store a full rte_flow description. */\n@@ -426,105 +721,49 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,\n \t      const struct rte_flow_item *items,\n \t      const struct rte_flow_action *actions)\n {\n-\tstruct rte_flow_desc *fd = NULL;\n-\tsize_t tmp;\n-\tsize_t off1 = 0;\n-\tsize_t off2 = 0;\n-\tsize_t size = 0;\n-\n-store:\n-\tif (items) {\n-\t\tconst struct rte_flow_item *item;\n-\n-\t\titem = items;\n-\t\tif (fd)\n-\t\t\tfd->items = (void *)&fd->data[off1];\n-\t\tdo {\n-\t\t\tstruct rte_flow_item *dst = NULL;\n-\n-\t\t\tif ((size_t)item->type >=\n-\t\t\t\tRTE_DIM(rte_flow_desc_item) ||\n-\t\t\t    !rte_flow_desc_item[item->type].name) {\n-\t\t\t\trte_errno = ENOTSUP;\n-\t\t\t\treturn 0;\n-\t\t\t}\n-\t\t\tif (fd)\n-\t\t\t\tdst = memcpy(fd->data + off1, item,\n-\t\t\t\t\t     sizeof(*item));\n-\t\t\toff1 += sizeof(*item);\n-\t\t\tif (item->spec) {\n-\t\t\t\tif (fd)\n-\t\t\t\t\tdst->spec = fd->data + off2;\n-\t\t\t\toff2 += flow_item_spec_copy\n-\t\t\t\t\t(fd ? fd->data + off2 : NULL, item,\n-\t\t\t\t\t ITEM_SPEC);\n-\t\t\t}\n-\t\t\tif (item->last) {\n-\t\t\t\tif (fd)\n-\t\t\t\t\tdst->last = fd->data + off2;\n-\t\t\t\toff2 += flow_item_spec_copy\n-\t\t\t\t\t(fd ? fd->data + off2 : NULL, item,\n-\t\t\t\t\t ITEM_LAST);\n-\t\t\t}\n-\t\t\tif (item->mask) {\n-\t\t\t\tif (fd)\n-\t\t\t\t\tdst->mask = fd->data + off2;\n-\t\t\t\toff2 += flow_item_spec_copy\n-\t\t\t\t\t(fd ? fd->data + off2 : NULL, item,\n-\t\t\t\t\t ITEM_MASK);\n-\t\t\t}\n-\t\t\toff2 = RTE_ALIGN_CEIL(off2, sizeof(double));\n-\t\t} while ((item++)->type != RTE_FLOW_ITEM_TYPE_END);\n-\t\toff1 = RTE_ALIGN_CEIL(off1, sizeof(double));\n-\t}\n-\tif (actions) {\n-\t\tconst struct rte_flow_action *action;\n-\n-\t\taction = actions;\n-\t\tif (fd)\n-\t\t\tfd->actions = (void *)&fd->data[off1];\n-\t\tdo {\n-\t\t\tstruct rte_flow_action *dst = NULL;\n-\n-\t\t\tif ((size_t)action->type >=\n-\t\t\t\tRTE_DIM(rte_flow_desc_action) ||\n-\t\t\t    !rte_flow_desc_action[action->type].name) {\n-\t\t\t\trte_errno = ENOTSUP;\n-\t\t\t\treturn 0;\n-\t\t\t}\n-\t\t\tif (fd)\n-\t\t\t\tdst = memcpy(fd->data + off1, action,\n-\t\t\t\t\t     sizeof(*action));\n-\t\t\toff1 += sizeof(*action);\n-\t\t\tif (action->conf) {\n-\t\t\t\tif (fd)\n-\t\t\t\t\tdst->conf = fd->data + off2;\n-\t\t\t\toff2 += flow_action_conf_copy\n-\t\t\t\t\t(fd ? fd->data + off2 : NULL, action);\n-\t\t\t}\n-\t\t\toff2 = RTE_ALIGN_CEIL(off2, sizeof(double));\n-\t\t} while ((action++)->type != RTE_FLOW_ACTION_TYPE_END);\n+\t/*\n+\t * Overlap struct rte_flow_conv with struct rte_flow_desc in order\n+\t * to convert the former to the latter without wasting space.\n+\t */\n+\tstruct rte_flow_conv_rule *dst =\n+\t\tlen ?\n+\t\t(void *)((uintptr_t)desc +\n+\t\t\t (offsetof(struct rte_flow_desc, actions) -\n+\t\t\t  offsetof(struct rte_flow_conv_rule, actions))) :\n+\t\tNULL;\n+\tsize_t dst_size =\n+\t\tlen > sizeof(*desc) - sizeof(*dst) ?\n+\t\tlen - (sizeof(*desc) - sizeof(*dst)) :\n+\t\t0;\n+\tstruct rte_flow_conv_rule src = {\n+\t\t.attr_ro = NULL,\n+\t\t.pattern_ro = items,\n+\t\t.actions_ro = actions,\n+\t};\n+\tint ret;\n+\n+\tRTE_BUILD_BUG_ON(sizeof(struct rte_flow_desc) <\n+\t\t\t sizeof(struct rte_flow_conv_rule));\n+\tif (dst_size &&\n+\t    (&dst->pattern != &desc->items ||\n+\t     &dst->actions != &desc->actions ||\n+\t     (uintptr_t)(dst + 1) != (uintptr_t)(desc + 1))) {\n+\t\trte_errno = EINVAL;\n+\t\treturn 0;\n \t}\n-\tif (fd != NULL)\n-\t\treturn size;\n-\toff1 = RTE_ALIGN_CEIL(off1, sizeof(double));\n-\ttmp = RTE_ALIGN_CEIL(offsetof(struct rte_flow_desc, data),\n-\t\t\t     sizeof(double));\n-\tsize = tmp + off1 + off2;\n-\tif (size > len)\n-\t\treturn size;\n-\tfd = desc;\n-\tif (fd != NULL) {\n-\t\t*fd = (const struct rte_flow_desc) {\n-\t\t\t.size = size,\n+\tret = rte_flow_conv(RTE_FLOW_CONV_OP_RULE, dst, dst_size, &src, NULL);\n+\tif (ret < 0)\n+\t\treturn 0;\n+\tret += sizeof(*desc) - sizeof(*dst);\n+\trte_memcpy(desc,\n+\t\t   (&(struct rte_flow_desc){\n+\t\t\t.size = ret,\n \t\t\t.attr = *attr,\n-\t\t};\n-\t\ttmp -= offsetof(struct rte_flow_desc, data);\n-\t\toff2 = tmp + off1;\n-\t\toff1 = tmp;\n-\t\tgoto store;\n-\t}\n-\treturn 0;\n+\t\t\t.items = dst_size ? dst->pattern : NULL,\n+\t\t\t.actions = dst_size ? dst->actions : NULL,\n+\t\t   }),\n+\t\t   len > sizeof(*desc) ? sizeof(*desc) : len);\n+\treturn ret;\n }\n \n /**\ndiff --git a/lib/librte_ethdev/rte_flow.h b/lib/librte_ethdev/rte_flow.h\nindex f8ba71cdb..1288e76ae 100644\n--- a/lib/librte_ethdev/rte_flow.h\n+++ b/lib/librte_ethdev/rte_flow.h\n@@ -18,6 +18,7 @@\n #include <stdint.h>\n \n #include <rte_arp.h>\n+#include <rte_common.h>\n #include <rte_ether.h>\n #include <rte_eth_ctrl.h>\n #include <rte_icmp.h>\n@@ -1932,6 +1933,119 @@ struct rte_flow_error {\n };\n \n /**\n+ * Complete flow rule description.\n+ *\n+ * This object type is used when converting a flow rule description.\n+ *\n+ * @see RTE_FLOW_CONV_OP_RULE\n+ * @see rte_flow_conv()\n+ */\n+RTE_STD_C11\n+struct rte_flow_conv_rule {\n+\tunion {\n+\t\tconst struct rte_flow_attr *attr_ro; /**< RO attributes. */\n+\t\tstruct rte_flow_attr *attr; /**< Attributes. */\n+\t};\n+\tunion {\n+\t\tconst struct rte_flow_item *pattern_ro; /**< RO pattern. */\n+\t\tstruct rte_flow_item *pattern; /**< Pattern items. */\n+\t};\n+\tunion {\n+\t\tconst struct rte_flow_action *actions_ro; /**< RO actions. */\n+\t\tstruct rte_flow_action *actions; /**< List of actions. */\n+\t};\n+};\n+\n+/**\n+ * Conversion operations for flow API objects.\n+ *\n+ * @see rte_flow_conv()\n+ */\n+enum rte_flow_conv_op {\n+\t/**\n+\t * No operation to perform.\n+\t *\n+\t * rte_flow_conv() simply returns 0.\n+\t */\n+\tRTE_FLOW_CONV_OP_NONE,\n+\n+\t/**\n+\t * Convert attributes structure.\n+\t *\n+\t * This is a basic copy of an attributes structure.\n+\t *\n+\t * - @p src type:\n+\t *   @code const struct rte_flow_attr * @endcode\n+\t * - @p dst type:\n+\t *   @code struct rte_flow_attr * @endcode\n+\t */\n+\tRTE_FLOW_CONV_OP_ATTR,\n+\n+\t/**\n+\t * Convert a single item.\n+\t *\n+\t * Duplicates @p spec, @p last and @p mask but not outside objects.\n+\t *\n+\t * - @p src type:\n+\t *   @code const struct rte_flow_item * @endcode\n+\t * - @p dst type:\n+\t *   @code struct rte_flow_item * @endcode\n+\t */\n+\tRTE_FLOW_CONV_OP_ITEM,\n+\n+\t/**\n+\t * Convert a single action.\n+\t *\n+\t * Duplicates @p conf but not outside objects.\n+\t *\n+\t * - @p src type:\n+\t *   @code const struct rte_flow_action * @endcode\n+\t * - @p dst type:\n+\t *   @code struct rte_flow_action * @endcode\n+\t */\n+\tRTE_FLOW_CONV_OP_ACTION,\n+\n+\t/**\n+\t * Convert an entire pattern.\n+\t *\n+\t * Duplicates all pattern items at once with the same constraints as\n+\t * RTE_FLOW_CONV_OP_ITEM.\n+\t *\n+\t * - @p src type:\n+\t *   @code const struct rte_flow_item * @endcode\n+\t * - @p dst type:\n+\t *   @code struct rte_flow_item * @endcode\n+\t */\n+\tRTE_FLOW_CONV_OP_PATTERN,\n+\n+\t/**\n+\t * Convert a list of actions.\n+\t *\n+\t * Duplicates the entire list of actions at once with the same\n+\t * constraints as RTE_FLOW_CONV_OP_ACTION.\n+\t *\n+\t * - @p src type:\n+\t *   @code const struct rte_flow_action * @endcode\n+\t * - @p dst type:\n+\t *   @code struct rte_flow_action * @endcode\n+\t */\n+\tRTE_FLOW_CONV_OP_ACTIONS,\n+\n+\t/**\n+\t * Convert a complete flow rule description.\n+\t *\n+\t * Comprises attributes, pattern and actions together at once with\n+\t * the usual constraints.\n+\t *\n+\t * - @p src type:\n+\t *   @code const struct rte_flow_conv_rule * @endcode\n+\t * - @p dst type:\n+\t *   @code struct rte_flow_conv_rule * @endcode\n+\t */\n+\tRTE_FLOW_CONV_OP_RULE,\n+};\n+\n+/**\n  * Check whether a flow rule can be created on a given port.\n  *\n  * The flow rule is validated for correctness and whether it could be accepted\n@@ -2162,10 +2276,7 @@ rte_flow_error_set(struct rte_flow_error *error,\n \t\t   const char *message);\n \n /**\n- * Generic flow representation.\n- *\n- * This form is sufficient to describe an rte_flow independently from any\n- * PMD implementation and allows for replayability and identification.\n+ * @see rte_flow_copy()\n  */\n struct rte_flow_desc {\n \tsize_t size; /**< Allocated space including data[]. */\n@@ -2178,6 +2289,9 @@ struct rte_flow_desc {\n /**\n  * Copy an rte_flow rule description.\n  *\n+ * This interface is kept for compatibility with older applications but is\n+ * implemented as a wrapper to rte_flow_conv().\n+ *\n  * @param[in] fd\n  *   Flow rule description.\n  * @param[in] len\n@@ -2201,6 +2315,54 @@ rte_flow_copy(struct rte_flow_desc *fd, size_t len,\n \t      const struct rte_flow_item *items,\n \t      const struct rte_flow_action *actions);\n \n+/**\n+ * Flow object conversion helper.\n+ *\n+ * This function performs conversion of various flow API objects to a\n+ * pre-allocated destination buffer. See enum rte_flow_conv_op for possible\n+ * operations and details about each of them.\n+ *\n+ * Since destination buffer must be large enough, it works in a manner\n+ * reminiscent of snprintf():\n+ *\n+ * - If @p size is 0, @p dst may be a NULL pointer, otherwise @p dst must be\n+ *   non-NULL.\n+ * - If positive, the returned value represents the number of bytes needed\n+ *   to store the conversion of @p src to @p dst according to @p op\n+ *   regardless of the @p size parameter.\n+ * - Since no more than @p size bytes can be written to @p dst, output is\n+ *   truncated and may be inconsistent when the returned value is larger\n+ *   than that.\n+ * - In case of conversion error, a negative error code is returned and\n+ *   @p dst contents are unspecified.\n+ *\n+ * @param op\n+ *   Operation to perform, related to the object type of @p dst.\n+ * @param[out] dst\n+ *   Destination buffer address. Must be suitably aligned by the caller.\n+ * @param size\n+ *   Destination buffer size in bytes.\n+ * @param[in] src\n+ *   Source object to copy. Depending on @p op, its type may differ from\n+ *   that of @p dst.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL. Initialized in case of\n+ *   error only.\n+ *\n+ * @return\n+ *   The number of bytes required to convert @p src to @p dst on success, a\n+ *   negative errno value otherwise and rte_errno is set.\n+ *\n+ * @see rte_flow_conv_op\n+ */\n+__rte_experimental\n+int\n+rte_flow_conv(enum rte_flow_conv_op op,\n+\t      void *dst,\n+\t      size_t size,\n+\t      const void *src,\n+\t      struct rte_flow_error *error);\n+\n #ifdef __cplusplus\n }\n #endif\n",
    "prefixes": [
        "v3",
        "1/7"
    ]
}