rte_eth_dev_info_get()
继续接之前的内容分析ethtool,分析setup_ports()中对网卡的初始化操作,包括几个重要的接口函数。
rte_eth_dev_info_get()中,根据port_id,进行一些必要信息的填充,要填充的结构为struct rte_eth_dev_info。该结构中,包含了一些网卡必要的信息,如最大rx,tx队列数,MTU的范围,rx,tx描述符的限制,另外还包含指向rte_device的指针。
int rte_eth_dev_info_get(uint16_t port_id, struct rte_eth_dev_info *dev_info) {
struct rte_eth_dev *dev;
const struct rte_eth_desc_lim lim = {
.nb_max = UINT16_MAX,
.nb_min = 0,
.nb_align = 1,
.nb_seg_max = UINT16_MAX,
.nb_mtu_seg_max = UINT16_MAX,
};
int diag;
……
memset(dev_info, 0, sizeof(struct rte_eth_dev_info));
dev_info->switch_info.domain_id = RTE_ETH_DEV_SWITCH_DOMAIN_ID_INVALID;
RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
dev = &rte_eth_devices[port_id];
dev_info->rx_desc_lim = lim;
dev_info->tx_desc_lim = lim;
dev_info->device = dev->device;
dev_info->min_mtu = RTE_ETHER_MIN_MTU;
dev_info->max_mtu = UINT16_MAX;
RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_infos_get, -ENOTSUP);
diag = (*dev->dev_ops->dev_infos_get)(dev, dev_info);
if (diag != 0) {
memset(dev_info, 0, sizeof(struct rte_eth_dev_info));
return eth_err(port_id, diag);
}
/* Maximum number of queues should be <= RTE_MAX_QUEUES_PER_PORT */
dev_info->max_rx_queues = RTE_MIN(dev_info->max_rx_queues,
RTE_MAX_QUEUES_PER_PORT);
dev_info->max_tx_queues = RTE_MIN(dev_info->max_tx_queues,
RTE_MAX_QUEUES_PER_PORT);
dev_info->driver_name = dev->device->driver->name;
dev_info->nb_rx_queues = dev->data->nb_rx_queues;
dev_info->nb_tx_queues = dev->data->nb_tx_queues;
dev_info->dev_flags = &dev->data->dev_flags;
return 0;
}
rte_eth_dev_info_get()中,首先初始化一个struct rte_eth_desc_lim,将其赋值给dev_info中对应的项。接下来最主要的调用dev->dev_ops->dev_infos_get(),以ixgbe设备为例,对应的接口函数为ixgbe_dev_info_get()。该func中,从rte_eth_dev的指针找到rte_pci_device的指针,然后从eth_dev和pci_device的data字段中获取到填充dev_info的必要信息,另外对dev_info的某些信息填充直接采用的是某些默认值。
rte_pktmbuf_pool_create()
之后是创建一个packet pool内存池用于数据包的分配,调用rte_pktmbuf_pool_create()创建一个内存池,这个内存池是用于收发数据包时对数据包进行缓存的,单位缓存的大小在此处设置为RTE_MBUF_DEFAULT_BUF_SIZE,该值为RTE_MBUF_DEFAULT_DATAROOM + RTE_PKTMBUF_HEADROOM,其中RTE_MBUF_DEFAULT_DATAROOM值为2048,用于存储数据包,RTE_PKTMBUF_HEADROOM值为128,用于存储数据包的解析信息。数据包的数量为rx tx的最大描述符的数量之和再加上PKTPOOL_EXTRA_SIZE(值为512)。
rte_pktmbuf_pool_create()最终调用的是rte_pktmbuf_pool_create_by_ops()
struct rte_mempool *
rte_pktmbuf_pool_create_by_ops(const char *name, unsigned int n,
unsigned int cache_size, uint16_t priv_size, uint16_t data_room_size,
int socket_id, const char *ops_name) {
struct rte_mempool *mp;
struct rte_pktmbuf_pool_private mbp_priv;
const char *mp_ops_name = ops_name;
unsigned elt_size;
int ret;
if (RTE_ALIGN(priv_size, RTE_MBUF_PRIV_ALIGN) != priv_size) {
……
}
elt_size = sizeof(struct rte_mbuf) + (unsigned)priv_size +
(unsigned)data_room_size;
memset(&mbp_priv, 0, sizeof(mbp_priv));
mbp_priv.mbuf_data_room_size = data_room_size;
mbp_priv.mbuf_priv_size = priv_size;
mp = rte_mempool_create_empty(name, n, elt_size, cache_size,
sizeof(struct rte_pktmbuf_pool_private), socket_id, 0);
……
if (mp_ops_name == NULL)
mp_ops_name = rte_mbuf_best_mempool_ops();
ret = rte_mempool_set_ops_byname(mp, mp_ops_name, NULL);
……
rte_pktmbuf_pool_init(mp, &mbp_priv);
ret = rte_mempool_populate_default(mp);
……
rte_mempool_obj_iter(mp, rte_pktmbuf_init, NULL);
return mp;
}
该func中,首选确认private data的大小对齐到RTE_MBUF_PRIV_ALIGN(大小为8),接下来指定在mempool中的每个元素的大小,即为上面提到的缓冲区大小加上private data的大小再加上struct rte_mbuf结构的大小。然后调用rte_mempool_create_empty()创建内存池,该func中,会进行一系列计算,将元素大小对齐的cache,并作一系列检查操作,接下来调用rte_memzone_reserve()和rte_mempool_trace_create_empty()从memzone中分配内存,然后做完成对mempool的trace的创建操作。
内存池创建完成之后,接下来需要对网卡进行配置,调用rte_eth_dev_configure()来进行,主要是通过struct rte_eth_conf这个数据结构进行。配置包括以下内容:
struct rte_eth_conf {
uint32_t link_speeds;
struct rte_eth_rxmode rxmode; /**< Port RX configuration. */
struct rte_eth_txmode txmode; /**< Port TX configuration. */
uint32_t lpbk_mode;
struct {
struct rte_eth_rss_conf rss_conf; /**< Port RSS configuration */
struct rte_eth_vmdq_dcb_conf vmdq_dcb_conf;
/**< Port vmdq+dcb configuration. */
struct rte_eth_dcb_rx_conf dcb_rx_conf;
/**< Port dcb RX configuration. */
struct rte_eth_vmdq_rx_conf vmdq_rx_conf;
/**< Port vmdq RX configuration. */
} rx_adv_conf; /**< Port RX filtering configuration. */
union {
struct rte_eth_vmdq_dcb_tx_conf vmdq_dcb_tx_conf;
/**< Port vmdq+dcb TX configuration. */
struct rte_eth_dcb_tx_conf dcb_tx_conf;
/**< Port dcb TX configuration. */
struct rte_eth_vmdq_tx_conf vmdq_tx_conf;
/**< Port vmdq TX configuration. */
} tx_adv_conf; /**< Port TX DCB configuration (union). */
uint32_t dcb_capability_en;
struct rte_fdir_conf fdir_conf; /**< FDIR configuration. DEPRECATED */
struct rte_intr_conf intr_conf; /**< Interrupt mode configuration. */
};
主要的内容有对链路速率的配置,rx tx模式的配置,rx方向一些必要的收包策略,tx方向的发包策略,flow director(流引导)策略,中断配置等。
rte_eth_rx_queue_setup()
配置完网卡之后,接下来配置网卡的rx tx队列,首先调用rte_eth_dev_adjust_nb_rx_tx_desc()来确定允许的rx tx的队列的大小不超过限制值,然后分别调用rte_eth_rx_queue_setup()和rte_eth_tx_queue_setup()来配置网卡的收发队列,ethtool例子中,目前只配置队列0。
rte_eth_rx_queue_setup()中,首先根据传入的mempool的信息,最终确定一个单位缓存的大小;另外需要检查rx队列索引的是否符合dev_info中获取到的对rx队列的限制条件;接下来检查队列的状态,是否是运行状态。之后分别调用dev->dev_ops->rx_queue_release()先将队列信息进行释放,然后再调用dev->dev_ops->rx_queue_setup()进行队列的初始化,最后调用rte_ethdev_trace_rxq_setup()对rx队列的跟踪信息初始化。
首先看一下rx_queue_setup(),以ixgbe_dev_rx_queue_setup()为例,首先要确保传入的dev设备的data->rx_queues[queue_id]指向的空间是空的,如果不为空需要先释放。接下来为这个rx_queues[queue_id]分配一块空间,并为其中的成员进行赋值,主要成员包括指向mempool的指针,最大描述符数量等。接下来调用rte_eth_dma_zone_reserve(),在dma区域上分配内存用于rx ring中描述符的分配,并将相关的地址信息存储在rx_queues[queue_id]->rx_ring中;然后为software ring分配空间,并将相关地址存储到rx_queues[queue_id]->sw_ring中;最后为software scatterd ring分配空间,并将相关地址存储到rx_queues[queue_id]->sw_sc_ring中。分配完空间之后,调用ixgbe_reset_rx_queue()对分配的空间进行一些初始化操作。
接下来看一下rx_queue_release(),以ixgbe_dev_rx_queue_release()为例,实现的是ixgbe_rx_queue_release(),该func主要的工作就是释放rx_queues[queue_id]中各指针指向的缓存。
后面介绍rte_eth_tx_queue_setup()的实现细节,未完待续……