DPDK 网卡驱动学习
DPDK版本19.02
初始化:
/* Launch threads, called at application init(). */ int rte_eal_init(int argc, char **argv) {.../* rte_eal_cpu_init() ->* eal_cpu_core_id()* eal_cpu_socket_id()* 读取/sys/devices/system/[cpu|node]* 设置lcore_config->[core_role|core_id|socket_id] */if (rte_eal_cpu_init() < 0) {rte_eal_init_alert("Cannot detect lcores.");rte_errno = ENOTSUP;return -1;}/* eal_parse_args() ->* eal_parse_common_option() ->* eal_parse_coremask()* eal_parse_master_lcore()* eal_parse_lcores()* eal_adjust_config()* 解析-c、--master_lcore、--lcores参数* 在eal_parse_lcores()中确认可用的logical CPU* 在eal_adjust_config()中设置rte_config.master_lcore为0 (设置第一个lcore为MASTER lcore) */fctret = eal_parse_args(argc, argv);if (fctret < 0) {rte_eal_init_alert("Invalid 'command line' arguments.");rte_errno = EINVAL;rte_atomic32_clear(&run_once);return -1;}.../* 初始化大页信息 */if (rte_eal_memory_init() < 0) {rte_eal_init_alert("Cannot init memory\n");rte_errno = ENOMEM;return -1;}.../* eal_thread_init_master() ->* eal_thread_set_affinity()* 设置当前线程为MASTER lcore* 在eal_thread_set_affinity()中绑定MASTER lcore到logical CPU */eal_thread_init_master(rte_config.master_lcore);.../* rte_bus_scan() ->* rte_pci_scan() ->* pci_scan_one() ->* pci_parse_sysfs_resource()* rte_pci_add_device()* 遍历rte_bus_list链表,调用每个bus的scan函数,pci为rte_pci_scan()* 遍历/sys/bus/pci/devices目录,为每个DBSF分配struct rte_pci_device* 逐行读取并解析每个DBSF的resource,保存到dev->mem_resource[i]* 将dev插入rte_pci_bus.device_list链表 */if (rte_bus_scan()) {rte_eal_init_alert("Cannot scan the buses for devices\n");rte_errno = ENODEV;return -1;}/* pthread_create() ->* eal_thread_loop() ->* eal_thread_set_affinity()* 为每个SLAVE lcore创建线程,线程函数为eal_thread_loop()* 在eal_thread_set_affinity()中绑定SLAVE lcore到logical CPU */RTE_LCORE_FOREACH_SLAVE(i) {/** create communication pipes between master thread* and children*//* MASTER lcore创建pipes用于MASTER和SLAVE lcore间通信(父子线程间通信) */if (pipe(lcore_config[i].pipe_master2slave) < 0)rte_panic("Cannot create pipe\n");if (pipe(lcore_config[i].pipe_slave2master) < 0)rte_panic("Cannot create pipe\n");lcore_config[i].state = WAIT; /* 设置SLAVE lcore的状态为WAIT *//* create a thread for each lcore */ret = pthread_create(&lcore_config[i].thread_id, NULL,eal_thread_loop, NULL);...}/** Launch a dummy function on all slave lcores, so that master lcore* knows they are all ready when this function returns.*/rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER);rte_eal_mp_wait_lcore();.../* Probe all the buses and devices/drivers on them *//* rte_bus_probe() ->* rte_pci_probe() ->* pci_probe_all_drivers() ->* rte_pci_probe_one_driver() ->* rte_pci_match()* rte_pci_map_device() ->* pci_uio_map_resource()* eth_ixgbe_pci_probe()* 遍历rte_bus_list链表,调用每个bus的probe函数,pci为rte_pci_probe()* rte_pci_probe()/pci_probe_all_drivers()分别遍历rte_pci_bus.device_list/driver_list链表,匹配设备和驱动* 映射BAR,调用驱动的probe函数,ixgbe为eth_ixgbe_pci_probe() */if (rte_bus_probe()) {rte_eal_init_alert("Cannot probe devices\n");rte_errno = ENOTSUP;return -1;}... }
在dpdk 16.11是没有bud这一层抽象的,直接通过rte_eal_initàrte_eal_pci_init调用pci设备的初始化。
也就是dpdk 16.11只支持pci这一种总线设备。但是到了dpdk17.11引入了bus的概念。在rte_bus_scan 进行bus的初始化
1 /* Scan all the buses for registered devices */2 int3 rte_bus_scan(void)4 {5 int ret;6 struct rte_bus *bus = NULL;7 8 TAILQ_FOREACH(bus, &rte_bus_list, next) {9 ret = bus->scan(); 10 if (ret) 11 RTE_LOG(ERR, EAL, "Scan for (%s) bus failed.\n", 12 bus->name); 13 } 14 15 return 0; 16 }
这个函数会调用rte_bus_list上注册的所有bus的scan函数,这些bus是通过rte_bus_register函数注册上去的,而宏RTE_REGISTER_BUS又是rte_bus_register的封装。
重要结构体
rte_bus_list
1 struct rte_bus {2 TAILQ_ENTRY(rte_bus) next; /**< Next bus object in linked list */3 const char *name; /**< Name of the bus */4 rte_bus_scan_t scan; /**< Scan for devices attached to bus */5 rte_bus_probe_t probe; /**< Probe devices on bus */6 rte_bus_find_device_t find_device; /**< Find a device on the bus */7 rte_bus_plug_t plug; /**< Probe single device for drivers */8 rte_bus_unplug_t unplug; /**< Remove single device from driver */9 rte_bus_parse_t parse; /**< Parse a device name */ 10 struct rte_bus_conf conf; /**< Bus configuration */ 11 }; 12 13 TAILQ_HEAD(rte_bus_list, rte_bus); 14 15 #define TAILQ_HEAD(name, type) \ 16 struct name { \ 17 struct type *tqh_first; /* first element */ \ 18 struct type **tqh_last; /* addr of last next element */ \ 19 } 20 21 /* 定义rte_bus_list */ 22 struct rte_bus_list rte_bus_list = 23 TAILQ_HEAD_INITIALIZER(rte_bus_list);
注册pci bus
将rte_pci_bus插入rte_bus_list链表
1 struct rte_pci_bus {2 struct rte_bus bus; /**< Inherit the generic class */3 struct rte_pci_device_list device_list; /**< List of PCI devices */4 struct rte_pci_driver_list driver_list; /**< List of PCI drivers */5 };6 7 /* 定义rte_pci_bus */8 struct rte_pci_bus rte_pci_bus = {9 .bus = { 10 .scan = rte_pci_scan, 11 .probe = rte_pci_probe, 12 .find_device = pci_find_device, 13 .plug = pci_plug, 14 .unplug = pci_unplug, 15 .parse = pci_parse, 16 }, 17 .device_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.device_list), 18 .driver_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.driver_list), 19 }; 20 21 RTE_REGISTER_BUS(pci, rte_pci_bus.bus); 22 23 #define RTE_REGISTER_BUS(nm, bus) \ 24 RTE_INIT_PRIO(businitfn_ ##nm, 101); \ /* 声明为gcc构造函数,先于main()执行 */ 25 static void businitfn_ ##nm(void) \ 26 {\ 27 (bus).name = RTE_STR(nm);\ 28 rte_bus_register(&bus); \ 29 } 30 31 void 32 rte_bus_register(struct rte_bus *bus) 33 { 34 RTE_VERIFY(bus); 35 RTE_VERIFY(bus->name && strlen(bus->name)); 36 /* A bus should mandatorily have the scan implemented */ 37 RTE_VERIFY(bus->scan); 38 RTE_VERIFY(bus->probe); 39 RTE_VERIFY(bus->find_device); 40 /* Buses supporting driver plug also require unplug. */ 41 RTE_VERIFY(!bus->plug || bus->unplug); 42 43 /* 将rte_pci_bus.bus插入rte_bus_list链表 */ 44 TAILQ_INSERT_TAIL(&rte_bus_list, bus, next); 45 RTE_LOG(DEBUG, EAL, "Registered [%s] bus.\n", bus->name); 46 }
设备初始化过程:
pci设备的初始化是通过以下路径完成的:rte_eal_inità rte_bus_scan->rte_pci_scan。而对应驱动的加载则是通过如下调用完成的:rte_eal_init->rte_bus_probe->rte_pci_probe完成的。
注册pci driver
将rte_ixgbe_pmd插入rte_pci_bus.driver_list链表
1 struct rte_pci_driver {2 TAILQ_ENTRY(rte_pci_driver) next; /**< Next in list. */3 struct rte_driver driver; /**< Inherit core driver. */4 struct rte_pci_bus *bus; /**< PCI bus reference. */5 pci_probe_t *probe; /**< Device Probe function. */6 pci_remove_t *remove; /**< Device Remove function. */7 const struct rte_pci_id *id_table; /**< ID table, NULL terminated. */8 uint32_t drv_flags; /**< Flags contolling handling of device. */9 }; 10 11 /* 定义rte_ixgbe_pmd */ 12 static struct rte_pci_driver rte_ixgbe_pmd = { 13 .id_table = pci_id_ixgbe_map, 14 .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC, 15 .probe = eth_ixgbe_pci_probe, 16 .remove = eth_ixgbe_pci_remove, 17 }; 18 19 RTE_PMD_REGISTER_PCI(net_ixgbe, rte_ixgbe_pmd); 20 21 #define RTE_PMD_REGISTER_PCI(nm, pci_drv) \ 22 RTE_INIT(pciinitfn_ ##nm); \ /* 声明为gcc构造函数,先于main()执行 */ 23 static void pciinitfn_ ##nm(void) \ 24 {\ 25 (pci_drv).driver.name = RTE_STR(nm);\ 26 rte_pci_register(&pci_drv); \ 27 } \ 28 RTE_PMD_EXPORT_NAME(nm, __COUNTER__) 29 30 void 31 rte_pci_register(struct rte_pci_driver *driver) 32 { 33 /* 将rte_ixgbe_pmd插入rte_pci_bus.driver_list链表 */ 34 TAILQ_INSERT_TAIL(&rte_pci_bus.driver_list, driver, next); 35 driver->bus = &rte_pci_bus; 36 }
eth_ixgbe_dev_init()
1 static int2 eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev)3 {4 ...5 eth_dev->dev_ops = &ixgbe_eth_dev_ops; /* 注册ixgbe_eth_dev_ops函数表 */6 eth_dev->rx_pkt_burst = &ixgbe_recv_pkts; /* burst收包函数 */7 eth_dev->tx_pkt_burst = &ixgbe_xmit_pkts; /* burst发包函数 */8 eth_dev->tx_pkt_prepare = &ixgbe_prep_pkts;9 ... 10 hw->device_id = pci_dev->id.device_id; /* device_id */ 11 hw->vendor_id = pci_dev->id.vendor_id; /* vendor_id */ 12 hw->hw_addr = (void *)pci_dev->mem_resource[0].addr; /* mmap()得到的BAR的虚拟地址 */ 13 ... 14 /* ixgbe_init_shared_code() -> 15 * ixgbe_set_mac_type() 16 * ixgbe_init_ops_82599() 17 * 在ixgbe_set_mac_type()中根据vendor_id和device_id设置hw->mac.type,82599为ixgbe_mac_82599EB 18 * 根据hw->mac.type调用对应的函数设置hw->mac.ops,82599为ixgbe_init_ops_82599() */ 19 diag = ixgbe_init_shared_code(hw); 20 ... 21 /* ixgbe_init_hw() -> 22 * ixgbe_call_func() -> 23 * ixgbe_init_hw_generic() -> 24 * ixgbe_reset_hw_82599() -> 25 * ixgbe_get_mac_addr_generic() 26 * 得到网卡的mac地址 */ 27 diag = ixgbe_init_hw(hw); 28 ... 29 ether_addr_copy((struct ether_addr *) hw->mac.perm_addr, 30 ð_dev->data->mac_addrs[0]); /* 复制网卡的mac地址到eth_dev->data->mac_addrs */ 31 ... 32 } 33 34 static const struct eth_dev_ops ixgbe_eth_dev_ops = { 35 .dev_configure = ixgbe_dev_configure, 36 .dev_start = ixgbe_dev_start, 37 ... 38 .rx_queue_setup = ixgbe_dev_rx_queue_setup, 39 ... 40 .tx_queue_setup = ixgbe_dev_tx_queue_setup, 41 ... 42 }
(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! !
eth_ixgbe_pci_probe()
1 static int eth_ixgbe_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,2 struct rte_pci_device *pci_dev)3 {4 return rte_eth_dev_pci_generic_probe(pci_dev,5 sizeof(struct ixgbe_adapter), eth_ixgbe_dev_init);6 }7 8 static inline int9 rte_eth_dev_pci_generic_probe(struct rte_pci_device *pci_dev, 10 size_t private_data_size, eth_dev_pci_callback_t dev_init) 11 { 12 ... 13 eth_dev = rte_eth_dev_pci_allocate(pci_dev, private_data_size); 14 ... 15 ret = dev_init(eth_dev); /* ixgbe为eth_ixgbe_dev_init() */ 16 ... 17 } 18 19 static inline struct rte_eth_dev * 20 rte_eth_dev_pci_allocate(struct rte_pci_device *dev, size_t private_data_size) 21 { 22 ... 23 /* rte_eth_dev_allocate() -> 24 * rte_eth_dev_find_free_port() 25 * rte_eth_dev_data_alloc() 26 * eth_dev_get() */ 27 eth_dev = rte_eth_dev_allocate(name); 28 ... 29 /* 分配private data,ixgbe为struct ixgbe_adapter */ 30 eth_dev->data->dev_private = rte_zmalloc_socket(name, 31 private_data_size, RTE_CACHE_LINE_SIZE, 32 dev->device.numa_node); 33 ... 34 } 35 36 struct rte_eth_dev * 37 rte_eth_dev_allocate(const char *name) 38 { 39 ... 40 /* 遍历rte_eth_devices数组,找到一个空闲的设备 */ 41 port_id = rte_eth_dev_find_free_port(); 42 ... 43 /* 分配rte_eth_dev_data数组 */ 44 rte_eth_dev_data_alloc(); 45 ... 46 /* 设置port_id对应的设备的state为RTE_ETH_DEV_ATTACHED */ 47 eth_dev = eth_dev_get(port_id); 48 ... 49 }
ixgbe_recv_pkts()
接收时回写:
1、网卡使用DMA写Rx FIFO中的Frame到Rx Ring Buffer中的mbuf,设置desc的DD为1
2、网卡驱动取走mbuf后,设置desc的DD为0,更新RDT
1 uint16_t2 ixgbe_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,3 uint16_t nb_pkts)4 {5 ...6 nb_rx = 0;7 nb_hold = 0;8 rxq = rx_queue;9 rx_id = rxq->rx_tail; /* 相当于ixgbe的next_to_clean */ 10 rx_ring = rxq->rx_ring; 11 sw_ring = rxq->sw_ring; 12 ... 13 while (nb_rx < nb_pkts) { 14 ... 15 /* 得到rx_tail指向的desc的指针 */ 16 rxdp = &rx_ring[rx_id]; 17 /* 若网卡回写的DD为0,跳出循环 */ 18 staterr = rxdp->wb.upper.status_error; 19 if (!(staterr & rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD))) 20 break; 21 /* 得到rx_tail指向的desc */ 22 rxd = *rxdp; 23 ... 24 /* 分配新mbuf */ 25 nmb = rte_mbuf_raw_alloc(rxq->mb_pool); 26 ... 27 nb_hold++; /* 统计接收的mbuf数 */ 28 rxe = &sw_ring[rx_id]; /* 得到旧mbuf */ 29 rx_id++; /* 得到下一个desc的index,注意是一个环形缓冲区 */ 30 if (rx_id == rxq->nb_rx_desc) 31 rx_id = 0; 32 ... 33 rte_ixgbe_prefetch(sw_ring[rx_id].mbuf); /* 预取下一个mbuf */ 34 ... 35 if ((rx_id & 0x3) == 0) { 36 rte_ixgbe_prefetch(&rx_ring[rx_id]); 37 rte_ixgbe_prefetch(&sw_ring[rx_id]); 38 } 39 ... 40 rxm = rxe->mbuf; /* rxm指向旧mbuf */ 41 rxe->mbuf = nmb; /* rxe->mbuf指向新mbuf */ 42 dma_addr = 43 rte_cpu_to_le_64(rte_mbuf_data_dma_addr_default(nmb)); /* 得到新mbuf的总线地址 */ 44 rxdp->read.hdr_addr = 0; /* 清零新mbuf对应的desc的DD,后续网卡会读desc */ 45 rxdp->read.pkt_addr = dma_addr; /* 设置新mbuf对应的desc的总线地址,后续网卡会读desc */ 46 ... 47 pkt_len = (uint16_t) (rte_le_to_cpu_16(rxd.wb.upper.length) - 48 rxq->crc_len); /* 包长 */ 49 rxm->data_off = RTE_PKTMBUF_HEADROOM; 50 rte_packet_prefetch((char *)rxm->buf_addr + rxm->data_off); 51 rxm->nb_segs = 1; 52 rxm->next = NULL; 53 rxm->pkt_len = pkt_len; 54 rxm->data_len = pkt_len; 55 rxm->port = rxq->port_id; 56 ... 57 if (likely(pkt_flags & PKT_RX_RSS_HASH)) /* RSS */ 58 rxm->hash.rss = rte_le_to_cpu_32( 59 rxd.wb.lower.hi_dword.rss); 60 else if (pkt_flags & PKT_RX_FDIR) { /* FDIR */ 61 rxm->hash.fdir.hash = rte_le_to_cpu_16( 62 rxd.wb.lower.hi_dword.csum_ip.csum) & 63 IXGBE_ATR_HASH_MASK; 64 rxm->hash.fdir.id = rte_le_to_cpu_16( 65 rxd.wb.lower.hi_dword.csum_ip.ip_id); 66 } 67 ... 68 rx_pkts[nb_rx++] = rxm; /* 将旧mbuf放入rx_pkts数组 */ 69 } 70 rxq->rx_tail = rx_id; /* rx_tail指向下一个desc */ 71 ... 72 nb_hold = (uint16_t) (nb_hold + rxq->nb_rx_hold); 73 /* 若已处理的mbuf数大于上限(默认为32),更新RDT */ 74 if (nb_hold > rxq->rx_free_thresh) { 75 ... 76 rx_id = (uint16_t) ((rx_id == 0) ? 77 (rxq->nb_rx_desc - 1) : (rx_id - 1)); 78 IXGBE_PCI_REG_WRITE(rxq->rdt_reg_addr, rx_id); /* 将rx_id写入RDT */ 79 nb_hold = 0; /* 清零nb_hold */ 80 } 81 rxq->nb_rx_hold = nb_hold; /* 更新nb_rx_hold */ 82 return nb_rx; 83 }
ixgbe_xmit_pkts()
1 uint16_t2 ixgbe_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,3 uint16_t nb_pkts)4 {5 ...6 txq = tx_queue;7 sw_ring = txq->sw_ring;8 txr = txq->tx_ring;9 tx_id = txq->tx_tail; /* 相当于ixgbe的next_to_use */ 10 txe = &sw_ring[tx_id]; /* 得到tx_tail指向的entry */ 11 txp = NULL; 12 ... 13 /* 若空闲的mbuf数小于下限(默认为32),清理空闲的mbuf */ 14 if (txq->nb_tx_free < txq->tx_free_thresh) 15 ixgbe_xmit_cleanup(txq); 16 ... 17 /* TX loop */ 18 for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) { 19 ... 20 tx_pkt = *tx_pkts++; /* 待发送的mbuf */ 21 pkt_len = tx_pkt->pkt_len; /* 待发送的mbuf的长度 */ 22 ... 23 nb_used = (uint16_t)(tx_pkt->nb_segs + new_ctx); /* 使用的desc数 */ 24 ... 25 tx_last = (uint16_t) (tx_id + nb_used - 1); /* tx_last指向最后一个desc */ 26 ... 27 if (tx_last >= txq->nb_tx_desc) /* 注意是一个环形缓冲区 */ 28 tx_last = (uint16_t) (tx_last - txq->nb_tx_desc); 29 ... 30 if (nb_used > txq->nb_tx_free) { 31 ... 32 if (ixgbe_xmit_cleanup(txq) != 0) { 33 /* Could not clean any descriptors */ 34 if (nb_tx == 0) /* 若是第一个包(未发包),return 0 */ 35 return 0; 36 goto end_of_tx; /* 若非第一个包(已发包),停止发包,更新发送队列参数 */ 37 } 38 ... 39 } 40 ... 41 /* 每个包可能包含多个分段,m_seg指向第一个分段 */ 42 m_seg = tx_pkt; 43 do { 44 txd = &txr[tx_id]; /* desc */ 45 txn = &sw_ring[txe->next_id]; /* 下一个entry */ 46 ... 47 txe->mbuf = m_seg; /* 将m_seg挂载到txe */ 48 ... 49 slen = m_seg->data_len; /* m_seg的长度 */ 50 buf_dma_addr = rte_mbuf_data_dma_addr(m_seg); /* m_seg的总线地址 */ 51 txd->read.buffer_addr = 52 rte_cpu_to_le_64(buf_dma_addr); /* 总线地址赋给txd->read.buffer_addr */ 53 txd->read.cmd_type_len = 54 rte_cpu_to_le_32(cmd_type_len | slen); /* 长度赋给txd->read.cmd_type_len */ 55 ... 56 txe->last_id = tx_last; /* last_id指向最后一个desc */ 57 tx_id = txe->next_id; /* tx_id指向下一个desc */ 58 txe = txn; /* txe指向下一个entry */ 59 m_seg = m_seg->next; /* m_seg指向下一个分段 */ 60 } while (m_seg != NULL); 61 ... 62 /* 最后一个分段 */ 63 cmd_type_len |= IXGBE_TXD_CMD_EOP; 64 txq->nb_tx_used = (uint16_t)(txq->nb_tx_used + nb_used); /* 更新nb_tx_used */ 65 txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_used); /* 更新nb_tx_free */ 66 ... 67 if (txq->nb_tx_used >= txq->tx_rs_thresh) { /* 若使用的mbuf数大于上限(默认为32),设置RS */ 68 ... 69 cmd_type_len |= IXGBE_TXD_CMD_RS; 70 ... 71 txp = NULL; /* txp为NULL表示已设置RS */ 72 } else 73 txp = txd; /* txp非NULL表示未设置RS */ 74 ... 75 txd->read.cmd_type_len |= rte_cpu_to_le_32(cmd_type_len); 76 } 77 ... 78 end_of_tx: 79 /* burst发包的最后一个包的最后一个分段 */ 80 ... 81 if (txp != NULL) /* 若未设置RS,设置RS */ 82 txp->read.cmd_type_len |= rte_cpu_to_le_32(IXGBE_TXD_CMD_RS); 83 ... 84 IXGBE_PCI_REG_WRITE_RELAXED(txq->tdt_reg_addr, tx_id); /* 将tx_id写入TDT */ 85 txq->tx_tail = tx_id; /* tx_tail指向下一个desc */ 86 ... 87 return nb_tx; 88 }
rte_eth_rx/tx_burst()
1 static inline uint16_t2 rte_eth_rx_burst(uint8_t port_id, uint16_t queue_id,3 struct rte_mbuf **rx_pkts, const uint16_t nb_pkts)4 {5 /* 得到port_id对应的设备 */6 struct rte_eth_dev *dev = &rte_eth_devices[port_id];7 ...8 /* ixgbe为ixgbe_recv_pkts() */9 int16_t nb_rx = (*dev->rx_pkt_burst)(dev->data->rx_queues[queue_id], 10 rx_pkts, nb_pkts); 11 ... 12 } 13 14 static inline uint16_t 15 rte_eth_tx_burst(uint8_t port_id, uint16_t queue_id, 16 struct rte_mbuf **tx_pkts, uint16_t nb_pkts) 17 { 18 /* 得到port_id对应的设备 */ 19 struct rte_eth_dev *dev = &rte_eth_devices[port_id]; 20 ... 21 /* ixgbe为ixgbe_xmit_pkts */ 22 return (*dev->tx_pkt_burst)(dev->data->tx_queues[queue_id], tx_pkts, nb_pkts); 23 }
1、从描述符中获取报文返回给应用层
首先根据应用层最后一次获取报文的位置,进而从描述符队列找到待被应用层接收的描述符。此时会判断描述符中的status_error是否已经打上了dd标记,有dd标记说明dma控制器已经把报文放到mbuf中了。这里解释下dd标记,当dma控制器将接收到的报文保存到描述符指向的mbuf空间时,由dma控制器打上dd标记,表示dma控制器已经把报文放到mbuf中了。应用层在获取完报文后,需要清除dd标记。
找到了描述符的位置,也就找到了mbuf空间。此时会根据描述符里面保存的信息,填充mbuf结构。例如填充报文的长度,vlanid, rss等信息。填充完mbuf后,将这个mbuf保存到应用层传进来的结构中,返回给应用层,这样应用层就获取到了这个报文。
1 uint16_t eth_igb_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)2 {3 while (nb_rx < nb_pkts) 4 {5 //从描述符队列中找到待被应用层最后一次接收的那个描述符位置6 rxdp = &rx_ring[rx_id];7 staterr = rxdp->wb.upper.status_error;8 //检查状态是否为dd, 不是则说明驱动还没有把报文放到接收队列,直接退出9 if (! (staterr & rte_cpu_to_le_32(E1000_RXD_STAT_DD))) 10 { 11 break; 12 }; 13 //找到了描述符的位置,也就从软件队列中找到了mbuf 14 rxe = &sw_ring[rx_id]; 15 rx_id++; 16 rxm = rxe->mbuf; 17 //填充mbuf 18 pkt_len = (uint16_t) (rte_le_to_cpu_16(rxd.wb.upper.length) - rxq->crc_len); 19 rxm->data_off = RTE_PKTMBUF_HEADROOM; 20 rxm->nb_segs = 1; 21 rxm->pkt_len = pkt_len; 22 rxm->data_len = pkt_len; 23 rxm->port = rxq->port_id; 24 rxm->hash.rss = rxd.wb.lower.hi_dword.rss; 25 rxm->vlan_tci = rte_le_to_cpu_16(rxd.wb.upper.vlan); 26 //保存到应用层 27 rx_pkts[nb_rx++] = rxm; 28 } 29 }
2、从内存池中获取新的mbuf告诉dma控制器
当应用层从软件队列中获取到mbuf后, 需要重新从内存池申请一个mbuf空间,并将mbuf地址放到描述符队列中, 相当于告诉dma控制器,后续将收到的报文保存到这个新的mbuf中, 这也是狸猫换太子的过程。描述符是mbuf与dma控制器的中介,那dma控制器怎么知道描述符队列的地址呢?这在上一篇文章中已经介绍过了,将描述符队列的地址写入到了寄存器中,dma控制器通过读取寄存器就知道描述符队列的地址。
需要注意的是,将mbuf的地址保存到描述符中,此时会将dd标记给清0,这样dma控制器就认为这个mbuf里面的内容已经被应用层接收了,收到新报文后可以重新放到这个mbuf中。
1 uint16_t eth_igb_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)2 {3 while (nb_rx < nb_pkts) 4 {5 //申请一个新的mbuf6 nmb = rte_rxmbuf_alloc(rxq->mb_pool);7 //因为原来的mbuf被应用层取走了。这里替换原来的软件队列mbuf,这样网卡收到报文后可以放到这个新的mbuf8 rxe->mbuf = nmb;9 dma_addr = rte_cpu_to_le_64(RTE_MBUF_DATA_DMA_ADDR_DEFAULT(nmb)); 10 //将mbuf地址保存到描述符中,相当于高速dma控制器mbuf的地址。 11 rxdp->read.hdr_addr = dma_addr; //这里会将dd标记清0 12 rxdp->read.pkt_addr = dma_addr; 13 } 14 }
原文链接:https://www.cnblogs.com/mysky007/p/11219593.html
DPDK 网卡驱动学习相关推荐
- DPDK网卡驱动流程总结
本文基于DPDK-16.07.2.Linux 4.4.2分析总结 1 简介 1.1 什么是UIO技术 UIO(Userspace I/O)是运行在用户空间的I/O技术,Linux系统中一般的驱动设备都 ...
- Linux 网卡驱动学习(六)(应用层、tcp 层、ip 层、设备层和驱动层作用解析)
本文将介绍网络连接建立的过程.收发包流程,以及其中应用层.tcp层.ip层.设备层和驱动层各层发挥的作用. 1.应用层 对于使用socket进行网络连接的服务器端程序,我们会先调用socket函数创建 ...
- 网卡驱动学习一、wireless基础知识(cfg80211 mac80211 nl80211)
1. 基本概念 cfg80211: 用于对无线设备进行配置管理.与FullMAC, mac80211和nl80211一起工作.(Kernel态) mac80211: 是一个driver开发者可用于为S ...
- DPDK — 网卡驱动初始化流程
目录 文章目录 目录 Intel 82599 ixgbe 网卡驱动示例 rte_eth_dev/rte_eth_dev_data 数据结构 rte_eth_dev_count 函数 rte_eth_d ...
- Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)
在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层. 网络堆栈是硬件中独立出来的部分.主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层 ...
- Linux 网卡驱动学习(九)(层二转发)
1.mac 地址表的自学习过程 port1上的A计算机要与port2上的B计算机通信时,A发到交换机上,交换机收到信息后,交换机先记录发port1所相应的a的mac地址并记录在自己的mac表中,然后再 ...
- Linux 网卡驱动学习(二)(网络驱动接口小结)
[摘要]前文我们分析了一个虚拟硬件的网络驱动样例.从中我们看到了网络设备的一些接口.事实上网络设备驱动和块设备驱动的功能比較相似,都是发送和接收数据包(数据请求). 当然它们实际是有非常多不同的. 1 ...
- 网卡驱动学习五、hostapd 移植测试
文章目录 1. openssl-1.1.1h 移植 1.1 openssl 简介 1.2 openssl-1.1.1h 下载 1.2 openssl-1.1.1h 解压配置 1.3 openssl-1 ...
- 网卡驱动学习四、mac80211概览
1. mac80211概览 目录 文章目录 1. mac80211概览 2. mac80211架构 3. mac80211代码结构 4. mac80211数据结构体 4.1 ieee80211 loc ...
最新文章
- linux 开机启动 自启动 设置
- python微型web框架flask介绍
- win7服务器远程灰色的,小编为你细说win7系统远程协助复选框是灰色的详细技巧...
- leetcode283. 移动零 比官方更好的解法。
- shell变量的使用
- 【重点】剑指offer——面试题65:滑动窗口的最大值
- okhttp post php,Android OkHttp 结合php 多图片上传实例
- Java字符串排序后输出
- C++高级编程资料汇总(参考手册+电子书+编程思想+算法分析)
- fun的用法c语言,fun的用法_fun的用法
- 邮件中的FYI和PFA代表什么意思
- Chrome和360浏览器“无法翻译此网页”解决方法
- 我的世界服务器vip账号和密码是多少,我的世界hypixel服务器会员有什么区别 hypixel服务器会员介绍...
- python实现微信发红包
- Codeforces Global Round 11 ABCD题解
- WinDBG 技巧:显示进程/线程环境参数(!peb 和 !teb 命令)
- NUC 折腾笔记 - 储存能力测试
- 限流常见方案,值得一看
- 企业数据资产管理平台建设方案(PPT)
- Windows Server 2003 服务器插入移动硬盘不显示