pipeline: add SWX headers and meta-data
authorCristian Dumitrescu <cristian.dumitrescu@intel.com>
Thu, 1 Oct 2020 10:19:32 +0000 (11:19 +0100)
committerDavid Marchand <david.marchand@redhat.com>
Thu, 1 Oct 2020 16:43:07 +0000 (18:43 +0200)
Add support for dynamically-defined packet headers and meta-data to
the SWX pipeline. The header and meta-data format are defined by the
struct type they instantiate.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
lib/librte_pipeline/rte_pipeline_version.map
lib/librte_pipeline/rte_swx_pipeline.c
lib/librte_pipeline/rte_swx_pipeline.h

index 5ec98d5..ac0d234 100644 (file)
@@ -63,8 +63,11 @@ EXPERIMENTAL {
        rte_swx_pipeline_build;
        rte_swx_pipeline_config;
        rte_swx_pipeline_free;
+       rte_swx_pipeline_packet_header_register;
+       rte_swx_pipeline_packet_metadata_register;
        rte_swx_pipeline_port_in_config;
        rte_swx_pipeline_port_in_type_register;
        rte_swx_pipeline_port_out_config;
        rte_swx_pipeline_port_out_type_register;
+       rte_swx_pipeline_struct_type_register;
 };
index 7aeac8c..cb2e32b 100644 (file)
@@ -20,6 +20,25 @@ do {                                                                           \
 #define CHECK_NAME(name, err_code)                                             \
        CHECK((name) && (name)[0], err_code)
 
+/*
+ * Struct.
+ */
+struct field {
+       char name[RTE_SWX_NAME_SIZE];
+       uint32_t n_bits;
+       uint32_t offset;
+};
+
+struct struct_type {
+       TAILQ_ENTRY(struct_type) node;
+       char name[RTE_SWX_NAME_SIZE];
+       struct field *fields;
+       uint32_t n_fields;
+       uint32_t n_bits;
+};
+
+TAILQ_HEAD(struct_type_tailq, struct_type);
+
 /*
  * Input port.
  */
@@ -71,24 +90,198 @@ struct port_out_runtime {
        void *obj;
 };
 
+/*
+ * Header.
+ */
+struct header {
+       TAILQ_ENTRY(header) node;
+       char name[RTE_SWX_NAME_SIZE];
+       struct struct_type *st;
+       uint32_t struct_id;
+       uint32_t id;
+};
+
+TAILQ_HEAD(header_tailq, header);
+
+struct header_runtime {
+       uint8_t *ptr0;
+};
+
+struct header_out_runtime {
+       uint8_t *ptr0;
+       uint8_t *ptr;
+       uint32_t n_bytes;
+};
+
 /*
  * Pipeline.
  */
+struct thread {
+       /* Structures. */
+       uint8_t **structs;
+
+       /* Packet headers. */
+       struct header_runtime *headers; /* Extracted or generated headers. */
+       struct header_out_runtime *headers_out; /* Emitted headers. */
+       uint8_t *header_storage;
+       uint8_t *header_out_storage;
+       uint64_t valid_headers;
+       uint32_t n_headers_out;
+
+       /* Packet meta-data. */
+       uint8_t *metadata;
+};
+
+#ifndef RTE_SWX_PIPELINE_THREADS_MAX
+#define RTE_SWX_PIPELINE_THREADS_MAX 16
+#endif
+
 struct rte_swx_pipeline {
+       struct struct_type_tailq struct_types;
        struct port_in_type_tailq port_in_types;
        struct port_in_tailq ports_in;
        struct port_out_type_tailq port_out_types;
        struct port_out_tailq ports_out;
+       struct header_tailq headers;
+       struct struct_type *metadata_st;
+       uint32_t metadata_struct_id;
 
        struct port_in_runtime *in;
        struct port_out_runtime *out;
+       struct thread threads[RTE_SWX_PIPELINE_THREADS_MAX];
 
+       uint32_t n_structs;
        uint32_t n_ports_in;
        uint32_t n_ports_out;
+       uint32_t n_headers;
        int build_done;
        int numa_node;
 };
 
+/*
+ * Struct.
+ */
+static struct struct_type *
+struct_type_find(struct rte_swx_pipeline *p, const char *name)
+{
+       struct struct_type *elem;
+
+       TAILQ_FOREACH(elem, &p->struct_types, node)
+               if (strcmp(elem->name, name) == 0)
+                       return elem;
+
+       return NULL;
+}
+
+int
+rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
+                                     const char *name,
+                                     struct rte_swx_field_params *fields,
+                                     uint32_t n_fields)
+{
+       struct struct_type *st;
+       uint32_t i;
+
+       CHECK(p, EINVAL);
+       CHECK_NAME(name, EINVAL);
+       CHECK(fields, EINVAL);
+       CHECK(n_fields, EINVAL);
+
+       for (i = 0; i < n_fields; i++) {
+               struct rte_swx_field_params *f = &fields[i];
+               uint32_t j;
+
+               CHECK_NAME(f->name, EINVAL);
+               CHECK(f->n_bits, EINVAL);
+               CHECK(f->n_bits <= 64, EINVAL);
+               CHECK((f->n_bits & 7) == 0, EINVAL);
+
+               for (j = 0; j < i; j++) {
+                       struct rte_swx_field_params *f_prev = &fields[j];
+
+                       CHECK(strcmp(f->name, f_prev->name), EINVAL);
+               }
+       }
+
+       CHECK(!struct_type_find(p, name), EEXIST);
+
+       /* Node allocation. */
+       st = calloc(1, sizeof(struct struct_type));
+       CHECK(st, ENOMEM);
+
+       st->fields = calloc(n_fields, sizeof(struct field));
+       if (!st->fields) {
+               free(st);
+               CHECK(0, ENOMEM);
+       }
+
+       /* Node initialization. */
+       strcpy(st->name, name);
+       for (i = 0; i < n_fields; i++) {
+               struct field *dst = &st->fields[i];
+               struct rte_swx_field_params *src = &fields[i];
+
+               strcpy(dst->name, src->name);
+               dst->n_bits = src->n_bits;
+               dst->offset = st->n_bits;
+
+               st->n_bits += src->n_bits;
+       }
+       st->n_fields = n_fields;
+
+       /* Node add to tailq. */
+       TAILQ_INSERT_TAIL(&p->struct_types, st, node);
+
+       return 0;
+}
+
+static int
+struct_build(struct rte_swx_pipeline *p)
+{
+       uint32_t i;
+
+       for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
+               struct thread *t = &p->threads[i];
+
+               t->structs = calloc(p->n_structs, sizeof(uint8_t *));
+               CHECK(t->structs, ENOMEM);
+       }
+
+       return 0;
+}
+
+static void
+struct_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];
+
+               free(t->structs);
+               t->structs = NULL;
+       }
+}
+
+static void
+struct_free(struct rte_swx_pipeline *p)
+{
+       struct_build_free(p);
+
+       /* Struct types. */
+       for ( ; ; ) {
+               struct struct_type *elem;
+
+               elem = TAILQ_FIRST(&p->struct_types);
+               if (!elem)
+                       break;
+
+               TAILQ_REMOVE(&p->struct_types, elem, node);
+               free(elem->fields);
+               free(elem);
+       }
+}
+
 /*
  * Input port.
  */
@@ -413,6 +606,205 @@ port_out_free(struct rte_swx_pipeline *p)
        }
 }
 
+/*
+ * Header.
+ */
+static struct header *
+header_find(struct rte_swx_pipeline *p, const char *name)
+{
+       struct header *elem;
+
+       TAILQ_FOREACH(elem, &p->headers, node)
+               if (strcmp(elem->name, name) == 0)
+                       return elem;
+
+       return NULL;
+}
+
+int
+rte_swx_pipeline_packet_header_register(struct rte_swx_pipeline *p,
+                                       const char *name,
+                                       const char *struct_type_name)
+{
+       struct struct_type *st;
+       struct header *h;
+       size_t n_headers_max;
+
+       CHECK(p, EINVAL);
+       CHECK_NAME(name, EINVAL);
+       CHECK_NAME(struct_type_name, EINVAL);
+
+       CHECK(!header_find(p, name), EEXIST);
+
+       st = struct_type_find(p, struct_type_name);
+       CHECK(st, EINVAL);
+
+       n_headers_max = RTE_SIZEOF_FIELD(struct thread, valid_headers) * 8;
+       CHECK(p->n_headers < n_headers_max, ENOSPC);
+
+       /* Node allocation. */
+       h = calloc(1, sizeof(struct header));
+       CHECK(h, ENOMEM);
+
+       /* Node initialization. */
+       strcpy(h->name, name);
+       h->st = st;
+       h->struct_id = p->n_structs;
+       h->id = p->n_headers;
+
+       /* Node add to tailq. */
+       TAILQ_INSERT_TAIL(&p->headers, h, node);
+       p->n_headers++;
+       p->n_structs++;
+
+       return 0;
+}
+
+static int
+header_build(struct rte_swx_pipeline *p)
+{
+       struct header *h;
+       uint32_t n_bytes = 0, i;
+
+       TAILQ_FOREACH(h, &p->headers, node) {
+               n_bytes += h->st->n_bits / 8;
+       }
+
+       for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
+               struct thread *t = &p->threads[i];
+               uint32_t offset = 0;
+
+               t->headers = calloc(p->n_headers,
+                                   sizeof(struct header_runtime));
+               CHECK(t->headers, ENOMEM);
+
+               t->headers_out = calloc(p->n_headers,
+                                       sizeof(struct header_out_runtime));
+               CHECK(t->headers_out, ENOMEM);
+
+               t->header_storage = calloc(1, n_bytes);
+               CHECK(t->header_storage, ENOMEM);
+
+               t->header_out_storage = calloc(1, n_bytes);
+               CHECK(t->header_out_storage, ENOMEM);
+
+               TAILQ_FOREACH(h, &p->headers, node) {
+                       uint8_t *header_storage;
+
+                       header_storage = &t->header_storage[offset];
+                       offset += h->st->n_bits / 8;
+
+                       t->headers[h->id].ptr0 = header_storage;
+                       t->structs[h->struct_id] = header_storage;
+               }
+       }
+
+       return 0;
+}
+
+static void
+header_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];
+
+               free(t->headers_out);
+               t->headers_out = NULL;
+
+               free(t->headers);
+               t->headers = NULL;
+
+               free(t->header_out_storage);
+               t->header_out_storage = NULL;
+
+               free(t->header_storage);
+               t->header_storage = NULL;
+       }
+}
+
+static void
+header_free(struct rte_swx_pipeline *p)
+{
+       header_build_free(p);
+
+       for ( ; ; ) {
+               struct header *elem;
+
+               elem = TAILQ_FIRST(&p->headers);
+               if (!elem)
+                       break;
+
+               TAILQ_REMOVE(&p->headers, elem, node);
+               free(elem);
+       }
+}
+
+/*
+ * Meta-data.
+ */
+int
+rte_swx_pipeline_packet_metadata_register(struct rte_swx_pipeline *p,
+                                         const char *struct_type_name)
+{
+       struct struct_type *st = NULL;
+
+       CHECK(p, EINVAL);
+
+       CHECK_NAME(struct_type_name, EINVAL);
+       st  = struct_type_find(p, struct_type_name);
+       CHECK(st, EINVAL);
+       CHECK(!p->metadata_st, EINVAL);
+
+       p->metadata_st = st;
+       p->metadata_struct_id = p->n_structs;
+
+       p->n_structs++;
+
+       return 0;
+}
+
+static int
+metadata_build(struct rte_swx_pipeline *p)
+{
+       uint32_t n_bytes = p->metadata_st->n_bits / 8;
+       uint32_t i;
+
+       /* Thread-level initialization. */
+       for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
+               struct thread *t = &p->threads[i];
+               uint8_t *metadata;
+
+               metadata = calloc(1, n_bytes);
+               CHECK(metadata, ENOMEM);
+
+               t->metadata = metadata;
+               t->structs[p->metadata_struct_id] = metadata;
+       }
+
+       return 0;
+}
+
+static void
+metadata_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];
+
+               free(t->metadata);
+               t->metadata = NULL;
+       }
+}
+
+static void
+metadata_free(struct rte_swx_pipeline *p)
+{
+       metadata_build_free(p);
+}
+
 /*
  * Pipeline.
  */
@@ -429,11 +821,14 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
        CHECK(pipeline, ENOMEM);
 
        /* Initialization. */
+       TAILQ_INIT(&pipeline->struct_types);
        TAILQ_INIT(&pipeline->port_in_types);
        TAILQ_INIT(&pipeline->ports_in);
        TAILQ_INIT(&pipeline->port_out_types);
        TAILQ_INIT(&pipeline->ports_out);
+       TAILQ_INIT(&pipeline->headers);
 
+       pipeline->n_structs = 1; /* Struct 0 is reserved for action_data. */
        pipeline->numa_node = numa_node;
 
        *p = pipeline;
@@ -446,8 +841,11 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
        if (!p)
                return;
 
+       metadata_free(p);
+       header_free(p);
        port_out_free(p);
        port_in_free(p);
+       struct_free(p);
 
        free(p);
 }
@@ -468,12 +866,27 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
        if (status)
                goto error;
 
+       status = struct_build(p);
+       if (status)
+               goto error;
+
+       status = header_build(p);
+       if (status)
+               goto error;
+
+       status = metadata_build(p);
+       if (status)
+               goto error;
+
        p->build_done = 1;
        return 0;
 
 error:
+       metadata_build_free(p);
+       header_build_free(p);
        port_out_build_free(p);
        port_in_build_free(p);
+       struct_build_free(p);
 
        return status;
 }
index 2be83bd..4a7b679 100644 (file)
@@ -147,6 +147,91 @@ rte_swx_pipeline_port_out_config(struct rte_swx_pipeline *p,
                                 const char *port_type_name,
                                 void *args);
 
+/*
+ * Packet headers and meta-data
+ */
+
+/** Structure (struct) field. */
+struct rte_swx_field_params {
+       /** Struct field name. */
+       const char *name;
+
+       /** Struct field size (in bits).
+        * Restriction: All struct fields must be a multiple of 8 bits.
+        * Restriction: All struct fields must be no greater than 64 bits.
+        */
+       uint32_t n_bits;
+};
+
+/**
+ * Pipeline struct type register
+ *
+ * Structs are used extensively in many part of the pipeline to define the size
+ * and layout of a specific memory piece such as: headers, meta-data, action
+ * data stored in a table entry, mailboxes for extern objects and functions.
+ * Similar to C language structs, they are a well defined sequence of fields,
+ * with each field having a unique name and a constant size.
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] name
+ *   Struct type name.
+ * @param[in] fields
+ *   The sequence of struct fields.
+ * @param[in] n_fields
+ *   The number of struct fields.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Struct type with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
+                                     const char *name,
+                                     struct rte_swx_field_params *fields,
+                                     uint32_t n_fields);
+
+/**
+ * Pipeline packet header register
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] name
+ *   Header name.
+ * @param[in] struct_type_name
+ *   The struct type instantiated by this packet header.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Header with this name already exists;
+ *   -ENOSPC: Maximum number of headers reached for the pipeline.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_packet_header_register(struct rte_swx_pipeline *p,
+                                       const char *name,
+                                       const char *struct_type_name);
+
+/**
+ * Pipeline packet meta-data register
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] struct_type_name
+ *   The struct type instantiated by the packet meta-data.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_packet_metadata_register(struct rte_swx_pipeline *p,
+                                         const char *struct_type_name);
+
+
 /**
  * Pipeline build
  *