diff --git a/lib/librte_pipeline/meson.build b/lib/librte_pipeline/meson.build index be1d9c3a47..65c1a8d6a9 100644 --- a/lib/librte_pipeline/meson.build +++ b/lib/librte_pipeline/meson.build @@ -5,6 +5,7 @@ sources = files('rte_pipeline.c', 'rte_port_in_action.c', 'rte_table_action.c', 'rte_swx_pipeline.c', + 'rte_swx_pipeline_spec.c', 'rte_swx_ctl.c',) headers = files('rte_pipeline.h', 'rte_port_in_action.h', diff --git a/lib/librte_pipeline/rte_pipeline_version.map b/lib/librte_pipeline/rte_pipeline_version.map index b5e35f2950..87c826f1b6 100644 --- a/lib/librte_pipeline/rte_pipeline_version.map +++ b/lib/librte_pipeline/rte_pipeline_version.map @@ -81,6 +81,7 @@ EXPERIMENTAL { rte_swx_ctl_table_ops_get; rte_swx_pipeline_action_config; rte_swx_pipeline_build; + rte_swx_pipeline_build_from_spec; rte_swx_pipeline_config; rte_swx_pipeline_extern_func_register; rte_swx_pipeline_extern_object_config; diff --git a/lib/librte_pipeline/rte_swx_pipeline.h b/lib/librte_pipeline/rte_swx_pipeline.h index 7b131b0de6..aed627393d 100644 --- a/lib/librte_pipeline/rte_swx_pipeline.h +++ b/lib/librte_pipeline/rte_swx_pipeline.h @@ -643,6 +643,32 @@ __rte_experimental int rte_swx_pipeline_build(struct rte_swx_pipeline *p); +/** + * Pipeline build from specification file + * + * @param[in] p + * Pipeline handle. + * @param[in] spec + * Pipeline specification file. + * @param[out] err_line + * In case of error and non-NULL, the line number within the *spec* file where + * the error occurred. The first line number in the file is 1. + * @param[out] err_msg + * In case of error and non-NULL, the error message. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough space/cannot allocate memory; + * -EEXIST: Resource with the same name already exists; + * -ENODEV: Extern object or table creation error. + */ +__rte_experimental +int +rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, + FILE *spec, + uint32_t *err_line, + const char **err_msg); + /** * Pipeline run * diff --git a/lib/librte_pipeline/rte_swx_pipeline_spec.c b/lib/librte_pipeline/rte_swx_pipeline_spec.c new file mode 100644 index 0000000000..d72badd03d --- /dev/null +++ b/lib/librte_pipeline/rte_swx_pipeline_spec.c @@ -0,0 +1,1439 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ +#include +#include +#include +#include +#include + +#include "rte_swx_pipeline.h" +#include "rte_swx_ctl.h" + +#define MAX_LINE_LENGTH 256 +#define MAX_TOKENS 16 +#define MAX_INSTRUCTION_LENGTH 256 + +#define STRUCT_BLOCK 0 +#define ACTION_BLOCK 1 +#define TABLE_BLOCK 2 +#define TABLE_KEY_BLOCK 3 +#define TABLE_ACTIONS_BLOCK 4 +#define APPLY_BLOCK 5 + +/* + * extobj. + * + * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ] + */ +struct extobj_spec { + char *name; + char *extern_type_name; + char *pragma; +}; + +static void +extobj_spec_free(struct extobj_spec *s) +{ + free(s->name); + free(s->extern_type_name); + free(s->pragma); +} + +static int +extobj_statement_parse(struct extobj_spec *s, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + /* Check format. */ + if (((n_tokens != 4) && (n_tokens != 6)) || + ((n_tokens == 4) && strcmp(tokens[2], "instanceof")) || + ((n_tokens == 6) && (strcmp(tokens[2], "instanceof") || + strcmp(tokens[4], "pragma")))) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid extobj statement."; + return -EINVAL; + } + + /* spec. */ + s->name = strdup(tokens[1]); + s->extern_type_name = strdup(tokens[3]); + s->pragma = (n_tokens == 6) ? strdup(tokens[5]) : NULL; + + if (!s->name || + !s->extern_type_name || + ((n_tokens == 6) && !s->pragma)) { + free(s->name); + free(s->extern_type_name); + free(s->pragma); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; +} + +/* + * struct. + * + * struct STRUCT_TYPE_NAME { + * bit FIELD_NAME + * ... + * } + */ +struct struct_spec { + char *name; + struct rte_swx_field_params *fields; + uint32_t n_fields; +}; + +static void +struct_spec_free(struct struct_spec *s) +{ + uint32_t i; + + if (!s) + return; + + free(s->name); + s->name = NULL; + + for (i = 0; i < s->n_fields; i++) { + uintptr_t name = (uintptr_t)s->fields[i].name; + + free((void *)name); + } + + free(s->fields); + s->fields = NULL; + + s->n_fields = 0; +} + +static int +struct_statement_parse(struct struct_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 struct 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 << STRUCT_BLOCK; + + return 0; +} + +static int +struct_block_parse(struct struct_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + struct rte_swx_field_params *new_fields; + char *p = tokens[0], *name; + uint32_t n_bits; + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << STRUCT_BLOCK); + return 0; + } + + /* Check format. */ + if ((n_tokens != 2) || + (strlen(p) < 6) || + (p[0] != 'b') || + (p[1] != 'i') || + (p[2] != 't') || + (p[3] != '<') || + (p[strlen(p) - 1] != '>')) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid struct field statement."; + return -EINVAL; + } + + /* Remove the "bit<" and ">". */ + p[strlen(p) - 1] = 0; + p += 4; + + n_bits = strtoul(p, &p, 0); + if ((p[0]) || + !n_bits || + (n_bits % 8) || + (n_bits > 64)) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid struct field size."; + return -EINVAL; + } + + /* spec. */ + name = strdup(tokens[1]); + if (!name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + new_fields = reallocarray(s->fields, + s->n_fields + 1, + sizeof(struct rte_swx_field_params)); + if (!new_fields) { + free(name); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + s->fields = new_fields; + s->fields[s->n_fields].name = name; + s->fields[s->n_fields].n_bits = n_bits; + s->n_fields++; + + return 0; +} + +/* + * header. + * + * header HEADER_NAME instanceof STRUCT_TYPE_NAME + */ +struct header_spec { + char *name; + char *struct_type_name; +}; + +static void +header_spec_free(struct header_spec *s) +{ + free(s->name); + free(s->struct_type_name); +} + +static int +header_statement_parse(struct header_spec *s, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + /* Check format. */ + if ((n_tokens != 4) || strcmp(tokens[2], "instanceof")) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid header statement."; + return -EINVAL; + } + + /* spec. */ + s->name = strdup(tokens[1]); + s->struct_type_name = strdup(tokens[3]); + + if (!s->name || !s->struct_type_name) { + free(s->name); + free(s->struct_type_name); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; +} + +/* + * metadata. + * + * metadata instanceof STRUCT_TYPE_NAME + */ +struct metadata_spec { + char *struct_type_name; +}; + +static void +metadata_spec_free(struct metadata_spec *s) +{ + free(s->struct_type_name); +} + +static int +metadata_statement_parse(struct metadata_spec *s, + 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[1], "instanceof")) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid metadata statement."; + return -EINVAL; + } + + /* spec. */ + s->struct_type_name = strdup(tokens[2]); + if (!s->struct_type_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; +} + +/* + * action. + * + * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME { + * INSTRUCTION + * ... + * } + */ +struct action_spec { + char *name; + char *args_struct_type_name; + const char **instructions; + uint32_t n_instructions; +}; + +static void +action_spec_free(struct action_spec *s) +{ + uint32_t i; + + if (!s) + return; + + free(s->name); + s->name = NULL; + + free(s->args_struct_type_name); + s->args_struct_type_name = NULL; + + for (i = 0; i < s->n_instructions; i++) { + uintptr_t instr = (uintptr_t)s->instructions[i]; + + free((void *)instr); + } + + free(s->instructions); + s->instructions = NULL; + + s->n_instructions = 0; +} + +static int +action_statement_parse(struct action_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 != 5) && (n_tokens != 6)) || + ((n_tokens == 5) && + (strcmp(tokens[2], "args") || + strcmp(tokens[3], "none") || + strcmp(tokens[4], "{"))) || + ((n_tokens == 6) && + (strcmp(tokens[2], "args") || + strcmp(tokens[3], "instanceof") || + strcmp(tokens[5], "{")))) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid action statement."; + return -EINVAL; + } + + /* spec. */ + s->name = strdup(tokens[1]); + s->args_struct_type_name = (n_tokens == 6) ? strdup(tokens[4]) : NULL; + + if ((!s->name) || ((n_tokens == 6) && !s->args_struct_type_name)) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + /* block_mask. */ + *block_mask |= 1 << ACTION_BLOCK; + + return 0; +} + +static int +action_block_parse(struct action_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + char buffer[MAX_INSTRUCTION_LENGTH], *instr; + const char **new_instructions; + uint32_t i; + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << ACTION_BLOCK); + return 0; + } + + /* spec. */ + buffer[0] = 0; + for (i = 0; i < n_tokens; i++) { + if (i) + strcat(buffer, " "); + strcat(buffer, tokens[i]); + } + + instr = strdup(buffer); + if (!instr) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + new_instructions = reallocarray(s->instructions, + s->n_instructions + 1, + sizeof(char *)); + if (!new_instructions) { + free(instr); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + s->instructions = new_instructions; + s->instructions[s->n_instructions] = instr; + s->n_instructions++; + + return 0; +} + +/* + * table. + * + * table { + * key { + * MATCH_FIELD_NAME exact | wildcard | lpm + * ... + * } + * actions { + * ACTION_NAME + * ... + * } + * default_action ACTION_NAME args none | ARGS_BYTE_ARRAY [ const ] + * instanceof TABLE_TYPE_NAME + * pragma ARGS + * size SIZE + * } + */ +struct table_spec { + char *name; + struct rte_swx_pipeline_table_params params; + char *recommended_table_type_name; + char *args; + uint32_t size; +}; + +static void +table_spec_free(struct table_spec *s) +{ + uintptr_t default_action_name; + uint32_t i; + + if (!s) + return; + + free(s->name); + s->name = NULL; + + for (i = 0; i < s->params.n_fields; i++) { + uintptr_t name = (uintptr_t)s->params.fields[i].name; + + free((void *)name); + } + + free(s->params.fields); + s->params.fields = NULL; + + s->params.n_fields = 0; + + for (i = 0; i < s->params.n_actions; i++) { + uintptr_t name = (uintptr_t)s->params.action_names[i]; + + free((void *)name); + } + + free(s->params.action_names); + s->params.action_names = NULL; + + s->params.n_actions = 0; + + default_action_name = (uintptr_t)s->params.default_action_name; + free((void *)default_action_name); + s->params.default_action_name = NULL; + + free(s->params.default_action_data); + s->params.default_action_data = NULL; + + s->params.default_action_is_const = 0; + + free(s->recommended_table_type_name); + s->recommended_table_type_name = NULL; + + free(s->args); + s->args = NULL; + + s->size = 0; +} + +static int +table_key_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 key statement."; + return -EINVAL; + } + + /* block_mask. */ + *block_mask |= 1 << TABLE_KEY_BLOCK; + + return 0; +} + +static int +table_key_block_parse(struct table_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + struct rte_swx_match_field_params *new_fields; + enum rte_swx_table_match_type match_type = RTE_SWX_TABLE_MATCH_WILDCARD; + char *name; + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << TABLE_KEY_BLOCK); + return 0; + } + + /* Check input arguments. */ + if ((n_tokens != 2) || + (strcmp(tokens[1], "exact") && + strcmp(tokens[1], "wildcard") && + strcmp(tokens[1], "lpm"))) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid match field statement."; + return -EINVAL; + } + + if (!strcmp(tokens[1], "wildcard")) + match_type = RTE_SWX_TABLE_MATCH_WILDCARD; + if (!strcmp(tokens[1], "lpm")) + match_type = RTE_SWX_TABLE_MATCH_LPM; + if (!strcmp(tokens[1], "exact")) + match_type = RTE_SWX_TABLE_MATCH_EXACT; + + 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 = reallocarray(s->params.fields, + s->params.n_fields + 1, + sizeof(struct rte_swx_match_field_params)); + if (!new_fields) { + free(name); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + s->params.fields = new_fields; + s->params.fields[s->params.n_fields].name = name; + s->params.fields[s->params.n_fields].match_type = match_type; + s->params.n_fields++; + + return 0; +} + +static int +table_actions_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 actions statement."; + return -EINVAL; + } + + /* block_mask. */ + *block_mask |= 1 << TABLE_ACTIONS_BLOCK; + + return 0; +} + +static int +table_actions_block_parse(struct table_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_action_names; + char *name; + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << TABLE_ACTIONS_BLOCK); + return 0; + } + + /* Check input arguments. */ + if (n_tokens != 1) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid action name 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_action_names = reallocarray(s->params.action_names, + s->params.n_actions + 1, + sizeof(char *)); + if (!new_action_names) { + free(name); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + s->params.action_names = new_action_names; + s->params.action_names[s->params.n_actions] = name; + s->params.n_actions++; + + return 0; +} + +static int +table_statement_parse(struct table_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 table 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 << TABLE_BLOCK; + + return 0; +} + +static int +table_block_parse(struct table_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 << TABLE_KEY_BLOCK)) + return table_key_block_parse(s, + block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + + if (*block_mask & (1 << TABLE_ACTIONS_BLOCK)) + return table_actions_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 << TABLE_BLOCK); + return 0; + } + + if (!strcmp(tokens[0], "key")) + return table_key_statement_parse(block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + + if (!strcmp(tokens[0], "actions")) + return table_actions_statement_parse(block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + + if (!strcmp(tokens[0], "default_action")) { + if (((n_tokens != 4) && (n_tokens != 5)) || + strcmp(tokens[2], "args") || + strcmp(tokens[3], "none") || + ((n_tokens == 5) && strcmp(tokens[4], "const"))) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid default_action statement."; + return -EINVAL; + } + + if (s->params.default_action_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Duplicate default_action stmt."; + return -EINVAL; + } + + s->params.default_action_name = strdup(tokens[1]); + if (!s->params.default_action_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + if (n_tokens == 5) + s->params.default_action_is_const = 1; + + return 0; + } + + if (!strcmp(tokens[0], "instanceof")) { + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid instanceof statement."; + return -EINVAL; + } + + if (s->recommended_table_type_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Duplicate instanceof statement."; + return -EINVAL; + } + + s->recommended_table_type_name = strdup(tokens[1]); + if (!s->recommended_table_type_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; + } + + if (!strcmp(tokens[0], "pragma")) { + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid pragma statement."; + return -EINVAL; + } + + if (s->args) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Duplicate pragma statement."; + return -EINVAL; + } + + s->args = strdup(tokens[1]); + if (!s->args) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; + } + + if (!strcmp(tokens[0], "size")) { + char *p = tokens[1]; + + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid pragma statement."; + return -EINVAL; + } + + s->size = strtoul(p, &p, 0); + if (p[0]) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid size argument."; + return -EINVAL; + } + + return 0; + } + + /* Anything else. */ + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid statement."; + return -EINVAL; +} + +/* + * apply. + * + * apply { + * INSTRUCTION + * ... + * } + */ +struct apply_spec { + const char **instructions; + uint32_t n_instructions; +}; + +static void +apply_spec_free(struct apply_spec *s) +{ + uint32_t i; + + if (!s) + return; + + for (i = 0; i < s->n_instructions; i++) { + uintptr_t instr = (uintptr_t)s->instructions[i]; + + free((void *)instr); + } + + free(s->instructions); + s->instructions = NULL; + + s->n_instructions = 0; +} + +static int +apply_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 apply statement."; + return -EINVAL; + } + + /* block_mask. */ + *block_mask |= 1 << APPLY_BLOCK; + + return 0; +} + +static int +apply_block_parse(struct apply_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + char buffer[MAX_INSTRUCTION_LENGTH], *instr; + const char **new_instructions; + uint32_t i; + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << APPLY_BLOCK); + return 0; + } + + /* spec. */ + buffer[0] = 0; + for (i = 0; i < n_tokens; i++) { + if (i) + strcat(buffer, " "); + strcat(buffer, tokens[i]); + } + + instr = strdup(buffer); + if (!instr) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + new_instructions = reallocarray(s->instructions, + s->n_instructions + 1, + sizeof(char *)); + if (!new_instructions) { + free(instr); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + s->instructions = new_instructions; + s->instructions[s->n_instructions] = instr; + s->n_instructions++; + + return 0; +} + +/* + * Pipeline. + */ +int +rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, + FILE *spec, + uint32_t *err_line, + const char **err_msg) +{ + struct extobj_spec extobj_spec = {0}; + struct struct_spec struct_spec = {0}; + struct header_spec header_spec = {0}; + struct metadata_spec metadata_spec = {0}; + struct action_spec action_spec = {0}; + struct table_spec table_spec = {0}; + struct apply_spec apply_spec = {0}; + uint32_t n_lines; + uint32_t block_mask = 0; + int status; + + /* Check the input arguments. */ + if (!p) { + if (err_line) + *err_line = 0; + if (err_msg) + *err_msg = "Null pipeline arument."; + status = -EINVAL; + goto error; + } + + if (!p) { + if (err_line) + *err_line = 0; + if (err_msg) + *err_msg = "Null specification file argument."; + status = -EINVAL; + goto error; + } + + for (n_lines = 1; ; n_lines++) { + char line[MAX_LINE_LENGTH]; + char *tokens[MAX_TOKENS], *ptr = line; + uint32_t n_tokens = 0; + + /* Read next line. */ + if (!fgets(line, sizeof(line), spec)) + break; + + /* Parse the line into tokens. */ + for ( ; ; ) { + char *token; + + /* Get token. */ + token = strtok_r(ptr, " \f\n\r\t\v", &ptr); + if (!token) + break; + + /* Handle comments. */ + if ((token[0] == '#') || + (token[0] == ';') || + ((token[0] == '/') && (token[1] == '/'))) { + break; + } + + /* Handle excessively long lines. */ + if (n_tokens >= MAX_TOKENS) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Too many tokens."; + status = -EINVAL; + goto error; + } + + /* Save token. */ + tokens[n_tokens] = token; + n_tokens++; + } + + /* Handle empty lines. */ + if (!n_tokens) + continue; + + /* struct block. */ + if (block_mask & (1 << STRUCT_BLOCK)) { + status = struct_block_parse(&struct_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + if (block_mask & (1 << STRUCT_BLOCK)) + continue; + + /* End of block. */ + status = rte_swx_pipeline_struct_type_register(p, + struct_spec.name, + struct_spec.fields, + struct_spec.n_fields); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Struct registration error."; + goto error; + } + + struct_spec_free(&struct_spec); + + continue; + } + + /* action block. */ + if (block_mask & (1 << ACTION_BLOCK)) { + status = action_block_parse(&action_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + if (block_mask & (1 << ACTION_BLOCK)) + continue; + + /* End of block. */ + status = rte_swx_pipeline_action_config(p, + action_spec.name, + action_spec.args_struct_type_name, + action_spec.instructions, + action_spec.n_instructions); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Action config error."; + goto error; + } + + action_spec_free(&action_spec); + + continue; + } + + /* table block. */ + if (block_mask & (1 << TABLE_BLOCK)) { + status = table_block_parse(&table_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + if (block_mask & (1 << TABLE_BLOCK)) + continue; + + /* End of block. */ + status = rte_swx_pipeline_table_config(p, + table_spec.name, + &table_spec.params, + table_spec.recommended_table_type_name, + table_spec.args, + table_spec.size); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Table configuration error."; + goto error; + } + + table_spec_free(&table_spec); + + continue; + } + + /* apply block. */ + if (block_mask & (1 << APPLY_BLOCK)) { + status = apply_block_parse(&apply_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + if (block_mask & (1 << APPLY_BLOCK)) + continue; + + /* End of block. */ + status = rte_swx_pipeline_instructions_config(p, + apply_spec.instructions, + apply_spec.n_instructions); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Pipeline instructions err."; + goto error; + } + + apply_spec_free(&apply_spec); + + continue; + } + + /* extobj. */ + if (!strcmp(tokens[0], "extobj")) { + status = extobj_statement_parse(&extobj_spec, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + status = rte_swx_pipeline_extern_object_config(p, + extobj_spec.name, + extobj_spec.extern_type_name, + extobj_spec.pragma); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Extern object config err."; + goto error; + } + + extobj_spec_free(&extobj_spec); + + continue; + } + + /* struct. */ + if (!strcmp(tokens[0], "struct")) { + status = struct_statement_parse(&struct_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + continue; + } + + /* header. */ + if (!strcmp(tokens[0], "header")) { + status = header_statement_parse(&header_spec, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + status = rte_swx_pipeline_packet_header_register(p, + header_spec.name, + header_spec.struct_type_name); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Header registration error."; + goto error; + } + + header_spec_free(&header_spec); + + continue; + } + + /* metadata. */ + if (!strcmp(tokens[0], "metadata")) { + status = metadata_statement_parse(&metadata_spec, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + status = rte_swx_pipeline_packet_metadata_register(p, + metadata_spec.struct_type_name); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Meta-data reg err."; + goto error; + } + + metadata_spec_free(&metadata_spec); + + continue; + } + + /* action. */ + if (!strcmp(tokens[0], "action")) { + status = action_statement_parse(&action_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + continue; + } + + /* table. */ + if (!strcmp(tokens[0], "table")) { + status = table_statement_parse(&table_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + continue; + } + + /* apply. */ + if (!strcmp(tokens[0], "apply")) { + status = apply_statement_parse(&block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + continue; + } + + /* Anything else. */ + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Unknown statement."; + status = -EINVAL; + goto error; + } + + /* Handle unfinished block. */ + if (block_mask) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Missing }."; + status = -EINVAL; + goto error; + } + + /* Pipeline build. */ + status = rte_swx_pipeline_build(p); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Pipeline build error."; + goto error; + } + + return 0; + +error: + extobj_spec_free(&extobj_spec); + struct_spec_free(&struct_spec); + header_spec_free(&header_spec); + metadata_spec_free(&metadata_spec); + action_spec_free(&action_spec); + table_spec_free(&table_spec); + apply_spec_free(&apply_spec); + return status; +}