From patchwork Fri Aug 3 13:36:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrien Mazarguil X-Patchwork-Id: 43564 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 83F171B590; Fri, 3 Aug 2018 15:36:56 +0200 (CEST) Received: from mail-wr1-f68.google.com (mail-wr1-f68.google.com [209.85.221.68]) by dpdk.org (Postfix) with ESMTP id E36BB1B582 for ; Fri, 3 Aug 2018 15:36:54 +0200 (CEST) Received: by mail-wr1-f68.google.com with SMTP id f12-v6so5425034wrv.12 for ; Fri, 03 Aug 2018 06:36:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=6wind-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=uZWaJBCF8DXwsgM3fA8vgZOFwnIey6nXwDh+v4XMXKw=; b=NcrSZjtmAMca6mIiC/Q9wseH7QivaKdJc3dSa8aHWiR/hkPBvNm5TF3Z4R2bU6xLGr MnaQXQIAZW2oxnt5cC+HwT6VuqU9ey8qi0UAEPVj0Wvgtq+YpsxkOO+ta3HfZ4vLkzrx GcGZQd4TvRTZQkO2kc9GEfZjM7MZ7G2HsP9gJ/P14rBIIYcGt3KvCukyThbum+GiPJ4r 0null5Fy5wDd72S+MS7hz/5xVTZwpNcz5ba5Ka/tcHZEINQ6eQ725PIDzyeVhvP4Eo7c CQdbCVnMIGrRDAJqysbF92mk8HLTeIa+tFoN8QoxSFPDedjsaCwZcqrKD7Q6Rwd4zY2j ectA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=uZWaJBCF8DXwsgM3fA8vgZOFwnIey6nXwDh+v4XMXKw=; b=qFcd7+tUugh4UO31n8RL+svYmbe7UrPDUHMK5t7sow10FxZXbi1KI8cAR42atZ2+KQ bvCt9gGoG+9Lfo+IPh9eYFl55RsKUyEqfBD2Bm3X3A+MnjMgxssYxisypretEpzJltCa pOKW18uPG3lyh6+0uKeCazdJkse9evA5oVxJH9dQxBHQAjyaY840lhvVexmFP+cPo8Op MyB5VNBmywgX3GBldDdWwTI0bfj92rTg2vxzOBJQ3L6pmsXetUQ15fxTIRqodog6AZib p9KJi5JxTHQCTNdaUvfhrZiQyBIb1uQsy5eVzvTLSEXXdMG07ICwyuK4/pKL0sQ3jOS8 C32A== X-Gm-Message-State: AOUpUlFpz91uUwuMQohj1xllkN3HEeQF8bgCU48uV2eCnlzzkXlVudhF EsrM6JhEY6bnIzSCZiMy+gJr/Q== X-Google-Smtp-Source: AAOMgpfdvU2uHMS2JT6JsUP/X9DwNVfvVqxA3HSoPUPf+rq7SO00R1LxP+GvM1CM0+zsSGIxjr0yGg== X-Received: by 2002:adf:f790:: with SMTP id q16-v6mr2599913wrp.86.1533303414657; Fri, 03 Aug 2018 06:36:54 -0700 (PDT) Received: from 6wind.com (host.78.145.23.62.rev.coltfrance.com. [62.23.145.78]) by smtp.gmail.com with ESMTPSA id s2-v6sm2175249wrw.52.2018.08.03.06.36.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 03 Aug 2018 06:36:53 -0700 (PDT) Date: Fri, 3 Aug 2018 15:36:37 +0200 From: Adrien Mazarguil To: Ferruh Yigit Cc: dev@dpdk.org, Wenzhuo Lu , Jingjing Wu , Bernard Iremonger Message-ID: <20180803132032.29038-4-adrien.mazarguil@6wind.com> References: <20180803132032.29038-1-adrien.mazarguil@6wind.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20180803132032.29038-1-adrien.mazarguil@6wind.com> X-Mailer: git-send-email 2.11.0 Subject: [dpdk-dev] [PATCH v2 3/7] app/testpmd: rely on flow API conversion function X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" This commit replaces all local information about pattern items and actions as well as flow rule duplication code with calls to rte_flow_conv(). Signed-off-by: Adrien Mazarguil Cc: Wenzhuo Lu Cc: Jingjing Wu Cc: Bernard Iremonger --- app/test-pmd/config.c | 407 +++++++------------------------------------- app/test-pmd/testpmd.h | 7 +- 2 files changed, 67 insertions(+), 347 deletions(-) diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 14ccd6864..669be168b 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -984,324 +984,35 @@ port_mtu_set(portid_t port_id, uint16_t mtu) /* Generic flow management functions. */ -/** Generate flow_item[] entry. */ -#define MK_FLOW_ITEM(t, s) \ - [RTE_FLOW_ITEM_TYPE_ ## t] = { \ - .name = # t, \ - .size = s, \ - } - -/** Information about known flow pattern items. */ -static const struct { - const char *name; - size_t size; -} flow_item[] = { - MK_FLOW_ITEM(END, 0), - MK_FLOW_ITEM(VOID, 0), - MK_FLOW_ITEM(INVERT, 0), - MK_FLOW_ITEM(ANY, sizeof(struct rte_flow_item_any)), - MK_FLOW_ITEM(PF, 0), - MK_FLOW_ITEM(VF, sizeof(struct rte_flow_item_vf)), - MK_FLOW_ITEM(PHY_PORT, sizeof(struct rte_flow_item_phy_port)), - MK_FLOW_ITEM(PORT_ID, sizeof(struct rte_flow_item_port_id)), - MK_FLOW_ITEM(RAW, sizeof(struct rte_flow_item_raw)), - MK_FLOW_ITEM(ETH, sizeof(struct rte_flow_item_eth)), - MK_FLOW_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)), - MK_FLOW_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)), - MK_FLOW_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)), - MK_FLOW_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)), - MK_FLOW_ITEM(UDP, sizeof(struct rte_flow_item_udp)), - MK_FLOW_ITEM(TCP, sizeof(struct rte_flow_item_tcp)), - MK_FLOW_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)), - MK_FLOW_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)), - MK_FLOW_ITEM(E_TAG, sizeof(struct rte_flow_item_e_tag)), - MK_FLOW_ITEM(NVGRE, sizeof(struct rte_flow_item_nvgre)), - MK_FLOW_ITEM(MPLS, sizeof(struct rte_flow_item_mpls)), - MK_FLOW_ITEM(GRE, sizeof(struct rte_flow_item_gre)), - MK_FLOW_ITEM(FUZZY, sizeof(struct rte_flow_item_fuzzy)), - MK_FLOW_ITEM(GTP, sizeof(struct rte_flow_item_gtp)), - MK_FLOW_ITEM(GTPC, sizeof(struct rte_flow_item_gtp)), - MK_FLOW_ITEM(GTPU, sizeof(struct rte_flow_item_gtp)), - MK_FLOW_ITEM(GENEVE, sizeof(struct rte_flow_item_geneve)), - MK_FLOW_ITEM(VXLAN_GPE, sizeof(struct rte_flow_item_vxlan_gpe)), - MK_FLOW_ITEM(ARP_ETH_IPV4, sizeof(struct rte_flow_item_arp_eth_ipv4)), - MK_FLOW_ITEM(IPV6_EXT, sizeof(struct rte_flow_item_ipv6_ext)), - MK_FLOW_ITEM(ICMP6, sizeof(struct rte_flow_item_icmp6)), - MK_FLOW_ITEM(ICMP6_ND_NS, sizeof(struct rte_flow_item_icmp6_nd_ns)), - MK_FLOW_ITEM(ICMP6_ND_NA, sizeof(struct rte_flow_item_icmp6_nd_na)), - MK_FLOW_ITEM(ICMP6_ND_OPT, sizeof(struct rte_flow_item_icmp6_nd_opt)), - MK_FLOW_ITEM(ICMP6_ND_OPT_SLA_ETH, - sizeof(struct rte_flow_item_icmp6_nd_opt_sla_eth)), - MK_FLOW_ITEM(ICMP6_ND_OPT_TLA_ETH, - sizeof(struct rte_flow_item_icmp6_nd_opt_tla_eth)), -}; - -/** Pattern item specification types. */ -enum item_spec_type { - ITEM_SPEC, - ITEM_LAST, - ITEM_MASK, -}; - -/** Compute storage space needed by item specification and copy it. */ -static size_t -flow_item_spec_copy(void *buf, const struct rte_flow_item *item, - enum item_spec_type type) -{ - size_t size = 0; - const void *data = - type == ITEM_SPEC ? item->spec : - type == ITEM_LAST ? item->last : - type == ITEM_MASK ? item->mask : - NULL; - - if (!item->spec || !data) - goto empty; - switch (item->type) { - union { - const struct rte_flow_item_raw *raw; - } spec; - union { - const struct rte_flow_item_raw *raw; - } last; - union { - const struct rte_flow_item_raw *raw; - } mask; - union { - const struct rte_flow_item_raw *raw; - } src; - union { - struct rte_flow_item_raw *raw; - } dst; - size_t off; - - case RTE_FLOW_ITEM_TYPE_RAW: - spec.raw = item->spec; - last.raw = item->last ? item->last : item->spec; - mask.raw = item->mask ? item->mask : &rte_flow_item_raw_mask; - src.raw = data; - dst.raw = buf; - off = RTE_ALIGN_CEIL(sizeof(struct rte_flow_item_raw), - sizeof(*src.raw->pattern)); - if (type == ITEM_SPEC || - (type == ITEM_MASK && - ((spec.raw->length & mask.raw->length) >= - (last.raw->length & mask.raw->length)))) - size = spec.raw->length & mask.raw->length; - else - size = last.raw->length & mask.raw->length; - size = off + size * sizeof(*src.raw->pattern); - if (dst.raw) { - memcpy(dst.raw, src.raw, sizeof(*src.raw)); - dst.raw->pattern = memcpy((uint8_t *)dst.raw + off, - src.raw->pattern, - size - off); - } - break; - default: - size = flow_item[item->type].size; - if (buf) - memcpy(buf, data, size); - break; - } -empty: - return RTE_ALIGN_CEIL(size, sizeof(double)); -} - -/** Generate flow_action[] entry. */ -#define MK_FLOW_ACTION(t, s) \ - [RTE_FLOW_ACTION_TYPE_ ## t] = { \ - .name = # t, \ - .size = s, \ - } - -/** Information about known flow actions. */ -static const struct { - const char *name; - size_t size; -} flow_action[] = { - MK_FLOW_ACTION(END, 0), - MK_FLOW_ACTION(VOID, 0), - MK_FLOW_ACTION(PASSTHRU, 0), - MK_FLOW_ACTION(MARK, sizeof(struct rte_flow_action_mark)), - MK_FLOW_ACTION(FLAG, 0), - MK_FLOW_ACTION(QUEUE, sizeof(struct rte_flow_action_queue)), - MK_FLOW_ACTION(DROP, 0), - MK_FLOW_ACTION(COUNT, sizeof(struct rte_flow_action_count)), - MK_FLOW_ACTION(RSS, sizeof(struct rte_flow_action_rss)), - MK_FLOW_ACTION(PF, 0), - MK_FLOW_ACTION(VF, sizeof(struct rte_flow_action_vf)), - MK_FLOW_ACTION(PHY_PORT, sizeof(struct rte_flow_action_phy_port)), - MK_FLOW_ACTION(PORT_ID, sizeof(struct rte_flow_action_port_id)), - MK_FLOW_ACTION(METER, sizeof(struct rte_flow_action_meter)), - MK_FLOW_ACTION(OF_SET_MPLS_TTL, - sizeof(struct rte_flow_action_of_set_mpls_ttl)), - MK_FLOW_ACTION(OF_DEC_MPLS_TTL, 0), - MK_FLOW_ACTION(OF_SET_NW_TTL, - sizeof(struct rte_flow_action_of_set_nw_ttl)), - MK_FLOW_ACTION(OF_DEC_NW_TTL, 0), - MK_FLOW_ACTION(OF_COPY_TTL_OUT, 0), - MK_FLOW_ACTION(OF_COPY_TTL_IN, 0), - MK_FLOW_ACTION(OF_POP_VLAN, 0), - MK_FLOW_ACTION(OF_PUSH_VLAN, - sizeof(struct rte_flow_action_of_push_vlan)), - MK_FLOW_ACTION(OF_SET_VLAN_VID, - sizeof(struct rte_flow_action_of_set_vlan_vid)), - MK_FLOW_ACTION(OF_SET_VLAN_PCP, - sizeof(struct rte_flow_action_of_set_vlan_pcp)), - MK_FLOW_ACTION(OF_POP_MPLS, - sizeof(struct rte_flow_action_of_pop_mpls)), - MK_FLOW_ACTION(OF_PUSH_MPLS, - sizeof(struct rte_flow_action_of_push_mpls)), -}; - -/** Compute storage space needed by action configuration and copy it. */ -static size_t -flow_action_conf_copy(void *buf, const struct rte_flow_action *action) -{ - size_t size = 0; - - if (!action->conf) - goto empty; - switch (action->type) { - union { - const struct rte_flow_action_rss *rss; - } src; - union { - struct rte_flow_action_rss *rss; - } dst; - size_t off; - - case RTE_FLOW_ACTION_TYPE_RSS: - src.rss = action->conf; - dst.rss = buf; - off = 0; - if (dst.rss) - *dst.rss = (struct rte_flow_action_rss){ - .func = src.rss->func, - .level = src.rss->level, - .types = src.rss->types, - .key_len = src.rss->key_len, - .queue_num = src.rss->queue_num, - }; - off += sizeof(*src.rss); - if (src.rss->key_len) { - off = RTE_ALIGN_CEIL(off, sizeof(double)); - size = sizeof(*src.rss->key) * src.rss->key_len; - if (dst.rss) - dst.rss->key = memcpy - ((void *)((uintptr_t)dst.rss + off), - src.rss->key, size); - off += size; - } - if (src.rss->queue_num) { - off = RTE_ALIGN_CEIL(off, sizeof(double)); - size = sizeof(*src.rss->queue) * src.rss->queue_num; - if (dst.rss) - dst.rss->queue = memcpy - ((void *)((uintptr_t)dst.rss + off), - src.rss->queue, size); - off += size; - } - size = off; - break; - default: - size = flow_action[action->type].size; - if (buf) - memcpy(buf, action->conf, size); - break; - } -empty: - return RTE_ALIGN_CEIL(size, sizeof(double)); -} - /** Generate a port_flow entry from attributes/pattern/actions. */ static struct port_flow * port_flow_new(const struct rte_flow_attr *attr, const struct rte_flow_item *pattern, - const struct rte_flow_action *actions) -{ - const struct rte_flow_item *item; - const struct rte_flow_action *action; - struct port_flow *pf = NULL; - size_t tmp; - size_t off1 = 0; - size_t off2 = 0; - int err = ENOTSUP; - -store: - item = pattern; - if (pf) - pf->pattern = (void *)&pf->data[off1]; - do { - struct rte_flow_item *dst = NULL; - - if ((unsigned int)item->type >= RTE_DIM(flow_item) || - !flow_item[item->type].name) - goto notsup; - if (pf) - dst = memcpy(pf->data + off1, item, sizeof(*item)); - off1 += sizeof(*item); - if (item->spec) { - if (pf) - dst->spec = pf->data + off2; - off2 += flow_item_spec_copy - (pf ? pf->data + off2 : NULL, item, ITEM_SPEC); - } - if (item->last) { - if (pf) - dst->last = pf->data + off2; - off2 += flow_item_spec_copy - (pf ? pf->data + off2 : NULL, item, ITEM_LAST); - } - if (item->mask) { - if (pf) - dst->mask = pf->data + off2; - off2 += flow_item_spec_copy - (pf ? pf->data + off2 : NULL, item, ITEM_MASK); - } - off2 = RTE_ALIGN_CEIL(off2, sizeof(double)); - } while ((item++)->type != RTE_FLOW_ITEM_TYPE_END); - off1 = RTE_ALIGN_CEIL(off1, sizeof(double)); - action = actions; - if (pf) - pf->actions = (void *)&pf->data[off1]; - do { - struct rte_flow_action *dst = NULL; - - if ((unsigned int)action->type >= RTE_DIM(flow_action) || - !flow_action[action->type].name) - goto notsup; - if (pf) - dst = memcpy(pf->data + off1, action, sizeof(*action)); - off1 += sizeof(*action); - if (action->conf) { - if (pf) - dst->conf = pf->data + off2; - off2 += flow_action_conf_copy - (pf ? pf->data + off2 : NULL, action); - } - off2 = RTE_ALIGN_CEIL(off2, sizeof(double)); - } while ((action++)->type != RTE_FLOW_ACTION_TYPE_END); - if (pf != NULL) + const struct rte_flow_action *actions, + struct rte_flow_error *error) +{ + const struct rte_flow_conv_rule rule = { + .attr_ro = attr, + .pattern_ro = pattern, + .actions_ro = actions, + }; + struct port_flow *pf; + int ret; + + ret = rte_flow_conv(RTE_FLOW_CONV_OP_RULE, NULL, 0, &rule, error); + if (ret < 0) + return NULL; + pf = calloc(1, offsetof(struct port_flow, rule) + ret); + if (!pf) { + rte_flow_error_set + (error, errno, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "calloc() failed"); + return NULL; + } + if (rte_flow_conv(RTE_FLOW_CONV_OP_RULE, &pf->rule, ret, &rule, + error) >= 0) return pf; - off1 = RTE_ALIGN_CEIL(off1, sizeof(double)); - tmp = RTE_ALIGN_CEIL(offsetof(struct port_flow, data), sizeof(double)); - pf = calloc(1, tmp + off1 + off2); - if (pf == NULL) - err = errno; - else { - *pf = (const struct port_flow){ - .size = tmp + off1 + off2, - .attr = *attr, - }; - tmp -= offsetof(struct port_flow, data); - off2 = tmp + off1; - off1 = tmp; - goto store; - } -notsup: - rte_errno = err; + free(pf); return NULL; } @@ -1391,13 +1102,10 @@ port_flow_create(portid_t port_id, id = port->flow_list->id + 1; } else id = 0; - pf = port_flow_new(attr, pattern, actions); + pf = port_flow_new(attr, pattern, actions, &error); if (!pf) { - int err = rte_errno; - - printf("Cannot allocate flow: %s\n", rte_strerror(err)); rte_flow_destroy(port_id, flow, NULL); - return -err; + return port_flow_complain(&error); } pf->next = port->flow_list; pf->id = id; @@ -1489,6 +1197,7 @@ port_flow_query(portid_t port_id, uint32_t rule, union { struct rte_flow_query_count count; } query; + int ret; if (port_id_is_invalid(port_id, ENABLED_WARN) || port_id == (portid_t)RTE_PORT_ALL) @@ -1501,11 +1210,10 @@ port_flow_query(portid_t port_id, uint32_t rule, printf("Flow rule #%u not found\n", rule); return -ENOENT; } - if ((unsigned int)action->type >= RTE_DIM(flow_action) || - !flow_action[action->type].name) - name = "unknown"; - else - name = flow_action[action->type].name; + ret = rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR, + &name, sizeof(name), action, &error); + if (ret < 0) + return port_flow_complain(&error); switch (action->type) { case RTE_FLOW_ACTION_TYPE_COUNT: break; @@ -1558,48 +1266,63 @@ port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n]) /* Sort flows by group, priority and ID. */ for (pf = port->flow_list; pf != NULL; pf = pf->next) { struct port_flow **tmp; + const struct rte_flow_attr *curr = pf->rule.attr; if (n) { /* Filter out unwanted groups. */ for (i = 0; i != n; ++i) - if (pf->attr.group == group[i]) + if (curr->group == group[i]) break; if (i == n) continue; } - tmp = &list; - while (*tmp && - (pf->attr.group > (*tmp)->attr.group || - (pf->attr.group == (*tmp)->attr.group && - pf->attr.priority > (*tmp)->attr.priority) || - (pf->attr.group == (*tmp)->attr.group && - pf->attr.priority == (*tmp)->attr.priority && - pf->id > (*tmp)->id))) - tmp = &(*tmp)->tmp; + for (tmp = &list; *tmp; tmp = &(*tmp)->tmp) { + const struct rte_flow_attr *comp = (*tmp)->rule.attr; + + if (curr->group > comp->group || + (curr->group == comp->group && + curr->priority > comp->priority) || + (curr->group == comp->group && + curr->priority == comp->priority && + pf->id > (*tmp)->id)) + continue; + break; + } pf->tmp = *tmp; *tmp = pf; } printf("ID\tGroup\tPrio\tAttr\tRule\n"); for (pf = list; pf != NULL; pf = pf->tmp) { - const struct rte_flow_item *item = pf->pattern; - const struct rte_flow_action *action = pf->actions; + const struct rte_flow_item *item = pf->rule.pattern; + const struct rte_flow_action *action = pf->rule.actions; + const char *name; printf("%" PRIu32 "\t%" PRIu32 "\t%" PRIu32 "\t%c%c%c\t", pf->id, - pf->attr.group, - pf->attr.priority, - pf->attr.ingress ? 'i' : '-', - pf->attr.egress ? 'e' : '-', - pf->attr.transfer ? 't' : '-'); + pf->rule.attr->group, + pf->rule.attr->priority, + pf->rule.attr->ingress ? 'i' : '-', + pf->rule.attr->egress ? 'e' : '-', + pf->rule.attr->transfer ? 't' : '-'); while (item->type != RTE_FLOW_ITEM_TYPE_END) { + if (rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_NAME_PTR, + &name, sizeof(name), + (void *)(uintptr_t)item->type, + NULL) <= 0) + name = "[UNKNOWN]"; if (item->type != RTE_FLOW_ITEM_TYPE_VOID) - printf("%s ", flow_item[item->type].name); + printf("%s ", name); ++item; } printf("=>"); while (action->type != RTE_FLOW_ACTION_TYPE_END) { + if (rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR, + &name, sizeof(name), + (void *)(uintptr_t)action->type, + NULL) <= 0) + name = "[UNKNOWN]"; if (action->type != RTE_FLOW_ACTION_TYPE_VOID) - printf(" %s", flow_action[action->type].name); + printf(" %s", name); ++action; } printf("\n"); diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index a1f661472..11afb487f 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -124,15 +124,12 @@ struct fwd_stream { /** Descriptor for a single flow. */ struct port_flow { - size_t size; /**< Allocated space including data[]. */ struct port_flow *next; /**< Next flow in list. */ struct port_flow *tmp; /**< Temporary linking. */ uint32_t id; /**< Flow rule ID. */ struct rte_flow *flow; /**< Opaque flow object returned by PMD. */ - struct rte_flow_attr attr; /**< Attributes. */ - struct rte_flow_item *pattern; /**< Pattern. */ - struct rte_flow_action *actions; /**< Actions. */ - uint8_t data[]; /**< Storage for pattern/actions. */ + struct rte_flow_conv_rule rule; /* Saved flow rule description. */ + uint8_t data[]; /**< Storage for flow rule description */ }; #ifdef SOFTNIC