pipeline: support action annotations
[dpdk.git] / lib / pipeline / rte_swx_pipeline.c
index 02519a0..5e089fb 100644 (file)
@@ -2359,6 +2359,9 @@ action_find(struct rte_swx_pipeline *p, const char *name);
 static int
 action_has_nbo_args(struct action *a);
 
+static int
+learner_action_args_check(struct rte_swx_pipeline *p, struct action *a, const char *mf_name);
+
 static int
 instr_learn_translate(struct rte_swx_pipeline *p,
                      struct action *action,
@@ -2368,16 +2371,31 @@ instr_learn_translate(struct rte_swx_pipeline *p,
                      struct instruction_data *data __rte_unused)
 {
        struct action *a;
+       const char *mf_name;
+       uint32_t mf_offset = 0;
 
        CHECK(action, EINVAL);
-       CHECK(n_tokens == 2, EINVAL);
+       CHECK((n_tokens == 2) || (n_tokens == 3), EINVAL);
 
        a = action_find(p, tokens[1]);
        CHECK(a, EINVAL);
        CHECK(!action_has_nbo_args(a), EINVAL);
 
+       mf_name = (n_tokens > 2) ? tokens[2] : NULL;
+       CHECK(!learner_action_args_check(p, a, mf_name), EINVAL);
+
+       if (mf_name) {
+               struct field *mf;
+
+               mf = metadata_field_parse(p, mf_name);
+               CHECK(mf, EINVAL);
+
+               mf_offset = mf->offset / 8;
+       }
+
        instr->type = INSTR_LEARNER_LEARN;
        instr->learn.action_id = a->id;
+       instr->learn.mf_offset = mf_offset;
 
        return 0;
 }
@@ -5965,7 +5983,7 @@ instr_label_check(struct instruction_data *instruction_data,
                        continue;
 
                for (j = i + 1; j < n_instructions; j++)
-                       CHECK(strcmp(label, data[j].label), EINVAL);
+                       CHECK(strcmp(label, instruction_data[j].label), EINVAL);
        }
 
        /* Get users for each instruction label. */
@@ -7291,7 +7309,7 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                              uint32_t size)
 {
        struct table_type *type;
-       struct table *t;
+       struct table *t = NULL;
        struct action *default_action;
        struct header *header = NULL;
        uint32_t action_data_size_max = 0, i;
@@ -7318,6 +7336,7 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                const char *action_name = params->action_names[i];
                struct action *a;
                uint32_t action_data_size;
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
 
                CHECK_NAME(action_name, EINVAL);
 
@@ -7328,6 +7347,12 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                action_data_size = a->st ? a->st->n_bits / 8 : 0;
                if (action_data_size > action_data_size_max)
                        action_data_size_max = action_data_size;
+
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
+               CHECK(action_is_for_table_entries || action_is_for_default_entry, EINVAL);
        }
 
        CHECK_NAME(params->default_action_name, EINVAL);
@@ -7336,6 +7361,9 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                            params->default_action_name))
                        break;
        CHECK(i < params->n_actions, EINVAL);
+       CHECK(!params->action_is_for_default_entry || params->action_is_for_default_entry[i],
+             EINVAL);
+
        default_action = action_find(p, params->default_action_name);
        CHECK((default_action->st && params->default_action_data) ||
              !params->default_action_data, EINVAL);
@@ -7362,28 +7390,27 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
        CHECK(t, ENOMEM);
 
        t->fields = calloc(params->n_fields, sizeof(struct match_field));
-       if (!t->fields) {
-               free(t);
-               CHECK(0, ENOMEM);
-       }
+       if (!t->fields)
+               goto nomem;
 
        t->actions = calloc(params->n_actions, sizeof(struct action *));
-       if (!t->actions) {
-               free(t->fields);
-               free(t);
-               CHECK(0, ENOMEM);
-       }
+       if (!t->actions)
+               goto nomem;
 
        if (action_data_size_max) {
                t->default_action_data = calloc(1, action_data_size_max);
-               if (!t->default_action_data) {
-                       free(t->actions);
-                       free(t->fields);
-                       free(t);
-                       CHECK(0, ENOMEM);
-               }
+               if (!t->default_action_data)
+                       goto nomem;
        }
 
+       t->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
+       if (!t->action_is_for_table_entries)
+               goto nomem;
+
+       t->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
+       if (!t->action_is_for_default_entry)
+               goto nomem;
+
        /* Node initialization. */
        strcpy(t->name, name);
        if (args && args[0])
@@ -7402,8 +7429,18 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
        t->n_fields = params->n_fields;
        t->header = header;
 
-       for (i = 0; i < params->n_actions; i++)
+       for (i = 0; i < params->n_actions; i++) {
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
+
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
+
                t->actions[i] = action_find(p, params->action_names[i]);
+               t->action_is_for_table_entries[i] = action_is_for_table_entries;
+               t->action_is_for_default_entry[i] = action_is_for_default_entry;
+       }
        t->default_action = default_action;
        if (default_action->st)
                memcpy(t->default_action_data,
@@ -7421,6 +7458,19 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
        p->n_tables++;
 
        return 0;
+
+nomem:
+       if (!t)
+               return -ENOMEM;
+
+       free(t->action_is_for_default_entry);
+       free(t->action_is_for_table_entries);
+       free(t->default_action_data);
+       free(t->actions);
+       free(t->fields);
+       free(t);
+
+       return -ENOMEM;
 }
 
 static struct rte_swx_table_params *
@@ -8161,23 +8211,18 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
 
        /* Action checks. */
        CHECK(params->n_actions, EINVAL);
-
        CHECK(params->action_names, EINVAL);
        for (i = 0; i < params->n_actions; i++) {
                const char *action_name = params->action_names[i];
-               const char *action_field_name = params->action_field_names[i];
                struct action *a;
                uint32_t action_data_size;
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
 
                CHECK_NAME(action_name, EINVAL);
 
                a = action_find(p, action_name);
                CHECK(a, EINVAL);
 
-               status = learner_action_args_check(p, a, action_field_name);
-               if (status)
-                       return status;
-
                status = learner_action_learning_check(p,
                                                       a,
                                                       params->action_names,
@@ -8188,6 +8233,12 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
                action_data_size = a->st ? a->st->n_bits / 8 : 0;
                if (action_data_size > action_data_size_max)
                        action_data_size_max = action_data_size;
+
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
+               CHECK(action_is_for_table_entries || action_is_for_default_entry, EINVAL);
        }
 
        CHECK_NAME(params->default_action_name, EINVAL);
@@ -8196,6 +8247,8 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
                            params->default_action_name))
                        break;
        CHECK(i < params->n_actions, EINVAL);
+       CHECK(!params->action_is_for_default_entry || params->action_is_for_default_entry[i],
+             EINVAL);
 
        default_action = action_find(p, params->default_action_name);
        CHECK((default_action->st && params->default_action_data) ||
@@ -8218,16 +8271,20 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
        if (!l->actions)
                goto nomem;
 
-       l->action_arg = calloc(params->n_actions, sizeof(struct field *));
-       if (!l->action_arg)
-               goto nomem;
-
        if (action_data_size_max) {
                l->default_action_data = calloc(1, action_data_size_max);
                if (!l->default_action_data)
                        goto nomem;
        }
 
+       l->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
+       if (!l->action_is_for_table_entries)
+               goto nomem;
+
+       l->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
+       if (!l->action_is_for_default_entry)
+               goto nomem;
+
        /* Node initialization. */
        strcpy(l->name, name);
 
@@ -8244,11 +8301,16 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
        l->header = header;
 
        for (i = 0; i < params->n_actions; i++) {
-               const char *mf_name = params->action_field_names[i];
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
 
-               l->actions[i] = action_find(p, params->action_names[i]);
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
 
-               l->action_arg[i] = mf_name ? metadata_field_parse(p, mf_name) : NULL;
+               l->actions[i] = action_find(p, params->action_names[i]);
+               l->action_is_for_table_entries[i] = action_is_for_table_entries;
+               l->action_is_for_default_entry[i] = action_is_for_default_entry;
        }
 
        l->default_action = default_action;
@@ -8280,7 +8342,9 @@ nomem:
        if (!l)
                return -ENOMEM;
 
-       free(l->action_arg);
+       free(l->action_is_for_default_entry);
+       free(l->action_is_for_table_entries);
+       free(l->default_action_data);
        free(l->actions);
        free(l->fields);
        free(l);
@@ -8375,7 +8439,6 @@ learner_build_free(struct rte_swx_pipeline *p)
                        struct learner_runtime *r = &t->learners[j];
 
                        free(r->mailbox);
-                       free(r->action_data);
                }
 
                free(t->learners);
@@ -8419,7 +8482,6 @@ learner_build(struct rte_swx_pipeline *p)
                TAILQ_FOREACH(l, &p->learners, node) {
                        struct learner_runtime *r = &t->learners[l->id];
                        uint64_t size;
-                       uint32_t j;
 
                        /* r->mailbox. */
                        size = rte_swx_table_learner_mailbox_size_get();
@@ -8435,21 +8497,6 @@ learner_build(struct rte_swx_pipeline *p)
                        r->key = l->header ?
                                &t->structs[l->header->struct_id] :
                                &t->structs[p->metadata_struct_id];
-
-                       /* r->action_data. */
-                       r->action_data = calloc(p->n_actions, sizeof(uint8_t *));
-                       if (!r->action_data) {
-                               status = -ENOMEM;
-                               goto error;
-                       }
-
-                       for (j = 0; j < l->n_actions; j++) {
-                               struct action *a = l->actions[j];
-                               struct field *mf = l->action_arg[j];
-                               uint8_t *m = t->structs[p->metadata_struct_id];
-
-                               r->action_data[a->id] = mf ? &m[mf->offset / 8] : NULL;
-                       }
                }
        }
 
@@ -8476,7 +8523,6 @@ learner_free(struct rte_swx_pipeline *p)
                TAILQ_REMOVE(&p->learners, l, node);
                free(l->fields);
                free(l->actions);
-               free(l->action_arg);
                free(l->default_action_data);
                free(l);
        }
@@ -9292,6 +9338,9 @@ rte_swx_ctl_table_action_info_get(struct rte_swx_pipeline *p,
 
        table_action->action_id = t->actions[table_action_id]->id;
 
+       table_action->action_is_for_table_entries = t->action_is_for_table_entries[table_action_id];
+       table_action->action_is_for_default_entry = t->action_is_for_default_entry[table_action_id];
+
        return 0;
 }
 
@@ -9479,6 +9528,12 @@ rte_swx_ctl_learner_action_info_get(struct rte_swx_pipeline *p,
 
        learner_action->action_id = l->actions[learner_action_id]->id;
 
+       learner_action->action_is_for_table_entries =
+               l->action_is_for_table_entries[learner_action_id];
+
+       learner_action->action_is_for_default_entry =
+               l->action_is_for_default_entry[learner_action_id];
+
        return 0;
 }
 
@@ -12177,6 +12232,26 @@ instruction_group_list_codegen(struct instruction_group_list *igl,
        }
 }
 
+static uint32_t
+instruction_group_list_custom_instructions_count(struct instruction_group_list *igl)
+{
+       struct instruction_group *g;
+       uint32_t n_custom_instr = 0;
+
+       /* Groups with a single instruction: no function is generated for this group, the group
+        * keeps its current instruction. Groups with more than two instructions: one function and
+        * the associated custom instruction get generated for each such group.
+        */
+       TAILQ_FOREACH(g, igl, node) {
+               if (g->first_instr_id == g->last_instr_id)
+                       continue;
+
+               n_custom_instr++;
+       }
+
+       return n_custom_instr;
+}
+
 static int
 pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
 {
@@ -12331,6 +12406,73 @@ free:
        return status;
 }
 
+static int
+pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
+                     struct instruction_group_list *igl)
+{
+       uint32_t n_custom_instr = instruction_group_list_custom_instructions_count(igl);
+
+       /* Check that enough space is available within the pipeline instruction table to store all
+        * the custom instructions.
+        */
+       if (INSTR_CUSTOM_0 + n_custom_instr > RTE_SWX_PIPELINE_INSTRUCTION_TABLE_SIZE_MAX)
+               return -ENOSPC;
+
+       return 0;
+}
+
+static void
+pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
+{
+       struct instruction_group *g;
+       uint32_t i;
+
+       /* Pipeline table instructions. */
+       for (i = 0; i < p->n_instructions; i++) {
+               struct instruction *instr = &p->instructions[i];
+
+               if (instr->type == INSTR_TABLE)
+                       instr->type = INSTR_TABLE_AF;
+
+               if (instr->type == INSTR_LEARNER)
+                       instr->type = INSTR_LEARNER_AF;
+       }
+
+       /* Pipeline custom instructions. */
+       i = 0;
+       TAILQ_FOREACH(g, igl, node) {
+               struct instruction *instr = &p->instructions[g->first_instr_id];
+               uint32_t j;
+
+               if (g->first_instr_id == g->last_instr_id)
+                       continue;
+
+               /* Install a new custom instruction. */
+               p->instruction_table[INSTR_CUSTOM_0 + i] = g->func;
+
+               /* First instruction of the group: change its type to the new custom instruction. */
+               instr->type = INSTR_CUSTOM_0 + i;
+
+               /* All the subsequent instructions of the group: invalidate. */
+               for (j = g->first_instr_id + 1; j <= g->last_instr_id; j++) {
+                       struct instruction_data *data = &p->instruction_data[j];
+
+                       data->invalid = 1;
+               }
+
+               i++;
+       }
+
+       /* Remove the invalidated instructions. */
+       p->n_instructions = instr_compact(p->instructions, p->instruction_data, p->n_instructions);
+
+       /* Resolve the jump destination for any "standalone" jump instructions (i.e. those jump
+        * instructions that are the only instruction within their group, so they were left
+        * unmodified).
+        */
+       instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
+}
+
 static int
 pipeline_compile(struct rte_swx_pipeline *p)
 {
@@ -12353,6 +12495,13 @@ pipeline_compile(struct rte_swx_pipeline *p)
        if (status)
                goto free;
 
+       /* Adjust instructions. */
+       status = pipeline_adjust_check(p, igl);
+       if (status)
+               goto free;
+
+       pipeline_adjust(p, igl);
+
 free:
        instruction_group_list_free(igl);