ovs+dpdk场景下的tx checksum offload-程序员宅基地

技术标签: 虚拟化  

一、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驱动是否有置为需要硬件做checksum
    if (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 offload
        if (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还是ipv6
                m->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 */
		//判断是否需要硬件做tso
		if (ol_flags & PKT_TX_TCP_SEG) {
			/* implies IP cksum in IPv4 */
			//硬件tso的场景下,需要重新计算tcp、ip的checksum
			//因此这里只判断是做ipv4还是ipv6的ip csum
			if (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;
			else
				type_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层的csum
			if (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_SECURITY
		if (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;
		}
	#endif

		txq->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);
		else
			vlan_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;
	}

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zgy666/article/details/104851752

智能推荐

HQL查询详细语法讲解_hqlun-程序员宅基地

文章浏览阅读616次。HQL查询HQL查询:Criteria查询对查询条件进行了面向对象封装,符合编程人员的思维方式,不过HQL(Hibernate Query Lanaguage)查询提供了更加丰富的和灵活的查询特性,因此Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语句的查询方式,同时也提供了更加面向对象_hqlun

第10章 指针(五) 使用地址-------指针的引用-程序员宅基地

文章浏览阅读43次。提纲:1、间接访问变量的指针运算符*2、野指针和空指针3、变量的指针表示:a、用指针引用普通变量b、用指针引用数组及其元素c、函数间传递指针:指针参数和返回值4、指针变量的引用和定义的区别5、存储指针的书写问题6、&amp;*和*&amp;7、变量用指针表示后 ,进行+ - * / 运算时,有必要加( )...

java通过opencv解析二维码(微信开源解码工具)_opencv java 二维码识别-程序员宅基地

文章浏览阅读1.5k次,点赞2次,收藏7次。微信开源了其二维码的解码功能,并贡献给 OpenCV 社区。其开源的 wechat_qrcode 项目被收录到 OpenCV contrib 项目中。从 OpenCV 4.5.2 版本开始,就可以直接使用。以往java解析二维码都是使用google.zxing,但是zxing解析二维码的成功率比较低,很多美化或者个性化的二维码无法解析。以上依赖用于windows系统 如果要发布到liunx系统把windows-x86_64改成liunx-x86_64。_opencv java 二维码识别

nodejs安装与卸载(高版本降为低版本)_nodejs卸载-程序员宅基地

文章浏览阅读1.2w次,点赞14次,收藏81次。初到公司发现电脑上面已经安装nodejs,但是版本是最新版本。但是问题来了,好像是不太支持vue3,所以查了一些资料,具体的也不是很清楚,反正就是nodejs的版本不能太高最好保持在16以下。好吧,那就重新卸载再来安装nodejs吧。_nodejs卸载

IPU(Image Processing Unit )-程序员宅基地

文章浏览阅读3k次,点赞2次,收藏11次。Chapter 38 Image Processing Unit (IPU)38.1 概述IPU计划成为应用程序处理器中的视频和图形子系统的一部分。IPU的目标是为从图像传感器和/或到显示设备的数据流提供全面支持。这项支助包括这些活动的所有方面:连接到相关设备-摄像机,显示器,图形加速器,电视编码器和解码器。相关图像处理与操作:传感器图像信号处理、显示处理、图像转换等。..._image processing unit

小程序 | 基于WAMP的新闻网小程序开发(体验全栈式开发微信小程序)_微信小程序能不能用wamp-程序员宅基地

文章浏览阅读2k次。之前学习微信小程序开发,主要是基于JS、WXML、WXSS的前端开发,对于后端技术不精的我也是使用了微信开发者工具中的云开发功能,但是今天突发奇想,特别想体验一下全栈式开发微信小程序,学习了一下基于WAMP的新闻网小程序开发。文章目录一、前端(实现小程序界面)1.1 index页面1.2 my页面二、后端(搭建本地服务器)2.1 安装phpStudy工具连接数据库2.2 使用Navicat数据库工具连接数据库2.3 配置phpStudy搭建本地服务器2.4 进一步配置.php文件实现核心功能三、连接前._微信小程序能不能用wamp

随便推点

Class.getResourceAsStream()与ClassLoader.getResourceAsStream()获取资源时的路径说明-程序员宅基地

文章浏览阅读45次。Class.getResourceAsStream(): com.xusweeter.iot.ws.vodafone.config.VodafoneServiceConfig.class.getResourceAsStream(VDF_CERT_NAME); 通过以上代码加载资源时:默认加载com.xusweeter.iot.ws.vodafone.config包下名称为VDF_CERT_NAME..._thread.currentthread().getcontextclassloader().getresourceasstream(certname)

SpringCloud集成Nacos作为配置中心_to enable urls as dynamic configuration sources, d-程序员宅基地

文章浏览阅读471次。我们在搭建自己的微服务的时候,可以选择使用eureka作为我们微服务的注册中心,使用nacos作为微服务的配置中心,接下来我们可以看下具体的搭建过程:(以下过程只展示Spring Cloud + Nacos的搭建过程,至于集成eureka的可以另行查找具体对接流程)_to enable urls as dynamic configuration sources, define system property arch

51nod1770: 数数字(模拟)_hiho1770 单调数-程序员宅基地

文章浏览阅读231次。1770 数数字基准时间限制:1 秒 空间限制:262144 KB 分值: 20 难度:3级算法题 收藏 关注统计一下 aaa ⋯ aaan个a × b 的结果里面有多少个数字d,a,b,d均为一位数。样例解释:3333333333*3=9999999999,里面有10个9。_hiho1770 单调数

Kail Linux使用aircrack-ng破解WiFi_aircrack-ng握手包-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏13次。Kail Linux使用aircrack-ng破解WiFi前言: 闲着无事,就自己开了个热点打算自己破解以练习kali Linux 和 Linux 的使用 准备:将准备好的无驱无线网卡接入kali虚拟机1.查看无线网卡是否接入成功 命令: iwconfig2.启动无线网卡监听模式 命令:airmon-ng start wlan..._aircrack-ng握手包

docker_73720353-63a5-41bf-b66e-78d8e49153b8-程序员宅基地

文章浏览阅读4.7k次。Docker是一个基于go语言遵从apache2.0协议开源的LXC的高级容器引擎(底层技术是Linux Container,docker只是管理底层的工具),并通过namespace、cgroup等来提供容器的资源隔离与安全保障等。Docker 最早采用 LXC 技术 (LXC 是 Linux 原生支持的容器技术,是一种内核虚拟化技术,可以提供轻量级的虚拟化,LXC将Linux进程沙盒化,使进程之间相互隔离 ),可以说docker 就是基于 LXC 发展起来的。_73720353-63a5-41bf-b66e-78d8e49153b8

【00】机器学习之旅-启程_机器学习 just in time learning-程序员宅基地

文章浏览阅读593次。【00】机器学习之旅启程 Machine Learning Roadmap【开始我的机器学习之路,感兴趣,就慢慢学,就在CSDN建立自己学习旅程】指南:Learning guideStreamlined guideinformation filterScope:Focus your scope on classification and regression t_机器学习 just in time learning