一、文件组织
rte_mempool.h:mempool类的属性、方法
rte_mempool.c:mempool对象的创建实现、mempool对象与ring对象(默认)的联系。
rte_mempool_ops.c:mempool对象的操作方法定义
rte_mempool_ring.c:mempoolring类型的操作方法
rte_mempool_stack.c:mempool stack类型的操作方法
二、mempool功能和结构
1.功能
对于提前申请的内存对象可以并发无锁的申请和释放(消费/生产、出队/入队)。当然可以使用栈的方式操作内存对象。
为了减少多核访问造成的冲突,引入了local_cache对象缓冲区。该local_cache非硬件上的cache,而是为了减少多核访问ring造成的临界区访问,coreX app会优先访问该local_cache上的对象。
入队的时候优先入local_cache中,出队的时候优先从local_cache中出队。
2.结构
mempool结构图:
三、使用方法
1、 创建mempool:rte_mempool_create
2、 使用mempool方法操作mempool对象:例如出队列,rte_mempool_get(mp,obj_p);
四、代码解析
mempool结构图:
1、mempool结构解析
objhdr_list:把每个objhdr联系起来。可以通过轮询objhdr_list链表,进而轮询每个对象,无论对象是否在ring中,统一管理对象。
memhdr_list:把每个memzone块联系起来。
在每个obj前加obj_hdr意图:可以通过ring中出队的obj,找到对应mempool、和对应obj_tlr。
2、创建一个mempool的流程
创建一个mempool尾队列的te头
创建一个mempool,hdr+local_cache+trl,并和te的data项联系起来
设置mempool的操作方法索引
申请一个mempool的操作对象(默认是ring),并将其和mempool联系起来
申请memzone内存,把内存对象移植到mempool中(obj_list、mem_list),再用mempool的入队方法(默认ring的入队方法)把内存对象入队到ring中
3、出(入)队的流程
找到local_cache
如果找到的local_cache为空,或者为单消费者,或者出队的数量比初始化的cache大(local_cache为空或者为单生产者),则从ring中直接出(入)队
否者,从local_cache(数量不足时,从ring中补足)出(入)队。
4、关键函数
1、rte_mempool_create_empty()
在rte_mempool_list链表中创建一个节点mempool_tailq_entry,再创建一个*data的实体(rte_mempool + local_cache + private_data),即mempool的数据结构。
2、rte_mempool_populate_phys()
通过已知地址连续的地址,分割出尽可能多的对象(obj_hdr + obj_body + obj_trl)。把obj_body入队到ring中;obj_hdr加入到rte_mempool_objhdr_list链表中;一块地址连续的地址块加入到rte_mempool_memhdr_list链表中。
3、optimize_object_size()
保证rank总数个连续的对象读写,都会落在不同的rank上
根据:两个互质数a,b为了使aX=bY等式成立,那么X最小的整数值是b,Y最小的整数值是a。推出的一个公式,可以自己验证。
==============================================================================================
DPDK以两种方式对外提供内存管理方法,一个是rte_mempool,主要用于网卡数据包的收发;一个是rte_malloc,主要为应用程序提供内存使用接口。本文讨论rte_mempool。rte_mempool由函数rte_mempool_create()负责创建,从rte_config.mem_config->free_memseg[]中取出合适大小的内存,放到rte_config.mem_config->memzone[]中。
本文中,以l2fwd为例,说明rte_mempool的创建及使用。
一、rte_mempool的创建
1 l2fwd_pktmbuf_pool =
2 rte_mempool_create("mbuf_pool", NB_MBUF,
3 MBUF_SIZE, 32,
4 sizeof(struct rte_pktmbuf_pool_private),
5 rte_pktmbuf_pool_init, NULL,
6 rte_pktmbuf_init, NULL,
7 rte_socket_id(), 0)
“mbuf_pool”:创建的rte_mempool的名称。
NB_MBUF:rte_mempool包含的rte_mbuf元素的个数。
MBUF_SIZE:每个rte_mbuf元素的大小。
1 #define RTE_PKTMBUF_HEADROOM 128
2 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
3 #define NB_MBUF 8192
1 struct rte_pktmbuf_pool_private {
2 uint16_t mbuf_data_room_size; /**< Size of data space in each mbuf.*/
3 };
rte_mempool由函数rte_mempool_create()负责创建。首先创建rte_ring,再创建rte_mempool,并建立两者之间的关联。
1、rte_ring_create()创建rte_ring无锁队列
1 r = rte_ring_create(rg_name, rte_align32pow2(n+1), socket_id, rg_flags);
具体步骤如下:
a、需要保证创建的队列数可以被2整除,即,count = rte_align32pow2(n + 1);
b、计算需要为count个队列分配的内存空间,即,ring_size = count * sizeof(void *) + sizeof(struct rte_ring);
struct rte_ring的数据结构如下,
1 struct rte_ring {
2 TAILQ_ENTRY(rte_ring) next; /**< Next in list. */
3
4 char name[RTE_RING_NAMESIZE]; /**< Name of the ring. */
5 int flags; /**< Flags supplied at creation. */
6
7 /** Ring producer status. */
8 struct prod {
9 uint32_t watermark; /**< Maximum items before EDQUOT. */
10 uint32_t sp_enqueue; /**< True, if single producer. */
11 uint32_t size; /**< Size of ring. */
12 uint32_t mask; /**< Mask (size-1) of ring. */
13 volatile uint32_t head; /**< Producer head. */
14 volatile uint32_t tail; /**< Producer tail. */
15 } prod __rte_cache_aligned;
16
17 /** Ring consumer status. */
18 struct cons {
19 uint32_t sc_dequeue; /**< True, if single consumer. */
20 uint32_t size; /**< Size of the ring. */
21 uint32_t mask; /**< Mask (size-1) of ring. */
22 volatile uint32_t head; /**< Consumer head. */
23 volatile uint32_t tail; /**< Consumer tail. */
24 #ifdef RTE_RING_SPLIT_PROD_CONS
25 } cons __rte_cache_aligned;
26 #else
27 } cons;
28 #endif
29
30 #ifdef RTE_LIBRTE_RING_DEBUG
31 struct rte_ring_debug_stats stats[RTE_MAX_LCORE];
32 #endif
33
34 void * ring[0] __rte_cache_aligned; /**< Memory space of ring starts here.
35 * not volatile so need to be careful
36 * about compiler re-ordering */
37 };
c、调用rte_memzone_reserve(),在rte_config.mem_config->free_memseg[]中查找一个合适的free_memseg(查找规则是free_memseg中剩余内存大于等于需要分配的内存,但是多余的部分是最小的),从该free_memseg中分配指定大小的内存,然后将分配的内存记录在rte_config.mem_config->memzone[]中。
d、初始化新分配的rte_ring。
1 r->flags = flags;
2 r->prod.watermark = count;
3 r->prod.sp_enqueue = !!(flags & RING_F_SP_ENQ);
4 r->cons.sc_dequeue = !!(flags & RING_F_SC_DEQ);
5 r->prod.size = r->cons.size = count;
6 r->prod.mask = r->cons.mask = count-1;
7 r->prod.head = r->cons.head = 0;
8 r->prod.tail = r->cons.tail = 0;
9
10 TAILQ_INSERT_TAIL(ring_list, r, next); // 挂到rte_config.mem_config->tailq_head[RTE_TAILQ_RING]队列中
2、创建并初始化rte_mempool
a、计算需要为rte_mempool申请的内存空间。包含:sizeof(struct rte_mempool)、private_data_size,以及n * objsz.total_size。
1 mempool_size = MEMPOOL_HEADER_SIZE(mp, pg_num) + private_data_size;
2 if (vaddr == NULL)
3 mempool_size += (size_t)objsz.total_size * n;
objsz.total_size = objsz.header_size + objsz.elt_size + objsz.trailer_size; 其中,
objsz.header_size = sizeof(struct rte_mempool *);
objsz.elt_size = MBUF_SIZE;
objsz.trailer_size = ????
b、调用rte_memzone_reserve(),在rte_config.mem_config->free_memseg[]中查找一个合适的free_memseg,在该free_memseg中分配mempool_size大小的内存,然后将新分配的内存记录到rte_config.mem_config->memzone[]中。
c、初始化新创建的rte_mempool,并调用rte_pktmbuf_pool_init()初始化rte_mempool的私有数据结构。
1 /* init the mempool structure */
2 mp = mz->addr;
3 memset(mp, 0, sizeof(*mp));
4 snprintf(mp->name, sizeof(mp->name), "%s", name);
5 mp->phys_addr = mz->phys_addr;
6 mp->ring = r;
7 mp->size = n;
8 mp->flags = flags;
9 mp->elt_size = objsz.elt_size;
10 mp->header_size = objsz.header_size;
11 mp->trailer_size = objsz.trailer_size;
12 mp->cache_size = cache_size;
13 mp->cache_flushthresh = (uint32_t)
14 (cache_size * CACHE_FLUSHTHRESH_MULTIPLIER);
15 mp->private_data_size = private_data_size;
16
17 /* calculate address of the first element for continuous mempool. */
18 obj = (char *)mp + MEMPOOL_HEADER_SIZE(mp, pg_num) +
19 private_data_size;
20
21 /* populate address translation fields. */
22 mp->pg_num = pg_num;
23 mp->pg_shift = pg_shift;
24 mp->pg_mask = RTE_LEN2MASK(mp->pg_shift, typeof(mp->pg_mask));
25
26 /* mempool elements allocated together with mempool */
27 mp->elt_va_start = (uintptr_t)obj;
28 mp->elt_pa[0] = mp->phys_addr +
29 (mp->elt_va_start - (uintptr_t)mp);
30
31 mp->elt_va_end = mp->elt_va_start;
32
33 RTE_EAL_TAILQ_INSERT_TAIL(RTE_TAILQ_MEMPOOL, rte_mempool_list, mp); //挂到rte_config.mem_config->tailq_head[RTE_TAILQ_MEMPOOL]队列中
d、调用mempool_populate(),以及rte_pktmbuf_init()初始化rte_mempool的每个rte_mbuf元素。
3、总结
相关数据结构的关联关系如下图: