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的完整内存关系图。
阅读(9182) | 评论(0) | 转发(1) |