<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Jun 6, 2023 at 10:05 AM Hu, Jiayu <<a href="mailto:jiayu.hu@intel.com">jiayu.hu@intel.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Kumara,<br>
<br>
The v3 patch is not complete and it seems to be a patch based on v2.<br>
In addition, did you test the code for tcp4 and tcp6 after your change?<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Will make sure that next patch contains the entire diff. <br></blockquote>

For others, please see replies inline.<br>
<br>
Thanks,<br>
Jiayu<br>
<br>
> -----Original Message-----<br>
> From: Kumara Parameshwaran <<a href="mailto:kumaraparamesh92@gmail.com" target="_blank">kumaraparamesh92@gmail.com</a>><br>
> Sent: Friday, June 2, 2023 2:34 PM<br>
> To: Hu, Jiayu <<a href="mailto:jiayu.hu@intel.com" target="_blank">jiayu.hu@intel.com</a>><br>
> Cc: <a href="mailto:dev@dpdk.org" target="_blank">dev@dpdk.org</a>; Kumara Parameshwaran<br>
> <<a href="mailto:kumaraparamesh92@gmail.com" target="_blank">kumaraparamesh92@gmail.com</a>><br>
> Subject: [PATCH v3] gro : ipv6 changes to support GRO for TCP/ipv6<br>
> <br>
> The patch adds GRO support for TCP/ipv6 packets. This does not include the<br>
> support for vxlan, udp ipv6 packets.<br>
> <br>
> Signed-off-by: Kumara Parameshwaran <<a href="mailto:kumaraparamesh92@gmail.com" target="_blank">kumaraparamesh92@gmail.com</a>><br>
> ---<br>
> v1:<br>
>       * Changes to support GRO for TCP/ipv6 packets. This does not<br>
> include<br>
>         vxlan changes.<br>
>       * The GRO is performed only for ipv6 packets that does not contain<br>
>        extension headers.<br>
>       * The logic for the TCP coalescing remains the same, in ipv6 header<br>
>         the source address, destination address, flow label, version fields<br>
>         are expected to be the same.<br>
>       * Re-organised the code to reuse certain tcp functions for both ipv4<br>
> and<br>
>         ipv6 flows.<br>
> v2:<br>
>       * Fix comments in gro_tcp6.h header file.<br>
> <br>
> v3:<br>
>       * Adderess review comments to fix code duplication for v4 and v6<br>
> <br>
>  lib/gro/gro_tcp.c        | 160 ++++++++++++++++++++++++<br>
>  lib/gro/gro_tcp.h        |  63 ++++++++++<br>
>  lib/gro/gro_tcp4.c       | 255 ++++++++++++---------------------------<br>
>  lib/gro/gro_tcp4.h       |  18 +--<br>
>  lib/gro/gro_tcp6.c       | 243 ++++++++++---------------------------<br>
>  lib/gro/gro_tcp6.h       |  31 +++--<br>
>  lib/gro/gro_vxlan_tcp4.c |  18 +--<br>
>  lib/gro/meson.build      |   1 +<br>
>  8 files changed, 396 insertions(+), 393 deletions(-)  create mode 100644<br>
> lib/gro/gro_tcp.c<br>
> <br>
> diff --git a/lib/gro/gro_tcp.c b/lib/gro/gro_tcp.c new file mode 100644 index<br>
> 0000000000..6a5aaada58<br>
> --- /dev/null<br>
> +++ b/lib/gro/gro_tcp.c<br>
> @@ -0,0 +1,160 @@<br>
> +/* SPDX-License-Identifier: BSD-3-Clause<br>
> + * Copyright(c) 2017 Intel Corporation<br>
> + */<br>
> +#include <rte_malloc.h><br>
> +#include <rte_mbuf.h><br>
> +#include <rte_ethdev.h><br>
> +<br>
> +#include "gro_tcp.h"<br>
> +<br>
> +static inline uint32_t<br>
> +find_an_empty_item(struct gro_tcp_item *items,<br>
> +     uint32_t table_size)<br>
> +{<br>
> +     uint32_t i;<br>
> +<br>
> +     for (i = 0; i < table_size; i++)<br>
> +             if (items[i].firstseg == NULL)<br>
> +                     return i;<br>
> +     return INVALID_ARRAY_INDEX;<br>
> +}<br>
> +<br>
> +uint32_t<br>
> +insert_new_tcp_item(struct rte_mbuf *pkt,<br>
> +             struct gro_tcp_item *items,<br>
> +             uint32_t *item_num,<br>
> +             uint32_t table_size,<br>
> +             uint64_t start_time,<br>
> +             uint32_t prev_idx,<br>
> +             uint32_t sent_seq,<br>
> +             uint16_t ip_id,<br>
> +             uint8_t is_atomic)<br>
> +{<br>
> +     uint32_t item_idx;<br>
> +<br>
> +     item_idx = find_an_empty_item(items, table_size);<br>
> +     if (item_idx == INVALID_ARRAY_INDEX)<br>
> +             return INVALID_ARRAY_INDEX;<br>
> +<br>
> +     items[item_idx].firstseg = pkt;<br>
> +     items[item_idx].lastseg = rte_pktmbuf_lastseg(pkt);<br>
> +     items[item_idx].start_time = start_time;<br>
> +     items[item_idx].next_pkt_idx = INVALID_ARRAY_INDEX;<br>
> +     items[item_idx].sent_seq = sent_seq;<br>
> +     items[item_idx].ip_id = ip_id;<br>
> +     items[item_idx].nb_merged = 1;<br>
> +     items[item_idx].is_atomic = is_atomic;<br>
> +     (*item_num) += 1;<br>
> +<br>
> +     /* if the previous packet exists, chain them together. */<br>
> +     if (prev_idx != INVALID_ARRAY_INDEX) {<br>
> +             items[item_idx].next_pkt_idx =<br>
> +                     items[prev_idx].next_pkt_idx;<br>
> +             items[prev_idx].next_pkt_idx = item_idx;<br>
> +     }<br>
> +<br>
> +     return item_idx;<br>
> +}<br>
> +<br>
> +uint32_t<br>
> +delete_tcp_item(struct gro_tcp_item *items, uint32_t item_idx,<br>
> +             uint32_t *item_num,<br>
> +             uint32_t prev_item_idx)<br>
> +{<br>
> +     uint32_t next_idx = items[item_idx].next_pkt_idx;<br>
> +<br>
> +     /* NULL indicates an empty item */<br>
> +     items[item_idx].firstseg = NULL;<br>
> +     (*item_num) -= 1;<br>
> +     if (prev_item_idx != INVALID_ARRAY_INDEX)<br>
> +             items[prev_item_idx].next_pkt_idx = next_idx;<br>
> +<br>
> +     return next_idx;<br>
> +}<br>
> +<br>
> +int32_t<br>
> +gro_tcp_reassemble(struct rte_mbuf *pkt,<br>
> +     void *tbl,<br>
> +     void *key,<br>
> +     int32_t tcp_dl,<br>
> +     struct gro_tcp_flow_ops *ops,<br>
> +     struct gro_tcp_item *items,<br>
> +     uint32_t *item_num,<br>
> +     uint32_t table_size,<br>
> +     uint16_t ip_id,<br>
> +     uint8_t is_atomic,<br>
> +     uint64_t start_time)<br>
<br>
In general, TCP4 and TCP6 share struct gro_tcp_item and have private flow structures,<br>
i.e., struct gro_tcp4/6_flow, and I like this abstraction. IMO, the code processing<br>
struct gro_tcp_item should be implemented as common functions shared by<br>
gro_tcp4.c and gro_tcp6.c. The code processing struct gro_tcp4/6_flow is tcp4 and<br>
tcp6 dependent and no need to abstract and share.<br>
<br>
In gro_tcp_reassemble(), it uses callback functions defined in struct gro_tcp_flow_ops<br>
to provide the different operations on struct gro_tcp4/6_flow. I don't think it's necessary<br>
for abstraction purpose as gro_tcp4/6_flows_ops implementations are alike and it also<br>
introduces extra cost on function calls.<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Sure, would make the changes accordingly.  <br></blockquote></blockquote><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"></blockquote>

The gro_tcp_reassemble() has two parts: the common part to process struct gro_tcp_item<br>
and the private part to process struct gro_tcp4/6_flow. I think a better way is to remove<br>
gro_tcp_reassemble() and struct gro_tcp_flow_ops, and implement the common part as<br>
an internal function in gro_tcp.c/gro_tcp.h, and make both gro_tcp4/6_reassemble()<br>
implement own private part and invoke the common function to process struct gro_tcp_item.<br>
With this change, there is no callback cost anymore and tcp4/6 can share the common function.<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Sure, got it. <br><br></blockquote></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"></blockquote>

> +{<br>
> +     uint32_t item_idx;<br>
> +     uint32_t cur_idx;<br>
> +     uint32_t prev_idx;<br>
> +     struct rte_tcp_hdr *tcp_hdr;<br>
> +     int cmp;<br>
> +     uint32_t sent_seq;<br>
> +<br>
> +     tcp_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_tcp_hdr *, pkt-<br>
> >l2_len + pkt->l3_len);<br>
> +     /*<br>
> +      * Don't process the packet which has FIN, SYN, RST, PSH, URG, ECE<br>
> +      * or CWR set.<br>
> +      */<br>
> +     if (tcp_hdr->tcp_flags != RTE_TCP_ACK_FLAG)<br>
> +             return -1;<br>
> +     sent_seq = rte_be_to_cpu_32(tcp_hdr->sent_seq);<br>
> +<br>
> +     ops->tcp_flow_key_init(key, tcp_hdr);<br>
> +<br>
> +     item_idx = ops->tcp_flow_lookup(tbl, key);<br>
> +     if (item_idx == INVALID_ARRAY_INDEX) {<br>
> +             item_idx = insert_new_tcp_item(pkt, items, item_num,<br>
> table_size, start_time,<br>
> +<br>
>       INVALID_ARRAY_INDEX, sent_seq, ip_id,<br>
> +                                                     is_atomic);<br>
> +             if (item_idx == INVALID_ARRAY_INDEX)<br>
> +                     return -1;<br>
> +             if (ops->tcp_flow_insert(tbl, key, item_idx) ==<br>
> +                     INVALID_ARRAY_INDEX) {<br>
> +                     /*<br>
> +                      * Fail to insert a new flow, so delete the<br>
> +                      * stored packet.<br>
> +                      */<br>
> +                     delete_tcp_item(items, item_idx, item_num,<br>
> INVALID_ARRAY_INDEX);<br>
> +                     return -1;<br>
> +             }<br>
> +             return 0;<br>
> +     }<br>
> +     /*<br>
> +      * Check all packets in the flow and try to find a neighbor for<br>
> +      * the input packet.<br>
> +      */<br>
> +     cur_idx = item_idx;<br>
> +     prev_idx = cur_idx;<br>
> +     do {<br>
> +             cmp = check_seq_option(&items[cur_idx], tcp_hdr,<br>
> +                             sent_seq, ip_id, pkt->l4_len, tcp_dl, 0,<br>
> +                             is_atomic);<br>
> +             if (cmp) {<br>
> +                     if (merge_two_tcp_packets(&items[cur_idx],<br>
> +                                             pkt, cmp, sent_seq, ip_id, 0))<br>
> +                             return 1;<br>
> +                     /*<br>
> +                      * Fail to merge the two packets, as the packet<br>
> +                      * length is greater than the max value. Store<br>
> +                      * the packet into the flow.<br>
> +                      */<br>
> +                     if (insert_new_tcp_item(pkt, items, item_num,<br>
> table_size, start_time, cur_idx,<br>
> +                                             sent_seq, ip_id, is_atomic) ==<br>
> +                                     INVALID_ARRAY_INDEX)<br>
> +                             return -1;<br>
> +                     return 0;<br>
> +             }<br>
> +             prev_idx = cur_idx;<br>
> +             cur_idx = items[cur_idx].next_pkt_idx;<br>
> +     } while (cur_idx != INVALID_ARRAY_INDEX);<br>
> +<br>
> +     /* Fail to find a neighbor, so store the packet into the flow. */<br>
> +     if (insert_new_tcp_item(pkt, items, item_num, table_size, start_time,<br>
> prev_idx, sent_seq,<br>
> +                             ip_id, is_atomic) == INVALID_ARRAY_INDEX)<br>
> +             return -1;<br>
> +<br>
> +     return 0;<br>
> +<br>
> +}<br>
> diff --git a/lib/gro/gro_tcp.h b/lib/gro/gro_tcp.h index<br>
> c5d248a022..202f485c18 100644<br>
> --- a/lib/gro/gro_tcp.h<br>
> +++ b/lib/gro/gro_tcp.h<br>
> @@ -1,6 +1,8 @@<br>
>  #ifndef _GRO_TCP_H_<br>
>  #define _GRO_TCP_H_<br>
> <br>
> +#define INVALID_ARRAY_INDEX 0xffffffffUL<br>
> +<br>
>  #include <rte_tcp.h><br>
> <br>
>  /*<br>
> @@ -14,6 +16,31 @@<br>
>  #define INVALID_TCP_HDRLEN(len) \<br>
>       (((len) < sizeof(struct rte_tcp_hdr)) || ((len) > MAX_TCP_HLEN))<br>
> <br>
> +struct gro_tcp_flow {<br>
> +     struct rte_ether_addr eth_saddr;<br>
> +     struct rte_ether_addr eth_daddr;<br>
> +     uint32_t recv_ack;<br>
> +     uint16_t src_port;<br>
> +     uint16_t dst_port;<br>
> +};<br>
> +<br>
> +#define ASSIGN_TCP_FLOW_KEY(k1, k2) \<br>
> +     rte_ether_addr_copy(&(k1->eth_saddr), &(k2->eth_saddr)); \<br>
> +     rte_ether_addr_copy(&(k1->eth_daddr), &(k2->eth_daddr)); \<br>
> +     k2->recv_ack = k1->recv_ack; \<br>
> +     k2->src_port = k1->src_port; \<br>
> +     k2->dst_port = k1->dst_port;<br>
<br>
For multiline macro, it's better to use do{...}while(0) to avoid unexpected errors<br>
in the future.<br>
<br>
> +<br>
> +typedef uint32_t (*gro_tcp_flow_lookup)(void *table, void *key);<br>
> +typedef uint32_t (*gro_tcp_flow_insert)(void *table, void *key,<br>
> +uint32_t item_idx); typedef void (*gro_tcp_flow_key_init)(void *key,<br>
> +struct rte_tcp_hdr *tcp_hdr);<br>
> +<br>
> +struct gro_tcp_flow_ops {<br>
> +     gro_tcp_flow_lookup tcp_flow_lookup;<br>
> +     gro_tcp_flow_insert tcp_flow_insert;<br>
> +     gro_tcp_flow_key_init tcp_flow_key_init; };<br>
> +<br>
>  struct gro_tcp_item {<br>
>       /*<br>
>        * The first MBUF segment of the packet. If the value @@ -44,6<br>
> +71,36 @@ struct gro_tcp_item {<br>
>       uint8_t is_atomic;<br>
>  };<br>
> <br>
> +uint32_t<br>
> +insert_new_tcp_item(struct rte_mbuf *pkt,<br>
> +             struct gro_tcp_item *items,<br>
> +             uint32_t *item_num,<br>
> +             uint32_t table_size,<br>
> +             uint64_t start_time,<br>
> +             uint32_t prev_idx,<br>
> +             uint32_t sent_seq,<br>
> +             uint16_t ip_id,<br>
> +             uint8_t is_atomic);<br>
> +<br>
> +uint32_t<br>
> +delete_tcp_item(struct gro_tcp_item *items,<br>
> +             uint32_t item_idx,<br>
> +             uint32_t *item_num,<br>
> +             uint32_t prev_item_idx);<br>
> +<br>
> +int32_t<br>
> +gro_tcp_reassemble(struct rte_mbuf *pkt,<br>
> +     void *tbl,<br>
> +     void *key,<br>
> +     int32_t tcp_dl,<br>
> +     struct gro_tcp_flow_ops *ops,<br>
> +     struct gro_tcp_item *items,<br>
> +     uint32_t *item_num,<br>
> +     uint32_t table_size,<br>
> +     uint16_t ip_id,<br>
> +     uint8_t is_atomic,<br>
> +     uint64_t start_time);<br>
> +<br>
>  /*<br>
>   * Merge two TCP packets without updating checksums.<br>
>   * If cmp is larger than 0, append the new packet to the @@ -152,4 +209,10<br>
> @@ check_seq_option(struct gro_tcp_item *item,<br>
>       return 0;<br>
>  }<br>
> <br>
> +static inline int<br>
> +is_same_tcp_flow(struct gro_tcp_flow *k1, struct gro_tcp_flow *k2) {<br>
> +     return (!memcmp(k1, k2, sizeof(struct gro_tcp_flow))); }<br>
> +<br>
>  #endif<br>
<br>
</blockquote></div></div>