LCOV - code coverage report
Current view: top level - root/contrail/vrouter/dpdk - vr_dpdk_gro.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 42 373 11.3 %
Date: 2026-08-03 02:19:58 Functions: 4 7 57.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2017 Juniper Networks.
       3             :  *
       4             :  * This program is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU General Public License as
       6             :  * published by the Free Software Foundation version 2.
       7             :  *
       8             :  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
       9             :  * kind, whether express or implied; without even the implied warranty
      10             :  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      11             :  * GNU General Public License for more details.
      12             :  *
      13             :  * vr_dpdk_gro.c -- TCP Offloads on the receiver
      14             :  *
      15             :  * This is an adaptation of FreeBSD's tcp_lro.c.
      16             :  * Copyright (c) 2007, Myricom Inc.
      17             :  * Copyright (c) 2008, Intel Corporation.
      18             :  * Copyright (c) 2012 The FreeBSD Foundation
      19             :  * All rights reserved.
      20             :  * BSD LICENSE
      21             :  *
      22             :  */
      23             : 
      24             : #include "vr_dpdk.h"
      25             : #include "vr_dpdk_netlink.h"
      26             : #include "vr_dpdk_usocket.h"
      27             : #include "vr_dpdk_virtio.h"
      28             : #include "vr_dpdk_gro.h"
      29             : #include "vr_packet.h"
      30             : #include "vr_datapath.h"
      31             : 
      32             : #include <netinet/ip.h>
      33             : #include <netinet/tcp.h>
      34             : 
      35             : #include <rte_errno.h>
      36             : #include <rte_ethdev.h>
      37             : #include <rte_ip_frag.h>
      38             : #include <rte_ip.h>
      39             : #include <rte_port_ethdev.h>
      40             : 
      41             : #include <rte_hash.h>
      42             : #include <rte_cycles.h>
      43             : #include <rte_malloc.h>
      44             : #include <rte_tcp.h>
      45             : #include <rte_eth_bond.h>
      46             : 
      47             : #define GRO_TABLE_NAME_LEN 45
      48             : 
      49             : int
      50         105 : vr_dpdk_gro_init(unsigned lcore_id, struct vr_dpdk_lcore *lcore)
      51             : {
      52             :     /* Parameters used for hash tables. Name is set later. */
      53         105 :     struct rte_hash_parameters gro_tbl_params_v4 = {
      54             :         .entries = 1<<3,
      55             :         .key_len = sizeof(struct vr_dpdk_gro_flow_key_v4),
      56             :         .hash_func = rte_jhash,
      57             :         .hash_func_init_val = 0,
      58             :         .socket_id = 0, /* TODO */
      59             :     };
      60         105 :     struct rte_hash_parameters gro_tbl_params_v6 = {
      61             :         .entries = 1<<3,
      62             :         .key_len = sizeof(struct vr_dpdk_gro_flow_key_v6),
      63             :         .hash_func = rte_jhash,
      64             :         .hash_func_init_val = 0,
      65             :         .socket_id = 0, /* TODO */
      66             :     };
      67             : 
      68             :     char gro_v4_tbl_name[GRO_TABLE_NAME_LEN], gro_v6_tbl_name[GRO_TABLE_NAME_LEN];
      69             : 
      70         105 :     snprintf(gro_v4_tbl_name, sizeof(gro_v4_tbl_name), "GRO_Table_v4_lcore_%d", lcore_id);
      71         105 :     gro_tbl_params_v4.name = gro_v4_tbl_name;
      72         105 :     lcore->gro.gro_tbl_v4_handle = rte_hash_create(&gro_tbl_params_v4);
      73         106 :     if (lcore->gro.gro_tbl_v4_handle == NULL) {
      74           0 :         RTE_LOG(ERR, VROUTER, "Warning! %s: lcore:%d, Unable to allocate memory "
      75             :                           "for IPv4 GRO table\n", __func__, lcore_id);
      76           0 :         return -ENOMEM;
      77             :     }
      78         106 :     snprintf(gro_v6_tbl_name, sizeof(gro_v6_tbl_name), "GRO_Table_v6_lcore_%d", lcore_id);
      79         106 :     gro_tbl_params_v6.name = gro_v6_tbl_name;
      80         106 :     lcore->gro.gro_tbl_v6_handle = rte_hash_create(&gro_tbl_params_v6);
      81         106 :     if (lcore->gro.gro_tbl_v6_handle == NULL) {
      82           0 :         RTE_LOG(ERR, VROUTER, "Warning! %s: lcore:%d, Unable to allocate memory "
      83             :                           "for IPv6 GRO table\n", __func__, lcore_id);
      84           0 :         return -ENOMEM;
      85             :     }
      86         106 :     return 0;
      87             : }
      88             : 
      89             : 
      90             : static uint16_t
      91           0 : dpdk_gro_csum_tcph(struct vr_tcp *tcph)
      92             : {
      93             :     uint32_t ch;
      94             :     uint16_t *p, l;
      95             : 
      96           0 :     ch = tcph->tcp_csum = 0x0000;
      97           0 :     l = VR_TCP_OFFSET(tcph->tcp_offset_r_flags);
      98           0 :     p = (uint16_t *)tcph;
      99           0 :     while (l > 0) {
     100           0 :         ch += (*p);
     101           0 :         p++;
     102           0 :         ch += (*p);
     103           0 :         p++;
     104           0 :         l--;
     105             :     }
     106           0 :     while (ch > 0xffff)
     107           0 :         ch = (ch >> 16) + (ch & 0xffff);
     108             : 
     109           0 :     return (ch & 0xffff);
     110             : }
     111             : 
     112             : static uint16_t
     113           0 : dpdk_gro_rx_csum_fixup(struct gro_entry *entry, uint8_t is_ipv6, void *nw_hdr,
     114             :                     struct vr_tcp *tcph, uint16_t tcp_data_len, uint16_t csum)
     115             : {
     116             :     uint32_t c;
     117             :     uint16_t cs;
     118             : 
     119           0 :     c = csum;
     120             : 
     121             :     /* For first packet, remove just the length from checksum
     122             :      * For subsequent packets, remove the checksum of full TCP header
     123             :      */
     124           0 :     if (!is_ipv6) {
     125           0 :         struct vr_ip* ip4 = nw_hdr;
     126           0 :         if (entry->mbuf_cnt == 1)
     127           0 :             cs = rte_be_to_cpu_16(ip4->ip_len) - sizeof(*ip4);
     128             :         else
     129           0 :             cs = rte_be_to_cpu_16(rte_ipv4_phdr_cksum((const struct rte_ipv4_hdr*)ip4,0));
     130             :     } else {
     131           0 :         struct vr_ip6* ip6 = nw_hdr;
     132           0 :         if (entry->mbuf_cnt == 1)
     133           0 :             cs = rte_be_to_cpu_16(ip6->ip6_plen);
     134             :         else
     135           0 :             cs = rte_be_to_cpu_16(rte_ipv6_phdr_cksum((const struct rte_ipv6_hdr*)ip6,0));
     136             :     }
     137             : 
     138           0 :     cs = ~cs;
     139           0 :     c += cs;
     140             : 
     141             :     /* Remove TCP header csum. */
     142           0 :     cs = rte_be_to_cpu_16(~dpdk_gro_csum_tcph(tcph));
     143           0 :     c += cs;
     144           0 :     while (c > 0xffff)
     145           0 :         c = (c >> 16) + (c & 0xffff);
     146             : 
     147           0 :     return (c & 0xffff);
     148             : }
     149             : 
     150             : static int
     151           0 : dpdk_gro_flush(struct gro_ctrl *gro, void *key, struct gro_entry *entry)
     152             : {
     153             :     int ret;
     154           0 :     struct vr_tcp *tcph = NULL;
     155           0 :     struct vr_interface *vif = NULL;
     156           0 :     struct vrouter *router = vrouter_get(0);
     157           0 :     struct rte_mbuf *m = entry->mbuf_head;
     158           0 :     struct vr_packet *pkt = vr_dpdk_mbuf_to_pkt(m);
     159             :     struct vr_forwarding_md fmd;
     160             :     uint32_t cl;
     161             :     uint16_t c;
     162             :     unsigned short drop_reason;
     163             :     struct vr_nexthop *nh;
     164             : 
     165           0 :     if (entry->mbuf_cnt > 1) {
     166             : 
     167           0 :         if (!entry->is_ipv6) {
     168             :             /* Fix IP header checksum for new length. */
     169           0 :             struct vr_ip *ip4 = entry->le_ip4;
     170           0 :             c = rte_be_to_cpu_16(~ip4->ip_csum);
     171           0 :             cl = c;
     172           0 :             c = rte_be_to_cpu_16(~ip4->ip_len);
     173           0 :             cl += c + entry->p_len;
     174           0 :             while (cl > 0xffff)
     175           0 :                 cl = (cl >> 16) + (cl & 0xffff);
     176           0 :             c = cl;
     177           0 :             ip4->ip_csum = rte_cpu_to_be_16(~c);
     178             : 
     179           0 :             entry->le_ip4->ip_len = rte_cpu_to_be_16(entry->p_len);
     180           0 :             tcph = (struct vr_tcp *)(entry->le_ip4 + 1);
     181           0 :             m->ol_flags |= PKT_RX_GSO_TCP4;
     182             :         } else {
     183           0 :             struct vr_ip6 *ip6 = entry->le_ip6;
     184           0 :             ip6->ip6_plen = rte_cpu_to_be_16(entry->p_len - sizeof(struct vr_ip6));
     185           0 :             tcph = (struct vr_tcp *)(entry->le_ip6 + 1);
     186           0 :             m->ol_flags |= PKT_RX_GSO_TCP6;
     187             :         }
     188           0 :         m->pkt_len =
     189           0 :                entry->p_len + pkt_get_network_header_off(pkt) - pkt_head_space(pkt);
     190           0 :         m->nb_segs = entry->mbuf_cnt;
     191           0 :         m->tso_segsz = entry->seg_sz;
     192             : 
     193             :         /* Incorporate the latest ACK into the TCP header. */
     194           0 :         tcph->tcp_ack = rte_cpu_to_be_32(entry->ack_seq);
     195           0 :         tcph->tcp_win = rte_cpu_to_be_16(entry->window);
     196             :         /* Incorporate latest timestamp into the TCP header. */
     197           0 :         if (entry->timestamp != 0) {
     198             :             uint32_t *ts_ptr;
     199             : 
     200           0 :             ts_ptr = (uint32_t *)(tcph + 1);
     201           0 :             ts_ptr[1] = rte_cpu_to_be_32(entry->tsval);
     202           0 :             ts_ptr[2] = rte_cpu_to_be_32(entry->tsecr);
     203             :         }
     204             : 
     205             :         /* Update the TCP header checksum. */
     206           0 :         if (!entry->is_ipv6)
     207           0 :             entry->ulp_csum += entry->p_len - sizeof(struct vr_ip);
     208             :         else
     209           0 :             entry->ulp_csum += entry->p_len - sizeof(struct vr_ip6);
     210             : 
     211           0 :         entry->ulp_csum += rte_cpu_to_be_16(dpdk_gro_csum_tcph(tcph));
     212           0 :         while (entry->ulp_csum > 0xffff)
     213           0 :             entry->ulp_csum = (entry->ulp_csum >> 16) +
     214           0 :                 (entry->ulp_csum & 0xffff);
     215           0 :         tcph->tcp_csum = entry->ulp_csum & 0xffff;
     216           0 :         tcph->tcp_csum = rte_cpu_to_be_16(~tcph->tcp_csum);
     217             :     }
     218             : 
     219           0 :     gro->gro_queued += entry->mbuf_cnt;
     220           0 :     gro->gro_flushed++;
     221             : 
     222             :     /* Delete flow entry from hash table */
     223           0 :     if (!entry->is_ipv6)
     224           0 :         ret = rte_hash_del_key(gro->gro_tbl_v4_handle, key);
     225             :     else
     226           0 :         ret = rte_hash_del_key(gro->gro_tbl_v6_handle, key);
     227           0 :     ASSERT(ret >= 0);
     228             : 
     229           0 :     nh = __vrouter_get_nexthop(router, entry->nh_id);
     230           0 :     if (!nh) {
     231           0 :         drop_reason = VP_DROP_INVALID_NH;
     232           0 :         goto drop;
     233             :     }
     234           0 :     vif = nh->nh_dev;
     235           0 :     if ((vif == NULL) || (!vif_is_virtual(vif))) {
     236           0 :         drop_reason = VP_DROP_INVALID_IF;
     237           0 :         goto drop;
     238             :     }
     239             : 
     240             :     /* Validate the gen_id of the vif */
     241           0 :     if (unlikely((vif->vif_idx != entry->dst_vif_idx) ||
     242             :                  (vif->vif_gen != entry->dst_vif_gen))) {
     243           0 :         drop_reason = VP_DROP_INVALID_IF;
     244           0 :         goto drop;
     245             :     }
     246             : 
     247           0 :     if (nh->nh_family == AF_BRIDGE) {
     248           0 :         if (!rte_pktmbuf_prepend(m, VR_ETHER_HLEN)) {
     249           0 :             drop_reason = VP_DROP_INVALID_PACKET;
     250           0 :             goto drop;
     251             :         }
     252             :     }
     253             : 
     254           0 :     pkt = vr_dpdk_packet_get(m, NULL);
     255           0 :     if (!pkt) {
     256           0 :         drop_reason = VP_DROP_INVALID_IF;
     257           0 :         goto drop;
     258             :     }
     259             : 
     260             :     /*
     261             :      * since vif was not available when we did vr_dpdk_packet_get, set vif
     262             :      * manually here
     263             :      */
     264           0 :     vif = __vrouter_get_interface(router, entry->src_vif_idx);
     265           0 :     if (!vif) {
     266           0 :         drop_reason = VP_DROP_INVALID_IF;
     267           0 :         goto drop;
     268             :     }
     269             : 
     270             :     /* Validate the gen_id of the vif */
     271           0 :     if (unlikely(vif->vif_gen != entry->src_vif_gen)) {
     272           0 :         drop_reason = VP_DROP_INVALID_IF;
     273           0 :         goto drop;
     274             :     }
     275             : 
     276           0 :     pkt->vp_if = vif;
     277             : 
     278           0 :     vr_init_forwarding_md(&fmd);
     279           0 :     fmd.fmd_dvrf = nh->nh_dev->vif_vrf;
     280             : 
     281           0 :     if (nh->nh_family == AF_BRIDGE) {
     282           0 :         if (vr_pkt_type(pkt, 0, &fmd)) {
     283           0 :             drop_reason = VP_DROP_INVALID_PACKET;
     284           0 :             goto drop;
     285             :         }
     286             :     } else {
     287           0 :         if (vr_ip_is_ip4((struct vr_ip *)pkt_data(pkt))) {
     288           0 :             pkt->vp_type = VP_TYPE_IP;
     289           0 :         } else if (vr_ip_is_ip6((struct vr_ip *)pkt_data(pkt))) {
     290           0 :             pkt->vp_type = VP_TYPE_IP6;
     291             :         } else {
     292           0 :             drop_reason = VP_DROP_INVALID_PROTOCOL;
     293           0 :             goto drop;
     294             :         }
     295             : 
     296           0 :         pkt_set_network_header(pkt, pkt->vp_data);
     297           0 :         pkt_set_inner_network_header(pkt, pkt->vp_data);
     298             :     }
     299             : 
     300             :     /* For GRO packet, count will be incremented once */
     301           0 :     if (vif) {
     302             :         struct vr_interface_stats *gro_vif_stats;
     303           0 :         gro_vif_stats = vif_get_stats(vif, vr_get_cpu());
     304           0 :         if (gro_vif_stats) {
     305           0 :             gro_vif_stats->vis_opackets++;
     306           0 :             gro_vif_stats->vis_obytes += pkt_len(pkt);
     307             :         }
     308             :     }
     309             : 
     310           0 :     pkt->vp_flags |= VP_FLAG_FLOW_SET | VP_FLAG_GROED;
     311           0 :     nh_output(pkt, nh, &fmd);
     312           0 :     rte_free(entry);
     313           0 :     return GRO_MERGED;
     314             : 
     315           0 : drop:
     316           0 :     vr_dpdk_pfree(m, vif, drop_reason);
     317           0 :     rte_free(entry);
     318           0 :     return 0;
     319             : }
     320             : 
     321             : void
     322          53 : dpdk_gro_free_all_flows(struct vr_dpdk_lcore *lcore)
     323             : {
     324             :     struct gro_entry *entry;
     325          53 :     uint32_t iter = 0;
     326             :     void *next_key;
     327          53 :     struct gro_ctrl *gro = &lcore->gro;
     328          53 :     if (gro->gro_tbl_v4_handle != NULL) {
     329           0 :         while (rte_hash_iterate(gro->gro_tbl_v4_handle, (const void**)&next_key,
     330           0 :                                                   (void**)&entry, &iter) >= 0) {
     331           0 :             vr_dpdk_pfree(entry->mbuf_head, NULL, VP_DROP_DISCARD);
     332           0 :             rte_free(entry);
     333             :         }
     334           0 :         rte_hash_reset(gro->gro_tbl_v4_handle);
     335             :     }
     336          53 :     iter = 0;
     337          53 :     if (gro->gro_tbl_v6_handle != NULL) {
     338           0 :         while (rte_hash_iterate(gro->gro_tbl_v6_handle, (const void**)&next_key,
     339           0 :                                                   (void**)&entry, &iter) >= 0) {
     340           0 :             vr_dpdk_pfree(entry->mbuf_head, NULL, VP_DROP_DISCARD);
     341           0 :             rte_free(entry);
     342             :         }
     343           0 :         rte_hash_reset(gro->gro_tbl_v6_handle);
     344             :     }
     345          53 : }
     346             : 
     347             : void
     348      631292 : dpdk_gro_flush_all_inactive(struct vr_dpdk_lcore *lcore)
     349             : {
     350      631292 :     uint32_t iter = 0;
     351             :     void *next_key;
     352             :     struct gro_entry *entry;
     353      631292 :     struct gro_ctrl *gro = &lcore->gro;
     354      631292 :     uint64_t cur_cycles = 0, diff_cycles;
     355      631292 :     const uint64_t gro_flush_cycles = 100 * VR_DPDK_TX_FLUSH_LOOPS;
     356             : 
     357      631292 :     if (gro->gro_tbl_v4_handle != NULL) {
     358      631292 :         cur_cycles = lcore->lcore_fwd_loops;
     359             :         /* Iterate through the hash table */
     360      631292 :         while (rte_hash_iterate(gro->gro_tbl_v4_handle, (const void**)&next_key,
     361      631296 :                                                   (void**)&entry, &iter) >= 0) {
     362           0 :             diff_cycles = cur_cycles - entry->mtime;
     363           0 :             if (unlikely(gro_flush_cycles < diff_cycles)) {
     364             :                 /* Flush flow */
     365           0 :                 dpdk_gro_flush(gro, next_key, entry);
     366           0 :                 lcore->gro.gro_flush_inactive_flows++;
     367             :             }
     368             :         }
     369             :     }
     370             : 
     371      631296 :     iter = 0;
     372      631296 :     if (gro->gro_tbl_v6_handle != NULL) {
     373      631295 :         cur_cycles = lcore->lcore_fwd_loops;
     374             :         /* Iterate through the hash table */
     375      631295 :         while (rte_hash_iterate(gro->gro_tbl_v6_handle, (const void**)&next_key,
     376      631297 :                                                   (void**)&entry, &iter) >= 0) {
     377           0 :             diff_cycles = cur_cycles - entry->mtime;
     378           0 :             if (unlikely(gro_flush_cycles < diff_cycles)) {
     379             :                 /* Flush flow */
     380           0 :                 dpdk_gro_flush(gro, next_key, entry);
     381           0 :                 lcore->gro.gro_flush_inactive_flows++;
     382             :             }
     383             :         }
     384             :     }
     385      631298 : }
     386             : 
     387             : int
     388           4 : dpdk_gro_process(struct vr_packet *pkt, struct vr_interface *vif, bool l2_pkt)
     389             : {
     390             :     struct vr_dpdk_gro_flow_key_v4 flow4;
     391             :     struct vr_dpdk_gro_flow_key_v6 flow6;
     392             :     struct gro_entry *entry;
     393             :     struct rte_mbuf *m;
     394             :     void *nw_hdr;
     395           4 :     struct vr_tcp *tcph = NULL;
     396             :     uint16_t tcp_data_len, ip_pkt_len, l;
     397             :     uint32_t *ts_ptr;
     398             :     int32_t ret;
     399             :     unsigned short csum;
     400           4 :     unsigned short src_vif_idx = 0;
     401           4 :     uint32_t nh_id = 0;
     402             :     unsigned lcore_id;
     403             :     struct vr_dpdk_lcore *lcore;
     404           4 :     uint8_t is_ipv6 = 0, flush = 0;
     405           4 :     void *key = NULL;
     406             :     struct vrouter *router;
     407             :     struct vr_interface *src_vif;
     408             :     struct vr_nexthop *nh;
     409             :     struct vr_gro *gro;
     410             : 
     411             : 
     412             :     /* Normal processing for VMs if -
     413             :      * => They dont require GRO (like DPDK VM's)
     414             :      * => They dont support mergeable buffers
     415             :      */
     416           4 :     if ((vif->vif_flags &
     417             :                 (VIF_FLAG_GRO_NEEDED | VIF_FLAG_MRG_RXBUF)) !=
     418             :                 (VIF_FLAG_GRO_NEEDED | VIF_FLAG_MRG_RXBUF)) {
     419           4 :         return 0;
     420             :     }
     421             : 
     422           0 :     lcore_id = rte_lcore_id();
     423           0 :     lcore = vr_dpdk.lcores[lcore_id];
     424             : 
     425             :     /* Packets arriving through non-Fwd cores - Normal processing */
     426           0 :     if (unlikely((lcore->gro.gro_tbl_v4_handle == NULL) ||
     427             :             (lcore->gro.gro_tbl_v6_handle == NULL))){
     428           0 :         return 0;
     429             :     }
     430             : 
     431           0 :     m = vr_dpdk_pkt_to_mbuf(pkt);
     432           0 :     nw_hdr = m->buf_addr + pkt_get_network_header_off(pkt);
     433           0 :     router = vrouter_get(0);
     434             :     /* Adjust mbuf */
     435           0 :     m->data_off = pkt_head_space(pkt);
     436           0 :     m->pkt_len = pkt_len(pkt);
     437           0 :     m->data_len = pkt_head_len(pkt);
     438             : 
     439             :     /* get the vif_idx and nh_id */
     440           0 :     gro = rte_pktmbuf_mtod(m, struct vr_gro *);
     441           0 :     src_vif_idx = gro->vg_vif_id;
     442           0 :     nh_id = gro->vg_nh_id;
     443           0 :     rte_pktmbuf_adj(m, sizeof(struct vr_gro));
     444           0 :     pkt_pull(pkt, sizeof(struct vr_gro));
     445             : 
     446           0 :     src_vif = __vrouter_get_interface(router, src_vif_idx);
     447             : 
     448             :     /* Make sure nh points to a virtual vif */
     449           0 :     nh = __vrouter_get_nexthop(router, nh_id);
     450           0 :     if ((nh == NULL) || (nh->nh_dev == NULL)
     451           0 :             || (!vif_is_virtual(nh->nh_dev))) {
     452           0 :         ret = GRO_NORMAL;
     453           0 :         goto func_exit;
     454             :     }
     455             : 
     456             :     /* Can't handle chained mbufs */
     457           0 :     if (m->nb_segs > 1) {
     458           0 :         flush = 1;
     459             :     }
     460             : 
     461             :     /* Parse IP header */
     462           0 :     if (vr_ip_is_ip6(nw_hdr)) {
     463           0 :         struct vr_ip6 *ip6 = nw_hdr;
     464           0 :         is_ipv6 = 1;
     465           0 :         flow6.proto = ip6->ip6_nxt;
     466             :         /* For non TCP packets, no GRO */
     467           0 :         if (flow6.proto != VR_IP_PROTO_TCP) {
     468           0 :             ret = GRO_NOT_APPLICABLE;
     469           0 :             goto func_exit;
     470             :         }
     471           0 :         rte_memcpy(&flow6.ip6_src, ip6->ip6_src, sizeof(flow6.ip6_src));
     472           0 :         rte_memcpy(&flow6.ip6_dst, ip6->ip6_dst, sizeof(flow6.ip6_dst));
     473           0 :         tcph = (struct vr_tcp *)(nw_hdr + sizeof(*ip6));
     474           0 :         flow6.port_src = rte_be_to_cpu_16(tcph->tcp_sport);
     475           0 :         flow6.port_dst = rte_be_to_cpu_16(tcph->tcp_dport);
     476           0 :         flow6.vif_idx = vif->vif_idx;
     477           0 :         ip_pkt_len = rte_be_to_cpu_16(ip6->ip6_plen) + sizeof(struct vr_ip6);
     478           0 :         tcp_data_len = rte_be_to_cpu_16(ip6->ip6_plen);
     479           0 :         key = &flow6;
     480           0 :     } else if (vr_ip_is_ip4(nw_hdr)) {
     481           0 :         struct vr_ip *ip4 = nw_hdr;
     482           0 :         unsigned int hlen = 0;
     483           0 :         hlen = ip4->ip_hl * 4;
     484           0 :         flow4.proto = ip4->ip_proto;
     485             :         /* For non TCP packets, no GRO */
     486           0 :         if (flow4.proto != VR_IP_PROTO_TCP) {
     487           0 :             ret = GRO_NOT_APPLICABLE;
     488           0 :             goto func_exit;
     489             :         }
     490             :         /* Ensure the packet is not fragmented. */
     491           0 :         else if (ip4->ip_frag_off & rte_cpu_to_be_16(IP_MF|IP_OFFMASK)) {
     492           0 :             flush = 1;
     493             :         }
     494             :         /* .. and there are no options. */
     495           0 :         else if (hlen != sizeof (*ip4)) {
     496           0 :             flush = 1;
     497             :         }
     498           0 :         flow4.ip_src = ip4->ip_saddr;
     499           0 :         flow4.ip_dst = ip4->ip_daddr;
     500           0 :         tcph = (struct vr_tcp *)(nw_hdr + hlen);
     501           0 :         flow4.port_src = rte_be_to_cpu_16(tcph->tcp_sport);
     502           0 :         flow4.port_dst = rte_be_to_cpu_16(tcph->tcp_dport);
     503           0 :         flow4.vif_idx = vif->vif_idx;
     504           0 :         ip_pkt_len = rte_be_to_cpu_16(ip4->ip_len);
     505           0 :         tcp_data_len = ip_pkt_len - (ip4->ip_hl << 2);
     506           0 :         key = &flow4;
     507             :     } else {
     508           0 :         ret = GRO_NOT_APPLICABLE;
     509           0 :         goto func_exit;
     510             :     }
     511           0 :     csum = rte_be_to_cpu_16(tcph->tcp_csum);
     512             : 
     513             :     /* Check TCP header constraints */
     514             : 
     515             :     /* Ensure no bits set besides ACK. */
     516           0 :     if (VR_TCP_FLAGS(tcph->tcp_offset_r_flags) & (~VR_TCP_FLAG_ACK)) {
     517           0 :         flush = 1;
     518             :     }
     519             : 
     520             :     /*
     521             :      * Check for timestamps.
     522             :      * Since the only option we handle are timestamps, we only have to
     523             :      * handle the simple case of aligned timestamps.
     524             :      */
     525           0 :     l = VR_TCP_OFFSET(tcph->tcp_offset_r_flags) << 2;
     526           0 :     tcp_data_len -= l;
     527           0 :     l -= sizeof(*tcph);
     528           0 :     ts_ptr = (uint32_t *)(tcph + 1);
     529           0 :     if (l != 0 && (unlikely(l != TCPOLEN_TSTAMP_APPA) ||
     530           0 :         (*ts_ptr != rte_cpu_to_be_32(TCPOPT_NOP<<24|TCPOPT_NOP<<16|
     531             :         TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)))) {
     532           0 :         flush = 1;
     533             :     }
     534             : 
     535             :     /* Cannot handle odd size segments */
     536           0 :     if (tcp_data_len & 1) {
     537           0 :         flush = 1;
     538             :     }
     539             : 
     540           0 :     if (likely(flush == 0)) {
     541             :         /* Check TCP checksum */
     542           0 :         tcph->tcp_csum = 0;
     543           0 :         if (!is_ipv6)
     544           0 :             tcph->tcp_csum = rte_ipv4_udptcp_cksum(nw_hdr, tcph);
     545             :         else
     546           0 :             tcph->tcp_csum = rte_ipv6_udptcp_cksum(nw_hdr, tcph);
     547             : 
     548           0 :         if (tcph->tcp_csum == 0xffff)
     549           0 :             tcph->tcp_csum = 0;
     550             : 
     551           0 :         if (csum != rte_be_to_cpu_16(tcph->tcp_csum)) {
     552           0 :             vr_dpdk_pfree(m, vif, VP_DROP_CKSUM_ERR);
     553           0 :             ret = GRO_MERGED;
     554           0 :             goto func_exit;
     555             :         }
     556             :     }
     557             : 
     558           0 :     if (!is_ipv6)
     559           0 :         ret = rte_hash_lookup_data(lcore->gro.gro_tbl_v4_handle, &flow4, (void*)&entry);
     560             :     else
     561           0 :         ret = rte_hash_lookup_data(lcore->gro.gro_tbl_v6_handle, &flow6, (void*)&entry);
     562             : 
     563           0 :     if (ret >= 0) {
     564             :         /* Update flow */
     565             : 
     566             :         /* Check if packet needs to be flushed */
     567           0 :         if (flush) {
     568           0 :             dpdk_gro_flush(&lcore->gro, key, entry);
     569           0 :             ret = GRO_CANNOT;
     570           0 :             goto func_exit;
     571             :         }
     572             : 
     573             :         /* Flush now if appending will result in overflow. */
     574           0 :         if (entry->p_len > (65535 - tcp_data_len)) {
     575           0 :             dpdk_gro_flush(&lcore->gro, key, entry);
     576           0 :             goto create;
     577             :         }
     578             : 
     579             :         /* Try to append the new segment. */
     580           0 :         if (unlikely(rte_be_to_cpu_32(tcph->tcp_seq) != entry->next_seq ||
     581             :            (tcp_data_len == 0))) {
     582             :             /* Out of order packet or duplicate ACK. */
     583           0 :             dpdk_gro_flush(&lcore->gro, key, entry);
     584           0 :             ret = GRO_CANNOT;
     585           0 :             goto func_exit;
     586             :         }
     587             : 
     588           0 :         if (l != 0) {
     589           0 :             uint32_t tsval = rte_be_to_cpu_32(*(ts_ptr + 1));
     590             :             /* Make sure timestamp values are increasing.
     591             :              * Also make sure the timestamp echo reply value is valid.
     592             :              * Note: timestamp echo reply is valid if its value is not 0.
     593             :              */
     594           0 :             if (unlikely(entry->tsval > tsval ||
     595             :                 rte_be_to_cpu_32(*(ts_ptr + 2)) == 0)) {
     596           0 :                 dpdk_gro_flush(&lcore->gro, key, entry);
     597           0 :                 ret = GRO_CANNOT;
     598           0 :                 goto func_exit;
     599             :             }
     600           0 :             entry->tsval = tsval;
     601           0 :             entry->tsecr = rte_be_to_cpu_32(*(ts_ptr + 2));
     602             :         }
     603             : 
     604           0 :         entry->next_seq += tcp_data_len;
     605           0 :         entry->ack_seq = rte_be_to_cpu_32(tcph->tcp_ack);
     606           0 :         entry->window = rte_be_to_cpu_16(tcph->tcp_win);
     607           0 :         entry->mbuf_cnt++;
     608           0 :         entry->ulp_csum += dpdk_gro_rx_csum_fixup(entry, is_ipv6, nw_hdr, tcph, tcp_data_len, ~csum);
     609             : 
     610           0 :         entry->p_len += tcp_data_len;
     611             : 
     612             :         /*
     613             :          * Adjust the mbuf so that rte_pktmbuf_mtod(m) points to the first byte of
     614             :          * the ULP payload.  Adjust the mbuf to avoid complications and
     615             :          * append new segment to existing mbuf chain.
     616             :          */
     617           0 :         rte_pktmbuf_adj(m, rte_pktmbuf_data_len(m) - tcp_data_len);
     618             : 
     619           0 :         entry->mbuf_tail->next = m;
     620           0 :         entry->mbuf_tail = rte_pktmbuf_lastseg(m);
     621             : 
     622             :         /* If segment size is different, flush */
     623           0 :         if (entry->seg_sz != tcp_data_len) {
     624           0 :             dpdk_gro_flush(&lcore->gro, key, entry);
     625           0 :             ret = GRO_MERGED;
     626           0 :             goto func_exit;
     627             :         }
     628             : 
     629             :         /* Save cycles */
     630           0 :         entry->mtime = lcore->lcore_fwd_loops;
     631             : 
     632           0 :         ret = GRO_MERGED;
     633             :     } else {
     634             :         /* Create new flow */
     635             :         struct gro_entry *entry;
     636           0 : create:
     637           0 :         if (tcp_data_len == 0) {
     638           0 :             ret = GRO_CANNOT;
     639           0 :             goto func_exit;
     640           0 :         } else if (flush) {
     641           0 :             ret = GRO_CANNOT;
     642           0 :             goto func_exit;
     643             :         }
     644             : 
     645           0 :         entry = rte_zmalloc("GRO_entry", sizeof(*entry), RTE_CACHE_LINE_SIZE);
     646           0 :         if (unlikely(entry == NULL)) {
     647           0 :             ret = GRO_ERROR;
     648           0 :             goto func_exit;
     649             :         }
     650           0 :         entry->is_ipv6 = is_ipv6;
     651           0 :         entry->src_vif_idx = src_vif_idx;
     652           0 :         if (likely(src_vif != NULL))
     653           0 :             entry->src_vif_gen = src_vif->vif_gen;
     654           0 :         entry->dst_vif_idx = nh->nh_dev->vif_idx;
     655           0 :         entry->dst_vif_gen = nh->nh_dev->vif_gen;
     656           0 :         entry->nh_id = nh_id;
     657           0 :         entry->mtime = lcore->lcore_fwd_loops;
     658           0 :         entry->p_len = ip_pkt_len;
     659           0 :         entry->next_seq = rte_be_to_cpu_32(tcph->tcp_seq) + tcp_data_len;
     660           0 :         entry->ack_seq = rte_be_to_cpu_32(tcph->tcp_ack);
     661           0 :         entry->window = rte_be_to_cpu_16(tcph->tcp_win);
     662           0 :         entry->seg_sz = tcp_data_len;
     663           0 :         if (!is_ipv6)
     664           0 :             entry->le_ip4 = nw_hdr;
     665             :         else
     666           0 :             entry->le_ip6 = nw_hdr;
     667           0 :         entry->mbuf_cnt++;
     668           0 :         if (unlikely(l != 0)) {
     669           0 :             entry->timestamp = 1;
     670           0 :             entry->tsval = rte_be_to_cpu_32(*(ts_ptr + 1));
     671           0 :             entry->tsecr = rte_be_to_cpu_32(*(ts_ptr + 2));
     672             :         }
     673           0 :         entry->mbuf_head = m;
     674           0 :         entry->mbuf_tail = rte_pktmbuf_lastseg(m);
     675             : 
     676           0 :         entry->ulp_csum = dpdk_gro_rx_csum_fixup(entry, is_ipv6, nw_hdr, tcph, tcp_data_len, ~csum);
     677           0 :         tcph->tcp_csum = rte_cpu_to_be_16(csum);/* Restore checksum on first packet. */
     678           0 :         lcore->gro.gro_flows++;
     679             : 
     680           0 :         if (!is_ipv6) {
     681           0 :             ret = (rte_hash_add_key_data(lcore->gro.gro_tbl_v4_handle, &flow4, entry) < 0)?
     682           0 :                                        GRO_ERROR : GRO_MERGED;
     683             :         } else {
     684           0 :             ret = (rte_hash_add_key_data(lcore->gro.gro_tbl_v6_handle, &flow6, entry) < 0)?
     685           0 :                                        GRO_ERROR : GRO_MERGED;
     686             :         }
     687             : 
     688           0 :         if (ret == GRO_MERGED) {
     689             :             /* point mbuf to network header */
     690           0 :             rte_pktmbuf_adj(m, pkt_get_network_header_off(pkt)- pkt_head_space(pkt));
     691           0 :             pkt_pull(pkt, pkt_get_network_header_off(pkt)- pkt_head_space(pkt));
     692           0 :         } else if (ret == GRO_ERROR) {
     693           0 :             rte_free(entry);
     694             :         }
     695             :     }
     696             : 
     697           0 : func_exit:
     698           0 :     if (ret != GRO_MERGED) {
     699           0 :         pkt_push(pkt, sizeof(struct vr_gro));
     700           0 :         rte_pktmbuf_prepend(m, sizeof(struct vr_gro));
     701             :     }
     702           0 :     return (ret == GRO_MERGED)?1:0;
     703             : }
     704             : 
     705             : 

Generated by: LCOV version 1.14