P小二 P小二
← 返回文章 DPU 约 7 分钟

13.DPU网络开发SDK——DPDK(十二)

DPU网络开发SDK——DPDK(十二)

继续分析rte_pci_probe_one_driver()的实现,接下来调用与设备匹配的驱动的probe方法,此处以ixgbe设备的驱动为例,其probe方法为eth_ixgbe_pci_probe()。

static int eth_ixgbe_pci_probe(struct rte_pci_driver *pci_drv rte_unused, struct rte_pci_device *pci_dev) { char name[RTE_ETH_NAME_MAX_LEN]; struct rte_eth_dev pf_ethdev; struct rte_eth_devargs eth_da; int i, retval; if (pci_dev->device.devargs) { retval = rte_eth_devargs_parse(pci_dev->device.devargs->args, ð_da); …… } else memset(ð_da, 0, sizeof(eth_da)); 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); …… pf_ethdev = rte_eth_dev_allocated(pci_dev->device.name); …… / probe VF representor ports / for (i = 0; i data->dev_private); …… representor.vf_id = eth_da.representor_ports[i]; representor.switch_domain_id = vfinfo->switch_domain_id; representor.pf_ethdev = pf_ethdev; / representor port net_bdf_port */ snprintf(name, sizeof(name), “net%s_representor%d”, pci_dev->device.name, eth_da.representor_ports[i]); retval = rte_eth_dev_create(&pci_dev->device, name, sizeof(struct ixgbe_vf_representor), NULL, NULL, ixgbe_vf_representor_init, &representor); …… } return 0;}


eth_ixgbe_pci_probe()中,如果存在,首先解析设备的devargs,调用rte_eth_devargs_parse()实现,主要是解析参数中传入的port和representor port的信息(对应的VFIO技术中的PF和VF)。之后调用rte_eth_dev_create()创建设备,调用该函数时,会传入两个函数参数,eth_dev_pci_specific_init()和eth_ixgbe_dev_init(),其中eth_ixgbe_dev_init()不得是空值。

- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- ```
int rte_eth_dev_create(struct rte_device *device, const char *name,   size_t priv_data_size, ethdev_bus_specific_init ethdev_bus_specific_init,   void *bus_init_params, ethdev_init_t ethdev_init, void *init_params) {   struct rte_eth_dev *ethdev;   int retval;   ……   if (rte_eal_process_type() == RTE_PROC_PRIMARY) {      ethdev = rte_eth_dev_allocate(name);      ……      if (priv_data_size) {         ethdev->data->dev_private = rte_zmalloc_socket(            name, priv_data_size, RTE_CACHE_LINE_SIZE,            device->numa_node);         ……      }   } else {      ethdev = rte_eth_dev_attach_secondary(name);      ……   }   ethdev->device = device;   if (ethdev_bus_specific_init) {      retval = ethdev_bus_specific_init(ethdev, bus_init_params);      if (retval) {         RTE_ETHDEV_LOG(ERR,            "ethdev bus specific initialisation failed\n");         goto probe_failed;      }   }   retval = ethdev_init(ethdev, init_params);   if (retval) {      RTE_ETHDEV_LOG(ERR, "ethdev initialisation failed\n");      goto probe_failed;   }   rte_eth_dev_probing_finish(ethdev);   return retval;   ……}

rte_eth_dev_create()中,会根据DPDK是primary还是secondary的不同,采用不同的方式创建设备

  • primary

primary模式下,调用rte_eth_dev_allocate()创建设备,创建设备之前,会检查模块全局指针eth_dev_shared_data是否进行了初始化,未做初始化时进行初始化操作。该指针指向的内存区域,包含了必要的锁和需要共享的数据,且是通过memzone接口创建出来的,故对该内存区域的修改能够被DPDK的secondary进程看到。接下来调用eth_dev_find_free_port()在rte_eth_devices数组中查找空闲的项(rte_eth_dev类型),找到的话,将设备的name,port_id,mtu等信息填充到rte_eth_dev->data中,另外在data->data_private中创建一块区域存储,用来存储struct ixgeb_adaptor结构,这里需要特别注意的是rte_eth_dev->data的存储区域,对应的是eth_dev_shared_data->data[i],对应rte_eth_dev->data的读写实际上是对eth_dev_shared_data->data[i]的读写。

  • secondary

secondary模式下,调用rte_eth_dev_attach_secondary()创建设备。会在eth_dev_shared_data中查找设备的名称,如果找到,则将eth_dev_shared_data中对应port的data赋值给rte_eth_dev->data。这样secondary进程中的数据看到的和primary中的数据是相同的。

创建完成之后,将rte_eth_dev和rte_device进行关联。如果ethdev_bus_specific_init()函数不为空,则执行,此处对应的是eth_dev_pci_specific_init(),主要工作是将rte_pci_device中的一些信息拷贝到rte_eth_dev中。接下来执行ethdev_init(),此处对应的是eth_ixgbe_dev_init()。

该func主要是初始化rte_eth_dev中的函数指针,保护数据包的收发,网卡配置,网卡信息获取,网卡队列操作等。如果是VFIO设备,还承担着PF初始化等工作。在最后,调用rte_eth_dev_probing_finish()将设备状态置为RTE_ETH_DEV_ATTACHED。

rte_eth_dev_create()之后,调用rte_eth_dev_allocated()获取PF设备的指针,根据devargs中配置的representor port的信息,配置VF设备。

此处仍然是调用rte_eth_dev_create()来创建设备,与之前不同的是,此时设备的data->data_private存储的是struct ixgeb_vf_representor,设备初始化接口变成了ixgbe_vf_representor_init(),其主要工作与eth_ixgbe_dev_init()类似。

至此,我们对rte_eth_devices数据结构的填充分析完成。从前面的分析内容可以看出,在DPDK的init阶段这些信息就已经被填充到模块局部变量rte_eth_devices当中,接下来,我们可以返回第(九)篇文章中,继续对example/ethtool进行分析。

ethtool这个范例中,自定义了setup_ports()接口,包含了如下的一些操作。

static void setup_ports(struct app_config *app_cfg, int cnt_ports) { …… for (idx_port = 0; idx_port ports[idx_port]; ret = rte_eth_dev_info_get(idx_port, &dev_info); …… size_pktpool = dev_info.rx_desc_lim.nb_max + dev_info.tx_desc_lim.nb_max + PKTPOOL_EXTRA_SIZE; snprintf(str_name, 16, “pkt_pool%i”, idx_port); ptr_port->pkt_pool = rte_pktmbuf_pool_create( str_name, size_pktpool, PKTPOOL_CACHE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); …… printf(“Init port %i..\n”, idx_port); ptr_port->port_active = 1; ptr_port->port_dirty = 0; ptr_port->idx_port = idx_port; if (rte_eth_dev_configure(idx_port, 1, 1, &cfg_port) pkt_pool) mac_addr); …… rte_spinlock_init(&ptr_port->lock); }}


setup_ports()将一些需要网卡设备进行配置的工作进行了封装,接下来将对这些内容进行分析。

**往期回顾:**

[12.DPU网络开发SDK——DPDK(十一)](http://mp.weixin.qq.com/s?__biz=MzAwNzc4MDgwNQ==&mid=2653126221&idx=1&sn=f65d4f4aeb36f636e77c453c54d17a09&chksm=80af3ee7b7d8b7f19b2f3aea7c6133be6988297bd6e5d27896d3684b9f64aa05cc9206303994&scene=21#wechat_redirect)

[11.DPU网络开发SDK——DPDK(十)](http://mp.weixin.qq.com/s?__biz=MzAwNzc4MDgwNQ==&mid=2653126205&idx=1&sn=372dc7222ad423a7f556f823a9f329bd&chksm=80af3e97b7d8b781f66714ba1a604592a29d5bf76014b50f02f550048715669831e1ed0d6e8e&scene=21#wechat_redirect)