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-11-09 02:10:49 Functions: 4 7 57.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14