DPDK系列之四十硬件加速和功能卸载

时间:2024-02-23 10:37:39  热度:0°C

一、硬件卸载

硬件加速,听名字就是明白是利用硬件加速。不太准确,硬件加速其实更有效进行硬件的分工,通过分工实现硬件的整体的效率的提升。其实硬件卸载就是硬件加速,而实现硬件加速就需要进行功能卸载,整体上就可以叫做硬件卸载。硬件卸载是指将某些任务或计算从计算机的主处理器 (CPU) 或一些软件功能转移到专用硬件组件(例如网络接口卡 (NIC) 或图形处理单元 (GPU))的过程,以提高系统性能和效率。而提到硬件卸载就不得提到软件定义网络:软件定义网络的主流的解决方案是从传统的包含专用硬件与控制平面相结合并提供选定的功能的交换机等单个设备,转移到软件定义网络抽象出的控制平面、数据平面、管理平面三个不同的层。第一层是管理层或管理平面。它包括像OpenStack这类应用所在的位置,可以将一些配置应用于网络并将其用于虚拟化等领域。下一层是控制平面。最下面是数据平面。它由硬件(例如白盒交换机)和软件(软件数据平面)组成,数据平面就是硬件卸载所在的地方。而软件定义网络一般是指通过某种技术将网络设备的控制面与数据面分离开,形成灵活智能的网络。软件数据平面是一个常用的术语,用来描述处理网络数据包中用户数据的应用程序。DPDK,数据平面开发套件,这下明白了吧。

二、DPDK中的应用

DPDK主要是应对网络应用开发,所以其主要和网卡打交道。DPDK为了支持网卡的硬件卸载,需要提供相关的软件接口,具体到实际情况,网卡的硬件卸载可能是基于端口设置也可能是基于每个包设置使能。DPDK的mbuf可能是对应着一个包也可能多个mbuf对应着一个包,这在mbuf中都有对应的标识来标定(ol_flags)。在“dpdk-stable-19/11/14liblibrte_mbuf te_mbuf_core/h”中有这些具体的定义,如“PKT_RX_IP_CKSUM_NONE”等等。在相同文件夹下,还有其它一些具体的宏定义,大家也可以参考。相关的硬件卸载功能主要是与实际的网卡的实现情况有关,其主要有以下几种情况:1、硬件与更新功能卸载它根据实际情况又可以分为:a/VLAN硬件卸载,通过报文中Tag标识来进行标识控制。通过硬件卸载可以直接使用硬件操作而非软件利用CPU来进行操作,减轻了CPU的负荷。b/IEEE1588硬件卸载功能,PTP(Precision Timing Protocol,精准时间同步协议),这个用硬件实现更便捷准确c/IP TCP/UDP/SCTP checksum硬件卸载功能,这个就不用说了,硬件更快捷高效d/Tunnel硬件卸载功能,这个更好理解,其实就是纯粹的协议的控制,隧道,就是一个安全通道,一个专门的通道,可以将不同的协议连接起来。类似于集装箱,直接把原始包扔进去做负载。这种简单的封装其实更适合用硬件来完成。

2、分片功能卸载分片说得有点高级,其实也很好理解,一个大城市不好管理,划成一个个小片不就好管理了,这就是分片。在网络通信中,上层应用可能不会顾及底层的硬件和驱动的缓冲大小,向下输出大量数据,可实际的TCP的传输是有大小控制的,这就需要将上层的数据分片成合适的大小。而这个分片的过程软件其实是不如硬件更容易实现。因为这种机械的固定的功能最是硬件的擅长的了。

3、组包功能卸载组包其实就上面分片的逆操作。毕竟数据最终还是回流到上层应用,必须保持数据的一致性呈现,这就需要把在数据传输过程中的一系列辅助的动作恢复原样。这就和在网上购物一样,可能是一个很小的物件,但在实际传输中可能不断的被打包,验证,然后装箱,运输。等到达目的地时再逆向操作,最终原样送到购买者手中。

三、源码分析

在DPDK提供了对IEEE1588的功能支持:

intrte_eth_timesync_enable(uint16_tport_id){structrte_eth_dev*dev/RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id/-ENODEV)/dev=&/rte_eth_devices[port_id]/RTE_FUNC_PTR_OR_ERR_RET(*dev->/dev_ops->/timesync_enable/-ENOTSUP)/returneth_err(port_id/(*dev->/dev_ops->/timesync_enable)(dev))/}intrte_eth_timesync_disable(uint16_tport_id){structrte_eth_dev*dev/RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id/-ENODEV)/dev=&/rte_eth_devices[port_id]/RTE_FUNC_PTR_OR_ERR_RET(*dev->/dev_ops->/timesync_disable/-ENOTSUP)/returneth_err(port_id/(*dev->/dev_ops->/timesync_disable)(dev))/}intrte_eth_timesync_read_rx_timestamp(uint16_tport_id/structtimespec*timestamp/uint32_tflags){structrte_eth_dev*dev/RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id/-ENODEV)/dev=&/rte_eth_devices[port_id]/RTE_FUNC_PTR_OR_ERR_RET(*dev->/dev_ops->/timesync_read_rx_timestamp/-ENOTSUP)/returneth_err(port_id/(*dev->/dev_ops->/timesync_read_rx_timestamp)(dev/timestamp/flags))/}intrte_eth_timesync_read_tx_timestamp(uint16_tport_id/structtimespec*timestamp){structrte_eth_dev*dev/RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id/-ENODEV)/dev=&/rte_eth_devices[port_id]/RTE_FUNC_PTR_OR_ERR_RET(*dev->/dev_ops->/timesync_read_tx_timestamp/-ENOTSUP)/returneth_err(port_id/(*dev->/dev_ops->/timesync_read_tx_timestamp)(dev/timestamp))/}

上面的代码很清晰就是使能和读取双向的时间戳。而在app/test-pmd中有计算checksum的方法:

if((tx_offloads&/DEV_TX_OFFLOAD_SCTP_CKSUM)&/&/((ipv4_hdr->/total_length&/0x3)==0)){ol_flags|=PKT_TX_SCTP_CKSUM/}else{sctp_hdr->/cksum=0/}}returnol_flags/}

看一下组包的支持:

*InitializesReceiveUnitrxctrl=IXGBE_READ_REG(hw/IXGBE_RXCTRL)/IXGBE_WRITE_REG(hw/IXGBE_RXCTRL/rxctrl&/~IXGBE_RXCTRL_RXEN)/fctrl=IXGBE_READ_REG(hw/IXGBE_FCTRL)/fctrl|=IXGBE_FCTRL_BAM/fctrl|=IXGBE_FCTRL_DPF/fctrl|=IXGBE_FCTRL_PMCF/IXGBE_WRITE_REG(hw/IXGBE_FCTRL/fctrl)/hlreg0=IXGBE_READ_REG(hw/IXGBE_HLREG0)/if(rx_conf->/offloads&/DEV_RX_OFFLOAD_KEEP_CRC)hlreg0&/=~IXGBE_HLREG0_RXCRCSTRP/elsehlreg0|=IXGBE_HLREG0_RXCRCSTRP/if(rx_conf->/offloads&/DEV_RX_OFFLOAD_JUMBO_FRAME){hlreg0|=IXGBE_HLREG0_JUMBOEN/maxfrs=IXGBE_READ_REG(hw/IXGBE_MAXFRS)/maxfrs&/=0x0000FFFF/maxfrs|=(rx_conf->/max_rx_pkt_len</</16)/IXGBE_WRITE_REG(hw/IXGBE_MAXFRS/maxfrs)/}elsehlreg0&/=~IXGBE_HLREG0_JUMBOEN/if(dev->/data->/dev_conf/lpbk_mode!=0){rc=ixgbe_check_supported_loopback_mode(dev)/if(rc</0){PMD_INIT_LOG(ERR/ Unsupportedloopbackmode )/returnrc/}hlreg0|=IXGBE_HLREG0_LPBK/}else{hlreg0&/=~IXGBE_HLREG0_LPBK/}IXGBE_WRITE_REG(hw/IXGBE_HLREG0/hlreg0)/rx_conf->/offloads&/=~DEV_RX_OFFLOAD_VLAN_STRIP/for(i=0/i</dev->/data->/nb_rx_queues/i++){rxq=dev->/data->/rx_queues[i]/if(rx_conf->/offloads&/DEV_RX_OFFLOAD_KEEP_CRC)rxq->/crc_len=RTE_ETHER_CRC_LEN/elserxq->/crc_len=0/bus_addr=rxq->/rx_ring_phys_addr/IXGBE_WRITE_REG(hw/IXGBE_RDBAL(rxq->/reg_idx)/(uint32_t)(bus_addr&/0x00000000ffffffffULL))/IXGBE_WRITE_REG(hw/IXGBE_RDBAH(rxq->/reg_idx)/(uint32_t)(bus_addr>/>/32))/IXGBE_WRITE_REG(hw/IXGBE_RDLEN(rxq->/reg_idx)/rxq->/nb_rx_desc*sizeof(unionixgbe_adv_rx_desc))/IXGBE_WRITE_REG(hw/IXGBE_RDH(rxq->/reg_idx)/0)/IXGBE_WRITE_REG(hw/IXGBE_RDT(rxq->/reg_idx)/0)/srrctl=IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF/if(rxq->/drop_en)srrctl|=IXGBE_SRRCTL_DROP_EN/buf_size=(uint16_t)(rte_pktmbuf_data_room_size(rxq->/mb_pool)-RTE_PKTMBUF_HEADROOM)/srrctl|=((buf_size>/>/IXGBE_SRRCTL_BSIZEPKT_SHIFT)&/IXGBE_SRRCTL_BSIZEPKT_MASK)/IXGBE_WRITE_REG(hw/IXGBE_SRRCTL(rxq->/reg_idx)/srrctl)/buf_size=(uint16_t)((srrctl&/IXGBE_SRRCTL_BSIZEPKT_MASK)</</IXGBE_SRRCTL_BSIZEPKT_SHIFT)/if(dev->/data->/dev_conf/rxmode/max_rx_pkt_len+2*IXGBE_VLAN_TAG_SIZE>/buf_size)dev->/data->/scattered_rx=1/if(rxq->/offloads&/DEV_RX_OFFLOAD_VLAN_STRIP)rx_conf->/offloads|=DEV_RX_OFFLOAD_VLAN_STRIP/}if(rx_conf->/offloads&/DEV_RX_OFFLOAD_SCATTER)dev->/data->/scattered_rx=1/ixgbe_dev_mq_rx_configure(dev)/staticintixgbe_set_rsc(structrte_eth_dev*dev){structrte_eth_rxmode*rx_conf=&/dev->/data->/dev_conf/rxmode/structixgbe_hw*hw=IXGBE_DEV_PRIVATE_TO_HW(dev->/data->/dev_private)/structrte_eth_dev_infodev_info={0}/boolrsc_capable=false/uint16_ti/uint32_trdrxctl/uint32_trfctl/dev->/dev_ops->/dev_infos_get(dev/&/dev_info)/if(dev_info/rx_offload_capa&/DEV_RX_OFFLOAD_TCP_LRO)rsc_capable=true/if(!rsc_capable&/&/(rx_conf->/offloads&/DEV_RX_OFFLOAD_TCP_LRO)){PMD_INIT_LOG(CRIT/ LROisrequestedonHWthatdoesn t supportit )/return-EINVAL/}if((rx_conf->/offloads&/DEV_RX_OFFLOAD_KEEP_CRC)&/&/(rx_conf->/offloads&/DEV_RX_OFFLOAD_TCP_LRO)){PMD_INIT_LOG(CRIT/ LROcan tbeenabledwhenHWCRC isdisabled )/return-EINVAL/}rfctl=IXGBE_READ_REG(hw/IXGBE_RFCTL)/if((rsc_capable)&/&/(rx_conf->/offloads&/DEV_RX_OFFLOAD_TCP_LRO))rfctl&/=~IXGBE_RFCTL_RSC_DIS/elserfctl|=IXGBE_RFCTL_RSC_DIS/rfctl|=IXGBE_RFCTL_NFSW_DIS|IXGBE_RFCTL_NFSR_DIS/IXGBE_WRITE_REG(hw/IXGBE_RFCTL/rfctl)/if(!(rx_conf->/offloads&/DEV_RX_OFFLOAD_TCP_LRO))return0/rdrxctl=IXGBE_READ_REG(hw/IXGBE_RDRXCTL)/rdrxctl|=IXGBE_RDRXCTL_RSCACKC/IXGBE_WRITE_REG(hw/IXGBE_RDRXCTL/rdrxctl)/for(i=0/i</dev->/data->/nb_rx_queues/i++){structixgbe_rx_queue*rxq=dev->/data->/rx_queues[i]/uint32_tsrrctl=IXGBE_READ_REG(hw/IXGBE_SRRCTL(rxq->/reg_idx))/uint32_trscctl=IXGBE_READ_REG(hw/IXGBE_RSCCTL(rxq->/reg_idx))/uint32_tpsrtype=IXGBE_READ_REG(hw/IXGBE_PSRTYPE(rxq->/reg_idx))/uint32_teitr=IXGBE_READ_REG(hw/IXGBE_EITR(rxq->/reg_idx))/srrctl&/=~IXGBE_SRRCTL_BSIZEHDR_MASK/srrctl|=(128</</IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT)&/IXGBE_SRRCTL_BSIZEHDR_MASK/rscctl|=IXGBE_RSCCTL_RSCEN/rscctl|=ixgbe_get_rscctl_maxdesc(rxq->/mb_pool)/psrtype|=IXGBE_PSRTYPE_TCPHDR/eitr&/=~IXGBE_EITR_ITR_INT_MASK/eitr|=IXGBE_EITR_INTERVAL_US(IXGBE_QUEUE_ITR_INTERVAL_DEFAULT)/eitr|=IXGBE_EITR_CNT_WDIS/IXGBE_WRITE_REG(hw/IXGBE_SRRCTL(rxq->/reg_idx)/srrctl)/IXGBE_WRITE_REG(hw/IXGBE_RSCCTL(rxq->/reg_idx)/rscctl)/IXGBE_WRITE_REG(hw/IXGBE_PSRTYPE(rxq->/reg_idx)/psrtype)/IXGBE_WRITE_REG(hw/IXGBE_EITR(rxq->/reg_idx)/eitr)/ixgbe_set_ivar(dev/rxq->/reg_idx/i/0)/}dev->/data->/lro=1/PMD_INIT_LOG(DEBUG/ enablingLROmode )/return0/}

ixgbe_set_rsc会对DEV_RX_OFFLOAD_TCP_LRO进行判断来决定是否组包,这个LRO(Large Receive Offload)其实是RSC(Receive Side Coalescing)的另外一种描述方式。

四、总结

将整个转发的流程相关内容搞清楚后,再去和代码匹配学习,就会发现学习起来相对容易了很多。其实还是推荐大家多看一下DPDK的官方文档,这样会更准确更清晰。但看官方文档有时对小白确实不是一个很友好的入门的方法。这就需要看一看别人学习和实践的心得了。仁者见仁吧,目的只有一个,学会并能熟练使用DPDK。

免责声明:
1. 《DPDK系列之四十硬件加速和功能卸载》内容来源于互联网,版权归原著者或相关公司所有。
2. 若《86561825文库网》收录的文本内容侵犯了您的权益或隐私,请立即通知我们删除。