转: dpdk rte_memzone_reserve实现-smart_y-ChinaUnix博客
rte_memzone_reserve_aligned() 函数相当于rte_malloc, 前者返回的描述符, 后者返回可用内存地址. 详情再看下链接.
dpdk rte_memzone_reserve实现
——lvyilong316
这个函数的功能是创建一个rte_memzone(代码参考dpdk 16.11)。我们上一篇(http://blog.chinaunix.net/uid-28541347-id-5775056.html)讲rte_malloc分配的实际上是一个malloc_elem,而rte_memzone也是描述内存块的一种结构,本质上是一个malloc_elem的封装。rte_memzone和malloc_elem的另一个不同之处在于:
(1) 这个结构是静态分配好的,就是全局变量struct rte_mem_config.memzon[]这个数组中的元素,其个数是固定的。
(2) 因为全局变量struct rte_mem_config整个结构都不是存放在hugepage中的,所以memzone结构也不是在hugepage中。所以malloc_elem作为内存描述符位置是在所分配的内存块的最前端,但memzone自身结构的位置和其描述的内存块并没有关系,仅仅通过指针相关联。
函数的注意调用路径如下:
l rte_memzone_reserve
点击(此处)折叠或打开
- /*
- * name: memzone的名称
- * len: memzone的大小
- * socket_id: memzone申请内存所在的socket id
- * flag:描述这块内存特征的标识
- */
- const struct rte_memzone *
- rte_memzone_reserve(const char *name, size_t len, int socket_id,
- unsigned flags)
- {
- return rte_memzone_reserve_thread_safe(name, len, socket_id,
- flags, RTE_CACHE_LINE_SIZE, 0);
- }
注意这个函数的返回值,rte_malloc返回的是一个可用内存块的起始地址,而rte_memzone_reserve返回的是rte_memzone这个内存描符的地址。其中主要调用线程安全函数rte_memzone_reserve_thread_safe进行分配。
l rte_memzone_reserve_thread_safe
点击(此处)折叠或打开
- static const struct rte_memzone *
- rte_memzone_reserve_thread_safe(const char *name, size_t len,
- int socket_id, unsigned flags, unsigned align,
- unsigned bound)
- {
- struct rte_mem_config *mcfg;
- const struct rte_memzone *mz = NULL;
- /* get pointer to global configuration */
- /*获取全局变量rte_mem_config结构*/
- mcfg = rte_eal_get_configuration()->mem_config;
- rte_rwlock_write_lock(&mcfg->mlock);
- mz = memzone_reserve_aligned_thread_unsafe(
- name, len, socket_id, flags, align, bound);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return mz;
- }
这个函数之所以成为线程安全,是因为启动有加锁/解锁操作。为什么要有锁操作呢,因为要对全局变量rte_mem_config结构的memzone成员数组进行修改,为了防止多线程并发所以需要加锁。其主要功能由非线程安全函数memzone_reserve_aligned_thread_unsafe完成。
l memzone_reserve_aligned_thread_unsafe
点击(此处)折叠或打开
- static const struct rte_memzone *
- memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
- int socket_id, unsigned flags, unsigned align, unsigned bound)
- {
- struct rte_memzone *mz;
- struct rte_mem_config *mcfg;
- size_t requested_len;
- int socket, i;
- /* 获取全局变量rte_mem_config结构的指针 */
- mcfg = rte_eal_get_configuration()->mem_config;
- /* no more room in config */
- /*如果分配的memzone数量已经超过了最大值,则返错(数组大小是有限的)*/
- if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
- RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
- rte_errno = ENOSPC;
- return NULL;
- }
- /*检查memzone的名字长度是否超过了限制*/
- if (strlen(name) > sizeof(mz->name) - 1) {
- RTE_LOG(DEBUG, EAL, "%s(): memzone <%s>: name too long\n",
- __func__, name);
- rte_errno = ENAMETOOLONG;
- return NULL;
- }
- /* 在mcfg->memzone[]中查找是否已有同名的memzone,如果有表示已存在,返回创建出错*/
- if ((memzone_lookup_thread_unsafe(name)) != NULL) {
- RTE_LOG(DEBUG, EAL, "%s(): memzone <%s> already exists\n",
- __func__, name);
- rte_errno = EEXIST;
- return NULL;
- }
- /* 检查对齐内存大小是否是2的幂大小 */
- if (align && !rte_is_power_of_2(align)) {
- RTE_LOG(ERR, EAL, "%s(): Invalid alignment: %u\n", __func__,
- align);
- rte_errno = EINVAL;
- return NULL;
- }
- /* alignment less than cache size is not allowed */
- if (align < RTE_CACHE_LINE_SIZE)/*对齐大小不能小于cache_line大小*/
- align = RTE_CACHE_LINE_SIZE;
- /* align length on cache boundary. Check for overflow before doing so */
- if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
- rte_errno = EINVAL; /* requested size too big */
- return NULL;
- }
- len += RTE_CACHE_LINE_MASK;
- len &= ~((size_t) RTE_CACHE_LINE_MASK); /*申请内存大小进行内存对齐计算*/
- /* save minimal requested length */
- /*当申请的内存大小小于RTE_CACHE_LINE_SIZE时,则至少要分配RTE_CACHE_LINE_SIZE大小的内存*/
- requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
- /* check that boundary condition is valid */
- if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
- rte_errno = EINVAL;
- return NULL;
- }
- /*检查socket_id的合法性*/
- if ((socket_id != SOCKET_ID_ANY) && (socket_id >= RTE_MAX_NUMA_NODES)) {
- rte_errno = EINVAL;
- return NULL;
- }
- /*如果不使用hugepage,memzone的内存分配就不会考虑socke_id,而直接设置为SOCKET_ID_ANY*/
- if (!rte_eal_has_hugepages())
- socket_id = SOCKET_ID_ANY;
- if (len == 0) { /*申请内存大小等于0的情况,则申请申请最大的连续内存空间*/
- if (bound != 0)
- requested_len = bound;
- else {
- requested_len = find_heap_max_free_elem(&socket_id, align);
- if (requested_len == 0) {
- rte_errno = ENOMEM;
- return NULL;
- }
- }
- }
- /*如果socket_id为SOCKET_ID_ANY,则先在当前cpu所在的socket上分配内存*/
- if (socket_id == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_id;
- /* 尝试在当前socket对应的malloc_heap上分配内存 */
- void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket], NULL,
- requested_len, flags, align, bound);
- /*如果socket_id为SOCKET_ID_ANY,且在当前socket上分配失败,就尝试在其他cpu分配*/
- if ((mz_addr == NULL) && (socket_id == SOCKET_ID_ANY)) {
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- if (socket == i)
- continue;
- mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[i],
- NULL, requested_len, flags, align, bound);
- if (mz_addr != NULL)
- break;
- }
- }
- if (mz_addr == NULL) {
- rte_errno = ENOMEM;
- return NULL;
- }
- /*获取对应内存的malloc_elem结构*/
- const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
- /* 从mcfg->memzone[]中找到一个还为使用的memzone结构 */
- mz = get_next_free_memzone();
- if (mz == NULL) {
- RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
- "in config!\n", __func__);
- rte_errno = ENOSPC;
- return NULL;
- }
- /*增加mcfg的memzone计数*/
- mcfg->memzone_cnt++;
- snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = rte_malloc_virt2phy(mz_addr);
- mz->addr = mz_addr;
- mz->len = (requested_len == 0 ? elem->size : requested_len);
- mz->hugepage_sz = elem->ms->hugepage_sz;/*memzone对应的socketid和hupagesize即为对应malloc_elem的值*/
- mz->socket_id = elem->ms->socket_id;
- mz->flags = 0;
- mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
- return mz;
- }
其中主要是调用malloc_heap_alloc分配一个malloc_elem及对应内存块,这个函数已经在上一篇”rte_malloc实现”中分析过了,这里不再展开。malloc_heap_alloc的返回值赋值给mz->addr(memzone的addr成员),上一篇已经分析过malloc_heap_alloc的返回值就是可用内存块的起始地址,关于rte_memzone,malloc_mem,可用内存块三者的关系如下图所示。
其中调用的malloc_elem_from_data就是通过可用内存块的起始地址找到其前部的malloc_elem结构。而get_next_free_memzone就是遍历全局结构rte_mem_config的memzone成员数组,找到一个可用的memzone,可用的判断条件就是memzone->addr为NULL。最后赋上一张带有memzone的完整内存关系图。