一、checksum:
tcp checksum包括三部分:
1)、伪头部校验和;
   伪头部包括: 源ip 、宿ip、 协议号、tcp 长度,主要用于校验是正确的目的机器接收到数据包
2)、tcp头部校验和;
3)、数据部分校验和;

当硬件有checksum offload能力时,可以通过ethtook -K tx on/off设置是否将checksum offload到硬件处理;
如果将checksum offload到硬件,那协议栈只需要计算伪头部的校验和,然后将其存放在tcp->check里;并同时
将csum_start、csum_offset高速硬件,csum_start表示硬件需要计算checksum的起始位置(tcp头部起始位置),
csum_offset表示硬件计算完checksum后将值存放的位置。

二、offload流程

1、传输层

tcp_sendmsg
    if (sk->sk_route_caps & NETIF_F_CSUM_MASK)(判断网卡是否有checksum offload功能,如果有,则将ip_summed置为CHECKSUM_PARTIAL)
        skb->ip_summed = CHECKSUM_PARTIAL;
        
    skb_copy_to_page_nocache
        skb_do_copy_data_nocache  (根据ip_summed值填充skb->csum;如果网卡没有offload能力,则这里先计算数据部分的checksum值,然后保存在skb->csum里;如果硬件有offload能力,仅仅只是将数据从用户态拷贝过来,不计算校验和)

tcp_push_one
            tcp_write_xmit
                tcp_transmit_skb
                    icsk->icsk_af_ops->send_check
                        tcp_v4_send_check
                            __tcp_v4_send_check
                            void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)

void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)
{struct tcphdr *th = tcp_hdr(skb);//如果需要offload,则th->check仅仅保存伪头部的校验和值,tcp头部及数据交给硬件计算if (skb->ip_summed == CHECKSUM_PARTIAL) {th->check = ~tcp_v4_check(skb->len, saddr, daddr, 0);//设置硬件计算校验和开始的地址偏移,csum_start指向tcp头部的起始地址处skb->csum_start = skb_transport_header(skb) - skb->head;//设置硬件计算校验和保存的地址偏移,保存在tcp->check处skb->csum_offset = offsetof(struct tcphdr, check);} else {//硬件没有offload能力,则需要在计算checksum//1)、skb->csum为skb_do_copy_data_nocache里计算的数据部分校验和;//2)、csum_partial(th, h->doff << 2, skb->csum))为计算tcp头部+数据的检验和;//3)、tcp_v4_check再将算出来的tcp头部与数据的检验和再加上伪头部校验和th->check = tcp_v4_check(skb->len, saddr, daddr,csum_partial(th,th->doff << 2,skb->csum));}}     

2、ip层
__ip_local_out
    __ip_local_out_sk
        ip_send_check(计算ip头的checksum,保存在iph->check)

3、virtio驱动层
start_xmit(virtio_net)    
    xmit_skb

static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)...if (skb->ip_summed == CHECKSUM_PARTIAL) {//消息头设置需要硬件checksum标志hdr->hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;//将csum_start、csum_offset保存在virtio_net_hdr里hdr->hdr.csum_start = cpu_to_virtio16(vi->vdev,skb_checksum_start_offset(skb));hdr->hdr.csum_offset = cpu_to_virtio16(vi->vdev, skb->csum_offset);} else {hdr->hdr.flags = 0;hdr->hdr.csum_offset = hdr->hdr.csum_start = 0;}
}

4、dpdk层vhost_user口收包

virtio_dev_tx_split    
    copy_desc_to_mbuf
        vhost_dequeue_offload

static __rte_always_inline void
vhost_dequeue_offload(struct virtio_net_hdr *hdr, struct rte_mbuf *m)
{uint16_t l4_proto = 0;void *l4_hdr = NULL;struct tcp_hdr *tcp_hdr = NULL;if (hdr->flags == 0 && hdr->gso_type == VIRTIO_NET_HDR_GSO_NONE)return;parse_ethernet(m, &l4_proto, &l4_hdr);//判断virtio驱动是否有置为需要硬件做checksumif (hdr->flags == VIRTIO_NET_HDR_F_NEEDS_CSUM) {//这里csum_start如果等于m->l2_len + m->l3_len,表明csum_start//为L4的起始位置,然后根据csum_offset决定是tcp offload还是udp offload,或者sctp offloadif (hdr->csum_start == (m->l2_len + m->l3_len)) {switch (hdr->csum_offset) {case (offsetof(struct tcp_hdr, cksum)):if (l4_proto == IPPROTO_TCP)m->ol_flags |= PKT_TX_TCP_CKSUM;break;case (offsetof(struct udp_hdr, dgram_cksum)):if (l4_proto == IPPROTO_UDP)m->ol_flags |= PKT_TX_UDP_CKSUM;break;case (offsetof(struct sctp_hdr, cksum)):if (l4_proto == IPPROTO_SCTP)m->ol_flags |= PKT_TX_SCTP_CKSUM;break;default:break;}}}if (l4_hdr && hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {case VIRTIO_NET_HDR_GSO_TCPV6:tcp_hdr = l4_hdr;m->ol_flags |= PKT_TX_TCP_SEG;m->tso_segsz = hdr->gso_size;m->l4_len = (tcp_hdr->data_off & 0xf0) >> 2;break;case VIRTIO_NET_HDR_GSO_TCPV4:tcp_hdr = l4_hdr;m->ol_flags |= PKT_TX_TCP_SEG;m->tso_segsz = hdr->gso_size;m->l4_len = (tcp_hdr->data_off & 0xf0) >> 2;//tso offload场景下,硬件需要重新拆分数据包,这个标志区分是ipv4还是ipv6m->ol_flags |= PKT_TX_IP_CKSUM;break;case VIRTIO_NET_HDR_GSO_UDP:m->ol_flags |= PKT_TX_UDP_SEG;m->tso_segsz = hdr->gso_size;m->l4_len = sizeof(struct udp_hdr);break;default:RTE_LOG(WARNING, VHOST_DATA,"unsupported gso type %u.\n", hdr->gso_type);break;}}           RTE_LOG(ERR, VHOST_DATA, "vhost_dequeue_offload  ol_flags %llu.\n", m->ol_flags);}
}

5、ovs将数据包转给dpdk口

static inline int
netdev_dpdk_eth_tx_burst(struct netdev_dpdk *dev, int qid,struct rte_mbuf **pkts, int cnt)
{uint32_t nb_tx = 0;//先通过dpdk的prepare接口计算伪头部csum(在tso offload开启场景下需要计算伪头部csum)cnt = rte_eth_tx_prepare(dev->port_id, qid, pkts, cnt);while (nb_tx != cnt) {uint32_t ret;ret = rte_eth_tx_burst(dev->port_id, qid, pkts + nb_tx, cnt - nb_tx);if (!ret) {break;}nb_tx += ret;}if (OVS_UNLIKELY(nb_tx != cnt)) {/* Free buffers, which we couldn't transmit, one at a time (each* packet could come from a different mempool) */int i;for (i = nb_tx; i < cnt; i++) {rte_pktmbuf_free(pkts[i]);}struct dp_netdev_port *dp_port = CONTAINER_OF(&dev->up, struct dp_netdev_port, netdev);        dp_port->need_reconfigure = true;int retsult = port_reconfigure(port);VLOG_ERR_RL(&rl, "zzzz after port reconfigure ret: %d \n", retsult);}return cnt - nb_tx;
}

6、dpdk,ixgbe驱动将数据转给硬件

ixgbe_xmit_pkts

ixgbe_set_xmit_ctx

static inline void  ixgbe_set_xmit_ctx(struct ixgbe_tx_queue *txq,volatile struct ixgbe_adv_tx_context_desc *ctx_txd,uint64_t ol_flags, union ixgbe_tx_offload tx_offload,__rte_unused uint64_t *mdata)
{uint32_t type_tucmd_mlhl;uint32_t mss_l4len_idx = 0;uint32_t ctx_idx;uint32_t vlan_macip_lens;union ixgbe_tx_offload tx_offload_mask;uint32_t seqnum_seed = 0;ctx_idx = txq->ctx_curr;tx_offload_mask.data[0] = 0;tx_offload_mask.data[1] = 0;type_tucmd_mlhl = 0;/* Specify which HW CTX to upload. */mss_l4len_idx |= (ctx_idx << IXGBE_ADVTXD_IDX_SHIFT);if (ol_flags & PKT_TX_VLAN_PKT) {tx_offload_mask.vlan_tci |= ~0;}/* check if TCP segmentation required for this packet *///判断是否需要硬件做tsoif (ol_flags & PKT_TX_TCP_SEG) {/* implies IP cksum in IPv4 *///硬件tso的场景下,需要重新计算tcp、ip的checksum//因此这里只判断是做ipv4还是ipv6的ip csumif (ol_flags & PKT_TX_IP_CKSUM)type_tucmd_mlhl = IXGBE_ADVTXD_TUCMD_IPV4 |IXGBE_ADVTXD_TUCMD_L4T_TCP |IXGBE_ADVTXD_DTYP_CTXT | IXGBE_ADVTXD_DCMD_DEXT;elsetype_tucmd_mlhl = IXGBE_ADVTXD_TUCMD_IPV6 |IXGBE_ADVTXD_TUCMD_L4T_TCP |IXGBE_ADVTXD_DTYP_CTXT | IXGBE_ADVTXD_DCMD_DEXT;//高速硬件l2、l3、l4及segment数tx_offload_mask.l2_len |= ~0;tx_offload_mask.l3_len |= ~0;tx_offload_mask.l4_len |= ~0;tx_offload_mask.tso_segsz |= ~0;mss_l4len_idx |= tx_offload.tso_segsz << IXGBE_ADVTXD_MSS_SHIFT;mss_l4len_idx |= tx_offload.l4_len << IXGBE_ADVTXD_L4LEN_SHIFT;} else { /* no TSO, check if hardware checksum is needed *///非tso场景下,硬件只需要计算tcp头+payload的csum,ip层的有需要才重新计算,因此//这里判断是否需要重新做ip层的csumif (ol_flags & PKT_TX_IP_CKSUM) {type_tucmd_mlhl = IXGBE_ADVTXD_TUCMD_IPV4;tx_offload_mask.l2_len |= ~0;tx_offload_mask.l3_len |= ~0;}switch (ol_flags & PKT_TX_L4_MASK) {case PKT_TX_UDP_CKSUM:type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_UDP |IXGBE_ADVTXD_DTYP_CTXT | IXGBE_ADVTXD_DCMD_DEXT;mss_l4len_idx |= sizeof(struct udp_hdr) << IXGBE_ADVTXD_L4LEN_SHIFT;tx_offload_mask.l2_len |= ~0;tx_offload_mask.l3_len |= ~0;break;//非tso的checksum场景,硬件只需要重新计算case PKT_TX_TCP_CKSUM:type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP |IXGBE_ADVTXD_DTYP_CTXT | IXGBE_ADVTXD_DCMD_DEXT;mss_l4len_idx |= sizeof(struct tcp_hdr) << IXGBE_ADVTXD_L4LEN_SHIFT;tx_offload_mask.l2_len |= ~0;tx_offload_mask.l3_len |= ~0;break;case PKT_TX_SCTP_CKSUM:type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_SCTP |IXGBE_ADVTXD_DTYP_CTXT | IXGBE_ADVTXD_DCMD_DEXT;mss_l4len_idx |= sizeof(struct sctp_hdr) << IXGBE_ADVTXD_L4LEN_SHIFT;tx_offload_mask.l2_len |= ~0;tx_offload_mask.l3_len |= ~0;break;default:type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_RSV |IXGBE_ADVTXD_DTYP_CTXT | IXGBE_ADVTXD_DCMD_DEXT;break;}}if (ol_flags & PKT_TX_OUTER_IP_CKSUM) {tx_offload_mask.outer_l2_len |= ~0;tx_offload_mask.outer_l3_len |= ~0;tx_offload_mask.l2_len |= ~0;seqnum_seed |= tx_offload.outer_l3_len<< IXGBE_ADVTXD_OUTER_IPLEN;seqnum_seed |= tx_offload.l2_len<< IXGBE_ADVTXD_TUNNEL_LEN;}#ifdef RTE_LIBRTE_SECURITYif (ol_flags & PKT_TX_SEC_OFFLOAD) {union ixgbe_crypto_tx_desc_md *md =(union ixgbe_crypto_tx_desc_md *)mdata;seqnum_seed |=(IXGBE_ADVTXD_IPSEC_SA_INDEX_MASK & md->sa_idx);type_tucmd_mlhl |= md->enc ?(IXGBE_ADVTXD_TUCMD_IPSEC_TYPE_ESP |IXGBE_ADVTXD_TUCMD_IPSEC_ENCRYPT_EN) : 0;type_tucmd_mlhl |=(md->pad_len & IXGBE_ADVTXD_IPSEC_ESP_LEN_MASK);tx_offload_mask.sa_idx |= ~0;tx_offload_mask.sec_pad_len |= ~0;}#endiftxq->ctx_cache[ctx_idx].flags = ol_flags;txq->ctx_cache[ctx_idx].tx_offload.data[0]  =tx_offload_mask.data[0] & tx_offload.data[0];txq->ctx_cache[ctx_idx].tx_offload.data[1]  =tx_offload_mask.data[1] & tx_offload.data[1];txq->ctx_cache[ctx_idx].tx_offload_mask    = tx_offload_mask;ctx_txd->type_tucmd_mlhl = rte_cpu_to_le_32(type_tucmd_mlhl);vlan_macip_lens = tx_offload.l3_len;if (ol_flags & PKT_TX_OUTER_IP_CKSUM)vlan_macip_lens |= (tx_offload.outer_l2_len <<IXGBE_ADVTXD_MACLEN_SHIFT);elsevlan_macip_lens |= (tx_offload.l2_len <<IXGBE_ADVTXD_MACLEN_SHIFT);vlan_macip_lens |= ((uint32_t)tx_offload.vlan_tci << IXGBE_ADVTXD_VLAN_SHIFT);ctx_txd->vlan_macip_lens = rte_cpu_to_le_32(vlan_macip_lens);ctx_txd->mss_l4len_idx   = rte_cpu_to_le_32(mss_l4len_idx);ctx_txd->seqnum_seed     = seqnum_seed;}

ovs+dpdk场景下的tx checksum offload相关推荐

  1. DPDK 16.04/16.11.2 默认tx offload是关闭的引起tx vlan offload无效

    打开IXGBE调试日志发发现:tx使用ixgbe_xmit_pkts_vec,默认tx offload无效了 PMD: ixgbe_set_tx_function(): Using simple tx ...

  2. OVS DPDK vs OVS Deep Dive(十六)

    背景 OvS(Open vSwitch)是云计算平台的重要连接组件,为虚拟机提供网络连,被各大云平台,基础设施供应商广泛使用,比如OpenStack, OpenNebula.vSwitch–Virtu ...

  3. OVS DPDK vhost-user详解(十三)

    vhost user协议的控制和数据通道 所有的控制信息通过UNIX套接口(控制通道)交互.包括为进行直接内存访问而交换的内存映射信息,以及当数据填入virtio队列后需要出发的kick事件和中断信息 ...

  4. OVS+DPDK Datapath 包分类技术

    本文主体内容译于[DPDK社区文档],但并没有逐字翻译,在原文的基础上进行了一些调整,增加了对TSS分类器的详细阐述. 1. 概览 本文描述了OVS+DPDK中的包分类器(datapath class ...

  5. FD.io VPP:探究分段场景下vlib_buf在收发包的处理(dpdk_plugin.so)、rte_mbuf与vlib_buf 关系

    Table of Contents rte_mbuf.vlib_buf 关系及内存分布 使用dpdk-收包接口函数 使用dpdk 发包接口函数 总结 参考阅读 在使用vpp老版本copy报文的时候,经 ...

  6. Checksum: 0x7259 [incorrect, should be 0x0a75 (maybe caused by TCP checksum offload?)]

    今天在分析一个TCP DUP ACK的问题时,发现Server端的Wireshark抓包出现下面的错误: Checksum: 0x7259 [incorrect, should be 0x0a75 ( ...

  7. OVS DPDK与QEMU之间如何通过vhost user协议通信 vhost user协议的控制和数据通道

    netdev_dpdk_vhost_construct定义在文件openvswitch-2.9.2/lib/netdev-dpdk.c1058 static int 1059 netdev_dpdk_ ...

  8. 负载均衡续:万亿流量场景下的负载均衡实践

    高并发优化系列文章(持续更新补充) 垂直性能提升 1.1. 架构优化:集群部署,负载均衡 1.2. 本篇内容:万亿流量下负载均衡的实现 上篇基本把负载均衡涉及到的基础都罗列了,那么到了实际场景下,特别 ...

  9. ​冲刺最后一公里——音视频场景下的边缘计算实践

    点击上方"LiveVideoStack"关注我们 近年来,边缘计算逐渐从未来风口变成了进行时,而内容分发这个天生与"下沉"密不可分的领域,在边缘计算实践中可谓一 ...

最新文章

  1. GitHub有望在中国开设子公司?
  2. Accessing tools within a VB6 program - AE中使用VB调用Geoprocessing
  3. C# 调用 taskkill命令结束服务进程
  4. Docker Review - dockerfile 实战_给基础镜像增加功能
  5. c#.winform,datagridview,数组,绑定,字符串,字符串数组绑定datagridview显示,长度,显示数组内容...
  6. 叙述计算机网络拓扑结构的定义,计算机网络拓扑结构的定义
  7. opencv python教程-OpenCV4 Python 最新中文版官方教程来了(附下载)
  8. JavaScript逻辑运算符“”和“||”短路原则的应用
  9. iphone通知和android,手机App 通知数量太多,让你备感压力吗?教你如何消除令人心烦的信息通知(iPhone、Android)...
  10. JAVA操作Trip数据库2
  11. 【工程师学算法】工程常用算法(二)—— 卡尔曼滤波(Kalman Filter)
  12. 微信支付的分账功能介绍
  13. [洛谷P4492] [HAOI2018]苹果树
  14. 联想笔记本——更换SSD并重装系统
  15. 四、RNN模型 与 NLP应用 —— Stacked RNN
  16. amazon ec2 一年试用免费申请
  17. 再论意识、行为和结果
  18. Core Data 教程(2): 如何预载/导入已有的数据
  19. 数据类型(一)基本的数据类型
  20. QNX的PPS订阅者模式

热门文章

  1. 读者问答第一期(端午节福利)
  2. 数据之美 百度GOOGLE统计的秘密
  3. 电脑控制手机 如何快速删除所有手机的联系人
  4. 34个案例,110页中小企业“链式”数字化转型典型案例集
  5. 股票投资的24堂必修课
  6. 智慧旅游建设的特点有哪些?
  7. Mac 重装Safari
  8. C# 窗口声音 提示音
  9. 心跳信号分类-- 学习反馈TASK5
  10. Oracle去重后再进行count()计数