原文
1、知识百科
rte_mbuf的结构与linux内核协议栈的skb_buf相似,在保存报文的内存块前后分别保留headroom和tailroom,以方便应用解封报文,headroom默认128字节,可以通过宏RTE_PKTMBUF_HEADROOM调整。
返回值 | 操作函数 | 函数功能 |
宏 | rte_pktmbuf_mtod(m, t) | 将指针指到数据部分的首部 |
宏 | 获取所有mbuf的数据长度 | |
宏 | 获取本mbuf的数据长度 | |
char * |
| |
char * |
| |
uint16_t |
| |
uint16_t |
| |
void |
| |
void |
| |
void |
| |
int |
| |
void |
| |
void |
| |
void |
| |
struct rte_mbuf * | 从rte_mempool获取一个mbuf | |
void |
| |
void |
| |
void |
| |
void | 将mbuf归还到rte_mempool中 | |
struct rte_mbuf * |
| |
void |
| |
uint16_t | 计算出mbuf的headroom大小 | |
uint16_t | 计算出mbuf的tailroom大小 | |
struct rte_mbuf * |
| |
char * |
| |
char * | 向mbuf追加数据,修改pkt_len和data_len | |
char * | 向mbuf删减数据,修改pkt_len和data_len | |
int | rte_pktmbuf_trim |
|
int | rte_pktmbuf_is_contiguous |
|
void | rte_pktmbuf_dump |
|
2、数据结构
rte_vlan_macip
union rte_vlan_macip {
uint32_t data;
struct {
uint16_t l3_len:9; //L3层首部= sizeof(struct ipv4_hdr)=20
uint16_t l2_len:7; // L3层首部= sizeof(struct ether_hdr)=14
uint16_t vlan_tci;
} f;
};
rte_pktmbuf
struct rte_pktmbuf {
struct rte_mbuf *next; /**< Next segment of scattered packet. */
void* data; //mbuf数据部分的起始位置
uint16_t data_len; //本mbuf的数据长度
uint8_t nb_segs; /**< Number of segments. */
uint8_t in_port; /**< Input port. */
uint32_t pkt_len; //所有mbuf的数据长度
union rte_vlan_macip vlan_macip;
union {
uint32_t rss; /**< RSS hash result if RSS enabled */
struct {
uint16_t hash;
uint16_t id;
} fdir; /**< Filter identifier if FDIR enabled */
uint32_t sched; /**< Hierarchical scheduler */
} hash; /**< hash information */
};
rte_mbuf
struct rte_mbuf {
struct rte_mempool *pool; /**< Pool from which mbuf was allocated. */
void *buf_addr; //mbuf的起始地址
phys_addr_t buf_physaddr; /**< Physical address of segment buffer. */
uint16_t buf_len; //mbuf的长度=mp->elt_size - sizeof(struct rte_mbuf)
union {
rte_atomic16_t refcnt_atomic; /**< Atomically accessed refcnt */
uint16_t refcnt; /**< Non-atomically accessed refcnt */
};
uint8_t type; /**< Type of mbuf. */
uint8_t reserved; /**< Unused field. Required for padding. */
uint16_t ol_flags; // PKT_TX_IP_CKSUM=0x1000,让网卡计算checksum
union {
struct rte_ctrlmbuf ctrl;
struct rte_pktmbuf pkt;
};
} __rte_cache_aligned;
3、操作函数
mbuf_init->rte_mempool_create
函数功能:mbuf由缓冲池rte_mempool管理,rte_mempool在初始化时一次申请多个mbuf,申请的mbuf个数和长度都由用户指定,使用rte_pktmbuf_alloc申请一个mbuf。
app.lcore_params[lcore].pool = rte_mempool_create(
name,
DEFAULT_MEMPOOL_BUFFERS, // 8192 * 32=256K个
DEFAULT_MBUF_SIZE, // (4096 + sizeof(struct rte_mbuf) + 128)
DEFAULT_MEMPOOL_CACHE_SIZE, //32
sizeof(struct rte_pktmbuf_pool_private),
rte_pktmbuf_pool_init, NULL,
rte_pktmbuf_init, NULL,
0,
0);
#define RTE_PKTMBUF_HEADROOM 128
备注:调用rte_mempool_create()函数创建rte_mempool的时候,指定申请多少个rte_mbuf以及每个rte_mbuf中elt_size大小,elt_size是为网卡接收的数据包预先分配的内存的大小,该内存块就是rte_mbuf->pkt.data的实际存储区域。
rte_pktmbuf_alloc
函数功能:从rte_mempool获取一个mbuf。
static inline struct rte_mbuf *rte_pktmbuf_alloc(struct rte_mempool *mp)
{
struct rte_mbuf *m;
if ((m = __rte_mbuf_raw_alloc(mp)) != NULL)
rte_pktmbuf_reset(m);
return (m);
}
rte_pktmbuf_free
函数功能:将mbuf归还到rte_mempool中。
static inline void rte_pktmbuf_free(struct rte_mbuf *m)
{
struct rte_mbuf *m_next;
__rte_mbuf_sanity_check(m, RTE_MBUF_PKT, 1);
while (m != NULL) {
m_next = m->pkt.next;
rte_pktmbuf_free_seg(m);
m = m_next;
}
}
rte_pktmbuf_mtod
函数功能:将指针指到数据部分的首部。
#define rte_pktmbuf_mtod(m, t) ((t)((m)->pkt.data))
rte_pktmbuf_pkt_len
函数功能:获取所有mbuf的数据长度。
#define rte_pktmbuf_pkt_len(m) ((m)->pkt.pkt_len)
rte_pktmbuf_data_len
函数功能:获取本mbuf的数据长度。
#define rte_pktmbuf_data_len(m) ((m)->pkt.data_len)
rte_pktmbuf_headroom
函数功能:计算出mbuf的headroom大小。
static inline uint16_t rte_pktmbuf_headroom(const struct rte_mbuf *m)
{
__rte_mbuf_sanity_check(m, RTE_MBUF_PKT, 1);
return (uint16_t) ((char*) m->pkt.data - (char*) m->buf_addr);
}
rte_pktmbuf_tailroom
函数功能:计算出mbuf的tailroom大小。
static inline uint16_t rte_pktmbuf_tailroom(const struct rte_mbuf *m)
{
__rte_mbuf_sanity_check(m, RTE_MBUF_PKT, 1);
return (uint16_t)(m->buf_len - rte_pktmbuf_headroom(m) -
m->pkt.data_len);
}
rte_pktmbuf_append
函数功能:向mbuf追加数据,修改pkt_len和data_len。
static inline char *rte_pktmbuf_append(struct rte_mbuf *m, uint16_t len)
{
void *tail;
struct rte_mbuf *m_last;
__rte_mbuf_sanity_check(m, RTE_MBUF_PKT, 1);
m_last = rte_pktmbuf_lastseg(m);
if (unlikely(len > rte_pktmbuf_tailroom(m_last)))
return NULL;
tail = (char*) m_last->pkt.data + m_last->pkt.data_len; //tail的首地址
m_last->pkt.data_len = (uint16_t)(m_last->pkt.data_len + len); //修改last mbuf的data_len
m->pkt.pkt_len = (m->pkt.pkt_len + len); //修改所有mbuf的pkt_len
return (char*) tail;
}
rte_pktmbuf_adj
函数功能:向mbuf删减数据,修改pkt_len和data_len。
static inline char *rte_pktmbuf_adj(struct rte_mbuf *m, uint16_t len)
{
__rte_mbuf_sanity_check(m, RTE_MBUF_PKT, 1);
if (unlikely(len > m->pkt.data_len))
return NULL;
m->pkt.data_len = (uint16_t)(m->pkt.data_len - len);
m->pkt.data = ((char*) m->pkt.data + len);
m->pkt.pkt_len = (m->pkt.pkt_len - len);
return (char*) m->pkt.data;
}
4、应用实例
网卡接收包
struct rte_mbuf *m = _m;
uint32_t buf_len = mp->elt_size - sizeof(struct rte_mbuf);
RTE_MBUF_ASSERT(mp->elt_size >= sizeof(struct rte_mbuf));
memset(m, 0, mp->elt_size);
m->buf_addr = (char *)m + sizeof(struct rte_mbuf);
m->buf_len = (uint16_t)buf_len;
m->pkt.data = (char*) m->buf_addr + RTE_MIN(RTE_PKTMBUF_HEADROOM, m->buf_len);
m->type = RTE_MBUF_PKT;
….
填充新数据
如果需要在tailroom 中加入N个字节数据,我们可以通过以下操作完成:
tail = m->pkt.data + m->pkt.data_len; // tail记录tailroom首地址
m->pkt.data_len += N;
m->pkt.pkt_len += N;
这些操作由rte_pktmbuf_append()实现,函数原型如下:
char *rte_pktmbuf_append(struct rte_mbuf *m, uint16_t len)
剥离数据
假设m->pkt.data指向报文的二层首地址,我们可以通过以下一系列操作剥去报文的二层头部:
m->pkt.data += 14;
m->pkt.data_len -= 14;
m->pkt.pkt_len -= 14;
这些操作已经由rte_pktmbuf_adj()实现,函数原型如下:
char *rte_pktmbuf_adj(struct rte_mbuf *m, uint16_t len)
5、参考资料
rte_mbuf
http://dpdk.org/doc/api/rte__mbuf_8h.html
http://www.tuicool.com/articles/rYfqMb
http://www.cnblogs.com/ziding/p/4214499.html
dpdk-mempool
http://dpdk.org/doc/api/rte__mempool_8h.html