创建mbuf的内存池
当我们要使用mbuf的时候,需要先创建一个mbuf的内存池,然后每次都从mp里面alloc和free即可。
相对dpdk提供的mbuf库来说,使用比较简单,具体代码在:dpdk/lib/librte_mbuf 目录里面
struct rte_mempool *
rte_pktmbuf_pool_create(const char *name, unsigned int n,
unsigned int cache_size, uint16_t priv_size, uint16_t data_room_size,
int socket_id)
{
return rte_pktmbuf_pool_create_by_ops(name, n, cache_size, priv_size,
data_room_size, socket_id, NULL);
}
具体输入参数:
const char *name 创建mp需要的名字
unsigned int n mp里面obj的数量,在这里表示mbuf的数量,如果你的代码缓存mbuf比较多,这里就需要设置大一些,防止不够用
unsigned int cache_size mp里面cache的数量
uint16_t priv_size 每一个mbuf私有数据空间的大小,不需要直接设置为0即可
uint16_t data_room_size mbuf的数据报文的大小,理论上需要加上room head的大小,建议使用默认值RTE_MBUF_DEFAULT_BUF_SIZE
int socket_id 申请内存的socket,不清楚设置那个的直接使用rte_socket_id()即可。
rte_pktmbuf_pool_create主要分为以下几步:
-
创建一个空的mp使用rte_mempool_create_empty
-
根据ops name选择相应的mp 的ops操作库,如果有自己定义的ops 操作库,则直接使用rte_pktmbuf_pool_create_by_ops,否则就使用rte_pktmbuf_pool_create创建mbuf的mp即可
-
如果没有指定ops name的话,系统默认rte_mbuf_best_mempool_ops
这里说一下dpdk提供的ops类型,主要分为三种:
①、用户自定义
②、硬件平台相关,这里运行不同nic自己设计内存buf,主要用到的主要有nxp的dpaa和cavium的octeontx
③、默认ring_mp_mc
优先级也是① > ② > ③ -
初始化mp rte_pktmbuf_pool_init
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 */
user_mbp_priv = opaque_arg;
if (user_mbp_priv == NULL) {
default_mbp_priv.mbuf_priv_size = 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);
mbp_priv = rte_mempool_get_priv(mp);
memcpy(mbp_priv, user_mbp_priv, sizeof(*mbp_priv));
}
- 申请n个mbuf内存rte_mempool_populate_default
- 初始化每一个mbuf
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;
priv_size = rte_pktmbuf_priv_size(mp);
mbuf_size = sizeof(struct rte_mbuf) + priv_size;
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, mbuf_size);
/* start of buffer is after mbuf structure and priv data */
m->priv_size = priv_size;
m->buf_addr = (char *)m + mbuf_size;
m->buf_iova = rte_mempool_virt2iova(m) + mbuf_size;
m->buf_len = (uint16_t)buf_len;
/* keep some headroom between start of buffer and data */
m->data_off = RTE_MIN(RTE_PKTMBUF_HEADROOM, (uint16_t)m->buf_len);
/* init some constant fields */
m->pool = mp;
m->nb_segs = 1;
m->port = MBUF_INVALID_PORT;
rte_mbuf_refcnt_set(m, 1);
m->next = NULL;
}
到此为止mbuf的内存池已经初始化完成.
相关api
- 申请mbuf
rte_mbuf_raw_alloc 从mp里面申请一块mbuf,不做初始化
rte_pktmbuf_alloc 从mp里面申请一块mbuf,同时对mbuf做初始化rte_pktmbuf_reset,对mbuf的变量重置为初始值
rte_pktmbuf_alloc_bulk 从mp里面一次申请多个mbuf并做reset操作
rte_pktmbuf_reset 复位mbuf控制块的信息 - 释放mbuf
rte_pktmbuf_free 释放mbuf到mp里面,如果mbuf属于多段buf,同时释放所有mbuf - 获取mbuf报文位置指针
rte_pktmbuf_mtod 获取报文开始的虚拟地址
rte_pktmbuf_iova 获取报文开始的物理地址 - 获取mbuf相关长度
rte_pktmbuf_pkt_len 获取报文总的长度
rte_pktmbuf_data_len 获取报文当前段的长度 - mbuf数据插入删除操作
rte_pktmbuf_prepend 在帧数据前插入一段内容
rte_pktmbuf_append 在帧数据后增加一段内容
rte_pktmbuf_adj 在帧数据前删除一段内容
rte_pktmbuf_trim 将帧数据后截掉一段内容
6、连接分离两段mbuf缓存
rte_pktmbuf_attach 此函数会连接两段属于不同缓存区的缓存, 称为间接缓存(indirect buffer) 。 对间接缓存的访
问效率低于直接缓存(意为一段缓存包含完整Mbuf结构和帧数据),也就是说,把mbuf1的buf_addr等信息替换为mbuf2里面的,如果要分离的话,需要重新从mbuf头部计算一下buf_addr等变量的值。
rte_pktmbuf_detach 分离两点缓存,和attach相反操作。 - 克隆mbuf
rte_pktmbuf_clone 此函数作为rte_pktmbuf_attach的更高一级抽象, 将正确设置连接后Mbuf的各个参数, 相对rte_pktmbuf_attach更为安全