pipeline: support packet mirroring
[dpdk.git] / lib / pipeline / rte_swx_pipeline.c
index e669dd0..9406aa7 100644 (file)
@@ -4,7 +4,15 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
-#include <arpa/inet.h>
+#include <dlfcn.h>
+
+#include <rte_swx_port_ethdev.h>
+#include <rte_swx_port_fd.h>
+#include <rte_swx_port_ring.h>
+#include "rte_swx_port_source_sink.h"
+
+#include <rte_swx_table_em.h>
+#include <rte_swx_table_wm.h>
 
 #include "rte_swx_pipeline_internal.h"
 
@@ -415,6 +423,8 @@ rte_swx_pipeline_port_out_type_register(struct rte_swx_pipeline *p,
        CHECK(ops->create, EINVAL);
        CHECK(ops->free, EINVAL);
        CHECK(ops->pkt_tx, EINVAL);
+       CHECK(ops->pkt_fast_clone_tx, EINVAL);
+       CHECK(ops->pkt_clone_tx, EINVAL);
        CHECK(ops->stats_read, EINVAL);
 
        CHECK(!port_out_type_find(p, name), EEXIST);
@@ -501,6 +511,8 @@ port_out_build(struct rte_swx_pipeline *p)
                struct port_out_runtime *out = &p->out[port->id];
 
                out->pkt_tx = port->type->ops.pkt_tx;
+               out->pkt_fast_clone_tx = port->type->ops.pkt_fast_clone_tx;
+               out->pkt_clone_tx = port->type->ops.pkt_clone_tx;
                out->flush = port->type->ops.flush;
                out->obj = port->obj;
        }
@@ -546,6 +558,81 @@ port_out_free(struct rte_swx_pipeline *p)
        }
 }
 
+/*
+ * Packet mirroring.
+ */
+int
+rte_swx_pipeline_mirroring_config(struct rte_swx_pipeline *p,
+                                 struct rte_swx_pipeline_mirroring_params *params)
+{
+       CHECK(p, EINVAL);
+       CHECK(params, EINVAL);
+       CHECK(params->n_slots, EINVAL);
+       CHECK(params->n_sessions, EINVAL);
+       CHECK(!p->build_done, EEXIST);
+
+       p->n_mirroring_slots = rte_align32pow2(params->n_slots);
+       if (p->n_mirroring_slots > 64)
+               p->n_mirroring_slots = 64;
+
+       p->n_mirroring_sessions = rte_align32pow2(params->n_sessions);
+
+       return 0;
+}
+
+static void
+mirroring_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];
+
+               /* mirroring_slots. */
+               free(t->mirroring_slots);
+               t->mirroring_slots = NULL;
+       }
+
+       /* mirroring_sessions. */
+       free(p->mirroring_sessions);
+       p->mirroring_sessions = NULL;
+}
+
+static void
+mirroring_free(struct rte_swx_pipeline *p)
+{
+       mirroring_build_free(p);
+}
+
+static int
+mirroring_build(struct rte_swx_pipeline *p)
+{
+       uint32_t i;
+
+       if (!p->n_mirroring_slots || !p->n_mirroring_sessions)
+               return 0;
+
+       for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
+               struct thread *t = &p->threads[i];
+
+               /* mirroring_slots. */
+               t->mirroring_slots = calloc(p->n_mirroring_slots, sizeof(uint32_t));
+               if (!t->mirroring_slots)
+                       goto error;
+       }
+
+       /* mirroring_sessions. */
+       p->mirroring_sessions = calloc(p->n_mirroring_sessions, sizeof(struct mirroring_session));
+       if (!p->mirroring_sessions)
+               goto error;
+
+       return 0;
+
+error:
+       mirroring_build_free(p);
+       return -ENOMEM;
+}
+
 /*
  * Extern object.
  */
@@ -1369,6 +1456,7 @@ instruction_is_tx(enum instruction_type type)
        switch (type) {
        case INSTR_TX:
        case INSTR_TX_I:
+       case INSTR_DROP:
                return 1;
 
        default:
@@ -1382,6 +1470,7 @@ instruction_does_tx(struct instruction *instr)
        switch (instr->type) {
        case INSTR_TX:
        case INSTR_TX_I:
+       case INSTR_DROP:
        case INSTR_HDR_EMIT_TX:
        case INSTR_HDR_EMIT2_TX:
        case INSTR_HDR_EMIT3_TX:
@@ -1590,7 +1679,7 @@ instr_tx_translate(struct rte_swx_pipeline *p,
 }
 
 static int
-instr_drop_translate(struct rte_swx_pipeline *p,
+instr_drop_translate(struct rte_swx_pipeline *p __rte_unused,
                     struct action *action __rte_unused,
                     char **tokens __rte_unused,
                     int n_tokens,
@@ -1599,9 +1688,8 @@ instr_drop_translate(struct rte_swx_pipeline *p,
 {
        CHECK(n_tokens == 1, EINVAL);
 
-       /* TX_I. */
-       instr->type = INSTR_TX_I;
-       instr->io.io.val = p->n_ports_out - 1;
+       /* DROP. */
+       instr->type = INSTR_DROP;
        return 0;
 }
 
@@ -1631,6 +1719,69 @@ instr_tx_i_exec(struct rte_swx_pipeline *p)
        instr_rx_exec(p);
 }
 
+static inline void
+instr_drop_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_drop_exec(p, t, ip);
+
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
+}
+
+/*
+ * mirror.
+ */
+static int
+instr_mirror_translate(struct rte_swx_pipeline *p,
+                      struct action *action,
+                      char **tokens,
+                      int n_tokens,
+                      struct instruction *instr,
+                      struct instruction_data *data __rte_unused)
+{
+       char *dst = tokens[1], *src = tokens[2];
+       struct field *fdst, *fsrc;
+       uint32_t dst_struct_id = 0, src_struct_id = 0;
+
+       CHECK(n_tokens == 3, EINVAL);
+
+       fdst = struct_field_parse(p, action, dst, &dst_struct_id);
+       CHECK(fdst, EINVAL);
+       CHECK(dst[0] != 'h', EINVAL);
+       CHECK(!fdst->var_size, EINVAL);
+
+       fsrc = struct_field_parse(p, action, src, &src_struct_id);
+       CHECK(fsrc, EINVAL);
+       CHECK(src[0] != 'h', EINVAL);
+       CHECK(!fsrc->var_size, EINVAL);
+
+       instr->type = INSTR_MIRROR;
+       instr->mirror.dst.struct_id = (uint8_t)dst_struct_id;
+       instr->mirror.dst.n_bits = fdst->n_bits;
+       instr->mirror.dst.offset = fdst->offset / 8;
+       instr->mirror.src.struct_id = (uint8_t)src_struct_id;
+       instr->mirror.src.n_bits = fsrc->n_bits;
+       instr->mirror.src.offset = fsrc->offset / 8;
+
+       return 0;
+}
+
+static inline void
+instr_mirror_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_mirror_exec(p, t, ip);
+
+       /* Thread. */
+       thread_ip_inc(p);
+}
+
 /*
  * extract.
  */
@@ -2358,6 +2509,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,
@@ -2367,16 +2521,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;
 }
@@ -2846,8 +3015,8 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
        CHECK(n_tokens == 3, EINVAL);
 
        fdst = header_field_parse(p, dst, &hdst);
-       CHECK(fdst && (fdst->n_bits == 16), EINVAL);
-       CHECK(!fdst->var_size, EINVAL);
+       CHECK(fdst, EINVAL);
+       CHECK(!fdst->var_size && (fdst->n_bits == 16), EINVAL);
 
        /* CKADD_FIELD. */
        fsrc = header_field_parse(p, src, &hsrc);
@@ -2867,17 +3036,16 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
        /* CKADD_STRUCT, CKADD_STRUCT20. */
        hsrc = header_parse(p, src);
        CHECK(hsrc, EINVAL);
-       CHECK(!hsrc->st->var_size, EINVAL);
 
        instr->type = INSTR_ALU_CKADD_STRUCT;
-       if ((hsrc->st->n_bits / 8) == 20)
+       if (!hsrc->st->var_size && ((hsrc->st->n_bits / 8) == 20))
                instr->type = INSTR_ALU_CKADD_STRUCT20;
 
        instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
        instr->alu.dst.n_bits = fdst->n_bits;
        instr->alu.dst.offset = fdst->offset / 8;
        instr->alu.src.struct_id = (uint8_t)hsrc->struct_id;
-       instr->alu.src.n_bits = hsrc->st->n_bits;
+       instr->alu.src.n_bits = (uint8_t)hsrc->id; /* The src header ID is stored here. */
        instr->alu.src.offset = 0; /* Unused. */
        return 0;
 }
@@ -2897,8 +3065,8 @@ instr_alu_cksub_translate(struct rte_swx_pipeline *p,
        CHECK(n_tokens == 3, EINVAL);
 
        fdst = header_field_parse(p, dst, &hdst);
-       CHECK(fdst && (fdst->n_bits == 16), EINVAL);
-       CHECK(!fdst->var_size, EINVAL);
+       CHECK(fdst, EINVAL);
+       CHECK(!fdst->var_size && (fdst->n_bits == 16), EINVAL);
 
        fsrc = header_field_parse(p, src, &hsrc);
        CHECK(fsrc, EINVAL);
@@ -4520,8 +4688,6 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
        /* index = HMEFT, length = HMEFT, color_in = I, color_out = MEF. */
@@ -4556,8 +4722,6 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
        /* index = I, length = HMEFT, color_in = MEFT, color_out = MEF. */
@@ -4588,8 +4752,6 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
        /* index = I, length = HMEFT, color_in = I, color_out = MEF. */
@@ -4619,11 +4781,9 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
-       CHECK(0, EINVAL);
+       return 0;
 }
 
 static inline void
@@ -5622,6 +5782,14 @@ instr_translate(struct rte_swx_pipeline *p,
                                            instr,
                                            data);
 
+       if (!strcmp(tokens[tpos], "mirror"))
+               return instr_mirror_translate(p,
+                                             action,
+                                             &tokens[tpos],
+                                             n_tokens - tpos,
+                                             instr,
+                                             data);
+
        if (!strcmp(tokens[tpos], "extract"))
                return instr_hdr_extract_translate(p,
                                                   action,
@@ -5918,7 +6086,7 @@ instr_translate(struct rte_swx_pipeline *p,
                                              instr,
                                              data);
 
-       CHECK(0, EINVAL);
+       return -EINVAL;
 }
 
 static struct instruction_data *
@@ -5964,7 +6132,20 @@ 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);
+       }
+
+       /* Check that no jump instruction (either conditional or not) can jump to itself (loop). */
+       for (i = 0; i < n_instructions; i++) {
+               struct instruction_data *data = &instruction_data[i];
+               char *label = data->label;
+               char *jmp_label = data->jmp_label;
+
+               /* Continue if this instruction does not have a label or it is not a jump. */
+               if (!label[0] || !jmp_label[0])
+                       continue;
+
+               CHECK(strcmp(label, jmp_label), EINVAL);
        }
 
        /* Get users for each instruction label. */
@@ -6188,7 +6369,7 @@ instr_pattern_emit_many_tx_search(struct instruction *instr,
        if (!i)
                return 0;
 
-       if (!instruction_is_tx(instr[i].type))
+       if (instr[i].type != INSTR_TX)
                return 0;
 
        if (data[i].n_users)
@@ -6632,6 +6813,8 @@ static instr_exec_t instruction_table[] = {
        [INSTR_RX] = instr_rx_exec,
        [INSTR_TX] = instr_tx_exec,
        [INSTR_TX_I] = instr_tx_i_exec,
+       [INSTR_DROP] = instr_drop_exec,
+       [INSTR_MIRROR] = instr_mirror_exec,
 
        [INSTR_HDR_EXTRACT] = instr_hdr_extract_exec,
        [INSTR_HDR_EXTRACT2] = instr_hdr_extract2_exec,
@@ -6946,8 +7129,8 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
                               uint32_t n_instructions)
 {
        struct struct_type *args_struct_type = NULL;
-       struct action *a;
-       int err;
+       struct action *a = NULL;
+       int status = 0;
 
        CHECK(p, EINVAL);
 
@@ -6963,12 +7146,16 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
 
        /* Node allocation. */
        a = calloc(1, sizeof(struct action));
-       CHECK(a, ENOMEM);
+       if (!a) {
+               status = -ENOMEM;
+               goto error;
+       }
+
        if (args_struct_type) {
                a->args_endianness = calloc(args_struct_type->n_fields, sizeof(int));
                if (!a->args_endianness) {
-                       free(a);
-                       CHECK(0, ENOMEM);
+                       status = -ENOMEM;
+                       goto error;
                }
        }
 
@@ -6978,18 +7165,26 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
        a->id = p->n_actions;
 
        /* Instruction translation. */
-       err = instruction_config(p, a, instructions, n_instructions);
-       if (err) {
-               free(a->args_endianness);
-               free(a);
-               return err;
-       }
+       status = instruction_config(p, a, instructions, n_instructions);
+       if (status)
+               goto error;
 
        /* Node add to tailq. */
        TAILQ_INSERT_TAIL(&p->actions, a, node);
        p->n_actions++;
 
        return 0;
+
+error:
+       if (!a)
+               return status;
+
+       free(a->args_endianness);
+       free(a->instructions);
+       free(a->instruction_data);
+       free(a);
+
+       return status;
 }
 
 static int
@@ -7034,8 +7229,9 @@ action_free(struct rte_swx_pipeline *p)
                        break;
 
                TAILQ_REMOVE(&p->actions, action, node);
-               free(action->instruction_data);
+               free(action->args_endianness);
                free(action->instructions);
+               free(action->instruction_data);
                free(action);
        }
 }
@@ -7074,6 +7270,91 @@ action_arg_src_mov_count(struct action *a,
        return n_users;
 }
 
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+#define field_ntoh(val, n_bits) (ntoh64((val) << (64 - n_bits)))
+#define field_hton(val, n_bits) (hton64((val) << (64 - n_bits)))
+#else
+#define field_ntoh(val, n_bits) (val)
+#define field_hton(val, n_bits) (val)
+#endif
+
+#define ACTION_ARGS_TOKENS_MAX 256
+
+static int
+action_args_parse(struct action *a, const char *args, uint8_t *data)
+{
+       char *tokens[ACTION_ARGS_TOKENS_MAX], *s0 = NULL, *s;
+       uint32_t n_tokens = 0, offset = 0, i;
+       int status = 0;
+
+       /* Checks. */
+       if (!a->st || !args || !args[0]) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       /* Memory allocation. */
+       s0 = strdup(args);
+       if (!s0) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       /* Parse the string into tokens. */
+       for (s = s0; ; ) {
+               char *token;
+
+               token = strtok_r(s, " \f\n\r\t\v", &s);
+               if (!token)
+                       break;
+
+               if (n_tokens >= RTE_DIM(tokens)) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               tokens[n_tokens] = token;
+               n_tokens++;
+       }
+
+       /* More checks. */
+       if (n_tokens != a->st->n_fields * 2) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       /* Process the action arguments. */
+       for (i = 0; i < a->st->n_fields; i++) {
+               struct field *f = &a->st->fields[i];
+               char *arg_name = tokens[i * 2];
+               char *arg_val = tokens[i * 2 + 1];
+               uint64_t val;
+
+               if (strcmp(arg_name, f->name)) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               val = strtoull(arg_val, &arg_val, 0);
+               if (arg_val[0]) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               /* Endianness conversion. */
+               if (a->args_endianness[i])
+                       val = field_hton(val, f->n_bits);
+
+               /* Copy to entry. */
+               memcpy(&data[offset], (uint8_t *)&val, f->n_bits / 8);
+               offset += f->n_bits / 8;
+       }
+
+error:
+       free(s0);
+       return status;
+}
+
 /*
  * Table.
  */
@@ -7290,7 +7571,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;
@@ -7317,6 +7598,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);
 
@@ -7327,6 +7609,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);
@@ -7335,9 +7623,12 @@ 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);
+       CHECK((default_action->st && params->default_action_args) || !params->default_action_args,
+             EINVAL);
 
        /* Table type checks. */
        if (recommended_table_type_name)
@@ -7358,31 +7649,43 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
 
        /* Memory allocation. */
        t = calloc(1, sizeof(struct table));
-       CHECK(t, ENOMEM);
+       if (!t) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        t->fields = calloc(params->n_fields, sizeof(struct match_field));
        if (!t->fields) {
-               free(t);
-               CHECK(0, ENOMEM);
+               status = -ENOMEM;
+               goto error;
        }
 
        t->actions = calloc(params->n_actions, sizeof(struct action *));
        if (!t->actions) {
-               free(t->fields);
-               free(t);
-               CHECK(0, ENOMEM);
+               status = -ENOMEM;
+               goto error;
        }
 
        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);
+                       status = -ENOMEM;
+                       goto error;
                }
        }
 
+       t->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
+       if (!t->action_is_for_table_entries) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       t->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
+       if (!t->action_is_for_default_entry) {
+               status = -ENOMEM;
+               goto error;
+       }
+
        /* Node initialization. */
        strcpy(t->name, name);
        if (args && args[0])
@@ -7401,13 +7704,27 @@ 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,
-                      params->default_action_data,
-                      default_action->st->n_bits / 8);
+       if (default_action->st) {
+               status = action_args_parse(default_action,
+                                          params->default_action_args,
+                                          t->default_action_data);
+               if (status)
+                       goto error;
+       }
+
        t->n_actions = params->n_actions;
        t->default_action_is_const = params->default_action_is_const;
        t->action_data_size_max = action_data_size_max;
@@ -7420,6 +7737,19 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
        p->n_tables++;
 
        return 0;
+
+error:
+       if (!t)
+               return status;
+
+       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 status;
 }
 
 static struct rte_swx_table_params *
@@ -8160,23 +8490,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,
@@ -8187,6 +8512,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);
@@ -8195,10 +8526,12 @@ 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) ||
-             !params->default_action_data, EINVAL);
+       CHECK((default_action->st && params->default_action_args) || !params->default_action_args,
+             EINVAL);
 
        /* Any other checks. */
        CHECK(size, EINVAL);
@@ -8206,25 +8539,41 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
 
        /* Memory allocation. */
        l = calloc(1, sizeof(struct learner));
-       if (!l)
-               goto nomem;
+       if (!l) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        l->fields = calloc(params->n_fields, sizeof(struct field *));
-       if (!l->fields)
-               goto nomem;
+       if (!l->fields) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        l->actions = calloc(params->n_actions, sizeof(struct action *));
-       if (!l->actions)
-               goto nomem;
-
-       l->action_arg = calloc(params->n_actions, sizeof(struct field *));
-       if (!l->action_arg)
-               goto nomem;
+       if (!l->actions) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        if (action_data_size_max) {
                l->default_action_data = calloc(1, action_data_size_max);
-               if (!l->default_action_data)
-                       goto nomem;
+               if (!l->default_action_data) {
+                       status = -ENOMEM;
+                       goto error;
+               }
+       }
+
+       l->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
+       if (!l->action_is_for_table_entries) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       l->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
+       if (!l->action_is_for_default_entry) {
+               status = -ENOMEM;
+               goto error;
        }
 
        /* Node initialization. */
@@ -8243,19 +8592,27 @@ 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;
 
-       if (default_action->st)
-               memcpy(l->default_action_data,
-                      params->default_action_data,
-                      default_action->st->n_bits / 8);
+       if (default_action->st) {
+               status = action_args_parse(default_action,
+                                          params->default_action_args,
+                                          l->default_action_data);
+               if (status)
+                       goto error;
+       }
 
        l->n_actions = params->n_actions;
 
@@ -8275,16 +8632,18 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
 
        return 0;
 
-nomem:
+error:
        if (!l)
-               return -ENOMEM;
+               return status;
 
-       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);
 
-       return -ENOMEM;
+       return status;
 }
 
 static void
@@ -8374,7 +8733,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);
@@ -8418,7 +8776,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();
@@ -8434,21 +8791,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;
-                       }
                }
        }
 
@@ -8475,7 +8817,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);
        }
@@ -8491,7 +8832,7 @@ table_state_build(struct rte_swx_pipeline *p)
        struct selector *s;
        struct learner *l;
 
-       p->table_state = calloc(p->n_tables + p->n_selectors,
+       p->table_state = calloc(p->n_tables + p->n_selectors + p->n_learners,
                                sizeof(struct rte_swx_table_state));
        CHECK(p->table_state, ENOMEM);
 
@@ -8927,17 +9268,145 @@ metarray_free(struct rte_swx_pipeline *p)
 /*
  * Pipeline.
  */
+void
+rte_swx_pipeline_free(struct rte_swx_pipeline *p)
+{
+       void *lib;
+
+       if (!p)
+               return;
+
+       lib = p->lib;
+
+       free(p->instruction_data);
+       free(p->instructions);
+
+       metarray_free(p);
+       regarray_free(p);
+       table_state_free(p);
+       learner_free(p);
+       selector_free(p);
+       table_free(p);
+       action_free(p);
+       instruction_table_free(p);
+       metadata_free(p);
+       header_free(p);
+       extern_func_free(p);
+       extern_obj_free(p);
+       mirroring_free(p);
+       port_out_free(p);
+       port_in_free(p);
+       struct_free(p);
+
+       free(p);
+
+       if (lib)
+               dlclose(lib);
+}
+
+static int
+port_in_types_register(struct rte_swx_pipeline *p)
+{
+       int status;
+
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "ethdev",
+               &rte_swx_port_ethdev_reader_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "ring",
+               &rte_swx_port_ring_reader_ops);
+       if (status)
+               return status;
+
+#ifdef RTE_PORT_PCAP
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "source",
+               &rte_swx_port_source_ops);
+       if (status)
+               return status;
+#endif
+
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "fd",
+               &rte_swx_port_fd_reader_ops);
+       if (status)
+               return status;
+
+       return 0;
+}
+
+static int
+port_out_types_register(struct rte_swx_pipeline *p)
+{
+       int status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "ethdev",
+               &rte_swx_port_ethdev_writer_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "ring",
+               &rte_swx_port_ring_writer_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "sink",
+               &rte_swx_port_sink_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "fd",
+               &rte_swx_port_fd_writer_ops);
+       if (status)
+               return status;
+
+       return 0;
+}
+
+static int
+table_types_register(struct rte_swx_pipeline *p)
+{
+       int status;
+
+       status = rte_swx_pipeline_table_type_register(p,
+               "exact",
+               RTE_SWX_TABLE_MATCH_EXACT,
+               &rte_swx_table_exact_match_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_table_type_register(p,
+               "wildcard",
+               RTE_SWX_TABLE_MATCH_WILDCARD,
+               &rte_swx_table_wildcard_match_ops);
+       if (status)
+               return status;
+
+       return 0;
+}
+
 int
 rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 {
-       struct rte_swx_pipeline *pipeline;
+       struct rte_swx_pipeline *pipeline = NULL;
+       int status = 0;
 
        /* Check input parameters. */
        CHECK(p, EINVAL);
 
        /* Memory allocation. */
        pipeline = calloc(1, sizeof(struct rte_swx_pipeline));
-       CHECK(pipeline, ENOMEM);
+       if (!pipeline) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        /* Initialization. */
        TAILQ_INIT(&pipeline->struct_types);
@@ -8961,36 +9430,24 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
        pipeline->n_structs = 1; /* Struct 0 is reserved for action_data. */
        pipeline->numa_node = numa_node;
 
-       *p = pipeline;
-       return 0;
-}
+       status = port_in_types_register(pipeline);
+       if (status)
+               goto error;
 
-void
-rte_swx_pipeline_free(struct rte_swx_pipeline *p)
-{
-       if (!p)
-               return;
+       status = port_out_types_register(pipeline);
+       if (status)
+               goto error;
 
-       free(p->instruction_data);
-       free(p->instructions);
+       status = table_types_register(pipeline);
+       if (status)
+               goto error;
 
-       metarray_free(p);
-       regarray_free(p);
-       table_state_free(p);
-       learner_free(p);
-       selector_free(p);
-       table_free(p);
-       action_free(p);
-       instruction_table_free(p);
-       metadata_free(p);
-       header_free(p);
-       extern_func_free(p);
-       extern_obj_free(p);
-       port_out_free(p);
-       port_in_free(p);
-       struct_free(p);
+       *p = pipeline;
+       return 0;
 
-       free(p);
+error:
+       rte_swx_pipeline_free(pipeline);
+       return status;
 }
 
 int
@@ -9021,6 +9478,9 @@ pipeline_compile(struct rte_swx_pipeline *p);
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 {
+       struct rte_swx_port_sink_params drop_port_params = {
+               .file_name = NULL,
+       };
        int status;
 
        CHECK(p, EINVAL);
@@ -9030,10 +9490,22 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
        if (status)
                goto error;
 
+       /* Drop port. */
+       status = rte_swx_pipeline_port_out_config(p,
+                                                 p->n_ports_out,
+                                                 "sink",
+                                                 &drop_port_params);
+       if (status)
+               goto error;
+
        status = port_out_build(p);
        if (status)
                goto error;
 
+       status = mirroring_build(p);
+       if (status)
+               goto error;
+
        status = struct_build(p);
        if (status)
                goto error;
@@ -9105,6 +9577,7 @@ error:
        header_build_free(p);
        extern_func_build_free(p);
        extern_obj_build_free(p);
+       mirroring_build_free(p);
        port_out_build_free(p);
        port_in_build_free(p);
        struct_build_free(p);
@@ -9156,6 +9629,8 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
 
        pipeline->n_ports_in = p->n_ports_in;
        pipeline->n_ports_out = p->n_ports_out;
+       pipeline->n_mirroring_slots = p->n_mirroring_slots;
+       pipeline->n_mirroring_sessions = p->n_mirroring_sessions;
        pipeline->n_actions = n_actions;
        pipeline->n_tables = n_tables;
        pipeline->n_selectors = p->n_selectors;
@@ -9284,6 +9759,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;
 }
 
@@ -9471,6 +9949,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;
 }
 
@@ -9834,6 +10318,27 @@ rte_swx_ctl_meter_stats_read(struct rte_swx_pipeline *p,
        return 0;
 }
 
+int
+rte_swx_ctl_pipeline_mirroring_session_set(struct rte_swx_pipeline *p,
+                                          uint32_t session_id,
+                                          struct rte_swx_pipeline_mirroring_session_params *params)
+{
+       struct mirroring_session *s;
+
+       CHECK(p, EINVAL);
+       CHECK(p->build_done, EEXIST);
+       CHECK(session_id < p->n_mirroring_sessions, EINVAL);
+       CHECK(params, EINVAL);
+       CHECK(params->port_id < p->n_ports_out, EINVAL);
+
+       s = &p->mirroring_sessions[session_id];
+       s->port_id = params->port_id;
+       s->fast_clone = params->fast_clone;
+       s->truncation_length = params->truncation_length ? params->truncation_length : UINT32_MAX;
+
+       return 0;
+}
+
 /*
  * Pipeline compilation.
  */
@@ -9845,6 +10350,8 @@ instr_type_to_name(struct instruction *instr)
 
        case INSTR_TX: return "INSTR_TX";
        case INSTR_TX_I: return "INSTR_TX_I";
+       case INSTR_DROP: return "INSTR_DROP";
+       case INSTR_MIRROR: return "INSTR_MIRROR";
 
        case INSTR_HDR_EXTRACT: return "INSTR_HDR_EXTRACT";
        case INSTR_HDR_EXTRACT2: return "INSTR_HDR_EXTRACT2";
@@ -10070,8 +10577,9 @@ instr_io_export(struct instruction *instr, FILE *f)
                instr_type_to_name(instr));
 
        /* instr.io. */
-       fprintf(f,
-               "\t\t.io = {\n");
+       if (n_io || n_io_imm || n_hdrs)
+               fprintf(f,
+                       "\t\t.io = {\n");
 
        /* instr.io.io. */
        if (n_io)
@@ -10137,14 +10645,43 @@ instr_io_export(struct instruction *instr, FILE *f)
        }
 
        /* instr.io - closing curly brace. */
-       fprintf(f,
-               "\t\t},\n");
+       if (n_io || n_io_imm || n_hdrs)
+               fprintf(f,
+                       "\t\t},\n");
 
        /* instr - closing curly brace. */
        fprintf(f,
                "\t},\n");
 }
 
+static void
+instr_mirror_export(struct instruction *instr, FILE *f)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.mirror = {\n"
+               "\t\t\t.dst = {\n"
+               "\t\t\t\t.struct_id = %u,\n"
+               "\t\t\t\t.n_bits = %u,\n"
+               "\t\t\t\t.offset = %u,\n"
+               "\t\t\t}\n,"
+               "\t\t\t.src = {\n"
+               "\t\t\t\t.struct_id = %u,\n"
+               "\t\t\t\t.n_bits = %u,\n"
+               "\t\t\t\t.offset = %u,\n"
+               "\t\t\t}\n,"
+               "\t\t},\n"
+               "\t},\n",
+               instr_type_to_name(instr),
+               instr->mirror.dst.struct_id,
+               instr->mirror.dst.n_bits,
+               instr->mirror.dst.offset,
+               instr->mirror.src.struct_id,
+               instr->mirror.src.n_bits,
+               instr->mirror.src.offset);
+}
+
 static void
 instr_hdr_validate_export(struct instruction *instr, FILE *f)
 {
@@ -10711,6 +11248,8 @@ static instruction_export_t export_table[] = {
 
        [INSTR_TX] = instr_io_export,
        [INSTR_TX_I] = instr_io_export,
+       [INSTR_DROP] = instr_io_export,
+       [INSTR_MIRROR] = instr_mirror_export,
 
        [INSTR_HDR_EXTRACT] = instr_io_export,
        [INSTR_HDR_EXTRACT2] = instr_io_export,
@@ -10928,6 +11467,8 @@ instr_type_to_func(struct instruction *instr)
 
        case INSTR_TX: return "__instr_tx_exec";
        case INSTR_TX_I: return "__instr_tx_i_exec";
+       case INSTR_DROP: return "__instr_drop_exec";
+       case INSTR_MIRROR: return "__instr_mirror_exec";
 
        case INSTR_HDR_EXTRACT: return "__instr_hdr_extract_exec";
        case INSTR_HDR_EXTRACT2: return "__instr_hdr_extract2_exec";
@@ -12169,6 +12710,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)
 {
@@ -12205,6 +12766,191 @@ pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
        return 0;
 }
 
+#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
+#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
+#endif
+
+static int
+pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
+{
+       struct action *a;
+       struct instruction_group *g;
+       char *dir_in, *buffer = NULL;
+       const char *dir_out;
+       int status = 0;
+
+       /* Get the environment variables. */
+       dir_in = getenv("RTE_INSTALL_DIR");
+       if (!dir_in) {
+               status = -EINVAL;
+               goto free;
+       }
+
+       dir_out = "/tmp";
+
+       /* Memory allocation for the command buffer. */
+       buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
+       if (!buffer) {
+               status = -ENOMEM;
+               goto free;
+       }
+
+       snprintf(buffer,
+                RTE_SWX_PIPELINE_CMD_MAX_SIZE,
+                "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
+                "-I %s/lib/pipeline "
+                "-I %s/lib/eal/include "
+                "-I %s/lib/eal/x86/include "
+                "-I %s/lib/eal/include/generic "
+                "-I %s/lib/meter "
+                "-I %s/lib/port "
+                "-I %s/lib/table "
+                "-I %s/lib/pipeline "
+                "-I %s/config "
+                "-I %s/build "
+                "-I %s/lib/eal/linux/include "
+                ">%s/pipeline.log 2>&1 "
+                "&& "
+                "gcc -shared %s/pipeline.o -o %s/libpipeline.so "
+                ">>%s/pipeline.log 2>&1",
+                dir_out,
+                dir_out,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_out,
+                dir_out,
+                dir_out,
+                dir_out);
+
+       /* Build the shared object library. */
+       status = system(buffer);
+       if (status)
+               goto free;
+
+       /* Open library. */
+       snprintf(buffer,
+                RTE_SWX_PIPELINE_CMD_MAX_SIZE,
+                "%s/libpipeline.so",
+                dir_out);
+
+       p->lib = dlopen(buffer, RTLD_LAZY);
+       if (!p->lib) {
+               status = -EIO;
+               goto free;
+       }
+
+       /* Get the action function symbols. */
+       TAILQ_FOREACH(a, &p->actions, node) {
+               snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
+
+               p->action_funcs[a->id] = dlsym(p->lib, buffer);
+               if (!p->action_funcs[a->id]) {
+                       status = -EINVAL;
+                       goto free;
+               }
+       }
+
+       /* Get the pipeline function symbols. */
+       TAILQ_FOREACH(g, igl, node) {
+               if (g->first_instr_id == g->last_instr_id)
+                       continue;
+
+               snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
+
+               g->func = dlsym(p->lib, buffer);
+               if (!g->func) {
+                       status = -EINVAL;
+                       goto free;
+               }
+       }
+
+free:
+       if (status && p->lib) {
+               dlclose(p->lib);
+               p->lib = NULL;
+       }
+
+       free(buffer);
+
+       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)
 {
@@ -12222,6 +12968,18 @@ pipeline_compile(struct rte_swx_pipeline *p)
        if (status)
                goto free;
 
+       /* Build and load the shared object library. */
+       status = pipeline_libload(p, igl);
+       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);