DPDK系列之三十九控制管理

一、基础介绍

通过前面的分析,对DPDK中对报文处理的过程有了一个初步的认知。从一个更高层次来看,传统的网络通信一般会通过上层应用、操作系统、网卡驱动和硬件四层。再往下,基本就不属于于计算机控制的系统了。
早期的应用,基本上这四个层次都比较单纯,所以也没啥复杂的管理方式,你来我往,你应我答这事就成了。可世风不古,上层应用的快速发展,使得底层的压力骤增。软件和硬件的发展是相互影响的,软件新需求的提出,硬件会在一个阶段后满足,即使无法通过正常的技术迭代满足,也可以使用其它方式。比如CPU的核心频率提高成本太高,那么并行技术(多核或多CPU)就出现了。明白了这些,再向下看DPDK中对网络中数据的管理就好理解了。
从大方向上看,DPDK只是省略了其中一些步骤,并不是完全重构了一套理论逻辑,所以在DPDK中,基本应用的仍然是成熟的理论和技术,也就是常说的工程创新。

二、多核和多队列技术

CPU核数的增加,其实就代表了多任务可以并行进行。CPU是干啥的?处理数据的。所以为了对应多个核,那么是不是应该有多个数据队列来供使用?这就是多队列技术。同样为了更好的处理多队列,OS的底层也要对其进行支持,毕竟你不能让每个上层应用自己都写一个多队列来处理和网络的通信。
目前基本网卡本身都支持了多队列技术,DPDK(或内核本身)也提供了多队列技术,这样就涉及到了一个问题,这两个多队列是如何进行应对的?如何应对才能达到最好的效果?这才是重点。
在前面的分析中可以发现,DPDK中的Pack I/O是天然支持多队列技术的。通过一系列的配置,可以绑定与CPU核心、队列等。而网卡报文的队列分配一般是使用RSS和Flow Director技术。RSS其实就是哈希均匀分配到队列,而后者则是使用查找的精确匹配方式将包发送到指定队列。另外还需要注意的是优先给,毕竟有的包命令是处理一些控制状态的,需要优先处理,这都需要在包分配时有一些应对机制。

三、流分类

为了实现多队列和提到的优先级,就需要对包的种类进行分类。比如常见的TCP、IP以及自定义或者云等虚拟包等。有两种处理的方式即上面提到的RSS和Flow Director。
1、RSS(Receive-Side Scaling),接收方向扩展
负载均衡,这个光看名字都知道什么意思,特别是这些年来开发技术的普及,基本上搞后台搞网络的都懂这个技术。简单来说就是不分彼此,不厚此薄彼,每个队列享受相同的待遇。这里面的重点是哈希计算的对应值怎么取?可以是网络四元组也可以是其它,这个根据实际情况来定。

2、Flow Director
精确字段匹配,可以将报文数据转到相应的队列中。在网卡上会有一个表,表中会有相关的关键字和动作。此表由驱动来进行管理,每当数据从网卡进入,就可以通过查询此表来进行相关的动作。
这种工作方式比RSS更有数据的灵活性,可以根据实际情况自定义相关的特定需求。
3、QoS-服务质量
其实就是对于些特定的业务需求进行调度,划分类型并确定优先级。
4、虚拟场景下的分类
现在的实际业务中,云虚拟化的业务占有非常大的比重,所以会有一组队列与SRIOV VF/VMDQ POOL等相对应进行数据处理。
5、流过滤
主要是通过分类进行合法性验证。过滤的方法有MAC地址过滤、VLAN标签过滤和管理数据包过滤。当然,在不同的网卡上也提供了多种规则供调用,它们包括:N tuple filter(N元组指定队列)、EtherType Filter(以太网报文的EtherType指定队列)、Cloud Filter(云应用中的VXLAN等隧道报文指定队列)等。

四、DPDK的应用

如果实现流分类,那么必须明白流是哪种类型,可这种类型从哪里得到呢?就需要解析数据报文的头,而在网卡中可以使用接收描述符来快速确定包的类型。在DPDK中的m_buf中定义有相关的对应字段来映射类型。

struct rte_mbuf
{.../** The packet type, which is the combination of outer/inner L2, L3, L4* and tunnel types. The packet_type is about data really present in the* mbuf. Example: if vlan stripping is enabled, a received vlan packet* would have RTE_PTYPE_L2_ETHER and not RTE_PTYPE_L2_VLAN because the* vlan is stripped from the data.*/RTE_STD_C11union {uint32_t packet_type; /**< L2/L3/L4 and tunnel information. */struct {uint32_t l2_type:4; /**< (Outer) L2 type. */uint32_t l3_type:4; /**< (Outer) L3 type. */uint32_t l4_type:4; /**< (Outer) L4 type. */uint32_t tun_type:4; /**< Tunnel type. */RTE_STD_C11union {uint8_t inner_esp_next_proto;/**< ESP next protocol type, valid if* RTE_PTYPE_TUNNEL_ESP tunnel type is set* on both Tx and Rx.*/__extension__struct {uint8_t inner_l2_type:4;/**< Inner L2 type. */uint8_t inner_l3_type:4;/**< Inner L3 type. */};};uint32_t inner_l4_type:4; /**< Inner L4 type. */};};...
};

有这个类型的处理就可以动态的快速把报文转到相关队列。不过需要注意的是,虽然主流的网卡都支持多队列,但并不是所有的都支持此功能。同样,即使支持,支持的程度也有不同。
DPDK中遇到的场景非常复杂,可能有的需要数据㔹发,有的需要命令处理有的需要逻辑转发等等不一而足。那么,对数据队列就可以动态的根据实际情况来进行定制。DPDK可以通过RSS实现负载均衡,然后再通过Flow Director实现报文的定向分配,通过绑定的专门的核心进行特定的数据队列处理。
另外DPDK中还可以通过虚拟化网卡来进行工作,实现动态的资源利用。

五、源码分析

先看一下相关的配置数据结构体定义:

//\dpdk-stable-19.11.14\lib\librte_ethdev
/*** A structure used to configure an Ethernet port.* Depending upon the RX multi-queue mode, extra advanced* configuration settings may be needed.*/
struct rte_eth_conf {uint32_t link_speeds; /**< bitmap of ETH_LINK_SPEED_XXX of speeds to beused. ETH_LINK_SPEED_FIXED disables linkautonegotiation, and a unique speed shall beset. Otherwise, the bitmap defines the set ofspeeds to be advertised. If the special valueETH_LINK_SPEED_AUTONEG (0) is used, all speedssupported are advertised. */struct rte_eth_rxmode rxmode; /**< Port RX configuration. */struct rte_eth_txmode txmode; /**< Port TX configuration. */uint32_t lpbk_mode; /**< Loopback operation mode. By default the valueis 0, meaning the loopback mode is disabled.Read the datasheet of given ethernet controllerfor details. The possible values of this fieldare defined in implementation of each driver. */struct {struct rte_eth_rss_conf rss_conf; /**< Port RSS configuration */struct rte_eth_vmdq_dcb_conf vmdq_dcb_conf;/**< Port vmdq+dcb configuration. */struct rte_eth_dcb_rx_conf dcb_rx_conf;/**< Port dcb RX configuration. */struct rte_eth_vmdq_rx_conf vmdq_rx_conf;/**< Port vmdq RX configuration. */} rx_adv_conf; /**< Port RX filtering configuration. */union {struct rte_eth_vmdq_dcb_tx_conf vmdq_dcb_tx_conf;/**< Port vmdq+dcb TX configuration. */struct rte_eth_dcb_tx_conf dcb_tx_conf;/**< Port dcb TX configuration. */struct rte_eth_vmdq_tx_conf vmdq_tx_conf;/**< Port vmdq TX configuration. */} tx_adv_conf; /**< Port TX DCB configuration (union). *//** Currently,Priority Flow Control(PFC) are supported,if DCB with PFCis needed,and the variable must be set ETH_DCB_PFC_SUPPORT. */uint32_t dcb_capability_en;struct rte_fdir_conf fdir_conf; /**< FDIR configuration. DEPRECATED */struct rte_intr_conf intr_conf; /**< Interrupt mode configuration. */
};

在这个结构定义中,可以看到intr_conf、fdir_conf和rx_adv_conf以及rxmode。而fdir_conf和rx_adv_conf就涉及Flow Director和RSS的配置。RSS分为三种即单一队列的RSS、带有硬件队列的RSS和还有消息中断的RSS(其实还是硬件不匹配造成的)。
在DPDK提供的例程l3fwd中,可以看到相关的配置代码:

int
main(int argc, char **argv)
{...//配置相关的RSS或FDstruct rte_eth_conf local_port_conf = port_conf;/* skip ports that are not enabled */if ((enabled_port_mask & (1 << portid)) == 0) {printf("\nSkipping disabled port %d\n", portid);continue;}/* init port */printf("Initializing port %d ... ", portid );fflush(stdout);nb_rx_queue = get_port_n_rx_queues(portid);n_tx_queue = nb_lcores;if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)n_tx_queue = MAX_TX_QUEUE_PER_PORT;printf("Creating queues: nb_rxq=%d nb_txq=%u... ",nb_rx_queue, (unsigned)n_tx_queue );ret = rte_eth_dev_info_get(portid, &dev_info);if (ret != 0)rte_exit(EXIT_FAILURE,"Error during getting device (port %u) info: %s\n",portid, strerror(-ret));if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)local_port_conf.txmode.offloads |=DEV_TX_OFFLOAD_MBUF_FAST_FREE;local_port_conf.rx_adv_conf.rss_conf.rss_hf &=dev_info.flow_type_rss_offloads;if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=port_conf.rx_adv_conf.rss_conf.rss_hf) {printf("Port %u modified RSS hash function based on hardware support,""requested:%#"PRIx64" configured:%#"PRIx64"\n",portid,port_conf.rx_adv_conf.rss_conf.rss_hf,local_port_conf.rx_adv_conf.rss_conf.rss_hf);}ret = rte_eth_dev_configure(portid, nb_rx_queue,(uint16_t)n_tx_queue, &local_port_conf);// 配置收发队列for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {if (rte_lcore_is_enabled(lcore_id) == 0)continue;if (numa_on)socketid =(uint8_t)rte_lcore_to_socket_id(lcore_id);elsesocketid = 0;printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);fflush(stdout);txconf = &dev_info.default_txconf;txconf->offloads = local_port_conf.txmode.offloads;ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,socketid, txconf);if (ret < 0)rte_exit(EXIT_FAILURE,"rte_eth_tx_queue_setup: err=%d, ""port=%d\n", ret, portid);qconf = &lcore_conf[lcore_id];qconf->tx_queue_id[portid] = queueid;queueid++;qconf->tx_port_id[qconf->n_tx_port] = portid;qconf->n_tx_port++;
}
...for(queue = 0; queue < qconf->n_rx_queue; ++queue) {struct rte_eth_rxconf rxq_conf;portid = qconf->rx_queue_list[queue].port_id;queueid = qconf->rx_queue_list[queue].queue_id;if (numa_on)socketid =(uint8_t)rte_lcore_to_socket_id(lcore_id);elsesocketid = 0;printf("rxq=%d,%d,%d ", portid, queueid, socketid);fflush(stdout);ret = rte_eth_dev_info_get(portid, &dev_info);if (ret != 0)rte_exit(EXIT_FAILURE,"Error during getting device (port %u) info: %s\n",portid, strerror(-ret));rxq_conf = dev_info.default_rxconf;rxq_conf.offloads = port_conf.rxmode.offloads;if (!per_port_pool)ret = rte_eth_rx_queue_setup(portid, queueid,nb_rxd, socketid,&rxq_conf,pktmbuf_pool[0][socketid]);elseret = rte_eth_rx_queue_setup(portid, queueid,nb_rxd, socketid,&rxq_conf,pktmbuf_pool[portid][socketid]);if (ret < 0)rte_exit(EXIT_FAILURE,"rte_eth_rx_queue_setup: err=%d, port=%d\n",ret, portid);
}
//启动设备
/* Start device */
ret = rte_eth_dev_start(portid);...
}

启动设备的函数在rte_ethdev.c:

int
rte_eth_dev_start(uint16_t port_id)
{struct rte_eth_dev *dev;struct rte_eth_dev_info dev_info;int diag;int ret;RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);dev = &rte_eth_devices[port_id];RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_start, -ENOTSUP);if (dev->data->dev_started != 0) {RTE_ETHDEV_LOG(INFO,"Device with port_id=%"PRIu16" already started\n",port_id);return 0;}ret = rte_eth_dev_info_get(port_id, &dev_info);if (ret != 0)return ret;/* Lets restore MAC now if device does not support live change */if (*dev_info.dev_flags & RTE_ETH_DEV_NOLIVE_MAC_ADDR)rte_eth_dev_mac_restore(dev, &dev_info);diag = (*dev->dev_ops->dev_start)(dev);if (diag == 0)dev->data->dev_started = 1;elsereturn eth_err(port_id, diag);ret = rte_eth_dev_config_restore(dev, &dev_info, port_id);if (ret != 0) {RTE_ETHDEV_LOG(ERR,"Error during restoring configuration for device (port %u): %s\n",port_id, rte_strerror(-ret));rte_eth_dev_stop(port_id);return ret;}if (dev->data->dev_conf.intr_conf.lsc == 0) {RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->link_update, -ENOTSUP);(*dev->dev_ops->link_update)(dev, 0);}return 0;
}

在DPDK使用RSS:

static struct rte_eth_conf port_conf = {.rxmode = {.mq_mode = ETH_MQ_RX_RSS,.max_rx_pkt_len = RTE_ETHER_MAX_LEN,.split_hdr_size = 0,.offloads = DEV_RX_OFFLOAD_CHECKSUM,},.rx_adv_conf = {.rss_conf = {.rss_key = NULL,.rss_hf = ETH_RSS_IP,},},.txmode = {.mq_mode = ETH_MQ_TX_NONE,},
};

使用Flow Director:

//\app\test-pmd
//cmd_flow_director_filter_parsed函数中
struct rte_eth_fdir_filter entry;
...
/* set to report FD ID by default */
entry.action.report_status = RTE_ETH_FDIR_REPORT_ID;
entry.action.rx_queue = res->queue_id;
entry.soft_id = res->fd_id_value;
if (!strcmp(res->ops, "add"))ret = rte_eth_dev_filter_ctrl(res->port_id, RTE_ETH_FILTER_FDIR,RTE_ETH_FILTER_ADD, &entry);
else if (!strcmp(res->ops, "del"))ret = rte_eth_dev_filter_ctrl(res->port_id, RTE_ETH_FILTER_FDIR,RTE_ETH_FILTER_DELETE, &entry);
elseret = rte_eth_dev_filter_ctrl(res->port_id, RTE_ETH_FILTER_FDIR,RTE_ETH_FILTER_UPDATE, &entry);
  • 注意,在DPDK21.11以上版本已经由Flow API代替了Flow Director接口。细节请参看相关文档。
    通过上面的分析就可以明白队列的控制相关流程,另外一些配置相关的,就需要大家自己多看相关文档了。

六、总结

涉及到硬件和OS,就需要看相关的厂商提供的文档和OS底层的配置。这里面有一个问题,特别是在硬件上,有些问题不是问题,而是规则。这个规则是否合理,就需要一个较长的时间来验证然后才可能更正。所以在DPDK这种和硬件亲和性较强的开发套件使用中,要有一个清醒的认识,不能一味的追求为什么?而到了规则的地方就按它的规则走就可以了。这不像纯软件,条条大路通罗马,实在不行可以硬来一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/228263.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python - coverage

coverage overage 是一个用于测量Python程序代码覆盖率的工具。它监视您的程序&#xff0c;注意代码的哪些部分已经执行&#xff0c;然后分析源代码&#xff0c;以确定哪些代码本可以执行&#xff0c;但没有执行。 覆盖率测量通常用于衡量测试的有效性。它可以显示代码的哪些…

整理了上百个开源中文大语言模型,涵盖模型、应用、数据集、微调、部署、评测

自ChatGPT为代表的大语言模型&#xff08;Large Language Model, LLM&#xff09;出现以后&#xff0c;由于其惊人的类通用人工智能&#xff08;AGI&#xff09;的能力&#xff0c;掀起了新一轮自然语言处理领域的研究和应用的浪潮。 尤其是以ChatGLM、LLaMA等平民玩家都能跑起…

抖音品牌力不足,如何开通抖音旗舰店?强开旗舰店全攻略来了!

随着直播的兴起&#xff0c;抖音电商在近年来的发展速度可谓是相当迅猛。越来越多的商家开始将重心投入到抖音电商。从开店、搭建直播间&#xff0c;起号&#xff0c;再到日常运营... 然而我们在第一步开店的时候&#xff0c;就遇到了不少麻烦。 1、选择开通抖音旗舰店&#x…

初识Flask

摆上中文版官方文档网站&#xff1a;https://flask.github.net.cn/quickstart.html 开启实验之路~~~~~~~~~~~~~ from flask import Flaskapp Flask(__name__) # 使用修饰器告诉flask触发函数的URL&#xff0c;绑定URL&#xff0c;后面的函数用于返回用户在浏览器上看到的内容…

Spring Cloud + Vue前后端分离-第5章 单表管理功能前后端开发

Spring Cloud Vue前后端分离-第5章 单表管理功能前后端开发 完成单表的增删改查 控台单表增删改查的前后端开发&#xff0c;重点学习前后端数据交互&#xff0c;vue ajax库axios的使用等 通用组件开发:分页、确认框、提示框、等待框等 常用的公共组件:确认框、提示框、等待…

系列九、事务

一、事务 1.1、概述 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即&#xff1a;这些操作要么同时成功&#xff0c;要么同时失败。 例如: 张三给李四转账1000块钱&…

使用邮件群发平台,轻松实现高效沟通的4大优势!

新媒体带动着众多线上平台的发展&#xff0c;使得流量为企业带来了可观的营收。但是&#xff0c;随着短视频市场的饱和&#xff0c;想要再次获得初始时的流量就变得越发困难。在这个时候&#xff0c;企业不妨将眼光往邮件群发这个传统的营销方式上倾斜&#xff0c;特别是出海、…

多进程间通信学习之共享内存

共享内存&#xff1a;1、在内核中创建共享内存&#xff1b;2、进程1和进程2都能够访问到&#xff0c;通过这段内存空间进行数据传递&#xff1b;3、共享内存是所有进程间通信方式中&#xff0c;效率最高&#xff0c;不需要在内核中往返进行拷贝&#xff1b;4、共享内存的内存空…

prometheus简介

什么是Prometheus Prometheus 是一个开源的服务监控系统和时序数据库&#xff0c;其提供了通用的数据模型和快捷数据采集、存储和查询接口。Prometheus的特点 多维数据模型&#xff1a;由度量名称和键值对标识的时间序列数据 时序数据&#xff0c;是在一段时间内通过重复测量&…

数据结构之---- 动态规划

数据结构之---- 动态规划 什么是动态规划&#xff1f; 动态规划是一个重要的算法范式&#xff0c;它将一个问题分解为一系列更小的子问题&#xff0c;并通过存储子问题的解来避免重复计算&#xff0c;从而大幅提升时间效率。 在本节中&#xff0c;我们从一个经典例题入手&am…

Web5:互联网发展的下一个前沿

最近发现了互联网的下一代发展“Web5”&#xff0c;这激起了我分享我对此的见解的兴趣。自 1993 年互联网诞生以来&#xff0c;它已经发生了很大的变化&#xff0c;为我们带来了大量很酷的应用程序&#xff0c;让我们的生活每天都变得更加美好。我们的想法是让互联网的使用变得…

盛最多水的容器

给定一个长度为 n 的整数列表 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容器。 示例1&…

Python基础01-环境搭建与输入输出

零、文章目录 Python基础01-环境搭建与输入输出 1、Python概述 &#xff08;1&#xff09;为什么要学习Python 技术趋势&#xff1a;Python自带明星属性&#xff0c;热度稳居编程语言界前三 简单易学&#xff1a;开发代码少&#xff0c;精确表达需求逻辑&#xff1b;33个关…

什么是Maven?

什么是Maven 1、Maven是依赖管理、项目构建工具。 pom.xml springBoot项目的核心配置文件&#xff0c;pom项目对象模型、Dependency依赖管理模型。 Maven中的GAVP是指&#xff1a; 1、GroupId&#xff1a;当前工程组织id&#xff0c;例如&#xff1a;com.jd.tddl 2、ArtifactI…

IS-IS原理与配置

IS-IS原理与配置 • IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP &#xff08;ConnectionL…

[ 8 种有效方法] 如何在没有备份的情况下恢复 Android 上永久删除的照片?

我们生命中最重要的时刻&#xff0c;但这样做有缺点&#xff0c;其中之一就是数据丢失的风险。您可能倾向于定期删除无意义的照片&#xff0c;同时保存可爱的照片&#xff0c;从而使您的 Android 设备井井有条。然而&#xff0c;有些人在删除自己珍视的图像时不小心犯了错误。您…

非递归方式遍历二叉树的原理

一、递归遍历代码 // 先序遍历 void PreOrder(BiTNode *T){if (T!NULL){visit(T); // 最简单的visit就是printf(T->data)PreOrder(T->lChild);PreOrder(T->rChild);} }// 中序遍历 void InOrder(BiTNode *T){if (T!NULL){InOrder(T->lchild);visit(T);InOrder(T-…

2312,生成编译命令

compile_commands.json包含各个目录的编译命令,它的生成: cmake: -DCMAKE_EXPORT_COMPILE_COMMANDSON;gn: gn gen . --export-compile-commandsninja ninja -t compdb cxx cc>compile_commands.json生成sphinx文档. 进入doc目录. 然后输入make html,然后等待即可!

Linux---文本搜索命令

1. grep命令的使用 命令说明grep文本搜索 grep命令效果图: 2. grep命令选项的使用 命令选项说明-i忽略大小写-n显示匹配行号-v显示不包含匹配文本的所有行 -i命令选项效果图: -n命令选项效果图: -v命令选项效果图: 3. grep命令结合正则表达式的使用 正则表达式说明^以指…