DPDK17.02内存池管理1 – rte_mempool_create_empty
该函数定义一个空的内存池,只有头部,未分配实际的数据内存空间。
1、数据结构
struct rte_mempool 结构的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | struct rte_mempool { char name[RTE_MEMZONE_NAMESIZE]; /**< 内存池的名字,全局唯一 */ RTE_STD_C11 union { void *pool_data; /**< 用来存储对象的ring或pool */ uint64_t pool_id; /**< 外部内存池id */ }; void *pool_config; /**< optional args for ops alloc. */ const struct rte_memzone *mz; /**< 指向对应的memzone */ int flags; /**< 内存池的标记 */ int socket_id; /**< Socket id passed at create. 创建时传入的 socket id */ uint32_t size; /**< 内存池的最大大小 */ uint32_t cache_size; /**< 每个核的默认本地cache的大小*/ uint32_t elt_size; /**< 内存池中的元素的大小 */ uint32_t header_size; /**< 内存池中元素之前的 header 的大小 */ uint32_t trailer_size; /**< 内存池中元素之后的 trailer 的大小 */ unsigned private_data_size; /**< 私有数据结构的大小 */ /** * 内存池的 rte_mempool_ops_table 数组的索引,包含回调函数指针。 * 这里使用索引而不是指向回调函数的指针,使任意次进程可以方便地使用该内存池。 */ int32_t ops_index; struct rte_mempool_cache *local_cache; /**< 针对每个核的cache */ uint32_t populated_size; /**< populated 填充对象的数量. */ struct rte_mempool_objhdr_list elt_list; /**< 内存池中的对象列表 */ uint32_t nb_mem_chunks; /**< 内存块(chunks)的数量 */ struct rte_mempool_memhdr_list mem_list; /**< 内存块列表 */ #ifdef RTE_LIBRTE_MEMPOOL_DEBUG /** 如果编译成调试模式,则定义每个核的编译数据 */ struct rte_mempool_debug_stats stats[RTE_MAX_LCORE]; #endif } __rte_cache_aligned; |
结构关系如下:
其中 RTE_MEMPOOL_CACHE_MAX_SIZE 的默认值为512,RTE_MAX_LCORE 默认值通常为64。
这两个值可以在编译前在配置文件中修改。
2、rte_mempool_create_empty() 函数
该函数创建一个空的内存池,只包含内存池的管理头。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | struct rte_mempool * rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size, unsigned cache_size, unsigned private_data_size, int socket_id, unsigned flags) { char mz_name[RTE_MEMZONE_NAMESIZE]; struct rte_mempool_list *mempool_list; struct rte_mempool *mp = NULL; struct rte_tailq_entry *te = NULL; const struct rte_memzone *mz = NULL; size_t mempool_size; int mz_flags = RTE_MEMZONE_1GB|RTE_MEMZONE_SIZE_HINT_ONLY; struct rte_mempool_objsz objsz; /* 用来保存内存池中元素的大小 */ unsigned lcore_id; int ret; /* 编译时进行检查,这些数据结构的大小必须是 cacheline 对齐的 */ RTE_BUILD_BUG_ON((sizeof(struct rte_mempool) & RTE_CACHE_LINE_MASK) != 0); RTE_BUILD_BUG_ON((sizeof(struct rte_mempool_cache) & RTE_CACHE_LINE_MASK) != 0); #ifdef RTE_LIBRTE_MEMPOOL_DEBUG RTE_BUILD_BUG_ON((sizeof(struct rte_mempool_debug_stats) & RTE_CACHE_LINE_MASK) != 0); RTE_BUILD_BUG_ON((offsetof(struct rte_mempool, stats) & RTE_CACHE_LINE_MASK) != 0); #endif /* 内存池列表 */ mempool_list = RTE_TAILQ_CAST(rte_mempool_tailq.head, rte_mempool_list); /* 要申请的 cache 太大 */ if (cache_size > RTE_MEMPOOL_CACHE_MAX_SIZE || CALC_CACHE_FLUSHTHRESH(cache_size) > n) { rte_errno = EINVAL; return NULL; } /* 如果设置了非cacheline 对齐,也就不使用 SPREAD 特性 */ if (flags & MEMPOOL_F_NO_CACHE_ALIGN) flags |= MEMPOOL_F_NO_SPREAD; /* 计算内存池对象的(经过对齐调整等)大小, * 包括 header,对象本身,trailer 等,保存到objsz中 */ if (!rte_mempool_calc_obj_size(elt_size, flags, &objsz)) { rte_errno = EINVAL; return NULL; } rte_rwlock_write_lock(RTE_EAL_MEMPOOL_RWLOCK); /* 内存池的私有数据大小,确保是cache alined */ private_data_size = (private_data_size + RTE_MEMPOOL_ALIGN_MASK) & (~RTE_MEMPOOL_ALIGN_MASK); /* 分配一个队列结点,后续通过该结点将函数中分配的内存池头关联到内存池队列 */ te = rte_zmalloc("MEMPOOL_TAILQ_ENTRY", sizeof(*te), 0); if (te == NULL) { RTE_LOG(ERR, MEMPOOL, "Cannot allocate tailq entry!\n"); goto exit_unlock; } /* 计算 mp 头的大小,如果设置了 cache,则加上缓存的大小 */ mempool_size = MEMPOOL_HEADER_SIZE(mp, cache_size); /* 加上私有数据的大小,通常为 struct rte_pktmbuf_pool_private 结构的大小 */ mempool_size += private_data_size; mempool_size = RTE_ALIGN_CEIL(mempool_size, RTE_MEMPOOL_ALIGN); /* 对齐 */ /* 内存池的名字 MP_<name> */ ret = snprintf(mz_name, sizeof(mz_name), RTE_MEMPOOL_MZ_FORMAT, name); if (ret < 0 || ret >= (int)sizeof(mz_name)) { rte_errno = ENAMETOOLONG; goto exit_unlock; } /* 为内存池的头部(大小为mempool_size)在heap中分配内存 */ mz = rte_memzone_reserve(mz_name, mempool_size, socket_id, mz_flags); if (mz == NULL) goto exit_unlock; /* init the mempool structure */ mp = mz->addr; memset(mp, 0, MEMPOOL_HEADER_SIZE(mp, cache_size)); ret = snprintf(mp->name, sizeof(mp->name), "%s", name); if (ret < 0 || ret >= (int)sizeof(mp->name)) { rte_errno = ENAMETOOLONG; goto exit_unlock; } mp->mz = mz; /* 将内存池头部关联到 memzone */ mp->size = n; /* 内存池中对象的数量 */ mp->flags = flags; mp->socket_id = socket_id; mp->elt_size = objsz.elt_size; mp->header_size = objsz.header_size; mp->trailer_size = objsz.trailer_size; /* 设置默认的 cache 大小,如果为0表示禁用 cache */ mp->cache_size = cache_size; mp->private_data_size = private_data_size; STAILQ_INIT(&mp->elt_list); STAILQ_INIT(&mp->mem_list); /* 将local_cache 指向对应的空间,紧跟在 struct mempool 结构的的后面 */ mp->local_cache = (struct rte_mempool_cache *) RTE_PTR_ADD(mp, MEMPOOL_HEADER_SIZE(mp, 0)); /* 初始化默认缓存 */ if (cache_size != 0) { for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) mempool_cache_init(&mp->local_cache[lcore_id], cache_size); } te->data = mp; /* 将mp 与 te 关联 */ rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); TAILQ_INSERT_TAIL(mempool_list, te, next); /* 将内存池的头插入到内存池队列中 */ rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); rte_rwlock_write_unlock(RTE_EAL_MEMPOOL_RWLOCK); return mp; /* 返回新创建的内存池 */ exit_unlock: rte_rwlock_write_unlock(RTE_EAL_MEMPOOL_RWLOCK); rte_free(te); rte_mempool_free(mp); return NULL; } |
创建完成后,其内存结构如下:
struct rte_pktmbuf_pool_private 结构用来描述pktmbuf内存池中每个mbuf 对象的空间大小。mbuf_data_room_size用来描述每个mbuf结构的数据空间大小,mbuf_priv_size用来描述每个mbuf中的私有空间的大小,如果不需要,该空间可以为0。
2.1 rte_memzone_reserve() 函数
该函数分配 struct rte_memzone 结构的内存并填充,如果分配失败则返回 NULL。
struct rte_memzone 结构体定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | struct rte_memzone { #define RTE_MEMZONE_NAMESIZE 32 /**< memzone 名称的最大长度 */ char name[RTE_MEMZONE_NAMESIZE]; /**< memzone 的名称 */ phys_addr_t phys_addr; /**< 起始物理地址 */ RTE_STD_C11 union { void *addr; /**< 起始虚拟地址 */ uint64_t addr_64; /**< 确保该地址总是64位的 */ }; size_t len; /**< memzone 所占用的空间大小(字节) */ uint64_t hugepage_sz; /**< memzone 下层的页的大小 */ int32_t socket_id; /**< NUMA socket ID. */ uint32_t flags; /**< memzone 的特性 */ uint32_t memseg_id; /**< memzone指向的数据空间所属的Memseg id */ } __attribute__((__packed__)); |
该函数在很多模块中使用,如mempool,ring等。
在 rte_mempool_create_empty() 函数中,该函数的调用关系为:
1 2 3 4 5 | rte_memzone_reserve(mz_name, mempool_size, socket_id, mz_flags) --> rte_memzone_reserve_thread_safe(mz_name, mempool_size, socket_id, mz_flags, RTE_CACHE_LINE_SIZE, 0) --> rte_rwlock_write_lock(&mcfg->mlock) mz = memzone_reserve_aligned_thread_unsafe(mz_name, mempool_size, socket_id, mz_flags, RTE_CACHE_LINE_SIZE, 0) rte_rwlock_write_unlock(&mcfg->mlock) |
memzone_reserve_aligned_thread_unsafe() 函数解析如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 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; /* 得到全局配置 */ mcfg = rte_eal_get_configuration()->mem_config; /* 配置中没有空闲 memzone 了,直接返回NULL */ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) { RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__); rte_errno = ENOSPC; return NULL; } if (strlen(name) > sizeof(mz->name) - 1) { /* mz 名称太长 */ RTE_LOG(DEBUG, EAL, "%s(): memzone <%s>: name too long\n", __func__, name); rte_errno = ENAMETOOLONG; return NULL; } /* 同名空间已经存在了 */ if ((memzone_lookup_thread_unsafe(name)) != NULL) { RTE_LOG(DEBUG, EAL, "%s(): memzone <%s> already exists\n", __func__, name); rte_errno = EEXIST; return NULL; } /* 没有对齐 */ if (align && !rte_is_power_of_2(align)) { RTE_LOG(ERR, EAL, "%s(): Invalid alignment: %u\n", __func__, align); rte_errno = EINVAL; return NULL; } /* 至少要与一个 cacheline 大小对齐 */ if (align < RTE_CACHE_LINE_SIZE) 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; } /* 分配的空间大小必须是 cacheline 对齐的 */ len += RTE_CACHE_LINE_MASK; len &= ~((size_t) RTE_CACHE_LINE_MASK); /* 最小分配一个 cacheline 大小的空间 */ 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; } if ((socket_id != SOCKET_ID_ANY) && (socket_id >= RTE_MAX_NUMA_NODES)) { rte_errno = EINVAL; return NULL; } if (!rte_eal_has_hugepages()) socket_id = SOCKET_ID_ANY; if (len == 0) { /* 如果没有指定分配长度 */ if (bound != 0) requested_len = bound; else { /* 返回指定heap中最大的空闲内存块 */ requested_len = find_heap_max_free_elem(&socket_id, align); if (requested_len == 0) { rte_errno = ENOMEM; return NULL; } } } if (socket_id == SOCKET_ID_ANY) socket = malloc_get_numa_socket(); else socket = socket_id; /* 从 heap 中分配指定大小的内存,真正存储 mp 等结构的内存空间 */ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket], NULL, requested_len, flags, align, bound); if ((mz_addr == NULL) && (socket_id == SOCKET_ID_ANY)) { /* 如果从当前Numa 结点的heap中没分配出来 * 且指定了可以从其他 Numa 结点分配, * 则尝试所有其他 Numa 结点的 heap */ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) { if (socket == i) continue; /* 从 heap 中为 mz 分配内存空间 */ 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; } /* 得到对应的 elem 头 */ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr); /* 从全局配置中找一个空闲的mz, 默认最多可以包含2560个mz, * 可以编译前在配置文件中修改 */ 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; } /* 初始化该 mz 头 */ 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; mz->socket_id = elem->ms->socket_id; mz->flags = 0; mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg; return mz; } |
2.2 mempool_cache_init() 函数
该函数用来初始化mp之后的每个lcore_cache 结构,该结构定义如下:
1 2 3 4 5 6 7 8 9 10 11 | struct rte_mempool_cache { uint32_t size; /**< Size of the cache */ uint32_t flushthresh; /**< 清除多余元素之前的 threadhold 阈值 */ uint32_t len; /**< 当前 cache 的数量 */ /* * 默认为 512 * 3, 设置为这个值是为了避免在某些情况下因 cache 不足 * 而产生溢出。 * RTE_MEMPOOL_CACHE_MAX_SIZE 的值可以在编译前在配置文件中修改 */ void *objs[RTE_MEMPOOL_CACHE_MAX_SIZE * 3]; /**< Cache 对象 */ } __rte_cache_aligned; |
mempool_cache_init() 函数定义如下:
1 2 3 4 5 6 | static void mempool_cache_init(struct rte_mempool_cache *cache, uint32_t size) { cache->size = size; cache->flushthresh = CALC_CACHE_FLUSHTHRESH(size); cache->len = 0; } |
3、rte_mempool_free() 函数
该函数用来释放已分配的内存池。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | void rte_mempool_free(struct rte_mempool *mp) { struct rte_mempool_list *mempool_list = NULL; struct rte_tailq_entry *te; if (mp == NULL) return; mempool_list = RTE_TAILQ_CAST(rte_mempool_tailq.head, rte_mempool_list); rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); /* find out tailq entry */ TAILQ_FOREACH(te, mempool_list, next) { if (te->data == (void *)mp) break; } if (te != NULL) { TAILQ_REMOVE(mempool_list, te, next); rte_free(te); } rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); rte_mempool_free_memchunks(mp); rte_mempool_ops_free(mp); rte_memzone_free(mp->mz); } |
————————————————————
原创文章,转载请注明: 转载自孙希栋的博客