DPDK17.02内存池管理3 – rte_pktmbuf_pool_create
该函数用来创建一个 mbuf 内存池并初始化。
1、rte_pktmbuf_pool_create() 函数
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 | struct rte_mempool *rte_pktmbuf_pool_create(const char *name, unsigned n, unsigned cache_size, uint16_t priv_size, uint16_t data_room_size, int socket_id) { struct rte_mempool *mp; struct rte_pktmbuf_pool_private mbp_priv; unsigned elt_size; int ret; if (RTE_ALIGN(priv_size, RTE_MBUF_PRIV_ALIGN) != priv_size) { RTE_LOG(ERR, MBUF, "mbuf priv_size=%u is not aligned\n", priv_size); rte_errno = EINVAL; return NULL; } /* 计算内存池中每个对象的占用空间大小。每个对象包含三部分,包括一个rte_mbuf 头,一块私有空间,以及真正存储数据的空间 */ elt_size = sizeof(struct rte_mbuf) + (unsigned)priv_size + (unsigned)data_room_size; mbp_priv.mbuf_data_room_size = data_room_size; mbp_priv.mbuf_priv_size = priv_size; /* 创建一个空的 mempool,只包含 mp 头 */ mp = rte_mempool_create_empty(name, n, elt_size, cache_size, sizeof(struct rte_pktmbuf_pool_private), socket_id, 0); if (mp == NULL) return NULL; /* 设置内存池的默认处理函数,默认为 ring_mp_mc */ ret = rte_mempool_set_ops_byname(mp, RTE_MBUF_DEFAULT_MEMPOOL_OPS, NULL); if (ret != 0) { /* 如果设置失败 */ RTE_LOG(ERR, MBUF, "error setting mempool handler\n"); rte_mempool_free(mp); /* 释放 mp */ rte_errno = -ret; return NULL; } /* 用 mbp_priv 初始化 mp 头结尾的私有数据区域 */ rte_pktmbuf_pool_init(mp, &mbp_priv); /* 该函数用来真正为内存池中的对象分配内存空间并完成初始化 */ ret = rte_mempool_populate_default(mp); if (ret < 0) { rte_mempool_free(mp); rte_errno = -ret; return NULL; } /* 该函数通过 rte_pktmbuf_init() 函数,对内存池中每个对象中的 rte_mbuf 结构进行初始化 */ rte_mempool_obj_iter(mp, rte_pktmbuf_init, NULL); return mp; } |
内存池中每个对象的结构如下:
2、rte_pktmbuf_pool_init()
该函数用来初始化mp头结构的尾部的私有数据结构。
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 | void rte_pktmbuf_pool_init(struct rte_mempool *mp, void *opaque_arg) { struct rte_pktmbuf_pool_private *user_mbp_priv, *mbp_priv; struct rte_pktmbuf_pool_private default_mbp_priv; uint16_t roomsz; RTE_ASSERT(mp->elt_size >= sizeof(struct rte_mbuf)); /* if no structure is provided, assume no mbuf private area */ /* 如果 opaque_arg 参数为NULL,则设定没有 mbuf 私有空间 */ user_mbp_priv = opaque_arg; if (user_mbp_priv == NULL) { default_mbp_priv.mbuf_priv_size = 0; /* 如果内存池的元素大小应该大于 rte_mbuf 结构的大小 * 其余的部分才是真正存储用户数据的空间 * 否则 mbuf 的数据空间的大小为0 */ if (mp->elt_size > sizeof(struct rte_mbuf)) roomsz = mp->elt_size - sizeof(struct rte_mbuf); else roomsz = 0; default_mbp_priv.mbuf_data_room_size = roomsz; user_mbp_priv = &default_mbp_priv; } RTE_ASSERT(mp->elt_size >= sizeof(struct rte_mbuf) + user_mbp_priv->mbuf_data_room_size + user_mbp_priv->mbuf_priv_size); /* 初始化 mp 结构中的私有数据区域 */ mbp_priv = rte_mempool_get_priv(mp); memcpy(mbp_priv, user_mbp_priv, sizeof(*mbp_priv)); } |
3、rte_mempool_populate_default() 函数
该函数用来初始化内存池中的所有对象的内存空间。
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 | int rte_mempool_populate_default(struct rte_mempool *mp) { int mz_flags = RTE_MEMZONE_1GB|RTE_MEMZONE_SIZE_HINT_ONLY; char mz_name[RTE_MEMZONE_NAMESIZE]; const struct rte_memzone *mz; size_t size, total_elt_sz, align, pg_sz, pg_shift; phys_addr_t paddr; unsigned mz_id, n; int ret; /* 如果该值非0,说明该 mp 头已经初始化过了 */ if (mp->nb_mem_chunks != 0) return -EEXIST; if (rte_xen_dom0_supported()) { pg_sz = RTE_PGSIZE_2M; pg_shift = rte_bsf32(pg_sz); align = pg_sz; } else if (rte_eal_has_hugepages()) { /* 如果使用大页 */ pg_shift = 0; /* 不再需要,因为已经是物理地址连续的了 */ pg_sz = 0; align = RTE_CACHE_LINE_SIZE; } else { /* 如果没有使用大页,则使用系统默认页的大小 */ pg_sz = getpagesize(); pg_shift = rte_bsf32(pg_sz); align = pg_sz; } /* 每个元素占用的空间总大小 */ total_elt_sz = mp->header_size + mp->elt_size + mp->trailer_size; /* 如果申请的空间比较大,一个memseg的空间可能不满足,可能需要多个 memseg 空间 */ for (mz_id = 0, n = mp->size; n > 0; mz_id++, n -= ret) { /* 计算存放给定数量的元素所需要最大空间大小 */ size = rte_mempool_xmem_size(n, total_elt_sz, pg_shift); /* 内存空间的名字,格式为 MP_<name>_%d */ ret = snprintf(mz_name, sizeof(mz_name), RTE_MEMPOOL_MZ_FORMAT "_%d", mp->name, mz_id); if (ret < 0 || ret >= (int)sizeof(mz_name)) { ret = -ENAMETOOLONG; goto fail; } /* 从 heap 中分配指定的空间 */ mz = rte_memzone_reserve_aligned(mz_name, size, mp->socket_id, mz_flags, align); /* 如果当前 heap 中所有的内存段都不满足, 则分配其中最大的一块 */ if (mz == NULL) mz = rte_memzone_reserve_aligned(mz_name, 0, mp->socket_id, mz_flags, align); if (mz == NULL) { ret = -rte_errno; goto fail; } /* 如果配置了不需要物理地址连续的内存池 */ if (mp->flags & MEMPOOL_F_NO_PHYS_CONTIG) paddr = RTE_BAD_PHYS_ADDR; else paddr = mz->phys_addr; /* 如果使用了大页,且当前不是 xen 的dom0(默认不支持) * 则使用物理地址进行内存池对象的填充 */ if (rte_eal_has_hugepages() && !rte_xen_dom0_supported()) /* 为内存池的每个对象与前面分配的大的内存块(memchunk)相关联 */ ret = rte_mempool_populate_phys(mp, mz->addr, paddr, mz->len, rte_mempool_memchunk_mz_free, (void *)(uintptr_t)mz); else ret = rte_mempool_populate_virt(mp, mz->addr, mz->len, pg_sz, rte_mempool_memchunk_mz_free, (void *)(uintptr_t)mz); if (ret < 0) { rte_memzone_free(mz); goto fail; } } return mp->size; fail: rte_mempool_free_memchunks(mp); return ret; } |
4、rte_mempool_populate_phys() 函数
使用创建的大的内存块(memchunk),为内存池分配每个对象,及其关联的 ring 结构。
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 | int rte_mempool_populate_phys(struct rte_mempool *mp, char *vaddr, phys_addr_t paddr, size_t len, rte_mempool_memchunk_free_cb_t *free_cb, void *opaque) { unsigned total_elt_sz; unsigned i = 0; size_t off; struct rte_mempool_memhdr *memhdr; int ret; /* create the internal ring if not already done */ /* 创建一个内部 ring,保存为 mp->pool_data */ if ((mp->flags & MEMPOOL_F_POOL_CREATED) == 0) { ret = rte_mempool_ops_alloc(mp); if (ret != 0) return ret; mp->flags |= MEMPOOL_F_POOL_CREATED; } /* 如果内存池已经填充过了 */ if (mp->populated_size >= mp->size) return -ENOSPC; /* 得到每个对象占用空间的总大小 */ total_elt_sz = mp->header_size + mp->elt_size + mp->trailer_size; /* 分配一个 struct rte_mempool_memhdr 结构,用来将这块内存块(memchunk)与当前mp相关联 */ memhdr = rte_zmalloc("MEMPOOL_MEMHDR", sizeof(*memhdr), 0); if (memhdr == NULL) return -ENOMEM; memhdr->mp = mp; /* 指向内存池头部 */ memhdr->addr = vaddr; /* 内存块(memchunk)的起始虚拟地址 */ memhdr->phys_addr = paddr; /* 内存块的起始物理地址 */ memhdr->len = len; /* 内存块的长度 */ memhdr->free_cb = free_cb; /* 释放当前内存块(memchunk)的回调函数 */ memhdr->opaque = opaque; /* 回调函数的参数,通常为当前内存块对应的memzone */ /* 计算到内存块(memchunk)起始位置的偏移 */ if (mp->flags & MEMPOOL_F_NO_CACHE_ALIGN) off = RTE_PTR_ALIGN_CEIL(vaddr, 8) - vaddr; else off = RTE_PTR_ALIGN_CEIL(vaddr, RTE_CACHE_LINE_SIZE) - vaddr; while (off + total_elt_sz <= len && mp->populated_size < mp->size) { off += mp->header_size; /* 跳过内存池对象的 struct rte_mempool_objhdr 结构 */ if (paddr == RTE_BAD_PHYS_ADDR) mempool_add_elem(mp, (char *)vaddr + off, RTE_BAD_PHYS_ADDR); else mempool_add_elem(mp, (char *)vaddr + off, paddr + off); off += mp->elt_size + mp->trailer_size; /* 跳过内存池对象的数据空间部分与 trailer部分 */ i++; /* 记录已经插入到内存池的对象的数量 */ } /* not enough room to store one object */ if (i == 0) return -EINVAL; /* 将当前内存块(memchunk)与当前内存池mp相关联 */ STAILQ_INSERT_TAIL(&mp->mem_list, memhdr, next); mp->nb_mem_chunks++; return i; /* 返回内存池的对象数量 */ } |
5、mempool_add_elem() 函数
该函数将memchunk 分成的一个个小的内存对象插入到内存池的ring中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | static void mempool_add_elem(struct rte_mempool *mp, void *obj, phys_addr_t physaddr) { struct rte_mempool_objhdr *hdr; struct rte_mempool_objtlr *tlr __rte_unused; hdr = RTE_PTR_SUB(obj, sizeof(*hdr)); /* 得到当前对象的struct rte_mempool_objhdr头部地址 */ hdr->mp = mp; /* 将其与mp相关联 */ hdr->physaddr = physaddr; STAILQ_INSERT_TAIL(&mp->elt_list, hdr, next); mp->populated_size++; #ifdef RTE_LIBRTE_MEMPOOL_DEBUG hdr->cookie = RTE_MEMPOOL_HEADER_COOKIE2; tlr = __mempool_get_trailer(obj); tlr->cookie = RTE_MEMPOOL_TRAILER_COOKIE; #endif /* 将当前对象插入到 mp 关联的ring结构中,即 */ rte_mempool_ops_enqueue_bulk(mp, &obj, 1); } |
6、rte_mempool_obj_iter() 函数
该函数通过调用回调函数rte_pktmbuf_init(),对内存池中的每个对象进行初始化,返回经过初始化的对象的数量。
6.1 rte_mempool_obj_iter() 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | uint32_t rte_mempool_obj_iter(struct rte_mempool *mp, rte_mempool_obj_cb_t *obj_cb, void *obj_cb_arg) { struct rte_mempool_objhdr *hdr; void *obj; unsigned n = 0; STAILQ_FOREACH(hdr, &mp->elt_list, next) { /* 遍历内存池中的每个对象 */ obj = (char *)hdr + sizeof(*hdr); /* 跳过每个对象的 hdr 管理结构 */ obj_cb(mp, obj_cb_arg, obj, n); /* 调用回调函数初始化每个对象 */ n++; } return n; } |
6.2 rte_pktmbuf_init() 函数
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 | void rte_pktmbuf_init(struct rte_mempool *mp, __attribute__((unused)) void *opaque_arg, void *_m, __attribute__((unused)) unsigned i) { struct rte_mbuf *m = _m; uint32_t mbuf_size, buf_len, priv_size; /* 得到 mp 头部的结尾处保存的struct rte_pktmbuf_pool_private结构中指定的私有数据的大小 */ priv_size = rte_pktmbuf_priv_size(mp); mbuf_size = sizeof(struct rte_mbuf) + priv_size; /* mbuf 和私有数据占用的空间总大小 */ /* 得到 mp 头部的结尾处保存的struct rte_pktmbuf_pool_private结构中指定的数据数据的大小 */ buf_len = rte_pktmbuf_data_room_size(mp); RTE_ASSERT(RTE_ALIGN(priv_size, RTE_MBUF_PRIV_ALIGN) == priv_size); RTE_ASSERT(mp->elt_size >= mbuf_size); RTE_ASSERT(buf_len <= UINT16_MAX); memset(m, 0, mp->elt_size); m->priv_size = priv_size; m->buf_addr = (char *)m + mbuf_size; /* 数据区域的起始地址在 mbuf 结构和私有数据之后 */ m->buf_physaddr = rte_mempool_virt2phy(mp, m) + mbuf_size; m->buf_len = (uint16_t)buf_len; /* 排除mbuf与私有空间之后的数据空间的大小 */ /* 在数据空间的起始地址和真正存储数据的数据空间保留一段空间 */ /* RTE_PKTMBUF_HEADROOM 宏在配置文件中定义,默认大小为128 */ m->data_off = RTE_MIN(RTE_PKTMBUF_HEADROOM, (uint16_t)m->buf_len); /* init some constant fields */ m->pool = mp; /* 将mbuf与其对应的mp 关联 */ m->nb_segs = 1; m->port = 0xff; } |
————————————————————
原创文章,转载请注明: 转载自孙希栋的博客