From patchwork Fri Jul 2 20:49:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 95221 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 C86FEA0C3F; Fri, 2 Jul 2021 22:49:59 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 8B3C241363; Fri, 2 Jul 2021 22:49:59 +0200 (CEST) Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by mails.dpdk.org (Postfix) with ESMTP id EA38941353 for ; Fri, 2 Jul 2021 22:49:56 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10033"; a="294428751" X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="294428751" Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jul 2021 13:49:54 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="494257766" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by fmsmga002.fm.intel.com with ESMTP; 02 Jul 2021 13:49:53 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Cc: Churchill Khangar Date: Fri, 2 Jul 2021 21:49:52 +0100 Message-Id: <20210702204952.61445-1-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 Subject: [dpdk-dev] [PATCH 1/5] examples/pipeline: improve table update CLI commands 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 Sender: "dev" From: Churchill Khangar For more felxibility, the single monolithic table update command is split into table entry add, table entry delete, table default entry add, pipeline commit and pipeline abort. Signed-off-by: Churchill Khangar Signed-off-by: Cristian Dumitrescu --- examples/pipeline/cli.c | 589 ++++++++++++++++------ examples/pipeline/examples/vxlan.cli | 3 +- examples/pipeline/examples/vxlan_pcap.cli | 3 +- 3 files changed, 428 insertions(+), 167 deletions(-) diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c index 215dd8e85..30754e319 100644 --- a/examples/pipeline/cli.c +++ b/examples/pipeline/cli.c @@ -1038,25 +1038,76 @@ table_entry_free(struct rte_swx_table_entry *entry) free(entry); } -static const char cmd_pipeline_table_update_help[] = -"pipeline table update " -" "; +#ifndef MAX_LINE_SIZE +#define MAX_LINE_SIZE 2048 +#endif + +static int +pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p, + const char *table_name, + FILE *file, + uint32_t *file_line_number) +{ + char *line = NULL; + uint32_t line_id = 0; + int status = 0; + + /* Buffer allocation. */ + line = malloc(MAX_LINE_SIZE); + if (!line) + return -ENOMEM; + + /* File read. */ + for (line_id = 1; ; line_id++) { + struct rte_swx_table_entry *entry; + int is_blank_or_comment; + + if (fgets(line, MAX_LINE_SIZE, file) == NULL) + break; + + entry = rte_swx_ctl_pipeline_table_entry_read(p, + table_name, + line, + &is_blank_or_comment); + if (!entry) { + if (is_blank_or_comment) + continue; + + status = -EINVAL; + goto error; + } + + status = rte_swx_ctl_pipeline_table_entry_add(p, + table_name, + entry); + table_entry_free(entry); + if (status) + goto error; + } + +error: + free(line); + *file_line_number = line_id; + return status; +} + +static const char cmd_pipeline_table_add_help[] = +"pipeline table add \n"; static void -cmd_pipeline_table_update(char **tokens, - uint32_t n_tokens, - char *out, - size_t out_size, - void *obj) +cmd_pipeline_table_add(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) { struct pipeline *p; - char *pipeline_name, *table_name, *line = NULL; - char *file_name_add, *file_name_delete, *file_name_default; - FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL; - uint32_t line_id; + char *pipeline_name, *table_name, *file_name; + FILE *file = NULL; + uint32_t file_line_number = 0; int status; - if (n_tokens != 8) { + if (n_tokens != 6) { snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); return; } @@ -1068,192 +1119,313 @@ cmd_pipeline_table_update(char **tokens, return; } - if (strcmp(tokens[2], "table") != 0) { - snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); + table_name = tokens[3]; + + file_name = tokens[5]; + file = fopen(file_name, "r"); + if (!file) { + snprintf(out, out_size, "Cannot open file %s.\n", file_name); + return; + } + + status = pipeline_table_entries_add(p->ctl, + table_name, + file, + &file_line_number); + if (status) + snprintf(out, out_size, "Invalid entry in file %s at line %u\n", + file_name, + file_line_number); + + fclose(file); +} + +static int +pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p, + const char *table_name, + FILE *file, + uint32_t *file_line_number) +{ + char *line = NULL; + uint32_t line_id = 0; + int status = 0; + + /* Buffer allocation. */ + line = malloc(MAX_LINE_SIZE); + if (!line) + return -ENOMEM; + + /* File read. */ + for (line_id = 1; ; line_id++) { + struct rte_swx_table_entry *entry; + int is_blank_or_comment; + + if (fgets(line, MAX_LINE_SIZE, file) == NULL) + break; + + entry = rte_swx_ctl_pipeline_table_entry_read(p, + table_name, + line, + &is_blank_or_comment); + if (!entry) { + if (is_blank_or_comment) + continue; + + status = -EINVAL; + goto error; + } + + status = rte_swx_ctl_pipeline_table_entry_delete(p, + table_name, + entry); + table_entry_free(entry); + if (status) + goto error; + } + +error: + *file_line_number = line_id; + free(line); + return status; +} + +static const char cmd_pipeline_table_delete_help[] = +"pipeline table delete \n"; + +static void +cmd_pipeline_table_delete(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *table_name, *file_name; + FILE *file = NULL; + uint32_t file_line_number = 0; + int status; + + if (n_tokens != 6) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); return; } table_name = tokens[3]; - if (strcmp(tokens[4], "update") != 0) { - snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update"); + file_name = tokens[5]; + file = fopen(file_name, "r"); + if (!file) { + snprintf(out, out_size, "Cannot open file %s.\n", file_name); return; } - file_name_add = tokens[5]; - file_name_delete = tokens[6]; - file_name_default = tokens[7]; + status = pipeline_table_entries_delete(p->ctl, + table_name, + file, + &file_line_number); + if (status) + snprintf(out, out_size, "Invalid entry in file %s at line %u\n", + file_name, + file_line_number); + + fclose(file); +} + +static int +pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p, + const char *table_name, + FILE *file, + uint32_t *file_line_number) +{ + char *line = NULL; + uint32_t line_id = 0; + int status = 0; + + /* Buffer allocation. */ + line = malloc(MAX_LINE_SIZE); + if (!line) + return -ENOMEM; + + /* File read. */ + for (line_id = 1; ; line_id++) { + struct rte_swx_table_entry *entry; + int is_blank_or_comment; - /* File open. */ - if (strcmp(file_name_add, "none")) { - file_add = fopen(file_name_add, "r"); - if (!file_add) { - snprintf(out, out_size, "Cannot open file %s.\n", - file_name_add); + if (fgets(line, MAX_LINE_SIZE, file) == NULL) + break; + + entry = rte_swx_ctl_pipeline_table_entry_read(p, + table_name, + line, + &is_blank_or_comment); + if (!entry) { + if (is_blank_or_comment) + continue; + + status = -EINVAL; goto error; } - } - if (strcmp(file_name_delete, "none")) { - file_delete = fopen(file_name_delete, "r"); - if (!file_delete) { - snprintf(out, out_size, "Cannot open file %s.\n", - file_name_delete); + status = rte_swx_ctl_pipeline_table_default_entry_add(p, + table_name, + entry); + table_entry_free(entry); + if (status) goto error; - } } - if (strcmp(file_name_default, "none")) { - file_default = fopen(file_name_default, "r"); - if (!file_default) { - snprintf(out, out_size, "Cannot open file %s.\n", - file_name_default); - goto error; - } +error: + *file_line_number = line_id; + free(line); + return status; +} + +static const char cmd_pipeline_table_default_help[] = +"pipeline table default \n"; + +static void +cmd_pipeline_table_default(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *table_name, *file_name; + FILE *file = NULL; + uint32_t file_line_number = 0; + int status; + + if (n_tokens != 6) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; } - if (!file_add && !file_delete && !file_default) { - snprintf(out, out_size, "Nothing to be done."); + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); return; } - /* Buffer allocation. */ - line = malloc(2048); - if (!line) { - snprintf(out, out_size, MSG_OUT_OF_MEMORY); - goto error; - } - - /* Add. */ - if (file_add) - for (line_id = 1; ; line_id++) { - struct rte_swx_table_entry *entry; - int is_blank_or_comment; - - if (fgets(line, 2048, file_add) == NULL) - break; - - entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, - table_name, - line, - &is_blank_or_comment); - if (!entry) { - if (is_blank_or_comment) - continue; - - snprintf(out, out_size, MSG_FILE_ERR, - file_name_add, line_id); - goto error; - } + table_name = tokens[3]; - status = rte_swx_ctl_pipeline_table_entry_add(p->ctl, - table_name, - entry); - table_entry_free(entry); - if (status) { - snprintf(out, out_size, - "Invalid entry in file %s at line %u", - file_name_add, line_id); - goto error; - } - } + file_name = tokens[5]; + file = fopen(file_name, "r"); + if (!file) { + snprintf(out, out_size, "Cannot open file %s.\n", file_name); + return; + } + status = pipeline_table_default_entry_add(p->ctl, + table_name, + file, + &file_line_number); + if (status) + snprintf(out, out_size, "Invalid entry in file %s at line %u\n", + file_name, + file_line_number); - /* Delete. */ - if (file_delete) - for (line_id = 1; ; line_id++) { - struct rte_swx_table_entry *entry; - int is_blank_or_comment; + fclose(file); +} - if (fgets(line, 2048, file_delete) == NULL) - break; +static const char cmd_pipeline_table_show_help[] = +"pipeline table show\n"; - entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, - table_name, - line, - &is_blank_or_comment); - if (!entry) { - if (is_blank_or_comment) - continue; +static void +cmd_pipeline_table_show(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *table_name; + int status; - snprintf(out, out_size, MSG_FILE_ERR, - file_name_delete, line_id); - goto error; - } + if (n_tokens != 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } - status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl, - table_name, - entry); - table_entry_free(entry); - if (status) { - snprintf(out, out_size, - "Invalid entry in file %s at line %u", - file_name_delete, line_id); - goto error; - } - } + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } - /* Default. */ - if (file_default) - for (line_id = 1; ; line_id++) { - struct rte_swx_table_entry *entry; - int is_blank_or_comment; + table_name = tokens[3]; + status = rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name); + if (status) + snprintf(out, out_size, MSG_ARG_INVALID, "table_name"); +} - if (fgets(line, 2048, file_default) == NULL) - break; +static const char cmd_pipeline_commit_help[] = +"pipeline commit\n"; - entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, - table_name, - line, - &is_blank_or_comment); - if (!entry) { - if (is_blank_or_comment) - continue; +static void +cmd_pipeline_commit(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name; + int status; - snprintf(out, out_size, MSG_FILE_ERR, - file_name_default, line_id); - goto error; - } + if (n_tokens != 3) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } - status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl, - table_name, - entry); - table_entry_free(entry); - if (status) { - snprintf(out, out_size, - "Invalid entry in file %s at line %u", - file_name_default, line_id); - goto error; - } - } + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } status = rte_swx_ctl_pipeline_commit(p->ctl, 1); - if (status) { - snprintf(out, out_size, "Commit failed."); - goto error; - } + if (status) + snprintf(out, out_size, "Commit failed. " + "Use \"commit\" to retry or \"abort\" to discard the pending work.\n"); +} +static const char cmd_pipeline_abort_help[] = +"pipeline abort\n"; - rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name); +static void +cmd_pipeline_abort(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name; - free(line); - if (file_add) - fclose(file_add); - if (file_delete) - fclose(file_delete); - if (file_default) - fclose(file_default); - return; + if (n_tokens != 3) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } -error: rte_swx_ctl_pipeline_abort(p->ctl); - free(line); - if (file_add) - fclose(file_add); - if (file_delete) - fclose(file_delete); - if (file_default) - fclose(file_default); } static const char cmd_pipeline_regrd_help[] = @@ -1992,7 +2164,12 @@ cmd_help(char **tokens, "\tpipeline port in\n" "\tpipeline port out\n" "\tpipeline build\n" - "\tpipeline table update\n" + "\tpipeline table add\n" + "\tpipeline table delete\n" + "\tpipeline table default\n" + "\tpipeline table show\n" + "\tpipeline commit\n" + "\tpipeline abort\n" "\tpipeline regrd\n" "\tpipeline regwr\n" "\tpipeline meter profile add\n" @@ -2056,9 +2233,52 @@ cmd_help(char **tokens, if ((strcmp(tokens[0], "pipeline") == 0) && (n_tokens == 3) && (strcmp(tokens[1], "table") == 0) && - (strcmp(tokens[2], "update") == 0)) { + (strcmp(tokens[2], "add") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_table_add_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 3) && + (strcmp(tokens[1], "table") == 0) && + (strcmp(tokens[2], "delete") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_table_delete_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 3) && + (strcmp(tokens[1], "table") == 0) && + (strcmp(tokens[2], "default") == 0)) { snprintf(out, out_size, "\n%s\n", - cmd_pipeline_table_update_help); + cmd_pipeline_table_default_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 3) && + (strcmp(tokens[1], "table") == 0) && + (strcmp(tokens[2], "show") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_table_show_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 2) && + (strcmp(tokens[1], "commit") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_commit_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 2) && + (strcmp(tokens[1], "abort") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_abort_help); return; } @@ -2216,9 +2436,48 @@ cli_process(char *in, char *out, size_t out_size, void *obj) return; } + if ((n_tokens >= 5) && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[4], "add") == 0)) { + cmd_pipeline_table_add(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 5) && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[4], "delete") == 0)) { + cmd_pipeline_table_delete(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 5) && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[4], "default") == 0)) { + cmd_pipeline_table_default(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 5) && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[4], "show") == 0)) { + cmd_pipeline_table_show(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 3) && + (strcmp(tokens[2], "commit") == 0)) { + cmd_pipeline_commit(tokens, n_tokens, out, + out_size, obj); + return; + } + if ((n_tokens >= 3) && - (strcmp(tokens[2], "table") == 0)) { - cmd_pipeline_table_update(tokens, n_tokens, out, + (strcmp(tokens[2], "abort") == 0)) { + cmd_pipeline_abort(tokens, n_tokens, out, out_size, obj); return; } diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli index 7bf4a5757..a3bde6a9f 100644 --- a/examples/pipeline/examples/vxlan.cli +++ b/examples/pipeline/examples/vxlan.cli @@ -22,6 +22,7 @@ pipeline PIPELINE0 port out 3 link LINK3 txq 0 bsz 32 pipeline PIPELINE0 port out 4 sink none pipeline PIPELINE0 build ./examples/pipeline/examples/vxlan.spec -pipeline PIPELINE0 table vxlan_table update ./examples/pipeline/examples/vxlan_table.txt none none +pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt +pipeline PIPELINE0 commit thread 1 pipeline PIPELINE0 enable diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli index 1636ba080..3cc9a94af 100644 --- a/examples/pipeline/examples/vxlan_pcap.cli +++ b/examples/pipeline/examples/vxlan_pcap.cli @@ -17,6 +17,7 @@ pipeline PIPELINE0 port out 3 sink none pipeline PIPELINE0 port out 4 sink none pipeline PIPELINE0 build ./examples/vxlan.spec -pipeline PIPELINE0 table vxlan_table update ./examples/vxlan_table.txt none none +pipeline PIPELINE0 table vxlan_table add ./examples/vxlan_table.txt +pipeline PIPELINE0 commit thread 1 pipeline PIPELINE0 enable From patchwork Fri Jul 2 20:50:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 95222 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 CF585A0C3F; Fri, 2 Jul 2021 22:50:10 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B465D41417; Fri, 2 Jul 2021 22:50:10 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by mails.dpdk.org (Postfix) with ESMTP id 1161841353 for ; Fri, 2 Jul 2021 22:50:07 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10033"; a="205775860" X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="205775860" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jul 2021 13:50:05 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="482704192" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by FMSMGA003.fm.intel.com with ESMTP; 02 Jul 2021 13:50:04 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Date: Fri, 2 Jul 2021 21:50:03 +0100 Message-Id: <20210702205003.61495-1-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 Subject: [dpdk-dev] [PATCH 2/5] table: add support for selector tables 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 Sender: "dev" A selector table is made up of groups of weighted members, with a given member potentially part of several groups. The select operation returns a member ID by first selecting a group based on an input group ID and then selecting a member within that group based on hashing one or several input header/meta-data fields. It is very useful for implementing an ECMP/WCMP-enabled FIB or a load balancer. It is part of the action selector described by the P4 Portable Switch Architecture (PSA) specification. Signed-off-by: Cristian Dumitrescu --- lib/table/meson.build | 2 + lib/table/rte_swx_table_selector.c | 574 +++++++++++++++++++++++++++++ lib/table/rte_swx_table_selector.h | 203 ++++++++++ lib/table/version.map | 8 + 4 files changed, 787 insertions(+) create mode 100644 lib/table/rte_swx_table_selector.c create mode 100644 lib/table/rte_swx_table_selector.h diff --git a/lib/table/meson.build b/lib/table/meson.build index b7b70b805..16e55f086 100644 --- a/lib/table/meson.build +++ b/lib/table/meson.build @@ -3,6 +3,7 @@ sources = files( 'rte_swx_table_em.c', + 'rte_swx_table_selector.c', 'rte_swx_table_wm.c', 'rte_table_acl.c', 'rte_table_array.c', @@ -20,6 +21,7 @@ headers = files( 'rte_lru.h', 'rte_swx_table.h', 'rte_swx_table_em.h', + 'rte_swx_table_selector.h', 'rte_swx_table_wm.h', 'rte_table.h', 'rte_table_acl.h', diff --git a/lib/table/rte_swx_table_selector.c b/lib/table/rte_swx_table_selector.c new file mode 100644 index 000000000..20cd262ef --- /dev/null +++ b/lib/table/rte_swx_table_selector.c @@ -0,0 +1,574 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2021 Intel Corporation + */ +#include +#include +#include +#include + +#include +#include + +#include "rte_swx_table_selector.h" + +#ifndef RTE_SWX_TABLE_SELECTOR_HUGE_PAGES_DISABLE + +#include + +static void * +env_calloc(size_t size, size_t alignment, int numa_node) +{ + return rte_zmalloc_socket(NULL, size, alignment, numa_node); +} + +static void +env_free(void *start, size_t size __rte_unused) +{ + rte_free(start); +} + +#else + +#include + +static void * +env_calloc(size_t size, size_t alignment __rte_unused, int numa_node) +{ + void *start; + + if (numa_available() == -1) + return NULL; + + start = numa_alloc_onnode(size, numa_node); + if (!start) + return NULL; + + memset(start, 0, size); + return start; +} + +static void +env_free(void *start, size_t size) +{ + if ((numa_available() == -1) || !start) + return; + + numa_free(start, size); +} + +#endif + +#if defined(RTE_ARCH_X86_64) + +#include + +#define crc32_u64(crc, v) _mm_crc32_u64(crc, v) + +#else + +static inline uint64_t +crc32_u64_generic(uint64_t crc, uint64_t value) +{ + int i; + + crc = (crc & 0xFFFFFFFFLLU) ^ value; + for (i = 63; i >= 0; i--) { + uint64_t mask; + + mask = -(crc & 1LLU); + crc = (crc >> 1LLU) ^ (0x82F63B78LLU & mask); + } + + return crc; +} + +#define crc32_u64(crc, v) crc32_u64_generic(crc, v) + +#endif + +/* Key size needs to be one of: 8, 16, 32 or 64. */ +static inline uint32_t +hash(void *key, void *key_mask, uint32_t key_size, uint32_t seed) +{ + uint64_t *k = key; + uint64_t *m = key_mask; + uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5; + + switch (key_size) { + case 8: + crc0 = crc32_u64(seed, k[0] & m[0]); + return crc0; + + case 16: + k0 = k[0] & m[0]; + + crc0 = crc32_u64(k0, seed); + crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); + + crc0 ^= crc1; + + return crc0; + + case 32: + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + + crc0 = crc32_u64(k0, seed); + crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = crc32_u64(k2, k[3] & m[3]); + crc3 = k2 >> 32; + + crc0 = crc32_u64(crc0, crc1); + crc1 = crc32_u64(crc2, crc3); + + crc0 ^= crc1; + + return crc0; + + case 64: + k0 = k[0] & m[0]; + k2 = k[2] & m[2]; + k5 = k[5] & m[5]; + + crc0 = crc32_u64(k0, seed); + crc1 = crc32_u64(k0 >> 32, k[1] & m[1]); + + crc2 = crc32_u64(k2, k[3] & m[3]); + crc3 = crc32_u64(k2 >> 32, k[4] & m[4]); + + crc4 = crc32_u64(k5, k[6] & m[6]); + crc5 = crc32_u64(k5 >> 32, k[7] & m[7]); + + crc0 = crc32_u64(crc0, (crc1 << 32) ^ crc2); + crc1 = crc32_u64(crc3, (crc4 << 32) ^ crc5); + + crc0 ^= crc1; + + return crc0; + + default: + crc0 = 0; + return crc0; + } +} + +struct group_member_info { + uint32_t member_id; + uint32_t member_weight; + uint32_t member_weight_normalized; + uint32_t count; +}; + +struct table { + /* Input parameters */ + struct rte_swx_table_selector_params params; + + /* Internal. */ + uint32_t *group_table; + uint64_t group_table_size; + struct group_member_info *members; + uint32_t n_members_per_group_max_log2; +}; + +uint64_t +rte_swx_table_selector_footprint_get(uint32_t n_groups_max, uint32_t n_members_per_group_max) +{ + uint64_t group_table_size, members_size; + + group_table_size = n_groups_max * n_members_per_group_max * sizeof(uint32_t); + + members_size = n_members_per_group_max * sizeof(struct group_member_info); + + return sizeof(struct table) + group_table_size + members_size; +} + +void +rte_swx_table_selector_free(void *table) +{ + struct table *t = table; + + if (!t) + return; + + free(t->members); + + env_free(t->group_table, t->group_table_size); + + free(t->params.selector_mask); + + free(t); +} + +static int +table_create_check(struct rte_swx_table_selector_params *params) +{ + if (!params) + return -1; + + if (!params->selector_size || + (params->selector_size > 64) || + !params->n_groups_max || + (params->n_groups_max > 1U << 31) || + !params->n_members_per_group_max || + (params->n_members_per_group_max > 1U << 31)) + return -EINVAL; + + return 0; +} + +static int +table_params_copy(struct table *t, struct rte_swx_table_selector_params *params) +{ + uint32_t selector_size, i; + + selector_size = rte_align32pow2(params->selector_size); + if (selector_size < 8) + selector_size = 8; + + memcpy(&t->params, params, sizeof(struct rte_swx_table_selector_params)); + t->params.selector_size = selector_size; + t->params.selector_mask = NULL; + t->params.n_groups_max = rte_align32pow2(params->n_groups_max); + t->params.n_members_per_group_max = rte_align32pow2(params->n_members_per_group_max); + + for (i = 0; i < 32; i++) + if (params->n_members_per_group_max == 1U << i) + t->n_members_per_group_max_log2 = i; + + /* t->params.selector_mask */ + t->params.selector_mask = calloc(selector_size, sizeof(uint8_t)); + if (!t->params.selector_mask) + goto error; + + if (params->selector_mask) + memcpy(t->params.selector_mask, params->selector_mask, params->selector_size); + else + memset(t->params.selector_mask, 0xFF, params->selector_size); + + return 0; + +error: + free(t->params.selector_mask); + t->params.selector_mask = NULL; + + return -ENOMEM; +} + +static int +group_set(struct table *t, + uint32_t group_id, + struct rte_swx_table_selector_group *group); + +void * +rte_swx_table_selector_create(struct rte_swx_table_selector_params *params, + struct rte_swx_table_selector_group **groups, + int numa_node) +{ + struct table *t = NULL; + uint32_t group_size, i; + int status; + + /* Check input arguments. */ + status = table_create_check(params); + if (status) + goto error; + + /* Table object. */ + t = calloc(1, sizeof(struct table)); + if (!t) + goto error; + + /* Parameter copy. */ + status = table_params_copy(t, params); + if (status) + goto error; + + /* Group. */ + group_size = params->n_members_per_group_max * sizeof(uint32_t); + t->group_table_size = params->n_groups_max * group_size; + + t->group_table = env_calloc(t->group_table_size, RTE_CACHE_LINE_SIZE, numa_node); + if (!t->group_table) + goto error; + + t->members = calloc(params->n_members_per_group_max, sizeof(struct group_member_info)); + if (!t->members) + goto error; + + if (groups) + for (i = 0; i < params->n_groups_max; i++) + if (groups[i]) { + status = group_set(t, i, groups[i]); + if (status) + goto error; + } + + return t; + +error: + rte_swx_table_selector_free(t); + return NULL; +} + + +static int +group_check(struct table *t, struct rte_swx_table_selector_group *group) +{ + struct rte_swx_table_selector_member *elem; + uint32_t n_members = 0; + + if (!group) + return 0; + + TAILQ_FOREACH(elem, &group->members, node) { + struct rte_swx_table_selector_member *e; + uint32_t n = 0; + + /* Check group size. */ + if (n_members >= t->params.n_members_per_group_max) + return -ENOSPC; + + /* Check attributes of the current group member. */ + if (elem->member_id >= t->params.n_members_per_group_max || + !elem->member_weight) + return -ENOSPC; + + /* Check against duplicate member IDs. */ + TAILQ_FOREACH(e, &group->members, node) + if (e->member_id == elem->member_id) + n++; + + if (n != 1) + return -EINVAL; + + /* Update group size. */ + n_members++; + } + + return 0; +} + +static uint32_t +members_read(struct group_member_info *members, + struct rte_swx_table_selector_group *group) +{ + struct rte_swx_table_selector_member *elem; + uint32_t n_members = 0; + + if (!group) + return 0; + + TAILQ_FOREACH(elem, &group->members, node) { + struct group_member_info *m = &members[n_members]; + + memset(m, 0, sizeof(struct group_member_info)); + + m->member_id = elem->member_id; + m->member_weight = elem->member_weight; + m->member_weight_normalized = elem->member_weight; + + n_members++; + } + + return n_members; +} + +static uint32_t +members_min_weight_find(struct group_member_info *members, uint32_t n_members) +{ + uint32_t min = UINT32_MAX, i; + + for (i = 0; i < n_members; i++) { + struct group_member_info *m = &members[i]; + + if (m->member_weight < min) + min = m->member_weight; + } + + return min; +} + +static uint32_t +members_weight_divisor_check(struct group_member_info *members, + uint32_t n_members, + uint32_t divisor) +{ + uint32_t i; + + for (i = 0; i < n_members; i++) { + struct group_member_info *m = &members[i]; + + if (m->member_weight_normalized % divisor) + return 0; /* FALSE. */ + } + + return 1; /* TRUE. */ +} + +static void +members_weight_divisor_apply(struct group_member_info *members, + uint32_t n_members, + uint32_t divisor) +{ + uint32_t i; + + for (i = 0; i < n_members; i++) { + struct group_member_info *m = &members[i]; + + m->member_weight_normalized /= divisor; + } +} + +static uint32_t +members_weight_sum(struct group_member_info *members, uint32_t n_members) +{ + uint32_t result = 0, i; + + for (i = 0; i < n_members; i++) { + struct group_member_info *m = &members[i]; + + result += m->member_weight_normalized; + } + + return result; +} + +static void +members_weight_scale(struct group_member_info *members, + uint32_t n_members, + uint32_t n_members_per_group_max, + uint32_t weight_sum) +{ + uint32_t multiplier, remainder, i; + + multiplier = n_members_per_group_max / weight_sum; + remainder = n_members_per_group_max % weight_sum; + + for (i = 0; i < n_members; i++) { + struct group_member_info *m = &members[i]; + + m->count = m->member_weight_normalized * multiplier; + } + + for (i = 0; i < n_members; i++) { + struct group_member_info *m = &members[i]; + uint32_t min; + + min = m->member_weight_normalized; + if (remainder < m->member_weight_normalized) + min = remainder; + + m->count += min; + remainder -= min; + if (!remainder) + break; + } +} + +static void +members_write(struct group_member_info *members, + uint32_t n_members, + uint32_t *group_table) +{ + uint32_t pos = 0, i; + + for (i = 0; i < n_members; i++) { + struct group_member_info *m = &members[i]; + uint32_t j; + + for (j = 0; j < m->count; j++) + group_table[pos++] = m->member_id; + } +} + +static int +group_set(struct table *t, + uint32_t group_id, + struct rte_swx_table_selector_group *group) +{ + uint32_t *gt = &t->group_table[group_id * t->params.n_members_per_group_max]; + struct group_member_info *members = t->members; + uint32_t n_members, weight_min, weight_sum, divisor; + int status = 0; + + /* Check input arguments. */ + if (group_id >= t->params.n_groups_max) + return -EINVAL; + + status = group_check(t, group); + if (status) + return status; + + /* Read group members. */ + n_members = members_read(members, group); + + if (!n_members) { + memset(gt, 0, t->params.n_members_per_group_max * sizeof(uint32_t)); + + return 0; + } + + /* Normalize weights. */ + weight_min = members_min_weight_find(members, n_members); + + for (divisor = 2; divisor <= weight_min; divisor++) + if (members_weight_divisor_check(members, n_members, divisor)) + members_weight_divisor_apply(members, n_members, divisor); + + /* Scale weights. */ + weight_sum = members_weight_sum(members, n_members); + if (weight_sum > t->params.n_members_per_group_max) + return -ENOSPC; + + members_weight_scale(members, n_members, t->params.n_members_per_group_max, weight_sum); + + /* Write group members to the group table. */ + members_write(members, n_members, gt); + + return 0; +} + +int +rte_swx_table_selector_group_set(void *table, + uint32_t group_id, + struct rte_swx_table_selector_group *group) +{ + struct table *t = table; + + return group_set(t, group_id, group); +} + +struct mailbox { + +}; + +uint64_t +rte_swx_table_selector_mailbox_size_get(void) +{ + return sizeof(struct mailbox); +} + +int +rte_swx_table_selector_select(void *table, + void *mailbox __rte_unused, + uint8_t **group_id_buffer, + uint8_t **selector_buffer, + uint8_t **member_id_buffer) +{ + struct table *t = table; + uint32_t *group_id_ptr, *member_id_ptr, group_id, member_id, selector, group_member_index; + + group_id_ptr = (uint32_t *)&(*group_id_buffer)[t->params.group_id_offset]; + member_id_ptr = (uint32_t *)&(*member_id_buffer)[t->params.member_id_offset]; + + group_id = *group_id_ptr & (t->params.n_groups_max - 1); + + selector = hash(&(*selector_buffer)[t->params.selector_offset], t->params.selector_mask, t->params.selector_size, 0); + group_member_index = selector & (t->params.n_members_per_group_max - 1); + + member_id = t->group_table[(group_id << t->n_members_per_group_max_log2) + group_member_index]; + *member_id_ptr = member_id; + + return 1; +} diff --git a/lib/table/rte_swx_table_selector.h b/lib/table/rte_swx_table_selector.h new file mode 100644 index 000000000..71b6a7481 --- /dev/null +++ b/lib/table/rte_swx_table_selector.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2021 Intel Corporation + */ +#ifndef __INCLUDE_RTE_SWX_TABLE_SELECTOR_H__ +#define __INCLUDE_RTE_SWX_TABLE_SELECTOR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * RTE SWX Selector Table + * + * Selector table interface. + */ + +#include +#include + +#include + +#include "rte_swx_table.h" + +/** Selector table creation parameters. */ +struct rte_swx_table_selector_params { + /** Group ID offset. */ + uint32_t group_id_offset; + + /** Selector size in bytes. Must be non-zero. */ + uint32_t selector_size; + + /** Offset of the first byte of the selector within the selector buffer. */ + uint32_t selector_offset; + + /** Mask of *selector_size* bytes logically laid over the bytes at positions + * selector_offset* .. (*selector_offset* + *selector_size* - 1) of the selector buffer in + * order to specify which bits from the selector buffer are part of the selector and which + * ones are not. A bit value of 1 in the *selector_mask* means the respective bit in the + * selector buffer is part of the selector, while a bit value of 0 means the opposite. A + * NULL value means that all the bits are part of the selector, i.e. the *selector_mask* + * is an all-ones mask. + */ + uint8_t *selector_mask; + + /** Member ID offset. */ + uint32_t member_id_offset; + + /** Maximum number of groups. Must be non-zero. */ + uint32_t n_groups_max; + + /** Maximum number of members per group. Must be non-zero. */ + uint32_t n_members_per_group_max; +}; + +/** Group member parameters. */ +struct rte_swx_table_selector_member { + /** Linked list connectivity. */ + TAILQ_ENTRY(rte_swx_table_selector_member) node; + + /** Member ID. */ + uint32_t member_id; + + /** Member weight. */ + uint32_t member_weight; +}; + +/** List of group members. */ +TAILQ_HEAD(rte_swx_table_selector_member_list, rte_swx_table_selector_member); + +/** Group parameters. */ +struct rte_swx_table_selector_group { + /** List of group members. */ + struct rte_swx_table_selector_member_list members; +}; + +/** + * Selector table memory footprint get + * + * @param[in] n_groups_max + * Maximum number of groups. Must be non-zero. + * @param[in] n_members_per_group_max + * Maximum number of members per group. Must be non-zero. + * @return + * Selector table memory footprint in bytes. + */ +__rte_experimental +uint64_t +rte_swx_table_selector_footprint_get(uint32_t n_groups_max, uint32_t n_members_per_group_max); + +/** + * Selector table mailbox size get + * + * The mailbox is used to store the context of a select operation that is in + * progress and it is passed as a parameter to the select operation. This allows + * for multiple concurrent select operations into the same table. + * + * @return + * Selector table mailbox footprint in bytes. + */ +__rte_experimental +uint64_t +rte_swx_table_selector_mailbox_size_get(void); + +/** + * Selector table create + * + * @param[in] params + * Selector table creation parameters. + * @param[in] groups + * Groups to be added to the table at creation time. When NULL, it signifies that all groups are + * invalid, otherwise it points to a pre-allocated array of size *n_groups_max*, where a NULL + * element indicates that the associated group is invalid. + * @param[in] numa_node + * Non-Uniform Memory Access (NUMA) node. + * @return + * Table handle, on success, or NULL, on error. + */ +__rte_experimental +void * +rte_swx_table_selector_create(struct rte_swx_table_selector_params *params, + struct rte_swx_table_selector_group **groups, + int numa_node); + +/** + * Group set + * + * @param[in] table + * Selector table handle. + * @param[in] group_id + * Group ID. + * @param[in] group + * Group parameters. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument(s); + * -ENOSPC: Too many group members. + */ +__rte_experimental +int +rte_swx_table_selector_group_set(void *table, + uint32_t group_id, + struct rte_swx_table_selector_group *group); + +/** + * Selector table select + * + * This operation selects a member from the given group based on a hasing scheme. + * + * Multiple invocations of this function may be required in order to complete a single select + * operation for a given table and a given group ID. The completion of the operation is flagged by + * a return value of 1; in case of a return value of 0, the function must be invoked again with + * exactly the same arguments. + * + * The mailbox argument is used to store the context of each on-going operation. The mailbox + * mechanism allows for multiple concurrent select operations into the same table. + * + * The typical reason an implementation may choose to split the operation into multiple steps is to + * hide the latency of the inherrent memory read operations: before a read operation with the + * source data likely not in the CPU cache, the source data prefetch is issued and the operation is + * postponed in favor of some other unrelated work, which the CPU executes in parallel with the + * source data being fetched into the CPU cache; later on, the operation is resumed, this time with + * the source data likely to be read from the CPU cache with no CPU pipeline stall, which + * significantly improves the operation performance. + * + * @param[in] table + * Selector table handle. + * @param[in] mailbox + * Mailbox for the current operation. + * @param[in] group_id_buffer + * Buffer where the input group ID is located at offset *group_id_offset*. + * @param[in] selector_buffer + * Buffer where the key to select a member within the identified group is located starting from + * offset *selector_offset*. Its size must be equal to the table *selector_size*. + * @param[in] member_id_buffer + * Buffer where the output member ID is to be placed at offset *member_id_offset*. + * @return + * 0 when the operation is not yet completed, and 1 when the operation is complete. No other + * return values are allowed. + */ +__rte_experimental +int +rte_swx_table_selector_select(void *table, + void *mailbox, + uint8_t **group_id_buffer, + uint8_t **selector_buffer, + uint8_t **member_id_buffer); + +/** + * Selector table free + * + * @param[in] table + * Selector table handle. + */ +__rte_experimental +void +rte_swx_table_selector_free(void *table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/table/version.map b/lib/table/version.map index eb0291ac4..29301480c 100644 --- a/lib/table/version.map +++ b/lib/table/version.map @@ -28,4 +28,12 @@ EXPERIMENTAL { # added in 21.05 rte_swx_table_wildcard_match_ops; + + # added in 21.08 + rte_swx_table_selector_create; + rte_swx_table_selector_footprint_get; + rte_swx_table_selector_free; + rte_swx_table_selector_group_set; + rte_swx_table_selector_mailbox_size_get; + rte_swx_table_selector_select; }; From patchwork Fri Jul 2 20:50:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 95223 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 6A2CAA0C3F; Fri, 2 Jul 2021 22:50:18 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4F94D4141D; Fri, 2 Jul 2021 22:50:18 +0200 (CEST) Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by mails.dpdk.org (Postfix) with ESMTP id CD28E4141D for ; Fri, 2 Jul 2021 22:50:15 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10033"; a="195947587" X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="195947587" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga101.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jul 2021 13:50:13 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="482704227" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by FMSMGA003.fm.intel.com with ESMTP; 02 Jul 2021 13:50:12 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Date: Fri, 2 Jul 2021 21:50:11 +0100 Message-Id: <20210702205011.61544-1-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 Subject: [dpdk-dev] [PATCH 3/5] pipeline: add support for selector tables 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 Sender: "dev" Add pipeline-level support for selector tables, Signed-off-by: Cristian Dumitrescu --- lib/pipeline/rte_swx_ctl.c | 700 ++++++++++++++++++++++++- lib/pipeline/rte_swx_ctl.h | 253 +++++++++ lib/pipeline/rte_swx_pipeline.c | 748 ++++++++++++++++++++++++--- lib/pipeline/rte_swx_pipeline.h | 51 ++ lib/pipeline/rte_swx_pipeline_spec.c | 354 ++++++++++++- lib/pipeline/version.map | 13 + 6 files changed, 2034 insertions(+), 85 deletions(-) diff --git a/lib/pipeline/rte_swx_ctl.c b/lib/pipeline/rte_swx_ctl.c index 5d04e750f..7e5ebb4e5 100644 --- a/lib/pipeline/rte_swx_ctl.c +++ b/lib/pipeline/rte_swx_ctl.c @@ -10,6 +10,8 @@ #include #include +#include + #include "rte_swx_ctl.h" #define CHECK(condition, err_code) \ @@ -89,11 +91,44 @@ struct table { uint32_t n_delete; }; +struct selector { + /* Selector table info. */ + struct rte_swx_ctl_selector_info info; + + /* group_id field. */ + struct rte_swx_ctl_table_match_field_info group_id_field; + + /* selector fields. */ + struct rte_swx_ctl_table_match_field_info *selector_fields; + + /* member_id field. */ + struct rte_swx_ctl_table_match_field_info member_id_field; + + /* Current selector table. Array of info.n_groups_max elements.*/ + struct rte_swx_table_selector_group **groups; + + /* Pending selector table subject to the next commit. Array of info.n_groups_max elements. + */ + struct rte_swx_table_selector_group **pending_groups; + + /* Valid flag per group. Array of n_groups_max elements. */ + int *groups_added; + + /* Pending delete flag per group. Group deletion is subject to the next commit. Array of + * info.n_groups_max elements. + */ + int *groups_pending_delete; + + /* Params. */ + struct rte_swx_table_selector_params params; +}; + struct rte_swx_ctl_pipeline { struct rte_swx_ctl_pipeline_info info; struct rte_swx_pipeline *p; struct action *actions; struct table *tables; + struct selector *selectors; struct rte_swx_table_state *ts; struct rte_swx_table_state *ts_next; int numa_node; @@ -709,6 +744,209 @@ table_free(struct rte_swx_ctl_pipeline *ctl) ctl->tables = NULL; } +static void +selector_group_members_free(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *group = s->groups[group_id]; + + if (!group) + return; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + free(group); + s->groups[group_id] = NULL; +} + +static void +selector_pending_group_members_free(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *group = s->pending_groups[group_id]; + + if (!group) + return; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + free(group); + s->pending_groups[group_id] = NULL; +} + +static int +selector_group_duplicate_to_pending(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *g, *gp; + struct rte_swx_table_selector_member *m; + + selector_pending_group_members_free(s, group_id); + + g = s->groups[group_id]; + gp = s->pending_groups[group_id]; + + if (!gp) { + gp = calloc(1, sizeof(struct rte_swx_table_selector_group)); + if (!gp) + goto error; + + TAILQ_INIT(&gp->members); + + s->pending_groups[group_id] = gp; + } + + if (!g) + return 0; + + TAILQ_FOREACH(m, &g->members, node) { + struct rte_swx_table_selector_member *mp; + + mp = calloc(1, sizeof(struct rte_swx_table_selector_member)); + if (!mp) + goto error; + + memcpy(mp, m, sizeof(struct rte_swx_table_selector_member)); + + TAILQ_INSERT_TAIL(&gp->members, mp, node); + } + + return 0; + +error: + selector_pending_group_members_free(s, group_id); + return -ENOMEM; +} + +static void +selector_free(struct rte_swx_ctl_pipeline *ctl) +{ + uint32_t i; + + if (ctl->selectors) + return; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + uint32_t i; + + /* selector_fields. */ + free(s->selector_fields); + + /* groups. */ + if (s->groups) + for (i = 0; i < s->info.n_groups_max; i++) + selector_group_members_free(s, i); + + free(s->groups); + + /* pending_groups. */ + if (s->pending_groups) + for (i = 0; i < s->info.n_groups_max; i++) + selector_pending_group_members_free(s, i); + + free(s->pending_groups); + + /* groups_added. */ + free(s->groups_added); + + /* groups_pending_delete. */ + free(s->groups_pending_delete); + + /* params. */ + free(s->params.selector_mask); + } + + free(ctl->selectors); + ctl->selectors = NULL; +} + +static struct selector * +selector_find(struct rte_swx_ctl_pipeline *ctl, const char *selector_name) +{ + uint32_t i; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + + if (!strcmp(selector_name, s->info.name)) + return s; + } + + return NULL; +} + +static int +selector_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_ctl_table_match_field_info *first = NULL, *last = NULL; + uint8_t *selector_mask = NULL; + uint32_t selector_size = 0, selector_offset = 0, i; + + /* Find first (smallest offset) and last (biggest offset) match fields. */ + first = &s->selector_fields[0]; + last = &s->selector_fields[0]; + + for (i = 1; i < s->info.n_selector_fields; i++) { + struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i]; + + if (f->offset < first->offset) + first = f; + + if (f->offset > last->offset) + last = f; + } + + /* selector_offset. */ + selector_offset = first->offset / 8; + + /* selector_size. */ + selector_size = (last->offset + last->n_bits - first->offset) / 8; + + /* selector_mask. */ + selector_mask = calloc(1, selector_size); + if (!selector_mask) + return -ENOMEM; + + for (i = 0; i < s->info.n_selector_fields; i++) { + struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i]; + uint32_t start; + size_t size; + + start = (f->offset - first->offset) / 8; + size = f->n_bits / 8; + + memset(&selector_mask[start], 0xFF, size); + } + + /* Fill in. */ + s->params.group_id_offset = s->group_id_field.offset / 8; + s->params.selector_size = selector_size; + s->params.selector_offset = selector_offset; + s->params.selector_mask = selector_mask; + s->params.member_id_offset = s->member_id_field.offset / 8; + s->params.n_groups_max = s->info.n_groups_max; + s->params.n_members_per_group_max = s->info.n_members_per_group_max; + + return 0; +} + static void table_state_free(struct rte_swx_ctl_pipeline *ctl) { @@ -730,6 +968,15 @@ table_state_free(struct rte_swx_ctl_pipeline *ctl) table->ops.free(ts->obj); } + /* For each selector table, free its table state. */ + for (i = 0; i < ctl->info.n_selectors; i++) { + struct rte_swx_table_state *ts = &ctl->ts_next[i]; + + /* Table object. */ + if (ts->obj) + rte_swx_table_selector_free(ts->obj); + } + free(ctl->ts_next); ctl->ts_next = NULL; } @@ -740,13 +987,14 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl) int status = 0; uint32_t i; - ctl->ts_next = calloc(ctl->info.n_tables, + ctl->ts_next = calloc(ctl->info.n_tables + ctl->info.n_selectors, sizeof(struct rte_swx_table_state)); if (!ctl->ts_next) { status = -ENOMEM; goto error; } + /* Tables. */ for (i = 0; i < ctl->info.n_tables; i++) { struct table *table = &ctl->tables[i]; struct rte_swx_table_state *ts = &ctl->ts[i]; @@ -782,6 +1030,19 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl) ts_next->default_action_id = ts->default_action_id; } + /* Selector tables. */ + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + i]; + + /* Table object. */ + ts_next->obj = rte_swx_table_selector_create(&s->params, NULL, ctl->numa_node); + if (!ts_next->obj) { + status = -ENODEV; + goto error; + } + } + return 0; error: @@ -799,6 +1060,8 @@ rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl) table_state_free(ctl); + selector_free(ctl); + table_free(ctl); free(ctl); @@ -940,6 +1203,77 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p) goto error; } + /* selector tables. */ + ctl->selectors = calloc(ctl->info.n_selectors, sizeof(struct selector)); + if (!ctl->selectors) + goto error; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + uint32_t j; + + /* info. */ + status = rte_swx_ctl_selector_info_get(p, i, &s->info); + if (status) + goto error; + + /* group_id field. */ + status = rte_swx_ctl_selector_group_id_field_info_get(p, + i, + &s->group_id_field); + if (status) + goto error; + + /* selector fields. */ + s->selector_fields = calloc(s->info.n_selector_fields, + sizeof(struct rte_swx_ctl_table_match_field_info)); + if (!s->selector_fields) + goto error; + + for (j = 0; j < s->info.n_selector_fields; j++) { + status = rte_swx_ctl_selector_field_info_get(p, + i, + j, + &s->selector_fields[j]); + if (status) + goto error; + } + + /* member_id field. */ + status = rte_swx_ctl_selector_member_id_field_info_get(p, + i, + &s->member_id_field); + if (status) + goto error; + + /* groups. */ + s->groups = calloc(s->info.n_groups_max, + sizeof(struct rte_swx_table_selector_group *)); + if (!s->groups) + goto error; + + /* pending_groups. */ + s->pending_groups = calloc(s->info.n_groups_max, + sizeof(struct rte_swx_table_selector_group *)); + if (!s->pending_groups) + goto error; + + /* groups_added. */ + s->groups_added = calloc(s->info.n_groups_max, sizeof(int)); + if (!s->groups_added) + goto error; + + /* groups_pending_delete. */ + s->groups_pending_delete = calloc(s->info.n_groups_max, sizeof(int)); + if (!s->groups_pending_delete) + goto error; + + /* params. */ + status = selector_params_get(ctl, i); + if (status) + goto error; + } + /* ts. */ status = rte_swx_pipeline_table_state_get(p, &ctl->ts); if (status) @@ -1499,6 +1833,295 @@ table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id) table_pending_default_free(table); } +int +rte_swx_ctl_pipeline_selector_group_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t *group_id) +{ + struct selector *s; + uint32_t i; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0] || !group_id) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s) + return -EINVAL; + + /* Find an unused group. */ + for (i = 0; i < s->info.n_groups_max; i++) + if (!s->groups_added[i]) { + *group_id = i; + s->groups_added[i] = 1; + return 0; + } + + return -ENOSPC; +} + +int +rte_swx_ctl_pipeline_selector_group_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id]) + return -EINVAL; + + /* Check if this group is already scheduled for deletion. */ + if (s->groups_pending_delete[group_id]) + return 0; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* Schedule removal of all the members from the current group. */ + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + /* Schedule the group for deletion. */ + s->groups_pending_delete[group_id] = 1; + + return 0; +} + +int +rte_swx_ctl_pipeline_selector_group_member_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id, + uint32_t member_id, + uint32_t member_weight) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + struct rte_swx_table_selector_member *m; + + if (!member_weight) + return rte_swx_ctl_pipeline_selector_group_member_delete(ctl, + selector_name, + group_id, + member_id); + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id] || + s->groups_pending_delete[group_id]) + return -EINVAL; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* If this member is already in this group, then simply update its weight and return. */ + TAILQ_FOREACH(m, &group->members, node) + if (m->member_id == member_id) { + m->member_weight = member_weight; + return 0; + } + + /* Add new member to this group. */ + m = calloc(1, sizeof(struct rte_swx_table_selector_member)); + if (!m) + return -ENOMEM; + + m->member_id = member_id; + m->member_weight = member_weight; + + TAILQ_INSERT_TAIL(&group->members, m, node); + + return 0; +} + +int +rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id __rte_unused, + uint32_t member_id __rte_unused) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + struct rte_swx_table_selector_member *m; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id] || + s->groups_pending_delete[group_id]) + return -EINVAL; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* Look for this member in the group and remove it, if found. */ + TAILQ_FOREACH(m, &group->members, node) + if (m->member_id == member_id) { + TAILQ_REMOVE(&group->members, m, node); + free(m); + return 0; + } + + return 0; +} + +static int +selector_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id]; + uint32_t group_id; + + /* Push pending group member changes (s->pending_groups[group_id]) to the selector table + * mirror copy (ts_next->obj). + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *group = s->pending_groups[group_id]; + int status; + + /* Skip this group if no change needed. */ + if (!group) + continue; + + /* Apply the pending changes for the current group. */ + status = rte_swx_table_selector_group_set(ts_next->obj, group_id, group); + if (status) + return status; + } + + return 0; +} + +static void +selector_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + uint32_t group_id; + + /* Commit pending group member changes (s->pending_groups[group_id]) to the stable group + * records (s->groups[group_id). + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *g = s->groups[group_id]; + struct rte_swx_table_selector_group *gp = s->pending_groups[group_id]; + + /* Skip this group if no change needed. */ + if (!gp) + continue; + + /* Transition the pending changes to stable. */ + s->groups[group_id] = gp; + s->pending_groups[group_id] = NULL; + + /* Free the old group member list. */ + if (!g) + continue; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&g->members); + if (!m) + break; + + TAILQ_REMOVE(&g->members, m, node); + free(m); + } + + free(g); + } + + /* Commit pending group validity changes (from s->groups_pending_delete[group_id] to + * s->groups_added[group_id]. + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) + if (s->groups_pending_delete[group_id]) { + s->groups_added[group_id] = 0; + s->groups_pending_delete[group_id] = 0; + } +} + +static void +selector_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_table_state *ts = &ctl->ts[ctl->info.n_tables + selector_id]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id]; + uint32_t group_id; + + /* Discard any previous changes to the selector table mirror copy (ts_next->obj). */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *gp = s->pending_groups[group_id]; + + if (gp) { + ts_next->obj = ts->obj; + break; + } + } +} + +static void +selector_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + uint32_t group_id; + + /* Discard any pending group member changes (s->pending_groups[group_id]). */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) + selector_pending_group_members_free(s, group_id); + + /* Discard any pending group deletions. */ + memset(s->groups_pending_delete, 0, s->info.n_groups_max * sizeof(int)); +} + int rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) { @@ -1508,8 +2131,8 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) CHECK(ctl, EINVAL); - /* Operate the changes on the current ts_next before it becomes the new - * ts. + /* Operate the changes on the current ts_next before it becomes the new ts. First, operate + * all the changes that can fail; if no failure, then operate the changes that cannot fail. */ for (i = 0; i < ctl->info.n_tables; i++) { status = table_rollfwd0(ctl, i, 0); @@ -1517,6 +2140,12 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) goto rollback; } + for (i = 0; i < ctl->info.n_selectors; i++) { + status = selector_rollfwd(ctl, i); + if (status) + goto rollback; + } + for (i = 0; i < ctl->info.n_tables; i++) table_rollfwd1(ctl, i); @@ -1529,7 +2158,10 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) ctl->ts = ctl->ts_next; ctl->ts_next = ts; - /* Operate the changes on the current ts_next, which is the previous ts. + /* Operate the changes on the current ts_next, which is the previous ts, in order to get + * the current ts_next in sync with the current ts. Since the changes that can fail did + * not fail on the previous ts_next, it is guaranteed that they will not fail on the + * current ts_next, hence no error checking is needed. */ for (i = 0; i < ctl->info.n_tables; i++) { table_rollfwd0(ctl, i, 1); @@ -1537,6 +2169,11 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) table_rollfwd2(ctl, i); } + for (i = 0; i < ctl->info.n_selectors; i++) { + selector_rollfwd(ctl, i); + selector_rollfwd_finalize(ctl, i); + } + return 0; rollback: @@ -1546,6 +2183,12 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) table_abort(ctl, i); } + for (i = 0; i < ctl->info.n_selectors; i++) { + selector_rollback(ctl, i); + if (abort_on_fail) + selector_abort(ctl, i); + } + return status; } @@ -1559,6 +2202,9 @@ rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl) for (i = 0; i < ctl->info.n_tables; i++) table_abort(ctl, i); + + for (i = 0; i < ctl->info.n_selectors; i++) + selector_abort(ctl, i); } static int @@ -1858,3 +2504,49 @@ rte_swx_ctl_pipeline_table_fprintf(FILE *f, n_entries); return 0; } + +int +rte_swx_ctl_pipeline_selector_fprintf(FILE *f, + struct rte_swx_ctl_pipeline *ctl, + const char *selector_name) +{ + struct selector *s; + uint32_t group_id; + + if (!f || !ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if(!s) + return -EINVAL; + + /* Selector. */ + fprintf(f, "# Selector %s: max groups %u, max members per group %u\n", + s->info.name, + s->info.n_groups_max, + s->info.n_members_per_group_max); + + /* Groups. */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *group = s->groups[group_id]; + struct rte_swx_table_selector_member *m; + uint32_t n_members = 0; + + fprintf(f, "Group %u = [", group_id); + + /* Non-empty group. */ + if (group) + TAILQ_FOREACH(m, &group->members, node) { + fprintf(f, "%u:%u ", m->member_id, m->member_weight); + n_members++; + } + + /* Empty group. */ + if (!n_members) + fprintf(f, "0:1 "); + + fprintf(f, "]\n"); + } + + return 0; +} diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h index dee788be8..f37301cf9 100644 --- a/lib/pipeline/rte_swx_ctl.h +++ b/lib/pipeline/rte_swx_ctl.h @@ -22,6 +22,7 @@ extern "C" { #include "rte_swx_port.h" #include "rte_swx_table.h" +#include "rte_swx_table_selector.h" struct rte_swx_pipeline; @@ -48,6 +49,9 @@ struct rte_swx_ctl_pipeline_info { /** Number of tables. */ uint32_t n_tables; + /** Number of selector tables. */ + uint32_t n_selectors; + /** Number of register arrays. */ uint32_t n_regarrays; @@ -385,6 +389,129 @@ rte_swx_ctl_pipeline_table_stats_read(struct rte_swx_pipeline *p, const char *table_name, struct rte_swx_table_stats *stats); +/* + * Selector Table Query API. + */ + +/** Selector info. */ +struct rte_swx_ctl_selector_info { + /** Selector table name. */ + char name[RTE_SWX_CTL_NAME_SIZE]; + + /** Number of selector fields. */ + uint32_t n_selector_fields; + + /** Maximum number of groups. */ + uint32_t n_groups_max; + + /** Maximum number of members per group. */ + uint32_t n_members_per_group_max; +}; + +/** + * Selector table info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors* - 1). + * @param[out] selector + * Selector table info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_selector_info *selector); + +/** + * Selector table "group ID" field info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors*). + * @param[out] field + * Selector table "group ID" field info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_group_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field); + +/** + * Sselector table selector field info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors*). + * @param[in] selector_field_id + * Selector table selector field ID (0 .. *n_selector_fields* - 1). + * @param[out] field + * Selector table selector field info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + uint32_t selector_field_id, + struct rte_swx_ctl_table_match_field_info *field); + +/** + * Selector table "member ID" field info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors*). + * @param[out] field + * Selector table "member ID" field info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_member_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field); + +/** Selector table statistics. */ +struct rte_swx_pipeline_selector_stats { + /** Number of packets. */ + uint64_t n_pkts; +}; + +/** + * Selector table statistics counters read + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_name + * Selector table name. + * @param[out] stats + * Selector table stats. Must point to a pre-allocated structure. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_stats_read(struct rte_swx_pipeline *p, + const char *selector_name, + struct rte_swx_pipeline_selector_stats *stats); + /* * Table Update API. */ @@ -529,6 +656,111 @@ rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl, const char *table_name, struct rte_swx_table_entry *entry); +/** + * Pipeline selector table group add + * + * Add a new group to a selector table. This operation is executed before this + * function returns and its result is independent of the result of the next + * commit operation. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[out] group_id + * The ID of the new group. Only valid when the function call is successful. + * This group is initially empty, i.e. it does not contain any members. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOSPC: All groups are currently in use, no group available. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t *group_id); + +/** + * Pipeline selector table group delete + * + * Schedule a group for deletion as part of the next commit operation. The group + * to be deleted can be empty or non-empty. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[in] group_id + * Group to be deleted from the selector table. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough memory. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id); + +/** + * Pipeline selector table member add to group + * + * Schedule the operation to add a new member to an existing group as part of + * the next commit operation. If this member is already in this group, the + * member weight is updated to the new value. A weight of zero means this member + * is to be deleted from the group. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[in] group_id + * The group ID. + * @param[in] member_id + * The member to be added to the group. + * @param[in] member_weight + * Member weight. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough memory; + * -ENOSPC: The group is full. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_member_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id, + uint32_t member_id, + uint32_t member_weight); + +/** + * Pipeline selector table member delete from group + * + * Schedule the operation to delete a member from an existing group as part of + * the next commit operation. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[in] group_id + * The group ID. Must be valid. + * @param[in] member_id + * The member to be added to the group. Must be valid. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id, + uint32_t member_id); + /** * Pipeline commit * @@ -608,6 +840,27 @@ rte_swx_ctl_pipeline_table_fprintf(FILE *f, struct rte_swx_ctl_pipeline *ctl, const char *table_name); +/** + * Pipeline selector print to file + * + * Print all the selector entries to file. + * + * @param[in] f + * Output file. + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_fprintf(FILE *f, + struct rte_swx_ctl_pipeline *ctl, + const char *selector_name); + /* * Register Array Query API. */ diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index a2732a1e5..22c860f28 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -15,6 +15,8 @@ #include #include +#include + #include "rte_swx_pipeline.h" #include "rte_swx_ctl.h" @@ -498,6 +500,7 @@ enum instruction_type { /* table TABLE */ INSTR_TABLE, + INSTR_SELECTOR, /* extern e.obj.func */ INSTR_EXTERN_OBJ, @@ -794,6 +797,38 @@ struct table_statistics { uint64_t *n_pkts_action; }; +/* + * Selector. + */ +struct selector { + TAILQ_ENTRY(selector) node; + char name[RTE_SWX_NAME_SIZE]; + + struct field *group_id_field; + struct field **selector_fields; + uint32_t n_selector_fields; + struct header *selector_header; + struct field *member_id_field; + + uint32_t n_groups_max; + uint32_t n_members_per_group_max; + + uint32_t id; +}; + +TAILQ_HEAD(selector_tailq, selector); + +struct selector_runtime { + void *mailbox; + uint8_t **group_id_buffer; + uint8_t **selector_buffer; + uint8_t **member_id_buffer; +}; + +struct selector_statistics { + uint64_t n_pkts; +}; + /* * Register array. */ @@ -873,6 +908,7 @@ struct thread { /* Tables. */ struct table_runtime *tables; + struct selector_runtime *selectors; struct rte_swx_table_state *table_state; uint64_t action_id; int hit; /* 0 = Miss, 1 = Hit. */ @@ -1308,6 +1344,7 @@ struct rte_swx_pipeline { struct action_tailq actions; struct table_type_tailq table_types; struct table_tailq tables; + struct selector_tailq selectors; struct regarray_tailq regarrays; struct meter_profile_tailq meter_profiles; struct metarray_tailq metarrays; @@ -1317,6 +1354,7 @@ struct rte_swx_pipeline { struct instruction **action_instructions; struct rte_swx_table_state *table_state; struct table_statistics *table_stats; + struct selector_statistics *selector_stats; struct regarray_runtime *regarray_runtime; struct metarray_runtime *metarray_runtime; struct instruction *instructions; @@ -1329,6 +1367,7 @@ struct rte_swx_pipeline { uint32_t n_extern_funcs; uint32_t n_actions; uint32_t n_tables; + uint32_t n_selectors; uint32_t n_regarrays; uint32_t n_metarrays; uint32_t n_headers; @@ -3450,6 +3489,9 @@ instr_hdr_invalidate_exec(struct rte_swx_pipeline *p) static struct table * table_find(struct rte_swx_pipeline *p, const char *name); +static struct selector * +selector_find(struct rte_swx_pipeline *p, const char *name); + static int instr_table_translate(struct rte_swx_pipeline *p, struct action *action, @@ -3459,16 +3501,26 @@ instr_table_translate(struct rte_swx_pipeline *p, struct instruction_data *data __rte_unused) { struct table *t; + struct selector *s; CHECK(!action, EINVAL); CHECK(n_tokens == 2, EINVAL); t = table_find(p, tokens[1]); - CHECK(t, EINVAL); + if (t) { + instr->type = INSTR_TABLE; + instr->table.table_id = t->id; + return 0; + } - instr->type = INSTR_TABLE; - instr->table.table_id = t->id; - return 0; + s = selector_find(p, tokens[1]); + if (s) { + instr->type = INSTR_SELECTOR; + instr->table.table_id = s->id; + return 0; + } + + CHECK(0, EINVAL); } static inline void @@ -3522,6 +3574,45 @@ instr_table_exec(struct rte_swx_pipeline *p) thread_ip_action_call(p, t, action_id); } +static inline void +instr_selector_exec(struct rte_swx_pipeline *p) +{ + struct thread *t = &p->threads[p->thread_id]; + struct instruction *ip = t->ip; + uint32_t selector_id = ip->table.table_id; + struct rte_swx_table_state *ts = &t->table_state[p->n_tables + selector_id]; + struct selector_runtime *selector = &t->selectors[selector_id]; + struct selector_statistics *stats = &p->selector_stats[selector_id]; + uint64_t n_pkts = stats->n_pkts; + int done; + + /* Table. */ + done = rte_swx_table_selector_select(ts->obj, + selector->mailbox, + selector->group_id_buffer, + selector->selector_buffer, + selector->member_id_buffer); + if (!done) { + /* Thread. */ + TRACE("[Thread %2u] selector %u (not finalized)\n", + p->thread_id, + selector_id); + + thread_yield(p); + return; + } + + + TRACE("[Thread %2u] selector %u\n", + p->thread_id, + selector_id); + + stats->n_pkts = n_pkts + 1; + + /* Thread. */ + thread_ip_inc(p); +} + /* * extern. */ @@ -8787,6 +8878,7 @@ static instr_exec_t instruction_table[] = { [INSTR_METER_IMI] = instr_meter_imi_exec, [INSTR_TABLE] = instr_table_exec, + [INSTR_SELECTOR] = instr_selector_exec, [INSTR_EXTERN_OBJ] = instr_extern_obj_exec, [INSTR_EXTERN_FUNC] = instr_extern_func_exec, @@ -9253,6 +9345,7 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p, CHECK_NAME(name, EINVAL); CHECK(!table_find(p, name), EEXIST); + CHECK(!selector_find(p, name), EEXIST); CHECK(params, EINVAL); @@ -9455,82 +9548,6 @@ table_params_free(struct rte_swx_table_params *params) free(params); } -static int -table_state_build(struct rte_swx_pipeline *p) -{ - struct table *table; - - p->table_state = calloc(p->n_tables, - sizeof(struct rte_swx_table_state)); - CHECK(p->table_state, ENOMEM); - - TAILQ_FOREACH(table, &p->tables, node) { - struct rte_swx_table_state *ts = &p->table_state[table->id]; - - if (table->type) { - struct rte_swx_table_params *params; - - /* ts->obj. */ - params = table_params_get(table); - CHECK(params, ENOMEM); - - ts->obj = table->type->ops.create(params, - NULL, - table->args, - p->numa_node); - - table_params_free(params); - CHECK(ts->obj, ENODEV); - } - - /* ts->default_action_data. */ - if (table->action_data_size_max) { - ts->default_action_data = - malloc(table->action_data_size_max); - CHECK(ts->default_action_data, ENOMEM); - - memcpy(ts->default_action_data, - table->default_action_data, - table->action_data_size_max); - } - - /* ts->default_action_id. */ - ts->default_action_id = table->default_action->id; - } - - return 0; -} - -static void -table_state_build_free(struct rte_swx_pipeline *p) -{ - uint32_t i; - - if (!p->table_state) - return; - - for (i = 0; i < p->n_tables; i++) { - struct rte_swx_table_state *ts = &p->table_state[i]; - struct table *table = table_find_by_id(p, i); - - /* ts->obj. */ - if (table->type && ts->obj) - table->type->ops.free(ts->obj); - - /* ts->default_action_data. */ - free(ts->default_action_data); - } - - free(p->table_state); - p->table_state = NULL; -} - -static void -table_state_free(struct rte_swx_pipeline *p) -{ - table_state_build_free(p); -} - static int table_stub_lkp(void *table __rte_unused, void *mailbox __rte_unused, @@ -9658,6 +9675,458 @@ table_free(struct rte_swx_pipeline *p) } } +/* + * Selector. + */ +static struct selector * +selector_find(struct rte_swx_pipeline *p, const char *name) +{ + struct selector *s; + + TAILQ_FOREACH(s, &p->selectors, node) + if (strcmp(s->name, name) == 0) + return s; + + return NULL; +} + +static struct selector * +selector_find_by_id(struct rte_swx_pipeline *p, uint32_t id) +{ + struct selector *s = NULL; + + TAILQ_FOREACH(s, &p->selectors, node) + if (s->id == id) + return s; + + return NULL; +} + +static int +selector_fields_check(struct rte_swx_pipeline *p, + struct rte_swx_pipeline_selector_params *params, + struct header **header) +{ + struct header *h0 = NULL; + struct field *hf, *mf; + uint32_t i; + + /* Return if no selector fields. */ + if (!params->n_selector_fields || !params->selector_field_names) + return -EINVAL; + + /* Check that all the selector fields either belong to the same header + * or are all meta-data fields. + */ + hf = header_field_parse(p, params->selector_field_names[0], &h0); + mf = metadata_field_parse(p, params->selector_field_names[0]); + if (!hf && !mf) + return -EINVAL; + + for (i = 1; i < params->n_selector_fields; i++) + if (h0) { + struct header *h; + + hf = header_field_parse(p, params->selector_field_names[i], &h); + if (!hf || (h->id != h0->id)) + return -EINVAL; + } else { + mf = metadata_field_parse(p, params->selector_field_names[i]); + if (!mf) + return -EINVAL; + } + + /* Check that there are no duplicated match fields. */ + for (i = 0; i < params->n_selector_fields; i++) { + const char *field_name = params->selector_field_names[i]; + uint32_t j; + + for (j = i + 1; j < params->n_selector_fields; j++) + if (!strcmp(params->selector_field_names[j], field_name)) + return -EINVAL; + } + + /* Return. */ + if (header) + *header = h0; + + return 0; +} + +int +rte_swx_pipeline_selector_config(struct rte_swx_pipeline *p, + const char *name, + struct rte_swx_pipeline_selector_params *params) +{ + struct selector *s; + struct header *selector_header = NULL; + struct field *group_id_field, *member_id_field; + uint32_t i; + int status = 0; + + CHECK(p, EINVAL); + + CHECK_NAME(name, EINVAL); + CHECK(!table_find(p, name), EEXIST); + CHECK(!selector_find(p, name), EEXIST); + + CHECK(params, EINVAL); + + CHECK_NAME(params->group_id_field_name, EINVAL); + group_id_field = metadata_field_parse(p, params->group_id_field_name); + CHECK(group_id_field, EINVAL); + + for (i = 0; i < params->n_selector_fields; i++) { + const char *field_name = params->selector_field_names[i]; + + CHECK_NAME(field_name, EINVAL); + } + status = selector_fields_check(p, params, &selector_header); + if (status) + return status; + + CHECK_NAME(params->member_id_field_name, EINVAL); + member_id_field = metadata_field_parse(p, params->member_id_field_name); + CHECK(member_id_field, EINVAL); + + CHECK(params->n_groups_max, EINVAL); + + CHECK(params->n_members_per_group_max, EINVAL); + + /* Memory allocation. */ + s = calloc(1, sizeof(struct selector)); + if (!s) { + status = -ENOMEM; + goto error; + } + + s->selector_fields = calloc(params->n_selector_fields, sizeof(struct field *)); + if (!s->selector_fields) { + status = -ENOMEM; + goto error; + } + + /* Node initialization. */ + strcpy(s->name, name); + + s->group_id_field = group_id_field; + + for (i = 0; i < params->n_selector_fields; i++) { + const char *field_name = params->selector_field_names[i]; + + s->selector_fields[i] = selector_header ? + header_field_parse(p, field_name, NULL) : + metadata_field_parse(p, field_name); + } + + s->n_selector_fields = params->n_selector_fields; + + s->selector_header = selector_header; + + s->member_id_field = member_id_field; + + s->n_groups_max = params->n_groups_max; + + s->n_members_per_group_max = params->n_members_per_group_max; + + s->id = p->n_selectors; + + /* Node add to tailq. */ + TAILQ_INSERT_TAIL(&p->selectors, s, node); + p->n_selectors++; + + return 0; + +error: + if (!s) + return status; + + free(s->selector_fields); + + free(s); + + return status; +} + +static void +selector_params_free(struct rte_swx_table_selector_params *params) +{ + if (!params) + return; + + free(params->selector_mask); + + free(params); +} + +static struct rte_swx_table_selector_params * +selector_table_params_get(struct selector *s) +{ + struct rte_swx_table_selector_params *params = NULL; + struct field *first, *last; + uint32_t i; + + /* Memory allocation. */ + params = calloc(1, sizeof(struct rte_swx_pipeline_selector_params)); + if (!params) + goto error; + + /* Group ID. */ + params->group_id_offset = s->group_id_field->offset / 8; + + /* Find first (smallest offset) and last (biggest offset) selector fields. */ + first = s->selector_fields[0]; + last = s->selector_fields[0]; + + for (i = 0; i < s->n_selector_fields; i++) { + struct field *f = s->selector_fields[i]; + + if (f->offset < first->offset) + first = f; + + if (f->offset > last->offset) + last = f; + } + + /* Selector offset and size. */ + params->selector_offset = first->offset / 8; + params->selector_size = (last->offset + last->n_bits - first->offset) / 8; + + /* Memory allocation. */ + params->selector_mask = calloc(1, params->selector_size); + if (!params->selector_mask) + goto error; + + /* Selector mask. */ + for (i = 0; i < s->n_selector_fields; i++) { + struct field *f = s->selector_fields[i]; + uint32_t start = (f->offset - first->offset) / 8; + size_t size = f->n_bits / 8; + + memset(¶ms->selector_mask[start], 0xFF, size); + } + + /* Member ID. */ + params->member_id_offset = s->member_id_field->offset / 8; + + /* Maximum number of groups. */ + params->n_groups_max = s->n_groups_max; + + /* Maximum number of members per group. */ + params->n_members_per_group_max = s->n_members_per_group_max; + + return params; + +error: + selector_params_free(params); + return NULL; +} + +static void +selector_build_free(struct rte_swx_pipeline *p) +{ + uint32_t i; + + for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) { + struct thread *t = &p->threads[i]; + uint32_t j; + + if (!t->selectors) + continue; + + for (j = 0; j < p->n_selectors; j++) { + struct selector_runtime *r = &t->selectors[j]; + + free(r->mailbox); + } + + free(t->selectors); + t->selectors = NULL; + } + + free(p->selector_stats); + p->selector_stats = NULL; +} + +static int +selector_build(struct rte_swx_pipeline *p) +{ + uint32_t i; + int status = 0; + + /* Per pipeline: selector statistics. */ + p->selector_stats = calloc(p->n_selectors, sizeof(struct selector_statistics)); + if (!p->selector_stats) { + status = -ENOMEM; + goto error; + } + + /* Per thread: selector run-time. */ + for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) { + struct thread *t = &p->threads[i]; + struct selector *s; + + t->selectors = calloc(p->n_selectors, sizeof(struct selector_runtime)); + if (!t->selectors) { + status = -ENOMEM; + goto error; + } + + TAILQ_FOREACH(s, &p->selectors, node) { + struct selector_runtime *r = &t->selectors[s->id]; + uint64_t size; + + /* r->mailbox. */ + size = rte_swx_table_selector_mailbox_size_get(); + if (size) { + r->mailbox = calloc(1, size); + if (!r->mailbox) { + status = -ENOMEM; + goto error; + } + } + + /* r->group_id_buffer. */ + r->group_id_buffer = &t->structs[p->metadata_struct_id]; + + /* r->selector_buffer. */ + r->selector_buffer = s->selector_header ? + &t->structs[s->selector_header->struct_id] : + &t->structs[p->metadata_struct_id]; + + /* r->member_id_buffer. */ + r->member_id_buffer = &t->structs[p->metadata_struct_id]; + } + } + + return 0; + +error: + selector_build_free(p); + return status; +} + +static void +selector_free(struct rte_swx_pipeline *p) +{ + selector_build_free(p); + + /* Selector tables. */ + for ( ; ; ) { + struct selector *elem; + + elem = TAILQ_FIRST(&p->selectors); + if (!elem) + break; + + TAILQ_REMOVE(&p->selectors, elem, node); + free(elem->selector_fields); + free(elem); + } +} + +/* + * Table state. + */ +static int +table_state_build(struct rte_swx_pipeline *p) +{ + struct table *table; + struct selector *s; + + p->table_state = calloc(p->n_tables + p->n_selectors, + sizeof(struct rte_swx_table_state)); + CHECK(p->table_state, ENOMEM); + + TAILQ_FOREACH(table, &p->tables, node) { + struct rte_swx_table_state *ts = &p->table_state[table->id]; + + if (table->type) { + struct rte_swx_table_params *params; + + /* ts->obj. */ + params = table_params_get(table); + CHECK(params, ENOMEM); + + ts->obj = table->type->ops.create(params, + NULL, + table->args, + p->numa_node); + + table_params_free(params); + CHECK(ts->obj, ENODEV); + } + + /* ts->default_action_data. */ + if (table->action_data_size_max) { + ts->default_action_data = + malloc(table->action_data_size_max); + CHECK(ts->default_action_data, ENOMEM); + + memcpy(ts->default_action_data, + table->default_action_data, + table->action_data_size_max); + } + + /* ts->default_action_id. */ + ts->default_action_id = table->default_action->id; + } + + TAILQ_FOREACH(s, &p->selectors, node) { + struct rte_swx_table_state *ts = &p->table_state[p->n_tables + s->id]; + struct rte_swx_table_selector_params *params; + + /* ts->obj. */ + params = selector_table_params_get(s); + CHECK(params, ENOMEM); + + ts->obj = rte_swx_table_selector_create(params, NULL, p->numa_node); + + selector_params_free(params); + CHECK(ts->obj, ENODEV); + } + + return 0; +} + +static void +table_state_build_free(struct rte_swx_pipeline *p) +{ + uint32_t i; + + if (!p->table_state) + return; + + for (i = 0; i < p->n_tables; i++) { + struct rte_swx_table_state *ts = &p->table_state[i]; + struct table *table = table_find_by_id(p, i); + + /* ts->obj. */ + if (table->type && ts->obj) + table->type->ops.free(ts->obj); + + /* ts->default_action_data. */ + free(ts->default_action_data); + } + + for (i = 0; i < p->n_selectors; i++) { + struct rte_swx_table_state *ts = &p->table_state[p->n_tables + i]; + + /* ts->obj. */ + if (ts->obj) + rte_swx_table_selector_free(ts->obj); + } + + free(p->table_state); + p->table_state = NULL; +} + +static void +table_state_free(struct rte_swx_pipeline *p) +{ + table_state_build_free(p); +} + /* * Register array. */ @@ -9988,6 +10457,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node) TAILQ_INIT(&pipeline->actions); TAILQ_INIT(&pipeline->table_types); TAILQ_INIT(&pipeline->tables); + TAILQ_INIT(&pipeline->selectors); TAILQ_INIT(&pipeline->regarrays); TAILQ_INIT(&pipeline->meter_profiles); TAILQ_INIT(&pipeline->metarrays); @@ -10010,6 +10480,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p) metarray_free(p); regarray_free(p); table_state_free(p); + selector_free(p); table_free(p); action_free(p); metadata_free(p); @@ -10089,6 +10560,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p) if (status) goto error; + status = selector_build(p); + if (status) + goto error; + status = table_state_build(p); if (status) goto error; @@ -10108,6 +10583,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p) metarray_build_free(p); regarray_build_free(p); table_state_build_free(p); + selector_build_free(p); table_build_free(p); action_build_free(p); metadata_build_free(p); @@ -10167,6 +10643,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p, pipeline->n_ports_out = p->n_ports_out; pipeline->n_actions = n_actions; pipeline->n_tables = n_tables; + pipeline->n_selectors = p->n_selectors; pipeline->n_regarrays = p->n_regarrays; pipeline->n_metarrays = p->n_metarrays; @@ -10320,6 +10797,98 @@ rte_swx_ctl_table_ops_get(struct rte_swx_pipeline *p, return 0; } +int +rte_swx_ctl_selector_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_selector_info *selector) +{ + struct selector *s = NULL; + + if (!p || !selector) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s) + return -EINVAL; + + strcpy(selector->name, s->name); + + selector->n_selector_fields = s->n_selector_fields; + selector->n_groups_max = s->n_groups_max; + selector->n_members_per_group_max = s->n_members_per_group_max; + + return 0; +} + +int +rte_swx_ctl_selector_group_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field) +{ + struct selector *s; + + if (!p || (selector_id >= p->n_selectors) || !field) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s) + return -EINVAL; + + field->match_type = RTE_SWX_TABLE_MATCH_EXACT; + field->is_header = 0; + field->n_bits = s->group_id_field->n_bits; + field->offset = s->group_id_field->offset; + + return 0; +} + +int +rte_swx_ctl_selector_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + uint32_t selector_field_id, + struct rte_swx_ctl_table_match_field_info *field) +{ + struct selector *s; + struct field *f; + + if (!p || (selector_id >= p->n_selectors) || !field) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s || (selector_field_id >= s->n_selector_fields)) + return -EINVAL; + + f = s->selector_fields[selector_field_id]; + field->match_type = RTE_SWX_TABLE_MATCH_EXACT; + field->is_header = s->selector_header ? 1 : 0; + field->n_bits = f->n_bits; + field->offset = f->offset; + + return 0; +} + +int +rte_swx_ctl_selector_member_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field) +{ + struct selector *s; + + if (!p || (selector_id >= p->n_selectors) || !field) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s) + return -EINVAL; + + field->match_type = RTE_SWX_TABLE_MATCH_EXACT; + field->is_header = 0; + field->n_bits = s->member_id_field->n_bits; + field->offset = s->member_id_field->offset; + + return 0; +} + int rte_swx_pipeline_table_state_get(struct rte_swx_pipeline *p, struct rte_swx_table_state **table_state) @@ -10405,6 +10974,25 @@ rte_swx_ctl_pipeline_table_stats_read(struct rte_swx_pipeline *p, return 0; } +int +rte_swx_ctl_pipeline_selector_stats_read(struct rte_swx_pipeline *p, + const char *selector_name, + struct rte_swx_pipeline_selector_stats *stats) +{ + struct selector *s; + + if (!p || !selector_name || !selector_name[0] || !stats) + return -EINVAL; + + s = selector_find(p, selector_name); + if (!s) + return -EINVAL; + + stats->n_pkts = p->selector_stats[s->id].n_pkts; + + return 0; +} + int rte_swx_ctl_regarray_info_get(struct rte_swx_pipeline *p, uint32_t regarray_id, diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h index feeb10a5c..cd395ac39 100644 --- a/lib/pipeline/rte_swx_pipeline.h +++ b/lib/pipeline/rte_swx_pipeline.h @@ -612,6 +612,57 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p, const char *args, uint32_t size); +/** Pipeline selector table parameters. */ +struct rte_swx_pipeline_selector_params { + /** The group ID field. Input into the selection operation. + * Restriction: This field must be a meta-data field. + */ + const char *group_id_field_name; + + /** The set of fields used to select (through a hashing scheme) the + * member within the current group. Inputs into the seletion operation. + * Restriction: All the selector fields must be part of the same struct, + * i.e. part of the same header or part of the meta-data structure. + */ + const char **selector_field_names; + + /** The number of selector fields. Must be non-zero. */ + uint32_t n_selector_fields; + + /** The member ID field. Output from the selection operation. + * Restriction: This field must be a meta-data field. + */ + const char *member_id_field_name; + + /** Maximum number of groups. Must be non-zero. */ + uint32_t n_groups_max; + + /** Maximum number of members per group. Must be non-zero. */ + uint32_t n_members_per_group_max; +}; + +/** + * Pipeline selector table configure + * + * @param[out] p + * Pipeline handle. + * @param[in] name + * Selector table name. + * @param[in] params + * Selector table parameters. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough space/cannot allocate memory; + * -EEXIST: Selector table with this name already exists; + * -ENODEV: Selector table creation error. + */ +__rte_experimental +int +rte_swx_pipeline_selector_config(struct rte_swx_pipeline *p, + const char *name, + struct rte_swx_pipeline_selector_params *params); + /** * Pipeline register array configure * diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c index 2e867d7bf..6980b0390 100644 --- a/lib/pipeline/rte_swx_pipeline_spec.c +++ b/lib/pipeline/rte_swx_pipeline_spec.c @@ -18,7 +18,9 @@ #define TABLE_BLOCK 2 #define TABLE_KEY_BLOCK 3 #define TABLE_ACTIONS_BLOCK 4 -#define APPLY_BLOCK 5 +#define SELECTOR_BLOCK 5 +#define SELECTOR_SELECTOR_BLOCK 6 +#define APPLY_BLOCK 7 /* * extobj. @@ -940,6 +942,307 @@ table_block_parse(struct table_spec *s, return -EINVAL; } +/* + * selector. + * + * selector SELECTOR_NAME { + * group_id FIELD_NAME + * selector { + * FIELD_NAME + * ... + * } + * member_id FIELD_NAME + * n_groups N_GROUPS + * n_members_per_group N_MEMBERS_PER_GROUP + * } + */ +struct selector_spec { + char *name; + struct rte_swx_pipeline_selector_params params; +}; + +static void +selector_spec_free(struct selector_spec *s) +{ + uintptr_t field_name; + uint32_t i; + + if (!s) + return; + + /* name. */ + free(s->name); + s->name = NULL; + + /* params->group_id_field_name. */ + field_name = (uintptr_t)s->params.group_id_field_name; + free((void *)field_name); + s->params.group_id_field_name = NULL; + + /* params->selector_field_names. */ + for (i = 0; i < s->params.n_selector_fields; i++) { + field_name = (uintptr_t)s->params.selector_field_names[i]; + + free((void *)field_name); + } + + free(s->params.selector_field_names); + s->params.selector_field_names = NULL; + + s->params.n_selector_fields = 0; + + /* params->member_id_field_name. */ + field_name = (uintptr_t)s->params.member_id_field_name; + free((void *)field_name); + s->params.member_id_field_name = NULL; + + /* params->n_groups_max. */ + s->params.n_groups_max = 0; + + /* params->n_members_per_group_max. */ + s->params.n_members_per_group_max = 0; +} + +static int +selector_statement_parse(struct selector_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + /* Check format. */ + if ((n_tokens != 3) || strcmp(tokens[2], "{")) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid selector statement."; + return -EINVAL; + } + + /* spec. */ + s->name = strdup(tokens[1]); + if (!s->name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + /* block_mask. */ + *block_mask |= 1 << SELECTOR_BLOCK; + + return 0; +} + +static int +selector_selector_statement_parse(uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + /* Check format. */ + if ((n_tokens != 2) || strcmp(tokens[1], "{")) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid selector statement."; + return -EINVAL; + } + + /* block_mask. */ + *block_mask |= 1 << SELECTOR_SELECTOR_BLOCK; + + return 0; +} + +static int +selector_selector_block_parse(struct selector_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + const char **new_fields; + char *name; + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << SELECTOR_SELECTOR_BLOCK); + return 0; + } + + /* Check input arguments. */ + if (n_tokens != 1) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid selector field statement."; + return -EINVAL; + } + + name = strdup(tokens[0]); + if (!name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + new_fields = realloc(s->params.selector_field_names, + (s->params.n_selector_fields + 1) * sizeof(char *)); + if (!new_fields) { + free(name); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + s->params.selector_field_names = new_fields; + s->params.selector_field_names[s->params.n_selector_fields] = name; + s->params.n_selector_fields++; + + return 0; +} + +static int +selector_block_parse(struct selector_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + if (*block_mask & (1 << SELECTOR_SELECTOR_BLOCK)) + return selector_selector_block_parse(s, + block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << SELECTOR_BLOCK); + return 0; + } + + if (!strcmp(tokens[0], "group_id")) { + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid group_id statement."; + return -EINVAL; + } + + s->params.group_id_field_name = strdup(tokens[1]); + if (!s->params.group_id_field_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; + } + + if (!strcmp(tokens[0], "selector")) + return selector_selector_statement_parse(block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + + if (!strcmp(tokens[0], "member_id")) { + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid member_id statement."; + return -EINVAL; + } + + s->params.member_id_field_name = strdup(tokens[1]); + if (!s->params.member_id_field_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; + } + + if (!strcmp(tokens[0], "n_groups_max")) { + char *p = tokens[1]; + + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_groups statement."; + return -EINVAL; + } + + s->params.n_groups_max = strtoul(p, &p, 0); + if (p[0]) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_groups argument."; + return -EINVAL; + } + + return 0; + } + + if (!strcmp(tokens[0], "n_members_per_group_max")) { + char *p = tokens[1]; + + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_members_per_group statement."; + return -EINVAL; + } + + s->params.n_members_per_group_max = strtoul(p, &p, 0); + if (p[0]) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_members_per_group argument."; + return -EINVAL; + } + + return 0; + } + + /* Anything else. */ + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid statement."; + return -EINVAL; +} + /* * regarray. * @@ -1203,6 +1506,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, struct metadata_spec metadata_spec = {0}; struct action_spec action_spec = {0}; struct table_spec table_spec = {0}; + struct selector_spec selector_spec = {0}; struct regarray_spec regarray_spec = {0}; struct metarray_spec metarray_spec = {0}; struct apply_spec apply_spec = {0}; @@ -1386,6 +1690,38 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, continue; } + /* selector block. */ + if (block_mask & (1 << SELECTOR_BLOCK)) { + status = selector_block_parse(&selector_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + if (block_mask & (1 << SELECTOR_BLOCK)) + continue; + + /* End of block. */ + status = rte_swx_pipeline_selector_config(p, + selector_spec.name, + &selector_spec.params); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Selector configuration error."; + goto error; + } + + selector_spec_free(&selector_spec); + + continue; + } + /* apply block. */ if (block_mask & (1 << APPLY_BLOCK)) { status = apply_block_parse(&apply_spec, @@ -1544,6 +1880,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, continue; } + /* selector. */ + if (!strcmp(tokens[0], "selector")) { + status = selector_statement_parse(&selector_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + continue; + } + /* regarray. */ if (!strcmp(tokens[0], "regarray")) { status = regarray_statement_parse(®array_spec, @@ -1651,6 +2002,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, metadata_spec_free(&metadata_spec); action_spec_free(&action_spec); table_spec_free(&table_spec); + selector_spec_free(&selector_spec); regarray_spec_free(®array_spec); metarray_spec_free(&metarray_spec); apply_spec_free(&apply_spec); diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map index a4d7d9788..ff0974c2e 100644 --- a/lib/pipeline/version.map +++ b/lib/pipeline/version.map @@ -116,4 +116,17 @@ EXPERIMENTAL { rte_swx_ctl_regarray_info_get; rte_swx_pipeline_metarray_config; rte_swx_pipeline_regarray_config; + + #added in 21.08 + rte_swx_pipeline_selector_config; + rte_swx_ctl_pipeline_selector_fprintf; + rte_swx_ctl_pipeline_selector_group_add; + rte_swx_ctl_pipeline_selector_group_delete; + rte_swx_ctl_pipeline_selector_group_member_add; + rte_swx_ctl_pipeline_selector_group_member_delete; + rte_swx_ctl_pipeline_selector_stats_read; + rte_swx_ctl_selector_info_get; + rte_swx_ctl_selector_field_info_get; + rte_swx_ctl_selector_group_id_field_info_get; + rte_swx_ctl_selector_member_id_field_info_get; }; From patchwork Fri Jul 2 20:50:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 95224 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 CA34CA0C3F; Fri, 2 Jul 2021 22:50:30 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B77AB4141C; Fri, 2 Jul 2021 22:50:30 +0200 (CEST) Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by mails.dpdk.org (Postfix) with ESMTP id 7CF164141C for ; Fri, 2 Jul 2021 22:50:28 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10033"; a="195947603" X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="195947603" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga101.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jul 2021 13:50:27 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="482704266" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by FMSMGA003.fm.intel.com with ESMTP; 02 Jul 2021 13:50:26 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Cc: Churchill Khangar Date: Fri, 2 Jul 2021 21:50:25 +0100 Message-Id: <20210702205025.61593-1-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 Subject: [dpdk-dev] [PATCH 4/5] examples/pipeline: add support for selector tables 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 Sender: "dev" Add application-evel support for selector tables. Signed-off-by: Churchill Khangar Signed-off-by: Cristian Dumitrescu --- examples/pipeline/cli.c | 568 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 568 insertions(+) diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c index 30754e319..889b2ee43 100644 --- a/examples/pipeline/cli.c +++ b/examples/pipeline/cli.c @@ -1368,6 +1368,472 @@ cmd_pipeline_table_show(char **tokens, snprintf(out, out_size, MSG_ARG_INVALID, "table_name"); } +static const char cmd_pipeline_selector_group_add_help[] = +"pipeline selector group add\n"; + +static void +cmd_pipeline_selector_group_add(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *selector_name; + uint32_t group_id; + int status; + + if (n_tokens != 6) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } + + if (strcmp(tokens[2], "selector") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector"); + return; + } + + selector_name = tokens[3]; + + if (strcmp(tokens[4], "group") || + strcmp(tokens[5], "add")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add"); + return; + } + + status = rte_swx_ctl_pipeline_selector_group_add(p->ctl, + selector_name, + &group_id); + if (status) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + else + snprintf(out, out_size, "Group ID: %u\n", group_id); + + return; +} + +static const char cmd_pipeline_selector_group_delete_help[] = +"pipeline selector group delete \n"; + +static void +cmd_pipeline_selector_group_delete(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *selector_name; + uint32_t group_id; + int status; + + if (n_tokens != 7) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } + + if (strcmp(tokens[2], "selector") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector"); + return; + } + + selector_name = tokens[3]; + + if (strcmp(tokens[4], "group") || + strcmp(tokens[5], "delete")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete"); + return; + } + + if (parser_read_uint32(&group_id, tokens[6]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "group_id"); + return; + } + + status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl, + selector_name, + group_id); + if (status) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + + return; +} + +#define GROUP_MEMBER_INFO_TOKENS_MAX 6 + +static int +token_is_comment(const char *token) +{ + if ((token[0] == '#') || + (token[0] == ';') || + ((token[0] == '/') && (token[1] == '/'))) + return 1; /* TRUE. */ + + return 0; /* FALSE. */ +} + +static int +pipeline_selector_group_member_read(const char *string, + uint32_t *group_id, + uint32_t *member_id, + uint32_t *weight, + int *is_blank_or_comment) +{ + char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens; + char *s0 = NULL, *s; + uint32_t n_tokens = 0, group_id_val, member_id_val, weight_val; + int blank_or_comment = 0; + + /* Check input arguments. */ + if (!string || !string[0]) + goto error; + + /* Memory allocation. */ + s0 = strdup(string); + if (!s0) + goto error; + + /* Parse the string into tokens. */ + for (s = s0; ; ) { + char *token; + + token = strtok_r(s, " \f\n\r\t\v", &s); + if (!token || token_is_comment(token)) + break; + + if (n_tokens > GROUP_MEMBER_INFO_TOKENS_MAX) + goto error; + + token_array[n_tokens] = token; + n_tokens++; + } + + if (!n_tokens) { + blank_or_comment = 1; + goto error; + } + + tokens = token_array; + + if (n_tokens < 4 || + strcmp(tokens[0], "group") || + strcmp(tokens[2], "member")) + goto error; + + /* + * Group ID. + */ + if (parser_read_uint32(&group_id_val, tokens[1]) != 0) + goto error; + *group_id = group_id_val; + + /* + * Member ID. + */ + if (parser_read_uint32(&member_id_val, tokens[3]) != 0) + goto error; + *member_id = member_id_val; + + tokens += 4; + n_tokens -= 4; + + /* + * Weight. + */ + if (n_tokens && !strcmp(tokens[0], "weight")) { + if (n_tokens < 2) + goto error; + + if (parser_read_uint32(&weight_val, tokens[1]) != 0) + goto error; + *weight = weight_val; + + tokens += 2; + n_tokens -= 2; + } + + if (n_tokens) + goto error; + + free(s0); + return 0; + +error: + free(s0); + if (is_blank_or_comment) + *is_blank_or_comment = blank_or_comment; + return -EINVAL; +} + +static int +pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p, + const char *selector_name, + FILE *file, + uint32_t *file_line_number) +{ + char *line = NULL; + uint32_t line_id = 0; + int status = 0; + + /* Buffer allocation. */ + line = malloc(MAX_LINE_SIZE); + if (!line) + return -ENOMEM; + + /* File read. */ + for (line_id = 1; ; line_id++) { + uint32_t group_id, member_id, weight; + int is_blank_or_comment; + + if (fgets(line, MAX_LINE_SIZE, file) == NULL) + break; + + status = pipeline_selector_group_member_read(line, + &group_id, + &member_id, + &weight, + &is_blank_or_comment); + if (status) { + if (is_blank_or_comment) + continue; + + goto error; + } + + status = rte_swx_ctl_pipeline_selector_group_member_add(p, + selector_name, + group_id, + member_id, + weight); + if (status) + goto error; + } + +error: + free(line); + *file_line_number = line_id; + return status; +} + +static const char cmd_pipeline_selector_group_member_add_help[] = +"pipeline selector group member add "; + +static void +cmd_pipeline_selector_group_member_add(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *selector_name, *file_name; + FILE *file = NULL; + uint32_t file_line_number = 0; + int status; + + if (n_tokens != 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } + + if (strcmp(tokens[2], "selector") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector"); + return; + } + + selector_name = tokens[3]; + + if (strcmp(tokens[4], "group") || + strcmp(tokens[5], "member") || + strcmp(tokens[6], "add")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add"); + return; + } + + file_name = tokens[7]; + file = fopen(file_name, "r"); + if (!file) { + snprintf(out, out_size, "Cannot open file %s.\n", file_name); + return; + } + + status = pipeline_selector_group_members_add(p->ctl, + selector_name, + file, + &file_line_number); + if (status) + snprintf(out, out_size, "Invalid entry in file %s at line %u\n", + file_name, + file_line_number); + + fclose(file); +} + +static int +pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p, + const char *selector_name, + FILE *file, + uint32_t *file_line_number) +{ + char *line = NULL; + uint32_t line_id = 0; + int status = 0; + + /* Buffer allocation. */ + line = malloc(MAX_LINE_SIZE); + if (!line) + return -ENOMEM; + + /* File read. */ + for (line_id = 1; ; line_id++) { + uint32_t group_id, member_id, weight; + int is_blank_or_comment; + + if (fgets(line, MAX_LINE_SIZE, file) == NULL) + break; + + status = pipeline_selector_group_member_read(line, + &group_id, + &member_id, + &weight, + &is_blank_or_comment); + if (status) { + if (is_blank_or_comment) + continue; + + goto error; + } + + status = rte_swx_ctl_pipeline_selector_group_member_delete(p, + selector_name, + group_id, + member_id); + if (status) + goto error; + } + +error: + free(line); + *file_line_number = line_id; + return status; +} + +static const char cmd_pipeline_selector_group_member_delete_help[] = +"pipeline selector group member delete "; + +static void +cmd_pipeline_selector_group_member_delete(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *selector_name, *file_name; + FILE *file = NULL; + uint32_t file_line_number = 0; + int status; + + if (n_tokens != 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } + + if (strcmp(tokens[2], "selector") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector"); + return; + } + + selector_name = tokens[3]; + + if (strcmp(tokens[4], "group") || + strcmp(tokens[5], "member") || + strcmp(tokens[6], "delete")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete"); + return; + } + + file_name = tokens[7]; + file = fopen(file_name, "r"); + if (!file) { + snprintf(out, out_size, "Cannot open file %s.\n", file_name); + return; + } + + status = pipeline_selector_group_members_delete(p->ctl, + selector_name, + file, + &file_line_number); + if (status) + snprintf(out, out_size, "Invalid entry in file %s at line %u\n", + file_name, + file_line_number); + + fclose(file); +} + +static const char cmd_pipeline_selector_show_help[] = +"pipeline selector show\n"; + +static void +cmd_pipeline_selector_show(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + void *obj) +{ + struct pipeline *p; + char *pipeline_name, *selector_name; + int status; + + if (n_tokens != 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + p = pipeline_find(obj, pipeline_name); + if (!p || !p->ctl) { + snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); + return; + } + + selector_name = tokens[3]; + status = rte_swx_ctl_pipeline_selector_fprintf(stdout, + p->ctl, selector_name); + if (status) + snprintf(out, out_size, MSG_ARG_INVALID, "selector_name"); + return; +} + static const char cmd_pipeline_commit_help[] = "pipeline commit\n"; @@ -2168,6 +2634,11 @@ cmd_help(char **tokens, "\tpipeline table delete\n" "\tpipeline table default\n" "\tpipeline table show\n" + "\tpipeline selector group add\n" + "\tpipeline selector group delete\n" + "\tpipeline selector group member add\n" + "\tpipeline selector group member delete\n" + "\tpipeline selector show\n" "\tpipeline commit\n" "\tpipeline abort\n" "\tpipeline regrd\n" @@ -2266,6 +2737,57 @@ cmd_help(char **tokens, return; } + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 4) && + (strcmp(tokens[1], "selector") == 0) && + (strcmp(tokens[2], "group") == 0) && + (strcmp(tokens[3], "add") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_selector_group_add_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 4) && + (strcmp(tokens[1], "selector") == 0) && + (strcmp(tokens[2], "group") == 0) && + (strcmp(tokens[3], "delete") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_selector_group_delete_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 5) && + (strcmp(tokens[1], "selector") == 0) && + (strcmp(tokens[2], "group") == 0) && + (strcmp(tokens[3], "member") == 0) && + (strcmp(tokens[4], "add") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_selector_group_member_add_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 5) && + (strcmp(tokens[1], "selector") == 0) && + (strcmp(tokens[2], "group") == 0) && + (strcmp(tokens[3], "member") == 0) && + (strcmp(tokens[4], "delete") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_selector_group_member_delete_help); + return; + } + + if ((strcmp(tokens[0], "pipeline") == 0) && + (n_tokens == 3) && + (strcmp(tokens[1], "selector") == 0) && + (strcmp(tokens[2], "show") == 0)) { + snprintf(out, out_size, "\n%s\n", + cmd_pipeline_selector_show_help); + return; + } + if ((strcmp(tokens[0], "pipeline") == 0) && (n_tokens == 2) && (strcmp(tokens[1], "commit") == 0)) { @@ -2468,6 +2990,52 @@ cli_process(char *in, char *out, size_t out_size, void *obj) return; } + if ((n_tokens >= 6) && + (strcmp(tokens[2], "selector") == 0) && + (strcmp(tokens[4], "group") == 0) && + (strcmp(tokens[5], "add") == 0)) { + cmd_pipeline_selector_group_add(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 6) && + (strcmp(tokens[2], "selector") == 0) && + (strcmp(tokens[4], "group") == 0) && + (strcmp(tokens[5], "delete") == 0)) { + cmd_pipeline_selector_group_delete(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 7) && + (strcmp(tokens[2], "selector") == 0) && + (strcmp(tokens[4], "group") == 0) && + (strcmp(tokens[5], "member") == 0) && + (strcmp(tokens[6], "add") == 0)) { + cmd_pipeline_selector_group_member_add(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 7) && + (strcmp(tokens[2], "selector") == 0) && + (strcmp(tokens[4], "group") == 0) && + (strcmp(tokens[5], "member") == 0) && + (strcmp(tokens[6], "delete") == 0)) { + cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out, + out_size, obj); + return; + } + + if ((n_tokens >= 5) && + (strcmp(tokens[2], "selector") == 0) && + (strcmp(tokens[4], "show") == 0)) { + cmd_pipeline_selector_show(tokens, n_tokens, out, + out_size, obj); + return; + } + if ((n_tokens >= 3) && (strcmp(tokens[2], "commit") == 0)) { cmd_pipeline_commit(tokens, n_tokens, out, From patchwork Fri Jul 2 20:52:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 95225 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 9EB55A0C3F; Fri, 2 Jul 2021 22:52:40 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 565B141414; Fri, 2 Jul 2021 22:52:40 +0200 (CEST) Received: from mga06.intel.com (mga06.intel.com [134.134.136.31]) by mails.dpdk.org (Postfix) with ESMTP id A0ACF41363 for ; Fri, 2 Jul 2021 22:52:38 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10033"; a="269910664" X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="269910664" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga104.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jul 2021 13:52:37 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="482704572" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by FMSMGA003.fm.intel.com with ESMTP; 02 Jul 2021 13:52:36 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Date: Fri, 2 Jul 2021 21:52:36 +0100 Message-Id: <20210702205236.61675-1-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 Subject: [dpdk-dev] [PATCH 5/5] examples/pipeline: add selector CLI and spec files 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 Sender: "dev" Signed-off-by: Cristian Dumitrescu --- examples/pipeline/examples/selector.cli | 31 +++++++++++ examples/pipeline/examples/selector.spec | 70 ++++++++++++++++++++++++ examples/pipeline/examples/selector.txt | 4 ++ 3 files changed, 105 insertions(+) create mode 100644 examples/pipeline/examples/selector.cli create mode 100644 examples/pipeline/examples/selector.spec create mode 100644 examples/pipeline/examples/selector.txt diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli new file mode 100644 index 000000000..36f3ead54 --- /dev/null +++ b/examples/pipeline/examples/selector.cli @@ -0,0 +1,31 @@ +; SPDX-License-Identifier: BSD-3-Clause +; Copyright(c) 2020 Intel Corporation + +mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0 + +link LINK0 dev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on +link LINK1 dev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on +link LINK2 dev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on +link LINK3 dev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on + +pipeline PIPELINE0 create 0 + +pipeline PIPELINE0 port in 0 link LINK0 rxq 0 bsz 32 +pipeline PIPELINE0 port in 1 link LINK1 rxq 0 bsz 32 +pipeline PIPELINE0 port in 2 link LINK2 rxq 0 bsz 32 +pipeline PIPELINE0 port in 3 link LINK3 rxq 0 bsz 32 + +pipeline PIPELINE0 port out 0 link LINK0 txq 0 bsz 32 +pipeline PIPELINE0 port out 1 link LINK1 txq 0 bsz 32 +pipeline PIPELINE0 port out 2 link LINK2 txq 0 bsz 32 +pipeline PIPELINE0 port out 3 link LINK3 txq 0 bsz 32 +pipeline PIPELINE0 port out 4 sink none + +pipeline PIPELINE0 build ./examples/pipeline/examples/selector.spec + +pipeline PIPELINE0 selector s group add +pipeline PIPELINE0 selector s group member add ./examples/pipeline/examples/selector.txt +pipeline PIPELINE0 commit +pipeline PIPELINE0 selector s show + +thread 1 pipeline PIPELINE0 enable diff --git a/examples/pipeline/examples/selector.spec b/examples/pipeline/examples/selector.spec new file mode 100644 index 000000000..5d3a7e5a3 --- /dev/null +++ b/examples/pipeline/examples/selector.spec @@ -0,0 +1,70 @@ +; SPDX-License-Identifier: BSD-3-Clause +; Copyright(c) 2020 Intel Corporation + +// +// Headers +// +struct ethernet_h { + bit<48> dst_addr + bit<48> src_addr + bit<16> ethertype +} + +struct ipv4_h { + bit<8> ver_ihl + bit<8> diffserv + bit<16> total_len + bit<16> identification + bit<16> flags_offset + bit<8> ttl + bit<8> protocol + bit<16> hdr_checksum + bit<32> src_addr + bit<32> dst_addr +} + +header ethernet instanceof ethernet_h +header ipv4 instanceof ipv4_h + +// +// Meta-data +// +struct metadata_t { + bit<32> port_in + bit<32> port_out + bit<32> group_id +} + +metadata instanceof metadata_t + +// +// Selectors. +// +selector s { + group_id m.group_id + + selector { + h.ipv4.protocol + h.ipv4.src_addr + h.ipv4.dst_addr + } + + member_id m.port_out + + n_groups_max 64 + n_members_per_group_max 16 +} + +// +// Pipeline. +// +apply { + rx m.port_in + extract h.ethernet + extract h.ipv4 + mov m.group_id h.ethernet.dst_addr + table s + emit h.ethernet + emit h.ipv4 + tx m.port_out +} diff --git a/examples/pipeline/examples/selector.txt b/examples/pipeline/examples/selector.txt new file mode 100644 index 000000000..b3c83c773 --- /dev/null +++ b/examples/pipeline/examples/selector.txt @@ -0,0 +1,4 @@ +group 0 member 0 weight 1 +group 0 member 1 weight 1 +group 0 member 2 weight 2 +group 0 member 3 weight 4