dpdk mempool之代码流程

dpdk mempool之代码流程

对于创建mempool的流程,dpdk的运行逻辑是:
rte_mempool_create_empty
rte_mempool_set_ops_byname
rte_mempool_populate_default
rte_mempool_obj_iter
所以本篇就主要分为这四个函数来讲解其处理逻辑和工作流程。

rte_mempool_create_empty

创建一个空的mp,输入参数分别为:

const char *name mp的名字
unsigned n obj的数量,在这由于是创建空的mp,所以这里只是把n保存到mp的size而已,不申请实际obj内存
unsigned elt_size obj的大小
unsigned cache_size local cache的数量
unsigned private_data_size 私有数据空间大小
int socket_id 向指定socket下的DDR申请内存
unsigned flags mp的flag

  1. 判断rte_mempool和rte_mempool_cache是否cache line对齐,如果不是,则在编译的时候报错;
  2. 获取rte_mempool_tailq链表头,申请struct rte_tailq_entry *te节点,用来保存已经申请到的mp,方便查找系统已经申请到的mp;
  3. cache_size合法性判断,cache_size不能大于RTE_MEMPOOL_CACHE_MAX_SIZE,同时cache_size的1.5倍不能大于总的obj个数n
  4. 使用rte_mempool_calc_obj_size计算obj的大小, obj的大小至少等于sz->header_size + sz->elt_size + sz->trailer_size同时需要考虑cache line和channels/ranks的对齐
  5. 计算一个空mp需要的大小mempool_size,
    mempool_size = sizeof(struct rte_mempool)+
    (sizeof(struct rte_mempool_cache) * RTE_MAX_LCORE) +
    private_data_size
  6. 使用rte_memzone_reserve申请mp需要的内存,同时初始化mp的变量,mp->local_cache设置为 mp+sizeof(struct rte_mempool)

到此为止,rte_mempool_create_empty已经完成.

rte_mempool_set_ops_byname

使用名字给mp获取ops,这里的ops初始化在dpdk mempool之概念学习已经介绍过了,这里就不赘述了.

rte_mempool_populate_default

填充mp,说白了就是申请obj,填充到mp里面,这里是连接rte_mempool_create_empty的作用。

  1. 初始化ops,前面对mp的ops操作都是注册和变量赋值,还没有真正调用函数,所以真正调用函数就是在这个函数里面实现。
mempool_ops_alloc_once
    ops->alloc

.alloc = common_ring_alloc,
common_ring_alloc
rte_ring_create

大致调用逻辑如下,就是使用初始化mp的ring,将其保存到mp->pool_data里面。

  1. 输入参数合理性判断,比如mp->socket_id和mp->nb_mem_chunks

  2. 申请mem_size,一个mem_size包含n个obj的大小,最后使用rte_mempool_populate_iova给每一个memhdr和obj赋值,将其设置到mp对应的链表里面同时把obj enqueue到ring里面去,允许用户获取。

rte_mempool_populate_iova
    rte_mempool_ops_populate
        rte_mempool_op_populate_default 
            mempool_add_elem
            rte_mempool_ops_enqueue_bulk

这个时候用户去从mp申请资源,走的通用接口如下:
rte_mempool_generic_get
get的时候,会优先从对应mp的cache里面获取obj,如下所示,
先从自己的core的cache拿到cache控制块&mp->local_cache[lcore_id],然后在__mempool_generic_get的时候,
判断cache->len当前的容量是否满足获取的个数,不够的话,再往cache里面增加obj,
最后再从cache里面获取obj。

static __rte_always_inline int
rte_mempool_get_bulk(struct rte_mempool *mp, void **obj_table, unsigned int n)
{
	struct rte_mempool_cache *cache;
	cache = rte_mempool_default_cache(mp, rte_lcore_id());
	return rte_mempool_generic_get(mp, obj_table, n, cache);
}
static __rte_always_inline int
__mempool_generic_get(struct rte_mempool *mp, void **obj_table,
		      unsigned int n, struct rte_mempool_cache *cache)
{
	int ret;
	uint32_t index, len;
	void **cache_objs;

	/* No cache provided or cannot be satisfied from cache */
	if (unlikely(cache == NULL || n >= cache->size))
		goto ring_dequeue;

	cache_objs = cache->objs;

	/* Can this be satisfied from the cache? */
	if (cache->len < n) {
		/* No. Backfill the cache first, and then fill from it */
		uint32_t req = n + (cache->size - cache->len);

		/* How many do we require i.e. number to fill the cache + the request */
		ret = rte_mempool_ops_dequeue_bulk(mp,
			&cache->objs[cache->len], req);
		if (unlikely(ret < 0)) {
			/*
			 * In the offchance that we are buffer constrained,
			 * where we are not able to allocate cache + n, go to
			 * the ring directly. If that fails, we are truly out of
			 * buffers.
			 */
			goto ring_dequeue;
		}

		cache->len += req;
	}

	/* Now fill in the response ... */
	for (index = 0, len = cache->len - 1; index < n; ++index, len--, obj_table++)
		*obj_table = cache_objs[len];

	cache->len -= n;

	__MEMPOOL_STAT_ADD(mp, get_success, n);

	return 0;

ring_dequeue:

	/* get remaining objects from ring */
	ret = rte_mempool_ops_dequeue_bulk(mp, obj_table, n);

	if (ret < 0)
		__MEMPOOL_STAT_ADD(mp, get_fail, n);
	else
		__MEMPOOL_STAT_ADD(mp, get_success, n);

	return ret;
}

rte_mempool_generic_put
put操作和get操作相反,这里就不多说了。

rte_mempool_obj_iter

主要是运行用户自定义函数,初始化obj

DPDK(Data Plane Development Kit)是一个高性能的网络数据包处理库,它直接操作硬件,提高了网络应用的性能。如果你想用DPDK编写两个机器间通信的代码,首先你需要设置好DPDK环境,并理解其基本的数据包操作流程。以下是一个简化的示例,展示了如何使用DPDK在两台机器之间发送和接收数据包: ```c // 引入必要的头文件 #include <rte_malloc.h> // 动态内存分配 #include <rte_mempool.h> // 内存池管理 #include <rte_ethdev.h> // 网络设备操作 #include <rte_eal.h> // DPDK全局环境初始化 #include <rte_per_lcore.h> // 核心相关操作 #include <rte_launch.h> // 并行任务启动 #include <rte_cycles.h> // 计数器操作 // 定义用于存放数据包的结构体 struct rte_mbuf *mbuf; void send_packet(void *arg) { // 获取网络设备句柄 uint16_t dev_id = *(uint16_t*)arg; rte_eth_dev_t *dev = rte_eth_dev_get_by_id(dev_id); // 创建内存池 struct rte_mempool *mp = rte_pktmbuf_pool_create("mempool", ..., RTE_MBUF_DEFAULT_POOL_SIZE, ..., rte_socket_id()); // 设置数据包发送队列 rte_eth_tx_queue_setup(dev->data->port_id, ...); // 发送数据包 mbuf = rte_pktmbuf_alloc(mp); rte_memcpy(rte_pktmbuf_mtod(mbuf, void*), your_data, your_data_size); rte_eth transmit_packet(dev, ..., mbuf); } void receive_packet(void *arg) { // ...类似send_packet函数,处理接收到的数据包 // 例如解析数据包、存储到某个缓冲区等 } int main(int argc, char **argv) { // 初始化DPDK rte_eal_init(argc, argv); // 分配核心 rte_mp_machine_id_t my_machine_id = rte_lcore_id(); rte_mp_task_id_t task_id = rte_mp_current_task_id(); // 配置发送端和接收端的设备ID uint16_t sender_dev_id = ...; uint16_t receiver_dev_id = ...; // 启动并行任务 rte_mp_launch(send_packet, &sender_dev_id, MP_THREAD_COUNT, 0); rte_mp_wait_all(); // 同理,在接收端启动receive_packet任务 // ... // 清理资源 rte_pktmbuf_free(mbuf); rte_mempool_free(mp); rte_eth_dev_free(dev); // 结束DPDK初始化 rte_eal_cleanup(); return 0; } ``` 请注意,上述代码是一个基础框架,实际的代码会包含更多的错误检查和详细配置步骤。同时,你需要根据具体的网络架构(例如交换机配置)调整数据包发送和接收的细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值