From patchwork Thu Aug 31 12:23:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mykola Kostenok X-Patchwork-Id: 130981 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id B6F3141FDD; Thu, 31 Aug 2023 14:24:24 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 11557402B8; Thu, 31 Aug 2023 14:23:22 +0200 (CEST) Received: from egress-ip17b.ess.de.barracuda.com (egress-ip17b.ess.de.barracuda.com [18.185.115.221]) by mails.dpdk.org (Postfix) with ESMTP id 2C46B402A8 for ; Thu, 31 Aug 2023 14:23:13 +0200 (CEST) Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05lp2169.outbound.protection.outlook.com [104.47.17.169]) by mx-outbound42-101.eu-central-1c.ess.aws.cudaops.com (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Thu, 31 Aug 2023 12:23:02 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=fO6+AZ08YhGxdp4LPEyM34/fqCbCiIaImivDRBCcAZ8mAiB4R2URnaZXWOpnjnBsUMiCNB4/VX7FE5YJRlxgNIUYnGQZv7UVHYIblyKb7+U4rO7TCbklx1krG6wUNw5LTnrC7PyXfQPFeHlkgmhTBkNVLs1oXpLBy1zHbt5Z6RvvtATPWv4k1Hgy/sIblvVaaAZXFNRqq41H+WOk0T9Bm/R8snh4HVRkQo0QCxhKx2v9ilcqrEiq0NZuof4ZVW3JvGgWAXSnjd+NZ+hvBksZV+n8zpCJmqBpjx/cwF2/+N9cZt/6W6l7rn0L8DKcnjAws7j7Nd6qr+Pz+SyWD2Tujg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; 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; bh=j2hpQcnfZddKuoNeLu46nv7zdHbInbkrnm6A0V0AWuc=; b=b9wQUNFtqee/zVROMKSyLqTUsU0c0Ibb11FaPnOtU7M2YFJiwNVk/9L1KXwx0ceTZ1u41YBa820P8BZvUKkYP/TpgII+zFQ04VJAWZvSJQPeFfE6hL5ZgB8RVdvki6MBQ08LA5Gs0J3sMUMNdIFyv9LvZwPp6SnvM+cBiCYbe7ayfaobXANBTlhD9TzOnzF5i+XQhCsIa71EXHlCIsnAiNMutGqmMe4aTnsNP2AXf6WxigOJXZ7ktclXrcE7VlyzVg2iH+DFsrfHoUHeSsMFLOiVU3oYdu4JAAQk3GHwIT6bFN90lQXQCm7esABGYGZD2Pp2T/h+rDcthcOWQxA4DQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 178.72.21.4) smtp.rcpttodomain=dpdk.org smtp.mailfrom=napatech.com; dmarc=fail (p=none sp=none pct=100) action=none header.from=napatech.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=napatech.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=j2hpQcnfZddKuoNeLu46nv7zdHbInbkrnm6A0V0AWuc=; b=RW5sdzxG85VYZMxpN0oMXYoxpXm+eSEisQmXkfMoYk5vosyWOxaBzlsXFG1jlGzmEigMTQ+7qVJVfNYm8JfS7IetoGkbvtlhlzJ+gpI7zXj+P4hS8SHN/vZ4FKlS8eLYSOja6Fj6B7nMlcuelYVCRzvqHm1WLYdfzarcQchTgfI= Received: from AS4P192CA0007.EURP192.PROD.OUTLOOK.COM (2603:10a6:20b:5da::19) by AM9P190MB1396.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:3b6::17) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6745.21; Thu, 31 Aug 2023 12:22:56 +0000 Received: from AM3PEPF00009B9B.eurprd04.prod.outlook.com (2603:10a6:20b:5da:cafe::13) by AS4P192CA0007.outlook.office365.com (2603:10a6:20b:5da::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6745.21 via Frontend Transport; Thu, 31 Aug 2023 12:22:56 +0000 X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 178.72.21.4) smtp.mailfrom=napatech.com; dkim=none (message not signed) header.d=none;dmarc=fail action=none header.from=napatech.com; Received-SPF: Fail (protection.outlook.com: domain of napatech.com does not designate 178.72.21.4 as permitted sender) receiver=protection.outlook.com; client-ip=178.72.21.4; helo=k8s-node.default.svc.cluster.local; Received: from k8s-node.default.svc.cluster.local (178.72.21.4) by AM3PEPF00009B9B.mail.protection.outlook.com (10.167.16.20) with Microsoft SMTP Server id 15.20.6745.17 via Frontend Transport; Thu, 31 Aug 2023 12:22:56 +0000 From: Mykola Kostenok To: dev@dpdk.org, mko-plv@napatech.com, thomas@monjalon.net Cc: ckm@napatech.com Subject: [PATCH v11 8/8] net/ntnic: adds socket connection to PMD Date: Thu, 31 Aug 2023 14:23:23 +0200 Message-Id: <20230831122323.967504-8-mko-plv@napatech.com> X-Mailer: git-send-email 2.39.3 In-Reply-To: <20230831122323.967504-1-mko-plv@napatech.com> References: <20230816132552.2483752-1-mko-plv@napatech.com> <20230831122323.967504-1-mko-plv@napatech.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM3PEPF00009B9B:EE_|AM9P190MB1396:EE_ X-MS-Office365-Filtering-Correlation-Id: dfabceae-734f-4ad6-ba6f-08dbaa1d01fa X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 1GKDC5JYkpTyBfGUjSz2sk/CHJnedeDq/ZvNOoxwaOASQXg0SfVkkra7gkQzhGKe4Gm9ze+6KOeoCKS3PC2WC6eUYEneYZtUPus2URYO0gqBhSub/iLZnd/btE1sil0dy8OYdaayfGT+cc8Xy1O8y1TXeGZcTw57QThBdWmV3Tjz7elni5O1ACwemlDc/qWu51o07aIQmwDXSM5FWisRqECI3MIlHgvTrMEuVXf59ihrDFb8zmx8b5yvcWzYTMIAZDSTyJ8i46kz9Q1Pd0wowO3OpUnj6kKhHRbTxcpvy1FqGyXBHJhgyrQgniRrez1g3r1O1jvSV0c2wv0XB1iNb/CipKkNUgEzLrPLLxxHLl9WVPSpPcWB7fwIA3mQWdGXzstSbIkzsTPFtq0r1KV9yXtDC0DXmp2LWl5PO8KR9Z0YspBp1aH9jEVAbM3lcWQ6TnPwwGtm8PrR63DxqSwB5RwRFGAdPAc2q5+XPwTdD09X1wfasVil3pBTcWSkskUREDNvfK5Fa8QP74fZaCrQww5njgn5UbiE2oB0G+xpNFqMYoUSqg398DGfYVaDWYEP22vLr2LX0DUnjVYhPULqJnZioUe7hujuyVgCKn6/ISrbVwduAnjg63zJEYED9kUJgQrPdXI63qbFABLREuS4WdVwrUoIKRjRuT0wMrUZacB7So0cSv1pWjcs9Ew//lu3KQ+vp1QugRZtv9nc0mxV2irykQnzTk8JLqpd6xSN0xUHvYuuB4nJt3jQm18hBxqw X-Forefront-Antispam-Report: CIP:178.72.21.4; CTRY:DK; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:k8s-node.default.svc.cluster.local; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230031)(396003)(136003)(39840400004)(376002)(346002)(186009)(451199024)(82310400011)(1800799009)(46966006)(36840700001)(356005)(6506007)(6666004)(6512007)(6486002)(36756003)(40480700001)(86362001)(36860700001)(81166007)(47076005)(107886003)(1076003)(956004)(2906002)(2616005)(30864003)(336012)(26005)(83380400001)(478600001)(70586007)(70206006)(8676002)(4326008)(5660300002)(9316004)(8936002)(316002)(36736006)(41300700001)(36900700001)(579004)(559001); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0: oztSKzBaXQcKNY0F3mea41eLok/cfC2FeutU24+SJ1QvxjWtPKlEcrMkKXEva27LE+NpKVqO8a/onBlNH08kS2ZD0H0dMs2QXdNDKGMIb7RfXYreGdTda3QPeaw2V9WRiMFTi0+r7vkTab80teO8G3DYRF/GNmi3tTEfVUY1e3qEA5u2wrBbTSxk5LkSU/kycpU3yTvK/tlEnfvvzKNw1Lp+uWIPT/1iP8aHXxB+EW/bnx7b0HhnfM9ZQn+zUTZcfbiL67KvK/SW53b2+Pq4zBi05WQzZRUf+4KQQsCxDVIKWIr/cis8ei48zK7tHwty2pOXBm4DpRz92x597AJF7iVWShXZ5NbY4XZTNN8oKglbIUCpZMDxe6N5P2wu2LAq2LYqdqgSmvBcXoI/bMJjrAMplj+ZwVDmqy8rWLrWeXg8JHi08AUX3eLVWbqNTxvs89hfascTtxwtYOJSYu9Qq0wmYsybQRC49doJ/CSESQWauY9Fp0XHy/Zui5hwSvyBQU19kUiE7qq39S16tasJ44jexxo2s6F1XRMA3cD2Yzxyr91igXfdb51895K2194gD8hkQbKvqL6TeTekxJzDOsCEjOIxXdLBt5Ip4xozxnqjAKzgJm69x/I63KhOGBUxVQh8Y9twz9eC4Z8De+wjiX0M5dnAD2mmPVE61YR8wkHxy3CmOeeflyVVhCe9eg+1VjjU329lkaFkKXzVcKh4sPj+t823DP08gfOgnKRqB3VBQIPd1yI/70vZkDnNzAqyniHe4yjnKzDiZOrJnKs8yd9Ld25Sh+A1OzCBX1KAPGU= X-OriginatorOrg: napatech.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 31 Aug 2023 12:22:56.5945 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: dfabceae-734f-4ad6-ba6f-08dbaa1d01fa X-MS-Exchange-CrossTenant-Id: c4540d0b-728a-4233-9da5-9ea30c7ec3ed X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=c4540d0b-728a-4233-9da5-9ea30c7ec3ed; Ip=[178.72.21.4]; Helo=[k8s-node.default.svc.cluster.local] X-MS-Exchange-CrossTenant-AuthSource: AM3PEPF00009B9B.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM9P190MB1396 X-BESS-ID: 1693484582-310853-12511-12441-1 X-BESS-VER: 2019.1_20230830.2058 X-BESS-Apparent-Source-IP: 104.47.17.169 X-BESS-Parts: H4sIAAAAAAACA4uuVkqtKFGyUioBkjpK+cVKVoZmxiZGxkB2BlDY3DLN3CLNwN zMyMLM1NDE2MLINNkwKTkt1cLcNDXZ2EypNhYAEnmISkMAAAA= X-BESS-Outbound-Spam-Score: 0.50 X-BESS-Outbound-Spam-Report: Code version 3.2, rules version 3.2.2.250513 [from cloudscan9-172.eu-central-1a.ess.aws.cudaops.com] Rule breakdown below pts rule name description ---- ---------------------- -------------------------------- 0.50 BSF_RULE7568M META: Custom Rule 7568M 0.00 BSF_BESS_OUTBOUND META: BESS Outbound X-BESS-Outbound-Spam-Status: SCORE=0.50 using account:ESS113687 scores of KILL_LEVEL=7.0 tests=BSF_RULE7568M, BSF_BESS_OUTBOUND X-BESS-BRTS-Status: 1 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Christian Koue Muf The socket connection is used by Napatech's tools for monitoring and rte_flow programming from other processes. Signed-off-by: Christian Koue Muf Reviewed-by: Mykola Kostenok --- v2: * Fixed WARNING:TYPO_SPELLING v4: * Fixed Alpine build v8: * Fixed token parser constant length. v10: * Fix uninitialized variables and build warnings. --- drivers/net/ntnic/meson.build | 24 + .../ntconnect/include/ntconn_mod_helper.h | 97 ++ .../net/ntnic/ntconnect/include/ntconnect.h | 96 ++ .../ntnic/ntconnect/include/ntconnect_api.h | 87 ++ .../ntconnect/include/ntconnect_api_adapter.h | 221 +++ .../ntconnect/include/ntconnect_api_flow.h | 168 +++ .../ntconnect/include/ntconnect_api_meter.h | 89 ++ .../include/ntconnect_api_statistic.h | 173 +++ .../ntconnect/include/ntconnect_api_test.h | 18 + drivers/net/ntnic/ntconnect/ntconn_server.c | 97 ++ drivers/net/ntnic/ntconnect/ntconnect.c | 641 ++++++++ .../ntnic/ntconnect_modules/ntconn_adapter.c | 775 ++++++++++ .../net/ntnic/ntconnect_modules/ntconn_flow.c | 1312 +++++++++++++++++ .../ntnic/ntconnect_modules/ntconn_meter.c | 517 +++++++ .../ntnic/ntconnect_modules/ntconn_modules.h | 19 + .../net/ntnic/ntconnect_modules/ntconn_stat.c | 877 +++++++++++ .../net/ntnic/ntconnect_modules/ntconn_test.c | 146 ++ 17 files changed, 5357 insertions(+) create mode 100644 drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect.h create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api.h create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h create mode 100644 drivers/net/ntnic/ntconnect/ntconn_server.c create mode 100644 drivers/net/ntnic/ntconnect/ntconnect.c create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_flow.c create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_meter.c create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_modules.h create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_stat.c create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_test.c diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build index f7454ffb79..ee8cf982ef 100644 --- a/drivers/net/ntnic/meson.build +++ b/drivers/net/ntnic/meson.build @@ -7,6 +7,22 @@ if not is_linux or not dpdk_conf.has('RTE_ARCH_X86_64') subdir_done() endif +# config object +ntnic_conf = configuration_data() + +# transfer options into config object +ntnic_conf.set('NT_TOOLS', true) + +# check option 'debug' (boolean flag derived from meson buildtype) +if get_option('debug') + cflags += '-DDEBUG' +endif + +# check nt_tools build option +if ntnic_conf.get('NT_TOOLS') + cflags += '-DNT_TOOLS' +endif + # includes includes = [ include_directories('.'), @@ -20,6 +36,7 @@ includes = [ include_directories('nthw/supported'), include_directories('nthw/flow_api'), include_directories('nthw/flow_filter'), + include_directories('ntconnect/include'), include_directories('sensors'), include_directories('sensors/avr_sensors'), include_directories('sensors/board_sensors'), @@ -41,6 +58,13 @@ sources = files( 'nim/nt_link_speed.c', 'nim/qsfp_sensors.c', 'nim/sfp_sensors.c', + 'ntconnect/ntconn_server.c', + 'ntconnect/ntconnect.c', + 'ntconnect_modules/ntconn_adapter.c', + 'ntconnect_modules/ntconn_flow.c', + 'ntconnect_modules/ntconn_meter.c', + 'ntconnect_modules/ntconn_stat.c', + 'ntconnect_modules/ntconn_test.c', 'nthw/core/nthw_clock_profiles.c', 'nthw/core/nthw_fpga.c', 'nthw/core/nthw_fpga_nt200a0x.c', diff --git a/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h b/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h new file mode 100644 index 0000000000..f55c4141cc --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONN_MOD_HELPER_H_ +#define _NTCONN_MOD_HELPER_H_ + +#include "ntconnect.h" + +/* + * Module parameter function call tree structures + */ +struct func_s { + const char *param; + struct func_s *sub_funcs; + int (*func)(void *hdl, int client_fd, struct ntconn_header_s *hdr, + char **data, int *len); +}; + +static inline int ntconn_error(char **data, int *len, const char *module, + enum ntconn_err_e err_code) +{ + *len = 0; + if (data) { + const ntconn_err_t *ntcerr = get_ntconn_error(err_code); + *data = malloc(4 + strlen(module) + 1 + + strlen(ntcerr->err_text) + 1); + if (*data) { + sprintf(*data, "----%s:%s", module, ntcerr->err_text); + *len = strlen(*data) + 1; /* add 0 - terminator */ + *(uint32_t *)*data = (uint32_t)ntcerr->err_code; + } + } + return REQUEST_ERR; +} + +static inline int ntconn_reply_status(char **data, int *len, + enum ntconn_reply_code_e code) +{ + *len = 0; + if (data) { + *data = malloc(sizeof(uint32_t)); + if (*data) { + *len = sizeof(uint32_t); + *(uint32_t *)*data = (uint32_t)code; + } + } + return REQUEST_OK; +} + +static inline int execute_function(const char *module, void *hdl, int client_id, + struct ntconn_header_s *hdr, char *function, + struct func_s *func_list, char **data, + int *len, int recur_depth) +{ + char *tok = strtok(function, ","); + + if (!tok) { + if (recur_depth == 0) + return ntconn_error(data, len, module, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + else + return ntconn_error(data, len, module, + NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE); + } + + hdr->len -= strlen(tok) + 1; + char *sub_funcs = function + strlen(tok) + 1; + int idx = 0; + + while (func_list[idx].param) { + if (strcmp(func_list[idx].param, tok) == 0) { + /* hit */ + if (func_list[idx].sub_funcs) { + return execute_function(module, hdl, client_id, + hdr, sub_funcs, + func_list[idx].sub_funcs, + data, len, + ++recur_depth); + } else if (func_list[idx].func) { + /* commands/parameters for function in text, zero-terminated */ + *data = sub_funcs; + return func_list[idx].func(hdl, client_id, hdr, + data, len); + } else { + return ntconn_error(data, len, module, + NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR); + } + } + idx++; + } + /* no hits */ + return ntconn_error(data, len, module, + NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED); +} + +#endif /* _NTCONN_MOD_HELPER_H_ */ diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect.h b/drivers/net/ntnic/ntconnect/include/ntconnect.h new file mode 100644 index 0000000000..9dcf2ec0a1 --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconnect.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONNECT_H_ +#define _NTCONNECT_H_ + +#include +#include +#include + +#include "ntconnect_api.h" + +#define REQUEST_OK 0 +#define REQUEST_ERR -1 + +typedef struct ntconn_api_s { + /* + * Name specifying this module. This name is used in the request string + */ + const char *module; + /* + * The version of this module integration + */ + uint32_t version_major; + uint32_t version_minor; + /* + * The request function: + * hdl : pointer to the context of this instance. + * client_id : identifying the client. To be able to manage client specific data/state. + * function : pointer to the remainder of the request command (Layer 3). May be modified. + * an example: ;adapter;get,interface,port0,link_speed function will + * then be 'get,interface,port0,link_speed'. + * hdr : header for length of command string and length of binary blop. + * The command string will start at "*data" and will have the length hdr->len. + * The binary blob will start at "&(*data)[hdr->len]" and will have the length + * hdr->blob_len. + * data : pointer to the resulting data. Typically this will be allocated. + * len : length of the data in the reply. + * + * return : REQUEST_OK on success, REQUEST_ERR on failure. On failure, the data and len + * can contain an describing error text + */ + int (*request)(void *hdl, int client_id, struct ntconn_header_s *hdr, + char *function, char **data, int *len); + /* + * After each request call, and when 'len' returns > 0, this function is called + * after sending reply to client. + * hdl : pointer to the context of this instance. + * data : the data pointer returned in the request function + */ + void (*free_data)(void *hdl, char *data); + /* + * Clean up of client specific data allocations. Called when client disconnects from server + * hdl : pointer to the context of this instance. + * client_id : identifying the client. + */ + void (*client_cleanup)(void *hdl, int client_id); +} ntconnapi_t; + +/* + * ntconn error + */ +typedef struct ntconn_err_s { + uint32_t err_code; + const char *err_text; +} ntconn_err_t; + +const ntconn_err_t *get_ntconn_error(enum ntconn_err_e err_code); + +typedef struct ntconn_mod_s { + void *hdl; + struct pci_id_s addr; + const ntconnapi_t *op; + + pthread_mutex_t mutex; + struct ntconn_mod_s *next; +} ntconn_mod_t; + +struct ntconn_server_s { + int serv_fd; + int running; + /* + * list of different pci_ids registered aka SmartNICs + */ + struct pci_id_s pci_id_list[MAX_PCI_IDS]; /* 0 - terminates */ + cpu_set_t cpuset; +}; + +int ntconn_server_register(void *server); + +int register_ntconn_mod(const struct rte_pci_addr *addr, void *hdl, + const ntconnapi_t *op); +int ntconnect_init(const char *sockname, cpu_set_t cpuset); + +#endif /* _NTCONNECT_H_ */ diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api.h new file mode 100644 index 0000000000..14668bf2ee --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONNECT_API_H_ +#define _NTCONNECT_API_H_ + +#include "stdint.h" +/* + * NtConnect API + */ + +#define NTCONNECT_SOCKET "/var/run/ntconnect/ntconnect.sock" + +enum ntconn_err_e { + NTCONN_ERR_CODE_NONE = 0U, + NTCONN_ERR_CODE_INTERNAL_ERROR, + NTCONN_ERR_CODE_INVALID_REQUEST, + NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR, + NTCONN_ERR_CODE_NO_DATA, + NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM, + NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE, + NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR, + NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED, +}; + +enum ntconn_reply_code_e { + NTCONN_ADAPTER_ERR_PORT_STATE_FAIL = 0U, + NTCONN_ADAPTER_ERR_WRONG_LINK_STATE, + NTCONN_ADAPTER_ERR_TX_POWER_FAIL, +}; + +enum { + NTCONN_TAG_NONE, + NTCONN_TAG_REQUEST, + NTCONN_TAG_REPLY, + NTCONN_TAG_ERROR +}; + +#define MESSAGE_BUFFER 256 +#define MAX_ERR_MESSAGE_LENGTH 256 + +struct reply_err_s { + enum ntconn_err_e err_code; + char msg[MAX_ERR_MESSAGE_LENGTH]; +}; + +#define NTCMOD_HDR_LEN sizeof(struct ntconn_header_s) +struct ntconn_header_s { + uint16_t tag; + uint16_t len; + uint32_t blob_len; +}; + +struct pci_id_s { + union { + uint64_t pci_id; + struct { + uint32_t domain; + uint8_t bus; + uint8_t devid; + uint8_t function; + uint8_t pad; + }; + }; +}; + +#define VERSION_HI(version) ((unsigned int)((version) >> 32)) +#define VERSION_LO(version) ((unsigned int)((version) & 0xffffffff)) + +/* + * Binary interface description for ntconnect module replies + */ + +/* + * server get,nic_pci_ids + */ +#define MAX_PCI_IDS 16 +#define NICS_PCI_ID_LEN 12 + +struct ntc_nic_pci_ids_s { + char nic_pci_id[MAX_PCI_IDS][NICS_PCI_ID_LEN + 1]; + int num_nics; +}; + +#endif /* _NTCONNECT_API_H_ */ diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h new file mode 100644 index 0000000000..affe905027 --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONNECT_API_ADAPTER_H_ +#define _NTCONNECT_API_ADAPTER_H_ + +/* + * adapter get,interfaces + */ +enum port_speed { + PORT_LINK_SPEED_UNKNOWN, + PORT_LINK_SPEED_NONE_REPORTED, + PORT_LINK_SPEED_10M, + PORT_LINK_SPEED_100M, + PORT_LINK_SPEED_1G, + PORT_LINK_SPEED_10G, + PORT_LINK_SPEED_25G, + PORT_LINK_SPEED_40G, + PORT_LINK_SPEED_50G, + PORT_LINK_SPEED_100G, +}; + +enum port_states { + PORT_STATE_DISABLED, + PORT_STATE_NIM_PRESENT, + PORT_STATE_NIM_ABSENT, + PORT_STATE_VIRTUAL_UNATTACHED, + PORT_STATE_VIRTUAL_SPLIT, + PORT_STATE_VIRTUAL_PACKED, + PORT_STATE_VIRTUAL_RELAY, +}; + +enum port_link { PORT_LINK_UNKNOWN, PORT_LINK_UP, PORT_LINK_DOWN }; + +enum port_type { + PORT_TYPE_PHY_NORMAL, /* Normal phy port (no LAG) */ + /* Link aggregated phy port in active/active LAG configuration */ + PORT_TYPE_PHY_LAG_ACTIVE_ACTIVE, + PORT_TYPE_PHY_LAG_PRIMARY, /* Primary phy port in active/backup LAG configuration */ + PORT_TYPE_PHY_LAG_BACKUP, /* Backup phy port in active/backup LAG configuration */ + PORT_TYPE_VIRT, + PORT_TYPE_LAST +}; + +enum nim_identifier_e { + NIM_UNKNOWN = 0x00, /* Nim type is unknown */ + NIM_GBIC = 0x01, /* Nim type = GBIC */ + NIM_FIXED = 0x02, /* Nim type = FIXED */ + NIM_SFP_SFP_PLUS = 0x03, /* Nim type = SFP/SFP+ */ + NIM_300_PIN_XBI = 0x04, /* Nim type = 300 pin XBI */ + NIM_XEN_PAK = 0x05, /* Nim type = XEN-PAK */ + NIM_XFP = 0x06, /* Nim type = XFP */ + NIM_XFF = 0x07, /* Nim type = XFF */ + NIM_XFP_E = 0x08, /* Nim type = XFP-E */ + NIM_XPAK = 0x09, /* Nim type = XPAK */ + NIM_X2 = 0x0A, /* Nim type = X2 */ + NIM_DWDM = 0x0B, /* Nim type = DWDM */ + NIM_QSFP = 0x0C, /* Nim type = QSFP */ + NIM_QSFP_PLUS = 0x0D, /* Nim type = QSFP+ */ + NIM_QSFP28 = 0x11, /* Nim type = QSFP28 */ + NIM_CFP4 = 0x12, /* Nim type = CFP4 */ +}; + +/* + * Port types + */ +enum port_type_e { + PORT_TYPE_NOT_AVAILABLE = + 0, /* The NIM/port type is not available (unknown) */ + PORT_TYPE_NOT_RECOGNISED, /* The NIM/port type not recognized */ + PORT_TYPE_RJ45, /* RJ45 type */ + PORT_TYPE_SFP_NOT_PRESENT, /* SFP type but slot is empty */ + PORT_TYPE_SFP_SX, /* SFP SX */ + PORT_TYPE_SFP_SX_DD, /* SFP SX digital diagnostic */ + PORT_TYPE_SFP_LX, /* SFP LX */ + PORT_TYPE_SFP_LX_DD, /* SFP LX digital diagnostic */ + PORT_TYPE_SFP_ZX, /* SFP ZX */ + PORT_TYPE_SFP_ZX_DD, /* SFP ZX digital diagnostic */ + PORT_TYPE_SFP_CU, /* SFP copper */ + PORT_TYPE_SFP_CU_DD, /* SFP copper digital diagnostic */ + PORT_TYPE_SFP_NOT_RECOGNISED, /* SFP unknown */ + PORT_TYPE_XFP, /* XFP */ + PORT_TYPE_XPAK, /* XPAK */ + PORT_TYPE_SFP_CU_TRI_SPEED, /* SFP copper tri-speed */ + PORT_TYPE_SFP_CU_TRI_SPEED_DD, /* SFP copper tri-speed digital diagnostic */ + PORT_TYPE_SFP_PLUS, /* SFP+ type */ + PORT_TYPE_SFP_PLUS_NOT_PRESENT, /* SFP+ type but slot is empty */ + PORT_TYPE_XFP_NOT_PRESENT, /* XFP type but slot is empty */ + PORT_TYPE_QSFP_PLUS_NOT_PRESENT, /* QSFP type but slot is empty */ + PORT_TYPE_QSFP_PLUS, /* QSFP type */ + PORT_TYPE_SFP_PLUS_PASSIVE_DAC, /* SFP+ Passive DAC */ + PORT_TYPE_SFP_PLUS_ACTIVE_DAC, /* SFP+ Active DAC */ + PORT_TYPE_CFP4, /* CFP4 type */ + PORT_TYPE_CFP4_LR4 = PORT_TYPE_CFP4, /* CFP4 100G, LR4 type */ + PORT_TYPE_CFP4_NOT_PRESENT, /* CFP4 type but slot is empty */ + PORT_TYPE_INITIALIZE, /* The port type is not fully established yet */ + PORT_TYPE_NIM_NOT_PRESENT, /* Generic "Not present" */ + PORT_TYPE_HCB, /* Test mode: Host Compliance Board */ + PORT_TYPE_NOT_SUPPORTED, /* The NIM type is not supported in this context */ + PORT_TYPE_SFP_PLUS_DUAL_RATE, /* SFP+ supports 1G/10G */ + PORT_TYPE_CFP4_SR4, /* CFP4 100G, SR4 type */ + PORT_TYPE_QSFP28_NOT_PRESENT, /* QSFP28 type but slot is empty */ + PORT_TYPE_QSFP28, /* QSFP28 type */ + PORT_TYPE_QSFP28_SR4, /* QSFP28-SR4 type */ + PORT_TYPE_QSFP28_LR4, /* QSFP28-LR4 type */ + /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */ + PORT_TYPE_QSFP_PLUS_4X10, + /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */ + PORT_TYPE_QSFP_PASSIVE_DAC_4X10, + PORT_TYPE_QSFP_PASSIVE_DAC = + PORT_TYPE_QSFP_PASSIVE_DAC_4X10, /* QSFP passive DAC type */ + /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */ + PORT_TYPE_QSFP_ACTIVE_DAC_4X10, + PORT_TYPE_QSFP_ACTIVE_DAC = + PORT_TYPE_QSFP_ACTIVE_DAC_4X10, /* QSFP active DAC type */ + PORT_TYPE_SFP_28, /* SFP28 type */ + PORT_TYPE_SFP_28_SR, /* SFP28-SR type */ + PORT_TYPE_SFP_28_LR, /* SFP28-LR type */ + PORT_TYPE_SFP_28_CR_CA_L, /* SFP28-CR-CA-L type */ + PORT_TYPE_SFP_28_CR_CA_S, /* SFP28-CR-CA-S type */ + PORT_TYPE_SFP_28_CR_CA_N, /* SFP28-CR-CA-N type */ + PORT_TYPE_QSFP28_CR_CA_L, /* QSFP28-CR-CA-L type */ + PORT_TYPE_QSFP28_CR_CA_S, /* QSFP28-CR-CA-S type */ + PORT_TYPE_QSFP28_CR_CA_N, /* QSFP28-CR-CA-N type */ + PORT_TYPE_SFP_28_SR_DR, /* SFP28-SR-DR type */ + PORT_TYPE_SFP_28_LR_DR, /* SFP28-LR-DR type */ + PORT_TYPE_SFP_FX, /* SFP FX */ + PORT_TYPE_SFP_PLUS_CU, /* SFP+ CU type */ + PORT_TYPE_QSFP28_FR, /* QSFP28-FR type. Uses PAM4 modulation on one lane only */ + PORT_TYPE_QSFP28_DR, /* QSFP28-DR type. Uses PAM4 modulation on one lane only */ + PORT_TYPE_QSFP28_LR, /* QSFP28-LR type. Uses PAM4 modulation on one lane only */ +}; + +struct mac_addr_s { + uint8_t addr_b[6]; +}; + +struct nim_link_length_s { + /* NIM link length (in meters) supported SM (9um). A value of 0xFFFF indicates that the + * length is >= 65535 m + */ + uint16_t sm; + uint16_t ebw; /* NIM link length (in meters) supported EBW (50um) */ + uint16_t mm50; /* NIM link length (in meters) supported MM (50um) */ + uint16_t mm62; /* NIM link length (in meters) supported MM (62.5um) */ + uint16_t copper; /* NIM link length (in meters) supported copper */ +}; + +struct nim_data_s { + uint8_t nim_id; + uint8_t port_type; + char vendor_name[17]; + char prod_no[17]; + char serial_no[17]; + char date[9]; + char rev[5]; + uint8_t pwr_level_req; + uint8_t pwr_level_cur; + struct nim_link_length_s link_length; +}; + +struct sensor { + uint8_t sign; + uint8_t type; + uint32_t current_value; + uint32_t min_value; + uint32_t max_value; + char name[50]; +}; + +struct ntc_sensors_s { + uint16_t adapter_sensors_cnt; + uint16_t ports_cnt; + uint16_t nim_sensors_cnt[8]; + char adapter_name[24]; +}; + +#define MAX_RSS_QUEUES 128 + +enum queue_dir_e { QUEUE_INPUT, QUEUE_OUTPUT }; + +struct queue_s { + enum queue_dir_e dir; + int idx; +}; + +struct ntc_interface_s { + uint8_t port_id; + enum port_type type; + enum port_link link; + enum port_states port_state; + enum port_speed port_speed; + struct pci_id_s pci_id; + struct mac_addr_s mac; + struct nim_data_s nim_data; + uint16_t mtu; + /* attached queues */ + struct { + struct queue_s queue[MAX_RSS_QUEUES]; + int num_queues; + }; +}; + +/* + * adapter get,interfaces + */ +struct ntc_interfaces_s { + int final_list; + uint8_t nb_ports; + struct ntc_interface_s intf[]; +}; + +/* + * adapter get,info + */ +struct ntc_adap_get_info_s { + char *fw_version[32]; +}; + +#endif /* _NTCONNECT_API_ADAPTER_H_ */ diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h new file mode 100644 index 0000000000..4091d61d7d --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONNECT_API_FILTER_H_ +#define _NTCONNECT_API_FILTER_H_ + +#include "stream_binary_flow_api.h" + +/* + * Create structures allocating the space to carry through ntconnect interface + */ +#define MAX_FLOW_STREAM_ELEM 16 +#define MAX_FLOW_STREAM_QUERY_DATA 1024 +#define MAX_FLOW_STREAM_ERROR_MSG 128 +#define MAX_FLOW_STREAM_VXLAN_TUN_ELEM 8 +#define MAX_FLOW_STREAM_COUNT_ACTIONS 4 + +#define MAX_PATH_LEN 128 + +enum ntconn_flow_err_e { + NTCONN_FLOW_ERR_NONE = 0, + NTCONN_FLOW_ERR_INTERNAL_ERROR = 0x100, + NTCONN_FLOW_ERR_PORT_IS_NOT_INITIALIZED, + NTCONN_FLOW_ERR_INVALID_PORT, + NTCONN_FLOW_ERR_UNEXPECTED_VIRTIO_PATH, + NTCONN_FLOW_ERR_UNSUPPORTED_ADAPTER, + NTCONN_FLOW_ERR_TO_MANY_FLOWS, + NTCONN_FLOW_ERR_NOT_YET_IMPLEMENTED, + NTCONN_FLOW_ERR_NO_VF_QUEUES, +}; + +struct flow_elem_types_s { + int valid; + union { + int start_addr; + struct flow_elem_eth eth; + struct flow_elem_vlan vlan[2]; + struct flow_elem_ipv4 ipv4; + struct flow_elem_ipv6 ipv6; + struct flow_elem_sctp sctp; + struct flow_elem_tcp tcp; + struct flow_elem_udp udp; + struct flow_elem_icmp icmp; + struct flow_elem_vxlan vxlan; + struct flow_elem_port_id port_id; + struct flow_elem_tag tag; + } u; +}; + +struct flow_elem_cpy { + enum flow_elem_type type; /* element type */ + struct flow_elem_types_s spec_cpy; + struct flow_elem_types_s mask_cpy; +}; + +struct flow_action_vxlan_encap_cpy { + /* Encapsulating vxlan tunnel definition */ + struct flow_elem_cpy vxlan_tunnel[MAX_FLOW_STREAM_VXLAN_TUN_ELEM]; +}; + +struct flow_action_rss_cpy { + struct flow_action_rss rss; + uint16_t cpy_queue[FLOW_MAX_QUEUES]; +}; + +#define MAX_ACTION_ENCAP_DATA 512 +struct flow_action_decap_cpy { + uint8_t data[MAX_ACTION_ENCAP_DATA]; + size_t size; + struct flow_elem_cpy item_cpy + [RAW_ENCAP_DECAP_ELEMS_MAX]; /* Need room for end command */ + int item_count; +}; + +struct flow_action_encap_cpy { + uint8_t data[MAX_ACTION_ENCAP_DATA]; + size_t size; + struct flow_elem_cpy item_cpy + [RAW_ENCAP_DECAP_ELEMS_MAX]; /* Need room for end command */ + int item_count; +}; + +struct flow_action_types_s { + int valid; + union { + int start_addr; + struct flow_action_rss_cpy rss; + struct flow_action_push_vlan vlan; + struct flow_action_set_vlan_vid vlan_vid; + struct flow_action_vxlan_encap_cpy vxlan; + struct flow_action_count count; + struct flow_action_mark mark; + struct flow_action_port_id port_id; + struct flow_action_tag tag; + struct flow_action_queue queue; + struct flow_action_decap_cpy decap; + struct flow_action_encap_cpy encap; + struct flow_action_jump jump; + struct flow_action_meter meter; + } u; +}; + +struct flow_action_cpy { + enum flow_action_type type; + struct flow_action_types_s conf_cpy; +}; + +struct query_flow_ntconnect { + uint8_t port; + struct flow_action_cpy action; + uint64_t flow; +}; + +struct create_flow_ntconnect { + uint8_t port; + uint8_t vport; + struct flow_attr attr; + struct flow_elem_cpy elem[MAX_FLOW_STREAM_ELEM]; + struct flow_action_cpy action[MAX_FLOW_STREAM_ELEM]; +}; + +struct destroy_flow_ntconnect { + uint8_t port; + uint64_t flow; +}; + +#define ERR_MSG_LEN 128LLU + +struct flow_setport_return { + struct flow_queue_id_s queues[FLOW_MAX_QUEUES]; + uint8_t num_queues; +}; + +struct flow_error_return_s { + enum flow_error_e type; + char err_msg[ERR_MSG_LEN]; + int status; +}; + +struct create_flow_return_s { + uint64_t flow; +}; + +struct validate_flow_return_s { + int status; +}; + +struct query_flow_return_s { + enum flow_error_e type; + char err_msg[ERR_MSG_LEN]; + int status; + uint32_t data_length; + uint8_t data[]; +}; + +struct flow_return_s { + enum flow_error_e type; + char err_msg[ERR_MSG_LEN]; + int status; +}; + +struct flow_error_ntconn { + enum flow_error_e type; + char message[ERR_MSG_LEN]; +}; + +#endif /* _NTCONNECT_API_FILTER_H_ */ diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h new file mode 100644 index 0000000000..901b0ccba1 --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONNECT_METER_FILTER_H_ +#define _NTCONNECT_METER_FILTER_H_ + +#define FLOW_COOKIE 0x12344321 + +/* + * Create structures allocating the space to carry through ntconnect interface + */ + +#define MAX_PATH_LEN 128 + +enum ntconn_meter_err_e { + NTCONN_METER_ERR_NONE = 0, + NTCONN_METER_ERR_INTERNAL_ERROR = 0x100, + NTCONN_METER_ERR_INVALID_PORT, + NTCONN_METER_ERR_UNEXPECTED_VIRTIO_PATH, + NTCONN_METER_ERR_PROFILE_ID, + NTCONN_METER_ERR_POLICY_ID, + NTCONN_METER_ERR_METER_ID, +}; + +enum ntconn_meter_command_e { + UNKNOWN_CMD, + ADD_PROFILE, + DEL_PROFILE, + ADD_POLICY, + DEL_POLICY, + CREATE_MTR, + DEL_MTR +}; + +#define ERR_MSG_LEN 128LLU + +struct meter_error_return_s { + enum rte_mtr_error_type type; + int status; + char err_msg[ERR_MSG_LEN]; +}; + +struct meter_setup_s { + uint8_t vport; + uint32_t id; + int shared; + union { + struct rte_mtr_meter_profile profile; + struct { + struct rte_mtr_meter_policy_params policy; + struct rte_flow_action actions_green[2]; + struct rte_flow_action actions_yellow[2]; + struct rte_flow_action actions_red[2]; + } p; + struct rte_mtr_params mtr_params; + }; +}; + +struct meter_get_stat_s { + uint8_t vport; + uint32_t mtr_id; + int clear; +}; + +struct meter_return_stat_s { + struct rte_mtr_stats stats; + uint64_t stats_mask; +}; + +struct meter_setup_ptr_s { + uint32_t id; + int shared; + union { + struct rte_mtr_meter_profile *profile; + struct rte_mtr_meter_policy_params *policy; + struct rte_mtr_params *mtr_params; + }; +}; + +struct meter_return_s { + int status; +}; + +struct meter_capabilities_return_s { + struct rte_mtr_capabilities cap; +}; + +#endif /* _NTCONNECT_METER_FILTER_H_ */ diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h new file mode 100644 index 0000000000..1022bc2056 --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONNECT_API_STATISTIC_H_ +#define _NTCONNECT_API_STATISTIC_H_ + +/* + * Supported defined statistic records for Stat layout version 6 - defined in nthw_stat module + */ +#define NUM_STAT_RECORD_TYPE_COLOR \ + (sizeof(struct color_type_fields_s) / sizeof(uint64_t)) +struct color_type_fields_s { + uint64_t pkts; + uint64_t octets; + uint64_t tcp_flgs; +}; + +#define NUM_STAT_RECORD_TYPE_FLOWMATCHER \ + (sizeof(struct flowmatcher_type_fields_s) / sizeof(uint64_t)) +struct flowmatcher_type_fields_s { + /* FLM 0.17 */ + uint64_t current; + uint64_t learn_done; + uint64_t learn_ignore; + uint64_t learn_fail; + uint64_t unlearn_done; + uint64_t unlearn_ignore; + uint64_t auto_unlearn_done; + uint64_t auto_unlearn_ignore; + uint64_t auto_unlearn_fail; + uint64_t timeout_unlearn_done; + uint64_t rel_done; + uint64_t rel_ignore; + uint64_t prb_done; + uint64_t prb_ignore; + /* FLM 0.20 */ + uint64_t sta_done; + uint64_t inf_done; + uint64_t inf_skip; + uint64_t pck_hit; + uint64_t pck_miss; + uint64_t pck_unh; + uint64_t pck_dis; + uint64_t csh_hit; + uint64_t csh_miss; + uint64_t csh_unh; + uint64_t cuc_start; + uint64_t cuc_move; +}; + +#define NUM_STAT_RECORD_TYPE_QUEUE \ + (sizeof(struct queue_type_fields_s) / sizeof(uint64_t)) +struct queue_type_fields_s { + uint64_t flush_pkts; + uint64_t drop_pkts; + uint64_t fwd_pkts; + uint64_t dbs_drop_pkts; + uint64_t flush_octets; + uint64_t drop_octets; + uint64_t fwd_octets; + uint64_t dbs_drop_octets; +}; + +/* + * Port stat counters for virtualization NICS with virtual ports support + */ +#define NUM_STAT_RECORD_TYPE_RX_PORT_VIRT \ + (sizeof(struct rtx_type_fields_virt_s) / sizeof(uint64_t)) +/* same for Rx and Tx counters on Virt */ +#define NUM_STAT_RECORD_TYPE_TX_PORT_VIRT NUM_STAT_RECORD_TYPE_RX_PORT_VIRT +struct rtx_type_fields_virt_s { + uint64_t octets; + uint64_t pkts; + uint64_t drop_events; + uint64_t qos_drop_octets; + uint64_t qos_drop_pkts; +}; + +/* + * Port RMON counters for Cap devices + */ +struct stat_rmon_s { + /* Sums that are calculated by software */ + uint64_t drop_events; + uint64_t pkts; + /* Read from FPGA */ + uint64_t octets; + uint64_t broadcast_pkts; + uint64_t multicast_pkts; + uint64_t unicast_pkts; + uint64_t pkts_alignment; + uint64_t pkts_code_violation; + uint64_t pkts_crc; + uint64_t undersize_pkts; + uint64_t oversize_pkts; + uint64_t fragments; + uint64_t jabbers_not_truncated; + uint64_t jabbers_truncated; + uint64_t pkts_64_octets; + uint64_t pkts_65_to_127_octets; + uint64_t pkts_128_to_255_octets; + uint64_t pkts_256_to_511_octets; + uint64_t pkts_512_to_1023_octets; + uint64_t pkts_1024_to_1518_octets; + uint64_t pkts_1519_to_2047_octets; + uint64_t pkts_2048_to_4095_octets; + uint64_t pkts_4096_to_8191_octets; + uint64_t pkts_8192_to_max_octets; +}; + +#define NUM_STAT_RECORD_TYPE_RX_PORT_CAP \ + (sizeof(struct rx_type_fields_cap_s) / sizeof(uint64_t)) +struct rx_type_fields_cap_s { + struct stat_rmon_s rmon; + uint64_t mac_drop_events; + uint64_t pkts_lr; + /* Rx only port counters */ + uint64_t duplicate; + uint64_t pkts_ip_chksum_error; + uint64_t pkts_udp_chksum_error; + uint64_t pkts_tcp_chksum_error; + uint64_t pkts_giant_undersize; + uint64_t pkts_baby_giant; + uint64_t pkts_not_isl_vlan_mpls; + uint64_t pkts_isl; + uint64_t pkts_vlan; + uint64_t pkts_isl_vlan; + uint64_t pkts_mpls; + uint64_t pkts_isl_mpls; + uint64_t pkts_vlan_mpls; + uint64_t pkts_isl_vlan_mpls; + uint64_t pkts_no_filter; + uint64_t pkts_dedup_drop; + uint64_t pkts_filter_drop; + uint64_t pkts_overflow; + uint64_t pkts_dbs_drop; + uint64_t octets_no_filter; + uint64_t octets_dedup_drop; + uint64_t octets_filter_drop; + uint64_t octets_overflow; + uint64_t octets_dbs_drop; + uint64_t ipft_first_hit; + uint64_t ipft_first_not_hit; + uint64_t ipft_mid_hit; + uint64_t ipft_mid_not_hit; + uint64_t ipft_last_hit; + uint64_t ipft_last_not_hit; +}; + +#define NUM_STAT_RECORD_TYPE_TX_PORT_CAP \ + (sizeof(struct tx_type_fields_cap_s) / sizeof(uint64_t)) +struct tx_type_fields_cap_s { + struct stat_rmon_s rmon; +}; + +/* + * stat get,colors + * stat get,queues + * stat get,rx_counters + * stat get,tx_counters + */ +#define STAT_INFO_ELEMENTS \ + (sizeof(struct ntc_stat_get_data_s) / sizeof(uint64_t)) + +struct ntc_stat_get_data_s { + uint64_t nb_counters; + uint64_t timestamp; + uint64_t is_virt; + uint64_t data[]; +}; + +#endif /* _NTCONNECT_API_STATISTIC_H_ */ diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h new file mode 100644 index 0000000000..44cacbd931 --- /dev/null +++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONNECT_TEST_FILTER_H_ +#define _NTCONNECT_TEST_FILTER_H_ + +/* + * Create structures allocating the space to carry through ntconnect interface + */ + +struct test_s { + int number; + int status; + uint64_t test[]; +}; + +#endif /* _NTCONNECT_TEST_FILTER_H_ */ diff --git a/drivers/net/ntnic/ntconnect/ntconn_server.c b/drivers/net/ntnic/ntconnect/ntconn_server.c new file mode 100644 index 0000000000..34a3c19955 --- /dev/null +++ b/drivers/net/ntnic/ntconnect/ntconn_server.c @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include + +#include "ntconnect.h" +#include "ntconn_mod_helper.h" +#include "nt_util.h" +#include "ntlog.h" + +/* + * Server module always registered on 0000:00:00.0 + */ +#define this_module_name "server" + +#define NTCONNECT_SERVER_VERSION_MAJOR 0U +#define NTCONNECT_SERVER_VERSION_MINOR 1U + +static int func_get_nic_pci(void *hdl, int client_fd, + struct ntconn_header_s *hdr, char **data, int *len); +static struct func_s funcs_get_level1[] = { + { "nic_pci_ids", NULL, func_get_nic_pci }, + { NULL, NULL, NULL }, +}; + +/* + * Entry level + */ +static struct func_s server_entry_funcs[] = { + { "get", funcs_get_level1, NULL }, + { NULL, NULL, NULL }, +}; + +static int func_get_nic_pci(void *hdl, int client_fd _unused, + struct ntconn_header_s *hdr _unused, char **data, + int *len) +{ + struct ntconn_server_s *serv = (struct ntconn_server_s *)hdl; + struct ntc_nic_pci_ids_s *npci = + calloc(1, sizeof(struct ntc_nic_pci_ids_s)); + if (!npci) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + int i = 0; + + while (i < MAX_PCI_IDS && serv->pci_id_list[i].pci_id) { + sprintf(npci->nic_pci_id[i], "%04x:%02x:%02x.%x", + serv->pci_id_list[i].domain & 0xffff, + serv->pci_id_list[i].bus, serv->pci_id_list[i].devid, + serv->pci_id_list[i].function); + i++; + } + npci->num_nics = i; + *data = (char *)npci; + *len = sizeof(struct ntc_nic_pci_ids_s); + + return REQUEST_OK; +} + +static int ntconn_server_request(void *hdl, int client_id, + struct ntconn_header_s *hdr, char *function, + char **data, int *len) +{ + return execute_function(this_module_name, hdl, client_id, hdr, function, + server_entry_funcs, data, len, 0); +} + +static void ntconn_server_free_data(void *hdl _unused, char *data) +{ + if (data) { +#ifdef DEBUG + NT_LOG(DBG, NTCONNECT, "server free data\n"); +#endif + free(data); + } +} + +static const ntconnapi_t ntconn_server_op = { this_module_name, + NTCONNECT_SERVER_VERSION_MAJOR, + NTCONNECT_SERVER_VERSION_MINOR, + ntconn_server_request, + ntconn_server_free_data, + NULL + }; + +int ntconn_server_register(void *server) +{ + const struct rte_pci_addr addr = { + .domain = 0, .bus = 0, .devid = 0, .function = 0 + }; + + return register_ntconn_mod(&addr, server, &ntconn_server_op); +} diff --git a/drivers/net/ntnic/ntconnect/ntconnect.c b/drivers/net/ntnic/ntconnect/ntconnect.c new file mode 100644 index 0000000000..697e101a03 --- /dev/null +++ b/drivers/net/ntnic/ntconnect/ntconnect.c @@ -0,0 +1,641 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nt_util.h" +#include "ntconnect.h" +#include "ntconnect_api.h" +#include "ntlog.h" + +/* clang-format off */ +ntconn_err_t ntconn_err[] = { + {NTCONN_ERR_CODE_NONE, "Success"}, + {NTCONN_ERR_CODE_INTERNAL_ERROR, "Internal error"}, + {NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR, "Internal error in reply from module"}, + {NTCONN_ERR_CODE_NO_DATA, "No data found"}, + {NTCONN_ERR_CODE_INVALID_REQUEST, "Invalid request"}, + {NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED, "Function not yet implemented"}, + {NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR, "Internal error in function call list"}, + {NTCONN_ERR_CODE_MISSING_INVALID_PARAM, "Missing or invalid parameter"}, + {NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE, "Function parameter is incomplete"}, + {NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED, + "Function or parameter not recognized/supported"}, + {-1, NULL} +}; + +/* clang-format on */ + +static ntconn_mod_t *ntcmod_base; +static pthread_t tid; +static pthread_t ctid; +static struct ntconn_server_s ntconn_serv; + +const ntconn_err_t *get_ntconn_error(enum ntconn_err_e err_code) +{ + int idx = 0; + + while (ntconn_err[idx].err_code != (uint32_t)-1 && + ntconn_err[idx].err_code != err_code) + idx++; + if (ntconn_err[idx].err_code == (uint32_t)-1) + idx = 1; + + return &ntconn_err[idx]; +} + +int register_ntconn_mod(const struct rte_pci_addr *addr, void *hdl, + const ntconnapi_t *op) +{ + /* Verify and check module name is unique */ +#ifdef DEBUG + NT_LOG(DBG, NTCONNECT, + "Registering pci: %04x:%02x:%02x.%x, module %s\n", addr->domain, + addr->bus, addr->devid, addr->function, op->module); +#endif + + ntconn_mod_t *ntcmod = (ntconn_mod_t *)malloc(sizeof(ntconn_mod_t)); + + if (!ntcmod) { + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return -1; + } + ntcmod->addr.domain = addr->domain; + ntcmod->addr.bus = addr->bus; + ntcmod->addr.devid = addr->devid; + ntcmod->addr.function = addr->function; + ntcmod->addr.pad = 0; + + ntcmod->hdl = hdl; + ntcmod->op = op; + pthread_mutex_init(&ntcmod->mutex, NULL); + + ntcmod->next = ntcmod_base; + ntcmod_base = ntcmod; + + if (ntcmod->addr.pci_id) { /* Avoid server fake pci_id */ + int i; + + for (i = 0; i < MAX_PCI_IDS; i++) { + if (ntconn_serv.pci_id_list[i].pci_id == 0) { + NT_LOG(DBG, NTCONNECT, + "insert at index %i PCI ID %" PRIX64 "\n", i, + ntcmod->addr.pci_id); + ntconn_serv.pci_id_list[i].pci_id = + ntcmod->addr.pci_id; + break; + } else if (ntconn_serv.pci_id_list[i].pci_id == + ntcmod->addr.pci_id) + break; + } + } + + return 0; +} + +static int unix_build_address(const char *path, struct sockaddr_un *addr) +{ + if (addr == NULL || path == NULL) + return -1; + memset(addr, 0, sizeof(struct sockaddr_un)); + addr->sun_family = AF_UNIX; + if (strlen(path) < sizeof(addr->sun_path)) { + rte_strscpy(addr->sun_path, path, sizeof(addr->sun_path) - 1); + return 0; + } + return -1; +} + +#define STATUS_OK 0 +#define STATUS_INTERNAL_ERROR -1 +#define STATUS_TRYAGAIN -2 +#define STATUS_INVALID_PARAMETER -3 +#define STATUS_CONNECTION_CLOSED -4 +#define STATUS_CONNECTION_INVALID -5 +#define STATUS_TIMEOUT -6 + +static int read_data(int fd, size_t len, uint8_t *data, size_t *recv_len, + int timeout) +{ + struct pollfd pfd; + ssize_t ret; + + pfd.fd = fd; + pfd.events = POLLIN; + pfd.revents = 0; + + ret = poll(&pfd, 1, timeout); + if (ret < 0) { + if (errno == EINTR) + return STATUS_TRYAGAIN; /* Caught signal before timeout */ + if (errno == EINVAL) + return STATUS_INVALID_PARAMETER; /* Timeout is negative */ + if (errno == EFAULT) + return STATUS_INVALID_PARAMETER; /* Fds argument is illegal */ + /* else */ + assert(0); + return STATUS_INTERNAL_ERROR; + } + + if (ret == 0) + return STATUS_TIMEOUT; + + if (pfd.revents == 0) { + assert(ret == 1); + assert(0); /* Revents cannot be zero when NtSocket_Poll returns 1 */ + return STATUS_TRYAGAIN; + } + + if ((pfd.revents & POLLIN) && + ((pfd.revents & (POLLERR | POLLNVAL)) == 0)) { + ret = recv(pfd.fd, data, len, 0); + if (ret < 0) { + int lerrno = errno; + + if (lerrno == EWOULDBLOCK || lerrno == EAGAIN) { + /* + * We have data but if the very first read turns out to return + * EWOULDBLOCK or EAGAIN it means that the remote end has dropped + * the connection + */ + NT_LOG(DBG, NTCONNECT, + "The socket with fd %d has been closed by remote end. %d [%s]\n", + pfd.fd, lerrno, strerror(lerrno)); + return STATUS_CONNECTION_CLOSED; + } + if (lerrno != EINTR) { + NT_LOG(ERR, NTCONNECT, + "recv() from fd %d received errno %d [%s]\n", + pfd.fd, lerrno, strerror(lerrno)); + return STATUS_CONNECTION_INVALID; + } + /* EINTR */ + return STATUS_TRYAGAIN; + } + if (ret == 0) { + if (pfd.revents & POLLHUP) { + /* This means that we have read all data and the remote end has + * HUP + */ +#ifdef DEBUG + NT_LOG(DBG, NTCONNECT, + "The remote end has terminated the session\n"); +#endif + return STATUS_CONNECTION_CLOSED; + } + return STATUS_TRYAGAIN; + } + + /* Ret can only be positive at this point */ + *recv_len = (size_t)ret; + return STATUS_OK; + } + + if ((pfd.revents & POLLHUP) == POLLHUP) { + /* this means that the remote end has HUP */ + NT_LOG(DBG, NTCONNECT, + "The remote end has terminated the session\n"); + return STATUS_CONNECTION_CLOSED; + } + + NT_LOG(ERR, NTCONNECT, + "poll() returned 0x%x. Invalidating the connection\n", + pfd.revents); + return STATUS_CONNECTION_INVALID; +} + +static int read_all(int clfd, uint8_t *data, size_t length) +{ + size_t recv_len = 0; + size_t left = length; + size_t pos = 0; + + while (left > 0) { + int ret = read_data(clfd, left, &data[pos], &recv_len, -1); + + if (ret == STATUS_OK) { + pos += recv_len; + left -= recv_len; + } else { + if (ret == STATUS_CONNECTION_CLOSED || ret == STATUS_TIMEOUT) { + /* Silently return status */ + return ret; + } + if (ret != STATUS_TRYAGAIN) { + NT_LOG(ERR, NTCONNECT, + "Failed getting packet. Error code: 0x%X\n", + ret); + return ret; + } + } + /* Try again */ + } + return STATUS_OK; +} + +static int write_all(int fd, const uint8_t *data, size_t size) +{ + size_t len = 0; + + while (len < size) { + ssize_t res = write(fd, (const void *)&data[len], size - len); + + if (res < 0) { + NT_LOG(ERR, NTCONNECT, "write to socket failed!"); + return STATUS_INTERNAL_ERROR; + } + len += res; + } + return 0; +} + +static int read_request(int clfd, char **rdata) +{ + uint8_t *data = malloc(MESSAGE_BUFFER * sizeof(uint8_t)); + + if (!data) { + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return STATUS_INTERNAL_ERROR; + } + + size_t recv_len = 0; + int ret = read_data(clfd, MESSAGE_BUFFER, data, &recv_len, -1); + + if (ret) { + free(data); + return ret; + } + + struct ntconn_header_s hdr; + + memcpy(&hdr, data, NTCMOD_HDR_LEN); + size_t length = (hdr.len + hdr.blob_len) * sizeof(uint8_t); + + if (length > MESSAGE_BUFFER) { + uint8_t *new_data = realloc(data, length); + + if (!new_data) { + NT_LOG(ERR, NTCONNECT, "memory reallocation failed"); + free(data); + return STATUS_INTERNAL_ERROR; + } + data = new_data; + ret = read_all(clfd, &data[recv_len], length - recv_len); + if (ret) { + free(data); + return ret; + } + } + + *rdata = (char *)data; + return STATUS_OK; +} + +static ntconn_mod_t *ntconnect_interpret_request(int clfd, + struct ntconn_header_s *hdr, + char **get_req _unused, + char **module_cmd, int *status) +{ + char pci_id[32]; + char module[64]; + ntconn_mod_t *result_ntcmod = NULL; + char *request = NULL; + + int ret = read_request(clfd, &request); + *status = ret; + *get_req = request; + + if (ret == STATUS_OK && request) { + *hdr = *(struct ntconn_header_s *)request; + + if (!hdr) { + NT_LOG(ERR, NTCONNECT, "hdr returned NULL\n"); + *status = STATUS_INTERNAL_ERROR; + return NULL; + } + + switch (hdr->tag) { + case NTCONN_TAG_REQUEST: { + unsigned long idx = NTCMOD_HDR_LEN; + char *saveptr; + char *req = &request[idx]; + + uint32_t domain = 0xffffffff; + uint8_t bus = 0xff; + uint8_t devid = 0xff; + uint8_t function = 0xff; + + char *tok = strtok_r(req, ";", &saveptr); + + idx += strlen(tok) + 1; + if (!tok) + goto err_out; + rte_strscpy(pci_id, tok, 31); + + tok = strtok_r(NULL, ";", &saveptr); + idx += strlen(tok) + 1; + if (!tok) + goto err_out; + rte_strscpy(module, tok, 63); + + tok = strtok_r(NULL, "", &saveptr); + hdr->len -= idx; + if (tok) + *module_cmd = &request[idx]; + + tok = strtok_r(pci_id, ":.", &saveptr); + if (!tok) + goto err_out; + domain = (uint32_t)strtol(tok, NULL, 16); + tok = strtok_r(NULL, ":.", &saveptr); + if (!tok) + goto err_out; + bus = (uint8_t)strtol(tok, NULL, 16); + + tok = strtok_r(NULL, ":.", &saveptr); + if (!tok) + goto err_out; + devid = (uint8_t)strtol(tok, NULL, 16); + tok = strtok_r(NULL, "", &saveptr); + if (!tok) + goto err_out; + function = (uint8_t)strtol(tok, NULL, 16); + + /* Search for module registered as */ + ntconn_mod_t *ntcmod = ntcmod_base; + + while (ntcmod) { + if (domain == ntcmod->addr.domain && + bus == ntcmod->addr.bus && + devid == ntcmod->addr.devid && + function == ntcmod->addr.function && + strcmp(ntcmod->op->module, module) == 0) { + result_ntcmod = ntcmod; + break; + } + ntcmod = ntcmod->next; + } + } + break; + + default: + break; + } + } + +err_out: + + return result_ntcmod; +} + +static int send_reply(int clfd, uint16_t reply_tag, const void *data, + uint32_t size) +{ + struct ntconn_header_s hdr; + + hdr.tag = reply_tag; + hdr.len = NTCMOD_HDR_LEN + size; + hdr.blob_len = 0; + uint8_t *message = malloc(hdr.len * sizeof(uint8_t)); + + if (!message) { + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return STATUS_INTERNAL_ERROR; + } + memcpy(message, (void *)&hdr, NTCMOD_HDR_LEN); + memcpy(&message[NTCMOD_HDR_LEN], data, size); + int res = write_all(clfd, message, hdr.len); + + free(message); + if (res) + return res; + + return 0; +} + +static int send_reply_free_data(int clfd, ntconn_mod_t *cmod, + uint16_t reply_tag, void *data, uint32_t size) +{ + int res = send_reply(clfd, reply_tag, data, size); + + if (size) { + pthread_mutex_lock(&cmod->mutex); + cmod->op->free_data(cmod->hdl, data); + pthread_mutex_unlock(&cmod->mutex); + } + + return res; +} + +static int ntconnect_send_error(int clfd, enum ntconn_err_e err_code) +{ + char err_buf[MAX_ERR_MESSAGE_LENGTH]; + const ntconn_err_t *ntcerr = get_ntconn_error(err_code); + + sprintf(err_buf, "----connect:%s", ntcerr->err_text); + unsigned int len = strlen(err_buf); + memcpy(err_buf, &ntcerr->err_code, sizeof(uint32_t)); + + return send_reply(clfd, NTCONN_TAG_ERROR, err_buf, len); +} + +static void *ntconnect_worker(void *arg) +{ + int status; + int clfd = (int)(uint64_t)arg; + char *module_cmd = NULL; + char *request = NULL; + struct ntconn_header_s hdr; + + do { + ntconn_mod_t *cmod = ntconnect_interpret_request(clfd, &hdr, + &request, + &module_cmd, + &status); + + if (cmod && module_cmd && status == 0) { + int len; + char *data; + + /* + * Handle general module commands + */ + if (strcmp(module_cmd, "version") == 0) { + uint64_t version = + ((uint64_t)cmod->op->version_major + << 32) + + (cmod->op->version_minor); + + if (send_reply(clfd, NTCONN_TAG_REPLY, + (void *)&version, + sizeof(uint64_t))) + break; + + } else { + /* + * Call module for execution of command + */ + data = NULL; + pthread_mutex_lock(&cmod->mutex); + int repl = cmod->op->request(cmod->hdl, clfd, + &hdr, module_cmd, + &data, &len); + pthread_mutex_unlock(&cmod->mutex); + + if (repl == REQUEST_OK && len >= 0) { + if (send_reply_free_data(clfd, cmod, + NTCONN_TAG_REPLY, + (void *)data, + (uint32_t)len)) + break; + + } else if (repl == REQUEST_ERR && len >= 0) { + if (send_reply_free_data(clfd, cmod, + NTCONN_TAG_ERROR, + (void *)data, + (uint32_t)len)) + break; + } else { + NT_LOG(ERR, NTCONNECT, + "Invalid result from module request function: module %s, result %i\n", + cmod->op->module, repl); + if (ntconnect_send_error(clfd, + NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR)) + break; + } + } + + } else if (status == STATUS_TIMEOUT) { + /* Other end is dead */ + NT_LOG(WRN, NTCONNECT, + "Client must be dead - timeout\n"); + break; + } else if (status == STATUS_CONNECTION_CLOSED) { + break; /* silently break out */ + } + /* Error - send error back */ + if (ntconnect_send_error(clfd, NTCONN_ERR_CODE_INVALID_REQUEST)) + break; + if (request) + free(request); + } while (1); /* while still connected */ + + close(clfd); + + /* call module cleanup callback function for client_id */ + ntconn_mod_t *ntcmod = ntcmod_base; + + while (ntcmod) { + if (ntcmod->op->client_cleanup) { + pthread_mutex_lock(&ntcmod->mutex); + ntcmod->op->client_cleanup(ntcmod->hdl, clfd); + pthread_mutex_unlock(&ntcmod->mutex); + } + + ntcmod = ntcmod->next; + } + pthread_exit(NULL); + return NULL; +} + +static void *ntconnect_server(void *arg) +{ + struct ntconn_server_s *ntcserv = (struct ntconn_server_s *)arg; + + ntcserv->running = 1; + +#ifdef DEBUG + NT_LOG(DBG, NTCONNECT, "Running NT Connection Server fd %i\n", + ntcserv->serv_fd); +#endif + + if (listen(ntcserv->serv_fd, 5) < 0) { + NT_LOG(ERR, NTCONNECT, + "Server failed on listen(), stopping thread. err: %s\n", + strerror(errno)); + pthread_exit(NULL); + return NULL; + } + + while (ntcserv->running) { + int clfd = accept(ntcserv->serv_fd, NULL, NULL); + + if (clfd < 0) { + NT_LOG(ERR, NTCONNECT, + "ERROR from accept(), stopping thread. err: %s\n", + strerror(errno)); + break; + } + pthread_create(&ctid, NULL, ntconnect_worker, + (void *)(uint64_t)clfd); + pthread_setaffinity_np(ctid, sizeof(cpu_set_t), + &ntcserv->cpuset); + /* Detach immediately. We will never join this thread */ + pthread_detach(ctid); + } + + pthread_exit(NULL); + return NULL; +} + +int ntconnect_init(const char *sockname, cpu_set_t cpuset) +{ + if (ntcmod_base) { + /* Make sure the socket directory exists */ + char *sockname_copy = strdup(sockname); + char *sockname_dir = dirname(sockname_copy); + + if (mkdir(sockname_dir, 0755) < 0 && errno != EEXIST) { + NT_LOG(ERR, NTCONNECT, + "Can't create socket directory: %s", + sockname_dir); + free(sockname_copy); + return -1; + } + free(sockname_copy); + + /* Add server to module list - cannot work without */ + ntconn_server_register(&ntconn_serv); + + /* Start named socket server */ + struct sockaddr_un addr; + + unix_build_address(sockname, &addr); + + ntconn_serv.serv_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ntconn_serv.cpuset = cpuset; + if (ntconn_serv.serv_fd == -1) + return -1; + + /* Make sure the node in filesystem is deleted otherwise bind will fail */ + unlink(sockname); + + if (bind(ntconn_serv.serv_fd, (struct sockaddr *)&addr, + sizeof(struct sockaddr_un)) == -1) { + close(ntconn_serv.serv_fd); + return -1; + } + + /* Run ntconnect service */ + pthread_create(&tid, NULL, ntconnect_server, &ntconn_serv); + pthread_setaffinity_np(tid, sizeof(cpu_set_t), + &ntconn_serv.cpuset); + } + + return 0; +} diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c b/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c new file mode 100644 index 0000000000..294b95846b --- /dev/null +++ b/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c @@ -0,0 +1,775 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntnic_ethdev.h" +#include "ntconnect.h" +#include "ntconnect_api_adapter.h" +#include "ntos_system.h" +#include "ntconn_modules.h" +#include "ntconn_mod_helper.h" +#include "nt_util.h" +#include "ntlog.h" + +#define NTCONN_ADAP_VERSION_MAJOR 0U +#define NTCONN_ADAP_VERSION_MINOR 1U + +#define this_module_name "adapter" + +#define MAX_ADAPTERS 2 + +static struct adap_hdl_s { + struct drv_s *drv; +} adap_hdl[MAX_ADAPTERS]; + +static int func_adapter_get_interfaces(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_adapter_get_info(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_adapter_get_sensors(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len); +static struct func_s funcs_get_level1[] = { + { "interfaces", NULL, func_adapter_get_interfaces }, + { "info", NULL, func_adapter_get_info }, + { "sensors", NULL, func_adapter_get_sensors }, + { NULL, NULL, NULL }, +}; + +static int func_adapter_set_interface(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_adapter_set_adapter(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static struct func_s funcs_set_level1[] = { + { "interface", NULL, func_adapter_set_interface }, + { "adapter", NULL, func_adapter_set_adapter }, + { NULL, NULL, NULL }, +}; + +/* + * Entry level + */ +static struct func_s adapter_entry_funcs[] = { + { "get", funcs_get_level1, NULL }, + { "set", funcs_set_level1, NULL }, + { NULL, NULL, NULL }, +}; + +static int read_link_speed(enum nt_link_speed_e link_speed) +{ + switch (link_speed) { + case NT_LINK_SPEED_10M: + return PORT_LINK_SPEED_10M; + case NT_LINK_SPEED_100M: + return PORT_LINK_SPEED_100M; + case NT_LINK_SPEED_1G: + return PORT_LINK_SPEED_1G; + case NT_LINK_SPEED_10G: + return PORT_LINK_SPEED_10G; + case NT_LINK_SPEED_25G: + return PORT_LINK_SPEED_25G; + case NT_LINK_SPEED_40G: + return PORT_LINK_SPEED_40G; + case NT_LINK_SPEED_50G: + return PORT_LINK_SPEED_50G; + case NT_LINK_SPEED_100G: + return PORT_LINK_SPEED_100G; + default: + break; + } + return PORT_LINK_SPEED_UNKNOWN; +} + +static nt_link_speed_t convert_link_speed(char *speed_str) +{ + if (strcmp(speed_str, "10M") == 0) + return NT_LINK_SPEED_10M; + else if (strcmp(speed_str, "100M") == 0) + return NT_LINK_SPEED_100M; + else if (strcmp(speed_str, "1G") == 0) + return NT_LINK_SPEED_1G; + else if (strcmp(speed_str, "10G") == 0) + return NT_LINK_SPEED_10G; + else if (strcmp(speed_str, "25G") == 0) + return NT_LINK_SPEED_25G; + else if (strcmp(speed_str, "40G") == 0) + return NT_LINK_SPEED_40G; + else if (strcmp(speed_str, "50G") == 0) + return NT_LINK_SPEED_50G; + else if (strcmp(speed_str, "100G") == 0) + return NT_LINK_SPEED_100G; + else + return NT_LINK_SPEED_UNKNOWN; +} + +static int func_adapter_get_interfaces(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct ntc_interfaces_s *ifs; + struct adap_hdl_s *adap = (struct adap_hdl_s *)hdl; + fpga_info_t *fpga_info = &adap->drv->ntdrv.adapter_info.fpga_info; + int lag_active; + int final_list = adap->drv->probe_finished; + /* keep final_list set before nb_ports are called */ + rte_compiler_barrier(); + int nb_ports = rte_eth_dev_count_avail(); + + /* Get the "internals" structure of phy port 0 to find out if we're running LAG */ + char phy0_name[128]; + + rte_eth_dev_get_name_by_port(0, phy0_name); + struct rte_eth_dev *phy0_eth_dev = rte_eth_dev_get_by_name(phy0_name); + + if (phy0_eth_dev == NULL || phy0_eth_dev->data == NULL || + phy0_eth_dev->data->dev_private == NULL) { + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_INTERNAL_ERROR); + } + struct pmd_internals *phy0_internals = + (struct pmd_internals *)phy0_eth_dev->data->dev_private; + lag_active = (phy0_internals->lag_config == NULL) ? 0 : 1; + if (lag_active) { + /* + * Phy ports are link aggregated. I.e. number of ports is actually + * one bigger than what rte_eth_dev_count_avail() returned + */ + nb_ports++; + + /* + * Sanity check: + * For now we know about LAG with 2 ports only. + * If in the future we get HW with more ports, make assert to alert + * the developers that something needs to be looked at... + */ + assert(fpga_info->n_phy_ports == 2); + } + + *len = sizeof(struct ntc_interfaces_s) + + sizeof(struct ntc_interface_s) * nb_ports; + ifs = malloc(*len); + if (!ifs) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + *data = (char *)ifs; + + ifs->nb_ports = nb_ports; + ifs->final_list = final_list; + + int i; + + /* First set the "port type" of the physical ports */ + if (lag_active) { + if (phy0_internals->lag_config->mode == BONDING_MODE_8023AD) { + /* Active/active LAG */ + for (i = 0; i < fpga_info->n_phy_ports; i++) { + ifs->intf[i].type = + PORT_TYPE_PHY_LAG_ACTIVE_ACTIVE; + } + } else if (phy0_internals->lag_config->mode == + BONDING_MODE_ACTIVE_BACKUP) { + /* Active/backup LAG */ + ifs->intf[phy0_internals->lag_config->primary_port] + .type = PORT_TYPE_PHY_LAG_PRIMARY; + ifs->intf[phy0_internals->lag_config->backup_port].type = + PORT_TYPE_PHY_LAG_BACKUP; + } else { + /* Unknown LAG mode */ + assert(0); + } + } else { + /* Normal phy ports (not link aggregated) */ + for (i = 0; i < fpga_info->n_phy_ports; i++) + ifs->intf[i].type = PORT_TYPE_PHY_NORMAL; + } + + /* Then set the remaining port values for the physical ports. */ + for (i = 0; i < fpga_info->n_phy_ports; i++) { + char name[128]; + + if (i > 0 && lag_active) { + /* + * Secondary link aggregated port. Just display the "internals" values + * from port 0 + */ + rte_eth_dev_get_name_by_port(0, name); + } else { + rte_eth_dev_get_name_by_port(i, name); + } + struct rte_eth_dev *eth_dev = rte_eth_dev_get_by_name(name); + + struct pmd_internals *internals = + (struct pmd_internals *)eth_dev->data->dev_private; + struct adapter_info_s *p_adapter_info = + &adap->drv->ntdrv.adapter_info; + + ifs->intf[i].port_id = i; + ifs->intf[i].pci_id.domain = internals->pci_dev->addr.domain; + ifs->intf[i].pci_id.bus = internals->pci_dev->addr.bus; + ifs->intf[i].pci_id.devid = internals->pci_dev->addr.devid; + ifs->intf[i].pci_id.function = + internals->pci_dev->addr.function; + ifs->intf[i].pci_id.pad = 0; + + const bool port_link_status = + nt4ga_port_get_link_status(p_adapter_info, i); + ifs->intf[i].link = port_link_status ? PORT_LINK_UP : + PORT_LINK_DOWN; + + const nt_link_speed_t port_link_speed = + nt4ga_port_get_link_speed(p_adapter_info, i); + ifs->intf[i].port_speed = read_link_speed(port_link_speed); + + const bool port_adm_state = + nt4ga_port_get_adm_state(p_adapter_info, i); + if (!port_adm_state) { + ifs->intf[i].port_state = PORT_STATE_DISABLED; + } else { + const bool port_nim_present = + nt4ga_port_get_nim_present(p_adapter_info, i); + if (port_nim_present) { + ifs->intf[i].port_state = + PORT_STATE_NIM_PRESENT; + } else { + ifs->intf[i].port_state = PORT_STATE_NIM_ABSENT; + } + } + + /* MTU */ + if (i > 0 && lag_active) { + /* Secondary link aggregated port. Display same MTU value as port 0 */ + rte_eth_dev_get_mtu(0, &ifs->intf[i].mtu); + } else { + rte_eth_dev_get_mtu(i, &ifs->intf[i].mtu); + } + + /* MAC */ + const uint64_t mac = + fpga_info->nthw_hw_info.vpd_info.mn_mac_addr_value + i; + ifs->intf[i].mac.addr_b[0] = (mac >> 40) & 0xFFu; + ifs->intf[i].mac.addr_b[1] = (mac >> 32) & 0xFFu; + ifs->intf[i].mac.addr_b[2] = (mac >> 24) & 0xFFu; + ifs->intf[i].mac.addr_b[3] = (mac >> 16) & 0xFFu; + ifs->intf[i].mac.addr_b[4] = (mac >> 8) & 0xFFu; + ifs->intf[i].mac.addr_b[5] = (mac >> 0) & 0xFFu; + + if (i > 0 && lag_active) { + /* Secondary link aggregated port. Queues not applicable */ + ifs->intf[i].num_queues = 0; + } else { + /* attached hw queues to this interface */ + unsigned int input_num = internals->nb_rx_queues; + /* + * These are the "input" queues, meaning these go to host and is attached + * to receiving from a port + */ + for (unsigned int ii = 0; ii < input_num; ii++) { + ifs->intf[i].queue[ii].idx = + internals->rxq_scg[ii].queue.hw_id; + ifs->intf[i].queue[ii].dir = QUEUE_INPUT; + } + + /* + * These are the "output" queues, meaning these go to a virtual port queue + * which typically is used by vDPA + */ + for (unsigned int ii = 0; ii < internals->vpq_nb_vq; + ii++) { + ifs->intf[i].queue[ii + input_num].idx = + internals->vpq[ii].hw_id; + ifs->intf[i].queue[ii + input_num].dir = + QUEUE_OUTPUT; + } + + ifs->intf[i].num_queues = + input_num + internals->vpq_nb_vq; + } + + /* NIM information */ + nim_i2c_ctx_t nim_ctx = + nt4ga_port_get_nim_capabilities(p_adapter_info, i); + + strlcpy((char *)&ifs->intf[i].nim_data.vendor_name, + nim_ctx.vendor_name, + sizeof(ifs->intf[i].nim_data.vendor_name)); + strlcpy((char *)&ifs->intf[i].nim_data.prod_no, nim_ctx.prod_no, + sizeof(ifs->intf[i].nim_data.prod_no)); + strlcpy((char *)&ifs->intf[i].nim_data.serial_no, + nim_ctx.serial_no, + sizeof(ifs->intf[i].nim_data.serial_no)); + strlcpy((char *)&ifs->intf[i].nim_data.date, nim_ctx.date, + sizeof(ifs->intf[i].nim_data.date)); + strlcpy((char *)&ifs->intf[i].nim_data.rev, nim_ctx.rev, + sizeof(ifs->intf[i].nim_data.rev)); + + if (nim_ctx.len_info[0] >= 0xFFFF) + ifs->intf[i].nim_data.link_length.sm = 0xFFFF; + else + ifs->intf[i].nim_data.link_length.sm = + nim_ctx.len_info[0]; + + ifs->intf[i].nim_data.link_length.ebw = nim_ctx.len_info[1]; + ifs->intf[i].nim_data.link_length.mm50 = nim_ctx.len_info[2]; + ifs->intf[i].nim_data.link_length.mm62 = nim_ctx.len_info[3]; + ifs->intf[i].nim_data.link_length.copper = nim_ctx.len_info[4]; + + ifs->intf[i].nim_data.pwr_level_req = nim_ctx.pwr_level_req; + ifs->intf[i].nim_data.pwr_level_cur = nim_ctx.pwr_level_cur; + ifs->intf[i].nim_data.nim_id = nim_ctx.nim_id; + ifs->intf[i].nim_data.port_type = nim_ctx.port_type; + } + + /* And finally handle the virtual ports. */ + int rte_eth_dev_virt_port_offset = lag_active ? 1 : + fpga_info->n_phy_ports; + for (; i < nb_ports; i++, rte_eth_dev_virt_port_offset++) { + /* Continue counting from the "i" value reached in the previous for loop */ + char name[128]; + + rte_eth_dev_get_name_by_port(rte_eth_dev_virt_port_offset, + name); + struct rte_eth_dev *eth_dev = rte_eth_dev_get_by_name(name); + + struct pmd_internals *internals = + (struct pmd_internals *)eth_dev->data->dev_private; + + ifs->intf[i].port_id = i; + ifs->intf[i].type = PORT_TYPE_VIRT; + ifs->intf[i].pci_id.domain = internals->pci_dev->addr.domain; + ifs->intf[i].pci_id.bus = internals->pci_dev->addr.bus; + ifs->intf[i].pci_id.devid = internals->pci_dev->addr.devid; + ifs->intf[i].pci_id.function = + internals->pci_dev->addr.function; + ifs->intf[i].pci_id.pad = 0; + + ifs->intf[i].port_speed = PORT_LINK_SPEED_NONE_REPORTED; + switch (internals->vport_comm) { + case VIRT_PORT_NEGOTIATED_NONE: + ifs->intf[i].port_state = PORT_STATE_VIRTUAL_UNATTACHED; + ifs->intf[i].link = PORT_LINK_DOWN; + break; + case VIRT_PORT_NEGOTIATED_SPLIT: + ifs->intf[i].port_state = PORT_STATE_VIRTUAL_SPLIT; + ifs->intf[i].link = PORT_LINK_UP; + break; + case VIRT_PORT_NEGOTIATED_PACKED: + ifs->intf[i].port_state = PORT_STATE_VIRTUAL_PACKED; + ifs->intf[i].link = PORT_LINK_UP; + break; + case VIRT_PORT_USE_RELAY: + ifs->intf[i].port_state = PORT_STATE_VIRTUAL_RELAY; + ifs->intf[i].link = PORT_LINK_UP; + break; + } + + /* MTU */ + rte_eth_dev_get_mtu(rte_eth_dev_virt_port_offset, + &ifs->intf[i].mtu); + + /* MAC */ + for (int ii = 0; ii < 6; ii++) { + ifs->intf[i].mac.addr_b[ii] = + internals->eth_addrs[0].addr_bytes[ii]; + } + + /* attached hw queues to this interface */ + unsigned int input_num = internals->nb_rx_queues; + + /* + * These are the "input" queues, meaning these go to host and is attached to + * receiving from a port + */ + for (unsigned int ii = 0; ii < input_num; ii++) { + ifs->intf[i].queue[ii].idx = + internals->rxq_scg[ii].queue.hw_id; + ifs->intf[i].queue[ii].dir = QUEUE_INPUT; + } + + /* + * These are the "output" queues, meaning these go to a virtual port queue + * which typically is used by vDPA + */ + unsigned int numq = + ((internals->vpq_nb_vq + input_num) > MAX_RSS_QUEUES) ? + MAX_RSS_QUEUES - input_num : + internals->vpq_nb_vq; + for (unsigned int ii = 0; ii < numq; ii++) { + ifs->intf[i].queue[ii + input_num].idx = + internals->vpq[ii].hw_id; + ifs->intf[i].queue[ii + input_num].dir = QUEUE_OUTPUT; + } + ifs->intf[i].num_queues = input_num + numq; + } + return REQUEST_OK; +} + +static int func_adapter_get_info(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct adap_hdl_s *adap = (struct adap_hdl_s *)hdl; + fpga_info_t *fpga_info = &adap->drv->ntdrv.adapter_info.fpga_info; + + *len = sizeof(struct ntc_adap_get_info_s); + *data = malloc(*len); + if (!*data) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + + snprintf(*data, 31, "%03d-%04d-%02d-%02d", fpga_info->n_fpga_type_id, + fpga_info->n_fpga_prod_id, fpga_info->n_fpga_ver_id, + fpga_info->n_fpga_rev_id); + + return REQUEST_OK; +} + +static int func_adapter_get_sensors(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct adapter_info_s *adapter = + &(((struct adap_hdl_s *)hdl)->drv->ntdrv.adapter_info); + struct sensor *sensor_ptr = NULL; + uint16_t sensors_num = 0; + uint8_t *sensors = NULL; + struct ntc_sensors_s sensors_info = { + .adapter_sensors_cnt = adapter->adapter_sensors_cnt, + .ports_cnt = adapter->fpga_info.n_phy_ports + }; + memcpy(sensors_info.adapter_name, adapter->p_dev_name, 24); + + /* Set a sum of sensor`s counters */ + sensors_num = adapter->adapter_sensors_cnt; + for (int i = 0; i < adapter->fpga_info.n_phy_ports; i++) { + sensors_num += adapter->nim_sensors_cnt[i]; + sensors_info.nim_sensors_cnt[i] = adapter->nim_sensors_cnt[i]; + } + + *len = sizeof(struct ntc_sensors_s) + + sensors_num * sizeof(struct sensor); + + /* Allocate memory for sensors array */ + sensors = malloc(*len); + if (!sensors) { + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + *len = 0; + return REQUEST_ERR; + } + memcpy(sensors, &sensors_info, sizeof(struct ntc_sensors_s)); + sensor_ptr = (struct sensor *)(sensors + sizeof(struct ntc_sensors_s)); + + /* Fetch adapter sensors */ + for (struct nt_sensor_group *ptr = adapter->adapter_sensors; + ptr != NULL; ptr = ptr->next) { + sensor_ptr->current_value = ptr->sensor->info.value; + sensor_ptr->min_value = ptr->sensor->info.value_lowest; + sensor_ptr->max_value = ptr->sensor->info.value_highest; + sensor_ptr->sign = ptr->sensor->si; + sensor_ptr->type = ptr->sensor->info.type; + memcpy(sensor_ptr->name, ptr->sensor->info.name, 50); + sensor_ptr++; + } + + /* Fetch NIM sensors */ + for (int i = 0; i < adapter->fpga_info.n_phy_ports; i++) { + for (struct nim_sensor_group *ptr = adapter->nim_sensors[i]; + ptr != NULL; ptr = ptr->next) { + sensor_ptr->current_value = ptr->sensor->info.value; + sensor_ptr->min_value = ptr->sensor->info.value_lowest; + sensor_ptr->max_value = ptr->sensor->info.value_highest; + sensor_ptr->sign = ptr->sensor->si; + sensor_ptr->type = ptr->sensor->info.type; + + memcpy(sensor_ptr->name, ptr->sensor->info.name, + (strlen(ptr->sensor->info.name) >= 50) ? + 50 : + strlen(ptr->sensor->info.name)); + sensor_ptr++; + } + } + + /* Send response */ + *data = (char *)sensors; + + return REQUEST_OK; +} + +static int set_port_enable(struct adap_hdl_s *adap, int port_nr) +{ + adapter_info_t *p_adapter_info = &adap->drv->ntdrv.adapter_info; + + nt4ga_port_set_adm_state(p_adapter_info, port_nr, true); + + return REQUEST_OK; +} + +static int set_port_disable(struct adap_hdl_s *adap, int port_nr) +{ + adapter_info_t *p_adapter_info = &adap->drv->ntdrv.adapter_info; + + nt4ga_port_set_adm_state(p_adapter_info, port_nr, false); + + return REQUEST_OK; +} + +static int set_link_up(struct adap_hdl_s *adap, int portid) +{ + struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info; + + const bool link_status = + nt4ga_port_get_link_status(p_adapter_info, portid); + + if (!link_status) { + nt4ga_port_set_link_status(p_adapter_info, portid, true); + NT_LOG(DBG, NTCONNECT, "Port %i: Link set to be up\n", portid); + } else { + NT_LOG(DBG, NTCONNECT, + "Port %i: Link is already set to be up\n", portid); + } + + return REQUEST_OK; +} + +static int set_link_down(struct adap_hdl_s *adap, int portid) +{ + struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info; + + const bool link_status = + nt4ga_port_get_link_status(p_adapter_info, portid); + + if (!link_status) { + NT_LOG(DBG, NTCONNECT, + "Port %i: Link is already set to be down\n", portid); + } else { + nt4ga_port_set_link_status(p_adapter_info, portid, false); + NT_LOG(DBG, NTCONNECT, "Port %i: Link set to be down\n", + portid); + } + + return REQUEST_OK; +} + +static int set_link_speed(struct adap_hdl_s *adap, int portid, char *speed_str, + char **data, int *len) +{ + struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info; + + const bool port_adm_state = + nt4ga_port_get_adm_state(p_adapter_info, portid); + if (!port_adm_state) { + const nt_link_speed_t speed = convert_link_speed(speed_str); + + if (speed != NT_LINK_SPEED_UNKNOWN) { + nt4ga_port_set_link_speed(p_adapter_info, portid, speed); + NT_LOG(DBG, NTCONNECT, "Port %i: set link speed - %s\n", + portid, speed_str); + } else { + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + } + } else { + NT_LOG(DBG, NTCONNECT, + "Port %i: fail to set link speed, port is enabled\n", + portid); + return ntconn_reply_status(data, len, + NTCONN_ADAPTER_ERR_WRONG_LINK_STATE); + } + + return REQUEST_OK; +} + +static int set_loopback_mode(struct adap_hdl_s *adap, int portid, int mode) +{ + struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info; + + NT_LOG(DBG, NTCONNECT, "Port %i: set loopback mode %i\n", portid, mode); + nt4ga_port_set_loopback_mode(p_adapter_info, portid, mode); + return REQUEST_OK; +} + +static int set_tx_power(struct adap_hdl_s *adap, int portid, bool disable, + char **data, int *len) +{ + struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info; + + NT_LOG(DBG, NTCONNECT, "Port %i: set tx_power %i\n", portid, disable); + if (nt4ga_port_tx_power(p_adapter_info, portid, disable)) { + NT_LOG(DBG, NTCONNECT, + "Port %i: ERROR while changing tx_power\n", portid); + return ntconn_reply_status(data, len, + NTCONN_ADAPTER_ERR_TX_POWER_FAIL); + } + return REQUEST_OK; +} + +static int func_adapter_set_interface(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct adap_hdl_s *adap = (struct adap_hdl_s *)hdl; + char *saveptr; + int port_nr; + int length; + char *tok; + + *len = 0; + + /* + * This will receive the request strings starting with "adapter;set,interface,...." + * so in the situation of a request like: "adapter,set,interface,port0,link_speed=10G" + * the remainder of the command "port0,link_speed=10G" will be pointed to by *data, + * zero-terminated on entry + */ + + if (!(data && *data)) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_INVALID_REQUEST); + + /* OK to modify *data */ + tok = strtok_r(*data, ",", &saveptr); + if (!tok) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + + length = strlen(tok); + + if (!(length > 4 && memcmp(tok, "port", 4) == 0)) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + + port_nr = atoi(tok + 4); + + /* Only set on phy ports */ + if (port_nr < adap->drv->ntdrv.adapter_info.fpga_info.n_phy_ports) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + + tok = strtok_r(NULL, "=,", &saveptr); + if (!tok) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + if (strcmp(tok, "link_speed") == 0) { + tok = strtok_r(NULL, ",", &saveptr); + if (!tok) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + return set_link_speed(adap, port_nr, tok, data, len); + } else if (strcmp(tok, "enable") == 0) { + return set_port_enable(adap, port_nr); + } else if (strcmp(tok, "disable") == 0) { + return set_port_disable(adap, port_nr); + } else if (strcmp(tok, "link_state") == 0) { + tok = strtok_r(NULL, ",", &saveptr); + if (!tok) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + if (strcmp(tok, "up") == 0) + return set_link_up(adap, port_nr); + else if (strcmp(tok, "down") == 0) + return set_link_down(adap, port_nr); + } else if (strcmp(tok, "host_loopback") == 0) { + tok = strtok_r(NULL, ",", &saveptr); + if (!tok) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + if (strcmp(tok, "on") == 0) + return set_loopback_mode(adap, port_nr, + NT_LINK_LOOPBACK_HOST); + else if (strcmp(tok, "off") == 0) + return set_loopback_mode(adap, port_nr, + NT_LINK_LOOPBACK_OFF); + } else if (strcmp(tok, "line_loopback") == 0) { + tok = strtok_r(NULL, ",", &saveptr); + if (!tok) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + if (strcmp(tok, "on") == 0) + return set_loopback_mode(adap, port_nr, + NT_LINK_LOOPBACK_LINE); + else if (strcmp(tok, "off") == 0) + return set_loopback_mode(adap, port_nr, + NT_LINK_LOOPBACK_OFF); + } else if (strcmp(tok, "tx_power") == 0) { + tok = strtok_r(NULL, ",", &saveptr); + if (!tok) + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); + if (strcmp(tok, "on") == 0) + return set_tx_power(adap, port_nr, false, data, len); + else if (strcmp(tok, "off") == 0) + return set_tx_power(adap, port_nr, true, data, len); + } + + /* Should return 0 on success */ + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_MISSING_INVALID_PARAM); +} + +static int func_adapter_set_adapter(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + if (data && *data) { + NT_LOG(DBG, NTCONNECT, + "Set adapter: Command: %s\n", *data); + } + + *len = 0; + + /* Should return 0 on success */ + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED); +} + +static int adap_request(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr, char *function, + char **data, int *len) +{ + return execute_function(this_module_name, hdl, client_id, hdr, function, + adapter_entry_funcs, data, len, 0); +} + +static void adap_free_data(void *hdl _unused, char *data) +{ + free(data); +} + +static void adap_client_cleanup(void *hdl _unused, int client_id _unused) +{ + /* Nothing to do */ +} + +static const ntconnapi_t ntconn_adap_op = { this_module_name, + NTCONN_ADAP_VERSION_MAJOR, + NTCONN_ADAP_VERSION_MINOR, + adap_request, + adap_free_data, + adap_client_cleanup + }; + +int ntconn_adap_register(struct drv_s *drv) +{ + int i; + + for (i = 0; i < MAX_ADAPTERS; i++) { + if (adap_hdl[i].drv == NULL) + break; + } + if (i == MAX_ADAPTERS) { + NT_LOG(ERR, NTCONNECT, + "Cannot register more adapters into NtConnect framework"); + return -1; + } + + adap_hdl[i].drv = drv; + return register_ntconn_mod(&drv->p_dev->addr, (void *)&adap_hdl[i], + &ntconn_adap_op); +} diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c b/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c new file mode 100644 index 0000000000..3d81242524 --- /dev/null +++ b/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c @@ -0,0 +1,1312 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include "ntnic_ethdev.h" +#include "ntconnect.h" +#include "ntos_system.h" +#include "ntconn_modules.h" +#include "ntconn_mod_helper.h" +#include "nt_util.h" +#include "ntlog.h" +#include "ntnic_vf_vdpa.h" + +#include "ntconnect_api_flow.h" +#include "ntconnect_api_meter.h" +#include "stream_binary_flow_api.h" + +#include +#include "flow_api.h" + +#define DEBUG_FLOW 1 + +#define NTCONN_FLOW_VERSION_MAJOR 0U +#define NTCONN_FLOW_VERSION_MINOR 1U + +#define this_module_name "filter" + +#define IN_PORT_TOK "in_port=" +#define VPATH_TOK "vpath=" + +#define MAX_CLIENTS 32 + +#define UNUSED __rte_unused + +static struct flow_hdl_s { + struct drv_s *drv; +} flow_hdl[MAX_CLIENTS]; + +#define MAX_PORTS 64 +static struct port_to_eth_s { + struct flow_eth_dev *flw_dev; + uint32_t forced_vlan_vid; + uint32_t caller_id; +} port_eth[MAX_PORTS]; + +static ntconn_err_t ntconn_err[] = { + { NTCONN_FLOW_ERR_NONE, "Success" }, + { NTCONN_FLOW_ERR_INTERNAL_ERROR, "Internal error" }, + { NTCONN_FLOW_ERR_PORT_IS_NOT_INITIALIZED, "Port is not initialized" }, + { NTCONN_FLOW_ERR_UNEXPECTED_VIRTIO_PATH, "Unexpected virtio path" }, + { NTCONN_FLOW_ERR_TO_MANY_FLOWS, "To many flows" }, + { NTCONN_FLOW_ERR_INVALID_PORT, "Invalid port" }, + { NTCONN_FLOW_ERR_NOT_YET_IMPLEMENTED, "Function not yet implemented" }, + { NTCONN_FLOW_ERR_UNSUPPORTED_ADAPTER, "Adapter is not supported" }, + { NTCONN_FLOW_ERR_NO_VF_QUEUES, "No queues for the VF is found" }, + { -1, NULL } +}; + +static const char *get_error_msg(enum ntconn_flow_err_e err_code) +{ + int idx = 0; + + while (ntconn_err[idx].err_code != (uint32_t)-1 && + ntconn_err[idx].err_code != err_code) + idx++; + if (ntconn_err[idx].err_code == (uint32_t)-1) + idx = 1; + + return ntconn_err[idx].err_text; +} + +static inline int ntconn_flow_err_reply_status(char **data, int *len, + enum ntconn_flow_err_e code, + int err) +{ + *data = malloc(sizeof(struct flow_return_s)); + if (*data) { + struct flow_return_s *return_value = + (struct flow_return_s *)*data; + *len = sizeof(struct flow_return_s); + return_value->status = err; + return_value->type = FLOW_ERROR_GENERAL; + const char *err_msg = get_error_msg(code); + + memcpy(return_value->err_msg, err_msg, + RTE_MIN(strlen(err_msg), ERR_MSG_LEN)); + return REQUEST_OK; + } + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory"); + return REQUEST_ERR; +} + +static inline int ntconn_flow_err_status(char **data, int *len, int err) +{ + *data = malloc(sizeof(struct flow_return_s)); + if (*data) { + struct flow_return_s *return_value = + (struct flow_return_s *)*data; + *len = sizeof(struct flow_return_s); + return_value->status = err; + return_value->type = FLOW_ERROR_GENERAL; + const char *err_msg = + get_error_msg(NTCONN_FLOW_ERR_INTERNAL_ERROR); + strlcpy(return_value->err_msg, err_msg, ERR_MSG_LEN); + return REQUEST_OK; + } + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory"); + return REQUEST_ERR; +} + +/* + * Filter functions + */ +static int func_flow_create(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, int *len); +static int func_flow_validate(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_flow_destroy(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_flow_flush(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, int *len); +static int func_flow_query(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, int *len); +static int func_flow_setport(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static struct func_s adapter_entry_funcs[] = { + { "setport", NULL, func_flow_setport }, + { "create", NULL, func_flow_create }, + { "validate", NULL, func_flow_validate }, + { "destroy", NULL, func_flow_destroy }, + { "flush", NULL, func_flow_flush }, + { "query", NULL, func_flow_query }, + { NULL, NULL, NULL }, +}; + +static int copy_return_status(char **data, int *len, int status, + struct flow_error *error) +{ + *data = malloc(sizeof(struct flow_return_s)); + if (*data) { + struct flow_return_s *return_value = + (struct flow_return_s *)*data; + *len = sizeof(struct flow_return_s); + + return_value->status = status; + return_value->type = error->type; + strlcpy(return_value->err_msg, error->message, ERR_MSG_LEN); + return REQUEST_OK; + } + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", + __func__); + return REQUEST_ERR; +} + +static void set_error(struct flow_error *error) +{ + error->type = FLOW_ERROR_SUCCESS; + error->message = "Operation successfully completed"; +} + +static int func_flow_setport(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr _unused, char **data, + int *len) +{ + uint32_t i; + struct flow_error error; + uint32_t nb_port; + uint8_t in_port = MAX_PORTS; + char vpath[MAX_PATH_LEN]; + char *saveptr; + + set_error(&error); + + nb_port = rte_eth_dev_count_avail(); + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "%s: \"%s\"\n", __func__, *data); + NT_LOG(DBG, NTCONNECT, "Number of ports: %u\n", nb_port); +#endif + + char *tok = strtok_r(*data, ",", &saveptr); + + if (tok) { + size_t length = strlen(tok); + if (length > strlen(IN_PORT_TOK) && memcmp(tok, IN_PORT_TOK, + strlen(IN_PORT_TOK)) == 0) + in_port = atoi(tok + strlen(IN_PORT_TOK)); + } +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "in_port: %u\n", in_port); +#endif + + tok = strtok_r(NULL, ",", &saveptr); + if (tok) { + size_t length = strlen(tok); + if (length > strlen(VPATH_TOK) && memcmp(tok, VPATH_TOK, strlen(VPATH_TOK)) == 0) + strlcpy(vpath, tok + strlen(VPATH_TOK), MAX_PATH_LEN); + } +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "vpath: %s\n", vpath); +#endif + + /* Check that the wanted ports are valid ports */ + if (in_port >= nb_port) { + NT_LOG(ERR, NTCONNECT, "port out of range"); + return ntconn_flow_err_status(data, len, ENODEV); + } + + struct pmd_internals *vp_internals = vp_path_instance_ready(vpath); + + if (!vp_internals) { + NT_LOG(ERR, NTCONNECT, "Failed to get VF device"); + return ntconn_flow_err_status(data, len, ENODEV); + } + + /* Get flow device */ + port_eth[in_port].flw_dev = vp_internals->flw_dev; + + if (port_eth[in_port].flw_dev == NULL) { + NT_LOG(ERR, NTCONNECT, "Failed to get eth device"); + return ntconn_flow_err_status(data, len, ENODEV); + } + + /* Only INLINE is supported */ + if (vp_internals->flw_dev->ndev->flow_profile != + FLOW_ETH_DEV_PROFILE_INLINE) { + /* Only inline profile is supported */ + NT_LOG(ERR, NTCONNECT, "Adapter is not supported"); + return ntconn_flow_err_status(data, len, ENODEV); + } + + if (vp_internals->vpq_nb_vq == 0) { + NT_LOG(ERR, NTCONNECT, "No queues for the VF is found"); + return ntconn_flow_err_status(data, len, ENODEV); + } + + /* Server and client must agree of the virtual port number */ + if (vp_internals->port != (in_port + 4U)) { + NT_LOG(ERR, NTCONNECT, + "Internal error: Virtual port out of sync"); + return ntconn_flow_err_status(data, len, ENODEV); + } + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "vport: %u\n", vp_internals->port); + NT_LOG(DBG, NTCONNECT, "vlan (forced): %u\n", vp_internals->vlan); +#endif + + port_eth[in_port].caller_id = vp_internals->port; + port_eth[in_port].forced_vlan_vid = vp_internals->vlan; + + *data = malloc(sizeof(struct flow_setport_return)); + if (*data) { + struct flow_setport_return *return_value = + (struct flow_setport_return *)*data; + *len = sizeof(struct flow_setport_return); + return_value->num_queues = vp_internals->vpq_nb_vq; + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "Number of queues: %u\n", + vp_internals->vpq_nb_vq); +#endif + for (i = 0; i < vp_internals->vpq_nb_vq && i < MAX_QUEUES; + i++) { +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "Queue: %u\n", + vp_internals->vpq[i].id); + NT_LOG(DBG, NTCONNECT, "HW ID: %u\n", + vp_internals->vpq[i].hw_id); +#endif + return_value->queues[i].id = vp_internals->vpq[i].id; + return_value->queues[i].hw_id = + vp_internals->vpq[i].hw_id; +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, + "Setup output port: %u, %04x:%02x:%02x.%x\n", + in_port, vp_internals->pci_dev->addr.domain, + vp_internals->pci_dev->addr.bus, + vp_internals->pci_dev->addr.devid, + vp_internals->pci_dev->addr.function); +#endif + } + return REQUEST_OK; + } + *len = 0; + return REQUEST_ERR; +} + +static int func_flow_flush(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr _unused, char **data, + int *len) +{ + struct flow_error error; + int port = MAX_PORTS; + int status = -1; + char *saveptr; + + set_error(&error); + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__); +#endif + + char *tok = strtok_r(*data, ",", &saveptr); + + if (tok) { + int length = strlen(tok); + + if (length > 5 && memcmp(tok, "port=", 5) == 0) + port = atoi(tok + 5); + } +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port); +#endif + + if (port >= MAX_PORTS) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "port id out of range"); + return ntconn_flow_err_reply_status(data, len, + NTCONN_FLOW_ERR_INVALID_PORT, + ENODEV); + } + + /* Call filter with data */ + status = flow_flush(port_eth[port].flw_dev, &error); + return copy_return_status(data, len, status, &error); +} + +static int func_flow_destroy(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr, char **data, int *len) +{ + struct flow_error error; + int port = MAX_PORTS; + uint64_t flow = 0; + int status = -1; + + struct destroy_flow_ntconnect *flow_cpy = + (struct destroy_flow_ntconnect *)&(*data)[hdr->len]; + + if (hdr->blob_len != sizeof(struct destroy_flow_ntconnect)) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Error in filter data"); + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_INVALID_REQUEST); + } + +#ifdef DEBUG_FLOW1 + NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__); +#endif + + port = flow_cpy->port; + +#ifdef DEBUG_FLOW1 + NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port); +#endif + + if (port >= MAX_PORTS) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "port id out of range"); + return ntconn_flow_err_reply_status(data, len, + NTCONN_FLOW_ERR_INVALID_PORT, + ENODEV); + } + + flow = flow_cpy->flow; + +#ifdef DEBUG_FLOW1 + NT_LOG(DBG, NTCONNECT, "flow=0x%016llX\n", + (unsigned long long)flow); +#endif + + /* Call filter with data */ + status = flow_destroy(port_eth[port].flw_dev, + (struct flow_handle *)flow, &error); + + *data = malloc(sizeof(struct flow_return_s)); + if (*data) { + struct flow_return_s *return_value = + (struct flow_return_s *)*data; + *len = sizeof(struct flow_return_s); + + return_value->status = status; + return_value->type = error.type; + strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN); + return REQUEST_OK; + } + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", + __func__); + return REQUEST_ERR; +} + +enum { + FLOW_API_FUNC_CREATE, + FLOW_API_FUNC_VALIDATE, +}; + +static uint64_t make_flow_create(int func, int port, + struct create_flow_ntconnect *flow_cpy, + int *status, struct flow_error *error) +{ + struct flow_elem elem[MAX_FLOW_STREAM_ELEM]; + struct flow_action action[MAX_FLOW_STREAM_ELEM]; + struct flow_action_vxlan_encap vxlan_tun; + struct flow_action_raw_encap encap; + struct flow_action_raw_decap decap; + struct flow_elem elem_tun[MAX_FLOW_STREAM_VXLAN_TUN_ELEM]; + int idx = -1; + + struct flow_attr *attr = &flow_cpy->attr; + struct flow_elem_cpy *elem_cpy = flow_cpy->elem; + struct flow_action_cpy *action_cpy = flow_cpy->action; + + error->type = FLOW_ERROR_GENERAL; + error->message = "To many flows"; + *status = NTCONN_FLOW_ERR_TO_MANY_FLOWS; + + attr->caller_id = port_eth[port].caller_id; + attr->forced_vlan_vid = port_eth[port].forced_vlan_vid; + + do { + idx++; + if (idx > MAX_FLOW_STREAM_ELEM) + goto error; + elem[idx].type = elem_cpy[idx].type; + if (!elem_cpy[idx].spec_cpy.valid) { + elem[idx].spec = NULL; + } else { + elem[idx].spec = + (void *)&elem_cpy[idx].spec_cpy.u.start_addr; + } + if (!elem_cpy[idx].mask_cpy.valid) { + elem[idx].mask = NULL; + } else { + elem[idx].mask = + (void *)&elem_cpy[idx].mask_cpy.u.start_addr; + } + } while (elem_cpy[idx].type != FLOW_ELEM_TYPE_END); + + idx = -1; + do { + idx++; + if (idx > MAX_FLOW_STREAM_ELEM) + goto error; + action[idx].type = action_cpy[idx].type; + if (!action_cpy[idx].conf_cpy.valid) { + action[idx].conf = NULL; + } else { + switch (action_cpy[idx].type) { + case FLOW_ACTION_TYPE_VXLAN_ENCAP: { + /* + * Special VXLAN ENCAP treatment create inner tunnel + * elements in action + */ + struct flow_elem_cpy *tun_elem_cpy = + (struct flow_elem_cpy *)action_cpy[idx] + .conf_cpy.u.vxlan.vxlan_tunnel; + vxlan_tun.vxlan_tunnel = elem_tun; + int tun_idx = -1; + + do { + tun_idx++; + if (tun_idx > + MAX_FLOW_STREAM_VXLAN_TUN_ELEM) { + error->message = + "To many VXLAN tunnels"; + goto error; + } + elem_tun[tun_idx].type = + tun_elem_cpy[tun_idx].type; + if (!tun_elem_cpy[tun_idx] + .spec_cpy.valid) { + elem_tun[tun_idx].spec = NULL; + } else { + elem_tun[tun_idx].spec = + (void *)&tun_elem_cpy[tun_idx] + .spec_cpy.u + .start_addr; + } + if (!tun_elem_cpy[tun_idx] + .mask_cpy.valid) { + elem_tun[tun_idx].mask = NULL; + } else { + elem_tun[tun_idx].mask = + (void *)&tun_elem_cpy[tun_idx] + .mask_cpy.u + .start_addr; + } + } while (tun_elem_cpy[tun_idx].type != + FLOW_ELEM_TYPE_END); + /* VXLAN ENCAP tunnel finished */ + action[idx].conf = &vxlan_tun; + } + break; + case FLOW_ACTION_TYPE_RSS: { + /* Need to set queue pointer */ + action_cpy[idx].conf_cpy.u.rss.rss.queue = + (const uint16_t *)&action_cpy[idx] + .conf_cpy.u.rss.cpy_queue; + action[idx].conf = (void *)&action_cpy[idx] + .conf_cpy.u.rss.rss; + } + break; + case FLOW_ACTION_TYPE_METER: { + /* Need to convert meter ID to uniq ID for the VF */ + action_cpy[idx].conf_cpy.u.meter.mtr_id = + ((flow_mtr_meters_supported() / + (RTE_MAX_ETHPORTS - 2)) * + (flow_cpy->vport - 4)) + + action_cpy[idx].conf_cpy.u.meter.mtr_id; + action[idx].conf = (void *)&action_cpy[idx] + .conf_cpy.u.meter; + } + break; + case FLOW_ACTION_TYPE_RAW_ENCAP: { + encap.preserve = NULL; + encap.data = + action_cpy[idx].conf_cpy.u.encap.data; + encap.item_count = + action_cpy[idx] + .conf_cpy.u.encap.item_count; + encap.size = + action_cpy[idx].conf_cpy.u.encap.size; + + for (int eidx = 0; + eidx < + action_cpy[idx].conf_cpy.u.encap.item_count; + eidx++) { + if (eidx > RAW_ENCAP_DECAP_ELEMS_MAX) { + error->message = + "To many encap items"; + goto error; + } + encap.items[eidx].type = + action_cpy[idx] + .conf_cpy.u.encap + .item_cpy[eidx] + .type; + if (action_cpy[idx] + .conf_cpy.u.encap + .item_cpy[eidx] + .spec_cpy.valid) { + encap.items[eidx].spec = + (void *)&action_cpy[idx] + .conf_cpy.u + .encap + .item_cpy[eidx] + .spec_cpy.u + .start_addr; + } else { + encap.items[eidx].spec = NULL; + } + if (action_cpy[idx] + .conf_cpy.u.encap + .item_cpy[eidx] + .mask_cpy.valid) { + encap.items[eidx].mask = + (void *)&action_cpy[idx] + .conf_cpy.u + .encap + .item_cpy[eidx] + .mask_cpy.u + .start_addr; + } else { + encap.items[eidx].mask = NULL; + } + } + action[idx].conf = &encap; + } + break; + case FLOW_ACTION_TYPE_RAW_DECAP: { + decap.data = + action_cpy[idx].conf_cpy.u.decap.data; + decap.item_count = + action_cpy[idx] + .conf_cpy.u.decap.item_count; + decap.size = + action_cpy[idx].conf_cpy.u.decap.size; + + for (int eidx = 0; + eidx < + action_cpy[idx].conf_cpy.u.decap.item_count; + eidx++) { + if (eidx > RAW_ENCAP_DECAP_ELEMS_MAX) { + error->message = + "To many decap items"; + goto error; + } + decap.items[eidx].type = + action_cpy[idx] + .conf_cpy.u.decap + .item_cpy[eidx] + .type; + if (action_cpy[idx] + .conf_cpy.u.decap + .item_cpy[eidx] + .spec_cpy.valid) { + decap.items[eidx].spec = + (void *)&action_cpy[idx] + .conf_cpy.u + .decap + .item_cpy[eidx] + .spec_cpy.u + .start_addr; + } else { + decap.items[eidx].spec = NULL; + } + if (action_cpy[idx] + .conf_cpy.u.decap + .item_cpy[eidx] + .mask_cpy.valid) { + decap.items[eidx].mask = + (void *)&action_cpy[idx] + .conf_cpy.u + .decap + .item_cpy[eidx] + .mask_cpy.u + .start_addr; + } else { + decap.items[eidx].mask = NULL; + } + } + action[idx].conf = &decap; + } + break; + default: { + /* Move conf pointer into conf_cpy data field */ + action[idx].conf = + (void *)&action_cpy[idx] + .conf_cpy.u.start_addr; + } + break; + } + } + } while (action_cpy[idx].type != FLOW_ACTION_TYPE_END); + + *status = NTCONN_FLOW_ERR_NONE; + if (func == FLOW_API_FUNC_VALIDATE) { + *status = flow_validate(port_eth[port].flw_dev, elem, action, + error); + return 0ULL; + } else { + return (uint64_t)flow_create(port_eth[port].flw_dev, attr, elem, + action, error); + } + +error: + return 0; +} + +static int func_flow_create(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr, char **data, int *len) +{ + int status; + struct flow_error error; + uint64_t flow = 0UL; + int port = MAX_PORTS; + + struct create_flow_ntconnect *flow_cpy = + (struct create_flow_ntconnect *)&(*data)[hdr->len]; + + if (hdr->blob_len != sizeof(struct create_flow_ntconnect)) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Error in filter data"); + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_INVALID_REQUEST); + } + + port = flow_cpy->port; + + if (port >= MAX_PORTS) { + NT_LOG(ERR, NTCONNECT, "port id out of range"); + return ntconn_flow_err_reply_status(data, len, + NTCONN_FLOW_ERR_INVALID_PORT, + ENODEV); + } + +#ifdef DEBUG_PARSING + int i; + + for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) { + if (flow_cpy[i].elem[i].type == FLOW_ELEM_TYPE_END) { + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_END\n"); + break; + } + switch (flow_cpy->elem[i].type) { + case FLOW_ELEM_TYPE_IPV4: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV4 %i\n", i); + NT_LOG(DBG, NTCONNECT, " src_ip: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[1] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[2] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[3] & 0xFF); + NT_LOG(DBG, NTCONNECT, " dst_ip: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[1] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[2] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[3] & 0xFF); + NT_LOG(DBG, NTCONNECT, " src_mask: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[1] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[2] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[3] & 0xFF); + NT_LOG(DBG, NTCONNECT, " dst_mask: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[1] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[2] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[3] & 0xFF); + break; + case FLOW_ELEM_TYPE_ETH: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ETH %i\n", i); + NT_LOG(DBG, NTCONNECT, + " src mac: %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[5] & 0xFF); + NT_LOG(DBG, NTCONNECT, + " dst mac: %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[5] & 0xFF); + NT_LOG(DBG, NTCONNECT, + " src mask %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[5] & 0xFF); + NT_LOG(DBG, NTCONNECT, + " dst mask %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[5] & 0xFF); + break; + case FLOW_ELEM_TYPE_VLAN: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VLAN %i\n", i); + break; + case FLOW_ELEM_TYPE_IPV6: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV6 %i\n", i); + break; + case FLOW_ELEM_TYPE_SCTP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_SCTP %i\n", i); + break; + case FLOW_ELEM_TYPE_TCP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_TCP %i\n", i); + break; + case FLOW_ELEM_TYPE_UDP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_UDP %i\n", i); + break; + case FLOW_ELEM_TYPE_ICMP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ICMP %i\n", i); + break; + case FLOW_ELEM_TYPE_VXLAN: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VXLAN %i\n", i); + break; + case FLOW_ELEM_TYPE_PORT_ID: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_PORT_ID %i\n", + i); + break; + default: + NT_LOG(DBG, NTCONNECT, "Unknown item %u\n", + flow_cpy->elem[i].type); + break; + } + } + + for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) { + uint32_t j; + + if (flow_cpy->action[i].type == FLOW_ACTION_TYPE_END) { + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_END\n"); + break; + } + switch (flow_cpy->action[i].type) { + case FLOW_ACTION_TYPE_RSS: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_RSS %i\n", i); + NT_LOG(DBG, NTCONNECT, " queue nb: %u\n", + flow_cpy->action[i].conf_cpy.u.rss.rss.queue_num); + NT_LOG(DBG, NTCONNECT, " queue: "); + for (j = 0; + j < flow_cpy->action[i] + .conf_cpy.u.rss.rss.queue_num && + j < FLOW_MAX_QUEUES; + j++) { + NT_LOG(DBG, NTCONNECT, "%u ", + flow_cpy->action[i] + .conf_cpy.u.rss.cpy_queue[j]); + } + NT_LOG(DBG, NTCONNECT, "\n"); + break; + + case FLOW_ACTION_TYPE_POP_VLAN: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_POP_VLAN %i\n", + i); + break; + case FLOW_ACTION_TYPE_PUSH_VLAN: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_PUSH_VLAN %i\n", i); + break; + case FLOW_ACTION_TYPE_SET_VLAN_VID: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_SET_VLAN_VID %i\n", i); + break; + case FLOW_ACTION_TYPE_SET_VLAN_PCP: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_SET_VLAN_PCP %i\n", i); + break; + case FLOW_ACTION_TYPE_VXLAN_DECAP: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_VXLAN_DECAP %i\n", i); + break; + case FLOW_ACTION_TYPE_VXLAN_ENCAP: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_VXLAN_ENCAP %i\n", i); + break; + case FLOW_ACTION_TYPE_DROP: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_DROP %i\n", i); + break; + case FLOW_ACTION_TYPE_COUNT: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_COUNT %i\n", + i); + break; + case FLOW_ACTION_TYPE_MARK: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_MARK %i\n", i); + break; + case FLOW_ACTION_TYPE_PORT_ID: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_PORT_ID %i: ID=%u\n", i, + flow_cpy->action[i].conf_cpy.u.port_id.id); + break; + case FLOW_ACTION_TYPE_QUEUE: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_QUEUE %i: queue=%u\n", i, + flow_cpy->action[i].conf_cpy.u.queue.index); + break; + case FLOW_ACTION_TYPE_SET_TAG: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_SET_TAG %i: idx=%u, data=%u, mask=%X\n", + i, flow_cpy->action[i].conf_cpy.u.tag.index, + flow_cpy->action[i].conf_cpy.u.tag.data, + flow_cpy->action[i].conf_cpy.u.tag.mask); + break; + default: + NT_LOG(DBG, NTCONNECT, "Unknown action %u\n", + flow_cpy->action[i].type); + break; + } + } +#endif + + /* Call filter with data */ + flow = make_flow_create(FLOW_API_FUNC_CREATE, port, flow_cpy, &status, + &error); + if (flow) { + *data = malloc(sizeof(struct create_flow_return_s)); + if (!*data) + goto create_flow_error_malloc; + struct create_flow_return_s *return_value = + (struct create_flow_return_s *)*data; + *len = sizeof(struct create_flow_return_s); + return_value->flow = flow; + return REQUEST_OK; + } + + *data = malloc(sizeof(struct flow_error_return_s)); + if (!*data) + goto create_flow_error_malloc; + struct flow_error_return_s *return_value = + (struct flow_error_return_s *)*data; + *len = sizeof(struct flow_error_return_s); + return_value->type = error.type; + strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN); + return REQUEST_OK; + +create_flow_error_malloc: + + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", __func__); + return REQUEST_ERR; +} + +static int func_flow_validate(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr, char **data, + int *len) +{ + int status; + struct flow_error error; + int port = MAX_PORTS; + + struct create_flow_ntconnect *flow_cpy = + (struct create_flow_ntconnect *)&(*data)[hdr->len]; + + if (hdr->blob_len != sizeof(struct create_flow_ntconnect)) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Error in filter data"); + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_INVALID_REQUEST); + } + + set_error(&error); + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "func_flow_create\n"); +#endif + + port = flow_cpy->port; + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port); +#endif + + if (port >= MAX_PORTS) { + NT_LOG(ERR, NTCONNECT, "port id out of range"); + return ntconn_flow_err_reply_status(data, len, + NTCONN_FLOW_ERR_INVALID_PORT, ENODEV); + } + +#ifdef DEBUG_PARSING + int i; + + for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) { + if (flow_cpy[i].elem[i].type == FLOW_ELEM_TYPE_END) { + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_END\n"); + break; + } + switch (flow_cpy->elem[i].type) { + case FLOW_ELEM_TYPE_IPV4: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV4 %i\n", i); + NT_LOG(DBG, NTCONNECT, " src_ip: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF); + NT_LOG(DBG, NTCONNECT, " dst_ip: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF); + NT_LOG(DBG, NTCONNECT, " src_mask: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF); + NT_LOG(DBG, NTCONNECT, " dst_mask: %u.%u.%u.%u\n", + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF, + ((const char *)&flow_cpy->elem[i] + .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF); + break; + case FLOW_ELEM_TYPE_ETH: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ETH %i\n", i); + NT_LOG(DBG, NTCONNECT, + " src mac: %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[5] & 0xFF); + NT_LOG(DBG, NTCONNECT, + " dst mac: %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[5] & 0xFF); + NT_LOG(DBG, NTCONNECT, + " src mask %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[5] & 0xFF); + NT_LOG(DBG, NTCONNECT, + " dst mask %02X:%02X:%02X:%02X:%02X:%02X\n", + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[0] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[1] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[2] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[3] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[4] & 0xFF, + flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[5] & 0xFF); + break; + case FLOW_ELEM_TYPE_VLAN: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VLAN %i\n", i); + break; + case FLOW_ELEM_TYPE_IPV6: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV6 %i\n", i); + break; + case FLOW_ELEM_TYPE_SCTP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_SCTP %i\n", i); + break; + case FLOW_ELEM_TYPE_TCP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_TCP %i\n", i); + break; + case FLOW_ELEM_TYPE_UDP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_UDP %i\n", i); + break; + case FLOW_ELEM_TYPE_ICMP: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ICMP %i\n", i); + break; + case FLOW_ELEM_TYPE_VXLAN: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VXLAN %i\n", i); + break; + case FLOW_ELEM_TYPE_PORT_ID: + NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_PORT_ID %i\n", + i); + break; + default: + NT_LOG(DBG, NTCONNECT, "Unknown item %u\n", + flow_cpy->elem[i].type); + break; + } + } + + for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) { + uint32_t j; + + if (flow_cpy->action[i].type == FLOW_ACTION_TYPE_END) { + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_END\n"); + break; + } + switch (flow_cpy->action[i].type) { + case FLOW_ACTION_TYPE_RSS: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_RSS %i\n", i); + NT_LOG(DBG, NTCONNECT, " queue nb: %u\n", + flow_cpy->action[i].conf_cpy.u.rss.rss.queue_num); + NT_LOG(DBG, NTCONNECT, " queue: "); + for (j = 0; + j < flow_cpy->action[i] + .conf_cpy.u.rss.rss.queue_num && + j < FLOW_MAX_QUEUES; + j++) { + NT_LOG(DBG, NTCONNECT, "%u ", + flow_cpy->action[i] + .conf_cpy.u.rss.cpy_queue[j]); + } + NT_LOG(DBG, NTCONNECT, "\n"); + break; + + case FLOW_ACTION_TYPE_POP_VLAN: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_POP_VLAN %i\n", + i); + break; + case FLOW_ACTION_TYPE_PUSH_VLAN: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_PUSH_VLAN %i\n", i); + break; + case FLOW_ACTION_TYPE_SET_VLAN_VID: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_SET_VLAN_VID %i\n", i); + break; + case FLOW_ACTION_TYPE_SET_VLAN_PCP: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_SET_VLAN_PCP %i\n", i); + break; + case FLOW_ACTION_TYPE_VXLAN_DECAP: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_VXLAN_DECAP %i\n", i); + break; + case FLOW_ACTION_TYPE_VXLAN_ENCAP: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_VXLAN_ENCAP %i\n", i); + break; + case FLOW_ACTION_TYPE_DROP: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_DROP %i\n", i); + break; + case FLOW_ACTION_TYPE_COUNT: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_COUNT %i\n", + i); + break; + case FLOW_ACTION_TYPE_MARK: + NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_MARK %i\n", i); + break; + case FLOW_ACTION_TYPE_PORT_ID: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_PORT_ID %i: ID=%u\n", i, + flow_cpy->action[i].conf_cpy.u.port_id.id); + break; + case FLOW_ACTION_TYPE_QUEUE: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_QUEUE %i: queue=%u\n", i, + flow_cpy->action[i].conf_cpy.u.queue.index); + break; + case FLOW_ACTION_TYPE_SET_TAG: + NT_LOG(DBG, NTCONNECT, + "FLOW_ACTION_TYPE_SET_TAG %i: idx=%u, data=%u, mask=%X\n", + i, flow_cpy->action[i].conf_cpy.u.tag.index, + flow_cpy->action[i].conf_cpy.u.tag.data, + flow_cpy->action[i].conf_cpy.u.tag.mask); + break; + default: + NT_LOG(DBG, NTCONNECT, "Unknown action %u\n", + flow_cpy->action[i].type); + break; + } + } +#endif + + /* Call filter with data */ + make_flow_create(FLOW_API_FUNC_VALIDATE, port, flow_cpy, &status, + &error); + return copy_return_status(data, len, status, &error); + + /* Call filter with data */ + make_flow_create(FLOW_API_FUNC_VALIDATE, port, flow_cpy, &status, + &error); + if (!status) { + *data = malloc(sizeof(struct validate_flow_return_s)); + if (!*data) + goto validate_flow_error_malloc; + struct validate_flow_return_s *return_value = + (struct validate_flow_return_s *)*data; + *len = sizeof(struct validate_flow_return_s); + return_value->status = 0; + return REQUEST_OK; + } + + *data = malloc(sizeof(struct flow_error_return_s)); + if (!*data) + goto validate_flow_error_malloc; + struct flow_error_return_s *return_value = + (struct flow_error_return_s *)*data; + *len = sizeof(struct flow_error_return_s); + return_value->type = error.type; + strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN); + return_value->status = status; + return REQUEST_OK; + +validate_flow_error_malloc: + + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", __func__); + return REQUEST_ERR; +} + +static int func_flow_query(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr, char **data, int *len) +{ + int status; + struct flow_error error; + int port = MAX_PORTS; + struct flow_handle *flow; + + struct query_flow_ntconnect *flow_cpy = + (struct query_flow_ntconnect *)&(*data)[hdr->len]; + + if (hdr->blob_len != sizeof(struct query_flow_ntconnect)) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Error in filter data"); + return ntconn_error(data, len, this_module_name, + NTCONN_ERR_CODE_INVALID_REQUEST); + } + + set_error(&error); + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__); +#endif + + port = flow_cpy->port; + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port); +#endif + + if (port >= MAX_PORTS) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "port id out of range"); + return ntconn_flow_err_reply_status(data, len, + NTCONN_FLOW_ERR_INVALID_PORT, ENODEV); + } + +#ifdef DEBUG_FLOW + NT_LOG(DBG, NTCONNECT, "flow=0x%016llX\n", + (unsigned long long)flow_cpy->flow); +#endif + + flow = (struct flow_handle *)flow_cpy->flow; + + const struct flow_action action = { + flow_cpy->action.type, + (const void *)&flow_cpy->action.conf_cpy.u.count + }; + + /* Call filter with data */ + void *data_out = NULL; + uint32_t length = 0; + + status = flow_query(port_eth[port].flw_dev, flow, &action, &data_out, + &length, &error); + + *data = malloc(sizeof(struct query_flow_return_s) + length); + if (*data) { + struct query_flow_return_s *return_value = + (struct query_flow_return_s *)*data; + *len = sizeof(struct query_flow_return_s) + length; + + return_value->status = status; + return_value->type = error.type; + strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN); + + if (data_out) { + memcpy(return_value->data, data_out, length); + return_value->data_length = length; + free(data_out); + } else { + return_value->data_length = 0; + } + return REQUEST_OK; + } + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", + __func__); + return REQUEST_ERR; +} + +static int flow_request(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr, char *function, + char **data, int *len) +{ + return execute_function(this_module_name, hdl, client_id, hdr, function, + adapter_entry_funcs, data, len, 0); +} + +static void flow_free_data(void *hdl _unused, char *data) +{ + if (data) + free(data); +} + +static void flow_client_cleanup(void *hdl _unused, int client_id _unused) +{ + /* Nothing to do */ +} + +static const ntconnapi_t ntconn_flow_op = { this_module_name, + NTCONN_FLOW_VERSION_MAJOR, + NTCONN_FLOW_VERSION_MINOR, + flow_request, + flow_free_data, + flow_client_cleanup + }; + +int ntconn_flow_register(struct drv_s *drv) +{ + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + if (flow_hdl[i].drv == NULL) + break; + } + if (i == MAX_CLIENTS) { + NT_LOG(ERR, NTCONNECT, + "Cannot register more adapters into NtConnect framework"); + return -1; + } + + flow_hdl[i].drv = drv; + return register_ntconn_mod(&drv->p_dev->addr, (void *)&flow_hdl[i], + &ntconn_flow_op); +} diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c b/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c new file mode 100644 index 0000000000..7c21690f8b --- /dev/null +++ b/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c @@ -0,0 +1,517 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include "ntnic_ethdev.h" +#include "ntconnect.h" +#include "ntos_system.h" +#include "ntconn_modules.h" +#include "ntconn_mod_helper.h" +#include "nt_util.h" +#include "ntlog.h" +#include "ntnic_vf_vdpa.h" + +#include "ntconnect_api_meter.h" +#include "flow_api_profile_inline.h" + +#include +#include +#include + +#define NTCONN_METER_VERSION_MAJOR 0U +#define NTCONN_METER_VERSION_MINOR 1U + +#define this_module_name "meter" + +#define MAX_CLIENTS 32 + +#define UNUSED __rte_unused + +static struct meter_hdl_s { + struct drv_s *drv; +} meter_hdl[MAX_CLIENTS]; + +static ntconn_err_t ntconn_err[] = { + { NTCONN_METER_ERR_NONE, "Success" }, + { NTCONN_METER_ERR_INTERNAL_ERROR, "Internal error" }, + { NTCONN_METER_ERR_INVALID_PORT, "Invalid virtual port" }, + { NTCONN_METER_ERR_PROFILE_ID, "Profile ID out of range" }, + { NTCONN_METER_ERR_POLICY_ID, "Policy ID out of range" }, + { NTCONN_METER_ERR_METER_ID, "Meter ID out of range" }, + { -1, NULL } +}; + +/********************************************************************/ +/* Get error message corresponding to the error code */ +/********************************************************************/ +static const char *get_error_msg(uint32_t err_code) +{ + int idx = 0; + + if (err_code < NTCONN_METER_ERR_INTERNAL_ERROR) { + const ntconn_err_t *err_msg = get_ntconn_error(err_code); + + return err_msg->err_text; + } + while (ntconn_err[idx].err_code != (uint32_t)-1 && + ntconn_err[idx].err_code != err_code) + idx++; + if (ntconn_err[idx].err_code == (uint32_t)-1) + idx = 1; + return ntconn_err[idx].err_text; +} + +/* + * Filter functions + */ +static int func_meter_get_capabilities(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_meter_setup(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, int *len); +static int func_meter_read(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, int *len); +static struct func_s adapter_entry_funcs[] = { + { "capabilities", NULL, func_meter_get_capabilities }, + { "setup", NULL, func_meter_setup }, + { "read", NULL, func_meter_read }, + { NULL, NULL, NULL }, +}; + +/**********************************************************************/ +/* copy error message corresponding to the error code to error struct */ +/**********************************************************************/ +static void copy_mtr_error(struct rte_mtr_error *error, uint32_t err) +{ + error->type = RTE_MTR_ERROR_TYPE_UNSPECIFIED; + error->message = get_error_msg(err); + error->cause = NULL; +} + +static int func_meter_get_capabilities(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + char *saveptr; + uint8_t vport = 0; + uint8_t port = 0; + int status; + struct rte_mtr_capabilities cap; + struct rte_mtr_error error; + +#ifdef DEBUG_METER + NT_LOG(DBG, NTCONNECT, "%s: \"%s\"\n", __func__, *data); +#endif + + char *tok = strtok_r(*data, ",", &saveptr); + + if (tok) { + int length = strlen(tok); + + if (length > 6 && memcmp(tok, "vport=", 6) == 0) + vport = atoi(tok + 6); + } +#ifdef DEBUG_METER + NT_LOG(DBG, NTCONNECT, "vport=%u\n", vport); +#endif + + if (vport == 0 || vport > 64) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Virtual port is invalid"); + copy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT); + status = -ENODEV; + goto error_get_capa; + } + + port = vport & 1; + status = rte_mtr_capabilities_get(port, &cap, &error); + if (status == 0) { + /* Handle success by copying the return values to the return struct */ + *data = malloc(sizeof(struct meter_capabilities_return_s)); + if (!*data) + goto error_get_capa_malloc; + struct meter_capabilities_return_s *return_value = + (struct meter_capabilities_return_s *)*data; + *len = sizeof(struct meter_capabilities_return_s); + memcpy(&return_value->cap, &cap, + sizeof(struct rte_mtr_capabilities)); + return REQUEST_OK; + } + +error_get_capa: + + /* Handle errors by copy errors to the error struct */ + NT_LOG(ERR, NTCONNECT, "Failed to get capabilities for port %u (%u)", + port, vport); + *data = malloc(sizeof(struct meter_error_return_s)); + if (!*data) + goto error_get_capa_malloc; + struct meter_error_return_s *return_value = + (struct meter_error_return_s *)*data; + *len = sizeof(struct meter_error_return_s); + return_value->status = status; + return_value->type = error.type; + strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN); + return REQUEST_OK; + +error_get_capa_malloc: + + *len = 0; + return REQUEST_ERR; +} + +static int func_meter_setup(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr, char **data, int *len) +{ + char *saveptr; + uint8_t port; + uint32_t max_id; + int status; + struct rte_mtr_error error; + int command = UNKNOWN_CMD; + +#ifdef DEBUG_METER + NT_LOG(DBG, NTCONNECT, "%s: \"%s\"\n", __func__, *data); +#endif + + if (hdr->blob_len != sizeof(struct meter_setup_s)) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Error: Profile data size is illegal"); + copy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST); + status = -EINTR; + goto error_meter_setup; + } + + /* Get the data blob containing the data for the meter function */ + struct meter_setup_s *cpy_data = + (struct meter_setup_s *)&(*data)[hdr->len]; + + if (cpy_data->vport < 4 || cpy_data->vport > 128) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Virtual port is invalid"); + copy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT); + status = -ENODEV; + goto error_meter_setup; + } + + char *tok = strtok_r(*data, ",", &saveptr); + + if (tok) { + int length = strlen(tok); + + if (length == 6) { + if (memcmp(tok, "addpro", 6) == 0) + command = ADD_PROFILE; + + else if (memcmp(tok, "delpro", 6) == 0) + command = DEL_PROFILE; + + else if (memcmp(tok, "addpol", 6) == 0) + command = ADD_POLICY; + + else if (memcmp(tok, "delpol", 6) == 0) + command = DEL_POLICY; + + else if (memcmp(tok, "crtmtr", 6) == 0) + command = CREATE_MTR; + + else if (memcmp(tok, "delmtr", 6) == 0) + command = DEL_MTR; + } + } + + if (command == UNKNOWN_CMD) { + NT_LOG(ERR, NTCONNECT, "Error: Invalid command"); + copy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST); + status = -EINVAL; + goto error_meter_setup; + } + + /* Port will be either 0 or 1 depending on the VF. */ + port = cpy_data->vport & 1; + + switch (command) { + case ADD_PROFILE: + max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2); + if (cpy_data->id > max_id) { + NT_LOG(ERR, NTCONNECT, + "Error: Profile ID %u out of range. Max value is %u", + cpy_data->id, max_id); + copy_mtr_error(&error, NTCONN_METER_ERR_PROFILE_ID); + status = -EINVAL; + goto error_meter_setup; + } + cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id; + status = rte_mtr_meter_profile_add(port, cpy_data->id, + &cpy_data->profile, &error); + if (status != 0) { + NT_LOG(ERR, NTCONNECT, + "Failed to add profile for port %u (%u)", port, + cpy_data->vport); + } + break; + case DEL_PROFILE: + max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2); + if (cpy_data->id > max_id) { + NT_LOG(ERR, NTCONNECT, + "Error: Profile ID %u out of range. Max value is %u", + cpy_data->id, max_id); + copy_mtr_error(&error, NTCONN_METER_ERR_PROFILE_ID); + status = -EINVAL; + goto error_meter_setup; + } + cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id; + status = rte_mtr_meter_profile_delete(port, cpy_data->id, + &error); + if (status != 0) { + NT_LOG(ERR, NTCONNECT, + "Failed to delete profile for port %u (%u)", + port, cpy_data->vport); + } + break; + case ADD_POLICY: + max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2); + if (cpy_data->id > max_id) { + NT_LOG(ERR, NTCONNECT, + "Error: Policy ID %u out of range. Max value is %u", + cpy_data->id, max_id); + copy_mtr_error(&error, NTCONN_METER_ERR_POLICY_ID); + status = -EINVAL; + goto error_meter_setup; + } + cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id; + cpy_data->p.policy.actions[RTE_COLOR_GREEN] = + cpy_data->p.actions_green; + cpy_data->p.policy.actions[RTE_COLOR_YELLOW] = + cpy_data->p.actions_yellow; + cpy_data->p.policy.actions[RTE_COLOR_RED] = + cpy_data->p.actions_red; + status = rte_mtr_meter_policy_add(port, cpy_data->id, + &cpy_data->p.policy, &error); + if (status != 0) { + NT_LOG(ERR, NTCONNECT, + "Failed to add policy for port %u (%u)", port, + cpy_data->vport); + } + break; + case DEL_POLICY: + max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2); + if (cpy_data->id > max_id) { + NT_LOG(ERR, NTCONNECT, + "Error: Policy ID %u out of range. Max value is %u", + cpy_data->id, max_id); + copy_mtr_error(&error, NTCONN_METER_ERR_POLICY_ID); + status = -EINVAL; + goto error_meter_setup; + } + cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id; + status = + rte_mtr_meter_policy_delete(port, cpy_data->id, &error); + if (status != 0) { + NT_LOG(ERR, NTCONNECT, + "Failed to delete policy for port %u (%u)", port, + cpy_data->vport); + } + break; + case CREATE_MTR: + max_id = flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2); + if (cpy_data->id > max_id) { + NT_LOG(ERR, NTCONNECT, + "Error: Meter ID %u out of range. Max value is %u", + cpy_data->id, max_id); + copy_mtr_error(&error, NTCONN_METER_ERR_METER_ID); + status = -EINVAL; + goto error_meter_setup; + } + cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id; + cpy_data->mtr_params.meter_profile_id = + ((cpy_data->vport - 4) * + (flow_mtr_meter_policy_n_max() / + (RTE_MAX_ETHPORTS - 2))) + + cpy_data->mtr_params.meter_profile_id; + cpy_data->mtr_params.meter_policy_id = + ((cpy_data->vport - 4) * + (flow_mtr_meter_policy_n_max() / + (RTE_MAX_ETHPORTS - 2))) + + cpy_data->mtr_params.meter_policy_id; + status = rte_mtr_create(port, cpy_data->id, + &cpy_data->mtr_params, cpy_data->shared, + &error); + if (status != 0) { + NT_LOG(ERR, NTCONNECT, + "Failed to create meter for port %u (%u)", port, + cpy_data->vport); + } + break; + case DEL_MTR: + max_id = flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2); + if (cpy_data->id > max_id) { + NT_LOG(ERR, NTCONNECT, + "Error: Meter ID %u out of range. Max value is %u", + cpy_data->id, max_id); + copy_mtr_error(&error, NTCONN_METER_ERR_METER_ID); + status = -EINVAL; + goto error_meter_setup; + } + cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id; + status = rte_mtr_destroy(port, cpy_data->id, &error); + if (status != 0) { + NT_LOG(ERR, NTCONNECT, + "Failed to destroy meter for port %u (%u)", port, + cpy_data->vport); + } + break; + } + + if (status == 0) { + /* Handle success by copying the return values to the return struct */ + *data = malloc(sizeof(struct meter_return_s)); + if (!*data) + goto error_meter_setup_malloc; + struct meter_return_s *return_value = + (struct meter_return_s *)*data; + *len = sizeof(struct meter_return_s); + return_value->status = 0; + return REQUEST_OK; + } + +error_meter_setup: + + /* Handle errors by copy errors to the error struct */ + *data = malloc(sizeof(struct meter_error_return_s)); + if (!*data) + goto error_meter_setup_malloc; + struct meter_error_return_s *return_value = + (struct meter_error_return_s *)*data; + *len = sizeof(struct meter_error_return_s); + return_value->status = status; + return_value->type = error.type; + strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN); + return REQUEST_OK; + +error_meter_setup_malloc: + + *len = 0; + return REQUEST_ERR; +} + +static int func_meter_read(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr, char **data, int *len) +{ + uint8_t port = 0; + int status; + struct rte_mtr_error error; + struct rte_mtr_stats stats; + uint64_t stats_mask; + +#ifdef DEBUG_METER + NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__); +#endif + + if (hdr->blob_len != sizeof(struct meter_get_stat_s)) { + *len = 0; + NT_LOG(ERR, NTCONNECT, + "Error: Read meter stats data size is illegal"); + copy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST); + status = -EINTR; + goto error_meter_read; + } + + /* Get the data blob containing the data for the meter function */ + struct meter_get_stat_s *cpy_data = + (struct meter_get_stat_s *)&(*data)[hdr->len]; + + if (cpy_data->vport < 4 || cpy_data->vport > 128) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "Virtual port is invalid"); + copy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT); + status = -ENODEV; + goto error_meter_read; + } + + port = cpy_data->vport & 1; + cpy_data->mtr_id = + ((cpy_data->vport - 4) * + (flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2))) + + cpy_data->mtr_id; + status = rte_mtr_stats_read(port, cpy_data->mtr_id, &stats, &stats_mask, + cpy_data->clear, &error); + if (status == 0) { + /* Handle success by copying the return values to the return struct */ + *data = malloc(sizeof(struct meter_return_stat_s)); + if (!*data) + goto error_meter_read_malloc; + struct meter_return_stat_s *return_value = + (struct meter_return_stat_s *)*data; + *len = sizeof(struct meter_return_stat_s); + return_value->stats_mask = stats_mask; + memcpy(&return_value->stats, &stats, + sizeof(struct rte_mtr_stats)); + return REQUEST_OK; + } + +error_meter_read: + /* Handle errors by copy errors to the error struct */ + NT_LOG(ERR, NTCONNECT, "Failed to read meter stats"); + *data = malloc(sizeof(struct meter_error_return_s)); + if (!*data) + goto error_meter_read_malloc; + struct meter_error_return_s *return_value = + (struct meter_error_return_s *)*data; + *len = sizeof(struct meter_error_return_s); + strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN); + return_value->status = status; + return_value->type = error.type; + return REQUEST_OK; + +error_meter_read_malloc: + *len = 0; + return REQUEST_ERR; +} + +static int meter_request(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr, char *function, + char **data, int *len) +{ + return execute_function(this_module_name, hdl, client_id, hdr, function, + adapter_entry_funcs, data, len, 0); +} + +static void meter_free_data(void *hdl _unused, char *data) +{ + if (data) + free(data); +} + +static void meter_client_cleanup(void *hdl _unused, int client_id _unused) +{ + /* Nothing to do */ +} + +static const ntconnapi_t ntconn_meter_op = { this_module_name, + NTCONN_METER_VERSION_MAJOR, + NTCONN_METER_VERSION_MINOR, + meter_request, + meter_free_data, + meter_client_cleanup + }; + +int ntconn_meter_register(struct drv_s *drv) +{ + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + if (meter_hdl[i].drv == NULL) + break; + } + if (i == MAX_CLIENTS) { + NT_LOG(ERR, NTCONNECT, + "Cannot register more adapters into NtConnect framework"); + return -1; + } + + meter_hdl[i].drv = drv; + return register_ntconn_mod(&drv->p_dev->addr, (void *)&meter_hdl[i], + &ntconn_meter_op); +} diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h b/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h new file mode 100644 index 0000000000..ea379015fe --- /dev/null +++ b/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTCONN_MODULES_H_ +#define _NTCONN_MODULES_H_ + +#include "ntos_system.h" + +/* + * All defined NT connection modules + */ +int ntconn_adap_register(struct drv_s *drv); +int ntconn_stat_register(struct drv_s *drv); +int ntconn_flow_register(struct drv_s *drv); +int ntconn_meter_register(struct drv_s *drv); +int ntconn_test_register(struct drv_s *drv); + +#endif /* _NTCONN_MODULES_H_ */ diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c b/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c new file mode 100644 index 0000000000..437cf9ddad --- /dev/null +++ b/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c @@ -0,0 +1,877 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include +#include +#include + +#include "ntconnect.h" +#include "ntconnect_api_statistic.h" +#include "ntos_system.h" +#include "ntconn_modules.h" +#include "ntconn_mod_helper.h" +#include "nt_util.h" +#include "ntlog.h" +#include "ntnic_xstats.h" + +#define STAT_VERSION_MAJOR 0U +#define STAT_VERSION_MINOR 2U + +#define this_module_name "stat" + +/* + * Supported Stat Layout Versions + */ +#define NUM_LAYOUT_VERSIONS_SUPPORTED (RTE_DIM(layout_versions_supported)) +static int layout_versions_supported[] = { + 6, + /* + * Add here other layout versions to support + * When more versions are added, add new version dependent binary reply structures + * in ntconnect_api.h file for client to select on reading layout_version + */ +}; + +enum snap_addr_select_e { + SNAP_COLORS, + SNAP_QUEUES, + SNAP_RX_PORT, + SNAP_TX_PORT, + SNAP_ADDR_COUNT +}; + +struct snap_addr_s { + const uint64_t *ptr; + unsigned int size; +}; + +struct snaps_s { + int client_id; + /* Pointers into buffer */ + struct snap_addr_s snap_addr[SNAP_ADDR_COUNT]; + uint64_t *buffer; + struct snaps_s *next; +}; + +static struct stat_hdl { + struct drv_s *drv; + nt4ga_stat_t *p_nt4ga_stat; + struct snaps_s *snaps_base; +} stat_hdl; + +enum stat_type_e { + STAT_TYPE_COLOR, + STAT_TYPE_QUEUE, + STAT_TYPE_RX, + STAT_TYPE_TX, + STAT_TYPE_FLOWMATCHER +}; + +static int func_get_snap_colors(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_get_snap_queues(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_get_snap_rx_port(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_get_snap_tx_port(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static struct func_s func_snap_level2[] = { + { "colors", NULL, func_get_snap_colors }, + { "queues", NULL, func_get_snap_queues }, + { "rx_counters", NULL, func_get_snap_rx_port }, + { "tx_counters", NULL, func_get_snap_tx_port }, + { NULL, NULL, NULL }, +}; + +static int func_get_layout_version(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_get_flm(void *hdl, int client_id, struct ntconn_header_s *hdr, + char **data, int *len); +static int func_get_color(void *hdl, int client_id, struct ntconn_header_s *hdr, + char **data, int *len); +static int func_get_queue(void *hdl, int client_id, struct ntconn_header_s *hdr, + char **data, int *len); +static int func_get_rx_counters(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_get_tx_counters(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); +static int func_get_flm_layout_version(void *hdl, int client_id, + struct ntconn_header_s *hdr, char **data, + int *len); + +static struct func_s funcs_get_level1[] = { + { "snapshot", func_snap_level2, NULL }, + { "layout_version", NULL, func_get_layout_version }, + { "flm", NULL, func_get_flm }, + { "colors", NULL, func_get_color }, + { "queues", NULL, func_get_queue }, + { "rx_counters", NULL, func_get_rx_counters }, + { "tx_counters", NULL, func_get_tx_counters }, + { "flm_layout_version", NULL, func_get_flm_layout_version }, + { NULL, NULL, NULL }, +}; + +/* + * Entry level + */ +static int func_snapshot(void *hdl, int client_id, struct ntconn_header_s *hdr, + char **data, int *len); +static struct func_s stat_entry_funcs[] = { + { "get", funcs_get_level1, NULL }, + { "snapshot", NULL, func_snapshot }, + { NULL, NULL, NULL }, +}; + +static int read_flm(nt4ga_stat_t *hwstat, uint64_t *val, int nbc) +{ + struct ntc_stat_get_data_s *cdata = (struct ntc_stat_get_data_s *)val; + + cdata->nb_counters = (uint64_t)nbc; + cdata->timestamp = hwstat->last_timestamp; + cdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch; + + struct rte_eth_xstat stats[100]; + struct rte_eth_xstat_name names[100]; + int cnt_names = nthw_xstats_get_names(hwstat, names, 100, + hwstat->mp_nthw_stat->mb_is_vswitch); + int cnt_values = nthw_xstats_get(hwstat, stats, 100, + hwstat->mp_nthw_stat->mb_is_vswitch, 0); + assert(cnt_names == cnt_values); + + /* virt/cap same */ + struct flowmatcher_type_fields_s *flm = + (struct flowmatcher_type_fields_s *)cdata->data; + if (hwstat->mp_stat_structs_flm) { + int c; + + for (c = 0; c < nbc; c++) { + flm->current = hwstat->mp_stat_structs_flm->current; + flm->learn_done = hwstat->mp_stat_structs_flm->learn_done; + flm->learn_ignore = + hwstat->mp_stat_structs_flm->learn_ignore; + flm->learn_fail = hwstat->mp_stat_structs_flm->learn_fail; + flm->unlearn_done = + hwstat->mp_stat_structs_flm->unlearn_done; + flm->unlearn_ignore = + hwstat->mp_stat_structs_flm->unlearn_ignore; + flm->auto_unlearn_done = + hwstat->mp_stat_structs_flm->auto_unlearn_done; + flm->auto_unlearn_ignore = + hwstat->mp_stat_structs_flm->auto_unlearn_ignore; + flm->auto_unlearn_fail = + hwstat->mp_stat_structs_flm->auto_unlearn_fail; + flm->timeout_unlearn_done = + hwstat->mp_stat_structs_flm->timeout_unlearn_done; + flm->rel_done = hwstat->mp_stat_structs_flm->rel_done; + flm->rel_ignore = hwstat->mp_stat_structs_flm->rel_ignore; + flm->prb_done = hwstat->mp_stat_structs_flm->prb_done; + flm->prb_ignore = hwstat->mp_stat_structs_flm->prb_ignore; + + flm->sta_done = hwstat->mp_stat_structs_flm->sta_done; + flm->inf_done = hwstat->mp_stat_structs_flm->inf_done; + flm->inf_skip = hwstat->mp_stat_structs_flm->inf_skip; + flm->pck_hit = hwstat->mp_stat_structs_flm->pck_hit; + flm->pck_miss = hwstat->mp_stat_structs_flm->pck_miss; + flm->pck_unh = hwstat->mp_stat_structs_flm->pck_unh; + flm->pck_dis = hwstat->mp_stat_structs_flm->pck_dis; + flm->csh_hit = hwstat->mp_stat_structs_flm->csh_hit; + flm->csh_miss = hwstat->mp_stat_structs_flm->csh_miss; + flm->csh_unh = hwstat->mp_stat_structs_flm->csh_unh; + flm->cuc_start = hwstat->mp_stat_structs_flm->cuc_start; + flm->cuc_move = hwstat->mp_stat_structs_flm->cuc_move; + } + } else { + memset(flm, 0, sizeof(*hwstat->mp_stat_structs_flm)); + } + return nbc * NUM_STAT_RECORD_TYPE_FLOWMATCHER + STAT_INFO_ELEMENTS; +} + +static int read_colors(nt4ga_stat_t *hwstat, uint64_t *val, int nbc) +{ + struct ntc_stat_get_data_s *cdata = (struct ntc_stat_get_data_s *)val; + + cdata->nb_counters = (uint64_t)nbc; + cdata->timestamp = hwstat->last_timestamp; + cdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch; + + /* virt/cap same */ + struct color_type_fields_s *clr = + (struct color_type_fields_s *)cdata->data; + int c; + + for (c = 0; c < nbc; c++) { + clr->pkts = hwstat->mp_stat_structs_color[c].color_packets; + clr->octets = hwstat->mp_stat_structs_color[c].color_bytes; + clr->tcp_flgs = + (uint64_t)hwstat->mp_stat_structs_color[c].tcp_flags; + clr++; + } + return nbc * NUM_STAT_RECORD_TYPE_COLOR + STAT_INFO_ELEMENTS; +} + +static int read_queues(nt4ga_stat_t *hwstat, uint64_t *val, int nbq) +{ + struct ntc_stat_get_data_s *qdata = (struct ntc_stat_get_data_s *)val; + + qdata->nb_counters = (uint64_t)nbq; + qdata->timestamp = hwstat->last_timestamp; + qdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch; + + /* virt/cap same */ + struct queue_type_fields_s *queue = + (struct queue_type_fields_s *)qdata->data; + int q; + + for (q = 0; q < nbq; q++) { + queue->flush_pkts = hwstat->mp_stat_structs_hb[q].flush_packets; + queue->drop_pkts = hwstat->mp_stat_structs_hb[q].drop_packets; + queue->fwd_pkts = hwstat->mp_stat_structs_hb[q].fwd_packets; + queue->dbs_drop_pkts = hwstat->mp_stat_structs_hb[q].dbs_drop_packets; + queue->flush_octets = hwstat->mp_stat_structs_hb[q].flush_bytes; + queue->drop_octets = hwstat->mp_stat_structs_hb[q].drop_bytes; + queue->fwd_octets = hwstat->mp_stat_structs_hb[q].fwd_bytes; + queue->dbs_drop_octets = hwstat->mp_stat_structs_hb[q].dbs_drop_bytes; + queue++; + } + return nbq * NUM_STAT_RECORD_TYPE_QUEUE + STAT_INFO_ELEMENTS; +} + +static void copy_rmon_stat(struct port_counters_v2 *cptr, + struct stat_rmon_s *rmon) +{ + rmon->drop_events = cptr->drop_events; + rmon->pkts = cptr->pkts; + rmon->octets = cptr->octets; + rmon->broadcast_pkts = cptr->broadcast_pkts; + rmon->multicast_pkts = cptr->multicast_pkts; + rmon->unicast_pkts = cptr->unicast_pkts; + rmon->pkts_alignment = cptr->pkts_alignment; + rmon->pkts_code_violation = cptr->pkts_code_violation; + rmon->pkts_crc = cptr->pkts_crc; + rmon->undersize_pkts = cptr->undersize_pkts; + rmon->oversize_pkts = cptr->oversize_pkts; + rmon->fragments = cptr->fragments; + rmon->jabbers_not_truncated = cptr->jabbers_not_truncated; + rmon->jabbers_truncated = cptr->jabbers_truncated; + rmon->pkts_64_octets = cptr->pkts_64_octets; + rmon->pkts_65_to_127_octets = cptr->pkts_65_to_127_octets; + rmon->pkts_128_to_255_octets = cptr->pkts_128_to_255_octets; + rmon->pkts_256_to_511_octets = cptr->pkts_256_to_511_octets; + rmon->pkts_512_to_1023_octets = cptr->pkts_512_to_1023_octets; + rmon->pkts_1024_to_1518_octets = cptr->pkts_1024_to_1518_octets; + rmon->pkts_1519_to_2047_octets = cptr->pkts_1519_to_2047_octets; + rmon->pkts_2048_to_4095_octets = cptr->pkts_2048_to_4095_octets; + rmon->pkts_4096_to_8191_octets = cptr->pkts_4096_to_8191_octets; + rmon->pkts_8192_to_max_octets = cptr->pkts_8192_to_max_octets; +} + +static int read_rx_counters(nt4ga_stat_t *hwstat, uint64_t *val, int nbp) +{ + struct ntc_stat_get_data_s *rxdata = (struct ntc_stat_get_data_s *)val; + + rxdata->nb_counters = (uint64_t)nbp; + rxdata->timestamp = hwstat->last_timestamp; + rxdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch; + + if (rxdata->is_virt) { + struct rtx_type_fields_virt_s *rxc = + (struct rtx_type_fields_virt_s *)rxdata->data; + int p; + + for (p = 0; p < nbp; p++) { + rxc->octets = + hwstat->virt.mp_stat_structs_port_rx[p].octets; + rxc->pkts = hwstat->virt.mp_stat_structs_port_rx[p].pkts; + rxc->drop_events = + hwstat->virt.mp_stat_structs_port_rx[p].drop_events; + rxc->qos_drop_octets = + hwstat->virt.mp_stat_structs_port_rx[p] + .qos_drop_octets; + rxc->qos_drop_pkts = hwstat->virt.mp_stat_structs_port_rx[p] + .qos_drop_pkts; + rxc++; + } + return nbp * NUM_STAT_RECORD_TYPE_RX_PORT_VIRT + + STAT_INFO_ELEMENTS; + } else { + struct rx_type_fields_cap_s *rxc = + (struct rx_type_fields_cap_s *)rxdata->data; + int p; + + for (p = 0; p < nbp; p++) { + copy_rmon_stat(&hwstat->cap.mp_stat_structs_port_rx[p], + &rxc->rmon); + + /* Rx only port counters */ + rxc->mac_drop_events = + hwstat->cap.mp_stat_structs_port_rx[p] + .mac_drop_events; + rxc->pkts_lr = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_lr; + rxc->duplicate = + hwstat->cap.mp_stat_structs_port_rx[p].duplicate; + rxc->pkts_ip_chksum_error = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_ip_chksum_error; + rxc->pkts_udp_chksum_error = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_udp_chksum_error; + rxc->pkts_tcp_chksum_error = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_tcp_chksum_error; + rxc->pkts_giant_undersize = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_giant_undersize; + rxc->pkts_baby_giant = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_baby_giant; + rxc->pkts_not_isl_vlan_mpls = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_not_isl_vlan_mpls; + rxc->pkts_isl = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_isl; + rxc->pkts_vlan = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_vlan; + rxc->pkts_isl_vlan = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_isl_vlan; + rxc->pkts_mpls = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_mpls; + rxc->pkts_isl_mpls = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_isl_mpls; + rxc->pkts_vlan_mpls = hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_vlan_mpls; + rxc->pkts_isl_vlan_mpls = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_isl_vlan_mpls; + rxc->pkts_no_filter = hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_no_filter; + rxc->pkts_dedup_drop = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_dedup_drop; + rxc->pkts_filter_drop = + hwstat->cap.mp_stat_structs_port_rx[p] + .pkts_filter_drop; + rxc->pkts_overflow = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_overflow; + rxc->pkts_dbs_drop = + hwstat->cap.mp_stat_structs_port_rx[p].pkts_dbs_drop; + rxc->octets_no_filter = + hwstat->cap.mp_stat_structs_port_rx[p] + .octets_no_filter; + rxc->octets_dedup_drop = + hwstat->cap.mp_stat_structs_port_rx[p] + .octets_dedup_drop; + rxc->octets_filter_drop = + hwstat->cap.mp_stat_structs_port_rx[p] + .octets_filter_drop; + rxc->octets_overflow = + hwstat->cap.mp_stat_structs_port_rx[p] + .octets_overflow; + rxc->octets_dbs_drop = + hwstat->cap.mp_stat_structs_port_rx[p] + .octets_dbs_drop; + rxc->ipft_first_hit = hwstat->cap.mp_stat_structs_port_rx[p] + .ipft_first_hit; + rxc->ipft_first_not_hit = + hwstat->cap.mp_stat_structs_port_rx[p] + .ipft_first_not_hit; + rxc->ipft_mid_hit = + hwstat->cap.mp_stat_structs_port_rx[p].ipft_mid_hit; + rxc->ipft_mid_not_hit = + hwstat->cap.mp_stat_structs_port_rx[p] + .ipft_mid_not_hit; + rxc->ipft_last_hit = + hwstat->cap.mp_stat_structs_port_rx[p].ipft_last_hit; + rxc->ipft_last_not_hit = + hwstat->cap.mp_stat_structs_port_rx[p] + .ipft_last_not_hit; + rxc++; + } + return nbp * NUM_STAT_RECORD_TYPE_RX_PORT_CAP + + STAT_INFO_ELEMENTS; + } +} + +static int read_tx_counters(nt4ga_stat_t *hwstat, uint64_t *val, int nbp) +{ + struct ntc_stat_get_data_s *txdata = (struct ntc_stat_get_data_s *)val; + + txdata->nb_counters = (uint64_t)nbp; + txdata->timestamp = hwstat->last_timestamp; + txdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch; + + if (txdata->is_virt) { + struct rtx_type_fields_virt_s *txc = + (struct rtx_type_fields_virt_s *)txdata->data; + int p; + + for (p = 0; p < nbp; p++) { + txc->octets = + hwstat->virt.mp_stat_structs_port_tx[p].octets; + txc->pkts = hwstat->virt.mp_stat_structs_port_tx[p].pkts; + txc->drop_events = + hwstat->virt.mp_stat_structs_port_tx[p].drop_events; + txc->qos_drop_octets = + hwstat->virt.mp_stat_structs_port_tx[p] + .qos_drop_octets; + txc->qos_drop_pkts = hwstat->virt.mp_stat_structs_port_tx[p] + .qos_drop_pkts; + txc++; + } + return nbp * NUM_STAT_RECORD_TYPE_TX_PORT_VIRT + + STAT_INFO_ELEMENTS; + } else { + struct tx_type_fields_cap_s *txc = + (struct tx_type_fields_cap_s *)txdata->data; + int p; + + for (p = 0; p < nbp; p++) { + copy_rmon_stat(&hwstat->cap.mp_stat_structs_port_tx[p], + &txc->rmon); + txc->rmon.pkts = hwstat->a_port_tx_packets_total[p]; + txc++; + } + return nbp * NUM_STAT_RECORD_TYPE_TX_PORT_CAP + + STAT_INFO_ELEMENTS; + } +} + +static int func_get_layout_version(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + *data = malloc(sizeof(int)); + if (!*data) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + + *(int *)*data = stat->p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version; + *len = sizeof(int); + return REQUEST_OK; +} + +static int func_get_flm_layout_version(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + *data = malloc(sizeof(int)); + if (!*data) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + + *(int *)*data = (stat->p_nt4ga_stat->flm_stat_ver < 18) ? 1 : 2; + *len = sizeof(int); + return REQUEST_OK; +} + +/* + * Return total number of 64bit counters occupied by this stat type + * additionally, returns total number of records for this type (ie number of queues, ports, etc) + */ +static int get_size(struct stat_hdl *stat, enum stat_type_e type, + int *num_records) +{ + int nrec = 0; + int size = 0; + + switch (type) { + case STAT_TYPE_COLOR: + nrec = stat->p_nt4ga_stat->mp_nthw_stat->m_nb_color_counters / 2; + size = nrec * NUM_STAT_RECORD_TYPE_COLOR; + break; + case STAT_TYPE_QUEUE: + nrec = stat->p_nt4ga_stat->mp_nthw_stat->m_nb_rx_host_buffers; + size = nrec * NUM_STAT_RECORD_TYPE_QUEUE; + break; + case STAT_TYPE_RX: + nrec = stat->p_nt4ga_stat->mn_rx_ports; + size = nrec * ((stat->p_nt4ga_stat->mp_nthw_stat->mb_is_vswitch) ? + NUM_STAT_RECORD_TYPE_RX_PORT_VIRT : + NUM_STAT_RECORD_TYPE_RX_PORT_CAP); + break; + case STAT_TYPE_TX: + nrec = stat->p_nt4ga_stat->mn_tx_ports; + size = nrec * ((stat->p_nt4ga_stat->mp_nthw_stat->mb_is_vswitch) ? + NUM_STAT_RECORD_TYPE_TX_PORT_VIRT : + NUM_STAT_RECORD_TYPE_TX_PORT_CAP); + break; + case STAT_TYPE_FLOWMATCHER: + nrec = 1; + size = nrec * NUM_STAT_RECORD_TYPE_FLOWMATCHER; + break; + } + + *num_records = nrec; + return size + STAT_INFO_ELEMENTS; +} + +static int do_get_stats(struct stat_hdl *stat, char **data, int *len, + enum stat_type_e stype, + int (*read_counters)(nt4ga_stat_t *, uint64_t *, int)) +{ + int nbg; + int size = get_size(stat, stype, &nbg); + + size *= sizeof(uint64_t); + uint64_t *val = (uint64_t *)malloc(size); + + if (!val) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + + pthread_mutex_lock(&stat->drv->ntdrv.stat_lck); + read_counters(stat->p_nt4ga_stat, val, nbg); + pthread_mutex_unlock(&stat->drv->ntdrv.stat_lck); + + *data = (char *)val; + *len = size; + return REQUEST_OK; +} + +/* + * Stat Request functions + */ +static int func_get_flm(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, char **data, + int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + return do_get_stats(stat, data, len, STAT_TYPE_FLOWMATCHER, read_flm); +} + +static int func_get_color(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, char **data, + int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + return do_get_stats(stat, data, len, STAT_TYPE_COLOR, read_colors); +} + +static int func_get_queue(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, char **data, + int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + return do_get_stats(stat, data, len, STAT_TYPE_QUEUE, read_queues); +} + +static int func_get_rx_counters(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + return do_get_stats(stat, data, len, STAT_TYPE_RX, read_rx_counters); +} + +static int func_get_tx_counters(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + return do_get_stats(stat, data, len, STAT_TYPE_TX, read_tx_counters); +} + +/* + * Snapshot handling. This is to ensure atomic reading of all statistics in one collection + */ + +static struct snaps_s *find_client_snap_data(struct stat_hdl *stat, + int client_id, + struct snaps_s **parent) +{ + struct snaps_s *snaps = stat->snaps_base; + + if (parent) + *parent = NULL; + while (snaps && snaps->client_id != client_id) { + if (parent) + *parent = snaps; + snaps = snaps->next; + } + + return snaps; +} + +static struct snaps_s *get_client_snap_data(struct stat_hdl *stat, + int client_id) +{ + struct snaps_s *snaps = find_client_snap_data(stat, client_id, NULL); + + if (!snaps) { + snaps = malloc(sizeof(struct snaps_s)); /* return NULL on malloc failure */ + if (snaps) { + snaps->client_id = client_id; + snaps->next = stat->snaps_base; + stat->snaps_base = snaps; + snaps->buffer = NULL; + } + } + return snaps; +} + +static int func_snapshot(void *hdl, int client_id, + struct ntconn_header_s *hdr _unused, char **data, + int *len) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + int nbc, nbq, nbpr, nbpt; + struct snaps_s *snaps; + + if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) { + *data = NULL; + *len = 0; + return REQUEST_ERR; + } + snaps = get_client_snap_data(stat, client_id); + if (!snaps) + goto err_out; + + if (snaps->buffer) + free(snaps->buffer); + + snaps->snap_addr[SNAP_COLORS].size = + (unsigned int)get_size(stat, STAT_TYPE_COLOR, &nbc); + snaps->snap_addr[SNAP_QUEUES].size = + (unsigned int)get_size(stat, STAT_TYPE_QUEUE, &nbq); + snaps->snap_addr[SNAP_RX_PORT].size = + (unsigned int)get_size(stat, STAT_TYPE_RX, &nbpr); + snaps->snap_addr[SNAP_TX_PORT].size = + (unsigned int)get_size(stat, STAT_TYPE_TX, &nbpt); + + unsigned int tot_size = snaps->snap_addr[SNAP_COLORS].size + + snaps->snap_addr[SNAP_QUEUES].size + + snaps->snap_addr[SNAP_RX_PORT].size + + snaps->snap_addr[SNAP_TX_PORT].size; + + snaps->buffer = malloc(tot_size * sizeof(uint64_t)); + if (!snaps->buffer) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + uint64_t *val = snaps->buffer; + + snaps->snap_addr[SNAP_COLORS].ptr = val; + pthread_mutex_lock(&stat->drv->ntdrv.stat_lck); + unsigned int size = read_colors(stat->p_nt4ga_stat, val, nbc); + + if (size != snaps->snap_addr[SNAP_COLORS].size) { + NT_LOG(ERR, NTCONNECT, "stat.snapshot: color size mismatch"); + goto err_out; + } + + val += size; + snaps->snap_addr[SNAP_QUEUES].ptr = val; + size = read_queues(stat->p_nt4ga_stat, val, nbq); + if (size != snaps->snap_addr[SNAP_QUEUES].size) { + NT_LOG(ERR, NTCONNECT, + "stat.snapshot: queue statistic size mismatch"); + goto err_out; + } + + val += size; + snaps->snap_addr[SNAP_RX_PORT].ptr = val; + size = read_rx_counters(stat->p_nt4ga_stat, val, nbpr); + if (size != snaps->snap_addr[SNAP_RX_PORT].size) { + NT_LOG(ERR, NTCONNECT, + "stat.snapshot: Rx port statistic size mismatch %i, %i", + size, snaps->snap_addr[SNAP_RX_PORT].size); + goto err_out; + } + + val += size; + snaps->snap_addr[SNAP_TX_PORT].ptr = val; + size = read_tx_counters(stat->p_nt4ga_stat, val, nbpt); + if (size != snaps->snap_addr[SNAP_TX_PORT].size) { + NT_LOG(ERR, NTCONNECT, + "stat.snapshot: Tx port statistic size mismatch"); + goto err_out; + } + + pthread_mutex_unlock(&stat->drv->ntdrv.stat_lck); + + *data = NULL; + *len = 0; + return REQUEST_OK; + +err_out: + pthread_mutex_unlock(&stat->drv->ntdrv.stat_lck); + return ntconn_error(data, len, "stat", + NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR); +} + +static int get_snap_data(void *hdl, int client_id, char **data, int *len, + enum snap_addr_select_e snap_addr_idx) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + struct snaps_s *snaps = find_client_snap_data(stat, client_id, NULL); + + if (!snaps || !snaps->buffer) + return ntconn_error(data, len, "stat", NTCONN_ERR_CODE_NO_DATA); + + int ln = snaps->snap_addr[snap_addr_idx].size * sizeof(uint64_t); + + *data = malloc(ln); + if (!data) { + *len = 0; + NT_LOG(ERR, NTCONNECT, "memory allocation failed"); + return REQUEST_ERR; + } + memcpy(*data, snaps->snap_addr[snap_addr_idx].ptr, ln); + *len = ln; + + return REQUEST_OK; +} + +static int func_get_snap_colors(void *hdl, int client_id, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + return get_snap_data(hdl, client_id, data, len, SNAP_COLORS); +} + +static int func_get_snap_queues(void *hdl, int client_id, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + return get_snap_data(hdl, client_id, data, len, SNAP_QUEUES); +} + +static int func_get_snap_rx_port(void *hdl, int client_id, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + return get_snap_data(hdl, client_id, data, len, SNAP_RX_PORT); +} + +static int func_get_snap_tx_port(void *hdl, int client_id, + struct ntconn_header_s *hdr _unused, + char **data, int *len) +{ + return get_snap_data(hdl, client_id, data, len, SNAP_TX_PORT); +} + +/* + * Stat main request function + */ +static int stat_request(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr, char *function, + char **data, int *len) +{ + return execute_function(this_module_name, hdl, client_id, hdr, function, + stat_entry_funcs, data, len, 0); +} + +static void stat_free_data(void *hdl _unused, char *data) +{ + free(data); +} + +static void stat_client_cleanup(void *hdl, int client_id) +{ + struct stat_hdl *stat = (struct stat_hdl *)hdl; + struct snaps_s *snaps_parent; + struct snaps_s *snaps = + find_client_snap_data(stat, client_id, &snaps_parent); + + if (!snaps) + return; + + if (snaps_parent) + snaps_parent->next = snaps->next; + else + stat->snaps_base = snaps->next; + + if (snaps->buffer) + free(snaps->buffer); + free(snaps); +} + +static const ntconnapi_t ntconn_stat_op = { + this_module_name, STAT_VERSION_MAJOR, STAT_VERSION_MINOR, + stat_request, stat_free_data, stat_client_cleanup +}; + +int ntconn_stat_register(struct drv_s *drv) +{ + stat_hdl.drv = drv; + stat_hdl.p_nt4ga_stat = &drv->ntdrv.adapter_info.nt4ga_stat; + + /* Check supported Layout_versions by this module */ + size_t i; + + for (i = 0; i < NUM_LAYOUT_VERSIONS_SUPPORTED; i++) { + if (stat_hdl.p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version == + layout_versions_supported[i]) + break; + } + + if (i == NUM_LAYOUT_VERSIONS_SUPPORTED) { + NT_LOG(ERR, NTCONNECT, + "stat: layout version %i is not supported. Module will not be activated", + stat_hdl.p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version); + return -1; + } + + return register_ntconn_mod(&drv->p_dev->addr, (void *)&stat_hdl, + &ntconn_stat_op); +} diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_test.c b/drivers/net/ntnic/ntconnect_modules/ntconn_test.c new file mode 100644 index 0000000000..907ea4ff5f --- /dev/null +++ b/drivers/net/ntnic/ntconnect_modules/ntconn_test.c @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include "ntnic_ethdev.h" +#include "ntconnect.h" +#include "ntos_system.h" +#include "ntconn_modules.h" +#include "ntconn_mod_helper.h" +#include "nt_util.h" +#include "ntlog.h" +#include "ntnic_vf_vdpa.h" + +#include "ntconnect_api_test.h" + +#define NTCONN_TEST_VERSION_MAJOR 0U +#define NTCONN_TEST_VERSION_MINOR 1U + +#define this_module_name "ntconnect_test" + +#define MAX_CLIENTS 32 + +#define UNUSED __rte_unused + +static struct test_hdl_s { + struct drv_s *drv; +} test_hdl[MAX_CLIENTS]; + +/* + * Test functions + */ +static int func_test(void *hdl, int client_id, struct ntconn_header_s *hdr, + char **data, int *len); +static struct func_s adapter_entry_funcs[] = { + { "test", NULL, func_test }, + { NULL, NULL, NULL }, +}; + +static int func_test(void *hdl _unused, int client_id _unused, + struct ntconn_header_s *hdr, char **data, int *len) +{ + int status = 0; + int number = 0; + uint32_t size; + struct test_s *test_cpy = (struct test_s *)&(*data)[hdr->len]; + + if (hdr->blob_len < sizeof(struct test_s)) { + NT_LOG(ERR, NTCONNECT, "Error in test data: to small"); + status = -1; + goto TEST_ERROR; + } + + number = test_cpy->number; + size = sizeof(struct test_s) + sizeof(uint64_t) * number; + + if (hdr->blob_len != size) { + NT_LOG(ERR, NTCONNECT, "Error in test data: wrong size"); + status = -1; + goto TEST_ERROR; + } + + { + *data = malloc(sizeof(struct test_s) + + number * sizeof(uint64_t)); + if (!*data) + goto TEST_ERROR_MALLOC; + struct test_s *return_value = (struct test_s *)*data; + *len = sizeof(struct test_s) + number * sizeof(uint64_t); + for (int i = 0; i < number; i++) + return_value->test[i] = test_cpy->test[i]; + return_value->status = 0; + return_value->number = number; + return REQUEST_OK; + } + +TEST_ERROR: + + { + *data = malloc(sizeof(struct test_s)); + if (!*data) + goto TEST_ERROR_MALLOC; + struct test_s *return_value = (struct test_s *)*data; + *len = sizeof(struct test_s); + return_value->status = status; + return_value->number = 0; + return REQUEST_OK; + } + +TEST_ERROR_MALLOC: + + *len = 0; + NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", __func__); + return REQUEST_ERR; +} + +enum { + FLOW_API_FUNC_CREATE, + FLOW_API_FUNC_VALIDATE, +}; + +static int test_request(void *hdl, int client_id _unused, + struct ntconn_header_s *hdr, char *function, + char **data, int *len) +{ + return execute_function(this_module_name, hdl, client_id, hdr, function, + adapter_entry_funcs, data, len, 0); +} + +static void test_free_data(void *hdl _unused, char *data) +{ + if (data) + free(data); +} + +static void test_client_cleanup(void *hdl _unused, int client_id _unused) +{ + /* Nothing to do */ +} + +static const ntconnapi_t ntconn_test_op = { this_module_name, + NTCONN_TEST_VERSION_MAJOR, + NTCONN_TEST_VERSION_MINOR, + test_request, + test_free_data, + test_client_cleanup + }; + +int ntconn_test_register(struct drv_s *drv) +{ + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + if (test_hdl[i].drv == NULL) + break; + } + if (i == MAX_CLIENTS) { + NT_LOG(ERR, NTCONNECT, + "Cannot register more adapters into NtConnect framework"); + return -1; + } + + test_hdl[i].drv = drv; + return register_ntconn_mod(&drv->p_dev->addr, (void *)&test_hdl[i], + &ntconn_test_op); +}