get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 132251,
    "url": "http://patchwork.dpdk.org/api/patches/132251/?format=api",
    "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/20230930100053.1227215-3-chaoyong.he@corigine.com/",
    "project": {
        "id": 1,
        "url": "http://patchwork.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": "<20230930100053.1227215-3-chaoyong.he@corigine.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230930100053.1227215-3-chaoyong.he@corigine.com",
    "date": "2023-09-30T10:00:51",
    "name": "[2/4] net/nfp: add infrastructure for ct flow merge",
    "commit_ref": null,
    "pull_url": null,
    "state": "changes-requested",
    "archived": true,
    "hash": "aaff8604ff18cab4585ea89672e2cd785981025a",
    "submitter": {
        "id": 2554,
        "url": "http://patchwork.dpdk.org/api/people/2554/?format=api",
        "name": "Chaoyong He",
        "email": "chaoyong.he@corigine.com"
    },
    "delegate": {
        "id": 319,
        "url": "http://patchwork.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/20230930100053.1227215-3-chaoyong.he@corigine.com/mbox/",
    "series": [
        {
            "id": 29708,
            "url": "http://patchwork.dpdk.org/api/series/29708/?format=api",
            "web_url": "http://patchwork.dpdk.org/project/dpdk/list/?series=29708",
            "date": "2023-09-30T10:00:49",
            "name": "support offload of simple conntrack flow rules",
            "version": 1,
            "mbox": "http://patchwork.dpdk.org/series/29708/mbox/"
        }
    ],
    "comments": "http://patchwork.dpdk.org/api/patches/132251/comments/",
    "check": "warning",
    "checks": "http://patchwork.dpdk.org/api/patches/132251/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 1C14042682;\n\tSat, 30 Sep 2023 12:01:59 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 5D515402CA;\n\tSat, 30 Sep 2023 12:01:47 +0200 (CEST)",
            "from NAM02-DM3-obe.outbound.protection.outlook.com\n (mail-dm3nam02on2105.outbound.protection.outlook.com [40.107.95.105])\n by mails.dpdk.org (Postfix) with ESMTP id 728BE40287\n for <dev@dpdk.org>; Sat, 30 Sep 2023 12:01:45 +0200 (CEST)",
            "from SJ0PR13MB5545.namprd13.prod.outlook.com (2603:10b6:a03:424::5)\n by PH7PR13MB5429.namprd13.prod.outlook.com (2603:10b6:510:138::10)\n with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6813.21; Sat, 30 Sep\n 2023 10:01:42 +0000",
            "from SJ0PR13MB5545.namprd13.prod.outlook.com\n ([fe80::28c0:63e2:ecd1:9314]) by SJ0PR13MB5545.namprd13.prod.outlook.com\n ([fe80::28c0:63e2:ecd1:9314%4]) with mapi id 15.20.6813.027; Sat, 30 Sep 2023\n 10:01:42 +0000"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=NMFZ0FKOqGRM0/x6hx/5wS/gOGVo1acLKaK9jjxCwmLxN8T5WcWoU1vFubjRXnoOfY11L0smYdBasjHAaI1S4Ns7qJyWxhnHt2ObulnbOAHKNlgB+rog8v83Smp8GzkUnO6HWFsKv9srG9xW0S9w1Fp4zL3Vlkqj9/V5I4m6gnjC6hhatNJSsUAy3UViHw50z22Paci0+NdcZFadwAp6eJdPO/OvGkzjYIEpRYKph4o0XZt83BsaUclh/UqKl4PBGqoe6riAJm4Uu6ZoO3oBq6lRLvlb1sIDpK/qg0CgcL53hA6XHuqn/AEfc9KNLqRt80CZOBd2akn3jreH6LXaAg==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector9901;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=uS7pVZ1ThvG3v0Ni+rCXB9KUw2Dt0eVhVOQLMNPfHes=;\n b=MKqr2Rv0I+Lmst+RBNvoTxSOZsWHkF+YMuVjXxifTbZefrbLhuqSDdbRiveYxWHLl1lJJksc/1ilHZ+t8bP8ntMlvRmpJrs0iFWz4phQode4PQVZAHvozI9hwhTWySDOABbS5sY2YW/6sIbJXP1SoJeCFgXiH1AmILJTgpzg4m6zDwjoPCEojY46NY7+JOeCB5TJmmP3lRKzikZcBhweObtIZsx3j4Yy5CxMzXNYqOE48U+5qEd48ameqTX3HlpwS4Wyv1NuiFg8M55HgTMk3yJkDvhJRChT6uohHrnpiRvgIWbcQPQgu8swfZbG347XtBI/JJ1fAPAMwkQ+R6QvqA==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=pass\n smtp.mailfrom=corigine.com; dmarc=pass action=none header.from=corigine.com;\n dkim=pass header.d=corigine.com; arc=none",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=corigine.onmicrosoft.com; s=selector2-corigine-onmicrosoft-com;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=uS7pVZ1ThvG3v0Ni+rCXB9KUw2Dt0eVhVOQLMNPfHes=;\n b=MZR9O0rJgrE5lyygPXsFHNJrYc6IM++14HNOYIRzs4kXtPAdsrzx63tDDIZheLd/Qli1g+E2MAhU53eDz4SZ8uGca8t41WqCUw4nWcAtH0KXWPp7cHjP8wcAhoDMUD5hNeyIBbGGWMj27/tIvxymXVbGuDJoZ7JWqHf+P+EJ0V0=",
        "Authentication-Results": "dkim=none (message not signed)\n header.d=none;dmarc=none action=none header.from=corigine.com;",
        "From": "Chaoyong He <chaoyong.he@corigine.com>",
        "To": "dev@dpdk.org",
        "Cc": "oss-drivers@corigine.com,\n\tChaoyong He <chaoyong.he@corigine.com>",
        "Subject": "[PATCH 2/4] net/nfp: add infrastructure for ct flow merge",
        "Date": "Sat, 30 Sep 2023 18:00:51 +0800",
        "Message-Id": "<20230930100053.1227215-3-chaoyong.he@corigine.com>",
        "X-Mailer": "git-send-email 2.39.1",
        "In-Reply-To": "<20230930100053.1227215-1-chaoyong.he@corigine.com>",
        "References": "<20230930100053.1227215-1-chaoyong.he@corigine.com>",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-ClientProxiedBy": "PH8PR22CA0022.namprd22.prod.outlook.com\n (2603:10b6:510:2d1::17) To SJ0PR13MB5545.namprd13.prod.outlook.com\n (2603:10b6:a03:424::5)",
        "MIME-Version": "1.0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "SJ0PR13MB5545:EE_|PH7PR13MB5429:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "9948fa67-07e4-4d73-3d4f-08dbc19c3ed0",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n wqW68OB4PnYBu9K5gHM2YqlEzD1S2khhiKYSGrrcRs0c+bajeP4Hi3V4zprFnH4y9N4asgwgU0XouVIxa3HlxDHr2/h6lzjx/8zgosEtdjQjf3xWvTKdylF83xgKjZC1pKxOtMWa98A83KSmZdlvbunNoXaoQLbC7TgB20+vbN0WJsoT0E16bHcyNI9UEE2Whu2ualOSvN+gM5Lk+AX6f5nnQFlLsR2d+hlIKBQS66nkdQpuWcXwLOsCNedwqzU5P0/rBbZXdDC3MG5ZWd7Xp+HruFUekRL3x08gP2B17bMN5AMz5ARQelz80iuaBNBCRQJe1MoIEe6Thsu4mOMHrZQViEhNclB9EhQyMaefTQIdTZAx0kByv9dYwoksbzHp3O2EAP9L8C3K0NoonpTwx/yBCIcw9EqS+4c3pIpSAsCXxwIrYdKzbg6lpfiukPY1MFAifc1q4RfR1d89nTAg8ENOvqh2eJY9cZzGg7KOpObhcW4od2ieuv8Oxp+A1w+aATqBZp7BMFzspZOknkHVEAxtAKd62+K0H5WGXR0OhTw6Y3wZd5/8PVGos03uiMcT0fvLa50P5e96LUpl8345n4shUi/760uigpoo8z+OPJRrFLwXsQH3EA2RbTTe6S2XJ5ehgYb1fQ2wgx+T1P09RKhnla+WXitgBjgoMYtFBCQ=",
        "X-Forefront-Antispam-Report": "CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:;\n IPV:NLI; SFV:NSPM; H:SJ0PR13MB5545.namprd13.prod.outlook.com; PTR:; CAT:NONE;\n SFS:(13230031)(366004)(346002)(136003)(39830400003)(396003)(376002)(230922051799003)(1800799009)(451199024)(64100799003)(186009)(66946007)(6916009)(8676002)(44832011)(316002)(4326008)(66476007)(66556008)(38100700002)(38350700002)(86362001)(6486002)(478600001)(30864003)(41300700001)(2906002)(5660300002)(8936002)(6512007)(2616005)(52116002)(107886003)(1076003)(26005)(36756003)(6666004)(83380400001)(6506007)(579004)(559001);\n DIR:OUT; SFP:1102;",
        "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1",
        "X-MS-Exchange-AntiSpam-MessageData-0": "\n rnkskTbK/HRFY5QCIHUbqxXhzbsDgiZzkpkjbTpZds+RCoK++JmBWw/d6blQD6mP8am3pqNo7N8S84D+zlzjl2i64493ZgDnMxfo9AMWk2c+jjkHWvFK8sNCJqOmcHszoLhalKV0RtFQsFvgKzS3lcE1SiraP2rLHS8AWCgVe+hW59ccA7+Bkf8mZXXp0aI+d/ej8S+J+rHhUsa2WEmG4VW/rVl599p4QihJ70P5S5FvTQC7Llvbi9SjZUlYekXNUf4CnmKoba9VdU3bZBjBHHr4kE0c2Huw+BsqY1qdFnlJ6D0NI3cOa2mEr+rAvnqeV/6nzhus37hhwUsiAsKLur5Oxfs6dkHhYLQQ78YK3xk3fh1HnEMh2aW+rI6mJQRbq/hVd3AYSTeS/lgmxs+TqI67Fme+Z/H/CFoAuamrOVLi3YGqkucCgCyhGBtLBZJyQ4Y108jt483zVM495My0r0zqe6H0O5E7dyfq7uxsRhLCZtchUfo/Rzcr59+hqkh7G+W7D1uptqqQ4mqhiGQuDunGnHC3fahWrREsYbPZJ+yxFO33MCcmMiDryh0px4vZMDJwgU4++dcQs19NNx4XtLJY+Pk55KCRsonpyDrQf+Xus/LIV2B5ZOeIak1FTxyik3lI8GAZLa+aM+Q7i+KTzxp8BbkhKVmBgCeO0JH34KOx1H9coJ54GCxz0357eH/Hyl4nBia5OOIr3U1rbs+AZ6K/EZvEf0/gfgdOU64AT19YAVCWSBfIOm5J5LU5zH/8wQP0jZkNykMQP+KYLUA7L+wCVhMiXDsuAWNNWGtO0jNYyBHkfwGBqAjRn1rRIbWAeMf36k2TGxDeD1PYbMpT/Hyd20llO80075B3sHOxeTA/PBlBX4LbE1ei6eEGia6PK23dRvyBIFznsjFL1/Q5lZ7mNy87NjaJslyBPNorWQcyqckPdxEVpuW9gUqK6ipF2tmxWeWQplmrYOlgq/EroSmnrcgHVy7LSCV0QTZRUN+d5EpgDCpVEQgLNjhIuO+k9gZHdWJX5rG1rTJHZSIubn/ysrwfqiWoJor2k4yuCZy9g8oPdTnhgwNVQE60r4MhNNkqX4m13d0CaIhXM9lY4D7heUwqixNQiELLQ4yYOzcGFJRszdBYjXkVVmmV9/eYslyxX0KDgC35pslFQOfiFQFlkxPaM8rvoBzw+clpoNDPs9zVsJpt6c8VVXwjObV+F0LklcpPyKYHpnWE2Dfm2CPc8XtPhkvx8WYMs2IGbgpqyUeP3U/HN84hT2kIfg8BHR+xKbyveBpg3epbV+Cp647zTdANQ5v8vwXQo2WCWfEtd00tMwq44nurHHN5DRldE2V03yW/JlvMZNF1xgyZW8EsZPM6vtRbaG5VpJy//q53r/0Jr/u742rcRfwTn+bFBoOrg1jmNsO517AYapVbA12dC1JrnfoqmSBXbHS2vuPhzZXG3mUgh41yyFBFPGjwdiWoCfOxz8SqibKE1Ez4qeEBCMaqahjcBngiO4rxLRXhlVZFtckrOkdQumlsvMVb0qp+eI6C0tM07l5wYDKY1aIIk+2qM16pRyUiL9HtM2r1mVH01qUlnMNF37yv6uDHE4vRaU3DdnzgW/hvgpsnGg==",
        "X-OriginatorOrg": "corigine.com",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 9948fa67-07e4-4d73-3d4f-08dbc19c3ed0",
        "X-MS-Exchange-CrossTenant-AuthSource": "SJ0PR13MB5545.namprd13.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Internal",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "30 Sep 2023 10:01:42.5287 (UTC)",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "Hosted",
        "X-MS-Exchange-CrossTenant-Id": "fe128f2c-073b-4c20-818e-7246a585940c",
        "X-MS-Exchange-CrossTenant-MailboxType": "HOSTED",
        "X-MS-Exchange-CrossTenant-UserPrincipalName": "\n mk5y7a7jQ0HB8RXezW6SxgL13kVq9zAHC0bXvEnPuXA8yrN/5W0aFZBAmsa9Admun5iokk2iGL+XR7laThYBdIUnGsKWWiQf6f3wO+WKFN8=",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "PH7PR13MB5429",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "Add the logic to process the merge of items and actions of\npre_ct and post_ct flow. The result will be stored in a field\nof merged flow.\n\nSigned-off-by: Chaoyong He <chaoyong.he@corigine.com>\n---\n drivers/net/nfp/flower/nfp_conntrack.c | 1626 ++++++++++++++++++++++++\n drivers/net/nfp/flower/nfp_conntrack.h |   32 +\n drivers/net/nfp/meson.build            |    1 +\n drivers/net/nfp/nfp_flow.c             |   56 +\n drivers/net/nfp/nfp_flow.h             |    4 +\n 5 files changed, 1719 insertions(+)\n create mode 100644 drivers/net/nfp/flower/nfp_conntrack.c\n create mode 100644 drivers/net/nfp/flower/nfp_conntrack.h",
    "diff": "diff --git a/drivers/net/nfp/flower/nfp_conntrack.c b/drivers/net/nfp/flower/nfp_conntrack.c\nnew file mode 100644\nindex 0000000000..24762de133\n--- /dev/null\n+++ b/drivers/net/nfp/flower/nfp_conntrack.c\n@@ -0,0 +1,1626 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Corigine, Inc.\n+ * All rights reserved.\n+ */\n+\n+#include \"nfp_conntrack.h\"\n+\n+#include <rte_malloc.h>\n+#include <rte_hash.h>\n+#include <rte_jhash.h>\n+\n+#include \"../nfp_flow.h\"\n+#include \"../nfp_logs.h\"\n+#include \"nfp_flower_representor.h\"\n+\n+struct ct_data {\n+\tuint8_t  ct_state;        /* Connection state. */\n+\tuint16_t ct_zone;         /* Connection zone. */\n+};\n+\n+enum ct_entry_type {\n+\tCT_TYPE_PRE_CT,\n+\tCT_TYPE_POST_CT,\n+};\n+\n+struct nfp_initial_flow {\n+\tstruct rte_flow_item *items;\n+\tstruct rte_flow_action *actions;\n+\tuint8_t items_cnt;\n+\tuint8_t actions_cnt;\n+};\n+\n+struct nfp_ct_flow_entry {\n+\tuint64_t cookie;\n+\tLIST_ENTRY(nfp_ct_flow_entry) pre_ct_list;\n+\tLIST_ENTRY(nfp_ct_flow_entry) post_ct_list;\n+\tLIST_HEAD(, nfp_ct_merge_entry) children;\n+\tenum ct_entry_type type;\n+\tstruct nfp_flower_representor *repr;\n+\tstruct nfp_ct_zone_entry *ze;\n+\tstruct nfp_initial_flow rule;\n+};\n+\n+struct nfp_ct_map_entry {\n+\tuint64_t cookie;\n+\tstruct nfp_ct_flow_entry *fe;\n+};\n+\n+struct nfp_ct_zone_entry {\n+\tuint32_t zone;\n+\tstruct nfp_flow_priv *priv;\n+\tLIST_HEAD(, nfp_ct_flow_entry) pre_ct_list;\n+\tLIST_HEAD(, nfp_ct_flow_entry) post_ct_list;\n+\tstruct rte_hash *ct_merge_table;\n+};\n+\n+struct nfp_ct_merge_entry {\n+\tuint64_t cookie[2];\n+\tLIST_ENTRY(nfp_ct_merge_entry) pre_ct_list;\n+\tLIST_ENTRY(nfp_ct_merge_entry) post_ct_list;\n+\tstruct nfp_initial_flow rule;\n+\tstruct nfp_ct_zone_entry *ze;\n+\tstruct nfp_ct_flow_entry *pre_ct_parent;\n+\tstruct nfp_ct_flow_entry *post_ct_parent;\n+};\n+\n+/* OVS_KEY_ATTR_CT_STATE flags */\n+#define OVS_CS_F_NEW            0x01 /* Beginning of a new connection. */\n+#define OVS_CS_F_ESTABLISHED    0x02 /* Part of an existing connection. */\n+#define OVS_CS_F_RELATED        0x04 /* Related to an established connection. */\n+#define OVS_CS_F_REPLY_DIR      0x08 /* Flow is in the reply direction. */\n+#define OVS_CS_F_INVALID        0x10 /* Could not track connection. */\n+#define OVS_CS_F_TRACKED        0x20 /* Conntrack has occurred. */\n+#define OVS_CS_F_SRC_NAT        0x40 /* Packet's source address/port was mangled by NAT. */\n+#define OVS_CS_F_DST_NAT        0x80 /* Packet's destination address/port was mangled by NAT. */\n+\n+typedef void (*nfp_action_free_fn)(void *field);\n+typedef bool (*nfp_action_copy_fn)(const void *src, void *dst);\n+\n+static bool\n+is_pre_ct_flow(const struct ct_data *ct,\n+\t\tconst struct rte_flow_action *actions)\n+{\n+\tconst struct rte_flow_action *action;\n+\n+\tif (ct == NULL)\n+\t\treturn false;\n+\n+\tfor (action = actions; action->type != RTE_FLOW_ACTION_TYPE_END; ++action) {\n+\t\tif (action->type == RTE_FLOW_ACTION_TYPE_CONNTRACK)\n+\t\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+static bool\n+is_post_ct_flow(const struct ct_data *ct)\n+{\n+\tif (ct == NULL)\n+\t\treturn false;\n+\n+\tif ((ct->ct_state & OVS_CS_F_ESTABLISHED) != 0)\n+\t\treturn true;\n+\n+\treturn false;\n+}\n+\n+static bool\n+is_ct_commit_flow(const struct ct_data *ct)\n+{\n+\tif (ct == NULL)\n+\t\treturn false;\n+\n+\tif ((ct->ct_state & OVS_CS_F_NEW) != 0)\n+\t\treturn true;\n+\n+\treturn false;\n+}\n+\n+static struct nfp_ct_merge_entry *\n+nfp_ct_merge_table_search(struct nfp_ct_zone_entry *ze,\n+\t\tchar *hash_data,\n+\t\tuint32_t hash_len)\n+{\n+\tint index;\n+\tuint32_t hash_key;\n+\tstruct nfp_ct_merge_entry *m_ent;\n+\n+\thash_key = rte_jhash(hash_data, hash_len, ze->priv->hash_seed);\n+\tindex = rte_hash_lookup_data(ze->ct_merge_table, &hash_key, (void **)&m_ent);\n+\tif (index < 0) {\n+\t\tPMD_DRV_LOG(DEBUG, \"Data NOT found in the ct_merge table\");\n+\t\treturn NULL;\n+\t}\n+\n+\treturn m_ent;\n+}\n+\n+static bool\n+nfp_ct_merge_table_add(struct nfp_ct_zone_entry *ze,\n+\t\tstruct nfp_ct_merge_entry *merge_entry)\n+{\n+\tint ret;\n+\tuint32_t hash_key;\n+\n+\thash_key = rte_jhash(merge_entry, sizeof(uint64_t) * 2, ze->priv->hash_seed);\n+\tret = rte_hash_add_key_data(ze->ct_merge_table, &hash_key, merge_entry);\n+\tif (ret != 0) {\n+\t\tPMD_DRV_LOG(ERR, \"Add to ct_merge table failed\");\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static void\n+nfp_ct_merge_table_delete(struct nfp_ct_zone_entry *ze,\n+\t\tstruct nfp_ct_merge_entry *m_ent)\n+{\n+\tint ret;\n+\tuint32_t hash_key;\n+\n+\thash_key = rte_jhash(m_ent, sizeof(uint64_t) * 2, ze->priv->hash_seed);\n+\tret = rte_hash_del_key(ze->ct_merge_table, &hash_key);\n+\tif (ret < 0)\n+\t\tPMD_DRV_LOG(ERR, \"Delete from ct_merge table failed, ret=%d\", ret);\n+}\n+\n+static void\n+nfp_ct_merge_entry_destroy(struct nfp_ct_merge_entry *m_ent)\n+{\n+\tstruct nfp_ct_zone_entry *ze;\n+\n+\tze = m_ent->ze;\n+\tnfp_ct_merge_table_delete(ze, m_ent);\n+\n+\trte_free(m_ent->rule.actions);\n+\trte_free(m_ent->rule.items);\n+\tLIST_REMOVE(m_ent, pre_ct_list);\n+\tLIST_REMOVE(m_ent, post_ct_list);\n+\trte_free(m_ent);\n+}\n+\n+struct nfp_ct_map_entry *\n+nfp_ct_map_table_search(struct nfp_flow_priv *priv,\n+\t\tchar *hash_data,\n+\t\tuint32_t hash_len)\n+{\n+\tint index;\n+\tuint32_t hash_key;\n+\tstruct nfp_ct_map_entry *me;\n+\n+\thash_key = rte_jhash(hash_data, hash_len, priv->hash_seed);\n+\tindex = rte_hash_lookup_data(priv->ct_map_table, &hash_key, (void **)&me);\n+\tif (index < 0) {\n+\t\tPMD_DRV_LOG(DEBUG, \"Data NOT found in the ct_map table\");\n+\t\treturn NULL;\n+\t}\n+\n+\treturn me;\n+}\n+\n+static bool\n+nfp_ct_map_table_add(struct nfp_flow_priv *priv,\n+\t\tstruct nfp_ct_map_entry *me)\n+{\n+\tint ret;\n+\tuint32_t hash_key;\n+\n+\thash_key = rte_jhash(me, sizeof(uint64_t), priv->hash_seed);\n+\tret = rte_hash_add_key_data(priv->ct_map_table, &hash_key, me);\n+\tif (ret != 0) {\n+\t\tPMD_DRV_LOG(ERR, \"Add to ct_map table failed\");\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static void\n+nfp_ct_map_table_delete(struct nfp_flow_priv *priv,\n+\t\tstruct nfp_ct_map_entry *me)\n+{\n+\tint ret;\n+\tuint32_t hash_key;\n+\n+\thash_key = rte_jhash(me, sizeof(uint64_t), priv->hash_seed);\n+\tret = rte_hash_del_key(priv->ct_map_table, &hash_key);\n+\tif (ret < 0)\n+\t\tPMD_DRV_LOG(ERR, \"Delete form ct_map table failed\");\n+}\n+\n+static void\n+nfp_ct_map_entry_destroy(struct nfp_ct_map_entry *me)\n+{\n+\trte_free(me);\n+}\n+\n+static void\n+nfp_ct_flow_item_free_real(void *field,\n+\t\tenum rte_flow_item_type type)\n+{\n+\tswitch (type) {\n+\tcase RTE_FLOW_ITEM_TYPE_VOID:\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_ETH:        /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_VLAN:       /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_MPLS:       /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_IPV4:       /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_IPV6:       /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_TCP:        /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_UDP:        /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_SCTP:       /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_ICMP:       /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_ICMP6:      /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_VXLAN:      /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_GRE:        /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_GRE_KEY:    /* FALLTHROUGH */\n+\tcase RTE_FLOW_ITEM_TYPE_GENEVE:\n+\t\trte_free(field);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\n+\n+static void\n+nfp_ct_flow_item_free(struct rte_flow_item *item)\n+{\n+\tif (item->spec != NULL)\n+\t\tnfp_ct_flow_item_free_real((void *)(ptrdiff_t)item->spec, item->type);\n+\n+\tif (item->mask != NULL)\n+\t\tnfp_ct_flow_item_free_real((void *)(ptrdiff_t)item->mask, item->type);\n+\n+\tif (item->last != NULL)\n+\t\tnfp_ct_flow_item_free_real((void *)(ptrdiff_t)item->last, item->type);\n+}\n+\n+static void\n+nfp_ct_flow_items_free(struct rte_flow_item *items,\n+\t\tuint8_t item_cnt)\n+{\n+\tuint8_t loop;\n+\n+\tfor (loop = 0; loop < item_cnt; ++loop)\n+\t\tnfp_ct_flow_item_free(items + loop);\n+}\n+\n+static bool\n+nfp_flow_item_conf_size_get(enum rte_flow_item_type type,\n+\t\tsize_t *size)\n+{\n+\tsize_t len = 0;\n+\n+\tswitch (type) {\n+\tcase RTE_FLOW_ITEM_TYPE_VOID:\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_ETH:\n+\t\tlen = sizeof(struct rte_flow_item_eth);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_VLAN:\n+\t\tlen = sizeof(struct rte_flow_item_vlan);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_MPLS:\n+\t\tlen = sizeof(struct rte_flow_item_mpls);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_IPV4:\n+\t\tlen = sizeof(struct rte_flow_item_ipv4);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_IPV6:\n+\t\tlen = sizeof(struct rte_flow_item_ipv6);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_TCP:\n+\t\tlen = sizeof(struct rte_flow_item_tcp);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_UDP:\n+\t\tlen = sizeof(struct rte_flow_item_udp);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_SCTP:\n+\t\tlen = sizeof(struct rte_flow_item_sctp);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_VXLAN:\n+\t\tlen = sizeof(struct rte_flow_item_vxlan);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_GRE:\n+\t\tlen = sizeof(struct rte_flow_item_gre);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_GRE_KEY:\n+\t\tlen = sizeof(rte_be32_t);\n+\t\tbreak;\n+\tcase RTE_FLOW_ITEM_TYPE_GENEVE:\n+\t\tlen = sizeof(struct rte_flow_item_geneve);\n+\t\tbreak;\n+\tdefault:\n+\t\tPMD_DRV_LOG(ERR, \"Unsupported item type: %d\", type);\n+\t\treturn false;\n+\t}\n+\n+\t*size = len;\n+\n+\treturn true;\n+}\n+\n+static void *\n+nfp_ct_flow_item_copy_real(const void *src,\n+\t\tenum rte_flow_item_type type)\n+{\n+\tbool ret;\n+\tvoid *dst;\n+\tsize_t len;\n+\n+\tret = nfp_flow_item_conf_size_get(type, &len);\n+\tif (!ret) {\n+\t\tPMD_DRV_LOG(ERR, \"Get flow item conf size failed\");\n+\t\treturn NULL;\n+\t}\n+\n+\tdst = rte_zmalloc(\"flow_item\", len, 0);\n+\tif (dst == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Malloc memory for ct item failed\");\n+\t\treturn NULL;\n+\t}\n+\n+\trte_memcpy(dst, src, len);\n+\n+\treturn dst;\n+}\n+\n+static bool\n+nfp_ct_flow_item_copy(const struct rte_flow_item *src,\n+\t\tstruct rte_flow_item *dst)\n+{\n+\tdst->type = src->type;\n+\n+\tif (src->spec != NULL) {\n+\t\tdst->spec = nfp_ct_flow_item_copy_real(src->spec, src->type);\n+\t\tif (dst->spec == NULL) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Copy spec of ct item failed\");\n+\t\t\tgoto end;\n+\t\t}\n+\t}\n+\n+\tif (src->mask != NULL) {\n+\t\tdst->mask = nfp_ct_flow_item_copy_real(src->mask, src->type);\n+\t\tif (dst->mask == NULL) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Copy mask of ct item failed\");\n+\t\t\tgoto free_spec;\n+\t\t}\n+\t}\n+\n+\tif (src->last != NULL) {\n+\t\tdst->last = nfp_ct_flow_item_copy_real(src->last, src->type);\n+\t\tif (dst->last == NULL) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Copy last of ct item failed\");\n+\t\t\tgoto free_mask;\n+\t\t}\n+\t}\n+\n+\treturn true;\n+\n+free_mask:\n+\tnfp_ct_flow_item_free_real((void *)(ptrdiff_t)dst->mask, dst->type);\n+free_spec:\n+\tnfp_ct_flow_item_free_real((void *)(ptrdiff_t)dst->spec, dst->type);\n+end:\n+\treturn false;\n+}\n+\n+static bool\n+nfp_ct_flow_items_copy(const struct rte_flow_item *src,\n+\t\tstruct rte_flow_item *dst,\n+\t\tuint8_t item_cnt)\n+{\n+\tbool ret;\n+\tuint8_t loop;\n+\n+\tfor (loop = 0; loop < item_cnt; ++loop) {\n+\t\tret = nfp_ct_flow_item_copy(src + loop, dst + loop);\n+\t\tif (!ret) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Copy ct item failed\");\n+\t\t\tnfp_ct_flow_items_free(dst, loop);\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n+static void\n+nfp_ct_flow_action_free_real(void *field,\n+\t\tnfp_action_free_fn func)\n+{\n+\tif (func != NULL)\n+\t\tfunc(field);\n+\n+\trte_free(field);\n+}\n+\n+static void\n+nfp_ct_flow_action_free_vxlan(void *field)\n+{\n+\tstruct vxlan_data *vxlan = field;\n+\n+\tnfp_ct_flow_items_free(vxlan->items, ACTION_VXLAN_ENCAP_ITEMS_NUM);\n+}\n+\n+static void\n+nfp_ct_flow_action_free_raw(void *field)\n+{\n+\tstruct rte_flow_action_raw_encap *raw_encap = field;\n+\n+\trte_free(raw_encap->data);\n+}\n+\n+static void\n+nfp_ct_flow_action_free(struct rte_flow_action *action)\n+{\n+\tnfp_action_free_fn func = NULL;\n+\n+\tif (action->conf == NULL)\n+\t\treturn;\n+\n+\tswitch (action->type) {\n+\tcase RTE_FLOW_ACTION_TYPE_VOID:           /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_MARK:           /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_DROP:           /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_COUNT:          /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_RSS:            /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_JUMP:           /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_OF_POP_VLAN:    /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:    /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_RAW_DECAP:\n+\t\treturn;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_SRC:    /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_DST:    /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_PORT_ID:        /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_SRC:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DST:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DSCP:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DSCP:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_SRC:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DST:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TTL:        /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TP_SRC:     /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TP_DST:\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:\n+\t\tfunc = nfp_ct_flow_action_free_vxlan;\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_RAW_ENCAP:\n+\t\tfunc = nfp_ct_flow_action_free_raw;\n+\t\tbreak;\n+\tdefault:\n+\t\tPMD_DRV_LOG(ERR, \"Unsupported action type: %d\", action->type);\n+\t\tbreak;\n+\t}\n+\n+\tnfp_ct_flow_action_free_real((void *)(ptrdiff_t)action->conf, func);\n+}\n+\n+static void\n+nfp_ct_flow_actions_free(struct rte_flow_action *actions,\n+\t\tuint8_t action_cnt)\n+{\n+\tuint8_t loop;\n+\n+\tfor (loop = 0; loop < action_cnt; ++loop)\n+\t\tnfp_ct_flow_action_free(actions + loop);\n+}\n+\n+static void *\n+nfp_ct_flow_action_copy_real(const void *src,\n+\t\tsize_t len,\n+\t\tnfp_action_copy_fn func)\n+{\n+\tbool ret;\n+\tvoid *dst;\n+\n+\tdst = rte_zmalloc(\"flow_action\", len, 0);\n+\tif (dst == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Malloc memory for ct action failed\");\n+\t\treturn NULL;\n+\t}\n+\n+\tif (func != NULL) {\n+\t\tret = func(src, dst);\n+\t\tif (!ret) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Copy ct action failed\");\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\treturn dst;\n+\t}\n+\n+\trte_memcpy(dst, src, len);\n+\n+\treturn dst;\n+}\n+\n+static bool\n+nfp_ct_flow_action_copy_vxlan(const void *src,\n+\t\tvoid *dst)\n+{\n+\tstruct vxlan_data *vxlan_dst = dst;\n+\tconst struct vxlan_data *vxlan_src = src;\n+\n+\tvxlan_dst->conf.definition = vxlan_dst->items;\n+\treturn nfp_ct_flow_items_copy(vxlan_src->items, vxlan_dst->items,\n+\t\t\tACTION_VXLAN_ENCAP_ITEMS_NUM);\n+}\n+\n+static bool\n+nfp_ct_flow_action_copy_raw(const void *src,\n+\t\tvoid *dst)\n+{\n+\tstruct rte_flow_action_raw_encap *raw_dst = dst;\n+\tconst struct rte_flow_action_raw_encap *raw_src = src;\n+\n+\traw_dst->size = raw_src->size;\n+\traw_dst->data = nfp_ct_flow_action_copy_real(raw_src->data,\n+\t\t\traw_src->size, NULL);\n+\tif (raw_dst->data == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Copy ct action process failed\");\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static bool\n+nfp_ct_flow_action_copy(const struct rte_flow_action *src,\n+\t\tstruct rte_flow_action *dst)\n+{\n+\tsize_t len;\n+\tnfp_action_copy_fn func = NULL;\n+\n+\tdst->type = src->type;\n+\n+\tif (src->conf == NULL)\n+\t\treturn true;\n+\n+\tswitch (src->type) {\n+\tcase RTE_FLOW_ACTION_TYPE_VOID:         /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_MARK:         /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_DROP:         /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_COUNT:        /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_RSS:          /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_JUMP:         /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_OF_POP_VLAN:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_RAW_DECAP:\n+\t\treturn true;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_SRC:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_DST:\n+\t\tlen = sizeof(struct rte_flow_action_set_mac);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_PORT_ID:\n+\t\tlen = sizeof(struct rte_flow_action_port_id);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN:\n+\t\tlen = sizeof(struct rte_flow_action_of_push_vlan);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_SRC:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DST:\n+\t\tlen = sizeof(struct rte_flow_action_set_ipv4);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DSCP:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DSCP:\n+\t\tlen = sizeof(struct rte_flow_action_set_dscp);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_SRC:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DST:\n+\t\tlen = sizeof(struct rte_flow_action_set_ipv6);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TTL:\n+\t\tlen = sizeof(struct rte_flow_action_set_ttl);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TP_SRC:  /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TP_DST:\n+\t\tlen = sizeof(struct rte_flow_action_set_tp);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:\n+\t\tlen = sizeof(struct vxlan_data);\n+\t\tfunc = nfp_ct_flow_action_copy_vxlan;\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_RAW_ENCAP:\n+\t\tlen = sizeof(struct rte_flow_action_raw_encap);\n+\t\tfunc = nfp_ct_flow_action_copy_raw;\n+\t\tbreak;\n+\tdefault:\n+\t\tPMD_DRV_LOG(DEBUG, \"Unsupported action type: %d\", src->type);\n+\t\treturn false;\n+\t}\n+\n+\tdst->conf = nfp_ct_flow_action_copy_real(src->conf, len, func);\n+\tif (dst->conf == NULL) {\n+\t\tPMD_DRV_LOG(DEBUG, \"Copy ct action process failed\");\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static bool\n+nfp_ct_flow_actions_copy(const struct rte_flow_action *src,\n+\t\tstruct rte_flow_action *dst,\n+\t\tuint8_t action_cnt)\n+{\n+\tbool ret;\n+\tuint8_t loop;\n+\n+\tfor (loop = 0; loop < action_cnt; ++loop) {\n+\t\tret = nfp_ct_flow_action_copy(src + loop, dst + loop);\n+\t\tif (!ret) {\n+\t\t\tPMD_DRV_LOG(DEBUG, \"Copy ct action failed\");\n+\t\t\tnfp_ct_flow_actions_free(dst, loop);\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n+static struct nfp_ct_flow_entry *\n+nfp_ct_flow_entry_get(struct nfp_ct_zone_entry *ze,\n+\t\tstruct nfp_flower_representor *repr,\n+\t\tconst struct rte_flow_item items[],\n+\t\tconst struct rte_flow_action actions[],\n+\t\tuint64_t cookie)\n+{\n+\tbool ret;\n+\tuint8_t loop;\n+\tuint8_t item_cnt = 1;      /* the RTE_FLOW_ITEM_TYPE_END */\n+\tuint8_t action_cnt = 1;    /* the RTE_FLOW_ACTION_TYPE_END */\n+\tstruct nfp_flow_priv *priv;\n+\tstruct nfp_ct_map_entry *me;\n+\tstruct nfp_ct_flow_entry *fe;\n+\n+\tfe = rte_zmalloc(\"ct_flow_entry\", sizeof(*fe), 0);\n+\tif (fe == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not alloc ct_flow entry\");\n+\t\treturn NULL;\n+\t}\n+\n+\tfe->ze = ze;\n+\tfe->repr = repr;\n+\tfe->cookie = cookie;\n+\tLIST_INIT(&fe->children);\n+\n+\tfor (loop = 0; (items + loop)->type != RTE_FLOW_ITEM_TYPE_END; loop++)\n+\t\titem_cnt++;\n+\tfor (loop = 0; (actions + loop)->type != RTE_FLOW_ACTION_TYPE_END; loop++)\n+\t\taction_cnt++;\n+\n+\tfe->rule.items = rte_zmalloc(\"ct_flow_item\",\n+\t\t\tsizeof(struct rte_flow_item) * item_cnt, 0);\n+\tif (fe->rule.items == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not alloc ct flow items\");\n+\t\tgoto free_flow_entry;\n+\t}\n+\n+\tfe->rule.actions = rte_zmalloc(\"ct_flow_action\",\n+\t\t\tsizeof(struct rte_flow_action) * action_cnt, 0);\n+\tif (fe->rule.actions == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not alloc ct flow actions\");\n+\t\tgoto free_flow_item;\n+\t}\n+\n+\t/* Deep copy of items */\n+\tret = nfp_ct_flow_items_copy(items, fe->rule.items, item_cnt);\n+\tif (!ret) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not deep copy ct flow items\");\n+\t\tgoto free_flow_action;\n+\t}\n+\n+\t/* Deep copy of actions */\n+\tret = nfp_ct_flow_actions_copy(actions, fe->rule.actions, action_cnt);\n+\tif (!ret) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not deep copy ct flow actions\");\n+\t\tgoto free_copied_items;\n+\t}\n+\n+\tfe->rule.items_cnt = item_cnt;\n+\tfe->rule.actions_cnt = action_cnt;\n+\n+\t/* Now add a ct map entry */\n+\tme = rte_zmalloc(\"ct_map_entry\", sizeof(*me), 0);\n+\tif (me == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Malloc memory for ct map entry failed\");\n+\t\tgoto free_copied_actions;\n+\t}\n+\n+\tme->cookie = fe->cookie;\n+\tme->fe = fe;\n+\n+\tpriv = repr->app_fw_flower->flow_priv;\n+\tret = nfp_ct_map_table_add(priv, me);\n+\tif (!ret) {\n+\t\tPMD_DRV_LOG(ERR, \"Add into ct map table failed\");\n+\t\tgoto free_map_entry;\n+\t}\n+\n+\treturn fe;\n+\n+free_map_entry:\n+\tnfp_ct_map_entry_destroy(me);\n+free_copied_actions:\n+\tnfp_ct_flow_actions_free(fe->rule.actions, action_cnt);\n+free_copied_items:\n+\tnfp_ct_flow_items_free(fe->rule.items, item_cnt);\n+free_flow_action:\n+\trte_free(fe->rule.actions);\n+free_flow_item:\n+\trte_free(fe->rule.items);\n+free_flow_entry:\n+\trte_free(fe);\n+\n+\treturn NULL;\n+}\n+\n+static void\n+nfp_flow_children_merge_free(struct nfp_ct_flow_entry *fe)\n+{\n+\tstruct nfp_ct_merge_entry *m_ent;\n+\n+\tswitch (fe->type) {\n+\tcase CT_TYPE_PRE_CT:\n+\t\tLIST_FOREACH(m_ent, &fe->children, pre_ct_list)\n+\t\t\tnfp_ct_merge_entry_destroy(m_ent);\n+\t\tbreak;\n+\tcase CT_TYPE_POST_CT:\n+\t\tLIST_FOREACH(m_ent, &fe->children, post_ct_list)\n+\t\t\tnfp_ct_merge_entry_destroy(m_ent);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\n+\n+static void\n+nfp_ct_flow_entry_destroy_partly(struct nfp_ct_flow_entry *fe)\n+{\n+\tstruct nfp_ct_map_entry *me;\n+\n+\tif (!LIST_EMPTY(&fe->children))\n+\t\tnfp_flow_children_merge_free(fe);\n+\n+\tme = nfp_ct_map_table_search(fe->ze->priv, (char *)&fe->cookie, sizeof(uint64_t));\n+\tif (me != NULL) {\n+\t\tnfp_ct_map_table_delete(fe->ze->priv, me);\n+\t\tnfp_ct_map_entry_destroy(me);\n+\t}\n+\n+\tnfp_ct_flow_actions_free(fe->rule.actions, fe->rule.actions_cnt);\n+\tnfp_ct_flow_items_free(fe->rule.items, fe->rule.items_cnt);\n+\trte_free(fe->rule.actions);\n+\trte_free(fe->rule.items);\n+\trte_free(fe);\n+}\n+\n+static void\n+nfp_ct_flow_entry_destroy(struct nfp_ct_flow_entry *fe)\n+{\n+\tLIST_REMOVE(fe, pre_ct_list);\n+\tLIST_REMOVE(fe, post_ct_list);\n+\n+\tnfp_ct_flow_entry_destroy_partly(fe);\n+}\n+\n+static struct nfp_ct_zone_entry *\n+nfp_ct_zone_table_search(struct nfp_flow_priv *priv,\n+\t\tchar *hash_data,\n+\t\tuint32_t hash_len)\n+{\n+\tint index;\n+\tuint32_t hash_key;\n+\tstruct nfp_ct_zone_entry *ze;\n+\n+\thash_key = rte_jhash(hash_data, hash_len, priv->hash_seed);\n+\tindex = rte_hash_lookup_data(priv->ct_zone_table, &hash_key, (void **)&ze);\n+\tif (index < 0) {\n+\t\tPMD_DRV_LOG(DEBUG, \"Data NOT found in the ct_zone table\");\n+\t\treturn NULL;\n+\t}\n+\n+\treturn ze;\n+}\n+\n+static bool\n+nfp_ct_zone_table_add(struct nfp_flow_priv *priv,\n+\t\tstruct nfp_ct_zone_entry *ze)\n+{\n+\tint ret;\n+\tuint32_t hash_key;\n+\n+\thash_key = rte_jhash(ze, sizeof(uint32_t), priv->hash_seed);\n+\tret = rte_hash_add_key_data(priv->ct_zone_table, &hash_key, ze);\n+\tif (ret != 0) {\n+\t\tPMD_DRV_LOG(ERR, \"Add to the ct_zone table failed\");\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static void\n+nfp_ct_zone_table_delete(struct nfp_flow_priv *priv,\n+\t\tstruct nfp_ct_zone_entry *ze)\n+{\n+\tint ret;\n+\tuint32_t hash_key;\n+\n+\thash_key = rte_jhash(ze, sizeof(uint32_t), priv->hash_seed);\n+\tret = rte_hash_del_key(priv->ct_zone_table, &hash_key);\n+\tif (ret < 0)\n+\t\tPMD_DRV_LOG(ERR, \"Delete from the ct_zone table failed\");\n+}\n+\n+static bool\n+nfp_ct_zone_entry_init(struct nfp_ct_zone_entry *ze,\n+\t\tstruct nfp_flow_priv *priv,\n+\t\tuint32_t zone,\n+\t\tbool wildcard)\n+{\n+\tchar hash_name[RTE_HASH_NAMESIZE];\n+\tstruct rte_hash_parameters ct_merge_hash_params = {\n+\t\t.entries    = 1000,\n+\t\t.hash_func  = rte_jhash,\n+\t\t.socket_id  = rte_socket_id(),\n+\t\t.key_len    = sizeof(uint32_t),\n+\t\t.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY,\n+\t};\n+\n+\tif (wildcard) {\n+\t\tct_merge_hash_params.name = \"ct_wc_merge_table\";\n+\t} else {\n+\t\tsnprintf(hash_name, sizeof(hash_name), \"ct_%d_merge_table\", ze->zone);\n+\t\tct_merge_hash_params.name = hash_name;\n+\t}\n+\n+\tct_merge_hash_params.hash_func_init_val = priv->hash_seed;\n+\tze->ct_merge_table = rte_hash_create(&ct_merge_hash_params);\n+\tif (ze->ct_merge_table == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"ct merge table creation failed\");\n+\t\treturn false;\n+\t}\n+\n+\tze->zone = zone;\n+\tze->priv = priv;\n+\tLIST_INIT(&ze->pre_ct_list);\n+\tLIST_INIT(&ze->post_ct_list);\n+\n+\treturn true;\n+}\n+\n+static void\n+nfp_ct_zone_entry_destroy(struct nfp_ct_zone_entry *ze)\n+{\n+\tstruct nfp_ct_flow_entry *fe;\n+\n+\tif (ze == NULL)\n+\t\treturn;\n+\n+\trte_hash_free(ze->ct_merge_table);\n+\n+\tLIST_FOREACH(fe, &ze->pre_ct_list, pre_ct_list)\n+\t\tnfp_ct_flow_entry_destroy(fe);\n+\n+\tLIST_FOREACH(fe, &ze->post_ct_list, post_ct_list)\n+\t\tnfp_ct_flow_entry_destroy(fe);\n+\n+\trte_free(ze);\n+}\n+\n+static struct nfp_ct_zone_entry *\n+nfp_ct_zone_entry_get(struct nfp_flow_priv *priv,\n+\t\tuint32_t zone,\n+\t\tbool wildcard)\n+{\n+\tbool is_ok;\n+\tstruct nfp_ct_zone_entry *ze;\n+\n+\tif (wildcard) {\n+\t\tif (priv->ct_zone_wc != NULL)\n+\t\t\treturn priv->ct_zone_wc;\n+\n+\t\tze = rte_zmalloc(\"ct_zone_wc\", sizeof(*ze), 0);\n+\t\tif (ze == NULL) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Could not alloc ct_zone_wc entry\");\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\tis_ok = nfp_ct_zone_entry_init(ze, priv, zone, true);\n+\t\tif (!is_ok) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Init ct zone wc entry failed\");\n+\t\t\tgoto free_ct_zone_entry;\n+\t\t}\n+\n+\t\tpriv->ct_zone_wc = ze;\n+\t} else {\n+\t\tze = nfp_ct_zone_table_search(priv, (char *)&zone, sizeof(uint32_t));\n+\t\tif (ze != NULL)\n+\t\t\treturn ze;\n+\n+\t\tze = rte_zmalloc(\"ct_zone_entry\", sizeof(*ze), 0);\n+\t\tif (ze == NULL) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Could not alloc ct_zone entry\");\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\tis_ok = nfp_ct_zone_entry_init(ze, priv, zone, false);\n+\t\tif (!is_ok) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Init ct zone entry failed\");\n+\t\t\tgoto free_ct_zone_entry;\n+\t\t}\n+\n+\t\tis_ok = nfp_ct_zone_table_add(priv, ze);\n+\t\tif (!is_ok) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Add into ct zone table failed\");\n+\t\t\tgoto free_ct_zone_entry;\n+\t\t}\n+\t}\n+\n+\treturn ze;\n+\n+free_ct_zone_entry:\n+\tnfp_ct_zone_entry_destroy(ze);\n+\n+\treturn NULL;\n+}\n+\n+static void\n+nfp_ct_zone_entry_free(struct nfp_ct_zone_entry *ze,\n+\t\tbool wildcard)\n+{\n+\tif (LIST_EMPTY(&ze->pre_ct_list) && LIST_EMPTY(&ze->post_ct_list)) {\n+\t\tif (!wildcard)\n+\t\t\tnfp_ct_zone_table_delete(ze->priv, ze);\n+\n+\t\tnfp_ct_zone_entry_destroy(ze);\n+\t}\n+}\n+\n+static inline bool\n+is_item_check_pass(const struct rte_flow_item *item1,\n+\t\tconst struct rte_flow_item *item2,\n+\t\tuint8_t *cnt_same)\n+{\n+\tbool pass;\n+\tuint32_t i;\n+\tsize_t size;\n+\tconst char *key1 = item1->spec;\n+\tconst char *key2 = item2->spec;\n+\tconst char *mask1 = item1->mask;\n+\tconst char *mask2 = item2->mask;\n+\n+\tif (item1->type != item2->type)\n+\t\treturn true;\n+\n+\tpass = nfp_flow_item_conf_size_get(item1->type, &size);\n+\tif (!pass)\n+\t\treturn false;\n+\n+\tfor (i = 0; i < size; i++) {\n+\t\tif ((key1[i] & mask1[i] & mask2[i]) ^ (key2[i] & mask1[i] & mask2[i]))\n+\t\t\treturn false;\n+\t}\n+\n+\t*cnt_same = *cnt_same + 1;\n+\n+\treturn true;\n+}\n+\n+static bool\n+nfp_ct_merge_items_check(struct rte_flow_item *items1,\n+\t\tstruct rte_flow_item *items2,\n+\t\tuint8_t *cnt_same)\n+{\n+\tbool pass;\n+\tbool is_tun_flow_1;\n+\tbool is_tun_flow_2;\n+\tconst struct rte_flow_item *item1;\n+\tconst struct rte_flow_item *item2;\n+\tconst struct rte_flow_item *inner_item1 = NULL;\n+\tconst struct rte_flow_item *inner_item2 = NULL;\n+\n+\tis_tun_flow_1 = nfp_flow_inner_item_get(items1, &inner_item1);\n+\tis_tun_flow_2 = nfp_flow_inner_item_get(items2, &inner_item2);\n+\n+\tif (is_tun_flow_1) {\n+\t\tif (is_tun_flow_2) {\n+\t\t\t/* Outer layer */\n+\t\t\tfor (item1 = items1; item1 != inner_item1; item1++) {\n+\t\t\t\tfor (item2 = items2; item2 != inner_item2; item2++) {\n+\t\t\t\t\tpass = is_item_check_pass(item1, item2, cnt_same);\n+\t\t\t\t\tif (!pass)\n+\t\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\t/* Inner layer */\n+\t\t\tfor (item1 = inner_item1; item1->type != RTE_FLOW_ITEM_TYPE_END; item1++) {\n+\t\t\t\tfor (item2 = inner_item2; item2->type != RTE_FLOW_ITEM_TYPE_END;\n+\t\t\t\t\t\titem2++) {\n+\t\t\t\t\tpass = is_item_check_pass(item1, item2, cnt_same);\n+\t\t\t\t\tif (!pass)\n+\t\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfor (item1 = items1; item1 != inner_item1; item1++) {\n+\t\t\t\tfor (item2 = items2; item2->type != RTE_FLOW_ITEM_TYPE_END;\n+\t\t\t\t\t\titem2++) {\n+\t\t\t\t\tpass = is_item_check_pass(item1, item2, cnt_same);\n+\t\t\t\t\tif (!pass)\n+\t\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\tif (is_tun_flow_2) {\n+\t\t\tfor (item1 = items1; item1->type != RTE_FLOW_ITEM_TYPE_END; item1++) {\n+\t\t\t\tfor (item2 = items2; item2 != inner_item2; item2++) {\n+\t\t\t\t\tpass = is_item_check_pass(item1, item2, cnt_same);\n+\t\t\t\t\tif (!pass)\n+\t\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfor (item1 = items1; item1->type != RTE_FLOW_ITEM_TYPE_END; item1++) {\n+\t\t\t\tfor (item2 = items2; item2->type != RTE_FLOW_ITEM_TYPE_END;\n+\t\t\t\t\t\titem2++) {\n+\t\t\t\t\tpass = is_item_check_pass(item1, item2, cnt_same);\n+\t\t\t\t\tif (!pass)\n+\t\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n+static inline bool\n+is_action_pattern_check_pass(struct rte_flow_item *items,\n+\t\tenum rte_flow_item_type type)\n+{\n+\tstruct rte_flow_item *item;\n+\n+\tfor (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {\n+\t\tif (item->type == type)\n+\t\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static bool\n+nfp_ct_merge_action_check(struct rte_flow_action *action,\n+\t\tstruct rte_flow_item *items)\n+{\n+\tbool pass = true;\n+\n+\tswitch (action->type) {\n+\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_SRC:    /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_DST:\n+\t\tpass = is_action_pattern_check_pass(items, RTE_FLOW_ITEM_TYPE_ETH);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_SRC:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DST:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DSCP:\n+\t\tpass = is_action_pattern_check_pass(items, RTE_FLOW_ITEM_TYPE_IPV4);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_SRC:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DST:   /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DSCP:\n+\t\tpass = is_action_pattern_check_pass(items, RTE_FLOW_ITEM_TYPE_IPV6);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TP_SRC:    /* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_SET_TP_DST:\n+\t\tpass = is_action_pattern_check_pass(items, RTE_FLOW_ITEM_TYPE_UDP);\n+\t\tpass |= is_action_pattern_check_pass(items, RTE_FLOW_ITEM_TYPE_TCP);\n+\t\tpass |= is_action_pattern_check_pass(items, RTE_FLOW_ITEM_TYPE_SCTP);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn pass;\n+}\n+\n+static bool\n+nfp_ct_merge_actions_check(struct rte_flow_action *actions,\n+\t\tstruct rte_flow_item *items,\n+\t\tuint8_t *cnt_same)\n+{\n+\tbool pass = true;\n+\tstruct rte_flow_action *action;\n+\n+\tfor (action = actions; action->type != RTE_FLOW_ACTION_TYPE_END; action++) {\n+\t\tswitch (action->type) {\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_SRC:    /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_MAC_DST:    /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_SRC:   /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DST:   /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_IPV4_DSCP:  /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_SRC:   /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DST:   /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_IPV6_DSCP:  /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_TP_SRC:     /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_SET_TP_DST:\n+\t\t\tpass = nfp_ct_merge_action_check(action, items);\n+\t\t\tbreak;\n+\t\tcase RTE_FLOW_ACTION_TYPE_CONNTRACK: /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_JUMP:      /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_COUNT:     /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_DROP:      /* FALLTHROUGH */\n+\t\tcase RTE_FLOW_ACTION_TYPE_VOID:\n+\t\t\t*cnt_same = *cnt_same + 1;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tpass = false;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn pass;\n+}\n+\n+static void\n+nfp_ct_merge_item_real(const struct rte_flow_item *item_src,\n+\t\tstruct rte_flow_item *item_dst)\n+{\n+\tuint32_t i;\n+\tsize_t size;\n+\tchar *key_dst;\n+\tchar *mask_dst;\n+\tconst char *key_src;\n+\tconst char *mask_src;\n+\n+\tkey_src = item_src->spec;\n+\tmask_src = item_src->mask;\n+\tkey_dst = (char *)(ptrdiff_t)item_dst->spec;\n+\tmask_dst = (char *)(ptrdiff_t)item_dst->mask;\n+\tnfp_flow_item_conf_size_get(item_src->type, &size);\n+\n+\tfor (i = 0; i < size; i++) {\n+\t\tkey_dst[i] |= key_src[i];\n+\t\tmask_dst[i] |= mask_src[i];\n+\t}\n+}\n+\n+static bool\n+nfp_ct_merge_item(uint32_t index,\n+\t\tconst struct rte_flow_item *item1,\n+\t\tconst struct rte_flow_item *item2_start,\n+\t\tconst struct rte_flow_item *item2_end,\n+\t\tstruct nfp_ct_merge_entry *merge_entry)\n+{\n+\tstruct rte_flow_item *item;\n+\tconst struct rte_flow_item *item2;\n+\n+\t/* Copy to the merged items */\n+\titem = &merge_entry->rule.items[index];\n+\t*item = *item1;\n+\n+\titem2 = item2_start;\n+\tif (item2_end != NULL) {\n+\t\tfor (; item2 != item2_end; item2++) {\n+\t\t\tif (item1->type == item2->type) {\n+\t\t\t\tnfp_ct_merge_item_real(item2, item);\n+\t\t\t\treturn true;\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\tfor (; item2->type != RTE_FLOW_ITEM_TYPE_END; item2++) {\n+\t\t\tif (item1->type == item2->type) {\n+\t\t\t\tnfp_ct_merge_item_real(item2, item);\n+\t\t\t\treturn true;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn false;\n+}\n+\n+static void\n+nfp_ct_merge_items(struct nfp_ct_merge_entry *merge_entry)\n+{\n+\tuint32_t index = 0;\n+\tbool is_tun_flow_1;\n+\tbool is_tun_flow_2;\n+\tstruct rte_flow_item *items1;\n+\tstruct rte_flow_item *items2;\n+\tstruct rte_flow_item *merge_item;\n+\tconst struct rte_flow_item *item;\n+\tconst struct rte_flow_item *inner1 = NULL;\n+\tconst struct rte_flow_item *inner2 = NULL;\n+\n+\titems1 = merge_entry->pre_ct_parent->rule.items;\n+\titems2 = merge_entry->post_ct_parent->rule.items;\n+\tis_tun_flow_1 = nfp_flow_inner_item_get(items1, &inner1);\n+\tis_tun_flow_2 = nfp_flow_inner_item_get(items2, &inner2);\n+\n+\tif (is_tun_flow_1) {\n+\t\tif (is_tun_flow_2) {\n+\t\t\t/* Outer layer */\n+\t\t\tfor (item = items1; item != inner1; item++, index++) {\n+\t\t\t\tif (nfp_ct_merge_item(index, item, items2, inner2, merge_entry))\n+\t\t\t\t\titems2++;\n+\t\t\t}\n+\n+\t\t\t/* Copy the remainning outer layer items */\n+\t\t\tfor (item = items2; item != inner2; item++, index++) {\n+\t\t\t\tmerge_item = &merge_entry->rule.items[index];\n+\t\t\t\t*merge_item = *item;\n+\t\t\t}\n+\n+\t\t\t/* Inner layer */\n+\t\t\tfor (item = inner1; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tif (nfp_ct_merge_item(index, item, inner2, NULL, merge_entry))\n+\t\t\t\t\titems2++;\n+\t\t\t}\n+\n+\t\t\t/* Copy the remainning inner layer items */\n+\t\t\tfor (item = items2; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tmerge_item = &merge_entry->rule.items[index];\n+\t\t\t\t*merge_item = *item;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfor (item = items1; item != inner1; item++, index++) {\n+\t\t\t\tif (nfp_ct_merge_item(index, item, items2, NULL, merge_entry))\n+\t\t\t\t\titems2++;\n+\t\t\t}\n+\n+\t\t\t/* Copy the remainning items */\n+\t\t\tfor (item = items2; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tmerge_item = &merge_entry->rule.items[index];\n+\t\t\t\t*merge_item = *item;\n+\t\t\t}\n+\n+\t\t\t/* Copy the inner layer items */\n+\t\t\tfor (item = inner1; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tmerge_item = &merge_entry->rule.items[index];\n+\t\t\t\t*merge_item = *item;\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\tif (is_tun_flow_2) {\n+\t\t\tfor (item = items1; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tif (nfp_ct_merge_item(index, item, items2, inner2, merge_entry))\n+\t\t\t\t\titems2++;\n+\t\t\t}\n+\n+\t\t\t/* Copy the remainning items */\n+\t\t\tfor (item = items2; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tmerge_item = &merge_entry->rule.items[index];\n+\t\t\t\t*merge_item = *item;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfor (item = items1; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tif (nfp_ct_merge_item(index, item, items2, NULL, merge_entry))\n+\t\t\t\t\titems2++;\n+\t\t\t}\n+\n+\t\t\t/* Copy the remainning items */\n+\t\t\tfor (item = items2; item->type != RTE_FLOW_ITEM_TYPE_END; item++, index++) {\n+\t\t\t\tmerge_item = &merge_entry->rule.items[index];\n+\t\t\t\t*merge_item = *item;\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+static void\n+nfp_ct_merge_actions(struct nfp_ct_merge_entry *merge_entry)\n+{\n+\tstruct rte_flow_action *action;\n+\tstruct rte_flow_action *merge_actions;\n+\n+\tmerge_actions = merge_entry->rule.actions;\n+\n+\taction = merge_entry->pre_ct_parent->rule.actions;\n+\tfor (; action->type != RTE_FLOW_ACTION_TYPE_END; action++) {\n+\t\tif (action->type == RTE_FLOW_ACTION_TYPE_CONNTRACK ||\n+\t\t\t\taction->type == RTE_FLOW_ACTION_TYPE_JUMP)\n+\t\t\tcontinue;\n+\n+\t\t*merge_actions = *action;\n+\t\tmerge_actions++;\n+\t}\n+\n+\taction = merge_entry->post_ct_parent->rule.actions;\n+\tfor (; action->type != RTE_FLOW_ACTION_TYPE_END; action++) {\n+\t\t*merge_actions = *action;\n+\t\tmerge_actions++;\n+\t}\n+}\n+\n+static bool\n+nfp_ct_do_flow_merge(struct nfp_ct_zone_entry *ze,\n+\t\tstruct nfp_ct_flow_entry *pre_ct_entry,\n+\t\tstruct nfp_ct_flow_entry *post_ct_entry)\n+{\n+\tbool ret;\n+\tuint64_t new_cookie[2];\n+\tuint8_t cnt_same_item = 0;\n+\tuint8_t cnt_same_action = 0;\n+\tstruct nfp_ct_merge_entry *merge_entry;\n+\n+\tif (pre_ct_entry->repr != post_ct_entry->repr)\n+\t\treturn true;\n+\n+\tret = nfp_ct_merge_items_check(pre_ct_entry->rule.items,\n+\t\t\tpost_ct_entry->rule.items, &cnt_same_item);\n+\tif (!ret)\n+\t\treturn true;\n+\n+\tret = nfp_ct_merge_actions_check(pre_ct_entry->rule.actions,\n+\t\t\tpost_ct_entry->rule.items, &cnt_same_action);\n+\tif (!ret)\n+\t\treturn true;\n+\n+\tnew_cookie[0] = pre_ct_entry->cookie;\n+\tnew_cookie[1] = post_ct_entry->cookie;\n+\tmerge_entry = nfp_ct_merge_table_search(ze, (char *)&new_cookie, sizeof(uint64_t) * 2);\n+\tif (merge_entry != NULL)\n+\t\treturn true;\n+\n+\tmerge_entry = rte_zmalloc(\"ct_merge_entry\", sizeof(*merge_entry), 0);\n+\tif (merge_entry == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Malloc memory for ct merge entry failed\");\n+\t\treturn false;\n+\t}\n+\n+\tmerge_entry->ze = ze;\n+\tmerge_entry->pre_ct_parent = pre_ct_entry;\n+\tmerge_entry->post_ct_parent = post_ct_entry;\n+\trte_memcpy(merge_entry->cookie, new_cookie, sizeof(new_cookie));\n+\tmerge_entry->rule.items_cnt = pre_ct_entry->rule.items_cnt +\n+\t\t\tpost_ct_entry->rule.items_cnt - cnt_same_item - 1;\n+\tmerge_entry->rule.actions_cnt = pre_ct_entry->rule.actions_cnt +\n+\t\t\tpost_ct_entry->rule.actions_cnt - cnt_same_action - 1;\n+\n+\tmerge_entry->rule.items = rte_zmalloc(\"ct_flow_item\",\n+\t\t\tsizeof(struct rte_flow_item) * merge_entry->rule.items_cnt, 0);\n+\tif (merge_entry->rule.items == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not alloc items for merged flow\");\n+\t\tgoto merge_exit;\n+\t}\n+\n+\tmerge_entry->rule.actions = rte_zmalloc(\"ct_flow_action\",\n+\t\t\tsizeof(struct rte_flow_action) * merge_entry->rule.actions_cnt, 0);\n+\tif (merge_entry->rule.actions == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not alloc actions for merged flow\");\n+\t\tgoto free_items;\n+\t}\n+\n+\tnfp_ct_merge_items(merge_entry);\n+\tnfp_ct_merge_actions(merge_entry);\n+\n+\t/* Add this entry to the pre_ct and post_ct lists */\n+\tLIST_INSERT_HEAD(&pre_ct_entry->children, merge_entry, pre_ct_list);\n+\tLIST_INSERT_HEAD(&post_ct_entry->children, merge_entry, post_ct_list);\n+\n+\tret = nfp_ct_merge_table_add(ze, merge_entry);\n+\tif (!ret) {\n+\t\tPMD_DRV_LOG(ERR, \"Add into ct merge table failed\");\n+\t\tgoto free_actions;\n+\t}\n+\n+\treturn true;\n+\n+free_actions:\n+\trte_free(merge_entry->rule.actions);\n+free_items:\n+\trte_free(merge_entry->rule.items);\n+merge_exit:\n+\tLIST_REMOVE(merge_entry, post_ct_list);\n+\tLIST_REMOVE(merge_entry, pre_ct_list);\n+\trte_free(merge_entry);\n+\n+\treturn ret;\n+}\n+\n+static bool\n+nfp_ct_merge_flow_entries(struct nfp_ct_flow_entry *fe,\n+\t\tstruct nfp_ct_zone_entry *ze_src,\n+\t\tstruct nfp_ct_zone_entry *ze_dst)\n+{\n+\tbool ret;\n+\tstruct nfp_ct_flow_entry *fe_tmp;\n+\n+\tif (fe->type == CT_TYPE_PRE_CT) {\n+\t\tLIST_FOREACH(fe_tmp, &ze_src->post_ct_list, post_ct_list) {\n+\t\t\tret = nfp_ct_do_flow_merge(ze_dst, fe, fe_tmp);\n+\t\t\tif (!ret) {\n+\t\t\t\tPMD_DRV_LOG(ERR, \"Merge for ct pre flow failed\");\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\tLIST_FOREACH(fe_tmp, &ze_src->pre_ct_list, pre_ct_list) {\n+\t\t\tret = nfp_ct_do_flow_merge(ze_dst, fe_tmp, fe);\n+\t\t\tif (!ret) {\n+\t\t\t\tPMD_DRV_LOG(ERR, \"Merge for ct post flow failed\");\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n+static bool\n+nfp_flow_handle_pre_ct(const struct rte_flow_item *ct_item,\n+\t\tstruct nfp_flower_representor *representor,\n+\t\tconst struct rte_flow_item items[],\n+\t\tconst struct rte_flow_action actions[],\n+\t\tuint64_t cookie)\n+{\n+\tbool ret;\n+\tstruct nfp_flow_priv *priv;\n+\tstruct nfp_ct_zone_entry *ze;\n+\tstruct nfp_ct_flow_entry *fe;\n+\tconst struct ct_data *ct = ct_item->spec;\n+\n+\tpriv = representor->app_fw_flower->flow_priv;\n+\tze = nfp_ct_zone_entry_get(priv, ct->ct_zone, false);\n+\tif (ze == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not get ct zone entry\");\n+\t\treturn false;\n+\t}\n+\n+\t/* Add entry to pre_ct_lsit */\n+\tfe = nfp_ct_flow_entry_get(ze, representor, items, actions, cookie);\n+\tif (fe == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not get ct flow entry\");\n+\t\tgoto ct_zone_entry_free;\n+\t}\n+\n+\tfe->type = CT_TYPE_PRE_CT;\n+\tLIST_INSERT_HEAD(&ze->pre_ct_list, fe, pre_ct_list);\n+\n+\tret = nfp_ct_merge_flow_entries(fe, ze, ze);\n+\tif (!ret) {\n+\t\tPMD_DRV_LOG(ERR, \"Merge ct flow entries failed\");\n+\t\tgoto ct_flow_entry_free;\n+\t}\n+\n+\t/* Need to check and merge with tables in the wc_zone as well */\n+\tif (priv->ct_zone_wc != NULL) {\n+\t\tret = nfp_ct_merge_flow_entries(fe, priv->ct_zone_wc, ze);\n+\t\tif (!ret) {\n+\t\t\tPMD_DRV_LOG(ERR, \"Merge ct flow entries wildcast failed\");\n+\t\t\tgoto ct_flow_entry_free;\n+\t\t}\n+\t}\n+\n+\t/* The real offload logic comes in next commit, so here just return false for now */\n+\n+ct_flow_entry_free:\n+\tnfp_ct_flow_entry_destroy(fe);\n+\n+ct_zone_entry_free:\n+\tnfp_ct_zone_entry_free(ze, false);\n+\n+\treturn false;\n+}\n+\n+static bool\n+nfp_flow_handle_post_ct(const struct rte_flow_item *ct_item,\n+\t\tstruct nfp_flower_representor *representor,\n+\t\tconst struct rte_flow_item items[],\n+\t\tconst struct rte_flow_action actions[],\n+\t\tuint64_t cookie)\n+{\n+\tbool ret;\n+\tvoid *next_data;\n+\tuint32_t iter = 0;\n+\tconst void *next_key;\n+\tbool wildcard = false;\n+\tstruct nfp_flow_priv *priv;\n+\tstruct nfp_ct_zone_entry *ze;\n+\tstruct nfp_ct_flow_entry *fe;\n+\tconst struct ct_data *ct = ct_item->spec;\n+\tconst struct ct_data *ct_mask = ct_item->mask;\n+\n+\tif (ct_mask->ct_zone == 0) {\n+\t\twildcard = true;\n+\t} else if (ct_mask->ct_zone != UINT16_MAX) {\n+\t\tPMD_DRV_LOG(ERR, \"Partially wildcard ct_zone is not supported\");\n+\t\treturn false;\n+\t}\n+\n+\tpriv = representor->app_fw_flower->flow_priv;\n+\tze = nfp_ct_zone_entry_get(priv, ct->ct_zone, wildcard);\n+\tif (ze == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not get ct zone entry\");\n+\t\treturn false;\n+\t}\n+\n+\t/* Add entry to post_ct_list */\n+\tfe = nfp_ct_flow_entry_get(ze, representor, items, actions, cookie);\n+\tif (fe == NULL) {\n+\t\tPMD_DRV_LOG(ERR, \"Could not get ct flow entry\");\n+\t\tgoto ct_zone_entry_free;\n+\t}\n+\n+\tfe->type = CT_TYPE_POST_CT;\n+\tLIST_INSERT_HEAD(&ze->post_ct_list, fe, post_ct_list);\n+\n+\tif (wildcard) {\n+\t\twhile (rte_hash_iterate(priv->ct_zone_table, &next_key, &next_data, &iter) >= 0) {\n+\t\t\tze = (struct nfp_ct_zone_entry *)next_data;\n+\t\t\tret = nfp_ct_merge_flow_entries(fe, ze, ze);\n+\t\t\tif (!ret) {\n+\t\t\t\tPMD_DRV_LOG(ERR, \"Merge ct flow entries wildcast failed\");\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\tret = nfp_ct_merge_flow_entries(fe, ze, ze);\n+\t}\n+\n+\tif (!ret)\n+\t\tgoto ct_flow_entry_free;\n+\n+\t/* The real offload logic comes in next commit, so here just return false for now */\n+\n+ct_flow_entry_free:\n+\tnfp_ct_flow_entry_destroy(fe);\n+\n+ct_zone_entry_free:\n+\tnfp_ct_zone_entry_free(ze, wildcard);\n+\n+\treturn false;\n+}\n+\n+struct rte_flow *\n+nfp_ct_flow_setup(struct nfp_flower_representor *representor,\n+\t\tconst struct rte_flow_item items[],\n+\t\tconst struct rte_flow_action actions[],\n+\t\tconst struct rte_flow_item *ct_item,\n+\t\tbool validate_flag,\n+\t\tuint64_t cookie)\n+{\n+\tconst struct ct_data *ct;\n+\n+\tif (ct_item == NULL)\n+\t\treturn NULL;\n+\n+\tct = ct_item->spec;\n+\n+\tif (is_ct_commit_flow(ct)) {\n+\t\treturn nfp_flow_process(representor, &items[1], actions,\n+\t\t\t\tvalidate_flag, cookie, false);\n+\t}\n+\n+\tif (is_post_ct_flow(ct)) {\n+\t\tif (nfp_flow_handle_post_ct(ct_item, representor, &items[1],\n+\t\t\t\tactions, cookie)) {\n+\t\t\treturn nfp_flow_process(representor, &items[1], actions,\n+\t\t\t\t\tvalidate_flag, cookie, false);\n+\t\t}\n+\n+\t\tPMD_DRV_LOG(ERR, \"Handle nfp post ct flow failed.\");\n+\t\treturn NULL;\n+\t}\n+\n+\tif (is_pre_ct_flow(ct, actions)) {\n+\t\tif (nfp_flow_handle_pre_ct(ct_item, representor, &items[1],\n+\t\t\t\tactions, cookie)) {\n+\t\t\treturn nfp_flow_process(representor, &items[1], actions,\n+\t\t\t\t\tvalidate_flag, cookie, false);\n+\t\t}\n+\n+\t\tPMD_DRV_LOG(ERR, \"Handle nfp pre ct flow failed.\");\n+\t\treturn NULL;\n+\t}\n+\n+\tPMD_DRV_LOG(ERR, \"Unsupported ct flow type.\");\n+\treturn NULL;\n+}\ndiff --git a/drivers/net/nfp/flower/nfp_conntrack.h b/drivers/net/nfp/flower/nfp_conntrack.h\nnew file mode 100644\nindex 0000000000..149a3eb040\n--- /dev/null\n+++ b/drivers/net/nfp/flower/nfp_conntrack.h\n@@ -0,0 +1,32 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Corigine, Inc.\n+ * All rights reserved.\n+ */\n+\n+#ifndef __NFP_CONNTRACK_H__\n+#define __NFP_CONNTRACK_H__\n+\n+#include <stdbool.h>\n+\n+#include <rte_flow.h>\n+\n+#include \"../nfp_flow.h\"\n+\n+struct nfp_ct_map_entry;\n+\n+struct nfp_ct_zone_entry;\n+\n+struct nfp_ct_merge_entry;\n+\n+struct nfp_ct_map_entry *nfp_ct_map_table_search(struct nfp_flow_priv *priv,\n+\t\tchar *hash_data,\n+\t\tuint32_t hash_len);\n+\n+struct rte_flow *nfp_ct_flow_setup(struct nfp_flower_representor *representor,\n+\t\tconst struct rte_flow_item items[],\n+\t\tconst struct rte_flow_action actions[],\n+\t\tconst struct rte_flow_item *ct_item,\n+\t\tbool validate_flag,\n+\t\tuint64_t cookie);\n+\n+#endif /* __NFP_CONNTRACK_H__ */\ndiff --git a/drivers/net/nfp/meson.build b/drivers/net/nfp/meson.build\nindex d422269c4b..e6a642da49 100644\n--- a/drivers/net/nfp/meson.build\n+++ b/drivers/net/nfp/meson.build\n@@ -6,6 +6,7 @@ if not is_linux or not dpdk_conf.get('RTE_ARCH_64')\n     reason = 'only supported on 64-bit Linux'\n endif\n sources = files(\n+        'flower/nfp_conntrack.c',\n         'flower/nfp_flower.c',\n         'flower/nfp_flower_cmsg.c',\n         'flower/nfp_flower_ctrl.c',\ndiff --git a/drivers/net/nfp/nfp_flow.c b/drivers/net/nfp/nfp_flow.c\nindex 1bb93bcfb5..16a5c7e055 100644\n--- a/drivers/net/nfp/nfp_flow.c\n+++ b/drivers/net/nfp/nfp_flow.c\n@@ -10,6 +10,7 @@\n #include <rte_jhash.h>\n #include <rte_malloc.h>\n \n+#include \"flower/nfp_conntrack.h\"\n #include \"flower/nfp_flower_representor.h\"\n #include \"nfpcore/nfp_rtsym.h\"\n #include \"nfp_logs.h\"\n@@ -3748,6 +3749,8 @@ nfp_flow_setup(struct nfp_flower_representor *representor,\n \t\tbool validate_flag)\n {\n \tuint64_t cookie;\n+\tconst struct rte_flow_item *item;\n+\tconst struct rte_flow_item *ct_item = NULL;\n \n \tif (attr->group != 0)\n \t\tPMD_DRV_LOG(INFO, \"Pretend we support group attribute.\");\n@@ -3758,8 +3761,19 @@ nfp_flow_setup(struct nfp_flower_representor *representor,\n \tif (attr->transfer != 0)\n \t\tPMD_DRV_LOG(INFO, \"Pretend we support transfer attribute.\");\n \n+\tfor (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {\n+\t\tif (item->type == RTE_FLOW_ITEM_TYPE_CONNTRACK) {\n+\t\t\tct_item = item;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n \tcookie = rte_rand();\n \n+\tif (ct_item != NULL)\n+\t\treturn nfp_ct_flow_setup(representor, items, actions,\n+\t\t\t\tct_item, validate_flag, cookie);\n+\n \treturn nfp_flow_process(representor, items, actions, validate_flag, cookie, true);\n }\n \n@@ -4235,6 +4249,23 @@ nfp_flow_priv_init(struct nfp_pf_dev *pf_dev)\n \t\t.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY,\n \t};\n \n+\tstruct rte_hash_parameters ct_zone_hash_params = {\n+\t\t.name       = \"ct_zone_table\",\n+\t\t.entries    = 65536,\n+\t\t.hash_func  = rte_jhash,\n+\t\t.socket_id  = rte_socket_id(),\n+\t\t.key_len    = sizeof(uint32_t),\n+\t\t.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY,\n+\t};\n+\n+\tstruct rte_hash_parameters ct_map_hash_params = {\n+\t\t.name       = \"ct_map_table\",\n+\t\t.hash_func  = rte_jhash,\n+\t\t.socket_id  = rte_socket_id(),\n+\t\t.key_len    = sizeof(uint32_t),\n+\t\t.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY,\n+\t};\n+\n \tctx_count = nfp_rtsym_read_le(pf_dev->sym_tbl,\n \t\t\t\"CONFIG_FC_HOST_CTX_COUNT\", &ret);\n \tif (ret < 0) {\n@@ -4325,6 +4356,25 @@ nfp_flow_priv_init(struct nfp_pf_dev *pf_dev)\n \t\tgoto free_flow_table;\n \t}\n \n+\t/* ct zone table */\n+\tct_zone_hash_params.hash_func_init_val = priv->hash_seed;\n+\tpriv->ct_zone_table = rte_hash_create(&ct_zone_hash_params);\n+\tif (priv->ct_zone_table == NULL) {\n+\t\tPMD_INIT_LOG(ERR, \"ct zone table creation failed\");\n+\t\tret = -ENOMEM;\n+\t\tgoto free_pre_tnl_table;\n+\t}\n+\n+\t/* ct map table */\n+\tct_map_hash_params.hash_func_init_val = priv->hash_seed;\n+\tct_map_hash_params.entries = ctx_count;\n+\tpriv->ct_map_table = rte_hash_create(&ct_map_hash_params);\n+\tif (priv->ct_map_table == NULL) {\n+\t\tPMD_INIT_LOG(ERR, \"ct map table creation failed\");\n+\t\tret = -ENOMEM;\n+\t\tgoto free_ct_zone_table;\n+\t}\n+\n \t/* ipv4 off list */\n \trte_spinlock_init(&priv->ipv4_off_lock);\n \tLIST_INIT(&priv->ipv4_off_list);\n@@ -4338,6 +4388,10 @@ nfp_flow_priv_init(struct nfp_pf_dev *pf_dev)\n \n \treturn 0;\n \n+free_ct_zone_table:\n+\trte_hash_free(priv->ct_zone_table);\n+free_pre_tnl_table:\n+\trte_hash_free(priv->pre_tun_table);\n free_flow_table:\n \trte_hash_free(priv->flow_table);\n free_mask_table:\n@@ -4363,6 +4417,8 @@ nfp_flow_priv_uninit(struct nfp_pf_dev *pf_dev)\n \tapp_fw_flower = NFP_PRIV_TO_APP_FW_FLOWER(pf_dev->app_fw_priv);\n \tpriv = app_fw_flower->flow_priv;\n \n+\trte_hash_free(priv->ct_map_table);\n+\trte_hash_free(priv->ct_zone_table);\n \trte_hash_free(priv->pre_tun_table);\n \trte_hash_free(priv->flow_table);\n \trte_hash_free(priv->mask_table);\ndiff --git a/drivers/net/nfp/nfp_flow.h b/drivers/net/nfp/nfp_flow.h\nindex 817eaecba2..df16cab8b5 100644\n--- a/drivers/net/nfp/nfp_flow.h\n+++ b/drivers/net/nfp/nfp_flow.h\n@@ -150,6 +150,10 @@ struct nfp_flow_priv {\n \trte_spinlock_t ipv6_off_lock; /**< Lock the ipv6 off list */\n \t/* neighbor next */\n \tLIST_HEAD(, nfp_fl_tun)nn_list; /**< Store nn entry */\n+\t/* Conntrack */\n+\tstruct rte_hash *ct_zone_table; /**< Hash table to store ct zone entry */\n+\tstruct nfp_ct_zone_entry *ct_zone_wc; /**< The wildcard ct zone entry */\n+\tstruct rte_hash *ct_map_table; /**< Hash table to store ct map entry */\n };\n \n struct rte_flow {\n",
    "prefixes": [
        "2/4"
    ]
}