* [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).