All of lore.kernel.org
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH 1/5] pipeline: prepare for variable size headers
@ 2021-07-27 16:36 Cristian Dumitrescu
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 2/5] pipeline: add support " Cristian Dumitrescu
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 16:36 UTC (permalink / raw)
  To: dev

The emit instruction that is responsible for pushing headers into the
output packet is now reading the header length from internal run-time
structures as opposed to constant value from the instruction opcode.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 84505e2a45..2f5cfacd85 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -251,6 +251,7 @@ TAILQ_HEAD(header_tailq, header);
 
 struct header_runtime {
 	uint8_t *ptr0;
+	uint32_t n_bytes;
 };
 
 struct header_out_runtime {
@@ -2522,11 +2523,14 @@ header_build(struct rte_swx_pipeline *p)
 
 		TAILQ_FOREACH(h, &p->headers, node) {
 			uint8_t *header_storage;
+			uint32_t n_bytes =  h->st->n_bits / 8;
 
 			header_storage = &t->header_storage[offset];
-			offset += h->st->n_bits / 8;
+			offset += n_bytes;
 
 			t->headers[h->id].ptr0 = header_storage;
+			t->headers[h->id].n_bytes = n_bytes;
+
 			t->structs[h->struct_id] = header_storage;
 		}
 	}
@@ -3262,9 +3266,11 @@ __instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit)
 	for (i = 0; i < n_emit; i++) {
 		uint32_t header_id = ip->io.hdr.header_id[i];
 		uint32_t struct_id = ip->io.hdr.struct_id[i];
-		uint32_t n_bytes = ip->io.hdr.n_bytes[i];
 
 		struct header_runtime *hi = &t->headers[header_id];
+		uint8_t *hi_ptr0 = hi->ptr0;
+		uint32_t n_bytes = hi->n_bytes;
+
 		uint8_t *hi_ptr = t->structs[struct_id];
 
 		if (!MASK64_BIT_GET(valid_headers, header_id))
@@ -3281,7 +3287,7 @@ __instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit)
 			if (!t->n_headers_out) {
 				ho = &t->headers_out[0];
 
-				ho->ptr0 = hi->ptr0;
+				ho->ptr0 = hi_ptr0;
 				ho->ptr = hi_ptr;
 
 				ho_ptr = hi_ptr;
@@ -3302,7 +3308,7 @@ __instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit)
 			ho->n_bytes = ho_nbytes;
 
 			ho++;
-			ho->ptr0 = hi->ptr0;
+			ho->ptr0 = hi_ptr0;
 			ho->ptr = hi_ptr;
 
 			ho_ptr = hi_ptr;
-- 
2.17.1


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

* [dpdk-dev] [PATCH 2/5] pipeline: add support for variable size headers
  2021-07-27 16:36 [dpdk-dev] [PATCH 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
@ 2021-07-27 16:36 ` Cristian Dumitrescu
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 3/5] pipeline: add variable size headers extract instruction Cristian Dumitrescu
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 16:36 UTC (permalink / raw)
  To: dev

Added support for variable size headers. The last field of a struct
type can now have a variable size between 0 and N bytes. Useful to
accommodate IPv4 packets with options, etc.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c      | 107 +++++++++++++++++++++---
 lib/pipeline/rte_swx_pipeline.h      |  15 +++-
 lib/pipeline/rte_swx_pipeline_spec.c | 116 ++++++++++++++++++---------
 3 files changed, 189 insertions(+), 49 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 2f5cfacd85..1a8259ffee 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -109,6 +109,7 @@ struct field {
 	char name[RTE_SWX_NAME_SIZE];
 	uint32_t n_bits;
 	uint32_t offset;
+	int var_size;
 };
 
 struct struct_type {
@@ -117,6 +118,8 @@ struct struct_type {
 	struct field *fields;
 	uint32_t n_fields;
 	uint32_t n_bits;
+	uint32_t n_bits_min;
+	int var_size;
 };
 
 TAILQ_HEAD(struct_type_tailq, struct_type);
@@ -1413,7 +1416,8 @@ int
 rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 				      const char *name,
 				      struct rte_swx_field_params *fields,
-				      uint32_t n_fields)
+				      uint32_t n_fields,
+				      int last_field_has_variable_size)
 {
 	struct struct_type *st;
 	uint32_t i;
@@ -1425,11 +1429,12 @@ rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 
 	for (i = 0; i < n_fields; i++) {
 		struct rte_swx_field_params *f = &fields[i];
+		int var_size = ((i == n_fields - 1) && last_field_has_variable_size) ? 1 : 0;
 		uint32_t j;
 
 		CHECK_NAME(f->name, EINVAL);
 		CHECK(f->n_bits, EINVAL);
-		CHECK(f->n_bits <= 64, EINVAL);
+		CHECK((f->n_bits <= 64) || var_size, EINVAL);
 		CHECK((f->n_bits & 7) == 0, EINVAL);
 
 		for (j = 0; j < i; j++) {
@@ -1456,14 +1461,18 @@ rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 	for (i = 0; i < n_fields; i++) {
 		struct field *dst = &st->fields[i];
 		struct rte_swx_field_params *src = &fields[i];
+		int var_size = ((i == n_fields - 1) && last_field_has_variable_size) ? 1 : 0;
 
 		strcpy(dst->name, src->name);
 		dst->n_bits = src->n_bits;
 		dst->offset = st->n_bits;
+		dst->var_size = var_size;
 
 		st->n_bits += src->n_bits;
+		st->n_bits_min += var_size ? 0 : src->n_bits;
 	}
 	st->n_fields = n_fields;
+	st->var_size = last_field_has_variable_size;
 
 	/* Node add to tailq. */
 	TAILQ_INSERT_TAIL(&p->struct_types, st, node);
@@ -1987,6 +1996,7 @@ rte_swx_pipeline_extern_type_register(struct rte_swx_pipeline *p,
 	CHECK_NAME(mailbox_struct_type_name, EINVAL);
 	mailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);
 	CHECK(mailbox_struct_type, EINVAL);
+	CHECK(!mailbox_struct_type->var_size, EINVAL);
 
 	CHECK(constructor, EINVAL);
 	CHECK(destructor, EINVAL);
@@ -2278,6 +2288,7 @@ rte_swx_pipeline_extern_func_register(struct rte_swx_pipeline *p,
 	CHECK_NAME(mailbox_struct_type_name, EINVAL);
 	mailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);
 	CHECK(mailbox_struct_type, EINVAL);
+	CHECK(!mailbox_struct_type->var_size, EINVAL);
 
 	CHECK(func, EINVAL);
 
@@ -2603,6 +2614,7 @@ rte_swx_pipeline_packet_metadata_register(struct rte_swx_pipeline *p,
 	CHECK_NAME(struct_type_name, EINVAL);
 	st  = struct_type_find(p, struct_type_name);
 	CHECK(st, EINVAL);
+	CHECK(!st->var_size, EINVAL);
 	CHECK(!p->metadata_st, EINVAL);
 
 	p->metadata_st = st;
@@ -3080,6 +3092,7 @@ instr_hdr_extract_translate(struct rte_swx_pipeline *p,
 
 	h = header_parse(p, tokens[1]);
 	CHECK(h, EINVAL);
+	CHECK(!h->st->var_size, EINVAL);
 
 	instr->type = INSTR_HDR_EXTRACT;
 	instr->io.hdr.header_id[0] = h->id;
@@ -3727,10 +3740,13 @@ instr_mov_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* MOV, MOV_MH, MOV_HM or MOV_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_MOV;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_MOV_MH;
@@ -3992,10 +4008,13 @@ instr_alu_add_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* ADD, ADD_HM, ADD_MH, ADD_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_ADD;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_ADD_HM;
@@ -4045,10 +4064,13 @@ instr_alu_sub_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* SUB, SUB_HM, SUB_MH, SUB_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_SUB;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_SUB_HM;
@@ -4097,10 +4119,13 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
 
 	fdst = header_field_parse(p, dst, &hdst);
 	CHECK(fdst && (fdst->n_bits == 16), EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* CKADD_FIELD. */
 	fsrc = header_field_parse(p, src, &hsrc);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_CKADD_FIELD;
 		instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
 		instr->alu.dst.n_bits = fdst->n_bits;
@@ -4114,6 +4139,7 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
 	/* CKADD_STRUCT, CKADD_STRUCT20. */
 	hsrc = header_parse(p, src);
 	CHECK(hsrc, EINVAL);
+	CHECK(!hsrc->st->var_size, EINVAL);
 
 	instr->type = INSTR_ALU_CKADD_STRUCT;
 	if ((hsrc->st->n_bits / 8) == 20)
@@ -4144,9 +4170,11 @@ instr_alu_cksub_translate(struct rte_swx_pipeline *p,
 
 	fdst = header_field_parse(p, dst, &hdst);
 	CHECK(fdst && (fdst->n_bits == 16), EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	fsrc = header_field_parse(p, src, &hsrc);
 	CHECK(fsrc, EINVAL);
+	CHECK(!fsrc->var_size, EINVAL);
 
 	instr->type = INSTR_ALU_CKSUB_FIELD;
 	instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
@@ -4175,10 +4203,13 @@ instr_alu_shl_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* SHL, SHL_HM, SHL_MH, SHL_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_SHL;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_SHL_HM;
@@ -4228,10 +4259,13 @@ instr_alu_shr_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* SHR, SHR_HM, SHR_MH, SHR_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_SHR;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_SHR_HM;
@@ -4281,10 +4315,13 @@ instr_alu_and_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* AND, AND_MH, AND_HM, AND_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_AND;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_ALU_AND_MH;
@@ -4334,10 +4371,13 @@ instr_alu_or_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* OR, OR_MH, OR_HM, OR_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_OR;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_ALU_OR_MH;
@@ -4387,10 +4427,13 @@ instr_alu_xor_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* XOR, XOR_MH, XOR_HM, XOR_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_XOR;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_ALU_XOR_MH;
@@ -5269,6 +5312,8 @@ instr_regprefetch_translate(struct rte_swx_pipeline *p,
 	/* REGPREFETCH_RH, REGPREFETCH_RM. */
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	if (fidx) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		instr->type = INSTR_REGPREFETCH_RM;
 		if (idx[0] == 'h')
 			instr->type = INSTR_REGPREFETCH_RH;
@@ -5312,10 +5357,13 @@ instr_regrd_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* REGRD_HRH, REGRD_HRM, REGRD_MRH, REGRD_MRM. */
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	if (fidx) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		instr->type = INSTR_REGRD_MRM;
 		if (dst[0] == 'h' && idx[0] != 'h')
 			instr->type = INSTR_REGRD_HRM;
@@ -5373,6 +5421,9 @@ instr_regwr_translate(struct rte_swx_pipeline *p,
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fidx && fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGWR_RMM;
 		if (idx[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_REGWR_RHM;
@@ -5393,6 +5444,8 @@ instr_regwr_translate(struct rte_swx_pipeline *p,
 
 	/* REGWR_RHI, REGWR_RMI. */
 	if (fidx && !fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		src_val = strtoull(src, &src, 0);
 		CHECK(!src[0], EINVAL);
 
@@ -5413,6 +5466,8 @@ instr_regwr_translate(struct rte_swx_pipeline *p,
 		idx_val = strtoul(idx, &idx, 0);
 		CHECK(!idx[0], EINVAL);
 
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGWR_RIM;
 		if (src[0] == 'h')
 			instr->type = INSTR_REGWR_RIH;
@@ -5462,6 +5517,9 @@ instr_regadd_translate(struct rte_swx_pipeline *p,
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fidx && fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGADD_RMM;
 		if (idx[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_REGADD_RHM;
@@ -5482,6 +5540,8 @@ instr_regadd_translate(struct rte_swx_pipeline *p,
 
 	/* REGADD_RHI, REGADD_RMI. */
 	if (fidx && !fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		src_val = strtoull(src, &src, 0);
 		CHECK(!src[0], EINVAL);
 
@@ -5502,6 +5562,8 @@ instr_regadd_translate(struct rte_swx_pipeline *p,
 		idx_val = strtoul(idx, &idx, 0);
 		CHECK(!idx[0], EINVAL);
 
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGADD_RIM;
 		if (src[0] == 'h')
 			instr->type = INSTR_REGADD_RIH;
@@ -6171,6 +6233,8 @@ instr_metprefetch_translate(struct rte_swx_pipeline *p,
 	/* METPREFETCH_H, METPREFETCH_M. */
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	if (fidx) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		instr->type = INSTR_METPREFETCH_M;
 		if (idx[0] == 'h')
 			instr->type = INSTR_METPREFETCH_H;
@@ -6216,14 +6280,19 @@ instr_meter_translate(struct rte_swx_pipeline *p,
 
 	flength = struct_field_parse(p, action, length, &length_struct_id);
 	CHECK(flength, EINVAL);
+	CHECK(!flength->var_size, EINVAL);
 
 	fcin = struct_field_parse(p, action, color_in, &color_in_struct_id);
 
 	fcout = struct_field_parse(p, NULL, color_out, &color_out_struct_id);
 	CHECK(fcout, EINVAL);
+	CHECK(!fcout->var_size, EINVAL);
 
 	/* index = HMEFT, length = HMEFT, color_in = MEFT, color_out = MEF. */
 	if (fidx && fcin) {
+		CHECK(!fidx->var_size, EINVAL);
+		CHECK(!fcin->var_size, EINVAL);
+
 		instr->type = INSTR_METER_MMM;
 		if (idx[0] == 'h' && length[0] == 'h')
 			instr->type = INSTR_METER_HHM;
@@ -6255,7 +6324,11 @@ instr_meter_translate(struct rte_swx_pipeline *p,
 
 	/* index = HMEFT, length = HMEFT, color_in = I, color_out = MEF. */
 	if (fidx && !fcin) {
-		uint32_t color_in_val = strtoul(color_in, &color_in, 0);
+		uint32_t color_in_val;
+
+		CHECK(!fidx->var_size, EINVAL);
+
+		color_in_val = strtoul(color_in, &color_in, 0);
 		CHECK(!color_in[0], EINVAL);
 
 		instr->type = INSTR_METER_MMI;
@@ -6292,6 +6365,8 @@ instr_meter_translate(struct rte_swx_pipeline *p,
 		idx_val = strtoul(idx, &idx, 0);
 		CHECK(!idx[0], EINVAL);
 
+		CHECK(!fcin->var_size, EINVAL);
+
 		instr->type = INSTR_METER_IMM;
 		if (length[0] == 'h')
 			instr->type = INSTR_METER_IHM;
@@ -7139,10 +7214,13 @@ instr_jmp_eq_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_EQ, JMP_EQ_MH, JMP_EQ_HM, JMP_EQ_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_EQ;
 		if (a[0] != 'h' && b[0] == 'h')
 			instr->type = INSTR_JMP_EQ_MH;
@@ -7196,10 +7274,13 @@ instr_jmp_neq_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_NEQ, JMP_NEQ_MH, JMP_NEQ_HM, JMP_NEQ_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_NEQ;
 		if (a[0] != 'h' && b[0] == 'h')
 			instr->type = INSTR_JMP_NEQ_MH;
@@ -7253,10 +7334,13 @@ instr_jmp_lt_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_LT, JMP_LT_MH, JMP_LT_HM, JMP_LT_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_LT;
 		if (a[0] == 'h' && b[0] != 'h')
 			instr->type = INSTR_JMP_LT_HM;
@@ -7310,10 +7394,13 @@ instr_jmp_gt_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_GT, JMP_GT_MH, JMP_GT_HM, JMP_GT_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_GT;
 		if (a[0] == 'h' && b[0] != 'h')
 			instr->type = INSTR_JMP_GT_HM;
@@ -8405,7 +8492,7 @@ instr_pattern_mov_all_validate_search(struct rte_swx_pipeline *p,
 		return 0;
 
 	h = header_find_by_struct_id(p, instr[0].mov.dst.struct_id);
-	if (!h)
+	if (!h || h->st->var_size)
 		return 0;
 
 	for (src_field_id = 0; src_field_id < a->st->n_fields; src_field_id++)
@@ -8987,7 +9074,7 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
 			       const char **instructions,
 			       uint32_t n_instructions)
 {
-	struct struct_type *args_struct_type;
+	struct struct_type *args_struct_type = NULL;
 	struct action *a;
 	int err;
 
@@ -9000,8 +9087,7 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
 		CHECK_NAME(args_struct_type_name, EINVAL);
 		args_struct_type = struct_type_find(p, args_struct_type_name);
 		CHECK(args_struct_type, EINVAL);
-	} else {
-		args_struct_type = NULL;
+		CHECK(!args_struct_type->var_size, EINVAL);
 	}
 
 	/* Node allocation. */
@@ -9250,6 +9336,9 @@ table_match_fields_check(struct rte_swx_pipeline *p,
 			goto end;
 		}
 
+		if (header)
+			*header = NULL;
+
 		return 0;
 	}
 
@@ -9265,7 +9354,7 @@ table_match_fields_check(struct rte_swx_pipeline *p,
 	 */
 	hf = header_field_parse(p, params->fields[0].name, &h0);
 	mf = metadata_field_parse(p, params->fields[0].name);
-	if (!hf && !mf) {
+	if ((!hf && !mf) || (hf && hf->var_size)) {
 		status = -EINVAL;
 		goto end;
 	}
@@ -9277,7 +9366,7 @@ table_match_fields_check(struct rte_swx_pipeline *p,
 			struct header *h;
 
 			hf = header_field_parse(p, params->fields[i].name, &h);
-			if (!hf || (h->id != h0->id)) {
+			if (!hf || (h->id != h0->id) || hf->var_size) {
 				status = -EINVAL;
 				goto end;
 			}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index cd395ac39d..5afca2bc20 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -298,6 +298,14 @@ struct rte_swx_field_params {
  * Similar to C language structs, they are a well defined sequence of fields,
  * with each field having a unique name and a constant size.
  *
+ * In order to use structs to express variable size packet headers such as IPv4
+ * with options, it is allowed for the last field of the struct type to have a
+ * variable size between 0 and *n_bits* bits, with the actual size of this field
+ * determined at run-time for each packet. This struct feature is restricted to
+ * just a few selected instructions that deal with packet headers, so a typical
+ * struct generally has a constant size that is fully known when its struct type
+ * is registered.
+ *
  * @param[in] p
  *   Pipeline handle.
  * @param[in] name
@@ -306,6 +314,10 @@ struct rte_swx_field_params {
  *   The sequence of struct fields.
  * @param[in] n_fields
  *   The number of struct fields.
+ * @param[in] last_field_has_variable_size
+ *   If non-zero (true), then the last field has a variable size between 0 and
+ *   *n_bits* bits, with its actual size determined at run-time for each packet.
+ *   If zero (false), then the last field has a constant size of *n_bits* bits.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
@@ -317,7 +329,8 @@ int
 rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 				      const char *name,
 				      struct rte_swx_field_params *fields,
-				      uint32_t n_fields);
+				      uint32_t n_fields,
+				      int last_field_has_variable_size);
 
 /**
  * Pipeline packet header register
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 6980b03900..754bbabe7d 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -95,7 +95,7 @@ extobj_statement_parse(struct extobj_spec *s,
  * struct.
  *
  * struct STRUCT_TYPE_NAME {
- *	bit<SIZE> FIELD_NAME
+ *	bit<SIZE> | varbit<SIZE> FIELD_NAME
  *	...
  * }
  */
@@ -103,6 +103,7 @@ struct struct_spec {
 	char *name;
 	struct rte_swx_field_params *fields;
 	uint32_t n_fields;
+	int varbit;
 };
 
 static void
@@ -126,6 +127,8 @@ struct_spec_free(struct struct_spec *s)
 	s->fields = NULL;
 
 	s->n_fields = 0;
+
+	s->varbit = 0;
 }
 
 static int
@@ -172,8 +175,9 @@ struct_block_parse(struct struct_spec *s,
 		   const char **err_msg)
 {
 	struct rte_swx_field_params *new_fields;
-	char *p = tokens[0], *name;
+	char *p = tokens[0], *name = NULL;
 	uint32_t n_bits;
+	int varbit = 0, error = 0, error_size_invalid = 0, error_varbit_not_last = 0;
 
 	/* Handle end of block. */
 	if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
@@ -182,64 +186,97 @@ struct_block_parse(struct struct_spec *s,
 	}
 
 	/* Check format. */
-	if ((n_tokens != 2) ||
-	    (strlen(p) < 6) ||
-	    (p[0] != 'b') ||
-	    (p[1] != 'i') ||
-	    (p[2] != 't') ||
-	    (p[3] != '<') ||
-	    (p[strlen(p) - 1] != '>')) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Invalid struct field statement.";
-		return -EINVAL;
+	if (n_tokens != 2) {
+		error = -EINVAL;
+		goto error;
 	}
 
-	/* Remove the "bit<" and ">". */
-	p[strlen(p) - 1] = 0;
-	p += 4;
+	if (s->varbit) {
+		error = -EINVAL;
+		error_varbit_not_last = 1;
+		goto error;
+	}
+
+	if (!strncmp(p, "bit<", strlen("bit<"))) {
+		size_t len = strlen(p);
+
+		if ((len < strlen("bit< >")) || (p[len - 1] != '>')) {
+			error = -EINVAL;
+			goto error;
+		}
+
+		/* Remove the "bit<" and ">". */
+		p[strlen(p) - 1] = 0;
+		p += strlen("bit<");
+	} else if (!strncmp(p, "varbit<", strlen("varbit<"))) {
+		size_t len = strlen(p);
+
+		if ((len < strlen("varbit< >")) || (p[len - 1] != '>')) {
+			error = -EINVAL;
+			goto error;
+		}
+
+		/* Remove the "varbit<" and ">". */
+		p[strlen(p) - 1] = 0;
+		p += strlen("varbit<");
+
+		/* Set the varbit flag. */
+		varbit = 1;
+	} else {
+		error = -EINVAL;
+		goto error;
+	}
 
 	n_bits = strtoul(p, &p, 0);
 	if ((p[0]) ||
 	    !n_bits ||
-	    (n_bits % 8) ||
-	    (n_bits > 64)) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Invalid struct field size.";
-		return -EINVAL;
+	    (n_bits % 8)) {
+		error = -EINVAL;
+		error_size_invalid = 1;
+		goto error;
 	}
 
 	/* spec. */
 	name = strdup(tokens[1]);
 	if (!name) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Memory allocation failed.";
-		return -ENOMEM;
+		error = -ENOMEM;
+		goto error;
 	}
 
-	new_fields = realloc(s->fields,
-			     (s->n_fields + 1) * sizeof(struct rte_swx_field_params));
+	new_fields = realloc(s->fields, (s->n_fields + 1) * sizeof(struct rte_swx_field_params));
 	if (!new_fields) {
-		free(name);
-
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Memory allocation failed.";
-		return -ENOMEM;
+		error = -ENOMEM;
+		goto error;
 	}
 
 	s->fields = new_fields;
 	s->fields[s->n_fields].name = name;
 	s->fields[s->n_fields].n_bits = n_bits;
 	s->n_fields++;
+	s->varbit = varbit;
 
 	return 0;
+
+error:
+	free(name);
+
+	if (err_line)
+		*err_line = n_lines;
+
+	if (err_msg) {
+		*err_msg = "Invalid struct field statement.";
+
+		if ((error == -EINVAL) && error_varbit_not_last)
+			*err_msg = "Varbit field is not the last struct field.";
+
+		if ((error == -EINVAL) && error_size_invalid)
+			*err_msg = "Invalid struct field size.";
+
+		if (error == -ENOMEM)
+			*err_msg = "Memory allocation failed.";
+	}
+
+	return error;
 }
 
 /*
@@ -1607,7 +1644,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			status = rte_swx_pipeline_struct_type_register(p,
 				struct_spec.name,
 				struct_spec.fields,
-				struct_spec.n_fields);
+				struct_spec.n_fields,
+				struct_spec.varbit);
 			if (status) {
 				if (err_line)
 					*err_line = n_lines;
-- 
2.17.1


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

* [dpdk-dev] [PATCH 3/5] pipeline: add variable size headers extract instruction
  2021-07-27 16:36 [dpdk-dev] [PATCH 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 2/5] pipeline: add support " Cristian Dumitrescu
@ 2021-07-27 16:36 ` Cristian Dumitrescu
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 4/5] pipeline: add header look-ahead instruction Cristian Dumitrescu
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 16:36 UTC (permalink / raw)
  To: dev

Added a mechanism to extract variable size headers through a special
flavor of the extract instruction. The length of the last struct field
which has variable size is passed as argument to the instruction.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 75 ++++++++++++++++++++++++++++++---
 1 file changed, 69 insertions(+), 6 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 1a8259ffee..d87fe77d91 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -303,6 +303,9 @@ enum instruction_type {
 	INSTR_HDR_EXTRACT7,
 	INSTR_HDR_EXTRACT8,
 
+	/* extract h.header m.last_field_size */
+	INSTR_HDR_EXTRACT_M,
+
 	/* emit h.header */
 	INSTR_HDR_EMIT,
 	INSTR_HDR_EMIT_TX,
@@ -3088,16 +3091,35 @@ instr_hdr_extract_translate(struct rte_swx_pipeline *p,
 	struct header *h;
 
 	CHECK(!action, EINVAL);
-	CHECK(n_tokens == 2, EINVAL);
+	CHECK((n_tokens == 2) || (n_tokens == 3), EINVAL);
 
 	h = header_parse(p, tokens[1]);
 	CHECK(h, EINVAL);
-	CHECK(!h->st->var_size, EINVAL);
 
-	instr->type = INSTR_HDR_EXTRACT;
-	instr->io.hdr.header_id[0] = h->id;
-	instr->io.hdr.struct_id[0] = h->struct_id;
-	instr->io.hdr.n_bytes[0] = h->st->n_bits / 8;
+	if (n_tokens == 2) {
+		CHECK(!h->st->var_size, EINVAL);
+
+		instr->type = INSTR_HDR_EXTRACT;
+		instr->io.hdr.header_id[0] = h->id;
+		instr->io.hdr.struct_id[0] = h->struct_id;
+		instr->io.hdr.n_bytes[0] = h->st->n_bits / 8;
+	} else {
+		struct field *mf;
+
+		CHECK(h->st->var_size, EINVAL);
+
+		mf = metadata_field_parse(p, tokens[2]);
+		CHECK(mf, EINVAL);
+		CHECK(!mf->var_size, EINVAL);
+
+		instr->type = INSTR_HDR_EXTRACT_M;
+		instr->io.io.offset = mf->offset / 8;
+		instr->io.io.n_bits = mf->n_bits;
+		instr->io.hdr.header_id[0] = h->id;
+		instr->io.hdr.struct_id[0] = h->struct_id;
+		instr->io.hdr.n_bytes[0] = h->st->n_bits_min / 8;
+	}
+
 	return 0;
 }
 
@@ -3237,6 +3259,46 @@ instr_hdr_extract8_exec(struct rte_swx_pipeline *p)
 	thread_ip_inc(p);
 }
 
+static inline void
+instr_hdr_extract_m_exec(struct rte_swx_pipeline *p)
+{
+	struct thread *t = &p->threads[p->thread_id];
+	struct instruction *ip = t->ip;
+
+	uint64_t valid_headers = t->valid_headers;
+	uint8_t *ptr = t->ptr;
+	uint32_t offset = t->pkt.offset;
+	uint32_t length = t->pkt.length;
+
+	uint32_t n_bytes_last = METADATA_READ(t, ip->io.io.offset, ip->io.io.n_bits);
+	uint32_t header_id = ip->io.hdr.header_id[0];
+	uint32_t struct_id = ip->io.hdr.struct_id[0];
+	uint32_t n_bytes = ip->io.hdr.n_bytes[0];
+
+	struct header_runtime *h = &t->headers[header_id];
+
+	TRACE("[Thread %2u]: extract header %u (%u + %u bytes)\n",
+	      p->thread_id,
+	      header_id,
+	      n_bytes,
+	      n_bytes_last);
+
+	n_bytes += n_bytes_last;
+
+	/* Headers. */
+	t->structs[struct_id] = ptr;
+	t->valid_headers = MASK64_BIT_SET(valid_headers, header_id);
+	h->n_bytes = n_bytes;
+
+	/* Packet. */
+	t->pkt.offset = offset + n_bytes;
+	t->pkt.length = length - n_bytes;
+	t->ptr = ptr + n_bytes;
+
+	/* Thread. */
+	thread_ip_inc(p);
+}
+
 /*
  * emit.
  */
@@ -8842,6 +8904,7 @@ static instr_exec_t instruction_table[] = {
 	[INSTR_HDR_EXTRACT6] = instr_hdr_extract6_exec,
 	[INSTR_HDR_EXTRACT7] = instr_hdr_extract7_exec,
 	[INSTR_HDR_EXTRACT8] = instr_hdr_extract8_exec,
+	[INSTR_HDR_EXTRACT_M] = instr_hdr_extract_m_exec,
 
 	[INSTR_HDR_EMIT] = instr_hdr_emit_exec,
 	[INSTR_HDR_EMIT_TX] = instr_hdr_emit_tx_exec,
-- 
2.17.1


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

* [dpdk-dev] [PATCH 4/5] pipeline: add header look-ahead instruction
  2021-07-27 16:36 [dpdk-dev] [PATCH 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 2/5] pipeline: add support " Cristian Dumitrescu
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 3/5] pipeline: add variable size headers extract instruction Cristian Dumitrescu
@ 2021-07-27 16:36 ` Cristian Dumitrescu
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 5/5] examples/pipeline: add variable size headers example Cristian Dumitrescu
  2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 16:36 UTC (permalink / raw)
  To: dev

Added look-ahead instruction to read a header from the input packet
without advancing the extraction pointer. This is typically used in
correlation with the special extract instruction to extract variable
size headers from the input packet: the first few header fields are
read without advancing the extraction pointer, just enough to detect
the actual length of the header (e.g. IPv4 IHL field); then the full
header is extracted.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 61 +++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index d87fe77d91..13028bcc6a 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -306,6 +306,9 @@ enum instruction_type {
 	/* extract h.header m.last_field_size */
 	INSTR_HDR_EXTRACT_M,
 
+	/* lookahead h.header */
+	INSTR_HDR_LOOKAHEAD,
+
 	/* emit h.header */
 	INSTR_HDR_EMIT,
 	INSTR_HDR_EMIT_TX,
@@ -3123,6 +3126,31 @@ instr_hdr_extract_translate(struct rte_swx_pipeline *p,
 	return 0;
 }
 
+static int
+instr_hdr_lookahead_translate(struct rte_swx_pipeline *p,
+			      struct action *action,
+			      char **tokens,
+			      int n_tokens,
+			      struct instruction *instr,
+			      struct instruction_data *data __rte_unused)
+{
+	struct header *h;
+
+	CHECK(!action, EINVAL);
+	CHECK(n_tokens == 2, EINVAL);
+
+	h = header_parse(p, tokens[1]);
+	CHECK(h, EINVAL);
+	CHECK(!h->st->var_size, EINVAL);
+
+	instr->type = INSTR_HDR_LOOKAHEAD;
+	instr->io.hdr.header_id[0] = h->id;
+	instr->io.hdr.struct_id[0] = h->struct_id;
+	instr->io.hdr.n_bytes[0] = 0; /* Unused. */
+
+	return 0;
+}
+
 static inline void
 __instr_hdr_extract_exec(struct rte_swx_pipeline *p, uint32_t n_extract);
 
@@ -3299,6 +3327,30 @@ instr_hdr_extract_m_exec(struct rte_swx_pipeline *p)
 	thread_ip_inc(p);
 }
 
+static inline void
+instr_hdr_lookahead_exec(struct rte_swx_pipeline *p)
+{
+	struct thread *t = &p->threads[p->thread_id];
+	struct instruction *ip = t->ip;
+
+	uint64_t valid_headers = t->valid_headers;
+	uint8_t *ptr = t->ptr;
+
+	uint32_t header_id = ip->io.hdr.header_id[0];
+	uint32_t struct_id = ip->io.hdr.struct_id[0];
+
+	TRACE("[Thread %2u]: lookahead header %u\n",
+	      p->thread_id,
+	      header_id);
+
+	/* Headers. */
+	t->structs[struct_id] = ptr;
+	t->valid_headers = MASK64_BIT_SET(valid_headers, header_id);
+
+	/* Thread. */
+	thread_ip_inc(p);
+}
+
 /*
  * emit.
  */
@@ -7916,6 +7968,14 @@ instr_translate(struct rte_swx_pipeline *p,
 						   instr,
 						   data);
 
+	if (!strcmp(tokens[tpos], "lookahead"))
+		return instr_hdr_lookahead_translate(p,
+						     action,
+						     &tokens[tpos],
+						     n_tokens - tpos,
+						     instr,
+						     data);
+
 	if (!strcmp(tokens[tpos], "emit"))
 		return instr_hdr_emit_translate(p,
 						action,
@@ -8905,6 +8965,7 @@ static instr_exec_t instruction_table[] = {
 	[INSTR_HDR_EXTRACT7] = instr_hdr_extract7_exec,
 	[INSTR_HDR_EXTRACT8] = instr_hdr_extract8_exec,
 	[INSTR_HDR_EXTRACT_M] = instr_hdr_extract_m_exec,
+	[INSTR_HDR_LOOKAHEAD] = instr_hdr_lookahead_exec,
 
 	[INSTR_HDR_EMIT] = instr_hdr_emit_exec,
 	[INSTR_HDR_EMIT_TX] = instr_hdr_emit_tx_exec,
-- 
2.17.1


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

* [dpdk-dev] [PATCH 5/5] examples/pipeline: add variable size headers example
  2021-07-27 16:36 [dpdk-dev] [PATCH 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
                   ` (2 preceding siblings ...)
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 4/5] pipeline: add header look-ahead instruction Cristian Dumitrescu
@ 2021-07-27 16:36 ` Cristian Dumitrescu
  2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 16:36 UTC (permalink / raw)
  To: dev

Added the files to illustrate the variable size header usage.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 examples/pipeline/examples/varbit.cli  | 35 ++++++++++
 examples/pipeline/examples/varbit.spec | 95 ++++++++++++++++++++++++++
 2 files changed, 130 insertions(+)
 create mode 100644 examples/pipeline/examples/varbit.cli
 create mode 100644 examples/pipeline/examples/varbit.spec

diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
new file mode 100644
index 0000000000..0589e32c15
--- /dev/null
+++ b/examples/pipeline/examples/varbit.cli
@@ -0,0 +1,35 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+;
+; Customize the LINK parameters to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+
+link LINK0 dev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+link LINK1 dev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+link LINK2 dev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+link LINK3 dev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; PIPELINE0 setup.
+;
+pipeline PIPELINE0 create 0
+
+pipeline PIPELINE0 port in 0 link LINK0 rxq 0 bsz 32
+pipeline PIPELINE0 port in 1 link LINK1 rxq 0 bsz 32
+pipeline PIPELINE0 port in 2 link LINK2 rxq 0 bsz 32
+pipeline PIPELINE0 port in 3 link LINK3 rxq 0 bsz 32
+
+pipeline PIPELINE0 port out 0 link LINK0 txq 0 bsz 32
+pipeline PIPELINE0 port out 1 link LINK1 txq 0 bsz 32
+pipeline PIPELINE0 port out 2 link LINK2 txq 0 bsz 32
+pipeline PIPELINE0 port out 3 link LINK3 txq 0 bsz 32
+pipeline PIPELINE0 port out 4 sink none
+
+pipeline PIPELINE0 build ./examples/pipeline/examples/varbit.spec
+
+;
+; Pipelines-to-threads mapping.
+;
+thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/varbit.spec b/examples/pipeline/examples/varbit.spec
new file mode 100644
index 0000000000..cd49403fa9
--- /dev/null
+++ b/examples/pipeline/examples/varbit.spec
@@ -0,0 +1,95 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+; This simple example illustrates how to work with variable size headers. The assumed input packet
+; is Ethernet/IPv4/UDP, with the IPv4 header containing between 0 and 40 bytes of options. To locate
+; the start of the UDP header, the size of the IPv4 header needs to be detected first, which is done
+; by reading the first byte of the IPv4 header that carries the 4-bit Internet Header Length (IHL)
+; field; this read is done with the "lookahead" instruction, which does not advance the extract
+; pointer within the input packet buffer. Once the size of the IPv4 header options is known for the
+; current packet, the IPv4 header is extracted by using the two-argument "extract" instruction. Then
+; the UDP header is extracted and modified.
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_top_h {
+	bit<8> ver_ihl
+}
+
+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
+	varbit<320> options
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+header ethernet instanceof ethernet_h
+header ipv4_top instanceof ipv4_top_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port
+	bit<32> options_size
+}
+
+metadata instanceof metadata_t
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port
+
+	// Extract the fixed size Ethernet header.
+	extract h.ethernet
+
+	// Extract the variable size IPv4 header with up to 10 options.
+	lookahead h.ipv4_top
+	mov m.options_size h.ipv4_top.ver_ihl
+	and m.options_size 0xF
+	sub m.options_size 5
+	shl m.options_size 2
+	extract h.ipv4 m.options_size
+
+	// Extract the fixed size UDP header.
+	extract h.udp
+
+	// Modify the UDP header.
+	mov h.udp.src_port 0xAABB
+	mov h.udp.dst_port 0xCCDD
+
+	// Decide the output port.
+	xor m.port 1
+
+	// Emit the Ethernet, IPv4 and UDP headers.
+	emit h.ethernet
+	emit h.ipv4
+	emit h.udp
+
+	tx m.port
+}
-- 
2.17.1


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

* [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers
  2021-07-27 16:36 [dpdk-dev] [PATCH 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
                   ` (3 preceding siblings ...)
  2021-07-27 16:36 ` [dpdk-dev] [PATCH 5/5] examples/pipeline: add variable size headers example Cristian Dumitrescu
@ 2021-07-27 17:43 ` Cristian Dumitrescu
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 2/5] pipeline: add support " Cristian Dumitrescu
                     ` (4 more replies)
  4 siblings, 5 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 17:43 UTC (permalink / raw)
  To: dev

The emit instruction that is responsible for pushing headers into the
output packet is now reading the header length from internal run-time
structures as opposed to constant value from the instruction opcode.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 84505e2a45..2f5cfacd85 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -251,6 +251,7 @@ TAILQ_HEAD(header_tailq, header);
 
 struct header_runtime {
 	uint8_t *ptr0;
+	uint32_t n_bytes;
 };
 
 struct header_out_runtime {
@@ -2522,11 +2523,14 @@ header_build(struct rte_swx_pipeline *p)
 
 		TAILQ_FOREACH(h, &p->headers, node) {
 			uint8_t *header_storage;
+			uint32_t n_bytes =  h->st->n_bits / 8;
 
 			header_storage = &t->header_storage[offset];
-			offset += h->st->n_bits / 8;
+			offset += n_bytes;
 
 			t->headers[h->id].ptr0 = header_storage;
+			t->headers[h->id].n_bytes = n_bytes;
+
 			t->structs[h->struct_id] = header_storage;
 		}
 	}
@@ -3262,9 +3266,11 @@ __instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit)
 	for (i = 0; i < n_emit; i++) {
 		uint32_t header_id = ip->io.hdr.header_id[i];
 		uint32_t struct_id = ip->io.hdr.struct_id[i];
-		uint32_t n_bytes = ip->io.hdr.n_bytes[i];
 
 		struct header_runtime *hi = &t->headers[header_id];
+		uint8_t *hi_ptr0 = hi->ptr0;
+		uint32_t n_bytes = hi->n_bytes;
+
 		uint8_t *hi_ptr = t->structs[struct_id];
 
 		if (!MASK64_BIT_GET(valid_headers, header_id))
@@ -3281,7 +3287,7 @@ __instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit)
 			if (!t->n_headers_out) {
 				ho = &t->headers_out[0];
 
-				ho->ptr0 = hi->ptr0;
+				ho->ptr0 = hi_ptr0;
 				ho->ptr = hi_ptr;
 
 				ho_ptr = hi_ptr;
@@ -3302,7 +3308,7 @@ __instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit)
 			ho->n_bytes = ho_nbytes;
 
 			ho++;
-			ho->ptr0 = hi->ptr0;
+			ho->ptr0 = hi_ptr0;
 			ho->ptr = hi_ptr;
 
 			ho_ptr = hi_ptr;
-- 
2.17.1


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

* [dpdk-dev] [PATCH V2 2/5] pipeline: add support for variable size headers
  2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
@ 2021-07-27 17:43   ` Cristian Dumitrescu
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 3/5] pipeline: add variable size headers extract instruction Cristian Dumitrescu
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 17:43 UTC (permalink / raw)
  To: dev

Added support for variable size headers. The last field of a struct
type can now have a variable size between 0 and N bytes. Useful to
accommodate IPv4 packets with options, etc.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c      | 107 ++++++++++++++++++++++---
 lib/pipeline/rte_swx_pipeline.h      |  15 +++-
 lib/pipeline/rte_swx_pipeline_spec.c | 115 ++++++++++++++++++---------
 3 files changed, 189 insertions(+), 48 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 2f5cfacd85..1a8259ffee 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -109,6 +109,7 @@ struct field {
 	char name[RTE_SWX_NAME_SIZE];
 	uint32_t n_bits;
 	uint32_t offset;
+	int var_size;
 };
 
 struct struct_type {
@@ -117,6 +118,8 @@ struct struct_type {
 	struct field *fields;
 	uint32_t n_fields;
 	uint32_t n_bits;
+	uint32_t n_bits_min;
+	int var_size;
 };
 
 TAILQ_HEAD(struct_type_tailq, struct_type);
@@ -1413,7 +1416,8 @@ int
 rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 				      const char *name,
 				      struct rte_swx_field_params *fields,
-				      uint32_t n_fields)
+				      uint32_t n_fields,
+				      int last_field_has_variable_size)
 {
 	struct struct_type *st;
 	uint32_t i;
@@ -1425,11 +1429,12 @@ rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 
 	for (i = 0; i < n_fields; i++) {
 		struct rte_swx_field_params *f = &fields[i];
+		int var_size = ((i == n_fields - 1) && last_field_has_variable_size) ? 1 : 0;
 		uint32_t j;
 
 		CHECK_NAME(f->name, EINVAL);
 		CHECK(f->n_bits, EINVAL);
-		CHECK(f->n_bits <= 64, EINVAL);
+		CHECK((f->n_bits <= 64) || var_size, EINVAL);
 		CHECK((f->n_bits & 7) == 0, EINVAL);
 
 		for (j = 0; j < i; j++) {
@@ -1456,14 +1461,18 @@ rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 	for (i = 0; i < n_fields; i++) {
 		struct field *dst = &st->fields[i];
 		struct rte_swx_field_params *src = &fields[i];
+		int var_size = ((i == n_fields - 1) && last_field_has_variable_size) ? 1 : 0;
 
 		strcpy(dst->name, src->name);
 		dst->n_bits = src->n_bits;
 		dst->offset = st->n_bits;
+		dst->var_size = var_size;
 
 		st->n_bits += src->n_bits;
+		st->n_bits_min += var_size ? 0 : src->n_bits;
 	}
 	st->n_fields = n_fields;
+	st->var_size = last_field_has_variable_size;
 
 	/* Node add to tailq. */
 	TAILQ_INSERT_TAIL(&p->struct_types, st, node);
@@ -1987,6 +1996,7 @@ rte_swx_pipeline_extern_type_register(struct rte_swx_pipeline *p,
 	CHECK_NAME(mailbox_struct_type_name, EINVAL);
 	mailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);
 	CHECK(mailbox_struct_type, EINVAL);
+	CHECK(!mailbox_struct_type->var_size, EINVAL);
 
 	CHECK(constructor, EINVAL);
 	CHECK(destructor, EINVAL);
@@ -2278,6 +2288,7 @@ rte_swx_pipeline_extern_func_register(struct rte_swx_pipeline *p,
 	CHECK_NAME(mailbox_struct_type_name, EINVAL);
 	mailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);
 	CHECK(mailbox_struct_type, EINVAL);
+	CHECK(!mailbox_struct_type->var_size, EINVAL);
 
 	CHECK(func, EINVAL);
 
@@ -2603,6 +2614,7 @@ rte_swx_pipeline_packet_metadata_register(struct rte_swx_pipeline *p,
 	CHECK_NAME(struct_type_name, EINVAL);
 	st  = struct_type_find(p, struct_type_name);
 	CHECK(st, EINVAL);
+	CHECK(!st->var_size, EINVAL);
 	CHECK(!p->metadata_st, EINVAL);
 
 	p->metadata_st = st;
@@ -3080,6 +3092,7 @@ instr_hdr_extract_translate(struct rte_swx_pipeline *p,
 
 	h = header_parse(p, tokens[1]);
 	CHECK(h, EINVAL);
+	CHECK(!h->st->var_size, EINVAL);
 
 	instr->type = INSTR_HDR_EXTRACT;
 	instr->io.hdr.header_id[0] = h->id;
@@ -3727,10 +3740,13 @@ instr_mov_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* MOV, MOV_MH, MOV_HM or MOV_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_MOV;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_MOV_MH;
@@ -3992,10 +4008,13 @@ instr_alu_add_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* ADD, ADD_HM, ADD_MH, ADD_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_ADD;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_ADD_HM;
@@ -4045,10 +4064,13 @@ instr_alu_sub_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* SUB, SUB_HM, SUB_MH, SUB_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_SUB;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_SUB_HM;
@@ -4097,10 +4119,13 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
 
 	fdst = header_field_parse(p, dst, &hdst);
 	CHECK(fdst && (fdst->n_bits == 16), EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* CKADD_FIELD. */
 	fsrc = header_field_parse(p, src, &hsrc);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_CKADD_FIELD;
 		instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
 		instr->alu.dst.n_bits = fdst->n_bits;
@@ -4114,6 +4139,7 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
 	/* CKADD_STRUCT, CKADD_STRUCT20. */
 	hsrc = header_parse(p, src);
 	CHECK(hsrc, EINVAL);
+	CHECK(!hsrc->st->var_size, EINVAL);
 
 	instr->type = INSTR_ALU_CKADD_STRUCT;
 	if ((hsrc->st->n_bits / 8) == 20)
@@ -4144,9 +4170,11 @@ instr_alu_cksub_translate(struct rte_swx_pipeline *p,
 
 	fdst = header_field_parse(p, dst, &hdst);
 	CHECK(fdst && (fdst->n_bits == 16), EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	fsrc = header_field_parse(p, src, &hsrc);
 	CHECK(fsrc, EINVAL);
+	CHECK(!fsrc->var_size, EINVAL);
 
 	instr->type = INSTR_ALU_CKSUB_FIELD;
 	instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
@@ -4175,10 +4203,13 @@ instr_alu_shl_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* SHL, SHL_HM, SHL_MH, SHL_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_SHL;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_SHL_HM;
@@ -4228,10 +4259,13 @@ instr_alu_shr_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* SHR, SHR_HM, SHR_MH, SHR_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_SHR;
 		if (dst[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_ALU_SHR_HM;
@@ -4281,10 +4315,13 @@ instr_alu_and_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* AND, AND_MH, AND_HM, AND_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_AND;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_ALU_AND_MH;
@@ -4334,10 +4371,13 @@ instr_alu_or_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* OR, OR_MH, OR_HM, OR_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_OR;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_ALU_OR_MH;
@@ -4387,10 +4427,13 @@ instr_alu_xor_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* XOR, XOR_MH, XOR_HM, XOR_HH. */
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fsrc) {
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_ALU_XOR;
 		if (dst[0] != 'h' && src[0] == 'h')
 			instr->type = INSTR_ALU_XOR_MH;
@@ -5269,6 +5312,8 @@ instr_regprefetch_translate(struct rte_swx_pipeline *p,
 	/* REGPREFETCH_RH, REGPREFETCH_RM. */
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	if (fidx) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		instr->type = INSTR_REGPREFETCH_RM;
 		if (idx[0] == 'h')
 			instr->type = INSTR_REGPREFETCH_RH;
@@ -5312,10 +5357,13 @@ instr_regrd_translate(struct rte_swx_pipeline *p,
 
 	fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
 	CHECK(fdst, EINVAL);
+	CHECK(!fdst->var_size, EINVAL);
 
 	/* REGRD_HRH, REGRD_HRM, REGRD_MRH, REGRD_MRM. */
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	if (fidx) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		instr->type = INSTR_REGRD_MRM;
 		if (dst[0] == 'h' && idx[0] != 'h')
 			instr->type = INSTR_REGRD_HRM;
@@ -5373,6 +5421,9 @@ instr_regwr_translate(struct rte_swx_pipeline *p,
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fidx && fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGWR_RMM;
 		if (idx[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_REGWR_RHM;
@@ -5393,6 +5444,8 @@ instr_regwr_translate(struct rte_swx_pipeline *p,
 
 	/* REGWR_RHI, REGWR_RMI. */
 	if (fidx && !fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		src_val = strtoull(src, &src, 0);
 		CHECK(!src[0], EINVAL);
 
@@ -5413,6 +5466,8 @@ instr_regwr_translate(struct rte_swx_pipeline *p,
 		idx_val = strtoul(idx, &idx, 0);
 		CHECK(!idx[0], EINVAL);
 
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGWR_RIM;
 		if (src[0] == 'h')
 			instr->type = INSTR_REGWR_RIH;
@@ -5462,6 +5517,9 @@ instr_regadd_translate(struct rte_swx_pipeline *p,
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	fsrc = struct_field_parse(p, action, src, &src_struct_id);
 	if (fidx && fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGADD_RMM;
 		if (idx[0] == 'h' && src[0] != 'h')
 			instr->type = INSTR_REGADD_RHM;
@@ -5482,6 +5540,8 @@ instr_regadd_translate(struct rte_swx_pipeline *p,
 
 	/* REGADD_RHI, REGADD_RMI. */
 	if (fidx && !fsrc) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		src_val = strtoull(src, &src, 0);
 		CHECK(!src[0], EINVAL);
 
@@ -5502,6 +5562,8 @@ instr_regadd_translate(struct rte_swx_pipeline *p,
 		idx_val = strtoul(idx, &idx, 0);
 		CHECK(!idx[0], EINVAL);
 
+		CHECK(!fsrc->var_size, EINVAL);
+
 		instr->type = INSTR_REGADD_RIM;
 		if (src[0] == 'h')
 			instr->type = INSTR_REGADD_RIH;
@@ -6171,6 +6233,8 @@ instr_metprefetch_translate(struct rte_swx_pipeline *p,
 	/* METPREFETCH_H, METPREFETCH_M. */
 	fidx = struct_field_parse(p, action, idx, &idx_struct_id);
 	if (fidx) {
+		CHECK(!fidx->var_size, EINVAL);
+
 		instr->type = INSTR_METPREFETCH_M;
 		if (idx[0] == 'h')
 			instr->type = INSTR_METPREFETCH_H;
@@ -6216,14 +6280,19 @@ instr_meter_translate(struct rte_swx_pipeline *p,
 
 	flength = struct_field_parse(p, action, length, &length_struct_id);
 	CHECK(flength, EINVAL);
+	CHECK(!flength->var_size, EINVAL);
 
 	fcin = struct_field_parse(p, action, color_in, &color_in_struct_id);
 
 	fcout = struct_field_parse(p, NULL, color_out, &color_out_struct_id);
 	CHECK(fcout, EINVAL);
+	CHECK(!fcout->var_size, EINVAL);
 
 	/* index = HMEFT, length = HMEFT, color_in = MEFT, color_out = MEF. */
 	if (fidx && fcin) {
+		CHECK(!fidx->var_size, EINVAL);
+		CHECK(!fcin->var_size, EINVAL);
+
 		instr->type = INSTR_METER_MMM;
 		if (idx[0] == 'h' && length[0] == 'h')
 			instr->type = INSTR_METER_HHM;
@@ -6255,7 +6324,11 @@ instr_meter_translate(struct rte_swx_pipeline *p,
 
 	/* index = HMEFT, length = HMEFT, color_in = I, color_out = MEF. */
 	if (fidx && !fcin) {
-		uint32_t color_in_val = strtoul(color_in, &color_in, 0);
+		uint32_t color_in_val;
+
+		CHECK(!fidx->var_size, EINVAL);
+
+		color_in_val = strtoul(color_in, &color_in, 0);
 		CHECK(!color_in[0], EINVAL);
 
 		instr->type = INSTR_METER_MMI;
@@ -6292,6 +6365,8 @@ instr_meter_translate(struct rte_swx_pipeline *p,
 		idx_val = strtoul(idx, &idx, 0);
 		CHECK(!idx[0], EINVAL);
 
+		CHECK(!fcin->var_size, EINVAL);
+
 		instr->type = INSTR_METER_IMM;
 		if (length[0] == 'h')
 			instr->type = INSTR_METER_IHM;
@@ -7139,10 +7214,13 @@ instr_jmp_eq_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_EQ, JMP_EQ_MH, JMP_EQ_HM, JMP_EQ_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_EQ;
 		if (a[0] != 'h' && b[0] == 'h')
 			instr->type = INSTR_JMP_EQ_MH;
@@ -7196,10 +7274,13 @@ instr_jmp_neq_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_NEQ, JMP_NEQ_MH, JMP_NEQ_HM, JMP_NEQ_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_NEQ;
 		if (a[0] != 'h' && b[0] == 'h')
 			instr->type = INSTR_JMP_NEQ_MH;
@@ -7253,10 +7334,13 @@ instr_jmp_lt_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_LT, JMP_LT_MH, JMP_LT_HM, JMP_LT_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_LT;
 		if (a[0] == 'h' && b[0] != 'h')
 			instr->type = INSTR_JMP_LT_HM;
@@ -7310,10 +7394,13 @@ instr_jmp_gt_translate(struct rte_swx_pipeline *p,
 
 	fa = struct_field_parse(p, action, a, &a_struct_id);
 	CHECK(fa, EINVAL);
+	CHECK(!fa->var_size, EINVAL);
 
 	/* JMP_GT, JMP_GT_MH, JMP_GT_HM, JMP_GT_HH. */
 	fb = struct_field_parse(p, action, b, &b_struct_id);
 	if (fb) {
+		CHECK(!fb->var_size, EINVAL);
+
 		instr->type = INSTR_JMP_GT;
 		if (a[0] == 'h' && b[0] != 'h')
 			instr->type = INSTR_JMP_GT_HM;
@@ -8405,7 +8492,7 @@ instr_pattern_mov_all_validate_search(struct rte_swx_pipeline *p,
 		return 0;
 
 	h = header_find_by_struct_id(p, instr[0].mov.dst.struct_id);
-	if (!h)
+	if (!h || h->st->var_size)
 		return 0;
 
 	for (src_field_id = 0; src_field_id < a->st->n_fields; src_field_id++)
@@ -8987,7 +9074,7 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
 			       const char **instructions,
 			       uint32_t n_instructions)
 {
-	struct struct_type *args_struct_type;
+	struct struct_type *args_struct_type = NULL;
 	struct action *a;
 	int err;
 
@@ -9000,8 +9087,7 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
 		CHECK_NAME(args_struct_type_name, EINVAL);
 		args_struct_type = struct_type_find(p, args_struct_type_name);
 		CHECK(args_struct_type, EINVAL);
-	} else {
-		args_struct_type = NULL;
+		CHECK(!args_struct_type->var_size, EINVAL);
 	}
 
 	/* Node allocation. */
@@ -9250,6 +9336,9 @@ table_match_fields_check(struct rte_swx_pipeline *p,
 			goto end;
 		}
 
+		if (header)
+			*header = NULL;
+
 		return 0;
 	}
 
@@ -9265,7 +9354,7 @@ table_match_fields_check(struct rte_swx_pipeline *p,
 	 */
 	hf = header_field_parse(p, params->fields[0].name, &h0);
 	mf = metadata_field_parse(p, params->fields[0].name);
-	if (!hf && !mf) {
+	if ((!hf && !mf) || (hf && hf->var_size)) {
 		status = -EINVAL;
 		goto end;
 	}
@@ -9277,7 +9366,7 @@ table_match_fields_check(struct rte_swx_pipeline *p,
 			struct header *h;
 
 			hf = header_field_parse(p, params->fields[i].name, &h);
-			if (!hf || (h->id != h0->id)) {
+			if (!hf || (h->id != h0->id) || hf->var_size) {
 				status = -EINVAL;
 				goto end;
 			}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index cd395ac39d..5afca2bc20 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -298,6 +298,14 @@ struct rte_swx_field_params {
  * Similar to C language structs, they are a well defined sequence of fields,
  * with each field having a unique name and a constant size.
  *
+ * In order to use structs to express variable size packet headers such as IPv4
+ * with options, it is allowed for the last field of the struct type to have a
+ * variable size between 0 and *n_bits* bits, with the actual size of this field
+ * determined at run-time for each packet. This struct feature is restricted to
+ * just a few selected instructions that deal with packet headers, so a typical
+ * struct generally has a constant size that is fully known when its struct type
+ * is registered.
+ *
  * @param[in] p
  *   Pipeline handle.
  * @param[in] name
@@ -306,6 +314,10 @@ struct rte_swx_field_params {
  *   The sequence of struct fields.
  * @param[in] n_fields
  *   The number of struct fields.
+ * @param[in] last_field_has_variable_size
+ *   If non-zero (true), then the last field has a variable size between 0 and
+ *   *n_bits* bits, with its actual size determined at run-time for each packet.
+ *   If zero (false), then the last field has a constant size of *n_bits* bits.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
@@ -317,7 +329,8 @@ int
 rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
 				      const char *name,
 				      struct rte_swx_field_params *fields,
-				      uint32_t n_fields);
+				      uint32_t n_fields,
+				      int last_field_has_variable_size);
 
 /**
  * Pipeline packet header register
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 6980b03900..c57893f18c 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -95,7 +95,7 @@ extobj_statement_parse(struct extobj_spec *s,
  * struct.
  *
  * struct STRUCT_TYPE_NAME {
- *	bit<SIZE> FIELD_NAME
+ *	bit<SIZE> | varbit<SIZE> FIELD_NAME
  *	...
  * }
  */
@@ -103,6 +103,7 @@ struct struct_spec {
 	char *name;
 	struct rte_swx_field_params *fields;
 	uint32_t n_fields;
+	int varbit;
 };
 
 static void
@@ -126,6 +127,8 @@ struct_spec_free(struct struct_spec *s)
 	s->fields = NULL;
 
 	s->n_fields = 0;
+
+	s->varbit = 0;
 }
 
 static int
@@ -172,8 +175,9 @@ struct_block_parse(struct struct_spec *s,
 		   const char **err_msg)
 {
 	struct rte_swx_field_params *new_fields;
-	char *p = tokens[0], *name;
+	char *p = tokens[0], *name = NULL;
 	uint32_t n_bits;
+	int varbit = 0, error = 0, error_size_invalid = 0, error_varbit_not_last = 0;
 
 	/* Handle end of block. */
 	if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
@@ -182,64 +186,98 @@ struct_block_parse(struct struct_spec *s,
 	}
 
 	/* Check format. */
-	if ((n_tokens != 2) ||
-	    (strlen(p) < 6) ||
-	    (p[0] != 'b') ||
-	    (p[1] != 'i') ||
-	    (p[2] != 't') ||
-	    (p[3] != '<') ||
-	    (p[strlen(p) - 1] != '>')) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Invalid struct field statement.";
-		return -EINVAL;
+	if (n_tokens != 2) {
+		error = -EINVAL;
+		goto error;
 	}
 
-	/* Remove the "bit<" and ">". */
-	p[strlen(p) - 1] = 0;
-	p += 4;
+	if (s->varbit) {
+		error = -EINVAL;
+		error_varbit_not_last = 1;
+		goto error;
+	}
+
+	if (!strncmp(p, "bit<", strlen("bit<"))) {
+		size_t len = strlen(p);
+
+		if ((len < strlen("bit< >")) || (p[len - 1] != '>')) {
+			error = -EINVAL;
+			goto error;
+		}
+
+		/* Remove the "bit<" and ">". */
+		p[strlen(p) - 1] = 0;
+		p += strlen("bit<");
+	} else if (!strncmp(p, "varbit<", strlen("varbit<"))) {
+		size_t len = strlen(p);
+
+		if ((len < strlen("varbit< >")) || (p[len - 1] != '>')) {
+			error = -EINVAL;
+			goto error;
+		}
+
+		/* Remove the "varbit<" and ">". */
+		p[strlen(p) - 1] = 0;
+		p += strlen("varbit<");
+
+		/* Set the varbit flag. */
+		varbit = 1;
+	} else {
+		error = -EINVAL;
+		goto error;
+	}
 
 	n_bits = strtoul(p, &p, 0);
 	if ((p[0]) ||
 	    !n_bits ||
 	    (n_bits % 8) ||
-	    (n_bits > 64)) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Invalid struct field size.";
-		return -EINVAL;
+	    ((n_bits > 64) && !varbit)) {
+		error = -EINVAL;
+		error_size_invalid = 1;
+		goto error;
 	}
 
 	/* spec. */
 	name = strdup(tokens[1]);
 	if (!name) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Memory allocation failed.";
-		return -ENOMEM;
+		error = -ENOMEM;
+		goto error;
 	}
 
-	new_fields = realloc(s->fields,
-			     (s->n_fields + 1) * sizeof(struct rte_swx_field_params));
+	new_fields = realloc(s->fields, (s->n_fields + 1) * sizeof(struct rte_swx_field_params));
 	if (!new_fields) {
-		free(name);
-
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Memory allocation failed.";
-		return -ENOMEM;
+		error = -ENOMEM;
+		goto error;
 	}
 
 	s->fields = new_fields;
 	s->fields[s->n_fields].name = name;
 	s->fields[s->n_fields].n_bits = n_bits;
 	s->n_fields++;
+	s->varbit = varbit;
 
 	return 0;
+
+error:
+	free(name);
+
+	if (err_line)
+		*err_line = n_lines;
+
+	if (err_msg) {
+		*err_msg = "Invalid struct field statement.";
+
+		if ((error == -EINVAL) && error_varbit_not_last)
+			*err_msg = "Varbit field is not the last struct field.";
+
+		if ((error == -EINVAL) && error_size_invalid)
+			*err_msg = "Invalid struct field size.";
+
+		if (error == -ENOMEM)
+			*err_msg = "Memory allocation failed.";
+	}
+
+	return error;
 }
 
 /*
@@ -1607,7 +1645,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			status = rte_swx_pipeline_struct_type_register(p,
 				struct_spec.name,
 				struct_spec.fields,
-				struct_spec.n_fields);
+				struct_spec.n_fields,
+				struct_spec.varbit);
 			if (status) {
 				if (err_line)
 					*err_line = n_lines;
-- 
2.17.1


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

* [dpdk-dev] [PATCH V2 3/5] pipeline: add variable size headers extract instruction
  2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 2/5] pipeline: add support " Cristian Dumitrescu
@ 2021-07-27 17:43   ` Cristian Dumitrescu
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 4/5] pipeline: add header look-ahead instruction Cristian Dumitrescu
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 17:43 UTC (permalink / raw)
  To: dev

Added a mechanism to extract variable size headers through a special
flavor of the extract instruction. The length of the last struct field
which has variable size is passed as argument to the instruction.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 75 ++++++++++++++++++++++++++++++---
 1 file changed, 69 insertions(+), 6 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 1a8259ffee..d87fe77d91 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -303,6 +303,9 @@ enum instruction_type {
 	INSTR_HDR_EXTRACT7,
 	INSTR_HDR_EXTRACT8,
 
+	/* extract h.header m.last_field_size */
+	INSTR_HDR_EXTRACT_M,
+
 	/* emit h.header */
 	INSTR_HDR_EMIT,
 	INSTR_HDR_EMIT_TX,
@@ -3088,16 +3091,35 @@ instr_hdr_extract_translate(struct rte_swx_pipeline *p,
 	struct header *h;
 
 	CHECK(!action, EINVAL);
-	CHECK(n_tokens == 2, EINVAL);
+	CHECK((n_tokens == 2) || (n_tokens == 3), EINVAL);
 
 	h = header_parse(p, tokens[1]);
 	CHECK(h, EINVAL);
-	CHECK(!h->st->var_size, EINVAL);
 
-	instr->type = INSTR_HDR_EXTRACT;
-	instr->io.hdr.header_id[0] = h->id;
-	instr->io.hdr.struct_id[0] = h->struct_id;
-	instr->io.hdr.n_bytes[0] = h->st->n_bits / 8;
+	if (n_tokens == 2) {
+		CHECK(!h->st->var_size, EINVAL);
+
+		instr->type = INSTR_HDR_EXTRACT;
+		instr->io.hdr.header_id[0] = h->id;
+		instr->io.hdr.struct_id[0] = h->struct_id;
+		instr->io.hdr.n_bytes[0] = h->st->n_bits / 8;
+	} else {
+		struct field *mf;
+
+		CHECK(h->st->var_size, EINVAL);
+
+		mf = metadata_field_parse(p, tokens[2]);
+		CHECK(mf, EINVAL);
+		CHECK(!mf->var_size, EINVAL);
+
+		instr->type = INSTR_HDR_EXTRACT_M;
+		instr->io.io.offset = mf->offset / 8;
+		instr->io.io.n_bits = mf->n_bits;
+		instr->io.hdr.header_id[0] = h->id;
+		instr->io.hdr.struct_id[0] = h->struct_id;
+		instr->io.hdr.n_bytes[0] = h->st->n_bits_min / 8;
+	}
+
 	return 0;
 }
 
@@ -3237,6 +3259,46 @@ instr_hdr_extract8_exec(struct rte_swx_pipeline *p)
 	thread_ip_inc(p);
 }
 
+static inline void
+instr_hdr_extract_m_exec(struct rte_swx_pipeline *p)
+{
+	struct thread *t = &p->threads[p->thread_id];
+	struct instruction *ip = t->ip;
+
+	uint64_t valid_headers = t->valid_headers;
+	uint8_t *ptr = t->ptr;
+	uint32_t offset = t->pkt.offset;
+	uint32_t length = t->pkt.length;
+
+	uint32_t n_bytes_last = METADATA_READ(t, ip->io.io.offset, ip->io.io.n_bits);
+	uint32_t header_id = ip->io.hdr.header_id[0];
+	uint32_t struct_id = ip->io.hdr.struct_id[0];
+	uint32_t n_bytes = ip->io.hdr.n_bytes[0];
+
+	struct header_runtime *h = &t->headers[header_id];
+
+	TRACE("[Thread %2u]: extract header %u (%u + %u bytes)\n",
+	      p->thread_id,
+	      header_id,
+	      n_bytes,
+	      n_bytes_last);
+
+	n_bytes += n_bytes_last;
+
+	/* Headers. */
+	t->structs[struct_id] = ptr;
+	t->valid_headers = MASK64_BIT_SET(valid_headers, header_id);
+	h->n_bytes = n_bytes;
+
+	/* Packet. */
+	t->pkt.offset = offset + n_bytes;
+	t->pkt.length = length - n_bytes;
+	t->ptr = ptr + n_bytes;
+
+	/* Thread. */
+	thread_ip_inc(p);
+}
+
 /*
  * emit.
  */
@@ -8842,6 +8904,7 @@ static instr_exec_t instruction_table[] = {
 	[INSTR_HDR_EXTRACT6] = instr_hdr_extract6_exec,
 	[INSTR_HDR_EXTRACT7] = instr_hdr_extract7_exec,
 	[INSTR_HDR_EXTRACT8] = instr_hdr_extract8_exec,
+	[INSTR_HDR_EXTRACT_M] = instr_hdr_extract_m_exec,
 
 	[INSTR_HDR_EMIT] = instr_hdr_emit_exec,
 	[INSTR_HDR_EMIT_TX] = instr_hdr_emit_tx_exec,
-- 
2.17.1


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

* [dpdk-dev] [PATCH V2 4/5] pipeline: add header look-ahead instruction
  2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 2/5] pipeline: add support " Cristian Dumitrescu
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 3/5] pipeline: add variable size headers extract instruction Cristian Dumitrescu
@ 2021-07-27 17:43   ` Cristian Dumitrescu
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 5/5] examples/pipeline: add variable size headers example Cristian Dumitrescu
  2021-09-27  7:16   ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Thomas Monjalon
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 17:43 UTC (permalink / raw)
  To: dev

Added look-ahead instruction to read a header from the input packet
without advancing the extraction pointer. This is typically used in
correlation with the special extract instruction to extract variable
size headers from the input packet: the first few header fields are
read without advancing the extraction pointer, just enough to detect
the actual length of the header (e.g. IPv4 IHL field); then the full
header is extracted.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 61 +++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index d87fe77d91..13028bcc6a 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -306,6 +306,9 @@ enum instruction_type {
 	/* extract h.header m.last_field_size */
 	INSTR_HDR_EXTRACT_M,
 
+	/* lookahead h.header */
+	INSTR_HDR_LOOKAHEAD,
+
 	/* emit h.header */
 	INSTR_HDR_EMIT,
 	INSTR_HDR_EMIT_TX,
@@ -3123,6 +3126,31 @@ instr_hdr_extract_translate(struct rte_swx_pipeline *p,
 	return 0;
 }
 
+static int
+instr_hdr_lookahead_translate(struct rte_swx_pipeline *p,
+			      struct action *action,
+			      char **tokens,
+			      int n_tokens,
+			      struct instruction *instr,
+			      struct instruction_data *data __rte_unused)
+{
+	struct header *h;
+
+	CHECK(!action, EINVAL);
+	CHECK(n_tokens == 2, EINVAL);
+
+	h = header_parse(p, tokens[1]);
+	CHECK(h, EINVAL);
+	CHECK(!h->st->var_size, EINVAL);
+
+	instr->type = INSTR_HDR_LOOKAHEAD;
+	instr->io.hdr.header_id[0] = h->id;
+	instr->io.hdr.struct_id[0] = h->struct_id;
+	instr->io.hdr.n_bytes[0] = 0; /* Unused. */
+
+	return 0;
+}
+
 static inline void
 __instr_hdr_extract_exec(struct rte_swx_pipeline *p, uint32_t n_extract);
 
@@ -3299,6 +3327,30 @@ instr_hdr_extract_m_exec(struct rte_swx_pipeline *p)
 	thread_ip_inc(p);
 }
 
+static inline void
+instr_hdr_lookahead_exec(struct rte_swx_pipeline *p)
+{
+	struct thread *t = &p->threads[p->thread_id];
+	struct instruction *ip = t->ip;
+
+	uint64_t valid_headers = t->valid_headers;
+	uint8_t *ptr = t->ptr;
+
+	uint32_t header_id = ip->io.hdr.header_id[0];
+	uint32_t struct_id = ip->io.hdr.struct_id[0];
+
+	TRACE("[Thread %2u]: lookahead header %u\n",
+	      p->thread_id,
+	      header_id);
+
+	/* Headers. */
+	t->structs[struct_id] = ptr;
+	t->valid_headers = MASK64_BIT_SET(valid_headers, header_id);
+
+	/* Thread. */
+	thread_ip_inc(p);
+}
+
 /*
  * emit.
  */
@@ -7916,6 +7968,14 @@ instr_translate(struct rte_swx_pipeline *p,
 						   instr,
 						   data);
 
+	if (!strcmp(tokens[tpos], "lookahead"))
+		return instr_hdr_lookahead_translate(p,
+						     action,
+						     &tokens[tpos],
+						     n_tokens - tpos,
+						     instr,
+						     data);
+
 	if (!strcmp(tokens[tpos], "emit"))
 		return instr_hdr_emit_translate(p,
 						action,
@@ -8905,6 +8965,7 @@ static instr_exec_t instruction_table[] = {
 	[INSTR_HDR_EXTRACT7] = instr_hdr_extract7_exec,
 	[INSTR_HDR_EXTRACT8] = instr_hdr_extract8_exec,
 	[INSTR_HDR_EXTRACT_M] = instr_hdr_extract_m_exec,
+	[INSTR_HDR_LOOKAHEAD] = instr_hdr_lookahead_exec,
 
 	[INSTR_HDR_EMIT] = instr_hdr_emit_exec,
 	[INSTR_HDR_EMIT_TX] = instr_hdr_emit_tx_exec,
-- 
2.17.1


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

* [dpdk-dev] [PATCH V2 5/5] examples/pipeline: add variable size headers example
  2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 4/5] pipeline: add header look-ahead instruction Cristian Dumitrescu
@ 2021-07-27 17:43   ` Cristian Dumitrescu
  2021-09-27  7:16   ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Thomas Monjalon
  4 siblings, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2021-07-27 17:43 UTC (permalink / raw)
  To: dev

Added the files to illustrate the variable size header usage.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 examples/pipeline/examples/varbit.cli  | 35 ++++++++++
 examples/pipeline/examples/varbit.spec | 95 ++++++++++++++++++++++++++
 2 files changed, 130 insertions(+)
 create mode 100644 examples/pipeline/examples/varbit.cli
 create mode 100644 examples/pipeline/examples/varbit.spec

diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
new file mode 100644
index 0000000000..0589e32c15
--- /dev/null
+++ b/examples/pipeline/examples/varbit.cli
@@ -0,0 +1,35 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+;
+; Customize the LINK parameters to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+
+link LINK0 dev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+link LINK1 dev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+link LINK2 dev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+link LINK3 dev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; PIPELINE0 setup.
+;
+pipeline PIPELINE0 create 0
+
+pipeline PIPELINE0 port in 0 link LINK0 rxq 0 bsz 32
+pipeline PIPELINE0 port in 1 link LINK1 rxq 0 bsz 32
+pipeline PIPELINE0 port in 2 link LINK2 rxq 0 bsz 32
+pipeline PIPELINE0 port in 3 link LINK3 rxq 0 bsz 32
+
+pipeline PIPELINE0 port out 0 link LINK0 txq 0 bsz 32
+pipeline PIPELINE0 port out 1 link LINK1 txq 0 bsz 32
+pipeline PIPELINE0 port out 2 link LINK2 txq 0 bsz 32
+pipeline PIPELINE0 port out 3 link LINK3 txq 0 bsz 32
+pipeline PIPELINE0 port out 4 sink none
+
+pipeline PIPELINE0 build ./examples/pipeline/examples/varbit.spec
+
+;
+; Pipelines-to-threads mapping.
+;
+thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/varbit.spec b/examples/pipeline/examples/varbit.spec
new file mode 100644
index 0000000000..cd49403fa9
--- /dev/null
+++ b/examples/pipeline/examples/varbit.spec
@@ -0,0 +1,95 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+; This simple example illustrates how to work with variable size headers. The assumed input packet
+; is Ethernet/IPv4/UDP, with the IPv4 header containing between 0 and 40 bytes of options. To locate
+; the start of the UDP header, the size of the IPv4 header needs to be detected first, which is done
+; by reading the first byte of the IPv4 header that carries the 4-bit Internet Header Length (IHL)
+; field; this read is done with the "lookahead" instruction, which does not advance the extract
+; pointer within the input packet buffer. Once the size of the IPv4 header options is known for the
+; current packet, the IPv4 header is extracted by using the two-argument "extract" instruction. Then
+; the UDP header is extracted and modified.
+
+//
+// Headers
+//
+struct ethernet_h {
+	bit<48> dst_addr
+	bit<48> src_addr
+	bit<16> ethertype
+}
+
+struct ipv4_top_h {
+	bit<8> ver_ihl
+}
+
+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
+	varbit<320> options
+}
+
+struct udp_h {
+	bit<16> src_port
+	bit<16> dst_port
+	bit<16> length
+	bit<16> checksum
+}
+
+header ethernet instanceof ethernet_h
+header ipv4_top instanceof ipv4_top_h
+header ipv4 instanceof ipv4_h
+header udp instanceof udp_h
+
+//
+// Meta-data
+//
+struct metadata_t {
+	bit<32> port
+	bit<32> options_size
+}
+
+metadata instanceof metadata_t
+
+//
+// Pipeline.
+//
+apply {
+	rx m.port
+
+	// Extract the fixed size Ethernet header.
+	extract h.ethernet
+
+	// Extract the variable size IPv4 header with up to 10 options.
+	lookahead h.ipv4_top
+	mov m.options_size h.ipv4_top.ver_ihl
+	and m.options_size 0xF
+	sub m.options_size 5
+	shl m.options_size 2
+	extract h.ipv4 m.options_size
+
+	// Extract the fixed size UDP header.
+	extract h.udp
+
+	// Modify the UDP header.
+	mov h.udp.src_port 0xAABB
+	mov h.udp.dst_port 0xCCDD
+
+	// Decide the output port.
+	xor m.port 1
+
+	// Emit the Ethernet, IPv4 and UDP headers.
+	emit h.ethernet
+	emit h.ipv4
+	emit h.udp
+
+	tx m.port
+}
-- 
2.17.1


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

* Re: [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers
  2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 5/5] examples/pipeline: add variable size headers example Cristian Dumitrescu
@ 2021-09-27  7:16   ` Thomas Monjalon
  4 siblings, 0 replies; 11+ messages in thread
From: Thomas Monjalon @ 2021-09-27  7:16 UTC (permalink / raw)
  To: Cristian Dumitrescu; +Cc: dev

27/07/2021 19:43, Cristian Dumitrescu:
> The emit instruction that is responsible for pushing headers into the
> output packet is now reading the header length from internal run-time
> structures as opposed to constant value from the instruction opcode.
> 
> Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>

Series applied, thanks.




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

end of thread, other threads:[~2021-09-27  7:16 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-27 16:36 [dpdk-dev] [PATCH 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
2021-07-27 16:36 ` [dpdk-dev] [PATCH 2/5] pipeline: add support " Cristian Dumitrescu
2021-07-27 16:36 ` [dpdk-dev] [PATCH 3/5] pipeline: add variable size headers extract instruction Cristian Dumitrescu
2021-07-27 16:36 ` [dpdk-dev] [PATCH 4/5] pipeline: add header look-ahead instruction Cristian Dumitrescu
2021-07-27 16:36 ` [dpdk-dev] [PATCH 5/5] examples/pipeline: add variable size headers example Cristian Dumitrescu
2021-07-27 17:43 ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Cristian Dumitrescu
2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 2/5] pipeline: add support " Cristian Dumitrescu
2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 3/5] pipeline: add variable size headers extract instruction Cristian Dumitrescu
2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 4/5] pipeline: add header look-ahead instruction Cristian Dumitrescu
2021-07-27 17:43   ` [dpdk-dev] [PATCH V2 5/5] examples/pipeline: add variable size headers example Cristian Dumitrescu
2021-09-27  7:16   ` [dpdk-dev] [PATCH V2 1/5] pipeline: prepare for variable size headers Thomas Monjalon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.