DPDK patches and discussions
 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	[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	[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	[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	[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	[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	[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	[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	[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	[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	[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 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).