pci probe
RTE_PMD_REGISTER_PCI(net_ixgbe, rte_ixgbe_pmd); 宏注册了net_ixgbe driver到pci bus
rte_ixgbe_pmd 的定义如下
static struct rte_pci_driver rte_ixgbe_pmd = {.id_table = pci_id_ixgbe_map,.drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC,.probe = eth_ixgbe_pci_probe,.remove = eth_ixgbe_pci_remove,
};
当扫描到到的device 能和pci_id_ixgbe_map 里的id 匹配上时就表示这个device和net_ixgbe 这个driver相匹配,接着便会调用.probe = eth_ixgbe_pci_probe指定的probe函数,这里我们关注下probe 函数对收发报函数的设置
static int
->eth_ixgbe_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,struct rte_pci_device *)->retval = rte_eth_dev_create(&pci_dev->device, pci_dev->device.name,sizeof(struct ixgbe_adapter),eth_dev_pci_specific_init, pci_dev,eth_ixgbe_dev_init, NULL);->eth_ixgbe_dev_init(struct rte_eth_dev *, void *init_params )
eth_ixgbe_dev_init 中会完成driver的初始化,其中就包括了调用 ixgbe_set_tx_function、ixgbe_set_rx_function根据情况设置对应的收发函数
static int
eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
{/*省略*/eth_dev->dev_ops = &ixgbe_eth_dev_ops;eth_dev->rx_pkt_burst = &ixgbe_recv_pkts;eth_dev->tx_pkt_burst = &ixgbe_xmit_pkts;eth_dev->tx_pkt_prepare = &ixgbe_prep_pkts;/*省略*/if (rte_eal_process_type() != RTE_PROC_PRIMARY) {struct ixgbe_tx_queue *txq;/* TX queue function in primary, set by last queue initialized* Tx queue may not initialized by primary process*/if (eth_dev->data->tx_queues) {txq = eth_dev->data->tx_queues[eth_dev->data->nb_tx_queues-1];ixgbe_set_tx_function(eth_dev, txq);} else {/* Use default TX function if we get here */PMD_INIT_LOG(NOTICE, "No TX queues configured yet. ""Using default TX function.");}ixgbe_set_rx_function(eth_dev);return 0; //对于从核只会执行到这里}/*省略*/return 0;
}
设置收发函数
- tx函数设置
- 发送队列offloads为0时
- 未启用向量 tx_pkt_burst = ixgbe_xmit_pkts_simple
- 启用向量 dev->tx_pkt_burst = ixgbe_xmit_pkts_vec;
- 发送队列offloads非0
- dev->tx_pkt_burst = ixgbe_xmit_pkts
- 发送队列offloads为0时
- rx函数设置
- if dev->data->lro设置时
- adapter->rx_bulk_alloc_allowed 设置,允许bulk时dev->rx_pkt_burst = ixgbe_recv_pkts_lro_bulk_alloc;
- dev->rx_pkt_burst = ixgbe_recv_pkts_lro_single_alloc;
- else if dev->data->scattered_rx 设置时
- dev->data->scattered_rx 设置,支持向量时 dev->rx_pkt_burst = ixgbe_recv_scattered_pkts_vec;
- adapter->rx_bulk_alloc_allowed 设置支持bulk时 dev->rx_pkt_burst = ixgbe_recv_pkts_lro_bulk_alloc;
- dev->rx_pkt_burst = ixgbe_recv_pkts_lro_single_alloc;
- else ifadapter->rx_vec_allowed 设置时
- dev->rx_pkt_burst = ixgbe_recv_pkts_vec;
- else if adapter->rx_bulk_alloc_allowed 设置时
- dev->rx_pkt_burst = ixgbe_recv_pkts_bulk_alloc;
- else dev->rx_pkt_burst = ixgbe_recv_pkts;
- if dev->data->lro设置时
启动网卡及配置收发队列
下面是以L2FWD为例,总结了下dpdk对网卡配置、收发队列配置的API。以下代码做了省略,删除了返回判断代码
int
main(int argc, char **argv)
{ struct lcore_queue_conf *qconf;int ret;uint16_t nb_ports;uint16_t nb_ports_available = 0;uint16_t portid, last_port;unsigned lcore_id, rx_lcore_id;unsigned nb_ports_in_mask = 0;unsigned int nb_lcores = 0;unsigned int nb_mbufs;/* init EAL */ret = rte_eal_init(argc, argv);if (ret < 0)rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");argc -= ret;argv += ret;force_quit = false;signal(SIGINT, signal_handler);signal(SIGTERM, signal_handler);/* 解析传入的参数 (after the EAL ones) */ret = l2fwd_parse_args(argc, argv);if (ret < 0)rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled");nb_ports = rte_eth_dev_count_avail();if (nb_ports == 0)rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");/* check port mask to possible port mask */if (l2fwd_enabled_port_mask & ~((1 << nb_ports) - 1))rte_exit(EXIT_FAILURE, "Invalid portmask; possible (0x%x)\n",(1 << nb_ports) - 1);/*计算所需的mbuf个数*/nb_mbufs = RTE_MAX(nb_ports * (nb_rxd + nb_txd + MAX_PKT_BURST +nb_lcores * MEMPOOL_CACHE_SIZE), 8192U);/* 创建内存池 */l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", nb_mbufs,MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE,rte_socket_id());/* Initialise each port */RTE_ETH_FOREACH_DEV(portid) {struct rte_eth_rxconf rxq_conf;struct rte_eth_txconf txq_conf;struct rte_eth_conf local_port_conf = port_conf;struct rte_eth_dev_info dev_info;/* skip ports that are not enabled */if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) {printf("Skipping disabled port %u\n", portid);continue;}nb_ports_available++;/* init port */printf("Initializing port %u... ", portid);fflush(stdout);rte_eth_dev_info_get(portid, &dev_info);if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)local_port_conf.txmode.offloads |=DEV_TX_OFFLOAD_MBUF_FAST_FREE;ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf);ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,&nb_txd);rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]);/* init one RX queue */fflush(stdout);rxq_conf = dev_info.default_rxconf;rxq_conf.offloads = local_port_conf.rxmode.offloads;ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,rte_eth_dev_socket_id(portid),&rxq_conf,l2fwd_pktmbuf_pool);if (ret < 0)rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n",ret, portid);/* init one TX queue on each port */fflush(stdout);txq_conf = dev_info.default_txconf;txq_conf.offloads = local_port_conf.txmode.offloads;ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,rte_eth_dev_socket_id(portid),&txq_conf);/* Initialize TX buffers */tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,rte_eth_dev_socket_id(portid));if (tx_buffer[portid] == NULL)rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n",portid);rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST);/*设置错误统计回调函数*/ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid],rte_eth_tx_buffer_count_callback,&port_statistics[portid].dropped);/* Start device */ret = rte_eth_dev_start(portid);rte_eth_promiscuous_enable(portid);}if (!nb_ports_available) {rte_exit(EXIT_FAILURE,"All available ports are disabled. Please set portmask.\n");}check_all_ports_link_status(l2fwd_enabled_port_mask);ret = 0;/* launch per-lcore init on every lcore */rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, NULL, CALL_MASTER);RTE_LCORE_FOREACH_SLAVE(lcore_id) {if (rte_eal_wait_lcore(lcore_id) < 0) {ret = -1;break;}}RTE_ETH_FOREACH_DEV(portid) {if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)continue;printf("Closing port %d...", portid);rte_eth_dev_stop(portid);rte_eth_dev_close(portid);printf(" Done\n");}printf("Bye...\n");return ret;
}
rte_eth_dev_configure
rte_eth_dev_configure是EAL对外提供的api(lib/librte_ethdev/rte_ethdev.c),下面分析下dpdk中对网卡设备的配置
rte_eth_rx_queue_setup
rte_eth_rx_queue_setup是EAL对外提供的api(lib/librte_ethdev/rte_ethdev.c),下面分析下dpdk中对队列的配置
- 在rte_eth_dev_configure中设置的offload属性会被设置到每个队列
- 调用调用eth_dev_ops中的rx_queue_setup回调函数
在ixgbe网卡中,对应的rx_queue_setup回调函数是ixgbe_dev_rx_queue_setup
rte_eth_rx_queue_setup就完成了
下面就是启动网卡设备了: