DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH 00/11] pipeline: add IPsec support
@ 2023-01-11 20:55 Cristian Dumitrescu
  2023-01-11 20:55 ` [PATCH 01/11] " Cristian Dumitrescu
                   ` (16 more replies)
  0 siblings, 17 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:55 UTC (permalink / raw)
  To: dev

This patch set introduces a companion block for the SWX pipeline for
IPsec support.

The IPsec block is external to the pipeline, hence it needs to be
explicitly instantiated by the user and connected to a pipeline
instance through the pipeline I/O ports.

Main features:
* IPsec inbound (encrypted input packets -> clear text output packets)
and outbound (clear text input packets -> encrypted output packets)
processing support for tunnel and transport modes.

Interaction of the IPsec block with the pipeline:
* Each IPsec block instance has its own set of Security Associations
(SAs) used to process the input packets. Each SA is identified by its
unique SA ID. The IPsec inbound and outbound SAs share the same ID
space.
* Each input packet is first mapped to one of the existing SAs by
using the SA ID and then processed according to the identified SA. The
SA ID is read from input packet. The SA ID field is typically written
by the pipeline before sending the packet to the IPsec block.

Cristian Dumitrescu (11):
  pipeline: add IPsec support
  examples/pipeline: rework memory pool support
  examples/pipeline: streamline ring support
  examples/pipeline: streamline the Ethernet device support
  examples/pipeline: support crypto devices
  examples/pipeline: add CLI command for crypto device
  examples/pipeline: add IPsec CLI commands
  examples/pipeline: rework the thread configuration updates
  examples/pipeline: support blocks other than pipelines
  examples/pipeline: add block enable/disable CLI commands
  examples/pipeline: add IPsec example

 examples/pipeline/cli.c                       |  896 ++++++--
 examples/pipeline/examples/fib.cli            |    4 +-
 examples/pipeline/examples/hash_func.cli      |    4 +-
 examples/pipeline/examples/ipsec.cli          |   57 +
 examples/pipeline/examples/ipsec.io           |   23 +
 examples/pipeline/examples/ipsec.spec         |  138 ++
 examples/pipeline/examples/ipsec_sa.txt       |  216 ++
 examples/pipeline/examples/l2fwd.cli          |    4 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |    4 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |    4 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |    4 +-
 examples/pipeline/examples/learner.cli        |    4 +-
 examples/pipeline/examples/meter.cli          |    4 +-
 examples/pipeline/examples/mirroring.cli      |    4 +-
 examples/pipeline/examples/recirculation.cli  |    4 +-
 examples/pipeline/examples/registers.cli      |    4 +-
 examples/pipeline/examples/selector.cli       |    4 +-
 examples/pipeline/examples/varbit.cli         |    4 +-
 examples/pipeline/examples/vxlan.cli          |    4 +-
 examples/pipeline/examples/vxlan_pcap.cli     |    4 +-
 examples/pipeline/main.c                      |   12 +-
 examples/pipeline/obj.c                       |  360 +---
 examples/pipeline/obj.h                       |  100 +-
 examples/pipeline/thread.c                    |  657 +++---
 examples/pipeline/thread.h                    |   24 +-
 lib/pipeline/meson.build                      |    4 +-
 lib/pipeline/rte_swx_ipsec.c                  | 1851 +++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h                  |  383 ++++
 lib/pipeline/version.map                      |    9 +
 29 files changed, 3772 insertions(+), 1018 deletions(-)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 01/11] pipeline: add IPsec support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-11 20:55 ` Cristian Dumitrescu
  2023-01-11 20:55 ` [PATCH 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:55 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

This block is providing IPsec support to the SWX pipeline. The IPsec
block is external to the pipeline, so it needs to be explicitly
instantiated and connected to a pipeline through the I/O ports.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 lib/pipeline/meson.build     |    4 +-
 lib/pipeline/rte_swx_ipsec.c | 1851 ++++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h |  383 +++++++
 lib/pipeline/version.map     |    9 +
 4 files changed, 2246 insertions(+), 1 deletion(-)
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build
index 3ca98ed194..aa3fd0c2b8 100644
--- a/lib/pipeline/meson.build
+++ b/lib/pipeline/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'rte_pipeline.c',
         'rte_port_in_action.c',
         'rte_table_action.c',
+	'rte_swx_ipsec.c',
         'rte_swx_pipeline.c',
         'rte_swx_pipeline_spec.c',
         'rte_swx_ctl.c',
@@ -19,8 +20,9 @@ headers = files(
         'rte_pipeline.h',
         'rte_port_in_action.h',
         'rte_table_action.h',
+	'rte_swx_ipsec.h',
         'rte_swx_pipeline.h',
         'rte_swx_extern.h',
         'rte_swx_ctl.h',
 )
-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']
+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']
diff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c
new file mode 100644
index 0000000000..7ad2ee7227
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.c
@@ -0,0 +1,1851 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_cryptodev.h>
+#include <rte_ipsec.h>
+
+#include "rte_swx_ipsec.h"
+
+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
+
+#include <rte_malloc.h>
+
+static void *
+env_calloc(size_t size, size_t alignment, int numa_node)
+{
+	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
+}
+
+static void
+env_free(void *start, size_t size __rte_unused)
+{
+	rte_free(start);
+}
+
+#else
+
+#include <numa.h>
+
+static void *
+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
+{
+	void *start;
+
+	if (numa_available() == -1)
+		return NULL;
+
+	start = numa_alloc_onnode(size, numa_node);
+	if (!start)
+		return NULL;
+
+	memset(start, 0, size);
+	return start;
+}
+
+static void
+env_free(void *start, size_t size)
+{
+	if ((numa_available() == -1) || !start)
+		return;
+
+	numa_free(start, size);
+}
+
+#endif
+
+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
+#endif
+
+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
+ * the mempool size to be at least 1.5 times the size of the mempool cache.
+ */
+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
+
+struct ipsec_sa {
+	struct rte_ipsec_session s;
+	int valid;
+};
+
+struct ipsec_pkts_in {
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_cops;
+};
+
+struct ipsec_pkts_out {
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_pkts;
+};
+
+struct rte_swx_ipsec {
+	/*
+	 * Parameters.
+	 */
+
+	/* IPsec instance name. */
+	char name[RTE_SWX_IPSEC_NAME_SIZE];
+
+	/* Input packet queue. */
+	struct rte_ring *ring_in;
+
+	/* Output packet queue. */
+	struct rte_ring *ring_out;
+
+	/* Crypto device ID. */
+	uint8_t dev_id;
+
+	/* Crypto device queue pair ID. */
+	uint16_t qp_id;
+
+	/* Burst sizes. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/* SA table size. */
+	size_t n_sa_max;
+
+	/*
+	 * Internals.
+	 */
+	/* Crypto device buffer pool for sessions. */
+	struct rte_mempool *mp_session;
+
+	/* Crypto device bufer pool for session private data. */
+	struct rte_mempool *mp_session_priv;
+
+	/* Pre-crypto packets. */
+	struct ipsec_pkts_in in;
+
+	/* Post-crypto packets. */
+	struct ipsec_pkts_out out;
+
+	/* Crypto device enqueue threshold. */
+	uint32_t crypto_wr_threshold;
+
+	/* Packets currently under crypto device processing. */
+	uint32_t n_pkts_crypto;
+
+	/* List of free SADB positions. */
+	uint32_t *sa_free_id;
+
+	/* Number of elements in the SADB list of free positions. */
+	size_t n_sa_free_id;
+
+	/* Allocated memory total size in bytes. */
+	size_t total_size;
+
+	/* Flag for registration to the global list of instances. */
+	int registered;
+
+	/*
+	 * Table memory.
+	 */
+	uint8_t memory[] __rte_cache_aligned;
+};
+
+static inline struct ipsec_sa *
+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
+{
+	struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
+
+	return &sadb[sa_id & (ipsec->n_sa_max - 1)];
+}
+
+/* Global list of instances. */
+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ipsec_tailq = {
+	.name = "RTE_SWX_IPSEC",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
+
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name ||
+	    !name[0] ||
+	    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
+		return NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ipsec;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ipsec_register(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ipsec;
+	TAILQ_INSERT_TAIL(ipsec_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ipsec_unregister(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		if (te->data == (void *)ipsec) {
+			TAILQ_REMOVE(ipsec_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
+
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
+{
+	size_t i;
+
+	if (!ipsec)
+		return;
+
+	/* Remove the current instance from the global list. */
+	if (ipsec->registered)
+		ipsec_unregister(ipsec);
+
+	/* SADB. */
+	for (i = 0; i < ipsec->n_sa_max; i++) {
+		struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
+
+		if (!sa->valid)
+			continue;
+
+		/* SA session. */
+		ipsec_session_free(ipsec, &sa->s);
+	}
+
+	/* Crypto device buffer pools. */
+	rte_mempool_free(ipsec->mp_session);
+	rte_mempool_free(ipsec->mp_session_priv);
+
+	/* IPsec object memory. */
+	env_free(ipsec, ipsec->total_size);
+}
+
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node)
+{
+	char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
+	struct rte_swx_ipsec *ipsec = NULL;
+	struct rte_ring *ring_in, *ring_out;
+	struct rte_cryptodev_info dev_info;
+	size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
+	int dev_id, status = 0;
+
+	/* Check input parameters. */
+	if (!ipsec_out ||
+	    !name ||
+	    !name[0] ||
+	    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
+	    !params ||
+	    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    !params->n_sa_max) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_in = rte_ring_lookup(params->ring_in_name);
+	if (!ring_in) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_out = rte_ring_lookup(params->ring_out_name);
+	if (!ring_out) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
+	if (dev_id == -1) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Memory allocation. */
+	n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
+
+	sadb_offset = sizeof(struct rte_swx_ipsec);
+	sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
+
+	sa_free_id_offset = sadb_offset + sadb_size;
+	sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
+
+	total_size = sa_free_id_offset + sa_free_id_size;
+	ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
+	if (!ipsec) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Initialization. */
+	strcpy(ipsec->name, name);
+	ipsec->ring_in = ring_in;
+	ipsec->ring_out = ring_out;
+	ipsec->dev_id = (uint8_t)dev_id;
+	ipsec->qp_id = params->crypto_dev_queue_pair_id;
+	memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
+	ipsec->n_sa_max = n_sa_max;
+
+	ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
+
+	ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
+	for (i = 0; i < n_sa_max; i++)
+		ipsec->sa_free_id[i] = n_sa_max - 1 - i;
+	ipsec->n_sa_free_id = n_sa_max;
+
+	ipsec->total_size = total_size;
+
+	/* Crypto device memory pools. */
+	snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
+	ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		0, /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool element private data size */
+		numa_node);
+	if (!ipsec->mp_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	snprintf(resource_name, sizeof(resource_name), "%s_mp_priv", name);
+	ipsec->mp_session_priv = rte_mempool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		rte_cryptodev_sym_get_private_session_size(dev_id), /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool private data size */
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		numa_node,
+		0); /* pool flags */
+	if (!ipsec->mp_session_priv) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Add the current instance to the global list. */
+	status = ipsec_register(ipsec);
+	if (status)
+		goto error;
+
+	ipsec->registered = 1;
+
+	*ipsec_out = ipsec;
+	return 0;
+
+error:
+	rte_swx_ipsec_free(ipsec);
+	return status;
+}
+
+static inline int
+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
+{
+	struct ipsec_sa *sa;
+	struct rte_ipsec_group *g;
+	int n_groups, n_pkts_in_group, i;
+
+	sa = ipsec->in.sa[0];
+
+	g = &ipsec->in.groups[0];
+	g->id.ptr = sa;
+	g->m = &ipsec->in.pkts[0];
+	n_pkts_in_group = 1;
+	n_groups = 1;
+
+	for (i = 1; i < n_pkts; i++) {
+		struct ipsec_sa *sa_new = ipsec->in.sa[i];
+
+		/* Same SA => Add the current pkt to the same group. */
+		if (sa_new == sa) {
+			n_pkts_in_group++;
+			continue;
+		}
+
+		/* Different SA => Close the current group & add the current pkt to a new group. */
+		g->cnt = n_pkts_in_group;
+		sa = sa_new;
+
+		g++;
+		g->id.ptr = sa;
+		g->m = &ipsec->in.pkts[i];
+		n_pkts_in_group = 1;
+		n_groups++;
+	}
+
+	/* Close the last group. */
+	g->cnt = n_pkts_in_group;
+
+	return n_groups;
+}
+
+static inline void
+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
+{
+	struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
+	struct rte_crypto_op **src = ipsec->in.group_cops;
+
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
+	uint32_t n_dst = ipsec->in.n_cops;
+	uint32_t n_dst_max = ipsec->bsz.crypto_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_cops;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
+	 * instead of moving the SRC to DST first and then using DST.
+	 */
+	if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
+		uint16_t n_ok;
+
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
+		ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
+
+		for (i = n_ok; i < n_src; i++) {
+			struct rte_crypto_op *cop = src[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		return;
+	}
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
+		n_pkts_crypto += n_ok;
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_crypto_op *cop = dst0[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	ipsec->n_pkts_crypto = n_pkts_crypto;
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->in.n_cops = n_dst;
+}
+
+/**
+ * Packet buffer anatomy:
+ *
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | Offset   | Size    | Description                                                              |
+ * | (Byte #) | (Bytes) |                                                                          |
+ * +==========+=========+==========================================================================+
+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |
+ * |          |         | The buf_addr field points to the start of the packet section.            |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 256      |         | Packet section.                                                          |
+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |
+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |
+ * +----------+---------+--------------------------------------------------------------------------+
+ */
+struct ipsec_mbuf {
+	struct ipsec_sa *sa;
+	struct rte_crypto_op cop;
+	struct rte_crypto_sym_op sym_cop;
+	uint8_t buffer[32]; /* The crypto IV is placed here. */
+};
+
+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
+
+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
+
+static inline void
+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
+{
+	int n_pkts, n_groups, i;
+
+	/* Read packets from the input ring. */
+	n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
+					   (void **)ipsec->in.pkts,
+					   ipsec->bsz.ring_rd,
+					   NULL);
+	if (!n_pkts)
+		return;
+
+	/* Get the SA for each packet. */
+	for (i = 0; i < n_pkts; i++) {
+		struct rte_mbuf *m = ipsec->in.pkts[i];
+		struct rte_swx_ipsec_input_packet_metadata *meta;
+		struct rte_ipv4_hdr *ipv4_hdr;
+		uint32_t sa_id;
+
+		meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
+
+		/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
+		sa_id = ntohl(meta->sa_id);
+
+		/* Consume the IPsec meta-data. */
+		m->data_off += META_LENGTH;
+		m->data_len -= META_LENGTH;
+		m->pkt_len -= META_LENGTH;
+
+		/* Set the fields required by the IPsec library. */
+		m->l2_len = 0;
+		m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
+			sizeof(struct rte_ipv4_hdr) :
+			sizeof(struct rte_ipv6_hdr);
+
+		/* Get the SA. */
+		ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
+	}
+
+	/* Group packets that share the same SA. */
+	n_groups = ipsec_sa_group(ipsec, n_pkts);
+
+	/* Write each group of packets sharing the same SA to the crypto device. */
+	for (i = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->in.groups[i];
+		struct ipsec_sa *sa = g->id.ptr;
+		struct rte_ipsec_session *s = &sa->s;
+		uint32_t j;
+		uint16_t n_pkts_ok;
+
+		/* Prepare the crypto ops for the current group. */
+		for (j = 0; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+			struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
+
+			priv->sa = sa;
+			ipsec->in.group_cops[j] = &priv->cop;
+		}
+
+		n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the crypto ops of the current group to the crypto device. */
+		ipsec_crypto_enqueue(ipsec, n_pkts_ok);
+	}
+}
+
+static inline void
+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
+{
+	struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
+	struct rte_mbuf **src = g->m;
+
+	uint32_t n_dst = ipsec->out.n_pkts;
+	uint32_t n_dst_max = ipsec->bsz.ring_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_pkts;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_mbuf *m = dst[i];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->out.n_pkts = n_dst;
+}
+
+static inline void
+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
+{
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, n_groups, i;
+
+	/* Read the crypto ops from the crypto device. */
+	if (!n_pkts_crypto)
+		return;
+
+	n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
+					     ipsec->qp_id,
+					     ipsec->out.cops,
+					     ipsec->bsz.crypto_rd);
+	if (!n_pkts)
+		return;
+
+	ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
+
+	/* Group packets that share the same SA. */
+	n_groups = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
+					      ipsec->out.group_pkts,
+					      ipsec->out.groups,
+					      n_pkts);
+
+	/* Peform post-crypto IPsec processing for each group of packets that share the same SA.
+	 * Write each group of packets to the output ring.
+	 */
+	for (i = 0, n_pkts = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->out.groups[i];
+		struct rte_ipsec_session *s = g->id.ptr;
+		uint32_t n_pkts_ok, j;
+
+		/* Perform post-crypto IPsec processing for the current group. */
+		n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the packets of the current group to the output ring. */
+		ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
+	}
+}
+
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
+{
+	rte_swx_ipsec_pre_crypto(ipsec);
+	rte_swx_ipsec_post_crypto(ipsec);
+}
+
+/**
+ * IPsec Control Plane API
+ */
+struct cipher_alg {
+	const char *name;
+	enum rte_crypto_cipher_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t key_size;
+};
+
+struct auth_alg {
+	const char *name;
+	enum rte_crypto_auth_algorithm alg;
+	uint32_t iv_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+};
+
+struct aead_alg {
+	const char *name;
+	enum rte_crypto_aead_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+	uint32_t aad_size;
+};
+
+static struct cipher_alg cipher_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_CIPHER_NULL,
+		.iv_size = 0,
+		.block_size = 4,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "aes-cbc-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 16,
+	},
+
+	[2] = {
+		.name = "aes-cbc-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 24,
+	},
+
+	[3] = {
+		.name = "aes-cbc-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 32,
+	},
+
+	[4] = {
+		.name = "aes-ctr-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+	},
+
+	[5] = {
+		.name = "aes-ctr-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 28,
+	},
+
+	[6] = {
+		.name = "aes-ctr-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 36,
+	},
+
+	[7] = {
+		.name = "3des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 24,
+	},
+
+	[8] = {
+		.name = "des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 8,
+	},
+};
+
+static struct auth_alg auth_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_AUTH_NULL,
+		.iv_size = 0,
+		.digest_size = 0,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "sha1-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 20,
+	},
+
+	[2] = {
+		.name = "sha256-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
+		.iv_size = 0,
+		.digest_size = 16,
+		.key_size = 32,
+	},
+
+	[3] = {
+		.name = "sha384-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
+		.iv_size = 0,
+		.digest_size = 24,
+		.key_size = 48,
+	},
+
+	[4] = {
+		.name = "sha512-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
+		.iv_size = 0,
+		.digest_size = 32,
+		.key_size = 64,
+	},
+
+	[5] = {
+		.name = "aes-gmac",
+		.alg = RTE_CRYPTO_AUTH_AES_GMAC,
+		.iv_size = 8,
+		.digest_size = 16,
+		.key_size = 20,
+	},
+
+	[6] = {
+		.name = "aes-xcbc-mac-96",
+		.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 16,
+	},
+};
+
+static struct aead_alg aead_algs[] = {
+	[0] = {
+		.name = "aes-gcm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[1] = {
+		.name = "aes-gcm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[2] = {
+		.name = "aes-gcm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[3] = {
+		.name = "aes-ccm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[4] = {
+		.name = "aes-ccm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[5] = {
+		.name = "aes-ccm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[6] = {
+		.name = "chacha20-poly1305",
+		.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
+		.iv_size = 12,
+		.block_size = 64,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+};
+
+static struct cipher_alg *
+cipher_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct cipher_alg *
+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static int
+char_to_hex(char c, uint8_t *val)
+{
+	if (c >= '0' && c <= '9') {
+		*val = c - '0';
+		return 0;
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		*val = c - 'A' + 10;
+		return 0;
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		*val = c - 'a' + 10;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
+{
+	uint32_t i;
+
+	/* Check input arguments. */
+	if (!src || !src[0] || !dst || !n_dst_bytes)
+		return -EINVAL;
+
+	/* Skip any leading "0x" or "0X" in the src string. */
+	if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
+		src += 2;
+
+	/* Convert each group of two hex characters in the src string to one byte in dst array. */
+	for (i = 0; i < n_dst_bytes; i++) {
+		uint8_t a, b;
+		int status;
+
+		status = char_to_hex(*src, &a);
+		if (status)
+			return status;
+		src++;
+
+		status = char_to_hex(*src, &b);
+		if (status)
+			return status;
+		src++;
+
+		dst[i] = a * 16 + b;
+	}
+
+	/* Check for the end of the src string. */
+	if (*src)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+token_is_comment(const char *token)
+{
+	if ((token[0] == '#') ||
+	    (token[0] == ';') ||
+	    ((token[0] == '/') && (token[1] == '/')))
+		return 1; /* TRUE. */
+
+	return 0; /* FALSE. */
+}
+
+#define MAX_TOKENS 64
+
+#define CHECK(condition, msg)          \
+do {                                   \
+	if (!(condition)) {            \
+		if (errmsg)            \
+			*errmsg = msg; \
+		goto error;            \
+	}                              \
+} while (0)
+
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg)
+{
+	char *token_array[MAX_TOKENS], **t;
+	struct rte_swx_ipsec_sa_params *p = NULL;
+	char *s0 = NULL, *s;
+	uint32_t n_tokens = 0;
+	int blank_or_comment = 0;
+
+	/* Check input arguments. */
+	CHECK(string && string[0], "NULL input");
+
+	/* Memory allocation. */
+	s0 = strdup(string);
+	p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
+	CHECK(s0 && p, "Not enough memory");
+
+	/* Parse the string into tokens. */
+	for (s = s0; ; ) {
+		char *token;
+
+		token = strtok_r(s, " \f\n\r\t\v", &s);
+		if (!token || token_is_comment(token))
+			break;
+
+		CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
+
+		token_array[n_tokens] = token;
+		n_tokens++;
+	}
+
+	t = token_array;
+	if (!n_tokens) {
+		blank_or_comment = 1;
+		goto error;
+	}
+
+	/*
+	 * Crypto operation.
+	 */
+	if (!strcmp(t[0], "encrypt"))
+		p->encrypt = 1;
+	else if (!strcmp(t[0], "decrypt"))
+		p->encrypt = 0;
+	else
+		CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
+
+	t++;
+	n_tokens--;
+
+	/*
+	 * Crypto parameters.
+	 */
+	CHECK(n_tokens >= 2, "Not enough tokens");
+
+	if (!strcmp(t[0], "cipher")) {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t key_size;
+
+		p->crypto.is_aead = 0;
+
+		/* cipher. */
+		cipher_alg = cipher_alg_find(t[1]);
+		CHECK(cipher_alg, "Unsupported cipher algorithm");
+
+		key_size = cipher_alg->key_size;
+		p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
+		p->crypto.cipher_auth.cipher.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
+				"Cipher algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
+			CHECK(!status, "Cipher key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+
+		/* authentication. */
+		CHECK(n_tokens >= 2, "Not enough tokens");
+		CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
+
+		auth_alg = auth_alg_find(t[1]);
+		CHECK(auth_alg, "Unsupported authentication algorithm");
+
+		key_size = auth_alg->key_size;
+		p->crypto.cipher_auth.auth.alg = auth_alg->alg;
+		p->crypto.cipher_auth.auth.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
+				"Authentication algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
+			CHECK(!status, "Authentication key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+	} else if (!strcmp(t[0], "aead")) {
+		struct aead_alg *alg;
+		uint32_t key_size;
+		int status;
+
+		p->crypto.is_aead = 1;
+
+		CHECK(n_tokens >= 4, "Not enough tokens");
+		alg = aead_alg_find(t[1]);
+		CHECK(alg, "Unsupported AEAD algorithm");
+
+		key_size = alg->key_size;
+		p->crypto.aead.alg = alg->alg;
+		p->crypto.aead.key_size = key_size;
+
+		CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
+		CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
+			"AEAD algorithm key too big");
+
+		status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
+		CHECK(!status, "AEAD key invalid format");
+
+		t += 4;
+		n_tokens -= 4;
+	} else
+		CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
+
+	/*
+	 * Packet ecapsulation parameters.
+	 */
+	CHECK(n_tokens >= 4, "Not enough tokens");
+	CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
+	CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
+
+	p->encap.esp.spi = strtoul(t[2], &t[2], 0);
+	CHECK(!t[2][0], "ESP SPI field invalid format");
+
+	t += 3;
+	n_tokens -= 3;
+
+	if (!strcmp(t[0], "tunnel")) {
+		p->encap.tunnel_mode = 1;
+
+		CHECK(n_tokens >= 6, "Not enough tokens");
+
+		if (!strcmp(t[1], "ipv4")) {
+			uint32_t addr;
+
+			p->encap.tunnel_ipv4 = 1;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			addr = strtoul(t[3], &t[3], 0);
+			CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
+			p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			addr = strtoul(t[5], &t[5], 0);
+			CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
+			p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
+
+			t += 6;
+			n_tokens -= 6;
+		} else if (!strcmp(t[1], "ipv6")) {
+			int status;
+
+			p->encap.tunnel_ipv4 = 0;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			status = hex_string_parse(t[3],
+						  p->encap.tunnel.ipv6.src_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 source address invalid format");
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			status = hex_string_parse(t[5],
+						  p->encap.tunnel.ipv6.dst_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 destination address invalid format");
+
+			t += 6;
+			n_tokens -= 6;
+		} else
+			CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
+	} else if (!strcmp(t[0], "transport")) {
+		p->encap.tunnel_mode = 0;
+
+		t++;
+		n_tokens--;
+	} else
+		CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
+
+	/*
+	 * Any other parameters.
+	 */
+	CHECK(!n_tokens, "Unexpected trailing tokens");
+
+	free(s0);
+	return p;
+
+error:
+	free(p);
+	free(s0);
+	if (is_blank_or_comment)
+		*is_blank_or_comment = blank_or_comment;
+	return NULL;
+}
+
+static void
+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv4_hdr ipv4_hdr = {
+		.version_ihl = 0x45,
+		.type_of_service = 0,
+		.total_length = 0, /* Cannot be pre-computed. */
+		.packet_id = 0,
+		.fragment_offset = 0,
+		.time_to_live = 64,
+		.next_proto_id = IPPROTO_ESP,
+		.hdr_checksum = 0, /* Cannot be pre-computed. */
+		.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
+		.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
+	};
+
+	memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
+}
+
+static void
+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv6_hdr ipv6_hdr = {
+		.vtc_flow = 0x60000000,
+		.payload_len = 0, /* Cannot be pre-computed. */
+		.proto = IPPROTO_ESP,
+		.hop_limits = 64,
+		.src_addr = {0},
+		.dst_addr = {0},
+	};
+
+	memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
+	memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
+	memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
+}
+
+/* IPsec library SA parameters. */
+static struct rte_crypto_sym_xform *
+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_crypto_sym_xform *xform,
+		uint32_t *salt_out)
+{
+	if (p->crypto.is_aead) {
+		struct aead_alg *alg;
+		uint32_t key_size, salt, iv_length;
+
+		alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
+		if (!alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		key_size = p->crypto.aead.key_size - 4;
+		memcpy(&salt, &p->crypto.aead.key[key_size], 4);
+
+		/* IV length. */
+		iv_length = 12;
+		if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
+			iv_length = 11 ;
+
+		/* xform. */
+		xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform[0].aead.op = p->encrypt ?
+			RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform[0].aead.algo = p->crypto.aead.alg;
+		xform[0].aead.key.data = p->crypto.aead.key;
+		xform[0].aead.key.length = key_size;
+		xform[0].aead.iv.offset = IV_OFFSET;
+		xform[0].aead.iv.length = iv_length;
+		xform[0].aead.digest_length = alg->digest_size;
+		xform[0].aead.aad_length = alg->aad_size;
+		xform[0].next = NULL;
+
+		*salt_out = salt;
+		return &xform[0];
+	} else {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
+
+		cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
+						   p->crypto.cipher_auth.cipher.key_size);
+		if (!cipher_alg)
+			return NULL;
+
+		auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
+					       p->crypto.cipher_auth.auth.key_size);
+		if (!auth_alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
+		auth_key_size = p->crypto.cipher_auth.auth.key_size;
+
+		switch (p->crypto.cipher_auth.cipher.alg) {
+		case RTE_CRYPTO_CIPHER_AES_CBC:
+		case RTE_CRYPTO_CIPHER_3DES_CBC:
+			salt = (uint32_t)rand();
+			break;
+
+		case RTE_CRYPTO_CIPHER_AES_CTR:
+			cipher_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
+			break;
+
+		default:
+			salt = 0;
+		}
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			auth_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
+		}
+
+		/* IV length. */
+		auth_iv_length = cipher_alg->iv_size;
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
+			auth_iv_length = 12;
+
+		/* xform. */
+		if (p->encrypt) {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[0].cipher.key.length = cipher_key_size;
+			xform[0].cipher.iv.offset = IV_OFFSET;
+			xform[0].cipher.iv.length = cipher_alg->iv_size;
+			xform[0].cipher.dataunit_len = 0;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+			xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[1].auth.key.length = auth_key_size;
+			xform[1].auth.iv.offset = IV_OFFSET;
+			xform[1].auth.iv.length = auth_iv_length;
+			xform[1].auth.digest_length = auth_alg->digest_size;
+			xform[1].next = NULL;
+		} else {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+			xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[0].auth.key.length = auth_key_size;
+			xform[0].auth.iv.offset = IV_OFFSET;
+			xform[0].auth.iv.length = auth_iv_length;
+			xform[0].auth.digest_length = auth_alg->digest_size;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[1].cipher.key.length = cipher_key_size;
+			xform[1].cipher.iv.offset = IV_OFFSET;
+			xform[1].cipher.iv.length = cipher_alg->iv_size;
+			xform[1].cipher.dataunit_len = 0;
+			xform[1].next = NULL;
+		}
+
+		*salt_out = salt;
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			if (p->encrypt)
+				return &xform[1];
+
+			xform[0].next = NULL;
+			return &xform[0];
+		}
+
+		return &xform[0];
+	}
+}
+
+static void
+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_security_ipsec_xform *ipsec_xform,
+		uint32_t salt)
+{
+	ipsec_xform->spi = p->encap.esp.spi;
+
+	ipsec_xform->salt = salt;
+
+	ipsec_xform->options.esn = 0;
+	ipsec_xform->options.udp_encap = 0;
+	ipsec_xform->options.copy_dscp = 1;
+	ipsec_xform->options.copy_flabel = 0;
+	ipsec_xform->options.copy_df = 0;
+	ipsec_xform->options.dec_ttl = 0;
+	ipsec_xform->options.ecn = 1;
+	ipsec_xform->options.stats = 0;
+	ipsec_xform->options.iv_gen_disable = 0;
+	ipsec_xform->options.tunnel_hdr_verify = 0;
+	ipsec_xform->options.udp_ports_verify = 0;
+	ipsec_xform->options.ip_csum_enable = 0;
+	ipsec_xform->options.l4_csum_enable = 0;
+	ipsec_xform->options.ip_reassembly_en = 0;
+	ipsec_xform->options.reserved_opts = 0;
+
+	ipsec_xform->direction = p->encrypt ?
+		RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
+		RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+
+	ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+
+	ipsec_xform->mode = p->encap.tunnel_mode ?
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
+
+	ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
+		RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
+		RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
+			ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
+			ipsec_xform->tunnel.ipv4.dscp = 0;
+			ipsec_xform->tunnel.ipv4.df = 0;
+			ipsec_xform->tunnel.ipv4.ttl = 64;
+		} else {
+			ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
+			ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
+			ipsec_xform->tunnel.ipv6.dscp = 0;
+			ipsec_xform->tunnel.ipv6.flabel = 0;
+			ipsec_xform->tunnel.ipv6.hlimit = 64;
+		}
+	}
+
+	ipsec_xform->life.packets_soft_limit = 0;
+	ipsec_xform->life.bytes_soft_limit = 0;
+	ipsec_xform->life.packets_hard_limit = 0;
+	ipsec_xform->life.bytes_hard_limit = 0;
+
+	ipsec_xform->replay_win_sz = 0;
+
+	ipsec_xform->esn.value = 0;
+
+	ipsec_xform->udp.dport = 0;
+	ipsec_xform->udp.sport = 0;
+}
+
+static int
+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
+		 struct rte_ipsec_sa_prm *sa_prm,
+		 struct rte_ipv4_hdr *ipv4_hdr,
+		 struct rte_ipv6_hdr *ipv6_hdr,
+		 struct rte_crypto_sym_xform *crypto_xform)
+{
+	uint32_t salt;
+
+	memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
+
+	sa_prm->userdata = 0; /* Not used. */
+
+	sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
+
+	/*
+	 * crypto_xform.
+	 */
+	sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
+	if (!sa_prm->crypto_xform)
+		return -EINVAL;
+
+	/*
+	 * ipsec_xform.
+	 */
+	ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
+
+	/*
+	 * tunnel / transport.
+	 *
+	 * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
+	 * the input packet type should be added to the SA configuration parameters.
+	 */
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* Assume input IP packet type as IPv4. */
+			sa_prm->tun.hdr = ipv4_hdr;
+		} else {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* Assume input IP packet type as IPv4. */
+			sa_prm->tun.hdr = ipv6_hdr;
+		}
+	} else {
+		sa_prm->trs.proto = IPPROTO_IPIP; /* Assume input IP packet type as IPv4. */
+	}
+
+	return 0;
+}
+
+static int
+ipsec_session_create(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *p,
+		     struct rte_ipsec_session *s)
+{
+	struct rte_ipv4_hdr ipv4_hdr;
+	struct rte_ipv6_hdr ipv6_hdr;
+	struct rte_crypto_sym_xform crypto_xform[2];
+	struct rte_ipsec_sa_prm sa_prm;
+	struct rte_ipsec_sa *sa = NULL;
+	struct rte_cryptodev_sym_session *crypto_session = NULL;
+	int sa_size;
+	int sa_valid = 0, crypto_session_valid = 0, status = 0;
+
+	tunnel_ipv4_header_set(&ipv4_hdr, p);
+	tunnel_ipv6_header_set(&ipv6_hdr, p);
+
+	/* IPsec library SA setup. */
+	status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
+	if (status)
+		goto error;
+
+	sa_size = rte_ipsec_sa_size(&sa_prm);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa = calloc(1, sa_size);
+	if (!sa) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa_valid = 1;
+
+	/* Cryptodev library session setup. */
+	crypto_session = rte_cryptodev_sym_session_create(ipsec->mp_session);
+	if (!crypto_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	status = rte_cryptodev_sym_session_init(ipsec->dev_id,
+						crypto_session,
+						sa_prm.crypto_xform,
+						ipsec->mp_session_priv);
+	if (status)
+		goto error;
+
+	crypto_session_valid = 1;
+
+	/* IPsec library session setup. */
+	s->sa = sa;
+	s->type = RTE_SECURITY_ACTION_TYPE_NONE;
+	s->crypto.ses = crypto_session;
+	s->crypto.dev_id = ipsec->dev_id;
+	s->pkt_func.prepare.async = NULL;
+	s->pkt_func.process = NULL;
+
+	return rte_ipsec_session_prepare(s);
+
+error:
+	/* sa. */
+	if (sa_valid)
+		rte_ipsec_sa_fini(sa);
+
+	free(sa);
+
+	/* crypto_session. */
+	if (crypto_session_valid)
+		rte_cryptodev_sym_session_clear(ipsec->dev_id, crypto_session);
+
+	if (crypto_session)
+		rte_cryptodev_sym_session_free(crypto_session);
+
+	/* s. */
+	memset(s, 0, sizeof(*s));
+
+	return status;
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec,
+		   struct rte_ipsec_session *s)
+{
+	if (!s)
+		return;
+
+	/* IPsec library SA. */
+	if (s->sa)
+		rte_ipsec_sa_fini(s->sa);
+	free(s->sa);
+
+	/* Cryptodev library session. */
+	if (s->crypto.ses) {
+		rte_cryptodev_sym_session_clear(ipsec->dev_id, s->crypto.ses);
+		rte_cryptodev_sym_session_free(s->crypto.ses);
+	}
+
+	/* IPsec library session. */
+	memset(s, 0, sizeof(*s));
+}
+
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *id)
+{
+	struct ipsec_sa *sa;
+	uint32_t sa_id;
+	int status;
+
+	/* Check the input parameters. */
+	if (!ipsec || !sa_params || !id)
+		return -EINVAL;
+
+	/* Allocate a free SADB entry. */
+	if (!ipsec->n_sa_free_id)
+		return -ENOSPC;
+
+	sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
+	ipsec->n_sa_free_id--;
+
+	/* Aquire the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	status = ipsec_session_create(ipsec, sa_params, &sa->s);
+	if (status) {
+		/* Free the allocated SADB entry. */
+		ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+		ipsec->n_sa_free_id++;
+
+		return status;
+	}
+
+	/* Validate the new SA. */
+	sa->valid = 1;
+	*id = sa_id;
+
+	return 0;
+}
+
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id)
+{
+	struct ipsec_sa *sa;
+
+	/* Check the input parameters. */
+	if (!ipsec || (sa_id >= ipsec->n_sa_max))
+		return;
+
+	/* Release the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	ipsec_session_free(ipsec, &sa->s);
+
+	/* Free the SADB entry. */
+	ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+	ipsec->n_sa_free_id++;
+
+	/* Invalidate the SA. */
+	sa->valid = 0;
+}
diff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h
new file mode 100644
index 0000000000..c2c638a5d7
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__
+#define __INCLUDE_RTE_SWX_IPSEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE SWX Internet Protocol Security (IPsec)
+ *
+ * This block is used to provide IPsec support to the SWX pipeline. The block is external to the
+ * pipeline, hence it needs to be explicitly instantiated by the user and connected to a pipeline
+ * instance using the pipeline I/O ports.
+ *
+ * Main features:
+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text
+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.
+ *
+ * Security Association (SA):
+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is
+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.
+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then
+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field
+ *   is typically written by the pipeline before sending the packet to the IPsec block.
+ *
+ * Packet format:
+ * - IPsec block input packet (i.e. pipeline output packet):
+ * 	- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.
+ * 	- IPv4 header.
+ * 	- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.
+ * - IPsec block output packet (i.e. pipeline input packet):
+ * 	- IPv4 header.
+ * 	- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.
+ *
+ * SA update procedure:
+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().
+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().
+ * - To update an existing SA, the control plane has to follow the following steps:
+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can
+ *      fail, for example when the SA table is full.
+ *   2. Wait until no more packets are using the old SA.
+ *   3. Delete the old SA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <rte_compat.h>
+#include <rte_crypto_sym.h>
+
+/**
+ * IPsec Setup API
+ */
+
+/** IPsec instance opaque data structure. */
+struct rte_swx_ipsec;
+
+/** Name size. */
+#ifndef RTE_SWX_IPSEC_NAME_SIZE
+#define RTE_SWX_IPSEC_NAME_SIZE 64
+#endif
+
+/** Maximum burst size. */
+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX
+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256
+#endif
+
+/** IPsec burst sizes. */
+struct rte_swx_ipsec_burst_size {
+	/** Input ring read burst size. */
+	uint32_t ring_rd;
+
+	/** Output ring write burst size. */
+	uint32_t ring_wr;
+
+	/** Crypto device request queue write burst size. */
+	uint32_t crypto_wr;
+
+	/** Crypto device response queue read burst size. */
+	uint32_t crypto_rd;
+};
+
+/**
+ * IPsec instance configuration parameters
+ *
+ */
+struct rte_swx_ipsec_params {
+	/** Input packet queue. */
+	const char *ring_in_name;
+
+	/** Output packet queue.  */
+	const char *ring_out_name;
+
+	/** Crypto device name. */
+	const char *crypto_dev_name;
+
+	/** Crypto device queue pair ID. */
+	uint32_t crypto_dev_queue_pair_id;
+
+	/** Burst size. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/** Maximum number of SAs. */
+	uint32_t n_sa_max;
+};
+
+/**
+ * IPsec input packet meta-data
+ *
+ */
+struct rte_swx_ipsec_input_packet_metadata {
+	/* SA ID. */
+	uint32_t sa_id;
+};
+
+/**
+ * IPsec instance find
+ *
+ * @param[in] name
+ *   IPsec instance name.
+ * @return
+ *   Valid IPsec instance handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name);
+
+/**
+ * IPsec instance create
+ *
+ * @param[out] ipsec
+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this
+ *   function returns successfully.
+ * @param[in] name
+ *   IPsec instance unique name.
+ * @param[in] params
+ *   IPsec instance configuration parameters.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node);
+
+/**
+ * IPsec instance free
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);
+
+/**
+ * IPsec Data Plane API
+ */
+
+/**
+ * IPsec instance run
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);
+
+/*
+ * IPsec Control Plane API
+ */
+
+/** Maximum key size in bytes. */
+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64
+
+/** IPsec SA crypto cipher parameters. */
+struct rte_swx_ipsec_sa_cipher_params {
+	/** Cipher algorithm. */
+	enum rte_crypto_cipher_algorithm alg;
+
+	/** Cipher key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Cipher key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto authentication parameters. */
+struct rte_swx_ipsec_sa_authentication_params {
+	/** Authentication algorithm. */
+	enum rte_crypto_auth_algorithm alg;
+
+	/** Authentication key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Authentication key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */
+struct rte_swx_ipsec_sa_aead_params {
+	/** AEAD algorithm. */
+	enum rte_crypto_aead_algorithm alg;
+
+	/** AEAD key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** AEAD key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec protocol encapsulation parameters. */
+struct rte_swx_ipsec_sa_encap_params {
+	/** Encapsulating Security Payload (ESP) header. */
+	struct {
+		/** Security Parameters Index (SPI) field. */
+		uint32_t spi;
+	} esp;
+
+	/** Tunnel mode when non-zero, transport mode when zero. */
+	int tunnel_mode;
+
+	/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */
+	int tunnel_ipv4;
+
+	/** Tunnel parameters. Valid for tunnel mode only. */
+	union {
+		/** IPv4 header. */
+		struct {
+			/** Source address. */
+			struct in_addr src_addr;
+
+			/** Destination address. */
+			struct in_addr dst_addr;
+		} ipv4;
+
+		/** IPv6 header. */
+		struct {
+			/** Source address. */
+			struct in6_addr src_addr;
+
+			/** Destination address. */
+			struct in6_addr dst_addr;
+		} ipv6;
+	} tunnel;
+};
+
+/** IPsec Security Association (SA) parameters. */
+struct rte_swx_ipsec_sa_params {
+	/** Crypto operation: encrypt when non-zero, decrypt when zero. */
+	int encrypt;
+
+	/** Crypto operation parameters. */
+	struct {
+		RTE_STD_C11
+		union {
+			struct {
+				/** Crypto cipher operation parameters. */
+				struct rte_swx_ipsec_sa_cipher_params cipher;
+
+				/** Crypto authentication operation parameters. */
+				struct rte_swx_ipsec_sa_authentication_params auth;
+			} cipher_auth;
+
+			/** Crypto AEAD operation parameters. */
+			struct rte_swx_ipsec_sa_aead_params aead;
+		};
+
+		/** Non-zero for AEAD, zero for cipher & authentication. */
+		int is_aead;
+	} crypto;
+
+	/** Packet encasulation parameters. */
+	struct rte_swx_ipsec_sa_encap_params encap;
+};
+
+/**
+ * IPsec SA add
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_params
+ *   SA parameters.
+ * @params[out] sa_id.
+ *   On success, the SA ID.
+ * @return
+ *   0 on success or error code otherwise.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *sa_id);
+
+/**
+ * IPsec SA delete
+ *
+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used
+ * by the Data Plane.
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @params[in] sa_id.
+ *   The SA ID.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id);
+
+/**
+ * IPsec SA read from string
+ *
+ * IPsec SA syntax:
+ *
+ * <sa>
+ *    : encrypt <crypto_params> <encap_params>
+ *    | decrypt <crypto_params> <encap_params>
+ *    ;
+ *
+ * <crypto_params>
+ *    : <cipher> <auth>
+ *    | <aead>
+ *    ;
+ *
+ * <cipher>
+ *    : cipher <ciher_alg> key <cipher_key>
+ *    | cipher <cipher_alg>
+ *    ;
+ *
+ * <auth>
+ *    : auth <authentication_alg> key <authentication_key>
+ *    | auth <authentication_alg>
+ *    ;
+ *
+ * <aead>
+ *    : aead <aead_alg> key <aead_key>
+ *    ;
+ *
+ * <encap_params>
+ *    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+ *    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+ *    | esp spi <spi> transport
+ *    ;
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] string
+ *   String containing the SA.
+ * @param[inout] is_blank_or_comment
+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when
+ *   *string* contains a blank or comment line and to zero otherwise.
+ * @param[inout] errmsg
+ *   On error, when its input value is not NULL, this argument points to a string with details on
+ *   the detected error.
+ * @return
+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.
+ */
+__rte_experimental
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 184f45a6b7..58e34459c3 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -155,4 +155,13 @@ EXPERIMENTAL {
 	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
+
+	#added in 23.03
+	rte_swx_ipsec_create;
+	rte_swx_ipsec_find;
+	rte_swx_ipsec_free;
+	rte_swx_ipsec_run;
+	rte_swx_ipsec_sa_add;
+	rte_swx_ipsec_sa_delete;
+	rte_swx_ipsec_sa_read;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 02/11] examples/pipeline: rework memory pool support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-11 20:55 ` [PATCH 01/11] " Cristian Dumitrescu
@ 2023-01-11 20:55 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:55 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Rework the memory pool CLI command to accommodate the MBUF private
meta-data area size parameter.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 72 ++++++++++-------
 examples/pipeline/examples/fib.cli            |  2 +-
 examples/pipeline/examples/hash_func.cli      |  2 +-
 examples/pipeline/examples/l2fwd.cli          |  2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |  2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |  2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |  2 +-
 examples/pipeline/examples/learner.cli        |  2 +-
 examples/pipeline/examples/meter.cli          |  2 +-
 examples/pipeline/examples/mirroring.cli      |  2 +-
 examples/pipeline/examples/recirculation.cli  |  2 +-
 examples/pipeline/examples/registers.cli      |  2 +-
 examples/pipeline/examples/selector.cli       |  2 +-
 examples/pipeline/examples/varbit.cli         |  2 +-
 examples/pipeline/examples/vxlan.cli          |  2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |  2 +-
 examples/pipeline/obj.c                       | 80 +------------------
 examples/pipeline/obj.h                       | 27 -------
 18 files changed, 63 insertions(+), 146 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 8013541c4b..c014598cf1 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -192,72 +192,88 @@ parse_table_entry(struct rte_swx_ctl_pipeline *p,
 }
 
 static const char cmd_mempool_help[] =
-"mempool <mempool_name>\n"
-"   buffer <buffer_size>\n"
-"   pool <pool_size>\n"
-"   cache <cache_size>\n"
-"   cpu <cpu_id>\n";
+"mempool <mempool_name> "
+"meta <mbuf_private_size> "
+"pkt <pkt_buffer_size> "
+"pool <pool_size> "
+"cache <cache_size> "
+"numa <numa_node>\n";
 
 static void
 cmd_mempool(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
+	    uint32_t n_tokens,
+	    char *out,
+	    size_t out_size,
+	    void *obj __rte_unused)
 {
-	struct mempool_params p;
-	char *name;
-	struct mempool *mempool;
+	struct rte_mempool *mp;
+	char *mempool_name;
+	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
 
-	if (n_tokens != 10) {
+	if (n_tokens != 12) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	name = tokens[1];
+	mempool_name = tokens[1];
+
+	if (strcmp(tokens[2], "meta")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
+		return;
+	}
 
-	if (strcmp(tokens[2], "buffer") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
+	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
+	if (strcmp(tokens[4], "pkt")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
 		return;
 	}
 
-	if (strcmp(tokens[4], "pool") != 0) {
+	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
+		return;
+	}
+
+	if (strcmp(tokens[6], "pool")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
 		return;
 	}
 
-	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
+	if (parser_read_uint32(&pool_size, tokens[7])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
 		return;
 	}
 
-	if (strcmp(tokens[6], "cache") != 0) {
+	if (strcmp(tokens[8], "cache")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
+	if (parser_read_uint32(&cache_size, tokens[9])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
 		return;
 	}
 
-	if (strcmp(tokens[8], "cpu") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+	if (strcmp(tokens[10], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+	if (parser_read_uint32(&numa_node, tokens[11])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	mempool = mempool_create(obj, name, &p);
-	if (mempool == NULL) {
+	mp = rte_pktmbuf_pool_create(mempool_name,
+				     pool_size,
+				     cache_size,
+				     mbuf_private_size,
+				     pkt_buffer_size,
+				     numa_node);
+	if (!mp) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 4e30c1320f..2450dc9ca4 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/fib.c /tmp/fib.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index b2e219e4c9..3840e47a2b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index 27e37021b9..c5505df296 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 11bb4543b9..bd700707f5 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index 8724dae3b0..aa64b32ab2 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 4db0a0dc56..619d17474f 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 6c8aa3921e..42184be1c8 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/learner.c /tmp/learner.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 21582554b9..0fa38b16b8 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/meter.c /tmp/meter.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 1d439e04d3..e7391de74b 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 52d0894f12..965c870fd4 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 3516f76a5b..aa775f71e5 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/registers.c /tmp/registers.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0e251b657..f0608de184 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/selector.c /tmp/selector.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 0f89990471..648b8882c3 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 1fbd1be6e4..4565adfaea 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index adc7f73312..510c2ad870 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index b7e2316eec..4aaec02d9c 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -13,18 +13,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
 
-/*
- * mempool
- */
-TAILQ_HEAD(mempool_list, mempool);
-
 /*
  * link
  */
@@ -39,75 +33,10 @@ TAILQ_HEAD(ring_list, ring);
  * obj
  */
 struct obj {
-	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
 };
 
-/*
- * mempool
- */
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
-
-struct mempool *
-mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
-{
-	struct mempool *mempool;
-	struct rte_mempool *m;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		mempool_find(obj, name) ||
-		(params == NULL) ||
-		(params->buffer_size < BUFFER_SIZE_MIN) ||
-		(params->pool_size == 0))
-		return NULL;
-
-	/* Resource create */
-	m = rte_pktmbuf_pool_create(
-		name,
-		params->pool_size,
-		params->cache_size,
-		0,
-		params->buffer_size - sizeof(struct rte_mbuf),
-		params->cpu_id);
-
-	if (m == NULL)
-		return NULL;
-
-	/* Node allocation */
-	mempool = calloc(1, sizeof(struct mempool));
-	if (mempool == NULL) {
-		rte_mempool_free(m);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(mempool->name, name, sizeof(mempool->name));
-	mempool->m = m;
-	mempool->buffer_size = params->buffer_size;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
-
-	return mempool;
-}
-
-struct mempool *
-mempool_find(struct obj *obj, const char *name)
-{
-	struct mempool *mempool;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(mempool, &obj->mempool_list, node)
-		if (strcmp(mempool->name, name) == 0)
-			return mempool;
-
-	return NULL;
-}
-
 /*
  * link
  */
@@ -172,7 +101,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct rte_eth_conf port_conf;
 	struct link *link;
 	struct link_params_rss *rss;
-	struct mempool *mempool;
+	struct rte_mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
 	uint16_t port_id = 0;
@@ -194,8 +123,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
 
-	mempool = mempool_find(obj, params->rx.mempool_name);
-	if (mempool == NULL)
+	mempool = rte_mempool_lookup(params->rx.mempool_name);
+	if (!mempool)
 		return NULL;
 
 	rss = params->rx.rss;
@@ -252,7 +181,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			params->rx.queue_size,
 			cpu_id,
 			NULL,
-			mempool->m);
+			mempool);
 
 		if (status < 0)
 			return NULL;
@@ -422,7 +351,6 @@ obj_init(void)
 	if (!obj)
 		return NULL;
 
-	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 8ea1c414c2..4ea610ceed 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <sys/queue.h>
 
-#include <rte_mempool.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
@@ -24,32 +23,6 @@ struct obj;
 struct obj *
 obj_init(void);
 
-/*
- * mempool
- */
-struct mempool_params {
-	uint32_t buffer_size;
-	uint32_t pool_size;
-	uint32_t cache_size;
-	uint32_t cpu_id;
-};
-
-struct mempool {
-	TAILQ_ENTRY(mempool) node;
-	char name[NAME_SIZE];
-	struct rte_mempool *m;
-	uint32_t buffer_size;
-};
-
-struct mempool *
-mempool_create(struct obj *obj,
-	       const char *name,
-	       struct mempool_params *params);
-
-struct mempool *
-mempool_find(struct obj *obj,
-	     const char *name);
-
 /*
  * link
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 03/11] examples/pipeline: streamline ring support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-11 20:55 ` [PATCH 01/11] " Cristian Dumitrescu
  2023-01-11 20:55 ` [PATCH 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Remove redundant ring related code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 24 ++++++++++------
 examples/pipeline/obj.c | 63 -----------------------------------------
 examples/pipeline/obj.h | 21 --------------
 3 files changed, 15 insertions(+), 93 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index c014598cf1..471dfbad38 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -11,6 +11,7 @@
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_ring.h>
 #include <rte_swx_port_ethdev.h>
 #include <rte_swx_port_ring.h>
 #include <rte_swx_port_source_sink.h>
@@ -491,11 +492,11 @@ cmd_ring(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct ring_params p;
+	struct rte_ring *r;
 	char *name;
-	struct ring *ring;
+	uint32_t size, numa_node;
 
 	if (n_tokens != 6) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
@@ -504,28 +505,32 @@ cmd_ring(char **tokens,
 
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "size") != 0) {
+	if (strcmp(tokens[2], "size")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
+	if (parser_read_uint32(&size, tokens[3])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
 		return;
 	}
 
-	if (strcmp(tokens[4], "numa") != 0) {
+	if (strcmp(tokens[4], "numa")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
+	if (parser_read_uint32(&numa_node, tokens[5])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	ring = ring_create(obj, name, &p);
-	if (!ring) {
+	r = rte_ring_create(
+		name,
+		size,
+		(int)numa_node,
+		RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (!r) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
@@ -2998,6 +3003,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 4aaec02d9c..f1a50133f4 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -24,17 +24,11 @@
  */
 TAILQ_HEAD(link_list, link);
 
-/*
- * ring
- */
-TAILQ_HEAD(ring_list, ring);
-
 /*
  * obj
  */
 struct obj {
 	struct link_list link_list;
-	struct ring_list ring_list;
 };
 
 /*
@@ -283,62 +277,6 @@ link_next(struct obj *obj, struct link *link)
 		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
 }
 
-/*
- * ring
- */
-struct ring *
-ring_create(struct obj *obj, const char *name, struct ring_params *params)
-{
-	struct ring *ring;
-	struct rte_ring *r;
-	unsigned int flags = RING_F_SP_ENQ | RING_F_SC_DEQ;
-
-	/* Check input params */
-	if (!name || ring_find(obj, name) || !params || !params->size)
-		return NULL;
-
-	/**
-	 * Resource create
-	 */
-	r = rte_ring_create(
-		name,
-		params->size,
-		params->numa_node,
-		flags);
-	if (!r)
-		return NULL;
-
-	/* Node allocation */
-	ring = calloc(1, sizeof(struct ring));
-	if (!ring) {
-		rte_ring_free(r);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(ring->name, name, sizeof(ring->name));
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->ring_list, ring, node);
-
-	return ring;
-}
-
-struct ring *
-ring_find(struct obj *obj, const char *name)
-{
-	struct ring *ring;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(ring, &obj->ring_list, node)
-		if (strcmp(ring->name, name) == 0)
-			return ring;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -352,7 +290,6 @@ obj_init(void)
 		return NULL;
 
 	TAILQ_INIT(&obj->link_list);
-	TAILQ_INIT(&obj->ring_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 4ea610ceed..dbbc6d39a0 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -73,25 +73,4 @@ link_find(struct obj *obj, const char *name);
 struct link *
 link_next(struct obj *obj, struct link *link);
 
-/*
- * ring
- */
-struct ring_params {
-	uint32_t size;
-	uint32_t numa_node;
-};
-
-struct ring {
-	TAILQ_ENTRY(ring) node;
-	char name[NAME_SIZE];
-};
-
-struct ring *
-ring_create(struct obj *obj,
-	   const char *name,
-	   struct ring_params *params);
-
-struct ring *
-ring_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 04/11] examples/pipeline: streamline the Ethernet device support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (2 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Streamline the Ethernet device support code and remove redundant code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c  | 175 ++++++++++++++++++------------------
 examples/pipeline/main.c |  12 +--
 examples/pipeline/obj.c  | 186 +++++++++------------------------------
 examples/pipeline/obj.h  |  51 ++---------
 4 files changed, 136 insertions(+), 288 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 471dfbad38..37fc6f9551 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -292,16 +292,14 @@ cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct link_params p;
-	struct link_params_rss rss;
-	struct link *link;
+	struct ethdev_params p = {0};
+	struct ethdev_params_rss rss = {0};
 	char *name;
+	int status;
 
-	memset(&p, 0, sizeof(p));
-
-	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
+	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -377,111 +375,98 @@ cmd_ethdev(char **tokens,
 		}
 	}
 
-	link = link_create(obj, name, &p);
-	if (link == NULL) {
+	status = ethdev_config(name, &p);
+	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
 }
 
-/* Print the link stats and info */
 static void
-print_link_info(struct link *link, char *out, size_t out_size)
+ethdev_show(uint16_t port_id, char **out, size_t *out_size)
 {
-	struct rte_eth_stats stats;
-	struct rte_ether_addr mac_addr;
-	struct rte_eth_link eth_link;
-	uint16_t mtu;
-	int ret;
-
-	memset(&stats, 0, sizeof(stats));
-	rte_eth_stats_get(link->port_id, &stats);
-
-	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
-	if (ret != 0) {
-		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	ret = rte_eth_link_get(link->port_id, &eth_link);
-	if (ret < 0) {
-		snprintf(out, out_size, "\n%s: link get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	rte_eth_dev_get_mtu(link->port_id, &mtu);
-
-	snprintf(out, out_size,
-		"\n"
-		"%s: flags=<%s> mtu %u\n"
-		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
-		"\tport# %u  speed %s\n"
-		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
-		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tTX errors %" PRIu64"\n",
-		link->name,
-		eth_link.link_status == 0 ? "DOWN" : "UP",
-		mtu,
-		RTE_ETHER_ADDR_BYTES(&mac_addr),
-		link->n_rxq,
-		link->n_txq,
-		link->port_id,
-		rte_eth_link_speed_to_str(eth_link.link_speed),
-		stats.ipackets,
-		stats.ibytes,
-		stats.ierrors,
-		stats.imissed,
-		stats.rx_nombuf,
-		stats.opackets,
-		stats.obytes,
-		stats.oerrors);
+	char name[RTE_ETH_NAME_MAX_LEN] = {0};
+	struct rte_eth_dev_info info = {0};
+	struct rte_eth_stats stats = {0};
+	struct rte_ether_addr addr = {0};
+	struct rte_eth_link link = {0};
+	uint32_t length;
+	uint16_t mtu = 0;
+
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return;
+
+	rte_eth_dev_get_name_by_port(port_id, name);
+	rte_eth_dev_info_get(port_id, &info);
+	rte_eth_stats_get(port_id, &stats);
+	rte_eth_macaddr_get(port_id, &addr);
+	rte_eth_link_get(port_id, &link);
+	rte_eth_dev_get_mtu(port_id, &mtu);
+
+	snprintf(*out, *out_size,
+		 "%s: flags=<%s> mtu %u\n"
+		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
+		 "\tport# %u  speed %s\n"
+		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
+		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tTX errors %" PRIu64"\n\n",
+		 name,
+		 link.link_status ? "UP" : "DOWN",
+		 mtu,
+		 RTE_ETHER_ADDR_BYTES(&addr),
+		 info.nb_rx_queues,
+		 info.nb_tx_queues,
+		 port_id,
+		 rte_eth_link_speed_to_str(link.link_speed),
+		 stats.ipackets,
+		 stats.ibytes,
+		 stats.ierrors,
+		 stats.imissed,
+		 stats.rx_nombuf,
+		 stats.opackets,
+		 stats.obytes,
+		 stats.oerrors);
+
+	length = strlen(*out);
+	*out_size -= length;
+	*out += length;
 }
 
-/*
- * ethdev show [<ethdev_name>]
- */
+
+static char cmd_ethdev_show_help[] =
+"ethdev show [ <ethdev_name> ]\n";
+
 static void
 cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
-	      void *obj)
+	      void *obj __rte_unused)
 {
-	struct link *link;
-	char *link_name;
+	uint16_t port_id;
 
 	if (n_tokens != 2 && n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (n_tokens == 2) {
-		link = link_next(obj, NULL);
-
-		while (link != NULL) {
-			out_size = out_size - strlen(out);
-			out = &out[strlen(out)];
-
-			print_link_info(link, out, out_size);
-			link = link_next(obj, link);
-		}
-	} else {
-		out_size = out_size - strlen(out);
-		out = &out[strlen(out)];
+	/* Single device. */
+	if (n_tokens == 3) {
+		int status;
 
-		link_name = tokens[2];
-		link = link_find(obj, link_name);
+		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
+		if (status)
+			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
 
-		if (link == NULL) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-					"Link does not exist");
-			return;
-		}
-		print_link_info(link, out, out_size);
+		ethdev_show(port_id, &out, &out_size);
+		return;
 	}
+
+	/*  All devices. */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		if (rte_eth_dev_is_valid_port(port_id))
+			ethdev_show(port_id, &out, &out_size);
 }
 
 static const char cmd_ring_help[] =
@@ -3003,6 +2988,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tethdev show\n"
 			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -3038,9 +3024,16 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "ethdev") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
-		return;
+	if (!strcmp(tokens[0], "ethdev")) {
+		if (n_tokens == 1) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
+			return;
+		}
+
+		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
+			return;
+		}
 	}
 
 	if (strcmp(tokens[0], "ring") == 0) {
diff --git a/examples/pipeline/main.c b/examples/pipeline/main.c
index 6fb839f4cb..da5dd2b3f6 100644
--- a/examples/pipeline/main.c
+++ b/examples/pipeline/main.c
@@ -135,7 +135,6 @@ int
 main(int argc, char **argv)
 {
 	struct conn *conn;
-	struct obj *obj;
 	int status;
 
 	/* Parse application arguments */
@@ -150,13 +149,6 @@ main(int argc, char **argv)
 		return status;
 	};
 
-	/* Obj */
-	obj = obj_init();
-	if (!obj) {
-		printf("Error: Obj initialization failed (%d)\n", status);
-		return status;
-	}
-
 	/* Thread */
 	status = thread_init();
 	if (status) {
@@ -174,10 +166,10 @@ main(int argc, char **argv)
 		cli_script_process(app.script_name,
 			app.conn.msg_in_len_max,
 			app.conn.msg_out_len_max,
-			obj);
+			NULL);
 
 	/* Connectivity */
-	app.conn.msg_handle_arg = obj;
+	app.conn.msg_handle_arg = NULL;
 	conn = conn_init(&app.conn);
 	if (!conn) {
 		printf("Error: Connectivity initialization failed (%d)\n",
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index f1a50133f4..a5c09e7219 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -4,35 +4,14 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#ifdef RTE_EXEC_ENV_LINUX
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#endif
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_ctl.h>
 
 #include "obj.h"
 
 /*
- * link
- */
-TAILQ_HEAD(link_list, link);
-
-/*
- * obj
- */
-struct obj {
-	struct link_list link_list;
-};
-
-/*
- * link
+ * ethdev
  */
 static struct rte_eth_conf port_conf_default = {
 	.link_speeds = 0,
@@ -59,7 +38,7 @@ static struct rte_eth_conf port_conf_default = {
 static int
 rss_setup(uint16_t port_id,
 	uint16_t reta_size,
-	struct link_params_rss *rss)
+	struct ethdev_params_rss *rss)
 {
 	struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
 	uint32_t i;
@@ -88,69 +67,64 @@ rss_setup(uint16_t port_id,
 	return status;
 }
 
-struct link *
-link_create(struct obj *obj, const char *name, struct link_params *params)
+int
+ethdev_config(const char *name, struct ethdev_params *params)
 {
 	struct rte_eth_dev_info port_info;
 	struct rte_eth_conf port_conf;
-	struct link *link;
-	struct link_params_rss *rss;
+	struct ethdev_params_rss *rss;
 	struct rte_mempool *mempool;
-	uint32_t cpu_id, i;
-	int status;
+	uint32_t i;
+	int numa_node, status;
 	uint16_t port_id = 0;
 
 	/* Check input params */
-	if ((name == NULL) ||
-		link_find(obj, name) ||
-		(params == NULL) ||
-		(params->rx.n_queues == 0) ||
-		(params->rx.queue_size == 0) ||
-		(params->tx.n_queues == 0) ||
-		(params->tx.queue_size == 0))
-		return NULL;
+	if (!name ||
+	    !name[0] ||
+	    !params ||
+	    !params->rx.n_queues ||
+	    !params->rx.queue_size ||
+	    !params->tx.n_queues ||
+	    !params->tx.queue_size)
+		return -EINVAL;
 
 	status = rte_eth_dev_get_port_by_name(name, &port_id);
 	if (status)
-		return NULL;
+		return -EINVAL;
 
-	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
-		return NULL;
+	status = rte_eth_dev_info_get(port_id, &port_info);
+	if (status)
+		return -EINVAL;
 
 	mempool = rte_mempool_lookup(params->rx.mempool_name);
 	if (!mempool)
-		return NULL;
+		return -EINVAL;
 
 	rss = params->rx.rss;
 	if (rss) {
-		if ((port_info.reta_size == 0) ||
-			(port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512))
-			return NULL;
+		if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512)
+			return -EINVAL;
 
-		if ((rss->n_queues == 0) ||
-			(rss->n_queues >= LINK_RXQ_RSS_MAX))
-			return NULL;
+		if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX)
+			return -EINVAL;
 
 		for (i = 0; i < rss->n_queues; i++)
 			if (rss->queue_id[i] >= port_info.max_rx_queues)
-				return NULL;
+				return -EINVAL;
 	}
 
-	/**
-	 * Resource create
-	 */
 	/* Port */
 	memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
 	if (rss) {
+		uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP;
+
 		port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
-		port_conf.rx_adv_conf.rss_conf.rss_hf =
-			(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
-			port_info.flow_type_rss_offloads;
+		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads;
 	}
 
-	cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
-	if (cpu_id == (uint32_t) SOCKET_ID_ANY)
-		cpu_id = 0;
+	numa_node = rte_eth_dev_socket_id(port_id);
+	if (numa_node == SOCKET_ID_ANY)
+		numa_node = 0;
 
 	status = rte_eth_dev_configure(
 		port_id,
@@ -159,12 +133,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		&port_conf);
 
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (params->promiscuous) {
 		status = rte_eth_promiscuous_enable(port_id);
-		if (status != 0)
-			return NULL;
+		if (status)
+			return -EINVAL;
 	}
 
 	/* Port RX */
@@ -173,12 +147,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->rx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL,
 			mempool);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port TX */
@@ -187,24 +161,24 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->tx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port start */
 	status = rte_eth_dev_start(port_id);
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (rss) {
 		status = rss_setup(port_id, port_info.reta_size, rss);
 
 		if (status) {
 			rte_eth_dev_stop(port_id);
-			return NULL;
+			return -EINVAL;
 		}
 	}
 
@@ -212,84 +186,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	status = rte_eth_dev_set_link_up(port_id);
 	if ((status < 0) && (status != -ENOTSUP)) {
 		rte_eth_dev_stop(port_id);
-		return NULL;
-	}
-
-	/* Node allocation */
-	link = calloc(1, sizeof(struct link));
-	if (link == NULL) {
-		rte_eth_dev_stop(port_id);
-		return NULL;
+		return -EINVAL;
 	}
 
-	/* Node fill in */
-	strlcpy(link->name, name, sizeof(link->name));
-	link->port_id = port_id;
-	link->n_rxq = params->rx.n_queues;
-	link->n_txq = params->tx.n_queues;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->link_list, link, node);
-
-	return link;
-}
-
-int
-link_is_up(struct obj *obj, const char *name)
-{
-	struct rte_eth_link link_params;
-	struct link *link;
-
-	/* Check input params */
-	if (!obj || !name)
-		return 0;
-
-	link = link_find(obj, name);
-	if (link == NULL)
-		return 0;
-
-	/* Resource */
-	if (rte_eth_link_get(link->port_id, &link_params) < 0)
-		return 0;
-
-	return (link_params.link_status == RTE_ETH_LINK_DOWN) ? 0 : 1;
-}
-
-struct link *
-link_find(struct obj *obj, const char *name)
-{
-	struct link *link;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(link, &obj->link_list, node)
-		if (strcmp(link->name, name) == 0)
-			return link;
-
-	return NULL;
-}
-
-struct link *
-link_next(struct obj *obj, struct link *link)
-{
-	return (link == NULL) ?
-		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
-}
-
-/*
- * obj
- */
-struct obj *
-obj_init(void)
-{
-	struct obj *obj;
-
-	obj = calloc(1, sizeof(struct obj));
-	if (!obj)
-		return NULL;
-
-	TAILQ_INIT(&obj->link_list);
-
-	return obj;
+	return 0;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index dbbc6d39a0..fb091f4ba7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -6,41 +6,25 @@
 #define _INCLUDE_OBJ_H_
 
 #include <stdint.h>
-#include <sys/queue.h>
-
-#include <rte_swx_pipeline.h>
-#include <rte_swx_ctl.h>
-
-#ifndef NAME_SIZE
-#define NAME_SIZE 64
-#endif
-
-/*
- * obj
- */
-struct obj;
-
-struct obj *
-obj_init(void);
 
 /*
- * link
+ * ethdev
  */
-#ifndef LINK_RXQ_RSS_MAX
-#define LINK_RXQ_RSS_MAX                                   16
+#ifndef ETHDEV_RXQ_RSS_MAX
+#define ETHDEV_RXQ_RSS_MAX 16
 #endif
 
-struct link_params_rss {
-	uint32_t queue_id[LINK_RXQ_RSS_MAX];
+struct ethdev_params_rss {
+	uint32_t queue_id[ETHDEV_RXQ_RSS_MAX];
 	uint32_t n_queues;
 };
 
-struct link_params {
+struct ethdev_params {
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
 		const char *mempool_name;
-		struct link_params_rss *rss;
+		struct ethdev_params_rss *rss;
 	} rx;
 
 	struct {
@@ -51,26 +35,7 @@ struct link_params {
 	int promiscuous;
 };
 
-struct link {
-	TAILQ_ENTRY(link) node;
-	char name[NAME_SIZE];
-	uint16_t port_id;
-	uint32_t n_rxq;
-	uint32_t n_txq;
-};
-
-struct link *
-link_create(struct obj *obj,
-	    const char *name,
-	    struct link_params *params);
-
 int
-link_is_up(struct obj *obj, const char *name);
-
-struct link *
-link_find(struct obj *obj, const char *name);
-
-struct link *
-link_next(struct obj *obj, struct link *link);
+ethdev_config(const char *name, struct ethdev_params *params);
 
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 05/11] examples/pipeline: support crypto devices
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (3 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add support for crypto devices in the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c | 61 +++++++++++++++++++++++++++++++++++++++++
 examples/pipeline/obj.h | 11 ++++++++
 2 files changed, 72 insertions(+)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index a5c09e7219..078395c6aa 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -7,6 +7,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_cryptodev.h>
 
 #include "obj.h"
 
@@ -191,3 +192,63 @@ ethdev_config(const char *name, struct ethdev_params *params)
 
 	return 0;
 }
+
+/*
+ * cryptodev
+ */
+int
+cryptodev_config(const char *name, struct cryptodev_params *params)
+{
+	struct rte_cryptodev_info dev_info;
+	struct rte_cryptodev_config dev_conf;
+	struct rte_cryptodev_qp_conf queue_conf;
+	uint8_t dev_id;
+	uint32_t socket_id, i;
+	int status;
+
+	/* Check input parameters. */
+	if (!name ||
+	    !params->n_queue_pairs ||
+	    !params->queue_size)
+		return -EINVAL;
+
+	/* Find the crypto device. */
+	status = rte_cryptodev_get_dev_id(name);
+	if (status < 0)
+		return -EINVAL;
+
+	dev_id = (uint8_t)status;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->n_queue_pairs > dev_info.max_nb_queue_pairs)
+		return -EINVAL;
+
+	socket_id = rte_cryptodev_socket_id(dev_id);
+
+	/* Configure the crypto device. */
+	dev_conf.socket_id = socket_id;
+	dev_conf.nb_queue_pairs = params->n_queue_pairs;
+	dev_conf.ff_disable = 0;
+
+	status = rte_cryptodev_configure(dev_id, &dev_conf);
+	if (status)
+		return status;
+
+	/* Configure the crypto device queue pairs. */
+	queue_conf.nb_descriptors = params->queue_size;
+	queue_conf.mp_session = NULL;
+	queue_conf.mp_session_private = NULL;
+
+	for (i = 0; i < params->n_queue_pairs; i++) {
+		status = rte_cryptodev_queue_pair_setup(dev_id, i, &queue_conf, socket_id);
+		if (status)
+			return status;
+	}
+
+	/* Start the crypto device. */
+	status = rte_cryptodev_start(dev_id);
+	if (status)
+		return status;
+
+	return 0;
+}
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index fb091f4ba7..0f103be6a7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -38,4 +38,15 @@ struct ethdev_params {
 int
 ethdev_config(const char *name, struct ethdev_params *params);
 
+/*
+ * cryptodev
+ */
+struct cryptodev_params {
+	uint32_t n_queue_pairs;
+	uint32_t queue_size;
+};
+
+int
+cryptodev_config(const char *name, struct cryptodev_params *params);
+
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 06/11] examples/pipeline: add CLI command for crypto device
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (4 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI command for the configuration of crypto devices.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 37fc6f9551..47c1adf772 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -521,6 +521,58 @@ cmd_ring(char **tokens,
 	}
 }
 
+static const char cmd_cryptodev_help[] =
+"cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
+
+static void
+cmd_cryptodev(char **tokens,
+	      uint32_t n_tokens,
+	      char *out,
+	      size_t out_size,
+	      void *obj __rte_unused)
+{
+	struct cryptodev_params params;
+	char *cryptodev_name;
+	int status;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	cryptodev_name = tokens[1];
+
+	if (strcmp(tokens[2], "queues")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
+		return;
+	}
+
+	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
+		return;
+	}
+
+	if (strcmp(tokens[4], "qsize")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
+		return;
+	}
+
+
+	if (parser_read_uint32(&params.queue_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
+		return;
+	}
+
+	status = cryptodev_config(cryptodev_name, &params);
+	if (status)
+		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
+}
+
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2990,6 +3042,7 @@ cmd_help(char **tokens,
 			"\tethdev\n"
 			"\tethdev show\n"
 			"\tring\n"
+			"\tcryptodev\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3041,6 +3094,11 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3296,6 +3354,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
+		return;
+	}
+
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 07/11] examples/pipeline: add IPsec CLI commands
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (5 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands for IPsec block configuration.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 298 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 298 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 47c1adf772..efe2ff61db 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -18,6 +18,7 @@
 #include <rte_swx_port_fd.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
+#include <rte_swx_ipsec.h>
 
 #include "cli.h"
 
@@ -2909,6 +2910,263 @@ cmd_pipeline_mirror_session(char **tokens,
 	}
 }
 
+static const char cmd_ipsec_create_help[] =
+"ipsec <ipsec_instance_name> create "
+"in <ring_in_name> out <ring_out_name> "
+"cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
+"bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
+"samax <n_sa_max> "
+"numa <numa_node>\n";
+
+static void
+cmd_ipsec_create(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec_params p;
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t numa_node;
+	int status;
+
+	if (n_tokens != 20) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+
+	if (strcmp(tokens[2], "create")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
+		return;
+	}
+
+	if (strcmp(tokens[3], "in")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+		return;
+	}
+
+	p.ring_in_name = tokens[4];
+
+	if (strcmp(tokens[5], "out")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+		return;
+	}
+
+	p.ring_out_name = tokens[6];
+
+	if (strcmp(tokens[7], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	p.crypto_dev_name = tokens[8];
+
+	if (strcmp(tokens[9], "cryptoq")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
+		return;
+	}
+
+	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
+		return;
+	}
+
+	if (strcmp(tokens[11], "bsz")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
+		return;
+	}
+
+	if (strcmp(tokens[16], "samax")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
+		return;
+	}
+
+	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
+		return;
+	}
+
+	if (strcmp(tokens[18], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
+		return;
+	}
+
+	if (parser_read_uint32(&numa_node, tokens[19])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	status = rte_swx_ipsec_create(&ipsec,
+				      ipsec_instance_name,
+				      &p,
+				      (int)numa_node);
+	if (status)
+		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
+}
+
+static const char cmd_ipsec_sa_add_help[] =
+"ipsec <ipsec_instance_name> sa add <file_name>\n";
+
+static void
+cmd_ipsec_sa_add(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name, *file_name, *line = NULL;
+	FILE *file = NULL;
+	uint32_t line_id = 0;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		goto free;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		goto free;
+	}
+
+	if (strcmp(tokens[3], "add")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+		goto free;
+	}
+
+	file_name = tokens[4];
+	file = fopen(file_name, "r");
+	if (!file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+		goto free;
+	}
+
+	/* Buffer allocation. */
+	line = malloc(MAX_LINE_SIZE);
+	if (!line) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	/* File read. */
+	for (line_id = 1; ; line_id++) {
+		struct rte_swx_ipsec_sa_params *sa;
+		const char *err_msg;
+		uint32_t sa_id = 0;
+		int is_blank_or_comment, status = 0;
+
+		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+			break;
+
+		/* Read SA from file. */
+		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
+		if (!sa) {
+			if (is_blank_or_comment)
+				continue;
+
+			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
+				file_name, line_id, err_msg);
+			goto free;
+		}
+
+		snprintf(out, out_size, "%s", line);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		/* Add the SA to the IPsec instance. Free the SA. */
+		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
+		if (status)
+			snprintf(out, out_size, "\t: Error (%d)\n", status);
+		else
+			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		free(sa);
+		if (status)
+			goto free;
+	}
+
+free:
+	if (file)
+		fclose(file);
+	free(line);
+}
+
+static const char cmd_ipsec_sa_delete_help[] =
+"ipsec <ipsec_instance_name> sa delete <sa_id>\n";
+
+static void
+cmd_ipsec_sa_delete(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t sa_id;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		return;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		return;
+	}
+
+	if (strcmp(tokens[3], "delete")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+		return;
+	}
+
+	if (parser_read_uint32(&sa_id, tokens[4])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
+		return;
+	}
+
+	rte_swx_ipsec_sa_delete(ipsec, sa_id);
+}
+
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
 
@@ -3067,6 +3325,9 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tipsec create\n"
+			"\tipsec sa add\n"
+			"\tipsec sa delete\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
 		return;
@@ -3291,6 +3552,26 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "add")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "delete")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
+		return;
+	}
+
 	if ((n_tokens == 3) &&
 		(strcmp(tokens[0], "thread") == 0) &&
 		(strcmp(tokens[1], "pipeline") == 0)) {
@@ -3539,6 +3820,23 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "ipsec")) {
+		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
+			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
+			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
+			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	if (strcmp(tokens[0], "thread") == 0) {
 		if ((n_tokens >= 5) &&
 			(strcmp(tokens[4], "enable") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 08/11] examples/pipeline: rework the thread configuration updates
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (6 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the configuration updates for the data plane threads were
performed through message queues. Now, this mechanism is replaced by
the control thread updating the mirror copy of the data plane thread
configuration followed by pointer swapping.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 154 ++---
 examples/pipeline/examples/fib.cli            |   2 +-
 examples/pipeline/examples/hash_func.cli      |   2 +-
 examples/pipeline/examples/l2fwd.cli          |   2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |   2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |   2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |   2 +-
 examples/pipeline/examples/learner.cli        |   2 +-
 examples/pipeline/examples/meter.cli          |   2 +-
 examples/pipeline/examples/mirroring.cli      |   2 +-
 examples/pipeline/examples/recirculation.cli  |   2 +-
 examples/pipeline/examples/registers.cli      |   2 +-
 examples/pipeline/examples/selector.cli       |   2 +-
 examples/pipeline/examples/varbit.cli         |   2 +-
 examples/pipeline/examples/vxlan.cli          |   2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |   2 +-
 examples/pipeline/thread.c                    | 586 ++++--------------
 examples/pipeline/thread.h                    |  17 +-
 18 files changed, 217 insertions(+), 570 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index efe2ff61db..d48cbb6240 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3167,119 +3167,86 @@ cmd_ipsec_sa_delete(char **tokens,
 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
 }
 
-static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
-
-#ifndef TIMER_PERIOD_MS_DEFAULT
-#define TIMER_PERIOD_MS_DEFAULT 10
-#endif
+static const char cmd_pipeline_enable_help[] =
+"pipeline <pipeline_name> enable thread <thread_id> \n";
 
 static void
-cmd_thread_pipeline_enable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_enable(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
 {
 	char *pipeline_name;
 	struct rte_swx_pipeline *p;
-	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
+	uint32_t thread_id;
 	int status;
 
-	if ((n_tokens != 5) && (n_tokens != 7)) {
+	if (n_tokens != 5) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "enable") != 0) {
+	if (strcmp(tokens[2], "enable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
 		return;
 	}
 
-	if (n_tokens == 7) {
-		if (strcmp(tokens[5], "period") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
-			return;
-		}
+	if (strcmp(tokens[3], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
 
-		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-			return;
-		}
+	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
+	status = pipeline_enable(p, thread_id);
 	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
+		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
 		return;
 	}
 }
 
-static const char cmd_thread_pipeline_disable_help[] =
-"thread <thread_id> pipeline <pipeline_name> disable\n";
+static const char cmd_pipeline_disable_help[] =
+"pipeline <pipeline_name> disable\n";
 
 static void
-cmd_thread_pipeline_disable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_disable(char **tokens,
+		     uint32_t n_tokens,
+		     char *out,
+		     size_t out_size,
+		     void *obj __rte_unused)
 {
 	struct rte_swx_pipeline *p;
 	char *pipeline_name;
-	uint32_t thread_id;
-	int status;
 
-	if (n_tokens != 5) {
+	if (n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "disable") != 0) {
+	if (strcmp(tokens[2], "disable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, p);
-	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL,
-			"thread pipeline disable");
-		return;
-	}
+	pipeline_disable(p);
 }
 
 static void
@@ -3325,11 +3292,12 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tpipeline enable\n"
+			"\tpipeline disable\n\n"
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
-			"\tthread pipeline enable\n"
-			"\tthread pipeline disable\n\n");
+			);
 		return;
 	}
 
@@ -3552,6 +3520,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
+		return;
+	}
+
 	if (!strcmp(tokens[0], "ipsec") &&
 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
@@ -3572,22 +3552,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((n_tokens == 3) &&
-		(strcmp(tokens[0], "thread") == 0) &&
-		(strcmp(tokens[1], "pipeline") == 0)) {
-		if (strcmp(tokens[2], "enable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_enable_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "disable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_disable_help);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3818,6 +3782,16 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
+			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
+			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
 	}
 
 	if (!strcmp(tokens[0], "ipsec")) {
@@ -3837,22 +3811,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
-	if (strcmp(tokens[0], "thread") == 0) {
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "enable") == 0)) {
-			cmd_thread_pipeline_enable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "disable") == 0)) {
-			cmd_thread_pipeline_disable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 2450dc9ca4..d2485cefe0 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -54,4 +54,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index 3840e47a2b..2c20f7a7a0 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index c5505df296..383fe346c0 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethde
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index bd700707f5..d02cb2c470 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index aa64b32ab2..d4ea449e03 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 619d17474f..8a25c98096 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 42184be1c8..04c7b4d26f 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/eth
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 0fa38b16b8..9e064dbb52 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 meter meters set profile platinum index from 0 to 15
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index e7391de74b..da6bd338fb 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 mirror session 3 port 0 clone slow truncate 128
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 965c870fd4..f925179bf7 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/exampl
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index aa775f71e5..58dde5fdf1 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0608de184..9af56cd0f1 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 selector s show
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 648b8882c3..621e7f5627 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethd
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 4565adfaea..73eb29a6b2 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 510c2ad870..828342408b 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -36,4 +36,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 6d15f51fb2..3001bc0858 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -3,17 +3,11 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
 
+#include <rte_atomic.h>
 #include <rte_common.h>
-#include <rte_cycles.h>
 #include <rte_lcore.h>
-#include <rte_ring.h>
-
-#include <rte_table_acl.h>
-#include <rte_table_array.h>
-#include <rte_table_hash.h>
-#include <rte_table_lpm.h>
-#include <rte_table_lpm_ipv6.h>
 
 #include "obj.h"
 #include "thread.h"
@@ -22,14 +16,6 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
-#ifndef THREAD_MSGQ_SIZE
-#define THREAD_MSGQ_SIZE                                   64
-#endif
-
-#ifndef THREAD_TIMER_PERIOD_MS
-#define THREAD_TIMER_PERIOD_MS                             100
-#endif
-
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -40,509 +26,209 @@
 #endif
 
 /**
- * Control thread: data plane thread context
+ * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
+ * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
+ * blocks.
+ *
+ * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
+ * single thread at any given time, so the same pipeline/block cannot show up in the list of
+ * pipelines/blocks of more than one thread at any specific moment.
+ *
+ * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
+ *  - Read-write by the CP thread;
+ *  - Read-only by the DP thread.
  */
 struct thread {
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-
-	uint32_t enabled;
-};
-
-static struct thread thread[RTE_MAX_LCORE];
-
-/**
- * Data plane threads: context
- */
-struct pipeline_data {
-	struct rte_swx_pipeline *p;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-};
-
-struct thread_data {
-	struct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];
-	uint32_t n_pipelines;
-
-	struct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-	uint64_t time_next_min;
+	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	volatile uint64_t n_pipelines;
+	int enabled;
 } __rte_cache_aligned;
 
-static struct thread_data thread_data[RTE_MAX_LCORE];
+static struct thread threads[RTE_MAX_LCORE];
 
 /**
- * Control thread: data plane thread init
+ * Control plane (CP) thread.
  */
-static void
-thread_free(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct thread *t = &thread[i];
-
-		if (!rte_lcore_is_enabled(i))
-			continue;
-
-		/* MSGQs */
-		rte_ring_free(t->msgq_req);
-
-		rte_ring_free(t->msgq_rsp);
-	}
-}
-
 int
 thread_init(void)
 {
-	uint32_t i;
-
-	RTE_LCORE_FOREACH_WORKER(i) {
-		char name[NAME_MAX];
-		struct rte_ring *msgq_req, *msgq_rsp;
-		struct thread *t = &thread[i];
-		struct thread_data *t_data = &thread_data[i];
-		uint32_t cpu_id = rte_lcore_to_socket_id(i);
-
-		/* MSGQs */
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-REQ", i);
-
-		msgq_req = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_req == NULL) {
-			thread_free();
-			return -1;
-		}
+	uint32_t thread_id;
 
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-RSP", i);
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
 
-		msgq_rsp = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_rsp == NULL) {
-			thread_free();
-			return -1;
-		}
-
-		/* Control thread records */
-		t->msgq_req = msgq_req;
-		t->msgq_rsp = msgq_rsp;
 		t->enabled = 1;
-
-		/* Data plane thread records */
-		t_data->n_pipelines = 0;
-		t_data->msgq_req = msgq_req;
-		t_data->msgq_rsp = msgq_rsp;
-		t_data->timer_period =
-			(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;
-		t_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;
-		t_data->time_next_min = t_data->time_next;
 	}
 
 	return 0;
 }
 
-static inline int
-thread_is_running(uint32_t thread_id)
+static uint32_t
+pipeline_find(struct rte_swx_pipeline *p)
 {
-	enum rte_lcore_state_t thread_state;
+	uint32_t thread_id;
 
-	thread_state = rte_eal_get_lcore_state(thread_id);
-	return (thread_state == RUNNING) ? 1 : 0;
-}
-
-/**
- * Control thread & data plane threads: message passing
- */
-enum thread_req_type {
-	THREAD_REQ_PIPELINE_ENABLE = 0,
-	THREAD_REQ_PIPELINE_DISABLE,
-	THREAD_REQ_MAX
-};
-
-struct thread_msg_req {
-	enum thread_req_type type;
-
-	union {
-		struct {
-			struct rte_swx_pipeline *p;
-			uint32_t timer_period_ms;
-		} pipeline_enable;
-
-		struct {
-			struct rte_swx_pipeline *p;
-		} pipeline_disable;
-	};
-};
-
-struct thread_msg_rsp {
-	int status;
-};
-
-/**
- * Control thread
- */
-static struct thread_msg_req *
-thread_msg_alloc(void)
-{
-	size_t size = RTE_MAX(sizeof(struct thread_msg_req),
-		sizeof(struct thread_msg_rsp));
-
-	return calloc(1, size);
-}
-
-static void
-thread_msg_free(struct thread_msg_rsp *rsp)
-{
-	free(rsp);
-}
-
-static struct thread_msg_rsp *
-thread_msg_send_recv(uint32_t thread_id,
-	struct thread_msg_req *req)
-{
-	struct thread *t = &thread[thread_id];
-	struct rte_ring *msgq_req = t->msgq_req;
-	struct rte_ring *msgq_rsp = t->msgq_rsp;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* send */
-	do {
-		status = rte_ring_sp_enqueue(msgq_req, req);
-	} while (status == -ENOBUFS);
-
-	/* recv */
-	do {
-		status = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);
-	} while (status != 0);
-
-	return rsp;
-}
-
-static int
-thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
-{
-	struct thread *t = &thread[thread_id];
-	struct thread_data *td = &thread_data[thread_id];
-	uint32_t i;
-
-	if (!t->enabled)
-		return 0; /* Pipeline NOT enabled on this thread. */
-
-	for (i = 0; i < td->n_pipelines; i++)
-		if (td->p[i] == p)
-			return 1; /* Pipeline enabled on this thread. */
-
-	return 0 /* Pipeline NOT enabled on this thread. */;
-}
-
-int
-thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
-{
-	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		struct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];
-
-		if (td->n_pipelines >= THREAD_PIPELINES_MAX)
-			return -1;
-
-		/* Data plane thread */
-		td->p[td->n_pipelines] = p;
-
-		tdp->p = p;
-		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
-		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
-		td->n_pipelines++;
+		if (!t->enabled)
+			continue;
 
-		return 0;
+		for (i = 0; i < t->n_pipelines; i++)
+			if (t->pipelines[i] == p)
+				break;
 	}
 
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p;
-	req->pipeline_enable.timer_period_ms = timer_period_ms;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
-
-	/* Read response */
-	status = rsp->status;
-
-	/* Free response */
-	thread_msg_free(rsp);
-
-	/* Request completion */
-	if (status)
-		return status;
-
-	return 0;
+	return thread_id;
 }
 
+/**
+ * Enable a given pipeline to run on a specific DP thread.
+ *
+ * CP thread:
+ *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
+ *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
+ *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
+ *    write barrier used below.
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
+ *    the new pipeline when it sees the updated t->n_pipelines value;
+ *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
+ *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
+ *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
+ */
 int
-thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
 {
 	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
+	uint64_t n_pipelines;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_pipeline_enabled(thread_id, p))
-		return 0;
+	if (!p || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
 
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		uint32_t i;
-
-		for (i = 0; i < td->n_pipelines; i++) {
-			struct pipeline_data *tdp = &td->pipeline_data[i];
-
-			if (tdp->p != p)
-				continue;
-
-			/* Data plane thread */
-			if (i < td->n_pipelines - 1) {
-				struct rte_swx_pipeline *pipeline_last =
-					td->p[td->n_pipelines - 1];
-				struct pipeline_data *tdp_last =
-					&td->pipeline_data[td->n_pipelines - 1];
-
-				td->p[i] = pipeline_last;
-				memcpy(tdp, tdp_last, sizeof(*tdp));
-			}
-
-			td->n_pipelines--;
-
-			break;
-		}
+	if (pipeline_find(p) < RTE_MAX_LCORE)
+		return -EEXIST;
 
-		return 0;
-	}
-
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
 
-	/* Read response */
-	status = rsp->status;
+	n_pipelines = t->n_pipelines;
 
-	/* Free response */
-	thread_msg_free(rsp);
+	/* Check there is room for at least one more pipeline. */
+	if (n_pipelines >= THREAD_PIPELINES_MAX)
+		return -ENOSPC;
 
-	/* Request completion */
-	if (status)
-		return status;
+	/* Install the new pipeline. */
+	t->pipelines[n_pipelines] = p;
+	rte_wmb();
+	t->n_pipelines = n_pipelines + 1;
 
 	return 0;
 }
 
 /**
- * Data plane threads: message handling
+ * Disable a given pipeline from running on any DP thread.
+ *
+ * CP thread:
+ *  - Detects the thread that is running the given pipeline, if any;
+ *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
+ *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
+ *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
+ *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
+ *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
+ *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
+ *    below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final value of t->n_pipelines;
+ *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
+ *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
+ *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
+ *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
+ *    behaviors are detailed below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final values of the t->pipeline[] array;
+ *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
+ *       the exception of the pipeline_last, which is not run during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
  */
-static inline struct thread_msg_req *
-thread_msg_recv(struct rte_ring *msgq_req)
-{
-	struct thread_msg_req *req;
-
-	int status = rte_ring_sc_dequeue(msgq_req, (void **) &req);
-
-	if (status != 0)
-		return NULL;
-
-	return req;
-}
-
-static inline void
-thread_msg_send(struct rte_ring *msgq_rsp,
-	struct thread_msg_rsp *rsp)
-{
-	int status;
-
-	do {
-		status = rte_ring_sp_enqueue(msgq_rsp, rsp);
-	} while (status == -ENOBUFS);
-}
-
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_enable(struct thread_data *t,
-	struct thread_msg_req *req)
+void
+pipeline_disable(struct rte_swx_pipeline *p)
 {
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	struct pipeline_data *p = &t->pipeline_data[t->n_pipelines];
-
-	/* Request */
-	if (t->n_pipelines >= THREAD_PIPELINES_MAX) {
-		rsp->status = -1;
-		return rsp;
-	}
-
-	t->p[t->n_pipelines] = req->pipeline_enable.p;
-
-	p->p = req->pipeline_enable.p;
-	p->timer_period = (rte_get_tsc_hz() *
-		req->pipeline_enable.timer_period_ms) / 1000;
-	p->time_next = rte_get_tsc_cycles() + p->timer_period;
+	struct thread *t;
+	uint64_t n_pipelines;
+	uint32_t thread_id, i;
 
-	t->n_pipelines++;
+	/* Check input params */
+	if (!p)
+		return;
 
-	/* Response */
-	rsp->status = 0;
-	return rsp;
-}
+	/* Find the thread that runs this pipeline. */
+	thread_id = pipeline_find(p);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
 
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_disable(struct thread_data *t,
-	struct thread_msg_req *req)
-{
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	uint32_t n_pipelines = t->n_pipelines;
-	struct rte_swx_pipeline *pipeline = req->pipeline_disable.p;
-	uint32_t i;
+	t = &threads[thread_id];
+	n_pipelines = t->n_pipelines;
 
-	/* find pipeline */
 	for (i = 0; i < n_pipelines; i++) {
-		struct pipeline_data *p = &t->pipeline_data[i];
+		struct rte_swx_pipeline *pipeline = t->pipelines[i];
 
-		if (p->p != pipeline)
+		if (pipeline != p)
 			continue;
 
 		if (i < n_pipelines - 1) {
-			struct rte_swx_pipeline *pipeline_last =
-				t->p[n_pipelines - 1];
-			struct pipeline_data *p_last =
-				&t->pipeline_data[n_pipelines - 1];
+			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
 
-			t->p[i] = pipeline_last;
-			memcpy(p, p_last, sizeof(*p));
+			t->pipelines[i] = pipeline_last;
 		}
 
-		t->n_pipelines--;
+		rte_wmb();
+		t->n_pipelines = n_pipelines - 1;
 
-		rsp->status = 0;
-		return rsp;
+		return;
 	}
 
-	/* should not get here */
-	rsp->status = 0;
-	return rsp;
-}
-
-static void
-thread_msg_handle(struct thread_data *t)
-{
-	for ( ; ; ) {
-		struct thread_msg_req *req;
-		struct thread_msg_rsp *rsp;
-
-		req = thread_msg_recv(t->msgq_req);
-		if (req == NULL)
-			break;
-
-		switch (req->type) {
-		case THREAD_REQ_PIPELINE_ENABLE:
-			rsp = thread_msg_handle_pipeline_enable(t, req);
-			break;
-
-		case THREAD_REQ_PIPELINE_DISABLE:
-			rsp = thread_msg_handle_pipeline_disable(t, req);
-			break;
-
-		default:
-			rsp = (struct thread_msg_rsp *) req;
-			rsp->status = -1;
-		}
-
-		thread_msg_send(t->msgq_rsp, rsp);
-	}
+	return;
 }
 
 /**
- * Data plane threads: main
+ * Data plane (DP) threads.
+ *
+ * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
+ * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
+ * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
+ * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
+ * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
+ * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
+ * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
+ * marked as volatile.
  */
 int
 thread_main(void *arg __rte_unused)
 {
-	struct thread_data *t;
-	uint32_t thread_id, i;
+	struct thread *t;
+	uint32_t thread_id;
 
 	thread_id = rte_lcore_id();
-	t = &thread_data[thread_id];
-
-	/* Dispatch loop */
-	for (i = 0; ; i++) {
-		uint32_t j;
-
-		/* Data Plane */
-		for (j = 0; j < t->n_pipelines; j++)
-			rte_swx_pipeline_run(t->p[j], PIPELINE_INSTR_QUANTA);
+	t = &threads[thread_id];
 
-		/* Control Plane */
-		if ((i & 0xF) == 0) {
-			uint64_t time = rte_get_tsc_cycles();
-			uint64_t time_next_min = UINT64_MAX;
-
-			if (time < t->time_next_min)
-				continue;
-
-			/* Thread message queues */
-			{
-				uint64_t time_next = t->time_next;
-
-				if (time_next <= time) {
-					thread_msg_handle(t);
-					time_next = time + t->timer_period;
-					t->time_next = time_next;
-				}
-
-				if (time_next < time_next_min)
-					time_next_min = time_next;
-			}
+	/* Dispatch loop. */
+	for ( ; ; ) {
+		uint32_t i;
 
-			t->time_next_min = time_next_min;
-		}
+		/* Pipelines. */
+		for (i = 0; i < t->n_pipelines; i++)
+			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 712cb25bbb..338d480abb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -9,18 +9,21 @@
 
 #include <rte_swx_pipeline.h>
 
+/**
+ * Control plane (CP) thread.
+ */
 int
-thread_pipeline_enable(uint32_t thread_id,
-		       struct rte_swx_pipeline *p,
-		       uint32_t timer_period_ms);
+thread_init(void);
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-			struct rte_swx_pipeline *p);
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 
-int
-thread_init(void);
+void
+pipeline_disable(struct rte_swx_pipeline *p);
 
+/**
+ * Data plane (DP) threads.
+ */
 int
 thread_main(void *arg);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 09/11] examples/pipeline: support blocks other than pipelines
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (7 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the data plane threads only supported the execution of
pipelines assigned to them through configuration updates. Now, the
data plane threads also support running blocks such as IPsec.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/thread.c | 145 +++++++++++++++++++++++++++++++++++++
 examples/pipeline/thread.h |   9 +++
 2 files changed, 154 insertions(+)

diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 3001bc0858..51d6d06a8b 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -16,6 +16,10 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
+#ifndef THREAD_BLOCKS_MAX
+#define THREAD_BLOCKS_MAX                                  256
+#endif
+
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -38,9 +42,16 @@
  *  - Read-write by the CP thread;
  *  - Read-only by the DP thread.
  */
+struct block {
+	block_run_f block_func;
+	void *block;
+};
+
 struct thread {
 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	struct block *blocks[THREAD_BLOCKS_MAX];
 	volatile uint64_t n_pipelines;
+	volatile uint64_t n_blocks;
 	int enabled;
 } __rte_cache_aligned;
 
@@ -53,14 +64,43 @@ int
 thread_init(void)
 {
 	uint32_t thread_id;
+	int status = 0;
 
 	RTE_LCORE_FOREACH_WORKER(thread_id) {
 		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
 		t->enabled = 1;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			struct block *b;
+
+			b = calloc(1, sizeof(struct block));
+			if (!b) {
+				status = -ENOMEM;
+				goto error;
+			}
+
+			t->blocks[i] = b;
+		}
 	}
 
 	return 0;
+
+error:
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		t->enabled = 0;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			free(t->blocks[i]);
+			t->blocks[i] = NULL;
+		}
+	}
+
+	return status;
 }
 
 static uint32_t
@@ -83,6 +123,26 @@ pipeline_find(struct rte_swx_pipeline *p)
 	return thread_id;
 }
 
+static uint32_t
+block_find(void *b)
+{
+	uint32_t thread_id;
+
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		if (!t->enabled)
+			continue;
+
+		for (i = 0; i < t->n_blocks; i++)
+			if (t->blocks[i]->block == b)
+				break;
+	}
+
+	return thread_id;
+}
+
 /**
  * Enable a given pipeline to run on a specific DP thread.
  *
@@ -201,9 +261,87 @@ pipeline_disable(struct rte_swx_pipeline *p)
 	return;
 }
 
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+
+	/* Check input params */
+	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
+
+	if (block_find(block) < RTE_MAX_LCORE)
+		return -EEXIST;
+
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
+
+	n_blocks = t->n_blocks;
+
+	/* Check there is room for at least one more block. */
+	if (n_blocks >= THREAD_BLOCKS_MAX)
+		return -ENOSPC;
+
+	/* Install the new block. */
+	t->blocks[n_blocks]->block_func = block_func;
+	t->blocks[n_blocks]->block = block;
+
+	rte_wmb();
+	t->n_blocks = n_blocks + 1;
+
+	return 0;
+}
+
+void
+block_disable(void *block)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+	uint32_t thread_id, i;
+
+	/* Check input params */
+	if (!block)
+		return;
+
+	/* Find the thread that runs this block. */
+	thread_id = block_find(block);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
+
+	t = &threads[thread_id];
+	n_blocks = t->n_blocks;
+
+	for (i = 0; i < n_blocks; i++) {
+		struct block *b = t->blocks[i];
+
+		if (block != b->block)
+			continue;
+
+		if (i < n_blocks - 1) {
+			struct block *block_last = t->blocks[n_blocks - 1];
+
+			t->blocks[i] = block_last;
+		}
+
+		rte_wmb();
+		t->n_blocks = n_blocks - 1;
+
+		rte_wmb();
+		t->blocks[n_blocks - 1] = b;
+
+		return;
+	}
+
+	return;
+}
+
 /**
  * Data plane (DP) threads.
  *
+
+
  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
@@ -229,6 +367,13 @@ thread_main(void *arg __rte_unused)
 		/* Pipelines. */
 		for (i = 0; i < t->n_pipelines; i++)
 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
+
+		/* Blocks. */
+		for (i = 0; i < t->n_blocks; i++) {
+			struct block *b = t->blocks[i];
+
+			b->block_func(b->block);
+		}
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 338d480abb..f2e643def5 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -21,6 +21,15 @@ pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 void
 pipeline_disable(struct rte_swx_pipeline *p);
 
+typedef void
+(*block_run_f)(void *block);
+
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id);
+
+void
+block_disable(void *block);
+
 /**
  * Data plane (DP) threads.
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 10/11] examples/pipeline: add block enable/disable CLI commands
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (8 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 20:56 ` [PATCH 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands to enable/disable block execution on data plane
threads.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index d48cbb6240..fad855e1dd 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3249,6 +3249,134 @@ cmd_pipeline_disable(char **tokens,
 	pipeline_disable(p);
 }
 
+static const char cmd_block_enable_help[] =
+"block type <block_type> instance <block_name> enable thread <thread_id>\n";
+
+static void
+cmd_block_enable(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	block_run_f block_func = NULL;
+	void *block = NULL;
+	uint32_t thread_id;
+	int status;
+
+	if (n_tokens != 8) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "enable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+		return;
+	}
+
+	if (strcmp(tokens[6], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
+
+	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block_func = (block_run_f)rte_swx_ipsec_run;
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	status = block_enable(block_func, block, thread_id);
+	if (status) {
+		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
+		return;
+	}
+}
+
+static const char cmd_block_disable_help[] =
+"block type <block_type> instance <block_name> disable\n";
+
+static void
+cmd_block_disable(char **tokens,
+		  uint32_t n_tokens,
+		  char *out,
+		  size_t out_size,
+		  void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	void *block = NULL;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "disable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	block_disable(block);
+}
+
 static void
 cmd_help(char **tokens,
 	 uint32_t n_tokens,
@@ -3297,6 +3425,8 @@ cmd_help(char **tokens,
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
+			"\tblock enable\n"
+			"\tblock disable\n"
 			);
 		return;
 	}
@@ -3552,6 +3682,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
+		return;
+	}
+
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3811,6 +3953,18 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "block")) {
+		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
+			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
+			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH 11/11] examples/pipeline: add IPsec example
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (9 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
@ 2023-01-11 20:56 ` Cristian Dumitrescu
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 20:56 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add example files to illustrate the pipeline IPsec support.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ipsec.cli    |  57 +++++++
 examples/pipeline/examples/ipsec.io     |  23 +++
 examples/pipeline/examples/ipsec.spec   | 138 +++++++++++++++
 examples/pipeline/examples/ipsec_sa.txt | 216 ++++++++++++++++++++++++
 4 files changed, 434 insertions(+)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt

diff --git a/examples/pipeline/examples/ipsec.cli b/examples/pipeline/examples/ipsec.cli
new file mode 100644
index 0000000000..8cb5bf4239
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.cli
@@ -0,0 +1,57 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 --vdev crypto_aesni_mb0 -- -s ./examples/pipeline/examples/ipsec.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/ipsec.spec /tmp/ipsec.c
+pipeline libbuild /tmp/ipsec.c /tmp/ipsec.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 128 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+cryptodev crypto_aesni_mb0 queues 1 qsize 128
+ring RING0 size 1024 numa 0
+ring RING1 size 1024 numa 0
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/ipsec.so io ./examples/pipeline/examples/ipsec.io numa 0
+
+;
+; List of IPsec devices.
+;
+ipsec IPSEC0 create in RING0 out RING1 cryptodev crypto_aesni_mb0 cryptoq 0 bsz 32 32 32 32 samax 512 numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+//pipeline PIPELINE0 table policy_table add ./examples/pipeline/examples/ipsec_policy_table.txt
+//pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/ipsec_routing_table.txt
+//pipeline PIPELINE0 table nexthop_table add ./examples/pipeline/examples/ipsec_nexthop_table.txt
+//pipeline PIPELINE0 commit
+
+ipsec IPSEC0 sa add ./examples/pipeline/examples/ipsec_sa.txt
+
+;
+; Pipelines and blocks mapping to CPU threads.
+;
+pipeline PIPELINE0 enable thread 1
+block type ipsec instance IPSEC0 enable thread 1
diff --git a/examples/pipeline/examples/ipsec.io b/examples/pipeline/examples/ipsec.io
new file mode 100644
index 0000000000..f5a3fcf961
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.io
@@ -0,0 +1,23 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ring RING1 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ring RING0 bsz 32
diff --git a/examples/pipeline/examples/ipsec.spec b/examples/pipeline/examples/ipsec.spec
new file mode 100644
index 0000000000..376d9f9334
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.spec
@@ -0,0 +1,138 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_h {
+	bit<8> ver_ihl
+	bit<8> diffserv
+	bit<16> total_len
+	bit<16> identification
+	bit<16> flags_offset
+	bit<8> ttl
+	bit<8> protocol
+	bit<16> hdr_checksum
+	bit<32> src_addr
+	bit<32> dst_addr
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+struct ipsec_internal_h {
+	bit<32> sa_id
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+header ipsec_internal instanceof ipsec_internal_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port_in
+	bit<32> port_out
+
+	bit<32> src_addr
+	bit<32> dst_addr
+	bit<8> protocol
+	bit<16> src_port
+	bit<16> dst_port	
+}
+
+metadata instanceof metadata_t
+
+//
+// Actions
+//
+struct encrypt_args_t {
+	bit<32> sa_id
+}
+
+action encrypt args instanceof encrypt_args_t {
+	//Set the IPsec internal header.
+	validate h.ipsec_internal
+	mov h.ipsec_internal.sa_id t.sa_id
+
+	return
+}
+
+action drop args none {
+	drop
+}
+
+//
+// Tables.
+//
+table policy_table {
+	key {
+		m.src_addr exact
+		m.dst_addr exact
+		m.protocol exact
+		m.src_port exact
+		m.dst_port exact
+	}
+
+	actions {
+		encrypt
+		drop
+	}
+
+	default_action encrypt args sa_id 0
+	size 65536
+}
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port_in
+
+	jmpeq FROM_IPSEC_TO_NET m.port_in 1
+
+FROM_NET_TO_IPSEC : extract h.ethernet
+
+	extract h.ipv4
+	mov m.src_addr h.ipv4.src_addr
+	mov m.dst_addr h.ipv4.dst_addr
+	mov m.protocol h.ipv4.protocol
+
+	extract h.udp
+	mov m.src_port h.udp.src_port
+	mov m.dst_port h.udp.dst_port
+
+	table policy_table
+
+	mov m.port_out 1
+
+	emit h.ipsec_internal
+	emit h.ipv4
+	emit h.udp
+	tx m.port_out
+
+FROM_IPSEC_TO_NET : extract h.ipv4
+
+	validate h.ethernet
+	mov h.ethernet.dst_addr 0xa0b0c0d0e0f0
+	mov h.ethernet.src_addr 0xa1b1c1d1e1f1
+	mov h.ethernet.ethertype 0x0800
+
+	mov m.port_out 0
+
+	emit h.ethernet
+	emit h.ipv4
+	tx m.port_out
+}
diff --git a/examples/pipeline/examples/ipsec_sa.txt b/examples/pipeline/examples/ipsec_sa.txt
new file mode 100644
index 0000000000..37bfd8a9ce
--- /dev/null
+++ b/examples/pipeline/examples/ipsec_sa.txt
@@ -0,0 +1,216 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+// IPsec SA syntax:
+//
+// <sa>
+//    : encrypt <crypto_params> <encap_params>
+//    | decrypt <crypto_params> <encap_params>
+//    ;
+//
+// <crypto_params>
+//    : <cipher> <auth>
+//    | <aead>
+//    ;
+//
+// <cipher>
+//    : cipher <ciher_alg> key <cipher_key>
+//    | cipher <cipher_alg>
+//    ;
+//
+// <auth>
+//    : auth <authentication_alg> key <authentication_key>
+//    | auth <authentication_alg>
+//    ;
+//
+// <aead>
+//    : aead <aead_alg> key <aead_key>
+//    ;
+//
+// <encap_params>
+//    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+//    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+//    | esp spi <spi> transport
+//    ;
+
+//
+// cipher = null, auth = null
+//
+encrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth null esp spi 100 transport
+decrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth null esp spi 100 transport
+
+//
+// aes-gcm-128
+//
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-gcm-192
+//
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-gcm-256
+//
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// aes-ccm-128
+//
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-ccm-192
+//
+// Note: Currently not supported by the "aesni_mb" library.
+//
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-ccm-256
+//
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// chacha20-poly1305
+//
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// cipher = aes-cbc-128, auth = null
+//
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-192, auth = null
+//
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-256, auth = null
+//
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-128, auth = null
+//
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-192, auth = null
+//
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-256, auth = null
+//
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+
+//
+// cipher = null, auth = sha1-hmac
+//
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// cipher = null, auth = sha256-hmac
+//
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+
+//
+// cipher = null, auth = sha384-hmac
+//
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+
+//
+// cipher = null, auth = sha512-hmac
+//
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 00/11] pipeline: add IPsec support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (10 preceding siblings ...)
  2023-01-11 20:56 ` [PATCH 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
@ 2023-01-11 23:43 ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 01/11] " Cristian Dumitrescu
                     ` (10 more replies)
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (4 subsequent siblings)
  16 siblings, 11 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev

This patch set introduces a companion block for the SWX pipeline for
IPsec support.

The IPsec block is external to the pipeline, hence it needs to be
explicitly instantiated by the user and connected to a pipeline
instance through the pipeline I/O ports.

Main features:
* IPsec inbound (encrypted input packets -> clear text output packets)
and outbound (clear text input packets -> encrypted output packets)
processing support for tunnel and transport modes.

Interaction of the IPsec block with the pipeline:
* Each IPsec block instance has its own set of Security Associations
(SAs) used to process the input packets. Each SA is identified by its
unique SA ID. The IPsec inbound and outbound SAs share the same ID
space.
* Each input packet is first mapped to one of the existing SAs by
using the SA ID and then processed according to the identified SA. The
SA ID is read from input packet. The SA ID field is typically written
by the pipeline before sending the packet to the IPsec block.

Cristian Dumitrescu (11):
  pipeline: add IPsec support
  examples/pipeline: rework memory pool support
  examples/pipeline: streamline ring support
  examples/pipeline: streamline the Ethernet device support
  examples/pipeline: support crypto devices
  examples/pipeline: add CLI command for crypto device
  examples/pipeline: add IPsec CLI commands
  examples/pipeline: rework the thread configuration updates
  examples/pipeline: support blocks other than pipelines
  examples/pipeline: add block enable/disable CLI commands
  examples/pipeline: add IPsec example

 examples/pipeline/cli.c                       |  896 ++++++--
 examples/pipeline/examples/fib.cli            |    4 +-
 examples/pipeline/examples/hash_func.cli      |    4 +-
 examples/pipeline/examples/ipsec.cli          |   57 +
 examples/pipeline/examples/ipsec.io           |   23 +
 examples/pipeline/examples/ipsec.spec         |  138 ++
 examples/pipeline/examples/ipsec_sa.txt       |  216 ++
 examples/pipeline/examples/l2fwd.cli          |    4 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |    4 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |    4 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |    4 +-
 examples/pipeline/examples/learner.cli        |    4 +-
 examples/pipeline/examples/meter.cli          |    4 +-
 examples/pipeline/examples/mirroring.cli      |    4 +-
 examples/pipeline/examples/recirculation.cli  |    4 +-
 examples/pipeline/examples/registers.cli      |    4 +-
 examples/pipeline/examples/selector.cli       |    4 +-
 examples/pipeline/examples/varbit.cli         |    4 +-
 examples/pipeline/examples/vxlan.cli          |    4 +-
 examples/pipeline/examples/vxlan_pcap.cli     |    4 +-
 examples/pipeline/main.c                      |   12 +-
 examples/pipeline/obj.c                       |  360 +---
 examples/pipeline/obj.h                       |  100 +-
 examples/pipeline/thread.c                    |  655 +++---
 examples/pipeline/thread.h                    |   24 +-
 lib/pipeline/meson.build                      |    4 +-
 lib/pipeline/rte_swx_ipsec.c                  | 1851 +++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h                  |  383 ++++
 lib/pipeline/version.map                      |    9 +
 29 files changed, 3770 insertions(+), 1018 deletions(-)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 01/11] pipeline: add IPsec support
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

This block is providing IPsec support to the SWX pipeline. The IPsec
block is external to the pipeline, so it needs to be explicitly
instantiated and connected to a pipeline through the I/O ports.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 lib/pipeline/meson.build     |    4 +-
 lib/pipeline/rte_swx_ipsec.c | 1851 ++++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h |  383 +++++++
 lib/pipeline/version.map     |    9 +
 4 files changed, 2246 insertions(+), 1 deletion(-)
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build
index 3ca98ed194..aa3fd0c2b8 100644
--- a/lib/pipeline/meson.build
+++ b/lib/pipeline/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'rte_pipeline.c',
         'rte_port_in_action.c',
         'rte_table_action.c',
+	'rte_swx_ipsec.c',
         'rte_swx_pipeline.c',
         'rte_swx_pipeline_spec.c',
         'rte_swx_ctl.c',
@@ -19,8 +20,9 @@ headers = files(
         'rte_pipeline.h',
         'rte_port_in_action.h',
         'rte_table_action.h',
+	'rte_swx_ipsec.h',
         'rte_swx_pipeline.h',
         'rte_swx_extern.h',
         'rte_swx_ctl.h',
 )
-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']
+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']
diff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c
new file mode 100644
index 0000000000..04b065ac33
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.c
@@ -0,0 +1,1851 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_cryptodev.h>
+#include <rte_ipsec.h>
+
+#include "rte_swx_ipsec.h"
+
+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
+
+#include <rte_malloc.h>
+
+static void *
+env_calloc(size_t size, size_t alignment, int numa_node)
+{
+	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
+}
+
+static void
+env_free(void *start, size_t size __rte_unused)
+{
+	rte_free(start);
+}
+
+#else
+
+#include <numa.h>
+
+static void *
+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
+{
+	void *start;
+
+	if (numa_available() == -1)
+		return NULL;
+
+	start = numa_alloc_onnode(size, numa_node);
+	if (!start)
+		return NULL;
+
+	memset(start, 0, size);
+	return start;
+}
+
+static void
+env_free(void *start, size_t size)
+{
+	if ((numa_available() == -1) || !start)
+		return;
+
+	numa_free(start, size);
+}
+
+#endif
+
+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
+#endif
+
+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
+ * the mempool size to be at least 1.5 times the size of the mempool cache.
+ */
+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
+
+struct ipsec_sa {
+	struct rte_ipsec_session s;
+	int valid;
+};
+
+struct ipsec_pkts_in {
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_cops;
+};
+
+struct ipsec_pkts_out {
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_pkts;
+};
+
+struct rte_swx_ipsec {
+	/*
+	 * Parameters.
+	 */
+
+	/* IPsec instance name. */
+	char name[RTE_SWX_IPSEC_NAME_SIZE];
+
+	/* Input packet queue. */
+	struct rte_ring *ring_in;
+
+	/* Output packet queue. */
+	struct rte_ring *ring_out;
+
+	/* Crypto device ID. */
+	uint8_t dev_id;
+
+	/* Crypto device queue pair ID. */
+	uint16_t qp_id;
+
+	/* Burst sizes. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/* SA table size. */
+	size_t n_sa_max;
+
+	/*
+	 * Internals.
+	 */
+	/* Crypto device buffer pool for sessions. */
+	struct rte_mempool *mp_session;
+
+	/* Crypto device buffer pool for session private data. */
+	struct rte_mempool *mp_session_priv;
+
+	/* Pre-crypto packets. */
+	struct ipsec_pkts_in in;
+
+	/* Post-crypto packets. */
+	struct ipsec_pkts_out out;
+
+	/* Crypto device enqueue threshold. */
+	uint32_t crypto_wr_threshold;
+
+	/* Packets currently under crypto device processing. */
+	uint32_t n_pkts_crypto;
+
+	/* List of free SADB positions. */
+	uint32_t *sa_free_id;
+
+	/* Number of elements in the SADB list of free positions. */
+	size_t n_sa_free_id;
+
+	/* Allocated memory total size in bytes. */
+	size_t total_size;
+
+	/* Flag for registration to the global list of instances. */
+	int registered;
+
+	/*
+	 * Table memory.
+	 */
+	uint8_t memory[] __rte_cache_aligned;
+};
+
+static inline struct ipsec_sa *
+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
+{
+	struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
+
+	return &sadb[sa_id & (ipsec->n_sa_max - 1)];
+}
+
+/* Global list of instances. */
+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ipsec_tailq = {
+	.name = "RTE_SWX_IPSEC",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
+
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name ||
+	    !name[0] ||
+	    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
+		return NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ipsec;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ipsec_register(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ipsec;
+	TAILQ_INSERT_TAIL(ipsec_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ipsec_unregister(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		if (te->data == (void *)ipsec) {
+			TAILQ_REMOVE(ipsec_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
+
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
+{
+	size_t i;
+
+	if (!ipsec)
+		return;
+
+	/* Remove the current instance from the global list. */
+	if (ipsec->registered)
+		ipsec_unregister(ipsec);
+
+	/* SADB. */
+	for (i = 0; i < ipsec->n_sa_max; i++) {
+		struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
+
+		if (!sa->valid)
+			continue;
+
+		/* SA session. */
+		ipsec_session_free(ipsec, &sa->s);
+	}
+
+	/* Crypto device buffer pools. */
+	rte_mempool_free(ipsec->mp_session);
+	rte_mempool_free(ipsec->mp_session_priv);
+
+	/* IPsec object memory. */
+	env_free(ipsec, ipsec->total_size);
+}
+
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node)
+{
+	char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
+	struct rte_swx_ipsec *ipsec = NULL;
+	struct rte_ring *ring_in, *ring_out;
+	struct rte_cryptodev_info dev_info;
+	size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
+	int dev_id, status = 0;
+
+	/* Check input parameters. */
+	if (!ipsec_out ||
+	    !name ||
+	    !name[0] ||
+	    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
+	    !params ||
+	    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    !params->n_sa_max) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_in = rte_ring_lookup(params->ring_in_name);
+	if (!ring_in) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_out = rte_ring_lookup(params->ring_out_name);
+	if (!ring_out) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
+	if (dev_id == -1) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Memory allocation. */
+	n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
+
+	sadb_offset = sizeof(struct rte_swx_ipsec);
+	sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
+
+	sa_free_id_offset = sadb_offset + sadb_size;
+	sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
+
+	total_size = sa_free_id_offset + sa_free_id_size;
+	ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
+	if (!ipsec) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Initialization. */
+	strcpy(ipsec->name, name);
+	ipsec->ring_in = ring_in;
+	ipsec->ring_out = ring_out;
+	ipsec->dev_id = (uint8_t)dev_id;
+	ipsec->qp_id = params->crypto_dev_queue_pair_id;
+	memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
+	ipsec->n_sa_max = n_sa_max;
+
+	ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
+
+	ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
+	for (i = 0; i < n_sa_max; i++)
+		ipsec->sa_free_id[i] = n_sa_max - 1 - i;
+	ipsec->n_sa_free_id = n_sa_max;
+
+	ipsec->total_size = total_size;
+
+	/* Crypto device memory pools. */
+	snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
+	ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		0, /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool element private data size */
+		numa_node);
+	if (!ipsec->mp_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	snprintf(resource_name, sizeof(resource_name), "%s_mp_priv", name);
+	ipsec->mp_session_priv = rte_mempool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		rte_cryptodev_sym_get_private_session_size(dev_id), /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool private data size */
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		numa_node,
+		0); /* pool flags */
+	if (!ipsec->mp_session_priv) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Add the current instance to the global list. */
+	status = ipsec_register(ipsec);
+	if (status)
+		goto error;
+
+	ipsec->registered = 1;
+
+	*ipsec_out = ipsec;
+	return 0;
+
+error:
+	rte_swx_ipsec_free(ipsec);
+	return status;
+}
+
+static inline int
+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
+{
+	struct ipsec_sa *sa;
+	struct rte_ipsec_group *g;
+	int n_groups, n_pkts_in_group, i;
+
+	sa = ipsec->in.sa[0];
+
+	g = &ipsec->in.groups[0];
+	g->id.ptr = sa;
+	g->m = &ipsec->in.pkts[0];
+	n_pkts_in_group = 1;
+	n_groups = 1;
+
+	for (i = 1; i < n_pkts; i++) {
+		struct ipsec_sa *sa_new = ipsec->in.sa[i];
+
+		/* Same SA => Add the current pkt to the same group. */
+		if (sa_new == sa) {
+			n_pkts_in_group++;
+			continue;
+		}
+
+		/* Different SA => Close the current group & add the current pkt to a new group. */
+		g->cnt = n_pkts_in_group;
+		sa = sa_new;
+
+		g++;
+		g->id.ptr = sa;
+		g->m = &ipsec->in.pkts[i];
+		n_pkts_in_group = 1;
+		n_groups++;
+	}
+
+	/* Close the last group. */
+	g->cnt = n_pkts_in_group;
+
+	return n_groups;
+}
+
+static inline void
+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
+{
+	struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
+	struct rte_crypto_op **src = ipsec->in.group_cops;
+
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
+	uint32_t n_dst = ipsec->in.n_cops;
+	uint32_t n_dst_max = ipsec->bsz.crypto_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_cops;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
+	 * instead of moving the SRC to DST first and then using DST.
+	 */
+	if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
+		uint16_t n_ok;
+
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
+		ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
+
+		for (i = n_ok; i < n_src; i++) {
+			struct rte_crypto_op *cop = src[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		return;
+	}
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
+		n_pkts_crypto += n_ok;
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_crypto_op *cop = dst0[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	ipsec->n_pkts_crypto = n_pkts_crypto;
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->in.n_cops = n_dst;
+}
+
+/**
+ * Packet buffer anatomy:
+ *
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | Offset   | Size    | Description                                                              |
+ * | (Byte #) | (Bytes) |                                                                          |
+ * +==========+=========+==========================================================================+
+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |
+ * |          |         | The buf_addr field points to the start of the packet section.            |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 256      |         | Packet section.                                                          |
+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |
+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |
+ * +----------+---------+--------------------------------------------------------------------------+
+ */
+struct ipsec_mbuf {
+	struct ipsec_sa *sa;
+	struct rte_crypto_op cop;
+	struct rte_crypto_sym_op sym_cop;
+	uint8_t buffer[32]; /* The crypto IV is placed here. */
+};
+
+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
+
+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
+
+static inline void
+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
+{
+	int n_pkts, n_groups, i;
+
+	/* Read packets from the input ring. */
+	n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
+					   (void **)ipsec->in.pkts,
+					   ipsec->bsz.ring_rd,
+					   NULL);
+	if (!n_pkts)
+		return;
+
+	/* Get the SA for each packet. */
+	for (i = 0; i < n_pkts; i++) {
+		struct rte_mbuf *m = ipsec->in.pkts[i];
+		struct rte_swx_ipsec_input_packet_metadata *meta;
+		struct rte_ipv4_hdr *ipv4_hdr;
+		uint32_t sa_id;
+
+		meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
+
+		/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
+		sa_id = ntohl(meta->sa_id);
+
+		/* Consume the IPsec meta-data. */
+		m->data_off += META_LENGTH;
+		m->data_len -= META_LENGTH;
+		m->pkt_len -= META_LENGTH;
+
+		/* Set the fields required by the IPsec library. */
+		m->l2_len = 0;
+		m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
+			sizeof(struct rte_ipv4_hdr) :
+			sizeof(struct rte_ipv6_hdr);
+
+		/* Get the SA. */
+		ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
+	}
+
+	/* Group packets that share the same SA. */
+	n_groups = ipsec_sa_group(ipsec, n_pkts);
+
+	/* Write each group of packets sharing the same SA to the crypto device. */
+	for (i = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->in.groups[i];
+		struct ipsec_sa *sa = g->id.ptr;
+		struct rte_ipsec_session *s = &sa->s;
+		uint32_t j;
+		uint16_t n_pkts_ok;
+
+		/* Prepare the crypto ops for the current group. */
+		for (j = 0; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+			struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
+
+			priv->sa = sa;
+			ipsec->in.group_cops[j] = &priv->cop;
+		}
+
+		n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the crypto ops of the current group to the crypto device. */
+		ipsec_crypto_enqueue(ipsec, n_pkts_ok);
+	}
+}
+
+static inline void
+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
+{
+	struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
+	struct rte_mbuf **src = g->m;
+
+	uint32_t n_dst = ipsec->out.n_pkts;
+	uint32_t n_dst_max = ipsec->bsz.ring_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_pkts;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_mbuf *m = dst[i];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->out.n_pkts = n_dst;
+}
+
+static inline void
+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
+{
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, ng, i;
+
+	/* Read the crypto ops from the crypto device. */
+	if (!n_pkts_crypto)
+		return;
+
+	n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
+					     ipsec->qp_id,
+					     ipsec->out.cops,
+					     ipsec->bsz.crypto_rd);
+	if (!n_pkts)
+		return;
+
+	ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
+
+	/* Group packets that share the same SA. */
+	ng = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
+					      ipsec->out.group_pkts,
+					      ipsec->out.groups,
+					      n_pkts);
+
+	/* Perform post-crypto IPsec processing for each group of packets that share the same SA.
+	 * Write each group of packets to the output ring.
+	 */
+	for (i = 0, n_pkts = 0; i < ng; i++) {
+		struct rte_ipsec_group *g = &ipsec->out.groups[i];
+		struct rte_ipsec_session *s = g->id.ptr;
+		uint32_t n_pkts_ok, j;
+
+		/* Perform post-crypto IPsec processing for the current group. */
+		n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the packets of the current group to the output ring. */
+		ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
+	}
+}
+
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
+{
+	rte_swx_ipsec_pre_crypto(ipsec);
+	rte_swx_ipsec_post_crypto(ipsec);
+}
+
+/**
+ * IPsec Control Plane API
+ */
+struct cipher_alg {
+	const char *name;
+	enum rte_crypto_cipher_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t key_size;
+};
+
+struct auth_alg {
+	const char *name;
+	enum rte_crypto_auth_algorithm alg;
+	uint32_t iv_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+};
+
+struct aead_alg {
+	const char *name;
+	enum rte_crypto_aead_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+	uint32_t aad_size;
+};
+
+static struct cipher_alg cipher_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_CIPHER_NULL,
+		.iv_size = 0,
+		.block_size = 4,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "aes-cbc-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 16,
+	},
+
+	[2] = {
+		.name = "aes-cbc-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 24,
+	},
+
+	[3] = {
+		.name = "aes-cbc-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 32,
+	},
+
+	[4] = {
+		.name = "aes-ctr-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+	},
+
+	[5] = {
+		.name = "aes-ctr-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 28,
+	},
+
+	[6] = {
+		.name = "aes-ctr-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 36,
+	},
+
+	[7] = {
+		.name = "3des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 24,
+	},
+
+	[8] = {
+		.name = "des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 8,
+	},
+};
+
+static struct auth_alg auth_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_AUTH_NULL,
+		.iv_size = 0,
+		.digest_size = 0,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "sha1-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 20,
+	},
+
+	[2] = {
+		.name = "sha256-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
+		.iv_size = 0,
+		.digest_size = 16,
+		.key_size = 32,
+	},
+
+	[3] = {
+		.name = "sha384-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
+		.iv_size = 0,
+		.digest_size = 24,
+		.key_size = 48,
+	},
+
+	[4] = {
+		.name = "sha512-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
+		.iv_size = 0,
+		.digest_size = 32,
+		.key_size = 64,
+	},
+
+	[5] = {
+		.name = "aes-gmac",
+		.alg = RTE_CRYPTO_AUTH_AES_GMAC,
+		.iv_size = 8,
+		.digest_size = 16,
+		.key_size = 20,
+	},
+
+	[6] = {
+		.name = "aes-xcbc-mac-96",
+		.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 16,
+	},
+};
+
+static struct aead_alg aead_algs[] = {
+	[0] = {
+		.name = "aes-gcm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[1] = {
+		.name = "aes-gcm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[2] = {
+		.name = "aes-gcm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[3] = {
+		.name = "aes-ccm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[4] = {
+		.name = "aes-ccm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[5] = {
+		.name = "aes-ccm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[6] = {
+		.name = "chacha20-poly1305",
+		.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
+		.iv_size = 12,
+		.block_size = 64,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+};
+
+static struct cipher_alg *
+cipher_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct cipher_alg *
+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static int
+char_to_hex(char c, uint8_t *val)
+{
+	if (c >= '0' && c <= '9') {
+		*val = c - '0';
+		return 0;
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		*val = c - 'A' + 10;
+		return 0;
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		*val = c - 'a' + 10;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
+{
+	uint32_t i;
+
+	/* Check input arguments. */
+	if (!src || !src[0] || !dst || !n_dst_bytes)
+		return -EINVAL;
+
+	/* Skip any leading "0x" or "0X" in the src string. */
+	if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
+		src += 2;
+
+	/* Convert each group of two hex characters in the src string to one byte in dst array. */
+	for (i = 0; i < n_dst_bytes; i++) {
+		uint8_t a, b;
+		int status;
+
+		status = char_to_hex(*src, &a);
+		if (status)
+			return status;
+		src++;
+
+		status = char_to_hex(*src, &b);
+		if (status)
+			return status;
+		src++;
+
+		dst[i] = a * 16 + b;
+	}
+
+	/* Check for the end of the src string. */
+	if (*src)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+token_is_comment(const char *token)
+{
+	if ((token[0] == '#') ||
+	    (token[0] == ';') ||
+	    ((token[0] == '/') && (token[1] == '/')))
+		return 1; /* TRUE. */
+
+	return 0; /* FALSE. */
+}
+
+#define MAX_TOKENS 64
+
+#define CHECK(condition, msg)          \
+do {                                   \
+	if (!(condition)) {            \
+		if (errmsg)            \
+			*errmsg = msg; \
+		goto error;            \
+	}                              \
+} while (0)
+
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg)
+{
+	char *token_array[MAX_TOKENS], **t;
+	struct rte_swx_ipsec_sa_params *p = NULL;
+	char *s0 = NULL, *s;
+	uint32_t n_tokens = 0;
+	int blank_or_comment = 0;
+
+	/* Check input arguments. */
+	CHECK(string && string[0], "NULL input");
+
+	/* Memory allocation. */
+	s0 = strdup(string);
+	p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
+	CHECK(s0 && p, "Not enough memory");
+
+	/* Parse the string into tokens. */
+	for (s = s0; ; ) {
+		char *token;
+
+		token = strtok_r(s, " \f\n\r\t\v", &s);
+		if (!token || token_is_comment(token))
+			break;
+
+		CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
+
+		token_array[n_tokens] = token;
+		n_tokens++;
+	}
+
+	t = token_array;
+	if (!n_tokens) {
+		blank_or_comment = 1;
+		goto error;
+	}
+
+	/*
+	 * Crypto operation.
+	 */
+	if (!strcmp(t[0], "encrypt"))
+		p->encrypt = 1;
+	else if (!strcmp(t[0], "decrypt"))
+		p->encrypt = 0;
+	else
+		CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
+
+	t++;
+	n_tokens--;
+
+	/*
+	 * Crypto parameters.
+	 */
+	CHECK(n_tokens >= 2, "Not enough tokens");
+
+	if (!strcmp(t[0], "cipher")) {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t key_size;
+
+		p->crypto.is_aead = 0;
+
+		/* cipher. */
+		cipher_alg = cipher_alg_find(t[1]);
+		CHECK(cipher_alg, "Unsupported cipher algorithm");
+
+		key_size = cipher_alg->key_size;
+		p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
+		p->crypto.cipher_auth.cipher.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
+				"Cipher algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
+			CHECK(!status, "Cipher key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+
+		/* authentication. */
+		CHECK(n_tokens >= 2, "Not enough tokens");
+		CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
+
+		auth_alg = auth_alg_find(t[1]);
+		CHECK(auth_alg, "Unsupported authentication algorithm");
+
+		key_size = auth_alg->key_size;
+		p->crypto.cipher_auth.auth.alg = auth_alg->alg;
+		p->crypto.cipher_auth.auth.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
+				"Authentication algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
+			CHECK(!status, "Authentication key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+	} else if (!strcmp(t[0], "aead")) {
+		struct aead_alg *alg;
+		uint32_t key_size;
+		int status;
+
+		p->crypto.is_aead = 1;
+
+		CHECK(n_tokens >= 4, "Not enough tokens");
+		alg = aead_alg_find(t[1]);
+		CHECK(alg, "Unsupported AEAD algorithm");
+
+		key_size = alg->key_size;
+		p->crypto.aead.alg = alg->alg;
+		p->crypto.aead.key_size = key_size;
+
+		CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
+		CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
+			"AEAD algorithm key too big");
+
+		status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
+		CHECK(!status, "AEAD key invalid format");
+
+		t += 4;
+		n_tokens -= 4;
+	} else
+		CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
+
+	/*
+	 * Packet ecapsulation parameters.
+	 */
+	CHECK(n_tokens >= 4, "Not enough tokens");
+	CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
+	CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
+
+	p->encap.esp.spi = strtoul(t[2], &t[2], 0);
+	CHECK(!t[2][0], "ESP SPI field invalid format");
+
+	t += 3;
+	n_tokens -= 3;
+
+	if (!strcmp(t[0], "tunnel")) {
+		p->encap.tunnel_mode = 1;
+
+		CHECK(n_tokens >= 6, "Not enough tokens");
+
+		if (!strcmp(t[1], "ipv4")) {
+			uint32_t addr;
+
+			p->encap.tunnel_ipv4 = 1;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			addr = strtoul(t[3], &t[3], 0);
+			CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
+			p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			addr = strtoul(t[5], &t[5], 0);
+			CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
+			p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
+
+			t += 6;
+			n_tokens -= 6;
+		} else if (!strcmp(t[1], "ipv6")) {
+			int status;
+
+			p->encap.tunnel_ipv4 = 0;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			status = hex_string_parse(t[3],
+						  p->encap.tunnel.ipv6.src_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 source address invalid format");
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			status = hex_string_parse(t[5],
+						  p->encap.tunnel.ipv6.dst_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 destination address invalid format");
+
+			t += 6;
+			n_tokens -= 6;
+		} else
+			CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
+	} else if (!strcmp(t[0], "transport")) {
+		p->encap.tunnel_mode = 0;
+
+		t++;
+		n_tokens--;
+	} else
+		CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
+
+	/*
+	 * Any other parameters.
+	 */
+	CHECK(!n_tokens, "Unexpected trailing tokens");
+
+	free(s0);
+	return p;
+
+error:
+	free(p);
+	free(s0);
+	if (is_blank_or_comment)
+		*is_blank_or_comment = blank_or_comment;
+	return NULL;
+}
+
+static void
+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv4_hdr ipv4_hdr = {
+		.version_ihl = 0x45,
+		.type_of_service = 0,
+		.total_length = 0, /* Cannot be pre-computed. */
+		.packet_id = 0,
+		.fragment_offset = 0,
+		.time_to_live = 64,
+		.next_proto_id = IPPROTO_ESP,
+		.hdr_checksum = 0, /* Cannot be pre-computed. */
+		.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
+		.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
+	};
+
+	memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
+}
+
+static void
+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv6_hdr ipv6_hdr = {
+		.vtc_flow = 0x60000000,
+		.payload_len = 0, /* Cannot be pre-computed. */
+		.proto = IPPROTO_ESP,
+		.hop_limits = 64,
+		.src_addr = {0},
+		.dst_addr = {0},
+	};
+
+	memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
+	memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
+	memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
+}
+
+/* IPsec library SA parameters. */
+static struct rte_crypto_sym_xform *
+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_crypto_sym_xform *xform,
+		uint32_t *salt_out)
+{
+	if (p->crypto.is_aead) {
+		struct aead_alg *alg;
+		uint32_t key_size, salt, iv_length;
+
+		alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
+		if (!alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		key_size = p->crypto.aead.key_size - 4;
+		memcpy(&salt, &p->crypto.aead.key[key_size], 4);
+
+		/* IV length. */
+		iv_length = 12;
+		if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
+			iv_length = 11;
+
+		/* xform. */
+		xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform[0].aead.op = p->encrypt ?
+			RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform[0].aead.algo = p->crypto.aead.alg;
+		xform[0].aead.key.data = p->crypto.aead.key;
+		xform[0].aead.key.length = key_size;
+		xform[0].aead.iv.offset = IV_OFFSET;
+		xform[0].aead.iv.length = iv_length;
+		xform[0].aead.digest_length = alg->digest_size;
+		xform[0].aead.aad_length = alg->aad_size;
+		xform[0].next = NULL;
+
+		*salt_out = salt;
+		return &xform[0];
+	} else {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
+
+		cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
+						   p->crypto.cipher_auth.cipher.key_size);
+		if (!cipher_alg)
+			return NULL;
+
+		auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
+					       p->crypto.cipher_auth.auth.key_size);
+		if (!auth_alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
+		auth_key_size = p->crypto.cipher_auth.auth.key_size;
+
+		switch (p->crypto.cipher_auth.cipher.alg) {
+		case RTE_CRYPTO_CIPHER_AES_CBC:
+		case RTE_CRYPTO_CIPHER_3DES_CBC:
+			salt = (uint32_t)rand();
+			break;
+
+		case RTE_CRYPTO_CIPHER_AES_CTR:
+			cipher_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
+			break;
+
+		default:
+			salt = 0;
+		}
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			auth_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
+		}
+
+		/* IV length. */
+		auth_iv_length = cipher_alg->iv_size;
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
+			auth_iv_length = 12;
+
+		/* xform. */
+		if (p->encrypt) {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[0].cipher.key.length = cipher_key_size;
+			xform[0].cipher.iv.offset = IV_OFFSET;
+			xform[0].cipher.iv.length = cipher_alg->iv_size;
+			xform[0].cipher.dataunit_len = 0;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+			xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[1].auth.key.length = auth_key_size;
+			xform[1].auth.iv.offset = IV_OFFSET;
+			xform[1].auth.iv.length = auth_iv_length;
+			xform[1].auth.digest_length = auth_alg->digest_size;
+			xform[1].next = NULL;
+		} else {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+			xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[0].auth.key.length = auth_key_size;
+			xform[0].auth.iv.offset = IV_OFFSET;
+			xform[0].auth.iv.length = auth_iv_length;
+			xform[0].auth.digest_length = auth_alg->digest_size;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[1].cipher.key.length = cipher_key_size;
+			xform[1].cipher.iv.offset = IV_OFFSET;
+			xform[1].cipher.iv.length = cipher_alg->iv_size;
+			xform[1].cipher.dataunit_len = 0;
+			xform[1].next = NULL;
+		}
+
+		*salt_out = salt;
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			if (p->encrypt)
+				return &xform[1];
+
+			xform[0].next = NULL;
+			return &xform[0];
+		}
+
+		return &xform[0];
+	}
+}
+
+static void
+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_security_ipsec_xform *ipsec_xform,
+		uint32_t salt)
+{
+	ipsec_xform->spi = p->encap.esp.spi;
+
+	ipsec_xform->salt = salt;
+
+	ipsec_xform->options.esn = 0;
+	ipsec_xform->options.udp_encap = 0;
+	ipsec_xform->options.copy_dscp = 1;
+	ipsec_xform->options.copy_flabel = 0;
+	ipsec_xform->options.copy_df = 0;
+	ipsec_xform->options.dec_ttl = 0;
+	ipsec_xform->options.ecn = 1;
+	ipsec_xform->options.stats = 0;
+	ipsec_xform->options.iv_gen_disable = 0;
+	ipsec_xform->options.tunnel_hdr_verify = 0;
+	ipsec_xform->options.udp_ports_verify = 0;
+	ipsec_xform->options.ip_csum_enable = 0;
+	ipsec_xform->options.l4_csum_enable = 0;
+	ipsec_xform->options.ip_reassembly_en = 0;
+	ipsec_xform->options.reserved_opts = 0;
+
+	ipsec_xform->direction = p->encrypt ?
+		RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
+		RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+
+	ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+
+	ipsec_xform->mode = p->encap.tunnel_mode ?
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
+
+	ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
+		RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
+		RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
+			ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
+			ipsec_xform->tunnel.ipv4.dscp = 0;
+			ipsec_xform->tunnel.ipv4.df = 0;
+			ipsec_xform->tunnel.ipv4.ttl = 64;
+		} else {
+			ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
+			ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
+			ipsec_xform->tunnel.ipv6.dscp = 0;
+			ipsec_xform->tunnel.ipv6.flabel = 0;
+			ipsec_xform->tunnel.ipv6.hlimit = 64;
+		}
+	}
+
+	ipsec_xform->life.packets_soft_limit = 0;
+	ipsec_xform->life.bytes_soft_limit = 0;
+	ipsec_xform->life.packets_hard_limit = 0;
+	ipsec_xform->life.bytes_hard_limit = 0;
+
+	ipsec_xform->replay_win_sz = 0;
+
+	ipsec_xform->esn.value = 0;
+
+	ipsec_xform->udp.dport = 0;
+	ipsec_xform->udp.sport = 0;
+}
+
+static int
+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
+		 struct rte_ipsec_sa_prm *sa_prm,
+		 struct rte_ipv4_hdr *ipv4_hdr,
+		 struct rte_ipv6_hdr *ipv6_hdr,
+		 struct rte_crypto_sym_xform *crypto_xform)
+{
+	uint32_t salt;
+
+	memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
+
+	sa_prm->userdata = 0; /* Not used. */
+
+	sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
+
+	/*
+	 * crypto_xform.
+	 */
+	sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
+	if (!sa_prm->crypto_xform)
+		return -EINVAL;
+
+	/*
+	 * ipsec_xform.
+	 */
+	ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
+
+	/*
+	 * tunnel / transport.
+	 *
+	 * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
+	 * the input packet type should be added to the SA configuration parameters.
+	 */
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv4_hdr;
+		} else {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv6_hdr;
+		}
+	} else {
+		sa_prm->trs.proto = IPPROTO_IPIP; /* IPv4. */
+	}
+
+	return 0;
+}
+
+static int
+ipsec_session_create(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *p,
+		     struct rte_ipsec_session *s)
+{
+	struct rte_ipv4_hdr ipv4_hdr;
+	struct rte_ipv6_hdr ipv6_hdr;
+	struct rte_crypto_sym_xform crypto_xform[2];
+	struct rte_ipsec_sa_prm sa_prm;
+	struct rte_ipsec_sa *sa = NULL;
+	struct rte_cryptodev_sym_session *crypto_session = NULL;
+	int sa_size;
+	int sa_valid = 0, crypto_session_valid = 0, status = 0;
+
+	tunnel_ipv4_header_set(&ipv4_hdr, p);
+	tunnel_ipv6_header_set(&ipv6_hdr, p);
+
+	/* IPsec library SA setup. */
+	status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
+	if (status)
+		goto error;
+
+	sa_size = rte_ipsec_sa_size(&sa_prm);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa = calloc(1, sa_size);
+	if (!sa) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa_valid = 1;
+
+	/* Cryptodev library session setup. */
+	crypto_session = rte_cryptodev_sym_session_create(ipsec->mp_session);
+	if (!crypto_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	status = rte_cryptodev_sym_session_init(ipsec->dev_id,
+						crypto_session,
+						sa_prm.crypto_xform,
+						ipsec->mp_session_priv);
+	if (status)
+		goto error;
+
+	crypto_session_valid = 1;
+
+	/* IPsec library session setup. */
+	s->sa = sa;
+	s->type = RTE_SECURITY_ACTION_TYPE_NONE;
+	s->crypto.ses = crypto_session;
+	s->crypto.dev_id = ipsec->dev_id;
+	s->pkt_func.prepare.async = NULL;
+	s->pkt_func.process = NULL;
+
+	return rte_ipsec_session_prepare(s);
+
+error:
+	/* sa. */
+	if (sa_valid)
+		rte_ipsec_sa_fini(sa);
+
+	free(sa);
+
+	/* crypto_session. */
+	if (crypto_session_valid)
+		rte_cryptodev_sym_session_clear(ipsec->dev_id, crypto_session);
+
+	if (crypto_session)
+		rte_cryptodev_sym_session_free(crypto_session);
+
+	/* s. */
+	memset(s, 0, sizeof(*s));
+
+	return status;
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec,
+		   struct rte_ipsec_session *s)
+{
+	if (!s)
+		return;
+
+	/* IPsec library SA. */
+	if (s->sa)
+		rte_ipsec_sa_fini(s->sa);
+	free(s->sa);
+
+	/* Cryptodev library session. */
+	if (s->crypto.ses) {
+		rte_cryptodev_sym_session_clear(ipsec->dev_id, s->crypto.ses);
+		rte_cryptodev_sym_session_free(s->crypto.ses);
+	}
+
+	/* IPsec library session. */
+	memset(s, 0, sizeof(*s));
+}
+
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *id)
+{
+	struct ipsec_sa *sa;
+	uint32_t sa_id;
+	int status;
+
+	/* Check the input parameters. */
+	if (!ipsec || !sa_params || !id)
+		return -EINVAL;
+
+	/* Allocate a free SADB entry. */
+	if (!ipsec->n_sa_free_id)
+		return -ENOSPC;
+
+	sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
+	ipsec->n_sa_free_id--;
+
+	/* Acquire the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	status = ipsec_session_create(ipsec, sa_params, &sa->s);
+	if (status) {
+		/* Free the allocated SADB entry. */
+		ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+		ipsec->n_sa_free_id++;
+
+		return status;
+	}
+
+	/* Validate the new SA. */
+	sa->valid = 1;
+	*id = sa_id;
+
+	return 0;
+}
+
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id)
+{
+	struct ipsec_sa *sa;
+
+	/* Check the input parameters. */
+	if (!ipsec || (sa_id >= ipsec->n_sa_max))
+		return;
+
+	/* Release the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	ipsec_session_free(ipsec, &sa->s);
+
+	/* Free the SADB entry. */
+	ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+	ipsec->n_sa_free_id++;
+
+	/* Invalidate the SA. */
+	sa->valid = 0;
+}
diff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h
new file mode 100644
index 0000000000..52962f8845
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__
+#define __INCLUDE_RTE_SWX_IPSEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE SWX Internet Protocol Security (IPsec)
+ *
+ * The IPsec block is a companion block for the SWX pipeline used to provide IPsec support to the
+ * pipeline. The block is external to the pipeline, hence it needs to be explicitly instantiated by
+ * the user and connected to a pipeline instance through the pipeline I/O ports.
+ *
+ * Main features:
+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text
+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.
+ *
+ * Security Association (SA):
+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is
+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.
+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then
+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field
+ *   is typically written by the pipeline before sending the packet to the IPsec block.
+ *
+ * Packet format:
+ * - IPsec block input packet (i.e. pipeline output packet):
+ *	- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.
+ *	- IPv4 header.
+ *	- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.
+ * - IPsec block output packet (i.e. pipeline input packet):
+ *	- IPv4 header.
+ *	- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.
+ *
+ * SA update procedure:
+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().
+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().
+ * - To update an existing SA, the control plane has to follow the following steps:
+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can
+ *      fail, for example when the SA table is full.
+ *   2. Wait until no more packets are using the old SA.
+ *   3. Delete the old SA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <rte_compat.h>
+#include <rte_crypto_sym.h>
+
+/**
+ * IPsec Setup API
+ */
+
+/** IPsec instance opaque data structure. */
+struct rte_swx_ipsec;
+
+/** Name size. */
+#ifndef RTE_SWX_IPSEC_NAME_SIZE
+#define RTE_SWX_IPSEC_NAME_SIZE 64
+#endif
+
+/** Maximum burst size. */
+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX
+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256
+#endif
+
+/** IPsec burst sizes. */
+struct rte_swx_ipsec_burst_size {
+	/** Input ring read burst size. */
+	uint32_t ring_rd;
+
+	/** Output ring write burst size. */
+	uint32_t ring_wr;
+
+	/** Crypto device request queue write burst size. */
+	uint32_t crypto_wr;
+
+	/** Crypto device response queue read burst size. */
+	uint32_t crypto_rd;
+};
+
+/**
+ * IPsec instance configuration parameters
+ *
+ */
+struct rte_swx_ipsec_params {
+	/** Input packet queue. */
+	const char *ring_in_name;
+
+	/** Output packet queue.  */
+	const char *ring_out_name;
+
+	/** Crypto device name. */
+	const char *crypto_dev_name;
+
+	/** Crypto device queue pair ID. */
+	uint32_t crypto_dev_queue_pair_id;
+
+	/** Burst size. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/** Maximum number of SAs. */
+	uint32_t n_sa_max;
+};
+
+/**
+ * IPsec input packet meta-data
+ *
+ */
+struct rte_swx_ipsec_input_packet_metadata {
+	/* SA ID. */
+	uint32_t sa_id;
+};
+
+/**
+ * IPsec instance find
+ *
+ * @param[in] name
+ *   IPsec instance name.
+ * @return
+ *   Valid IPsec instance handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name);
+
+/**
+ * IPsec instance create
+ *
+ * @param[out] ipsec
+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this
+ *   function returns successfully.
+ * @param[in] name
+ *   IPsec instance unique name.
+ * @param[in] params
+ *   IPsec instance configuration parameters.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node);
+
+/**
+ * IPsec instance free
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);
+
+/**
+ * IPsec Data Plane API
+ */
+
+/**
+ * IPsec instance run
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);
+
+/*
+ * IPsec Control Plane API
+ */
+
+/** Maximum key size in bytes. */
+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64
+
+/** IPsec SA crypto cipher parameters. */
+struct rte_swx_ipsec_sa_cipher_params {
+	/** Cipher algorithm. */
+	enum rte_crypto_cipher_algorithm alg;
+
+	/** Cipher key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Cipher key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto authentication parameters. */
+struct rte_swx_ipsec_sa_authentication_params {
+	/** Authentication algorithm. */
+	enum rte_crypto_auth_algorithm alg;
+
+	/** Authentication key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Authentication key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */
+struct rte_swx_ipsec_sa_aead_params {
+	/** AEAD algorithm. */
+	enum rte_crypto_aead_algorithm alg;
+
+	/** AEAD key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** AEAD key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec protocol encapsulation parameters. */
+struct rte_swx_ipsec_sa_encap_params {
+	/** Encapsulating Security Payload (ESP) header. */
+	struct {
+		/** Security Parameters Index (SPI) field. */
+		uint32_t spi;
+	} esp;
+
+	/** Tunnel mode when non-zero, transport mode when zero. */
+	int tunnel_mode;
+
+	/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */
+	int tunnel_ipv4;
+
+	/** Tunnel parameters. Valid for tunnel mode only. */
+	union {
+		/** IPv4 header. */
+		struct {
+			/** Source address. */
+			struct in_addr src_addr;
+
+			/** Destination address. */
+			struct in_addr dst_addr;
+		} ipv4;
+
+		/** IPv6 header. */
+		struct {
+			/** Source address. */
+			struct in6_addr src_addr;
+
+			/** Destination address. */
+			struct in6_addr dst_addr;
+		} ipv6;
+	} tunnel;
+};
+
+/** IPsec Security Association (SA) parameters. */
+struct rte_swx_ipsec_sa_params {
+	/** Crypto operation: encrypt when non-zero, decrypt when zero. */
+	int encrypt;
+
+	/** Crypto operation parameters. */
+	struct {
+		RTE_STD_C11
+		union {
+			struct {
+				/** Crypto cipher operation parameters. */
+				struct rte_swx_ipsec_sa_cipher_params cipher;
+
+				/** Crypto authentication operation parameters. */
+				struct rte_swx_ipsec_sa_authentication_params auth;
+			} cipher_auth;
+
+			/** Crypto AEAD operation parameters. */
+			struct rte_swx_ipsec_sa_aead_params aead;
+		};
+
+		/** Non-zero for AEAD, zero for cipher & authentication. */
+		int is_aead;
+	} crypto;
+
+	/** Packet encasulation parameters. */
+	struct rte_swx_ipsec_sa_encap_params encap;
+};
+
+/**
+ * IPsec SA add
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_params
+ *   SA parameters.
+ * @params[out] sa_id.
+ *   On success, the SA ID.
+ * @return
+ *   0 on success or error code otherwise.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *sa_id);
+
+/**
+ * IPsec SA delete
+ *
+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used
+ * by the Data Plane.
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @params[in] sa_id.
+ *   The SA ID.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id);
+
+/**
+ * IPsec SA read from string
+ *
+ * IPsec SA syntax:
+ *
+ * <sa>
+ *    : encrypt <crypto_params> <encap_params>
+ *    | decrypt <crypto_params> <encap_params>
+ *    ;
+ *
+ * <crypto_params>
+ *    : <cipher> <auth>
+ *    | <aead>
+ *    ;
+ *
+ * <cipher>
+ *    : cipher <ciher_alg> key <cipher_key>
+ *    | cipher <cipher_alg>
+ *    ;
+ *
+ * <auth>
+ *    : auth <authentication_alg> key <authentication_key>
+ *    | auth <authentication_alg>
+ *    ;
+ *
+ * <aead>
+ *    : aead <aead_alg> key <aead_key>
+ *    ;
+ *
+ * <encap_params>
+ *    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+ *    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+ *    | esp spi <spi> transport
+ *    ;
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] string
+ *   String containing the SA.
+ * @param[in,out] is_blank_or_comment
+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when
+ *   *string* contains a blank or comment line and to zero otherwise.
+ * @param[in,out] errmsg
+ *   On error, when its input value is not NULL, this argument points to a string with details on
+ *   the detected error.
+ * @return
+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.
+ */
+__rte_experimental
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 184f45a6b7..58e34459c3 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -155,4 +155,13 @@ EXPERIMENTAL {
 	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
+
+	#added in 23.03
+	rte_swx_ipsec_create;
+	rte_swx_ipsec_find;
+	rte_swx_ipsec_free;
+	rte_swx_ipsec_run;
+	rte_swx_ipsec_sa_add;
+	rte_swx_ipsec_sa_delete;
+	rte_swx_ipsec_sa_read;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 02/11] examples/pipeline: rework memory pool support
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 01/11] " Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Rework the memory pool CLI command to accommodate the MBUF private
meta-data area size parameter.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 72 ++++++++++-------
 examples/pipeline/examples/fib.cli            |  2 +-
 examples/pipeline/examples/hash_func.cli      |  2 +-
 examples/pipeline/examples/l2fwd.cli          |  2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |  2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |  2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |  2 +-
 examples/pipeline/examples/learner.cli        |  2 +-
 examples/pipeline/examples/meter.cli          |  2 +-
 examples/pipeline/examples/mirroring.cli      |  2 +-
 examples/pipeline/examples/recirculation.cli  |  2 +-
 examples/pipeline/examples/registers.cli      |  2 +-
 examples/pipeline/examples/selector.cli       |  2 +-
 examples/pipeline/examples/varbit.cli         |  2 +-
 examples/pipeline/examples/vxlan.cli          |  2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |  2 +-
 examples/pipeline/obj.c                       | 80 +------------------
 examples/pipeline/obj.h                       | 27 -------
 18 files changed, 63 insertions(+), 146 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 8013541c4b..c014598cf1 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -192,72 +192,88 @@ parse_table_entry(struct rte_swx_ctl_pipeline *p,
 }
 
 static const char cmd_mempool_help[] =
-"mempool <mempool_name>\n"
-"   buffer <buffer_size>\n"
-"   pool <pool_size>\n"
-"   cache <cache_size>\n"
-"   cpu <cpu_id>\n";
+"mempool <mempool_name> "
+"meta <mbuf_private_size> "
+"pkt <pkt_buffer_size> "
+"pool <pool_size> "
+"cache <cache_size> "
+"numa <numa_node>\n";
 
 static void
 cmd_mempool(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
+	    uint32_t n_tokens,
+	    char *out,
+	    size_t out_size,
+	    void *obj __rte_unused)
 {
-	struct mempool_params p;
-	char *name;
-	struct mempool *mempool;
+	struct rte_mempool *mp;
+	char *mempool_name;
+	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
 
-	if (n_tokens != 10) {
+	if (n_tokens != 12) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	name = tokens[1];
+	mempool_name = tokens[1];
+
+	if (strcmp(tokens[2], "meta")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
+		return;
+	}
 
-	if (strcmp(tokens[2], "buffer") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
+	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
+	if (strcmp(tokens[4], "pkt")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
 		return;
 	}
 
-	if (strcmp(tokens[4], "pool") != 0) {
+	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
+		return;
+	}
+
+	if (strcmp(tokens[6], "pool")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
 		return;
 	}
 
-	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
+	if (parser_read_uint32(&pool_size, tokens[7])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
 		return;
 	}
 
-	if (strcmp(tokens[6], "cache") != 0) {
+	if (strcmp(tokens[8], "cache")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
+	if (parser_read_uint32(&cache_size, tokens[9])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
 		return;
 	}
 
-	if (strcmp(tokens[8], "cpu") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+	if (strcmp(tokens[10], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+	if (parser_read_uint32(&numa_node, tokens[11])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	mempool = mempool_create(obj, name, &p);
-	if (mempool == NULL) {
+	mp = rte_pktmbuf_pool_create(mempool_name,
+				     pool_size,
+				     cache_size,
+				     mbuf_private_size,
+				     pkt_buffer_size,
+				     numa_node);
+	if (!mp) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 4e30c1320f..2450dc9ca4 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/fib.c /tmp/fib.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index b2e219e4c9..3840e47a2b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index 27e37021b9..c5505df296 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 11bb4543b9..bd700707f5 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index 8724dae3b0..aa64b32ab2 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 4db0a0dc56..619d17474f 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 6c8aa3921e..42184be1c8 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/learner.c /tmp/learner.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 21582554b9..0fa38b16b8 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/meter.c /tmp/meter.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 1d439e04d3..e7391de74b 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 52d0894f12..965c870fd4 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 3516f76a5b..aa775f71e5 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/registers.c /tmp/registers.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0e251b657..f0608de184 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/selector.c /tmp/selector.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 0f89990471..648b8882c3 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 1fbd1be6e4..4565adfaea 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index adc7f73312..510c2ad870 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index b7e2316eec..4aaec02d9c 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -13,18 +13,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
 
-/*
- * mempool
- */
-TAILQ_HEAD(mempool_list, mempool);
-
 /*
  * link
  */
@@ -39,75 +33,10 @@ TAILQ_HEAD(ring_list, ring);
  * obj
  */
 struct obj {
-	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
 };
 
-/*
- * mempool
- */
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
-
-struct mempool *
-mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
-{
-	struct mempool *mempool;
-	struct rte_mempool *m;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		mempool_find(obj, name) ||
-		(params == NULL) ||
-		(params->buffer_size < BUFFER_SIZE_MIN) ||
-		(params->pool_size == 0))
-		return NULL;
-
-	/* Resource create */
-	m = rte_pktmbuf_pool_create(
-		name,
-		params->pool_size,
-		params->cache_size,
-		0,
-		params->buffer_size - sizeof(struct rte_mbuf),
-		params->cpu_id);
-
-	if (m == NULL)
-		return NULL;
-
-	/* Node allocation */
-	mempool = calloc(1, sizeof(struct mempool));
-	if (mempool == NULL) {
-		rte_mempool_free(m);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(mempool->name, name, sizeof(mempool->name));
-	mempool->m = m;
-	mempool->buffer_size = params->buffer_size;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
-
-	return mempool;
-}
-
-struct mempool *
-mempool_find(struct obj *obj, const char *name)
-{
-	struct mempool *mempool;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(mempool, &obj->mempool_list, node)
-		if (strcmp(mempool->name, name) == 0)
-			return mempool;
-
-	return NULL;
-}
-
 /*
  * link
  */
@@ -172,7 +101,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct rte_eth_conf port_conf;
 	struct link *link;
 	struct link_params_rss *rss;
-	struct mempool *mempool;
+	struct rte_mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
 	uint16_t port_id = 0;
@@ -194,8 +123,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
 
-	mempool = mempool_find(obj, params->rx.mempool_name);
-	if (mempool == NULL)
+	mempool = rte_mempool_lookup(params->rx.mempool_name);
+	if (!mempool)
 		return NULL;
 
 	rss = params->rx.rss;
@@ -252,7 +181,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			params->rx.queue_size,
 			cpu_id,
 			NULL,
-			mempool->m);
+			mempool);
 
 		if (status < 0)
 			return NULL;
@@ -422,7 +351,6 @@ obj_init(void)
 	if (!obj)
 		return NULL;
 
-	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 8ea1c414c2..4ea610ceed 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <sys/queue.h>
 
-#include <rte_mempool.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
@@ -24,32 +23,6 @@ struct obj;
 struct obj *
 obj_init(void);
 
-/*
- * mempool
- */
-struct mempool_params {
-	uint32_t buffer_size;
-	uint32_t pool_size;
-	uint32_t cache_size;
-	uint32_t cpu_id;
-};
-
-struct mempool {
-	TAILQ_ENTRY(mempool) node;
-	char name[NAME_SIZE];
-	struct rte_mempool *m;
-	uint32_t buffer_size;
-};
-
-struct mempool *
-mempool_create(struct obj *obj,
-	       const char *name,
-	       struct mempool_params *params);
-
-struct mempool *
-mempool_find(struct obj *obj,
-	     const char *name);
-
 /*
  * link
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 03/11] examples/pipeline: streamline ring support
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 01/11] " Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Remove redundant ring related code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 24 ++++++++++------
 examples/pipeline/obj.c | 63 -----------------------------------------
 examples/pipeline/obj.h | 21 --------------
 3 files changed, 15 insertions(+), 93 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index c014598cf1..471dfbad38 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -11,6 +11,7 @@
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_ring.h>
 #include <rte_swx_port_ethdev.h>
 #include <rte_swx_port_ring.h>
 #include <rte_swx_port_source_sink.h>
@@ -491,11 +492,11 @@ cmd_ring(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct ring_params p;
+	struct rte_ring *r;
 	char *name;
-	struct ring *ring;
+	uint32_t size, numa_node;
 
 	if (n_tokens != 6) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
@@ -504,28 +505,32 @@ cmd_ring(char **tokens,
 
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "size") != 0) {
+	if (strcmp(tokens[2], "size")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
+	if (parser_read_uint32(&size, tokens[3])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
 		return;
 	}
 
-	if (strcmp(tokens[4], "numa") != 0) {
+	if (strcmp(tokens[4], "numa")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
+	if (parser_read_uint32(&numa_node, tokens[5])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	ring = ring_create(obj, name, &p);
-	if (!ring) {
+	r = rte_ring_create(
+		name,
+		size,
+		(int)numa_node,
+		RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (!r) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
@@ -2998,6 +3003,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 4aaec02d9c..f1a50133f4 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -24,17 +24,11 @@
  */
 TAILQ_HEAD(link_list, link);
 
-/*
- * ring
- */
-TAILQ_HEAD(ring_list, ring);
-
 /*
  * obj
  */
 struct obj {
 	struct link_list link_list;
-	struct ring_list ring_list;
 };
 
 /*
@@ -283,62 +277,6 @@ link_next(struct obj *obj, struct link *link)
 		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
 }
 
-/*
- * ring
- */
-struct ring *
-ring_create(struct obj *obj, const char *name, struct ring_params *params)
-{
-	struct ring *ring;
-	struct rte_ring *r;
-	unsigned int flags = RING_F_SP_ENQ | RING_F_SC_DEQ;
-
-	/* Check input params */
-	if (!name || ring_find(obj, name) || !params || !params->size)
-		return NULL;
-
-	/**
-	 * Resource create
-	 */
-	r = rte_ring_create(
-		name,
-		params->size,
-		params->numa_node,
-		flags);
-	if (!r)
-		return NULL;
-
-	/* Node allocation */
-	ring = calloc(1, sizeof(struct ring));
-	if (!ring) {
-		rte_ring_free(r);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(ring->name, name, sizeof(ring->name));
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->ring_list, ring, node);
-
-	return ring;
-}
-
-struct ring *
-ring_find(struct obj *obj, const char *name)
-{
-	struct ring *ring;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(ring, &obj->ring_list, node)
-		if (strcmp(ring->name, name) == 0)
-			return ring;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -352,7 +290,6 @@ obj_init(void)
 		return NULL;
 
 	TAILQ_INIT(&obj->link_list);
-	TAILQ_INIT(&obj->ring_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 4ea610ceed..dbbc6d39a0 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -73,25 +73,4 @@ link_find(struct obj *obj, const char *name);
 struct link *
 link_next(struct obj *obj, struct link *link);
 
-/*
- * ring
- */
-struct ring_params {
-	uint32_t size;
-	uint32_t numa_node;
-};
-
-struct ring {
-	TAILQ_ENTRY(ring) node;
-	char name[NAME_SIZE];
-};
-
-struct ring *
-ring_create(struct obj *obj,
-	   const char *name,
-	   struct ring_params *params);
-
-struct ring *
-ring_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 04/11] examples/pipeline: streamline the Ethernet device support
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Streamline the Ethernet device support code and remove redundant code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c  | 175 ++++++++++++++++++------------------
 examples/pipeline/main.c |  12 +--
 examples/pipeline/obj.c  | 186 +++++++++------------------------------
 examples/pipeline/obj.h  |  51 ++---------
 4 files changed, 136 insertions(+), 288 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 471dfbad38..37fc6f9551 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -292,16 +292,14 @@ cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct link_params p;
-	struct link_params_rss rss;
-	struct link *link;
+	struct ethdev_params p = {0};
+	struct ethdev_params_rss rss = {0};
 	char *name;
+	int status;
 
-	memset(&p, 0, sizeof(p));
-
-	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
+	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -377,111 +375,98 @@ cmd_ethdev(char **tokens,
 		}
 	}
 
-	link = link_create(obj, name, &p);
-	if (link == NULL) {
+	status = ethdev_config(name, &p);
+	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
 }
 
-/* Print the link stats and info */
 static void
-print_link_info(struct link *link, char *out, size_t out_size)
+ethdev_show(uint16_t port_id, char **out, size_t *out_size)
 {
-	struct rte_eth_stats stats;
-	struct rte_ether_addr mac_addr;
-	struct rte_eth_link eth_link;
-	uint16_t mtu;
-	int ret;
-
-	memset(&stats, 0, sizeof(stats));
-	rte_eth_stats_get(link->port_id, &stats);
-
-	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
-	if (ret != 0) {
-		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	ret = rte_eth_link_get(link->port_id, &eth_link);
-	if (ret < 0) {
-		snprintf(out, out_size, "\n%s: link get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	rte_eth_dev_get_mtu(link->port_id, &mtu);
-
-	snprintf(out, out_size,
-		"\n"
-		"%s: flags=<%s> mtu %u\n"
-		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
-		"\tport# %u  speed %s\n"
-		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
-		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tTX errors %" PRIu64"\n",
-		link->name,
-		eth_link.link_status == 0 ? "DOWN" : "UP",
-		mtu,
-		RTE_ETHER_ADDR_BYTES(&mac_addr),
-		link->n_rxq,
-		link->n_txq,
-		link->port_id,
-		rte_eth_link_speed_to_str(eth_link.link_speed),
-		stats.ipackets,
-		stats.ibytes,
-		stats.ierrors,
-		stats.imissed,
-		stats.rx_nombuf,
-		stats.opackets,
-		stats.obytes,
-		stats.oerrors);
+	char name[RTE_ETH_NAME_MAX_LEN] = {0};
+	struct rte_eth_dev_info info = {0};
+	struct rte_eth_stats stats = {0};
+	struct rte_ether_addr addr = {0};
+	struct rte_eth_link link = {0};
+	uint32_t length;
+	uint16_t mtu = 0;
+
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return;
+
+	rte_eth_dev_get_name_by_port(port_id, name);
+	rte_eth_dev_info_get(port_id, &info);
+	rte_eth_stats_get(port_id, &stats);
+	rte_eth_macaddr_get(port_id, &addr);
+	rte_eth_link_get(port_id, &link);
+	rte_eth_dev_get_mtu(port_id, &mtu);
+
+	snprintf(*out, *out_size,
+		 "%s: flags=<%s> mtu %u\n"
+		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
+		 "\tport# %u  speed %s\n"
+		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
+		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tTX errors %" PRIu64"\n\n",
+		 name,
+		 link.link_status ? "UP" : "DOWN",
+		 mtu,
+		 RTE_ETHER_ADDR_BYTES(&addr),
+		 info.nb_rx_queues,
+		 info.nb_tx_queues,
+		 port_id,
+		 rte_eth_link_speed_to_str(link.link_speed),
+		 stats.ipackets,
+		 stats.ibytes,
+		 stats.ierrors,
+		 stats.imissed,
+		 stats.rx_nombuf,
+		 stats.opackets,
+		 stats.obytes,
+		 stats.oerrors);
+
+	length = strlen(*out);
+	*out_size -= length;
+	*out += length;
 }
 
-/*
- * ethdev show [<ethdev_name>]
- */
+
+static char cmd_ethdev_show_help[] =
+"ethdev show [ <ethdev_name> ]\n";
+
 static void
 cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
-	      void *obj)
+	      void *obj __rte_unused)
 {
-	struct link *link;
-	char *link_name;
+	uint16_t port_id;
 
 	if (n_tokens != 2 && n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (n_tokens == 2) {
-		link = link_next(obj, NULL);
-
-		while (link != NULL) {
-			out_size = out_size - strlen(out);
-			out = &out[strlen(out)];
-
-			print_link_info(link, out, out_size);
-			link = link_next(obj, link);
-		}
-	} else {
-		out_size = out_size - strlen(out);
-		out = &out[strlen(out)];
+	/* Single device. */
+	if (n_tokens == 3) {
+		int status;
 
-		link_name = tokens[2];
-		link = link_find(obj, link_name);
+		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
+		if (status)
+			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
 
-		if (link == NULL) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-					"Link does not exist");
-			return;
-		}
-		print_link_info(link, out, out_size);
+		ethdev_show(port_id, &out, &out_size);
+		return;
 	}
+
+	/*  All devices. */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		if (rte_eth_dev_is_valid_port(port_id))
+			ethdev_show(port_id, &out, &out_size);
 }
 
 static const char cmd_ring_help[] =
@@ -3003,6 +2988,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tethdev show\n"
 			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -3038,9 +3024,16 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "ethdev") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
-		return;
+	if (!strcmp(tokens[0], "ethdev")) {
+		if (n_tokens == 1) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
+			return;
+		}
+
+		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
+			return;
+		}
 	}
 
 	if (strcmp(tokens[0], "ring") == 0) {
diff --git a/examples/pipeline/main.c b/examples/pipeline/main.c
index 6fb839f4cb..da5dd2b3f6 100644
--- a/examples/pipeline/main.c
+++ b/examples/pipeline/main.c
@@ -135,7 +135,6 @@ int
 main(int argc, char **argv)
 {
 	struct conn *conn;
-	struct obj *obj;
 	int status;
 
 	/* Parse application arguments */
@@ -150,13 +149,6 @@ main(int argc, char **argv)
 		return status;
 	};
 
-	/* Obj */
-	obj = obj_init();
-	if (!obj) {
-		printf("Error: Obj initialization failed (%d)\n", status);
-		return status;
-	}
-
 	/* Thread */
 	status = thread_init();
 	if (status) {
@@ -174,10 +166,10 @@ main(int argc, char **argv)
 		cli_script_process(app.script_name,
 			app.conn.msg_in_len_max,
 			app.conn.msg_out_len_max,
-			obj);
+			NULL);
 
 	/* Connectivity */
-	app.conn.msg_handle_arg = obj;
+	app.conn.msg_handle_arg = NULL;
 	conn = conn_init(&app.conn);
 	if (!conn) {
 		printf("Error: Connectivity initialization failed (%d)\n",
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index f1a50133f4..a5c09e7219 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -4,35 +4,14 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#ifdef RTE_EXEC_ENV_LINUX
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#endif
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_ctl.h>
 
 #include "obj.h"
 
 /*
- * link
- */
-TAILQ_HEAD(link_list, link);
-
-/*
- * obj
- */
-struct obj {
-	struct link_list link_list;
-};
-
-/*
- * link
+ * ethdev
  */
 static struct rte_eth_conf port_conf_default = {
 	.link_speeds = 0,
@@ -59,7 +38,7 @@ static struct rte_eth_conf port_conf_default = {
 static int
 rss_setup(uint16_t port_id,
 	uint16_t reta_size,
-	struct link_params_rss *rss)
+	struct ethdev_params_rss *rss)
 {
 	struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
 	uint32_t i;
@@ -88,69 +67,64 @@ rss_setup(uint16_t port_id,
 	return status;
 }
 
-struct link *
-link_create(struct obj *obj, const char *name, struct link_params *params)
+int
+ethdev_config(const char *name, struct ethdev_params *params)
 {
 	struct rte_eth_dev_info port_info;
 	struct rte_eth_conf port_conf;
-	struct link *link;
-	struct link_params_rss *rss;
+	struct ethdev_params_rss *rss;
 	struct rte_mempool *mempool;
-	uint32_t cpu_id, i;
-	int status;
+	uint32_t i;
+	int numa_node, status;
 	uint16_t port_id = 0;
 
 	/* Check input params */
-	if ((name == NULL) ||
-		link_find(obj, name) ||
-		(params == NULL) ||
-		(params->rx.n_queues == 0) ||
-		(params->rx.queue_size == 0) ||
-		(params->tx.n_queues == 0) ||
-		(params->tx.queue_size == 0))
-		return NULL;
+	if (!name ||
+	    !name[0] ||
+	    !params ||
+	    !params->rx.n_queues ||
+	    !params->rx.queue_size ||
+	    !params->tx.n_queues ||
+	    !params->tx.queue_size)
+		return -EINVAL;
 
 	status = rte_eth_dev_get_port_by_name(name, &port_id);
 	if (status)
-		return NULL;
+		return -EINVAL;
 
-	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
-		return NULL;
+	status = rte_eth_dev_info_get(port_id, &port_info);
+	if (status)
+		return -EINVAL;
 
 	mempool = rte_mempool_lookup(params->rx.mempool_name);
 	if (!mempool)
-		return NULL;
+		return -EINVAL;
 
 	rss = params->rx.rss;
 	if (rss) {
-		if ((port_info.reta_size == 0) ||
-			(port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512))
-			return NULL;
+		if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512)
+			return -EINVAL;
 
-		if ((rss->n_queues == 0) ||
-			(rss->n_queues >= LINK_RXQ_RSS_MAX))
-			return NULL;
+		if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX)
+			return -EINVAL;
 
 		for (i = 0; i < rss->n_queues; i++)
 			if (rss->queue_id[i] >= port_info.max_rx_queues)
-				return NULL;
+				return -EINVAL;
 	}
 
-	/**
-	 * Resource create
-	 */
 	/* Port */
 	memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
 	if (rss) {
+		uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP;
+
 		port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
-		port_conf.rx_adv_conf.rss_conf.rss_hf =
-			(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
-			port_info.flow_type_rss_offloads;
+		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads;
 	}
 
-	cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
-	if (cpu_id == (uint32_t) SOCKET_ID_ANY)
-		cpu_id = 0;
+	numa_node = rte_eth_dev_socket_id(port_id);
+	if (numa_node == SOCKET_ID_ANY)
+		numa_node = 0;
 
 	status = rte_eth_dev_configure(
 		port_id,
@@ -159,12 +133,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		&port_conf);
 
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (params->promiscuous) {
 		status = rte_eth_promiscuous_enable(port_id);
-		if (status != 0)
-			return NULL;
+		if (status)
+			return -EINVAL;
 	}
 
 	/* Port RX */
@@ -173,12 +147,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->rx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL,
 			mempool);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port TX */
@@ -187,24 +161,24 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->tx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port start */
 	status = rte_eth_dev_start(port_id);
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (rss) {
 		status = rss_setup(port_id, port_info.reta_size, rss);
 
 		if (status) {
 			rte_eth_dev_stop(port_id);
-			return NULL;
+			return -EINVAL;
 		}
 	}
 
@@ -212,84 +186,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	status = rte_eth_dev_set_link_up(port_id);
 	if ((status < 0) && (status != -ENOTSUP)) {
 		rte_eth_dev_stop(port_id);
-		return NULL;
-	}
-
-	/* Node allocation */
-	link = calloc(1, sizeof(struct link));
-	if (link == NULL) {
-		rte_eth_dev_stop(port_id);
-		return NULL;
+		return -EINVAL;
 	}
 
-	/* Node fill in */
-	strlcpy(link->name, name, sizeof(link->name));
-	link->port_id = port_id;
-	link->n_rxq = params->rx.n_queues;
-	link->n_txq = params->tx.n_queues;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->link_list, link, node);
-
-	return link;
-}
-
-int
-link_is_up(struct obj *obj, const char *name)
-{
-	struct rte_eth_link link_params;
-	struct link *link;
-
-	/* Check input params */
-	if (!obj || !name)
-		return 0;
-
-	link = link_find(obj, name);
-	if (link == NULL)
-		return 0;
-
-	/* Resource */
-	if (rte_eth_link_get(link->port_id, &link_params) < 0)
-		return 0;
-
-	return (link_params.link_status == RTE_ETH_LINK_DOWN) ? 0 : 1;
-}
-
-struct link *
-link_find(struct obj *obj, const char *name)
-{
-	struct link *link;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(link, &obj->link_list, node)
-		if (strcmp(link->name, name) == 0)
-			return link;
-
-	return NULL;
-}
-
-struct link *
-link_next(struct obj *obj, struct link *link)
-{
-	return (link == NULL) ?
-		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
-}
-
-/*
- * obj
- */
-struct obj *
-obj_init(void)
-{
-	struct obj *obj;
-
-	obj = calloc(1, sizeof(struct obj));
-	if (!obj)
-		return NULL;
-
-	TAILQ_INIT(&obj->link_list);
-
-	return obj;
+	return 0;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index dbbc6d39a0..fb091f4ba7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -6,41 +6,25 @@
 #define _INCLUDE_OBJ_H_
 
 #include <stdint.h>
-#include <sys/queue.h>
-
-#include <rte_swx_pipeline.h>
-#include <rte_swx_ctl.h>
-
-#ifndef NAME_SIZE
-#define NAME_SIZE 64
-#endif
-
-/*
- * obj
- */
-struct obj;
-
-struct obj *
-obj_init(void);
 
 /*
- * link
+ * ethdev
  */
-#ifndef LINK_RXQ_RSS_MAX
-#define LINK_RXQ_RSS_MAX                                   16
+#ifndef ETHDEV_RXQ_RSS_MAX
+#define ETHDEV_RXQ_RSS_MAX 16
 #endif
 
-struct link_params_rss {
-	uint32_t queue_id[LINK_RXQ_RSS_MAX];
+struct ethdev_params_rss {
+	uint32_t queue_id[ETHDEV_RXQ_RSS_MAX];
 	uint32_t n_queues;
 };
 
-struct link_params {
+struct ethdev_params {
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
 		const char *mempool_name;
-		struct link_params_rss *rss;
+		struct ethdev_params_rss *rss;
 	} rx;
 
 	struct {
@@ -51,26 +35,7 @@ struct link_params {
 	int promiscuous;
 };
 
-struct link {
-	TAILQ_ENTRY(link) node;
-	char name[NAME_SIZE];
-	uint16_t port_id;
-	uint32_t n_rxq;
-	uint32_t n_txq;
-};
-
-struct link *
-link_create(struct obj *obj,
-	    const char *name,
-	    struct link_params *params);
-
 int
-link_is_up(struct obj *obj, const char *name);
-
-struct link *
-link_find(struct obj *obj, const char *name);
-
-struct link *
-link_next(struct obj *obj, struct link *link);
+ethdev_config(const char *name, struct ethdev_params *params);
 
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 05/11] examples/pipeline: support crypto devices
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add support for crypto devices in the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c | 61 +++++++++++++++++++++++++++++++++++++++++
 examples/pipeline/obj.h | 11 ++++++++
 2 files changed, 72 insertions(+)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index a5c09e7219..078395c6aa 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -7,6 +7,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_cryptodev.h>
 
 #include "obj.h"
 
@@ -191,3 +192,63 @@ ethdev_config(const char *name, struct ethdev_params *params)
 
 	return 0;
 }
+
+/*
+ * cryptodev
+ */
+int
+cryptodev_config(const char *name, struct cryptodev_params *params)
+{
+	struct rte_cryptodev_info dev_info;
+	struct rte_cryptodev_config dev_conf;
+	struct rte_cryptodev_qp_conf queue_conf;
+	uint8_t dev_id;
+	uint32_t socket_id, i;
+	int status;
+
+	/* Check input parameters. */
+	if (!name ||
+	    !params->n_queue_pairs ||
+	    !params->queue_size)
+		return -EINVAL;
+
+	/* Find the crypto device. */
+	status = rte_cryptodev_get_dev_id(name);
+	if (status < 0)
+		return -EINVAL;
+
+	dev_id = (uint8_t)status;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->n_queue_pairs > dev_info.max_nb_queue_pairs)
+		return -EINVAL;
+
+	socket_id = rte_cryptodev_socket_id(dev_id);
+
+	/* Configure the crypto device. */
+	dev_conf.socket_id = socket_id;
+	dev_conf.nb_queue_pairs = params->n_queue_pairs;
+	dev_conf.ff_disable = 0;
+
+	status = rte_cryptodev_configure(dev_id, &dev_conf);
+	if (status)
+		return status;
+
+	/* Configure the crypto device queue pairs. */
+	queue_conf.nb_descriptors = params->queue_size;
+	queue_conf.mp_session = NULL;
+	queue_conf.mp_session_private = NULL;
+
+	for (i = 0; i < params->n_queue_pairs; i++) {
+		status = rte_cryptodev_queue_pair_setup(dev_id, i, &queue_conf, socket_id);
+		if (status)
+			return status;
+	}
+
+	/* Start the crypto device. */
+	status = rte_cryptodev_start(dev_id);
+	if (status)
+		return status;
+
+	return 0;
+}
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index fb091f4ba7..0f103be6a7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -38,4 +38,15 @@ struct ethdev_params {
 int
 ethdev_config(const char *name, struct ethdev_params *params);
 
+/*
+ * cryptodev
+ */
+struct cryptodev_params {
+	uint32_t n_queue_pairs;
+	uint32_t queue_size;
+};
+
+int
+cryptodev_config(const char *name, struct cryptodev_params *params);
+
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 06/11] examples/pipeline: add CLI command for crypto device
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (4 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI command for the configuration of crypto devices.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 37fc6f9551..47c1adf772 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -521,6 +521,58 @@ cmd_ring(char **tokens,
 	}
 }
 
+static const char cmd_cryptodev_help[] =
+"cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
+
+static void
+cmd_cryptodev(char **tokens,
+	      uint32_t n_tokens,
+	      char *out,
+	      size_t out_size,
+	      void *obj __rte_unused)
+{
+	struct cryptodev_params params;
+	char *cryptodev_name;
+	int status;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	cryptodev_name = tokens[1];
+
+	if (strcmp(tokens[2], "queues")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
+		return;
+	}
+
+	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
+		return;
+	}
+
+	if (strcmp(tokens[4], "qsize")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
+		return;
+	}
+
+
+	if (parser_read_uint32(&params.queue_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
+		return;
+	}
+
+	status = cryptodev_config(cryptodev_name, &params);
+	if (status)
+		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
+}
+
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2990,6 +3042,7 @@ cmd_help(char **tokens,
 			"\tethdev\n"
 			"\tethdev show\n"
 			"\tring\n"
+			"\tcryptodev\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3041,6 +3094,11 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3296,6 +3354,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
+		return;
+	}
+
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 07/11] examples/pipeline: add IPsec CLI commands
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (5 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands for IPsec block configuration.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 298 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 298 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 47c1adf772..efe2ff61db 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -18,6 +18,7 @@
 #include <rte_swx_port_fd.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
+#include <rte_swx_ipsec.h>
 
 #include "cli.h"
 
@@ -2909,6 +2910,263 @@ cmd_pipeline_mirror_session(char **tokens,
 	}
 }
 
+static const char cmd_ipsec_create_help[] =
+"ipsec <ipsec_instance_name> create "
+"in <ring_in_name> out <ring_out_name> "
+"cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
+"bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
+"samax <n_sa_max> "
+"numa <numa_node>\n";
+
+static void
+cmd_ipsec_create(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec_params p;
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t numa_node;
+	int status;
+
+	if (n_tokens != 20) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+
+	if (strcmp(tokens[2], "create")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
+		return;
+	}
+
+	if (strcmp(tokens[3], "in")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+		return;
+	}
+
+	p.ring_in_name = tokens[4];
+
+	if (strcmp(tokens[5], "out")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+		return;
+	}
+
+	p.ring_out_name = tokens[6];
+
+	if (strcmp(tokens[7], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	p.crypto_dev_name = tokens[8];
+
+	if (strcmp(tokens[9], "cryptoq")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
+		return;
+	}
+
+	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
+		return;
+	}
+
+	if (strcmp(tokens[11], "bsz")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
+		return;
+	}
+
+	if (strcmp(tokens[16], "samax")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
+		return;
+	}
+
+	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
+		return;
+	}
+
+	if (strcmp(tokens[18], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
+		return;
+	}
+
+	if (parser_read_uint32(&numa_node, tokens[19])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	status = rte_swx_ipsec_create(&ipsec,
+				      ipsec_instance_name,
+				      &p,
+				      (int)numa_node);
+	if (status)
+		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
+}
+
+static const char cmd_ipsec_sa_add_help[] =
+"ipsec <ipsec_instance_name> sa add <file_name>\n";
+
+static void
+cmd_ipsec_sa_add(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name, *file_name, *line = NULL;
+	FILE *file = NULL;
+	uint32_t line_id = 0;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		goto free;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		goto free;
+	}
+
+	if (strcmp(tokens[3], "add")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+		goto free;
+	}
+
+	file_name = tokens[4];
+	file = fopen(file_name, "r");
+	if (!file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+		goto free;
+	}
+
+	/* Buffer allocation. */
+	line = malloc(MAX_LINE_SIZE);
+	if (!line) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	/* File read. */
+	for (line_id = 1; ; line_id++) {
+		struct rte_swx_ipsec_sa_params *sa;
+		const char *err_msg;
+		uint32_t sa_id = 0;
+		int is_blank_or_comment, status = 0;
+
+		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+			break;
+
+		/* Read SA from file. */
+		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
+		if (!sa) {
+			if (is_blank_or_comment)
+				continue;
+
+			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
+				file_name, line_id, err_msg);
+			goto free;
+		}
+
+		snprintf(out, out_size, "%s", line);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		/* Add the SA to the IPsec instance. Free the SA. */
+		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
+		if (status)
+			snprintf(out, out_size, "\t: Error (%d)\n", status);
+		else
+			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		free(sa);
+		if (status)
+			goto free;
+	}
+
+free:
+	if (file)
+		fclose(file);
+	free(line);
+}
+
+static const char cmd_ipsec_sa_delete_help[] =
+"ipsec <ipsec_instance_name> sa delete <sa_id>\n";
+
+static void
+cmd_ipsec_sa_delete(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t sa_id;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		return;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		return;
+	}
+
+	if (strcmp(tokens[3], "delete")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+		return;
+	}
+
+	if (parser_read_uint32(&sa_id, tokens[4])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
+		return;
+	}
+
+	rte_swx_ipsec_sa_delete(ipsec, sa_id);
+}
+
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
 
@@ -3067,6 +3325,9 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tipsec create\n"
+			"\tipsec sa add\n"
+			"\tipsec sa delete\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
 		return;
@@ -3291,6 +3552,26 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "add")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "delete")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
+		return;
+	}
+
 	if ((n_tokens == 3) &&
 		(strcmp(tokens[0], "thread") == 0) &&
 		(strcmp(tokens[1], "pipeline") == 0)) {
@@ -3539,6 +3820,23 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "ipsec")) {
+		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
+			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
+			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
+			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	if (strcmp(tokens[0], "thread") == 0) {
 		if ((n_tokens >= 5) &&
 			(strcmp(tokens[4], "enable") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 08/11] examples/pipeline: rework the thread configuration updates
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (6 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the configuration updates for the data plane threads were
performed through message queues. Now, this mechanism is replaced by
the control thread updating the mirror copy of the data plane thread
configuration followed by pointer swapping.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 154 ++---
 examples/pipeline/examples/fib.cli            |   2 +-
 examples/pipeline/examples/hash_func.cli      |   2 +-
 examples/pipeline/examples/l2fwd.cli          |   2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |   2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |   2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |   2 +-
 examples/pipeline/examples/learner.cli        |   2 +-
 examples/pipeline/examples/meter.cli          |   2 +-
 examples/pipeline/examples/mirroring.cli      |   2 +-
 examples/pipeline/examples/recirculation.cli  |   2 +-
 examples/pipeline/examples/registers.cli      |   2 +-
 examples/pipeline/examples/selector.cli       |   2 +-
 examples/pipeline/examples/varbit.cli         |   2 +-
 examples/pipeline/examples/vxlan.cli          |   2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |   2 +-
 examples/pipeline/thread.c                    | 586 ++++--------------
 examples/pipeline/thread.h                    |  17 +-
 18 files changed, 217 insertions(+), 570 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index efe2ff61db..0331ddd327 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3167,119 +3167,86 @@ cmd_ipsec_sa_delete(char **tokens,
 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
 }
 
-static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
-
-#ifndef TIMER_PERIOD_MS_DEFAULT
-#define TIMER_PERIOD_MS_DEFAULT 10
-#endif
+static const char cmd_pipeline_enable_help[] =
+"pipeline <pipeline_name> enable thread <thread_id>\n";
 
 static void
-cmd_thread_pipeline_enable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_enable(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
 {
 	char *pipeline_name;
 	struct rte_swx_pipeline *p;
-	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
+	uint32_t thread_id;
 	int status;
 
-	if ((n_tokens != 5) && (n_tokens != 7)) {
+	if (n_tokens != 5) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "enable") != 0) {
+	if (strcmp(tokens[2], "enable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
 		return;
 	}
 
-	if (n_tokens == 7) {
-		if (strcmp(tokens[5], "period") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
-			return;
-		}
+	if (strcmp(tokens[3], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
 
-		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-			return;
-		}
+	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
+	status = pipeline_enable(p, thread_id);
 	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
+		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
 		return;
 	}
 }
 
-static const char cmd_thread_pipeline_disable_help[] =
-"thread <thread_id> pipeline <pipeline_name> disable\n";
+static const char cmd_pipeline_disable_help[] =
+"pipeline <pipeline_name> disable\n";
 
 static void
-cmd_thread_pipeline_disable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_disable(char **tokens,
+		     uint32_t n_tokens,
+		     char *out,
+		     size_t out_size,
+		     void *obj __rte_unused)
 {
 	struct rte_swx_pipeline *p;
 	char *pipeline_name;
-	uint32_t thread_id;
-	int status;
 
-	if (n_tokens != 5) {
+	if (n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "disable") != 0) {
+	if (strcmp(tokens[2], "disable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, p);
-	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL,
-			"thread pipeline disable");
-		return;
-	}
+	pipeline_disable(p);
 }
 
 static void
@@ -3325,11 +3292,12 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tpipeline enable\n"
+			"\tpipeline disable\n\n"
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
-			"\tthread pipeline enable\n"
-			"\tthread pipeline disable\n\n");
+			);
 		return;
 	}
 
@@ -3552,6 +3520,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
+		return;
+	}
+
 	if (!strcmp(tokens[0], "ipsec") &&
 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
@@ -3572,22 +3552,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((n_tokens == 3) &&
-		(strcmp(tokens[0], "thread") == 0) &&
-		(strcmp(tokens[1], "pipeline") == 0)) {
-		if (strcmp(tokens[2], "enable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_enable_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "disable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_disable_help);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3818,6 +3782,16 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
+			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
+			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
 	}
 
 	if (!strcmp(tokens[0], "ipsec")) {
@@ -3837,22 +3811,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
-	if (strcmp(tokens[0], "thread") == 0) {
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "enable") == 0)) {
-			cmd_thread_pipeline_enable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "disable") == 0)) {
-			cmd_thread_pipeline_disable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 2450dc9ca4..d2485cefe0 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -54,4 +54,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index 3840e47a2b..2c20f7a7a0 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index c5505df296..383fe346c0 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethde
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index bd700707f5..d02cb2c470 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index aa64b32ab2..d4ea449e03 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 619d17474f..8a25c98096 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 42184be1c8..04c7b4d26f 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/eth
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 0fa38b16b8..9e064dbb52 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 meter meters set profile platinum index from 0 to 15
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index e7391de74b..da6bd338fb 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 mirror session 3 port 0 clone slow truncate 128
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 965c870fd4..f925179bf7 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/exampl
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index aa775f71e5..58dde5fdf1 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0608de184..9af56cd0f1 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 selector s show
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 648b8882c3..621e7f5627 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethd
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 4565adfaea..73eb29a6b2 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 510c2ad870..828342408b 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -36,4 +36,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 6d15f51fb2..3001bc0858 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -3,17 +3,11 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
 
+#include <rte_atomic.h>
 #include <rte_common.h>
-#include <rte_cycles.h>
 #include <rte_lcore.h>
-#include <rte_ring.h>
-
-#include <rte_table_acl.h>
-#include <rte_table_array.h>
-#include <rte_table_hash.h>
-#include <rte_table_lpm.h>
-#include <rte_table_lpm_ipv6.h>
 
 #include "obj.h"
 #include "thread.h"
@@ -22,14 +16,6 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
-#ifndef THREAD_MSGQ_SIZE
-#define THREAD_MSGQ_SIZE                                   64
-#endif
-
-#ifndef THREAD_TIMER_PERIOD_MS
-#define THREAD_TIMER_PERIOD_MS                             100
-#endif
-
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -40,509 +26,209 @@
 #endif
 
 /**
- * Control thread: data plane thread context
+ * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
+ * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
+ * blocks.
+ *
+ * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
+ * single thread at any given time, so the same pipeline/block cannot show up in the list of
+ * pipelines/blocks of more than one thread at any specific moment.
+ *
+ * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
+ *  - Read-write by the CP thread;
+ *  - Read-only by the DP thread.
  */
 struct thread {
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-
-	uint32_t enabled;
-};
-
-static struct thread thread[RTE_MAX_LCORE];
-
-/**
- * Data plane threads: context
- */
-struct pipeline_data {
-	struct rte_swx_pipeline *p;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-};
-
-struct thread_data {
-	struct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];
-	uint32_t n_pipelines;
-
-	struct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-	uint64_t time_next_min;
+	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	volatile uint64_t n_pipelines;
+	int enabled;
 } __rte_cache_aligned;
 
-static struct thread_data thread_data[RTE_MAX_LCORE];
+static struct thread threads[RTE_MAX_LCORE];
 
 /**
- * Control thread: data plane thread init
+ * Control plane (CP) thread.
  */
-static void
-thread_free(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct thread *t = &thread[i];
-
-		if (!rte_lcore_is_enabled(i))
-			continue;
-
-		/* MSGQs */
-		rte_ring_free(t->msgq_req);
-
-		rte_ring_free(t->msgq_rsp);
-	}
-}
-
 int
 thread_init(void)
 {
-	uint32_t i;
-
-	RTE_LCORE_FOREACH_WORKER(i) {
-		char name[NAME_MAX];
-		struct rte_ring *msgq_req, *msgq_rsp;
-		struct thread *t = &thread[i];
-		struct thread_data *t_data = &thread_data[i];
-		uint32_t cpu_id = rte_lcore_to_socket_id(i);
-
-		/* MSGQs */
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-REQ", i);
-
-		msgq_req = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_req == NULL) {
-			thread_free();
-			return -1;
-		}
+	uint32_t thread_id;
 
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-RSP", i);
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
 
-		msgq_rsp = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_rsp == NULL) {
-			thread_free();
-			return -1;
-		}
-
-		/* Control thread records */
-		t->msgq_req = msgq_req;
-		t->msgq_rsp = msgq_rsp;
 		t->enabled = 1;
-
-		/* Data plane thread records */
-		t_data->n_pipelines = 0;
-		t_data->msgq_req = msgq_req;
-		t_data->msgq_rsp = msgq_rsp;
-		t_data->timer_period =
-			(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;
-		t_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;
-		t_data->time_next_min = t_data->time_next;
 	}
 
 	return 0;
 }
 
-static inline int
-thread_is_running(uint32_t thread_id)
+static uint32_t
+pipeline_find(struct rte_swx_pipeline *p)
 {
-	enum rte_lcore_state_t thread_state;
+	uint32_t thread_id;
 
-	thread_state = rte_eal_get_lcore_state(thread_id);
-	return (thread_state == RUNNING) ? 1 : 0;
-}
-
-/**
- * Control thread & data plane threads: message passing
- */
-enum thread_req_type {
-	THREAD_REQ_PIPELINE_ENABLE = 0,
-	THREAD_REQ_PIPELINE_DISABLE,
-	THREAD_REQ_MAX
-};
-
-struct thread_msg_req {
-	enum thread_req_type type;
-
-	union {
-		struct {
-			struct rte_swx_pipeline *p;
-			uint32_t timer_period_ms;
-		} pipeline_enable;
-
-		struct {
-			struct rte_swx_pipeline *p;
-		} pipeline_disable;
-	};
-};
-
-struct thread_msg_rsp {
-	int status;
-};
-
-/**
- * Control thread
- */
-static struct thread_msg_req *
-thread_msg_alloc(void)
-{
-	size_t size = RTE_MAX(sizeof(struct thread_msg_req),
-		sizeof(struct thread_msg_rsp));
-
-	return calloc(1, size);
-}
-
-static void
-thread_msg_free(struct thread_msg_rsp *rsp)
-{
-	free(rsp);
-}
-
-static struct thread_msg_rsp *
-thread_msg_send_recv(uint32_t thread_id,
-	struct thread_msg_req *req)
-{
-	struct thread *t = &thread[thread_id];
-	struct rte_ring *msgq_req = t->msgq_req;
-	struct rte_ring *msgq_rsp = t->msgq_rsp;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* send */
-	do {
-		status = rte_ring_sp_enqueue(msgq_req, req);
-	} while (status == -ENOBUFS);
-
-	/* recv */
-	do {
-		status = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);
-	} while (status != 0);
-
-	return rsp;
-}
-
-static int
-thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
-{
-	struct thread *t = &thread[thread_id];
-	struct thread_data *td = &thread_data[thread_id];
-	uint32_t i;
-
-	if (!t->enabled)
-		return 0; /* Pipeline NOT enabled on this thread. */
-
-	for (i = 0; i < td->n_pipelines; i++)
-		if (td->p[i] == p)
-			return 1; /* Pipeline enabled on this thread. */
-
-	return 0 /* Pipeline NOT enabled on this thread. */;
-}
-
-int
-thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
-{
-	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		struct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];
-
-		if (td->n_pipelines >= THREAD_PIPELINES_MAX)
-			return -1;
-
-		/* Data plane thread */
-		td->p[td->n_pipelines] = p;
-
-		tdp->p = p;
-		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
-		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
-		td->n_pipelines++;
+		if (!t->enabled)
+			continue;
 
-		return 0;
+		for (i = 0; i < t->n_pipelines; i++)
+			if (t->pipelines[i] == p)
+				break;
 	}
 
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p;
-	req->pipeline_enable.timer_period_ms = timer_period_ms;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
-
-	/* Read response */
-	status = rsp->status;
-
-	/* Free response */
-	thread_msg_free(rsp);
-
-	/* Request completion */
-	if (status)
-		return status;
-
-	return 0;
+	return thread_id;
 }
 
+/**
+ * Enable a given pipeline to run on a specific DP thread.
+ *
+ * CP thread:
+ *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
+ *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
+ *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
+ *    write barrier used below.
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
+ *    the new pipeline when it sees the updated t->n_pipelines value;
+ *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
+ *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
+ *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
+ */
 int
-thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
 {
 	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
+	uint64_t n_pipelines;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_pipeline_enabled(thread_id, p))
-		return 0;
+	if (!p || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
 
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		uint32_t i;
-
-		for (i = 0; i < td->n_pipelines; i++) {
-			struct pipeline_data *tdp = &td->pipeline_data[i];
-
-			if (tdp->p != p)
-				continue;
-
-			/* Data plane thread */
-			if (i < td->n_pipelines - 1) {
-				struct rte_swx_pipeline *pipeline_last =
-					td->p[td->n_pipelines - 1];
-				struct pipeline_data *tdp_last =
-					&td->pipeline_data[td->n_pipelines - 1];
-
-				td->p[i] = pipeline_last;
-				memcpy(tdp, tdp_last, sizeof(*tdp));
-			}
-
-			td->n_pipelines--;
-
-			break;
-		}
+	if (pipeline_find(p) < RTE_MAX_LCORE)
+		return -EEXIST;
 
-		return 0;
-	}
-
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
 
-	/* Read response */
-	status = rsp->status;
+	n_pipelines = t->n_pipelines;
 
-	/* Free response */
-	thread_msg_free(rsp);
+	/* Check there is room for at least one more pipeline. */
+	if (n_pipelines >= THREAD_PIPELINES_MAX)
+		return -ENOSPC;
 
-	/* Request completion */
-	if (status)
-		return status;
+	/* Install the new pipeline. */
+	t->pipelines[n_pipelines] = p;
+	rte_wmb();
+	t->n_pipelines = n_pipelines + 1;
 
 	return 0;
 }
 
 /**
- * Data plane threads: message handling
+ * Disable a given pipeline from running on any DP thread.
+ *
+ * CP thread:
+ *  - Detects the thread that is running the given pipeline, if any;
+ *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
+ *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
+ *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
+ *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
+ *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
+ *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
+ *    below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final value of t->n_pipelines;
+ *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
+ *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
+ *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
+ *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
+ *    behaviors are detailed below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final values of the t->pipeline[] array;
+ *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
+ *       the exception of the pipeline_last, which is not run during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
  */
-static inline struct thread_msg_req *
-thread_msg_recv(struct rte_ring *msgq_req)
-{
-	struct thread_msg_req *req;
-
-	int status = rte_ring_sc_dequeue(msgq_req, (void **) &req);
-
-	if (status != 0)
-		return NULL;
-
-	return req;
-}
-
-static inline void
-thread_msg_send(struct rte_ring *msgq_rsp,
-	struct thread_msg_rsp *rsp)
-{
-	int status;
-
-	do {
-		status = rte_ring_sp_enqueue(msgq_rsp, rsp);
-	} while (status == -ENOBUFS);
-}
-
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_enable(struct thread_data *t,
-	struct thread_msg_req *req)
+void
+pipeline_disable(struct rte_swx_pipeline *p)
 {
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	struct pipeline_data *p = &t->pipeline_data[t->n_pipelines];
-
-	/* Request */
-	if (t->n_pipelines >= THREAD_PIPELINES_MAX) {
-		rsp->status = -1;
-		return rsp;
-	}
-
-	t->p[t->n_pipelines] = req->pipeline_enable.p;
-
-	p->p = req->pipeline_enable.p;
-	p->timer_period = (rte_get_tsc_hz() *
-		req->pipeline_enable.timer_period_ms) / 1000;
-	p->time_next = rte_get_tsc_cycles() + p->timer_period;
+	struct thread *t;
+	uint64_t n_pipelines;
+	uint32_t thread_id, i;
 
-	t->n_pipelines++;
+	/* Check input params */
+	if (!p)
+		return;
 
-	/* Response */
-	rsp->status = 0;
-	return rsp;
-}
+	/* Find the thread that runs this pipeline. */
+	thread_id = pipeline_find(p);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
 
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_disable(struct thread_data *t,
-	struct thread_msg_req *req)
-{
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	uint32_t n_pipelines = t->n_pipelines;
-	struct rte_swx_pipeline *pipeline = req->pipeline_disable.p;
-	uint32_t i;
+	t = &threads[thread_id];
+	n_pipelines = t->n_pipelines;
 
-	/* find pipeline */
 	for (i = 0; i < n_pipelines; i++) {
-		struct pipeline_data *p = &t->pipeline_data[i];
+		struct rte_swx_pipeline *pipeline = t->pipelines[i];
 
-		if (p->p != pipeline)
+		if (pipeline != p)
 			continue;
 
 		if (i < n_pipelines - 1) {
-			struct rte_swx_pipeline *pipeline_last =
-				t->p[n_pipelines - 1];
-			struct pipeline_data *p_last =
-				&t->pipeline_data[n_pipelines - 1];
+			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
 
-			t->p[i] = pipeline_last;
-			memcpy(p, p_last, sizeof(*p));
+			t->pipelines[i] = pipeline_last;
 		}
 
-		t->n_pipelines--;
+		rte_wmb();
+		t->n_pipelines = n_pipelines - 1;
 
-		rsp->status = 0;
-		return rsp;
+		return;
 	}
 
-	/* should not get here */
-	rsp->status = 0;
-	return rsp;
-}
-
-static void
-thread_msg_handle(struct thread_data *t)
-{
-	for ( ; ; ) {
-		struct thread_msg_req *req;
-		struct thread_msg_rsp *rsp;
-
-		req = thread_msg_recv(t->msgq_req);
-		if (req == NULL)
-			break;
-
-		switch (req->type) {
-		case THREAD_REQ_PIPELINE_ENABLE:
-			rsp = thread_msg_handle_pipeline_enable(t, req);
-			break;
-
-		case THREAD_REQ_PIPELINE_DISABLE:
-			rsp = thread_msg_handle_pipeline_disable(t, req);
-			break;
-
-		default:
-			rsp = (struct thread_msg_rsp *) req;
-			rsp->status = -1;
-		}
-
-		thread_msg_send(t->msgq_rsp, rsp);
-	}
+	return;
 }
 
 /**
- * Data plane threads: main
+ * Data plane (DP) threads.
+ *
+ * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
+ * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
+ * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
+ * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
+ * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
+ * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
+ * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
+ * marked as volatile.
  */
 int
 thread_main(void *arg __rte_unused)
 {
-	struct thread_data *t;
-	uint32_t thread_id, i;
+	struct thread *t;
+	uint32_t thread_id;
 
 	thread_id = rte_lcore_id();
-	t = &thread_data[thread_id];
-
-	/* Dispatch loop */
-	for (i = 0; ; i++) {
-		uint32_t j;
-
-		/* Data Plane */
-		for (j = 0; j < t->n_pipelines; j++)
-			rte_swx_pipeline_run(t->p[j], PIPELINE_INSTR_QUANTA);
+	t = &threads[thread_id];
 
-		/* Control Plane */
-		if ((i & 0xF) == 0) {
-			uint64_t time = rte_get_tsc_cycles();
-			uint64_t time_next_min = UINT64_MAX;
-
-			if (time < t->time_next_min)
-				continue;
-
-			/* Thread message queues */
-			{
-				uint64_t time_next = t->time_next;
-
-				if (time_next <= time) {
-					thread_msg_handle(t);
-					time_next = time + t->timer_period;
-					t->time_next = time_next;
-				}
-
-				if (time_next < time_next_min)
-					time_next_min = time_next;
-			}
+	/* Dispatch loop. */
+	for ( ; ; ) {
+		uint32_t i;
 
-			t->time_next_min = time_next_min;
-		}
+		/* Pipelines. */
+		for (i = 0; i < t->n_pipelines; i++)
+			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 712cb25bbb..338d480abb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -9,18 +9,21 @@
 
 #include <rte_swx_pipeline.h>
 
+/**
+ * Control plane (CP) thread.
+ */
 int
-thread_pipeline_enable(uint32_t thread_id,
-		       struct rte_swx_pipeline *p,
-		       uint32_t timer_period_ms);
+thread_init(void);
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-			struct rte_swx_pipeline *p);
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 
-int
-thread_init(void);
+void
+pipeline_disable(struct rte_swx_pipeline *p);
 
+/**
+ * Data plane (DP) threads.
+ */
 int
 thread_main(void *arg);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 09/11] examples/pipeline: support blocks other than pipelines
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (7 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the data plane threads only supported the execution of
pipelines assigned to them through configuration updates. Now, the
data plane threads also support running blocks such as IPsec.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/thread.c | 143 +++++++++++++++++++++++++++++++++++++
 examples/pipeline/thread.h |   9 +++
 2 files changed, 152 insertions(+)

diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 3001bc0858..dc3ea73fbf 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -16,6 +16,10 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
+#ifndef THREAD_BLOCKS_MAX
+#define THREAD_BLOCKS_MAX                                  256
+#endif
+
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -38,9 +42,16 @@
  *  - Read-write by the CP thread;
  *  - Read-only by the DP thread.
  */
+struct block {
+	block_run_f block_func;
+	void *block;
+};
+
 struct thread {
 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	struct block *blocks[THREAD_BLOCKS_MAX];
 	volatile uint64_t n_pipelines;
+	volatile uint64_t n_blocks;
 	int enabled;
 } __rte_cache_aligned;
 
@@ -53,14 +64,43 @@ int
 thread_init(void)
 {
 	uint32_t thread_id;
+	int status = 0;
 
 	RTE_LCORE_FOREACH_WORKER(thread_id) {
 		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
 		t->enabled = 1;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			struct block *b;
+
+			b = calloc(1, sizeof(struct block));
+			if (!b) {
+				status = -ENOMEM;
+				goto error;
+			}
+
+			t->blocks[i] = b;
+		}
 	}
 
 	return 0;
+
+error:
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		t->enabled = 0;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			free(t->blocks[i]);
+			t->blocks[i] = NULL;
+		}
+	}
+
+	return status;
 }
 
 static uint32_t
@@ -83,6 +123,26 @@ pipeline_find(struct rte_swx_pipeline *p)
 	return thread_id;
 }
 
+static uint32_t
+block_find(void *b)
+{
+	uint32_t thread_id;
+
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		if (!t->enabled)
+			continue;
+
+		for (i = 0; i < t->n_blocks; i++)
+			if (t->blocks[i]->block == b)
+				break;
+	}
+
+	return thread_id;
+}
+
 /**
  * Enable a given pipeline to run on a specific DP thread.
  *
@@ -201,9 +261,85 @@ pipeline_disable(struct rte_swx_pipeline *p)
 	return;
 }
 
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+
+	/* Check input params */
+	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
+
+	if (block_find(block) < RTE_MAX_LCORE)
+		return -EEXIST;
+
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
+
+	n_blocks = t->n_blocks;
+
+	/* Check there is room for at least one more block. */
+	if (n_blocks >= THREAD_BLOCKS_MAX)
+		return -ENOSPC;
+
+	/* Install the new block. */
+	t->blocks[n_blocks]->block_func = block_func;
+	t->blocks[n_blocks]->block = block;
+
+	rte_wmb();
+	t->n_blocks = n_blocks + 1;
+
+	return 0;
+}
+
+void
+block_disable(void *block)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+	uint32_t thread_id, i;
+
+	/* Check input params */
+	if (!block)
+		return;
+
+	/* Find the thread that runs this block. */
+	thread_id = block_find(block);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
+
+	t = &threads[thread_id];
+	n_blocks = t->n_blocks;
+
+	for (i = 0; i < n_blocks; i++) {
+		struct block *b = t->blocks[i];
+
+		if (block != b->block)
+			continue;
+
+		if (i < n_blocks - 1) {
+			struct block *block_last = t->blocks[n_blocks - 1];
+
+			t->blocks[i] = block_last;
+		}
+
+		rte_wmb();
+		t->n_blocks = n_blocks - 1;
+
+		rte_wmb();
+		t->blocks[n_blocks - 1] = b;
+
+		return;
+	}
+}
+
 /**
  * Data plane (DP) threads.
  *
+
+
  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
@@ -229,6 +365,13 @@ thread_main(void *arg __rte_unused)
 		/* Pipelines. */
 		for (i = 0; i < t->n_pipelines; i++)
 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
+
+		/* Blocks. */
+		for (i = 0; i < t->n_blocks; i++) {
+			struct block *b = t->blocks[i];
+
+			b->block_func(b->block);
+		}
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 338d480abb..f2e643def5 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -21,6 +21,15 @@ pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 void
 pipeline_disable(struct rte_swx_pipeline *p);
 
+typedef void
+(*block_run_f)(void *block);
+
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id);
+
+void
+block_disable(void *block);
+
 /**
  * Data plane (DP) threads.
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 10/11] examples/pipeline: add block enable/disable CLI commands
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (8 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  2023-01-11 23:43   ` [PATCH V2 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands to enable/disable block execution on data plane
threads.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 0331ddd327..c0fe20a386 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3249,6 +3249,134 @@ cmd_pipeline_disable(char **tokens,
 	pipeline_disable(p);
 }
 
+static const char cmd_block_enable_help[] =
+"block type <block_type> instance <block_name> enable thread <thread_id>\n";
+
+static void
+cmd_block_enable(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	block_run_f block_func = NULL;
+	void *block = NULL;
+	uint32_t thread_id;
+	int status;
+
+	if (n_tokens != 8) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "enable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+		return;
+	}
+
+	if (strcmp(tokens[6], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
+
+	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block_func = (block_run_f)rte_swx_ipsec_run;
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	status = block_enable(block_func, block, thread_id);
+	if (status) {
+		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
+		return;
+	}
+}
+
+static const char cmd_block_disable_help[] =
+"block type <block_type> instance <block_name> disable\n";
+
+static void
+cmd_block_disable(char **tokens,
+		  uint32_t n_tokens,
+		  char *out,
+		  size_t out_size,
+		  void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	void *block = NULL;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "disable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	block_disable(block);
+}
+
 static void
 cmd_help(char **tokens,
 	 uint32_t n_tokens,
@@ -3297,6 +3425,8 @@ cmd_help(char **tokens,
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
+			"\tblock enable\n"
+			"\tblock disable\n"
 			);
 		return;
 	}
@@ -3552,6 +3682,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
+		return;
+	}
+
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3811,6 +3953,18 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "block")) {
+		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
+			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
+			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V2 11/11] examples/pipeline: add IPsec example
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (9 preceding siblings ...)
  2023-01-11 23:43   ` [PATCH V2 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
@ 2023-01-11 23:43   ` Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-11 23:43 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add example files to illustrate the pipeline IPsec support.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ipsec.cli    |  57 +++++++
 examples/pipeline/examples/ipsec.io     |  23 +++
 examples/pipeline/examples/ipsec.spec   | 138 +++++++++++++++
 examples/pipeline/examples/ipsec_sa.txt | 216 ++++++++++++++++++++++++
 4 files changed, 434 insertions(+)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt

diff --git a/examples/pipeline/examples/ipsec.cli b/examples/pipeline/examples/ipsec.cli
new file mode 100644
index 0000000000..8cb5bf4239
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.cli
@@ -0,0 +1,57 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 --vdev crypto_aesni_mb0 -- -s ./examples/pipeline/examples/ipsec.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/ipsec.spec /tmp/ipsec.c
+pipeline libbuild /tmp/ipsec.c /tmp/ipsec.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 128 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+cryptodev crypto_aesni_mb0 queues 1 qsize 128
+ring RING0 size 1024 numa 0
+ring RING1 size 1024 numa 0
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/ipsec.so io ./examples/pipeline/examples/ipsec.io numa 0
+
+;
+; List of IPsec devices.
+;
+ipsec IPSEC0 create in RING0 out RING1 cryptodev crypto_aesni_mb0 cryptoq 0 bsz 32 32 32 32 samax 512 numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+//pipeline PIPELINE0 table policy_table add ./examples/pipeline/examples/ipsec_policy_table.txt
+//pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/ipsec_routing_table.txt
+//pipeline PIPELINE0 table nexthop_table add ./examples/pipeline/examples/ipsec_nexthop_table.txt
+//pipeline PIPELINE0 commit
+
+ipsec IPSEC0 sa add ./examples/pipeline/examples/ipsec_sa.txt
+
+;
+; Pipelines and blocks mapping to CPU threads.
+;
+pipeline PIPELINE0 enable thread 1
+block type ipsec instance IPSEC0 enable thread 1
diff --git a/examples/pipeline/examples/ipsec.io b/examples/pipeline/examples/ipsec.io
new file mode 100644
index 0000000000..f5a3fcf961
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.io
@@ -0,0 +1,23 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ring RING1 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ring RING0 bsz 32
diff --git a/examples/pipeline/examples/ipsec.spec b/examples/pipeline/examples/ipsec.spec
new file mode 100644
index 0000000000..09aa831881
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.spec
@@ -0,0 +1,138 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_h {
+	bit<8> ver_ihl
+	bit<8> diffserv
+	bit<16> total_len
+	bit<16> identification
+	bit<16> flags_offset
+	bit<8> ttl
+	bit<8> protocol
+	bit<16> hdr_checksum
+	bit<32> src_addr
+	bit<32> dst_addr
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+struct ipsec_internal_h {
+	bit<32> sa_id
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+header ipsec_internal instanceof ipsec_internal_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port_in
+	bit<32> port_out
+
+	bit<32> src_addr
+	bit<32> dst_addr
+	bit<8> protocol
+	bit<16> src_port
+	bit<16> dst_port
+}
+
+metadata instanceof metadata_t
+
+//
+// Actions
+//
+struct encrypt_args_t {
+	bit<32> sa_id
+}
+
+action encrypt args instanceof encrypt_args_t {
+	//Set the IPsec internal header.
+	validate h.ipsec_internal
+	mov h.ipsec_internal.sa_id t.sa_id
+
+	return
+}
+
+action drop args none {
+	drop
+}
+
+//
+// Tables.
+//
+table policy_table {
+	key {
+		m.src_addr exact
+		m.dst_addr exact
+		m.protocol exact
+		m.src_port exact
+		m.dst_port exact
+	}
+
+	actions {
+		encrypt
+		drop
+	}
+
+	default_action encrypt args sa_id 0
+	size 65536
+}
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port_in
+
+	jmpeq FROM_IPSEC_TO_NET m.port_in 1
+
+FROM_NET_TO_IPSEC : extract h.ethernet
+
+	extract h.ipv4
+	mov m.src_addr h.ipv4.src_addr
+	mov m.dst_addr h.ipv4.dst_addr
+	mov m.protocol h.ipv4.protocol
+
+	extract h.udp
+	mov m.src_port h.udp.src_port
+	mov m.dst_port h.udp.dst_port
+
+	table policy_table
+
+	mov m.port_out 1
+
+	emit h.ipsec_internal
+	emit h.ipv4
+	emit h.udp
+	tx m.port_out
+
+FROM_IPSEC_TO_NET : extract h.ipv4
+
+	validate h.ethernet
+	mov h.ethernet.dst_addr 0xa0b0c0d0e0f0
+	mov h.ethernet.src_addr 0xa1b1c1d1e1f1
+	mov h.ethernet.ethertype 0x0800
+
+	mov m.port_out 0
+
+	emit h.ethernet
+	emit h.ipv4
+	tx m.port_out
+}
diff --git a/examples/pipeline/examples/ipsec_sa.txt b/examples/pipeline/examples/ipsec_sa.txt
new file mode 100644
index 0000000000..37bfd8a9ce
--- /dev/null
+++ b/examples/pipeline/examples/ipsec_sa.txt
@@ -0,0 +1,216 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+// IPsec SA syntax:
+//
+// <sa>
+//    : encrypt <crypto_params> <encap_params>
+//    | decrypt <crypto_params> <encap_params>
+//    ;
+//
+// <crypto_params>
+//    : <cipher> <auth>
+//    | <aead>
+//    ;
+//
+// <cipher>
+//    : cipher <ciher_alg> key <cipher_key>
+//    | cipher <cipher_alg>
+//    ;
+//
+// <auth>
+//    : auth <authentication_alg> key <authentication_key>
+//    | auth <authentication_alg>
+//    ;
+//
+// <aead>
+//    : aead <aead_alg> key <aead_key>
+//    ;
+//
+// <encap_params>
+//    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+//    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+//    | esp spi <spi> transport
+//    ;
+
+//
+// cipher = null, auth = null
+//
+encrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth null esp spi 100 transport
+decrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth null esp spi 100 transport
+
+//
+// aes-gcm-128
+//
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-gcm-192
+//
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-gcm-256
+//
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// aes-ccm-128
+//
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-ccm-192
+//
+// Note: Currently not supported by the "aesni_mb" library.
+//
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-ccm-256
+//
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// chacha20-poly1305
+//
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// cipher = aes-cbc-128, auth = null
+//
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-192, auth = null
+//
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-256, auth = null
+//
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-128, auth = null
+//
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-192, auth = null
+//
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-256, auth = null
+//
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+
+//
+// cipher = null, auth = sha1-hmac
+//
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// cipher = null, auth = sha256-hmac
+//
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+
+//
+// cipher = null, auth = sha384-hmac
+//
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+
+//
+// cipher = null, auth = sha512-hmac
+//
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 00/11] pipeline: add IPsec support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (11 preceding siblings ...)
  2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-12 15:45 ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 01/11] " Cristian Dumitrescu
                     ` (10 more replies)
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (3 subsequent siblings)
  16 siblings, 11 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev

This patch set introduces a companion block for the SWX pipeline for
IPsec support.

The IPsec block is external to the pipeline, hence it needs to be
explicitly instantiated by the user and connected to a pipeline
instance through the pipeline I/O ports.

Main features:
* IPsec inbound (encrypted input packets -> clear text output packets)
and outbound (clear text input packets -> encrypted output packets)
processing support for tunnel and transport modes.

Interaction of the IPsec block with the pipeline:
* Each IPsec block instance has its own set of Security Associations
(SAs) used to process the input packets. Each SA is identified by its
unique SA ID. The IPsec inbound and outbound SAs share the same ID
space.
* Each input packet is first mapped to one of the existing SAs by
using the SA ID and then processed according to the identified SA. The
SA ID is read from input packet. The SA ID field is typically written
by the pipeline before sending the packet to the IPsec block.

Change log:

V3:
Rebase on top of main latest.

V2:
Fix minor style issues.

Cristian Dumitrescu (11):
  pipeline: add IPsec support
  examples/pipeline: rework memory pool support
  examples/pipeline: streamline ring support
  examples/pipeline: streamline the Ethernet device support
  examples/pipeline: support crypto devices
  examples/pipeline: add CLI command for crypto device
  examples/pipeline: add IPsec CLI commands
  examples/pipeline: rework the thread configuration updates
  examples/pipeline: support blocks other than pipelines
  examples/pipeline: add block enable/disable CLI commands
  examples/pipeline: add IPsec example

 examples/pipeline/cli.c                       |  896 ++++++--
 examples/pipeline/examples/fib.cli            |    4 +-
 examples/pipeline/examples/hash_func.cli      |    4 +-
 examples/pipeline/examples/ipsec.cli          |   57 +
 examples/pipeline/examples/ipsec.io           |   23 +
 examples/pipeline/examples/ipsec.spec         |  138 ++
 examples/pipeline/examples/ipsec_sa.txt       |  216 ++
 examples/pipeline/examples/l2fwd.cli          |    4 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |    4 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |    4 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |    4 +-
 examples/pipeline/examples/learner.cli        |    4 +-
 examples/pipeline/examples/meter.cli          |    4 +-
 examples/pipeline/examples/mirroring.cli      |    4 +-
 examples/pipeline/examples/recirculation.cli  |    4 +-
 examples/pipeline/examples/registers.cli      |    4 +-
 examples/pipeline/examples/selector.cli       |    4 +-
 examples/pipeline/examples/varbit.cli         |    4 +-
 examples/pipeline/examples/vxlan.cli          |    4 +-
 examples/pipeline/examples/vxlan_pcap.cli     |    4 +-
 examples/pipeline/main.c                      |   12 +-
 examples/pipeline/obj.c                       |  359 +---
 examples/pipeline/obj.h                       |  100 +-
 examples/pipeline/thread.c                    |  655 +++---
 examples/pipeline/thread.h                    |   24 +-
 lib/pipeline/meson.build                      |    4 +-
 lib/pipeline/rte_swx_ipsec.c                  | 1821 +++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h                  |  383 ++++
 lib/pipeline/version.map                      |    9 +
 29 files changed, 3739 insertions(+), 1018 deletions(-)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 01/11] pipeline: add IPsec support
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

This block is providing IPsec support to the SWX pipeline. The IPsec
block is external to the pipeline, so it needs to be explicitly
instantiated and connected to a pipeline through the I/O ports.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 lib/pipeline/meson.build     |    4 +-
 lib/pipeline/rte_swx_ipsec.c | 1821 ++++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h |  383 +++++++
 lib/pipeline/version.map     |    9 +
 4 files changed, 2216 insertions(+), 1 deletion(-)
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build
index 3ca98ed194..aa3fd0c2b8 100644
--- a/lib/pipeline/meson.build
+++ b/lib/pipeline/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'rte_pipeline.c',
         'rte_port_in_action.c',
         'rte_table_action.c',
+	'rte_swx_ipsec.c',
         'rte_swx_pipeline.c',
         'rte_swx_pipeline_spec.c',
         'rte_swx_ctl.c',
@@ -19,8 +20,9 @@ headers = files(
         'rte_pipeline.h',
         'rte_port_in_action.h',
         'rte_table_action.h',
+	'rte_swx_ipsec.h',
         'rte_swx_pipeline.h',
         'rte_swx_extern.h',
         'rte_swx_ctl.h',
 )
-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']
+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']
diff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c
new file mode 100644
index 0000000000..b23056e23e
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.c
@@ -0,0 +1,1821 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_cryptodev.h>
+#include <rte_ipsec.h>
+
+#include "rte_swx_ipsec.h"
+
+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
+
+#include <rte_malloc.h>
+
+static void *
+env_calloc(size_t size, size_t alignment, int numa_node)
+{
+	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
+}
+
+static void
+env_free(void *start, size_t size __rte_unused)
+{
+	rte_free(start);
+}
+
+#else
+
+#include <numa.h>
+
+static void *
+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
+{
+	void *start;
+
+	if (numa_available() == -1)
+		return NULL;
+
+	start = numa_alloc_onnode(size, numa_node);
+	if (!start)
+		return NULL;
+
+	memset(start, 0, size);
+	return start;
+}
+
+static void
+env_free(void *start, size_t size)
+{
+	if ((numa_available() == -1) || !start)
+		return;
+
+	numa_free(start, size);
+}
+
+#endif
+
+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
+#endif
+
+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
+ * the mempool size to be at least 1.5 times the size of the mempool cache.
+ */
+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
+
+struct ipsec_sa {
+	struct rte_ipsec_session s;
+	int valid;
+};
+
+struct ipsec_pkts_in {
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_cops;
+};
+
+struct ipsec_pkts_out {
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_pkts;
+};
+
+struct rte_swx_ipsec {
+	/*
+	 * Parameters.
+	 */
+
+	/* IPsec instance name. */
+	char name[RTE_SWX_IPSEC_NAME_SIZE];
+
+	/* Input packet queue. */
+	struct rte_ring *ring_in;
+
+	/* Output packet queue. */
+	struct rte_ring *ring_out;
+
+	/* Crypto device ID. */
+	uint8_t dev_id;
+
+	/* Crypto device queue pair ID. */
+	uint16_t qp_id;
+
+	/* Burst sizes. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/* SA table size. */
+	size_t n_sa_max;
+
+	/*
+	 * Internals.
+	 */
+	/* Crypto device buffer pool for sessions. */
+	struct rte_mempool *mp_session;
+
+	/* Pre-crypto packets. */
+	struct ipsec_pkts_in in;
+
+	/* Post-crypto packets. */
+	struct ipsec_pkts_out out;
+
+	/* Crypto device enqueue threshold. */
+	uint32_t crypto_wr_threshold;
+
+	/* Packets currently under crypto device processing. */
+	uint32_t n_pkts_crypto;
+
+	/* List of free SADB positions. */
+	uint32_t *sa_free_id;
+
+	/* Number of elements in the SADB list of free positions. */
+	size_t n_sa_free_id;
+
+	/* Allocated memory total size in bytes. */
+	size_t total_size;
+
+	/* Flag for registration to the global list of instances. */
+	int registered;
+
+	/*
+	 * Table memory.
+	 */
+	uint8_t memory[] __rte_cache_aligned;
+};
+
+static inline struct ipsec_sa *
+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
+{
+	struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
+
+	return &sadb[sa_id & (ipsec->n_sa_max - 1)];
+}
+
+/* Global list of instances. */
+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ipsec_tailq = {
+	.name = "RTE_SWX_IPSEC",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
+
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name ||
+	    !name[0] ||
+	    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
+		return NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ipsec;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ipsec_register(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ipsec;
+	TAILQ_INSERT_TAIL(ipsec_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ipsec_unregister(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		if (te->data == (void *)ipsec) {
+			TAILQ_REMOVE(ipsec_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
+
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
+{
+	size_t i;
+
+	if (!ipsec)
+		return;
+
+	/* Remove the current instance from the global list. */
+	if (ipsec->registered)
+		ipsec_unregister(ipsec);
+
+	/* SADB. */
+	for (i = 0; i < ipsec->n_sa_max; i++) {
+		struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
+
+		if (!sa->valid)
+			continue;
+
+		/* SA session. */
+		ipsec_session_free(ipsec, &sa->s);
+	}
+
+	/* Crypto device buffer pools. */
+	rte_mempool_free(ipsec->mp_session);
+
+	/* IPsec object memory. */
+	env_free(ipsec, ipsec->total_size);
+}
+
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node)
+{
+	char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
+	struct rte_swx_ipsec *ipsec = NULL;
+	struct rte_ring *ring_in, *ring_out;
+	struct rte_cryptodev_info dev_info;
+	size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
+	uint32_t dev_session_size;
+	int dev_id, status = 0;
+
+	/* Check input parameters. */
+	if (!ipsec_out ||
+	    !name ||
+	    !name[0] ||
+	    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
+	    !params ||
+	    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    !params->n_sa_max) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_in = rte_ring_lookup(params->ring_in_name);
+	if (!ring_in) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_out = rte_ring_lookup(params->ring_out_name);
+	if (!ring_out) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
+	if (dev_id == -1) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Memory allocation. */
+	n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
+
+	sadb_offset = sizeof(struct rte_swx_ipsec);
+	sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
+
+	sa_free_id_offset = sadb_offset + sadb_size;
+	sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
+
+	total_size = sa_free_id_offset + sa_free_id_size;
+	ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
+	if (!ipsec) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Initialization. */
+	strcpy(ipsec->name, name);
+	ipsec->ring_in = ring_in;
+	ipsec->ring_out = ring_out;
+	ipsec->dev_id = (uint8_t)dev_id;
+	ipsec->qp_id = params->crypto_dev_queue_pair_id;
+	memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
+	ipsec->n_sa_max = n_sa_max;
+
+	ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
+
+	ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
+	for (i = 0; i < n_sa_max; i++)
+		ipsec->sa_free_id[i] = n_sa_max - 1 - i;
+	ipsec->n_sa_free_id = n_sa_max;
+
+	ipsec->total_size = total_size;
+
+	/* Crypto device memory pools. */
+	dev_session_size = rte_cryptodev_sym_get_private_session_size((uint8_t)dev_id);
+
+	snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
+	ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		dev_session_size, /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool element private data size */
+		numa_node);
+	if (!ipsec->mp_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Add the current instance to the global list. */
+	status = ipsec_register(ipsec);
+	if (status)
+		goto error;
+
+	ipsec->registered = 1;
+
+	*ipsec_out = ipsec;
+	return 0;
+
+error:
+	rte_swx_ipsec_free(ipsec);
+	return status;
+}
+
+static inline int
+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
+{
+	struct ipsec_sa *sa;
+	struct rte_ipsec_group *g;
+	int n_groups, n_pkts_in_group, i;
+
+	sa = ipsec->in.sa[0];
+
+	g = &ipsec->in.groups[0];
+	g->id.ptr = sa;
+	g->m = &ipsec->in.pkts[0];
+	n_pkts_in_group = 1;
+	n_groups = 1;
+
+	for (i = 1; i < n_pkts; i++) {
+		struct ipsec_sa *sa_new = ipsec->in.sa[i];
+
+		/* Same SA => Add the current pkt to the same group. */
+		if (sa_new == sa) {
+			n_pkts_in_group++;
+			continue;
+		}
+
+		/* Different SA => Close the current group & add the current pkt to a new group. */
+		g->cnt = n_pkts_in_group;
+		sa = sa_new;
+
+		g++;
+		g->id.ptr = sa;
+		g->m = &ipsec->in.pkts[i];
+		n_pkts_in_group = 1;
+		n_groups++;
+	}
+
+	/* Close the last group. */
+	g->cnt = n_pkts_in_group;
+
+	return n_groups;
+}
+
+static inline void
+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
+{
+	struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
+	struct rte_crypto_op **src = ipsec->in.group_cops;
+
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
+	uint32_t n_dst = ipsec->in.n_cops;
+	uint32_t n_dst_max = ipsec->bsz.crypto_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_cops;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
+	 * instead of moving the SRC to DST first and then using DST.
+	 */
+	if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
+		uint16_t n_ok;
+
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
+		ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
+
+		for (i = n_ok; i < n_src; i++) {
+			struct rte_crypto_op *cop = src[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		return;
+	}
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
+		n_pkts_crypto += n_ok;
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_crypto_op *cop = dst0[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	ipsec->n_pkts_crypto = n_pkts_crypto;
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->in.n_cops = n_dst;
+}
+
+/**
+ * Packet buffer anatomy:
+ *
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | Offset   | Size    | Description                                                              |
+ * | (Byte #) | (Bytes) |                                                                          |
+ * +==========+=========+==========================================================================+
+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |
+ * |          |         | The buf_addr field points to the start of the packet section.            |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 256      |         | Packet section.                                                          |
+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |
+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |
+ * +----------+---------+--------------------------------------------------------------------------+
+ */
+struct ipsec_mbuf {
+	struct ipsec_sa *sa;
+	struct rte_crypto_op cop;
+	struct rte_crypto_sym_op sym_cop;
+	uint8_t buffer[32]; /* The crypto IV is placed here. */
+};
+
+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
+
+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
+
+static inline void
+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
+{
+	int n_pkts, n_groups, i;
+
+	/* Read packets from the input ring. */
+	n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
+					   (void **)ipsec->in.pkts,
+					   ipsec->bsz.ring_rd,
+					   NULL);
+	if (!n_pkts)
+		return;
+
+	/* Get the SA for each packet. */
+	for (i = 0; i < n_pkts; i++) {
+		struct rte_mbuf *m = ipsec->in.pkts[i];
+		struct rte_swx_ipsec_input_packet_metadata *meta;
+		struct rte_ipv4_hdr *ipv4_hdr;
+		uint32_t sa_id;
+
+		meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
+
+		/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
+		sa_id = ntohl(meta->sa_id);
+
+		/* Consume the IPsec meta-data. */
+		m->data_off += META_LENGTH;
+		m->data_len -= META_LENGTH;
+		m->pkt_len -= META_LENGTH;
+
+		/* Set the fields required by the IPsec library. */
+		m->l2_len = 0;
+		m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
+			sizeof(struct rte_ipv4_hdr) :
+			sizeof(struct rte_ipv6_hdr);
+
+		/* Get the SA. */
+		ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
+	}
+
+	/* Group packets that share the same SA. */
+	n_groups = ipsec_sa_group(ipsec, n_pkts);
+
+	/* Write each group of packets sharing the same SA to the crypto device. */
+	for (i = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->in.groups[i];
+		struct ipsec_sa *sa = g->id.ptr;
+		struct rte_ipsec_session *s = &sa->s;
+		uint32_t j;
+		uint16_t n_pkts_ok;
+
+		/* Prepare the crypto ops for the current group. */
+		for (j = 0; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+			struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
+
+			priv->sa = sa;
+			ipsec->in.group_cops[j] = &priv->cop;
+		}
+
+		n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the crypto ops of the current group to the crypto device. */
+		ipsec_crypto_enqueue(ipsec, n_pkts_ok);
+	}
+}
+
+static inline void
+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
+{
+	struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
+	struct rte_mbuf **src = g->m;
+
+	uint32_t n_dst = ipsec->out.n_pkts;
+	uint32_t n_dst_max = ipsec->bsz.ring_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_pkts;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_mbuf *m = dst[i];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->out.n_pkts = n_dst;
+}
+
+static inline void
+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
+{
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, ng, i;
+
+	/* Read the crypto ops from the crypto device. */
+	if (!n_pkts_crypto)
+		return;
+
+	n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
+					     ipsec->qp_id,
+					     ipsec->out.cops,
+					     ipsec->bsz.crypto_rd);
+	if (!n_pkts)
+		return;
+
+	ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
+
+	/* Group packets that share the same SA. */
+	ng = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
+					      ipsec->out.group_pkts,
+					      ipsec->out.groups,
+					      n_pkts);
+
+	/* Perform post-crypto IPsec processing for each group of packets that share the same SA.
+	 * Write each group of packets to the output ring.
+	 */
+	for (i = 0, n_pkts = 0; i < ng; i++) {
+		struct rte_ipsec_group *g = &ipsec->out.groups[i];
+		struct rte_ipsec_session *s = g->id.ptr;
+		uint32_t n_pkts_ok, j;
+
+		/* Perform post-crypto IPsec processing for the current group. */
+		n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the packets of the current group to the output ring. */
+		ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
+	}
+}
+
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
+{
+	rte_swx_ipsec_pre_crypto(ipsec);
+	rte_swx_ipsec_post_crypto(ipsec);
+}
+
+/**
+ * IPsec Control Plane API
+ */
+struct cipher_alg {
+	const char *name;
+	enum rte_crypto_cipher_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t key_size;
+};
+
+struct auth_alg {
+	const char *name;
+	enum rte_crypto_auth_algorithm alg;
+	uint32_t iv_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+};
+
+struct aead_alg {
+	const char *name;
+	enum rte_crypto_aead_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+	uint32_t aad_size;
+};
+
+static struct cipher_alg cipher_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_CIPHER_NULL,
+		.iv_size = 0,
+		.block_size = 4,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "aes-cbc-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 16,
+	},
+
+	[2] = {
+		.name = "aes-cbc-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 24,
+	},
+
+	[3] = {
+		.name = "aes-cbc-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 32,
+	},
+
+	[4] = {
+		.name = "aes-ctr-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+	},
+
+	[5] = {
+		.name = "aes-ctr-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 28,
+	},
+
+	[6] = {
+		.name = "aes-ctr-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 36,
+	},
+
+	[7] = {
+		.name = "3des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 24,
+	},
+
+	[8] = {
+		.name = "des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 8,
+	},
+};
+
+static struct auth_alg auth_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_AUTH_NULL,
+		.iv_size = 0,
+		.digest_size = 0,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "sha1-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 20,
+	},
+
+	[2] = {
+		.name = "sha256-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
+		.iv_size = 0,
+		.digest_size = 16,
+		.key_size = 32,
+	},
+
+	[3] = {
+		.name = "sha384-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
+		.iv_size = 0,
+		.digest_size = 24,
+		.key_size = 48,
+	},
+
+	[4] = {
+		.name = "sha512-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
+		.iv_size = 0,
+		.digest_size = 32,
+		.key_size = 64,
+	},
+
+	[5] = {
+		.name = "aes-gmac",
+		.alg = RTE_CRYPTO_AUTH_AES_GMAC,
+		.iv_size = 8,
+		.digest_size = 16,
+		.key_size = 20,
+	},
+
+	[6] = {
+		.name = "aes-xcbc-mac-96",
+		.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 16,
+	},
+};
+
+static struct aead_alg aead_algs[] = {
+	[0] = {
+		.name = "aes-gcm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[1] = {
+		.name = "aes-gcm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[2] = {
+		.name = "aes-gcm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[3] = {
+		.name = "aes-ccm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[4] = {
+		.name = "aes-ccm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[5] = {
+		.name = "aes-ccm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[6] = {
+		.name = "chacha20-poly1305",
+		.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
+		.iv_size = 12,
+		.block_size = 64,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+};
+
+static struct cipher_alg *
+cipher_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct cipher_alg *
+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static int
+char_to_hex(char c, uint8_t *val)
+{
+	if (c >= '0' && c <= '9') {
+		*val = c - '0';
+		return 0;
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		*val = c - 'A' + 10;
+		return 0;
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		*val = c - 'a' + 10;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
+{
+	uint32_t i;
+
+	/* Check input arguments. */
+	if (!src || !src[0] || !dst || !n_dst_bytes)
+		return -EINVAL;
+
+	/* Skip any leading "0x" or "0X" in the src string. */
+	if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
+		src += 2;
+
+	/* Convert each group of two hex characters in the src string to one byte in dst array. */
+	for (i = 0; i < n_dst_bytes; i++) {
+		uint8_t a, b;
+		int status;
+
+		status = char_to_hex(*src, &a);
+		if (status)
+			return status;
+		src++;
+
+		status = char_to_hex(*src, &b);
+		if (status)
+			return status;
+		src++;
+
+		dst[i] = a * 16 + b;
+	}
+
+	/* Check for the end of the src string. */
+	if (*src)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+token_is_comment(const char *token)
+{
+	if ((token[0] == '#') ||
+	    (token[0] == ';') ||
+	    ((token[0] == '/') && (token[1] == '/')))
+		return 1; /* TRUE. */
+
+	return 0; /* FALSE. */
+}
+
+#define MAX_TOKENS 64
+
+#define CHECK(condition, msg)          \
+do {                                   \
+	if (!(condition)) {            \
+		if (errmsg)            \
+			*errmsg = msg; \
+		goto error;            \
+	}                              \
+} while (0)
+
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg)
+{
+	char *token_array[MAX_TOKENS], **t;
+	struct rte_swx_ipsec_sa_params *p = NULL;
+	char *s0 = NULL, *s;
+	uint32_t n_tokens = 0;
+	int blank_or_comment = 0;
+
+	/* Check input arguments. */
+	CHECK(string && string[0], "NULL input");
+
+	/* Memory allocation. */
+	s0 = strdup(string);
+	p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
+	CHECK(s0 && p, "Not enough memory");
+
+	/* Parse the string into tokens. */
+	for (s = s0; ; ) {
+		char *token;
+
+		token = strtok_r(s, " \f\n\r\t\v", &s);
+		if (!token || token_is_comment(token))
+			break;
+
+		CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
+
+		token_array[n_tokens] = token;
+		n_tokens++;
+	}
+
+	t = token_array;
+	if (!n_tokens) {
+		blank_or_comment = 1;
+		goto error;
+	}
+
+	/*
+	 * Crypto operation.
+	 */
+	if (!strcmp(t[0], "encrypt"))
+		p->encrypt = 1;
+	else if (!strcmp(t[0], "decrypt"))
+		p->encrypt = 0;
+	else
+		CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
+
+	t++;
+	n_tokens--;
+
+	/*
+	 * Crypto parameters.
+	 */
+	CHECK(n_tokens >= 2, "Not enough tokens");
+
+	if (!strcmp(t[0], "cipher")) {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t key_size;
+
+		p->crypto.is_aead = 0;
+
+		/* cipher. */
+		cipher_alg = cipher_alg_find(t[1]);
+		CHECK(cipher_alg, "Unsupported cipher algorithm");
+
+		key_size = cipher_alg->key_size;
+		p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
+		p->crypto.cipher_auth.cipher.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
+				"Cipher algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
+			CHECK(!status, "Cipher key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+
+		/* authentication. */
+		CHECK(n_tokens >= 2, "Not enough tokens");
+		CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
+
+		auth_alg = auth_alg_find(t[1]);
+		CHECK(auth_alg, "Unsupported authentication algorithm");
+
+		key_size = auth_alg->key_size;
+		p->crypto.cipher_auth.auth.alg = auth_alg->alg;
+		p->crypto.cipher_auth.auth.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
+				"Authentication algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
+			CHECK(!status, "Authentication key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+	} else if (!strcmp(t[0], "aead")) {
+		struct aead_alg *alg;
+		uint32_t key_size;
+		int status;
+
+		p->crypto.is_aead = 1;
+
+		CHECK(n_tokens >= 4, "Not enough tokens");
+		alg = aead_alg_find(t[1]);
+		CHECK(alg, "Unsupported AEAD algorithm");
+
+		key_size = alg->key_size;
+		p->crypto.aead.alg = alg->alg;
+		p->crypto.aead.key_size = key_size;
+
+		CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
+		CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
+			"AEAD algorithm key too big");
+
+		status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
+		CHECK(!status, "AEAD key invalid format");
+
+		t += 4;
+		n_tokens -= 4;
+	} else
+		CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
+
+	/*
+	 * Packet ecapsulation parameters.
+	 */
+	CHECK(n_tokens >= 4, "Not enough tokens");
+	CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
+	CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
+
+	p->encap.esp.spi = strtoul(t[2], &t[2], 0);
+	CHECK(!t[2][0], "ESP SPI field invalid format");
+
+	t += 3;
+	n_tokens -= 3;
+
+	if (!strcmp(t[0], "tunnel")) {
+		p->encap.tunnel_mode = 1;
+
+		CHECK(n_tokens >= 6, "Not enough tokens");
+
+		if (!strcmp(t[1], "ipv4")) {
+			uint32_t addr;
+
+			p->encap.tunnel_ipv4 = 1;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			addr = strtoul(t[3], &t[3], 0);
+			CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
+			p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			addr = strtoul(t[5], &t[5], 0);
+			CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
+			p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
+
+			t += 6;
+			n_tokens -= 6;
+		} else if (!strcmp(t[1], "ipv6")) {
+			int status;
+
+			p->encap.tunnel_ipv4 = 0;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			status = hex_string_parse(t[3],
+						  p->encap.tunnel.ipv6.src_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 source address invalid format");
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			status = hex_string_parse(t[5],
+						  p->encap.tunnel.ipv6.dst_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 destination address invalid format");
+
+			t += 6;
+			n_tokens -= 6;
+		} else
+			CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
+	} else if (!strcmp(t[0], "transport")) {
+		p->encap.tunnel_mode = 0;
+
+		t++;
+		n_tokens--;
+	} else
+		CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
+
+	/*
+	 * Any other parameters.
+	 */
+	CHECK(!n_tokens, "Unexpected trailing tokens");
+
+	free(s0);
+	return p;
+
+error:
+	free(p);
+	free(s0);
+	if (is_blank_or_comment)
+		*is_blank_or_comment = blank_or_comment;
+	return NULL;
+}
+
+static void
+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv4_hdr ipv4_hdr = {
+		.version_ihl = 0x45,
+		.type_of_service = 0,
+		.total_length = 0, /* Cannot be pre-computed. */
+		.packet_id = 0,
+		.fragment_offset = 0,
+		.time_to_live = 64,
+		.next_proto_id = IPPROTO_ESP,
+		.hdr_checksum = 0, /* Cannot be pre-computed. */
+		.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
+		.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
+	};
+
+	memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
+}
+
+static void
+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv6_hdr ipv6_hdr = {
+		.vtc_flow = 0x60000000,
+		.payload_len = 0, /* Cannot be pre-computed. */
+		.proto = IPPROTO_ESP,
+		.hop_limits = 64,
+		.src_addr = {0},
+		.dst_addr = {0},
+	};
+
+	memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
+	memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
+	memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
+}
+
+/* IPsec library SA parameters. */
+static struct rte_crypto_sym_xform *
+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_crypto_sym_xform *xform,
+		uint32_t *salt_out)
+{
+	if (p->crypto.is_aead) {
+		struct aead_alg *alg;
+		uint32_t key_size, salt, iv_length;
+
+		alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
+		if (!alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		key_size = p->crypto.aead.key_size - 4;
+		memcpy(&salt, &p->crypto.aead.key[key_size], 4);
+
+		/* IV length. */
+		iv_length = 12;
+		if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
+			iv_length = 11;
+
+		/* xform. */
+		xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform[0].aead.op = p->encrypt ?
+			RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform[0].aead.algo = p->crypto.aead.alg;
+		xform[0].aead.key.data = p->crypto.aead.key;
+		xform[0].aead.key.length = key_size;
+		xform[0].aead.iv.offset = IV_OFFSET;
+		xform[0].aead.iv.length = iv_length;
+		xform[0].aead.digest_length = alg->digest_size;
+		xform[0].aead.aad_length = alg->aad_size;
+		xform[0].next = NULL;
+
+		*salt_out = salt;
+		return &xform[0];
+	} else {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
+
+		cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
+						   p->crypto.cipher_auth.cipher.key_size);
+		if (!cipher_alg)
+			return NULL;
+
+		auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
+					       p->crypto.cipher_auth.auth.key_size);
+		if (!auth_alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
+		auth_key_size = p->crypto.cipher_auth.auth.key_size;
+
+		switch (p->crypto.cipher_auth.cipher.alg) {
+		case RTE_CRYPTO_CIPHER_AES_CBC:
+		case RTE_CRYPTO_CIPHER_3DES_CBC:
+			salt = (uint32_t)rand();
+			break;
+
+		case RTE_CRYPTO_CIPHER_AES_CTR:
+			cipher_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
+			break;
+
+		default:
+			salt = 0;
+		}
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			auth_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
+		}
+
+		/* IV length. */
+		auth_iv_length = cipher_alg->iv_size;
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
+			auth_iv_length = 12;
+
+		/* xform. */
+		if (p->encrypt) {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[0].cipher.key.length = cipher_key_size;
+			xform[0].cipher.iv.offset = IV_OFFSET;
+			xform[0].cipher.iv.length = cipher_alg->iv_size;
+			xform[0].cipher.dataunit_len = 0;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+			xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[1].auth.key.length = auth_key_size;
+			xform[1].auth.iv.offset = IV_OFFSET;
+			xform[1].auth.iv.length = auth_iv_length;
+			xform[1].auth.digest_length = auth_alg->digest_size;
+			xform[1].next = NULL;
+		} else {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+			xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[0].auth.key.length = auth_key_size;
+			xform[0].auth.iv.offset = IV_OFFSET;
+			xform[0].auth.iv.length = auth_iv_length;
+			xform[0].auth.digest_length = auth_alg->digest_size;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[1].cipher.key.length = cipher_key_size;
+			xform[1].cipher.iv.offset = IV_OFFSET;
+			xform[1].cipher.iv.length = cipher_alg->iv_size;
+			xform[1].cipher.dataunit_len = 0;
+			xform[1].next = NULL;
+		}
+
+		*salt_out = salt;
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			if (p->encrypt)
+				return &xform[1];
+
+			xform[0].next = NULL;
+			return &xform[0];
+		}
+
+		return &xform[0];
+	}
+}
+
+static void
+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_security_ipsec_xform *ipsec_xform,
+		uint32_t salt)
+{
+	ipsec_xform->spi = p->encap.esp.spi;
+
+	ipsec_xform->salt = salt;
+
+	ipsec_xform->options.esn = 0;
+	ipsec_xform->options.udp_encap = 0;
+	ipsec_xform->options.copy_dscp = 1;
+	ipsec_xform->options.copy_flabel = 0;
+	ipsec_xform->options.copy_df = 0;
+	ipsec_xform->options.dec_ttl = 0;
+	ipsec_xform->options.ecn = 1;
+	ipsec_xform->options.stats = 0;
+	ipsec_xform->options.iv_gen_disable = 0;
+	ipsec_xform->options.tunnel_hdr_verify = 0;
+	ipsec_xform->options.udp_ports_verify = 0;
+	ipsec_xform->options.ip_csum_enable = 0;
+	ipsec_xform->options.l4_csum_enable = 0;
+	ipsec_xform->options.ip_reassembly_en = 0;
+	ipsec_xform->options.reserved_opts = 0;
+
+	ipsec_xform->direction = p->encrypt ?
+		RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
+		RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+
+	ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+
+	ipsec_xform->mode = p->encap.tunnel_mode ?
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
+
+	ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
+		RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
+		RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
+			ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
+			ipsec_xform->tunnel.ipv4.dscp = 0;
+			ipsec_xform->tunnel.ipv4.df = 0;
+			ipsec_xform->tunnel.ipv4.ttl = 64;
+		} else {
+			ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
+			ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
+			ipsec_xform->tunnel.ipv6.dscp = 0;
+			ipsec_xform->tunnel.ipv6.flabel = 0;
+			ipsec_xform->tunnel.ipv6.hlimit = 64;
+		}
+	}
+
+	ipsec_xform->life.packets_soft_limit = 0;
+	ipsec_xform->life.bytes_soft_limit = 0;
+	ipsec_xform->life.packets_hard_limit = 0;
+	ipsec_xform->life.bytes_hard_limit = 0;
+
+	ipsec_xform->replay_win_sz = 0;
+
+	ipsec_xform->esn.value = 0;
+
+	ipsec_xform->udp.dport = 0;
+	ipsec_xform->udp.sport = 0;
+}
+
+static int
+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
+		 struct rte_ipsec_sa_prm *sa_prm,
+		 struct rte_ipv4_hdr *ipv4_hdr,
+		 struct rte_ipv6_hdr *ipv6_hdr,
+		 struct rte_crypto_sym_xform *crypto_xform)
+{
+	uint32_t salt;
+
+	memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
+
+	sa_prm->userdata = 0; /* Not used. */
+
+	sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
+
+	/*
+	 * crypto_xform.
+	 */
+	sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
+	if (!sa_prm->crypto_xform)
+		return -EINVAL;
+
+	/*
+	 * ipsec_xform.
+	 */
+	ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
+
+	/*
+	 * tunnel / transport.
+	 *
+	 * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
+	 * the input packet type should be added to the SA configuration parameters.
+	 */
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv4_hdr;
+		} else {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv6_hdr;
+		}
+	} else {
+		sa_prm->trs.proto = IPPROTO_IPIP; /* IPv4. */
+	}
+
+	return 0;
+}
+
+static int
+ipsec_session_create(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *p,
+		     struct rte_ipsec_session *s)
+{
+	struct rte_ipv4_hdr ipv4_hdr;
+	struct rte_ipv6_hdr ipv6_hdr;
+	struct rte_crypto_sym_xform crypto_xform[2];
+	struct rte_ipsec_sa_prm sa_prm;
+	struct rte_ipsec_sa *sa = NULL;
+	struct rte_cryptodev_sym_session *crypto_session = NULL;
+	int sa_size;
+	int sa_valid = 0, status = 0;
+
+	tunnel_ipv4_header_set(&ipv4_hdr, p);
+	tunnel_ipv6_header_set(&ipv6_hdr, p);
+
+	/* IPsec library SA setup. */
+	status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
+	if (status)
+		goto error;
+
+	sa_size = rte_ipsec_sa_size(&sa_prm);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa = calloc(1, sa_size);
+	if (!sa) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa_valid = 1;
+
+	/* Cryptodev library session setup. */
+	crypto_session = rte_cryptodev_sym_session_create(ipsec->dev_id,
+							  sa_prm.crypto_xform,
+							  ipsec->mp_session);
+	if (!crypto_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* IPsec library session setup. */
+	s->sa = sa;
+	s->type = RTE_SECURITY_ACTION_TYPE_NONE;
+	s->crypto.ses = crypto_session;
+	s->crypto.dev_id = ipsec->dev_id;
+	s->pkt_func.prepare.async = NULL;
+	s->pkt_func.process = NULL;
+
+	return rte_ipsec_session_prepare(s);
+
+error:
+	/* sa. */
+	if (sa_valid)
+		rte_ipsec_sa_fini(sa);
+
+	free(sa);
+
+	/* crypto_session. */
+	if (crypto_session)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, crypto_session);
+
+	/* s. */
+	memset(s, 0, sizeof(*s));
+
+	return status;
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec,
+		   struct rte_ipsec_session *s)
+{
+	if (!s)
+		return;
+
+	/* IPsec library SA. */
+	if (s->sa)
+		rte_ipsec_sa_fini(s->sa);
+	free(s->sa);
+
+	/* Cryptodev library session. */
+	if (s->crypto.ses)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, s->crypto.ses);
+
+	/* IPsec library session. */
+	memset(s, 0, sizeof(*s));
+}
+
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *id)
+{
+	struct ipsec_sa *sa;
+	uint32_t sa_id;
+	int status;
+
+	/* Check the input parameters. */
+	if (!ipsec || !sa_params || !id)
+		return -EINVAL;
+
+	/* Allocate a free SADB entry. */
+	if (!ipsec->n_sa_free_id)
+		return -ENOSPC;
+
+	sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
+	ipsec->n_sa_free_id--;
+
+	/* Acquire the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	status = ipsec_session_create(ipsec, sa_params, &sa->s);
+	if (status) {
+		/* Free the allocated SADB entry. */
+		ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+		ipsec->n_sa_free_id++;
+
+		return status;
+	}
+
+	/* Validate the new SA. */
+	sa->valid = 1;
+	*id = sa_id;
+
+	return 0;
+}
+
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id)
+{
+	struct ipsec_sa *sa;
+
+	/* Check the input parameters. */
+	if (!ipsec || (sa_id >= ipsec->n_sa_max))
+		return;
+
+	/* Release the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	ipsec_session_free(ipsec, &sa->s);
+
+	/* Free the SADB entry. */
+	ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+	ipsec->n_sa_free_id++;
+
+	/* Invalidate the SA. */
+	sa->valid = 0;
+}
diff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h
new file mode 100644
index 0000000000..52962f8845
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__
+#define __INCLUDE_RTE_SWX_IPSEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE SWX Internet Protocol Security (IPsec)
+ *
+ * The IPsec block is a companion block for the SWX pipeline used to provide IPsec support to the
+ * pipeline. The block is external to the pipeline, hence it needs to be explicitly instantiated by
+ * the user and connected to a pipeline instance through the pipeline I/O ports.
+ *
+ * Main features:
+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text
+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.
+ *
+ * Security Association (SA):
+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is
+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.
+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then
+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field
+ *   is typically written by the pipeline before sending the packet to the IPsec block.
+ *
+ * Packet format:
+ * - IPsec block input packet (i.e. pipeline output packet):
+ *	- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.
+ *	- IPv4 header.
+ *	- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.
+ * - IPsec block output packet (i.e. pipeline input packet):
+ *	- IPv4 header.
+ *	- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.
+ *
+ * SA update procedure:
+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().
+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().
+ * - To update an existing SA, the control plane has to follow the following steps:
+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can
+ *      fail, for example when the SA table is full.
+ *   2. Wait until no more packets are using the old SA.
+ *   3. Delete the old SA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <rte_compat.h>
+#include <rte_crypto_sym.h>
+
+/**
+ * IPsec Setup API
+ */
+
+/** IPsec instance opaque data structure. */
+struct rte_swx_ipsec;
+
+/** Name size. */
+#ifndef RTE_SWX_IPSEC_NAME_SIZE
+#define RTE_SWX_IPSEC_NAME_SIZE 64
+#endif
+
+/** Maximum burst size. */
+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX
+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256
+#endif
+
+/** IPsec burst sizes. */
+struct rte_swx_ipsec_burst_size {
+	/** Input ring read burst size. */
+	uint32_t ring_rd;
+
+	/** Output ring write burst size. */
+	uint32_t ring_wr;
+
+	/** Crypto device request queue write burst size. */
+	uint32_t crypto_wr;
+
+	/** Crypto device response queue read burst size. */
+	uint32_t crypto_rd;
+};
+
+/**
+ * IPsec instance configuration parameters
+ *
+ */
+struct rte_swx_ipsec_params {
+	/** Input packet queue. */
+	const char *ring_in_name;
+
+	/** Output packet queue.  */
+	const char *ring_out_name;
+
+	/** Crypto device name. */
+	const char *crypto_dev_name;
+
+	/** Crypto device queue pair ID. */
+	uint32_t crypto_dev_queue_pair_id;
+
+	/** Burst size. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/** Maximum number of SAs. */
+	uint32_t n_sa_max;
+};
+
+/**
+ * IPsec input packet meta-data
+ *
+ */
+struct rte_swx_ipsec_input_packet_metadata {
+	/* SA ID. */
+	uint32_t sa_id;
+};
+
+/**
+ * IPsec instance find
+ *
+ * @param[in] name
+ *   IPsec instance name.
+ * @return
+ *   Valid IPsec instance handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name);
+
+/**
+ * IPsec instance create
+ *
+ * @param[out] ipsec
+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this
+ *   function returns successfully.
+ * @param[in] name
+ *   IPsec instance unique name.
+ * @param[in] params
+ *   IPsec instance configuration parameters.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node);
+
+/**
+ * IPsec instance free
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);
+
+/**
+ * IPsec Data Plane API
+ */
+
+/**
+ * IPsec instance run
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);
+
+/*
+ * IPsec Control Plane API
+ */
+
+/** Maximum key size in bytes. */
+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64
+
+/** IPsec SA crypto cipher parameters. */
+struct rte_swx_ipsec_sa_cipher_params {
+	/** Cipher algorithm. */
+	enum rte_crypto_cipher_algorithm alg;
+
+	/** Cipher key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Cipher key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto authentication parameters. */
+struct rte_swx_ipsec_sa_authentication_params {
+	/** Authentication algorithm. */
+	enum rte_crypto_auth_algorithm alg;
+
+	/** Authentication key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Authentication key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */
+struct rte_swx_ipsec_sa_aead_params {
+	/** AEAD algorithm. */
+	enum rte_crypto_aead_algorithm alg;
+
+	/** AEAD key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** AEAD key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec protocol encapsulation parameters. */
+struct rte_swx_ipsec_sa_encap_params {
+	/** Encapsulating Security Payload (ESP) header. */
+	struct {
+		/** Security Parameters Index (SPI) field. */
+		uint32_t spi;
+	} esp;
+
+	/** Tunnel mode when non-zero, transport mode when zero. */
+	int tunnel_mode;
+
+	/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */
+	int tunnel_ipv4;
+
+	/** Tunnel parameters. Valid for tunnel mode only. */
+	union {
+		/** IPv4 header. */
+		struct {
+			/** Source address. */
+			struct in_addr src_addr;
+
+			/** Destination address. */
+			struct in_addr dst_addr;
+		} ipv4;
+
+		/** IPv6 header. */
+		struct {
+			/** Source address. */
+			struct in6_addr src_addr;
+
+			/** Destination address. */
+			struct in6_addr dst_addr;
+		} ipv6;
+	} tunnel;
+};
+
+/** IPsec Security Association (SA) parameters. */
+struct rte_swx_ipsec_sa_params {
+	/** Crypto operation: encrypt when non-zero, decrypt when zero. */
+	int encrypt;
+
+	/** Crypto operation parameters. */
+	struct {
+		RTE_STD_C11
+		union {
+			struct {
+				/** Crypto cipher operation parameters. */
+				struct rte_swx_ipsec_sa_cipher_params cipher;
+
+				/** Crypto authentication operation parameters. */
+				struct rte_swx_ipsec_sa_authentication_params auth;
+			} cipher_auth;
+
+			/** Crypto AEAD operation parameters. */
+			struct rte_swx_ipsec_sa_aead_params aead;
+		};
+
+		/** Non-zero for AEAD, zero for cipher & authentication. */
+		int is_aead;
+	} crypto;
+
+	/** Packet encasulation parameters. */
+	struct rte_swx_ipsec_sa_encap_params encap;
+};
+
+/**
+ * IPsec SA add
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_params
+ *   SA parameters.
+ * @params[out] sa_id.
+ *   On success, the SA ID.
+ * @return
+ *   0 on success or error code otherwise.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *sa_id);
+
+/**
+ * IPsec SA delete
+ *
+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used
+ * by the Data Plane.
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @params[in] sa_id.
+ *   The SA ID.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id);
+
+/**
+ * IPsec SA read from string
+ *
+ * IPsec SA syntax:
+ *
+ * <sa>
+ *    : encrypt <crypto_params> <encap_params>
+ *    | decrypt <crypto_params> <encap_params>
+ *    ;
+ *
+ * <crypto_params>
+ *    : <cipher> <auth>
+ *    | <aead>
+ *    ;
+ *
+ * <cipher>
+ *    : cipher <ciher_alg> key <cipher_key>
+ *    | cipher <cipher_alg>
+ *    ;
+ *
+ * <auth>
+ *    : auth <authentication_alg> key <authentication_key>
+ *    | auth <authentication_alg>
+ *    ;
+ *
+ * <aead>
+ *    : aead <aead_alg> key <aead_key>
+ *    ;
+ *
+ * <encap_params>
+ *    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+ *    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+ *    | esp spi <spi> transport
+ *    ;
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] string
+ *   String containing the SA.
+ * @param[in,out] is_blank_or_comment
+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when
+ *   *string* contains a blank or comment line and to zero otherwise.
+ * @param[in,out] errmsg
+ *   On error, when its input value is not NULL, this argument points to a string with details on
+ *   the detected error.
+ * @return
+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.
+ */
+__rte_experimental
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 184f45a6b7..58e34459c3 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -155,4 +155,13 @@ EXPERIMENTAL {
 	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
+
+	#added in 23.03
+	rte_swx_ipsec_create;
+	rte_swx_ipsec_find;
+	rte_swx_ipsec_free;
+	rte_swx_ipsec_run;
+	rte_swx_ipsec_sa_add;
+	rte_swx_ipsec_sa_delete;
+	rte_swx_ipsec_sa_read;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 02/11] examples/pipeline: rework memory pool support
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 01/11] " Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Rework the memory pool CLI command to accommodate the MBUF private
meta-data area size parameter.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 72 ++++++++++-------
 examples/pipeline/examples/fib.cli            |  2 +-
 examples/pipeline/examples/hash_func.cli      |  2 +-
 examples/pipeline/examples/l2fwd.cli          |  2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |  2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |  2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |  2 +-
 examples/pipeline/examples/learner.cli        |  2 +-
 examples/pipeline/examples/meter.cli          |  2 +-
 examples/pipeline/examples/mirroring.cli      |  2 +-
 examples/pipeline/examples/recirculation.cli  |  2 +-
 examples/pipeline/examples/registers.cli      |  2 +-
 examples/pipeline/examples/selector.cli       |  2 +-
 examples/pipeline/examples/varbit.cli         |  2 +-
 examples/pipeline/examples/vxlan.cli          |  2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |  2 +-
 examples/pipeline/obj.c                       | 80 +------------------
 examples/pipeline/obj.h                       | 27 -------
 18 files changed, 63 insertions(+), 146 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index e1e7aaddc1..14f1cfa47e 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -192,72 +192,88 @@ parse_table_entry(struct rte_swx_ctl_pipeline *p,
 }
 
 static const char cmd_mempool_help[] =
-"mempool <mempool_name>\n"
-"   buffer <buffer_size>\n"
-"   pool <pool_size>\n"
-"   cache <cache_size>\n"
-"   cpu <cpu_id>\n";
+"mempool <mempool_name> "
+"meta <mbuf_private_size> "
+"pkt <pkt_buffer_size> "
+"pool <pool_size> "
+"cache <cache_size> "
+"numa <numa_node>\n";
 
 static void
 cmd_mempool(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
+	    uint32_t n_tokens,
+	    char *out,
+	    size_t out_size,
+	    void *obj __rte_unused)
 {
-	struct mempool_params p;
-	char *name;
-	struct mempool *mempool;
+	struct rte_mempool *mp;
+	char *mempool_name;
+	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
 
-	if (n_tokens != 10) {
+	if (n_tokens != 12) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	name = tokens[1];
+	mempool_name = tokens[1];
+
+	if (strcmp(tokens[2], "meta")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
+		return;
+	}
 
-	if (strcmp(tokens[2], "buffer") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
+	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
+	if (strcmp(tokens[4], "pkt")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
 		return;
 	}
 
-	if (strcmp(tokens[4], "pool") != 0) {
+	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
+		return;
+	}
+
+	if (strcmp(tokens[6], "pool")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
 		return;
 	}
 
-	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
+	if (parser_read_uint32(&pool_size, tokens[7])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
 		return;
 	}
 
-	if (strcmp(tokens[6], "cache") != 0) {
+	if (strcmp(tokens[8], "cache")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
+	if (parser_read_uint32(&cache_size, tokens[9])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
 		return;
 	}
 
-	if (strcmp(tokens[8], "cpu") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+	if (strcmp(tokens[10], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+	if (parser_read_uint32(&numa_node, tokens[11])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	mempool = mempool_create(obj, name, &p);
-	if (mempool == NULL) {
+	mp = rte_pktmbuf_pool_create(mempool_name,
+				     pool_size,
+				     cache_size,
+				     mbuf_private_size,
+				     pkt_buffer_size,
+				     numa_node);
+	if (!mp) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 4e30c1320f..2450dc9ca4 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/fib.c /tmp/fib.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index b2e219e4c9..3840e47a2b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index 27e37021b9..c5505df296 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 11bb4543b9..bd700707f5 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index 8724dae3b0..aa64b32ab2 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 4db0a0dc56..619d17474f 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 6c8aa3921e..42184be1c8 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/learner.c /tmp/learner.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 21582554b9..0fa38b16b8 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/meter.c /tmp/meter.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 1d439e04d3..e7391de74b 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 52d0894f12..965c870fd4 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 3516f76a5b..aa775f71e5 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/registers.c /tmp/registers.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0e251b657..f0608de184 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/selector.c /tmp/selector.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 0f89990471..648b8882c3 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 1fbd1be6e4..4565adfaea 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index adc7f73312..510c2ad870 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index cac825f22a..697d14a901 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -13,18 +13,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
 
-/*
- * mempool
- */
-TAILQ_HEAD(mempool_list, mempool);
-
 /*
  * link
  */
@@ -39,75 +33,10 @@ TAILQ_HEAD(ring_list, ring);
  * obj
  */
 struct obj {
-	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
 };
 
-/*
- * mempool
- */
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
-
-struct mempool *
-mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
-{
-	struct mempool *mempool;
-	struct rte_mempool *m;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		mempool_find(obj, name) ||
-		(params == NULL) ||
-		(params->buffer_size < BUFFER_SIZE_MIN) ||
-		(params->pool_size == 0))
-		return NULL;
-
-	/* Resource create */
-	m = rte_pktmbuf_pool_create(
-		name,
-		params->pool_size,
-		params->cache_size,
-		0,
-		params->buffer_size - sizeof(struct rte_mbuf),
-		params->cpu_id);
-
-	if (m == NULL)
-		return NULL;
-
-	/* Node allocation */
-	mempool = calloc(1, sizeof(struct mempool));
-	if (mempool == NULL) {
-		rte_mempool_free(m);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(mempool->name, name, sizeof(mempool->name));
-	mempool->m = m;
-	mempool->buffer_size = params->buffer_size;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
-
-	return mempool;
-}
-
-struct mempool *
-mempool_find(struct obj *obj, const char *name)
-{
-	struct mempool *mempool;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(mempool, &obj->mempool_list, node)
-		if (strcmp(mempool->name, name) == 0)
-			return mempool;
-
-	return NULL;
-}
-
 /*
  * link
  */
@@ -171,7 +100,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct rte_eth_conf port_conf;
 	struct link *link;
 	struct link_params_rss *rss;
-	struct mempool *mempool;
+	struct rte_mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
 	uint16_t port_id = 0;
@@ -193,8 +122,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
 
-	mempool = mempool_find(obj, params->rx.mempool_name);
-	if (mempool == NULL)
+	mempool = rte_mempool_lookup(params->rx.mempool_name);
+	if (!mempool)
 		return NULL;
 
 	rss = params->rx.rss;
@@ -251,7 +180,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			params->rx.queue_size,
 			cpu_id,
 			NULL,
-			mempool->m);
+			mempool);
 
 		if (status < 0)
 			return NULL;
@@ -421,7 +350,6 @@ obj_init(void)
 	if (!obj)
 		return NULL;
 
-	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 8ea1c414c2..4ea610ceed 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <sys/queue.h>
 
-#include <rte_mempool.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
@@ -24,32 +23,6 @@ struct obj;
 struct obj *
 obj_init(void);
 
-/*
- * mempool
- */
-struct mempool_params {
-	uint32_t buffer_size;
-	uint32_t pool_size;
-	uint32_t cache_size;
-	uint32_t cpu_id;
-};
-
-struct mempool {
-	TAILQ_ENTRY(mempool) node;
-	char name[NAME_SIZE];
-	struct rte_mempool *m;
-	uint32_t buffer_size;
-};
-
-struct mempool *
-mempool_create(struct obj *obj,
-	       const char *name,
-	       struct mempool_params *params);
-
-struct mempool *
-mempool_find(struct obj *obj,
-	     const char *name);
-
 /*
  * link
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 03/11] examples/pipeline: streamline ring support
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 01/11] " Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Remove redundant ring related code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 24 ++++++++++------
 examples/pipeline/obj.c | 63 -----------------------------------------
 examples/pipeline/obj.h | 21 --------------
 3 files changed, 15 insertions(+), 93 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 14f1cfa47e..517682f7c9 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -11,6 +11,7 @@
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_ring.h>
 #include <rte_swx_port_ethdev.h>
 #include <rte_swx_port_ring.h>
 #include <rte_swx_port_source_sink.h>
@@ -491,11 +492,11 @@ cmd_ring(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct ring_params p;
+	struct rte_ring *r;
 	char *name;
-	struct ring *ring;
+	uint32_t size, numa_node;
 
 	if (n_tokens != 6) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
@@ -504,28 +505,32 @@ cmd_ring(char **tokens,
 
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "size") != 0) {
+	if (strcmp(tokens[2], "size")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
+	if (parser_read_uint32(&size, tokens[3])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
 		return;
 	}
 
-	if (strcmp(tokens[4], "numa") != 0) {
+	if (strcmp(tokens[4], "numa")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
+	if (parser_read_uint32(&numa_node, tokens[5])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	ring = ring_create(obj, name, &p);
-	if (!ring) {
+	r = rte_ring_create(
+		name,
+		size,
+		(int)numa_node,
+		RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (!r) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
@@ -2999,6 +3004,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 697d14a901..3614b99d28 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -24,17 +24,11 @@
  */
 TAILQ_HEAD(link_list, link);
 
-/*
- * ring
- */
-TAILQ_HEAD(ring_list, ring);
-
 /*
  * obj
  */
 struct obj {
 	struct link_list link_list;
-	struct ring_list ring_list;
 };
 
 /*
@@ -282,62 +276,6 @@ link_next(struct obj *obj, struct link *link)
 		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
 }
 
-/*
- * ring
- */
-struct ring *
-ring_create(struct obj *obj, const char *name, struct ring_params *params)
-{
-	struct ring *ring;
-	struct rte_ring *r;
-	unsigned int flags = RING_F_SP_ENQ | RING_F_SC_DEQ;
-
-	/* Check input params */
-	if (!name || ring_find(obj, name) || !params || !params->size)
-		return NULL;
-
-	/**
-	 * Resource create
-	 */
-	r = rte_ring_create(
-		name,
-		params->size,
-		params->numa_node,
-		flags);
-	if (!r)
-		return NULL;
-
-	/* Node allocation */
-	ring = calloc(1, sizeof(struct ring));
-	if (!ring) {
-		rte_ring_free(r);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(ring->name, name, sizeof(ring->name));
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->ring_list, ring, node);
-
-	return ring;
-}
-
-struct ring *
-ring_find(struct obj *obj, const char *name)
-{
-	struct ring *ring;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(ring, &obj->ring_list, node)
-		if (strcmp(ring->name, name) == 0)
-			return ring;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -351,7 +289,6 @@ obj_init(void)
 		return NULL;
 
 	TAILQ_INIT(&obj->link_list);
-	TAILQ_INIT(&obj->ring_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 4ea610ceed..dbbc6d39a0 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -73,25 +73,4 @@ link_find(struct obj *obj, const char *name);
 struct link *
 link_next(struct obj *obj, struct link *link);
 
-/*
- * ring
- */
-struct ring_params {
-	uint32_t size;
-	uint32_t numa_node;
-};
-
-struct ring {
-	TAILQ_ENTRY(ring) node;
-	char name[NAME_SIZE];
-};
-
-struct ring *
-ring_create(struct obj *obj,
-	   const char *name,
-	   struct ring_params *params);
-
-struct ring *
-ring_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Streamline the Ethernet device support code and remove redundant code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c  | 175 ++++++++++++++++++------------------
 examples/pipeline/main.c |  12 +--
 examples/pipeline/obj.c  | 186 +++++++++------------------------------
 examples/pipeline/obj.h  |  51 ++---------
 4 files changed, 136 insertions(+), 288 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 517682f7c9..f4c8300ea7 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -292,16 +292,14 @@ cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct link_params p;
-	struct link_params_rss rss;
-	struct link *link;
+	struct ethdev_params p = {0};
+	struct ethdev_params_rss rss = {0};
 	char *name;
+	int status;
 
-	memset(&p, 0, sizeof(p));
-
-	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
+	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -377,111 +375,98 @@ cmd_ethdev(char **tokens,
 		}
 	}
 
-	link = link_create(obj, name, &p);
-	if (link == NULL) {
+	status = ethdev_config(name, &p);
+	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
 }
 
-/* Print the link stats and info */
 static void
-print_link_info(struct link *link, char *out, size_t out_size)
+ethdev_show(uint16_t port_id, char **out, size_t *out_size)
 {
-	struct rte_eth_stats stats;
-	struct rte_ether_addr mac_addr;
-	struct rte_eth_link eth_link;
-	uint16_t mtu;
-	int ret;
-
-	memset(&stats, 0, sizeof(stats));
-	rte_eth_stats_get(link->port_id, &stats);
-
-	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
-	if (ret != 0) {
-		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	ret = rte_eth_link_get(link->port_id, &eth_link);
-	if (ret < 0) {
-		snprintf(out, out_size, "\n%s: link get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	rte_eth_dev_get_mtu(link->port_id, &mtu);
-
-	snprintf(out, out_size,
-		"\n"
-		"%s: flags=<%s> mtu %u\n"
-		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
-		"\tport# %u  speed %s\n"
-		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
-		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tTX errors %" PRIu64"\n",
-		link->name,
-		eth_link.link_status == 0 ? "DOWN" : "UP",
-		mtu,
-		RTE_ETHER_ADDR_BYTES(&mac_addr),
-		link->n_rxq,
-		link->n_txq,
-		link->port_id,
-		rte_eth_link_speed_to_str(eth_link.link_speed),
-		stats.ipackets,
-		stats.ibytes,
-		stats.ierrors,
-		stats.imissed,
-		stats.rx_nombuf,
-		stats.opackets,
-		stats.obytes,
-		stats.oerrors);
+	char name[RTE_ETH_NAME_MAX_LEN] = {0};
+	struct rte_eth_dev_info info = {0};
+	struct rte_eth_stats stats = {0};
+	struct rte_ether_addr addr = {0};
+	struct rte_eth_link link = {0};
+	uint32_t length;
+	uint16_t mtu = 0;
+
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return;
+
+	rte_eth_dev_get_name_by_port(port_id, name);
+	rte_eth_dev_info_get(port_id, &info);
+	rte_eth_stats_get(port_id, &stats);
+	rte_eth_macaddr_get(port_id, &addr);
+	rte_eth_link_get(port_id, &link);
+	rte_eth_dev_get_mtu(port_id, &mtu);
+
+	snprintf(*out, *out_size,
+		 "%s: flags=<%s> mtu %u\n"
+		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
+		 "\tport# %u  speed %s\n"
+		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
+		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tTX errors %" PRIu64"\n\n",
+		 name,
+		 link.link_status ? "UP" : "DOWN",
+		 mtu,
+		 RTE_ETHER_ADDR_BYTES(&addr),
+		 info.nb_rx_queues,
+		 info.nb_tx_queues,
+		 port_id,
+		 rte_eth_link_speed_to_str(link.link_speed),
+		 stats.ipackets,
+		 stats.ibytes,
+		 stats.ierrors,
+		 stats.imissed,
+		 stats.rx_nombuf,
+		 stats.opackets,
+		 stats.obytes,
+		 stats.oerrors);
+
+	length = strlen(*out);
+	*out_size -= length;
+	*out += length;
 }
 
-/*
- * ethdev show [<ethdev_name>]
- */
+
+static char cmd_ethdev_show_help[] =
+"ethdev show [ <ethdev_name> ]\n";
+
 static void
 cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
-	      void *obj)
+	      void *obj __rte_unused)
 {
-	struct link *link;
-	char *link_name;
+	uint16_t port_id;
 
 	if (n_tokens != 2 && n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (n_tokens == 2) {
-		link = link_next(obj, NULL);
-
-		while (link != NULL) {
-			out_size = out_size - strlen(out);
-			out = &out[strlen(out)];
-
-			print_link_info(link, out, out_size);
-			link = link_next(obj, link);
-		}
-	} else {
-		out_size = out_size - strlen(out);
-		out = &out[strlen(out)];
+	/* Single device. */
+	if (n_tokens == 3) {
+		int status;
 
-		link_name = tokens[2];
-		link = link_find(obj, link_name);
+		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
+		if (status)
+			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
 
-		if (link == NULL) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-					"Link does not exist");
-			return;
-		}
-		print_link_info(link, out, out_size);
+		ethdev_show(port_id, &out, &out_size);
+		return;
 	}
+
+	/*  All devices. */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		if (rte_eth_dev_is_valid_port(port_id))
+			ethdev_show(port_id, &out, &out_size);
 }
 
 static const char cmd_ring_help[] =
@@ -3004,6 +2989,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tethdev show\n"
 			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -3039,9 +3025,16 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "ethdev") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
-		return;
+	if (!strcmp(tokens[0], "ethdev")) {
+		if (n_tokens == 1) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
+			return;
+		}
+
+		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
+			return;
+		}
 	}
 
 	if (strcmp(tokens[0], "ring") == 0) {
diff --git a/examples/pipeline/main.c b/examples/pipeline/main.c
index 6fb839f4cb..da5dd2b3f6 100644
--- a/examples/pipeline/main.c
+++ b/examples/pipeline/main.c
@@ -135,7 +135,6 @@ int
 main(int argc, char **argv)
 {
 	struct conn *conn;
-	struct obj *obj;
 	int status;
 
 	/* Parse application arguments */
@@ -150,13 +149,6 @@ main(int argc, char **argv)
 		return status;
 	};
 
-	/* Obj */
-	obj = obj_init();
-	if (!obj) {
-		printf("Error: Obj initialization failed (%d)\n", status);
-		return status;
-	}
-
 	/* Thread */
 	status = thread_init();
 	if (status) {
@@ -174,10 +166,10 @@ main(int argc, char **argv)
 		cli_script_process(app.script_name,
 			app.conn.msg_in_len_max,
 			app.conn.msg_out_len_max,
-			obj);
+			NULL);
 
 	/* Connectivity */
-	app.conn.msg_handle_arg = obj;
+	app.conn.msg_handle_arg = NULL;
 	conn = conn_init(&app.conn);
 	if (!conn) {
 		printf("Error: Connectivity initialization failed (%d)\n",
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 3614b99d28..143b968472 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -4,35 +4,14 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#ifdef RTE_EXEC_ENV_LINUX
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#endif
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_ctl.h>
 
 #include "obj.h"
 
 /*
- * link
- */
-TAILQ_HEAD(link_list, link);
-
-/*
- * obj
- */
-struct obj {
-	struct link_list link_list;
-};
-
-/*
- * link
+ * ethdev
  */
 static struct rte_eth_conf port_conf_default = {
 	.link_speeds = 0,
@@ -58,7 +37,7 @@ static struct rte_eth_conf port_conf_default = {
 static int
 rss_setup(uint16_t port_id,
 	uint16_t reta_size,
-	struct link_params_rss *rss)
+	struct ethdev_params_rss *rss)
 {
 	struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
 	uint32_t i;
@@ -87,69 +66,64 @@ rss_setup(uint16_t port_id,
 	return status;
 }
 
-struct link *
-link_create(struct obj *obj, const char *name, struct link_params *params)
+int
+ethdev_config(const char *name, struct ethdev_params *params)
 {
 	struct rte_eth_dev_info port_info;
 	struct rte_eth_conf port_conf;
-	struct link *link;
-	struct link_params_rss *rss;
+	struct ethdev_params_rss *rss;
 	struct rte_mempool *mempool;
-	uint32_t cpu_id, i;
-	int status;
+	uint32_t i;
+	int numa_node, status;
 	uint16_t port_id = 0;
 
 	/* Check input params */
-	if ((name == NULL) ||
-		link_find(obj, name) ||
-		(params == NULL) ||
-		(params->rx.n_queues == 0) ||
-		(params->rx.queue_size == 0) ||
-		(params->tx.n_queues == 0) ||
-		(params->tx.queue_size == 0))
-		return NULL;
+	if (!name ||
+	    !name[0] ||
+	    !params ||
+	    !params->rx.n_queues ||
+	    !params->rx.queue_size ||
+	    !params->tx.n_queues ||
+	    !params->tx.queue_size)
+		return -EINVAL;
 
 	status = rte_eth_dev_get_port_by_name(name, &port_id);
 	if (status)
-		return NULL;
+		return -EINVAL;
 
-	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
-		return NULL;
+	status = rte_eth_dev_info_get(port_id, &port_info);
+	if (status)
+		return -EINVAL;
 
 	mempool = rte_mempool_lookup(params->rx.mempool_name);
 	if (!mempool)
-		return NULL;
+		return -EINVAL;
 
 	rss = params->rx.rss;
 	if (rss) {
-		if ((port_info.reta_size == 0) ||
-			(port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512))
-			return NULL;
+		if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512)
+			return -EINVAL;
 
-		if ((rss->n_queues == 0) ||
-			(rss->n_queues >= LINK_RXQ_RSS_MAX))
-			return NULL;
+		if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX)
+			return -EINVAL;
 
 		for (i = 0; i < rss->n_queues; i++)
 			if (rss->queue_id[i] >= port_info.max_rx_queues)
-				return NULL;
+				return -EINVAL;
 	}
 
-	/**
-	 * Resource create
-	 */
 	/* Port */
 	memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
 	if (rss) {
+		uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP;
+
 		port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
-		port_conf.rx_adv_conf.rss_conf.rss_hf =
-			(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
-			port_info.flow_type_rss_offloads;
+		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads;
 	}
 
-	cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
-	if (cpu_id == (uint32_t) SOCKET_ID_ANY)
-		cpu_id = 0;
+	numa_node = rte_eth_dev_socket_id(port_id);
+	if (numa_node == SOCKET_ID_ANY)
+		numa_node = 0;
 
 	status = rte_eth_dev_configure(
 		port_id,
@@ -158,12 +132,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		&port_conf);
 
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (params->promiscuous) {
 		status = rte_eth_promiscuous_enable(port_id);
-		if (status != 0)
-			return NULL;
+		if (status)
+			return -EINVAL;
 	}
 
 	/* Port RX */
@@ -172,12 +146,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->rx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL,
 			mempool);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port TX */
@@ -186,24 +160,24 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->tx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port start */
 	status = rte_eth_dev_start(port_id);
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (rss) {
 		status = rss_setup(port_id, port_info.reta_size, rss);
 
 		if (status) {
 			rte_eth_dev_stop(port_id);
-			return NULL;
+			return -EINVAL;
 		}
 	}
 
@@ -211,84 +185,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	status = rte_eth_dev_set_link_up(port_id);
 	if ((status < 0) && (status != -ENOTSUP)) {
 		rte_eth_dev_stop(port_id);
-		return NULL;
-	}
-
-	/* Node allocation */
-	link = calloc(1, sizeof(struct link));
-	if (link == NULL) {
-		rte_eth_dev_stop(port_id);
-		return NULL;
+		return -EINVAL;
 	}
 
-	/* Node fill in */
-	strlcpy(link->name, name, sizeof(link->name));
-	link->port_id = port_id;
-	link->n_rxq = params->rx.n_queues;
-	link->n_txq = params->tx.n_queues;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->link_list, link, node);
-
-	return link;
-}
-
-int
-link_is_up(struct obj *obj, const char *name)
-{
-	struct rte_eth_link link_params;
-	struct link *link;
-
-	/* Check input params */
-	if (!obj || !name)
-		return 0;
-
-	link = link_find(obj, name);
-	if (link == NULL)
-		return 0;
-
-	/* Resource */
-	if (rte_eth_link_get(link->port_id, &link_params) < 0)
-		return 0;
-
-	return (link_params.link_status == RTE_ETH_LINK_DOWN) ? 0 : 1;
-}
-
-struct link *
-link_find(struct obj *obj, const char *name)
-{
-	struct link *link;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(link, &obj->link_list, node)
-		if (strcmp(link->name, name) == 0)
-			return link;
-
-	return NULL;
-}
-
-struct link *
-link_next(struct obj *obj, struct link *link)
-{
-	return (link == NULL) ?
-		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
-}
-
-/*
- * obj
- */
-struct obj *
-obj_init(void)
-{
-	struct obj *obj;
-
-	obj = calloc(1, sizeof(struct obj));
-	if (!obj)
-		return NULL;
-
-	TAILQ_INIT(&obj->link_list);
-
-	return obj;
+	return 0;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index dbbc6d39a0..fb091f4ba7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -6,41 +6,25 @@
 #define _INCLUDE_OBJ_H_
 
 #include <stdint.h>
-#include <sys/queue.h>
-
-#include <rte_swx_pipeline.h>
-#include <rte_swx_ctl.h>
-
-#ifndef NAME_SIZE
-#define NAME_SIZE 64
-#endif
-
-/*
- * obj
- */
-struct obj;
-
-struct obj *
-obj_init(void);
 
 /*
- * link
+ * ethdev
  */
-#ifndef LINK_RXQ_RSS_MAX
-#define LINK_RXQ_RSS_MAX                                   16
+#ifndef ETHDEV_RXQ_RSS_MAX
+#define ETHDEV_RXQ_RSS_MAX 16
 #endif
 
-struct link_params_rss {
-	uint32_t queue_id[LINK_RXQ_RSS_MAX];
+struct ethdev_params_rss {
+	uint32_t queue_id[ETHDEV_RXQ_RSS_MAX];
 	uint32_t n_queues;
 };
 
-struct link_params {
+struct ethdev_params {
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
 		const char *mempool_name;
-		struct link_params_rss *rss;
+		struct ethdev_params_rss *rss;
 	} rx;
 
 	struct {
@@ -51,26 +35,7 @@ struct link_params {
 	int promiscuous;
 };
 
-struct link {
-	TAILQ_ENTRY(link) node;
-	char name[NAME_SIZE];
-	uint16_t port_id;
-	uint32_t n_rxq;
-	uint32_t n_txq;
-};
-
-struct link *
-link_create(struct obj *obj,
-	    const char *name,
-	    struct link_params *params);
-
 int
-link_is_up(struct obj *obj, const char *name);
-
-struct link *
-link_find(struct obj *obj, const char *name);
-
-struct link *
-link_next(struct obj *obj, struct link *link);
+ethdev_config(const char *name, struct ethdev_params *params);
 
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 05/11] examples/pipeline: support crypto devices
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add support for crypto devices in the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c | 60 +++++++++++++++++++++++++++++++++++++++++
 examples/pipeline/obj.h | 11 ++++++++
 2 files changed, 71 insertions(+)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 143b968472..25bfd2da4f 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -7,6 +7,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_cryptodev.h>
 
 #include "obj.h"
 
@@ -190,3 +191,62 @@ ethdev_config(const char *name, struct ethdev_params *params)
 
 	return 0;
 }
+
+/*
+ * cryptodev
+ */
+int
+cryptodev_config(const char *name, struct cryptodev_params *params)
+{
+	struct rte_cryptodev_info dev_info;
+	struct rte_cryptodev_config dev_conf = {0};
+	struct rte_cryptodev_qp_conf queue_conf = {0};
+	uint8_t dev_id;
+	uint32_t socket_id, i;
+	int status;
+
+	/* Check input parameters. */
+	if (!name ||
+	    !params->n_queue_pairs ||
+	    !params->queue_size)
+		return -EINVAL;
+
+	/* Find the crypto device. */
+	status = rte_cryptodev_get_dev_id(name);
+	if (status < 0)
+		return -EINVAL;
+
+	dev_id = (uint8_t)status;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->n_queue_pairs > dev_info.max_nb_queue_pairs)
+		return -EINVAL;
+
+	socket_id = rte_cryptodev_socket_id(dev_id);
+
+	/* Configure the crypto device. */
+	dev_conf.socket_id = socket_id;
+	dev_conf.nb_queue_pairs = params->n_queue_pairs;
+	dev_conf.ff_disable = 0;
+
+	status = rte_cryptodev_configure(dev_id, &dev_conf);
+	if (status)
+		return status;
+
+	/* Configure the crypto device queue pairs. */
+	queue_conf.nb_descriptors = params->queue_size;
+	queue_conf.mp_session = NULL;
+
+	for (i = 0; i < params->n_queue_pairs; i++) {
+		status = rte_cryptodev_queue_pair_setup(dev_id, i, &queue_conf, socket_id);
+		if (status)
+			return status;
+	}
+
+	/* Start the crypto device. */
+	status = rte_cryptodev_start(dev_id);
+	if (status)
+		return status;
+
+	return 0;
+}
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index fb091f4ba7..0f103be6a7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -38,4 +38,15 @@ struct ethdev_params {
 int
 ethdev_config(const char *name, struct ethdev_params *params);
 
+/*
+ * cryptodev
+ */
+struct cryptodev_params {
+	uint32_t n_queue_pairs;
+	uint32_t queue_size;
+};
+
+int
+cryptodev_config(const char *name, struct cryptodev_params *params);
+
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (4 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI command for the configuration of crypto devices.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f4c8300ea7..6b5d5a3370 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -521,6 +521,58 @@ cmd_ring(char **tokens,
 	}
 }
 
+static const char cmd_cryptodev_help[] =
+"cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
+
+static void
+cmd_cryptodev(char **tokens,
+	      uint32_t n_tokens,
+	      char *out,
+	      size_t out_size,
+	      void *obj __rte_unused)
+{
+	struct cryptodev_params params;
+	char *cryptodev_name;
+	int status;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	cryptodev_name = tokens[1];
+
+	if (strcmp(tokens[2], "queues")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
+		return;
+	}
+
+	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
+		return;
+	}
+
+	if (strcmp(tokens[4], "qsize")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
+		return;
+	}
+
+
+	if (parser_read_uint32(&params.queue_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
+		return;
+	}
+
+	status = cryptodev_config(cryptodev_name, &params);
+	if (status)
+		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
+}
+
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2991,6 +3043,7 @@ cmd_help(char **tokens,
 			"\tethdev\n"
 			"\tethdev show\n"
 			"\tring\n"
+			"\tcryptodev\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3042,6 +3095,11 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3297,6 +3355,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
+		return;
+	}
+
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (5 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands for IPsec block configuration.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 298 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 298 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 6b5d5a3370..92ec0fd8e3 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -18,6 +18,7 @@
 #include <rte_swx_port_fd.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
+#include <rte_swx_ipsec.h>
 
 #include "cli.h"
 
@@ -2910,6 +2911,263 @@ cmd_pipeline_mirror_session(char **tokens,
 	}
 }
 
+static const char cmd_ipsec_create_help[] =
+"ipsec <ipsec_instance_name> create "
+"in <ring_in_name> out <ring_out_name> "
+"cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
+"bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
+"samax <n_sa_max> "
+"numa <numa_node>\n";
+
+static void
+cmd_ipsec_create(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec_params p;
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t numa_node;
+	int status;
+
+	if (n_tokens != 20) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+
+	if (strcmp(tokens[2], "create")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
+		return;
+	}
+
+	if (strcmp(tokens[3], "in")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+		return;
+	}
+
+	p.ring_in_name = tokens[4];
+
+	if (strcmp(tokens[5], "out")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+		return;
+	}
+
+	p.ring_out_name = tokens[6];
+
+	if (strcmp(tokens[7], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	p.crypto_dev_name = tokens[8];
+
+	if (strcmp(tokens[9], "cryptoq")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
+		return;
+	}
+
+	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
+		return;
+	}
+
+	if (strcmp(tokens[11], "bsz")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
+		return;
+	}
+
+	if (strcmp(tokens[16], "samax")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
+		return;
+	}
+
+	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
+		return;
+	}
+
+	if (strcmp(tokens[18], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
+		return;
+	}
+
+	if (parser_read_uint32(&numa_node, tokens[19])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	status = rte_swx_ipsec_create(&ipsec,
+				      ipsec_instance_name,
+				      &p,
+				      (int)numa_node);
+	if (status)
+		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
+}
+
+static const char cmd_ipsec_sa_add_help[] =
+"ipsec <ipsec_instance_name> sa add <file_name>\n";
+
+static void
+cmd_ipsec_sa_add(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name, *file_name, *line = NULL;
+	FILE *file = NULL;
+	uint32_t line_id = 0;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		goto free;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		goto free;
+	}
+
+	if (strcmp(tokens[3], "add")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+		goto free;
+	}
+
+	file_name = tokens[4];
+	file = fopen(file_name, "r");
+	if (!file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+		goto free;
+	}
+
+	/* Buffer allocation. */
+	line = malloc(MAX_LINE_SIZE);
+	if (!line) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	/* File read. */
+	for (line_id = 1; ; line_id++) {
+		struct rte_swx_ipsec_sa_params *sa;
+		const char *err_msg;
+		uint32_t sa_id = 0;
+		int is_blank_or_comment, status = 0;
+
+		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+			break;
+
+		/* Read SA from file. */
+		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
+		if (!sa) {
+			if (is_blank_or_comment)
+				continue;
+
+			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
+				file_name, line_id, err_msg);
+			goto free;
+		}
+
+		snprintf(out, out_size, "%s", line);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		/* Add the SA to the IPsec instance. Free the SA. */
+		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
+		if (status)
+			snprintf(out, out_size, "\t: Error (%d)\n", status);
+		else
+			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		free(sa);
+		if (status)
+			goto free;
+	}
+
+free:
+	if (file)
+		fclose(file);
+	free(line);
+}
+
+static const char cmd_ipsec_sa_delete_help[] =
+"ipsec <ipsec_instance_name> sa delete <sa_id>\n";
+
+static void
+cmd_ipsec_sa_delete(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t sa_id;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		return;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		return;
+	}
+
+	if (strcmp(tokens[3], "delete")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+		return;
+	}
+
+	if (parser_read_uint32(&sa_id, tokens[4])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
+		return;
+	}
+
+	rte_swx_ipsec_sa_delete(ipsec, sa_id);
+}
+
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
 
@@ -3068,6 +3326,9 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tipsec create\n"
+			"\tipsec sa add\n"
+			"\tipsec sa delete\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
 		return;
@@ -3292,6 +3553,26 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "add")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "delete")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
+		return;
+	}
+
 	if ((n_tokens == 3) &&
 		(strcmp(tokens[0], "thread") == 0) &&
 		(strcmp(tokens[1], "pipeline") == 0)) {
@@ -3540,6 +3821,23 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "ipsec")) {
+		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
+			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
+			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
+			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	if (strcmp(tokens[0], "thread") == 0) {
 		if ((n_tokens >= 5) &&
 			(strcmp(tokens[4], "enable") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (6 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the configuration updates for the data plane threads were
performed through message queues. Now, this mechanism is replaced by
the control thread updating the mirror copy of the data plane thread
configuration followed by pointer swapping.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 154 ++---
 examples/pipeline/examples/fib.cli            |   2 +-
 examples/pipeline/examples/hash_func.cli      |   2 +-
 examples/pipeline/examples/l2fwd.cli          |   2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |   2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |   2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |   2 +-
 examples/pipeline/examples/learner.cli        |   2 +-
 examples/pipeline/examples/meter.cli          |   2 +-
 examples/pipeline/examples/mirroring.cli      |   2 +-
 examples/pipeline/examples/recirculation.cli  |   2 +-
 examples/pipeline/examples/registers.cli      |   2 +-
 examples/pipeline/examples/selector.cli       |   2 +-
 examples/pipeline/examples/varbit.cli         |   2 +-
 examples/pipeline/examples/vxlan.cli          |   2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |   2 +-
 examples/pipeline/thread.c                    | 586 ++++--------------
 examples/pipeline/thread.h                    |  17 +-
 18 files changed, 217 insertions(+), 570 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 92ec0fd8e3..68bb93a46c 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3168,119 +3168,86 @@ cmd_ipsec_sa_delete(char **tokens,
 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
 }
 
-static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
-
-#ifndef TIMER_PERIOD_MS_DEFAULT
-#define TIMER_PERIOD_MS_DEFAULT 10
-#endif
+static const char cmd_pipeline_enable_help[] =
+"pipeline <pipeline_name> enable thread <thread_id>\n";
 
 static void
-cmd_thread_pipeline_enable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_enable(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
 {
 	char *pipeline_name;
 	struct rte_swx_pipeline *p;
-	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
+	uint32_t thread_id;
 	int status;
 
-	if ((n_tokens != 5) && (n_tokens != 7)) {
+	if (n_tokens != 5) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "enable") != 0) {
+	if (strcmp(tokens[2], "enable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
 		return;
 	}
 
-	if (n_tokens == 7) {
-		if (strcmp(tokens[5], "period") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
-			return;
-		}
+	if (strcmp(tokens[3], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
 
-		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-			return;
-		}
+	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
+	status = pipeline_enable(p, thread_id);
 	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
+		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
 		return;
 	}
 }
 
-static const char cmd_thread_pipeline_disable_help[] =
-"thread <thread_id> pipeline <pipeline_name> disable\n";
+static const char cmd_pipeline_disable_help[] =
+"pipeline <pipeline_name> disable\n";
 
 static void
-cmd_thread_pipeline_disable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_disable(char **tokens,
+		     uint32_t n_tokens,
+		     char *out,
+		     size_t out_size,
+		     void *obj __rte_unused)
 {
 	struct rte_swx_pipeline *p;
 	char *pipeline_name;
-	uint32_t thread_id;
-	int status;
 
-	if (n_tokens != 5) {
+	if (n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "disable") != 0) {
+	if (strcmp(tokens[2], "disable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, p);
-	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL,
-			"thread pipeline disable");
-		return;
-	}
+	pipeline_disable(p);
 }
 
 static void
@@ -3326,11 +3293,12 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tpipeline enable\n"
+			"\tpipeline disable\n\n"
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
-			"\tthread pipeline enable\n"
-			"\tthread pipeline disable\n\n");
+			);
 		return;
 	}
 
@@ -3553,6 +3521,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
+		return;
+	}
+
 	if (!strcmp(tokens[0], "ipsec") &&
 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
@@ -3573,22 +3553,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((n_tokens == 3) &&
-		(strcmp(tokens[0], "thread") == 0) &&
-		(strcmp(tokens[1], "pipeline") == 0)) {
-		if (strcmp(tokens[2], "enable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_enable_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "disable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_disable_help);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3819,6 +3783,16 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
+			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
+			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
 	}
 
 	if (!strcmp(tokens[0], "ipsec")) {
@@ -3838,22 +3812,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
-	if (strcmp(tokens[0], "thread") == 0) {
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "enable") == 0)) {
-			cmd_thread_pipeline_enable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "disable") == 0)) {
-			cmd_thread_pipeline_disable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 2450dc9ca4..d2485cefe0 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -54,4 +54,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index 3840e47a2b..2c20f7a7a0 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index c5505df296..383fe346c0 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethde
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index bd700707f5..d02cb2c470 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index aa64b32ab2..d4ea449e03 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 619d17474f..8a25c98096 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 42184be1c8..04c7b4d26f 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/eth
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 0fa38b16b8..9e064dbb52 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 meter meters set profile platinum index from 0 to 15
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index e7391de74b..da6bd338fb 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 mirror session 3 port 0 clone slow truncate 128
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 965c870fd4..f925179bf7 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/exampl
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index aa775f71e5..58dde5fdf1 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0608de184..9af56cd0f1 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 selector s show
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 648b8882c3..621e7f5627 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethd
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 4565adfaea..73eb29a6b2 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 510c2ad870..828342408b 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -36,4 +36,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 6d15f51fb2..3001bc0858 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -3,17 +3,11 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
 
+#include <rte_atomic.h>
 #include <rte_common.h>
-#include <rte_cycles.h>
 #include <rte_lcore.h>
-#include <rte_ring.h>
-
-#include <rte_table_acl.h>
-#include <rte_table_array.h>
-#include <rte_table_hash.h>
-#include <rte_table_lpm.h>
-#include <rte_table_lpm_ipv6.h>
 
 #include "obj.h"
 #include "thread.h"
@@ -22,14 +16,6 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
-#ifndef THREAD_MSGQ_SIZE
-#define THREAD_MSGQ_SIZE                                   64
-#endif
-
-#ifndef THREAD_TIMER_PERIOD_MS
-#define THREAD_TIMER_PERIOD_MS                             100
-#endif
-
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -40,509 +26,209 @@
 #endif
 
 /**
- * Control thread: data plane thread context
+ * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
+ * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
+ * blocks.
+ *
+ * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
+ * single thread at any given time, so the same pipeline/block cannot show up in the list of
+ * pipelines/blocks of more than one thread at any specific moment.
+ *
+ * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
+ *  - Read-write by the CP thread;
+ *  - Read-only by the DP thread.
  */
 struct thread {
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-
-	uint32_t enabled;
-};
-
-static struct thread thread[RTE_MAX_LCORE];
-
-/**
- * Data plane threads: context
- */
-struct pipeline_data {
-	struct rte_swx_pipeline *p;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-};
-
-struct thread_data {
-	struct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];
-	uint32_t n_pipelines;
-
-	struct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-	uint64_t time_next_min;
+	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	volatile uint64_t n_pipelines;
+	int enabled;
 } __rte_cache_aligned;
 
-static struct thread_data thread_data[RTE_MAX_LCORE];
+static struct thread threads[RTE_MAX_LCORE];
 
 /**
- * Control thread: data plane thread init
+ * Control plane (CP) thread.
  */
-static void
-thread_free(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct thread *t = &thread[i];
-
-		if (!rte_lcore_is_enabled(i))
-			continue;
-
-		/* MSGQs */
-		rte_ring_free(t->msgq_req);
-
-		rte_ring_free(t->msgq_rsp);
-	}
-}
-
 int
 thread_init(void)
 {
-	uint32_t i;
-
-	RTE_LCORE_FOREACH_WORKER(i) {
-		char name[NAME_MAX];
-		struct rte_ring *msgq_req, *msgq_rsp;
-		struct thread *t = &thread[i];
-		struct thread_data *t_data = &thread_data[i];
-		uint32_t cpu_id = rte_lcore_to_socket_id(i);
-
-		/* MSGQs */
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-REQ", i);
-
-		msgq_req = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_req == NULL) {
-			thread_free();
-			return -1;
-		}
+	uint32_t thread_id;
 
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-RSP", i);
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
 
-		msgq_rsp = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_rsp == NULL) {
-			thread_free();
-			return -1;
-		}
-
-		/* Control thread records */
-		t->msgq_req = msgq_req;
-		t->msgq_rsp = msgq_rsp;
 		t->enabled = 1;
-
-		/* Data plane thread records */
-		t_data->n_pipelines = 0;
-		t_data->msgq_req = msgq_req;
-		t_data->msgq_rsp = msgq_rsp;
-		t_data->timer_period =
-			(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;
-		t_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;
-		t_data->time_next_min = t_data->time_next;
 	}
 
 	return 0;
 }
 
-static inline int
-thread_is_running(uint32_t thread_id)
+static uint32_t
+pipeline_find(struct rte_swx_pipeline *p)
 {
-	enum rte_lcore_state_t thread_state;
+	uint32_t thread_id;
 
-	thread_state = rte_eal_get_lcore_state(thread_id);
-	return (thread_state == RUNNING) ? 1 : 0;
-}
-
-/**
- * Control thread & data plane threads: message passing
- */
-enum thread_req_type {
-	THREAD_REQ_PIPELINE_ENABLE = 0,
-	THREAD_REQ_PIPELINE_DISABLE,
-	THREAD_REQ_MAX
-};
-
-struct thread_msg_req {
-	enum thread_req_type type;
-
-	union {
-		struct {
-			struct rte_swx_pipeline *p;
-			uint32_t timer_period_ms;
-		} pipeline_enable;
-
-		struct {
-			struct rte_swx_pipeline *p;
-		} pipeline_disable;
-	};
-};
-
-struct thread_msg_rsp {
-	int status;
-};
-
-/**
- * Control thread
- */
-static struct thread_msg_req *
-thread_msg_alloc(void)
-{
-	size_t size = RTE_MAX(sizeof(struct thread_msg_req),
-		sizeof(struct thread_msg_rsp));
-
-	return calloc(1, size);
-}
-
-static void
-thread_msg_free(struct thread_msg_rsp *rsp)
-{
-	free(rsp);
-}
-
-static struct thread_msg_rsp *
-thread_msg_send_recv(uint32_t thread_id,
-	struct thread_msg_req *req)
-{
-	struct thread *t = &thread[thread_id];
-	struct rte_ring *msgq_req = t->msgq_req;
-	struct rte_ring *msgq_rsp = t->msgq_rsp;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* send */
-	do {
-		status = rte_ring_sp_enqueue(msgq_req, req);
-	} while (status == -ENOBUFS);
-
-	/* recv */
-	do {
-		status = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);
-	} while (status != 0);
-
-	return rsp;
-}
-
-static int
-thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
-{
-	struct thread *t = &thread[thread_id];
-	struct thread_data *td = &thread_data[thread_id];
-	uint32_t i;
-
-	if (!t->enabled)
-		return 0; /* Pipeline NOT enabled on this thread. */
-
-	for (i = 0; i < td->n_pipelines; i++)
-		if (td->p[i] == p)
-			return 1; /* Pipeline enabled on this thread. */
-
-	return 0 /* Pipeline NOT enabled on this thread. */;
-}
-
-int
-thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
-{
-	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		struct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];
-
-		if (td->n_pipelines >= THREAD_PIPELINES_MAX)
-			return -1;
-
-		/* Data plane thread */
-		td->p[td->n_pipelines] = p;
-
-		tdp->p = p;
-		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
-		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
-		td->n_pipelines++;
+		if (!t->enabled)
+			continue;
 
-		return 0;
+		for (i = 0; i < t->n_pipelines; i++)
+			if (t->pipelines[i] == p)
+				break;
 	}
 
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p;
-	req->pipeline_enable.timer_period_ms = timer_period_ms;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
-
-	/* Read response */
-	status = rsp->status;
-
-	/* Free response */
-	thread_msg_free(rsp);
-
-	/* Request completion */
-	if (status)
-		return status;
-
-	return 0;
+	return thread_id;
 }
 
+/**
+ * Enable a given pipeline to run on a specific DP thread.
+ *
+ * CP thread:
+ *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
+ *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
+ *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
+ *    write barrier used below.
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
+ *    the new pipeline when it sees the updated t->n_pipelines value;
+ *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
+ *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
+ *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
+ */
 int
-thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
 {
 	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
+	uint64_t n_pipelines;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_pipeline_enabled(thread_id, p))
-		return 0;
+	if (!p || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
 
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		uint32_t i;
-
-		for (i = 0; i < td->n_pipelines; i++) {
-			struct pipeline_data *tdp = &td->pipeline_data[i];
-
-			if (tdp->p != p)
-				continue;
-
-			/* Data plane thread */
-			if (i < td->n_pipelines - 1) {
-				struct rte_swx_pipeline *pipeline_last =
-					td->p[td->n_pipelines - 1];
-				struct pipeline_data *tdp_last =
-					&td->pipeline_data[td->n_pipelines - 1];
-
-				td->p[i] = pipeline_last;
-				memcpy(tdp, tdp_last, sizeof(*tdp));
-			}
-
-			td->n_pipelines--;
-
-			break;
-		}
+	if (pipeline_find(p) < RTE_MAX_LCORE)
+		return -EEXIST;
 
-		return 0;
-	}
-
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
 
-	/* Read response */
-	status = rsp->status;
+	n_pipelines = t->n_pipelines;
 
-	/* Free response */
-	thread_msg_free(rsp);
+	/* Check there is room for at least one more pipeline. */
+	if (n_pipelines >= THREAD_PIPELINES_MAX)
+		return -ENOSPC;
 
-	/* Request completion */
-	if (status)
-		return status;
+	/* Install the new pipeline. */
+	t->pipelines[n_pipelines] = p;
+	rte_wmb();
+	t->n_pipelines = n_pipelines + 1;
 
 	return 0;
 }
 
 /**
- * Data plane threads: message handling
+ * Disable a given pipeline from running on any DP thread.
+ *
+ * CP thread:
+ *  - Detects the thread that is running the given pipeline, if any;
+ *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
+ *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
+ *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
+ *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
+ *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
+ *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
+ *    below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final value of t->n_pipelines;
+ *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
+ *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
+ *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
+ *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
+ *    behaviors are detailed below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final values of the t->pipeline[] array;
+ *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
+ *       the exception of the pipeline_last, which is not run during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
  */
-static inline struct thread_msg_req *
-thread_msg_recv(struct rte_ring *msgq_req)
-{
-	struct thread_msg_req *req;
-
-	int status = rte_ring_sc_dequeue(msgq_req, (void **) &req);
-
-	if (status != 0)
-		return NULL;
-
-	return req;
-}
-
-static inline void
-thread_msg_send(struct rte_ring *msgq_rsp,
-	struct thread_msg_rsp *rsp)
-{
-	int status;
-
-	do {
-		status = rte_ring_sp_enqueue(msgq_rsp, rsp);
-	} while (status == -ENOBUFS);
-}
-
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_enable(struct thread_data *t,
-	struct thread_msg_req *req)
+void
+pipeline_disable(struct rte_swx_pipeline *p)
 {
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	struct pipeline_data *p = &t->pipeline_data[t->n_pipelines];
-
-	/* Request */
-	if (t->n_pipelines >= THREAD_PIPELINES_MAX) {
-		rsp->status = -1;
-		return rsp;
-	}
-
-	t->p[t->n_pipelines] = req->pipeline_enable.p;
-
-	p->p = req->pipeline_enable.p;
-	p->timer_period = (rte_get_tsc_hz() *
-		req->pipeline_enable.timer_period_ms) / 1000;
-	p->time_next = rte_get_tsc_cycles() + p->timer_period;
+	struct thread *t;
+	uint64_t n_pipelines;
+	uint32_t thread_id, i;
 
-	t->n_pipelines++;
+	/* Check input params */
+	if (!p)
+		return;
 
-	/* Response */
-	rsp->status = 0;
-	return rsp;
-}
+	/* Find the thread that runs this pipeline. */
+	thread_id = pipeline_find(p);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
 
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_disable(struct thread_data *t,
-	struct thread_msg_req *req)
-{
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	uint32_t n_pipelines = t->n_pipelines;
-	struct rte_swx_pipeline *pipeline = req->pipeline_disable.p;
-	uint32_t i;
+	t = &threads[thread_id];
+	n_pipelines = t->n_pipelines;
 
-	/* find pipeline */
 	for (i = 0; i < n_pipelines; i++) {
-		struct pipeline_data *p = &t->pipeline_data[i];
+		struct rte_swx_pipeline *pipeline = t->pipelines[i];
 
-		if (p->p != pipeline)
+		if (pipeline != p)
 			continue;
 
 		if (i < n_pipelines - 1) {
-			struct rte_swx_pipeline *pipeline_last =
-				t->p[n_pipelines - 1];
-			struct pipeline_data *p_last =
-				&t->pipeline_data[n_pipelines - 1];
+			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
 
-			t->p[i] = pipeline_last;
-			memcpy(p, p_last, sizeof(*p));
+			t->pipelines[i] = pipeline_last;
 		}
 
-		t->n_pipelines--;
+		rte_wmb();
+		t->n_pipelines = n_pipelines - 1;
 
-		rsp->status = 0;
-		return rsp;
+		return;
 	}
 
-	/* should not get here */
-	rsp->status = 0;
-	return rsp;
-}
-
-static void
-thread_msg_handle(struct thread_data *t)
-{
-	for ( ; ; ) {
-		struct thread_msg_req *req;
-		struct thread_msg_rsp *rsp;
-
-		req = thread_msg_recv(t->msgq_req);
-		if (req == NULL)
-			break;
-
-		switch (req->type) {
-		case THREAD_REQ_PIPELINE_ENABLE:
-			rsp = thread_msg_handle_pipeline_enable(t, req);
-			break;
-
-		case THREAD_REQ_PIPELINE_DISABLE:
-			rsp = thread_msg_handle_pipeline_disable(t, req);
-			break;
-
-		default:
-			rsp = (struct thread_msg_rsp *) req;
-			rsp->status = -1;
-		}
-
-		thread_msg_send(t->msgq_rsp, rsp);
-	}
+	return;
 }
 
 /**
- * Data plane threads: main
+ * Data plane (DP) threads.
+ *
+ * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
+ * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
+ * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
+ * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
+ * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
+ * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
+ * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
+ * marked as volatile.
  */
 int
 thread_main(void *arg __rte_unused)
 {
-	struct thread_data *t;
-	uint32_t thread_id, i;
+	struct thread *t;
+	uint32_t thread_id;
 
 	thread_id = rte_lcore_id();
-	t = &thread_data[thread_id];
-
-	/* Dispatch loop */
-	for (i = 0; ; i++) {
-		uint32_t j;
-
-		/* Data Plane */
-		for (j = 0; j < t->n_pipelines; j++)
-			rte_swx_pipeline_run(t->p[j], PIPELINE_INSTR_QUANTA);
+	t = &threads[thread_id];
 
-		/* Control Plane */
-		if ((i & 0xF) == 0) {
-			uint64_t time = rte_get_tsc_cycles();
-			uint64_t time_next_min = UINT64_MAX;
-
-			if (time < t->time_next_min)
-				continue;
-
-			/* Thread message queues */
-			{
-				uint64_t time_next = t->time_next;
-
-				if (time_next <= time) {
-					thread_msg_handle(t);
-					time_next = time + t->timer_period;
-					t->time_next = time_next;
-				}
-
-				if (time_next < time_next_min)
-					time_next_min = time_next;
-			}
+	/* Dispatch loop. */
+	for ( ; ; ) {
+		uint32_t i;
 
-			t->time_next_min = time_next_min;
-		}
+		/* Pipelines. */
+		for (i = 0; i < t->n_pipelines; i++)
+			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 712cb25bbb..338d480abb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -9,18 +9,21 @@
 
 #include <rte_swx_pipeline.h>
 
+/**
+ * Control plane (CP) thread.
+ */
 int
-thread_pipeline_enable(uint32_t thread_id,
-		       struct rte_swx_pipeline *p,
-		       uint32_t timer_period_ms);
+thread_init(void);
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-			struct rte_swx_pipeline *p);
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 
-int
-thread_init(void);
+void
+pipeline_disable(struct rte_swx_pipeline *p);
 
+/**
+ * Data plane (DP) threads.
+ */
 int
 thread_main(void *arg);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (7 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the data plane threads only supported the execution of
pipelines assigned to them through configuration updates. Now, the
data plane threads also support running blocks such as IPsec.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/thread.c | 143 +++++++++++++++++++++++++++++++++++++
 examples/pipeline/thread.h |   9 +++
 2 files changed, 152 insertions(+)

diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 3001bc0858..dc3ea73fbf 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -16,6 +16,10 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
+#ifndef THREAD_BLOCKS_MAX
+#define THREAD_BLOCKS_MAX                                  256
+#endif
+
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -38,9 +42,16 @@
  *  - Read-write by the CP thread;
  *  - Read-only by the DP thread.
  */
+struct block {
+	block_run_f block_func;
+	void *block;
+};
+
 struct thread {
 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	struct block *blocks[THREAD_BLOCKS_MAX];
 	volatile uint64_t n_pipelines;
+	volatile uint64_t n_blocks;
 	int enabled;
 } __rte_cache_aligned;
 
@@ -53,14 +64,43 @@ int
 thread_init(void)
 {
 	uint32_t thread_id;
+	int status = 0;
 
 	RTE_LCORE_FOREACH_WORKER(thread_id) {
 		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
 		t->enabled = 1;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			struct block *b;
+
+			b = calloc(1, sizeof(struct block));
+			if (!b) {
+				status = -ENOMEM;
+				goto error;
+			}
+
+			t->blocks[i] = b;
+		}
 	}
 
 	return 0;
+
+error:
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		t->enabled = 0;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			free(t->blocks[i]);
+			t->blocks[i] = NULL;
+		}
+	}
+
+	return status;
 }
 
 static uint32_t
@@ -83,6 +123,26 @@ pipeline_find(struct rte_swx_pipeline *p)
 	return thread_id;
 }
 
+static uint32_t
+block_find(void *b)
+{
+	uint32_t thread_id;
+
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		if (!t->enabled)
+			continue;
+
+		for (i = 0; i < t->n_blocks; i++)
+			if (t->blocks[i]->block == b)
+				break;
+	}
+
+	return thread_id;
+}
+
 /**
  * Enable a given pipeline to run on a specific DP thread.
  *
@@ -201,9 +261,85 @@ pipeline_disable(struct rte_swx_pipeline *p)
 	return;
 }
 
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+
+	/* Check input params */
+	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
+
+	if (block_find(block) < RTE_MAX_LCORE)
+		return -EEXIST;
+
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
+
+	n_blocks = t->n_blocks;
+
+	/* Check there is room for at least one more block. */
+	if (n_blocks >= THREAD_BLOCKS_MAX)
+		return -ENOSPC;
+
+	/* Install the new block. */
+	t->blocks[n_blocks]->block_func = block_func;
+	t->blocks[n_blocks]->block = block;
+
+	rte_wmb();
+	t->n_blocks = n_blocks + 1;
+
+	return 0;
+}
+
+void
+block_disable(void *block)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+	uint32_t thread_id, i;
+
+	/* Check input params */
+	if (!block)
+		return;
+
+	/* Find the thread that runs this block. */
+	thread_id = block_find(block);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
+
+	t = &threads[thread_id];
+	n_blocks = t->n_blocks;
+
+	for (i = 0; i < n_blocks; i++) {
+		struct block *b = t->blocks[i];
+
+		if (block != b->block)
+			continue;
+
+		if (i < n_blocks - 1) {
+			struct block *block_last = t->blocks[n_blocks - 1];
+
+			t->blocks[i] = block_last;
+		}
+
+		rte_wmb();
+		t->n_blocks = n_blocks - 1;
+
+		rte_wmb();
+		t->blocks[n_blocks - 1] = b;
+
+		return;
+	}
+}
+
 /**
  * Data plane (DP) threads.
  *
+
+
  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
@@ -229,6 +365,13 @@ thread_main(void *arg __rte_unused)
 		/* Pipelines. */
 		for (i = 0; i < t->n_pipelines; i++)
 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
+
+		/* Blocks. */
+		for (i = 0; i < t->n_blocks; i++) {
+			struct block *b = t->blocks[i];
+
+			b->block_func(b->block);
+		}
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 338d480abb..f2e643def5 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -21,6 +21,15 @@ pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 void
 pipeline_disable(struct rte_swx_pipeline *p);
 
+typedef void
+(*block_run_f)(void *block);
+
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id);
+
+void
+block_disable(void *block);
+
 /**
  * Data plane (DP) threads.
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (8 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  2023-01-12 15:45   ` [PATCH V3 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands to enable/disable block execution on data plane
threads.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 68bb93a46c..37250b7072 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3250,6 +3250,134 @@ cmd_pipeline_disable(char **tokens,
 	pipeline_disable(p);
 }
 
+static const char cmd_block_enable_help[] =
+"block type <block_type> instance <block_name> enable thread <thread_id>\n";
+
+static void
+cmd_block_enable(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	block_run_f block_func = NULL;
+	void *block = NULL;
+	uint32_t thread_id;
+	int status;
+
+	if (n_tokens != 8) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "enable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+		return;
+	}
+
+	if (strcmp(tokens[6], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
+
+	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block_func = (block_run_f)rte_swx_ipsec_run;
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	status = block_enable(block_func, block, thread_id);
+	if (status) {
+		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
+		return;
+	}
+}
+
+static const char cmd_block_disable_help[] =
+"block type <block_type> instance <block_name> disable\n";
+
+static void
+cmd_block_disable(char **tokens,
+		  uint32_t n_tokens,
+		  char *out,
+		  size_t out_size,
+		  void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	void *block = NULL;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "disable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	block_disable(block);
+}
+
 static void
 cmd_help(char **tokens,
 	 uint32_t n_tokens,
@@ -3298,6 +3426,8 @@ cmd_help(char **tokens,
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
+			"\tblock enable\n"
+			"\tblock disable\n"
 			);
 		return;
 	}
@@ -3553,6 +3683,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
+		return;
+	}
+
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3812,6 +3954,18 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "block")) {
+		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
+			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
+			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 11/11] examples/pipeline: add IPsec example
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (9 preceding siblings ...)
  2023-01-12 15:45   ` [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
@ 2023-01-12 15:45   ` Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:45 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add example files to illustrate the pipeline IPsec support.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ipsec.cli    |  57 +++++++
 examples/pipeline/examples/ipsec.io     |  23 +++
 examples/pipeline/examples/ipsec.spec   | 138 +++++++++++++++
 examples/pipeline/examples/ipsec_sa.txt | 216 ++++++++++++++++++++++++
 4 files changed, 434 insertions(+)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt

diff --git a/examples/pipeline/examples/ipsec.cli b/examples/pipeline/examples/ipsec.cli
new file mode 100644
index 0000000000..8cb5bf4239
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.cli
@@ -0,0 +1,57 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 --vdev crypto_aesni_mb0 -- -s ./examples/pipeline/examples/ipsec.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/ipsec.spec /tmp/ipsec.c
+pipeline libbuild /tmp/ipsec.c /tmp/ipsec.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 128 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+cryptodev crypto_aesni_mb0 queues 1 qsize 128
+ring RING0 size 1024 numa 0
+ring RING1 size 1024 numa 0
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/ipsec.so io ./examples/pipeline/examples/ipsec.io numa 0
+
+;
+; List of IPsec devices.
+;
+ipsec IPSEC0 create in RING0 out RING1 cryptodev crypto_aesni_mb0 cryptoq 0 bsz 32 32 32 32 samax 512 numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+//pipeline PIPELINE0 table policy_table add ./examples/pipeline/examples/ipsec_policy_table.txt
+//pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/ipsec_routing_table.txt
+//pipeline PIPELINE0 table nexthop_table add ./examples/pipeline/examples/ipsec_nexthop_table.txt
+//pipeline PIPELINE0 commit
+
+ipsec IPSEC0 sa add ./examples/pipeline/examples/ipsec_sa.txt
+
+;
+; Pipelines and blocks mapping to CPU threads.
+;
+pipeline PIPELINE0 enable thread 1
+block type ipsec instance IPSEC0 enable thread 1
diff --git a/examples/pipeline/examples/ipsec.io b/examples/pipeline/examples/ipsec.io
new file mode 100644
index 0000000000..f5a3fcf961
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.io
@@ -0,0 +1,23 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ring RING1 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ring RING0 bsz 32
diff --git a/examples/pipeline/examples/ipsec.spec b/examples/pipeline/examples/ipsec.spec
new file mode 100644
index 0000000000..09aa831881
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.spec
@@ -0,0 +1,138 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_h {
+	bit<8> ver_ihl
+	bit<8> diffserv
+	bit<16> total_len
+	bit<16> identification
+	bit<16> flags_offset
+	bit<8> ttl
+	bit<8> protocol
+	bit<16> hdr_checksum
+	bit<32> src_addr
+	bit<32> dst_addr
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+struct ipsec_internal_h {
+	bit<32> sa_id
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+header ipsec_internal instanceof ipsec_internal_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port_in
+	bit<32> port_out
+
+	bit<32> src_addr
+	bit<32> dst_addr
+	bit<8> protocol
+	bit<16> src_port
+	bit<16> dst_port
+}
+
+metadata instanceof metadata_t
+
+//
+// Actions
+//
+struct encrypt_args_t {
+	bit<32> sa_id
+}
+
+action encrypt args instanceof encrypt_args_t {
+	//Set the IPsec internal header.
+	validate h.ipsec_internal
+	mov h.ipsec_internal.sa_id t.sa_id
+
+	return
+}
+
+action drop args none {
+	drop
+}
+
+//
+// Tables.
+//
+table policy_table {
+	key {
+		m.src_addr exact
+		m.dst_addr exact
+		m.protocol exact
+		m.src_port exact
+		m.dst_port exact
+	}
+
+	actions {
+		encrypt
+		drop
+	}
+
+	default_action encrypt args sa_id 0
+	size 65536
+}
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port_in
+
+	jmpeq FROM_IPSEC_TO_NET m.port_in 1
+
+FROM_NET_TO_IPSEC : extract h.ethernet
+
+	extract h.ipv4
+	mov m.src_addr h.ipv4.src_addr
+	mov m.dst_addr h.ipv4.dst_addr
+	mov m.protocol h.ipv4.protocol
+
+	extract h.udp
+	mov m.src_port h.udp.src_port
+	mov m.dst_port h.udp.dst_port
+
+	table policy_table
+
+	mov m.port_out 1
+
+	emit h.ipsec_internal
+	emit h.ipv4
+	emit h.udp
+	tx m.port_out
+
+FROM_IPSEC_TO_NET : extract h.ipv4
+
+	validate h.ethernet
+	mov h.ethernet.dst_addr 0xa0b0c0d0e0f0
+	mov h.ethernet.src_addr 0xa1b1c1d1e1f1
+	mov h.ethernet.ethertype 0x0800
+
+	mov m.port_out 0
+
+	emit h.ethernet
+	emit h.ipv4
+	tx m.port_out
+}
diff --git a/examples/pipeline/examples/ipsec_sa.txt b/examples/pipeline/examples/ipsec_sa.txt
new file mode 100644
index 0000000000..37bfd8a9ce
--- /dev/null
+++ b/examples/pipeline/examples/ipsec_sa.txt
@@ -0,0 +1,216 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+// IPsec SA syntax:
+//
+// <sa>
+//    : encrypt <crypto_params> <encap_params>
+//    | decrypt <crypto_params> <encap_params>
+//    ;
+//
+// <crypto_params>
+//    : <cipher> <auth>
+//    | <aead>
+//    ;
+//
+// <cipher>
+//    : cipher <ciher_alg> key <cipher_key>
+//    | cipher <cipher_alg>
+//    ;
+//
+// <auth>
+//    : auth <authentication_alg> key <authentication_key>
+//    | auth <authentication_alg>
+//    ;
+//
+// <aead>
+//    : aead <aead_alg> key <aead_key>
+//    ;
+//
+// <encap_params>
+//    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+//    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+//    | esp spi <spi> transport
+//    ;
+
+//
+// cipher = null, auth = null
+//
+encrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth null esp spi 100 transport
+decrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth null esp spi 100 transport
+
+//
+// aes-gcm-128
+//
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-gcm-192
+//
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-gcm-256
+//
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// aes-ccm-128
+//
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-ccm-192
+//
+// Note: Currently not supported by the "aesni_mb" library.
+//
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-ccm-256
+//
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// chacha20-poly1305
+//
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// cipher = aes-cbc-128, auth = null
+//
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-192, auth = null
+//
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-256, auth = null
+//
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-128, auth = null
+//
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-192, auth = null
+//
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-256, auth = null
+//
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+
+//
+// cipher = null, auth = sha1-hmac
+//
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// cipher = null, auth = sha256-hmac
+//
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+
+//
+// cipher = null, auth = sha384-hmac
+//
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+
+//
+// cipher = null, auth = sha512-hmac
+//
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 00/11] pipeline: add IPsec support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (12 preceding siblings ...)
  2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-12 15:49 ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 01/11] " Cristian Dumitrescu
                     ` (10 more replies)
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (2 subsequent siblings)
  16 siblings, 11 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev

This patch set introduces a companion block for the SWX pipeline for
IPsec support.

The IPsec block is external to the pipeline, hence it needs to be
explicitly instantiated by the user and connected to a pipeline
instance through the pipeline I/O ports.

Main features:
* IPsec inbound (encrypted input packets -> clear text output packets)
and outbound (clear text input packets -> encrypted output packets)
processing support for tunnel and transport modes.

Interaction of the IPsec block with the pipeline:
* Each IPsec block instance has its own set of Security Associations
(SAs) used to process the input packets. Each SA is identified by its
unique SA ID. The IPsec inbound and outbound SAs share the same ID
space.
* Each input packet is first mapped to one of the existing SAs by
using the SA ID and then processed according to the identified SA. The
SA ID is read from input packet. The SA ID field is typically written
by the pipeline before sending the packet to the IPsec block.

Change log:

V3:
Rebase on top of main latest.

V2:
Fix minor style issues.

Cristian Dumitrescu (11):
  pipeline: add IPsec support
  examples/pipeline: rework memory pool support
  examples/pipeline: streamline ring support
  examples/pipeline: streamline the Ethernet device support
  examples/pipeline: support crypto devices
  examples/pipeline: add CLI command for crypto device
  examples/pipeline: add IPsec CLI commands
  examples/pipeline: rework the thread configuration updates
  examples/pipeline: support blocks other than pipelines
  examples/pipeline: add block enable/disable CLI commands
  examples/pipeline: add IPsec example

 examples/pipeline/cli.c                       |  896 ++++++--
 examples/pipeline/examples/fib.cli            |    4 +-
 examples/pipeline/examples/hash_func.cli      |    4 +-
 examples/pipeline/examples/ipsec.cli          |   57 +
 examples/pipeline/examples/ipsec.io           |   23 +
 examples/pipeline/examples/ipsec.spec         |  138 ++
 examples/pipeline/examples/ipsec_sa.txt       |  216 ++
 examples/pipeline/examples/l2fwd.cli          |    4 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |    4 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |    4 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |    4 +-
 examples/pipeline/examples/learner.cli        |    4 +-
 examples/pipeline/examples/meter.cli          |    4 +-
 examples/pipeline/examples/mirroring.cli      |    4 +-
 examples/pipeline/examples/recirculation.cli  |    4 +-
 examples/pipeline/examples/registers.cli      |    4 +-
 examples/pipeline/examples/selector.cli       |    4 +-
 examples/pipeline/examples/varbit.cli         |    4 +-
 examples/pipeline/examples/vxlan.cli          |    4 +-
 examples/pipeline/examples/vxlan_pcap.cli     |    4 +-
 examples/pipeline/main.c                      |   12 +-
 examples/pipeline/obj.c                       |  359 +---
 examples/pipeline/obj.h                       |  100 +-
 examples/pipeline/thread.c                    |  655 +++---
 examples/pipeline/thread.h                    |   24 +-
 lib/pipeline/meson.build                      |    4 +-
 lib/pipeline/rte_swx_ipsec.c                  | 1821 +++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h                  |  383 ++++
 lib/pipeline/version.map                      |    9 +
 29 files changed, 3739 insertions(+), 1018 deletions(-)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 01/11] pipeline: add IPsec support
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

This block is providing IPsec support to the SWX pipeline. The IPsec
block is external to the pipeline, so it needs to be explicitly
instantiated and connected to a pipeline through the I/O ports.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 lib/pipeline/meson.build     |    4 +-
 lib/pipeline/rte_swx_ipsec.c | 1821 ++++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h |  383 +++++++
 lib/pipeline/version.map     |    9 +
 4 files changed, 2216 insertions(+), 1 deletion(-)
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build
index 3ca98ed194..aa3fd0c2b8 100644
--- a/lib/pipeline/meson.build
+++ b/lib/pipeline/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'rte_pipeline.c',
         'rte_port_in_action.c',
         'rte_table_action.c',
+	'rte_swx_ipsec.c',
         'rte_swx_pipeline.c',
         'rte_swx_pipeline_spec.c',
         'rte_swx_ctl.c',
@@ -19,8 +20,9 @@ headers = files(
         'rte_pipeline.h',
         'rte_port_in_action.h',
         'rte_table_action.h',
+	'rte_swx_ipsec.h',
         'rte_swx_pipeline.h',
         'rte_swx_extern.h',
         'rte_swx_ctl.h',
 )
-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']
+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']
diff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c
new file mode 100644
index 0000000000..b23056e23e
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.c
@@ -0,0 +1,1821 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_cryptodev.h>
+#include <rte_ipsec.h>
+
+#include "rte_swx_ipsec.h"
+
+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
+
+#include <rte_malloc.h>
+
+static void *
+env_calloc(size_t size, size_t alignment, int numa_node)
+{
+	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
+}
+
+static void
+env_free(void *start, size_t size __rte_unused)
+{
+	rte_free(start);
+}
+
+#else
+
+#include <numa.h>
+
+static void *
+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
+{
+	void *start;
+
+	if (numa_available() == -1)
+		return NULL;
+
+	start = numa_alloc_onnode(size, numa_node);
+	if (!start)
+		return NULL;
+
+	memset(start, 0, size);
+	return start;
+}
+
+static void
+env_free(void *start, size_t size)
+{
+	if ((numa_available() == -1) || !start)
+		return;
+
+	numa_free(start, size);
+}
+
+#endif
+
+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
+#endif
+
+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
+ * the mempool size to be at least 1.5 times the size of the mempool cache.
+ */
+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
+
+struct ipsec_sa {
+	struct rte_ipsec_session s;
+	int valid;
+};
+
+struct ipsec_pkts_in {
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_cops;
+};
+
+struct ipsec_pkts_out {
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_pkts;
+};
+
+struct rte_swx_ipsec {
+	/*
+	 * Parameters.
+	 */
+
+	/* IPsec instance name. */
+	char name[RTE_SWX_IPSEC_NAME_SIZE];
+
+	/* Input packet queue. */
+	struct rte_ring *ring_in;
+
+	/* Output packet queue. */
+	struct rte_ring *ring_out;
+
+	/* Crypto device ID. */
+	uint8_t dev_id;
+
+	/* Crypto device queue pair ID. */
+	uint16_t qp_id;
+
+	/* Burst sizes. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/* SA table size. */
+	size_t n_sa_max;
+
+	/*
+	 * Internals.
+	 */
+	/* Crypto device buffer pool for sessions. */
+	struct rte_mempool *mp_session;
+
+	/* Pre-crypto packets. */
+	struct ipsec_pkts_in in;
+
+	/* Post-crypto packets. */
+	struct ipsec_pkts_out out;
+
+	/* Crypto device enqueue threshold. */
+	uint32_t crypto_wr_threshold;
+
+	/* Packets currently under crypto device processing. */
+	uint32_t n_pkts_crypto;
+
+	/* List of free SADB positions. */
+	uint32_t *sa_free_id;
+
+	/* Number of elements in the SADB list of free positions. */
+	size_t n_sa_free_id;
+
+	/* Allocated memory total size in bytes. */
+	size_t total_size;
+
+	/* Flag for registration to the global list of instances. */
+	int registered;
+
+	/*
+	 * Table memory.
+	 */
+	uint8_t memory[] __rte_cache_aligned;
+};
+
+static inline struct ipsec_sa *
+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
+{
+	struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
+
+	return &sadb[sa_id & (ipsec->n_sa_max - 1)];
+}
+
+/* Global list of instances. */
+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ipsec_tailq = {
+	.name = "RTE_SWX_IPSEC",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
+
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name ||
+	    !name[0] ||
+	    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
+		return NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ipsec;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ipsec_register(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ipsec;
+	TAILQ_INSERT_TAIL(ipsec_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ipsec_unregister(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		if (te->data == (void *)ipsec) {
+			TAILQ_REMOVE(ipsec_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
+
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
+{
+	size_t i;
+
+	if (!ipsec)
+		return;
+
+	/* Remove the current instance from the global list. */
+	if (ipsec->registered)
+		ipsec_unregister(ipsec);
+
+	/* SADB. */
+	for (i = 0; i < ipsec->n_sa_max; i++) {
+		struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
+
+		if (!sa->valid)
+			continue;
+
+		/* SA session. */
+		ipsec_session_free(ipsec, &sa->s);
+	}
+
+	/* Crypto device buffer pools. */
+	rte_mempool_free(ipsec->mp_session);
+
+	/* IPsec object memory. */
+	env_free(ipsec, ipsec->total_size);
+}
+
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node)
+{
+	char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
+	struct rte_swx_ipsec *ipsec = NULL;
+	struct rte_ring *ring_in, *ring_out;
+	struct rte_cryptodev_info dev_info;
+	size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
+	uint32_t dev_session_size;
+	int dev_id, status = 0;
+
+	/* Check input parameters. */
+	if (!ipsec_out ||
+	    !name ||
+	    !name[0] ||
+	    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
+	    !params ||
+	    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    !params->n_sa_max) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_in = rte_ring_lookup(params->ring_in_name);
+	if (!ring_in) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_out = rte_ring_lookup(params->ring_out_name);
+	if (!ring_out) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
+	if (dev_id == -1) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Memory allocation. */
+	n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
+
+	sadb_offset = sizeof(struct rte_swx_ipsec);
+	sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
+
+	sa_free_id_offset = sadb_offset + sadb_size;
+	sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
+
+	total_size = sa_free_id_offset + sa_free_id_size;
+	ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
+	if (!ipsec) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Initialization. */
+	strcpy(ipsec->name, name);
+	ipsec->ring_in = ring_in;
+	ipsec->ring_out = ring_out;
+	ipsec->dev_id = (uint8_t)dev_id;
+	ipsec->qp_id = params->crypto_dev_queue_pair_id;
+	memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
+	ipsec->n_sa_max = n_sa_max;
+
+	ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
+
+	ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
+	for (i = 0; i < n_sa_max; i++)
+		ipsec->sa_free_id[i] = n_sa_max - 1 - i;
+	ipsec->n_sa_free_id = n_sa_max;
+
+	ipsec->total_size = total_size;
+
+	/* Crypto device memory pools. */
+	dev_session_size = rte_cryptodev_sym_get_private_session_size((uint8_t)dev_id);
+
+	snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
+	ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		dev_session_size, /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool element private data size */
+		numa_node);
+	if (!ipsec->mp_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Add the current instance to the global list. */
+	status = ipsec_register(ipsec);
+	if (status)
+		goto error;
+
+	ipsec->registered = 1;
+
+	*ipsec_out = ipsec;
+	return 0;
+
+error:
+	rte_swx_ipsec_free(ipsec);
+	return status;
+}
+
+static inline int
+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
+{
+	struct ipsec_sa *sa;
+	struct rte_ipsec_group *g;
+	int n_groups, n_pkts_in_group, i;
+
+	sa = ipsec->in.sa[0];
+
+	g = &ipsec->in.groups[0];
+	g->id.ptr = sa;
+	g->m = &ipsec->in.pkts[0];
+	n_pkts_in_group = 1;
+	n_groups = 1;
+
+	for (i = 1; i < n_pkts; i++) {
+		struct ipsec_sa *sa_new = ipsec->in.sa[i];
+
+		/* Same SA => Add the current pkt to the same group. */
+		if (sa_new == sa) {
+			n_pkts_in_group++;
+			continue;
+		}
+
+		/* Different SA => Close the current group & add the current pkt to a new group. */
+		g->cnt = n_pkts_in_group;
+		sa = sa_new;
+
+		g++;
+		g->id.ptr = sa;
+		g->m = &ipsec->in.pkts[i];
+		n_pkts_in_group = 1;
+		n_groups++;
+	}
+
+	/* Close the last group. */
+	g->cnt = n_pkts_in_group;
+
+	return n_groups;
+}
+
+static inline void
+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
+{
+	struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
+	struct rte_crypto_op **src = ipsec->in.group_cops;
+
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
+	uint32_t n_dst = ipsec->in.n_cops;
+	uint32_t n_dst_max = ipsec->bsz.crypto_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_cops;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
+	 * instead of moving the SRC to DST first and then using DST.
+	 */
+	if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
+		uint16_t n_ok;
+
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
+		ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
+
+		for (i = n_ok; i < n_src; i++) {
+			struct rte_crypto_op *cop = src[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		return;
+	}
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
+		n_pkts_crypto += n_ok;
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_crypto_op *cop = dst0[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	ipsec->n_pkts_crypto = n_pkts_crypto;
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->in.n_cops = n_dst;
+}
+
+/**
+ * Packet buffer anatomy:
+ *
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | Offset   | Size    | Description                                                              |
+ * | (Byte #) | (Bytes) |                                                                          |
+ * +==========+=========+==========================================================================+
+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |
+ * |          |         | The buf_addr field points to the start of the packet section.            |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 256      |         | Packet section.                                                          |
+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |
+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |
+ * +----------+---------+--------------------------------------------------------------------------+
+ */
+struct ipsec_mbuf {
+	struct ipsec_sa *sa;
+	struct rte_crypto_op cop;
+	struct rte_crypto_sym_op sym_cop;
+	uint8_t buffer[32]; /* The crypto IV is placed here. */
+};
+
+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
+
+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
+
+static inline void
+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
+{
+	int n_pkts, n_groups, i;
+
+	/* Read packets from the input ring. */
+	n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
+					   (void **)ipsec->in.pkts,
+					   ipsec->bsz.ring_rd,
+					   NULL);
+	if (!n_pkts)
+		return;
+
+	/* Get the SA for each packet. */
+	for (i = 0; i < n_pkts; i++) {
+		struct rte_mbuf *m = ipsec->in.pkts[i];
+		struct rte_swx_ipsec_input_packet_metadata *meta;
+		struct rte_ipv4_hdr *ipv4_hdr;
+		uint32_t sa_id;
+
+		meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
+
+		/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
+		sa_id = ntohl(meta->sa_id);
+
+		/* Consume the IPsec meta-data. */
+		m->data_off += META_LENGTH;
+		m->data_len -= META_LENGTH;
+		m->pkt_len -= META_LENGTH;
+
+		/* Set the fields required by the IPsec library. */
+		m->l2_len = 0;
+		m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
+			sizeof(struct rte_ipv4_hdr) :
+			sizeof(struct rte_ipv6_hdr);
+
+		/* Get the SA. */
+		ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
+	}
+
+	/* Group packets that share the same SA. */
+	n_groups = ipsec_sa_group(ipsec, n_pkts);
+
+	/* Write each group of packets sharing the same SA to the crypto device. */
+	for (i = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->in.groups[i];
+		struct ipsec_sa *sa = g->id.ptr;
+		struct rte_ipsec_session *s = &sa->s;
+		uint32_t j;
+		uint16_t n_pkts_ok;
+
+		/* Prepare the crypto ops for the current group. */
+		for (j = 0; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+			struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
+
+			priv->sa = sa;
+			ipsec->in.group_cops[j] = &priv->cop;
+		}
+
+		n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the crypto ops of the current group to the crypto device. */
+		ipsec_crypto_enqueue(ipsec, n_pkts_ok);
+	}
+}
+
+static inline void
+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
+{
+	struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
+	struct rte_mbuf **src = g->m;
+
+	uint32_t n_dst = ipsec->out.n_pkts;
+	uint32_t n_dst_max = ipsec->bsz.ring_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_pkts;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_mbuf *m = dst[i];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->out.n_pkts = n_dst;
+}
+
+static inline void
+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
+{
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, ng, i;
+
+	/* Read the crypto ops from the crypto device. */
+	if (!n_pkts_crypto)
+		return;
+
+	n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
+					     ipsec->qp_id,
+					     ipsec->out.cops,
+					     ipsec->bsz.crypto_rd);
+	if (!n_pkts)
+		return;
+
+	ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
+
+	/* Group packets that share the same SA. */
+	ng = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
+					      ipsec->out.group_pkts,
+					      ipsec->out.groups,
+					      n_pkts);
+
+	/* Perform post-crypto IPsec processing for each group of packets that share the same SA.
+	 * Write each group of packets to the output ring.
+	 */
+	for (i = 0, n_pkts = 0; i < ng; i++) {
+		struct rte_ipsec_group *g = &ipsec->out.groups[i];
+		struct rte_ipsec_session *s = g->id.ptr;
+		uint32_t n_pkts_ok, j;
+
+		/* Perform post-crypto IPsec processing for the current group. */
+		n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the packets of the current group to the output ring. */
+		ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
+	}
+}
+
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
+{
+	rte_swx_ipsec_pre_crypto(ipsec);
+	rte_swx_ipsec_post_crypto(ipsec);
+}
+
+/**
+ * IPsec Control Plane API
+ */
+struct cipher_alg {
+	const char *name;
+	enum rte_crypto_cipher_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t key_size;
+};
+
+struct auth_alg {
+	const char *name;
+	enum rte_crypto_auth_algorithm alg;
+	uint32_t iv_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+};
+
+struct aead_alg {
+	const char *name;
+	enum rte_crypto_aead_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+	uint32_t aad_size;
+};
+
+static struct cipher_alg cipher_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_CIPHER_NULL,
+		.iv_size = 0,
+		.block_size = 4,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "aes-cbc-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 16,
+	},
+
+	[2] = {
+		.name = "aes-cbc-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 24,
+	},
+
+	[3] = {
+		.name = "aes-cbc-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 32,
+	},
+
+	[4] = {
+		.name = "aes-ctr-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+	},
+
+	[5] = {
+		.name = "aes-ctr-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 28,
+	},
+
+	[6] = {
+		.name = "aes-ctr-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 36,
+	},
+
+	[7] = {
+		.name = "3des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 24,
+	},
+
+	[8] = {
+		.name = "des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 8,
+	},
+};
+
+static struct auth_alg auth_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_AUTH_NULL,
+		.iv_size = 0,
+		.digest_size = 0,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "sha1-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 20,
+	},
+
+	[2] = {
+		.name = "sha256-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
+		.iv_size = 0,
+		.digest_size = 16,
+		.key_size = 32,
+	},
+
+	[3] = {
+		.name = "sha384-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
+		.iv_size = 0,
+		.digest_size = 24,
+		.key_size = 48,
+	},
+
+	[4] = {
+		.name = "sha512-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
+		.iv_size = 0,
+		.digest_size = 32,
+		.key_size = 64,
+	},
+
+	[5] = {
+		.name = "aes-gmac",
+		.alg = RTE_CRYPTO_AUTH_AES_GMAC,
+		.iv_size = 8,
+		.digest_size = 16,
+		.key_size = 20,
+	},
+
+	[6] = {
+		.name = "aes-xcbc-mac-96",
+		.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 16,
+	},
+};
+
+static struct aead_alg aead_algs[] = {
+	[0] = {
+		.name = "aes-gcm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[1] = {
+		.name = "aes-gcm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[2] = {
+		.name = "aes-gcm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[3] = {
+		.name = "aes-ccm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[4] = {
+		.name = "aes-ccm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[5] = {
+		.name = "aes-ccm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[6] = {
+		.name = "chacha20-poly1305",
+		.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
+		.iv_size = 12,
+		.block_size = 64,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+};
+
+static struct cipher_alg *
+cipher_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct cipher_alg *
+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static int
+char_to_hex(char c, uint8_t *val)
+{
+	if (c >= '0' && c <= '9') {
+		*val = c - '0';
+		return 0;
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		*val = c - 'A' + 10;
+		return 0;
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		*val = c - 'a' + 10;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
+{
+	uint32_t i;
+
+	/* Check input arguments. */
+	if (!src || !src[0] || !dst || !n_dst_bytes)
+		return -EINVAL;
+
+	/* Skip any leading "0x" or "0X" in the src string. */
+	if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
+		src += 2;
+
+	/* Convert each group of two hex characters in the src string to one byte in dst array. */
+	for (i = 0; i < n_dst_bytes; i++) {
+		uint8_t a, b;
+		int status;
+
+		status = char_to_hex(*src, &a);
+		if (status)
+			return status;
+		src++;
+
+		status = char_to_hex(*src, &b);
+		if (status)
+			return status;
+		src++;
+
+		dst[i] = a * 16 + b;
+	}
+
+	/* Check for the end of the src string. */
+	if (*src)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+token_is_comment(const char *token)
+{
+	if ((token[0] == '#') ||
+	    (token[0] == ';') ||
+	    ((token[0] == '/') && (token[1] == '/')))
+		return 1; /* TRUE. */
+
+	return 0; /* FALSE. */
+}
+
+#define MAX_TOKENS 64
+
+#define CHECK(condition, msg)          \
+do {                                   \
+	if (!(condition)) {            \
+		if (errmsg)            \
+			*errmsg = msg; \
+		goto error;            \
+	}                              \
+} while (0)
+
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg)
+{
+	char *token_array[MAX_TOKENS], **t;
+	struct rte_swx_ipsec_sa_params *p = NULL;
+	char *s0 = NULL, *s;
+	uint32_t n_tokens = 0;
+	int blank_or_comment = 0;
+
+	/* Check input arguments. */
+	CHECK(string && string[0], "NULL input");
+
+	/* Memory allocation. */
+	s0 = strdup(string);
+	p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
+	CHECK(s0 && p, "Not enough memory");
+
+	/* Parse the string into tokens. */
+	for (s = s0; ; ) {
+		char *token;
+
+		token = strtok_r(s, " \f\n\r\t\v", &s);
+		if (!token || token_is_comment(token))
+			break;
+
+		CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
+
+		token_array[n_tokens] = token;
+		n_tokens++;
+	}
+
+	t = token_array;
+	if (!n_tokens) {
+		blank_or_comment = 1;
+		goto error;
+	}
+
+	/*
+	 * Crypto operation.
+	 */
+	if (!strcmp(t[0], "encrypt"))
+		p->encrypt = 1;
+	else if (!strcmp(t[0], "decrypt"))
+		p->encrypt = 0;
+	else
+		CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
+
+	t++;
+	n_tokens--;
+
+	/*
+	 * Crypto parameters.
+	 */
+	CHECK(n_tokens >= 2, "Not enough tokens");
+
+	if (!strcmp(t[0], "cipher")) {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t key_size;
+
+		p->crypto.is_aead = 0;
+
+		/* cipher. */
+		cipher_alg = cipher_alg_find(t[1]);
+		CHECK(cipher_alg, "Unsupported cipher algorithm");
+
+		key_size = cipher_alg->key_size;
+		p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
+		p->crypto.cipher_auth.cipher.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
+				"Cipher algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
+			CHECK(!status, "Cipher key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+
+		/* authentication. */
+		CHECK(n_tokens >= 2, "Not enough tokens");
+		CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
+
+		auth_alg = auth_alg_find(t[1]);
+		CHECK(auth_alg, "Unsupported authentication algorithm");
+
+		key_size = auth_alg->key_size;
+		p->crypto.cipher_auth.auth.alg = auth_alg->alg;
+		p->crypto.cipher_auth.auth.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
+				"Authentication algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
+			CHECK(!status, "Authentication key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+	} else if (!strcmp(t[0], "aead")) {
+		struct aead_alg *alg;
+		uint32_t key_size;
+		int status;
+
+		p->crypto.is_aead = 1;
+
+		CHECK(n_tokens >= 4, "Not enough tokens");
+		alg = aead_alg_find(t[1]);
+		CHECK(alg, "Unsupported AEAD algorithm");
+
+		key_size = alg->key_size;
+		p->crypto.aead.alg = alg->alg;
+		p->crypto.aead.key_size = key_size;
+
+		CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
+		CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
+			"AEAD algorithm key too big");
+
+		status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
+		CHECK(!status, "AEAD key invalid format");
+
+		t += 4;
+		n_tokens -= 4;
+	} else
+		CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
+
+	/*
+	 * Packet ecapsulation parameters.
+	 */
+	CHECK(n_tokens >= 4, "Not enough tokens");
+	CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
+	CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
+
+	p->encap.esp.spi = strtoul(t[2], &t[2], 0);
+	CHECK(!t[2][0], "ESP SPI field invalid format");
+
+	t += 3;
+	n_tokens -= 3;
+
+	if (!strcmp(t[0], "tunnel")) {
+		p->encap.tunnel_mode = 1;
+
+		CHECK(n_tokens >= 6, "Not enough tokens");
+
+		if (!strcmp(t[1], "ipv4")) {
+			uint32_t addr;
+
+			p->encap.tunnel_ipv4 = 1;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			addr = strtoul(t[3], &t[3], 0);
+			CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
+			p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			addr = strtoul(t[5], &t[5], 0);
+			CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
+			p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
+
+			t += 6;
+			n_tokens -= 6;
+		} else if (!strcmp(t[1], "ipv6")) {
+			int status;
+
+			p->encap.tunnel_ipv4 = 0;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			status = hex_string_parse(t[3],
+						  p->encap.tunnel.ipv6.src_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 source address invalid format");
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			status = hex_string_parse(t[5],
+						  p->encap.tunnel.ipv6.dst_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 destination address invalid format");
+
+			t += 6;
+			n_tokens -= 6;
+		} else
+			CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
+	} else if (!strcmp(t[0], "transport")) {
+		p->encap.tunnel_mode = 0;
+
+		t++;
+		n_tokens--;
+	} else
+		CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
+
+	/*
+	 * Any other parameters.
+	 */
+	CHECK(!n_tokens, "Unexpected trailing tokens");
+
+	free(s0);
+	return p;
+
+error:
+	free(p);
+	free(s0);
+	if (is_blank_or_comment)
+		*is_blank_or_comment = blank_or_comment;
+	return NULL;
+}
+
+static void
+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv4_hdr ipv4_hdr = {
+		.version_ihl = 0x45,
+		.type_of_service = 0,
+		.total_length = 0, /* Cannot be pre-computed. */
+		.packet_id = 0,
+		.fragment_offset = 0,
+		.time_to_live = 64,
+		.next_proto_id = IPPROTO_ESP,
+		.hdr_checksum = 0, /* Cannot be pre-computed. */
+		.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
+		.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
+	};
+
+	memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
+}
+
+static void
+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv6_hdr ipv6_hdr = {
+		.vtc_flow = 0x60000000,
+		.payload_len = 0, /* Cannot be pre-computed. */
+		.proto = IPPROTO_ESP,
+		.hop_limits = 64,
+		.src_addr = {0},
+		.dst_addr = {0},
+	};
+
+	memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
+	memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
+	memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
+}
+
+/* IPsec library SA parameters. */
+static struct rte_crypto_sym_xform *
+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_crypto_sym_xform *xform,
+		uint32_t *salt_out)
+{
+	if (p->crypto.is_aead) {
+		struct aead_alg *alg;
+		uint32_t key_size, salt, iv_length;
+
+		alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
+		if (!alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		key_size = p->crypto.aead.key_size - 4;
+		memcpy(&salt, &p->crypto.aead.key[key_size], 4);
+
+		/* IV length. */
+		iv_length = 12;
+		if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
+			iv_length = 11;
+
+		/* xform. */
+		xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform[0].aead.op = p->encrypt ?
+			RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform[0].aead.algo = p->crypto.aead.alg;
+		xform[0].aead.key.data = p->crypto.aead.key;
+		xform[0].aead.key.length = key_size;
+		xform[0].aead.iv.offset = IV_OFFSET;
+		xform[0].aead.iv.length = iv_length;
+		xform[0].aead.digest_length = alg->digest_size;
+		xform[0].aead.aad_length = alg->aad_size;
+		xform[0].next = NULL;
+
+		*salt_out = salt;
+		return &xform[0];
+	} else {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
+
+		cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
+						   p->crypto.cipher_auth.cipher.key_size);
+		if (!cipher_alg)
+			return NULL;
+
+		auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
+					       p->crypto.cipher_auth.auth.key_size);
+		if (!auth_alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
+		auth_key_size = p->crypto.cipher_auth.auth.key_size;
+
+		switch (p->crypto.cipher_auth.cipher.alg) {
+		case RTE_CRYPTO_CIPHER_AES_CBC:
+		case RTE_CRYPTO_CIPHER_3DES_CBC:
+			salt = (uint32_t)rand();
+			break;
+
+		case RTE_CRYPTO_CIPHER_AES_CTR:
+			cipher_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
+			break;
+
+		default:
+			salt = 0;
+		}
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			auth_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
+		}
+
+		/* IV length. */
+		auth_iv_length = cipher_alg->iv_size;
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
+			auth_iv_length = 12;
+
+		/* xform. */
+		if (p->encrypt) {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[0].cipher.key.length = cipher_key_size;
+			xform[0].cipher.iv.offset = IV_OFFSET;
+			xform[0].cipher.iv.length = cipher_alg->iv_size;
+			xform[0].cipher.dataunit_len = 0;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+			xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[1].auth.key.length = auth_key_size;
+			xform[1].auth.iv.offset = IV_OFFSET;
+			xform[1].auth.iv.length = auth_iv_length;
+			xform[1].auth.digest_length = auth_alg->digest_size;
+			xform[1].next = NULL;
+		} else {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+			xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[0].auth.key.length = auth_key_size;
+			xform[0].auth.iv.offset = IV_OFFSET;
+			xform[0].auth.iv.length = auth_iv_length;
+			xform[0].auth.digest_length = auth_alg->digest_size;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[1].cipher.key.length = cipher_key_size;
+			xform[1].cipher.iv.offset = IV_OFFSET;
+			xform[1].cipher.iv.length = cipher_alg->iv_size;
+			xform[1].cipher.dataunit_len = 0;
+			xform[1].next = NULL;
+		}
+
+		*salt_out = salt;
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			if (p->encrypt)
+				return &xform[1];
+
+			xform[0].next = NULL;
+			return &xform[0];
+		}
+
+		return &xform[0];
+	}
+}
+
+static void
+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_security_ipsec_xform *ipsec_xform,
+		uint32_t salt)
+{
+	ipsec_xform->spi = p->encap.esp.spi;
+
+	ipsec_xform->salt = salt;
+
+	ipsec_xform->options.esn = 0;
+	ipsec_xform->options.udp_encap = 0;
+	ipsec_xform->options.copy_dscp = 1;
+	ipsec_xform->options.copy_flabel = 0;
+	ipsec_xform->options.copy_df = 0;
+	ipsec_xform->options.dec_ttl = 0;
+	ipsec_xform->options.ecn = 1;
+	ipsec_xform->options.stats = 0;
+	ipsec_xform->options.iv_gen_disable = 0;
+	ipsec_xform->options.tunnel_hdr_verify = 0;
+	ipsec_xform->options.udp_ports_verify = 0;
+	ipsec_xform->options.ip_csum_enable = 0;
+	ipsec_xform->options.l4_csum_enable = 0;
+	ipsec_xform->options.ip_reassembly_en = 0;
+	ipsec_xform->options.reserved_opts = 0;
+
+	ipsec_xform->direction = p->encrypt ?
+		RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
+		RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+
+	ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+
+	ipsec_xform->mode = p->encap.tunnel_mode ?
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
+
+	ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
+		RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
+		RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
+			ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
+			ipsec_xform->tunnel.ipv4.dscp = 0;
+			ipsec_xform->tunnel.ipv4.df = 0;
+			ipsec_xform->tunnel.ipv4.ttl = 64;
+		} else {
+			ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
+			ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
+			ipsec_xform->tunnel.ipv6.dscp = 0;
+			ipsec_xform->tunnel.ipv6.flabel = 0;
+			ipsec_xform->tunnel.ipv6.hlimit = 64;
+		}
+	}
+
+	ipsec_xform->life.packets_soft_limit = 0;
+	ipsec_xform->life.bytes_soft_limit = 0;
+	ipsec_xform->life.packets_hard_limit = 0;
+	ipsec_xform->life.bytes_hard_limit = 0;
+
+	ipsec_xform->replay_win_sz = 0;
+
+	ipsec_xform->esn.value = 0;
+
+	ipsec_xform->udp.dport = 0;
+	ipsec_xform->udp.sport = 0;
+}
+
+static int
+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
+		 struct rte_ipsec_sa_prm *sa_prm,
+		 struct rte_ipv4_hdr *ipv4_hdr,
+		 struct rte_ipv6_hdr *ipv6_hdr,
+		 struct rte_crypto_sym_xform *crypto_xform)
+{
+	uint32_t salt;
+
+	memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
+
+	sa_prm->userdata = 0; /* Not used. */
+
+	sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
+
+	/*
+	 * crypto_xform.
+	 */
+	sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
+	if (!sa_prm->crypto_xform)
+		return -EINVAL;
+
+	/*
+	 * ipsec_xform.
+	 */
+	ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
+
+	/*
+	 * tunnel / transport.
+	 *
+	 * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
+	 * the input packet type should be added to the SA configuration parameters.
+	 */
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv4_hdr;
+		} else {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv6_hdr;
+		}
+	} else {
+		sa_prm->trs.proto = IPPROTO_IPIP; /* IPv4. */
+	}
+
+	return 0;
+}
+
+static int
+ipsec_session_create(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *p,
+		     struct rte_ipsec_session *s)
+{
+	struct rte_ipv4_hdr ipv4_hdr;
+	struct rte_ipv6_hdr ipv6_hdr;
+	struct rte_crypto_sym_xform crypto_xform[2];
+	struct rte_ipsec_sa_prm sa_prm;
+	struct rte_ipsec_sa *sa = NULL;
+	struct rte_cryptodev_sym_session *crypto_session = NULL;
+	int sa_size;
+	int sa_valid = 0, status = 0;
+
+	tunnel_ipv4_header_set(&ipv4_hdr, p);
+	tunnel_ipv6_header_set(&ipv6_hdr, p);
+
+	/* IPsec library SA setup. */
+	status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
+	if (status)
+		goto error;
+
+	sa_size = rte_ipsec_sa_size(&sa_prm);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa = calloc(1, sa_size);
+	if (!sa) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa_valid = 1;
+
+	/* Cryptodev library session setup. */
+	crypto_session = rte_cryptodev_sym_session_create(ipsec->dev_id,
+							  sa_prm.crypto_xform,
+							  ipsec->mp_session);
+	if (!crypto_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* IPsec library session setup. */
+	s->sa = sa;
+	s->type = RTE_SECURITY_ACTION_TYPE_NONE;
+	s->crypto.ses = crypto_session;
+	s->crypto.dev_id = ipsec->dev_id;
+	s->pkt_func.prepare.async = NULL;
+	s->pkt_func.process = NULL;
+
+	return rte_ipsec_session_prepare(s);
+
+error:
+	/* sa. */
+	if (sa_valid)
+		rte_ipsec_sa_fini(sa);
+
+	free(sa);
+
+	/* crypto_session. */
+	if (crypto_session)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, crypto_session);
+
+	/* s. */
+	memset(s, 0, sizeof(*s));
+
+	return status;
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec,
+		   struct rte_ipsec_session *s)
+{
+	if (!s)
+		return;
+
+	/* IPsec library SA. */
+	if (s->sa)
+		rte_ipsec_sa_fini(s->sa);
+	free(s->sa);
+
+	/* Cryptodev library session. */
+	if (s->crypto.ses)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, s->crypto.ses);
+
+	/* IPsec library session. */
+	memset(s, 0, sizeof(*s));
+}
+
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *id)
+{
+	struct ipsec_sa *sa;
+	uint32_t sa_id;
+	int status;
+
+	/* Check the input parameters. */
+	if (!ipsec || !sa_params || !id)
+		return -EINVAL;
+
+	/* Allocate a free SADB entry. */
+	if (!ipsec->n_sa_free_id)
+		return -ENOSPC;
+
+	sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
+	ipsec->n_sa_free_id--;
+
+	/* Acquire the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	status = ipsec_session_create(ipsec, sa_params, &sa->s);
+	if (status) {
+		/* Free the allocated SADB entry. */
+		ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+		ipsec->n_sa_free_id++;
+
+		return status;
+	}
+
+	/* Validate the new SA. */
+	sa->valid = 1;
+	*id = sa_id;
+
+	return 0;
+}
+
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id)
+{
+	struct ipsec_sa *sa;
+
+	/* Check the input parameters. */
+	if (!ipsec || (sa_id >= ipsec->n_sa_max))
+		return;
+
+	/* Release the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	ipsec_session_free(ipsec, &sa->s);
+
+	/* Free the SADB entry. */
+	ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+	ipsec->n_sa_free_id++;
+
+	/* Invalidate the SA. */
+	sa->valid = 0;
+}
diff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h
new file mode 100644
index 0000000000..52962f8845
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__
+#define __INCLUDE_RTE_SWX_IPSEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE SWX Internet Protocol Security (IPsec)
+ *
+ * The IPsec block is a companion block for the SWX pipeline used to provide IPsec support to the
+ * pipeline. The block is external to the pipeline, hence it needs to be explicitly instantiated by
+ * the user and connected to a pipeline instance through the pipeline I/O ports.
+ *
+ * Main features:
+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text
+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.
+ *
+ * Security Association (SA):
+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is
+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.
+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then
+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field
+ *   is typically written by the pipeline before sending the packet to the IPsec block.
+ *
+ * Packet format:
+ * - IPsec block input packet (i.e. pipeline output packet):
+ *	- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.
+ *	- IPv4 header.
+ *	- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.
+ * - IPsec block output packet (i.e. pipeline input packet):
+ *	- IPv4 header.
+ *	- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.
+ *
+ * SA update procedure:
+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().
+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().
+ * - To update an existing SA, the control plane has to follow the following steps:
+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can
+ *      fail, for example when the SA table is full.
+ *   2. Wait until no more packets are using the old SA.
+ *   3. Delete the old SA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <rte_compat.h>
+#include <rte_crypto_sym.h>
+
+/**
+ * IPsec Setup API
+ */
+
+/** IPsec instance opaque data structure. */
+struct rte_swx_ipsec;
+
+/** Name size. */
+#ifndef RTE_SWX_IPSEC_NAME_SIZE
+#define RTE_SWX_IPSEC_NAME_SIZE 64
+#endif
+
+/** Maximum burst size. */
+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX
+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256
+#endif
+
+/** IPsec burst sizes. */
+struct rte_swx_ipsec_burst_size {
+	/** Input ring read burst size. */
+	uint32_t ring_rd;
+
+	/** Output ring write burst size. */
+	uint32_t ring_wr;
+
+	/** Crypto device request queue write burst size. */
+	uint32_t crypto_wr;
+
+	/** Crypto device response queue read burst size. */
+	uint32_t crypto_rd;
+};
+
+/**
+ * IPsec instance configuration parameters
+ *
+ */
+struct rte_swx_ipsec_params {
+	/** Input packet queue. */
+	const char *ring_in_name;
+
+	/** Output packet queue.  */
+	const char *ring_out_name;
+
+	/** Crypto device name. */
+	const char *crypto_dev_name;
+
+	/** Crypto device queue pair ID. */
+	uint32_t crypto_dev_queue_pair_id;
+
+	/** Burst size. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/** Maximum number of SAs. */
+	uint32_t n_sa_max;
+};
+
+/**
+ * IPsec input packet meta-data
+ *
+ */
+struct rte_swx_ipsec_input_packet_metadata {
+	/* SA ID. */
+	uint32_t sa_id;
+};
+
+/**
+ * IPsec instance find
+ *
+ * @param[in] name
+ *   IPsec instance name.
+ * @return
+ *   Valid IPsec instance handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name);
+
+/**
+ * IPsec instance create
+ *
+ * @param[out] ipsec
+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this
+ *   function returns successfully.
+ * @param[in] name
+ *   IPsec instance unique name.
+ * @param[in] params
+ *   IPsec instance configuration parameters.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node);
+
+/**
+ * IPsec instance free
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);
+
+/**
+ * IPsec Data Plane API
+ */
+
+/**
+ * IPsec instance run
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);
+
+/*
+ * IPsec Control Plane API
+ */
+
+/** Maximum key size in bytes. */
+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64
+
+/** IPsec SA crypto cipher parameters. */
+struct rte_swx_ipsec_sa_cipher_params {
+	/** Cipher algorithm. */
+	enum rte_crypto_cipher_algorithm alg;
+
+	/** Cipher key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Cipher key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto authentication parameters. */
+struct rte_swx_ipsec_sa_authentication_params {
+	/** Authentication algorithm. */
+	enum rte_crypto_auth_algorithm alg;
+
+	/** Authentication key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Authentication key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */
+struct rte_swx_ipsec_sa_aead_params {
+	/** AEAD algorithm. */
+	enum rte_crypto_aead_algorithm alg;
+
+	/** AEAD key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** AEAD key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec protocol encapsulation parameters. */
+struct rte_swx_ipsec_sa_encap_params {
+	/** Encapsulating Security Payload (ESP) header. */
+	struct {
+		/** Security Parameters Index (SPI) field. */
+		uint32_t spi;
+	} esp;
+
+	/** Tunnel mode when non-zero, transport mode when zero. */
+	int tunnel_mode;
+
+	/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */
+	int tunnel_ipv4;
+
+	/** Tunnel parameters. Valid for tunnel mode only. */
+	union {
+		/** IPv4 header. */
+		struct {
+			/** Source address. */
+			struct in_addr src_addr;
+
+			/** Destination address. */
+			struct in_addr dst_addr;
+		} ipv4;
+
+		/** IPv6 header. */
+		struct {
+			/** Source address. */
+			struct in6_addr src_addr;
+
+			/** Destination address. */
+			struct in6_addr dst_addr;
+		} ipv6;
+	} tunnel;
+};
+
+/** IPsec Security Association (SA) parameters. */
+struct rte_swx_ipsec_sa_params {
+	/** Crypto operation: encrypt when non-zero, decrypt when zero. */
+	int encrypt;
+
+	/** Crypto operation parameters. */
+	struct {
+		RTE_STD_C11
+		union {
+			struct {
+				/** Crypto cipher operation parameters. */
+				struct rte_swx_ipsec_sa_cipher_params cipher;
+
+				/** Crypto authentication operation parameters. */
+				struct rte_swx_ipsec_sa_authentication_params auth;
+			} cipher_auth;
+
+			/** Crypto AEAD operation parameters. */
+			struct rte_swx_ipsec_sa_aead_params aead;
+		};
+
+		/** Non-zero for AEAD, zero for cipher & authentication. */
+		int is_aead;
+	} crypto;
+
+	/** Packet encasulation parameters. */
+	struct rte_swx_ipsec_sa_encap_params encap;
+};
+
+/**
+ * IPsec SA add
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_params
+ *   SA parameters.
+ * @params[out] sa_id.
+ *   On success, the SA ID.
+ * @return
+ *   0 on success or error code otherwise.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *sa_id);
+
+/**
+ * IPsec SA delete
+ *
+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used
+ * by the Data Plane.
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @params[in] sa_id.
+ *   The SA ID.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id);
+
+/**
+ * IPsec SA read from string
+ *
+ * IPsec SA syntax:
+ *
+ * <sa>
+ *    : encrypt <crypto_params> <encap_params>
+ *    | decrypt <crypto_params> <encap_params>
+ *    ;
+ *
+ * <crypto_params>
+ *    : <cipher> <auth>
+ *    | <aead>
+ *    ;
+ *
+ * <cipher>
+ *    : cipher <ciher_alg> key <cipher_key>
+ *    | cipher <cipher_alg>
+ *    ;
+ *
+ * <auth>
+ *    : auth <authentication_alg> key <authentication_key>
+ *    | auth <authentication_alg>
+ *    ;
+ *
+ * <aead>
+ *    : aead <aead_alg> key <aead_key>
+ *    ;
+ *
+ * <encap_params>
+ *    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+ *    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+ *    | esp spi <spi> transport
+ *    ;
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] string
+ *   String containing the SA.
+ * @param[in,out] is_blank_or_comment
+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when
+ *   *string* contains a blank or comment line and to zero otherwise.
+ * @param[in,out] errmsg
+ *   On error, when its input value is not NULL, this argument points to a string with details on
+ *   the detected error.
+ * @return
+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.
+ */
+__rte_experimental
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 184f45a6b7..58e34459c3 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -155,4 +155,13 @@ EXPERIMENTAL {
 	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
+
+	#added in 23.03
+	rte_swx_ipsec_create;
+	rte_swx_ipsec_find;
+	rte_swx_ipsec_free;
+	rte_swx_ipsec_run;
+	rte_swx_ipsec_sa_add;
+	rte_swx_ipsec_sa_delete;
+	rte_swx_ipsec_sa_read;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 02/11] examples/pipeline: rework memory pool support
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 01/11] " Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Rework the memory pool CLI command to accommodate the MBUF private
meta-data area size parameter.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 72 ++++++++++-------
 examples/pipeline/examples/fib.cli            |  2 +-
 examples/pipeline/examples/hash_func.cli      |  2 +-
 examples/pipeline/examples/l2fwd.cli          |  2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |  2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |  2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |  2 +-
 examples/pipeline/examples/learner.cli        |  2 +-
 examples/pipeline/examples/meter.cli          |  2 +-
 examples/pipeline/examples/mirroring.cli      |  2 +-
 examples/pipeline/examples/recirculation.cli  |  2 +-
 examples/pipeline/examples/registers.cli      |  2 +-
 examples/pipeline/examples/selector.cli       |  2 +-
 examples/pipeline/examples/varbit.cli         |  2 +-
 examples/pipeline/examples/vxlan.cli          |  2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |  2 +-
 examples/pipeline/obj.c                       | 80 +------------------
 examples/pipeline/obj.h                       | 27 -------
 18 files changed, 63 insertions(+), 146 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index e1e7aaddc1..14f1cfa47e 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -192,72 +192,88 @@ parse_table_entry(struct rte_swx_ctl_pipeline *p,
 }
 
 static const char cmd_mempool_help[] =
-"mempool <mempool_name>\n"
-"   buffer <buffer_size>\n"
-"   pool <pool_size>\n"
-"   cache <cache_size>\n"
-"   cpu <cpu_id>\n";
+"mempool <mempool_name> "
+"meta <mbuf_private_size> "
+"pkt <pkt_buffer_size> "
+"pool <pool_size> "
+"cache <cache_size> "
+"numa <numa_node>\n";
 
 static void
 cmd_mempool(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
+	    uint32_t n_tokens,
+	    char *out,
+	    size_t out_size,
+	    void *obj __rte_unused)
 {
-	struct mempool_params p;
-	char *name;
-	struct mempool *mempool;
+	struct rte_mempool *mp;
+	char *mempool_name;
+	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
 
-	if (n_tokens != 10) {
+	if (n_tokens != 12) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	name = tokens[1];
+	mempool_name = tokens[1];
+
+	if (strcmp(tokens[2], "meta")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
+		return;
+	}
 
-	if (strcmp(tokens[2], "buffer") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
+	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
+	if (strcmp(tokens[4], "pkt")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
 		return;
 	}
 
-	if (strcmp(tokens[4], "pool") != 0) {
+	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
+		return;
+	}
+
+	if (strcmp(tokens[6], "pool")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
 		return;
 	}
 
-	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
+	if (parser_read_uint32(&pool_size, tokens[7])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
 		return;
 	}
 
-	if (strcmp(tokens[6], "cache") != 0) {
+	if (strcmp(tokens[8], "cache")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
+	if (parser_read_uint32(&cache_size, tokens[9])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
 		return;
 	}
 
-	if (strcmp(tokens[8], "cpu") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+	if (strcmp(tokens[10], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+	if (parser_read_uint32(&numa_node, tokens[11])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	mempool = mempool_create(obj, name, &p);
-	if (mempool == NULL) {
+	mp = rte_pktmbuf_pool_create(mempool_name,
+				     pool_size,
+				     cache_size,
+				     mbuf_private_size,
+				     pkt_buffer_size,
+				     numa_node);
+	if (!mp) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 4e30c1320f..2450dc9ca4 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/fib.c /tmp/fib.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index b2e219e4c9..3840e47a2b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index 27e37021b9..c5505df296 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 11bb4543b9..bd700707f5 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index 8724dae3b0..aa64b32ab2 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 4db0a0dc56..619d17474f 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 6c8aa3921e..42184be1c8 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/learner.c /tmp/learner.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 21582554b9..0fa38b16b8 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/meter.c /tmp/meter.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 1d439e04d3..e7391de74b 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 52d0894f12..965c870fd4 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 3516f76a5b..aa775f71e5 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/registers.c /tmp/registers.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0e251b657..f0608de184 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/selector.c /tmp/selector.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 0f89990471..648b8882c3 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 1fbd1be6e4..4565adfaea 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index adc7f73312..510c2ad870 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index cac825f22a..697d14a901 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -13,18 +13,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
 
-/*
- * mempool
- */
-TAILQ_HEAD(mempool_list, mempool);
-
 /*
  * link
  */
@@ -39,75 +33,10 @@ TAILQ_HEAD(ring_list, ring);
  * obj
  */
 struct obj {
-	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
 };
 
-/*
- * mempool
- */
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
-
-struct mempool *
-mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
-{
-	struct mempool *mempool;
-	struct rte_mempool *m;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		mempool_find(obj, name) ||
-		(params == NULL) ||
-		(params->buffer_size < BUFFER_SIZE_MIN) ||
-		(params->pool_size == 0))
-		return NULL;
-
-	/* Resource create */
-	m = rte_pktmbuf_pool_create(
-		name,
-		params->pool_size,
-		params->cache_size,
-		0,
-		params->buffer_size - sizeof(struct rte_mbuf),
-		params->cpu_id);
-
-	if (m == NULL)
-		return NULL;
-
-	/* Node allocation */
-	mempool = calloc(1, sizeof(struct mempool));
-	if (mempool == NULL) {
-		rte_mempool_free(m);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(mempool->name, name, sizeof(mempool->name));
-	mempool->m = m;
-	mempool->buffer_size = params->buffer_size;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
-
-	return mempool;
-}
-
-struct mempool *
-mempool_find(struct obj *obj, const char *name)
-{
-	struct mempool *mempool;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(mempool, &obj->mempool_list, node)
-		if (strcmp(mempool->name, name) == 0)
-			return mempool;
-
-	return NULL;
-}
-
 /*
  * link
  */
@@ -171,7 +100,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct rte_eth_conf port_conf;
 	struct link *link;
 	struct link_params_rss *rss;
-	struct mempool *mempool;
+	struct rte_mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
 	uint16_t port_id = 0;
@@ -193,8 +122,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
 
-	mempool = mempool_find(obj, params->rx.mempool_name);
-	if (mempool == NULL)
+	mempool = rte_mempool_lookup(params->rx.mempool_name);
+	if (!mempool)
 		return NULL;
 
 	rss = params->rx.rss;
@@ -251,7 +180,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			params->rx.queue_size,
 			cpu_id,
 			NULL,
-			mempool->m);
+			mempool);
 
 		if (status < 0)
 			return NULL;
@@ -421,7 +350,6 @@ obj_init(void)
 	if (!obj)
 		return NULL;
 
-	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 8ea1c414c2..4ea610ceed 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <sys/queue.h>
 
-#include <rte_mempool.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
@@ -24,32 +23,6 @@ struct obj;
 struct obj *
 obj_init(void);
 
-/*
- * mempool
- */
-struct mempool_params {
-	uint32_t buffer_size;
-	uint32_t pool_size;
-	uint32_t cache_size;
-	uint32_t cpu_id;
-};
-
-struct mempool {
-	TAILQ_ENTRY(mempool) node;
-	char name[NAME_SIZE];
-	struct rte_mempool *m;
-	uint32_t buffer_size;
-};
-
-struct mempool *
-mempool_create(struct obj *obj,
-	       const char *name,
-	       struct mempool_params *params);
-
-struct mempool *
-mempool_find(struct obj *obj,
-	     const char *name);
-
 /*
  * link
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 03/11] examples/pipeline: streamline ring support
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 01/11] " Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Remove redundant ring related code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 24 ++++++++++------
 examples/pipeline/obj.c | 63 -----------------------------------------
 examples/pipeline/obj.h | 21 --------------
 3 files changed, 15 insertions(+), 93 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 14f1cfa47e..517682f7c9 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -11,6 +11,7 @@
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_ring.h>
 #include <rte_swx_port_ethdev.h>
 #include <rte_swx_port_ring.h>
 #include <rte_swx_port_source_sink.h>
@@ -491,11 +492,11 @@ cmd_ring(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct ring_params p;
+	struct rte_ring *r;
 	char *name;
-	struct ring *ring;
+	uint32_t size, numa_node;
 
 	if (n_tokens != 6) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
@@ -504,28 +505,32 @@ cmd_ring(char **tokens,
 
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "size") != 0) {
+	if (strcmp(tokens[2], "size")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
+	if (parser_read_uint32(&size, tokens[3])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
 		return;
 	}
 
-	if (strcmp(tokens[4], "numa") != 0) {
+	if (strcmp(tokens[4], "numa")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
+	if (parser_read_uint32(&numa_node, tokens[5])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	ring = ring_create(obj, name, &p);
-	if (!ring) {
+	r = rte_ring_create(
+		name,
+		size,
+		(int)numa_node,
+		RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (!r) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
@@ -2999,6 +3004,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 697d14a901..3614b99d28 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -24,17 +24,11 @@
  */
 TAILQ_HEAD(link_list, link);
 
-/*
- * ring
- */
-TAILQ_HEAD(ring_list, ring);
-
 /*
  * obj
  */
 struct obj {
 	struct link_list link_list;
-	struct ring_list ring_list;
 };
 
 /*
@@ -282,62 +276,6 @@ link_next(struct obj *obj, struct link *link)
 		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
 }
 
-/*
- * ring
- */
-struct ring *
-ring_create(struct obj *obj, const char *name, struct ring_params *params)
-{
-	struct ring *ring;
-	struct rte_ring *r;
-	unsigned int flags = RING_F_SP_ENQ | RING_F_SC_DEQ;
-
-	/* Check input params */
-	if (!name || ring_find(obj, name) || !params || !params->size)
-		return NULL;
-
-	/**
-	 * Resource create
-	 */
-	r = rte_ring_create(
-		name,
-		params->size,
-		params->numa_node,
-		flags);
-	if (!r)
-		return NULL;
-
-	/* Node allocation */
-	ring = calloc(1, sizeof(struct ring));
-	if (!ring) {
-		rte_ring_free(r);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(ring->name, name, sizeof(ring->name));
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->ring_list, ring, node);
-
-	return ring;
-}
-
-struct ring *
-ring_find(struct obj *obj, const char *name)
-{
-	struct ring *ring;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(ring, &obj->ring_list, node)
-		if (strcmp(ring->name, name) == 0)
-			return ring;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -351,7 +289,6 @@ obj_init(void)
 		return NULL;
 
 	TAILQ_INIT(&obj->link_list);
-	TAILQ_INIT(&obj->ring_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 4ea610ceed..dbbc6d39a0 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -73,25 +73,4 @@ link_find(struct obj *obj, const char *name);
 struct link *
 link_next(struct obj *obj, struct link *link);
 
-/*
- * ring
- */
-struct ring_params {
-	uint32_t size;
-	uint32_t numa_node;
-};
-
-struct ring {
-	TAILQ_ENTRY(ring) node;
-	char name[NAME_SIZE];
-};
-
-struct ring *
-ring_create(struct obj *obj,
-	   const char *name,
-	   struct ring_params *params);
-
-struct ring *
-ring_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Streamline the Ethernet device support code and remove redundant code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c  | 175 ++++++++++++++++++------------------
 examples/pipeline/main.c |  12 +--
 examples/pipeline/obj.c  | 186 +++++++++------------------------------
 examples/pipeline/obj.h  |  51 ++---------
 4 files changed, 136 insertions(+), 288 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 517682f7c9..f4c8300ea7 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -292,16 +292,14 @@ cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct link_params p;
-	struct link_params_rss rss;
-	struct link *link;
+	struct ethdev_params p = {0};
+	struct ethdev_params_rss rss = {0};
 	char *name;
+	int status;
 
-	memset(&p, 0, sizeof(p));
-
-	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
+	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -377,111 +375,98 @@ cmd_ethdev(char **tokens,
 		}
 	}
 
-	link = link_create(obj, name, &p);
-	if (link == NULL) {
+	status = ethdev_config(name, &p);
+	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
 }
 
-/* Print the link stats and info */
 static void
-print_link_info(struct link *link, char *out, size_t out_size)
+ethdev_show(uint16_t port_id, char **out, size_t *out_size)
 {
-	struct rte_eth_stats stats;
-	struct rte_ether_addr mac_addr;
-	struct rte_eth_link eth_link;
-	uint16_t mtu;
-	int ret;
-
-	memset(&stats, 0, sizeof(stats));
-	rte_eth_stats_get(link->port_id, &stats);
-
-	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
-	if (ret != 0) {
-		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	ret = rte_eth_link_get(link->port_id, &eth_link);
-	if (ret < 0) {
-		snprintf(out, out_size, "\n%s: link get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	rte_eth_dev_get_mtu(link->port_id, &mtu);
-
-	snprintf(out, out_size,
-		"\n"
-		"%s: flags=<%s> mtu %u\n"
-		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
-		"\tport# %u  speed %s\n"
-		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
-		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tTX errors %" PRIu64"\n",
-		link->name,
-		eth_link.link_status == 0 ? "DOWN" : "UP",
-		mtu,
-		RTE_ETHER_ADDR_BYTES(&mac_addr),
-		link->n_rxq,
-		link->n_txq,
-		link->port_id,
-		rte_eth_link_speed_to_str(eth_link.link_speed),
-		stats.ipackets,
-		stats.ibytes,
-		stats.ierrors,
-		stats.imissed,
-		stats.rx_nombuf,
-		stats.opackets,
-		stats.obytes,
-		stats.oerrors);
+	char name[RTE_ETH_NAME_MAX_LEN] = {0};
+	struct rte_eth_dev_info info = {0};
+	struct rte_eth_stats stats = {0};
+	struct rte_ether_addr addr = {0};
+	struct rte_eth_link link = {0};
+	uint32_t length;
+	uint16_t mtu = 0;
+
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return;
+
+	rte_eth_dev_get_name_by_port(port_id, name);
+	rte_eth_dev_info_get(port_id, &info);
+	rte_eth_stats_get(port_id, &stats);
+	rte_eth_macaddr_get(port_id, &addr);
+	rte_eth_link_get(port_id, &link);
+	rte_eth_dev_get_mtu(port_id, &mtu);
+
+	snprintf(*out, *out_size,
+		 "%s: flags=<%s> mtu %u\n"
+		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
+		 "\tport# %u  speed %s\n"
+		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
+		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tTX errors %" PRIu64"\n\n",
+		 name,
+		 link.link_status ? "UP" : "DOWN",
+		 mtu,
+		 RTE_ETHER_ADDR_BYTES(&addr),
+		 info.nb_rx_queues,
+		 info.nb_tx_queues,
+		 port_id,
+		 rte_eth_link_speed_to_str(link.link_speed),
+		 stats.ipackets,
+		 stats.ibytes,
+		 stats.ierrors,
+		 stats.imissed,
+		 stats.rx_nombuf,
+		 stats.opackets,
+		 stats.obytes,
+		 stats.oerrors);
+
+	length = strlen(*out);
+	*out_size -= length;
+	*out += length;
 }
 
-/*
- * ethdev show [<ethdev_name>]
- */
+
+static char cmd_ethdev_show_help[] =
+"ethdev show [ <ethdev_name> ]\n";
+
 static void
 cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
-	      void *obj)
+	      void *obj __rte_unused)
 {
-	struct link *link;
-	char *link_name;
+	uint16_t port_id;
 
 	if (n_tokens != 2 && n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (n_tokens == 2) {
-		link = link_next(obj, NULL);
-
-		while (link != NULL) {
-			out_size = out_size - strlen(out);
-			out = &out[strlen(out)];
-
-			print_link_info(link, out, out_size);
-			link = link_next(obj, link);
-		}
-	} else {
-		out_size = out_size - strlen(out);
-		out = &out[strlen(out)];
+	/* Single device. */
+	if (n_tokens == 3) {
+		int status;
 
-		link_name = tokens[2];
-		link = link_find(obj, link_name);
+		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
+		if (status)
+			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
 
-		if (link == NULL) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-					"Link does not exist");
-			return;
-		}
-		print_link_info(link, out, out_size);
+		ethdev_show(port_id, &out, &out_size);
+		return;
 	}
+
+	/*  All devices. */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		if (rte_eth_dev_is_valid_port(port_id))
+			ethdev_show(port_id, &out, &out_size);
 }
 
 static const char cmd_ring_help[] =
@@ -3004,6 +2989,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tethdev show\n"
 			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -3039,9 +3025,16 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "ethdev") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
-		return;
+	if (!strcmp(tokens[0], "ethdev")) {
+		if (n_tokens == 1) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
+			return;
+		}
+
+		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
+			return;
+		}
 	}
 
 	if (strcmp(tokens[0], "ring") == 0) {
diff --git a/examples/pipeline/main.c b/examples/pipeline/main.c
index 6fb839f4cb..da5dd2b3f6 100644
--- a/examples/pipeline/main.c
+++ b/examples/pipeline/main.c
@@ -135,7 +135,6 @@ int
 main(int argc, char **argv)
 {
 	struct conn *conn;
-	struct obj *obj;
 	int status;
 
 	/* Parse application arguments */
@@ -150,13 +149,6 @@ main(int argc, char **argv)
 		return status;
 	};
 
-	/* Obj */
-	obj = obj_init();
-	if (!obj) {
-		printf("Error: Obj initialization failed (%d)\n", status);
-		return status;
-	}
-
 	/* Thread */
 	status = thread_init();
 	if (status) {
@@ -174,10 +166,10 @@ main(int argc, char **argv)
 		cli_script_process(app.script_name,
 			app.conn.msg_in_len_max,
 			app.conn.msg_out_len_max,
-			obj);
+			NULL);
 
 	/* Connectivity */
-	app.conn.msg_handle_arg = obj;
+	app.conn.msg_handle_arg = NULL;
 	conn = conn_init(&app.conn);
 	if (!conn) {
 		printf("Error: Connectivity initialization failed (%d)\n",
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 3614b99d28..143b968472 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -4,35 +4,14 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#ifdef RTE_EXEC_ENV_LINUX
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#endif
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_ctl.h>
 
 #include "obj.h"
 
 /*
- * link
- */
-TAILQ_HEAD(link_list, link);
-
-/*
- * obj
- */
-struct obj {
-	struct link_list link_list;
-};
-
-/*
- * link
+ * ethdev
  */
 static struct rte_eth_conf port_conf_default = {
 	.link_speeds = 0,
@@ -58,7 +37,7 @@ static struct rte_eth_conf port_conf_default = {
 static int
 rss_setup(uint16_t port_id,
 	uint16_t reta_size,
-	struct link_params_rss *rss)
+	struct ethdev_params_rss *rss)
 {
 	struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
 	uint32_t i;
@@ -87,69 +66,64 @@ rss_setup(uint16_t port_id,
 	return status;
 }
 
-struct link *
-link_create(struct obj *obj, const char *name, struct link_params *params)
+int
+ethdev_config(const char *name, struct ethdev_params *params)
 {
 	struct rte_eth_dev_info port_info;
 	struct rte_eth_conf port_conf;
-	struct link *link;
-	struct link_params_rss *rss;
+	struct ethdev_params_rss *rss;
 	struct rte_mempool *mempool;
-	uint32_t cpu_id, i;
-	int status;
+	uint32_t i;
+	int numa_node, status;
 	uint16_t port_id = 0;
 
 	/* Check input params */
-	if ((name == NULL) ||
-		link_find(obj, name) ||
-		(params == NULL) ||
-		(params->rx.n_queues == 0) ||
-		(params->rx.queue_size == 0) ||
-		(params->tx.n_queues == 0) ||
-		(params->tx.queue_size == 0))
-		return NULL;
+	if (!name ||
+	    !name[0] ||
+	    !params ||
+	    !params->rx.n_queues ||
+	    !params->rx.queue_size ||
+	    !params->tx.n_queues ||
+	    !params->tx.queue_size)
+		return -EINVAL;
 
 	status = rte_eth_dev_get_port_by_name(name, &port_id);
 	if (status)
-		return NULL;
+		return -EINVAL;
 
-	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
-		return NULL;
+	status = rte_eth_dev_info_get(port_id, &port_info);
+	if (status)
+		return -EINVAL;
 
 	mempool = rte_mempool_lookup(params->rx.mempool_name);
 	if (!mempool)
-		return NULL;
+		return -EINVAL;
 
 	rss = params->rx.rss;
 	if (rss) {
-		if ((port_info.reta_size == 0) ||
-			(port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512))
-			return NULL;
+		if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512)
+			return -EINVAL;
 
-		if ((rss->n_queues == 0) ||
-			(rss->n_queues >= LINK_RXQ_RSS_MAX))
-			return NULL;
+		if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX)
+			return -EINVAL;
 
 		for (i = 0; i < rss->n_queues; i++)
 			if (rss->queue_id[i] >= port_info.max_rx_queues)
-				return NULL;
+				return -EINVAL;
 	}
 
-	/**
-	 * Resource create
-	 */
 	/* Port */
 	memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
 	if (rss) {
+		uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP;
+
 		port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
-		port_conf.rx_adv_conf.rss_conf.rss_hf =
-			(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
-			port_info.flow_type_rss_offloads;
+		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads;
 	}
 
-	cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
-	if (cpu_id == (uint32_t) SOCKET_ID_ANY)
-		cpu_id = 0;
+	numa_node = rte_eth_dev_socket_id(port_id);
+	if (numa_node == SOCKET_ID_ANY)
+		numa_node = 0;
 
 	status = rte_eth_dev_configure(
 		port_id,
@@ -158,12 +132,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		&port_conf);
 
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (params->promiscuous) {
 		status = rte_eth_promiscuous_enable(port_id);
-		if (status != 0)
-			return NULL;
+		if (status)
+			return -EINVAL;
 	}
 
 	/* Port RX */
@@ -172,12 +146,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->rx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL,
 			mempool);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port TX */
@@ -186,24 +160,24 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->tx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port start */
 	status = rte_eth_dev_start(port_id);
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (rss) {
 		status = rss_setup(port_id, port_info.reta_size, rss);
 
 		if (status) {
 			rte_eth_dev_stop(port_id);
-			return NULL;
+			return -EINVAL;
 		}
 	}
 
@@ -211,84 +185,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	status = rte_eth_dev_set_link_up(port_id);
 	if ((status < 0) && (status != -ENOTSUP)) {
 		rte_eth_dev_stop(port_id);
-		return NULL;
-	}
-
-	/* Node allocation */
-	link = calloc(1, sizeof(struct link));
-	if (link == NULL) {
-		rte_eth_dev_stop(port_id);
-		return NULL;
+		return -EINVAL;
 	}
 
-	/* Node fill in */
-	strlcpy(link->name, name, sizeof(link->name));
-	link->port_id = port_id;
-	link->n_rxq = params->rx.n_queues;
-	link->n_txq = params->tx.n_queues;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->link_list, link, node);
-
-	return link;
-}
-
-int
-link_is_up(struct obj *obj, const char *name)
-{
-	struct rte_eth_link link_params;
-	struct link *link;
-
-	/* Check input params */
-	if (!obj || !name)
-		return 0;
-
-	link = link_find(obj, name);
-	if (link == NULL)
-		return 0;
-
-	/* Resource */
-	if (rte_eth_link_get(link->port_id, &link_params) < 0)
-		return 0;
-
-	return (link_params.link_status == RTE_ETH_LINK_DOWN) ? 0 : 1;
-}
-
-struct link *
-link_find(struct obj *obj, const char *name)
-{
-	struct link *link;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(link, &obj->link_list, node)
-		if (strcmp(link->name, name) == 0)
-			return link;
-
-	return NULL;
-}
-
-struct link *
-link_next(struct obj *obj, struct link *link)
-{
-	return (link == NULL) ?
-		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
-}
-
-/*
- * obj
- */
-struct obj *
-obj_init(void)
-{
-	struct obj *obj;
-
-	obj = calloc(1, sizeof(struct obj));
-	if (!obj)
-		return NULL;
-
-	TAILQ_INIT(&obj->link_list);
-
-	return obj;
+	return 0;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index dbbc6d39a0..fb091f4ba7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -6,41 +6,25 @@
 #define _INCLUDE_OBJ_H_
 
 #include <stdint.h>
-#include <sys/queue.h>
-
-#include <rte_swx_pipeline.h>
-#include <rte_swx_ctl.h>
-
-#ifndef NAME_SIZE
-#define NAME_SIZE 64
-#endif
-
-/*
- * obj
- */
-struct obj;
-
-struct obj *
-obj_init(void);
 
 /*
- * link
+ * ethdev
  */
-#ifndef LINK_RXQ_RSS_MAX
-#define LINK_RXQ_RSS_MAX                                   16
+#ifndef ETHDEV_RXQ_RSS_MAX
+#define ETHDEV_RXQ_RSS_MAX 16
 #endif
 
-struct link_params_rss {
-	uint32_t queue_id[LINK_RXQ_RSS_MAX];
+struct ethdev_params_rss {
+	uint32_t queue_id[ETHDEV_RXQ_RSS_MAX];
 	uint32_t n_queues;
 };
 
-struct link_params {
+struct ethdev_params {
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
 		const char *mempool_name;
-		struct link_params_rss *rss;
+		struct ethdev_params_rss *rss;
 	} rx;
 
 	struct {
@@ -51,26 +35,7 @@ struct link_params {
 	int promiscuous;
 };
 
-struct link {
-	TAILQ_ENTRY(link) node;
-	char name[NAME_SIZE];
-	uint16_t port_id;
-	uint32_t n_rxq;
-	uint32_t n_txq;
-};
-
-struct link *
-link_create(struct obj *obj,
-	    const char *name,
-	    struct link_params *params);
-
 int
-link_is_up(struct obj *obj, const char *name);
-
-struct link *
-link_find(struct obj *obj, const char *name);
-
-struct link *
-link_next(struct obj *obj, struct link *link);
+ethdev_config(const char *name, struct ethdev_params *params);
 
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 05/11] examples/pipeline: support crypto devices
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add support for crypto devices in the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c | 60 +++++++++++++++++++++++++++++++++++++++++
 examples/pipeline/obj.h | 11 ++++++++
 2 files changed, 71 insertions(+)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 143b968472..25bfd2da4f 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -7,6 +7,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_cryptodev.h>
 
 #include "obj.h"
 
@@ -190,3 +191,62 @@ ethdev_config(const char *name, struct ethdev_params *params)
 
 	return 0;
 }
+
+/*
+ * cryptodev
+ */
+int
+cryptodev_config(const char *name, struct cryptodev_params *params)
+{
+	struct rte_cryptodev_info dev_info;
+	struct rte_cryptodev_config dev_conf = {0};
+	struct rte_cryptodev_qp_conf queue_conf = {0};
+	uint8_t dev_id;
+	uint32_t socket_id, i;
+	int status;
+
+	/* Check input parameters. */
+	if (!name ||
+	    !params->n_queue_pairs ||
+	    !params->queue_size)
+		return -EINVAL;
+
+	/* Find the crypto device. */
+	status = rte_cryptodev_get_dev_id(name);
+	if (status < 0)
+		return -EINVAL;
+
+	dev_id = (uint8_t)status;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->n_queue_pairs > dev_info.max_nb_queue_pairs)
+		return -EINVAL;
+
+	socket_id = rte_cryptodev_socket_id(dev_id);
+
+	/* Configure the crypto device. */
+	dev_conf.socket_id = socket_id;
+	dev_conf.nb_queue_pairs = params->n_queue_pairs;
+	dev_conf.ff_disable = 0;
+
+	status = rte_cryptodev_configure(dev_id, &dev_conf);
+	if (status)
+		return status;
+
+	/* Configure the crypto device queue pairs. */
+	queue_conf.nb_descriptors = params->queue_size;
+	queue_conf.mp_session = NULL;
+
+	for (i = 0; i < params->n_queue_pairs; i++) {
+		status = rte_cryptodev_queue_pair_setup(dev_id, i, &queue_conf, socket_id);
+		if (status)
+			return status;
+	}
+
+	/* Start the crypto device. */
+	status = rte_cryptodev_start(dev_id);
+	if (status)
+		return status;
+
+	return 0;
+}
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index fb091f4ba7..0f103be6a7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -38,4 +38,15 @@ struct ethdev_params {
 int
 ethdev_config(const char *name, struct ethdev_params *params);
 
+/*
+ * cryptodev
+ */
+struct cryptodev_params {
+	uint32_t n_queue_pairs;
+	uint32_t queue_size;
+};
+
+int
+cryptodev_config(const char *name, struct cryptodev_params *params);
+
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (4 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI command for the configuration of crypto devices.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f4c8300ea7..6b5d5a3370 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -521,6 +521,58 @@ cmd_ring(char **tokens,
 	}
 }
 
+static const char cmd_cryptodev_help[] =
+"cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
+
+static void
+cmd_cryptodev(char **tokens,
+	      uint32_t n_tokens,
+	      char *out,
+	      size_t out_size,
+	      void *obj __rte_unused)
+{
+	struct cryptodev_params params;
+	char *cryptodev_name;
+	int status;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	cryptodev_name = tokens[1];
+
+	if (strcmp(tokens[2], "queues")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
+		return;
+	}
+
+	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
+		return;
+	}
+
+	if (strcmp(tokens[4], "qsize")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
+		return;
+	}
+
+
+	if (parser_read_uint32(&params.queue_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
+		return;
+	}
+
+	status = cryptodev_config(cryptodev_name, &params);
+	if (status)
+		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
+}
+
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2991,6 +3043,7 @@ cmd_help(char **tokens,
 			"\tethdev\n"
 			"\tethdev show\n"
 			"\tring\n"
+			"\tcryptodev\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3042,6 +3095,11 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3297,6 +3355,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
+		return;
+	}
+
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (5 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands for IPsec block configuration.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 298 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 298 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 6b5d5a3370..92ec0fd8e3 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -18,6 +18,7 @@
 #include <rte_swx_port_fd.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
+#include <rte_swx_ipsec.h>
 
 #include "cli.h"
 
@@ -2910,6 +2911,263 @@ cmd_pipeline_mirror_session(char **tokens,
 	}
 }
 
+static const char cmd_ipsec_create_help[] =
+"ipsec <ipsec_instance_name> create "
+"in <ring_in_name> out <ring_out_name> "
+"cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
+"bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
+"samax <n_sa_max> "
+"numa <numa_node>\n";
+
+static void
+cmd_ipsec_create(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec_params p;
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t numa_node;
+	int status;
+
+	if (n_tokens != 20) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+
+	if (strcmp(tokens[2], "create")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
+		return;
+	}
+
+	if (strcmp(tokens[3], "in")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+		return;
+	}
+
+	p.ring_in_name = tokens[4];
+
+	if (strcmp(tokens[5], "out")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+		return;
+	}
+
+	p.ring_out_name = tokens[6];
+
+	if (strcmp(tokens[7], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	p.crypto_dev_name = tokens[8];
+
+	if (strcmp(tokens[9], "cryptoq")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
+		return;
+	}
+
+	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
+		return;
+	}
+
+	if (strcmp(tokens[11], "bsz")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
+		return;
+	}
+
+	if (strcmp(tokens[16], "samax")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
+		return;
+	}
+
+	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
+		return;
+	}
+
+	if (strcmp(tokens[18], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
+		return;
+	}
+
+	if (parser_read_uint32(&numa_node, tokens[19])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	status = rte_swx_ipsec_create(&ipsec,
+				      ipsec_instance_name,
+				      &p,
+				      (int)numa_node);
+	if (status)
+		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
+}
+
+static const char cmd_ipsec_sa_add_help[] =
+"ipsec <ipsec_instance_name> sa add <file_name>\n";
+
+static void
+cmd_ipsec_sa_add(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name, *file_name, *line = NULL;
+	FILE *file = NULL;
+	uint32_t line_id = 0;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		goto free;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		goto free;
+	}
+
+	if (strcmp(tokens[3], "add")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+		goto free;
+	}
+
+	file_name = tokens[4];
+	file = fopen(file_name, "r");
+	if (!file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+		goto free;
+	}
+
+	/* Buffer allocation. */
+	line = malloc(MAX_LINE_SIZE);
+	if (!line) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	/* File read. */
+	for (line_id = 1; ; line_id++) {
+		struct rte_swx_ipsec_sa_params *sa;
+		const char *err_msg;
+		uint32_t sa_id = 0;
+		int is_blank_or_comment, status = 0;
+
+		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+			break;
+
+		/* Read SA from file. */
+		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
+		if (!sa) {
+			if (is_blank_or_comment)
+				continue;
+
+			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
+				file_name, line_id, err_msg);
+			goto free;
+		}
+
+		snprintf(out, out_size, "%s", line);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		/* Add the SA to the IPsec instance. Free the SA. */
+		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
+		if (status)
+			snprintf(out, out_size, "\t: Error (%d)\n", status);
+		else
+			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		free(sa);
+		if (status)
+			goto free;
+	}
+
+free:
+	if (file)
+		fclose(file);
+	free(line);
+}
+
+static const char cmd_ipsec_sa_delete_help[] =
+"ipsec <ipsec_instance_name> sa delete <sa_id>\n";
+
+static void
+cmd_ipsec_sa_delete(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t sa_id;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		return;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		return;
+	}
+
+	if (strcmp(tokens[3], "delete")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+		return;
+	}
+
+	if (parser_read_uint32(&sa_id, tokens[4])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
+		return;
+	}
+
+	rte_swx_ipsec_sa_delete(ipsec, sa_id);
+}
+
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
 
@@ -3068,6 +3326,9 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tipsec create\n"
+			"\tipsec sa add\n"
+			"\tipsec sa delete\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
 		return;
@@ -3292,6 +3553,26 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "add")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "delete")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
+		return;
+	}
+
 	if ((n_tokens == 3) &&
 		(strcmp(tokens[0], "thread") == 0) &&
 		(strcmp(tokens[1], "pipeline") == 0)) {
@@ -3540,6 +3821,23 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "ipsec")) {
+		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
+			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
+			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
+			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	if (strcmp(tokens[0], "thread") == 0) {
 		if ((n_tokens >= 5) &&
 			(strcmp(tokens[4], "enable") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (6 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the configuration updates for the data plane threads were
performed through message queues. Now, this mechanism is replaced by
the control thread updating the mirror copy of the data plane thread
configuration followed by pointer swapping.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 154 ++---
 examples/pipeline/examples/fib.cli            |   2 +-
 examples/pipeline/examples/hash_func.cli      |   2 +-
 examples/pipeline/examples/l2fwd.cli          |   2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |   2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |   2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |   2 +-
 examples/pipeline/examples/learner.cli        |   2 +-
 examples/pipeline/examples/meter.cli          |   2 +-
 examples/pipeline/examples/mirroring.cli      |   2 +-
 examples/pipeline/examples/recirculation.cli  |   2 +-
 examples/pipeline/examples/registers.cli      |   2 +-
 examples/pipeline/examples/selector.cli       |   2 +-
 examples/pipeline/examples/varbit.cli         |   2 +-
 examples/pipeline/examples/vxlan.cli          |   2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |   2 +-
 examples/pipeline/thread.c                    | 586 ++++--------------
 examples/pipeline/thread.h                    |  17 +-
 18 files changed, 217 insertions(+), 570 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 92ec0fd8e3..68bb93a46c 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3168,119 +3168,86 @@ cmd_ipsec_sa_delete(char **tokens,
 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
 }
 
-static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
-
-#ifndef TIMER_PERIOD_MS_DEFAULT
-#define TIMER_PERIOD_MS_DEFAULT 10
-#endif
+static const char cmd_pipeline_enable_help[] =
+"pipeline <pipeline_name> enable thread <thread_id>\n";
 
 static void
-cmd_thread_pipeline_enable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_enable(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
 {
 	char *pipeline_name;
 	struct rte_swx_pipeline *p;
-	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
+	uint32_t thread_id;
 	int status;
 
-	if ((n_tokens != 5) && (n_tokens != 7)) {
+	if (n_tokens != 5) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "enable") != 0) {
+	if (strcmp(tokens[2], "enable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
 		return;
 	}
 
-	if (n_tokens == 7) {
-		if (strcmp(tokens[5], "period") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
-			return;
-		}
+	if (strcmp(tokens[3], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
 
-		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-			return;
-		}
+	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
+	status = pipeline_enable(p, thread_id);
 	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
+		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
 		return;
 	}
 }
 
-static const char cmd_thread_pipeline_disable_help[] =
-"thread <thread_id> pipeline <pipeline_name> disable\n";
+static const char cmd_pipeline_disable_help[] =
+"pipeline <pipeline_name> disable\n";
 
 static void
-cmd_thread_pipeline_disable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_disable(char **tokens,
+		     uint32_t n_tokens,
+		     char *out,
+		     size_t out_size,
+		     void *obj __rte_unused)
 {
 	struct rte_swx_pipeline *p;
 	char *pipeline_name;
-	uint32_t thread_id;
-	int status;
 
-	if (n_tokens != 5) {
+	if (n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "disable") != 0) {
+	if (strcmp(tokens[2], "disable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, p);
-	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL,
-			"thread pipeline disable");
-		return;
-	}
+	pipeline_disable(p);
 }
 
 static void
@@ -3326,11 +3293,12 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tpipeline enable\n"
+			"\tpipeline disable\n\n"
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
-			"\tthread pipeline enable\n"
-			"\tthread pipeline disable\n\n");
+			);
 		return;
 	}
 
@@ -3553,6 +3521,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
+		return;
+	}
+
 	if (!strcmp(tokens[0], "ipsec") &&
 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
@@ -3573,22 +3553,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((n_tokens == 3) &&
-		(strcmp(tokens[0], "thread") == 0) &&
-		(strcmp(tokens[1], "pipeline") == 0)) {
-		if (strcmp(tokens[2], "enable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_enable_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "disable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_disable_help);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3819,6 +3783,16 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
+			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
+			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
 	}
 
 	if (!strcmp(tokens[0], "ipsec")) {
@@ -3838,22 +3812,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
-	if (strcmp(tokens[0], "thread") == 0) {
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "enable") == 0)) {
-			cmd_thread_pipeline_enable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "disable") == 0)) {
-			cmd_thread_pipeline_disable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 2450dc9ca4..d2485cefe0 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -54,4 +54,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index 3840e47a2b..2c20f7a7a0 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index c5505df296..383fe346c0 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethde
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index bd700707f5..d02cb2c470 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index aa64b32ab2..d4ea449e03 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 619d17474f..8a25c98096 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 42184be1c8..04c7b4d26f 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/eth
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 0fa38b16b8..9e064dbb52 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 meter meters set profile platinum index from 0 to 15
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index e7391de74b..da6bd338fb 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 mirror session 3 port 0 clone slow truncate 128
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 965c870fd4..f925179bf7 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/exampl
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index aa775f71e5..58dde5fdf1 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0608de184..9af56cd0f1 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 selector s show
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 648b8882c3..621e7f5627 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethd
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 4565adfaea..73eb29a6b2 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 510c2ad870..828342408b 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -36,4 +36,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 6d15f51fb2..3001bc0858 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -3,17 +3,11 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
 
+#include <rte_atomic.h>
 #include <rte_common.h>
-#include <rte_cycles.h>
 #include <rte_lcore.h>
-#include <rte_ring.h>
-
-#include <rte_table_acl.h>
-#include <rte_table_array.h>
-#include <rte_table_hash.h>
-#include <rte_table_lpm.h>
-#include <rte_table_lpm_ipv6.h>
 
 #include "obj.h"
 #include "thread.h"
@@ -22,14 +16,6 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
-#ifndef THREAD_MSGQ_SIZE
-#define THREAD_MSGQ_SIZE                                   64
-#endif
-
-#ifndef THREAD_TIMER_PERIOD_MS
-#define THREAD_TIMER_PERIOD_MS                             100
-#endif
-
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -40,509 +26,209 @@
 #endif
 
 /**
- * Control thread: data plane thread context
+ * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
+ * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
+ * blocks.
+ *
+ * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
+ * single thread at any given time, so the same pipeline/block cannot show up in the list of
+ * pipelines/blocks of more than one thread at any specific moment.
+ *
+ * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
+ *  - Read-write by the CP thread;
+ *  - Read-only by the DP thread.
  */
 struct thread {
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-
-	uint32_t enabled;
-};
-
-static struct thread thread[RTE_MAX_LCORE];
-
-/**
- * Data plane threads: context
- */
-struct pipeline_data {
-	struct rte_swx_pipeline *p;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-};
-
-struct thread_data {
-	struct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];
-	uint32_t n_pipelines;
-
-	struct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-	uint64_t time_next_min;
+	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	volatile uint64_t n_pipelines;
+	int enabled;
 } __rte_cache_aligned;
 
-static struct thread_data thread_data[RTE_MAX_LCORE];
+static struct thread threads[RTE_MAX_LCORE];
 
 /**
- * Control thread: data plane thread init
+ * Control plane (CP) thread.
  */
-static void
-thread_free(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct thread *t = &thread[i];
-
-		if (!rte_lcore_is_enabled(i))
-			continue;
-
-		/* MSGQs */
-		rte_ring_free(t->msgq_req);
-
-		rte_ring_free(t->msgq_rsp);
-	}
-}
-
 int
 thread_init(void)
 {
-	uint32_t i;
-
-	RTE_LCORE_FOREACH_WORKER(i) {
-		char name[NAME_MAX];
-		struct rte_ring *msgq_req, *msgq_rsp;
-		struct thread *t = &thread[i];
-		struct thread_data *t_data = &thread_data[i];
-		uint32_t cpu_id = rte_lcore_to_socket_id(i);
-
-		/* MSGQs */
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-REQ", i);
-
-		msgq_req = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_req == NULL) {
-			thread_free();
-			return -1;
-		}
+	uint32_t thread_id;
 
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-RSP", i);
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
 
-		msgq_rsp = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_rsp == NULL) {
-			thread_free();
-			return -1;
-		}
-
-		/* Control thread records */
-		t->msgq_req = msgq_req;
-		t->msgq_rsp = msgq_rsp;
 		t->enabled = 1;
-
-		/* Data plane thread records */
-		t_data->n_pipelines = 0;
-		t_data->msgq_req = msgq_req;
-		t_data->msgq_rsp = msgq_rsp;
-		t_data->timer_period =
-			(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;
-		t_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;
-		t_data->time_next_min = t_data->time_next;
 	}
 
 	return 0;
 }
 
-static inline int
-thread_is_running(uint32_t thread_id)
+static uint32_t
+pipeline_find(struct rte_swx_pipeline *p)
 {
-	enum rte_lcore_state_t thread_state;
+	uint32_t thread_id;
 
-	thread_state = rte_eal_get_lcore_state(thread_id);
-	return (thread_state == RUNNING) ? 1 : 0;
-}
-
-/**
- * Control thread & data plane threads: message passing
- */
-enum thread_req_type {
-	THREAD_REQ_PIPELINE_ENABLE = 0,
-	THREAD_REQ_PIPELINE_DISABLE,
-	THREAD_REQ_MAX
-};
-
-struct thread_msg_req {
-	enum thread_req_type type;
-
-	union {
-		struct {
-			struct rte_swx_pipeline *p;
-			uint32_t timer_period_ms;
-		} pipeline_enable;
-
-		struct {
-			struct rte_swx_pipeline *p;
-		} pipeline_disable;
-	};
-};
-
-struct thread_msg_rsp {
-	int status;
-};
-
-/**
- * Control thread
- */
-static struct thread_msg_req *
-thread_msg_alloc(void)
-{
-	size_t size = RTE_MAX(sizeof(struct thread_msg_req),
-		sizeof(struct thread_msg_rsp));
-
-	return calloc(1, size);
-}
-
-static void
-thread_msg_free(struct thread_msg_rsp *rsp)
-{
-	free(rsp);
-}
-
-static struct thread_msg_rsp *
-thread_msg_send_recv(uint32_t thread_id,
-	struct thread_msg_req *req)
-{
-	struct thread *t = &thread[thread_id];
-	struct rte_ring *msgq_req = t->msgq_req;
-	struct rte_ring *msgq_rsp = t->msgq_rsp;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* send */
-	do {
-		status = rte_ring_sp_enqueue(msgq_req, req);
-	} while (status == -ENOBUFS);
-
-	/* recv */
-	do {
-		status = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);
-	} while (status != 0);
-
-	return rsp;
-}
-
-static int
-thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
-{
-	struct thread *t = &thread[thread_id];
-	struct thread_data *td = &thread_data[thread_id];
-	uint32_t i;
-
-	if (!t->enabled)
-		return 0; /* Pipeline NOT enabled on this thread. */
-
-	for (i = 0; i < td->n_pipelines; i++)
-		if (td->p[i] == p)
-			return 1; /* Pipeline enabled on this thread. */
-
-	return 0 /* Pipeline NOT enabled on this thread. */;
-}
-
-int
-thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
-{
-	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		struct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];
-
-		if (td->n_pipelines >= THREAD_PIPELINES_MAX)
-			return -1;
-
-		/* Data plane thread */
-		td->p[td->n_pipelines] = p;
-
-		tdp->p = p;
-		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
-		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
-		td->n_pipelines++;
+		if (!t->enabled)
+			continue;
 
-		return 0;
+		for (i = 0; i < t->n_pipelines; i++)
+			if (t->pipelines[i] == p)
+				break;
 	}
 
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p;
-	req->pipeline_enable.timer_period_ms = timer_period_ms;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
-
-	/* Read response */
-	status = rsp->status;
-
-	/* Free response */
-	thread_msg_free(rsp);
-
-	/* Request completion */
-	if (status)
-		return status;
-
-	return 0;
+	return thread_id;
 }
 
+/**
+ * Enable a given pipeline to run on a specific DP thread.
+ *
+ * CP thread:
+ *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
+ *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
+ *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
+ *    write barrier used below.
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
+ *    the new pipeline when it sees the updated t->n_pipelines value;
+ *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
+ *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
+ *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
+ */
 int
-thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
 {
 	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
+	uint64_t n_pipelines;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_pipeline_enabled(thread_id, p))
-		return 0;
+	if (!p || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
 
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		uint32_t i;
-
-		for (i = 0; i < td->n_pipelines; i++) {
-			struct pipeline_data *tdp = &td->pipeline_data[i];
-
-			if (tdp->p != p)
-				continue;
-
-			/* Data plane thread */
-			if (i < td->n_pipelines - 1) {
-				struct rte_swx_pipeline *pipeline_last =
-					td->p[td->n_pipelines - 1];
-				struct pipeline_data *tdp_last =
-					&td->pipeline_data[td->n_pipelines - 1];
-
-				td->p[i] = pipeline_last;
-				memcpy(tdp, tdp_last, sizeof(*tdp));
-			}
-
-			td->n_pipelines--;
-
-			break;
-		}
+	if (pipeline_find(p) < RTE_MAX_LCORE)
+		return -EEXIST;
 
-		return 0;
-	}
-
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
 
-	/* Read response */
-	status = rsp->status;
+	n_pipelines = t->n_pipelines;
 
-	/* Free response */
-	thread_msg_free(rsp);
+	/* Check there is room for at least one more pipeline. */
+	if (n_pipelines >= THREAD_PIPELINES_MAX)
+		return -ENOSPC;
 
-	/* Request completion */
-	if (status)
-		return status;
+	/* Install the new pipeline. */
+	t->pipelines[n_pipelines] = p;
+	rte_wmb();
+	t->n_pipelines = n_pipelines + 1;
 
 	return 0;
 }
 
 /**
- * Data plane threads: message handling
+ * Disable a given pipeline from running on any DP thread.
+ *
+ * CP thread:
+ *  - Detects the thread that is running the given pipeline, if any;
+ *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
+ *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
+ *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
+ *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
+ *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
+ *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
+ *    below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final value of t->n_pipelines;
+ *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
+ *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
+ *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
+ *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
+ *    behaviors are detailed below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final values of the t->pipeline[] array;
+ *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
+ *       the exception of the pipeline_last, which is not run during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
  */
-static inline struct thread_msg_req *
-thread_msg_recv(struct rte_ring *msgq_req)
-{
-	struct thread_msg_req *req;
-
-	int status = rte_ring_sc_dequeue(msgq_req, (void **) &req);
-
-	if (status != 0)
-		return NULL;
-
-	return req;
-}
-
-static inline void
-thread_msg_send(struct rte_ring *msgq_rsp,
-	struct thread_msg_rsp *rsp)
-{
-	int status;
-
-	do {
-		status = rte_ring_sp_enqueue(msgq_rsp, rsp);
-	} while (status == -ENOBUFS);
-}
-
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_enable(struct thread_data *t,
-	struct thread_msg_req *req)
+void
+pipeline_disable(struct rte_swx_pipeline *p)
 {
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	struct pipeline_data *p = &t->pipeline_data[t->n_pipelines];
-
-	/* Request */
-	if (t->n_pipelines >= THREAD_PIPELINES_MAX) {
-		rsp->status = -1;
-		return rsp;
-	}
-
-	t->p[t->n_pipelines] = req->pipeline_enable.p;
-
-	p->p = req->pipeline_enable.p;
-	p->timer_period = (rte_get_tsc_hz() *
-		req->pipeline_enable.timer_period_ms) / 1000;
-	p->time_next = rte_get_tsc_cycles() + p->timer_period;
+	struct thread *t;
+	uint64_t n_pipelines;
+	uint32_t thread_id, i;
 
-	t->n_pipelines++;
+	/* Check input params */
+	if (!p)
+		return;
 
-	/* Response */
-	rsp->status = 0;
-	return rsp;
-}
+	/* Find the thread that runs this pipeline. */
+	thread_id = pipeline_find(p);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
 
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_disable(struct thread_data *t,
-	struct thread_msg_req *req)
-{
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	uint32_t n_pipelines = t->n_pipelines;
-	struct rte_swx_pipeline *pipeline = req->pipeline_disable.p;
-	uint32_t i;
+	t = &threads[thread_id];
+	n_pipelines = t->n_pipelines;
 
-	/* find pipeline */
 	for (i = 0; i < n_pipelines; i++) {
-		struct pipeline_data *p = &t->pipeline_data[i];
+		struct rte_swx_pipeline *pipeline = t->pipelines[i];
 
-		if (p->p != pipeline)
+		if (pipeline != p)
 			continue;
 
 		if (i < n_pipelines - 1) {
-			struct rte_swx_pipeline *pipeline_last =
-				t->p[n_pipelines - 1];
-			struct pipeline_data *p_last =
-				&t->pipeline_data[n_pipelines - 1];
+			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
 
-			t->p[i] = pipeline_last;
-			memcpy(p, p_last, sizeof(*p));
+			t->pipelines[i] = pipeline_last;
 		}
 
-		t->n_pipelines--;
+		rte_wmb();
+		t->n_pipelines = n_pipelines - 1;
 
-		rsp->status = 0;
-		return rsp;
+		return;
 	}
 
-	/* should not get here */
-	rsp->status = 0;
-	return rsp;
-}
-
-static void
-thread_msg_handle(struct thread_data *t)
-{
-	for ( ; ; ) {
-		struct thread_msg_req *req;
-		struct thread_msg_rsp *rsp;
-
-		req = thread_msg_recv(t->msgq_req);
-		if (req == NULL)
-			break;
-
-		switch (req->type) {
-		case THREAD_REQ_PIPELINE_ENABLE:
-			rsp = thread_msg_handle_pipeline_enable(t, req);
-			break;
-
-		case THREAD_REQ_PIPELINE_DISABLE:
-			rsp = thread_msg_handle_pipeline_disable(t, req);
-			break;
-
-		default:
-			rsp = (struct thread_msg_rsp *) req;
-			rsp->status = -1;
-		}
-
-		thread_msg_send(t->msgq_rsp, rsp);
-	}
+	return;
 }
 
 /**
- * Data plane threads: main
+ * Data plane (DP) threads.
+ *
+ * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
+ * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
+ * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
+ * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
+ * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
+ * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
+ * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
+ * marked as volatile.
  */
 int
 thread_main(void *arg __rte_unused)
 {
-	struct thread_data *t;
-	uint32_t thread_id, i;
+	struct thread *t;
+	uint32_t thread_id;
 
 	thread_id = rte_lcore_id();
-	t = &thread_data[thread_id];
-
-	/* Dispatch loop */
-	for (i = 0; ; i++) {
-		uint32_t j;
-
-		/* Data Plane */
-		for (j = 0; j < t->n_pipelines; j++)
-			rte_swx_pipeline_run(t->p[j], PIPELINE_INSTR_QUANTA);
+	t = &threads[thread_id];
 
-		/* Control Plane */
-		if ((i & 0xF) == 0) {
-			uint64_t time = rte_get_tsc_cycles();
-			uint64_t time_next_min = UINT64_MAX;
-
-			if (time < t->time_next_min)
-				continue;
-
-			/* Thread message queues */
-			{
-				uint64_t time_next = t->time_next;
-
-				if (time_next <= time) {
-					thread_msg_handle(t);
-					time_next = time + t->timer_period;
-					t->time_next = time_next;
-				}
-
-				if (time_next < time_next_min)
-					time_next_min = time_next;
-			}
+	/* Dispatch loop. */
+	for ( ; ; ) {
+		uint32_t i;
 
-			t->time_next_min = time_next_min;
-		}
+		/* Pipelines. */
+		for (i = 0; i < t->n_pipelines; i++)
+			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 712cb25bbb..338d480abb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -9,18 +9,21 @@
 
 #include <rte_swx_pipeline.h>
 
+/**
+ * Control plane (CP) thread.
+ */
 int
-thread_pipeline_enable(uint32_t thread_id,
-		       struct rte_swx_pipeline *p,
-		       uint32_t timer_period_ms);
+thread_init(void);
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-			struct rte_swx_pipeline *p);
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 
-int
-thread_init(void);
+void
+pipeline_disable(struct rte_swx_pipeline *p);
 
+/**
+ * Data plane (DP) threads.
+ */
 int
 thread_main(void *arg);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (7 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the data plane threads only supported the execution of
pipelines assigned to them through configuration updates. Now, the
data plane threads also support running blocks such as IPsec.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/thread.c | 143 +++++++++++++++++++++++++++++++++++++
 examples/pipeline/thread.h |   9 +++
 2 files changed, 152 insertions(+)

diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 3001bc0858..dc3ea73fbf 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -16,6 +16,10 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
+#ifndef THREAD_BLOCKS_MAX
+#define THREAD_BLOCKS_MAX                                  256
+#endif
+
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -38,9 +42,16 @@
  *  - Read-write by the CP thread;
  *  - Read-only by the DP thread.
  */
+struct block {
+	block_run_f block_func;
+	void *block;
+};
+
 struct thread {
 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	struct block *blocks[THREAD_BLOCKS_MAX];
 	volatile uint64_t n_pipelines;
+	volatile uint64_t n_blocks;
 	int enabled;
 } __rte_cache_aligned;
 
@@ -53,14 +64,43 @@ int
 thread_init(void)
 {
 	uint32_t thread_id;
+	int status = 0;
 
 	RTE_LCORE_FOREACH_WORKER(thread_id) {
 		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
 		t->enabled = 1;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			struct block *b;
+
+			b = calloc(1, sizeof(struct block));
+			if (!b) {
+				status = -ENOMEM;
+				goto error;
+			}
+
+			t->blocks[i] = b;
+		}
 	}
 
 	return 0;
+
+error:
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		t->enabled = 0;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			free(t->blocks[i]);
+			t->blocks[i] = NULL;
+		}
+	}
+
+	return status;
 }
 
 static uint32_t
@@ -83,6 +123,26 @@ pipeline_find(struct rte_swx_pipeline *p)
 	return thread_id;
 }
 
+static uint32_t
+block_find(void *b)
+{
+	uint32_t thread_id;
+
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		if (!t->enabled)
+			continue;
+
+		for (i = 0; i < t->n_blocks; i++)
+			if (t->blocks[i]->block == b)
+				break;
+	}
+
+	return thread_id;
+}
+
 /**
  * Enable a given pipeline to run on a specific DP thread.
  *
@@ -201,9 +261,85 @@ pipeline_disable(struct rte_swx_pipeline *p)
 	return;
 }
 
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+
+	/* Check input params */
+	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
+
+	if (block_find(block) < RTE_MAX_LCORE)
+		return -EEXIST;
+
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
+
+	n_blocks = t->n_blocks;
+
+	/* Check there is room for at least one more block. */
+	if (n_blocks >= THREAD_BLOCKS_MAX)
+		return -ENOSPC;
+
+	/* Install the new block. */
+	t->blocks[n_blocks]->block_func = block_func;
+	t->blocks[n_blocks]->block = block;
+
+	rte_wmb();
+	t->n_blocks = n_blocks + 1;
+
+	return 0;
+}
+
+void
+block_disable(void *block)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+	uint32_t thread_id, i;
+
+	/* Check input params */
+	if (!block)
+		return;
+
+	/* Find the thread that runs this block. */
+	thread_id = block_find(block);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
+
+	t = &threads[thread_id];
+	n_blocks = t->n_blocks;
+
+	for (i = 0; i < n_blocks; i++) {
+		struct block *b = t->blocks[i];
+
+		if (block != b->block)
+			continue;
+
+		if (i < n_blocks - 1) {
+			struct block *block_last = t->blocks[n_blocks - 1];
+
+			t->blocks[i] = block_last;
+		}
+
+		rte_wmb();
+		t->n_blocks = n_blocks - 1;
+
+		rte_wmb();
+		t->blocks[n_blocks - 1] = b;
+
+		return;
+	}
+}
+
 /**
  * Data plane (DP) threads.
  *
+
+
  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
@@ -229,6 +365,13 @@ thread_main(void *arg __rte_unused)
 		/* Pipelines. */
 		for (i = 0; i < t->n_pipelines; i++)
 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
+
+		/* Blocks. */
+		for (i = 0; i < t->n_blocks; i++) {
+			struct block *b = t->blocks[i];
+
+			b->block_func(b->block);
+		}
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 338d480abb..f2e643def5 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -21,6 +21,15 @@ pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 void
 pipeline_disable(struct rte_swx_pipeline *p);
 
+typedef void
+(*block_run_f)(void *block);
+
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id);
+
+void
+block_disable(void *block);
+
 /**
  * Data plane (DP) threads.
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (8 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  2023-01-12 15:49   ` [PATCH V3 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands to enable/disable block execution on data plane
threads.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 68bb93a46c..37250b7072 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3250,6 +3250,134 @@ cmd_pipeline_disable(char **tokens,
 	pipeline_disable(p);
 }
 
+static const char cmd_block_enable_help[] =
+"block type <block_type> instance <block_name> enable thread <thread_id>\n";
+
+static void
+cmd_block_enable(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	block_run_f block_func = NULL;
+	void *block = NULL;
+	uint32_t thread_id;
+	int status;
+
+	if (n_tokens != 8) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "enable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+		return;
+	}
+
+	if (strcmp(tokens[6], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
+
+	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block_func = (block_run_f)rte_swx_ipsec_run;
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	status = block_enable(block_func, block, thread_id);
+	if (status) {
+		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
+		return;
+	}
+}
+
+static const char cmd_block_disable_help[] =
+"block type <block_type> instance <block_name> disable\n";
+
+static void
+cmd_block_disable(char **tokens,
+		  uint32_t n_tokens,
+		  char *out,
+		  size_t out_size,
+		  void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	void *block = NULL;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "disable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	block_disable(block);
+}
+
 static void
 cmd_help(char **tokens,
 	 uint32_t n_tokens,
@@ -3298,6 +3426,8 @@ cmd_help(char **tokens,
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
+			"\tblock enable\n"
+			"\tblock disable\n"
 			);
 		return;
 	}
@@ -3553,6 +3683,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
+		return;
+	}
+
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3812,6 +3954,18 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "block")) {
+		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
+			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
+			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V3 11/11] examples/pipeline: add IPsec example
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (9 preceding siblings ...)
  2023-01-12 15:49   ` [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
@ 2023-01-12 15:49   ` Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 15:49 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add example files to illustrate the pipeline IPsec support.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ipsec.cli    |  57 +++++++
 examples/pipeline/examples/ipsec.io     |  23 +++
 examples/pipeline/examples/ipsec.spec   | 138 +++++++++++++++
 examples/pipeline/examples/ipsec_sa.txt | 216 ++++++++++++++++++++++++
 4 files changed, 434 insertions(+)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt

diff --git a/examples/pipeline/examples/ipsec.cli b/examples/pipeline/examples/ipsec.cli
new file mode 100644
index 0000000000..8cb5bf4239
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.cli
@@ -0,0 +1,57 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 --vdev crypto_aesni_mb0 -- -s ./examples/pipeline/examples/ipsec.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/ipsec.spec /tmp/ipsec.c
+pipeline libbuild /tmp/ipsec.c /tmp/ipsec.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 128 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+cryptodev crypto_aesni_mb0 queues 1 qsize 128
+ring RING0 size 1024 numa 0
+ring RING1 size 1024 numa 0
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/ipsec.so io ./examples/pipeline/examples/ipsec.io numa 0
+
+;
+; List of IPsec devices.
+;
+ipsec IPSEC0 create in RING0 out RING1 cryptodev crypto_aesni_mb0 cryptoq 0 bsz 32 32 32 32 samax 512 numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+//pipeline PIPELINE0 table policy_table add ./examples/pipeline/examples/ipsec_policy_table.txt
+//pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/ipsec_routing_table.txt
+//pipeline PIPELINE0 table nexthop_table add ./examples/pipeline/examples/ipsec_nexthop_table.txt
+//pipeline PIPELINE0 commit
+
+ipsec IPSEC0 sa add ./examples/pipeline/examples/ipsec_sa.txt
+
+;
+; Pipelines and blocks mapping to CPU threads.
+;
+pipeline PIPELINE0 enable thread 1
+block type ipsec instance IPSEC0 enable thread 1
diff --git a/examples/pipeline/examples/ipsec.io b/examples/pipeline/examples/ipsec.io
new file mode 100644
index 0000000000..f5a3fcf961
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.io
@@ -0,0 +1,23 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ring RING1 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ring RING0 bsz 32
diff --git a/examples/pipeline/examples/ipsec.spec b/examples/pipeline/examples/ipsec.spec
new file mode 100644
index 0000000000..09aa831881
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.spec
@@ -0,0 +1,138 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_h {
+	bit<8> ver_ihl
+	bit<8> diffserv
+	bit<16> total_len
+	bit<16> identification
+	bit<16> flags_offset
+	bit<8> ttl
+	bit<8> protocol
+	bit<16> hdr_checksum
+	bit<32> src_addr
+	bit<32> dst_addr
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+struct ipsec_internal_h {
+	bit<32> sa_id
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+header ipsec_internal instanceof ipsec_internal_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port_in
+	bit<32> port_out
+
+	bit<32> src_addr
+	bit<32> dst_addr
+	bit<8> protocol
+	bit<16> src_port
+	bit<16> dst_port
+}
+
+metadata instanceof metadata_t
+
+//
+// Actions
+//
+struct encrypt_args_t {
+	bit<32> sa_id
+}
+
+action encrypt args instanceof encrypt_args_t {
+	//Set the IPsec internal header.
+	validate h.ipsec_internal
+	mov h.ipsec_internal.sa_id t.sa_id
+
+	return
+}
+
+action drop args none {
+	drop
+}
+
+//
+// Tables.
+//
+table policy_table {
+	key {
+		m.src_addr exact
+		m.dst_addr exact
+		m.protocol exact
+		m.src_port exact
+		m.dst_port exact
+	}
+
+	actions {
+		encrypt
+		drop
+	}
+
+	default_action encrypt args sa_id 0
+	size 65536
+}
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port_in
+
+	jmpeq FROM_IPSEC_TO_NET m.port_in 1
+
+FROM_NET_TO_IPSEC : extract h.ethernet
+
+	extract h.ipv4
+	mov m.src_addr h.ipv4.src_addr
+	mov m.dst_addr h.ipv4.dst_addr
+	mov m.protocol h.ipv4.protocol
+
+	extract h.udp
+	mov m.src_port h.udp.src_port
+	mov m.dst_port h.udp.dst_port
+
+	table policy_table
+
+	mov m.port_out 1
+
+	emit h.ipsec_internal
+	emit h.ipv4
+	emit h.udp
+	tx m.port_out
+
+FROM_IPSEC_TO_NET : extract h.ipv4
+
+	validate h.ethernet
+	mov h.ethernet.dst_addr 0xa0b0c0d0e0f0
+	mov h.ethernet.src_addr 0xa1b1c1d1e1f1
+	mov h.ethernet.ethertype 0x0800
+
+	mov m.port_out 0
+
+	emit h.ethernet
+	emit h.ipv4
+	tx m.port_out
+}
diff --git a/examples/pipeline/examples/ipsec_sa.txt b/examples/pipeline/examples/ipsec_sa.txt
new file mode 100644
index 0000000000..37bfd8a9ce
--- /dev/null
+++ b/examples/pipeline/examples/ipsec_sa.txt
@@ -0,0 +1,216 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+// IPsec SA syntax:
+//
+// <sa>
+//    : encrypt <crypto_params> <encap_params>
+//    | decrypt <crypto_params> <encap_params>
+//    ;
+//
+// <crypto_params>
+//    : <cipher> <auth>
+//    | <aead>
+//    ;
+//
+// <cipher>
+//    : cipher <ciher_alg> key <cipher_key>
+//    | cipher <cipher_alg>
+//    ;
+//
+// <auth>
+//    : auth <authentication_alg> key <authentication_key>
+//    | auth <authentication_alg>
+//    ;
+//
+// <aead>
+//    : aead <aead_alg> key <aead_key>
+//    ;
+//
+// <encap_params>
+//    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+//    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+//    | esp spi <spi> transport
+//    ;
+
+//
+// cipher = null, auth = null
+//
+encrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth null esp spi 100 transport
+decrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth null esp spi 100 transport
+
+//
+// aes-gcm-128
+//
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-gcm-192
+//
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-gcm-256
+//
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// aes-ccm-128
+//
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-ccm-192
+//
+// Note: Currently not supported by the "aesni_mb" library.
+//
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-ccm-256
+//
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// chacha20-poly1305
+//
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// cipher = aes-cbc-128, auth = null
+//
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-192, auth = null
+//
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-256, auth = null
+//
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-128, auth = null
+//
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-192, auth = null
+//
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-256, auth = null
+//
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+
+//
+// cipher = null, auth = sha1-hmac
+//
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// cipher = null, auth = sha256-hmac
+//
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+
+//
+// cipher = null, auth = sha384-hmac
+//
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+
+//
+// cipher = null, auth = sha512-hmac
+//
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 00/11] pipeline: add IPsec support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (13 preceding siblings ...)
  2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-12 18:53 ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 01/11] " Cristian Dumitrescu
                     ` (11 more replies)
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
  16 siblings, 12 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev

This patch set introduces a companion block for the SWX pipeline for
IPsec support.

The IPsec block is external to the pipeline, hence it needs to be
explicitly instantiated by the user and connected to a pipeline
instance through the pipeline I/O ports.

Main features:
* IPsec inbound (encrypted input packets -> clear text output packets)
and outbound (clear text input packets -> encrypted output packets)
processing support for tunnel and transport modes.

Interaction of the IPsec block with the pipeline:
* Each IPsec block instance has its own set of Security Associations
(SAs) used to process the input packets. Each SA is identified by its
unique SA ID. The IPsec inbound and outbound SAs share the same ID
space.
* Each input packet is first mapped to one of the existing SAs by
using the SA ID and then processed according to the identified SA. The
SA ID is read from input packet. The SA ID field is typically written
by the pipeline before sending the packet to the IPsec block.

Change log:

V4:
Fixed Doxygen issues.

V3:
Rebased on top of main latest.

V2:
Fixed minor style issues.

Cristian Dumitrescu (11):
  pipeline: add IPsec support
  examples/pipeline: rework memory pool support
  examples/pipeline: streamline ring support
  examples/pipeline: streamline the Ethernet device support
  examples/pipeline: support crypto devices
  examples/pipeline: add CLI command for crypto device
  examples/pipeline: add IPsec CLI commands
  examples/pipeline: rework the thread configuration updates
  examples/pipeline: support blocks other than pipelines
  examples/pipeline: add block enable/disable CLI commands
  examples/pipeline: add IPsec example

 examples/pipeline/cli.c                       |  896 ++++++--
 examples/pipeline/examples/fib.cli            |    4 +-
 examples/pipeline/examples/hash_func.cli      |    4 +-
 examples/pipeline/examples/ipsec.cli          |   57 +
 examples/pipeline/examples/ipsec.io           |   23 +
 examples/pipeline/examples/ipsec.spec         |  138 ++
 examples/pipeline/examples/ipsec_sa.txt       |  216 ++
 examples/pipeline/examples/l2fwd.cli          |    4 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |    4 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |    4 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |    4 +-
 examples/pipeline/examples/learner.cli        |    4 +-
 examples/pipeline/examples/meter.cli          |    4 +-
 examples/pipeline/examples/mirroring.cli      |    4 +-
 examples/pipeline/examples/recirculation.cli  |    4 +-
 examples/pipeline/examples/registers.cli      |    4 +-
 examples/pipeline/examples/selector.cli       |    4 +-
 examples/pipeline/examples/varbit.cli         |    4 +-
 examples/pipeline/examples/vxlan.cli          |    4 +-
 examples/pipeline/examples/vxlan_pcap.cli     |    4 +-
 examples/pipeline/main.c                      |   12 +-
 examples/pipeline/obj.c                       |  359 +---
 examples/pipeline/obj.h                       |  100 +-
 examples/pipeline/thread.c                    |  655 +++---
 examples/pipeline/thread.h                    |   24 +-
 lib/pipeline/meson.build                      |    4 +-
 lib/pipeline/rte_swx_ipsec.c                  | 1821 +++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h                  |  383 ++++
 lib/pipeline/version.map                      |    9 +
 29 files changed, 3739 insertions(+), 1018 deletions(-)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 01/11] pipeline: add IPsec support
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
                     ` (10 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

This block is providing IPsec support to the SWX pipeline. The IPsec
block is external to the pipeline, so it needs to be explicitly
instantiated and connected to a pipeline through the I/O ports.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 lib/pipeline/meson.build     |    4 +-
 lib/pipeline/rte_swx_ipsec.c | 1821 ++++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h |  383 +++++++
 lib/pipeline/version.map     |    9 +
 4 files changed, 2216 insertions(+), 1 deletion(-)
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build
index 3ca98ed194..aa3fd0c2b8 100644
--- a/lib/pipeline/meson.build
+++ b/lib/pipeline/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'rte_pipeline.c',
         'rte_port_in_action.c',
         'rte_table_action.c',
+	'rte_swx_ipsec.c',
         'rte_swx_pipeline.c',
         'rte_swx_pipeline_spec.c',
         'rte_swx_ctl.c',
@@ -19,8 +20,9 @@ headers = files(
         'rte_pipeline.h',
         'rte_port_in_action.h',
         'rte_table_action.h',
+	'rte_swx_ipsec.h',
         'rte_swx_pipeline.h',
         'rte_swx_extern.h',
         'rte_swx_ctl.h',
 )
-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']
+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']
diff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c
new file mode 100644
index 0000000000..b23056e23e
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.c
@@ -0,0 +1,1821 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_cryptodev.h>
+#include <rte_ipsec.h>
+
+#include "rte_swx_ipsec.h"
+
+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
+
+#include <rte_malloc.h>
+
+static void *
+env_calloc(size_t size, size_t alignment, int numa_node)
+{
+	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
+}
+
+static void
+env_free(void *start, size_t size __rte_unused)
+{
+	rte_free(start);
+}
+
+#else
+
+#include <numa.h>
+
+static void *
+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
+{
+	void *start;
+
+	if (numa_available() == -1)
+		return NULL;
+
+	start = numa_alloc_onnode(size, numa_node);
+	if (!start)
+		return NULL;
+
+	memset(start, 0, size);
+	return start;
+}
+
+static void
+env_free(void *start, size_t size)
+{
+	if ((numa_available() == -1) || !start)
+		return;
+
+	numa_free(start, size);
+}
+
+#endif
+
+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
+#endif
+
+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
+ * the mempool size to be at least 1.5 times the size of the mempool cache.
+ */
+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
+
+struct ipsec_sa {
+	struct rte_ipsec_session s;
+	int valid;
+};
+
+struct ipsec_pkts_in {
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_cops;
+};
+
+struct ipsec_pkts_out {
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_pkts;
+};
+
+struct rte_swx_ipsec {
+	/*
+	 * Parameters.
+	 */
+
+	/* IPsec instance name. */
+	char name[RTE_SWX_IPSEC_NAME_SIZE];
+
+	/* Input packet queue. */
+	struct rte_ring *ring_in;
+
+	/* Output packet queue. */
+	struct rte_ring *ring_out;
+
+	/* Crypto device ID. */
+	uint8_t dev_id;
+
+	/* Crypto device queue pair ID. */
+	uint16_t qp_id;
+
+	/* Burst sizes. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/* SA table size. */
+	size_t n_sa_max;
+
+	/*
+	 * Internals.
+	 */
+	/* Crypto device buffer pool for sessions. */
+	struct rte_mempool *mp_session;
+
+	/* Pre-crypto packets. */
+	struct ipsec_pkts_in in;
+
+	/* Post-crypto packets. */
+	struct ipsec_pkts_out out;
+
+	/* Crypto device enqueue threshold. */
+	uint32_t crypto_wr_threshold;
+
+	/* Packets currently under crypto device processing. */
+	uint32_t n_pkts_crypto;
+
+	/* List of free SADB positions. */
+	uint32_t *sa_free_id;
+
+	/* Number of elements in the SADB list of free positions. */
+	size_t n_sa_free_id;
+
+	/* Allocated memory total size in bytes. */
+	size_t total_size;
+
+	/* Flag for registration to the global list of instances. */
+	int registered;
+
+	/*
+	 * Table memory.
+	 */
+	uint8_t memory[] __rte_cache_aligned;
+};
+
+static inline struct ipsec_sa *
+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
+{
+	struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
+
+	return &sadb[sa_id & (ipsec->n_sa_max - 1)];
+}
+
+/* Global list of instances. */
+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ipsec_tailq = {
+	.name = "RTE_SWX_IPSEC",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
+
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name ||
+	    !name[0] ||
+	    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
+		return NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ipsec;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ipsec_register(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ipsec;
+	TAILQ_INSERT_TAIL(ipsec_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ipsec_unregister(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		if (te->data == (void *)ipsec) {
+			TAILQ_REMOVE(ipsec_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
+
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
+{
+	size_t i;
+
+	if (!ipsec)
+		return;
+
+	/* Remove the current instance from the global list. */
+	if (ipsec->registered)
+		ipsec_unregister(ipsec);
+
+	/* SADB. */
+	for (i = 0; i < ipsec->n_sa_max; i++) {
+		struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
+
+		if (!sa->valid)
+			continue;
+
+		/* SA session. */
+		ipsec_session_free(ipsec, &sa->s);
+	}
+
+	/* Crypto device buffer pools. */
+	rte_mempool_free(ipsec->mp_session);
+
+	/* IPsec object memory. */
+	env_free(ipsec, ipsec->total_size);
+}
+
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node)
+{
+	char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
+	struct rte_swx_ipsec *ipsec = NULL;
+	struct rte_ring *ring_in, *ring_out;
+	struct rte_cryptodev_info dev_info;
+	size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
+	uint32_t dev_session_size;
+	int dev_id, status = 0;
+
+	/* Check input parameters. */
+	if (!ipsec_out ||
+	    !name ||
+	    !name[0] ||
+	    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
+	    !params ||
+	    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    !params->n_sa_max) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_in = rte_ring_lookup(params->ring_in_name);
+	if (!ring_in) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_out = rte_ring_lookup(params->ring_out_name);
+	if (!ring_out) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
+	if (dev_id == -1) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Memory allocation. */
+	n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
+
+	sadb_offset = sizeof(struct rte_swx_ipsec);
+	sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
+
+	sa_free_id_offset = sadb_offset + sadb_size;
+	sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
+
+	total_size = sa_free_id_offset + sa_free_id_size;
+	ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
+	if (!ipsec) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Initialization. */
+	strcpy(ipsec->name, name);
+	ipsec->ring_in = ring_in;
+	ipsec->ring_out = ring_out;
+	ipsec->dev_id = (uint8_t)dev_id;
+	ipsec->qp_id = params->crypto_dev_queue_pair_id;
+	memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
+	ipsec->n_sa_max = n_sa_max;
+
+	ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
+
+	ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
+	for (i = 0; i < n_sa_max; i++)
+		ipsec->sa_free_id[i] = n_sa_max - 1 - i;
+	ipsec->n_sa_free_id = n_sa_max;
+
+	ipsec->total_size = total_size;
+
+	/* Crypto device memory pools. */
+	dev_session_size = rte_cryptodev_sym_get_private_session_size((uint8_t)dev_id);
+
+	snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
+	ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		dev_session_size, /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool element private data size */
+		numa_node);
+	if (!ipsec->mp_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Add the current instance to the global list. */
+	status = ipsec_register(ipsec);
+	if (status)
+		goto error;
+
+	ipsec->registered = 1;
+
+	*ipsec_out = ipsec;
+	return 0;
+
+error:
+	rte_swx_ipsec_free(ipsec);
+	return status;
+}
+
+static inline int
+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
+{
+	struct ipsec_sa *sa;
+	struct rte_ipsec_group *g;
+	int n_groups, n_pkts_in_group, i;
+
+	sa = ipsec->in.sa[0];
+
+	g = &ipsec->in.groups[0];
+	g->id.ptr = sa;
+	g->m = &ipsec->in.pkts[0];
+	n_pkts_in_group = 1;
+	n_groups = 1;
+
+	for (i = 1; i < n_pkts; i++) {
+		struct ipsec_sa *sa_new = ipsec->in.sa[i];
+
+		/* Same SA => Add the current pkt to the same group. */
+		if (sa_new == sa) {
+			n_pkts_in_group++;
+			continue;
+		}
+
+		/* Different SA => Close the current group & add the current pkt to a new group. */
+		g->cnt = n_pkts_in_group;
+		sa = sa_new;
+
+		g++;
+		g->id.ptr = sa;
+		g->m = &ipsec->in.pkts[i];
+		n_pkts_in_group = 1;
+		n_groups++;
+	}
+
+	/* Close the last group. */
+	g->cnt = n_pkts_in_group;
+
+	return n_groups;
+}
+
+static inline void
+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
+{
+	struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
+	struct rte_crypto_op **src = ipsec->in.group_cops;
+
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
+	uint32_t n_dst = ipsec->in.n_cops;
+	uint32_t n_dst_max = ipsec->bsz.crypto_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_cops;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
+	 * instead of moving the SRC to DST first and then using DST.
+	 */
+	if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
+		uint16_t n_ok;
+
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
+		ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
+
+		for (i = n_ok; i < n_src; i++) {
+			struct rte_crypto_op *cop = src[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		return;
+	}
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
+		n_pkts_crypto += n_ok;
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_crypto_op *cop = dst0[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	ipsec->n_pkts_crypto = n_pkts_crypto;
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->in.n_cops = n_dst;
+}
+
+/**
+ * Packet buffer anatomy:
+ *
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | Offset   | Size    | Description                                                              |
+ * | (Byte #) | (Bytes) |                                                                          |
+ * +==========+=========+==========================================================================+
+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |
+ * |          |         | The buf_addr field points to the start of the packet section.            |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 256      |         | Packet section.                                                          |
+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |
+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |
+ * +----------+---------+--------------------------------------------------------------------------+
+ */
+struct ipsec_mbuf {
+	struct ipsec_sa *sa;
+	struct rte_crypto_op cop;
+	struct rte_crypto_sym_op sym_cop;
+	uint8_t buffer[32]; /* The crypto IV is placed here. */
+};
+
+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
+
+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
+
+static inline void
+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
+{
+	int n_pkts, n_groups, i;
+
+	/* Read packets from the input ring. */
+	n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
+					   (void **)ipsec->in.pkts,
+					   ipsec->bsz.ring_rd,
+					   NULL);
+	if (!n_pkts)
+		return;
+
+	/* Get the SA for each packet. */
+	for (i = 0; i < n_pkts; i++) {
+		struct rte_mbuf *m = ipsec->in.pkts[i];
+		struct rte_swx_ipsec_input_packet_metadata *meta;
+		struct rte_ipv4_hdr *ipv4_hdr;
+		uint32_t sa_id;
+
+		meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
+
+		/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
+		sa_id = ntohl(meta->sa_id);
+
+		/* Consume the IPsec meta-data. */
+		m->data_off += META_LENGTH;
+		m->data_len -= META_LENGTH;
+		m->pkt_len -= META_LENGTH;
+
+		/* Set the fields required by the IPsec library. */
+		m->l2_len = 0;
+		m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
+			sizeof(struct rte_ipv4_hdr) :
+			sizeof(struct rte_ipv6_hdr);
+
+		/* Get the SA. */
+		ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
+	}
+
+	/* Group packets that share the same SA. */
+	n_groups = ipsec_sa_group(ipsec, n_pkts);
+
+	/* Write each group of packets sharing the same SA to the crypto device. */
+	for (i = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->in.groups[i];
+		struct ipsec_sa *sa = g->id.ptr;
+		struct rte_ipsec_session *s = &sa->s;
+		uint32_t j;
+		uint16_t n_pkts_ok;
+
+		/* Prepare the crypto ops for the current group. */
+		for (j = 0; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+			struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
+
+			priv->sa = sa;
+			ipsec->in.group_cops[j] = &priv->cop;
+		}
+
+		n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the crypto ops of the current group to the crypto device. */
+		ipsec_crypto_enqueue(ipsec, n_pkts_ok);
+	}
+}
+
+static inline void
+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
+{
+	struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
+	struct rte_mbuf **src = g->m;
+
+	uint32_t n_dst = ipsec->out.n_pkts;
+	uint32_t n_dst_max = ipsec->bsz.ring_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_pkts;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_mbuf *m = dst[i];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->out.n_pkts = n_dst;
+}
+
+static inline void
+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
+{
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, ng, i;
+
+	/* Read the crypto ops from the crypto device. */
+	if (!n_pkts_crypto)
+		return;
+
+	n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
+					     ipsec->qp_id,
+					     ipsec->out.cops,
+					     ipsec->bsz.crypto_rd);
+	if (!n_pkts)
+		return;
+
+	ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
+
+	/* Group packets that share the same SA. */
+	ng = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
+					      ipsec->out.group_pkts,
+					      ipsec->out.groups,
+					      n_pkts);
+
+	/* Perform post-crypto IPsec processing for each group of packets that share the same SA.
+	 * Write each group of packets to the output ring.
+	 */
+	for (i = 0, n_pkts = 0; i < ng; i++) {
+		struct rte_ipsec_group *g = &ipsec->out.groups[i];
+		struct rte_ipsec_session *s = g->id.ptr;
+		uint32_t n_pkts_ok, j;
+
+		/* Perform post-crypto IPsec processing for the current group. */
+		n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the packets of the current group to the output ring. */
+		ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
+	}
+}
+
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
+{
+	rte_swx_ipsec_pre_crypto(ipsec);
+	rte_swx_ipsec_post_crypto(ipsec);
+}
+
+/**
+ * IPsec Control Plane API
+ */
+struct cipher_alg {
+	const char *name;
+	enum rte_crypto_cipher_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t key_size;
+};
+
+struct auth_alg {
+	const char *name;
+	enum rte_crypto_auth_algorithm alg;
+	uint32_t iv_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+};
+
+struct aead_alg {
+	const char *name;
+	enum rte_crypto_aead_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+	uint32_t aad_size;
+};
+
+static struct cipher_alg cipher_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_CIPHER_NULL,
+		.iv_size = 0,
+		.block_size = 4,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "aes-cbc-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 16,
+	},
+
+	[2] = {
+		.name = "aes-cbc-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 24,
+	},
+
+	[3] = {
+		.name = "aes-cbc-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 32,
+	},
+
+	[4] = {
+		.name = "aes-ctr-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+	},
+
+	[5] = {
+		.name = "aes-ctr-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 28,
+	},
+
+	[6] = {
+		.name = "aes-ctr-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 36,
+	},
+
+	[7] = {
+		.name = "3des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 24,
+	},
+
+	[8] = {
+		.name = "des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 8,
+	},
+};
+
+static struct auth_alg auth_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_AUTH_NULL,
+		.iv_size = 0,
+		.digest_size = 0,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "sha1-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 20,
+	},
+
+	[2] = {
+		.name = "sha256-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
+		.iv_size = 0,
+		.digest_size = 16,
+		.key_size = 32,
+	},
+
+	[3] = {
+		.name = "sha384-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
+		.iv_size = 0,
+		.digest_size = 24,
+		.key_size = 48,
+	},
+
+	[4] = {
+		.name = "sha512-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
+		.iv_size = 0,
+		.digest_size = 32,
+		.key_size = 64,
+	},
+
+	[5] = {
+		.name = "aes-gmac",
+		.alg = RTE_CRYPTO_AUTH_AES_GMAC,
+		.iv_size = 8,
+		.digest_size = 16,
+		.key_size = 20,
+	},
+
+	[6] = {
+		.name = "aes-xcbc-mac-96",
+		.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 16,
+	},
+};
+
+static struct aead_alg aead_algs[] = {
+	[0] = {
+		.name = "aes-gcm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[1] = {
+		.name = "aes-gcm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[2] = {
+		.name = "aes-gcm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[3] = {
+		.name = "aes-ccm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[4] = {
+		.name = "aes-ccm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[5] = {
+		.name = "aes-ccm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[6] = {
+		.name = "chacha20-poly1305",
+		.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
+		.iv_size = 12,
+		.block_size = 64,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+};
+
+static struct cipher_alg *
+cipher_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct cipher_alg *
+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static int
+char_to_hex(char c, uint8_t *val)
+{
+	if (c >= '0' && c <= '9') {
+		*val = c - '0';
+		return 0;
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		*val = c - 'A' + 10;
+		return 0;
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		*val = c - 'a' + 10;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
+{
+	uint32_t i;
+
+	/* Check input arguments. */
+	if (!src || !src[0] || !dst || !n_dst_bytes)
+		return -EINVAL;
+
+	/* Skip any leading "0x" or "0X" in the src string. */
+	if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
+		src += 2;
+
+	/* Convert each group of two hex characters in the src string to one byte in dst array. */
+	for (i = 0; i < n_dst_bytes; i++) {
+		uint8_t a, b;
+		int status;
+
+		status = char_to_hex(*src, &a);
+		if (status)
+			return status;
+		src++;
+
+		status = char_to_hex(*src, &b);
+		if (status)
+			return status;
+		src++;
+
+		dst[i] = a * 16 + b;
+	}
+
+	/* Check for the end of the src string. */
+	if (*src)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+token_is_comment(const char *token)
+{
+	if ((token[0] == '#') ||
+	    (token[0] == ';') ||
+	    ((token[0] == '/') && (token[1] == '/')))
+		return 1; /* TRUE. */
+
+	return 0; /* FALSE. */
+}
+
+#define MAX_TOKENS 64
+
+#define CHECK(condition, msg)          \
+do {                                   \
+	if (!(condition)) {            \
+		if (errmsg)            \
+			*errmsg = msg; \
+		goto error;            \
+	}                              \
+} while (0)
+
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg)
+{
+	char *token_array[MAX_TOKENS], **t;
+	struct rte_swx_ipsec_sa_params *p = NULL;
+	char *s0 = NULL, *s;
+	uint32_t n_tokens = 0;
+	int blank_or_comment = 0;
+
+	/* Check input arguments. */
+	CHECK(string && string[0], "NULL input");
+
+	/* Memory allocation. */
+	s0 = strdup(string);
+	p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
+	CHECK(s0 && p, "Not enough memory");
+
+	/* Parse the string into tokens. */
+	for (s = s0; ; ) {
+		char *token;
+
+		token = strtok_r(s, " \f\n\r\t\v", &s);
+		if (!token || token_is_comment(token))
+			break;
+
+		CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
+
+		token_array[n_tokens] = token;
+		n_tokens++;
+	}
+
+	t = token_array;
+	if (!n_tokens) {
+		blank_or_comment = 1;
+		goto error;
+	}
+
+	/*
+	 * Crypto operation.
+	 */
+	if (!strcmp(t[0], "encrypt"))
+		p->encrypt = 1;
+	else if (!strcmp(t[0], "decrypt"))
+		p->encrypt = 0;
+	else
+		CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
+
+	t++;
+	n_tokens--;
+
+	/*
+	 * Crypto parameters.
+	 */
+	CHECK(n_tokens >= 2, "Not enough tokens");
+
+	if (!strcmp(t[0], "cipher")) {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t key_size;
+
+		p->crypto.is_aead = 0;
+
+		/* cipher. */
+		cipher_alg = cipher_alg_find(t[1]);
+		CHECK(cipher_alg, "Unsupported cipher algorithm");
+
+		key_size = cipher_alg->key_size;
+		p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
+		p->crypto.cipher_auth.cipher.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
+				"Cipher algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
+			CHECK(!status, "Cipher key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+
+		/* authentication. */
+		CHECK(n_tokens >= 2, "Not enough tokens");
+		CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
+
+		auth_alg = auth_alg_find(t[1]);
+		CHECK(auth_alg, "Unsupported authentication algorithm");
+
+		key_size = auth_alg->key_size;
+		p->crypto.cipher_auth.auth.alg = auth_alg->alg;
+		p->crypto.cipher_auth.auth.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
+				"Authentication algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
+			CHECK(!status, "Authentication key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+	} else if (!strcmp(t[0], "aead")) {
+		struct aead_alg *alg;
+		uint32_t key_size;
+		int status;
+
+		p->crypto.is_aead = 1;
+
+		CHECK(n_tokens >= 4, "Not enough tokens");
+		alg = aead_alg_find(t[1]);
+		CHECK(alg, "Unsupported AEAD algorithm");
+
+		key_size = alg->key_size;
+		p->crypto.aead.alg = alg->alg;
+		p->crypto.aead.key_size = key_size;
+
+		CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
+		CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
+			"AEAD algorithm key too big");
+
+		status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
+		CHECK(!status, "AEAD key invalid format");
+
+		t += 4;
+		n_tokens -= 4;
+	} else
+		CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
+
+	/*
+	 * Packet ecapsulation parameters.
+	 */
+	CHECK(n_tokens >= 4, "Not enough tokens");
+	CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
+	CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
+
+	p->encap.esp.spi = strtoul(t[2], &t[2], 0);
+	CHECK(!t[2][0], "ESP SPI field invalid format");
+
+	t += 3;
+	n_tokens -= 3;
+
+	if (!strcmp(t[0], "tunnel")) {
+		p->encap.tunnel_mode = 1;
+
+		CHECK(n_tokens >= 6, "Not enough tokens");
+
+		if (!strcmp(t[1], "ipv4")) {
+			uint32_t addr;
+
+			p->encap.tunnel_ipv4 = 1;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			addr = strtoul(t[3], &t[3], 0);
+			CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
+			p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			addr = strtoul(t[5], &t[5], 0);
+			CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
+			p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
+
+			t += 6;
+			n_tokens -= 6;
+		} else if (!strcmp(t[1], "ipv6")) {
+			int status;
+
+			p->encap.tunnel_ipv4 = 0;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			status = hex_string_parse(t[3],
+						  p->encap.tunnel.ipv6.src_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 source address invalid format");
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			status = hex_string_parse(t[5],
+						  p->encap.tunnel.ipv6.dst_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 destination address invalid format");
+
+			t += 6;
+			n_tokens -= 6;
+		} else
+			CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
+	} else if (!strcmp(t[0], "transport")) {
+		p->encap.tunnel_mode = 0;
+
+		t++;
+		n_tokens--;
+	} else
+		CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
+
+	/*
+	 * Any other parameters.
+	 */
+	CHECK(!n_tokens, "Unexpected trailing tokens");
+
+	free(s0);
+	return p;
+
+error:
+	free(p);
+	free(s0);
+	if (is_blank_or_comment)
+		*is_blank_or_comment = blank_or_comment;
+	return NULL;
+}
+
+static void
+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv4_hdr ipv4_hdr = {
+		.version_ihl = 0x45,
+		.type_of_service = 0,
+		.total_length = 0, /* Cannot be pre-computed. */
+		.packet_id = 0,
+		.fragment_offset = 0,
+		.time_to_live = 64,
+		.next_proto_id = IPPROTO_ESP,
+		.hdr_checksum = 0, /* Cannot be pre-computed. */
+		.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
+		.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
+	};
+
+	memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
+}
+
+static void
+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv6_hdr ipv6_hdr = {
+		.vtc_flow = 0x60000000,
+		.payload_len = 0, /* Cannot be pre-computed. */
+		.proto = IPPROTO_ESP,
+		.hop_limits = 64,
+		.src_addr = {0},
+		.dst_addr = {0},
+	};
+
+	memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
+	memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
+	memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
+}
+
+/* IPsec library SA parameters. */
+static struct rte_crypto_sym_xform *
+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_crypto_sym_xform *xform,
+		uint32_t *salt_out)
+{
+	if (p->crypto.is_aead) {
+		struct aead_alg *alg;
+		uint32_t key_size, salt, iv_length;
+
+		alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
+		if (!alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		key_size = p->crypto.aead.key_size - 4;
+		memcpy(&salt, &p->crypto.aead.key[key_size], 4);
+
+		/* IV length. */
+		iv_length = 12;
+		if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
+			iv_length = 11;
+
+		/* xform. */
+		xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform[0].aead.op = p->encrypt ?
+			RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform[0].aead.algo = p->crypto.aead.alg;
+		xform[0].aead.key.data = p->crypto.aead.key;
+		xform[0].aead.key.length = key_size;
+		xform[0].aead.iv.offset = IV_OFFSET;
+		xform[0].aead.iv.length = iv_length;
+		xform[0].aead.digest_length = alg->digest_size;
+		xform[0].aead.aad_length = alg->aad_size;
+		xform[0].next = NULL;
+
+		*salt_out = salt;
+		return &xform[0];
+	} else {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
+
+		cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
+						   p->crypto.cipher_auth.cipher.key_size);
+		if (!cipher_alg)
+			return NULL;
+
+		auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
+					       p->crypto.cipher_auth.auth.key_size);
+		if (!auth_alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
+		auth_key_size = p->crypto.cipher_auth.auth.key_size;
+
+		switch (p->crypto.cipher_auth.cipher.alg) {
+		case RTE_CRYPTO_CIPHER_AES_CBC:
+		case RTE_CRYPTO_CIPHER_3DES_CBC:
+			salt = (uint32_t)rand();
+			break;
+
+		case RTE_CRYPTO_CIPHER_AES_CTR:
+			cipher_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
+			break;
+
+		default:
+			salt = 0;
+		}
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			auth_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
+		}
+
+		/* IV length. */
+		auth_iv_length = cipher_alg->iv_size;
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
+			auth_iv_length = 12;
+
+		/* xform. */
+		if (p->encrypt) {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[0].cipher.key.length = cipher_key_size;
+			xform[0].cipher.iv.offset = IV_OFFSET;
+			xform[0].cipher.iv.length = cipher_alg->iv_size;
+			xform[0].cipher.dataunit_len = 0;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+			xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[1].auth.key.length = auth_key_size;
+			xform[1].auth.iv.offset = IV_OFFSET;
+			xform[1].auth.iv.length = auth_iv_length;
+			xform[1].auth.digest_length = auth_alg->digest_size;
+			xform[1].next = NULL;
+		} else {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+			xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[0].auth.key.length = auth_key_size;
+			xform[0].auth.iv.offset = IV_OFFSET;
+			xform[0].auth.iv.length = auth_iv_length;
+			xform[0].auth.digest_length = auth_alg->digest_size;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[1].cipher.key.length = cipher_key_size;
+			xform[1].cipher.iv.offset = IV_OFFSET;
+			xform[1].cipher.iv.length = cipher_alg->iv_size;
+			xform[1].cipher.dataunit_len = 0;
+			xform[1].next = NULL;
+		}
+
+		*salt_out = salt;
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			if (p->encrypt)
+				return &xform[1];
+
+			xform[0].next = NULL;
+			return &xform[0];
+		}
+
+		return &xform[0];
+	}
+}
+
+static void
+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_security_ipsec_xform *ipsec_xform,
+		uint32_t salt)
+{
+	ipsec_xform->spi = p->encap.esp.spi;
+
+	ipsec_xform->salt = salt;
+
+	ipsec_xform->options.esn = 0;
+	ipsec_xform->options.udp_encap = 0;
+	ipsec_xform->options.copy_dscp = 1;
+	ipsec_xform->options.copy_flabel = 0;
+	ipsec_xform->options.copy_df = 0;
+	ipsec_xform->options.dec_ttl = 0;
+	ipsec_xform->options.ecn = 1;
+	ipsec_xform->options.stats = 0;
+	ipsec_xform->options.iv_gen_disable = 0;
+	ipsec_xform->options.tunnel_hdr_verify = 0;
+	ipsec_xform->options.udp_ports_verify = 0;
+	ipsec_xform->options.ip_csum_enable = 0;
+	ipsec_xform->options.l4_csum_enable = 0;
+	ipsec_xform->options.ip_reassembly_en = 0;
+	ipsec_xform->options.reserved_opts = 0;
+
+	ipsec_xform->direction = p->encrypt ?
+		RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
+		RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+
+	ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+
+	ipsec_xform->mode = p->encap.tunnel_mode ?
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
+
+	ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
+		RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
+		RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
+			ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
+			ipsec_xform->tunnel.ipv4.dscp = 0;
+			ipsec_xform->tunnel.ipv4.df = 0;
+			ipsec_xform->tunnel.ipv4.ttl = 64;
+		} else {
+			ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
+			ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
+			ipsec_xform->tunnel.ipv6.dscp = 0;
+			ipsec_xform->tunnel.ipv6.flabel = 0;
+			ipsec_xform->tunnel.ipv6.hlimit = 64;
+		}
+	}
+
+	ipsec_xform->life.packets_soft_limit = 0;
+	ipsec_xform->life.bytes_soft_limit = 0;
+	ipsec_xform->life.packets_hard_limit = 0;
+	ipsec_xform->life.bytes_hard_limit = 0;
+
+	ipsec_xform->replay_win_sz = 0;
+
+	ipsec_xform->esn.value = 0;
+
+	ipsec_xform->udp.dport = 0;
+	ipsec_xform->udp.sport = 0;
+}
+
+static int
+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
+		 struct rte_ipsec_sa_prm *sa_prm,
+		 struct rte_ipv4_hdr *ipv4_hdr,
+		 struct rte_ipv6_hdr *ipv6_hdr,
+		 struct rte_crypto_sym_xform *crypto_xform)
+{
+	uint32_t salt;
+
+	memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
+
+	sa_prm->userdata = 0; /* Not used. */
+
+	sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
+
+	/*
+	 * crypto_xform.
+	 */
+	sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
+	if (!sa_prm->crypto_xform)
+		return -EINVAL;
+
+	/*
+	 * ipsec_xform.
+	 */
+	ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
+
+	/*
+	 * tunnel / transport.
+	 *
+	 * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
+	 * the input packet type should be added to the SA configuration parameters.
+	 */
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv4_hdr;
+		} else {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv6_hdr;
+		}
+	} else {
+		sa_prm->trs.proto = IPPROTO_IPIP; /* IPv4. */
+	}
+
+	return 0;
+}
+
+static int
+ipsec_session_create(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *p,
+		     struct rte_ipsec_session *s)
+{
+	struct rte_ipv4_hdr ipv4_hdr;
+	struct rte_ipv6_hdr ipv6_hdr;
+	struct rte_crypto_sym_xform crypto_xform[2];
+	struct rte_ipsec_sa_prm sa_prm;
+	struct rte_ipsec_sa *sa = NULL;
+	struct rte_cryptodev_sym_session *crypto_session = NULL;
+	int sa_size;
+	int sa_valid = 0, status = 0;
+
+	tunnel_ipv4_header_set(&ipv4_hdr, p);
+	tunnel_ipv6_header_set(&ipv6_hdr, p);
+
+	/* IPsec library SA setup. */
+	status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
+	if (status)
+		goto error;
+
+	sa_size = rte_ipsec_sa_size(&sa_prm);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa = calloc(1, sa_size);
+	if (!sa) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa_valid = 1;
+
+	/* Cryptodev library session setup. */
+	crypto_session = rte_cryptodev_sym_session_create(ipsec->dev_id,
+							  sa_prm.crypto_xform,
+							  ipsec->mp_session);
+	if (!crypto_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* IPsec library session setup. */
+	s->sa = sa;
+	s->type = RTE_SECURITY_ACTION_TYPE_NONE;
+	s->crypto.ses = crypto_session;
+	s->crypto.dev_id = ipsec->dev_id;
+	s->pkt_func.prepare.async = NULL;
+	s->pkt_func.process = NULL;
+
+	return rte_ipsec_session_prepare(s);
+
+error:
+	/* sa. */
+	if (sa_valid)
+		rte_ipsec_sa_fini(sa);
+
+	free(sa);
+
+	/* crypto_session. */
+	if (crypto_session)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, crypto_session);
+
+	/* s. */
+	memset(s, 0, sizeof(*s));
+
+	return status;
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec,
+		   struct rte_ipsec_session *s)
+{
+	if (!s)
+		return;
+
+	/* IPsec library SA. */
+	if (s->sa)
+		rte_ipsec_sa_fini(s->sa);
+	free(s->sa);
+
+	/* Cryptodev library session. */
+	if (s->crypto.ses)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, s->crypto.ses);
+
+	/* IPsec library session. */
+	memset(s, 0, sizeof(*s));
+}
+
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *id)
+{
+	struct ipsec_sa *sa;
+	uint32_t sa_id;
+	int status;
+
+	/* Check the input parameters. */
+	if (!ipsec || !sa_params || !id)
+		return -EINVAL;
+
+	/* Allocate a free SADB entry. */
+	if (!ipsec->n_sa_free_id)
+		return -ENOSPC;
+
+	sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
+	ipsec->n_sa_free_id--;
+
+	/* Acquire the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	status = ipsec_session_create(ipsec, sa_params, &sa->s);
+	if (status) {
+		/* Free the allocated SADB entry. */
+		ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+		ipsec->n_sa_free_id++;
+
+		return status;
+	}
+
+	/* Validate the new SA. */
+	sa->valid = 1;
+	*id = sa_id;
+
+	return 0;
+}
+
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id)
+{
+	struct ipsec_sa *sa;
+
+	/* Check the input parameters. */
+	if (!ipsec || (sa_id >= ipsec->n_sa_max))
+		return;
+
+	/* Release the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	ipsec_session_free(ipsec, &sa->s);
+
+	/* Free the SADB entry. */
+	ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+	ipsec->n_sa_free_id++;
+
+	/* Invalidate the SA. */
+	sa->valid = 0;
+}
diff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h
new file mode 100644
index 0000000000..ebfb7ea5ea
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__
+#define __INCLUDE_RTE_SWX_IPSEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE SWX Internet Protocol Security (IPsec)
+ *
+ * The IPsec block is a companion block for the SWX pipeline used to provide IPsec support to the
+ * pipeline. The block is external to the pipeline, hence it needs to be explicitly instantiated by
+ * the user and connected to a pipeline instance through the pipeline I/O ports.
+ *
+ * Main features:
+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text
+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.
+ *
+ * Security Association (SA):
+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is
+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.
+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then
+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field
+ *   is typically written by the pipeline before sending the packet to the IPsec block.
+ *
+ * Packet format:
+ * - IPsec block input packet (i.e. pipeline output packet):
+ *	- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.
+ *	- IPv4 header.
+ *	- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.
+ * - IPsec block output packet (i.e. pipeline input packet):
+ *	- IPv4 header.
+ *	- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.
+ *
+ * SA update procedure:
+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().
+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().
+ * - To update an existing SA, the control plane has to follow the following steps:
+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can
+ *      fail, for example when the SA table is full.
+ *   2. Wait until no more packets are using the old SA.
+ *   3. Delete the old SA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <rte_compat.h>
+#include <rte_crypto_sym.h>
+
+/**
+ * IPsec Setup API
+ */
+
+/** IPsec instance opaque data structure. */
+struct rte_swx_ipsec;
+
+/** Name size. */
+#ifndef RTE_SWX_IPSEC_NAME_SIZE
+#define RTE_SWX_IPSEC_NAME_SIZE 64
+#endif
+
+/** Maximum burst size. */
+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX
+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256
+#endif
+
+/** IPsec burst sizes. */
+struct rte_swx_ipsec_burst_size {
+	/** Input ring read burst size. */
+	uint32_t ring_rd;
+
+	/** Output ring write burst size. */
+	uint32_t ring_wr;
+
+	/** Crypto device request queue write burst size. */
+	uint32_t crypto_wr;
+
+	/** Crypto device response queue read burst size. */
+	uint32_t crypto_rd;
+};
+
+/**
+ * IPsec instance configuration parameters
+ *
+ */
+struct rte_swx_ipsec_params {
+	/** Input packet queue. */
+	const char *ring_in_name;
+
+	/** Output packet queue.  */
+	const char *ring_out_name;
+
+	/** Crypto device name. */
+	const char *crypto_dev_name;
+
+	/** Crypto device queue pair ID. */
+	uint32_t crypto_dev_queue_pair_id;
+
+	/** Burst size. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/** Maximum number of SAs. */
+	uint32_t n_sa_max;
+};
+
+/**
+ * IPsec input packet meta-data
+ *
+ */
+struct rte_swx_ipsec_input_packet_metadata {
+	/* SA ID. */
+	uint32_t sa_id;
+};
+
+/**
+ * IPsec instance find
+ *
+ * @param[in] name
+ *   IPsec instance name.
+ * @return
+ *   Valid IPsec instance handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name);
+
+/**
+ * IPsec instance create
+ *
+ * @param[out] ipsec
+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this
+ *   function returns successfully.
+ * @param[in] name
+ *   IPsec instance unique name.
+ * @param[in] params
+ *   IPsec instance configuration parameters.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node);
+
+/**
+ * IPsec instance free
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);
+
+/**
+ * IPsec Data Plane API
+ */
+
+/**
+ * IPsec instance run
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);
+
+/*
+ * IPsec Control Plane API
+ */
+
+/** Maximum key size in bytes. */
+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64
+
+/** IPsec SA crypto cipher parameters. */
+struct rte_swx_ipsec_sa_cipher_params {
+	/** Cipher algorithm. */
+	enum rte_crypto_cipher_algorithm alg;
+
+	/** Cipher key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Cipher key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto authentication parameters. */
+struct rte_swx_ipsec_sa_authentication_params {
+	/** Authentication algorithm. */
+	enum rte_crypto_auth_algorithm alg;
+
+	/** Authentication key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Authentication key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */
+struct rte_swx_ipsec_sa_aead_params {
+	/** AEAD algorithm. */
+	enum rte_crypto_aead_algorithm alg;
+
+	/** AEAD key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** AEAD key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec protocol encapsulation parameters. */
+struct rte_swx_ipsec_sa_encap_params {
+	/** Encapsulating Security Payload (ESP) header. */
+	struct {
+		/** Security Parameters Index (SPI) field. */
+		uint32_t spi;
+	} esp;
+
+	/** Tunnel mode when non-zero, transport mode when zero. */
+	int tunnel_mode;
+
+	/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */
+	int tunnel_ipv4;
+
+	/** Tunnel parameters. Valid for tunnel mode only. */
+	union {
+		/** IPv4 header. */
+		struct {
+			/** Source address. */
+			struct in_addr src_addr;
+
+			/** Destination address. */
+			struct in_addr dst_addr;
+		} ipv4;
+
+		/** IPv6 header. */
+		struct {
+			/** Source address. */
+			struct in6_addr src_addr;
+
+			/** Destination address. */
+			struct in6_addr dst_addr;
+		} ipv6;
+	} tunnel;
+};
+
+/** IPsec Security Association (SA) parameters. */
+struct rte_swx_ipsec_sa_params {
+	/** Crypto operation: encrypt when non-zero, decrypt when zero. */
+	int encrypt;
+
+	/** Crypto operation parameters. */
+	struct {
+		RTE_STD_C11
+		union {
+			struct {
+				/** Crypto cipher operation parameters. */
+				struct rte_swx_ipsec_sa_cipher_params cipher;
+
+				/** Crypto authentication operation parameters. */
+				struct rte_swx_ipsec_sa_authentication_params auth;
+			} cipher_auth;
+
+			/** Crypto AEAD operation parameters. */
+			struct rte_swx_ipsec_sa_aead_params aead;
+		};
+
+		/** Non-zero for AEAD, zero for cipher & authentication. */
+		int is_aead;
+	} crypto;
+
+	/** Packet encasulation parameters. */
+	struct rte_swx_ipsec_sa_encap_params encap;
+};
+
+/**
+ * IPsec SA add
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_params
+ *   SA parameters.
+ * @param[out] sa_id
+ *   On success, the SA ID.
+ * @return
+ *   0 on success or error code otherwise.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *sa_id);
+
+/**
+ * IPsec SA delete
+ *
+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used
+ * by the Data Plane.
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_id
+ *   The SA ID.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id);
+
+/**
+ * IPsec SA read from string
+ *
+ * IPsec SA syntax:
+ *
+ * \<sa>
+ *    : encrypt \<crypto_params> \<encap_params>
+ *    | decrypt \<crypto_params> \<encap_params>
+ *    ;
+ *
+ * \<crypto_params>
+ *    : \<cipher> \<auth>
+ *    | \<aead>
+ *    ;
+ *
+ * \<cipher>
+ *    : cipher \<ciher_alg> key \<cipher_key>
+ *    | cipher \<cipher_alg>
+ *    ;
+ *
+ * \<auth>
+ *    : auth \<authentication_alg> key \<authentication_key>
+ *    | auth \<authentication_alg>
+ *    ;
+ *
+ * \<aead>
+ *    : aead \<aead_alg> key \<aead_key>
+ *    ;
+ *
+ * \<encap_params>
+ *    : esp spi \<spi> tunnel ipv4 srcaddr \<ipv4_src_addr> dstaddr \<ipv4_dst_addr>
+ *    | esp spi \<spi> tunnel ipv6 srcaddr \<ipv6_src_addr> dstaddr \<ipv6_dst_addr>
+ *    | esp spi \<spi> transport
+ *    ;
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] string
+ *   String containing the SA.
+ * @param[in,out] is_blank_or_comment
+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when
+ *   *string* contains a blank or comment line and to zero otherwise.
+ * @param[in,out] errmsg
+ *   On error, when its input value is not NULL, this argument points to a string with details on
+ *   the detected error.
+ * @return
+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.
+ */
+__rte_experimental
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 184f45a6b7..58e34459c3 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -155,4 +155,13 @@ EXPERIMENTAL {
 	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
+
+	#added in 23.03
+	rte_swx_ipsec_create;
+	rte_swx_ipsec_find;
+	rte_swx_ipsec_free;
+	rte_swx_ipsec_run;
+	rte_swx_ipsec_sa_add;
+	rte_swx_ipsec_sa_delete;
+	rte_swx_ipsec_sa_read;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 02/11] examples/pipeline: rework memory pool support
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 01/11] " Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
                     ` (9 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Rework the memory pool CLI command to accommodate the MBUF private
meta-data area size parameter.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 72 ++++++++++-------
 examples/pipeline/examples/fib.cli            |  2 +-
 examples/pipeline/examples/hash_func.cli      |  2 +-
 examples/pipeline/examples/l2fwd.cli          |  2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |  2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |  2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |  2 +-
 examples/pipeline/examples/learner.cli        |  2 +-
 examples/pipeline/examples/meter.cli          |  2 +-
 examples/pipeline/examples/mirroring.cli      |  2 +-
 examples/pipeline/examples/recirculation.cli  |  2 +-
 examples/pipeline/examples/registers.cli      |  2 +-
 examples/pipeline/examples/selector.cli       |  2 +-
 examples/pipeline/examples/varbit.cli         |  2 +-
 examples/pipeline/examples/vxlan.cli          |  2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |  2 +-
 examples/pipeline/obj.c                       | 80 +------------------
 examples/pipeline/obj.h                       | 27 -------
 18 files changed, 63 insertions(+), 146 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index e1e7aaddc1..14f1cfa47e 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -192,72 +192,88 @@ parse_table_entry(struct rte_swx_ctl_pipeline *p,
 }
 
 static const char cmd_mempool_help[] =
-"mempool <mempool_name>\n"
-"   buffer <buffer_size>\n"
-"   pool <pool_size>\n"
-"   cache <cache_size>\n"
-"   cpu <cpu_id>\n";
+"mempool <mempool_name> "
+"meta <mbuf_private_size> "
+"pkt <pkt_buffer_size> "
+"pool <pool_size> "
+"cache <cache_size> "
+"numa <numa_node>\n";
 
 static void
 cmd_mempool(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
+	    uint32_t n_tokens,
+	    char *out,
+	    size_t out_size,
+	    void *obj __rte_unused)
 {
-	struct mempool_params p;
-	char *name;
-	struct mempool *mempool;
+	struct rte_mempool *mp;
+	char *mempool_name;
+	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
 
-	if (n_tokens != 10) {
+	if (n_tokens != 12) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	name = tokens[1];
+	mempool_name = tokens[1];
+
+	if (strcmp(tokens[2], "meta")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
+		return;
+	}
 
-	if (strcmp(tokens[2], "buffer") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
+	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
+	if (strcmp(tokens[4], "pkt")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
 		return;
 	}
 
-	if (strcmp(tokens[4], "pool") != 0) {
+	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
+		return;
+	}
+
+	if (strcmp(tokens[6], "pool")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
 		return;
 	}
 
-	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
+	if (parser_read_uint32(&pool_size, tokens[7])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
 		return;
 	}
 
-	if (strcmp(tokens[6], "cache") != 0) {
+	if (strcmp(tokens[8], "cache")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
+	if (parser_read_uint32(&cache_size, tokens[9])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
 		return;
 	}
 
-	if (strcmp(tokens[8], "cpu") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+	if (strcmp(tokens[10], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+	if (parser_read_uint32(&numa_node, tokens[11])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	mempool = mempool_create(obj, name, &p);
-	if (mempool == NULL) {
+	mp = rte_pktmbuf_pool_create(mempool_name,
+				     pool_size,
+				     cache_size,
+				     mbuf_private_size,
+				     pkt_buffer_size,
+				     numa_node);
+	if (!mp) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 4e30c1320f..2450dc9ca4 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/fib.c /tmp/fib.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index b2e219e4c9..3840e47a2b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index 27e37021b9..c5505df296 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 11bb4543b9..bd700707f5 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index 8724dae3b0..aa64b32ab2 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 4db0a0dc56..619d17474f 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 6c8aa3921e..42184be1c8 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/learner.c /tmp/learner.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 21582554b9..0fa38b16b8 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/meter.c /tmp/meter.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 1d439e04d3..e7391de74b 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 52d0894f12..965c870fd4 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 3516f76a5b..aa775f71e5 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/registers.c /tmp/registers.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0e251b657..f0608de184 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/selector.c /tmp/selector.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 0f89990471..648b8882c3 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 1fbd1be6e4..4565adfaea 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index adc7f73312..510c2ad870 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index cac825f22a..697d14a901 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -13,18 +13,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
 
-/*
- * mempool
- */
-TAILQ_HEAD(mempool_list, mempool);
-
 /*
  * link
  */
@@ -39,75 +33,10 @@ TAILQ_HEAD(ring_list, ring);
  * obj
  */
 struct obj {
-	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
 };
 
-/*
- * mempool
- */
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
-
-struct mempool *
-mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
-{
-	struct mempool *mempool;
-	struct rte_mempool *m;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		mempool_find(obj, name) ||
-		(params == NULL) ||
-		(params->buffer_size < BUFFER_SIZE_MIN) ||
-		(params->pool_size == 0))
-		return NULL;
-
-	/* Resource create */
-	m = rte_pktmbuf_pool_create(
-		name,
-		params->pool_size,
-		params->cache_size,
-		0,
-		params->buffer_size - sizeof(struct rte_mbuf),
-		params->cpu_id);
-
-	if (m == NULL)
-		return NULL;
-
-	/* Node allocation */
-	mempool = calloc(1, sizeof(struct mempool));
-	if (mempool == NULL) {
-		rte_mempool_free(m);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(mempool->name, name, sizeof(mempool->name));
-	mempool->m = m;
-	mempool->buffer_size = params->buffer_size;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
-
-	return mempool;
-}
-
-struct mempool *
-mempool_find(struct obj *obj, const char *name)
-{
-	struct mempool *mempool;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(mempool, &obj->mempool_list, node)
-		if (strcmp(mempool->name, name) == 0)
-			return mempool;
-
-	return NULL;
-}
-
 /*
  * link
  */
@@ -171,7 +100,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct rte_eth_conf port_conf;
 	struct link *link;
 	struct link_params_rss *rss;
-	struct mempool *mempool;
+	struct rte_mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
 	uint16_t port_id = 0;
@@ -193,8 +122,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
 
-	mempool = mempool_find(obj, params->rx.mempool_name);
-	if (mempool == NULL)
+	mempool = rte_mempool_lookup(params->rx.mempool_name);
+	if (!mempool)
 		return NULL;
 
 	rss = params->rx.rss;
@@ -251,7 +180,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			params->rx.queue_size,
 			cpu_id,
 			NULL,
-			mempool->m);
+			mempool);
 
 		if (status < 0)
 			return NULL;
@@ -421,7 +350,6 @@ obj_init(void)
 	if (!obj)
 		return NULL;
 
-	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 8ea1c414c2..4ea610ceed 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <sys/queue.h>
 
-#include <rte_mempool.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
@@ -24,32 +23,6 @@ struct obj;
 struct obj *
 obj_init(void);
 
-/*
- * mempool
- */
-struct mempool_params {
-	uint32_t buffer_size;
-	uint32_t pool_size;
-	uint32_t cache_size;
-	uint32_t cpu_id;
-};
-
-struct mempool {
-	TAILQ_ENTRY(mempool) node;
-	char name[NAME_SIZE];
-	struct rte_mempool *m;
-	uint32_t buffer_size;
-};
-
-struct mempool *
-mempool_create(struct obj *obj,
-	       const char *name,
-	       struct mempool_params *params);
-
-struct mempool *
-mempool_find(struct obj *obj,
-	     const char *name);
-
 /*
  * link
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 03/11] examples/pipeline: streamline ring support
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 01/11] " Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
                     ` (8 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Remove redundant ring related code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 24 ++++++++++------
 examples/pipeline/obj.c | 63 -----------------------------------------
 examples/pipeline/obj.h | 21 --------------
 3 files changed, 15 insertions(+), 93 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 14f1cfa47e..517682f7c9 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -11,6 +11,7 @@
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_ring.h>
 #include <rte_swx_port_ethdev.h>
 #include <rte_swx_port_ring.h>
 #include <rte_swx_port_source_sink.h>
@@ -491,11 +492,11 @@ cmd_ring(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct ring_params p;
+	struct rte_ring *r;
 	char *name;
-	struct ring *ring;
+	uint32_t size, numa_node;
 
 	if (n_tokens != 6) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
@@ -504,28 +505,32 @@ cmd_ring(char **tokens,
 
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "size") != 0) {
+	if (strcmp(tokens[2], "size")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
+	if (parser_read_uint32(&size, tokens[3])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
 		return;
 	}
 
-	if (strcmp(tokens[4], "numa") != 0) {
+	if (strcmp(tokens[4], "numa")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
+	if (parser_read_uint32(&numa_node, tokens[5])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	ring = ring_create(obj, name, &p);
-	if (!ring) {
+	r = rte_ring_create(
+		name,
+		size,
+		(int)numa_node,
+		RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (!r) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
@@ -2999,6 +3004,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 697d14a901..3614b99d28 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -24,17 +24,11 @@
  */
 TAILQ_HEAD(link_list, link);
 
-/*
- * ring
- */
-TAILQ_HEAD(ring_list, ring);
-
 /*
  * obj
  */
 struct obj {
 	struct link_list link_list;
-	struct ring_list ring_list;
 };
 
 /*
@@ -282,62 +276,6 @@ link_next(struct obj *obj, struct link *link)
 		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
 }
 
-/*
- * ring
- */
-struct ring *
-ring_create(struct obj *obj, const char *name, struct ring_params *params)
-{
-	struct ring *ring;
-	struct rte_ring *r;
-	unsigned int flags = RING_F_SP_ENQ | RING_F_SC_DEQ;
-
-	/* Check input params */
-	if (!name || ring_find(obj, name) || !params || !params->size)
-		return NULL;
-
-	/**
-	 * Resource create
-	 */
-	r = rte_ring_create(
-		name,
-		params->size,
-		params->numa_node,
-		flags);
-	if (!r)
-		return NULL;
-
-	/* Node allocation */
-	ring = calloc(1, sizeof(struct ring));
-	if (!ring) {
-		rte_ring_free(r);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(ring->name, name, sizeof(ring->name));
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->ring_list, ring, node);
-
-	return ring;
-}
-
-struct ring *
-ring_find(struct obj *obj, const char *name)
-{
-	struct ring *ring;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(ring, &obj->ring_list, node)
-		if (strcmp(ring->name, name) == 0)
-			return ring;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -351,7 +289,6 @@ obj_init(void)
 		return NULL;
 
 	TAILQ_INIT(&obj->link_list);
-	TAILQ_INIT(&obj->ring_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 4ea610ceed..dbbc6d39a0 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -73,25 +73,4 @@ link_find(struct obj *obj, const char *name);
 struct link *
 link_next(struct obj *obj, struct link *link);
 
-/*
- * ring
- */
-struct ring_params {
-	uint32_t size;
-	uint32_t numa_node;
-};
-
-struct ring {
-	TAILQ_ENTRY(ring) node;
-	char name[NAME_SIZE];
-};
-
-struct ring *
-ring_create(struct obj *obj,
-	   const char *name,
-	   struct ring_params *params);
-
-struct ring *
-ring_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 04/11] examples/pipeline: streamline the Ethernet device support
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
                     ` (7 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Streamline the Ethernet device support code and remove redundant code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c  | 175 ++++++++++++++++++------------------
 examples/pipeline/main.c |  12 +--
 examples/pipeline/obj.c  | 186 +++++++++------------------------------
 examples/pipeline/obj.h  |  51 ++---------
 4 files changed, 136 insertions(+), 288 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 517682f7c9..f4c8300ea7 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -292,16 +292,14 @@ cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct link_params p;
-	struct link_params_rss rss;
-	struct link *link;
+	struct ethdev_params p = {0};
+	struct ethdev_params_rss rss = {0};
 	char *name;
+	int status;
 
-	memset(&p, 0, sizeof(p));
-
-	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
+	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -377,111 +375,98 @@ cmd_ethdev(char **tokens,
 		}
 	}
 
-	link = link_create(obj, name, &p);
-	if (link == NULL) {
+	status = ethdev_config(name, &p);
+	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
 }
 
-/* Print the link stats and info */
 static void
-print_link_info(struct link *link, char *out, size_t out_size)
+ethdev_show(uint16_t port_id, char **out, size_t *out_size)
 {
-	struct rte_eth_stats stats;
-	struct rte_ether_addr mac_addr;
-	struct rte_eth_link eth_link;
-	uint16_t mtu;
-	int ret;
-
-	memset(&stats, 0, sizeof(stats));
-	rte_eth_stats_get(link->port_id, &stats);
-
-	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
-	if (ret != 0) {
-		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	ret = rte_eth_link_get(link->port_id, &eth_link);
-	if (ret < 0) {
-		snprintf(out, out_size, "\n%s: link get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	rte_eth_dev_get_mtu(link->port_id, &mtu);
-
-	snprintf(out, out_size,
-		"\n"
-		"%s: flags=<%s> mtu %u\n"
-		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
-		"\tport# %u  speed %s\n"
-		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
-		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tTX errors %" PRIu64"\n",
-		link->name,
-		eth_link.link_status == 0 ? "DOWN" : "UP",
-		mtu,
-		RTE_ETHER_ADDR_BYTES(&mac_addr),
-		link->n_rxq,
-		link->n_txq,
-		link->port_id,
-		rte_eth_link_speed_to_str(eth_link.link_speed),
-		stats.ipackets,
-		stats.ibytes,
-		stats.ierrors,
-		stats.imissed,
-		stats.rx_nombuf,
-		stats.opackets,
-		stats.obytes,
-		stats.oerrors);
+	char name[RTE_ETH_NAME_MAX_LEN] = {0};
+	struct rte_eth_dev_info info = {0};
+	struct rte_eth_stats stats = {0};
+	struct rte_ether_addr addr = {0};
+	struct rte_eth_link link = {0};
+	uint32_t length;
+	uint16_t mtu = 0;
+
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return;
+
+	rte_eth_dev_get_name_by_port(port_id, name);
+	rte_eth_dev_info_get(port_id, &info);
+	rte_eth_stats_get(port_id, &stats);
+	rte_eth_macaddr_get(port_id, &addr);
+	rte_eth_link_get(port_id, &link);
+	rte_eth_dev_get_mtu(port_id, &mtu);
+
+	snprintf(*out, *out_size,
+		 "%s: flags=<%s> mtu %u\n"
+		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
+		 "\tport# %u  speed %s\n"
+		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
+		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tTX errors %" PRIu64"\n\n",
+		 name,
+		 link.link_status ? "UP" : "DOWN",
+		 mtu,
+		 RTE_ETHER_ADDR_BYTES(&addr),
+		 info.nb_rx_queues,
+		 info.nb_tx_queues,
+		 port_id,
+		 rte_eth_link_speed_to_str(link.link_speed),
+		 stats.ipackets,
+		 stats.ibytes,
+		 stats.ierrors,
+		 stats.imissed,
+		 stats.rx_nombuf,
+		 stats.opackets,
+		 stats.obytes,
+		 stats.oerrors);
+
+	length = strlen(*out);
+	*out_size -= length;
+	*out += length;
 }
 
-/*
- * ethdev show [<ethdev_name>]
- */
+
+static char cmd_ethdev_show_help[] =
+"ethdev show [ <ethdev_name> ]\n";
+
 static void
 cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
-	      void *obj)
+	      void *obj __rte_unused)
 {
-	struct link *link;
-	char *link_name;
+	uint16_t port_id;
 
 	if (n_tokens != 2 && n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (n_tokens == 2) {
-		link = link_next(obj, NULL);
-
-		while (link != NULL) {
-			out_size = out_size - strlen(out);
-			out = &out[strlen(out)];
-
-			print_link_info(link, out, out_size);
-			link = link_next(obj, link);
-		}
-	} else {
-		out_size = out_size - strlen(out);
-		out = &out[strlen(out)];
+	/* Single device. */
+	if (n_tokens == 3) {
+		int status;
 
-		link_name = tokens[2];
-		link = link_find(obj, link_name);
+		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
+		if (status)
+			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
 
-		if (link == NULL) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-					"Link does not exist");
-			return;
-		}
-		print_link_info(link, out, out_size);
+		ethdev_show(port_id, &out, &out_size);
+		return;
 	}
+
+	/*  All devices. */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		if (rte_eth_dev_is_valid_port(port_id))
+			ethdev_show(port_id, &out, &out_size);
 }
 
 static const char cmd_ring_help[] =
@@ -3004,6 +2989,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tethdev show\n"
 			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -3039,9 +3025,16 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "ethdev") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
-		return;
+	if (!strcmp(tokens[0], "ethdev")) {
+		if (n_tokens == 1) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
+			return;
+		}
+
+		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
+			return;
+		}
 	}
 
 	if (strcmp(tokens[0], "ring") == 0) {
diff --git a/examples/pipeline/main.c b/examples/pipeline/main.c
index 6fb839f4cb..da5dd2b3f6 100644
--- a/examples/pipeline/main.c
+++ b/examples/pipeline/main.c
@@ -135,7 +135,6 @@ int
 main(int argc, char **argv)
 {
 	struct conn *conn;
-	struct obj *obj;
 	int status;
 
 	/* Parse application arguments */
@@ -150,13 +149,6 @@ main(int argc, char **argv)
 		return status;
 	};
 
-	/* Obj */
-	obj = obj_init();
-	if (!obj) {
-		printf("Error: Obj initialization failed (%d)\n", status);
-		return status;
-	}
-
 	/* Thread */
 	status = thread_init();
 	if (status) {
@@ -174,10 +166,10 @@ main(int argc, char **argv)
 		cli_script_process(app.script_name,
 			app.conn.msg_in_len_max,
 			app.conn.msg_out_len_max,
-			obj);
+			NULL);
 
 	/* Connectivity */
-	app.conn.msg_handle_arg = obj;
+	app.conn.msg_handle_arg = NULL;
 	conn = conn_init(&app.conn);
 	if (!conn) {
 		printf("Error: Connectivity initialization failed (%d)\n",
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 3614b99d28..143b968472 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -4,35 +4,14 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#ifdef RTE_EXEC_ENV_LINUX
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#endif
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_ctl.h>
 
 #include "obj.h"
 
 /*
- * link
- */
-TAILQ_HEAD(link_list, link);
-
-/*
- * obj
- */
-struct obj {
-	struct link_list link_list;
-};
-
-/*
- * link
+ * ethdev
  */
 static struct rte_eth_conf port_conf_default = {
 	.link_speeds = 0,
@@ -58,7 +37,7 @@ static struct rte_eth_conf port_conf_default = {
 static int
 rss_setup(uint16_t port_id,
 	uint16_t reta_size,
-	struct link_params_rss *rss)
+	struct ethdev_params_rss *rss)
 {
 	struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
 	uint32_t i;
@@ -87,69 +66,64 @@ rss_setup(uint16_t port_id,
 	return status;
 }
 
-struct link *
-link_create(struct obj *obj, const char *name, struct link_params *params)
+int
+ethdev_config(const char *name, struct ethdev_params *params)
 {
 	struct rte_eth_dev_info port_info;
 	struct rte_eth_conf port_conf;
-	struct link *link;
-	struct link_params_rss *rss;
+	struct ethdev_params_rss *rss;
 	struct rte_mempool *mempool;
-	uint32_t cpu_id, i;
-	int status;
+	uint32_t i;
+	int numa_node, status;
 	uint16_t port_id = 0;
 
 	/* Check input params */
-	if ((name == NULL) ||
-		link_find(obj, name) ||
-		(params == NULL) ||
-		(params->rx.n_queues == 0) ||
-		(params->rx.queue_size == 0) ||
-		(params->tx.n_queues == 0) ||
-		(params->tx.queue_size == 0))
-		return NULL;
+	if (!name ||
+	    !name[0] ||
+	    !params ||
+	    !params->rx.n_queues ||
+	    !params->rx.queue_size ||
+	    !params->tx.n_queues ||
+	    !params->tx.queue_size)
+		return -EINVAL;
 
 	status = rte_eth_dev_get_port_by_name(name, &port_id);
 	if (status)
-		return NULL;
+		return -EINVAL;
 
-	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
-		return NULL;
+	status = rte_eth_dev_info_get(port_id, &port_info);
+	if (status)
+		return -EINVAL;
 
 	mempool = rte_mempool_lookup(params->rx.mempool_name);
 	if (!mempool)
-		return NULL;
+		return -EINVAL;
 
 	rss = params->rx.rss;
 	if (rss) {
-		if ((port_info.reta_size == 0) ||
-			(port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512))
-			return NULL;
+		if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512)
+			return -EINVAL;
 
-		if ((rss->n_queues == 0) ||
-			(rss->n_queues >= LINK_RXQ_RSS_MAX))
-			return NULL;
+		if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX)
+			return -EINVAL;
 
 		for (i = 0; i < rss->n_queues; i++)
 			if (rss->queue_id[i] >= port_info.max_rx_queues)
-				return NULL;
+				return -EINVAL;
 	}
 
-	/**
-	 * Resource create
-	 */
 	/* Port */
 	memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
 	if (rss) {
+		uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP;
+
 		port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
-		port_conf.rx_adv_conf.rss_conf.rss_hf =
-			(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
-			port_info.flow_type_rss_offloads;
+		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads;
 	}
 
-	cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
-	if (cpu_id == (uint32_t) SOCKET_ID_ANY)
-		cpu_id = 0;
+	numa_node = rte_eth_dev_socket_id(port_id);
+	if (numa_node == SOCKET_ID_ANY)
+		numa_node = 0;
 
 	status = rte_eth_dev_configure(
 		port_id,
@@ -158,12 +132,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		&port_conf);
 
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (params->promiscuous) {
 		status = rte_eth_promiscuous_enable(port_id);
-		if (status != 0)
-			return NULL;
+		if (status)
+			return -EINVAL;
 	}
 
 	/* Port RX */
@@ -172,12 +146,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->rx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL,
 			mempool);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port TX */
@@ -186,24 +160,24 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->tx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port start */
 	status = rte_eth_dev_start(port_id);
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (rss) {
 		status = rss_setup(port_id, port_info.reta_size, rss);
 
 		if (status) {
 			rte_eth_dev_stop(port_id);
-			return NULL;
+			return -EINVAL;
 		}
 	}
 
@@ -211,84 +185,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	status = rte_eth_dev_set_link_up(port_id);
 	if ((status < 0) && (status != -ENOTSUP)) {
 		rte_eth_dev_stop(port_id);
-		return NULL;
-	}
-
-	/* Node allocation */
-	link = calloc(1, sizeof(struct link));
-	if (link == NULL) {
-		rte_eth_dev_stop(port_id);
-		return NULL;
+		return -EINVAL;
 	}
 
-	/* Node fill in */
-	strlcpy(link->name, name, sizeof(link->name));
-	link->port_id = port_id;
-	link->n_rxq = params->rx.n_queues;
-	link->n_txq = params->tx.n_queues;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->link_list, link, node);
-
-	return link;
-}
-
-int
-link_is_up(struct obj *obj, const char *name)
-{
-	struct rte_eth_link link_params;
-	struct link *link;
-
-	/* Check input params */
-	if (!obj || !name)
-		return 0;
-
-	link = link_find(obj, name);
-	if (link == NULL)
-		return 0;
-
-	/* Resource */
-	if (rte_eth_link_get(link->port_id, &link_params) < 0)
-		return 0;
-
-	return (link_params.link_status == RTE_ETH_LINK_DOWN) ? 0 : 1;
-}
-
-struct link *
-link_find(struct obj *obj, const char *name)
-{
-	struct link *link;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(link, &obj->link_list, node)
-		if (strcmp(link->name, name) == 0)
-			return link;
-
-	return NULL;
-}
-
-struct link *
-link_next(struct obj *obj, struct link *link)
-{
-	return (link == NULL) ?
-		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
-}
-
-/*
- * obj
- */
-struct obj *
-obj_init(void)
-{
-	struct obj *obj;
-
-	obj = calloc(1, sizeof(struct obj));
-	if (!obj)
-		return NULL;
-
-	TAILQ_INIT(&obj->link_list);
-
-	return obj;
+	return 0;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index dbbc6d39a0..fb091f4ba7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -6,41 +6,25 @@
 #define _INCLUDE_OBJ_H_
 
 #include <stdint.h>
-#include <sys/queue.h>
-
-#include <rte_swx_pipeline.h>
-#include <rte_swx_ctl.h>
-
-#ifndef NAME_SIZE
-#define NAME_SIZE 64
-#endif
-
-/*
- * obj
- */
-struct obj;
-
-struct obj *
-obj_init(void);
 
 /*
- * link
+ * ethdev
  */
-#ifndef LINK_RXQ_RSS_MAX
-#define LINK_RXQ_RSS_MAX                                   16
+#ifndef ETHDEV_RXQ_RSS_MAX
+#define ETHDEV_RXQ_RSS_MAX 16
 #endif
 
-struct link_params_rss {
-	uint32_t queue_id[LINK_RXQ_RSS_MAX];
+struct ethdev_params_rss {
+	uint32_t queue_id[ETHDEV_RXQ_RSS_MAX];
 	uint32_t n_queues;
 };
 
-struct link_params {
+struct ethdev_params {
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
 		const char *mempool_name;
-		struct link_params_rss *rss;
+		struct ethdev_params_rss *rss;
 	} rx;
 
 	struct {
@@ -51,26 +35,7 @@ struct link_params {
 	int promiscuous;
 };
 
-struct link {
-	TAILQ_ENTRY(link) node;
-	char name[NAME_SIZE];
-	uint16_t port_id;
-	uint32_t n_rxq;
-	uint32_t n_txq;
-};
-
-struct link *
-link_create(struct obj *obj,
-	    const char *name,
-	    struct link_params *params);
-
 int
-link_is_up(struct obj *obj, const char *name);
-
-struct link *
-link_find(struct obj *obj, const char *name);
-
-struct link *
-link_next(struct obj *obj, struct link *link);
+ethdev_config(const char *name, struct ethdev_params *params);
 
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 05/11] examples/pipeline: support crypto devices
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
                     ` (6 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add support for crypto devices in the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c | 60 +++++++++++++++++++++++++++++++++++++++++
 examples/pipeline/obj.h | 11 ++++++++
 2 files changed, 71 insertions(+)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 143b968472..25bfd2da4f 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -7,6 +7,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_cryptodev.h>
 
 #include "obj.h"
 
@@ -190,3 +191,62 @@ ethdev_config(const char *name, struct ethdev_params *params)
 
 	return 0;
 }
+
+/*
+ * cryptodev
+ */
+int
+cryptodev_config(const char *name, struct cryptodev_params *params)
+{
+	struct rte_cryptodev_info dev_info;
+	struct rte_cryptodev_config dev_conf = {0};
+	struct rte_cryptodev_qp_conf queue_conf = {0};
+	uint8_t dev_id;
+	uint32_t socket_id, i;
+	int status;
+
+	/* Check input parameters. */
+	if (!name ||
+	    !params->n_queue_pairs ||
+	    !params->queue_size)
+		return -EINVAL;
+
+	/* Find the crypto device. */
+	status = rte_cryptodev_get_dev_id(name);
+	if (status < 0)
+		return -EINVAL;
+
+	dev_id = (uint8_t)status;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->n_queue_pairs > dev_info.max_nb_queue_pairs)
+		return -EINVAL;
+
+	socket_id = rte_cryptodev_socket_id(dev_id);
+
+	/* Configure the crypto device. */
+	dev_conf.socket_id = socket_id;
+	dev_conf.nb_queue_pairs = params->n_queue_pairs;
+	dev_conf.ff_disable = 0;
+
+	status = rte_cryptodev_configure(dev_id, &dev_conf);
+	if (status)
+		return status;
+
+	/* Configure the crypto device queue pairs. */
+	queue_conf.nb_descriptors = params->queue_size;
+	queue_conf.mp_session = NULL;
+
+	for (i = 0; i < params->n_queue_pairs; i++) {
+		status = rte_cryptodev_queue_pair_setup(dev_id, i, &queue_conf, socket_id);
+		if (status)
+			return status;
+	}
+
+	/* Start the crypto device. */
+	status = rte_cryptodev_start(dev_id);
+	if (status)
+		return status;
+
+	return 0;
+}
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index fb091f4ba7..0f103be6a7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -38,4 +38,15 @@ struct ethdev_params {
 int
 ethdev_config(const char *name, struct ethdev_params *params);
 
+/*
+ * cryptodev
+ */
+struct cryptodev_params {
+	uint32_t n_queue_pairs;
+	uint32_t queue_size;
+};
+
+int
+cryptodev_config(const char *name, struct cryptodev_params *params);
+
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 06/11] examples/pipeline: add CLI command for crypto device
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (4 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
                     ` (5 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI command for the configuration of crypto devices.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f4c8300ea7..6b5d5a3370 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -521,6 +521,58 @@ cmd_ring(char **tokens,
 	}
 }
 
+static const char cmd_cryptodev_help[] =
+"cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
+
+static void
+cmd_cryptodev(char **tokens,
+	      uint32_t n_tokens,
+	      char *out,
+	      size_t out_size,
+	      void *obj __rte_unused)
+{
+	struct cryptodev_params params;
+	char *cryptodev_name;
+	int status;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	cryptodev_name = tokens[1];
+
+	if (strcmp(tokens[2], "queues")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
+		return;
+	}
+
+	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
+		return;
+	}
+
+	if (strcmp(tokens[4], "qsize")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
+		return;
+	}
+
+
+	if (parser_read_uint32(&params.queue_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
+		return;
+	}
+
+	status = cryptodev_config(cryptodev_name, &params);
+	if (status)
+		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
+}
+
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2991,6 +3043,7 @@ cmd_help(char **tokens,
 			"\tethdev\n"
 			"\tethdev show\n"
 			"\tring\n"
+			"\tcryptodev\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3042,6 +3095,11 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3297,6 +3355,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
+		return;
+	}
+
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 07/11] examples/pipeline: add IPsec CLI commands
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (5 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
                     ` (4 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands for IPsec block configuration.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 298 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 298 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 6b5d5a3370..92ec0fd8e3 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -18,6 +18,7 @@
 #include <rte_swx_port_fd.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
+#include <rte_swx_ipsec.h>
 
 #include "cli.h"
 
@@ -2910,6 +2911,263 @@ cmd_pipeline_mirror_session(char **tokens,
 	}
 }
 
+static const char cmd_ipsec_create_help[] =
+"ipsec <ipsec_instance_name> create "
+"in <ring_in_name> out <ring_out_name> "
+"cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
+"bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
+"samax <n_sa_max> "
+"numa <numa_node>\n";
+
+static void
+cmd_ipsec_create(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec_params p;
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t numa_node;
+	int status;
+
+	if (n_tokens != 20) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+
+	if (strcmp(tokens[2], "create")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
+		return;
+	}
+
+	if (strcmp(tokens[3], "in")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+		return;
+	}
+
+	p.ring_in_name = tokens[4];
+
+	if (strcmp(tokens[5], "out")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+		return;
+	}
+
+	p.ring_out_name = tokens[6];
+
+	if (strcmp(tokens[7], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	p.crypto_dev_name = tokens[8];
+
+	if (strcmp(tokens[9], "cryptoq")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
+		return;
+	}
+
+	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
+		return;
+	}
+
+	if (strcmp(tokens[11], "bsz")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
+		return;
+	}
+
+	if (strcmp(tokens[16], "samax")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
+		return;
+	}
+
+	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
+		return;
+	}
+
+	if (strcmp(tokens[18], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
+		return;
+	}
+
+	if (parser_read_uint32(&numa_node, tokens[19])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	status = rte_swx_ipsec_create(&ipsec,
+				      ipsec_instance_name,
+				      &p,
+				      (int)numa_node);
+	if (status)
+		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
+}
+
+static const char cmd_ipsec_sa_add_help[] =
+"ipsec <ipsec_instance_name> sa add <file_name>\n";
+
+static void
+cmd_ipsec_sa_add(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name, *file_name, *line = NULL;
+	FILE *file = NULL;
+	uint32_t line_id = 0;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		goto free;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		goto free;
+	}
+
+	if (strcmp(tokens[3], "add")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+		goto free;
+	}
+
+	file_name = tokens[4];
+	file = fopen(file_name, "r");
+	if (!file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+		goto free;
+	}
+
+	/* Buffer allocation. */
+	line = malloc(MAX_LINE_SIZE);
+	if (!line) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	/* File read. */
+	for (line_id = 1; ; line_id++) {
+		struct rte_swx_ipsec_sa_params *sa;
+		const char *err_msg;
+		uint32_t sa_id = 0;
+		int is_blank_or_comment, status = 0;
+
+		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+			break;
+
+		/* Read SA from file. */
+		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
+		if (!sa) {
+			if (is_blank_or_comment)
+				continue;
+
+			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
+				file_name, line_id, err_msg);
+			goto free;
+		}
+
+		snprintf(out, out_size, "%s", line);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		/* Add the SA to the IPsec instance. Free the SA. */
+		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
+		if (status)
+			snprintf(out, out_size, "\t: Error (%d)\n", status);
+		else
+			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		free(sa);
+		if (status)
+			goto free;
+	}
+
+free:
+	if (file)
+		fclose(file);
+	free(line);
+}
+
+static const char cmd_ipsec_sa_delete_help[] =
+"ipsec <ipsec_instance_name> sa delete <sa_id>\n";
+
+static void
+cmd_ipsec_sa_delete(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t sa_id;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		return;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		return;
+	}
+
+	if (strcmp(tokens[3], "delete")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+		return;
+	}
+
+	if (parser_read_uint32(&sa_id, tokens[4])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
+		return;
+	}
+
+	rte_swx_ipsec_sa_delete(ipsec, sa_id);
+}
+
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
 
@@ -3068,6 +3326,9 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tipsec create\n"
+			"\tipsec sa add\n"
+			"\tipsec sa delete\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
 		return;
@@ -3292,6 +3553,26 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "add")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "delete")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
+		return;
+	}
+
 	if ((n_tokens == 3) &&
 		(strcmp(tokens[0], "thread") == 0) &&
 		(strcmp(tokens[1], "pipeline") == 0)) {
@@ -3540,6 +3821,23 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "ipsec")) {
+		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
+			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
+			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
+			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	if (strcmp(tokens[0], "thread") == 0) {
 		if ((n_tokens >= 5) &&
 			(strcmp(tokens[4], "enable") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 08/11] examples/pipeline: rework the thread configuration updates
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (6 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
                     ` (3 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the configuration updates for the data plane threads were
performed through message queues. Now, this mechanism is replaced by
the control thread updating the mirror copy of the data plane thread
configuration followed by pointer swapping.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 154 ++---
 examples/pipeline/examples/fib.cli            |   2 +-
 examples/pipeline/examples/hash_func.cli      |   2 +-
 examples/pipeline/examples/l2fwd.cli          |   2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |   2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |   2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |   2 +-
 examples/pipeline/examples/learner.cli        |   2 +-
 examples/pipeline/examples/meter.cli          |   2 +-
 examples/pipeline/examples/mirroring.cli      |   2 +-
 examples/pipeline/examples/recirculation.cli  |   2 +-
 examples/pipeline/examples/registers.cli      |   2 +-
 examples/pipeline/examples/selector.cli       |   2 +-
 examples/pipeline/examples/varbit.cli         |   2 +-
 examples/pipeline/examples/vxlan.cli          |   2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |   2 +-
 examples/pipeline/thread.c                    | 586 ++++--------------
 examples/pipeline/thread.h                    |  17 +-
 18 files changed, 217 insertions(+), 570 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 92ec0fd8e3..68bb93a46c 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3168,119 +3168,86 @@ cmd_ipsec_sa_delete(char **tokens,
 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
 }
 
-static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
-
-#ifndef TIMER_PERIOD_MS_DEFAULT
-#define TIMER_PERIOD_MS_DEFAULT 10
-#endif
+static const char cmd_pipeline_enable_help[] =
+"pipeline <pipeline_name> enable thread <thread_id>\n";
 
 static void
-cmd_thread_pipeline_enable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_enable(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
 {
 	char *pipeline_name;
 	struct rte_swx_pipeline *p;
-	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
+	uint32_t thread_id;
 	int status;
 
-	if ((n_tokens != 5) && (n_tokens != 7)) {
+	if (n_tokens != 5) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "enable") != 0) {
+	if (strcmp(tokens[2], "enable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
 		return;
 	}
 
-	if (n_tokens == 7) {
-		if (strcmp(tokens[5], "period") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
-			return;
-		}
+	if (strcmp(tokens[3], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
 
-		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-			return;
-		}
+	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
+	status = pipeline_enable(p, thread_id);
 	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
+		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
 		return;
 	}
 }
 
-static const char cmd_thread_pipeline_disable_help[] =
-"thread <thread_id> pipeline <pipeline_name> disable\n";
+static const char cmd_pipeline_disable_help[] =
+"pipeline <pipeline_name> disable\n";
 
 static void
-cmd_thread_pipeline_disable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_disable(char **tokens,
+		     uint32_t n_tokens,
+		     char *out,
+		     size_t out_size,
+		     void *obj __rte_unused)
 {
 	struct rte_swx_pipeline *p;
 	char *pipeline_name;
-	uint32_t thread_id;
-	int status;
 
-	if (n_tokens != 5) {
+	if (n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "disable") != 0) {
+	if (strcmp(tokens[2], "disable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, p);
-	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL,
-			"thread pipeline disable");
-		return;
-	}
+	pipeline_disable(p);
 }
 
 static void
@@ -3326,11 +3293,12 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tpipeline enable\n"
+			"\tpipeline disable\n\n"
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
-			"\tthread pipeline enable\n"
-			"\tthread pipeline disable\n\n");
+			);
 		return;
 	}
 
@@ -3553,6 +3521,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
+		return;
+	}
+
 	if (!strcmp(tokens[0], "ipsec") &&
 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
@@ -3573,22 +3553,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((n_tokens == 3) &&
-		(strcmp(tokens[0], "thread") == 0) &&
-		(strcmp(tokens[1], "pipeline") == 0)) {
-		if (strcmp(tokens[2], "enable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_enable_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "disable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_disable_help);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3819,6 +3783,16 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
+			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
+			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
 	}
 
 	if (!strcmp(tokens[0], "ipsec")) {
@@ -3838,22 +3812,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
-	if (strcmp(tokens[0], "thread") == 0) {
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "enable") == 0)) {
-			cmd_thread_pipeline_enable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "disable") == 0)) {
-			cmd_thread_pipeline_disable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 2450dc9ca4..d2485cefe0 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -54,4 +54,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index 3840e47a2b..2c20f7a7a0 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index c5505df296..383fe346c0 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethde
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index bd700707f5..d02cb2c470 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index aa64b32ab2..d4ea449e03 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 619d17474f..8a25c98096 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 42184be1c8..04c7b4d26f 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/eth
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 0fa38b16b8..9e064dbb52 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 meter meters set profile platinum index from 0 to 15
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index e7391de74b..da6bd338fb 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 mirror session 3 port 0 clone slow truncate 128
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 965c870fd4..f925179bf7 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/exampl
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index aa775f71e5..58dde5fdf1 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0608de184..9af56cd0f1 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 selector s show
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 648b8882c3..621e7f5627 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethd
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 4565adfaea..73eb29a6b2 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 510c2ad870..828342408b 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -36,4 +36,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 6d15f51fb2..3001bc0858 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -3,17 +3,11 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
 
+#include <rte_atomic.h>
 #include <rte_common.h>
-#include <rte_cycles.h>
 #include <rte_lcore.h>
-#include <rte_ring.h>
-
-#include <rte_table_acl.h>
-#include <rte_table_array.h>
-#include <rte_table_hash.h>
-#include <rte_table_lpm.h>
-#include <rte_table_lpm_ipv6.h>
 
 #include "obj.h"
 #include "thread.h"
@@ -22,14 +16,6 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
-#ifndef THREAD_MSGQ_SIZE
-#define THREAD_MSGQ_SIZE                                   64
-#endif
-
-#ifndef THREAD_TIMER_PERIOD_MS
-#define THREAD_TIMER_PERIOD_MS                             100
-#endif
-
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -40,509 +26,209 @@
 #endif
 
 /**
- * Control thread: data plane thread context
+ * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
+ * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
+ * blocks.
+ *
+ * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
+ * single thread at any given time, so the same pipeline/block cannot show up in the list of
+ * pipelines/blocks of more than one thread at any specific moment.
+ *
+ * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
+ *  - Read-write by the CP thread;
+ *  - Read-only by the DP thread.
  */
 struct thread {
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-
-	uint32_t enabled;
-};
-
-static struct thread thread[RTE_MAX_LCORE];
-
-/**
- * Data plane threads: context
- */
-struct pipeline_data {
-	struct rte_swx_pipeline *p;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-};
-
-struct thread_data {
-	struct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];
-	uint32_t n_pipelines;
-
-	struct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-	uint64_t time_next_min;
+	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	volatile uint64_t n_pipelines;
+	int enabled;
 } __rte_cache_aligned;
 
-static struct thread_data thread_data[RTE_MAX_LCORE];
+static struct thread threads[RTE_MAX_LCORE];
 
 /**
- * Control thread: data plane thread init
+ * Control plane (CP) thread.
  */
-static void
-thread_free(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct thread *t = &thread[i];
-
-		if (!rte_lcore_is_enabled(i))
-			continue;
-
-		/* MSGQs */
-		rte_ring_free(t->msgq_req);
-
-		rte_ring_free(t->msgq_rsp);
-	}
-}
-
 int
 thread_init(void)
 {
-	uint32_t i;
-
-	RTE_LCORE_FOREACH_WORKER(i) {
-		char name[NAME_MAX];
-		struct rte_ring *msgq_req, *msgq_rsp;
-		struct thread *t = &thread[i];
-		struct thread_data *t_data = &thread_data[i];
-		uint32_t cpu_id = rte_lcore_to_socket_id(i);
-
-		/* MSGQs */
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-REQ", i);
-
-		msgq_req = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_req == NULL) {
-			thread_free();
-			return -1;
-		}
+	uint32_t thread_id;
 
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-RSP", i);
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
 
-		msgq_rsp = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_rsp == NULL) {
-			thread_free();
-			return -1;
-		}
-
-		/* Control thread records */
-		t->msgq_req = msgq_req;
-		t->msgq_rsp = msgq_rsp;
 		t->enabled = 1;
-
-		/* Data plane thread records */
-		t_data->n_pipelines = 0;
-		t_data->msgq_req = msgq_req;
-		t_data->msgq_rsp = msgq_rsp;
-		t_data->timer_period =
-			(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;
-		t_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;
-		t_data->time_next_min = t_data->time_next;
 	}
 
 	return 0;
 }
 
-static inline int
-thread_is_running(uint32_t thread_id)
+static uint32_t
+pipeline_find(struct rte_swx_pipeline *p)
 {
-	enum rte_lcore_state_t thread_state;
+	uint32_t thread_id;
 
-	thread_state = rte_eal_get_lcore_state(thread_id);
-	return (thread_state == RUNNING) ? 1 : 0;
-}
-
-/**
- * Control thread & data plane threads: message passing
- */
-enum thread_req_type {
-	THREAD_REQ_PIPELINE_ENABLE = 0,
-	THREAD_REQ_PIPELINE_DISABLE,
-	THREAD_REQ_MAX
-};
-
-struct thread_msg_req {
-	enum thread_req_type type;
-
-	union {
-		struct {
-			struct rte_swx_pipeline *p;
-			uint32_t timer_period_ms;
-		} pipeline_enable;
-
-		struct {
-			struct rte_swx_pipeline *p;
-		} pipeline_disable;
-	};
-};
-
-struct thread_msg_rsp {
-	int status;
-};
-
-/**
- * Control thread
- */
-static struct thread_msg_req *
-thread_msg_alloc(void)
-{
-	size_t size = RTE_MAX(sizeof(struct thread_msg_req),
-		sizeof(struct thread_msg_rsp));
-
-	return calloc(1, size);
-}
-
-static void
-thread_msg_free(struct thread_msg_rsp *rsp)
-{
-	free(rsp);
-}
-
-static struct thread_msg_rsp *
-thread_msg_send_recv(uint32_t thread_id,
-	struct thread_msg_req *req)
-{
-	struct thread *t = &thread[thread_id];
-	struct rte_ring *msgq_req = t->msgq_req;
-	struct rte_ring *msgq_rsp = t->msgq_rsp;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* send */
-	do {
-		status = rte_ring_sp_enqueue(msgq_req, req);
-	} while (status == -ENOBUFS);
-
-	/* recv */
-	do {
-		status = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);
-	} while (status != 0);
-
-	return rsp;
-}
-
-static int
-thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
-{
-	struct thread *t = &thread[thread_id];
-	struct thread_data *td = &thread_data[thread_id];
-	uint32_t i;
-
-	if (!t->enabled)
-		return 0; /* Pipeline NOT enabled on this thread. */
-
-	for (i = 0; i < td->n_pipelines; i++)
-		if (td->p[i] == p)
-			return 1; /* Pipeline enabled on this thread. */
-
-	return 0 /* Pipeline NOT enabled on this thread. */;
-}
-
-int
-thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
-{
-	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		struct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];
-
-		if (td->n_pipelines >= THREAD_PIPELINES_MAX)
-			return -1;
-
-		/* Data plane thread */
-		td->p[td->n_pipelines] = p;
-
-		tdp->p = p;
-		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
-		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
-		td->n_pipelines++;
+		if (!t->enabled)
+			continue;
 
-		return 0;
+		for (i = 0; i < t->n_pipelines; i++)
+			if (t->pipelines[i] == p)
+				break;
 	}
 
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p;
-	req->pipeline_enable.timer_period_ms = timer_period_ms;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
-
-	/* Read response */
-	status = rsp->status;
-
-	/* Free response */
-	thread_msg_free(rsp);
-
-	/* Request completion */
-	if (status)
-		return status;
-
-	return 0;
+	return thread_id;
 }
 
+/**
+ * Enable a given pipeline to run on a specific DP thread.
+ *
+ * CP thread:
+ *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
+ *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
+ *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
+ *    write barrier used below.
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
+ *    the new pipeline when it sees the updated t->n_pipelines value;
+ *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
+ *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
+ *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
+ */
 int
-thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
 {
 	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
+	uint64_t n_pipelines;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_pipeline_enabled(thread_id, p))
-		return 0;
+	if (!p || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
 
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		uint32_t i;
-
-		for (i = 0; i < td->n_pipelines; i++) {
-			struct pipeline_data *tdp = &td->pipeline_data[i];
-
-			if (tdp->p != p)
-				continue;
-
-			/* Data plane thread */
-			if (i < td->n_pipelines - 1) {
-				struct rte_swx_pipeline *pipeline_last =
-					td->p[td->n_pipelines - 1];
-				struct pipeline_data *tdp_last =
-					&td->pipeline_data[td->n_pipelines - 1];
-
-				td->p[i] = pipeline_last;
-				memcpy(tdp, tdp_last, sizeof(*tdp));
-			}
-
-			td->n_pipelines--;
-
-			break;
-		}
+	if (pipeline_find(p) < RTE_MAX_LCORE)
+		return -EEXIST;
 
-		return 0;
-	}
-
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
 
-	/* Read response */
-	status = rsp->status;
+	n_pipelines = t->n_pipelines;
 
-	/* Free response */
-	thread_msg_free(rsp);
+	/* Check there is room for at least one more pipeline. */
+	if (n_pipelines >= THREAD_PIPELINES_MAX)
+		return -ENOSPC;
 
-	/* Request completion */
-	if (status)
-		return status;
+	/* Install the new pipeline. */
+	t->pipelines[n_pipelines] = p;
+	rte_wmb();
+	t->n_pipelines = n_pipelines + 1;
 
 	return 0;
 }
 
 /**
- * Data plane threads: message handling
+ * Disable a given pipeline from running on any DP thread.
+ *
+ * CP thread:
+ *  - Detects the thread that is running the given pipeline, if any;
+ *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
+ *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
+ *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
+ *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
+ *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
+ *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
+ *    below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final value of t->n_pipelines;
+ *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
+ *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
+ *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
+ *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
+ *    behaviors are detailed below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final values of the t->pipeline[] array;
+ *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
+ *       the exception of the pipeline_last, which is not run during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
  */
-static inline struct thread_msg_req *
-thread_msg_recv(struct rte_ring *msgq_req)
-{
-	struct thread_msg_req *req;
-
-	int status = rte_ring_sc_dequeue(msgq_req, (void **) &req);
-
-	if (status != 0)
-		return NULL;
-
-	return req;
-}
-
-static inline void
-thread_msg_send(struct rte_ring *msgq_rsp,
-	struct thread_msg_rsp *rsp)
-{
-	int status;
-
-	do {
-		status = rte_ring_sp_enqueue(msgq_rsp, rsp);
-	} while (status == -ENOBUFS);
-}
-
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_enable(struct thread_data *t,
-	struct thread_msg_req *req)
+void
+pipeline_disable(struct rte_swx_pipeline *p)
 {
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	struct pipeline_data *p = &t->pipeline_data[t->n_pipelines];
-
-	/* Request */
-	if (t->n_pipelines >= THREAD_PIPELINES_MAX) {
-		rsp->status = -1;
-		return rsp;
-	}
-
-	t->p[t->n_pipelines] = req->pipeline_enable.p;
-
-	p->p = req->pipeline_enable.p;
-	p->timer_period = (rte_get_tsc_hz() *
-		req->pipeline_enable.timer_period_ms) / 1000;
-	p->time_next = rte_get_tsc_cycles() + p->timer_period;
+	struct thread *t;
+	uint64_t n_pipelines;
+	uint32_t thread_id, i;
 
-	t->n_pipelines++;
+	/* Check input params */
+	if (!p)
+		return;
 
-	/* Response */
-	rsp->status = 0;
-	return rsp;
-}
+	/* Find the thread that runs this pipeline. */
+	thread_id = pipeline_find(p);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
 
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_disable(struct thread_data *t,
-	struct thread_msg_req *req)
-{
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	uint32_t n_pipelines = t->n_pipelines;
-	struct rte_swx_pipeline *pipeline = req->pipeline_disable.p;
-	uint32_t i;
+	t = &threads[thread_id];
+	n_pipelines = t->n_pipelines;
 
-	/* find pipeline */
 	for (i = 0; i < n_pipelines; i++) {
-		struct pipeline_data *p = &t->pipeline_data[i];
+		struct rte_swx_pipeline *pipeline = t->pipelines[i];
 
-		if (p->p != pipeline)
+		if (pipeline != p)
 			continue;
 
 		if (i < n_pipelines - 1) {
-			struct rte_swx_pipeline *pipeline_last =
-				t->p[n_pipelines - 1];
-			struct pipeline_data *p_last =
-				&t->pipeline_data[n_pipelines - 1];
+			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
 
-			t->p[i] = pipeline_last;
-			memcpy(p, p_last, sizeof(*p));
+			t->pipelines[i] = pipeline_last;
 		}
 
-		t->n_pipelines--;
+		rte_wmb();
+		t->n_pipelines = n_pipelines - 1;
 
-		rsp->status = 0;
-		return rsp;
+		return;
 	}
 
-	/* should not get here */
-	rsp->status = 0;
-	return rsp;
-}
-
-static void
-thread_msg_handle(struct thread_data *t)
-{
-	for ( ; ; ) {
-		struct thread_msg_req *req;
-		struct thread_msg_rsp *rsp;
-
-		req = thread_msg_recv(t->msgq_req);
-		if (req == NULL)
-			break;
-
-		switch (req->type) {
-		case THREAD_REQ_PIPELINE_ENABLE:
-			rsp = thread_msg_handle_pipeline_enable(t, req);
-			break;
-
-		case THREAD_REQ_PIPELINE_DISABLE:
-			rsp = thread_msg_handle_pipeline_disable(t, req);
-			break;
-
-		default:
-			rsp = (struct thread_msg_rsp *) req;
-			rsp->status = -1;
-		}
-
-		thread_msg_send(t->msgq_rsp, rsp);
-	}
+	return;
 }
 
 /**
- * Data plane threads: main
+ * Data plane (DP) threads.
+ *
+ * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
+ * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
+ * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
+ * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
+ * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
+ * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
+ * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
+ * marked as volatile.
  */
 int
 thread_main(void *arg __rte_unused)
 {
-	struct thread_data *t;
-	uint32_t thread_id, i;
+	struct thread *t;
+	uint32_t thread_id;
 
 	thread_id = rte_lcore_id();
-	t = &thread_data[thread_id];
-
-	/* Dispatch loop */
-	for (i = 0; ; i++) {
-		uint32_t j;
-
-		/* Data Plane */
-		for (j = 0; j < t->n_pipelines; j++)
-			rte_swx_pipeline_run(t->p[j], PIPELINE_INSTR_QUANTA);
+	t = &threads[thread_id];
 
-		/* Control Plane */
-		if ((i & 0xF) == 0) {
-			uint64_t time = rte_get_tsc_cycles();
-			uint64_t time_next_min = UINT64_MAX;
-
-			if (time < t->time_next_min)
-				continue;
-
-			/* Thread message queues */
-			{
-				uint64_t time_next = t->time_next;
-
-				if (time_next <= time) {
-					thread_msg_handle(t);
-					time_next = time + t->timer_period;
-					t->time_next = time_next;
-				}
-
-				if (time_next < time_next_min)
-					time_next_min = time_next;
-			}
+	/* Dispatch loop. */
+	for ( ; ; ) {
+		uint32_t i;
 
-			t->time_next_min = time_next_min;
-		}
+		/* Pipelines. */
+		for (i = 0; i < t->n_pipelines; i++)
+			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 712cb25bbb..338d480abb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -9,18 +9,21 @@
 
 #include <rte_swx_pipeline.h>
 
+/**
+ * Control plane (CP) thread.
+ */
 int
-thread_pipeline_enable(uint32_t thread_id,
-		       struct rte_swx_pipeline *p,
-		       uint32_t timer_period_ms);
+thread_init(void);
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-			struct rte_swx_pipeline *p);
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 
-int
-thread_init(void);
+void
+pipeline_disable(struct rte_swx_pipeline *p);
 
+/**
+ * Data plane (DP) threads.
+ */
 int
 thread_main(void *arg);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 09/11] examples/pipeline: support blocks other than pipelines
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (7 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
                     ` (2 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the data plane threads only supported the execution of
pipelines assigned to them through configuration updates. Now, the
data plane threads also support running blocks such as IPsec.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/thread.c | 143 +++++++++++++++++++++++++++++++++++++
 examples/pipeline/thread.h |   9 +++
 2 files changed, 152 insertions(+)

diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 3001bc0858..dc3ea73fbf 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -16,6 +16,10 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
+#ifndef THREAD_BLOCKS_MAX
+#define THREAD_BLOCKS_MAX                                  256
+#endif
+
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -38,9 +42,16 @@
  *  - Read-write by the CP thread;
  *  - Read-only by the DP thread.
  */
+struct block {
+	block_run_f block_func;
+	void *block;
+};
+
 struct thread {
 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	struct block *blocks[THREAD_BLOCKS_MAX];
 	volatile uint64_t n_pipelines;
+	volatile uint64_t n_blocks;
 	int enabled;
 } __rte_cache_aligned;
 
@@ -53,14 +64,43 @@ int
 thread_init(void)
 {
 	uint32_t thread_id;
+	int status = 0;
 
 	RTE_LCORE_FOREACH_WORKER(thread_id) {
 		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
 		t->enabled = 1;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			struct block *b;
+
+			b = calloc(1, sizeof(struct block));
+			if (!b) {
+				status = -ENOMEM;
+				goto error;
+			}
+
+			t->blocks[i] = b;
+		}
 	}
 
 	return 0;
+
+error:
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		t->enabled = 0;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			free(t->blocks[i]);
+			t->blocks[i] = NULL;
+		}
+	}
+
+	return status;
 }
 
 static uint32_t
@@ -83,6 +123,26 @@ pipeline_find(struct rte_swx_pipeline *p)
 	return thread_id;
 }
 
+static uint32_t
+block_find(void *b)
+{
+	uint32_t thread_id;
+
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		if (!t->enabled)
+			continue;
+
+		for (i = 0; i < t->n_blocks; i++)
+			if (t->blocks[i]->block == b)
+				break;
+	}
+
+	return thread_id;
+}
+
 /**
  * Enable a given pipeline to run on a specific DP thread.
  *
@@ -201,9 +261,85 @@ pipeline_disable(struct rte_swx_pipeline *p)
 	return;
 }
 
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+
+	/* Check input params */
+	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
+
+	if (block_find(block) < RTE_MAX_LCORE)
+		return -EEXIST;
+
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
+
+	n_blocks = t->n_blocks;
+
+	/* Check there is room for at least one more block. */
+	if (n_blocks >= THREAD_BLOCKS_MAX)
+		return -ENOSPC;
+
+	/* Install the new block. */
+	t->blocks[n_blocks]->block_func = block_func;
+	t->blocks[n_blocks]->block = block;
+
+	rte_wmb();
+	t->n_blocks = n_blocks + 1;
+
+	return 0;
+}
+
+void
+block_disable(void *block)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+	uint32_t thread_id, i;
+
+	/* Check input params */
+	if (!block)
+		return;
+
+	/* Find the thread that runs this block. */
+	thread_id = block_find(block);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
+
+	t = &threads[thread_id];
+	n_blocks = t->n_blocks;
+
+	for (i = 0; i < n_blocks; i++) {
+		struct block *b = t->blocks[i];
+
+		if (block != b->block)
+			continue;
+
+		if (i < n_blocks - 1) {
+			struct block *block_last = t->blocks[n_blocks - 1];
+
+			t->blocks[i] = block_last;
+		}
+
+		rte_wmb();
+		t->n_blocks = n_blocks - 1;
+
+		rte_wmb();
+		t->blocks[n_blocks - 1] = b;
+
+		return;
+	}
+}
+
 /**
  * Data plane (DP) threads.
  *
+
+
  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
@@ -229,6 +365,13 @@ thread_main(void *arg __rte_unused)
 		/* Pipelines. */
 		for (i = 0; i < t->n_pipelines; i++)
 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
+
+		/* Blocks. */
+		for (i = 0; i < t->n_blocks; i++) {
+			struct block *b = t->blocks[i];
+
+			b->block_func(b->block);
+		}
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 338d480abb..f2e643def5 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -21,6 +21,15 @@ pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 void
 pipeline_disable(struct rte_swx_pipeline *p);
 
+typedef void
+(*block_run_f)(void *block);
+
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id);
+
+void
+block_disable(void *block);
+
 /**
  * Data plane (DP) threads.
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 10/11] examples/pipeline: add block enable/disable CLI commands
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (8 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-12 18:53   ` [PATCH V4 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  2023-01-26  9:17   ` [PATCH V4 00/11] pipeline: add IPsec support Thomas Monjalon
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands to enable/disable block execution on data plane
threads.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 68bb93a46c..37250b7072 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3250,6 +3250,134 @@ cmd_pipeline_disable(char **tokens,
 	pipeline_disable(p);
 }
 
+static const char cmd_block_enable_help[] =
+"block type <block_type> instance <block_name> enable thread <thread_id>\n";
+
+static void
+cmd_block_enable(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	block_run_f block_func = NULL;
+	void *block = NULL;
+	uint32_t thread_id;
+	int status;
+
+	if (n_tokens != 8) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "enable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+		return;
+	}
+
+	if (strcmp(tokens[6], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
+
+	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block_func = (block_run_f)rte_swx_ipsec_run;
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	status = block_enable(block_func, block, thread_id);
+	if (status) {
+		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
+		return;
+	}
+}
+
+static const char cmd_block_disable_help[] =
+"block type <block_type> instance <block_name> disable\n";
+
+static void
+cmd_block_disable(char **tokens,
+		  uint32_t n_tokens,
+		  char *out,
+		  size_t out_size,
+		  void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	void *block = NULL;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "disable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	block_disable(block);
+}
+
 static void
 cmd_help(char **tokens,
 	 uint32_t n_tokens,
@@ -3298,6 +3426,8 @@ cmd_help(char **tokens,
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
+			"\tblock enable\n"
+			"\tblock disable\n"
 			);
 		return;
 	}
@@ -3553,6 +3683,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
+		return;
+	}
+
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3812,6 +3954,18 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "block")) {
+		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
+			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
+			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V4 11/11] examples/pipeline: add IPsec example
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (9 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
@ 2023-01-12 18:53   ` Cristian Dumitrescu
  2023-01-26  9:17   ` [PATCH V4 00/11] pipeline: add IPsec support Thomas Monjalon
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-12 18:53 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add example files to illustrate the pipeline IPsec support.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ipsec.cli    |  57 +++++++
 examples/pipeline/examples/ipsec.io     |  23 +++
 examples/pipeline/examples/ipsec.spec   | 138 +++++++++++++++
 examples/pipeline/examples/ipsec_sa.txt | 216 ++++++++++++++++++++++++
 4 files changed, 434 insertions(+)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt

diff --git a/examples/pipeline/examples/ipsec.cli b/examples/pipeline/examples/ipsec.cli
new file mode 100644
index 0000000000..8cb5bf4239
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.cli
@@ -0,0 +1,57 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 --vdev crypto_aesni_mb0 -- -s ./examples/pipeline/examples/ipsec.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/ipsec.spec /tmp/ipsec.c
+pipeline libbuild /tmp/ipsec.c /tmp/ipsec.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 128 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+cryptodev crypto_aesni_mb0 queues 1 qsize 128
+ring RING0 size 1024 numa 0
+ring RING1 size 1024 numa 0
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/ipsec.so io ./examples/pipeline/examples/ipsec.io numa 0
+
+;
+; List of IPsec devices.
+;
+ipsec IPSEC0 create in RING0 out RING1 cryptodev crypto_aesni_mb0 cryptoq 0 bsz 32 32 32 32 samax 512 numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+//pipeline PIPELINE0 table policy_table add ./examples/pipeline/examples/ipsec_policy_table.txt
+//pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/ipsec_routing_table.txt
+//pipeline PIPELINE0 table nexthop_table add ./examples/pipeline/examples/ipsec_nexthop_table.txt
+//pipeline PIPELINE0 commit
+
+ipsec IPSEC0 sa add ./examples/pipeline/examples/ipsec_sa.txt
+
+;
+; Pipelines and blocks mapping to CPU threads.
+;
+pipeline PIPELINE0 enable thread 1
+block type ipsec instance IPSEC0 enable thread 1
diff --git a/examples/pipeline/examples/ipsec.io b/examples/pipeline/examples/ipsec.io
new file mode 100644
index 0000000000..f5a3fcf961
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.io
@@ -0,0 +1,23 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ring RING1 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ring RING0 bsz 32
diff --git a/examples/pipeline/examples/ipsec.spec b/examples/pipeline/examples/ipsec.spec
new file mode 100644
index 0000000000..09aa831881
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.spec
@@ -0,0 +1,138 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_h {
+	bit<8> ver_ihl
+	bit<8> diffserv
+	bit<16> total_len
+	bit<16> identification
+	bit<16> flags_offset
+	bit<8> ttl
+	bit<8> protocol
+	bit<16> hdr_checksum
+	bit<32> src_addr
+	bit<32> dst_addr
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+struct ipsec_internal_h {
+	bit<32> sa_id
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+header ipsec_internal instanceof ipsec_internal_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port_in
+	bit<32> port_out
+
+	bit<32> src_addr
+	bit<32> dst_addr
+	bit<8> protocol
+	bit<16> src_port
+	bit<16> dst_port
+}
+
+metadata instanceof metadata_t
+
+//
+// Actions
+//
+struct encrypt_args_t {
+	bit<32> sa_id
+}
+
+action encrypt args instanceof encrypt_args_t {
+	//Set the IPsec internal header.
+	validate h.ipsec_internal
+	mov h.ipsec_internal.sa_id t.sa_id
+
+	return
+}
+
+action drop args none {
+	drop
+}
+
+//
+// Tables.
+//
+table policy_table {
+	key {
+		m.src_addr exact
+		m.dst_addr exact
+		m.protocol exact
+		m.src_port exact
+		m.dst_port exact
+	}
+
+	actions {
+		encrypt
+		drop
+	}
+
+	default_action encrypt args sa_id 0
+	size 65536
+}
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port_in
+
+	jmpeq FROM_IPSEC_TO_NET m.port_in 1
+
+FROM_NET_TO_IPSEC : extract h.ethernet
+
+	extract h.ipv4
+	mov m.src_addr h.ipv4.src_addr
+	mov m.dst_addr h.ipv4.dst_addr
+	mov m.protocol h.ipv4.protocol
+
+	extract h.udp
+	mov m.src_port h.udp.src_port
+	mov m.dst_port h.udp.dst_port
+
+	table policy_table
+
+	mov m.port_out 1
+
+	emit h.ipsec_internal
+	emit h.ipv4
+	emit h.udp
+	tx m.port_out
+
+FROM_IPSEC_TO_NET : extract h.ipv4
+
+	validate h.ethernet
+	mov h.ethernet.dst_addr 0xa0b0c0d0e0f0
+	mov h.ethernet.src_addr 0xa1b1c1d1e1f1
+	mov h.ethernet.ethertype 0x0800
+
+	mov m.port_out 0
+
+	emit h.ethernet
+	emit h.ipv4
+	tx m.port_out
+}
diff --git a/examples/pipeline/examples/ipsec_sa.txt b/examples/pipeline/examples/ipsec_sa.txt
new file mode 100644
index 0000000000..37bfd8a9ce
--- /dev/null
+++ b/examples/pipeline/examples/ipsec_sa.txt
@@ -0,0 +1,216 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+// IPsec SA syntax:
+//
+// <sa>
+//    : encrypt <crypto_params> <encap_params>
+//    | decrypt <crypto_params> <encap_params>
+//    ;
+//
+// <crypto_params>
+//    : <cipher> <auth>
+//    | <aead>
+//    ;
+//
+// <cipher>
+//    : cipher <ciher_alg> key <cipher_key>
+//    | cipher <cipher_alg>
+//    ;
+//
+// <auth>
+//    : auth <authentication_alg> key <authentication_key>
+//    | auth <authentication_alg>
+//    ;
+//
+// <aead>
+//    : aead <aead_alg> key <aead_key>
+//    ;
+//
+// <encap_params>
+//    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+//    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+//    | esp spi <spi> transport
+//    ;
+
+//
+// cipher = null, auth = null
+//
+encrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth null esp spi 100 transport
+decrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth null esp spi 100 transport
+
+//
+// aes-gcm-128
+//
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-gcm-192
+//
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-gcm-256
+//
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// aes-ccm-128
+//
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-ccm-192
+//
+// Note: Currently not supported by the "aesni_mb" library.
+//
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-ccm-256
+//
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// chacha20-poly1305
+//
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// cipher = aes-cbc-128, auth = null
+//
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-192, auth = null
+//
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-256, auth = null
+//
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-128, auth = null
+//
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-192, auth = null
+//
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-256, auth = null
+//
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+
+//
+// cipher = null, auth = sha1-hmac
+//
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// cipher = null, auth = sha256-hmac
+//
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+
+//
+// cipher = null, auth = sha384-hmac
+//
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+
+//
+// cipher = null, auth = sha512-hmac
+//
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH V4 00/11] pipeline: add IPsec support
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (10 preceding siblings ...)
  2023-01-12 18:53   ` [PATCH V4 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
@ 2023-01-26  9:17   ` Thomas Monjalon
  2023-01-26 13:11     ` Dumitrescu, Cristian
  11 siblings, 1 reply; 87+ messages in thread
From: Thomas Monjalon @ 2023-01-26  9:17 UTC (permalink / raw)
  To: Cristian Dumitrescu; +Cc: dev

12/01/2023 19:53, Cristian Dumitrescu:
> This patch set introduces a companion block for the SWX pipeline for
> IPsec support.

Please can you make sure there is no error in CI?

I see this problem on CentOS 7.9:
examples/pipeline/cli.c:298:9: error: missing braces around initializer



^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH V4 00/11] pipeline: add IPsec support
  2023-01-26  9:17   ` [PATCH V4 00/11] pipeline: add IPsec support Thomas Monjalon
@ 2023-01-26 13:11     ` Dumitrescu, Cristian
  0 siblings, 0 replies; 87+ messages in thread
From: Dumitrescu, Cristian @ 2023-01-26 13:11 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev



> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Thursday, January 26, 2023 9:17 AM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> Cc: dev@dpdk.org
> Subject: Re: [PATCH V4 00/11] pipeline: add IPsec support
> 
> 12/01/2023 19:53, Cristian Dumitrescu:
> > This patch set introduces a companion block for the SWX pipeline for
> > IPsec support.
> 
> Please can you make sure there is no error in CI?
> 
> I see this problem on CentOS 7.9:
> examples/pipeline/cli.c:298:9: error: missing braces around initializer
> 

Yes, will send V5 in a few minutes.

This is valid C syntax, but it looks like the old (i.e. from 2015) gcc 4.8.5 that comes with CentOS 7.9 complains about it.

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 00/11] pipeline: add IPsec support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (14 preceding siblings ...)
  2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-26 13:34 ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 01/11] " Cristian Dumitrescu
                     ` (10 more replies)
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
  16 siblings, 11 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev

This patch set introduces a companion block for the SWX pipeline for
IPsec support.

The IPsec block is external to the pipeline, hence it needs to be
explicitly instantiated by the user and connected to a pipeline
instance through the pipeline I/O ports.

Main features:
* IPsec inbound (encrypted input packets -> clear text output packets)
and outbound (clear text input packets -> encrypted output packets)
processing support for tunnel and transport modes.

Interaction of the IPsec block with the pipeline:
* Each IPsec block instance has its own set of Security Associations
(SAs) used to process the input packets. Each SA is identified by its
unique SA ID. The IPsec inbound and outbound SAs share the same ID
space.
* Each input packet is first mapped to one of the existing SAs by
using the SA ID and then processed according to the identified SA. The
SA ID is read from input packet. The SA ID field is typically written
by the pipeline before sending the packet to the IPsec block.

Change log:

V5:
Fixed build issue for gcc 4.8.5.

V4:
Fixed Doxygen issues.

V3:
Rebased on top of main latest.

V2:
Fixed minor style issues.

Cristian Dumitrescu (11):
  pipeline: add IPsec support
  examples/pipeline: rework memory pool support
  examples/pipeline: streamline ring support
  examples/pipeline: streamline the Ethernet device support
  examples/pipeline: support crypto devices
  examples/pipeline: add CLI command for crypto device
  examples/pipeline: add IPsec CLI commands
  examples/pipeline: rework the thread configuration updates
  examples/pipeline: support blocks other than pipelines
  examples/pipeline: add block enable/disable CLI commands
  examples/pipeline: add IPsec example

 examples/pipeline/cli.c                       |  896 ++++++--
 examples/pipeline/examples/fib.cli            |    4 +-
 examples/pipeline/examples/hash_func.cli      |    4 +-
 examples/pipeline/examples/ipsec.cli          |   57 +
 examples/pipeline/examples/ipsec.io           |   23 +
 examples/pipeline/examples/ipsec.spec         |  138 ++
 examples/pipeline/examples/ipsec_sa.txt       |  216 ++
 examples/pipeline/examples/l2fwd.cli          |    4 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |    4 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |    4 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |    4 +-
 examples/pipeline/examples/learner.cli        |    4 +-
 examples/pipeline/examples/meter.cli          |    4 +-
 examples/pipeline/examples/mirroring.cli      |    4 +-
 examples/pipeline/examples/recirculation.cli  |    4 +-
 examples/pipeline/examples/registers.cli      |    4 +-
 examples/pipeline/examples/selector.cli       |    4 +-
 examples/pipeline/examples/varbit.cli         |    4 +-
 examples/pipeline/examples/vxlan.cli          |    4 +-
 examples/pipeline/examples/vxlan_pcap.cli     |    4 +-
 examples/pipeline/main.c                      |   12 +-
 examples/pipeline/obj.c                       |  361 +---
 examples/pipeline/obj.h                       |  100 +-
 examples/pipeline/thread.c                    |  655 +++---
 examples/pipeline/thread.h                    |   24 +-
 lib/pipeline/meson.build                      |    4 +-
 lib/pipeline/rte_swx_ipsec.c                  | 1821 +++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h                  |  383 ++++
 lib/pipeline/version.map                      |    9 +
 29 files changed, 3741 insertions(+), 1018 deletions(-)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 01/11] pipeline: add IPsec support
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

This block is providing IPsec support to the SWX pipeline. The IPsec
block is external to the pipeline, so it needs to be explicitly
instantiated and connected to a pipeline through the I/O ports.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 lib/pipeline/meson.build     |    4 +-
 lib/pipeline/rte_swx_ipsec.c | 1821 ++++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h |  383 +++++++
 lib/pipeline/version.map     |    9 +
 4 files changed, 2216 insertions(+), 1 deletion(-)
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build
index 3ca98ed194..aa3fd0c2b8 100644
--- a/lib/pipeline/meson.build
+++ b/lib/pipeline/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'rte_pipeline.c',
         'rte_port_in_action.c',
         'rte_table_action.c',
+	'rte_swx_ipsec.c',
         'rte_swx_pipeline.c',
         'rte_swx_pipeline_spec.c',
         'rte_swx_ctl.c',
@@ -19,8 +20,9 @@ headers = files(
         'rte_pipeline.h',
         'rte_port_in_action.h',
         'rte_table_action.h',
+	'rte_swx_ipsec.h',
         'rte_swx_pipeline.h',
         'rte_swx_extern.h',
         'rte_swx_ctl.h',
 )
-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']
+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']
diff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c
new file mode 100644
index 0000000000..b23056e23e
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.c
@@ -0,0 +1,1821 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_cryptodev.h>
+#include <rte_ipsec.h>
+
+#include "rte_swx_ipsec.h"
+
+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
+
+#include <rte_malloc.h>
+
+static void *
+env_calloc(size_t size, size_t alignment, int numa_node)
+{
+	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
+}
+
+static void
+env_free(void *start, size_t size __rte_unused)
+{
+	rte_free(start);
+}
+
+#else
+
+#include <numa.h>
+
+static void *
+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
+{
+	void *start;
+
+	if (numa_available() == -1)
+		return NULL;
+
+	start = numa_alloc_onnode(size, numa_node);
+	if (!start)
+		return NULL;
+
+	memset(start, 0, size);
+	return start;
+}
+
+static void
+env_free(void *start, size_t size)
+{
+	if ((numa_available() == -1) || !start)
+		return;
+
+	numa_free(start, size);
+}
+
+#endif
+
+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
+#endif
+
+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
+ * the mempool size to be at least 1.5 times the size of the mempool cache.
+ */
+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
+
+struct ipsec_sa {
+	struct rte_ipsec_session s;
+	int valid;
+};
+
+struct ipsec_pkts_in {
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_cops;
+};
+
+struct ipsec_pkts_out {
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_pkts;
+};
+
+struct rte_swx_ipsec {
+	/*
+	 * Parameters.
+	 */
+
+	/* IPsec instance name. */
+	char name[RTE_SWX_IPSEC_NAME_SIZE];
+
+	/* Input packet queue. */
+	struct rte_ring *ring_in;
+
+	/* Output packet queue. */
+	struct rte_ring *ring_out;
+
+	/* Crypto device ID. */
+	uint8_t dev_id;
+
+	/* Crypto device queue pair ID. */
+	uint16_t qp_id;
+
+	/* Burst sizes. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/* SA table size. */
+	size_t n_sa_max;
+
+	/*
+	 * Internals.
+	 */
+	/* Crypto device buffer pool for sessions. */
+	struct rte_mempool *mp_session;
+
+	/* Pre-crypto packets. */
+	struct ipsec_pkts_in in;
+
+	/* Post-crypto packets. */
+	struct ipsec_pkts_out out;
+
+	/* Crypto device enqueue threshold. */
+	uint32_t crypto_wr_threshold;
+
+	/* Packets currently under crypto device processing. */
+	uint32_t n_pkts_crypto;
+
+	/* List of free SADB positions. */
+	uint32_t *sa_free_id;
+
+	/* Number of elements in the SADB list of free positions. */
+	size_t n_sa_free_id;
+
+	/* Allocated memory total size in bytes. */
+	size_t total_size;
+
+	/* Flag for registration to the global list of instances. */
+	int registered;
+
+	/*
+	 * Table memory.
+	 */
+	uint8_t memory[] __rte_cache_aligned;
+};
+
+static inline struct ipsec_sa *
+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
+{
+	struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
+
+	return &sadb[sa_id & (ipsec->n_sa_max - 1)];
+}
+
+/* Global list of instances. */
+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ipsec_tailq = {
+	.name = "RTE_SWX_IPSEC",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
+
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name ||
+	    !name[0] ||
+	    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
+		return NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ipsec;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ipsec_register(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ipsec;
+	TAILQ_INSERT_TAIL(ipsec_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ipsec_unregister(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		if (te->data == (void *)ipsec) {
+			TAILQ_REMOVE(ipsec_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
+
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
+{
+	size_t i;
+
+	if (!ipsec)
+		return;
+
+	/* Remove the current instance from the global list. */
+	if (ipsec->registered)
+		ipsec_unregister(ipsec);
+
+	/* SADB. */
+	for (i = 0; i < ipsec->n_sa_max; i++) {
+		struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
+
+		if (!sa->valid)
+			continue;
+
+		/* SA session. */
+		ipsec_session_free(ipsec, &sa->s);
+	}
+
+	/* Crypto device buffer pools. */
+	rte_mempool_free(ipsec->mp_session);
+
+	/* IPsec object memory. */
+	env_free(ipsec, ipsec->total_size);
+}
+
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node)
+{
+	char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
+	struct rte_swx_ipsec *ipsec = NULL;
+	struct rte_ring *ring_in, *ring_out;
+	struct rte_cryptodev_info dev_info;
+	size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
+	uint32_t dev_session_size;
+	int dev_id, status = 0;
+
+	/* Check input parameters. */
+	if (!ipsec_out ||
+	    !name ||
+	    !name[0] ||
+	    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
+	    !params ||
+	    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    !params->n_sa_max) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_in = rte_ring_lookup(params->ring_in_name);
+	if (!ring_in) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_out = rte_ring_lookup(params->ring_out_name);
+	if (!ring_out) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
+	if (dev_id == -1) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Memory allocation. */
+	n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
+
+	sadb_offset = sizeof(struct rte_swx_ipsec);
+	sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
+
+	sa_free_id_offset = sadb_offset + sadb_size;
+	sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
+
+	total_size = sa_free_id_offset + sa_free_id_size;
+	ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
+	if (!ipsec) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Initialization. */
+	strcpy(ipsec->name, name);
+	ipsec->ring_in = ring_in;
+	ipsec->ring_out = ring_out;
+	ipsec->dev_id = (uint8_t)dev_id;
+	ipsec->qp_id = params->crypto_dev_queue_pair_id;
+	memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
+	ipsec->n_sa_max = n_sa_max;
+
+	ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
+
+	ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
+	for (i = 0; i < n_sa_max; i++)
+		ipsec->sa_free_id[i] = n_sa_max - 1 - i;
+	ipsec->n_sa_free_id = n_sa_max;
+
+	ipsec->total_size = total_size;
+
+	/* Crypto device memory pools. */
+	dev_session_size = rte_cryptodev_sym_get_private_session_size((uint8_t)dev_id);
+
+	snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
+	ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		dev_session_size, /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool element private data size */
+		numa_node);
+	if (!ipsec->mp_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Add the current instance to the global list. */
+	status = ipsec_register(ipsec);
+	if (status)
+		goto error;
+
+	ipsec->registered = 1;
+
+	*ipsec_out = ipsec;
+	return 0;
+
+error:
+	rte_swx_ipsec_free(ipsec);
+	return status;
+}
+
+static inline int
+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
+{
+	struct ipsec_sa *sa;
+	struct rte_ipsec_group *g;
+	int n_groups, n_pkts_in_group, i;
+
+	sa = ipsec->in.sa[0];
+
+	g = &ipsec->in.groups[0];
+	g->id.ptr = sa;
+	g->m = &ipsec->in.pkts[0];
+	n_pkts_in_group = 1;
+	n_groups = 1;
+
+	for (i = 1; i < n_pkts; i++) {
+		struct ipsec_sa *sa_new = ipsec->in.sa[i];
+
+		/* Same SA => Add the current pkt to the same group. */
+		if (sa_new == sa) {
+			n_pkts_in_group++;
+			continue;
+		}
+
+		/* Different SA => Close the current group & add the current pkt to a new group. */
+		g->cnt = n_pkts_in_group;
+		sa = sa_new;
+
+		g++;
+		g->id.ptr = sa;
+		g->m = &ipsec->in.pkts[i];
+		n_pkts_in_group = 1;
+		n_groups++;
+	}
+
+	/* Close the last group. */
+	g->cnt = n_pkts_in_group;
+
+	return n_groups;
+}
+
+static inline void
+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
+{
+	struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
+	struct rte_crypto_op **src = ipsec->in.group_cops;
+
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
+	uint32_t n_dst = ipsec->in.n_cops;
+	uint32_t n_dst_max = ipsec->bsz.crypto_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_cops;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
+	 * instead of moving the SRC to DST first and then using DST.
+	 */
+	if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
+		uint16_t n_ok;
+
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
+		ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
+
+		for (i = n_ok; i < n_src; i++) {
+			struct rte_crypto_op *cop = src[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		return;
+	}
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
+		n_pkts_crypto += n_ok;
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_crypto_op *cop = dst0[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	ipsec->n_pkts_crypto = n_pkts_crypto;
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->in.n_cops = n_dst;
+}
+
+/**
+ * Packet buffer anatomy:
+ *
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | Offset   | Size    | Description                                                              |
+ * | (Byte #) | (Bytes) |                                                                          |
+ * +==========+=========+==========================================================================+
+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |
+ * |          |         | The buf_addr field points to the start of the packet section.            |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 256      |         | Packet section.                                                          |
+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |
+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |
+ * +----------+---------+--------------------------------------------------------------------------+
+ */
+struct ipsec_mbuf {
+	struct ipsec_sa *sa;
+	struct rte_crypto_op cop;
+	struct rte_crypto_sym_op sym_cop;
+	uint8_t buffer[32]; /* The crypto IV is placed here. */
+};
+
+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
+
+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
+
+static inline void
+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
+{
+	int n_pkts, n_groups, i;
+
+	/* Read packets from the input ring. */
+	n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
+					   (void **)ipsec->in.pkts,
+					   ipsec->bsz.ring_rd,
+					   NULL);
+	if (!n_pkts)
+		return;
+
+	/* Get the SA for each packet. */
+	for (i = 0; i < n_pkts; i++) {
+		struct rte_mbuf *m = ipsec->in.pkts[i];
+		struct rte_swx_ipsec_input_packet_metadata *meta;
+		struct rte_ipv4_hdr *ipv4_hdr;
+		uint32_t sa_id;
+
+		meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
+
+		/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
+		sa_id = ntohl(meta->sa_id);
+
+		/* Consume the IPsec meta-data. */
+		m->data_off += META_LENGTH;
+		m->data_len -= META_LENGTH;
+		m->pkt_len -= META_LENGTH;
+
+		/* Set the fields required by the IPsec library. */
+		m->l2_len = 0;
+		m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
+			sizeof(struct rte_ipv4_hdr) :
+			sizeof(struct rte_ipv6_hdr);
+
+		/* Get the SA. */
+		ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
+	}
+
+	/* Group packets that share the same SA. */
+	n_groups = ipsec_sa_group(ipsec, n_pkts);
+
+	/* Write each group of packets sharing the same SA to the crypto device. */
+	for (i = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->in.groups[i];
+		struct ipsec_sa *sa = g->id.ptr;
+		struct rte_ipsec_session *s = &sa->s;
+		uint32_t j;
+		uint16_t n_pkts_ok;
+
+		/* Prepare the crypto ops for the current group. */
+		for (j = 0; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+			struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
+
+			priv->sa = sa;
+			ipsec->in.group_cops[j] = &priv->cop;
+		}
+
+		n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the crypto ops of the current group to the crypto device. */
+		ipsec_crypto_enqueue(ipsec, n_pkts_ok);
+	}
+}
+
+static inline void
+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
+{
+	struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
+	struct rte_mbuf **src = g->m;
+
+	uint32_t n_dst = ipsec->out.n_pkts;
+	uint32_t n_dst_max = ipsec->bsz.ring_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_pkts;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_mbuf *m = dst[i];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->out.n_pkts = n_dst;
+}
+
+static inline void
+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
+{
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, ng, i;
+
+	/* Read the crypto ops from the crypto device. */
+	if (!n_pkts_crypto)
+		return;
+
+	n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
+					     ipsec->qp_id,
+					     ipsec->out.cops,
+					     ipsec->bsz.crypto_rd);
+	if (!n_pkts)
+		return;
+
+	ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
+
+	/* Group packets that share the same SA. */
+	ng = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
+					      ipsec->out.group_pkts,
+					      ipsec->out.groups,
+					      n_pkts);
+
+	/* Perform post-crypto IPsec processing for each group of packets that share the same SA.
+	 * Write each group of packets to the output ring.
+	 */
+	for (i = 0, n_pkts = 0; i < ng; i++) {
+		struct rte_ipsec_group *g = &ipsec->out.groups[i];
+		struct rte_ipsec_session *s = g->id.ptr;
+		uint32_t n_pkts_ok, j;
+
+		/* Perform post-crypto IPsec processing for the current group. */
+		n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the packets of the current group to the output ring. */
+		ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
+	}
+}
+
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
+{
+	rte_swx_ipsec_pre_crypto(ipsec);
+	rte_swx_ipsec_post_crypto(ipsec);
+}
+
+/**
+ * IPsec Control Plane API
+ */
+struct cipher_alg {
+	const char *name;
+	enum rte_crypto_cipher_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t key_size;
+};
+
+struct auth_alg {
+	const char *name;
+	enum rte_crypto_auth_algorithm alg;
+	uint32_t iv_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+};
+
+struct aead_alg {
+	const char *name;
+	enum rte_crypto_aead_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+	uint32_t aad_size;
+};
+
+static struct cipher_alg cipher_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_CIPHER_NULL,
+		.iv_size = 0,
+		.block_size = 4,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "aes-cbc-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 16,
+	},
+
+	[2] = {
+		.name = "aes-cbc-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 24,
+	},
+
+	[3] = {
+		.name = "aes-cbc-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 32,
+	},
+
+	[4] = {
+		.name = "aes-ctr-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+	},
+
+	[5] = {
+		.name = "aes-ctr-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 28,
+	},
+
+	[6] = {
+		.name = "aes-ctr-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 36,
+	},
+
+	[7] = {
+		.name = "3des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 24,
+	},
+
+	[8] = {
+		.name = "des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 8,
+	},
+};
+
+static struct auth_alg auth_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_AUTH_NULL,
+		.iv_size = 0,
+		.digest_size = 0,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "sha1-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 20,
+	},
+
+	[2] = {
+		.name = "sha256-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
+		.iv_size = 0,
+		.digest_size = 16,
+		.key_size = 32,
+	},
+
+	[3] = {
+		.name = "sha384-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
+		.iv_size = 0,
+		.digest_size = 24,
+		.key_size = 48,
+	},
+
+	[4] = {
+		.name = "sha512-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
+		.iv_size = 0,
+		.digest_size = 32,
+		.key_size = 64,
+	},
+
+	[5] = {
+		.name = "aes-gmac",
+		.alg = RTE_CRYPTO_AUTH_AES_GMAC,
+		.iv_size = 8,
+		.digest_size = 16,
+		.key_size = 20,
+	},
+
+	[6] = {
+		.name = "aes-xcbc-mac-96",
+		.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 16,
+	},
+};
+
+static struct aead_alg aead_algs[] = {
+	[0] = {
+		.name = "aes-gcm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[1] = {
+		.name = "aes-gcm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[2] = {
+		.name = "aes-gcm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[3] = {
+		.name = "aes-ccm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[4] = {
+		.name = "aes-ccm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[5] = {
+		.name = "aes-ccm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[6] = {
+		.name = "chacha20-poly1305",
+		.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
+		.iv_size = 12,
+		.block_size = 64,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+};
+
+static struct cipher_alg *
+cipher_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct cipher_alg *
+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static int
+char_to_hex(char c, uint8_t *val)
+{
+	if (c >= '0' && c <= '9') {
+		*val = c - '0';
+		return 0;
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		*val = c - 'A' + 10;
+		return 0;
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		*val = c - 'a' + 10;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
+{
+	uint32_t i;
+
+	/* Check input arguments. */
+	if (!src || !src[0] || !dst || !n_dst_bytes)
+		return -EINVAL;
+
+	/* Skip any leading "0x" or "0X" in the src string. */
+	if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
+		src += 2;
+
+	/* Convert each group of two hex characters in the src string to one byte in dst array. */
+	for (i = 0; i < n_dst_bytes; i++) {
+		uint8_t a, b;
+		int status;
+
+		status = char_to_hex(*src, &a);
+		if (status)
+			return status;
+		src++;
+
+		status = char_to_hex(*src, &b);
+		if (status)
+			return status;
+		src++;
+
+		dst[i] = a * 16 + b;
+	}
+
+	/* Check for the end of the src string. */
+	if (*src)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+token_is_comment(const char *token)
+{
+	if ((token[0] == '#') ||
+	    (token[0] == ';') ||
+	    ((token[0] == '/') && (token[1] == '/')))
+		return 1; /* TRUE. */
+
+	return 0; /* FALSE. */
+}
+
+#define MAX_TOKENS 64
+
+#define CHECK(condition, msg)          \
+do {                                   \
+	if (!(condition)) {            \
+		if (errmsg)            \
+			*errmsg = msg; \
+		goto error;            \
+	}                              \
+} while (0)
+
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg)
+{
+	char *token_array[MAX_TOKENS], **t;
+	struct rte_swx_ipsec_sa_params *p = NULL;
+	char *s0 = NULL, *s;
+	uint32_t n_tokens = 0;
+	int blank_or_comment = 0;
+
+	/* Check input arguments. */
+	CHECK(string && string[0], "NULL input");
+
+	/* Memory allocation. */
+	s0 = strdup(string);
+	p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
+	CHECK(s0 && p, "Not enough memory");
+
+	/* Parse the string into tokens. */
+	for (s = s0; ; ) {
+		char *token;
+
+		token = strtok_r(s, " \f\n\r\t\v", &s);
+		if (!token || token_is_comment(token))
+			break;
+
+		CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
+
+		token_array[n_tokens] = token;
+		n_tokens++;
+	}
+
+	t = token_array;
+	if (!n_tokens) {
+		blank_or_comment = 1;
+		goto error;
+	}
+
+	/*
+	 * Crypto operation.
+	 */
+	if (!strcmp(t[0], "encrypt"))
+		p->encrypt = 1;
+	else if (!strcmp(t[0], "decrypt"))
+		p->encrypt = 0;
+	else
+		CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
+
+	t++;
+	n_tokens--;
+
+	/*
+	 * Crypto parameters.
+	 */
+	CHECK(n_tokens >= 2, "Not enough tokens");
+
+	if (!strcmp(t[0], "cipher")) {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t key_size;
+
+		p->crypto.is_aead = 0;
+
+		/* cipher. */
+		cipher_alg = cipher_alg_find(t[1]);
+		CHECK(cipher_alg, "Unsupported cipher algorithm");
+
+		key_size = cipher_alg->key_size;
+		p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
+		p->crypto.cipher_auth.cipher.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
+				"Cipher algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
+			CHECK(!status, "Cipher key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+
+		/* authentication. */
+		CHECK(n_tokens >= 2, "Not enough tokens");
+		CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
+
+		auth_alg = auth_alg_find(t[1]);
+		CHECK(auth_alg, "Unsupported authentication algorithm");
+
+		key_size = auth_alg->key_size;
+		p->crypto.cipher_auth.auth.alg = auth_alg->alg;
+		p->crypto.cipher_auth.auth.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
+				"Authentication algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
+			CHECK(!status, "Authentication key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+	} else if (!strcmp(t[0], "aead")) {
+		struct aead_alg *alg;
+		uint32_t key_size;
+		int status;
+
+		p->crypto.is_aead = 1;
+
+		CHECK(n_tokens >= 4, "Not enough tokens");
+		alg = aead_alg_find(t[1]);
+		CHECK(alg, "Unsupported AEAD algorithm");
+
+		key_size = alg->key_size;
+		p->crypto.aead.alg = alg->alg;
+		p->crypto.aead.key_size = key_size;
+
+		CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
+		CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
+			"AEAD algorithm key too big");
+
+		status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
+		CHECK(!status, "AEAD key invalid format");
+
+		t += 4;
+		n_tokens -= 4;
+	} else
+		CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
+
+	/*
+	 * Packet ecapsulation parameters.
+	 */
+	CHECK(n_tokens >= 4, "Not enough tokens");
+	CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
+	CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
+
+	p->encap.esp.spi = strtoul(t[2], &t[2], 0);
+	CHECK(!t[2][0], "ESP SPI field invalid format");
+
+	t += 3;
+	n_tokens -= 3;
+
+	if (!strcmp(t[0], "tunnel")) {
+		p->encap.tunnel_mode = 1;
+
+		CHECK(n_tokens >= 6, "Not enough tokens");
+
+		if (!strcmp(t[1], "ipv4")) {
+			uint32_t addr;
+
+			p->encap.tunnel_ipv4 = 1;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			addr = strtoul(t[3], &t[3], 0);
+			CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
+			p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			addr = strtoul(t[5], &t[5], 0);
+			CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
+			p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
+
+			t += 6;
+			n_tokens -= 6;
+		} else if (!strcmp(t[1], "ipv6")) {
+			int status;
+
+			p->encap.tunnel_ipv4 = 0;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			status = hex_string_parse(t[3],
+						  p->encap.tunnel.ipv6.src_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 source address invalid format");
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			status = hex_string_parse(t[5],
+						  p->encap.tunnel.ipv6.dst_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 destination address invalid format");
+
+			t += 6;
+			n_tokens -= 6;
+		} else
+			CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
+	} else if (!strcmp(t[0], "transport")) {
+		p->encap.tunnel_mode = 0;
+
+		t++;
+		n_tokens--;
+	} else
+		CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
+
+	/*
+	 * Any other parameters.
+	 */
+	CHECK(!n_tokens, "Unexpected trailing tokens");
+
+	free(s0);
+	return p;
+
+error:
+	free(p);
+	free(s0);
+	if (is_blank_or_comment)
+		*is_blank_or_comment = blank_or_comment;
+	return NULL;
+}
+
+static void
+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv4_hdr ipv4_hdr = {
+		.version_ihl = 0x45,
+		.type_of_service = 0,
+		.total_length = 0, /* Cannot be pre-computed. */
+		.packet_id = 0,
+		.fragment_offset = 0,
+		.time_to_live = 64,
+		.next_proto_id = IPPROTO_ESP,
+		.hdr_checksum = 0, /* Cannot be pre-computed. */
+		.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
+		.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
+	};
+
+	memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
+}
+
+static void
+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv6_hdr ipv6_hdr = {
+		.vtc_flow = 0x60000000,
+		.payload_len = 0, /* Cannot be pre-computed. */
+		.proto = IPPROTO_ESP,
+		.hop_limits = 64,
+		.src_addr = {0},
+		.dst_addr = {0},
+	};
+
+	memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
+	memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
+	memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
+}
+
+/* IPsec library SA parameters. */
+static struct rte_crypto_sym_xform *
+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_crypto_sym_xform *xform,
+		uint32_t *salt_out)
+{
+	if (p->crypto.is_aead) {
+		struct aead_alg *alg;
+		uint32_t key_size, salt, iv_length;
+
+		alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
+		if (!alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		key_size = p->crypto.aead.key_size - 4;
+		memcpy(&salt, &p->crypto.aead.key[key_size], 4);
+
+		/* IV length. */
+		iv_length = 12;
+		if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
+			iv_length = 11;
+
+		/* xform. */
+		xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform[0].aead.op = p->encrypt ?
+			RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform[0].aead.algo = p->crypto.aead.alg;
+		xform[0].aead.key.data = p->crypto.aead.key;
+		xform[0].aead.key.length = key_size;
+		xform[0].aead.iv.offset = IV_OFFSET;
+		xform[0].aead.iv.length = iv_length;
+		xform[0].aead.digest_length = alg->digest_size;
+		xform[0].aead.aad_length = alg->aad_size;
+		xform[0].next = NULL;
+
+		*salt_out = salt;
+		return &xform[0];
+	} else {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
+
+		cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
+						   p->crypto.cipher_auth.cipher.key_size);
+		if (!cipher_alg)
+			return NULL;
+
+		auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
+					       p->crypto.cipher_auth.auth.key_size);
+		if (!auth_alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
+		auth_key_size = p->crypto.cipher_auth.auth.key_size;
+
+		switch (p->crypto.cipher_auth.cipher.alg) {
+		case RTE_CRYPTO_CIPHER_AES_CBC:
+		case RTE_CRYPTO_CIPHER_3DES_CBC:
+			salt = (uint32_t)rand();
+			break;
+
+		case RTE_CRYPTO_CIPHER_AES_CTR:
+			cipher_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
+			break;
+
+		default:
+			salt = 0;
+		}
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			auth_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
+		}
+
+		/* IV length. */
+		auth_iv_length = cipher_alg->iv_size;
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
+			auth_iv_length = 12;
+
+		/* xform. */
+		if (p->encrypt) {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[0].cipher.key.length = cipher_key_size;
+			xform[0].cipher.iv.offset = IV_OFFSET;
+			xform[0].cipher.iv.length = cipher_alg->iv_size;
+			xform[0].cipher.dataunit_len = 0;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+			xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[1].auth.key.length = auth_key_size;
+			xform[1].auth.iv.offset = IV_OFFSET;
+			xform[1].auth.iv.length = auth_iv_length;
+			xform[1].auth.digest_length = auth_alg->digest_size;
+			xform[1].next = NULL;
+		} else {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+			xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[0].auth.key.length = auth_key_size;
+			xform[0].auth.iv.offset = IV_OFFSET;
+			xform[0].auth.iv.length = auth_iv_length;
+			xform[0].auth.digest_length = auth_alg->digest_size;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[1].cipher.key.length = cipher_key_size;
+			xform[1].cipher.iv.offset = IV_OFFSET;
+			xform[1].cipher.iv.length = cipher_alg->iv_size;
+			xform[1].cipher.dataunit_len = 0;
+			xform[1].next = NULL;
+		}
+
+		*salt_out = salt;
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			if (p->encrypt)
+				return &xform[1];
+
+			xform[0].next = NULL;
+			return &xform[0];
+		}
+
+		return &xform[0];
+	}
+}
+
+static void
+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_security_ipsec_xform *ipsec_xform,
+		uint32_t salt)
+{
+	ipsec_xform->spi = p->encap.esp.spi;
+
+	ipsec_xform->salt = salt;
+
+	ipsec_xform->options.esn = 0;
+	ipsec_xform->options.udp_encap = 0;
+	ipsec_xform->options.copy_dscp = 1;
+	ipsec_xform->options.copy_flabel = 0;
+	ipsec_xform->options.copy_df = 0;
+	ipsec_xform->options.dec_ttl = 0;
+	ipsec_xform->options.ecn = 1;
+	ipsec_xform->options.stats = 0;
+	ipsec_xform->options.iv_gen_disable = 0;
+	ipsec_xform->options.tunnel_hdr_verify = 0;
+	ipsec_xform->options.udp_ports_verify = 0;
+	ipsec_xform->options.ip_csum_enable = 0;
+	ipsec_xform->options.l4_csum_enable = 0;
+	ipsec_xform->options.ip_reassembly_en = 0;
+	ipsec_xform->options.reserved_opts = 0;
+
+	ipsec_xform->direction = p->encrypt ?
+		RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
+		RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+
+	ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+
+	ipsec_xform->mode = p->encap.tunnel_mode ?
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
+
+	ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
+		RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
+		RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
+			ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
+			ipsec_xform->tunnel.ipv4.dscp = 0;
+			ipsec_xform->tunnel.ipv4.df = 0;
+			ipsec_xform->tunnel.ipv4.ttl = 64;
+		} else {
+			ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
+			ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
+			ipsec_xform->tunnel.ipv6.dscp = 0;
+			ipsec_xform->tunnel.ipv6.flabel = 0;
+			ipsec_xform->tunnel.ipv6.hlimit = 64;
+		}
+	}
+
+	ipsec_xform->life.packets_soft_limit = 0;
+	ipsec_xform->life.bytes_soft_limit = 0;
+	ipsec_xform->life.packets_hard_limit = 0;
+	ipsec_xform->life.bytes_hard_limit = 0;
+
+	ipsec_xform->replay_win_sz = 0;
+
+	ipsec_xform->esn.value = 0;
+
+	ipsec_xform->udp.dport = 0;
+	ipsec_xform->udp.sport = 0;
+}
+
+static int
+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
+		 struct rte_ipsec_sa_prm *sa_prm,
+		 struct rte_ipv4_hdr *ipv4_hdr,
+		 struct rte_ipv6_hdr *ipv6_hdr,
+		 struct rte_crypto_sym_xform *crypto_xform)
+{
+	uint32_t salt;
+
+	memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
+
+	sa_prm->userdata = 0; /* Not used. */
+
+	sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
+
+	/*
+	 * crypto_xform.
+	 */
+	sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
+	if (!sa_prm->crypto_xform)
+		return -EINVAL;
+
+	/*
+	 * ipsec_xform.
+	 */
+	ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
+
+	/*
+	 * tunnel / transport.
+	 *
+	 * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
+	 * the input packet type should be added to the SA configuration parameters.
+	 */
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv4_hdr;
+		} else {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv6_hdr;
+		}
+	} else {
+		sa_prm->trs.proto = IPPROTO_IPIP; /* IPv4. */
+	}
+
+	return 0;
+}
+
+static int
+ipsec_session_create(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *p,
+		     struct rte_ipsec_session *s)
+{
+	struct rte_ipv4_hdr ipv4_hdr;
+	struct rte_ipv6_hdr ipv6_hdr;
+	struct rte_crypto_sym_xform crypto_xform[2];
+	struct rte_ipsec_sa_prm sa_prm;
+	struct rte_ipsec_sa *sa = NULL;
+	struct rte_cryptodev_sym_session *crypto_session = NULL;
+	int sa_size;
+	int sa_valid = 0, status = 0;
+
+	tunnel_ipv4_header_set(&ipv4_hdr, p);
+	tunnel_ipv6_header_set(&ipv6_hdr, p);
+
+	/* IPsec library SA setup. */
+	status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
+	if (status)
+		goto error;
+
+	sa_size = rte_ipsec_sa_size(&sa_prm);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa = calloc(1, sa_size);
+	if (!sa) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa_valid = 1;
+
+	/* Cryptodev library session setup. */
+	crypto_session = rte_cryptodev_sym_session_create(ipsec->dev_id,
+							  sa_prm.crypto_xform,
+							  ipsec->mp_session);
+	if (!crypto_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* IPsec library session setup. */
+	s->sa = sa;
+	s->type = RTE_SECURITY_ACTION_TYPE_NONE;
+	s->crypto.ses = crypto_session;
+	s->crypto.dev_id = ipsec->dev_id;
+	s->pkt_func.prepare.async = NULL;
+	s->pkt_func.process = NULL;
+
+	return rte_ipsec_session_prepare(s);
+
+error:
+	/* sa. */
+	if (sa_valid)
+		rte_ipsec_sa_fini(sa);
+
+	free(sa);
+
+	/* crypto_session. */
+	if (crypto_session)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, crypto_session);
+
+	/* s. */
+	memset(s, 0, sizeof(*s));
+
+	return status;
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec,
+		   struct rte_ipsec_session *s)
+{
+	if (!s)
+		return;
+
+	/* IPsec library SA. */
+	if (s->sa)
+		rte_ipsec_sa_fini(s->sa);
+	free(s->sa);
+
+	/* Cryptodev library session. */
+	if (s->crypto.ses)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, s->crypto.ses);
+
+	/* IPsec library session. */
+	memset(s, 0, sizeof(*s));
+}
+
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *id)
+{
+	struct ipsec_sa *sa;
+	uint32_t sa_id;
+	int status;
+
+	/* Check the input parameters. */
+	if (!ipsec || !sa_params || !id)
+		return -EINVAL;
+
+	/* Allocate a free SADB entry. */
+	if (!ipsec->n_sa_free_id)
+		return -ENOSPC;
+
+	sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
+	ipsec->n_sa_free_id--;
+
+	/* Acquire the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	status = ipsec_session_create(ipsec, sa_params, &sa->s);
+	if (status) {
+		/* Free the allocated SADB entry. */
+		ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+		ipsec->n_sa_free_id++;
+
+		return status;
+	}
+
+	/* Validate the new SA. */
+	sa->valid = 1;
+	*id = sa_id;
+
+	return 0;
+}
+
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id)
+{
+	struct ipsec_sa *sa;
+
+	/* Check the input parameters. */
+	if (!ipsec || (sa_id >= ipsec->n_sa_max))
+		return;
+
+	/* Release the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	ipsec_session_free(ipsec, &sa->s);
+
+	/* Free the SADB entry. */
+	ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+	ipsec->n_sa_free_id++;
+
+	/* Invalidate the SA. */
+	sa->valid = 0;
+}
diff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h
new file mode 100644
index 0000000000..ebfb7ea5ea
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__
+#define __INCLUDE_RTE_SWX_IPSEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE SWX Internet Protocol Security (IPsec)
+ *
+ * The IPsec block is a companion block for the SWX pipeline used to provide IPsec support to the
+ * pipeline. The block is external to the pipeline, hence it needs to be explicitly instantiated by
+ * the user and connected to a pipeline instance through the pipeline I/O ports.
+ *
+ * Main features:
+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text
+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.
+ *
+ * Security Association (SA):
+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is
+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.
+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then
+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field
+ *   is typically written by the pipeline before sending the packet to the IPsec block.
+ *
+ * Packet format:
+ * - IPsec block input packet (i.e. pipeline output packet):
+ *	- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.
+ *	- IPv4 header.
+ *	- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.
+ * - IPsec block output packet (i.e. pipeline input packet):
+ *	- IPv4 header.
+ *	- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.
+ *
+ * SA update procedure:
+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().
+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().
+ * - To update an existing SA, the control plane has to follow the following steps:
+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can
+ *      fail, for example when the SA table is full.
+ *   2. Wait until no more packets are using the old SA.
+ *   3. Delete the old SA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <rte_compat.h>
+#include <rte_crypto_sym.h>
+
+/**
+ * IPsec Setup API
+ */
+
+/** IPsec instance opaque data structure. */
+struct rte_swx_ipsec;
+
+/** Name size. */
+#ifndef RTE_SWX_IPSEC_NAME_SIZE
+#define RTE_SWX_IPSEC_NAME_SIZE 64
+#endif
+
+/** Maximum burst size. */
+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX
+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256
+#endif
+
+/** IPsec burst sizes. */
+struct rte_swx_ipsec_burst_size {
+	/** Input ring read burst size. */
+	uint32_t ring_rd;
+
+	/** Output ring write burst size. */
+	uint32_t ring_wr;
+
+	/** Crypto device request queue write burst size. */
+	uint32_t crypto_wr;
+
+	/** Crypto device response queue read burst size. */
+	uint32_t crypto_rd;
+};
+
+/**
+ * IPsec instance configuration parameters
+ *
+ */
+struct rte_swx_ipsec_params {
+	/** Input packet queue. */
+	const char *ring_in_name;
+
+	/** Output packet queue.  */
+	const char *ring_out_name;
+
+	/** Crypto device name. */
+	const char *crypto_dev_name;
+
+	/** Crypto device queue pair ID. */
+	uint32_t crypto_dev_queue_pair_id;
+
+	/** Burst size. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/** Maximum number of SAs. */
+	uint32_t n_sa_max;
+};
+
+/**
+ * IPsec input packet meta-data
+ *
+ */
+struct rte_swx_ipsec_input_packet_metadata {
+	/* SA ID. */
+	uint32_t sa_id;
+};
+
+/**
+ * IPsec instance find
+ *
+ * @param[in] name
+ *   IPsec instance name.
+ * @return
+ *   Valid IPsec instance handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name);
+
+/**
+ * IPsec instance create
+ *
+ * @param[out] ipsec
+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this
+ *   function returns successfully.
+ * @param[in] name
+ *   IPsec instance unique name.
+ * @param[in] params
+ *   IPsec instance configuration parameters.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node);
+
+/**
+ * IPsec instance free
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);
+
+/**
+ * IPsec Data Plane API
+ */
+
+/**
+ * IPsec instance run
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);
+
+/*
+ * IPsec Control Plane API
+ */
+
+/** Maximum key size in bytes. */
+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64
+
+/** IPsec SA crypto cipher parameters. */
+struct rte_swx_ipsec_sa_cipher_params {
+	/** Cipher algorithm. */
+	enum rte_crypto_cipher_algorithm alg;
+
+	/** Cipher key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Cipher key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto authentication parameters. */
+struct rte_swx_ipsec_sa_authentication_params {
+	/** Authentication algorithm. */
+	enum rte_crypto_auth_algorithm alg;
+
+	/** Authentication key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Authentication key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */
+struct rte_swx_ipsec_sa_aead_params {
+	/** AEAD algorithm. */
+	enum rte_crypto_aead_algorithm alg;
+
+	/** AEAD key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** AEAD key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec protocol encapsulation parameters. */
+struct rte_swx_ipsec_sa_encap_params {
+	/** Encapsulating Security Payload (ESP) header. */
+	struct {
+		/** Security Parameters Index (SPI) field. */
+		uint32_t spi;
+	} esp;
+
+	/** Tunnel mode when non-zero, transport mode when zero. */
+	int tunnel_mode;
+
+	/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */
+	int tunnel_ipv4;
+
+	/** Tunnel parameters. Valid for tunnel mode only. */
+	union {
+		/** IPv4 header. */
+		struct {
+			/** Source address. */
+			struct in_addr src_addr;
+
+			/** Destination address. */
+			struct in_addr dst_addr;
+		} ipv4;
+
+		/** IPv6 header. */
+		struct {
+			/** Source address. */
+			struct in6_addr src_addr;
+
+			/** Destination address. */
+			struct in6_addr dst_addr;
+		} ipv6;
+	} tunnel;
+};
+
+/** IPsec Security Association (SA) parameters. */
+struct rte_swx_ipsec_sa_params {
+	/** Crypto operation: encrypt when non-zero, decrypt when zero. */
+	int encrypt;
+
+	/** Crypto operation parameters. */
+	struct {
+		RTE_STD_C11
+		union {
+			struct {
+				/** Crypto cipher operation parameters. */
+				struct rte_swx_ipsec_sa_cipher_params cipher;
+
+				/** Crypto authentication operation parameters. */
+				struct rte_swx_ipsec_sa_authentication_params auth;
+			} cipher_auth;
+
+			/** Crypto AEAD operation parameters. */
+			struct rte_swx_ipsec_sa_aead_params aead;
+		};
+
+		/** Non-zero for AEAD, zero for cipher & authentication. */
+		int is_aead;
+	} crypto;
+
+	/** Packet encasulation parameters. */
+	struct rte_swx_ipsec_sa_encap_params encap;
+};
+
+/**
+ * IPsec SA add
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_params
+ *   SA parameters.
+ * @param[out] sa_id
+ *   On success, the SA ID.
+ * @return
+ *   0 on success or error code otherwise.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *sa_id);
+
+/**
+ * IPsec SA delete
+ *
+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used
+ * by the Data Plane.
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_id
+ *   The SA ID.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id);
+
+/**
+ * IPsec SA read from string
+ *
+ * IPsec SA syntax:
+ *
+ * \<sa>
+ *    : encrypt \<crypto_params> \<encap_params>
+ *    | decrypt \<crypto_params> \<encap_params>
+ *    ;
+ *
+ * \<crypto_params>
+ *    : \<cipher> \<auth>
+ *    | \<aead>
+ *    ;
+ *
+ * \<cipher>
+ *    : cipher \<ciher_alg> key \<cipher_key>
+ *    | cipher \<cipher_alg>
+ *    ;
+ *
+ * \<auth>
+ *    : auth \<authentication_alg> key \<authentication_key>
+ *    | auth \<authentication_alg>
+ *    ;
+ *
+ * \<aead>
+ *    : aead \<aead_alg> key \<aead_key>
+ *    ;
+ *
+ * \<encap_params>
+ *    : esp spi \<spi> tunnel ipv4 srcaddr \<ipv4_src_addr> dstaddr \<ipv4_dst_addr>
+ *    | esp spi \<spi> tunnel ipv6 srcaddr \<ipv6_src_addr> dstaddr \<ipv6_dst_addr>
+ *    | esp spi \<spi> transport
+ *    ;
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] string
+ *   String containing the SA.
+ * @param[in,out] is_blank_or_comment
+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when
+ *   *string* contains a blank or comment line and to zero otherwise.
+ * @param[in,out] errmsg
+ *   On error, when its input value is not NULL, this argument points to a string with details on
+ *   the detected error.
+ * @return
+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.
+ */
+__rte_experimental
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 184f45a6b7..58e34459c3 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -155,4 +155,13 @@ EXPERIMENTAL {
 	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
+
+	#added in 23.03
+	rte_swx_ipsec_create;
+	rte_swx_ipsec_find;
+	rte_swx_ipsec_free;
+	rte_swx_ipsec_run;
+	rte_swx_ipsec_sa_add;
+	rte_swx_ipsec_sa_delete;
+	rte_swx_ipsec_sa_read;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 02/11] examples/pipeline: rework memory pool support
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 01/11] " Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Rework the memory pool CLI command to accommodate the MBUF private
meta-data area size parameter.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 72 ++++++++++-------
 examples/pipeline/examples/fib.cli            |  2 +-
 examples/pipeline/examples/hash_func.cli      |  2 +-
 examples/pipeline/examples/l2fwd.cli          |  2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |  2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |  2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |  2 +-
 examples/pipeline/examples/learner.cli        |  2 +-
 examples/pipeline/examples/meter.cli          |  2 +-
 examples/pipeline/examples/mirroring.cli      |  2 +-
 examples/pipeline/examples/recirculation.cli  |  2 +-
 examples/pipeline/examples/registers.cli      |  2 +-
 examples/pipeline/examples/selector.cli       |  2 +-
 examples/pipeline/examples/varbit.cli         |  2 +-
 examples/pipeline/examples/vxlan.cli          |  2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |  2 +-
 examples/pipeline/obj.c                       | 80 +------------------
 examples/pipeline/obj.h                       | 27 -------
 18 files changed, 63 insertions(+), 146 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index e1e7aaddc1..14f1cfa47e 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -192,72 +192,88 @@ parse_table_entry(struct rte_swx_ctl_pipeline *p,
 }
 
 static const char cmd_mempool_help[] =
-"mempool <mempool_name>\n"
-"   buffer <buffer_size>\n"
-"   pool <pool_size>\n"
-"   cache <cache_size>\n"
-"   cpu <cpu_id>\n";
+"mempool <mempool_name> "
+"meta <mbuf_private_size> "
+"pkt <pkt_buffer_size> "
+"pool <pool_size> "
+"cache <cache_size> "
+"numa <numa_node>\n";
 
 static void
 cmd_mempool(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
+	    uint32_t n_tokens,
+	    char *out,
+	    size_t out_size,
+	    void *obj __rte_unused)
 {
-	struct mempool_params p;
-	char *name;
-	struct mempool *mempool;
+	struct rte_mempool *mp;
+	char *mempool_name;
+	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
 
-	if (n_tokens != 10) {
+	if (n_tokens != 12) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	name = tokens[1];
+	mempool_name = tokens[1];
+
+	if (strcmp(tokens[2], "meta")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
+		return;
+	}
 
-	if (strcmp(tokens[2], "buffer") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
+	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
+	if (strcmp(tokens[4], "pkt")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
 		return;
 	}
 
-	if (strcmp(tokens[4], "pool") != 0) {
+	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
+		return;
+	}
+
+	if (strcmp(tokens[6], "pool")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
 		return;
 	}
 
-	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
+	if (parser_read_uint32(&pool_size, tokens[7])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
 		return;
 	}
 
-	if (strcmp(tokens[6], "cache") != 0) {
+	if (strcmp(tokens[8], "cache")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
+	if (parser_read_uint32(&cache_size, tokens[9])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
 		return;
 	}
 
-	if (strcmp(tokens[8], "cpu") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+	if (strcmp(tokens[10], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+	if (parser_read_uint32(&numa_node, tokens[11])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	mempool = mempool_create(obj, name, &p);
-	if (mempool == NULL) {
+	mp = rte_pktmbuf_pool_create(mempool_name,
+				     pool_size,
+				     cache_size,
+				     mbuf_private_size,
+				     pkt_buffer_size,
+				     numa_node);
+	if (!mp) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 4e30c1320f..2450dc9ca4 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/fib.c /tmp/fib.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index b2e219e4c9..3840e47a2b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index 27e37021b9..c5505df296 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 11bb4543b9..bd700707f5 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index 8724dae3b0..aa64b32ab2 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 4db0a0dc56..619d17474f 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 6c8aa3921e..42184be1c8 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/learner.c /tmp/learner.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 21582554b9..0fa38b16b8 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/meter.c /tmp/meter.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 1d439e04d3..e7391de74b 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 52d0894f12..965c870fd4 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 3516f76a5b..aa775f71e5 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/registers.c /tmp/registers.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0e251b657..f0608de184 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/selector.c /tmp/selector.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 0f89990471..648b8882c3 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 1fbd1be6e4..4565adfaea 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index adc7f73312..510c2ad870 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index cac825f22a..697d14a901 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -13,18 +13,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
 
-/*
- * mempool
- */
-TAILQ_HEAD(mempool_list, mempool);
-
 /*
  * link
  */
@@ -39,75 +33,10 @@ TAILQ_HEAD(ring_list, ring);
  * obj
  */
 struct obj {
-	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
 };
 
-/*
- * mempool
- */
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
-
-struct mempool *
-mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
-{
-	struct mempool *mempool;
-	struct rte_mempool *m;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		mempool_find(obj, name) ||
-		(params == NULL) ||
-		(params->buffer_size < BUFFER_SIZE_MIN) ||
-		(params->pool_size == 0))
-		return NULL;
-
-	/* Resource create */
-	m = rte_pktmbuf_pool_create(
-		name,
-		params->pool_size,
-		params->cache_size,
-		0,
-		params->buffer_size - sizeof(struct rte_mbuf),
-		params->cpu_id);
-
-	if (m == NULL)
-		return NULL;
-
-	/* Node allocation */
-	mempool = calloc(1, sizeof(struct mempool));
-	if (mempool == NULL) {
-		rte_mempool_free(m);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(mempool->name, name, sizeof(mempool->name));
-	mempool->m = m;
-	mempool->buffer_size = params->buffer_size;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
-
-	return mempool;
-}
-
-struct mempool *
-mempool_find(struct obj *obj, const char *name)
-{
-	struct mempool *mempool;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(mempool, &obj->mempool_list, node)
-		if (strcmp(mempool->name, name) == 0)
-			return mempool;
-
-	return NULL;
-}
-
 /*
  * link
  */
@@ -171,7 +100,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct rte_eth_conf port_conf;
 	struct link *link;
 	struct link_params_rss *rss;
-	struct mempool *mempool;
+	struct rte_mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
 	uint16_t port_id = 0;
@@ -193,8 +122,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
 
-	mempool = mempool_find(obj, params->rx.mempool_name);
-	if (mempool == NULL)
+	mempool = rte_mempool_lookup(params->rx.mempool_name);
+	if (!mempool)
 		return NULL;
 
 	rss = params->rx.rss;
@@ -251,7 +180,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			params->rx.queue_size,
 			cpu_id,
 			NULL,
-			mempool->m);
+			mempool);
 
 		if (status < 0)
 			return NULL;
@@ -421,7 +350,6 @@ obj_init(void)
 	if (!obj)
 		return NULL;
 
-	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 8ea1c414c2..4ea610ceed 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <sys/queue.h>
 
-#include <rte_mempool.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
@@ -24,32 +23,6 @@ struct obj;
 struct obj *
 obj_init(void);
 
-/*
- * mempool
- */
-struct mempool_params {
-	uint32_t buffer_size;
-	uint32_t pool_size;
-	uint32_t cache_size;
-	uint32_t cpu_id;
-};
-
-struct mempool {
-	TAILQ_ENTRY(mempool) node;
-	char name[NAME_SIZE];
-	struct rte_mempool *m;
-	uint32_t buffer_size;
-};
-
-struct mempool *
-mempool_create(struct obj *obj,
-	       const char *name,
-	       struct mempool_params *params);
-
-struct mempool *
-mempool_find(struct obj *obj,
-	     const char *name);
-
 /*
  * link
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 03/11] examples/pipeline: streamline ring support
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 01/11] " Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Remove redundant ring related code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 24 ++++++++++------
 examples/pipeline/obj.c | 63 -----------------------------------------
 examples/pipeline/obj.h | 21 --------------
 3 files changed, 15 insertions(+), 93 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 14f1cfa47e..517682f7c9 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -11,6 +11,7 @@
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_ring.h>
 #include <rte_swx_port_ethdev.h>
 #include <rte_swx_port_ring.h>
 #include <rte_swx_port_source_sink.h>
@@ -491,11 +492,11 @@ cmd_ring(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct ring_params p;
+	struct rte_ring *r;
 	char *name;
-	struct ring *ring;
+	uint32_t size, numa_node;
 
 	if (n_tokens != 6) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
@@ -504,28 +505,32 @@ cmd_ring(char **tokens,
 
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "size") != 0) {
+	if (strcmp(tokens[2], "size")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
+	if (parser_read_uint32(&size, tokens[3])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
 		return;
 	}
 
-	if (strcmp(tokens[4], "numa") != 0) {
+	if (strcmp(tokens[4], "numa")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
+	if (parser_read_uint32(&numa_node, tokens[5])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	ring = ring_create(obj, name, &p);
-	if (!ring) {
+	r = rte_ring_create(
+		name,
+		size,
+		(int)numa_node,
+		RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (!r) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
@@ -2999,6 +3004,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 697d14a901..3614b99d28 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -24,17 +24,11 @@
  */
 TAILQ_HEAD(link_list, link);
 
-/*
- * ring
- */
-TAILQ_HEAD(ring_list, ring);
-
 /*
  * obj
  */
 struct obj {
 	struct link_list link_list;
-	struct ring_list ring_list;
 };
 
 /*
@@ -282,62 +276,6 @@ link_next(struct obj *obj, struct link *link)
 		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
 }
 
-/*
- * ring
- */
-struct ring *
-ring_create(struct obj *obj, const char *name, struct ring_params *params)
-{
-	struct ring *ring;
-	struct rte_ring *r;
-	unsigned int flags = RING_F_SP_ENQ | RING_F_SC_DEQ;
-
-	/* Check input params */
-	if (!name || ring_find(obj, name) || !params || !params->size)
-		return NULL;
-
-	/**
-	 * Resource create
-	 */
-	r = rte_ring_create(
-		name,
-		params->size,
-		params->numa_node,
-		flags);
-	if (!r)
-		return NULL;
-
-	/* Node allocation */
-	ring = calloc(1, sizeof(struct ring));
-	if (!ring) {
-		rte_ring_free(r);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(ring->name, name, sizeof(ring->name));
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->ring_list, ring, node);
-
-	return ring;
-}
-
-struct ring *
-ring_find(struct obj *obj, const char *name)
-{
-	struct ring *ring;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(ring, &obj->ring_list, node)
-		if (strcmp(ring->name, name) == 0)
-			return ring;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -351,7 +289,6 @@ obj_init(void)
 		return NULL;
 
 	TAILQ_INIT(&obj->link_list);
-	TAILQ_INIT(&obj->ring_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 4ea610ceed..dbbc6d39a0 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -73,25 +73,4 @@ link_find(struct obj *obj, const char *name);
 struct link *
 link_next(struct obj *obj, struct link *link);
 
-/*
- * ring
- */
-struct ring_params {
-	uint32_t size;
-	uint32_t numa_node;
-};
-
-struct ring {
-	TAILQ_ENTRY(ring) node;
-	char name[NAME_SIZE];
-};
-
-struct ring *
-ring_create(struct obj *obj,
-	   const char *name,
-	   struct ring_params *params);
-
-struct ring *
-ring_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 04/11] examples/pipeline: streamline the Ethernet device support
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Streamline the Ethernet device support code and remove redundant code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c  | 175 ++++++++++++++++++------------------
 examples/pipeline/main.c |  12 +--
 examples/pipeline/obj.c  | 186 +++++++++------------------------------
 examples/pipeline/obj.h  |  51 ++---------
 4 files changed, 136 insertions(+), 288 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 517682f7c9..f4c8300ea7 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -292,16 +292,14 @@ cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct link_params p;
-	struct link_params_rss rss;
-	struct link *link;
+	struct ethdev_params p = {0};
+	struct ethdev_params_rss rss = {0};
 	char *name;
+	int status;
 
-	memset(&p, 0, sizeof(p));
-
-	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
+	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -377,111 +375,98 @@ cmd_ethdev(char **tokens,
 		}
 	}
 
-	link = link_create(obj, name, &p);
-	if (link == NULL) {
+	status = ethdev_config(name, &p);
+	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
 }
 
-/* Print the link stats and info */
 static void
-print_link_info(struct link *link, char *out, size_t out_size)
+ethdev_show(uint16_t port_id, char **out, size_t *out_size)
 {
-	struct rte_eth_stats stats;
-	struct rte_ether_addr mac_addr;
-	struct rte_eth_link eth_link;
-	uint16_t mtu;
-	int ret;
-
-	memset(&stats, 0, sizeof(stats));
-	rte_eth_stats_get(link->port_id, &stats);
-
-	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
-	if (ret != 0) {
-		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	ret = rte_eth_link_get(link->port_id, &eth_link);
-	if (ret < 0) {
-		snprintf(out, out_size, "\n%s: link get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	rte_eth_dev_get_mtu(link->port_id, &mtu);
-
-	snprintf(out, out_size,
-		"\n"
-		"%s: flags=<%s> mtu %u\n"
-		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
-		"\tport# %u  speed %s\n"
-		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
-		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tTX errors %" PRIu64"\n",
-		link->name,
-		eth_link.link_status == 0 ? "DOWN" : "UP",
-		mtu,
-		RTE_ETHER_ADDR_BYTES(&mac_addr),
-		link->n_rxq,
-		link->n_txq,
-		link->port_id,
-		rte_eth_link_speed_to_str(eth_link.link_speed),
-		stats.ipackets,
-		stats.ibytes,
-		stats.ierrors,
-		stats.imissed,
-		stats.rx_nombuf,
-		stats.opackets,
-		stats.obytes,
-		stats.oerrors);
+	char name[RTE_ETH_NAME_MAX_LEN] = {0};
+	struct rte_eth_dev_info info = {0};
+	struct rte_eth_stats stats = {0};
+	struct rte_ether_addr addr = {0};
+	struct rte_eth_link link = {0};
+	uint32_t length;
+	uint16_t mtu = 0;
+
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return;
+
+	rte_eth_dev_get_name_by_port(port_id, name);
+	rte_eth_dev_info_get(port_id, &info);
+	rte_eth_stats_get(port_id, &stats);
+	rte_eth_macaddr_get(port_id, &addr);
+	rte_eth_link_get(port_id, &link);
+	rte_eth_dev_get_mtu(port_id, &mtu);
+
+	snprintf(*out, *out_size,
+		 "%s: flags=<%s> mtu %u\n"
+		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
+		 "\tport# %u  speed %s\n"
+		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
+		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tTX errors %" PRIu64"\n\n",
+		 name,
+		 link.link_status ? "UP" : "DOWN",
+		 mtu,
+		 RTE_ETHER_ADDR_BYTES(&addr),
+		 info.nb_rx_queues,
+		 info.nb_tx_queues,
+		 port_id,
+		 rte_eth_link_speed_to_str(link.link_speed),
+		 stats.ipackets,
+		 stats.ibytes,
+		 stats.ierrors,
+		 stats.imissed,
+		 stats.rx_nombuf,
+		 stats.opackets,
+		 stats.obytes,
+		 stats.oerrors);
+
+	length = strlen(*out);
+	*out_size -= length;
+	*out += length;
 }
 
-/*
- * ethdev show [<ethdev_name>]
- */
+
+static char cmd_ethdev_show_help[] =
+"ethdev show [ <ethdev_name> ]\n";
+
 static void
 cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
-	      void *obj)
+	      void *obj __rte_unused)
 {
-	struct link *link;
-	char *link_name;
+	uint16_t port_id;
 
 	if (n_tokens != 2 && n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (n_tokens == 2) {
-		link = link_next(obj, NULL);
-
-		while (link != NULL) {
-			out_size = out_size - strlen(out);
-			out = &out[strlen(out)];
-
-			print_link_info(link, out, out_size);
-			link = link_next(obj, link);
-		}
-	} else {
-		out_size = out_size - strlen(out);
-		out = &out[strlen(out)];
+	/* Single device. */
+	if (n_tokens == 3) {
+		int status;
 
-		link_name = tokens[2];
-		link = link_find(obj, link_name);
+		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
+		if (status)
+			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
 
-		if (link == NULL) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-					"Link does not exist");
-			return;
-		}
-		print_link_info(link, out, out_size);
+		ethdev_show(port_id, &out, &out_size);
+		return;
 	}
+
+	/*  All devices. */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		if (rte_eth_dev_is_valid_port(port_id))
+			ethdev_show(port_id, &out, &out_size);
 }
 
 static const char cmd_ring_help[] =
@@ -3004,6 +2989,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tethdev show\n"
 			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -3039,9 +3025,16 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "ethdev") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
-		return;
+	if (!strcmp(tokens[0], "ethdev")) {
+		if (n_tokens == 1) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
+			return;
+		}
+
+		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
+			return;
+		}
 	}
 
 	if (strcmp(tokens[0], "ring") == 0) {
diff --git a/examples/pipeline/main.c b/examples/pipeline/main.c
index 6fb839f4cb..da5dd2b3f6 100644
--- a/examples/pipeline/main.c
+++ b/examples/pipeline/main.c
@@ -135,7 +135,6 @@ int
 main(int argc, char **argv)
 {
 	struct conn *conn;
-	struct obj *obj;
 	int status;
 
 	/* Parse application arguments */
@@ -150,13 +149,6 @@ main(int argc, char **argv)
 		return status;
 	};
 
-	/* Obj */
-	obj = obj_init();
-	if (!obj) {
-		printf("Error: Obj initialization failed (%d)\n", status);
-		return status;
-	}
-
 	/* Thread */
 	status = thread_init();
 	if (status) {
@@ -174,10 +166,10 @@ main(int argc, char **argv)
 		cli_script_process(app.script_name,
 			app.conn.msg_in_len_max,
 			app.conn.msg_out_len_max,
-			obj);
+			NULL);
 
 	/* Connectivity */
-	app.conn.msg_handle_arg = obj;
+	app.conn.msg_handle_arg = NULL;
 	conn = conn_init(&app.conn);
 	if (!conn) {
 		printf("Error: Connectivity initialization failed (%d)\n",
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 3614b99d28..143b968472 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -4,35 +4,14 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#ifdef RTE_EXEC_ENV_LINUX
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#endif
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_ctl.h>
 
 #include "obj.h"
 
 /*
- * link
- */
-TAILQ_HEAD(link_list, link);
-
-/*
- * obj
- */
-struct obj {
-	struct link_list link_list;
-};
-
-/*
- * link
+ * ethdev
  */
 static struct rte_eth_conf port_conf_default = {
 	.link_speeds = 0,
@@ -58,7 +37,7 @@ static struct rte_eth_conf port_conf_default = {
 static int
 rss_setup(uint16_t port_id,
 	uint16_t reta_size,
-	struct link_params_rss *rss)
+	struct ethdev_params_rss *rss)
 {
 	struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
 	uint32_t i;
@@ -87,69 +66,64 @@ rss_setup(uint16_t port_id,
 	return status;
 }
 
-struct link *
-link_create(struct obj *obj, const char *name, struct link_params *params)
+int
+ethdev_config(const char *name, struct ethdev_params *params)
 {
 	struct rte_eth_dev_info port_info;
 	struct rte_eth_conf port_conf;
-	struct link *link;
-	struct link_params_rss *rss;
+	struct ethdev_params_rss *rss;
 	struct rte_mempool *mempool;
-	uint32_t cpu_id, i;
-	int status;
+	uint32_t i;
+	int numa_node, status;
 	uint16_t port_id = 0;
 
 	/* Check input params */
-	if ((name == NULL) ||
-		link_find(obj, name) ||
-		(params == NULL) ||
-		(params->rx.n_queues == 0) ||
-		(params->rx.queue_size == 0) ||
-		(params->tx.n_queues == 0) ||
-		(params->tx.queue_size == 0))
-		return NULL;
+	if (!name ||
+	    !name[0] ||
+	    !params ||
+	    !params->rx.n_queues ||
+	    !params->rx.queue_size ||
+	    !params->tx.n_queues ||
+	    !params->tx.queue_size)
+		return -EINVAL;
 
 	status = rte_eth_dev_get_port_by_name(name, &port_id);
 	if (status)
-		return NULL;
+		return -EINVAL;
 
-	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
-		return NULL;
+	status = rte_eth_dev_info_get(port_id, &port_info);
+	if (status)
+		return -EINVAL;
 
 	mempool = rte_mempool_lookup(params->rx.mempool_name);
 	if (!mempool)
-		return NULL;
+		return -EINVAL;
 
 	rss = params->rx.rss;
 	if (rss) {
-		if ((port_info.reta_size == 0) ||
-			(port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512))
-			return NULL;
+		if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512)
+			return -EINVAL;
 
-		if ((rss->n_queues == 0) ||
-			(rss->n_queues >= LINK_RXQ_RSS_MAX))
-			return NULL;
+		if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX)
+			return -EINVAL;
 
 		for (i = 0; i < rss->n_queues; i++)
 			if (rss->queue_id[i] >= port_info.max_rx_queues)
-				return NULL;
+				return -EINVAL;
 	}
 
-	/**
-	 * Resource create
-	 */
 	/* Port */
 	memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
 	if (rss) {
+		uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP;
+
 		port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
-		port_conf.rx_adv_conf.rss_conf.rss_hf =
-			(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
-			port_info.flow_type_rss_offloads;
+		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads;
 	}
 
-	cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
-	if (cpu_id == (uint32_t) SOCKET_ID_ANY)
-		cpu_id = 0;
+	numa_node = rte_eth_dev_socket_id(port_id);
+	if (numa_node == SOCKET_ID_ANY)
+		numa_node = 0;
 
 	status = rte_eth_dev_configure(
 		port_id,
@@ -158,12 +132,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		&port_conf);
 
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (params->promiscuous) {
 		status = rte_eth_promiscuous_enable(port_id);
-		if (status != 0)
-			return NULL;
+		if (status)
+			return -EINVAL;
 	}
 
 	/* Port RX */
@@ -172,12 +146,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->rx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL,
 			mempool);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port TX */
@@ -186,24 +160,24 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->tx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port start */
 	status = rte_eth_dev_start(port_id);
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (rss) {
 		status = rss_setup(port_id, port_info.reta_size, rss);
 
 		if (status) {
 			rte_eth_dev_stop(port_id);
-			return NULL;
+			return -EINVAL;
 		}
 	}
 
@@ -211,84 +185,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	status = rte_eth_dev_set_link_up(port_id);
 	if ((status < 0) && (status != -ENOTSUP)) {
 		rte_eth_dev_stop(port_id);
-		return NULL;
-	}
-
-	/* Node allocation */
-	link = calloc(1, sizeof(struct link));
-	if (link == NULL) {
-		rte_eth_dev_stop(port_id);
-		return NULL;
+		return -EINVAL;
 	}
 
-	/* Node fill in */
-	strlcpy(link->name, name, sizeof(link->name));
-	link->port_id = port_id;
-	link->n_rxq = params->rx.n_queues;
-	link->n_txq = params->tx.n_queues;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->link_list, link, node);
-
-	return link;
-}
-
-int
-link_is_up(struct obj *obj, const char *name)
-{
-	struct rte_eth_link link_params;
-	struct link *link;
-
-	/* Check input params */
-	if (!obj || !name)
-		return 0;
-
-	link = link_find(obj, name);
-	if (link == NULL)
-		return 0;
-
-	/* Resource */
-	if (rte_eth_link_get(link->port_id, &link_params) < 0)
-		return 0;
-
-	return (link_params.link_status == RTE_ETH_LINK_DOWN) ? 0 : 1;
-}
-
-struct link *
-link_find(struct obj *obj, const char *name)
-{
-	struct link *link;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(link, &obj->link_list, node)
-		if (strcmp(link->name, name) == 0)
-			return link;
-
-	return NULL;
-}
-
-struct link *
-link_next(struct obj *obj, struct link *link)
-{
-	return (link == NULL) ?
-		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
-}
-
-/*
- * obj
- */
-struct obj *
-obj_init(void)
-{
-	struct obj *obj;
-
-	obj = calloc(1, sizeof(struct obj));
-	if (!obj)
-		return NULL;
-
-	TAILQ_INIT(&obj->link_list);
-
-	return obj;
+	return 0;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index dbbc6d39a0..fb091f4ba7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -6,41 +6,25 @@
 #define _INCLUDE_OBJ_H_
 
 #include <stdint.h>
-#include <sys/queue.h>
-
-#include <rte_swx_pipeline.h>
-#include <rte_swx_ctl.h>
-
-#ifndef NAME_SIZE
-#define NAME_SIZE 64
-#endif
-
-/*
- * obj
- */
-struct obj;
-
-struct obj *
-obj_init(void);
 
 /*
- * link
+ * ethdev
  */
-#ifndef LINK_RXQ_RSS_MAX
-#define LINK_RXQ_RSS_MAX                                   16
+#ifndef ETHDEV_RXQ_RSS_MAX
+#define ETHDEV_RXQ_RSS_MAX 16
 #endif
 
-struct link_params_rss {
-	uint32_t queue_id[LINK_RXQ_RSS_MAX];
+struct ethdev_params_rss {
+	uint32_t queue_id[ETHDEV_RXQ_RSS_MAX];
 	uint32_t n_queues;
 };
 
-struct link_params {
+struct ethdev_params {
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
 		const char *mempool_name;
-		struct link_params_rss *rss;
+		struct ethdev_params_rss *rss;
 	} rx;
 
 	struct {
@@ -51,26 +35,7 @@ struct link_params {
 	int promiscuous;
 };
 
-struct link {
-	TAILQ_ENTRY(link) node;
-	char name[NAME_SIZE];
-	uint16_t port_id;
-	uint32_t n_rxq;
-	uint32_t n_txq;
-};
-
-struct link *
-link_create(struct obj *obj,
-	    const char *name,
-	    struct link_params *params);
-
 int
-link_is_up(struct obj *obj, const char *name);
-
-struct link *
-link_find(struct obj *obj, const char *name);
-
-struct link *
-link_next(struct obj *obj, struct link *link);
+ethdev_config(const char *name, struct ethdev_params *params);
 
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 05/11] examples/pipeline: support crypto devices
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add support for crypto devices in the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c | 62 +++++++++++++++++++++++++++++++++++++++++
 examples/pipeline/obj.h | 11 ++++++++
 2 files changed, 73 insertions(+)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 143b968472..d39034ec22 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -7,6 +7,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_cryptodev.h>
 
 #include "obj.h"
 
@@ -190,3 +191,64 @@ ethdev_config(const char *name, struct ethdev_params *params)
 
 	return 0;
 }
+
+/*
+ * cryptodev
+ */
+int
+cryptodev_config(const char *name, struct cryptodev_params *params)
+{
+	struct rte_cryptodev_info dev_info;
+	struct rte_cryptodev_config dev_conf;
+	struct rte_cryptodev_qp_conf queue_conf;
+	uint8_t dev_id;
+	uint32_t socket_id, i;
+	int status;
+
+	/* Check input parameters. */
+	if (!name ||
+	    !params->n_queue_pairs ||
+	    !params->queue_size)
+		return -EINVAL;
+
+	/* Find the crypto device. */
+	status = rte_cryptodev_get_dev_id(name);
+	if (status < 0)
+		return -EINVAL;
+
+	dev_id = (uint8_t)status;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->n_queue_pairs > dev_info.max_nb_queue_pairs)
+		return -EINVAL;
+
+	socket_id = rte_cryptodev_socket_id(dev_id);
+
+	/* Configure the crypto device. */
+	memset(&dev_conf, 0, sizeof(dev_conf));
+	dev_conf.socket_id = socket_id;
+	dev_conf.nb_queue_pairs = params->n_queue_pairs;
+	dev_conf.ff_disable = 0;
+
+	status = rte_cryptodev_configure(dev_id, &dev_conf);
+	if (status)
+		return status;
+
+	/* Configure the crypto device queue pairs. */
+	memset(&queue_conf, 0, sizeof(queue_conf));
+	queue_conf.nb_descriptors = params->queue_size;
+	queue_conf.mp_session = NULL;
+
+	for (i = 0; i < params->n_queue_pairs; i++) {
+		status = rte_cryptodev_queue_pair_setup(dev_id, i, &queue_conf, socket_id);
+		if (status)
+			return status;
+	}
+
+	/* Start the crypto device. */
+	status = rte_cryptodev_start(dev_id);
+	if (status)
+		return status;
+
+	return 0;
+}
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index fb091f4ba7..0f103be6a7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -38,4 +38,15 @@ struct ethdev_params {
 int
 ethdev_config(const char *name, struct ethdev_params *params);
 
+/*
+ * cryptodev
+ */
+struct cryptodev_params {
+	uint32_t n_queue_pairs;
+	uint32_t queue_size;
+};
+
+int
+cryptodev_config(const char *name, struct cryptodev_params *params);
+
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 06/11] examples/pipeline: add CLI command for crypto device
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (4 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI command for the configuration of crypto devices.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f4c8300ea7..6b5d5a3370 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -521,6 +521,58 @@ cmd_ring(char **tokens,
 	}
 }
 
+static const char cmd_cryptodev_help[] =
+"cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
+
+static void
+cmd_cryptodev(char **tokens,
+	      uint32_t n_tokens,
+	      char *out,
+	      size_t out_size,
+	      void *obj __rte_unused)
+{
+	struct cryptodev_params params;
+	char *cryptodev_name;
+	int status;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	cryptodev_name = tokens[1];
+
+	if (strcmp(tokens[2], "queues")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
+		return;
+	}
+
+	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
+		return;
+	}
+
+	if (strcmp(tokens[4], "qsize")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
+		return;
+	}
+
+
+	if (parser_read_uint32(&params.queue_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
+		return;
+	}
+
+	status = cryptodev_config(cryptodev_name, &params);
+	if (status)
+		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
+}
+
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2991,6 +3043,7 @@ cmd_help(char **tokens,
 			"\tethdev\n"
 			"\tethdev show\n"
 			"\tring\n"
+			"\tcryptodev\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3042,6 +3095,11 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3297,6 +3355,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
+		return;
+	}
+
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 07/11] examples/pipeline: add IPsec CLI commands
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (5 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands for IPsec block configuration.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 298 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 298 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 6b5d5a3370..92ec0fd8e3 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -18,6 +18,7 @@
 #include <rte_swx_port_fd.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
+#include <rte_swx_ipsec.h>
 
 #include "cli.h"
 
@@ -2910,6 +2911,263 @@ cmd_pipeline_mirror_session(char **tokens,
 	}
 }
 
+static const char cmd_ipsec_create_help[] =
+"ipsec <ipsec_instance_name> create "
+"in <ring_in_name> out <ring_out_name> "
+"cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
+"bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
+"samax <n_sa_max> "
+"numa <numa_node>\n";
+
+static void
+cmd_ipsec_create(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec_params p;
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t numa_node;
+	int status;
+
+	if (n_tokens != 20) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+
+	if (strcmp(tokens[2], "create")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
+		return;
+	}
+
+	if (strcmp(tokens[3], "in")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+		return;
+	}
+
+	p.ring_in_name = tokens[4];
+
+	if (strcmp(tokens[5], "out")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+		return;
+	}
+
+	p.ring_out_name = tokens[6];
+
+	if (strcmp(tokens[7], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	p.crypto_dev_name = tokens[8];
+
+	if (strcmp(tokens[9], "cryptoq")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
+		return;
+	}
+
+	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
+		return;
+	}
+
+	if (strcmp(tokens[11], "bsz")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
+		return;
+	}
+
+	if (strcmp(tokens[16], "samax")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
+		return;
+	}
+
+	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
+		return;
+	}
+
+	if (strcmp(tokens[18], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
+		return;
+	}
+
+	if (parser_read_uint32(&numa_node, tokens[19])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	status = rte_swx_ipsec_create(&ipsec,
+				      ipsec_instance_name,
+				      &p,
+				      (int)numa_node);
+	if (status)
+		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
+}
+
+static const char cmd_ipsec_sa_add_help[] =
+"ipsec <ipsec_instance_name> sa add <file_name>\n";
+
+static void
+cmd_ipsec_sa_add(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name, *file_name, *line = NULL;
+	FILE *file = NULL;
+	uint32_t line_id = 0;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		goto free;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		goto free;
+	}
+
+	if (strcmp(tokens[3], "add")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+		goto free;
+	}
+
+	file_name = tokens[4];
+	file = fopen(file_name, "r");
+	if (!file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+		goto free;
+	}
+
+	/* Buffer allocation. */
+	line = malloc(MAX_LINE_SIZE);
+	if (!line) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	/* File read. */
+	for (line_id = 1; ; line_id++) {
+		struct rte_swx_ipsec_sa_params *sa;
+		const char *err_msg;
+		uint32_t sa_id = 0;
+		int is_blank_or_comment, status = 0;
+
+		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+			break;
+
+		/* Read SA from file. */
+		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
+		if (!sa) {
+			if (is_blank_or_comment)
+				continue;
+
+			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
+				file_name, line_id, err_msg);
+			goto free;
+		}
+
+		snprintf(out, out_size, "%s", line);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		/* Add the SA to the IPsec instance. Free the SA. */
+		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
+		if (status)
+			snprintf(out, out_size, "\t: Error (%d)\n", status);
+		else
+			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		free(sa);
+		if (status)
+			goto free;
+	}
+
+free:
+	if (file)
+		fclose(file);
+	free(line);
+}
+
+static const char cmd_ipsec_sa_delete_help[] =
+"ipsec <ipsec_instance_name> sa delete <sa_id>\n";
+
+static void
+cmd_ipsec_sa_delete(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t sa_id;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		return;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		return;
+	}
+
+	if (strcmp(tokens[3], "delete")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+		return;
+	}
+
+	if (parser_read_uint32(&sa_id, tokens[4])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
+		return;
+	}
+
+	rte_swx_ipsec_sa_delete(ipsec, sa_id);
+}
+
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
 
@@ -3068,6 +3326,9 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tipsec create\n"
+			"\tipsec sa add\n"
+			"\tipsec sa delete\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
 		return;
@@ -3292,6 +3553,26 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "add")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "delete")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
+		return;
+	}
+
 	if ((n_tokens == 3) &&
 		(strcmp(tokens[0], "thread") == 0) &&
 		(strcmp(tokens[1], "pipeline") == 0)) {
@@ -3540,6 +3821,23 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "ipsec")) {
+		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
+			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
+			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
+			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	if (strcmp(tokens[0], "thread") == 0) {
 		if ((n_tokens >= 5) &&
 			(strcmp(tokens[4], "enable") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 08/11] examples/pipeline: rework the thread configuration updates
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (6 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the configuration updates for the data plane threads were
performed through message queues. Now, this mechanism is replaced by
the control thread updating the mirror copy of the data plane thread
configuration followed by pointer swapping.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 154 ++---
 examples/pipeline/examples/fib.cli            |   2 +-
 examples/pipeline/examples/hash_func.cli      |   2 +-
 examples/pipeline/examples/l2fwd.cli          |   2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |   2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |   2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |   2 +-
 examples/pipeline/examples/learner.cli        |   2 +-
 examples/pipeline/examples/meter.cli          |   2 +-
 examples/pipeline/examples/mirroring.cli      |   2 +-
 examples/pipeline/examples/recirculation.cli  |   2 +-
 examples/pipeline/examples/registers.cli      |   2 +-
 examples/pipeline/examples/selector.cli       |   2 +-
 examples/pipeline/examples/varbit.cli         |   2 +-
 examples/pipeline/examples/vxlan.cli          |   2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |   2 +-
 examples/pipeline/thread.c                    | 586 ++++--------------
 examples/pipeline/thread.h                    |  17 +-
 18 files changed, 217 insertions(+), 570 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 92ec0fd8e3..68bb93a46c 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3168,119 +3168,86 @@ cmd_ipsec_sa_delete(char **tokens,
 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
 }
 
-static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
-
-#ifndef TIMER_PERIOD_MS_DEFAULT
-#define TIMER_PERIOD_MS_DEFAULT 10
-#endif
+static const char cmd_pipeline_enable_help[] =
+"pipeline <pipeline_name> enable thread <thread_id>\n";
 
 static void
-cmd_thread_pipeline_enable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_enable(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
 {
 	char *pipeline_name;
 	struct rte_swx_pipeline *p;
-	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
+	uint32_t thread_id;
 	int status;
 
-	if ((n_tokens != 5) && (n_tokens != 7)) {
+	if (n_tokens != 5) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "enable") != 0) {
+	if (strcmp(tokens[2], "enable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
 		return;
 	}
 
-	if (n_tokens == 7) {
-		if (strcmp(tokens[5], "period") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
-			return;
-		}
+	if (strcmp(tokens[3], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
 
-		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-			return;
-		}
+	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
+	status = pipeline_enable(p, thread_id);
 	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
+		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
 		return;
 	}
 }
 
-static const char cmd_thread_pipeline_disable_help[] =
-"thread <thread_id> pipeline <pipeline_name> disable\n";
+static const char cmd_pipeline_disable_help[] =
+"pipeline <pipeline_name> disable\n";
 
 static void
-cmd_thread_pipeline_disable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_disable(char **tokens,
+		     uint32_t n_tokens,
+		     char *out,
+		     size_t out_size,
+		     void *obj __rte_unused)
 {
 	struct rte_swx_pipeline *p;
 	char *pipeline_name;
-	uint32_t thread_id;
-	int status;
 
-	if (n_tokens != 5) {
+	if (n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "disable") != 0) {
+	if (strcmp(tokens[2], "disable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, p);
-	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL,
-			"thread pipeline disable");
-		return;
-	}
+	pipeline_disable(p);
 }
 
 static void
@@ -3326,11 +3293,12 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tpipeline enable\n"
+			"\tpipeline disable\n\n"
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
-			"\tthread pipeline enable\n"
-			"\tthread pipeline disable\n\n");
+			);
 		return;
 	}
 
@@ -3553,6 +3521,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
+		return;
+	}
+
 	if (!strcmp(tokens[0], "ipsec") &&
 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
@@ -3573,22 +3553,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((n_tokens == 3) &&
-		(strcmp(tokens[0], "thread") == 0) &&
-		(strcmp(tokens[1], "pipeline") == 0)) {
-		if (strcmp(tokens[2], "enable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_enable_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "disable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_disable_help);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3819,6 +3783,16 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
+			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
+			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
 	}
 
 	if (!strcmp(tokens[0], "ipsec")) {
@@ -3838,22 +3812,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
-	if (strcmp(tokens[0], "thread") == 0) {
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "enable") == 0)) {
-			cmd_thread_pipeline_enable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "disable") == 0)) {
-			cmd_thread_pipeline_disable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 2450dc9ca4..d2485cefe0 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -54,4 +54,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index 3840e47a2b..2c20f7a7a0 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index c5505df296..383fe346c0 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethde
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index bd700707f5..d02cb2c470 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index aa64b32ab2..d4ea449e03 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 619d17474f..8a25c98096 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 42184be1c8..04c7b4d26f 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/eth
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 0fa38b16b8..9e064dbb52 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 meter meters set profile platinum index from 0 to 15
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index e7391de74b..da6bd338fb 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 mirror session 3 port 0 clone slow truncate 128
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 965c870fd4..f925179bf7 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/exampl
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index aa775f71e5..58dde5fdf1 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0608de184..9af56cd0f1 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 selector s show
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 648b8882c3..621e7f5627 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethd
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 4565adfaea..73eb29a6b2 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 510c2ad870..828342408b 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -36,4 +36,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 6d15f51fb2..3001bc0858 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -3,17 +3,11 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
 
+#include <rte_atomic.h>
 #include <rte_common.h>
-#include <rte_cycles.h>
 #include <rte_lcore.h>
-#include <rte_ring.h>
-
-#include <rte_table_acl.h>
-#include <rte_table_array.h>
-#include <rte_table_hash.h>
-#include <rte_table_lpm.h>
-#include <rte_table_lpm_ipv6.h>
 
 #include "obj.h"
 #include "thread.h"
@@ -22,14 +16,6 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
-#ifndef THREAD_MSGQ_SIZE
-#define THREAD_MSGQ_SIZE                                   64
-#endif
-
-#ifndef THREAD_TIMER_PERIOD_MS
-#define THREAD_TIMER_PERIOD_MS                             100
-#endif
-
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -40,509 +26,209 @@
 #endif
 
 /**
- * Control thread: data plane thread context
+ * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
+ * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
+ * blocks.
+ *
+ * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
+ * single thread at any given time, so the same pipeline/block cannot show up in the list of
+ * pipelines/blocks of more than one thread at any specific moment.
+ *
+ * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
+ *  - Read-write by the CP thread;
+ *  - Read-only by the DP thread.
  */
 struct thread {
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-
-	uint32_t enabled;
-};
-
-static struct thread thread[RTE_MAX_LCORE];
-
-/**
- * Data plane threads: context
- */
-struct pipeline_data {
-	struct rte_swx_pipeline *p;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-};
-
-struct thread_data {
-	struct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];
-	uint32_t n_pipelines;
-
-	struct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-	uint64_t time_next_min;
+	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	volatile uint64_t n_pipelines;
+	int enabled;
 } __rte_cache_aligned;
 
-static struct thread_data thread_data[RTE_MAX_LCORE];
+static struct thread threads[RTE_MAX_LCORE];
 
 /**
- * Control thread: data plane thread init
+ * Control plane (CP) thread.
  */
-static void
-thread_free(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct thread *t = &thread[i];
-
-		if (!rte_lcore_is_enabled(i))
-			continue;
-
-		/* MSGQs */
-		rte_ring_free(t->msgq_req);
-
-		rte_ring_free(t->msgq_rsp);
-	}
-}
-
 int
 thread_init(void)
 {
-	uint32_t i;
-
-	RTE_LCORE_FOREACH_WORKER(i) {
-		char name[NAME_MAX];
-		struct rte_ring *msgq_req, *msgq_rsp;
-		struct thread *t = &thread[i];
-		struct thread_data *t_data = &thread_data[i];
-		uint32_t cpu_id = rte_lcore_to_socket_id(i);
-
-		/* MSGQs */
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-REQ", i);
-
-		msgq_req = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_req == NULL) {
-			thread_free();
-			return -1;
-		}
+	uint32_t thread_id;
 
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-RSP", i);
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
 
-		msgq_rsp = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_rsp == NULL) {
-			thread_free();
-			return -1;
-		}
-
-		/* Control thread records */
-		t->msgq_req = msgq_req;
-		t->msgq_rsp = msgq_rsp;
 		t->enabled = 1;
-
-		/* Data plane thread records */
-		t_data->n_pipelines = 0;
-		t_data->msgq_req = msgq_req;
-		t_data->msgq_rsp = msgq_rsp;
-		t_data->timer_period =
-			(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;
-		t_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;
-		t_data->time_next_min = t_data->time_next;
 	}
 
 	return 0;
 }
 
-static inline int
-thread_is_running(uint32_t thread_id)
+static uint32_t
+pipeline_find(struct rte_swx_pipeline *p)
 {
-	enum rte_lcore_state_t thread_state;
+	uint32_t thread_id;
 
-	thread_state = rte_eal_get_lcore_state(thread_id);
-	return (thread_state == RUNNING) ? 1 : 0;
-}
-
-/**
- * Control thread & data plane threads: message passing
- */
-enum thread_req_type {
-	THREAD_REQ_PIPELINE_ENABLE = 0,
-	THREAD_REQ_PIPELINE_DISABLE,
-	THREAD_REQ_MAX
-};
-
-struct thread_msg_req {
-	enum thread_req_type type;
-
-	union {
-		struct {
-			struct rte_swx_pipeline *p;
-			uint32_t timer_period_ms;
-		} pipeline_enable;
-
-		struct {
-			struct rte_swx_pipeline *p;
-		} pipeline_disable;
-	};
-};
-
-struct thread_msg_rsp {
-	int status;
-};
-
-/**
- * Control thread
- */
-static struct thread_msg_req *
-thread_msg_alloc(void)
-{
-	size_t size = RTE_MAX(sizeof(struct thread_msg_req),
-		sizeof(struct thread_msg_rsp));
-
-	return calloc(1, size);
-}
-
-static void
-thread_msg_free(struct thread_msg_rsp *rsp)
-{
-	free(rsp);
-}
-
-static struct thread_msg_rsp *
-thread_msg_send_recv(uint32_t thread_id,
-	struct thread_msg_req *req)
-{
-	struct thread *t = &thread[thread_id];
-	struct rte_ring *msgq_req = t->msgq_req;
-	struct rte_ring *msgq_rsp = t->msgq_rsp;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* send */
-	do {
-		status = rte_ring_sp_enqueue(msgq_req, req);
-	} while (status == -ENOBUFS);
-
-	/* recv */
-	do {
-		status = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);
-	} while (status != 0);
-
-	return rsp;
-}
-
-static int
-thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
-{
-	struct thread *t = &thread[thread_id];
-	struct thread_data *td = &thread_data[thread_id];
-	uint32_t i;
-
-	if (!t->enabled)
-		return 0; /* Pipeline NOT enabled on this thread. */
-
-	for (i = 0; i < td->n_pipelines; i++)
-		if (td->p[i] == p)
-			return 1; /* Pipeline enabled on this thread. */
-
-	return 0 /* Pipeline NOT enabled on this thread. */;
-}
-
-int
-thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
-{
-	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		struct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];
-
-		if (td->n_pipelines >= THREAD_PIPELINES_MAX)
-			return -1;
-
-		/* Data plane thread */
-		td->p[td->n_pipelines] = p;
-
-		tdp->p = p;
-		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
-		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
-		td->n_pipelines++;
+		if (!t->enabled)
+			continue;
 
-		return 0;
+		for (i = 0; i < t->n_pipelines; i++)
+			if (t->pipelines[i] == p)
+				break;
 	}
 
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p;
-	req->pipeline_enable.timer_period_ms = timer_period_ms;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
-
-	/* Read response */
-	status = rsp->status;
-
-	/* Free response */
-	thread_msg_free(rsp);
-
-	/* Request completion */
-	if (status)
-		return status;
-
-	return 0;
+	return thread_id;
 }
 
+/**
+ * Enable a given pipeline to run on a specific DP thread.
+ *
+ * CP thread:
+ *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
+ *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
+ *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
+ *    write barrier used below.
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
+ *    the new pipeline when it sees the updated t->n_pipelines value;
+ *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
+ *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
+ *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
+ */
 int
-thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
 {
 	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
+	uint64_t n_pipelines;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_pipeline_enabled(thread_id, p))
-		return 0;
+	if (!p || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
 
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		uint32_t i;
-
-		for (i = 0; i < td->n_pipelines; i++) {
-			struct pipeline_data *tdp = &td->pipeline_data[i];
-
-			if (tdp->p != p)
-				continue;
-
-			/* Data plane thread */
-			if (i < td->n_pipelines - 1) {
-				struct rte_swx_pipeline *pipeline_last =
-					td->p[td->n_pipelines - 1];
-				struct pipeline_data *tdp_last =
-					&td->pipeline_data[td->n_pipelines - 1];
-
-				td->p[i] = pipeline_last;
-				memcpy(tdp, tdp_last, sizeof(*tdp));
-			}
-
-			td->n_pipelines--;
-
-			break;
-		}
+	if (pipeline_find(p) < RTE_MAX_LCORE)
+		return -EEXIST;
 
-		return 0;
-	}
-
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
 
-	/* Read response */
-	status = rsp->status;
+	n_pipelines = t->n_pipelines;
 
-	/* Free response */
-	thread_msg_free(rsp);
+	/* Check there is room for at least one more pipeline. */
+	if (n_pipelines >= THREAD_PIPELINES_MAX)
+		return -ENOSPC;
 
-	/* Request completion */
-	if (status)
-		return status;
+	/* Install the new pipeline. */
+	t->pipelines[n_pipelines] = p;
+	rte_wmb();
+	t->n_pipelines = n_pipelines + 1;
 
 	return 0;
 }
 
 /**
- * Data plane threads: message handling
+ * Disable a given pipeline from running on any DP thread.
+ *
+ * CP thread:
+ *  - Detects the thread that is running the given pipeline, if any;
+ *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
+ *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
+ *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
+ *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
+ *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
+ *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
+ *    below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final value of t->n_pipelines;
+ *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
+ *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
+ *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
+ *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
+ *    behaviors are detailed below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final values of the t->pipeline[] array;
+ *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
+ *       the exception of the pipeline_last, which is not run during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
  */
-static inline struct thread_msg_req *
-thread_msg_recv(struct rte_ring *msgq_req)
-{
-	struct thread_msg_req *req;
-
-	int status = rte_ring_sc_dequeue(msgq_req, (void **) &req);
-
-	if (status != 0)
-		return NULL;
-
-	return req;
-}
-
-static inline void
-thread_msg_send(struct rte_ring *msgq_rsp,
-	struct thread_msg_rsp *rsp)
-{
-	int status;
-
-	do {
-		status = rte_ring_sp_enqueue(msgq_rsp, rsp);
-	} while (status == -ENOBUFS);
-}
-
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_enable(struct thread_data *t,
-	struct thread_msg_req *req)
+void
+pipeline_disable(struct rte_swx_pipeline *p)
 {
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	struct pipeline_data *p = &t->pipeline_data[t->n_pipelines];
-
-	/* Request */
-	if (t->n_pipelines >= THREAD_PIPELINES_MAX) {
-		rsp->status = -1;
-		return rsp;
-	}
-
-	t->p[t->n_pipelines] = req->pipeline_enable.p;
-
-	p->p = req->pipeline_enable.p;
-	p->timer_period = (rte_get_tsc_hz() *
-		req->pipeline_enable.timer_period_ms) / 1000;
-	p->time_next = rte_get_tsc_cycles() + p->timer_period;
+	struct thread *t;
+	uint64_t n_pipelines;
+	uint32_t thread_id, i;
 
-	t->n_pipelines++;
+	/* Check input params */
+	if (!p)
+		return;
 
-	/* Response */
-	rsp->status = 0;
-	return rsp;
-}
+	/* Find the thread that runs this pipeline. */
+	thread_id = pipeline_find(p);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
 
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_disable(struct thread_data *t,
-	struct thread_msg_req *req)
-{
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	uint32_t n_pipelines = t->n_pipelines;
-	struct rte_swx_pipeline *pipeline = req->pipeline_disable.p;
-	uint32_t i;
+	t = &threads[thread_id];
+	n_pipelines = t->n_pipelines;
 
-	/* find pipeline */
 	for (i = 0; i < n_pipelines; i++) {
-		struct pipeline_data *p = &t->pipeline_data[i];
+		struct rte_swx_pipeline *pipeline = t->pipelines[i];
 
-		if (p->p != pipeline)
+		if (pipeline != p)
 			continue;
 
 		if (i < n_pipelines - 1) {
-			struct rte_swx_pipeline *pipeline_last =
-				t->p[n_pipelines - 1];
-			struct pipeline_data *p_last =
-				&t->pipeline_data[n_pipelines - 1];
+			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
 
-			t->p[i] = pipeline_last;
-			memcpy(p, p_last, sizeof(*p));
+			t->pipelines[i] = pipeline_last;
 		}
 
-		t->n_pipelines--;
+		rte_wmb();
+		t->n_pipelines = n_pipelines - 1;
 
-		rsp->status = 0;
-		return rsp;
+		return;
 	}
 
-	/* should not get here */
-	rsp->status = 0;
-	return rsp;
-}
-
-static void
-thread_msg_handle(struct thread_data *t)
-{
-	for ( ; ; ) {
-		struct thread_msg_req *req;
-		struct thread_msg_rsp *rsp;
-
-		req = thread_msg_recv(t->msgq_req);
-		if (req == NULL)
-			break;
-
-		switch (req->type) {
-		case THREAD_REQ_PIPELINE_ENABLE:
-			rsp = thread_msg_handle_pipeline_enable(t, req);
-			break;
-
-		case THREAD_REQ_PIPELINE_DISABLE:
-			rsp = thread_msg_handle_pipeline_disable(t, req);
-			break;
-
-		default:
-			rsp = (struct thread_msg_rsp *) req;
-			rsp->status = -1;
-		}
-
-		thread_msg_send(t->msgq_rsp, rsp);
-	}
+	return;
 }
 
 /**
- * Data plane threads: main
+ * Data plane (DP) threads.
+ *
+ * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
+ * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
+ * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
+ * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
+ * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
+ * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
+ * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
+ * marked as volatile.
  */
 int
 thread_main(void *arg __rte_unused)
 {
-	struct thread_data *t;
-	uint32_t thread_id, i;
+	struct thread *t;
+	uint32_t thread_id;
 
 	thread_id = rte_lcore_id();
-	t = &thread_data[thread_id];
-
-	/* Dispatch loop */
-	for (i = 0; ; i++) {
-		uint32_t j;
-
-		/* Data Plane */
-		for (j = 0; j < t->n_pipelines; j++)
-			rte_swx_pipeline_run(t->p[j], PIPELINE_INSTR_QUANTA);
+	t = &threads[thread_id];
 
-		/* Control Plane */
-		if ((i & 0xF) == 0) {
-			uint64_t time = rte_get_tsc_cycles();
-			uint64_t time_next_min = UINT64_MAX;
-
-			if (time < t->time_next_min)
-				continue;
-
-			/* Thread message queues */
-			{
-				uint64_t time_next = t->time_next;
-
-				if (time_next <= time) {
-					thread_msg_handle(t);
-					time_next = time + t->timer_period;
-					t->time_next = time_next;
-				}
-
-				if (time_next < time_next_min)
-					time_next_min = time_next;
-			}
+	/* Dispatch loop. */
+	for ( ; ; ) {
+		uint32_t i;
 
-			t->time_next_min = time_next_min;
-		}
+		/* Pipelines. */
+		for (i = 0; i < t->n_pipelines; i++)
+			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 712cb25bbb..338d480abb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -9,18 +9,21 @@
 
 #include <rte_swx_pipeline.h>
 
+/**
+ * Control plane (CP) thread.
+ */
 int
-thread_pipeline_enable(uint32_t thread_id,
-		       struct rte_swx_pipeline *p,
-		       uint32_t timer_period_ms);
+thread_init(void);
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-			struct rte_swx_pipeline *p);
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 
-int
-thread_init(void);
+void
+pipeline_disable(struct rte_swx_pipeline *p);
 
+/**
+ * Data plane (DP) threads.
+ */
 int
 thread_main(void *arg);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 09/11] examples/pipeline: support blocks other than pipelines
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (7 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the data plane threads only supported the execution of
pipelines assigned to them through configuration updates. Now, the
data plane threads also support running blocks such as IPsec.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/thread.c | 143 +++++++++++++++++++++++++++++++++++++
 examples/pipeline/thread.h |   9 +++
 2 files changed, 152 insertions(+)

diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 3001bc0858..dc3ea73fbf 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -16,6 +16,10 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
+#ifndef THREAD_BLOCKS_MAX
+#define THREAD_BLOCKS_MAX                                  256
+#endif
+
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -38,9 +42,16 @@
  *  - Read-write by the CP thread;
  *  - Read-only by the DP thread.
  */
+struct block {
+	block_run_f block_func;
+	void *block;
+};
+
 struct thread {
 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	struct block *blocks[THREAD_BLOCKS_MAX];
 	volatile uint64_t n_pipelines;
+	volatile uint64_t n_blocks;
 	int enabled;
 } __rte_cache_aligned;
 
@@ -53,14 +64,43 @@ int
 thread_init(void)
 {
 	uint32_t thread_id;
+	int status = 0;
 
 	RTE_LCORE_FOREACH_WORKER(thread_id) {
 		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
 		t->enabled = 1;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			struct block *b;
+
+			b = calloc(1, sizeof(struct block));
+			if (!b) {
+				status = -ENOMEM;
+				goto error;
+			}
+
+			t->blocks[i] = b;
+		}
 	}
 
 	return 0;
+
+error:
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		t->enabled = 0;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			free(t->blocks[i]);
+			t->blocks[i] = NULL;
+		}
+	}
+
+	return status;
 }
 
 static uint32_t
@@ -83,6 +123,26 @@ pipeline_find(struct rte_swx_pipeline *p)
 	return thread_id;
 }
 
+static uint32_t
+block_find(void *b)
+{
+	uint32_t thread_id;
+
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		if (!t->enabled)
+			continue;
+
+		for (i = 0; i < t->n_blocks; i++)
+			if (t->blocks[i]->block == b)
+				break;
+	}
+
+	return thread_id;
+}
+
 /**
  * Enable a given pipeline to run on a specific DP thread.
  *
@@ -201,9 +261,85 @@ pipeline_disable(struct rte_swx_pipeline *p)
 	return;
 }
 
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+
+	/* Check input params */
+	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
+
+	if (block_find(block) < RTE_MAX_LCORE)
+		return -EEXIST;
+
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
+
+	n_blocks = t->n_blocks;
+
+	/* Check there is room for at least one more block. */
+	if (n_blocks >= THREAD_BLOCKS_MAX)
+		return -ENOSPC;
+
+	/* Install the new block. */
+	t->blocks[n_blocks]->block_func = block_func;
+	t->blocks[n_blocks]->block = block;
+
+	rte_wmb();
+	t->n_blocks = n_blocks + 1;
+
+	return 0;
+}
+
+void
+block_disable(void *block)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+	uint32_t thread_id, i;
+
+	/* Check input params */
+	if (!block)
+		return;
+
+	/* Find the thread that runs this block. */
+	thread_id = block_find(block);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
+
+	t = &threads[thread_id];
+	n_blocks = t->n_blocks;
+
+	for (i = 0; i < n_blocks; i++) {
+		struct block *b = t->blocks[i];
+
+		if (block != b->block)
+			continue;
+
+		if (i < n_blocks - 1) {
+			struct block *block_last = t->blocks[n_blocks - 1];
+
+			t->blocks[i] = block_last;
+		}
+
+		rte_wmb();
+		t->n_blocks = n_blocks - 1;
+
+		rte_wmb();
+		t->blocks[n_blocks - 1] = b;
+
+		return;
+	}
+}
+
 /**
  * Data plane (DP) threads.
  *
+
+
  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
@@ -229,6 +365,13 @@ thread_main(void *arg __rte_unused)
 		/* Pipelines. */
 		for (i = 0; i < t->n_pipelines; i++)
 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
+
+		/* Blocks. */
+		for (i = 0; i < t->n_blocks; i++) {
+			struct block *b = t->blocks[i];
+
+			b->block_func(b->block);
+		}
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 338d480abb..f2e643def5 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -21,6 +21,15 @@ pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 void
 pipeline_disable(struct rte_swx_pipeline *p);
 
+typedef void
+(*block_run_f)(void *block);
+
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id);
+
+void
+block_disable(void *block);
+
 /**
  * Data plane (DP) threads.
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 10/11] examples/pipeline: add block enable/disable CLI commands
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (8 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  2023-01-26 13:34   ` [PATCH V5 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands to enable/disable block execution on data plane
threads.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 68bb93a46c..37250b7072 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3250,6 +3250,134 @@ cmd_pipeline_disable(char **tokens,
 	pipeline_disable(p);
 }
 
+static const char cmd_block_enable_help[] =
+"block type <block_type> instance <block_name> enable thread <thread_id>\n";
+
+static void
+cmd_block_enable(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	block_run_f block_func = NULL;
+	void *block = NULL;
+	uint32_t thread_id;
+	int status;
+
+	if (n_tokens != 8) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "enable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+		return;
+	}
+
+	if (strcmp(tokens[6], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
+
+	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block_func = (block_run_f)rte_swx_ipsec_run;
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	status = block_enable(block_func, block, thread_id);
+	if (status) {
+		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
+		return;
+	}
+}
+
+static const char cmd_block_disable_help[] =
+"block type <block_type> instance <block_name> disable\n";
+
+static void
+cmd_block_disable(char **tokens,
+		  uint32_t n_tokens,
+		  char *out,
+		  size_t out_size,
+		  void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	void *block = NULL;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "disable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	block_disable(block);
+}
+
 static void
 cmd_help(char **tokens,
 	 uint32_t n_tokens,
@@ -3298,6 +3426,8 @@ cmd_help(char **tokens,
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
+			"\tblock enable\n"
+			"\tblock disable\n"
 			);
 		return;
 	}
@@ -3553,6 +3683,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
+		return;
+	}
+
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3812,6 +3954,18 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "block")) {
+		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
+			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
+			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V5 11/11] examples/pipeline: add IPsec example
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
                     ` (9 preceding siblings ...)
  2023-01-26 13:34   ` [PATCH V5 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
@ 2023-01-26 13:34   ` Cristian Dumitrescu
  10 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 13:34 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add example files to illustrate the pipeline IPsec support.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ipsec.cli    |  57 +++++++
 examples/pipeline/examples/ipsec.io     |  23 +++
 examples/pipeline/examples/ipsec.spec   | 138 +++++++++++++++
 examples/pipeline/examples/ipsec_sa.txt | 216 ++++++++++++++++++++++++
 4 files changed, 434 insertions(+)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt

diff --git a/examples/pipeline/examples/ipsec.cli b/examples/pipeline/examples/ipsec.cli
new file mode 100644
index 0000000000..8cb5bf4239
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.cli
@@ -0,0 +1,57 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 --vdev crypto_aesni_mb0 -- -s ./examples/pipeline/examples/ipsec.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/ipsec.spec /tmp/ipsec.c
+pipeline libbuild /tmp/ipsec.c /tmp/ipsec.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 128 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+cryptodev crypto_aesni_mb0 queues 1 qsize 128
+ring RING0 size 1024 numa 0
+ring RING1 size 1024 numa 0
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/ipsec.so io ./examples/pipeline/examples/ipsec.io numa 0
+
+;
+; List of IPsec devices.
+;
+ipsec IPSEC0 create in RING0 out RING1 cryptodev crypto_aesni_mb0 cryptoq 0 bsz 32 32 32 32 samax 512 numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+//pipeline PIPELINE0 table policy_table add ./examples/pipeline/examples/ipsec_policy_table.txt
+//pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/ipsec_routing_table.txt
+//pipeline PIPELINE0 table nexthop_table add ./examples/pipeline/examples/ipsec_nexthop_table.txt
+//pipeline PIPELINE0 commit
+
+ipsec IPSEC0 sa add ./examples/pipeline/examples/ipsec_sa.txt
+
+;
+; Pipelines and blocks mapping to CPU threads.
+;
+pipeline PIPELINE0 enable thread 1
+block type ipsec instance IPSEC0 enable thread 1
diff --git a/examples/pipeline/examples/ipsec.io b/examples/pipeline/examples/ipsec.io
new file mode 100644
index 0000000000..f5a3fcf961
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.io
@@ -0,0 +1,23 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ring RING1 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ring RING0 bsz 32
diff --git a/examples/pipeline/examples/ipsec.spec b/examples/pipeline/examples/ipsec.spec
new file mode 100644
index 0000000000..09aa831881
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.spec
@@ -0,0 +1,138 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_h {
+	bit<8> ver_ihl
+	bit<8> diffserv
+	bit<16> total_len
+	bit<16> identification
+	bit<16> flags_offset
+	bit<8> ttl
+	bit<8> protocol
+	bit<16> hdr_checksum
+	bit<32> src_addr
+	bit<32> dst_addr
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+struct ipsec_internal_h {
+	bit<32> sa_id
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+header ipsec_internal instanceof ipsec_internal_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port_in
+	bit<32> port_out
+
+	bit<32> src_addr
+	bit<32> dst_addr
+	bit<8> protocol
+	bit<16> src_port
+	bit<16> dst_port
+}
+
+metadata instanceof metadata_t
+
+//
+// Actions
+//
+struct encrypt_args_t {
+	bit<32> sa_id
+}
+
+action encrypt args instanceof encrypt_args_t {
+	//Set the IPsec internal header.
+	validate h.ipsec_internal
+	mov h.ipsec_internal.sa_id t.sa_id
+
+	return
+}
+
+action drop args none {
+	drop
+}
+
+//
+// Tables.
+//
+table policy_table {
+	key {
+		m.src_addr exact
+		m.dst_addr exact
+		m.protocol exact
+		m.src_port exact
+		m.dst_port exact
+	}
+
+	actions {
+		encrypt
+		drop
+	}
+
+	default_action encrypt args sa_id 0
+	size 65536
+}
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port_in
+
+	jmpeq FROM_IPSEC_TO_NET m.port_in 1
+
+FROM_NET_TO_IPSEC : extract h.ethernet
+
+	extract h.ipv4
+	mov m.src_addr h.ipv4.src_addr
+	mov m.dst_addr h.ipv4.dst_addr
+	mov m.protocol h.ipv4.protocol
+
+	extract h.udp
+	mov m.src_port h.udp.src_port
+	mov m.dst_port h.udp.dst_port
+
+	table policy_table
+
+	mov m.port_out 1
+
+	emit h.ipsec_internal
+	emit h.ipv4
+	emit h.udp
+	tx m.port_out
+
+FROM_IPSEC_TO_NET : extract h.ipv4
+
+	validate h.ethernet
+	mov h.ethernet.dst_addr 0xa0b0c0d0e0f0
+	mov h.ethernet.src_addr 0xa1b1c1d1e1f1
+	mov h.ethernet.ethertype 0x0800
+
+	mov m.port_out 0
+
+	emit h.ethernet
+	emit h.ipv4
+	tx m.port_out
+}
diff --git a/examples/pipeline/examples/ipsec_sa.txt b/examples/pipeline/examples/ipsec_sa.txt
new file mode 100644
index 0000000000..37bfd8a9ce
--- /dev/null
+++ b/examples/pipeline/examples/ipsec_sa.txt
@@ -0,0 +1,216 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+// IPsec SA syntax:
+//
+// <sa>
+//    : encrypt <crypto_params> <encap_params>
+//    | decrypt <crypto_params> <encap_params>
+//    ;
+//
+// <crypto_params>
+//    : <cipher> <auth>
+//    | <aead>
+//    ;
+//
+// <cipher>
+//    : cipher <ciher_alg> key <cipher_key>
+//    | cipher <cipher_alg>
+//    ;
+//
+// <auth>
+//    : auth <authentication_alg> key <authentication_key>
+//    | auth <authentication_alg>
+//    ;
+//
+// <aead>
+//    : aead <aead_alg> key <aead_key>
+//    ;
+//
+// <encap_params>
+//    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+//    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+//    | esp spi <spi> transport
+//    ;
+
+//
+// cipher = null, auth = null
+//
+encrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth null esp spi 100 transport
+decrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth null esp spi 100 transport
+
+//
+// aes-gcm-128
+//
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-gcm-192
+//
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-gcm-256
+//
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// aes-ccm-128
+//
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-ccm-192
+//
+// Note: Currently not supported by the "aesni_mb" library.
+//
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-ccm-256
+//
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// chacha20-poly1305
+//
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// cipher = aes-cbc-128, auth = null
+//
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-192, auth = null
+//
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-256, auth = null
+//
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-128, auth = null
+//
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-192, auth = null
+//
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-256, auth = null
+//
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+
+//
+// cipher = null, auth = sha1-hmac
+//
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// cipher = null, auth = sha256-hmac
+//
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+
+//
+// cipher = null, auth = sha384-hmac
+//
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+
+//
+// cipher = null, auth = sha512-hmac
+//
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 00/11] pipeline: add IPsec support
  2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
                   ` (15 preceding siblings ...)
  2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
@ 2023-01-26 14:12 ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 01/11] " Cristian Dumitrescu
                     ` (11 more replies)
  16 siblings, 12 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev

This patch set introduces a companion block for the SWX pipeline for
IPsec support.

The IPsec block is external to the pipeline, hence it needs to be
explicitly instantiated by the user and connected to a pipeline
instance through the pipeline I/O ports.

Main features:
* IPsec inbound (encrypted input packets -> clear text output packets)
and outbound (clear text input packets -> encrypted output packets)
processing support for tunnel and transport modes.

Interaction of the IPsec block with the pipeline:
* Each IPsec block instance has its own set of Security Associations
(SAs) used to process the input packets. Each SA is identified by its
unique SA ID. The IPsec inbound and outbound SAs share the same ID
space.
* Each input packet is first mapped to one of the existing SAs by
using the SA ID and then processed according to the identified SA. The
SA ID is read from input packet. The SA ID field is typically written
by the pipeline before sending the packet to the IPsec block.

Change log:

V6:
Fixed more build issues for gcc 4.8.5.

V5:
Fixed build issue for gcc 4.8.5.

V4:
Fixed Doxygen issues.

V3:
Rebased on top of main latest.

V2:
Fixed minor style issues.

Cristian Dumitrescu (11):
  pipeline: add IPsec support
  examples/pipeline: rework memory pool support
  examples/pipeline: streamline ring support
  examples/pipeline: streamline the Ethernet device support
  examples/pipeline: support crypto devices
  examples/pipeline: add CLI command for crypto device
  examples/pipeline: add IPsec CLI commands
  examples/pipeline: rework the thread configuration updates
  examples/pipeline: support blocks other than pipelines
  examples/pipeline: add block enable/disable CLI commands
  examples/pipeline: add IPsec example

 examples/pipeline/cli.c                       |  893 ++++++--
 examples/pipeline/examples/fib.cli            |    4 +-
 examples/pipeline/examples/hash_func.cli      |    4 +-
 examples/pipeline/examples/ipsec.cli          |   57 +
 examples/pipeline/examples/ipsec.io           |   23 +
 examples/pipeline/examples/ipsec.spec         |  138 ++
 examples/pipeline/examples/ipsec_sa.txt       |  216 ++
 examples/pipeline/examples/l2fwd.cli          |    4 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |    4 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |    4 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |    4 +-
 examples/pipeline/examples/learner.cli        |    4 +-
 examples/pipeline/examples/meter.cli          |    4 +-
 examples/pipeline/examples/mirroring.cli      |    4 +-
 examples/pipeline/examples/recirculation.cli  |    4 +-
 examples/pipeline/examples/registers.cli      |    4 +-
 examples/pipeline/examples/selector.cli       |    4 +-
 examples/pipeline/examples/varbit.cli         |    4 +-
 examples/pipeline/examples/vxlan.cli          |    4 +-
 examples/pipeline/examples/vxlan_pcap.cli     |    4 +-
 examples/pipeline/main.c                      |   12 +-
 examples/pipeline/obj.c                       |  361 +---
 examples/pipeline/obj.h                       |  100 +-
 examples/pipeline/thread.c                    |  655 +++---
 examples/pipeline/thread.h                    |   24 +-
 lib/pipeline/meson.build                      |    4 +-
 lib/pipeline/rte_swx_ipsec.c                  | 1821 +++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h                  |  383 ++++
 lib/pipeline/version.map                      |    9 +
 29 files changed, 3741 insertions(+), 1015 deletions(-)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 01/11] pipeline: add IPsec support
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
                     ` (10 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

This block is providing IPsec support to the SWX pipeline. The IPsec
block is external to the pipeline, so it needs to be explicitly
instantiated and connected to a pipeline through the I/O ports.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 lib/pipeline/meson.build     |    4 +-
 lib/pipeline/rte_swx_ipsec.c | 1821 ++++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_ipsec.h |  383 +++++++
 lib/pipeline/version.map     |    9 +
 4 files changed, 2216 insertions(+), 1 deletion(-)
 create mode 100644 lib/pipeline/rte_swx_ipsec.c
 create mode 100644 lib/pipeline/rte_swx_ipsec.h

diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build
index 3ca98ed194..aa3fd0c2b8 100644
--- a/lib/pipeline/meson.build
+++ b/lib/pipeline/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'rte_pipeline.c',
         'rte_port_in_action.c',
         'rte_table_action.c',
+	'rte_swx_ipsec.c',
         'rte_swx_pipeline.c',
         'rte_swx_pipeline_spec.c',
         'rte_swx_ctl.c',
@@ -19,8 +20,9 @@ headers = files(
         'rte_pipeline.h',
         'rte_port_in_action.h',
         'rte_table_action.h',
+	'rte_swx_ipsec.h',
         'rte_swx_pipeline.h',
         'rte_swx_extern.h',
         'rte_swx_ctl.h',
 )
-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']
+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']
diff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c
new file mode 100644
index 0000000000..b23056e23e
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.c
@@ -0,0 +1,1821 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_cryptodev.h>
+#include <rte_ipsec.h>
+
+#include "rte_swx_ipsec.h"
+
+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
+
+#include <rte_malloc.h>
+
+static void *
+env_calloc(size_t size, size_t alignment, int numa_node)
+{
+	return rte_zmalloc_socket(NULL, size, alignment, numa_node);
+}
+
+static void
+env_free(void *start, size_t size __rte_unused)
+{
+	rte_free(start);
+}
+
+#else
+
+#include <numa.h>
+
+static void *
+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
+{
+	void *start;
+
+	if (numa_available() == -1)
+		return NULL;
+
+	start = numa_alloc_onnode(size, numa_node);
+	if (!start)
+		return NULL;
+
+	memset(start, 0, size);
+	return start;
+}
+
+static void
+env_free(void *start, size_t size)
+{
+	if ((numa_available() == -1) || !start)
+		return;
+
+	numa_free(start, size);
+}
+
+#endif
+
+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
+#endif
+
+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
+ * the mempool size to be at least 1.5 times the size of the mempool cache.
+ */
+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
+
+struct ipsec_sa {
+	struct rte_ipsec_session s;
+	int valid;
+};
+
+struct ipsec_pkts_in {
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_cops;
+};
+
+struct ipsec_pkts_out {
+	struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
+	uint32_t n_pkts;
+};
+
+struct rte_swx_ipsec {
+	/*
+	 * Parameters.
+	 */
+
+	/* IPsec instance name. */
+	char name[RTE_SWX_IPSEC_NAME_SIZE];
+
+	/* Input packet queue. */
+	struct rte_ring *ring_in;
+
+	/* Output packet queue. */
+	struct rte_ring *ring_out;
+
+	/* Crypto device ID. */
+	uint8_t dev_id;
+
+	/* Crypto device queue pair ID. */
+	uint16_t qp_id;
+
+	/* Burst sizes. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/* SA table size. */
+	size_t n_sa_max;
+
+	/*
+	 * Internals.
+	 */
+	/* Crypto device buffer pool for sessions. */
+	struct rte_mempool *mp_session;
+
+	/* Pre-crypto packets. */
+	struct ipsec_pkts_in in;
+
+	/* Post-crypto packets. */
+	struct ipsec_pkts_out out;
+
+	/* Crypto device enqueue threshold. */
+	uint32_t crypto_wr_threshold;
+
+	/* Packets currently under crypto device processing. */
+	uint32_t n_pkts_crypto;
+
+	/* List of free SADB positions. */
+	uint32_t *sa_free_id;
+
+	/* Number of elements in the SADB list of free positions. */
+	size_t n_sa_free_id;
+
+	/* Allocated memory total size in bytes. */
+	size_t total_size;
+
+	/* Flag for registration to the global list of instances. */
+	int registered;
+
+	/*
+	 * Table memory.
+	 */
+	uint8_t memory[] __rte_cache_aligned;
+};
+
+static inline struct ipsec_sa *
+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
+{
+	struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
+
+	return &sadb[sa_id & (ipsec->n_sa_max - 1)];
+}
+
+/* Global list of instances. */
+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ipsec_tailq = {
+	.name = "RTE_SWX_IPSEC",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
+
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name ||
+	    !name[0] ||
+	    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
+		return NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ipsec;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ipsec_register(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
+
+		if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ipsec;
+	TAILQ_INSERT_TAIL(ipsec_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ipsec_unregister(struct rte_swx_ipsec *ipsec)
+{
+	struct rte_swx_ipsec_list *ipsec_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ipsec_list, next) {
+		if (te->data == (void *)ipsec) {
+			TAILQ_REMOVE(ipsec_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
+
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
+{
+	size_t i;
+
+	if (!ipsec)
+		return;
+
+	/* Remove the current instance from the global list. */
+	if (ipsec->registered)
+		ipsec_unregister(ipsec);
+
+	/* SADB. */
+	for (i = 0; i < ipsec->n_sa_max; i++) {
+		struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
+
+		if (!sa->valid)
+			continue;
+
+		/* SA session. */
+		ipsec_session_free(ipsec, &sa->s);
+	}
+
+	/* Crypto device buffer pools. */
+	rte_mempool_free(ipsec->mp_session);
+
+	/* IPsec object memory. */
+	env_free(ipsec, ipsec->total_size);
+}
+
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node)
+{
+	char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
+	struct rte_swx_ipsec *ipsec = NULL;
+	struct rte_ring *ring_in, *ring_out;
+	struct rte_cryptodev_info dev_info;
+	size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
+	uint32_t dev_session_size;
+	int dev_id, status = 0;
+
+	/* Check input parameters. */
+	if (!ipsec_out ||
+	    !name ||
+	    !name[0] ||
+	    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
+	    !params ||
+	    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
+	    !params->n_sa_max) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_in = rte_ring_lookup(params->ring_in_name);
+	if (!ring_in) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	ring_out = rte_ring_lookup(params->ring_out_name);
+	if (!ring_out) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
+	if (dev_id == -1) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Memory allocation. */
+	n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
+
+	sadb_offset = sizeof(struct rte_swx_ipsec);
+	sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
+
+	sa_free_id_offset = sadb_offset + sadb_size;
+	sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
+
+	total_size = sa_free_id_offset + sa_free_id_size;
+	ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
+	if (!ipsec) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Initialization. */
+	strcpy(ipsec->name, name);
+	ipsec->ring_in = ring_in;
+	ipsec->ring_out = ring_out;
+	ipsec->dev_id = (uint8_t)dev_id;
+	ipsec->qp_id = params->crypto_dev_queue_pair_id;
+	memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
+	ipsec->n_sa_max = n_sa_max;
+
+	ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
+
+	ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
+	for (i = 0; i < n_sa_max; i++)
+		ipsec->sa_free_id[i] = n_sa_max - 1 - i;
+	ipsec->n_sa_free_id = n_sa_max;
+
+	ipsec->total_size = total_size;
+
+	/* Crypto device memory pools. */
+	dev_session_size = rte_cryptodev_sym_get_private_session_size((uint8_t)dev_id);
+
+	snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
+	ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
+		n_sa_max, /* number of pool elements */
+		dev_session_size, /* pool element size */
+		RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
+		0, /* pool element private data size */
+		numa_node);
+	if (!ipsec->mp_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* Add the current instance to the global list. */
+	status = ipsec_register(ipsec);
+	if (status)
+		goto error;
+
+	ipsec->registered = 1;
+
+	*ipsec_out = ipsec;
+	return 0;
+
+error:
+	rte_swx_ipsec_free(ipsec);
+	return status;
+}
+
+static inline int
+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
+{
+	struct ipsec_sa *sa;
+	struct rte_ipsec_group *g;
+	int n_groups, n_pkts_in_group, i;
+
+	sa = ipsec->in.sa[0];
+
+	g = &ipsec->in.groups[0];
+	g->id.ptr = sa;
+	g->m = &ipsec->in.pkts[0];
+	n_pkts_in_group = 1;
+	n_groups = 1;
+
+	for (i = 1; i < n_pkts; i++) {
+		struct ipsec_sa *sa_new = ipsec->in.sa[i];
+
+		/* Same SA => Add the current pkt to the same group. */
+		if (sa_new == sa) {
+			n_pkts_in_group++;
+			continue;
+		}
+
+		/* Different SA => Close the current group & add the current pkt to a new group. */
+		g->cnt = n_pkts_in_group;
+		sa = sa_new;
+
+		g++;
+		g->id.ptr = sa;
+		g->m = &ipsec->in.pkts[i];
+		n_pkts_in_group = 1;
+		n_groups++;
+	}
+
+	/* Close the last group. */
+	g->cnt = n_pkts_in_group;
+
+	return n_groups;
+}
+
+static inline void
+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
+{
+	struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
+	struct rte_crypto_op **src = ipsec->in.group_cops;
+
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
+	uint32_t n_dst = ipsec->in.n_cops;
+	uint32_t n_dst_max = ipsec->bsz.crypto_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_cops;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
+	 * instead of moving the SRC to DST first and then using DST.
+	 */
+	if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
+		uint16_t n_ok;
+
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
+		ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
+
+		for (i = n_ok; i < n_src; i++) {
+			struct rte_crypto_op *cop = src[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		return;
+	}
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
+		n_pkts_crypto += n_ok;
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_crypto_op *cop = dst0[i];
+			struct rte_mbuf *m = cop->sym->m_src;
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	ipsec->n_pkts_crypto = n_pkts_crypto;
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->in.n_cops = n_dst;
+}
+
+/**
+ * Packet buffer anatomy:
+ *
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | Offset   | Size    | Description                                                              |
+ * | (Byte #) | (Bytes) |                                                                          |
+ * +==========+=========+==========================================================================+
+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |
+ * |          |         | The buf_addr field points to the start of the packet section.            |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |
+ * +----------+---------+--------------------------------------------------------------------------+
+ * | 256      |         | Packet section.                                                          |
+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |
+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |
+ * +----------+---------+--------------------------------------------------------------------------+
+ */
+struct ipsec_mbuf {
+	struct ipsec_sa *sa;
+	struct rte_crypto_op cop;
+	struct rte_crypto_sym_op sym_cop;
+	uint8_t buffer[32]; /* The crypto IV is placed here. */
+};
+
+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
+
+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
+
+static inline void
+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
+{
+	int n_pkts, n_groups, i;
+
+	/* Read packets from the input ring. */
+	n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
+					   (void **)ipsec->in.pkts,
+					   ipsec->bsz.ring_rd,
+					   NULL);
+	if (!n_pkts)
+		return;
+
+	/* Get the SA for each packet. */
+	for (i = 0; i < n_pkts; i++) {
+		struct rte_mbuf *m = ipsec->in.pkts[i];
+		struct rte_swx_ipsec_input_packet_metadata *meta;
+		struct rte_ipv4_hdr *ipv4_hdr;
+		uint32_t sa_id;
+
+		meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
+
+		/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
+		sa_id = ntohl(meta->sa_id);
+
+		/* Consume the IPsec meta-data. */
+		m->data_off += META_LENGTH;
+		m->data_len -= META_LENGTH;
+		m->pkt_len -= META_LENGTH;
+
+		/* Set the fields required by the IPsec library. */
+		m->l2_len = 0;
+		m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
+			sizeof(struct rte_ipv4_hdr) :
+			sizeof(struct rte_ipv6_hdr);
+
+		/* Get the SA. */
+		ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
+	}
+
+	/* Group packets that share the same SA. */
+	n_groups = ipsec_sa_group(ipsec, n_pkts);
+
+	/* Write each group of packets sharing the same SA to the crypto device. */
+	for (i = 0; i < n_groups; i++) {
+		struct rte_ipsec_group *g = &ipsec->in.groups[i];
+		struct ipsec_sa *sa = g->id.ptr;
+		struct rte_ipsec_session *s = &sa->s;
+		uint32_t j;
+		uint16_t n_pkts_ok;
+
+		/* Prepare the crypto ops for the current group. */
+		for (j = 0; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+			struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
+
+			priv->sa = sa;
+			ipsec->in.group_cops[j] = &priv->cop;
+		}
+
+		n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the crypto ops of the current group to the crypto device. */
+		ipsec_crypto_enqueue(ipsec, n_pkts_ok);
+	}
+}
+
+static inline void
+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
+{
+	struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
+	struct rte_mbuf **src = g->m;
+
+	uint32_t n_dst = ipsec->out.n_pkts;
+	uint32_t n_dst_max = ipsec->bsz.ring_wr;
+	uint32_t n_dst_avail = n_dst_max - n_dst;
+	uint32_t n_src = n_pkts;
+	uint32_t i;
+
+	dst = &dst0[n_dst];
+
+	/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
+	for ( ; n_src >= n_dst_avail; ) {
+		uint32_t n_ok;
+
+		/* Move from SRC to DST. */
+		for (i = 0; i < n_dst_avail; i++)
+			*dst++ = *src++;
+
+		n_src -= n_dst_avail;
+
+		/* DST full: send burst from DST. */
+		n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
+
+		for (i = n_ok ; i < n_dst_max; i++) {
+			struct rte_mbuf *m = dst[i];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Next iteration. */
+		dst = dst0;
+		n_dst = 0;
+		n_dst_avail = n_dst_max;
+	}
+
+	/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
+	for (i = 0; i < n_src; i++)
+		*dst++ = *src++;
+
+	n_dst += n_src;
+
+	ipsec->out.n_pkts = n_dst;
+}
+
+static inline void
+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
+{
+	uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, ng, i;
+
+	/* Read the crypto ops from the crypto device. */
+	if (!n_pkts_crypto)
+		return;
+
+	n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
+					     ipsec->qp_id,
+					     ipsec->out.cops,
+					     ipsec->bsz.crypto_rd);
+	if (!n_pkts)
+		return;
+
+	ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
+
+	/* Group packets that share the same SA. */
+	ng = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
+					      ipsec->out.group_pkts,
+					      ipsec->out.groups,
+					      n_pkts);
+
+	/* Perform post-crypto IPsec processing for each group of packets that share the same SA.
+	 * Write each group of packets to the output ring.
+	 */
+	for (i = 0, n_pkts = 0; i < ng; i++) {
+		struct rte_ipsec_group *g = &ipsec->out.groups[i];
+		struct rte_ipsec_session *s = g->id.ptr;
+		uint32_t n_pkts_ok, j;
+
+		/* Perform post-crypto IPsec processing for the current group. */
+		n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
+
+		for (j = n_pkts_ok; j < g->cnt; j++) {
+			struct rte_mbuf *m = g->m[j];
+
+			rte_pktmbuf_free(m);
+		}
+
+		/* Write the packets of the current group to the output ring. */
+		ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
+	}
+}
+
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
+{
+	rte_swx_ipsec_pre_crypto(ipsec);
+	rte_swx_ipsec_post_crypto(ipsec);
+}
+
+/**
+ * IPsec Control Plane API
+ */
+struct cipher_alg {
+	const char *name;
+	enum rte_crypto_cipher_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t key_size;
+};
+
+struct auth_alg {
+	const char *name;
+	enum rte_crypto_auth_algorithm alg;
+	uint32_t iv_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+};
+
+struct aead_alg {
+	const char *name;
+	enum rte_crypto_aead_algorithm alg;
+	uint32_t iv_size;
+	uint32_t block_size;
+	uint32_t digest_size;
+	uint32_t key_size;
+	uint32_t aad_size;
+};
+
+static struct cipher_alg cipher_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_CIPHER_NULL,
+		.iv_size = 0,
+		.block_size = 4,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "aes-cbc-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 16,
+	},
+
+	[2] = {
+		.name = "aes-cbc-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 24,
+	},
+
+	[3] = {
+		.name = "aes-cbc-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 32,
+	},
+
+	[4] = {
+		.name = "aes-ctr-128",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+	},
+
+	[5] = {
+		.name = "aes-ctr-192",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 28,
+	},
+
+	[6] = {
+		.name = "aes-ctr-256",
+		.alg = RTE_CRYPTO_CIPHER_AES_CTR,
+		.iv_size = 16,
+		.block_size = 16,
+		.key_size = 36,
+	},
+
+	[7] = {
+		.name = "3des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 24,
+	},
+
+	[8] = {
+		.name = "des-cbc",
+		.alg = RTE_CRYPTO_CIPHER_DES_CBC,
+		.iv_size = 8,
+		.block_size = 8,
+		.key_size = 8,
+	},
+};
+
+static struct auth_alg auth_algs[] = {
+	[0] = {
+		.name = "null",
+		.alg = RTE_CRYPTO_AUTH_NULL,
+		.iv_size = 0,
+		.digest_size = 0,
+		.key_size = 0,
+	},
+
+	[1] = {
+		.name = "sha1-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 20,
+	},
+
+	[2] = {
+		.name = "sha256-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
+		.iv_size = 0,
+		.digest_size = 16,
+		.key_size = 32,
+	},
+
+	[3] = {
+		.name = "sha384-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
+		.iv_size = 0,
+		.digest_size = 24,
+		.key_size = 48,
+	},
+
+	[4] = {
+		.name = "sha512-hmac",
+		.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
+		.iv_size = 0,
+		.digest_size = 32,
+		.key_size = 64,
+	},
+
+	[5] = {
+		.name = "aes-gmac",
+		.alg = RTE_CRYPTO_AUTH_AES_GMAC,
+		.iv_size = 8,
+		.digest_size = 16,
+		.key_size = 20,
+	},
+
+	[6] = {
+		.name = "aes-xcbc-mac-96",
+		.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
+		.iv_size = 0,
+		.digest_size = 12,
+		.key_size = 16,
+	},
+};
+
+static struct aead_alg aead_algs[] = {
+	[0] = {
+		.name = "aes-gcm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[1] = {
+		.name = "aes-gcm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[2] = {
+		.name = "aes-gcm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_GCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[3] = {
+		.name = "aes-ccm-128",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 20,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[4] = {
+		.name = "aes-ccm-192",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 28,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[5] = {
+		.name = "aes-ccm-256",
+		.alg = RTE_CRYPTO_AEAD_AES_CCM,
+		.iv_size = 8,
+		.block_size = 4,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+
+	[6] = {
+		.name = "chacha20-poly1305",
+		.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
+		.iv_size = 12,
+		.block_size = 64,
+		.key_size = 36,
+		.digest_size = 16,
+		.aad_size = 8,
+	},
+};
+
+static struct cipher_alg *
+cipher_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct cipher_alg *
+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algs); i++) {
+		struct cipher_alg *alg = &cipher_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct auth_alg *
+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(auth_algs); i++) {
+		struct auth_alg *alg = &auth_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (!strcmp(name, alg->name))
+			return alg;
+	}
+
+	return NULL;
+}
+
+static struct aead_alg *
+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(aead_algs); i++) {
+		struct aead_alg *alg = &aead_algs[i];
+
+		if (alg->alg == alg_id && alg->key_size == key_size)
+			return alg;
+	}
+
+	return NULL;
+}
+
+static int
+char_to_hex(char c, uint8_t *val)
+{
+	if (c >= '0' && c <= '9') {
+		*val = c - '0';
+		return 0;
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		*val = c - 'A' + 10;
+		return 0;
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		*val = c - 'a' + 10;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
+{
+	uint32_t i;
+
+	/* Check input arguments. */
+	if (!src || !src[0] || !dst || !n_dst_bytes)
+		return -EINVAL;
+
+	/* Skip any leading "0x" or "0X" in the src string. */
+	if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
+		src += 2;
+
+	/* Convert each group of two hex characters in the src string to one byte in dst array. */
+	for (i = 0; i < n_dst_bytes; i++) {
+		uint8_t a, b;
+		int status;
+
+		status = char_to_hex(*src, &a);
+		if (status)
+			return status;
+		src++;
+
+		status = char_to_hex(*src, &b);
+		if (status)
+			return status;
+		src++;
+
+		dst[i] = a * 16 + b;
+	}
+
+	/* Check for the end of the src string. */
+	if (*src)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+token_is_comment(const char *token)
+{
+	if ((token[0] == '#') ||
+	    (token[0] == ';') ||
+	    ((token[0] == '/') && (token[1] == '/')))
+		return 1; /* TRUE. */
+
+	return 0; /* FALSE. */
+}
+
+#define MAX_TOKENS 64
+
+#define CHECK(condition, msg)          \
+do {                                   \
+	if (!(condition)) {            \
+		if (errmsg)            \
+			*errmsg = msg; \
+		goto error;            \
+	}                              \
+} while (0)
+
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg)
+{
+	char *token_array[MAX_TOKENS], **t;
+	struct rte_swx_ipsec_sa_params *p = NULL;
+	char *s0 = NULL, *s;
+	uint32_t n_tokens = 0;
+	int blank_or_comment = 0;
+
+	/* Check input arguments. */
+	CHECK(string && string[0], "NULL input");
+
+	/* Memory allocation. */
+	s0 = strdup(string);
+	p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
+	CHECK(s0 && p, "Not enough memory");
+
+	/* Parse the string into tokens. */
+	for (s = s0; ; ) {
+		char *token;
+
+		token = strtok_r(s, " \f\n\r\t\v", &s);
+		if (!token || token_is_comment(token))
+			break;
+
+		CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
+
+		token_array[n_tokens] = token;
+		n_tokens++;
+	}
+
+	t = token_array;
+	if (!n_tokens) {
+		blank_or_comment = 1;
+		goto error;
+	}
+
+	/*
+	 * Crypto operation.
+	 */
+	if (!strcmp(t[0], "encrypt"))
+		p->encrypt = 1;
+	else if (!strcmp(t[0], "decrypt"))
+		p->encrypt = 0;
+	else
+		CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
+
+	t++;
+	n_tokens--;
+
+	/*
+	 * Crypto parameters.
+	 */
+	CHECK(n_tokens >= 2, "Not enough tokens");
+
+	if (!strcmp(t[0], "cipher")) {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t key_size;
+
+		p->crypto.is_aead = 0;
+
+		/* cipher. */
+		cipher_alg = cipher_alg_find(t[1]);
+		CHECK(cipher_alg, "Unsupported cipher algorithm");
+
+		key_size = cipher_alg->key_size;
+		p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
+		p->crypto.cipher_auth.cipher.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
+				"Cipher algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
+			CHECK(!status, "Cipher key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+
+		/* authentication. */
+		CHECK(n_tokens >= 2, "Not enough tokens");
+		CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
+
+		auth_alg = auth_alg_find(t[1]);
+		CHECK(auth_alg, "Unsupported authentication algorithm");
+
+		key_size = auth_alg->key_size;
+		p->crypto.cipher_auth.auth.alg = auth_alg->alg;
+		p->crypto.cipher_auth.auth.key_size = key_size;
+
+		t += 2;
+		n_tokens -= 2;
+
+		if (key_size) {
+			int status;
+
+			CHECK(n_tokens >= 2, "Not enough tokens");
+			CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
+			CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
+				"Authentication algorithm key too big");
+
+			status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
+			CHECK(!status, "Authentication key invalid format");
+
+			t += 2;
+			n_tokens -= 2;
+		}
+	} else if (!strcmp(t[0], "aead")) {
+		struct aead_alg *alg;
+		uint32_t key_size;
+		int status;
+
+		p->crypto.is_aead = 1;
+
+		CHECK(n_tokens >= 4, "Not enough tokens");
+		alg = aead_alg_find(t[1]);
+		CHECK(alg, "Unsupported AEAD algorithm");
+
+		key_size = alg->key_size;
+		p->crypto.aead.alg = alg->alg;
+		p->crypto.aead.key_size = key_size;
+
+		CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
+		CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
+			"AEAD algorithm key too big");
+
+		status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
+		CHECK(!status, "AEAD key invalid format");
+
+		t += 4;
+		n_tokens -= 4;
+	} else
+		CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
+
+	/*
+	 * Packet ecapsulation parameters.
+	 */
+	CHECK(n_tokens >= 4, "Not enough tokens");
+	CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
+	CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
+
+	p->encap.esp.spi = strtoul(t[2], &t[2], 0);
+	CHECK(!t[2][0], "ESP SPI field invalid format");
+
+	t += 3;
+	n_tokens -= 3;
+
+	if (!strcmp(t[0], "tunnel")) {
+		p->encap.tunnel_mode = 1;
+
+		CHECK(n_tokens >= 6, "Not enough tokens");
+
+		if (!strcmp(t[1], "ipv4")) {
+			uint32_t addr;
+
+			p->encap.tunnel_ipv4 = 1;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			addr = strtoul(t[3], &t[3], 0);
+			CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
+			p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			addr = strtoul(t[5], &t[5], 0);
+			CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
+			p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
+
+			t += 6;
+			n_tokens -= 6;
+		} else if (!strcmp(t[1], "ipv6")) {
+			int status;
+
+			p->encap.tunnel_ipv4 = 0;
+
+			CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
+
+			status = hex_string_parse(t[3],
+						  p->encap.tunnel.ipv6.src_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 source address invalid format");
+
+			CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
+
+			status = hex_string_parse(t[5],
+						  p->encap.tunnel.ipv6.dst_addr.s6_addr,
+						  16);
+			CHECK(!status, "Tunnel IPv6 destination address invalid format");
+
+			t += 6;
+			n_tokens -= 6;
+		} else
+			CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
+	} else if (!strcmp(t[0], "transport")) {
+		p->encap.tunnel_mode = 0;
+
+		t++;
+		n_tokens--;
+	} else
+		CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
+
+	/*
+	 * Any other parameters.
+	 */
+	CHECK(!n_tokens, "Unexpected trailing tokens");
+
+	free(s0);
+	return p;
+
+error:
+	free(p);
+	free(s0);
+	if (is_blank_or_comment)
+		*is_blank_or_comment = blank_or_comment;
+	return NULL;
+}
+
+static void
+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv4_hdr ipv4_hdr = {
+		.version_ihl = 0x45,
+		.type_of_service = 0,
+		.total_length = 0, /* Cannot be pre-computed. */
+		.packet_id = 0,
+		.fragment_offset = 0,
+		.time_to_live = 64,
+		.next_proto_id = IPPROTO_ESP,
+		.hdr_checksum = 0, /* Cannot be pre-computed. */
+		.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
+		.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
+	};
+
+	memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
+}
+
+static void
+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
+{
+	struct rte_ipv6_hdr ipv6_hdr = {
+		.vtc_flow = 0x60000000,
+		.payload_len = 0, /* Cannot be pre-computed. */
+		.proto = IPPROTO_ESP,
+		.hop_limits = 64,
+		.src_addr = {0},
+		.dst_addr = {0},
+	};
+
+	memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
+	memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
+	memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
+}
+
+/* IPsec library SA parameters. */
+static struct rte_crypto_sym_xform *
+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_crypto_sym_xform *xform,
+		uint32_t *salt_out)
+{
+	if (p->crypto.is_aead) {
+		struct aead_alg *alg;
+		uint32_t key_size, salt, iv_length;
+
+		alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
+		if (!alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		key_size = p->crypto.aead.key_size - 4;
+		memcpy(&salt, &p->crypto.aead.key[key_size], 4);
+
+		/* IV length. */
+		iv_length = 12;
+		if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
+			iv_length = 11;
+
+		/* xform. */
+		xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
+		xform[0].aead.op = p->encrypt ?
+			RTE_CRYPTO_AEAD_OP_ENCRYPT :
+			RTE_CRYPTO_AEAD_OP_DECRYPT;
+		xform[0].aead.algo = p->crypto.aead.alg;
+		xform[0].aead.key.data = p->crypto.aead.key;
+		xform[0].aead.key.length = key_size;
+		xform[0].aead.iv.offset = IV_OFFSET;
+		xform[0].aead.iv.length = iv_length;
+		xform[0].aead.digest_length = alg->digest_size;
+		xform[0].aead.aad_length = alg->aad_size;
+		xform[0].next = NULL;
+
+		*salt_out = salt;
+		return &xform[0];
+	} else {
+		struct cipher_alg *cipher_alg;
+		struct auth_alg *auth_alg;
+		uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
+
+		cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
+						   p->crypto.cipher_auth.cipher.key_size);
+		if (!cipher_alg)
+			return NULL;
+
+		auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
+					       p->crypto.cipher_auth.auth.key_size);
+		if (!auth_alg)
+			return NULL;
+
+		/* salt and salt-related key size adjustment. */
+		cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
+		auth_key_size = p->crypto.cipher_auth.auth.key_size;
+
+		switch (p->crypto.cipher_auth.cipher.alg) {
+		case RTE_CRYPTO_CIPHER_AES_CBC:
+		case RTE_CRYPTO_CIPHER_3DES_CBC:
+			salt = (uint32_t)rand();
+			break;
+
+		case RTE_CRYPTO_CIPHER_AES_CTR:
+			cipher_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
+			break;
+
+		default:
+			salt = 0;
+		}
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			auth_key_size -= 4;
+			memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
+		}
+
+		/* IV length. */
+		auth_iv_length = cipher_alg->iv_size;
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
+			auth_iv_length = 12;
+
+		/* xform. */
+		if (p->encrypt) {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[0].cipher.key.length = cipher_key_size;
+			xform[0].cipher.iv.offset = IV_OFFSET;
+			xform[0].cipher.iv.length = cipher_alg->iv_size;
+			xform[0].cipher.dataunit_len = 0;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+			xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[1].auth.key.length = auth_key_size;
+			xform[1].auth.iv.offset = IV_OFFSET;
+			xform[1].auth.iv.length = auth_iv_length;
+			xform[1].auth.digest_length = auth_alg->digest_size;
+			xform[1].next = NULL;
+		} else {
+			xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+			xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
+			xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
+			xform[0].auth.key.length = auth_key_size;
+			xform[0].auth.iv.offset = IV_OFFSET;
+			xform[0].auth.iv.length = auth_iv_length;
+			xform[0].auth.digest_length = auth_alg->digest_size;
+			xform[0].next = &xform[1];
+
+			xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
+			xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
+			xform[1].cipher.key.length = cipher_key_size;
+			xform[1].cipher.iv.offset = IV_OFFSET;
+			xform[1].cipher.iv.length = cipher_alg->iv_size;
+			xform[1].cipher.dataunit_len = 0;
+			xform[1].next = NULL;
+		}
+
+		*salt_out = salt;
+
+		if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
+			if (p->encrypt)
+				return &xform[1];
+
+			xform[0].next = NULL;
+			return &xform[0];
+		}
+
+		return &xform[0];
+	}
+}
+
+static void
+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
+		struct rte_security_ipsec_xform *ipsec_xform,
+		uint32_t salt)
+{
+	ipsec_xform->spi = p->encap.esp.spi;
+
+	ipsec_xform->salt = salt;
+
+	ipsec_xform->options.esn = 0;
+	ipsec_xform->options.udp_encap = 0;
+	ipsec_xform->options.copy_dscp = 1;
+	ipsec_xform->options.copy_flabel = 0;
+	ipsec_xform->options.copy_df = 0;
+	ipsec_xform->options.dec_ttl = 0;
+	ipsec_xform->options.ecn = 1;
+	ipsec_xform->options.stats = 0;
+	ipsec_xform->options.iv_gen_disable = 0;
+	ipsec_xform->options.tunnel_hdr_verify = 0;
+	ipsec_xform->options.udp_ports_verify = 0;
+	ipsec_xform->options.ip_csum_enable = 0;
+	ipsec_xform->options.l4_csum_enable = 0;
+	ipsec_xform->options.ip_reassembly_en = 0;
+	ipsec_xform->options.reserved_opts = 0;
+
+	ipsec_xform->direction = p->encrypt ?
+		RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
+		RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+
+	ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+
+	ipsec_xform->mode = p->encap.tunnel_mode ?
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
+
+	ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
+		RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
+		RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
+			ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
+			ipsec_xform->tunnel.ipv4.dscp = 0;
+			ipsec_xform->tunnel.ipv4.df = 0;
+			ipsec_xform->tunnel.ipv4.ttl = 64;
+		} else {
+			ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
+			ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
+			ipsec_xform->tunnel.ipv6.dscp = 0;
+			ipsec_xform->tunnel.ipv6.flabel = 0;
+			ipsec_xform->tunnel.ipv6.hlimit = 64;
+		}
+	}
+
+	ipsec_xform->life.packets_soft_limit = 0;
+	ipsec_xform->life.bytes_soft_limit = 0;
+	ipsec_xform->life.packets_hard_limit = 0;
+	ipsec_xform->life.bytes_hard_limit = 0;
+
+	ipsec_xform->replay_win_sz = 0;
+
+	ipsec_xform->esn.value = 0;
+
+	ipsec_xform->udp.dport = 0;
+	ipsec_xform->udp.sport = 0;
+}
+
+static int
+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
+		 struct rte_ipsec_sa_prm *sa_prm,
+		 struct rte_ipv4_hdr *ipv4_hdr,
+		 struct rte_ipv6_hdr *ipv6_hdr,
+		 struct rte_crypto_sym_xform *crypto_xform)
+{
+	uint32_t salt;
+
+	memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
+
+	sa_prm->userdata = 0; /* Not used. */
+
+	sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
+
+	/*
+	 * crypto_xform.
+	 */
+	sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
+	if (!sa_prm->crypto_xform)
+		return -EINVAL;
+
+	/*
+	 * ipsec_xform.
+	 */
+	ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
+
+	/*
+	 * tunnel / transport.
+	 *
+	 * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
+	 * the input packet type should be added to the SA configuration parameters.
+	 */
+	if (p->encap.tunnel_mode) {
+		if (p->encap.tunnel_ipv4) {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv4_hdr;
+		} else {
+			sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
+			sa_prm->tun.hdr_l3_off = 0;
+			sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
+			sa_prm->tun.hdr = ipv6_hdr;
+		}
+	} else {
+		sa_prm->trs.proto = IPPROTO_IPIP; /* IPv4. */
+	}
+
+	return 0;
+}
+
+static int
+ipsec_session_create(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *p,
+		     struct rte_ipsec_session *s)
+{
+	struct rte_ipv4_hdr ipv4_hdr;
+	struct rte_ipv6_hdr ipv6_hdr;
+	struct rte_crypto_sym_xform crypto_xform[2];
+	struct rte_ipsec_sa_prm sa_prm;
+	struct rte_ipsec_sa *sa = NULL;
+	struct rte_cryptodev_sym_session *crypto_session = NULL;
+	int sa_size;
+	int sa_valid = 0, status = 0;
+
+	tunnel_ipv4_header_set(&ipv4_hdr, p);
+	tunnel_ipv6_header_set(&ipv6_hdr, p);
+
+	/* IPsec library SA setup. */
+	status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
+	if (status)
+		goto error;
+
+	sa_size = rte_ipsec_sa_size(&sa_prm);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa = calloc(1, sa_size);
+	if (!sa) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
+	if (sa_size < 0) {
+		status = sa_size;
+		goto error;
+	}
+	if (!sa_size) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	sa_valid = 1;
+
+	/* Cryptodev library session setup. */
+	crypto_session = rte_cryptodev_sym_session_create(ipsec->dev_id,
+							  sa_prm.crypto_xform,
+							  ipsec->mp_session);
+	if (!crypto_session) {
+		status = -ENOMEM;
+		goto error;
+	}
+
+	/* IPsec library session setup. */
+	s->sa = sa;
+	s->type = RTE_SECURITY_ACTION_TYPE_NONE;
+	s->crypto.ses = crypto_session;
+	s->crypto.dev_id = ipsec->dev_id;
+	s->pkt_func.prepare.async = NULL;
+	s->pkt_func.process = NULL;
+
+	return rte_ipsec_session_prepare(s);
+
+error:
+	/* sa. */
+	if (sa_valid)
+		rte_ipsec_sa_fini(sa);
+
+	free(sa);
+
+	/* crypto_session. */
+	if (crypto_session)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, crypto_session);
+
+	/* s. */
+	memset(s, 0, sizeof(*s));
+
+	return status;
+}
+
+static void
+ipsec_session_free(struct rte_swx_ipsec *ipsec,
+		   struct rte_ipsec_session *s)
+{
+	if (!s)
+		return;
+
+	/* IPsec library SA. */
+	if (s->sa)
+		rte_ipsec_sa_fini(s->sa);
+	free(s->sa);
+
+	/* Cryptodev library session. */
+	if (s->crypto.ses)
+		rte_cryptodev_sym_session_free(ipsec->dev_id, s->crypto.ses);
+
+	/* IPsec library session. */
+	memset(s, 0, sizeof(*s));
+}
+
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *id)
+{
+	struct ipsec_sa *sa;
+	uint32_t sa_id;
+	int status;
+
+	/* Check the input parameters. */
+	if (!ipsec || !sa_params || !id)
+		return -EINVAL;
+
+	/* Allocate a free SADB entry. */
+	if (!ipsec->n_sa_free_id)
+		return -ENOSPC;
+
+	sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
+	ipsec->n_sa_free_id--;
+
+	/* Acquire the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	status = ipsec_session_create(ipsec, sa_params, &sa->s);
+	if (status) {
+		/* Free the allocated SADB entry. */
+		ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+		ipsec->n_sa_free_id++;
+
+		return status;
+	}
+
+	/* Validate the new SA. */
+	sa->valid = 1;
+	*id = sa_id;
+
+	return 0;
+}
+
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id)
+{
+	struct ipsec_sa *sa;
+
+	/* Check the input parameters. */
+	if (!ipsec || (sa_id >= ipsec->n_sa_max))
+		return;
+
+	/* Release the SA resources. */
+	sa = ipsec_sa_get(ipsec, sa_id);
+
+	ipsec_session_free(ipsec, &sa->s);
+
+	/* Free the SADB entry. */
+	ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
+	ipsec->n_sa_free_id++;
+
+	/* Invalidate the SA. */
+	sa->valid = 0;
+}
diff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h
new file mode 100644
index 0000000000..ebfb7ea5ea
--- /dev/null
+++ b/lib/pipeline/rte_swx_ipsec.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__
+#define __INCLUDE_RTE_SWX_IPSEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE SWX Internet Protocol Security (IPsec)
+ *
+ * The IPsec block is a companion block for the SWX pipeline used to provide IPsec support to the
+ * pipeline. The block is external to the pipeline, hence it needs to be explicitly instantiated by
+ * the user and connected to a pipeline instance through the pipeline I/O ports.
+ *
+ * Main features:
+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text
+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.
+ *
+ * Security Association (SA):
+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is
+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.
+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then
+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field
+ *   is typically written by the pipeline before sending the packet to the IPsec block.
+ *
+ * Packet format:
+ * - IPsec block input packet (i.e. pipeline output packet):
+ *	- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.
+ *	- IPv4 header.
+ *	- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.
+ * - IPsec block output packet (i.e. pipeline input packet):
+ *	- IPv4 header.
+ *	- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.
+ *
+ * SA update procedure:
+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().
+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().
+ * - To update an existing SA, the control plane has to follow the following steps:
+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can
+ *      fail, for example when the SA table is full.
+ *   2. Wait until no more packets are using the old SA.
+ *   3. Delete the old SA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <rte_compat.h>
+#include <rte_crypto_sym.h>
+
+/**
+ * IPsec Setup API
+ */
+
+/** IPsec instance opaque data structure. */
+struct rte_swx_ipsec;
+
+/** Name size. */
+#ifndef RTE_SWX_IPSEC_NAME_SIZE
+#define RTE_SWX_IPSEC_NAME_SIZE 64
+#endif
+
+/** Maximum burst size. */
+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX
+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256
+#endif
+
+/** IPsec burst sizes. */
+struct rte_swx_ipsec_burst_size {
+	/** Input ring read burst size. */
+	uint32_t ring_rd;
+
+	/** Output ring write burst size. */
+	uint32_t ring_wr;
+
+	/** Crypto device request queue write burst size. */
+	uint32_t crypto_wr;
+
+	/** Crypto device response queue read burst size. */
+	uint32_t crypto_rd;
+};
+
+/**
+ * IPsec instance configuration parameters
+ *
+ */
+struct rte_swx_ipsec_params {
+	/** Input packet queue. */
+	const char *ring_in_name;
+
+	/** Output packet queue.  */
+	const char *ring_out_name;
+
+	/** Crypto device name. */
+	const char *crypto_dev_name;
+
+	/** Crypto device queue pair ID. */
+	uint32_t crypto_dev_queue_pair_id;
+
+	/** Burst size. */
+	struct rte_swx_ipsec_burst_size bsz;
+
+	/** Maximum number of SAs. */
+	uint32_t n_sa_max;
+};
+
+/**
+ * IPsec input packet meta-data
+ *
+ */
+struct rte_swx_ipsec_input_packet_metadata {
+	/* SA ID. */
+	uint32_t sa_id;
+};
+
+/**
+ * IPsec instance find
+ *
+ * @param[in] name
+ *   IPsec instance name.
+ * @return
+ *   Valid IPsec instance handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ipsec *
+rte_swx_ipsec_find(const char *name);
+
+/**
+ * IPsec instance create
+ *
+ * @param[out] ipsec
+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this
+ *   function returns successfully.
+ * @param[in] name
+ *   IPsec instance unique name.
+ * @param[in] params
+ *   IPsec instance configuration parameters.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,
+		     const char *name,
+		     struct rte_swx_ipsec_params *params,
+		     int numa_node);
+
+/**
+ * IPsec instance free
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);
+
+/**
+ * IPsec Data Plane API
+ */
+
+/**
+ * IPsec instance run
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);
+
+/*
+ * IPsec Control Plane API
+ */
+
+/** Maximum key size in bytes. */
+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64
+
+/** IPsec SA crypto cipher parameters. */
+struct rte_swx_ipsec_sa_cipher_params {
+	/** Cipher algorithm. */
+	enum rte_crypto_cipher_algorithm alg;
+
+	/** Cipher key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Cipher key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto authentication parameters. */
+struct rte_swx_ipsec_sa_authentication_params {
+	/** Authentication algorithm. */
+	enum rte_crypto_auth_algorithm alg;
+
+	/** Authentication key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** Authentication key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */
+struct rte_swx_ipsec_sa_aead_params {
+	/** AEAD algorithm. */
+	enum rte_crypto_aead_algorithm alg;
+
+	/** AEAD key. */
+	uint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];
+
+	/** AEAD key size in bytes. */
+	uint32_t key_size;
+};
+
+/** IPsec protocol encapsulation parameters. */
+struct rte_swx_ipsec_sa_encap_params {
+	/** Encapsulating Security Payload (ESP) header. */
+	struct {
+		/** Security Parameters Index (SPI) field. */
+		uint32_t spi;
+	} esp;
+
+	/** Tunnel mode when non-zero, transport mode when zero. */
+	int tunnel_mode;
+
+	/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */
+	int tunnel_ipv4;
+
+	/** Tunnel parameters. Valid for tunnel mode only. */
+	union {
+		/** IPv4 header. */
+		struct {
+			/** Source address. */
+			struct in_addr src_addr;
+
+			/** Destination address. */
+			struct in_addr dst_addr;
+		} ipv4;
+
+		/** IPv6 header. */
+		struct {
+			/** Source address. */
+			struct in6_addr src_addr;
+
+			/** Destination address. */
+			struct in6_addr dst_addr;
+		} ipv6;
+	} tunnel;
+};
+
+/** IPsec Security Association (SA) parameters. */
+struct rte_swx_ipsec_sa_params {
+	/** Crypto operation: encrypt when non-zero, decrypt when zero. */
+	int encrypt;
+
+	/** Crypto operation parameters. */
+	struct {
+		RTE_STD_C11
+		union {
+			struct {
+				/** Crypto cipher operation parameters. */
+				struct rte_swx_ipsec_sa_cipher_params cipher;
+
+				/** Crypto authentication operation parameters. */
+				struct rte_swx_ipsec_sa_authentication_params auth;
+			} cipher_auth;
+
+			/** Crypto AEAD operation parameters. */
+			struct rte_swx_ipsec_sa_aead_params aead;
+		};
+
+		/** Non-zero for AEAD, zero for cipher & authentication. */
+		int is_aead;
+	} crypto;
+
+	/** Packet encasulation parameters. */
+	struct rte_swx_ipsec_sa_encap_params encap;
+};
+
+/**
+ * IPsec SA add
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_params
+ *   SA parameters.
+ * @param[out] sa_id
+ *   On success, the SA ID.
+ * @return
+ *   0 on success or error code otherwise.
+ */
+__rte_experimental
+int
+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
+		     struct rte_swx_ipsec_sa_params *sa_params,
+		     uint32_t *sa_id);
+
+/**
+ * IPsec SA delete
+ *
+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used
+ * by the Data Plane.
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] sa_id
+ *   The SA ID.
+ */
+__rte_experimental
+void
+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
+			uint32_t sa_id);
+
+/**
+ * IPsec SA read from string
+ *
+ * IPsec SA syntax:
+ *
+ * \<sa>
+ *    : encrypt \<crypto_params> \<encap_params>
+ *    | decrypt \<crypto_params> \<encap_params>
+ *    ;
+ *
+ * \<crypto_params>
+ *    : \<cipher> \<auth>
+ *    | \<aead>
+ *    ;
+ *
+ * \<cipher>
+ *    : cipher \<ciher_alg> key \<cipher_key>
+ *    | cipher \<cipher_alg>
+ *    ;
+ *
+ * \<auth>
+ *    : auth \<authentication_alg> key \<authentication_key>
+ *    | auth \<authentication_alg>
+ *    ;
+ *
+ * \<aead>
+ *    : aead \<aead_alg> key \<aead_key>
+ *    ;
+ *
+ * \<encap_params>
+ *    : esp spi \<spi> tunnel ipv4 srcaddr \<ipv4_src_addr> dstaddr \<ipv4_dst_addr>
+ *    | esp spi \<spi> tunnel ipv6 srcaddr \<ipv6_src_addr> dstaddr \<ipv6_dst_addr>
+ *    | esp spi \<spi> transport
+ *    ;
+ *
+ * @param[in] ipsec
+ *   IPsec instance handle.
+ * @param[in] string
+ *   String containing the SA.
+ * @param[in,out] is_blank_or_comment
+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when
+ *   *string* contains a blank or comment line and to zero otherwise.
+ * @param[in,out] errmsg
+ *   On error, when its input value is not NULL, this argument points to a string with details on
+ *   the detected error.
+ * @return
+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.
+ */
+__rte_experimental
+struct rte_swx_ipsec_sa_params *
+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,
+		      const char *string,
+		      int *is_blank_or_comment,
+		      const char **errmsg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 184f45a6b7..58e34459c3 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -155,4 +155,13 @@ EXPERIMENTAL {
 	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
+
+	#added in 23.03
+	rte_swx_ipsec_create;
+	rte_swx_ipsec_find;
+	rte_swx_ipsec_free;
+	rte_swx_ipsec_run;
+	rte_swx_ipsec_sa_add;
+	rte_swx_ipsec_sa_delete;
+	rte_swx_ipsec_sa_read;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 02/11] examples/pipeline: rework memory pool support
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 01/11] " Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
                     ` (9 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Rework the memory pool CLI command to accommodate the MBUF private
meta-data area size parameter.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 72 ++++++++++-------
 examples/pipeline/examples/fib.cli            |  2 +-
 examples/pipeline/examples/hash_func.cli      |  2 +-
 examples/pipeline/examples/l2fwd.cli          |  2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |  2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |  2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |  2 +-
 examples/pipeline/examples/learner.cli        |  2 +-
 examples/pipeline/examples/meter.cli          |  2 +-
 examples/pipeline/examples/mirroring.cli      |  2 +-
 examples/pipeline/examples/recirculation.cli  |  2 +-
 examples/pipeline/examples/registers.cli      |  2 +-
 examples/pipeline/examples/selector.cli       |  2 +-
 examples/pipeline/examples/varbit.cli         |  2 +-
 examples/pipeline/examples/vxlan.cli          |  2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |  2 +-
 examples/pipeline/obj.c                       | 80 +------------------
 examples/pipeline/obj.h                       | 27 -------
 18 files changed, 63 insertions(+), 146 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index e1e7aaddc1..14f1cfa47e 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -192,72 +192,88 @@ parse_table_entry(struct rte_swx_ctl_pipeline *p,
 }
 
 static const char cmd_mempool_help[] =
-"mempool <mempool_name>\n"
-"   buffer <buffer_size>\n"
-"   pool <pool_size>\n"
-"   cache <cache_size>\n"
-"   cpu <cpu_id>\n";
+"mempool <mempool_name> "
+"meta <mbuf_private_size> "
+"pkt <pkt_buffer_size> "
+"pool <pool_size> "
+"cache <cache_size> "
+"numa <numa_node>\n";
 
 static void
 cmd_mempool(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
+	    uint32_t n_tokens,
+	    char *out,
+	    size_t out_size,
+	    void *obj __rte_unused)
 {
-	struct mempool_params p;
-	char *name;
-	struct mempool *mempool;
+	struct rte_mempool *mp;
+	char *mempool_name;
+	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
 
-	if (n_tokens != 10) {
+	if (n_tokens != 12) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	name = tokens[1];
+	mempool_name = tokens[1];
+
+	if (strcmp(tokens[2], "meta")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
+		return;
+	}
 
-	if (strcmp(tokens[2], "buffer") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
+	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
+	if (strcmp(tokens[4], "pkt")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
 		return;
 	}
 
-	if (strcmp(tokens[4], "pool") != 0) {
+	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
+		return;
+	}
+
+	if (strcmp(tokens[6], "pool")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
 		return;
 	}
 
-	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
+	if (parser_read_uint32(&pool_size, tokens[7])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
 		return;
 	}
 
-	if (strcmp(tokens[6], "cache") != 0) {
+	if (strcmp(tokens[8], "cache")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
+	if (parser_read_uint32(&cache_size, tokens[9])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
 		return;
 	}
 
-	if (strcmp(tokens[8], "cpu") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+	if (strcmp(tokens[10], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+	if (parser_read_uint32(&numa_node, tokens[11])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	mempool = mempool_create(obj, name, &p);
-	if (mempool == NULL) {
+	mp = rte_pktmbuf_pool_create(mempool_name,
+				     pool_size,
+				     cache_size,
+				     mbuf_private_size,
+				     pkt_buffer_size,
+				     numa_node);
+	if (!mp) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 4e30c1320f..2450dc9ca4 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/fib.c /tmp/fib.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index b2e219e4c9..3840e47a2b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index 27e37021b9..c5505df296 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 11bb4543b9..bd700707f5 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index 8724dae3b0..aa64b32ab2 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 4db0a0dc56..619d17474f 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 6c8aa3921e..42184be1c8 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/learner.c /tmp/learner.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 21582554b9..0fa38b16b8 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/meter.c /tmp/meter.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 1d439e04d3..e7391de74b 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 52d0894f12..965c870fd4 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 3516f76a5b..aa775f71e5 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/registers.c /tmp/registers.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0e251b657..f0608de184 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/selector.c /tmp/selector.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 0f89990471..648b8882c3 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 1fbd1be6e4..4565adfaea 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index adc7f73312..510c2ad870 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -18,7 +18,7 @@ pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 ;
 ; Note: Customize the parameters below to match your setup.
 ;
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
 
 ;
 ; List of pipelines.
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index cac825f22a..697d14a901 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -13,18 +13,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
 
-/*
- * mempool
- */
-TAILQ_HEAD(mempool_list, mempool);
-
 /*
  * link
  */
@@ -39,75 +33,10 @@ TAILQ_HEAD(ring_list, ring);
  * obj
  */
 struct obj {
-	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
 };
 
-/*
- * mempool
- */
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
-
-struct mempool *
-mempool_create(struct obj *obj, const char *name, struct mempool_params *params)
-{
-	struct mempool *mempool;
-	struct rte_mempool *m;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		mempool_find(obj, name) ||
-		(params == NULL) ||
-		(params->buffer_size < BUFFER_SIZE_MIN) ||
-		(params->pool_size == 0))
-		return NULL;
-
-	/* Resource create */
-	m = rte_pktmbuf_pool_create(
-		name,
-		params->pool_size,
-		params->cache_size,
-		0,
-		params->buffer_size - sizeof(struct rte_mbuf),
-		params->cpu_id);
-
-	if (m == NULL)
-		return NULL;
-
-	/* Node allocation */
-	mempool = calloc(1, sizeof(struct mempool));
-	if (mempool == NULL) {
-		rte_mempool_free(m);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(mempool->name, name, sizeof(mempool->name));
-	mempool->m = m;
-	mempool->buffer_size = params->buffer_size;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);
-
-	return mempool;
-}
-
-struct mempool *
-mempool_find(struct obj *obj, const char *name)
-{
-	struct mempool *mempool;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(mempool, &obj->mempool_list, node)
-		if (strcmp(mempool->name, name) == 0)
-			return mempool;
-
-	return NULL;
-}
-
 /*
  * link
  */
@@ -171,7 +100,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct rte_eth_conf port_conf;
 	struct link *link;
 	struct link_params_rss *rss;
-	struct mempool *mempool;
+	struct rte_mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
 	uint16_t port_id = 0;
@@ -193,8 +122,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
 
-	mempool = mempool_find(obj, params->rx.mempool_name);
-	if (mempool == NULL)
+	mempool = rte_mempool_lookup(params->rx.mempool_name);
+	if (!mempool)
 		return NULL;
 
 	rss = params->rx.rss;
@@ -251,7 +180,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			params->rx.queue_size,
 			cpu_id,
 			NULL,
-			mempool->m);
+			mempool);
 
 		if (status < 0)
 			return NULL;
@@ -421,7 +350,6 @@ obj_init(void)
 	if (!obj)
 		return NULL;
 
-	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 8ea1c414c2..4ea610ceed 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <sys/queue.h>
 
-#include <rte_mempool.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
@@ -24,32 +23,6 @@ struct obj;
 struct obj *
 obj_init(void);
 
-/*
- * mempool
- */
-struct mempool_params {
-	uint32_t buffer_size;
-	uint32_t pool_size;
-	uint32_t cache_size;
-	uint32_t cpu_id;
-};
-
-struct mempool {
-	TAILQ_ENTRY(mempool) node;
-	char name[NAME_SIZE];
-	struct rte_mempool *m;
-	uint32_t buffer_size;
-};
-
-struct mempool *
-mempool_create(struct obj *obj,
-	       const char *name,
-	       struct mempool_params *params);
-
-struct mempool *
-mempool_find(struct obj *obj,
-	     const char *name);
-
 /*
  * link
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 03/11] examples/pipeline: streamline ring support
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 01/11] " Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
                     ` (8 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Remove redundant ring related code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 24 ++++++++++------
 examples/pipeline/obj.c | 63 -----------------------------------------
 examples/pipeline/obj.h | 21 --------------
 3 files changed, 15 insertions(+), 93 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 14f1cfa47e..517682f7c9 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -11,6 +11,7 @@
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_ring.h>
 #include <rte_swx_port_ethdev.h>
 #include <rte_swx_port_ring.h>
 #include <rte_swx_port_source_sink.h>
@@ -491,11 +492,11 @@ cmd_ring(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct ring_params p;
+	struct rte_ring *r;
 	char *name;
-	struct ring *ring;
+	uint32_t size, numa_node;
 
 	if (n_tokens != 6) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
@@ -504,28 +505,32 @@ cmd_ring(char **tokens,
 
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "size") != 0) {
+	if (strcmp(tokens[2], "size")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
 		return;
 	}
 
-	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
+	if (parser_read_uint32(&size, tokens[3])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
 		return;
 	}
 
-	if (strcmp(tokens[4], "numa") != 0) {
+	if (strcmp(tokens[4], "numa")) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
 
-	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
+	if (parser_read_uint32(&numa_node, tokens[5])) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
 		return;
 	}
 
-	ring = ring_create(obj, name, &p);
-	if (!ring) {
+	r = rte_ring_create(
+		name,
+		size,
+		(int)numa_node,
+		RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (!r) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
@@ -2999,6 +3004,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 697d14a901..3614b99d28 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -24,17 +24,11 @@
  */
 TAILQ_HEAD(link_list, link);
 
-/*
- * ring
- */
-TAILQ_HEAD(ring_list, ring);
-
 /*
  * obj
  */
 struct obj {
 	struct link_list link_list;
-	struct ring_list ring_list;
 };
 
 /*
@@ -282,62 +276,6 @@ link_next(struct obj *obj, struct link *link)
 		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
 }
 
-/*
- * ring
- */
-struct ring *
-ring_create(struct obj *obj, const char *name, struct ring_params *params)
-{
-	struct ring *ring;
-	struct rte_ring *r;
-	unsigned int flags = RING_F_SP_ENQ | RING_F_SC_DEQ;
-
-	/* Check input params */
-	if (!name || ring_find(obj, name) || !params || !params->size)
-		return NULL;
-
-	/**
-	 * Resource create
-	 */
-	r = rte_ring_create(
-		name,
-		params->size,
-		params->numa_node,
-		flags);
-	if (!r)
-		return NULL;
-
-	/* Node allocation */
-	ring = calloc(1, sizeof(struct ring));
-	if (!ring) {
-		rte_ring_free(r);
-		return NULL;
-	}
-
-	/* Node fill in */
-	strlcpy(ring->name, name, sizeof(ring->name));
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->ring_list, ring, node);
-
-	return ring;
-}
-
-struct ring *
-ring_find(struct obj *obj, const char *name)
-{
-	struct ring *ring;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(ring, &obj->ring_list, node)
-		if (strcmp(ring->name, name) == 0)
-			return ring;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -351,7 +289,6 @@ obj_init(void)
 		return NULL;
 
 	TAILQ_INIT(&obj->link_list);
-	TAILQ_INIT(&obj->ring_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index 4ea610ceed..dbbc6d39a0 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -73,25 +73,4 @@ link_find(struct obj *obj, const char *name);
 struct link *
 link_next(struct obj *obj, struct link *link);
 
-/*
- * ring
- */
-struct ring_params {
-	uint32_t size;
-	uint32_t numa_node;
-};
-
-struct ring {
-	TAILQ_ENTRY(ring) node;
-	char name[NAME_SIZE];
-};
-
-struct ring *
-ring_create(struct obj *obj,
-	   const char *name,
-	   struct ring_params *params);
-
-struct ring *
-ring_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 04/11] examples/pipeline: streamline the Ethernet device support
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
                     ` (7 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Streamline the Ethernet device support code and remove redundant code.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c  | 172 ++++++++++++++++++------------------
 examples/pipeline/main.c |  12 +--
 examples/pipeline/obj.c  | 186 +++++++++------------------------------
 examples/pipeline/obj.h  |  51 ++---------
 4 files changed, 136 insertions(+), 285 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 517682f7c9..617af63d63 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -292,16 +292,17 @@ cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct link_params p;
-	struct link_params_rss rss;
-	struct link *link;
+	struct ethdev_params p;
+	struct ethdev_params_rss rss;
 	char *name;
+	int status;
 
 	memset(&p, 0, sizeof(p));
+	memset(&rss, 0, sizeof(rss));
 
-	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
+	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -377,111 +378,98 @@ cmd_ethdev(char **tokens,
 		}
 	}
 
-	link = link_create(obj, name, &p);
-	if (link == NULL) {
+	status = ethdev_config(name, &p);
+	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
 		return;
 	}
 }
 
-/* Print the link stats and info */
 static void
-print_link_info(struct link *link, char *out, size_t out_size)
+ethdev_show(uint16_t port_id, char **out, size_t *out_size)
 {
+	char name[RTE_ETH_NAME_MAX_LEN];
+	struct rte_eth_dev_info info;
 	struct rte_eth_stats stats;
-	struct rte_ether_addr mac_addr;
-	struct rte_eth_link eth_link;
-	uint16_t mtu;
-	int ret;
-
-	memset(&stats, 0, sizeof(stats));
-	rte_eth_stats_get(link->port_id, &stats);
-
-	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
-	if (ret != 0) {
-		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	ret = rte_eth_link_get(link->port_id, &eth_link);
-	if (ret < 0) {
-		snprintf(out, out_size, "\n%s: link get failed: %s",
-			 link->name, rte_strerror(-ret));
-		return;
-	}
-
-	rte_eth_dev_get_mtu(link->port_id, &mtu);
-
-	snprintf(out, out_size,
-		"\n"
-		"%s: flags=<%s> mtu %u\n"
-		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
-		"\tport# %u  speed %s\n"
-		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
-		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
-		"\tTX errors %" PRIu64"\n",
-		link->name,
-		eth_link.link_status == 0 ? "DOWN" : "UP",
-		mtu,
-		RTE_ETHER_ADDR_BYTES(&mac_addr),
-		link->n_rxq,
-		link->n_txq,
-		link->port_id,
-		rte_eth_link_speed_to_str(eth_link.link_speed),
-		stats.ipackets,
-		stats.ibytes,
-		stats.ierrors,
-		stats.imissed,
-		stats.rx_nombuf,
-		stats.opackets,
-		stats.obytes,
-		stats.oerrors);
+	struct rte_ether_addr addr;
+	struct rte_eth_link link;
+	uint32_t length;
+	uint16_t mtu = 0;
+
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return;
+
+	rte_eth_dev_get_name_by_port(port_id, name);
+	rte_eth_dev_info_get(port_id, &info);
+	rte_eth_stats_get(port_id, &stats);
+	rte_eth_macaddr_get(port_id, &addr);
+	rte_eth_link_get(port_id, &link);
+	rte_eth_dev_get_mtu(port_id, &mtu);
+
+	snprintf(*out, *out_size,
+		 "%s: flags=<%s> mtu %u\n"
+		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
+		 "\tport# %u  speed %s\n"
+		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
+		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
+		 "\tTX errors %" PRIu64"\n\n",
+		 name,
+		 link.link_status ? "UP" : "DOWN",
+		 mtu,
+		 RTE_ETHER_ADDR_BYTES(&addr),
+		 info.nb_rx_queues,
+		 info.nb_tx_queues,
+		 port_id,
+		 rte_eth_link_speed_to_str(link.link_speed),
+		 stats.ipackets,
+		 stats.ibytes,
+		 stats.ierrors,
+		 stats.imissed,
+		 stats.rx_nombuf,
+		 stats.opackets,
+		 stats.obytes,
+		 stats.oerrors);
+
+	length = strlen(*out);
+	*out_size -= length;
+	*out += length;
 }
 
-/*
- * ethdev show [<ethdev_name>]
- */
+
+static char cmd_ethdev_show_help[] =
+"ethdev show [ <ethdev_name> ]\n";
+
 static void
 cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
-	      void *obj)
+	      void *obj __rte_unused)
 {
-	struct link *link;
-	char *link_name;
+	uint16_t port_id;
 
 	if (n_tokens != 2 && n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (n_tokens == 2) {
-		link = link_next(obj, NULL);
-
-		while (link != NULL) {
-			out_size = out_size - strlen(out);
-			out = &out[strlen(out)];
-
-			print_link_info(link, out, out_size);
-			link = link_next(obj, link);
-		}
-	} else {
-		out_size = out_size - strlen(out);
-		out = &out[strlen(out)];
+	/* Single device. */
+	if (n_tokens == 3) {
+		int status;
 
-		link_name = tokens[2];
-		link = link_find(obj, link_name);
+		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
+		if (status)
+			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
 
-		if (link == NULL) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-					"Link does not exist");
-			return;
-		}
-		print_link_info(link, out, out_size);
+		ethdev_show(port_id, &out, &out_size);
+		return;
 	}
+
+	/*  All devices. */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		if (rte_eth_dev_is_valid_port(port_id))
+			ethdev_show(port_id, &out, &out_size);
 }
 
 static const char cmd_ring_help[] =
@@ -3004,6 +2992,7 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
+			"\tethdev show\n"
 			"\tring\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -3039,9 +3028,16 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "ethdev") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
-		return;
+	if (!strcmp(tokens[0], "ethdev")) {
+		if (n_tokens == 1) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
+			return;
+		}
+
+		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
+			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
+			return;
+		}
 	}
 
 	if (strcmp(tokens[0], "ring") == 0) {
diff --git a/examples/pipeline/main.c b/examples/pipeline/main.c
index 6fb839f4cb..da5dd2b3f6 100644
--- a/examples/pipeline/main.c
+++ b/examples/pipeline/main.c
@@ -135,7 +135,6 @@ int
 main(int argc, char **argv)
 {
 	struct conn *conn;
-	struct obj *obj;
 	int status;
 
 	/* Parse application arguments */
@@ -150,13 +149,6 @@ main(int argc, char **argv)
 		return status;
 	};
 
-	/* Obj */
-	obj = obj_init();
-	if (!obj) {
-		printf("Error: Obj initialization failed (%d)\n", status);
-		return status;
-	}
-
 	/* Thread */
 	status = thread_init();
 	if (status) {
@@ -174,10 +166,10 @@ main(int argc, char **argv)
 		cli_script_process(app.script_name,
 			app.conn.msg_in_len_max,
 			app.conn.msg_out_len_max,
-			obj);
+			NULL);
 
 	/* Connectivity */
-	app.conn.msg_handle_arg = obj;
+	app.conn.msg_handle_arg = NULL;
 	conn = conn_init(&app.conn);
 	if (!conn) {
 		printf("Error: Connectivity initialization failed (%d)\n",
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 3614b99d28..143b968472 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -4,35 +4,14 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#ifdef RTE_EXEC_ENV_LINUX
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#endif
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_ctl.h>
 
 #include "obj.h"
 
 /*
- * link
- */
-TAILQ_HEAD(link_list, link);
-
-/*
- * obj
- */
-struct obj {
-	struct link_list link_list;
-};
-
-/*
- * link
+ * ethdev
  */
 static struct rte_eth_conf port_conf_default = {
 	.link_speeds = 0,
@@ -58,7 +37,7 @@ static struct rte_eth_conf port_conf_default = {
 static int
 rss_setup(uint16_t port_id,
 	uint16_t reta_size,
-	struct link_params_rss *rss)
+	struct ethdev_params_rss *rss)
 {
 	struct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];
 	uint32_t i;
@@ -87,69 +66,64 @@ rss_setup(uint16_t port_id,
 	return status;
 }
 
-struct link *
-link_create(struct obj *obj, const char *name, struct link_params *params)
+int
+ethdev_config(const char *name, struct ethdev_params *params)
 {
 	struct rte_eth_dev_info port_info;
 	struct rte_eth_conf port_conf;
-	struct link *link;
-	struct link_params_rss *rss;
+	struct ethdev_params_rss *rss;
 	struct rte_mempool *mempool;
-	uint32_t cpu_id, i;
-	int status;
+	uint32_t i;
+	int numa_node, status;
 	uint16_t port_id = 0;
 
 	/* Check input params */
-	if ((name == NULL) ||
-		link_find(obj, name) ||
-		(params == NULL) ||
-		(params->rx.n_queues == 0) ||
-		(params->rx.queue_size == 0) ||
-		(params->tx.n_queues == 0) ||
-		(params->tx.queue_size == 0))
-		return NULL;
+	if (!name ||
+	    !name[0] ||
+	    !params ||
+	    !params->rx.n_queues ||
+	    !params->rx.queue_size ||
+	    !params->tx.n_queues ||
+	    !params->tx.queue_size)
+		return -EINVAL;
 
 	status = rte_eth_dev_get_port_by_name(name, &port_id);
 	if (status)
-		return NULL;
+		return -EINVAL;
 
-	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
-		return NULL;
+	status = rte_eth_dev_info_get(port_id, &port_info);
+	if (status)
+		return -EINVAL;
 
 	mempool = rte_mempool_lookup(params->rx.mempool_name);
 	if (!mempool)
-		return NULL;
+		return -EINVAL;
 
 	rss = params->rx.rss;
 	if (rss) {
-		if ((port_info.reta_size == 0) ||
-			(port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512))
-			return NULL;
+		if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512)
+			return -EINVAL;
 
-		if ((rss->n_queues == 0) ||
-			(rss->n_queues >= LINK_RXQ_RSS_MAX))
-			return NULL;
+		if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX)
+			return -EINVAL;
 
 		for (i = 0; i < rss->n_queues; i++)
 			if (rss->queue_id[i] >= port_info.max_rx_queues)
-				return NULL;
+				return -EINVAL;
 	}
 
-	/**
-	 * Resource create
-	 */
 	/* Port */
 	memcpy(&port_conf, &port_conf_default, sizeof(port_conf));
 	if (rss) {
+		uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP;
+
 		port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
-		port_conf.rx_adv_conf.rss_conf.rss_hf =
-			(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
-			port_info.flow_type_rss_offloads;
+		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads;
 	}
 
-	cpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);
-	if (cpu_id == (uint32_t) SOCKET_ID_ANY)
-		cpu_id = 0;
+	numa_node = rte_eth_dev_socket_id(port_id);
+	if (numa_node == SOCKET_ID_ANY)
+		numa_node = 0;
 
 	status = rte_eth_dev_configure(
 		port_id,
@@ -158,12 +132,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		&port_conf);
 
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (params->promiscuous) {
 		status = rte_eth_promiscuous_enable(port_id);
-		if (status != 0)
-			return NULL;
+		if (status)
+			return -EINVAL;
 	}
 
 	/* Port RX */
@@ -172,12 +146,12 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->rx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL,
 			mempool);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port TX */
@@ -186,24 +160,24 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 			port_id,
 			i,
 			params->tx.queue_size,
-			cpu_id,
+			numa_node,
 			NULL);
 
 		if (status < 0)
-			return NULL;
+			return -EINVAL;
 	}
 
 	/* Port start */
 	status = rte_eth_dev_start(port_id);
 	if (status < 0)
-		return NULL;
+		return -EINVAL;
 
 	if (rss) {
 		status = rss_setup(port_id, port_info.reta_size, rss);
 
 		if (status) {
 			rte_eth_dev_stop(port_id);
-			return NULL;
+			return -EINVAL;
 		}
 	}
 
@@ -211,84 +185,8 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	status = rte_eth_dev_set_link_up(port_id);
 	if ((status < 0) && (status != -ENOTSUP)) {
 		rte_eth_dev_stop(port_id);
-		return NULL;
-	}
-
-	/* Node allocation */
-	link = calloc(1, sizeof(struct link));
-	if (link == NULL) {
-		rte_eth_dev_stop(port_id);
-		return NULL;
+		return -EINVAL;
 	}
 
-	/* Node fill in */
-	strlcpy(link->name, name, sizeof(link->name));
-	link->port_id = port_id;
-	link->n_rxq = params->rx.n_queues;
-	link->n_txq = params->tx.n_queues;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->link_list, link, node);
-
-	return link;
-}
-
-int
-link_is_up(struct obj *obj, const char *name)
-{
-	struct rte_eth_link link_params;
-	struct link *link;
-
-	/* Check input params */
-	if (!obj || !name)
-		return 0;
-
-	link = link_find(obj, name);
-	if (link == NULL)
-		return 0;
-
-	/* Resource */
-	if (rte_eth_link_get(link->port_id, &link_params) < 0)
-		return 0;
-
-	return (link_params.link_status == RTE_ETH_LINK_DOWN) ? 0 : 1;
-}
-
-struct link *
-link_find(struct obj *obj, const char *name)
-{
-	struct link *link;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(link, &obj->link_list, node)
-		if (strcmp(link->name, name) == 0)
-			return link;
-
-	return NULL;
-}
-
-struct link *
-link_next(struct obj *obj, struct link *link)
-{
-	return (link == NULL) ?
-		TAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);
-}
-
-/*
- * obj
- */
-struct obj *
-obj_init(void)
-{
-	struct obj *obj;
-
-	obj = calloc(1, sizeof(struct obj));
-	if (!obj)
-		return NULL;
-
-	TAILQ_INIT(&obj->link_list);
-
-	return obj;
+	return 0;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index dbbc6d39a0..fb091f4ba7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -6,41 +6,25 @@
 #define _INCLUDE_OBJ_H_
 
 #include <stdint.h>
-#include <sys/queue.h>
-
-#include <rte_swx_pipeline.h>
-#include <rte_swx_ctl.h>
-
-#ifndef NAME_SIZE
-#define NAME_SIZE 64
-#endif
-
-/*
- * obj
- */
-struct obj;
-
-struct obj *
-obj_init(void);
 
 /*
- * link
+ * ethdev
  */
-#ifndef LINK_RXQ_RSS_MAX
-#define LINK_RXQ_RSS_MAX                                   16
+#ifndef ETHDEV_RXQ_RSS_MAX
+#define ETHDEV_RXQ_RSS_MAX 16
 #endif
 
-struct link_params_rss {
-	uint32_t queue_id[LINK_RXQ_RSS_MAX];
+struct ethdev_params_rss {
+	uint32_t queue_id[ETHDEV_RXQ_RSS_MAX];
 	uint32_t n_queues;
 };
 
-struct link_params {
+struct ethdev_params {
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
 		const char *mempool_name;
-		struct link_params_rss *rss;
+		struct ethdev_params_rss *rss;
 	} rx;
 
 	struct {
@@ -51,26 +35,7 @@ struct link_params {
 	int promiscuous;
 };
 
-struct link {
-	TAILQ_ENTRY(link) node;
-	char name[NAME_SIZE];
-	uint16_t port_id;
-	uint32_t n_rxq;
-	uint32_t n_txq;
-};
-
-struct link *
-link_create(struct obj *obj,
-	    const char *name,
-	    struct link_params *params);
-
 int
-link_is_up(struct obj *obj, const char *name);
-
-struct link *
-link_find(struct obj *obj, const char *name);
-
-struct link *
-link_next(struct obj *obj, struct link *link);
+ethdev_config(const char *name, struct ethdev_params *params);
 
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 05/11] examples/pipeline: support crypto devices
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
                     ` (6 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add support for crypto devices in the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c | 62 +++++++++++++++++++++++++++++++++++++++++
 examples/pipeline/obj.h | 11 ++++++++
 2 files changed, 73 insertions(+)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 143b968472..d39034ec22 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -7,6 +7,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_cryptodev.h>
 
 #include "obj.h"
 
@@ -190,3 +191,64 @@ ethdev_config(const char *name, struct ethdev_params *params)
 
 	return 0;
 }
+
+/*
+ * cryptodev
+ */
+int
+cryptodev_config(const char *name, struct cryptodev_params *params)
+{
+	struct rte_cryptodev_info dev_info;
+	struct rte_cryptodev_config dev_conf;
+	struct rte_cryptodev_qp_conf queue_conf;
+	uint8_t dev_id;
+	uint32_t socket_id, i;
+	int status;
+
+	/* Check input parameters. */
+	if (!name ||
+	    !params->n_queue_pairs ||
+	    !params->queue_size)
+		return -EINVAL;
+
+	/* Find the crypto device. */
+	status = rte_cryptodev_get_dev_id(name);
+	if (status < 0)
+		return -EINVAL;
+
+	dev_id = (uint8_t)status;
+
+	rte_cryptodev_info_get(dev_id, &dev_info);
+	if (params->n_queue_pairs > dev_info.max_nb_queue_pairs)
+		return -EINVAL;
+
+	socket_id = rte_cryptodev_socket_id(dev_id);
+
+	/* Configure the crypto device. */
+	memset(&dev_conf, 0, sizeof(dev_conf));
+	dev_conf.socket_id = socket_id;
+	dev_conf.nb_queue_pairs = params->n_queue_pairs;
+	dev_conf.ff_disable = 0;
+
+	status = rte_cryptodev_configure(dev_id, &dev_conf);
+	if (status)
+		return status;
+
+	/* Configure the crypto device queue pairs. */
+	memset(&queue_conf, 0, sizeof(queue_conf));
+	queue_conf.nb_descriptors = params->queue_size;
+	queue_conf.mp_session = NULL;
+
+	for (i = 0; i < params->n_queue_pairs; i++) {
+		status = rte_cryptodev_queue_pair_setup(dev_id, i, &queue_conf, socket_id);
+		if (status)
+			return status;
+	}
+
+	/* Start the crypto device. */
+	status = rte_cryptodev_start(dev_id);
+	if (status)
+		return status;
+
+	return 0;
+}
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index fb091f4ba7..0f103be6a7 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -38,4 +38,15 @@ struct ethdev_params {
 int
 ethdev_config(const char *name, struct ethdev_params *params);
 
+/*
+ * cryptodev
+ */
+struct cryptodev_params {
+	uint32_t n_queue_pairs;
+	uint32_t queue_size;
+};
+
+int
+cryptodev_config(const char *name, struct cryptodev_params *params);
+
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 06/11] examples/pipeline: add CLI command for crypto device
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (4 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
                     ` (5 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI command for the configuration of crypto devices.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 617af63d63..dbaf436620 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -524,6 +524,58 @@ cmd_ring(char **tokens,
 	}
 }
 
+static const char cmd_cryptodev_help[] =
+"cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
+
+static void
+cmd_cryptodev(char **tokens,
+	      uint32_t n_tokens,
+	      char *out,
+	      size_t out_size,
+	      void *obj __rte_unused)
+{
+	struct cryptodev_params params;
+	char *cryptodev_name;
+	int status;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	cryptodev_name = tokens[1];
+
+	if (strcmp(tokens[2], "queues")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
+		return;
+	}
+
+	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
+		return;
+	}
+
+	if (strcmp(tokens[4], "qsize")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
+		return;
+	}
+
+
+	if (parser_read_uint32(&params.queue_size, tokens[5])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
+		return;
+	}
+
+	status = cryptodev_config(cryptodev_name, &params);
+	if (status)
+		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
+}
+
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2994,6 +3046,7 @@ cmd_help(char **tokens,
 			"\tethdev\n"
 			"\tethdev show\n"
 			"\tring\n"
+			"\tcryptodev\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3045,6 +3098,11 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3300,6 +3358,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
+	if (!strcmp(tokens[0], "cryptodev")) {
+		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
+		return;
+	}
+
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 07/11] examples/pipeline: add IPsec CLI commands
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (5 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
                     ` (4 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands for IPsec block configuration.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 298 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 298 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index dbaf436620..bcb3e54fb0 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -18,6 +18,7 @@
 #include <rte_swx_port_fd.h>
 #include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
+#include <rte_swx_ipsec.h>
 
 #include "cli.h"
 
@@ -2913,6 +2914,263 @@ cmd_pipeline_mirror_session(char **tokens,
 	}
 }
 
+static const char cmd_ipsec_create_help[] =
+"ipsec <ipsec_instance_name> create "
+"in <ring_in_name> out <ring_out_name> "
+"cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
+"bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
+"samax <n_sa_max> "
+"numa <numa_node>\n";
+
+static void
+cmd_ipsec_create(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec_params p;
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t numa_node;
+	int status;
+
+	if (n_tokens != 20) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+
+	if (strcmp(tokens[2], "create")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
+		return;
+	}
+
+	if (strcmp(tokens[3], "in")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+		return;
+	}
+
+	p.ring_in_name = tokens[4];
+
+	if (strcmp(tokens[5], "out")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+		return;
+	}
+
+	p.ring_out_name = tokens[6];
+
+	if (strcmp(tokens[7], "cryptodev")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
+		return;
+	}
+
+	p.crypto_dev_name = tokens[8];
+
+	if (strcmp(tokens[9], "cryptoq")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
+		return;
+	}
+
+	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
+		return;
+	}
+
+	if (strcmp(tokens[11], "bsz")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
+		return;
+	}
+
+	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
+		return;
+	}
+
+	if (strcmp(tokens[16], "samax")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
+		return;
+	}
+
+	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
+		return;
+	}
+
+	if (strcmp(tokens[18], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
+		return;
+	}
+
+	if (parser_read_uint32(&numa_node, tokens[19])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	status = rte_swx_ipsec_create(&ipsec,
+				      ipsec_instance_name,
+				      &p,
+				      (int)numa_node);
+	if (status)
+		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
+}
+
+static const char cmd_ipsec_sa_add_help[] =
+"ipsec <ipsec_instance_name> sa add <file_name>\n";
+
+static void
+cmd_ipsec_sa_add(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name, *file_name, *line = NULL;
+	FILE *file = NULL;
+	uint32_t line_id = 0;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		goto free;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		goto free;
+	}
+
+	if (strcmp(tokens[3], "add")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+		goto free;
+	}
+
+	file_name = tokens[4];
+	file = fopen(file_name, "r");
+	if (!file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+		goto free;
+	}
+
+	/* Buffer allocation. */
+	line = malloc(MAX_LINE_SIZE);
+	if (!line) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	/* File read. */
+	for (line_id = 1; ; line_id++) {
+		struct rte_swx_ipsec_sa_params *sa;
+		const char *err_msg;
+		uint32_t sa_id = 0;
+		int is_blank_or_comment, status = 0;
+
+		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+			break;
+
+		/* Read SA from file. */
+		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
+		if (!sa) {
+			if (is_blank_or_comment)
+				continue;
+
+			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
+				file_name, line_id, err_msg);
+			goto free;
+		}
+
+		snprintf(out, out_size, "%s", line);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		/* Add the SA to the IPsec instance. Free the SA. */
+		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
+		if (status)
+			snprintf(out, out_size, "\t: Error (%d)\n", status);
+		else
+			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
+		out_size -= strlen(out);
+		out += strlen(out);
+
+		free(sa);
+		if (status)
+			goto free;
+	}
+
+free:
+	if (file)
+		fclose(file);
+	free(line);
+}
+
+static const char cmd_ipsec_sa_delete_help[] =
+"ipsec <ipsec_instance_name> sa delete <sa_id>\n";
+
+static void
+cmd_ipsec_sa_delete(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
+{
+	struct rte_swx_ipsec *ipsec;
+	char *ipsec_instance_name;
+	uint32_t sa_id;
+
+	if (n_tokens != 5) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	ipsec_instance_name = tokens[1];
+	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
+	if (!ipsec) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
+		return;
+	}
+
+	if (strcmp(tokens[2], "sa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
+		return;
+	}
+
+	if (strcmp(tokens[3], "delete")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+		return;
+	}
+
+	if (parser_read_uint32(&sa_id, tokens[4])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
+		return;
+	}
+
+	rte_swx_ipsec_sa_delete(ipsec, sa_id);
+}
+
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
 
@@ -3071,6 +3329,9 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tipsec create\n"
+			"\tipsec sa add\n"
+			"\tipsec sa delete\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
 		return;
@@ -3295,6 +3556,26 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "add")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "ipsec") &&
+		(n_tokens == 3) && !strcmp(tokens[1], "sa")
+		&& !strcmp(tokens[2], "delete")) {
+		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
+		return;
+	}
+
 	if ((n_tokens == 3) &&
 		(strcmp(tokens[0], "thread") == 0) &&
 		(strcmp(tokens[1], "pipeline") == 0)) {
@@ -3543,6 +3824,23 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "ipsec")) {
+		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
+			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
+			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
+			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	if (strcmp(tokens[0], "thread") == 0) {
 		if ((n_tokens >= 5) &&
 			(strcmp(tokens[4], "enable") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 08/11] examples/pipeline: rework the thread configuration updates
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (6 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
                     ` (3 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the configuration updates for the data plane threads were
performed through message queues. Now, this mechanism is replaced by
the control thread updating the mirror copy of the data plane thread
configuration followed by pointer swapping.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c                       | 154 ++---
 examples/pipeline/examples/fib.cli            |   2 +-
 examples/pipeline/examples/hash_func.cli      |   2 +-
 examples/pipeline/examples/l2fwd.cli          |   2 +-
 examples/pipeline/examples/l2fwd_macswp.cli   |   2 +-
 .../pipeline/examples/l2fwd_macswp_pcap.cli   |   2 +-
 examples/pipeline/examples/l2fwd_pcap.cli     |   2 +-
 examples/pipeline/examples/learner.cli        |   2 +-
 examples/pipeline/examples/meter.cli          |   2 +-
 examples/pipeline/examples/mirroring.cli      |   2 +-
 examples/pipeline/examples/recirculation.cli  |   2 +-
 examples/pipeline/examples/registers.cli      |   2 +-
 examples/pipeline/examples/selector.cli       |   2 +-
 examples/pipeline/examples/varbit.cli         |   2 +-
 examples/pipeline/examples/vxlan.cli          |   2 +-
 examples/pipeline/examples/vxlan_pcap.cli     |   2 +-
 examples/pipeline/thread.c                    | 586 ++++--------------
 examples/pipeline/thread.h                    |  17 +-
 18 files changed, 217 insertions(+), 570 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index bcb3e54fb0..d9c325c89c 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3171,119 +3171,86 @@ cmd_ipsec_sa_delete(char **tokens,
 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
 }
 
-static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
-
-#ifndef TIMER_PERIOD_MS_DEFAULT
-#define TIMER_PERIOD_MS_DEFAULT 10
-#endif
+static const char cmd_pipeline_enable_help[] =
+"pipeline <pipeline_name> enable thread <thread_id>\n";
 
 static void
-cmd_thread_pipeline_enable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_enable(char **tokens,
+		    uint32_t n_tokens,
+		    char *out,
+		    size_t out_size,
+		    void *obj __rte_unused)
 {
 	char *pipeline_name;
 	struct rte_swx_pipeline *p;
-	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
+	uint32_t thread_id;
 	int status;
 
-	if ((n_tokens != 5) && (n_tokens != 7)) {
+	if (n_tokens != 5) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "enable") != 0) {
+	if (strcmp(tokens[2], "enable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
 		return;
 	}
 
-	if (n_tokens == 7) {
-		if (strcmp(tokens[5], "period") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
-			return;
-		}
+	if (strcmp(tokens[3], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
 
-		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-			return;
-		}
+	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
+	status = pipeline_enable(p, thread_id);
 	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
+		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
 		return;
 	}
 }
 
-static const char cmd_thread_pipeline_disable_help[] =
-"thread <thread_id> pipeline <pipeline_name> disable\n";
+static const char cmd_pipeline_disable_help[] =
+"pipeline <pipeline_name> disable\n";
 
 static void
-cmd_thread_pipeline_disable(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj __rte_unused)
+cmd_pipeline_disable(char **tokens,
+		     uint32_t n_tokens,
+		     char *out,
+		     size_t out_size,
+		     void *obj __rte_unused)
 {
 	struct rte_swx_pipeline *p;
 	char *pipeline_name;
-	uint32_t thread_id;
-	int status;
 
-	if (n_tokens != 5) {
+	if (n_tokens != 3) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
-		return;
-	}
-
-	if (strcmp(tokens[2], "pipeline") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	pipeline_name = tokens[3];
+	pipeline_name = tokens[1];
 	p = rte_swx_pipeline_find(pipeline_name);
 	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	if (strcmp(tokens[4], "disable") != 0) {
+	if (strcmp(tokens[2], "disable") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, p);
-	if (status) {
-		snprintf(out, out_size, MSG_CMD_FAIL,
-			"thread pipeline disable");
-		return;
-	}
+	pipeline_disable(p);
 }
 
 static void
@@ -3329,11 +3296,12 @@ cmd_help(char **tokens,
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
 			"\tpipeline mirror session\n"
+			"\tpipeline enable\n"
+			"\tpipeline disable\n\n"
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
-			"\tthread pipeline enable\n"
-			"\tthread pipeline disable\n\n");
+			);
 		return;
 	}
 
@@ -3556,6 +3524,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "pipeline") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
+		return;
+	}
+
 	if (!strcmp(tokens[0], "ipsec") &&
 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
@@ -3576,22 +3556,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((n_tokens == 3) &&
-		(strcmp(tokens[0], "thread") == 0) &&
-		(strcmp(tokens[1], "pipeline") == 0)) {
-		if (strcmp(tokens[2], "enable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_enable_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "disable") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_thread_pipeline_disable_help);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3822,6 +3786,16 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
+			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
+			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
 	}
 
 	if (!strcmp(tokens[0], "ipsec")) {
@@ -3841,22 +3815,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
-	if (strcmp(tokens[0], "thread") == 0) {
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "enable") == 0)) {
-			cmd_thread_pipeline_enable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-
-		if ((n_tokens >= 5) &&
-			(strcmp(tokens[4], "disable") == 0)) {
-			cmd_thread_pipeline_disable(tokens, n_tokens,
-				out, out_size, obj);
-			return;
-		}
-	}
-
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 2450dc9ca4..d2485cefe0 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -54,4 +54,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index 3840e47a2b..2c20f7a7a0 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index c5505df296..383fe346c0 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethde
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index bd700707f5..d02cb2c470 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index aa64b32ab2..d4ea449e03 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/example
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 619d17474f..8a25c98096 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -28,4 +28,4 @@ pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 42184be1c8..04c7b4d26f 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/eth
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index 0fa38b16b8..9e064dbb52 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 meter meters set profile platinum index from 0 to 15
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index e7391de74b..da6bd338fb 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 mirror session 3 port 0 clone slow truncate 128
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index 965c870fd4..f925179bf7 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/exampl
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index aa775f71e5..58dde5fdf1 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/e
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index f0608de184..9af56cd0f1 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -42,4 +42,4 @@ pipeline PIPELINE0 selector s show
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 648b8882c3..621e7f5627 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -32,4 +32,4 @@ pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethd
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 4565adfaea..73eb29a6b2 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -40,4 +40,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 510c2ad870..828342408b 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -36,4 +36,4 @@ pipeline PIPELINE0 commit
 ;
 ; Pipelines-to-threads mapping.
 ;
-thread 1 pipeline PIPELINE0 enable
+pipeline PIPELINE0 enable thread 1
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 6d15f51fb2..3001bc0858 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -3,17 +3,11 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
 
+#include <rte_atomic.h>
 #include <rte_common.h>
-#include <rte_cycles.h>
 #include <rte_lcore.h>
-#include <rte_ring.h>
-
-#include <rte_table_acl.h>
-#include <rte_table_array.h>
-#include <rte_table_hash.h>
-#include <rte_table_lpm.h>
-#include <rte_table_lpm_ipv6.h>
 
 #include "obj.h"
 #include "thread.h"
@@ -22,14 +16,6 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
-#ifndef THREAD_MSGQ_SIZE
-#define THREAD_MSGQ_SIZE                                   64
-#endif
-
-#ifndef THREAD_TIMER_PERIOD_MS
-#define THREAD_TIMER_PERIOD_MS                             100
-#endif
-
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -40,509 +26,209 @@
 #endif
 
 /**
- * Control thread: data plane thread context
+ * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
+ * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
+ * blocks.
+ *
+ * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
+ * single thread at any given time, so the same pipeline/block cannot show up in the list of
+ * pipelines/blocks of more than one thread at any specific moment.
+ *
+ * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
+ *  - Read-write by the CP thread;
+ *  - Read-only by the DP thread.
  */
 struct thread {
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-
-	uint32_t enabled;
-};
-
-static struct thread thread[RTE_MAX_LCORE];
-
-/**
- * Data plane threads: context
- */
-struct pipeline_data {
-	struct rte_swx_pipeline *p;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-};
-
-struct thread_data {
-	struct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];
-	uint32_t n_pipelines;
-
-	struct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];
-	struct rte_ring *msgq_req;
-	struct rte_ring *msgq_rsp;
-	uint64_t timer_period; /* Measured in CPU cycles. */
-	uint64_t time_next;
-	uint64_t time_next_min;
+	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	volatile uint64_t n_pipelines;
+	int enabled;
 } __rte_cache_aligned;
 
-static struct thread_data thread_data[RTE_MAX_LCORE];
+static struct thread threads[RTE_MAX_LCORE];
 
 /**
- * Control thread: data plane thread init
+ * Control plane (CP) thread.
  */
-static void
-thread_free(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct thread *t = &thread[i];
-
-		if (!rte_lcore_is_enabled(i))
-			continue;
-
-		/* MSGQs */
-		rte_ring_free(t->msgq_req);
-
-		rte_ring_free(t->msgq_rsp);
-	}
-}
-
 int
 thread_init(void)
 {
-	uint32_t i;
-
-	RTE_LCORE_FOREACH_WORKER(i) {
-		char name[NAME_MAX];
-		struct rte_ring *msgq_req, *msgq_rsp;
-		struct thread *t = &thread[i];
-		struct thread_data *t_data = &thread_data[i];
-		uint32_t cpu_id = rte_lcore_to_socket_id(i);
-
-		/* MSGQs */
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-REQ", i);
-
-		msgq_req = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_req == NULL) {
-			thread_free();
-			return -1;
-		}
+	uint32_t thread_id;
 
-		snprintf(name, sizeof(name), "THREAD-%04x-MSGQ-RSP", i);
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
 
-		msgq_rsp = rte_ring_create(name,
-			THREAD_MSGQ_SIZE,
-			cpu_id,
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
-
-		if (msgq_rsp == NULL) {
-			thread_free();
-			return -1;
-		}
-
-		/* Control thread records */
-		t->msgq_req = msgq_req;
-		t->msgq_rsp = msgq_rsp;
 		t->enabled = 1;
-
-		/* Data plane thread records */
-		t_data->n_pipelines = 0;
-		t_data->msgq_req = msgq_req;
-		t_data->msgq_rsp = msgq_rsp;
-		t_data->timer_period =
-			(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;
-		t_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;
-		t_data->time_next_min = t_data->time_next;
 	}
 
 	return 0;
 }
 
-static inline int
-thread_is_running(uint32_t thread_id)
+static uint32_t
+pipeline_find(struct rte_swx_pipeline *p)
 {
-	enum rte_lcore_state_t thread_state;
+	uint32_t thread_id;
 
-	thread_state = rte_eal_get_lcore_state(thread_id);
-	return (thread_state == RUNNING) ? 1 : 0;
-}
-
-/**
- * Control thread & data plane threads: message passing
- */
-enum thread_req_type {
-	THREAD_REQ_PIPELINE_ENABLE = 0,
-	THREAD_REQ_PIPELINE_DISABLE,
-	THREAD_REQ_MAX
-};
-
-struct thread_msg_req {
-	enum thread_req_type type;
-
-	union {
-		struct {
-			struct rte_swx_pipeline *p;
-			uint32_t timer_period_ms;
-		} pipeline_enable;
-
-		struct {
-			struct rte_swx_pipeline *p;
-		} pipeline_disable;
-	};
-};
-
-struct thread_msg_rsp {
-	int status;
-};
-
-/**
- * Control thread
- */
-static struct thread_msg_req *
-thread_msg_alloc(void)
-{
-	size_t size = RTE_MAX(sizeof(struct thread_msg_req),
-		sizeof(struct thread_msg_rsp));
-
-	return calloc(1, size);
-}
-
-static void
-thread_msg_free(struct thread_msg_rsp *rsp)
-{
-	free(rsp);
-}
-
-static struct thread_msg_rsp *
-thread_msg_send_recv(uint32_t thread_id,
-	struct thread_msg_req *req)
-{
-	struct thread *t = &thread[thread_id];
-	struct rte_ring *msgq_req = t->msgq_req;
-	struct rte_ring *msgq_rsp = t->msgq_rsp;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* send */
-	do {
-		status = rte_ring_sp_enqueue(msgq_req, req);
-	} while (status == -ENOBUFS);
-
-	/* recv */
-	do {
-		status = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);
-	} while (status != 0);
-
-	return rsp;
-}
-
-static int
-thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
-{
-	struct thread *t = &thread[thread_id];
-	struct thread_data *td = &thread_data[thread_id];
-	uint32_t i;
-
-	if (!t->enabled)
-		return 0; /* Pipeline NOT enabled on this thread. */
-
-	for (i = 0; i < td->n_pipelines; i++)
-		if (td->p[i] == p)
-			return 1; /* Pipeline enabled on this thread. */
-
-	return 0 /* Pipeline NOT enabled on this thread. */;
-}
-
-int
-thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
-{
-	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
-
-	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		struct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];
-
-		if (td->n_pipelines >= THREAD_PIPELINES_MAX)
-			return -1;
-
-		/* Data plane thread */
-		td->p[td->n_pipelines] = p;
-
-		tdp->p = p;
-		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
-		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
-		td->n_pipelines++;
+		if (!t->enabled)
+			continue;
 
-		return 0;
+		for (i = 0; i < t->n_pipelines; i++)
+			if (t->pipelines[i] == p)
+				break;
 	}
 
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p;
-	req->pipeline_enable.timer_period_ms = timer_period_ms;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
-
-	/* Read response */
-	status = rsp->status;
-
-	/* Free response */
-	thread_msg_free(rsp);
-
-	/* Request completion */
-	if (status)
-		return status;
-
-	return 0;
+	return thread_id;
 }
 
+/**
+ * Enable a given pipeline to run on a specific DP thread.
+ *
+ * CP thread:
+ *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
+ *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
+ *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
+ *    write barrier used below.
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
+ *    the new pipeline when it sees the updated t->n_pipelines value;
+ *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
+ *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
+ *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
+ */
 int
-thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
 {
 	struct thread *t;
-	struct thread_msg_req *req;
-	struct thread_msg_rsp *rsp;
-	int status;
+	uint64_t n_pipelines;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) || !p)
-		return -1;
-
-	t = &thread[thread_id];
-	if (t->enabled == 0)
-		return -1;
-
-	if (!thread_is_pipeline_enabled(thread_id, p))
-		return 0;
+	if (!p || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
 
-	if (!thread_is_running(thread_id)) {
-		struct thread_data *td = &thread_data[thread_id];
-		uint32_t i;
-
-		for (i = 0; i < td->n_pipelines; i++) {
-			struct pipeline_data *tdp = &td->pipeline_data[i];
-
-			if (tdp->p != p)
-				continue;
-
-			/* Data plane thread */
-			if (i < td->n_pipelines - 1) {
-				struct rte_swx_pipeline *pipeline_last =
-					td->p[td->n_pipelines - 1];
-				struct pipeline_data *tdp_last =
-					&td->pipeline_data[td->n_pipelines - 1];
-
-				td->p[i] = pipeline_last;
-				memcpy(tdp, tdp_last, sizeof(*tdp));
-			}
-
-			td->n_pipelines--;
-
-			break;
-		}
+	if (pipeline_find(p) < RTE_MAX_LCORE)
+		return -EEXIST;
 
-		return 0;
-	}
-
-	/* Allocate request */
-	req = thread_msg_alloc();
-	if (req == NULL)
-		return -1;
-
-	/* Write request */
-	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p;
-
-	/* Send request and wait for response */
-	rsp = thread_msg_send_recv(thread_id, req);
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
 
-	/* Read response */
-	status = rsp->status;
+	n_pipelines = t->n_pipelines;
 
-	/* Free response */
-	thread_msg_free(rsp);
+	/* Check there is room for at least one more pipeline. */
+	if (n_pipelines >= THREAD_PIPELINES_MAX)
+		return -ENOSPC;
 
-	/* Request completion */
-	if (status)
-		return status;
+	/* Install the new pipeline. */
+	t->pipelines[n_pipelines] = p;
+	rte_wmb();
+	t->n_pipelines = n_pipelines + 1;
 
 	return 0;
 }
 
 /**
- * Data plane threads: message handling
+ * Disable a given pipeline from running on any DP thread.
+ *
+ * CP thread:
+ *  - Detects the thread that is running the given pipeline, if any;
+ *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
+ *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
+ *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
+ *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
+ *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
+ *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
+ *    below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final value of t->n_pipelines;
+ *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
+ *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
+ *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
+ *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
+ *    behaviors are detailed below, which are all valid:
+ *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
+ *       exactly one time during the current dispatch loop iteration. This takes place when the DP
+ *       thread sees the final values of the t->pipeline[] array;
+ *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
+ *       the exception of the pipeline_last, which is not run during the current dispatch loop
+ *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
+ *
+ * DP thread:
+ *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
  */
-static inline struct thread_msg_req *
-thread_msg_recv(struct rte_ring *msgq_req)
-{
-	struct thread_msg_req *req;
-
-	int status = rte_ring_sc_dequeue(msgq_req, (void **) &req);
-
-	if (status != 0)
-		return NULL;
-
-	return req;
-}
-
-static inline void
-thread_msg_send(struct rte_ring *msgq_rsp,
-	struct thread_msg_rsp *rsp)
-{
-	int status;
-
-	do {
-		status = rte_ring_sp_enqueue(msgq_rsp, rsp);
-	} while (status == -ENOBUFS);
-}
-
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_enable(struct thread_data *t,
-	struct thread_msg_req *req)
+void
+pipeline_disable(struct rte_swx_pipeline *p)
 {
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	struct pipeline_data *p = &t->pipeline_data[t->n_pipelines];
-
-	/* Request */
-	if (t->n_pipelines >= THREAD_PIPELINES_MAX) {
-		rsp->status = -1;
-		return rsp;
-	}
-
-	t->p[t->n_pipelines] = req->pipeline_enable.p;
-
-	p->p = req->pipeline_enable.p;
-	p->timer_period = (rte_get_tsc_hz() *
-		req->pipeline_enable.timer_period_ms) / 1000;
-	p->time_next = rte_get_tsc_cycles() + p->timer_period;
+	struct thread *t;
+	uint64_t n_pipelines;
+	uint32_t thread_id, i;
 
-	t->n_pipelines++;
+	/* Check input params */
+	if (!p)
+		return;
 
-	/* Response */
-	rsp->status = 0;
-	return rsp;
-}
+	/* Find the thread that runs this pipeline. */
+	thread_id = pipeline_find(p);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
 
-static struct thread_msg_rsp *
-thread_msg_handle_pipeline_disable(struct thread_data *t,
-	struct thread_msg_req *req)
-{
-	struct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;
-	uint32_t n_pipelines = t->n_pipelines;
-	struct rte_swx_pipeline *pipeline = req->pipeline_disable.p;
-	uint32_t i;
+	t = &threads[thread_id];
+	n_pipelines = t->n_pipelines;
 
-	/* find pipeline */
 	for (i = 0; i < n_pipelines; i++) {
-		struct pipeline_data *p = &t->pipeline_data[i];
+		struct rte_swx_pipeline *pipeline = t->pipelines[i];
 
-		if (p->p != pipeline)
+		if (pipeline != p)
 			continue;
 
 		if (i < n_pipelines - 1) {
-			struct rte_swx_pipeline *pipeline_last =
-				t->p[n_pipelines - 1];
-			struct pipeline_data *p_last =
-				&t->pipeline_data[n_pipelines - 1];
+			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
 
-			t->p[i] = pipeline_last;
-			memcpy(p, p_last, sizeof(*p));
+			t->pipelines[i] = pipeline_last;
 		}
 
-		t->n_pipelines--;
+		rte_wmb();
+		t->n_pipelines = n_pipelines - 1;
 
-		rsp->status = 0;
-		return rsp;
+		return;
 	}
 
-	/* should not get here */
-	rsp->status = 0;
-	return rsp;
-}
-
-static void
-thread_msg_handle(struct thread_data *t)
-{
-	for ( ; ; ) {
-		struct thread_msg_req *req;
-		struct thread_msg_rsp *rsp;
-
-		req = thread_msg_recv(t->msgq_req);
-		if (req == NULL)
-			break;
-
-		switch (req->type) {
-		case THREAD_REQ_PIPELINE_ENABLE:
-			rsp = thread_msg_handle_pipeline_enable(t, req);
-			break;
-
-		case THREAD_REQ_PIPELINE_DISABLE:
-			rsp = thread_msg_handle_pipeline_disable(t, req);
-			break;
-
-		default:
-			rsp = (struct thread_msg_rsp *) req;
-			rsp->status = -1;
-		}
-
-		thread_msg_send(t->msgq_rsp, rsp);
-	}
+	return;
 }
 
 /**
- * Data plane threads: main
+ * Data plane (DP) threads.
+ *
+ * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
+ * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
+ * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
+ * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
+ * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
+ * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
+ * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
+ * marked as volatile.
  */
 int
 thread_main(void *arg __rte_unused)
 {
-	struct thread_data *t;
-	uint32_t thread_id, i;
+	struct thread *t;
+	uint32_t thread_id;
 
 	thread_id = rte_lcore_id();
-	t = &thread_data[thread_id];
-
-	/* Dispatch loop */
-	for (i = 0; ; i++) {
-		uint32_t j;
-
-		/* Data Plane */
-		for (j = 0; j < t->n_pipelines; j++)
-			rte_swx_pipeline_run(t->p[j], PIPELINE_INSTR_QUANTA);
+	t = &threads[thread_id];
 
-		/* Control Plane */
-		if ((i & 0xF) == 0) {
-			uint64_t time = rte_get_tsc_cycles();
-			uint64_t time_next_min = UINT64_MAX;
-
-			if (time < t->time_next_min)
-				continue;
-
-			/* Thread message queues */
-			{
-				uint64_t time_next = t->time_next;
-
-				if (time_next <= time) {
-					thread_msg_handle(t);
-					time_next = time + t->timer_period;
-					t->time_next = time_next;
-				}
-
-				if (time_next < time_next_min)
-					time_next_min = time_next;
-			}
+	/* Dispatch loop. */
+	for ( ; ; ) {
+		uint32_t i;
 
-			t->time_next_min = time_next_min;
-		}
+		/* Pipelines. */
+		for (i = 0; i < t->n_pipelines; i++)
+			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 712cb25bbb..338d480abb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -9,18 +9,21 @@
 
 #include <rte_swx_pipeline.h>
 
+/**
+ * Control plane (CP) thread.
+ */
 int
-thread_pipeline_enable(uint32_t thread_id,
-		       struct rte_swx_pipeline *p,
-		       uint32_t timer_period_ms);
+thread_init(void);
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-			struct rte_swx_pipeline *p);
+pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 
-int
-thread_init(void);
+void
+pipeline_disable(struct rte_swx_pipeline *p);
 
+/**
+ * Data plane (DP) threads.
+ */
 int
 thread_main(void *arg);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 09/11] examples/pipeline: support blocks other than pipelines
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (7 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
                     ` (2 subsequent siblings)
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Previously, the data plane threads only supported the execution of
pipelines assigned to them through configuration updates. Now, the
data plane threads also support running blocks such as IPsec.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/thread.c | 143 +++++++++++++++++++++++++++++++++++++
 examples/pipeline/thread.h |   9 +++
 2 files changed, 152 insertions(+)

diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 3001bc0858..dc3ea73fbf 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -16,6 +16,10 @@
 #define THREAD_PIPELINES_MAX                               256
 #endif
 
+#ifndef THREAD_BLOCKS_MAX
+#define THREAD_BLOCKS_MAX                                  256
+#endif
+
 /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
  * work, but not too big to avoid starving any other pipelines mapped to the
  * same thread. For a pipeline that executes 10 instructions per packet, a
@@ -38,9 +42,16 @@
  *  - Read-write by the CP thread;
  *  - Read-only by the DP thread.
  */
+struct block {
+	block_run_f block_func;
+	void *block;
+};
+
 struct thread {
 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
+	struct block *blocks[THREAD_BLOCKS_MAX];
 	volatile uint64_t n_pipelines;
+	volatile uint64_t n_blocks;
 	int enabled;
 } __rte_cache_aligned;
 
@@ -53,14 +64,43 @@ int
 thread_init(void)
 {
 	uint32_t thread_id;
+	int status = 0;
 
 	RTE_LCORE_FOREACH_WORKER(thread_id) {
 		struct thread *t = &threads[thread_id];
+		uint32_t i;
 
 		t->enabled = 1;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			struct block *b;
+
+			b = calloc(1, sizeof(struct block));
+			if (!b) {
+				status = -ENOMEM;
+				goto error;
+			}
+
+			t->blocks[i] = b;
+		}
 	}
 
 	return 0;
+
+error:
+	RTE_LCORE_FOREACH_WORKER(thread_id) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		t->enabled = 0;
+
+		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
+			free(t->blocks[i]);
+			t->blocks[i] = NULL;
+		}
+	}
+
+	return status;
 }
 
 static uint32_t
@@ -83,6 +123,26 @@ pipeline_find(struct rte_swx_pipeline *p)
 	return thread_id;
 }
 
+static uint32_t
+block_find(void *b)
+{
+	uint32_t thread_id;
+
+	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
+		struct thread *t = &threads[thread_id];
+		uint32_t i;
+
+		if (!t->enabled)
+			continue;
+
+		for (i = 0; i < t->n_blocks; i++)
+			if (t->blocks[i]->block == b)
+				break;
+	}
+
+	return thread_id;
+}
+
 /**
  * Enable a given pipeline to run on a specific DP thread.
  *
@@ -201,9 +261,85 @@ pipeline_disable(struct rte_swx_pipeline *p)
 	return;
 }
 
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+
+	/* Check input params */
+	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
+		return -EINVAL;
+
+	if (block_find(block) < RTE_MAX_LCORE)
+		return -EEXIST;
+
+	t = &threads[thread_id];
+	if (!t->enabled)
+		return -EINVAL;
+
+	n_blocks = t->n_blocks;
+
+	/* Check there is room for at least one more block. */
+	if (n_blocks >= THREAD_BLOCKS_MAX)
+		return -ENOSPC;
+
+	/* Install the new block. */
+	t->blocks[n_blocks]->block_func = block_func;
+	t->blocks[n_blocks]->block = block;
+
+	rte_wmb();
+	t->n_blocks = n_blocks + 1;
+
+	return 0;
+}
+
+void
+block_disable(void *block)
+{
+	struct thread *t;
+	uint64_t n_blocks;
+	uint32_t thread_id, i;
+
+	/* Check input params */
+	if (!block)
+		return;
+
+	/* Find the thread that runs this block. */
+	thread_id = block_find(block);
+	if (thread_id == RTE_MAX_LCORE)
+		return;
+
+	t = &threads[thread_id];
+	n_blocks = t->n_blocks;
+
+	for (i = 0; i < n_blocks; i++) {
+		struct block *b = t->blocks[i];
+
+		if (block != b->block)
+			continue;
+
+		if (i < n_blocks - 1) {
+			struct block *block_last = t->blocks[n_blocks - 1];
+
+			t->blocks[i] = block_last;
+		}
+
+		rte_wmb();
+		t->n_blocks = n_blocks - 1;
+
+		rte_wmb();
+		t->blocks[n_blocks - 1] = b;
+
+		return;
+	}
+}
+
 /**
  * Data plane (DP) threads.
  *
+
+
  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
@@ -229,6 +365,13 @@ thread_main(void *arg __rte_unused)
 		/* Pipelines. */
 		for (i = 0; i < t->n_pipelines; i++)
 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
+
+		/* Blocks. */
+		for (i = 0; i < t->n_blocks; i++) {
+			struct block *b = t->blocks[i];
+
+			b->block_func(b->block);
+		}
 	}
 
 	return 0;
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index 338d480abb..f2e643def5 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -21,6 +21,15 @@ pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id);
 void
 pipeline_disable(struct rte_swx_pipeline *p);
 
+typedef void
+(*block_run_f)(void *block);
+
+int
+block_enable(block_run_f block_func, void *block, uint32_t thread_id);
+
+void
+block_disable(void *block);
+
 /**
  * Data plane (DP) threads.
  */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 10/11] examples/pipeline: add block enable/disable CLI commands
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (8 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-01-26 14:12   ` [PATCH V6 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
  2023-02-05 16:13   ` [PATCH V6 00/11] pipeline: add IPsec support Thomas Monjalon
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add CLI commands to enable/disable block execution on data plane
threads.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index d9c325c89c..87f9c0370d 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -3253,6 +3253,134 @@ cmd_pipeline_disable(char **tokens,
 	pipeline_disable(p);
 }
 
+static const char cmd_block_enable_help[] =
+"block type <block_type> instance <block_name> enable thread <thread_id>\n";
+
+static void
+cmd_block_enable(char **tokens,
+		 uint32_t n_tokens,
+		 char *out,
+		 size_t out_size,
+		 void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	block_run_f block_func = NULL;
+	void *block = NULL;
+	uint32_t thread_id;
+	int status;
+
+	if (n_tokens != 8) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "enable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+		return;
+	}
+
+	if (strcmp(tokens[6], "thread") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
+		return;
+	}
+
+	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block_func = (block_run_f)rte_swx_ipsec_run;
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	status = block_enable(block_func, block, thread_id);
+	if (status) {
+		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
+		return;
+	}
+}
+
+static const char cmd_block_disable_help[] =
+"block type <block_type> instance <block_name> disable\n";
+
+static void
+cmd_block_disable(char **tokens,
+		  uint32_t n_tokens,
+		  char *out,
+		  size_t out_size,
+		  void *obj __rte_unused)
+{
+	char *block_type, *block_name;
+	void *block = NULL;
+
+	if (n_tokens != 6) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	if (strcmp(tokens[1], "type") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
+		return;
+	}
+
+	block_type = tokens[2];
+
+	if (strcmp(tokens[3], "instance") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
+		return;
+	}
+
+	block_name = tokens[4];
+
+	if (strcmp(tokens[5], "disable") != 0) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+		return;
+	}
+
+	if (!strcmp(block_type, "ipsec")) {
+		struct rte_swx_ipsec *ipsec;
+
+		ipsec = rte_swx_ipsec_find(block_name);
+		if (!ipsec) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
+			return;
+		}
+
+		block = (void *)ipsec;
+	} else {
+		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
+		return;
+	}
+
+	block_disable(block);
+}
+
 static void
 cmd_help(char **tokens,
 	 uint32_t n_tokens,
@@ -3301,6 +3429,8 @@ cmd_help(char **tokens,
 			"\tipsec create\n"
 			"\tipsec sa add\n"
 			"\tipsec sa delete\n"
+			"\tblock enable\n"
+			"\tblock disable\n"
 			);
 		return;
 	}
@@ -3556,6 +3686,18 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
+		return;
+	}
+
+	if (!strcmp(tokens[0], "block") &&
+		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
+		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
+		return;
+	}
+
 	snprintf(out, out_size, "Invalid command\n");
 }
 
@@ -3815,6 +3957,18 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		}
 	}
 
+	if (!strcmp(tokens[0], "block")) {
+		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
+			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+
+		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
+			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
+			return;
+		}
+	}
+
 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH V6 11/11] examples/pipeline: add IPsec example
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (9 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
@ 2023-01-26 14:12   ` Cristian Dumitrescu
  2023-02-05 16:13   ` [PATCH V6 00/11] pipeline: add IPsec support Thomas Monjalon
  11 siblings, 0 replies; 87+ messages in thread
From: Cristian Dumitrescu @ 2023-01-26 14:12 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R

Add example files to illustrate the pipeline IPsec support.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ipsec.cli    |  57 +++++++
 examples/pipeline/examples/ipsec.io     |  23 +++
 examples/pipeline/examples/ipsec.spec   | 138 +++++++++++++++
 examples/pipeline/examples/ipsec_sa.txt | 216 ++++++++++++++++++++++++
 4 files changed, 434 insertions(+)
 create mode 100644 examples/pipeline/examples/ipsec.cli
 create mode 100644 examples/pipeline/examples/ipsec.io
 create mode 100644 examples/pipeline/examples/ipsec.spec
 create mode 100644 examples/pipeline/examples/ipsec_sa.txt

diff --git a/examples/pipeline/examples/ipsec.cli b/examples/pipeline/examples/ipsec.cli
new file mode 100644
index 0000000000..8cb5bf4239
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.cli
@@ -0,0 +1,57 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 --vdev crypto_aesni_mb0 -- -s ./examples/pipeline/examples/ipsec.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/ipsec.spec /tmp/ipsec.c
+pipeline libbuild /tmp/ipsec.c /tmp/ipsec.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 128 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+cryptodev crypto_aesni_mb0 queues 1 qsize 128
+ring RING0 size 1024 numa 0
+ring RING1 size 1024 numa 0
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/ipsec.so io ./examples/pipeline/examples/ipsec.io numa 0
+
+;
+; List of IPsec devices.
+;
+ipsec IPSEC0 create in RING0 out RING1 cryptodev crypto_aesni_mb0 cryptoq 0 bsz 32 32 32 32 samax 512 numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+//pipeline PIPELINE0 table policy_table add ./examples/pipeline/examples/ipsec_policy_table.txt
+//pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/ipsec_routing_table.txt
+//pipeline PIPELINE0 table nexthop_table add ./examples/pipeline/examples/ipsec_nexthop_table.txt
+//pipeline PIPELINE0 commit
+
+ipsec IPSEC0 sa add ./examples/pipeline/examples/ipsec_sa.txt
+
+;
+; Pipelines and blocks mapping to CPU threads.
+;
+pipeline PIPELINE0 enable thread 1
+block type ipsec instance IPSEC0 enable thread 1
diff --git a/examples/pipeline/examples/ipsec.io b/examples/pipeline/examples/ipsec.io
new file mode 100644
index 0000000000..f5a3fcf961
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.io
@@ -0,0 +1,23 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ring RING1 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ring RING0 bsz 32
diff --git a/examples/pipeline/examples/ipsec.spec b/examples/pipeline/examples/ipsec.spec
new file mode 100644
index 0000000000..09aa831881
--- /dev/null
+++ b/examples/pipeline/examples/ipsec.spec
@@ -0,0 +1,138 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_h {
+	bit<8> ver_ihl
+	bit<8> diffserv
+	bit<16> total_len
+	bit<16> identification
+	bit<16> flags_offset
+	bit<8> ttl
+	bit<8> protocol
+	bit<16> hdr_checksum
+	bit<32> src_addr
+	bit<32> dst_addr
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+struct ipsec_internal_h {
+	bit<32> sa_id
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+header ipsec_internal instanceof ipsec_internal_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port_in
+	bit<32> port_out
+
+	bit<32> src_addr
+	bit<32> dst_addr
+	bit<8> protocol
+	bit<16> src_port
+	bit<16> dst_port
+}
+
+metadata instanceof metadata_t
+
+//
+// Actions
+//
+struct encrypt_args_t {
+	bit<32> sa_id
+}
+
+action encrypt args instanceof encrypt_args_t {
+	//Set the IPsec internal header.
+	validate h.ipsec_internal
+	mov h.ipsec_internal.sa_id t.sa_id
+
+	return
+}
+
+action drop args none {
+	drop
+}
+
+//
+// Tables.
+//
+table policy_table {
+	key {
+		m.src_addr exact
+		m.dst_addr exact
+		m.protocol exact
+		m.src_port exact
+		m.dst_port exact
+	}
+
+	actions {
+		encrypt
+		drop
+	}
+
+	default_action encrypt args sa_id 0
+	size 65536
+}
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port_in
+
+	jmpeq FROM_IPSEC_TO_NET m.port_in 1
+
+FROM_NET_TO_IPSEC : extract h.ethernet
+
+	extract h.ipv4
+	mov m.src_addr h.ipv4.src_addr
+	mov m.dst_addr h.ipv4.dst_addr
+	mov m.protocol h.ipv4.protocol
+
+	extract h.udp
+	mov m.src_port h.udp.src_port
+	mov m.dst_port h.udp.dst_port
+
+	table policy_table
+
+	mov m.port_out 1
+
+	emit h.ipsec_internal
+	emit h.ipv4
+	emit h.udp
+	tx m.port_out
+
+FROM_IPSEC_TO_NET : extract h.ipv4
+
+	validate h.ethernet
+	mov h.ethernet.dst_addr 0xa0b0c0d0e0f0
+	mov h.ethernet.src_addr 0xa1b1c1d1e1f1
+	mov h.ethernet.ethertype 0x0800
+
+	mov m.port_out 0
+
+	emit h.ethernet
+	emit h.ipv4
+	tx m.port_out
+}
diff --git a/examples/pipeline/examples/ipsec_sa.txt b/examples/pipeline/examples/ipsec_sa.txt
new file mode 100644
index 0000000000..37bfd8a9ce
--- /dev/null
+++ b/examples/pipeline/examples/ipsec_sa.txt
@@ -0,0 +1,216 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+// IPsec SA syntax:
+//
+// <sa>
+//    : encrypt <crypto_params> <encap_params>
+//    | decrypt <crypto_params> <encap_params>
+//    ;
+//
+// <crypto_params>
+//    : <cipher> <auth>
+//    | <aead>
+//    ;
+//
+// <cipher>
+//    : cipher <ciher_alg> key <cipher_key>
+//    | cipher <cipher_alg>
+//    ;
+//
+// <auth>
+//    : auth <authentication_alg> key <authentication_key>
+//    | auth <authentication_alg>
+//    ;
+//
+// <aead>
+//    : aead <aead_alg> key <aead_key>
+//    ;
+//
+// <encap_params>
+//    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>
+//    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>
+//    | esp spi <spi> transport
+//    ;
+
+//
+// cipher = null, auth = null
+//
+encrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth null esp spi 100 transport
+decrypt cipher null auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth null esp spi 100 transport
+
+//
+// aes-gcm-128
+//
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-gcm-192
+//
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-gcm-256
+//
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-gcm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// aes-ccm-128
+//
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-128 key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// aes-ccm-192
+//
+// Note: Currently not supported by the "aesni_mb" library.
+//
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;encrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+;decrypt aead aes-ccm-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b esp spi 100 transport
+
+//
+// aes-ccm-256
+//
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead aes-ccm-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// chacha20-poly1305
+//
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt aead chacha20-poly1305 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 esp spi 100 transport
+
+//
+// cipher = aes-cbc-128, auth = null
+//
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-128 key 0x000102030405060708090a0b0c0d0e0f auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-192, auth = null
+//
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-192 key 0x000102030405060708090a0b0c0d0e0f1011121314151617 auth null esp spi 100 transport
+
+//
+// cipher = aes-cbc-256, auth = null
+//
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-cbc-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-128, auth = null
+//
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-128 key 0x000102030405060708090a0b0c0d0e0f10111213 auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-192, auth = null
+//
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-192 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b auth null esp spi 100 transport
+
+//
+// cipher = aes-ctr-256, auth = null
+//
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher aes-ctr-256 key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223 auth null esp spi 100 transport
+
+//
+// cipher = null, auth = sha1-hmac
+//
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha1-hmac key 0x000102030405060708090a0b0c0d0e0f10111213 esp spi 100 transport
+
+//
+// cipher = null, auth = sha256-hmac
+//
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha256-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f esp spi 100 transport
+
+//
+// cipher = null, auth = sha384-hmac
+//
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha384-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f esp spi 100 transport
+
+//
+// cipher = null, auth = sha512-hmac
+//
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+encrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv4 srcaddr 0xa0a1a2a3 dstaddr 0xb0b1b2b3
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 tunnel ipv6 srcaddr 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf dstaddr b0b1b2b3b4b5b6b7b8b9babbbcbdbebf
+decrypt cipher null auth sha512-hmac key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f esp spi 100 transport
-- 
2.34.1


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH V6 00/11] pipeline: add IPsec support
  2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
                     ` (10 preceding siblings ...)
  2023-01-26 14:12   ` [PATCH V6 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
@ 2023-02-05 16:13   ` Thomas Monjalon
  11 siblings, 0 replies; 87+ messages in thread
From: Thomas Monjalon @ 2023-02-05 16:13 UTC (permalink / raw)
  To: Cristian Dumitrescu; +Cc: dev

26/01/2023 15:12, Cristian Dumitrescu:
> This patch set introduces a companion block for the SWX pipeline for
> IPsec support.
> 
> The IPsec block is external to the pipeline, hence it needs to be
> explicitly instantiated by the user and connected to a pipeline
> instance through the pipeline I/O ports.
> 
> Main features:
> * IPsec inbound (encrypted input packets -> clear text output packets)
> and outbound (clear text input packets -> encrypted output packets)
> processing support for tunnel and transport modes.
> 
> Interaction of the IPsec block with the pipeline:
> * Each IPsec block instance has its own set of Security Associations
> (SAs) used to process the input packets. Each SA is identified by its
> unique SA ID. The IPsec inbound and outbound SAs share the same ID
> space.
> * Each input packet is first mapped to one of the existing SAs by
> using the SA ID and then processed according to the identified SA. The
> SA ID is read from input packet. The SA ID field is typically written
> by the pipeline before sending the packet to the IPsec block.

Applied, thanks.




^ permalink raw reply	[flat|nested] 87+ messages in thread

end of thread, other threads:[~2023-02-05 16:14 UTC | newest]

Thread overview: 87+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-11 20:55 [PATCH 00/11] pipeline: add IPsec support Cristian Dumitrescu
2023-01-11 20:55 ` [PATCH 01/11] " Cristian Dumitrescu
2023-01-11 20:55 ` [PATCH 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
2023-01-11 20:56 ` [PATCH 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
2023-01-11 23:43 ` [PATCH V2 00/11] pipeline: add IPsec support Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 01/11] " Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
2023-01-11 23:43   ` [PATCH V2 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
2023-01-12 15:45 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 01/11] " Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
2023-01-12 15:45   ` [PATCH V3 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
2023-01-12 15:49 ` [PATCH V3 00/11] pipeline: add IPsec support Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 01/11] " Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
2023-01-12 15:49   ` [PATCH V3 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
2023-01-12 18:53 ` [PATCH V4 00/11] pipeline: add IPsec support Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 01/11] " Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
2023-01-12 18:53   ` [PATCH V4 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
2023-01-26  9:17   ` [PATCH V4 00/11] pipeline: add IPsec support Thomas Monjalon
2023-01-26 13:11     ` Dumitrescu, Cristian
2023-01-26 13:34 ` [PATCH V5 " Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 01/11] " Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
2023-01-26 13:34   ` [PATCH V5 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
2023-01-26 14:12 ` [PATCH V6 00/11] pipeline: add IPsec support Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 01/11] " Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 02/11] examples/pipeline: rework memory pool support Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 03/11] examples/pipeline: streamline ring support Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 04/11] examples/pipeline: streamline the Ethernet device support Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 05/11] examples/pipeline: support crypto devices Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 06/11] examples/pipeline: add CLI command for crypto device Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 07/11] examples/pipeline: add IPsec CLI commands Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 08/11] examples/pipeline: rework the thread configuration updates Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 09/11] examples/pipeline: support blocks other than pipelines Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 10/11] examples/pipeline: add block enable/disable CLI commands Cristian Dumitrescu
2023-01-26 14:12   ` [PATCH V6 11/11] examples/pipeline: add IPsec example Cristian Dumitrescu
2023-02-05 16:13   ` [PATCH V6 00/11] pipeline: add IPsec support Thomas Monjalon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).