dpdk rte_memzone_reserve实现

9140阅读 0评论2019-12-31 lvyilong316
分类:LINUX

dpdk rte_memzone_reserve实现

——lvyilong316

这个函数的功能是创建一个rte_memzone(代码参考dpdk 16.11)。我们上一篇http://blog.chinaunix.net/uid-28541347-id-5775056.htmlrte_malloc分配的实际上是一个malloc_elem,而rte_memzone也是描述内存块的一种结构,本质上是一个malloc_elem的封装rte_memzonemalloc_elem的另一个不同之处在于:

(1) 这个结构是静态分配好的,就是全局变量struct rte_mem_config.memzon[]这个数组中的元素,其个数是固定的。

(2) 因为全局变量struct rte_mem_config整个结构都不是存放在hugepage中的,所以memzone结构也不是在hugepage中。所以malloc_elem作为内存描述符位置是在所分配的内存块的最前端,但memzone自身结构的位置和其描述的内存块并没有关系,仅仅通过指针相关联。

函数的注意调用路径如下:

l rte_memzone_reserve

点击(此处)折叠或打开

  1. /*
  2. * name: memzone的名称
  3. * len: memzone的大小
  4. * socket_id: memzone申请内存所在的socket id
  5. * flag:描述这块内存特征的标识
  6. */
  7. const struct rte_memzone *
  8. rte_memzone_reserve(const char *name, size_t len, int socket_id,
  9.          unsigned flags)
  10. {
  11.     return rte_memzone_reserve_thread_safe(name, len, socket_id,
  12.                      flags, RTE_CACHE_LINE_SIZE, 0);
  13. }


    注意这个函数的返回值,rte_malloc返回的是一个可用内存块的起始地址,而rte_memzone_reserve返回的是rte_memzone这个内存描符的地址。其中主要调用线程安全函数rte_memzone_reserve_thread_safe进行分配。

l rte_memzone_reserve_thread_safe

点击(此处)折叠或打开

  1. static const struct rte_memzone *
  2. rte_memzone_reserve_thread_safe(const char *name, size_t len,
  3.                 int socket_id, unsigned flags, unsigned align,
  4.                 unsigned bound)
  5. {
  6.     struct rte_mem_config *mcfg;
  7.     const struct rte_memzone *mz = NULL;

  8.     /* get pointer to global configuration */
  9.     /*获取全局变量rte_mem_config结构*/
  10.     mcfg = rte_eal_get_configuration()->mem_config;

  11.     rte_rwlock_write_lock(&mcfg->mlock);

  12.     mz = memzone_reserve_aligned_thread_unsafe(
  13.         name, len, socket_id, flags, align, bound);

  14.     rte_rwlock_write_unlock(&mcfg->mlock);

  15.     return mz;
  16. }
      这个函数之所以成为线程安全,是因为启动有加锁/解锁操作。为什么要有锁操作呢,因为要对全局变量rte_mem_config结构的memzone成员数组进行修改,为了防止多线程并发所以需要加锁。其主要功能由非线程安全函数memzone_reserve_aligned_thread_unsafe完成。


l memzone_reserve_aligned_thread_unsafe

点击(此处)折叠或打开

  1. static const struct rte_memzone *
  2. memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
  3.         int socket_id, unsigned flags, unsigned align, unsigned bound)
  4. {
  5.     struct rte_memzone *mz;
  6.     struct rte_mem_config *mcfg;
  7.     size_t requested_len;
  8.     int socket, i;

  9.     /* 获取全局变量rte_mem_config结构的指针 */
  10.     mcfg = rte_eal_get_configuration()->mem_config;

  11.     /* no more room in config */
  12.     /*如果分配的memzone数量已经超过了最大值,则返错(数组大小是有限的)*/
  13.     if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
  14.         RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
  15.         rte_errno = ENOSPC;
  16.         return NULL;
  17.     }
  18.     /*检查memzone的名字长度是否超过了限制*/
  19.     if (strlen(name) > sizeof(mz->name) - 1) {
  20.         RTE_LOG(DEBUG, EAL, "%s(): memzone <%s>: name too long\n",
  21.             __func__, name);
  22.         rte_errno = ENAMETOOLONG;
  23.         return NULL;
  24.     }

  25.     /* 在mcfg->memzone[]中查找是否已有同名的memzone,如果有表示已存在,返回创建出错*/
  26.     if ((memzone_lookup_thread_unsafe(name)) != NULL) {
  27.         RTE_LOG(DEBUG, EAL, "%s(): memzone <%s> already exists\n",
  28.             __func__, name);
  29.         rte_errno = EEXIST;
  30.         return NULL;
  31.     }

  32.     /* 检查对齐内存大小是否是2的幂大小 */
  33.     if (align && !rte_is_power_of_2(align)) {
  34.         RTE_LOG(ERR, EAL, "%s(): Invalid alignment: %u\n", __func__,
  35.                 align);
  36.         rte_errno = EINVAL;
  37.         return NULL;
  38.     }

  39.     /* alignment less than cache size is not allowed */
  40.     if (align < RTE_CACHE_LINE_SIZE)/*对齐大小不能小于cache_line大小*/
  41.         align = RTE_CACHE_LINE_SIZE;

  42.     /* align length on cache boundary. Check for overflow before doing so */
  43.     if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
  44.         rte_errno = EINVAL; /* requested size too big */
  45.         return NULL;
  46.     }

  47.     len += RTE_CACHE_LINE_MASK;
  48.     len &= ~((size_t) RTE_CACHE_LINE_MASK); /*申请内存大小进行内存对齐计算*/

  49.     /* save minimal requested length */
  50.     /*当申请的内存大小小于RTE_CACHE_LINE_SIZE时,则至少要分配RTE_CACHE_LINE_SIZE大小的内存*/
  51.     requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);

  52.     /* check that boundary condition is valid */
  53.     if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
  54.         rte_errno = EINVAL;
  55.         return NULL;
  56.     }
  57.     /*检查socket_id的合法性*/
  58.     if ((socket_id != SOCKET_ID_ANY) && (socket_id >= RTE_MAX_NUMA_NODES)) {
  59.         rte_errno = EINVAL;
  60.         return NULL;
  61.     }
  62.     /*如果不使用hugepage,memzone的内存分配就不会考虑socke_id,而直接设置为SOCKET_ID_ANY*/
  63.     if (!rte_eal_has_hugepages())
  64.         socket_id = SOCKET_ID_ANY;

  65.     if (len == 0) { /*申请内存大小等于0的情况,则申请申请最大的连续内存空间*/
  66.         if (bound != 0)
  67.             requested_len = bound;
  68.         else {
  69.             requested_len = find_heap_max_free_elem(&socket_id, align);
  70.             if (requested_len == 0) {
  71.                 rte_errno = ENOMEM;
  72.                 return NULL;
  73.             }
  74.         }
  75.     }
  76.     /*如果socket_id为SOCKET_ID_ANY,则先在当前cpu所在的socket上分配内存*/
  77.     if (socket_id == SOCKET_ID_ANY)
  78.         socket = malloc_get_numa_socket();
  79.     else
  80.         socket = socket_id;

  81.     /* 尝试在当前socket对应的malloc_heap上分配内存 */
  82.     void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket], NULL,
  83.             requested_len, flags, align, bound);
  84.     /*如果socket_id为SOCKET_ID_ANY,且在当前socket上分配失败,就尝试在其他cpu分配*/
  85.     if ((mz_addr == NULL) && (socket_id == SOCKET_ID_ANY)) {
  86.         /* try other heaps */
  87.         for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
  88.             if (socket == i)
  89.                 continue;

  90.             mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[i],
  91.                     NULL, requested_len, flags, align, bound);
  92.             if (mz_addr != NULL)
  93.                 break;
  94.         }
  95.     }

  96.     if (mz_addr == NULL) {
  97.         rte_errno = ENOMEM;
  98.         return NULL;
  99.     }
  100.     /*获取对应内存的malloc_elem结构*/
  101.     const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);

  102.     /* 从mcfg->memzone[]中找到一个还为使用的memzone结构 */
  103.     mz = get_next_free_memzone();

  104.     if (mz == NULL) {
  105.         RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
  106.                 "in config!\n", __func__);
  107.         rte_errno = ENOSPC;
  108.         return NULL;
  109.     }
  110.     /*增加mcfg的memzone计数*/
  111.     mcfg->memzone_cnt++;
  112.     snprintf(mz->name, sizeof(mz->name), "%s", name);
  113.     mz->phys_addr = rte_malloc_virt2phy(mz_addr);
  114.     mz->addr = mz_addr;
  115.     mz->len = (requested_len == 0 ? elem->size : requested_len);
  116.     mz->hugepage_sz = elem->ms->hugepage_sz;/*memzone对应的socketid和hupagesize即为对应malloc_elem的值*/
  117.     mz->socket_id = elem->ms->socket_id;
  118.     mz->flags = 0;
  119.     mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;

  120.     return mz;
  121. }


    其中主要是调用malloc_heap_alloc分配一个malloc_elem及对应内存块,这个函数已经在上一篇rte_malloc实现中分析过了,这里不再展开。malloc_heap_alloc的返回值赋值给mz->addrmemzoneaddr成员),上一篇已经分析过malloc_heap_alloc的返回值就是可用内存块的起始地址,关于rte_memzonemalloc_mem,可用内存块三者的关系如下图所示。

其中调用的malloc_elem_from_data就是通过可用内存块的起始地址找到其前部的malloc_elem结构。而get_next_free_memzone就是遍历全局结构rte_mem_configmemzone成员数组,找到一个可用的memzone,可用的判断条件就是memzone->addrNULL。最后赋上一张带有memzone的完整内存关系图。

上一篇:Off-CPU 性能分析
下一篇:Cache的基本知识